improve error reporting from IMAP list parsing

so far, we'd print only a generic message - except in two cases, where
the generic error would be preceded by a specific one. now we always
print a single reasonably specific message.
This commit is contained in:
Oswald Buddenhagen 2021-11-24 22:25:49 +01:00
parent c7f50a3069
commit a07be5f175

View File

@ -75,6 +75,7 @@ typedef struct {
list_t *head, **stack[MAX_LIST_DEPTH]; list_t *head, **stack[MAX_LIST_DEPTH];
int (*callback)( imap_store_t *ctx, list_t *list ); int (*callback)( imap_store_t *ctx, list_t *list );
int level, need_bytes; int level, need_bytes;
const char *err;
} parse_list_state_t; } parse_list_state_t;
typedef struct imap_cmd imap_cmd_t; typedef struct imap_cmd imap_cmd_t;
@ -109,6 +110,7 @@ union imap_store {
uint caps; // CAPABILITY results uint caps; // CAPABILITY results
string_list_t *auth_mechs; string_list_t *auth_mechs;
parse_list_state_t parse_list_sts; parse_list_state_t parse_list_sts;
const char *parse_list_what;
char *parse_list_cmd; char *parse_list_cmd;
// Command queue // Command queue
imap_cmd_t *pending, **pending_append; imap_cmd_t *pending, **pending_append;
@ -819,8 +821,10 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
goto getbytes; goto getbytes;
} }
if (!s) if (!s) {
sts->err = "missing value";
return LIST_BAD; return LIST_BAD;
}
for (;;) { for (;;) {
while (isspace( (uchar)*s )) while (isspace( (uchar)*s ))
s++; s++;
@ -836,7 +840,7 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
if (*s == '(') { if (*s == '(') {
/* sublist */ /* sublist */
if (sts->level == MAX_LIST_DEPTH) if (sts->level == MAX_LIST_DEPTH)
goto bail; goto toodeep;
s++; s++;
cur->val = LIST; cur->val = LIST;
sts->stack[sts->level++] = curp; sts->stack[sts->level++] = curp;
@ -846,11 +850,12 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
} else if (ctx && *s == '{') { } else if (ctx && *s == '{') {
/* literal */ /* literal */
bytes = (int)(cur->len = strtoul( s + 1, &s, 10 )); bytes = (int)(cur->len = strtoul( s + 1, &s, 10 ));
if (*s != '}' || *++s) if (*s != '}' || *++s) {
sts->err = "malformed literal";
goto bail; goto bail;
}
if ((uint)bytes >= INT_MAX) { if ((uint)bytes >= INT_MAX) {
error( "IMAP error: excessively large literal from %s " sts->err = "excessively large literal - THIS MIGHT BE AN ATTEMPT TO HACK YOU!";
"- THIS MIGHT BE AN ATTEMPT TO HACK YOU!\n", ctx->conn.name );
goto bail; goto bail;
} }
@ -861,7 +866,7 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
n = socket_read( &ctx->conn, s, (uint)bytes ); n = socket_read( &ctx->conn, s, (uint)bytes );
if (n < 0) { if (n < 0) {
badeof: badeof:
error( "IMAP error: unexpected EOF from %s\n", ctx->conn.name ); sts->err = "unexpected EOF";
goto bail; goto bail;
} }
bytes -= n; bytes -= n;
@ -891,8 +896,10 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
while ((c = *s++) != '"') { while ((c = *s++) != '"') {
if (c == '\\') if (c == '\\')
c = *s++; c = *s++;
if (!c) if (!c) {
sts->err = "unterminated quoted string";
goto bail; goto bail;
}
*d++ = c; *d++ = c;
} }
cur->len = (uint)(d - p); cur->len = (uint)(d - p);
@ -914,8 +921,10 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
if (!sts->level) if (!sts->level)
break; break;
next2: next2:
if (!*s) if (!*s) {
sts->err = "unterminated list";
goto bail; goto bail;
}
} }
*sp = s; *sp = s;
return LIST_OK; return LIST_OK;
@ -926,6 +935,8 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
sts->need_bytes = bytes; sts->need_bytes = bytes;
return LIST_PARTIAL; return LIST_PARTIAL;
} }
toodeep:
sts->err = "too deeply nested lists";
bail: bail:
free_list( sts->head ); free_list( sts->head );
sts->level = 0; sts->level = 0;
@ -937,6 +948,7 @@ parse_list_init( parse_list_state_t *sts )
{ {
sts->need_bytes = -1; sts->need_bytes = -1;
sts->level = 1; sts->level = 1;
sts->err = NULL;
sts->head = NULL; sts->head = NULL;
sts->stack[0] = &sts->head; sts->stack[0] = &sts->head;
} }
@ -964,12 +976,23 @@ parse_next_list( imap_store_t *ctx, int (*cb)( imap_store_t *ctx, list_t *list )
} }
static int static int
parse_list( imap_store_t *ctx, char *s, int (*cb)( imap_store_t *ctx, list_t *list ) ) parse_list( imap_store_t *ctx, char *s, int (*cb)( imap_store_t *ctx, list_t *list ), const char *what )
{ {
ctx->parse_list_cmd = s; ctx->parse_list_cmd = s;
ctx->parse_list_what = what;
return parse_next_list( ctx, cb ); return parse_next_list( ctx, cb );
} }
static int
parse_list_perror( imap_store_t *ctx )
{
if (!ctx->parse_list_sts.err)
ctx->parse_list_sts.err = "unexpected value";
error( "IMAP error: malformed %s response from %s: %s\n",
ctx->parse_list_what, ctx->conn.name, ctx->parse_list_sts.err );
return LIST_BAD;
}
static int parse_namespace_rsp_p2( imap_store_t *, list_t * ); static int parse_namespace_rsp_p2( imap_store_t *, list_t * );
static int parse_namespace_rsp_p3( imap_store_t *, list_t * ); static int parse_namespace_rsp_p3( imap_store_t *, list_t * );
@ -981,8 +1004,7 @@ parse_namespace_rsp( imap_store_t *ctx, list_t *list )
if (!list) { if (!list) {
bad: bad:
error( "IMAP error: malformed NAMESPACE response\n" ); return parse_list_perror( ctx );
return LIST_BAD;
} }
if (list->val != NIL) { if (list->val != NIL) {
if (list->val != LIST) if (list->val != LIST)
@ -1121,10 +1143,8 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list )
uint uid = 0, size = 0, msgid_len = 0; uint uid = 0, size = 0, msgid_len = 0;
time_t date = 0; time_t date = 0;
if (!is_list( list )) { if (!is_list( list ))
error( "IMAP error: bogus FETCH response\n" ); return parse_list_perror( ctx );
return LIST_BAD;
}
for (tmp = list->child; tmp; tmp = tmp->next) { for (tmp = list->child; tmp; tmp = tmp->next) {
if (!is_atom( tmp )) { if (!is_atom( tmp )) {
@ -1354,10 +1374,8 @@ parse_list_rsp( imap_store_t *ctx, list_t *list )
{ {
list_t *lp; list_t *lp;
if (!is_list( list )) { if (!is_list( list ))
error( "IMAP error: malformed LIST response\n" ); return parse_list_perror( ctx );
return LIST_BAD;
}
for (lp = list->child; lp; lp = lp->next) for (lp = list->child; lp; lp = lp->next)
if (is_atom( lp ) && !strcasecmp( lp->val, "\\NoSelect" )) if (is_atom( lp ) && !strcasecmp( lp->val, "\\NoSelect" ))
return LIST_OK; return LIST_OK;
@ -1367,10 +1385,8 @@ parse_list_rsp( imap_store_t *ctx, list_t *list )
static int static int
parse_list_rsp_p1( imap_store_t *ctx, list_t *list ) parse_list_rsp_p1( imap_store_t *ctx, list_t *list )
{ {
if (!is_opt_atom( list )) { if (!is_opt_atom( list ))
error( "IMAP error: malformed LIST response\n" ); return parse_list_perror( ctx );
return LIST_BAD;
}
if (!ctx->delimiter[0] && is_atom( list )) if (!ctx->delimiter[0] && is_atom( list ))
ctx->delimiter[0] = list->val[0]; ctx->delimiter[0] = list->val[0];
return parse_next_list( ctx, parse_list_rsp_p2 ); return parse_next_list( ctx, parse_list_rsp_p2 );
@ -1413,10 +1429,8 @@ parse_list_rsp_p2( imap_store_t *ctx, list_t *list )
int argl; int argl;
uint l; uint l;
if (!is_atom( list )) { if (!is_atom( list ))
error( "IMAP error: malformed LIST response\n" ); return parse_list_perror( ctx );
return LIST_BAD;
}
arg = list->val; arg = list->val;
argl = (int)list->len; argl = (int)list->len;
if (argl > 1000) { if (argl > 1000) {
@ -1623,10 +1637,10 @@ imap_socket_read( void *aux )
} else if (!strcmp( "CAPABILITY", arg )) { } else if (!strcmp( "CAPABILITY", arg )) {
parse_capability( ctx, cmd ); parse_capability( ctx, cmd );
} else if (!strcmp( "LIST", arg ) || !strcmp( "LSUB", arg )) { } else if (!strcmp( "LIST", arg ) || !strcmp( "LSUB", arg )) {
resp = parse_list( ctx, cmd, parse_list_rsp ); resp = parse_list( ctx, cmd, parse_list_rsp, "LIST" );
goto listret; goto listret;
} else if (!strcmp( "NAMESPACE", arg )) { } else if (!strcmp( "NAMESPACE", arg )) {
resp = parse_list( ctx, cmd, parse_namespace_rsp ); resp = parse_list( ctx, cmd, parse_namespace_rsp, "NAMESPACE" );
goto listret; goto listret;
} else if ((arg1 = next_arg( &cmd ))) { } else if ((arg1 = next_arg( &cmd ))) {
if (!strcmp( "EXISTS", arg1 )) { if (!strcmp( "EXISTS", arg1 )) {
@ -1645,7 +1659,7 @@ imap_socket_read( void *aux )
if (!(seq = strtoul( arg, &arg1, 10 )) || *arg1) if (!(seq = strtoul( arg, &arg1, 10 )) || *arg1)
goto badseq; goto badseq;
ctx->fetch_seq = seq; ctx->fetch_seq = seq;
resp = parse_list( ctx, cmd, parse_fetch_rsp ); resp = parse_list( ctx, cmd, parse_fetch_rsp, "FETCH" );
goto listret; goto listret;
} }
} else { } else {