summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2021-08-15 20:53:04 -0600
committerEduardo Chappa <chappa@washington.edu>2021-08-15 20:53:04 -0600
commite58edb33f71687cb0b12c10a6cea2db2f8a35011 (patch)
treea68016ebb2985f0c0142105a63a2568b6dd4e231
parent94fb9813ffc82c72aecdbc37d696bdd9c24a91bc (diff)
downloadalpine-e58edb33f71687cb0b12c10a6cea2db2f8a35011.tar.xz
* The c-client library parses information from an IMAP server during
non-authenticated state which could lead to denial of service. Reported by Damian Poddebniak from Münster University of Applied Sciences.
-rw-r--r--imap/src/c-client/imap4r1.c61
-rw-r--r--pith/imap.c4
-rw-r--r--pith/pine.hlp7
3 files changed, 49 insertions, 23 deletions
diff --git a/imap/src/c-client/imap4r1.c b/imap/src/c-client/imap4r1.c
index 6970396a..b4479e72 100644
--- a/imap/src/c-client/imap4r1.c
+++ b/imap/src/c-client/imap4r1.c
@@ -66,7 +66,6 @@ typedef struct imap_parsed_reply {
#define IMAPTMPLEN 16*MAILTMPLEN
-
/* IMAP4 I/O stream local data */
typedef struct imap_local {
@@ -75,6 +74,7 @@ typedef struct imap_local {
MAILSTATUS *stat; /* status to fill in */
IMAPCAP cap; /* server capabilities */
char *appendmailbox; /* mailbox being appended to */
+ unsigned int authed: 1; /* state: authed or not */
unsigned int uidsearch : 1; /* UID searching */
unsigned int byeseen : 1; /* saw a BYE response */
/* got implicit capabilities */
@@ -909,7 +909,13 @@ MAILSTREAM *imap_open (MAILSTREAM *stream)
return NIL; /* lost during greeting */
}
+ if(stream->unhealthy){
+ mm_log("Aborting due to bad protocol before authentication", ERROR);
+ return NIL;
+ }
+
preauthed = !strcmp (reply->key,"PREAUTH");
+ if(preauthed) LOCAL->authed = T;
/* STARTTLS is not allowed in PREAUTH state */
if (LOCAL->netstream && preauthed){
sslstart_t stls = (sslstart_t) mail_parameters (NIL,GET_SSLSTART,NIL);
@@ -928,6 +934,10 @@ MAILSTREAM *imap_open (MAILSTREAM *stream)
if (LOCAL->netstream && /* does server support STARTTLS? */
stls && LOCAL->cap.starttls && !mb.sslflag && !mb.notlsflag &&
imap_OK (stream,imap_send (stream,"STARTTLS",NIL))) {
+ if(stream->unhealthy){
+ mm_log("Aborting due to bad protocol before authentication", ERROR);
+ return NIL;
+ }
mb.tlsflag = T; /* TLS OK, get into TLS at this end */
LOCAL->netstream->dtb = ssld;
if (!(LOCAL->netstream->stream =
@@ -939,6 +949,10 @@ MAILSTREAM *imap_open (MAILSTREAM *stream)
}
/* get capabilities now that TLS in effect */
if (LOCAL->netstream) imap_capability (stream);
+ if(stream->unhealthy){
+ mm_log("Aborting due to bad protocol before authentication", ERROR);
+ return NIL;
+ }
}
else if (mb.tlsflag) { /* user specified /starttls but can't do it */
mm_log ("Unable to negotiate TLS with this server",ERROR);
@@ -958,8 +972,8 @@ MAILSTREAM *imap_open (MAILSTREAM *stream)
(LOCAL->cap.auth ? imap_auth (stream,&mb,tmp,usr) :
imap_login (stream,&mb,tmp,usr)))) {
/* failed, is there a referral? */
- if (mb.tlsflag) LOCAL->tlsflag = T;
- if (ir && LOCAL->referral &&
+ if (mb.tlsflag) LOCAL->tlsflag = T;
+ if (ir && LOCAL->referral &&
(s = (*ir) (stream,LOCAL->referral,REFAUTHFAILED))) {
imap_close (stream,NIL);
fs_give ((void **) &stream->mailbox);
@@ -977,6 +991,7 @@ MAILSTREAM *imap_open (MAILSTREAM *stream)
/* recurse to log in on real site */
return imap_open (stream);
}
+ else LOCAL->authed = T;
}
}
/* get server capabilities again */
@@ -4129,11 +4144,11 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
t = strtok_r (NIL,"\n",&r); /* and locate the text after it */
/* now take the action */
/* change in size of mailbox */
- if (!strcmp (s,"EXISTS") && (msgno >= stream->nmsgs))
+ if (LOCAL->authed && !strcmp (s,"EXISTS") && (msgno >= stream->nmsgs))
mail_exists (stream,msgno);
- else if (!strcmp (s,"RECENT") && (msgno <= stream->nmsgs))
+ else if (LOCAL->authed && !strcmp (s,"RECENT") && (msgno <= stream->nmsgs))
mail_recent (stream,msgno);
- else if (!strcmp (s,"EXPUNGE") && msgno && (msgno <= stream->nmsgs)) {
+ else if (LOCAL->authed && !strcmp (s,"EXPUNGE") && msgno && (msgno <= stream->nmsgs)) {
mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
MESSAGECACHE *elt = (MESSAGECACHE *) (*mc) (stream,msgno,CH_ELT);
if (elt) imap_gc_body (elt->private.msg.body);
@@ -4141,7 +4156,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
mail_expunged (stream,msgno);
}
- else if (t && (!strcmp (s,"FETCH") || !strcmp (s,"STORE")) &&
+ else if (LOCAL->authed && t && (!strcmp (s,"FETCH") || !strcmp (s,"STORE")) &&
msgno && (msgno <= stream->nmsgs)) {
char *prop;
GETS_DATA md;
@@ -4310,14 +4325,14 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
if (ie && env) (*ie) (stream,msgno,env);
}
/* obsolete response to COPY */
- else if (strcmp (s,"COPY")) {
+ else if (strcmp (s,"COPY") || !LOCAL->authed) {
sprintf (LOCAL->tmp,"Unknown message data: %lu %.80s",msgno,(char *) s);
mm_notify (stream,LOCAL->tmp,WARN);
stream->unhealthy = T;
}
}
- else if (!strcmp (reply->key,"FLAGS") && reply->text &&
+ else if (LOCAL->authed && !strcmp (reply->key,"FLAGS") && reply->text &&
(*reply->text == '(') &&
(s = strtok_r (reply->text+1," )",&r)))
do if (*s != '\\') {
@@ -4331,7 +4346,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
else if (!stream->user_flags[i]) stream->user_flags[i++] = cpystr (s);
}
while ((s = strtok_r (NIL," )",&r)) != NULL);
- else if (!strcmp (reply->key,"SEARCH")) {
+ else if (LOCAL->authed && !strcmp (reply->key,"SEARCH")) {
/* only do something if have text */
if (reply->text && (t = strtok_r (reply->text," ",&r))) do
if ((i = strtoul (t,NIL,10)) != 0L) {
@@ -4345,7 +4360,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
}
} while ((t = strtok_r (NIL," ",&r)) != NULL);
}
- else if (!strcmp (reply->key,"SORT")) {
+ else if (LOCAL->authed && !strcmp (reply->key,"SORT")) {
sortresults_t sr = (sortresults_t)
mail_parameters (NIL,GET_SORTRESULTS,NIL);
LOCAL->sortsize = 0; /* initialize sort data */
@@ -4363,7 +4378,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
/* also return via callback if requested */
if (sr) (*sr) (stream,LOCAL->sortdata,LOCAL->sortsize);
}
- else if (!strcmp (reply->key,"THREAD")) {
+ else if (LOCAL->authed && !strcmp (reply->key,"THREAD")) {
threadresults_t tr = (threadresults_t)
mail_parameters (NIL,GET_THREADRESULTS,NIL);
if (LOCAL->threaddata) mail_free_threadnode (&LOCAL->threaddata);
@@ -4378,7 +4393,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
}
}
- else if (!strcmp (reply->key,"STATUS") && reply->text) {
+ else if (LOCAL->authed && !strcmp (reply->key,"STATUS") && reply->text) {
MAILSTATUS status;
unsigned char *txt = reply->text;
if ((t = imap_parse_astring (stream,&txt,reply,&j)) && txt &&
@@ -4425,7 +4440,8 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
if (t) fs_give ((void **) &t);
}
- else if ((!strcmp (reply->key,"LIST") || !strcmp (reply->key,"LSUB")) &&
+ else if (LOCAL->authed
+ && (!strcmp (reply->key,"LIST") || !strcmp (reply->key,"LSUB")) &&
reply->text && (*reply->text == '(') &&
(s = strchr (reply->text,')')) && (s[1] == ' ')) {
char delimiter = '\0';
@@ -4469,7 +4485,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
fs_give ((void **) &t); /* flush mailbox name */
}
}
- else if (!strcmp (reply->key,"NAMESPACE")) {
+ else if (LOCAL->authed && !strcmp (reply->key,"NAMESPACE")) {
if (LOCAL->namespace) {
mail_free_namespace (&LOCAL->namespace[0]);
mail_free_namespace (&LOCAL->namespace[1]);
@@ -4492,7 +4508,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
}
}
- else if (!strcmp (reply->key,"ACL") && (s = reply->text) &&
+ else if (LOCAL->authed && !strcmp (reply->key,"ACL") && (s = reply->text) &&
(t = imap_parse_astring (stream,&s,reply,NIL))) {
getacl_t ar = (getacl_t) mail_parameters (NIL,GET_ACL,NIL);
if (s && (*s++ == ' ')) {
@@ -4517,7 +4533,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
fs_give ((void **) &t); /* free mailbox name */
}
- else if (!strcmp (reply->key,"LISTRIGHTS") && (s = reply->text) &&
+ else if (LOCAL->authed && !strcmp (reply->key,"LISTRIGHTS") && (s = reply->text) &&
(t = imap_parse_astring (stream,&s,reply,NIL))) {
listrights_t lr = (listrights_t) mail_parameters (NIL,GET_LISTRIGHTS,NIL);
char *id,*r;
@@ -4560,7 +4576,8 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
fs_give ((void **) &t); /* free mailbox name */
}
- else if (!strcmp (reply->key,"MYRIGHTS") && (s = reply->text) &&
+ else if (LOCAL->authed
+ && !strcmp (reply->key,"MYRIGHTS") && (s = reply->text) &&
(t = imap_parse_astring (stream,&s,reply,NIL))) {
myrights_t mr = (myrights_t) mail_parameters (NIL,GET_MYRIGHTS,NIL);
char *r;
@@ -4582,7 +4599,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
}
/* this response has a bizarre syntax! */
- else if (!strcmp (reply->key,"QUOTA") && (s = reply->text) &&
+ else if (LOCAL->authed && !strcmp (reply->key,"QUOTA") && (s = reply->text) &&
(t = imap_parse_astring (stream,&s,reply,NIL))) {
/* in case error */
sprintf (LOCAL->tmp,"Bad quota resource list for %.80s",(char *) t);
@@ -4623,10 +4640,10 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
}
fs_give ((void **) &t); /* free root name */
}
- else if (!strcmp (reply->key,"ID") && (s = reply->text)){
+ else if (LOCAL->authed && !strcmp (reply->key,"ID") && (s = reply->text)){
if(compare_cstring (s,"NIL")) LOCAL->id = imap_parse_idlist(s);
}
- else if (!strcmp (reply->key,"QUOTAROOT") && (s = reply->text) &&
+ else if (LOCAL->authed && !strcmp (reply->key,"QUOTAROOT") && (s = reply->text) &&
(t = imap_parse_astring (stream,&s,reply,NIL))) {
sprintf (LOCAL->tmp,"Bad quota root list for %.80s",(char *) t);
if (s && (*s++ == ' ')) {
@@ -4664,7 +4681,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
}
else if (!strcmp (reply->key,"CAPABILITY") && reply->text)
imap_parse_capabilities (stream,reply->text);
- else if (!strcmp (reply->key,"MAILBOX") && reply->text) {
+ else if (LOCAL->authed && !strcmp (reply->key,"MAILBOX") && reply->text) {
if (LOCAL->prefix &&
((strlen (LOCAL->prefix) + strlen (reply->text)) < IMAPTMPLEN))
sprintf (t = LOCAL->tmp,"%s%s",LOCAL->prefix,(char *) reply->text);
diff --git a/pith/imap.c b/pith/imap.c
index 869de9ad..fb7c3972 100644
--- a/pith/imap.c
+++ b/pith/imap.c
@@ -450,6 +450,8 @@ mm_list(MAILSTREAM *stream, int delimiter, char *mailbox, long int attributes)
(attributes & LATT_HASNOCHILDREN) ? ", has no children" : ""));
#endif
+ if(!mm_list_info || !mm_list_info->filter) return;
+
if(!mm_list_info->stream || stream == mm_list_info->stream)
(*mm_list_info->filter)(stream, mailbox, delimiter,
attributes, mm_list_info->data,
@@ -472,6 +474,8 @@ mm_lsub(MAILSTREAM *stream, int delimiter, char *mailbox, long int attributes)
(attributes & LATT_HASNOCHILDREN) ? ", has no children" : ""));
#endif
+ if(!mm_list_info || !mm_list_info->filter) return;
+
if(!mm_list_info->stream || stream == mm_list_info->stream)
(*mm_list_info->filter)(stream, mailbox, delimiter,
attributes, mm_list_info->data,
diff --git a/pith/pine.hlp b/pith/pine.hlp
index 30288ba9..09c4d2db 100644
--- a/pith/pine.hlp
+++ b/pith/pine.hlp
@@ -140,7 +140,7 @@ with help text for the config screen and the composer that didn't have any
reasonable place to be called from.
Dummy change to get revision in pine.hlp
============= h_revision =================
-Alpine Commit 572 2021-08-10 21:38:40
+Alpine Commit 573 2021-08-15 20:51:36
============= h_news =================
<HTML>
<HEAD>
@@ -240,6 +240,11 @@ New features include:
Bugs addressed include:
<UL>
+<LI> The c-client library parses information from an IMAP server during
+ non-authenticated state which could lead to denial of service.
+ Reported by Damian Poddebniak from M&uuml;nster University of Applied
+ Sciences.
+
<LI> Memory corruption when alpine searches for a string that is
an incomplete utf8 string in a local folder. This could happen by
chopping a string to make it fit a buffer without regard to its content.