]> Pileus Git - ~andy/fetchmail/blob - smtp.c
Fix MD5 compile on Solaris.
[~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 "i18n.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       int  n = strlen(smtp_response);
189
190       set_timeout(0);
191       (void)set_signal_handler(SIGALRM, alrmsave);
192
193       if (smtp_response[strlen(smtp_response)-1] == '\n')
194           smtp_response[strlen(smtp_response)-1] = '\0';
195       if (smtp_response[strlen(smtp_response)-1] == '\r')
196           smtp_response[strlen(smtp_response)-1] = '\0';
197       if (n < 4)
198           return SM_ERROR;
199       smtp_response[n] = '\0';
200       if (outlevel >= O_MONITOR)
201           report(stdout, "%cMTP< %s\n", smtp_mode, smtp_response);
202       for (hp = extensions; hp->name; hp++)
203           if (!strncasecmp(hp->name, smtp_response+4, strlen(hp->name))) {
204               *opt |= hp->value;
205               if (strncmp(hp->name, "AUTH ", 5) == 0)
206                 strncpy(auth_response, smtp_response, sizeof(auth_response));
207                 auth_response[sizeof(auth_response)-1] = '\0';
208           }
209       if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') && smtp_response[3] == ' ') {
210           if (*opt & ESMTP_AUTH)
211                 SMTP_auth(sock, smtp_mode, name, password, auth_response);
212           return SM_OK;
213       }
214       else if (smtp_response[3] != '-')
215           return SM_ERROR;
216
217       alrmsave = set_signal_handler(SIGALRM, null_signal_handler);
218       set_timeout(tmout);
219   }
220   return SM_UNRECOVERABLE;
221 }
222
223 int SMTP_from(int sock, char smtp_mode, const char *from, const char *opts)
224 /* send a "MAIL FROM:" message to the SMTP listener */
225 {
226     int ok;
227     char buf[MSGBUFSIZE];
228
229     if (from[0]=='<')
230         snprintf(buf, sizeof(buf), "MAIL FROM:%s", from);
231     else
232         snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", from);
233     if (opts)
234         snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s", opts);
235     SockPrintf(sock,"%s\r\n", buf);
236     if (outlevel >= O_MONITOR)
237         report(stdout, "%cMTP> %s\n", smtp_mode, buf);
238     ok = SMTP_ok(sock, smtp_mode, TIMEOUT_MAIL);
239     return ok;
240 }
241
242 int SMTP_rcpt(int sock, char smtp_mode, const char *to)
243 /* send a "RCPT TO:" message to the SMTP listener */
244 {
245   int ok;
246
247   SockPrintf(sock,"RCPT TO:<%s>\r\n", to);
248   if (outlevel >= O_MONITOR)
249       report(stdout, "%cMTP> RCPT TO:<%s>\n", smtp_mode, to);
250   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_RCPT);
251   return ok;
252 }
253
254 int SMTP_data(int sock, char smtp_mode)
255 /* send a "DATA" message to the SMTP listener */
256 {
257   int ok;
258
259   SockPrintf(sock,"DATA\r\n");
260   if (outlevel >= O_MONITOR)
261       report(stdout, "%cMTP> DATA\n", smtp_mode);
262   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DATA);
263   return ok;
264 }
265
266 int SMTP_rset(int sock, char smtp_mode)
267 /* send a "RSET" message to the SMTP listener */
268 {
269   int ok;
270
271   SockPrintf(sock,"RSET\r\n");
272   if (outlevel >= O_MONITOR)
273       report(stdout, "%cMTP> RSET\n", smtp_mode);
274   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
275   return ok;
276 }
277
278 int SMTP_quit(int sock, char smtp_mode)
279 /* send a "QUIT" message to the SMTP listener */
280 {
281   int ok;
282
283   SockPrintf(sock,"QUIT\r\n");
284   if (outlevel >= O_MONITOR)
285       report(stdout, "%cMTP> QUIT\n", smtp_mode);
286   ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
287   return ok;
288 }
289
290 int SMTP_eom(int sock, char smtp_mode)
291 /* send a message data terminator to the SMTP listener */
292 {
293   SockPrintf(sock,".\r\n");
294   if (outlevel >= O_MONITOR)
295       report(stdout, "%cMTP>. (EOM)\n", smtp_mode);
296
297   /* 
298    * When doing LMTP, must process many of these at the outer level. 
299    */
300   if (smtp_mode == 'S')
301       return SMTP_ok(sock, smtp_mode, TIMEOUT_EOM);
302   else
303       return SM_OK;
304 }
305
306 time_t last_smtp_ok = 0;
307
308 int SMTP_ok(int sock, char smtp_mode, int mintimeout)
309 /**< returns status of SMTP connection and saves the message in
310  * smtp_response, without trailing [CR]LF, but with normalized CRLF
311  * between multiple lines of multi-line replies */
312 {
313     SIGHANDLERTYPE alrmsave;
314     char reply[MSGBUFSIZE], *i;
315
316     /* set an alarm for smtp ok */
317     alrmsave = set_signal_handler(SIGALRM, null_signal_handler);
318     set_timeout(mytimeout >= mintimeout ? mytimeout : mintimeout);
319
320     smtp_response[0] = '\0';
321
322     while ((SockRead(sock, reply, sizeof(reply)-1)) != -1)
323     {
324         size_t n;
325
326         /* restore alarm */
327         set_timeout(0);
328         set_signal_handler(SIGALRM, alrmsave);
329
330         n = strlen(reply);
331         if (n > 0 && reply[n-1] == '\n')
332             n--;
333         if (n > 0 && reply[n-1] == '\r')
334             n--;
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 */