]> Pileus Git - ~andy/fetchmail/blob - smtp.c
Merge branch 'legacy_63'
[~andy/fetchmail] / smtp.c
1 /*
2  * smtp.c -- code for speaking SMTP to a listener port
3  *
4  * Concept due to Harry Hochheiser.  Implementation by ESR.  Cleanup and
5  * strict RFC821 compliance by Cameron MacPherson.
6  *
7  * Copyright 1997 Eric S. Raymond, 2009 Matthias Andree
8  * For license terms, see the file COPYING in this directory.
9  */
10
11 #include "config.h"
12 #include "fetchmail.h"
13
14 #include <ctype.h>
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <signal.h>
19 #include "socket.h"
20 #include "smtp.h"
21 #include "gettext.h"
22
23 struct opt
24 {
25     const char *name;
26     int value;
27 };
28
29 static struct opt extensions[] =
30 {
31     {"8BITMIME",        ESMTP_8BITMIME},
32     {"SIZE",            ESMTP_SIZE},
33     {"ETRN",            ESMTP_ETRN},
34     {"AUTH ",           ESMTP_AUTH},
35 #ifdef ODMR_ENABLE
36     {"ATRN",            ESMTP_ATRN},
37 #endif /* ODMR_ENABLE */
38     {(char *)NULL, 0},
39 };
40
41 char smtp_response[MSGBUFSIZE];
42
43 /* XXX: this must not be used for LMTP! */
44 int SMTP_helo(int sock, char smtp_mode, const char *host)
45 /* send a "HELO" message to the SMTP listener */
46 {
47   int ok;
48
49   SockPrintf(sock,"HELO %s\r\n", host);
50   if (outlevel >= O_MONITOR)
51       report(stdout, "%cMTP> HELO %s\n", smtp_mode, host);
52   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_HELO);
53   return ok;
54 }
55
56 static void SMTP_auth_error(int sock, const char *msg)
57 {
58     SockPrintf(sock, "*\r\n");
59     SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
60     if (outlevel >= O_MONITOR) report(stdout, "%s", msg);
61 }
62
63 static void SMTP_auth(int sock, char smtp_mode, char *username, char *password, char *buf)
64 /* ESMTP Authentication support for fetchmail by Wojciech Polak */
65 {       
66         int c;
67         char *p = 0;
68         char b64buf[512];
69         char tmp[512];
70
71         if (!username || !password) return;
72
73         memset(b64buf, 0, sizeof(b64buf));
74         memset(tmp, 0, sizeof(tmp));
75
76         if (strstr(buf, "CRAM-MD5")) {
77                 unsigned char digest[16];
78                 memset(digest, 0, sizeof(digest));
79
80                 if (outlevel >= O_MONITOR)
81                         report(stdout, GT_("ESMTP CRAM-MD5 Authentication...\n"));
82                 SockPrintf(sock, "AUTH CRAM-MD5\r\n");
83                 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
84                 strlcpy(tmp, smtp_response, sizeof(tmp));
85
86                 if (strncmp(tmp, "334", 3)) { /* Server rejects AUTH */
87                         SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
88                         return;
89                 }
90
91                 p = strchr(tmp, ' ');
92                 p++;
93                 /* (hmh) from64tobits will not NULL-terminate strings! */
94                 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
95                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
96                         return;
97                 }
98                 if (outlevel >= O_DEBUG)
99                         report(stdout, GT_("Challenge decoded: %s\n"), b64buf);
100                 hmac_md5((unsigned char *)password, strlen(password),
101                          (unsigned char *)b64buf, strlen(b64buf), digest, sizeof(digest));
102                 snprintf(tmp, sizeof(tmp),
103                 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
104                 username,  digest[0], digest[1], digest[2], digest[3],
105                 digest[4], digest[5], digest[6], digest[7], digest[8],
106                 digest[9], digest[10], digest[11], digest[12], digest[13],
107                 digest[14], digest[15]);
108
109                 to64frombits(b64buf, tmp, strlen(tmp));
110                 SockPrintf(sock, "%s\r\n", b64buf);
111                 SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
112         }
113         else if (strstr(buf, "PLAIN")) {
114                 int len;
115                 if (outlevel >= O_MONITOR)
116                         report(stdout, GT_("ESMTP PLAIN Authentication...\n"));
117                 snprintf(tmp, sizeof(tmp), "^%s^%s", username, password);
118
119                 len = strlen(tmp);
120                 for (c = len - 1; c >= 0; c--)
121                 {
122                         if (tmp[c] == '^')
123                                 tmp[c] = '\0';
124                 }
125                 to64frombits(b64buf, tmp, len);
126                 SockPrintf(sock, "AUTH PLAIN %s\r\n", b64buf);
127                 SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
128         }
129         else if (strstr(buf, "LOGIN")) {
130                 if (outlevel >= O_MONITOR)
131                         report(stdout, GT_("ESMTP LOGIN Authentication...\n"));
132                 SockPrintf(sock, "AUTH LOGIN\r\n");
133                 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
134                 strlcpy(tmp, smtp_response, sizeof(tmp));
135
136                 if (strncmp(tmp, "334", 3)) { /* Server rejects AUTH */
137                         SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
138                         return;
139                 }
140
141                 p = strchr(tmp, ' ');
142                 p++;
143                 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
144                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
145                         return;
146                 }
147                 to64frombits(b64buf, username, strlen(username));
148                 SockPrintf(sock, "%s\r\n", b64buf);
149                 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
150                 strlcpy(tmp, smtp_response, sizeof(tmp));
151                 p = strchr(tmp, ' ');
152                 if (!p) {
153                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
154                         return;
155                 }
156                 p++;
157                 memset(b64buf, 0, sizeof(b64buf));
158                 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
159                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
160                         return;
161                 }
162                 to64frombits(b64buf, password, strlen(password));
163                 SockPrintf(sock, "%s\r\n", b64buf);
164                 SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
165         }
166         return;
167 }
168
169 int SMTP_ehlo(int sock, char smtp_mode, const char *host, char *name, char *password, int *opt)
170 /* send a "EHLO" message to the SMTP listener, return extension status bits */
171 {
172   struct opt *hp;
173   char auth_response[511];
174   SIGHANDLERTYPE alrmsave;
175   const int tmout = (mytimeout >= TIMEOUT_HELO ? mytimeout : TIMEOUT_HELO);
176
177   SockPrintf(sock,"%cHLO %s\r\n", (smtp_mode == 'S') ? 'E' : smtp_mode, host);
178   if (outlevel >= O_MONITOR)
179       report(stdout, "%cMTP> %cHLO %s\n", 
180             smtp_mode, (smtp_mode == 'S') ? 'E' : smtp_mode, host);
181
182   alrmsave = set_signal_handler(SIGALRM, null_signal_handler);
183   set_timeout(tmout);
184
185   *opt = 0;
186   while ((SockRead(sock, smtp_response, sizeof(smtp_response)-1)) != -1)
187   {
188       size_t n;
189
190       set_timeout(0);
191       (void)set_signal_handler(SIGALRM, alrmsave);
192
193       n = strlen(smtp_response);
194       if (n > 0 && smtp_response[n-1] == '\n')
195           smtp_response[--n] = '\0';
196       if (n > 0 && smtp_response[n-1] == '\r')
197           smtp_response[--n] = '\0';
198       if (n < 4)
199           return SM_ERROR;
200       smtp_response[n] = '\0';
201       if (outlevel >= O_MONITOR)
202           report(stdout, "%cMTP< %s\n", smtp_mode, smtp_response);
203       for (hp = extensions; hp->name; hp++)
204           if (!strncasecmp(hp->name, smtp_response+4, strlen(hp->name))) {
205               *opt |= hp->value;
206               if (strncmp(hp->name, "AUTH ", 5) == 0)
207                 strncpy(auth_response, smtp_response, sizeof(auth_response));
208                 auth_response[sizeof(auth_response)-1] = '\0';
209           }
210       if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') && smtp_response[3] == ' ') {
211           if (*opt & ESMTP_AUTH)
212                 SMTP_auth(sock, smtp_mode, name, password, auth_response);
213           return SM_OK;
214       }
215       else if (smtp_response[3] != '-')
216           return SM_ERROR;
217
218       alrmsave = set_signal_handler(SIGALRM, null_signal_handler);
219       set_timeout(tmout);
220   }
221   return SM_UNRECOVERABLE;
222 }
223
224 int SMTP_from(int sock, char smtp_mode, const char *from, const char *opts)
225 /* send a "MAIL FROM:" message to the SMTP listener */
226 {
227     int ok;
228     char buf[MSGBUFSIZE];
229
230     if (from[0]=='<')
231         snprintf(buf, sizeof(buf), "MAIL FROM:%s", from);
232     else
233         snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", from);
234     if (opts)
235         snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s", opts);
236     SockPrintf(sock,"%s\r\n", buf);
237     if (outlevel >= O_MONITOR)
238         report(stdout, "%cMTP> %s\n", smtp_mode, buf);
239     ok = SMTP_ok(sock, smtp_mode, TIMEOUT_MAIL);
240     return ok;
241 }
242
243 int SMTP_rcpt(int sock, char smtp_mode, const char *to)
244 /* send a "RCPT TO:" message to the SMTP listener */
245 {
246   int ok;
247
248   SockPrintf(sock,"RCPT TO:<%s>\r\n", to);
249   if (outlevel >= O_MONITOR)
250       report(stdout, "%cMTP> RCPT TO:<%s>\n", smtp_mode, to);
251   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_RCPT);
252   return ok;
253 }
254
255 int SMTP_data(int sock, char smtp_mode)
256 /* send a "DATA" message to the SMTP listener */
257 {
258   int ok;
259
260   SockPrintf(sock,"DATA\r\n");
261   if (outlevel >= O_MONITOR)
262       report(stdout, "%cMTP> DATA\n", smtp_mode);
263   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DATA);
264   return ok;
265 }
266
267 int SMTP_rset(int sock, char smtp_mode)
268 /* send a "RSET" message to the SMTP listener */
269 {
270   int ok;
271
272   SockPrintf(sock,"RSET\r\n");
273   if (outlevel >= O_MONITOR)
274       report(stdout, "%cMTP> RSET\n", smtp_mode);
275   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
276   return ok;
277 }
278
279 int SMTP_quit(int sock, char smtp_mode)
280 /* send a "QUIT" message to the SMTP listener */
281 {
282   int ok;
283
284   SockPrintf(sock,"QUIT\r\n");
285   if (outlevel >= O_MONITOR)
286       report(stdout, "%cMTP> QUIT\n", smtp_mode);
287   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
288   return ok;
289 }
290
291 int SMTP_eom(int sock, char smtp_mode)
292 /* send a message data terminator to the SMTP listener */
293 {
294   SockPrintf(sock,".\r\n");
295   if (outlevel >= O_MONITOR)
296       report(stdout, "%cMTP>. (EOM)\n", smtp_mode);
297
298   /* 
299    * When doing LMTP, must process many of these at the outer level. 
300    */
301   if (smtp_mode == 'S')
302       return SMTP_ok(sock, smtp_mode, TIMEOUT_EOM);
303   else
304       return SM_OK;
305 }
306
307 time_t last_smtp_ok = 0;
308
309 int SMTP_ok(int sock, char smtp_mode, int mintimeout)
310 /**< returns status of SMTP connection and saves the message in
311  * smtp_response, without trailing [CR]LF, but with normalized CRLF
312  * between multiple lines of multi-line replies */
313 {
314     SIGHANDLERTYPE alrmsave;
315     char reply[MSGBUFSIZE], *i;
316
317     /* set an alarm for smtp ok */
318     alrmsave = set_signal_handler(SIGALRM, null_signal_handler);
319     set_timeout(mytimeout >= mintimeout ? mytimeout : mintimeout);
320
321     smtp_response[0] = '\0';
322
323     while ((SockRead(sock, reply, sizeof(reply)-1)) != -1)
324     {
325         size_t n;
326
327         /* restore alarm */
328         set_timeout(0);
329         set_signal_handler(SIGALRM, alrmsave);
330
331         n = strlen(reply);
332         if (n > 0 && reply[n-1] == '\n')
333             reply[--n] = '\0';
334         if (n > 0 && reply[n-1] == '\r')
335             reply[--n] = '\0';
336
337         /* stomp over control characters */
338         for (i = reply; *i; i++)
339             if (iscntrl((unsigned char)*i))
340                 *i = '?';
341
342         if (outlevel >= O_MONITOR)
343             report(stdout, "%cMTP< %s\n", smtp_mode, reply);
344         /* note that \0 is part of the strchr search string and the
345          * blank after the reply code is optional (RFC 5321 4.2.1) */
346         if (n < 3 || !strchr(" -", reply[3]))
347         {
348             if (outlevel >= O_MONITOR)
349                 report(stderr, GT_("smtp listener protocol error\n"));
350             return SM_UNRECOVERABLE;
351         }
352
353         last_smtp_ok = time((time_t *) NULL);
354
355         strlcat(smtp_response, reply,  sizeof(smtp_response));
356
357         if (strchr("123", reply[0])
358                 && isdigit((unsigned char)reply[1])
359                 && isdigit((unsigned char)reply[2])
360                 && strchr(" ", reply[3])) /* matches space and \0 */ {
361             return SM_OK;
362         } else if (reply[3] != '-')
363             return SM_ERROR;
364
365         strlcat(smtp_response, "\r\n", sizeof(smtp_response));
366
367         /* set an alarm for smtp ok */
368         set_signal_handler(SIGALRM, null_signal_handler);
369         set_timeout(mytimeout);
370     }
371
372     /* restore alarm */
373     set_timeout(0);
374     set_signal_handler(SIGALRM, alrmsave);
375
376     if (outlevel >= O_MONITOR)
377         report(stderr, GT_("smtp listener protocol error\n"));
378     return SM_UNRECOVERABLE;
379 }
380
381 /* smtp.c ends here */