OSDN Git Service

change sample configuration
[pam-smtpauth/pam_smtpauth.git] / smtpauth.c
1 /*
2  * smtpauth.c
3  * $Id: smtpauth.c,v 1.14 2009/06/12 08:55:50 taizo Exp $
4  * Copyright (C) 2009 HDE, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GNU Emacs; see the file COPYING.  If not, write to
18  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #define _GNU_SOURCE
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <ctype.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <syslog.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/utsname.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <netdb.h>
37 #include <sys/ioctl.h>
38 #include <net/if.h>
39
40 #include "smtpauth.h"
41
42 #ifdef USE_SSL
43 #include <openssl/err.h>
44 #include <openssl/md5.h>
45 #else
46 #include "global.h"
47 #include "md5.h"
48 #endif
49
50 #define EHLO_CMD ("EHLO ")      /* ESMTP ehlo command */
51 #define AUTH_CMD ("AUTH ")      /* ESMTP auth command */
52 #define QUIT_CMD ("QUIT ")      /* ESMTP quit command */
53
54 #define RESP_LEN 1000
55 #define RESP_IERROR      "internal error"
56 #define RESP_UNAVAILABLE "remote authentication server is currently unavailable"
57 #define RESP_UNEXPECTED  "unexpected response from remote authentication server"
58 #define RESP_SYNCERROR   "error synchronizing with remote authentication server"
59 #define RESP_CREDERROR   "remote authentication server rejected your credentials"
60
61 #define AUTH_NG 0
62 #define AUTH_OK 1
63
64 #define AUTH_PLAIN      1 << 0
65 #define AUTH_LOGIN      1 << 1
66 #define AUTH_CRAM_MD5   1 << 2
67 #define AUTH_DIGEST_MD5 1 << 3
68
69 #define B64(c)  (isascii(c) ? base64val[(int)(c)] : -1)
70
71 extern int retry_writev(socket_t *socket, struct iovec *iov, int iovcnt);
72 extern int socket_read(socket_t * sock, char *buf, size_t len);
73 extern void socket_perror(const char *func, socket_t *sock, int ret);
74
75 void md5_hex_hmac(char *hexdigest, unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len);
76 void hmac_md5(unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len, unsigned char *digest);
77 int start_tls(smtp_t *smtp, config_t * cfg);
78
79
80 int smtp_quit(socket_t *socket, config_t * cfg);
81 int auth_cram_md5(socket_t *socket, config_t * cfg);
82 int auth_login(socket_t *socket, config_t * cfg);
83 int auth_plain(socket_t *socket, config_t * cfg);
84
85 config_t global;
86 static const char base64char[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
87 static const char base64val[128] = {
88     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
89     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
90     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
91     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
92     -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
93     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
94     -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
95     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
96 };
97
98
99 void
100 md5_hex_hmac(char *hexdigest, unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len) {
101
102     unsigned char digest[16];
103     int cnt;
104
105     hmac_md5(text, text_len, key, key_len, digest);
106     for(cnt=0; cnt<16; cnt++) {
107         sprintf(hexdigest + 2 * cnt, "%02x", digest[cnt]);
108     }
109 }
110
111
112 void
113 hmac_md5(unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len, unsigned char *digest) {
114
115     MD5_CTX context;
116     unsigned char k_ipad[64];
117     unsigned char k_opad[64];
118     int cnt;
119
120     memset(k_ipad, 0, sizeof(k_ipad));
121     memset(k_opad, 0, sizeof(k_opad));
122     if(key_len > 64) {
123         MD5_CTX tctx;
124
125 #ifdef USE_SSL
126         MD5_Init(&tctx);
127         MD5_Update(&tctx, key, key_len);
128         MD5_Final(k_ipad, &tctx);
129         MD5_Final(k_opad, &tctx);
130 #else
131         MD5Init(&tctx);
132         MD5Update(&tctx, key, key_len);
133         MD5Final(k_ipad, &tctx);
134         MD5Final(k_opad, &tctx);
135 #endif
136     } else {
137         memcpy(k_ipad, key, key_len);
138         memcpy(k_opad, key, key_len);
139     }
140
141     for(cnt=0; cnt<64; cnt++) {
142         k_ipad[cnt] ^= 0x36;
143         k_opad[cnt] ^= 0x5c;
144     }
145
146 #ifdef USE_SSL
147     MD5_Init(&context);
148     MD5_Update(&context, k_ipad, 64);
149     MD5_Update(&context, text, text_len);
150     MD5_Final(digest, &context);
151
152     MD5_Init(&context);
153     MD5_Update(&context, k_opad, 64);
154     MD5_Update(&context, digest, 16);
155     MD5_Final(digest, &context);
156 #else
157     MD5Init(&context);
158     MD5Update(&context, k_ipad, 64);
159     MD5Update(&context, text, text_len);
160     MD5Final(digest, &context);
161
162     MD5Init(&context);
163     MD5Update(&context, k_opad, 64);
164     MD5Update(&context, digest, 16);
165     MD5Final(digest, &context);
166 #endif
167 }
168
169
170 void
171 base64_encode(char *out, const char *in, int inlen) {
172
173     const char *inp = in;
174     char *outp = out;
175
176     while(inlen >= 3) {
177         *outp++ = base64char[(inp[0] >> 2) & 0x3f];
178         *outp++ = base64char[((inp[0] & 0x03) << 4) | ((inp[1] >> 4) & 0x0f)];
179         *outp++ = base64char[((inp[1] & 0x0f) << 2) | ((inp[2] >> 6) & 0x03)];
180         *outp++ = base64char[inp[2] & 0x3f];
181         inp += 3;
182         inlen -= 3;
183     }
184     if(inlen > 0) {
185         *outp++ = base64char[(inp[0] >> 2) & 0x3f];
186         if(inlen == 1) {
187             *outp++ = base64char[(inp[0] & 0x03) << 4];
188             *outp++ = '=';
189         }
190         else {
191             *outp++ = base64char[((inp[0] & 0x03) << 4) | ((inp[1] >> 4) & 0x0f)];
192             *outp++ = base64char[((inp[1] & 0x0f) << 2)];
193         }
194         *outp++ = '=';
195     }
196     *outp = '\0';
197 }
198
199
200 int
201 base64_decode(char *out, const char *in, int inlen) {
202
203     const char *inp = in;
204     char *outp = out;
205     char buf[4];
206
207     if( inlen < 0) {
208         inlen = 2100000000;
209     }
210     while(inlen >=4 && *inp != '\0') {
211         buf[0] = *inp++;
212         inlen--;
213         if( B64(buf[0]) == -1) break;
214
215         buf[1] = *inp++;
216         inlen--;
217         if(B64(buf[1]) == -1) break;
218
219         buf[2] = *inp++;
220         inlen--;
221         if(buf[2] != '=' && B64(buf[2]) == -1) break;
222
223         buf[3] = *inp++;
224         inlen--;
225         if(buf[3] != '=' && B64(buf[3]) ==  -1) break;
226
227         *outp++ = ((B64(buf[0]) << 2) & 0xfc) | ((B64(buf[1]) >> 4) & 0x03);
228         if(buf[2] != '=') {
229            *outp++ = ((B64(buf[1]) & 0x0f) << 4) | ((B64(buf[2]) >> 2) & 0x0f);
230            if(buf[3] != '=') {
231                 *outp++ = ((B64(buf[2]) & 0x03) << 6) | (B64(buf[3]) & 0x3f);
232            }
233         }
234     }
235     return outp - out;
236 }
237
238
239 smtp_t *
240 smtp_auth(config_t * cfg) {
241
242     int s;
243     struct sockaddr_in addr;
244     struct hostent *he;
245     smtp_t *smtp = NULL;
246     char msgbuf[256];
247
248     struct iovec iov[5];
249     char *c;
250     int rc;
251     char rbuf[RESP_LEN];
252     int auth = 0;
253     int avail_auth_type = 0;
254     char *tbuf;
255     struct utsname  h_name[1];
256     char *myhostname;
257
258     int                n;
259     struct sockaddr_in taddr;
260     int                sd;
261     struct ifconf      ifconf;
262     struct ifreq       *ifr, ifreq;
263     unsigned char      *ifptr;
264     int                iflen;
265 #ifdef USE_SSL
266     int use_ssl;
267 #endif
268
269     if(!cfg->password) {
270         if(!global.password) {
271             global.password = getpass("Password:");
272             if(!global.password) {
273                 return 0;
274             }
275             if(!*global.password) {
276                 global.password = NULL;
277                 goto bail;
278             }
279             global.password = strdup(global.password);
280         }
281         cfg->password = strdup(global.password);
282     }
283
284     assert(cfg->username != NULL);
285     assert(cfg->password != NULL);
286
287     smtp = calloc(1, sizeof(smtp_t));
288     smtp->sock = calloc(1, sizeof(socket_t));
289     smtp->buf = calloc(1, sizeof(buffer_t));
290     smtp->buf->sock = smtp->sock;
291     smtp->sock->fd = -1;
292     smtp->error = 0;
293
294     /* open connection to SMTP server */
295     memset(&addr, 0, sizeof(addr));
296     addr.sin_port = htons(cfg->port);
297     addr.sin_family = AF_INET;
298     he = gethostbyname(cfg->host);
299     if(!he) {
300         smtp->error = 1;
301         strcpy(msgbuf, "Error: resolving hostname ");
302         strcat(msgbuf, cfg->host);
303         smtp->error_message = malloc(strlen(msgbuf) + 1);
304         strcpy(smtp->error_message, msgbuf);
305         goto bail;
306     }
307
308     if((sd = socket(PF_INET, SOCK_DGRAM, 0)) != -1) {
309         bzero(&ifconf, sizeof(struct ifconf));
310         bzero(&ifreq, sizeof(struct ifreq));
311         iflen = 10 * sizeof(struct ifreq);
312         ifptr = malloc(iflen);
313         ifconf.ifc_len = iflen;
314         ifconf.ifc_ifcu.ifcu_req = (struct ifreq *)ifptr;
315         if(ioctl(sd, SIOCGIFCONF, &ifconf) != -1) {
316             for(iflen=sizeof(struct ifreq); iflen<=ifconf.ifc_len; iflen+=sizeof(struct ifreq)) {
317                 ifr = (struct ifreq *)ifptr;
318                 strcpy(ifreq.ifr_ifrn.ifrn_name, ifr->ifr_name);
319                 if(ioctl(sd, SIOCGIFADDR, &ifreq) != -1) {
320                     n = 0;
321                     while(he->h_addr_list[n]) {
322                         if(he->h_addrtype == AF_INET) {
323                             memset((char*)&taddr, 0, sizeof(taddr));
324                             memcpy((char*)&taddr.sin_addr, he->h_addr_list[n], he->h_length);
325 #ifdef DEBUG
326                             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): my ip: %s",
327                               inet_ntoa(((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr));
328                             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp ip: %s",
329                               inet_ntoa(taddr.sin_addr));
330 #endif
331                             if(((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr == taddr.sin_addr.s_addr) {
332                                 smtp->error = 1;
333                                 strcpy(msgbuf, "Error: this host is specified. ");
334                                 strcat(msgbuf, inet_ntoa(taddr.sin_addr));
335                                 smtp->error_message = malloc(strlen(msgbuf) + 1);
336                                 strcpy(smtp->error_message, msgbuf);
337                                 goto bail;
338                             }
339                         }
340                         n++;
341                     }
342                 }
343                 ifptr += sizeof(struct ifreq);
344             }
345         }
346     }
347     addr.sin_addr.s_addr = *((int *)he->h_addr_list[0]);
348     s = socket(PF_INET, SOCK_STREAM, 0);
349     if(connect(s, (struct sockaddr *) &addr, sizeof(addr))) {
350 #ifdef DEBUG
351         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): connection error = %s",strerror(errno));
352 #endif
353         smtp->error = 1;
354         strcpy(msgbuf, "Error: connecting to ");
355         strcat(msgbuf, inet_ntoa(addr.sin_addr));
356         smtp->error_message = malloc(strlen(msgbuf) + 1);
357         strcpy(smtp->error_message, msgbuf);
358         goto bail;
359     }
360     smtp->sock->fd = s;
361
362 #ifdef USE_SSL
363     use_ssl = 0;
364     if(cfg->use_smtps) {
365       if(start_tls(smtp, cfg)) {
366         smtp->error = 1;
367         strcpy(msgbuf, "Error: start_tls");
368         smtp->error_message = malloc(strlen(msgbuf) + 1);
369         strcpy(smtp->error_message, msgbuf);
370         goto bail;
371       }
372       use_ssl = 1;
373     }
374 #endif
375
376     /* CLAIM: we now have a TCP connection to the remote SMTP server */
377     alarm(cfg->timeout);
378
379     rc = socket_read(smtp->sock, rbuf, sizeof(rbuf));
380     alarm(0);
381     if(rc == -1) {
382 #ifdef DEBUG
383         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (banner): %m");
384 #endif
385         smtp->error = 1;
386         strcpy(msgbuf, RESP_SYNCERROR);
387         smtp->error_message = malloc(strlen(msgbuf) + 1);
388         strcpy(smtp->error_message, msgbuf);
389         goto bail;
390     }
391     rbuf[rc] = '\0';
392     c = strpbrk(rbuf, "\r\n");
393     if(c != NULL) {
394         *c = '\0';
395     }
396
397     if(strncmp(rbuf, "220 ", sizeof("220 ")-1)) {
398 #ifdef DEBUG
399         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): unexpected response during initial handshake: %s", rbuf);
400 #endif
401         smtp->error = 1;
402         strcpy(msgbuf, RESP_UNEXPECTED);
403         smtp->error_message = malloc(strlen(msgbuf) + 1);
404         strcpy(smtp->error_message, msgbuf);
405         goto bail;
406     }
407
408     if((uname(h_name)) < 0){
409         myhostname = "localhost.localdomain";
410     } else {
411         myhostname = h_name->nodename;
412     }
413
414     iov[0].iov_base = EHLO_CMD;
415     iov[0].iov_len  = sizeof(EHLO_CMD) - 1;
416     iov[1].iov_base = myhostname;
417     iov[1].iov_len  = strlen(myhostname);
418     iov[2].iov_base = "\r\n";
419     iov[2].iov_len  = sizeof("\r\n") - 1;
420
421 #ifdef DEBUG
422     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", EHLO_CMD, myhostname);
423 #endif
424     alarm(cfg->timeout);
425     rc = retry_writev(smtp->sock, iov, 3);
426     memset(iov, 0, sizeof(iov));
427     alarm(0);
428     if(rc == -1) {
429 #ifdef DEBUG
430         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): writev: %m");
431 #endif
432         smtp->error = 1;
433         strcpy(msgbuf, RESP_IERROR);
434         smtp->error_message = malloc(strlen(msgbuf) + 1);
435         strcpy(smtp->error_message, msgbuf);
436         goto bail;
437     }
438
439     /* read and parse the EHLO response */
440     alarm(cfg->timeout);
441     rc = socket_read(smtp->sock, rbuf, sizeof(rbuf));
442     alarm(0);
443     if(rc == -1) {
444 #ifdef DEBUG
445         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
446 #endif
447         smtp->error = 1;
448         strcpy(msgbuf, RESP_IERROR);
449         smtp->error_message = malloc(strlen(msgbuf) + 1);
450         strcpy(smtp->error_message, msgbuf);
451         goto bail;
452     }
453
454     if((tbuf = strstr(rbuf, "250-AUTH"))) {
455         if(strncmp(tbuf, "250", sizeof("250")-1) == 0) {
456             char *p = tbuf;
457             p += 3;
458             if(*p == '-' || *p == ' ') p++;
459             if(strncasecmp(p, "AUTH", sizeof("AUTH")-1) == 0) {
460                 p += 5;
461                 if(strcasestr(p, "PLAIN"))
462                     avail_auth_type |= AUTH_PLAIN;
463                 if(strcasestr(p, "LOGIN"))
464                     avail_auth_type |= AUTH_LOGIN;
465                 if(strcasestr(p, "CRAM-MD5"))
466                     avail_auth_type |= AUTH_CRAM_MD5;
467                 if(strcasestr(p, "DIGEST-MD5"))
468                     avail_auth_type |= AUTH_DIGEST_MD5;
469             }
470         }
471     }
472
473     if(avail_auth_type == 0) {
474 #ifdef DEBUG
475         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp authentication is not implemented: %s", rbuf);
476 #endif
477         smtp->error = 1;
478         strcpy(msgbuf, RESP_UNEXPECTED);
479         smtp->error_message = malloc(strlen(msgbuf) + 1);
480         strcpy(smtp->error_message, msgbuf);
481         goto bail;
482     }
483 #ifdef DEBUG
484     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth_type: %d", avail_auth_type);
485 #endif
486
487     /* build the AUTH command */
488     if(avail_auth_type & AUTH_CRAM_MD5) {
489         auth = auth_cram_md5(smtp->sock,&global);
490     }
491     else if((avail_auth_type & AUTH_LOGIN) != 0) {
492         auth = auth_login(smtp->sock,&global);
493     }
494     else if((avail_auth_type & AUTH_PLAIN) != 0) {
495         auth = auth_plain(smtp->sock,&global);
496     }
497     else {
498 #ifdef DEBUG
499         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp authentication is not implemented: %s", rbuf);
500 #endif
501         smtp->error = 1;
502         strcpy(msgbuf, RESP_UNEXPECTED);
503         smtp->error_message = malloc(strlen(msgbuf) + 1);
504         strcpy(smtp->error_message, msgbuf);
505         goto bail;
506     }
507
508 #ifdef DEBUG
509     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth) auth: [%d]", auth);
510 #endif
511     if(auth == 0) {
512 #ifdef DEBUG
513         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth) rejected: [%s]", global.username);
514 #endif
515         smtp->error = 2;
516         strcpy(msgbuf, RESP_CREDERROR);
517         smtp->error_message = malloc(strlen(msgbuf) + 1);
518         strcpy(smtp->error_message, msgbuf);
519         goto bail;
520     }
521
522     smtp_quit(smtp->sock,&global);
523     return smtp;
524
525     bail:
526         smtp_quit(smtp->sock,&global);
527         if(smtp->error == 1)
528             return smtp;
529         else if(smtp->error == 2)
530             return smtp;
531         return smtp;
532 }
533
534
535 int
536 smtp_quit(socket_t *socket, config_t *cfg) {
537
538     struct iovec iov[3];
539     int rc;
540
541     iov[0].iov_base = QUIT_CMD;
542     iov[0].iov_len  = sizeof(QUIT_CMD) - 1;
543     iov[1].iov_base = "\r\n";
544     iov[1].iov_len  = sizeof("\r\n") - 1;
545
546 #ifdef DEBUG
547     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", QUIT_CMD);
548 #endif
549     alarm(cfg->timeout);
550     rc = retry_writev(socket, iov, 2);
551     memset(iov, 0, sizeof(iov));
552     alarm(0);
553     if(rc == -1) {
554 #ifdef DEBUG
555         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): quit writev: %m");
556 #endif
557     }
558     (void)close(socket->fd);
559     return 1;
560 }
561
562
563
564 int
565 auth_cram_md5(socket_t *socket, config_t *cfg) {
566
567     struct iovec iov[3];
568     int rc;
569     char rbuf[RESP_LEN];
570     char buf[RESP_LEN];
571
572 #ifdef DEBUG
573     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH CRAM-MD5");
574 #endif
575     iov[0].iov_base = AUTH_CMD;
576     iov[0].iov_len  = sizeof(AUTH_CMD) - 1;
577     iov[1].iov_base = "CRAM-MD5";
578     iov[1].iov_len  = strlen("CRAM-MD5");
579     iov[2].iov_base = "\r\n";
580     iov[2].iov_len  = sizeof("\r\n") - 1;
581
582 #ifdef DEBUG
583     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "CRAM-MD5");
584 #endif
585     alarm(cfg->timeout);
586     rc = retry_writev(socket, iov, 3);
587     memset(iov, 0, sizeof(iov));
588     alarm(0);
589     if(rc == -1) {
590 #ifdef DEBUG
591         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): cram-md5 writev: %m");
592 #endif
593         return AUTH_NG;
594     }
595
596     alarm(cfg->timeout);
597     rc = socket_read(socket, rbuf, sizeof(rbuf));
598     alarm(0);
599     if(rc == -1) {
600 #ifdef DEBUG
601         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
602 #endif
603         return AUTH_NG;
604     }
605
606 #ifdef DEBUG
607     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
608 #endif
609     if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
610         char *response;
611         char *response64;
612         char *challenge;
613         int challengelen;
614         unsigned char hexdigest[33];
615
616         challenge = malloc(strlen(rbuf + 4) + 1);
617         challengelen = base64_decode(challenge, rbuf + 4, -1);
618         challenge[challengelen] = '\0';
619 #ifdef DEBUG
620         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): challenge=%s", challenge);
621 #endif
622
623         snprintf(buf, sizeof(buf), "%s", cfg->password);
624         md5_hex_hmac(hexdigest, challenge, challengelen, buf, strlen(cfg->password));
625         free(challenge);
626
627         response = malloc(sizeof(char)*128);
628         sprintf(response, "%s %s", cfg->username, hexdigest);
629         response64 = malloc((strlen(response) + 3) * 2 + 1);
630         base64_encode(response64, response, strlen(response));
631         free(response);
632
633         iov[0].iov_base = response64;
634         iov[0].iov_len  = strlen(response64);
635         iov[1].iov_base = "\r\n";
636         iov[1].iov_len  = sizeof("\r\n") - 1;
637
638 #ifdef DEBUG
639         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", response64);
640 #endif
641         alarm(cfg->timeout);
642         rc = retry_writev(socket, iov, 2);
643         memset(iov, 0, sizeof(iov));
644         alarm(0);
645         if(rc == -1) {
646 #ifdef DEBUG
647             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): cram-md5 writev: %m");
648 #endif
649             return AUTH_NG;
650         }
651
652         alarm(cfg->timeout);
653         rc = socket_read(socket, rbuf, sizeof(rbuf));
654         alarm(0);
655         if(rc == -1) {
656 #ifdef DEBUG
657             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
658 #endif
659             return AUTH_NG;
660         }
661
662 #ifdef DEBUG
663         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
664 #endif
665         if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
666 #ifdef DEBUG
667             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
668 #endif
669             return AUTH_NG;
670         }
671         free(response64);
672     } else {
673 #ifdef DEBUG
674         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems cram-md5 mech is not implemented.");
675 #endif
676         return AUTH_NG;
677     }
678     return AUTH_OK;
679 }
680
681
682 int
683 auth_login(socket_t *socket, config_t *cfg) {
684
685     struct iovec iov[3];
686     int rc;
687     char rbuf[RESP_LEN];
688     //char buf[RESP_LEN];
689     char *buf;
690
691 #ifdef DEBUG
692     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH LOGIN");
693 #endif
694     iov[0].iov_base = AUTH_CMD;
695     iov[0].iov_len  = sizeof(AUTH_CMD) - 1;
696     iov[1].iov_base = "LOGIN";
697     iov[1].iov_len  = strlen("LOGIN");
698     iov[2].iov_base = "\r\n";
699     iov[2].iov_len  = sizeof("\r\n") - 1;
700
701 #ifdef DEBUG
702     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "LOGIN");
703 #endif
704     alarm(cfg->timeout);
705     rc = retry_writev(socket, iov, 3);
706     memset(iov, 0, sizeof(iov));
707     alarm(0);
708     if(rc == -1) {
709 #ifdef DEBUG
710         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
711 #endif
712         return AUTH_NG;
713     }
714
715     alarm(cfg->timeout);
716     rc = socket_read(socket, rbuf, sizeof(rbuf));
717     alarm(0);
718     if(rc == -1) {
719 #ifdef DEBUG
720         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
721 #endif
722         return AUTH_NG;
723     }
724
725 #ifdef DEBUG
726     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
727 #endif
728     if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
729         buf = malloc(sizeof(char)*128);
730         base64_encode(buf, cfg->username, strlen(cfg->username));
731
732         iov[0].iov_base = buf;
733         iov[0].iov_len  = strlen(buf);
734         iov[1].iov_base = "\r\n";
735         iov[1].iov_len  = sizeof("\r\n") - 1;
736
737 #ifdef DEBUG
738         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
739 #endif
740         alarm(cfg->timeout);
741         rc = retry_writev(socket, iov, 2);
742         memset(iov, 0, sizeof(iov));
743         alarm(0);
744         if(rc == -1) {
745 #ifdef DEBUG
746             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
747 #endif
748             return AUTH_NG;
749         }
750
751         alarm(cfg->timeout);
752         rc = socket_read(socket, rbuf, sizeof(rbuf));
753         alarm(0);
754         if(rc == -1) {
755 #ifdef DEBUG
756             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
757 #endif
758             return AUTH_NG;
759         }
760
761 #ifdef DEBUG
762         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
763 #endif
764         if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
765             buf = malloc(sizeof(char)*128);
766             base64_encode(buf, cfg->password, strlen(cfg->password));
767
768             iov[0].iov_base = buf;
769             iov[0].iov_len  = strlen(buf);
770             iov[1].iov_base = "\r\n";
771             iov[1].iov_len  = sizeof("\r\n") - 1;
772
773 #ifdef DEBUG
774             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
775 #endif
776             alarm(cfg->timeout);
777             rc = retry_writev(socket, iov, 2);
778             memset(iov, 0, sizeof(iov));
779             alarm(0);
780             if(rc == -1) {
781 #ifdef DEBUG
782                 syslog(LOG_WARNING, "pam_smtpauth(smtpauth): login writev: %m");
783 #endif
784                 return AUTH_NG;
785             }
786
787             alarm(cfg->timeout);
788             rc = socket_read(socket, rbuf, sizeof(rbuf));
789             alarm(0);
790             if(rc == -1) {
791 #ifdef DEBUG
792                 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
793 #endif
794                 return AUTH_NG;
795             }
796
797 #ifdef DEBUG
798             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
799 #endif
800             if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
801 #ifdef DEBUG
802                 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
803 #endif
804                 return AUTH_NG;
805             }
806         } else {
807 #ifdef DEBUG
808             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems login mech is not implemented.");
809 #endif
810             return AUTH_NG;
811         }
812     } else {
813 #ifdef DEBUG
814         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems login mech is not implemented.");
815 #endif
816         return AUTH_NG;
817     }
818     return AUTH_OK;
819 }
820
821
822 int
823 auth_plain(socket_t *socket, config_t *cfg) {
824
825     struct iovec iov[3];
826     int rc;
827     char rbuf[RESP_LEN];
828     //char buf[RESP_LEN];
829     char *buf;
830     int cnt, len;
831     char phrase[512];
832
833 #ifdef DEBUG
834     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH PLAIN");
835 #endif
836     sprintf(phrase,"%s^%s^%s", cfg->username, cfg->username, cfg->password);
837     len = strlen(phrase);
838     for(cnt=len-1; cnt>=0; cnt--) {
839         if(phrase[cnt] == '^') {
840             phrase[cnt] = '\0';
841         }
842     }
843     buf = malloc(sizeof(char)*128);
844     base64_encode(buf, phrase, len);
845
846     iov[0].iov_base = AUTH_CMD;
847     iov[0].iov_len  = sizeof(AUTH_CMD) - 1;
848     iov[1].iov_base = "PLAIN ";
849     iov[1].iov_len  = strlen("PLAIN ");
850     iov[2].iov_base = buf;
851     iov[2].iov_len  = strlen(buf);
852     iov[3].iov_base = "\r\n";
853     iov[3].iov_len  = sizeof("\r\n") - 1;
854
855 #ifdef DEBUG
856     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s %s", AUTH_CMD, "PLAIN", buf);
857 #endif
858     alarm(cfg->timeout);
859     rc = retry_writev(socket, iov, 4);
860     memset(iov, 0, sizeof(iov));
861     free(buf);
862     alarm(0);
863     if(rc == -1) {
864 #ifdef DEBUG
865         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): plain writev: %m");
866 #endif
867         return AUTH_NG;
868     }
869
870     alarm(cfg->timeout);
871     rc = socket_read(socket, rbuf, sizeof(rbuf));
872     alarm(0);
873     if(rc == -1) {
874 #ifdef DEBUG
875         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
876 #endif
877         return AUTH_NG;
878     }
879
880     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
881     if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
882 #ifdef DEBUG
883         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
884 #endif
885         return AUTH_NG;
886     }
887     return AUTH_OK;
888 }
889
890
891 #ifdef USE_SSL
892 SSL_CTX *SSLContext = 0;
893
894 #ifdef VERYIFY_CERT
895 /* this gets called when a certificate is to be verified */
896 static int
897 verify_cert(SSL *ssl) {
898
899     X509 *cert;
900     int err;
901     char buf[256];
902     int ret = -1;
903     BIO *bio;
904
905     cert = SSL_get_peer_certificate(ssl);
906     if(!cert) {
907 #ifdef DEBUG
908         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: no server certificate.");
909 #endif
910         return -1;
911     }
912
913     err = SSL_get_verify_result(ssl);
914     if(err == X509_V_OK) {
915         return 0;
916     }
917
918 #ifdef DEBUG
919     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error:  can't verify certificate: %s (%d).",
920              X509_verify_cert_error_string(err), err);
921 #endif
922     X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
923     fprintf(stderr,"\nSubject: %s\n", buf);
924     X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
925     fprintf(stderr,"Issuer:  %s\n", buf);
926     bio = BIO_new(BIO_s_mem());
927     ASN1_TIME_print(bio, X509_get_notBefore(cert));
928     memset(buf, 0, sizeof(buf));
929     BIO_read(bio, buf, sizeof(buf) - 1);
930     fprintf(stderr,"Valid from: %s\n", buf);
931     ASN1_TIME_print(bio, X509_get_notAfter(cert));
932     memset(buf, 0, sizeof(buf));
933     BIO_read(bio, buf, sizeof(buf) - 1);
934     BIO_free(bio);
935     fprintf(stderr,"      to:   %s\n", buf);
936
937     fprintf(stderr, 
938         "\nThere is no way to verify this certificate.\n"
939          " It is possible that a hostile attacker has replaced the server certificate.\n"
940          " Continue at your own risk!\n"
941          "\nAccept this certificate anyway? [no]: ");
942     if(fgets(buf, sizeof(buf), stdin) && (buf[0] == 'y' || buf[0] == 'Y')) {
943         ret = 0;
944         fprintf(stderr, "\nFine, but don't say I didn't warn you!\n\n");
945     }
946     return ret;
947 }
948 #endif
949
950 static int
951 init_ssl(config_t *conf) {
952
953     SSL_METHOD *method;
954     int options = 0;
955
956     if(!conf->certfile) {
957 #ifdef DEBUG
958         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSLCertificateFile not defined.");
959 #endif
960         return -1;
961     }
962     SSL_load_error_strings();
963     SSL_library_init();
964     if(conf->use_tlsv1 && !conf->use_sslv2 && !conf->use_sslv3)
965         method = TLSv1_client_method();
966     else
967         method = SSLv23_client_method();
968
969     SSLContext = SSL_CTX_new(method);
970
971     if(access(conf->certfile, F_OK)) {
972         if(errno != ENOENT) {
973 #ifdef DEBUG
974             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSLCertificateFile is not accessible.");
975 #endif
976             return -1;
977         }
978 #ifdef DEBUG
979         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Warning: SSLCertificateFile doesn't exist, can't verify server certificates.");
980 #endif
981     } else if(!SSL_CTX_load_verify_locations(SSLContext, conf->certfile, NULL)) {
982 #ifdef DEBUG
983         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSL_CTX_load_verify_locations: %s.",ERR_error_string(ERR_get_error(), 0));
984 #endif
985         SSL_CTX_free(SSLContext);
986         return -1;
987     }
988
989     if(!conf->use_sslv2) {
990         options |= SSL_OP_NO_SSLv2;
991     }
992     if(!conf->use_sslv3) {
993         options |= SSL_OP_NO_SSLv3;
994     }
995     if(!conf->use_tlsv1) {
996         options |= SSL_OP_NO_TLSv1;
997     }
998
999     SSL_CTX_set_options(SSLContext, options);
1000
1001     /* we check the result of the verification after SSL_connect() */
1002     SSL_CTX_set_verify(SSLContext, SSL_VERIFY_NONE, 0);
1003     return 0;
1004 }
1005
1006 int
1007 start_tls(smtp_t *smtp, config_t *cfg) {
1008
1009     int ret;
1010     /* initialize SSL */
1011     if(init_ssl(cfg)) {
1012 #ifdef DEBUG
1013         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): failed to initialize ssl session.");
1014 #endif
1015         return 1;
1016     }
1017
1018     smtp->sock->ssl = SSL_new(SSLContext);
1019     SSL_set_fd(smtp->sock->ssl, smtp->sock->fd);
1020     if((ret = SSL_connect(smtp->sock->ssl)) <= 0) {
1021         socket_perror("connect", smtp->sock, ret);
1022 #ifdef DEBUG
1023         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): failed to connect ssl session.");
1024 #endif
1025         return 1;
1026     }
1027 #ifdef VERIFY_CERT
1028     /* verify the server certificate */
1029     if(verify_cert(smtp->sock->ssl)) {
1030         return 1;
1031     }
1032 #endif
1033     smtp->sock->use_ssl = 1;
1034 #ifdef DEBUG
1035     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): SSL support enabled.");
1036 #endif
1037     return 0;
1038 }
1039 #endif
1040