Line 0
Link Here
|
|
|
1 |
SECURITY FIX: truncate UIDL replies, lest malicious or compromised |
2 |
POP3 servers overflow fetchmail's stack. Debian bug #212762. |
3 |
This is a remote root exploit. |
4 |
|
5 |
--- ./pop3.c~ 2003-10-15 21:22:31.000000000 +0200 |
6 |
+++ ./pop3.c 2005-07-20 18:33:26.000000000 +0200 |
7 |
@@ -16,7 +16,8 @@ |
8 |
#if defined(STDC_HEADERS) |
9 |
#include <stdlib.h> |
10 |
#endif |
11 |
- |
12 |
+#include <errno.h> |
13 |
+ |
14 |
#include "fetchmail.h" |
15 |
#include "socket.h" |
16 |
#include "i18n.h" |
17 |
@@ -590,7 +591,8 @@ |
18 |
return(PS_SUCCESS); |
19 |
} |
20 |
|
21 |
-static int pop3_gettopid( int sock, int num , char *id) |
22 |
+#define POSIX_space "\t\n\v\f\r " |
23 |
+static int pop3_gettopid(int sock, int num , char *id, size_t idsize) |
24 |
{ |
25 |
int ok; |
26 |
int got_it; |
27 |
@@ -603,25 +605,51 @@ |
28 |
{ |
29 |
if (DOTLINE(buf)) |
30 |
break; |
31 |
- if ( ! got_it && ! strncasecmp("Message-Id:", buf, 11 )) { |
32 |
- got_it = 1; |
33 |
- /* prevent stack overflows */ |
34 |
- buf[IDLEN+12] = 0; |
35 |
- sscanf( buf+12, "%s", id); |
36 |
+ if (!got_it && 0 == strncasecmp("Message-Id:", buf, 11)) { |
37 |
+ char *p = buf + 11; |
38 |
+ p += strspn(p, POSIX_space); |
39 |
+ p = strtok(p, POSIX_space); |
40 |
+ strlcpy(id, p, idsize); |
41 |
} |
42 |
} |
43 |
return 0; |
44 |
} |
45 |
|
46 |
-static int pop3_getuidl( int sock, int num , char *id) |
47 |
+/** Parse destructively the UID response (leading +OK must have been |
48 |
+ * stripped off) in buf, store the number in gotnum, and store the ID |
49 |
+ * into the caller-provided buffer "id" of size "idsize". |
50 |
+ * Returns PS_SUCCESS or PS_PROTOCOL for failure. */ |
51 |
+static int parseuid(char *buf, unsigned long *gotnum, char *id, size_t idsize) |
52 |
+{ |
53 |
+ char *i, *j; |
54 |
+ |
55 |
+ i = strtok(buf, POSIX_space); |
56 |
+ errno = 0; |
57 |
+ *gotnum = strtoul(i, &j, 10); |
58 |
+ if (*j != '\0' || j == i || errno) { |
59 |
+ report(stderr, GT_("Cannot handle UIDL response from upstream server.\n")); |
60 |
+ return PS_PROTOCOL; |
61 |
+ } |
62 |
+ i = strtok(NULL, POSIX_space); |
63 |
+ strlcpy(id, i, idsize); |
64 |
+ return PS_SUCCESS; |
65 |
+} |
66 |
+ |
67 |
+static int pop3_getuidl(int sock, int num , char *id, size_t idsize) |
68 |
{ |
69 |
int ok; |
70 |
char buf [POPBUFSIZE+1]; |
71 |
+ unsigned long gotnum; |
72 |
+ |
73 |
gen_send(sock, "UIDL %d", num); |
74 |
if ((ok = pop3_ok(sock, buf)) != 0) |
75 |
return(ok); |
76 |
- if (sscanf(buf, "%d %s", &num, id) != 2) |
77 |
- return(PS_PROTOCOL); |
78 |
+ if ((ok = parseuid(buf, &gotnum, id, idsize))) |
79 |
+ return ok; |
80 |
+ if (gotnum != num) { |
81 |
+ report(stderr, GT_("Server responded with UID for wrong message.\n")); |
82 |
+ return PS_PROTOCOL; |
83 |
+ } |
84 |
return(PS_SUCCESS); |
85 |
} |
86 |
|
87 |
@@ -638,7 +666,7 @@ |
88 |
struct idlist *new; |
89 |
|
90 |
try_nr = (first_nr + last_nr) / 2; |
91 |
- if( (ok = pop3_getuidl( sock, try_nr, id )) != 0 ) |
92 |
+ if ((ok = pop3_getuidl(sock, try_nr, id, sizeof(id))) != 0) |
93 |
return ok; |
94 |
if ((new = str_in_list(&ctl->oldsaved, id, FALSE))) |
95 |
{ |
96 |
@@ -700,10 +728,10 @@ |
97 |
int first_nr, list_len, try_id, try_nr, add_id; |
98 |
int num; |
99 |
char id [IDLEN+1]; |
100 |
- |
101 |
- if( (ok = pop3_gettopid( sock, 1, id )) != 0 ) |
102 |
+ |
103 |
+ if ((ok = pop3_gettopid(sock, 1, id, sizeof(id))) != 0) |
104 |
return ok; |
105 |
- |
106 |
+ |
107 |
if( ( first_nr = str_nr_in_list(&ctl->oldsaved, id) ) == -1 ) { |
108 |
/* the first message is unknown -> all messages are new */ |
109 |
*newp = *countp; |
110 |
@@ -715,7 +743,7 @@ |
111 |
try_id = list_len - first_nr; /* -1 + 1 */ |
112 |
if( try_id > 1 ) { |
113 |
if( try_id <= *countp ) { |
114 |
- if( (ok = pop3_gettopid( sock, try_id, id )) != 0 ) |
115 |
+ if ((ok = pop3_gettopid(sock, try_id, id, sizeof(id))) != 0) |
116 |
return ok; |
117 |
|
118 |
try_nr = str_nr_last_in_list(&ctl->oldsaved, id); |
119 |
@@ -739,7 +767,7 @@ |
120 |
} else |
121 |
try_id += add_id; |
122 |
|
123 |
- if( (ok = pop3_gettopid( sock, try_id, id )) != 0 ) |
124 |
+ if ((ok = pop3_gettopid(sock, try_id, id, sizeof(id))) != 0) |
125 |
return ok; |
126 |
try_nr = str_nr_in_list(&ctl->oldsaved, id); |
127 |
} |
128 |
@@ -801,7 +829,7 @@ |
129 |
|
130 |
/* |
131 |
* Newer, RFC-1725-conformant POP servers may not have the LAST command. |
132 |
- * We work as hard as possible to hide this ugliness, but it makes |
133 |
+ * We work as hard as possible to hide this ugliness, but it makes |
134 |
* counting new messages intrinsically quadratic in the worst case. |
135 |
*/ |
136 |
last = 0; |
137 |
@@ -839,15 +867,15 @@ |
138 |
} |
139 |
*newp = (*countp - last); |
140 |
} |
141 |
- else |
142 |
- { |
143 |
+ else |
144 |
+ { |
145 |
if (dofastuidl) |
146 |
return(pop3_fastuidl( sock, ctl, *countp, newp)); |
147 |
/* grab the mailbox's UID list */ |
148 |
if ((ok = gen_transact(sock, "UIDL")) != 0) |
149 |
{ |
150 |
/* don't worry, yet! do it the slow way */ |
151 |
- if((ok = pop3_slowuidl( sock, ctl, countp, newp))!=0) |
152 |
+ if ((ok = pop3_slowuidl(sock, ctl, countp, newp))) |
153 |
{ |
154 |
report(stderr, GT_("protocol error while fetching UIDLs\n")); |
155 |
return(PS_ERROR); |
156 |
@@ -855,27 +883,32 @@ |
157 |
} |
158 |
else |
159 |
{ |
160 |
- int num; |
161 |
+ unsigned long unum; |
162 |
|
163 |
*newp = 0; |
164 |
- while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) |
165 |
+ while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) |
166 |
{ |
167 |
- if (DOTLINE(buf)) |
168 |
- break; |
169 |
- else if (sscanf(buf, "%d %s", &num, id) == 2) |
170 |
+ if (DOTLINE(buf)) |
171 |
+ break; |
172 |
+ |
173 |
+ if (parseuid(buf, &unum, id, sizeof(id)) == PS_SUCCESS) |
174 |
{ |
175 |
- struct idlist *old, *new; |
176 |
+ struct idlist *old, *new; |
177 |
|
178 |
new = save_str(&ctl->newsaved, id, UID_UNSEEN); |
179 |
- new->val.status.num = num; |
180 |
+ new->val.status.num = unum; |
181 |
|
182 |
if ((old = str_in_list(&ctl->oldsaved, id, FALSE))) |
183 |
{ |
184 |
flag mark = old->val.status.mark; |
185 |
if (mark == UID_DELETED || mark == UID_EXPUNGED) |
186 |
{ |
187 |
+ /* XXX FIXME: switch 3 occurrences from |
188 |
+ * (int)unum or (unsigned int)unum to |
189 |
+ * remove the cast and use %lu - not now |
190 |
+ * though, time for new release */ |
191 |
if (outlevel >= O_VERBOSE) |
192 |
- report(stderr, GT_("id=%s (num=%d) was deleted, but is still present!\n"), id, num); |
193 |
+ report(stderr, GT_("id=%s (num=%d) was deleted, but is still present!\n"), id, (int)unum); |
194 |
/* just mark it as seen now! */ |
195 |
old->val.status.mark = mark = UID_SEEN; |
196 |
} |
197 |
@@ -884,25 +917,25 @@ |
198 |
{ |
199 |
(*newp)++; |
200 |
if (outlevel >= O_DEBUG) |
201 |
- report(stdout, GT_("%u is unseen\n"), num); |
202 |
+ report(stdout, GT_("%u is unseen\n"), (unsigned int)unum); |
203 |
} |
204 |
} |
205 |
else |
206 |
{ |
207 |
(*newp)++; |
208 |
if (outlevel >= O_DEBUG) |
209 |
- report(stdout, GT_("%u is unseen\n"), num); |
210 |
+ report(stdout, GT_("%u is unseen\n"), (unsigned int)unum); |
211 |
/* add it to oldsaved also! In case, we do not |
212 |
* swap the lists (say, due to socket error), |
213 |
* the same mail will not be downloaded again. |
214 |
*/ |
215 |
old = save_str(&ctl->oldsaved, id, UID_UNSEEN); |
216 |
- old->val.status.num = num; |
217 |
+ old->val.status.num = unum; |
218 |
} |
219 |
} |
220 |
- } |
221 |
- } |
222 |
- } |
223 |
+ } |
224 |
+ } |
225 |
+ } |
226 |
} |
227 |
|
228 |
return(PS_SUCCESS); |
229 |
@@ -986,7 +1019,7 @@ |
230 |
} |
231 |
|
232 |
/* get the uidl first! */ |
233 |
- if (pop3_getuidl(sock, num, id) != PS_SUCCESS) |
234 |
+ if (pop3_getuidl(sock, num, id, sizeof(id)) != PS_SUCCESS) |
235 |
return(TRUE); |
236 |
|
237 |
if ((new = str_in_list(&ctl->oldsaved, id, FALSE))) { |