OSDN Git Service

create subdir
[pam-smtpauth/pam_smtpauth.git] / src / 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 <syslog.h>
30 #include <time.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 DIGEST_MD5_REALM_LEN   256
70 #define DIGEST_MD5_NONCE_LEN   64
71 #define DIGEST_MD5_CNONCE_LEN  33
72 #define DIGEST_MD5_QOP_LEN     64
73 #define DIGEST_MD5_URI_LEN     261
74
75 extern void base64_encode(char *out, const char *in, int inlen);
76 extern int base64_decode(char *out, const char *in, int inlen);
77 extern int retry_writev(socket_t *sock, struct iovec *iov, int iovcnt);
78 extern int socket_read(socket_t *sock, char *buf, size_t len);
79 extern int socket_close(socket_t *sock);
80 extern void socket_perror(const char *func, socket_t *sock, int ret);
81 extern void set_timeout(int timeout);
82
83 void md5_hex_hmac(char *hexdigest, unsigned char *text, unsigned int text_len, unsigned char *key, unsigned int key_len);
84 void hmac_md5(unsigned char *text, unsigned int text_len, unsigned char *key, unsigned int key_len, unsigned char *digest);
85 int start_tls(smtp_t *smtp, config_t *cfg);
86
87 int smtp_quit(socket_t *sock, config_t *cfg);
88 int auth_plain(socket_t *sock, config_t *cfg);
89 int auth_login(socket_t *sock, config_t *cfg);
90 int auth_cram_md5(socket_t *sock, config_t *cfg);
91 int auth_digest_md5(socket_t *sock, config_t *cfg);
92
93 config_t global;
94
95 static void
96 bin2hex(char *out, const unsigned char *in, int in_len) {
97     static const char hex[17] = "0123456789abcdef";
98     int cnt;
99
100     for(cnt=0; cnt<in_len; cnt++) {
101         out[cnt * 2]       = hex[in[cnt] >> 4];
102         out[(cnt * 2) + 1] = hex[in[cnt]&0x0F];
103     }
104 }
105
106 void
107 make_digest(char *md5str, unsigned char *digest) {
108     bin2hex(md5str, digest, 16);
109     md5str[32] = '\0';
110 }
111
112 void
113 md5_hex_hmac(char *hexdigest, unsigned char *text, unsigned int text_len, unsigned char *key, unsigned int key_len) {
114
115     unsigned char digest[16];
116     int cnt;
117
118     hmac_md5(text, text_len, key, key_len, digest);
119     for(cnt=0; cnt<16; cnt++) {
120         sprintf(hexdigest + 2 * cnt, "%02x", digest[cnt]);
121     }
122 }
123
124
125 void
126 hmac_md5(unsigned char *text, unsigned int text_len, unsigned char *key, unsigned int key_len, unsigned char *digest) {
127
128     MD5_CTX context;
129     unsigned char k_ipad[64];
130     unsigned char k_opad[64];
131     int cnt;
132
133     memset(k_ipad, 0, sizeof(k_ipad));
134     memset(k_opad, 0, sizeof(k_opad));
135     if(key_len > 64) {
136         MD5_CTX tctx;
137
138 #ifdef USE_SSL
139         MD5_Init(&tctx);
140         MD5_Update(&tctx, key, key_len);
141         MD5_Final(k_ipad, &tctx);
142         MD5_Final(k_opad, &tctx);
143 #else
144         MD5Init(&tctx);
145         MD5Update(&tctx, key, key_len);
146         MD5Final(k_ipad, &tctx);
147         MD5Final(k_opad, &tctx);
148 #endif
149     } else {
150         memcpy(k_ipad, key, key_len);
151         memcpy(k_opad, key, key_len);
152     }
153
154     for(cnt=0; cnt<64; cnt++) {
155         k_ipad[cnt] ^= 0x36;
156         k_opad[cnt] ^= 0x5c;
157     }
158
159 #ifdef USE_SSL
160     MD5_Init(&context);
161     MD5_Update(&context, k_ipad, 64);
162     MD5_Update(&context, text, text_len);
163     MD5_Final(digest, &context);
164
165     MD5_Init(&context);
166     MD5_Update(&context, k_opad, 64);
167     MD5_Update(&context, digest, 16);
168     MD5_Final(digest, &context);
169 #else
170     MD5Init(&context);
171     MD5Update(&context, k_ipad, 64);
172     MD5Update(&context, text, text_len);
173     MD5Final(digest, &context);
174
175     MD5Init(&context);
176     MD5Update(&context, k_opad, 64);
177     MD5Update(&context, digest, 16);
178     MD5Final(digest, &context);
179 #endif
180 }
181
182
183 int
184 extract_token(const char *str, const char *token, char *value, int len) {
185
186     char *p = NULL, *q = NULL;
187
188     memset(value,0x00,sizeof(char)*len);
189     if((p = strstr(str, token)) != NULL) {
190         p += strlen(token);
191         if(*p == '\"') {
192             if((q = strchr(p + 1, '\"')) == NULL)
193                 return -1;
194             strncpy(value, p + 1, q - p - 1 >= len ? len - 1 : q - p - 1);
195
196         } else {
197             if((q = strchr(p, ',')) == NULL)
198                 q += strlen(p);
199             strncpy(value, p, q - p >= len ? len - 1 : q - p);
200         }
201     }
202 #ifdef DEBUG
203     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): str=%s", str);
204 #endif
205
206     return 0;
207 }
208
209 void
210 digest_md5(char *response, unsigned char *text, unsigned int text_len, const char *login, const char *passwd) {
211
212     char realm[DIGEST_MD5_REALM_LEN];
213     char nonce[DIGEST_MD5_NONCE_LEN];
214     char qop[DIGEST_MD5_QOP_LEN];
215     char uri[DIGEST_MD5_URI_LEN];
216     char cnonce[DIGEST_MD5_CNONCE_LEN];
217
218     unsigned char random[16];
219
220     MD5_CTX ctx;
221     unsigned char digest[16];
222     char hexA1[33], hexA2[33], resp[33];
223
224
225 #ifdef DEBUG
226     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): text=%s", text);
227 #endif
228
229     extract_token((const char *)text, "nonce=", nonce, DIGEST_MD5_NONCE_LEN);
230     extract_token((const char *)text, "realm=", realm, DIGEST_MD5_REALM_LEN);
231     extract_token((const char *)text, "qop=", qop, DIGEST_MD5_QOP_LEN);
232
233     srand(time(NULL));
234     snprintf((char *)random, sizeof(random), "%ld", (long int)rand());
235     bin2hex(cnonce, random, 8);
236     cnonce[16] = '\0';
237
238     sprintf(uri, "smtp/%s", realm);
239 #ifdef DEBUG
240     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): realm=%s", realm);
241     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): nonce=%s", nonce);
242     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): qop=%s", qop);
243     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): cnonce=%s", cnonce);
244     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): uri=%s", uri);
245 #endif
246
247     /* A1 */
248 #ifdef USE_SSL
249     MD5_Init(&ctx);
250     MD5_Update(&ctx, login, strlen(login));
251     MD5_Update(&ctx, ":", 1);
252     MD5_Update(&ctx, realm, strlen(realm));
253     MD5_Update(&ctx, ":", 1);
254     MD5_Update(&ctx, passwd, strlen(passwd));
255     MD5_Final(digest, &ctx);
256 #else
257     MD5Init(&ctx);
258     MD5Update(&ctx, login, strlen(login));
259     MD5Update(&ctx, ":", 1);
260     MD5Update(&ctx, realm, strlen(realm));
261     MD5Update(&ctx, ":", 1);
262     MD5Update(&ctx, passwd, strlen(passwd));
263     MD5Final(digest, &ctx);
264 #endif
265
266 #ifdef USE_SSL
267     MD5_Init(&ctx);
268     MD5_Update(&ctx, digest, 16);
269     MD5_Update(&ctx, ":", 1);
270     MD5_Update(&ctx, nonce, strlen(nonce));
271     MD5_Update(&ctx, ":", 1);
272     MD5_Update(&ctx, cnonce, strlen(cnonce));
273     MD5_Final(digest, &ctx);
274 #else
275     MD5Init(&ctx);
276     MD5Update(&ctx, digest, 16);
277     MD5Update(&ctx, ":", 1);
278     MD5Update(&ctx, nonce, strlen(nonce));
279     MD5Update(&ctx, ":", 1);
280     MD5Update(&ctx, cnonce, strlen(cnonce));
281     MD5Final(digest, &ctx);
282 #endif
283     make_digest(hexA1, digest);
284
285 #ifdef DEBUG
286     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): A1=%s", hexA1);
287 #endif
288
289     /* A2 */
290 #ifdef USE_SSL
291     MD5_Init(&ctx);
292     MD5_Update(&ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
293     MD5_Update(&ctx, uri, strlen(uri));
294     if(!strcmp(qop, "auth-int")) {
295         MD5_Update(&ctx, ":00000000000000000000000000000000", sizeof(":00000000000000000000000000000000") - 1);
296     }
297     MD5_Final(digest, &ctx);
298 #else
299     MD5Init(&ctx);
300     MD5Update(&ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
301     MD5Update(&ctx, uri, strlen(uri));
302     if(!strcmp(qop, "auth-int")) {
303         MD5Update(&ctx, ":00000000000000000000000000000000", sizeof(":00000000000000000000000000000000") - 1);
304     }
305     MD5Final(digest, &ctx);
306 #endif
307     make_digest(hexA2, digest);
308
309 #ifdef DEBUG
310     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): A2=%s", hexA2);
311 #endif
312
313     /* response */
314 #ifdef USE_SSL
315     MD5_Init(&ctx);
316     MD5_Update(&ctx, hexA1, 32);
317     MD5_Update(&ctx, ":", 1);
318     MD5_Update(&ctx, nonce, strlen(nonce));
319     MD5_Update(&ctx, ":00000001:", sizeof(":00000001:") - 1);
320     MD5_Update(&ctx, cnonce, strlen(cnonce));
321     MD5_Update(&ctx, ":", 1);
322     MD5_Update(&ctx, qop, strlen(qop));
323     MD5_Update(&ctx, ":", 1);
324     MD5_Update(&ctx, hexA2, 32);
325     MD5_Final(digest, &ctx);
326 #else
327     MD5Init(&ctx);
328     MD5Update(&ctx, hexA1, 32);
329     MD5Update(&ctx, ":", 1);
330     MD5Update(&ctx, nonce, strlen(nonce));
331     MD5Update(&ctx, ":00000001:", sizeof(":00000001:") - 1);
332     MD5Update(&ctx, cnonce, strlen(cnonce));
333     MD5Update(&ctx, ":", 1);
334     MD5Update(&ctx, qop, strlen(qop));
335     MD5Update(&ctx, ":", 1);
336     MD5Update(&ctx, hexA2, 32);
337     MD5Final(digest, &ctx);
338 #endif
339
340     make_digest(resp, digest);
341
342 #ifdef DEBUG
343     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): resp=%s", resp);
344 #endif
345
346     sprintf(response, "charset=utf-8,username=\"%s\",realm=\"%s\",nonce=\"%s\","
347                "nc=00000001,cnonce=\"%s\",digest-uri=\"%s\",qop=%s,"
348                "response=%s",
349                login, realm, nonce, cnonce, uri, qop, resp);
350
351 #ifdef DEBUG
352     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): response:%s", response);
353     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): text:%s", text);
354 #endif
355 }
356
357
358 smtp_t *
359 smtp_auth(config_t *cfg) {
360
361     int s;
362     struct sockaddr_in addr;
363     struct hostent *he;
364     smtp_t *smtp = NULL;
365     char msgbuf[256];
366
367     struct iovec iov[5];
368     char *c;
369     int rc;
370     char rbuf[RESP_LEN];
371     int auth = 0;
372     int avail_auth_type = 0;
373     char *tbuf;
374     struct utsname  h_name[1];
375     char *myhostname;
376
377     int                n;
378     struct sockaddr_in taddr;
379     int                sd;
380     struct ifconf      ifconf;
381     struct ifreq       *ifr, ifreq;
382     unsigned char      *ifptr;
383     int                iflen;
384 #ifdef USE_SSL
385     int use_ssl;
386 #endif
387
388     if(!cfg->password) {
389         if(!global.password) {
390             global.password = getpass("Password:");
391             if(!global.password) {
392                 return 0;
393             }
394             if(!*global.password) {
395                 global.password = NULL;
396                 goto bail;
397             }
398             global.password = strdup(global.password);
399         }
400         cfg->password = strdup(global.password);
401     }
402
403     assert(cfg->username != NULL);
404     assert(cfg->password != NULL);
405
406     smtp = calloc(1, sizeof(smtp_t));
407     smtp->sock = calloc(1, sizeof(socket_t));
408     smtp->buf = calloc(1, sizeof(buffer_t));
409     smtp->buf->sock = smtp->sock;
410     smtp->sock->fd = -1;
411     smtp->error = 0;
412
413     /* open connection to SMTP server */
414     memset(&addr, 0, sizeof(addr));
415     addr.sin_port = htons(cfg->port);
416     addr.sin_family = AF_INET;
417     he = gethostbyname(cfg->host);
418     if(!he) {
419         smtp->error = 1;
420         strcpy(msgbuf, "Error: resolving hostname ");
421         strcat(msgbuf, cfg->host);
422         smtp->error_message = malloc(strlen(msgbuf) + 1);
423         strcpy(smtp->error_message, msgbuf);
424         goto bail;
425     }
426
427     if((sd = socket(PF_INET, SOCK_DGRAM, 0)) != -1) {
428         bzero(&ifconf, sizeof(struct ifconf));
429         bzero(&ifreq, sizeof(struct ifreq));
430         iflen = 10 * sizeof(struct ifreq);
431         ifptr = malloc(iflen);
432         ifconf.ifc_len = iflen;
433         ifconf.ifc_ifcu.ifcu_req = (struct ifreq *)ifptr;
434         if(ioctl(sd, SIOCGIFCONF, &ifconf) != -1) {
435             for(iflen=sizeof(struct ifreq); iflen<=ifconf.ifc_len; iflen+=sizeof(struct ifreq)) {
436                 ifr = (struct ifreq *)ifptr;
437                 strcpy(ifreq.ifr_ifrn.ifrn_name, ifr->ifr_name);
438                 if(ioctl(sd, SIOCGIFADDR, &ifreq) != -1) {
439                     n = 0;
440                     while(he->h_addr_list[n]) {
441                         if(he->h_addrtype == AF_INET) {
442                             memset((char*)&taddr, 0, sizeof(taddr));
443                             memcpy((char*)&taddr.sin_addr, he->h_addr_list[n], he->h_length);
444 #ifdef DEBUG
445                             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): my ip: %s",
446                               inet_ntoa(((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr));
447                             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp ip: %s",
448                               inet_ntoa(taddr.sin_addr));
449 #endif
450                             if(((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr == taddr.sin_addr.s_addr) {
451                                 smtp->error = 1;
452                                 strcpy(msgbuf, "Error: this host is specified. ");
453                                 strcat(msgbuf, inet_ntoa(taddr.sin_addr));
454                                 smtp->error_message = malloc(strlen(msgbuf) + 1);
455                                 strcpy(smtp->error_message, msgbuf);
456                                 goto bail;
457                             }
458                         }
459                         n++;
460                     }
461                 }
462                 ifptr += sizeof(struct ifreq);
463             }
464         }
465     }
466     addr.sin_addr.s_addr = *((int *)he->h_addr_list[0]);
467     s = socket(PF_INET, SOCK_STREAM, 0);
468
469     if(cfg->conn_timeout > 0) {
470         set_timeout(cfg->conn_timeout);
471     }
472     if(connect(s, (struct sockaddr *) &addr, sizeof(addr))) {
473 #ifdef DEBUG
474         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): connection error = %s",strerror(errno));
475 #endif
476         smtp->error = 1;
477         strcpy(msgbuf, "Error: connecting to ");
478         strcat(msgbuf, inet_ntoa(addr.sin_addr));
479         smtp->error_message = malloc(strlen(msgbuf) + 1);
480         strcpy(smtp->error_message, msgbuf);
481         goto bail;
482     }
483     smtp->sock->fd = s;
484     if(cfg->conn_timeout > 0) {
485         set_timeout(0);
486     }
487
488 #ifdef USE_SSL
489     use_ssl = 0;
490     if(cfg->use_smtps) {
491       if(start_tls(smtp, cfg)) {
492         smtp->error = 1;
493         strcpy(msgbuf, "Error: start_tls");
494         smtp->error_message = malloc(strlen(msgbuf) + 1);
495         strcpy(smtp->error_message, msgbuf);
496         goto bail;
497       }
498       use_ssl = 1;
499     }
500 #endif
501
502     /* CLAIM: we now have a TCP connection to the remote SMTP server */
503     if(cfg->timeout > 0) {
504         set_timeout(cfg->timeout);
505     }
506     rc = socket_read(smtp->sock, rbuf, sizeof(rbuf));
507     if(cfg->timeout > 0) {
508         set_timeout(0);
509     }
510     if(rc == -1) {
511 #ifdef DEBUG
512         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (banner): %m");
513 #endif
514         smtp->error = 1;
515         strcpy(msgbuf, RESP_SYNCERROR);
516         smtp->error_message = malloc(strlen(msgbuf) + 1);
517         strcpy(smtp->error_message, msgbuf);
518         goto bail;
519     }
520     rbuf[rc] = '\0';
521     c = strpbrk(rbuf, "\r\n");
522     if(c != NULL) {
523         *c = '\0';
524     }
525
526     if(strncmp(rbuf, "220 ", sizeof("220 ")-1)) {
527 #ifdef DEBUG
528         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): unexpected response during initial handshake: %s", rbuf);
529 #endif
530         smtp->error = 1;
531         strcpy(msgbuf, RESP_UNEXPECTED);
532         smtp->error_message = malloc(strlen(msgbuf) + 1);
533         strcpy(smtp->error_message, msgbuf);
534         goto bail;
535     }
536
537     if((uname(h_name)) < 0){
538         myhostname = "localhost.localdomain";
539     } else {
540         myhostname = h_name->nodename;
541     }
542
543     iov[0].iov_base = EHLO_CMD;
544     iov[0].iov_len  = sizeof(EHLO_CMD) - 1;
545     iov[1].iov_base = myhostname;
546     iov[1].iov_len  = strlen(myhostname);
547     iov[2].iov_base = "\r\n";
548     iov[2].iov_len  = sizeof("\r\n") - 1;
549
550 #ifdef DEBUG
551     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", EHLO_CMD, myhostname);
552 #endif
553     if(cfg->timeout > 0) {
554         set_timeout(cfg->timeout);
555     }
556     rc = retry_writev(smtp->sock, iov, 3);
557     memset(iov, 0, sizeof(iov));
558     if(cfg->timeout > 0) {
559         set_timeout(0);
560     }
561     if(rc == -1) {
562 #ifdef DEBUG
563         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): writev: %m");
564 #endif
565         smtp->error = 1;
566         strcpy(msgbuf, RESP_IERROR);
567         smtp->error_message = malloc(strlen(msgbuf) + 1);
568         strcpy(smtp->error_message, msgbuf);
569         goto bail;
570     }
571
572     /* read and parse the EHLO response */
573     if(cfg->timeout > 0) {
574         set_timeout(cfg->timeout);
575     }
576     rc = socket_read(smtp->sock, rbuf, sizeof(rbuf));
577     if(cfg->timeout > 0) {
578         set_timeout(0);
579     }
580     if(rc == -1) {
581 #ifdef DEBUG
582         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
583 #endif
584         smtp->error = 1;
585         strcpy(msgbuf, RESP_IERROR);
586         smtp->error_message = malloc(strlen(msgbuf) + 1);
587         strcpy(smtp->error_message, msgbuf);
588         goto bail;
589     }
590
591     if((tbuf = strstr(rbuf, "250-STARTTLS"))) {
592 #ifdef DEBUG
593         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): STARTTLS not supported.");
594 #endif
595     }
596
597     if((tbuf = strstr(rbuf, "250-AUTH"))) {
598         if(strncmp(tbuf, "250", sizeof("250")-1) == 0) {
599             char *p = tbuf;
600             p += 3;
601             if(*p == '-' || *p == ' ') p++;
602             if(strncasecmp(p, "AUTH", sizeof("AUTH")-1) == 0) {
603                 p += 5;
604                 if(strcasestr(p, "PLAIN"))
605                     avail_auth_type |= AUTH_PLAIN;
606                 if(strcasestr(p, "LOGIN"))
607                     avail_auth_type |= AUTH_LOGIN;
608                 if(strcasestr(p, "CRAM-MD5"))
609                     avail_auth_type |= AUTH_CRAM_MD5;
610                 if(strcasestr(p, "DIGEST-MD5"))
611                     avail_auth_type |= AUTH_DIGEST_MD5;
612             }
613         }
614     }
615
616     if(avail_auth_type == 0) {
617 #ifdef DEBUG
618         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp authentication is not implemented: %s", rbuf);
619 #endif
620         smtp->error = 1;
621         strcpy(msgbuf, RESP_UNEXPECTED);
622         smtp->error_message = malloc(strlen(msgbuf) + 1);
623         strcpy(smtp->error_message, msgbuf);
624         goto bail;
625     }
626 #ifdef DEBUG
627     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth_type: %d", avail_auth_type);
628 #endif
629
630     /* build the AUTH command */
631     if(avail_auth_type & AUTH_CRAM_MD5) {
632         auth = auth_cram_md5(smtp->sock,&global);
633     }
634     else if((avail_auth_type & AUTH_LOGIN) != 0) {
635         auth = auth_login(smtp->sock,&global);
636     }
637     else if((avail_auth_type & AUTH_PLAIN) != 0) {
638         auth = auth_plain(smtp->sock,&global);
639     }
640     else if((avail_auth_type & AUTH_DIGEST_MD5) != 0) {
641         auth = auth_digest_md5(smtp->sock,&global);
642     }
643     else {
644 #ifdef DEBUG
645         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp authentication is not implemented: %s", rbuf);
646 #endif
647         smtp->error = 1;
648         strcpy(msgbuf, RESP_UNEXPECTED);
649         smtp->error_message = malloc(strlen(msgbuf) + 1);
650         strcpy(smtp->error_message, msgbuf);
651         goto bail;
652     }
653
654 #ifdef DEBUG
655     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth) auth: [%d]", auth);
656 #endif
657     if(auth == 0) {
658 #ifdef DEBUG
659         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth) rejected: [%s]", global.username);
660 #endif
661         smtp->error = 2;
662         strcpy(msgbuf, RESP_CREDERROR);
663         smtp->error_message = malloc(strlen(msgbuf) + 1);
664         strcpy(smtp->error_message, msgbuf);
665         goto bail;
666     }
667
668     smtp_quit(smtp->sock,&global);
669     return smtp;
670
671     bail:
672         smtp_quit(smtp->sock,&global);
673         if(smtp->error == 1)
674             return smtp;
675         else if(smtp->error == 2)
676             return smtp;
677         return smtp;
678 }
679
680
681 int
682 smtp_quit(socket_t *sock, config_t *cfg) {
683
684     struct iovec iov[3];
685     int rc;
686
687     iov[0].iov_base = QUIT_CMD;
688     iov[0].iov_len  = sizeof(QUIT_CMD) - 1;
689     iov[1].iov_base = "\r\n";
690     iov[1].iov_len  = sizeof("\r\n") - 1;
691
692 #ifdef DEBUG
693     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", QUIT_CMD);
694 #endif
695     if(cfg->timeout > 0) {
696         set_timeout(cfg->timeout);
697     }
698     rc = retry_writev(sock, iov, 2);
699     memset(iov, 0, sizeof(iov));
700     if(cfg->timeout > 0) {
701         set_timeout(0);
702     }
703     if(rc == -1) {
704 #ifdef DEBUG
705         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): quit writev: %m");
706 #endif
707     }
708     (void)socket_close(sock);
709     return 1;
710 }
711
712
713 int
714 auth_cram_md5(socket_t *sock, config_t *cfg) {
715
716     struct iovec iov[3];
717     int rc;
718     char rbuf[RESP_LEN];
719     char buf[RESP_LEN];
720
721 #ifdef DEBUG
722     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH CRAM-MD5");
723 #endif
724     iov[0].iov_base = AUTH_CMD;
725     iov[0].iov_len  = sizeof(AUTH_CMD) - 1;
726     iov[1].iov_base = "CRAM-MD5";
727     iov[1].iov_len  = strlen("CRAM-MD5");
728     iov[2].iov_base = "\r\n";
729     iov[2].iov_len  = sizeof("\r\n") - 1;
730
731 #ifdef DEBUG
732     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "CRAM-MD5");
733 #endif
734     if(cfg->timeout > 0) {
735         set_timeout(cfg->timeout);
736     }
737     rc = retry_writev(sock, iov, 3);
738     memset(iov, 0, sizeof(iov));
739     if(cfg->timeout > 0) {
740         set_timeout(0);
741     }
742     if(rc == -1) {
743 #ifdef DEBUG
744         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): cram-md5 writev: %m");
745 #endif
746         return AUTH_NG;
747     }
748
749     if(cfg->timeout > 0) {
750         set_timeout(cfg->timeout);
751     }
752     rc = socket_read(sock, rbuf, sizeof(rbuf));
753     if(cfg->timeout > 0) {
754         set_timeout(0);
755     }
756     if(rc == -1) {
757 #ifdef DEBUG
758         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
759 #endif
760         return AUTH_NG;
761     }
762
763 #ifdef DEBUG
764     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
765 #endif
766     if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
767         char *response;
768         char *response64;
769         unsigned char *challenge;
770         int challengelen;
771         unsigned char hexdigest[33];
772
773         challenge = malloc(strlen(rbuf + 4) + 1);
774         challengelen = base64_decode((char *)challenge, rbuf + 4, -1);
775         challenge[challengelen] = '\0';
776 #ifdef DEBUG
777         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): challenge=%s", challenge);
778 #endif
779
780         snprintf(buf, sizeof(buf), "%s", cfg->password);
781         md5_hex_hmac((char *)hexdigest, challenge, challengelen, (unsigned char*)buf, strlen(cfg->password));
782         free(challenge);
783
784         response = malloc(sizeof(char)*128);
785         sprintf(response, "%s %s", cfg->username, hexdigest);
786         response64 = malloc((strlen(response) + 3) * 2 + 1);
787         base64_encode(response64, response, strlen(response));
788         free(response);
789
790         iov[0].iov_base = response64;
791         iov[0].iov_len  = strlen(response64);
792         iov[1].iov_base = "\r\n";
793         iov[1].iov_len  = sizeof("\r\n") - 1;
794
795 #ifdef DEBUG
796         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", response64);
797 #endif
798         if(cfg->timeout > 0) {
799             set_timeout(cfg->timeout);
800         }
801         rc = retry_writev(sock, iov, 2);
802         memset(iov, 0, sizeof(iov));
803         if(cfg->timeout > 0) {
804             set_timeout(0);
805         }
806         if(rc == -1) {
807 #ifdef DEBUG
808             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): cram-md5 writev: %m");
809 #endif
810             return AUTH_NG;
811         }
812
813         if(cfg->timeout > 0) {
814             set_timeout(cfg->timeout);
815         }
816         rc = socket_read(sock, rbuf, sizeof(rbuf));
817         if(cfg->timeout > 0) {
818             set_timeout(0);
819         }
820         if(rc == -1) {
821 #ifdef DEBUG
822             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
823 #endif
824             return AUTH_NG;
825         }
826
827 #ifdef DEBUG
828         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
829 #endif
830         if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
831 #ifdef DEBUG
832             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
833 #endif
834             return AUTH_NG;
835         }
836         free(response64);
837     } else {
838 #ifdef DEBUG
839         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems cram-md5 mech is not implemented.");
840 #endif
841         return AUTH_NG;
842     }
843     return AUTH_OK;
844 }
845
846
847 int
848 auth_login(socket_t *sock, config_t *cfg) {
849
850     struct iovec iov[3];
851     int rc;
852     char rbuf[RESP_LEN];
853     //char buf[RESP_LEN];
854     char *buf;
855
856 #ifdef DEBUG
857     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH LOGIN");
858 #endif
859     iov[0].iov_base = AUTH_CMD;
860     iov[0].iov_len  = sizeof(AUTH_CMD) - 1;
861     iov[1].iov_base = "LOGIN";
862     iov[1].iov_len  = strlen("LOGIN");
863     iov[2].iov_base = "\r\n";
864     iov[2].iov_len  = sizeof("\r\n") - 1;
865
866 #ifdef DEBUG
867     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "LOGIN");
868 #endif
869     if(cfg->timeout > 0) {
870         set_timeout(cfg->timeout);
871     }
872     rc = retry_writev(sock, iov, 3);
873     memset(iov, 0, sizeof(iov));
874     if(cfg->timeout > 0) {
875         set_timeout(0);
876     }
877     if(rc == -1) {
878 #ifdef DEBUG
879         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
880 #endif
881         return AUTH_NG;
882     }
883
884     if(cfg->timeout > 0) {
885         set_timeout(cfg->timeout);
886     }
887     rc = socket_read(sock, rbuf, sizeof(rbuf));
888     if(cfg->timeout > 0) {
889         set_timeout(0);
890     }
891     if(rc == -1) {
892 #ifdef DEBUG
893         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
894 #endif
895         return AUTH_NG;
896     }
897
898 #ifdef DEBUG
899     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
900 #endif
901     if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
902         buf = malloc(sizeof(char)*128);
903         base64_encode(buf, cfg->username, strlen(cfg->username));
904
905         iov[0].iov_base = buf;
906         iov[0].iov_len  = strlen(buf);
907         iov[1].iov_base = "\r\n";
908         iov[1].iov_len  = sizeof("\r\n") - 1;
909
910 #ifdef DEBUG
911         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
912 #endif
913         if(cfg->timeout > 0) {
914             set_timeout(cfg->timeout);
915         }
916         rc = retry_writev(sock, iov, 2);
917         memset(iov, 0, sizeof(iov));
918         if(cfg->timeout > 0) {
919             set_timeout(0);
920         }
921         if(rc == -1) {
922 #ifdef DEBUG
923             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
924 #endif
925             return AUTH_NG;
926         }
927
928         if(cfg->timeout > 0) {
929             set_timeout(cfg->timeout);
930         }
931         rc = socket_read(sock, rbuf, sizeof(rbuf));
932         if(cfg->timeout > 0) {
933             set_timeout(0);
934         }
935         if(rc == -1) {
936 #ifdef DEBUG
937             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
938 #endif
939             return AUTH_NG;
940         }
941
942 #ifdef DEBUG
943         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
944 #endif
945         if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
946             buf = malloc(sizeof(char)*128);
947             base64_encode(buf, cfg->password, strlen(cfg->password));
948
949             iov[0].iov_base = buf;
950             iov[0].iov_len  = strlen(buf);
951             iov[1].iov_base = "\r\n";
952             iov[1].iov_len  = sizeof("\r\n") - 1;
953
954 #ifdef DEBUG
955             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
956 #endif
957             if(cfg->timeout > 0) {
958                 set_timeout(cfg->timeout);
959             }
960             rc = retry_writev(sock, iov, 2);
961             memset(iov, 0, sizeof(iov));
962             if(cfg->timeout > 0) {
963                 set_timeout(0);
964             }
965             if(rc == -1) {
966 #ifdef DEBUG
967                 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
968 #endif
969                 return AUTH_NG;
970             }
971
972             if(cfg->timeout > 0) {
973                 set_timeout(cfg->timeout);
974             }
975             rc = socket_read(sock, rbuf, sizeof(rbuf));
976             if(cfg->timeout > 0) {
977                 set_timeout(0);
978             }
979             if(rc == -1) {
980 #ifdef DEBUG
981                 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
982 #endif
983                 return AUTH_NG;
984             }
985
986 #ifdef DEBUG
987             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
988 #endif
989             if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
990 #ifdef DEBUG
991                 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
992 #endif
993                 return AUTH_NG;
994             }
995         } else {
996 #ifdef DEBUG
997             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems login mech is not implemented.");
998 #endif
999             return AUTH_NG;
1000         }
1001     } else {
1002 #ifdef DEBUG
1003         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems login mech is not implemented.");
1004 #endif
1005         return AUTH_NG;
1006     }
1007     return AUTH_OK;
1008 }
1009
1010
1011 int
1012 auth_plain(socket_t *sock, config_t *cfg) {
1013
1014     struct iovec iov[3];
1015     int rc;
1016     char rbuf[RESP_LEN];
1017     //char buf[RESP_LEN];
1018     char *buf;
1019     int cnt, len;
1020     char phrase[512];
1021
1022 #ifdef DEBUG
1023     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH PLAIN");
1024 #endif
1025     sprintf(phrase,"%s^%s^%s", cfg->username, cfg->username, cfg->password);
1026     len = strlen(phrase);
1027     for(cnt=len-1; cnt>=0; cnt--) {
1028         if(phrase[cnt] == '^') {
1029             phrase[cnt] = '\0';
1030         }
1031     }
1032     buf = malloc(sizeof(char)*128);
1033     base64_encode(buf, phrase, len);
1034
1035     iov[0].iov_base = AUTH_CMD;
1036     iov[0].iov_len  = sizeof(AUTH_CMD) - 1;
1037     iov[1].iov_base = "PLAIN ";
1038     iov[1].iov_len  = strlen("PLAIN ");
1039     iov[2].iov_base = buf;
1040     iov[2].iov_len  = strlen(buf);
1041     iov[3].iov_base = "\r\n";
1042     iov[3].iov_len  = sizeof("\r\n") - 1;
1043
1044 #ifdef DEBUG
1045     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s %s", AUTH_CMD, "PLAIN", buf);
1046 #endif
1047     if(cfg->timeout > 0) {
1048         set_timeout(cfg->timeout);
1049     }
1050     rc = retry_writev(sock, iov, 4);
1051     memset(iov, 0, sizeof(iov));
1052     free(buf);
1053     if(cfg->timeout > 0) {
1054         set_timeout(0);
1055     }
1056     if(rc == -1) {
1057 #ifdef DEBUG
1058         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): plain writev: %m");
1059 #endif
1060         return AUTH_NG;
1061     }
1062
1063     if(cfg->timeout > 0) {
1064         set_timeout(cfg->timeout);
1065     }
1066     rc = socket_read(sock, rbuf, sizeof(rbuf));
1067     if(cfg->timeout > 0) {
1068         set_timeout(0);
1069     }
1070     if(rc == -1) {
1071 #ifdef DEBUG
1072         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
1073 #endif
1074         return AUTH_NG;
1075     }
1076
1077     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
1078     if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
1079 #ifdef DEBUG
1080         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
1081 #endif
1082         return AUTH_NG;
1083     }
1084     return AUTH_OK;
1085 }
1086
1087
1088 int
1089 auth_digest_md5(socket_t *sock, config_t *cfg) {
1090
1091     struct iovec iov[3];
1092     int rc;
1093     char rbuf[RESP_LEN];
1094     char *buf;
1095
1096 #ifdef DEBUG
1097     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH DIGEST-MD5");
1098 #endif
1099
1100     iov[0].iov_base = AUTH_CMD;
1101     iov[0].iov_len  = sizeof(AUTH_CMD) - 1;
1102     iov[1].iov_base = "DIGEST-MD5";
1103     iov[1].iov_len  = strlen("DIGEST-MD5");
1104     iov[2].iov_base = "\r\n";
1105     iov[2].iov_len  = sizeof("\r\n") - 1;
1106
1107 #ifdef DEBUG
1108     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "DIGEST-MD5");
1109 #endif
1110     if(cfg->timeout > 0) {
1111         set_timeout(cfg->timeout);
1112     }
1113     rc = retry_writev(sock, iov, 3);
1114     memset(iov, 0, sizeof(iov));
1115     if(cfg->timeout > 0) {
1116         set_timeout(0);
1117     }
1118     if(rc == -1) {
1119 #ifdef DEBUG
1120         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): digest-md5 writev: %m");
1121 #endif
1122         return AUTH_NG;
1123     }
1124
1125     if(cfg->timeout > 0) {
1126         set_timeout(cfg->timeout);
1127     }
1128     rc = socket_read(sock, rbuf, sizeof(rbuf));
1129     if(cfg->timeout > 0) {
1130         set_timeout(0);
1131     }
1132     if(rc == -1) {
1133 #ifdef DEBUG
1134         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
1135 #endif
1136         return AUTH_NG;
1137     }
1138
1139 #ifdef DEBUG
1140     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
1141 #endif
1142     if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
1143         char *response;
1144         char *response64;
1145         char *challenge;
1146         int challengelen;
1147         unsigned char hexdigest[256];
1148
1149         challenge = malloc(strlen(rbuf + 4) + 1);
1150         challengelen = base64_decode(challenge, rbuf + 4, -1);
1151         challenge[challengelen] = '\0';
1152 #ifdef DEBUG
1153         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): challenge=%s", challenge);
1154 #endif
1155
1156         digest_md5((char *)hexdigest, (unsigned char*)challenge, challengelen, cfg->username, cfg->password);
1157 #ifdef DEBUG
1158         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): hexdigest=%s", hexdigest);
1159 #endif
1160
1161         response = malloc(sizeof(char)*256);
1162         snprintf(response, 256, "%s", hexdigest);
1163 #ifdef DEBUG
1164         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): response=%s", response);
1165 #endif
1166         response64 = malloc((strlen(response) + 3) * 2 + 1);
1167         base64_encode(response64, response, strlen(response));
1168         free(response);
1169 #ifdef DEBUG
1170         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): response64=%s", response64);
1171 #endif
1172
1173         iov[0].iov_base = response64;
1174         iov[0].iov_len  = strlen(response64);
1175         iov[1].iov_base = "\r\n";
1176         iov[1].iov_len  = sizeof("\r\n") - 1;
1177
1178 #ifdef DEBUG
1179         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", response64);
1180 #endif
1181         if(cfg->timeout > 0) {
1182             set_timeout(cfg->timeout);
1183         }
1184         rc = retry_writev(sock, iov, 2);
1185         memset(iov, 0, sizeof(iov));
1186         if(cfg->timeout > 0) {
1187             set_timeout(0);
1188         }
1189         if(rc == -1) {
1190 #ifdef DEBUG
1191             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): digest-md5 writev: %m");
1192 #endif
1193             return AUTH_NG;
1194         }
1195
1196         if(cfg->timeout > 0) {
1197             set_timeout(cfg->timeout);
1198         }
1199         rc = socket_read(sock, rbuf, sizeof(rbuf));
1200         if(cfg->timeout > 0) {
1201             set_timeout(0);
1202         }
1203         if(rc == -1) {
1204 #ifdef DEBUG
1205             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
1206 #endif
1207             return AUTH_NG;
1208         }
1209
1210 #ifdef DEBUG
1211         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
1212 #endif
1213         if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
1214             int buflen;
1215
1216             buf = malloc(strlen(rbuf + 4) + 1);
1217             buflen = base64_decode(buf, rbuf + 4, -1);
1218             buf[buflen] = '\0';
1219
1220             iov[0].iov_base = buf;
1221             iov[0].iov_len  = strlen(buf);
1222             iov[1].iov_base = "\r\n";
1223             iov[1].iov_len  = sizeof("\r\n") - 1;
1224
1225 #ifdef DEBUG
1226             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
1227 #endif
1228             if(cfg->timeout > 0) {
1229                 set_timeout(cfg->timeout);
1230             }
1231             rc = retry_writev(sock, iov, 2);
1232             memset(iov, 0, sizeof(iov));
1233             if(cfg->timeout > 0) {
1234                 set_timeout(0);
1235             }
1236             if(rc == -1) {
1237 #ifdef DEBUG
1238                 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): digest-md5 writev: %m");
1239 #endif
1240                 return AUTH_NG;
1241             }
1242
1243             if(cfg->timeout > 0) {
1244                 set_timeout(cfg->timeout);
1245             }
1246             rc = socket_read(sock, rbuf, sizeof(rbuf));
1247             if(cfg->timeout > 0) {
1248                 set_timeout(0);
1249             }
1250             if(rc == -1) {
1251 #ifdef DEBUG
1252                 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
1253 #endif
1254                 return AUTH_NG;
1255             }
1256
1257 #ifdef DEBUG
1258             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
1259 #endif
1260             if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
1261 #ifdef DEBUG
1262                 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
1263 #endif
1264                 return AUTH_NG;
1265             }
1266         } else {
1267 #ifdef DEBUG
1268             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems digest-md5 mech is not implemented.");
1269 #endif
1270             return AUTH_NG;
1271         }
1272         free(response64);
1273     } else {
1274 #ifdef DEBUG
1275         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems digest-md5 mech is not implemented.");
1276 #endif
1277         return AUTH_NG;
1278     }
1279     return AUTH_OK;
1280 }
1281
1282 #ifdef USE_SSL
1283 SSL_CTX *SSLContext = 0;
1284
1285 #ifdef VERYIFY_CERT
1286 static int
1287 verify_cert(SSL *ssl) {
1288
1289     X509 *cert;
1290     int err;
1291     char buf[256];
1292     int ret = -1;
1293     BIO *bio;
1294
1295     cert = SSL_get_peer_certificate(ssl);
1296     if(!cert) {
1297 #ifdef DEBUG
1298         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: no server certificate.");
1299 #endif
1300         return -1;
1301     }
1302
1303     err = SSL_get_verify_result(ssl);
1304     if(err == X509_V_OK) {
1305         return 0;
1306     }
1307
1308 #ifdef DEBUG
1309     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error:  can't verify certificate: %s (%d).",
1310              X509_verify_cert_error_string(err), err);
1311 #endif
1312     X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
1313     fprintf(stderr,"\nSubject: %s\n", buf);
1314     X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
1315     fprintf(stderr,"Issuer:  %s\n", buf);
1316     bio = BIO_new(BIO_s_mem());
1317     ASN1_TIME_print(bio, X509_get_notBefore(cert));
1318     memset(buf, 0, sizeof(buf));
1319     BIO_read(bio, buf, sizeof(buf) - 1);
1320     fprintf(stderr,"Valid from: %s\n", buf);
1321     ASN1_TIME_print(bio, X509_get_notAfter(cert));
1322     memset(buf, 0, sizeof(buf));
1323     BIO_read(bio, buf, sizeof(buf) - 1);
1324     BIO_free(bio);
1325     fprintf(stderr,"      to:   %s\n", buf);
1326
1327     fprintf(stderr, 
1328         "\nThere is no way to verify this certificate.\n"
1329          " It is possible that a hostile attacker has replaced the server certificate.\n"
1330          " Continue at your own risk!\n"
1331          "\nAccept this certificate anyway? [no]: ");
1332     if(fgets(buf, sizeof(buf), stdin) && (buf[0] == 'y' || buf[0] == 'Y')) {
1333         ret = 0;
1334         fprintf(stderr, "\nFine, but don't say I didn't warn you!\n\n");
1335     }
1336     return ret;
1337 }
1338 #endif
1339
1340 static int
1341 init_ssl(config_t *conf) {
1342
1343     SSL_METHOD *method;
1344     int options = 0;
1345
1346     if(!conf->certfile) {
1347 #ifdef DEBUG
1348         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSLCertificateFile not defined.");
1349 #endif
1350         return -1;
1351     }
1352     SSL_load_error_strings();
1353     SSL_library_init();
1354     if(conf->use_tlsv1 && !conf->use_sslv2 && !conf->use_sslv3)
1355         method = TLSv1_client_method();
1356     else
1357         method = SSLv23_client_method();
1358
1359     SSLContext = SSL_CTX_new(method);
1360
1361     if(access(conf->certfile, F_OK)) {
1362         if(errno != ENOENT) {
1363 #ifdef DEBUG
1364             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSLCertificateFile is not accessible.");
1365 #endif
1366             return -1;
1367         }
1368 #ifdef DEBUG
1369         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Warning: SSLCertificateFile doesn't exist, can't verify server certificates.");
1370 #endif
1371     } else if(!SSL_CTX_load_verify_locations(SSLContext, conf->certfile, NULL)) {
1372 #ifdef DEBUG
1373         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSL_CTX_load_verify_locations: %s.",ERR_error_string(ERR_get_error(), 0));
1374 #endif
1375         SSL_CTX_free(SSLContext);
1376         return -1;
1377     }
1378
1379     if(!conf->use_sslv2) {
1380         options |= SSL_OP_NO_SSLv2;
1381     }
1382     if(!conf->use_sslv3) {
1383         options |= SSL_OP_NO_SSLv3;
1384     }
1385     if(!conf->use_tlsv1) {
1386         options |= SSL_OP_NO_TLSv1;
1387     }
1388
1389     SSL_CTX_set_options(SSLContext, options);
1390
1391     /* we check the result of the verification after SSL_connect() */
1392     SSL_CTX_set_verify(SSLContext, SSL_VERIFY_NONE, 0);
1393     return 0;
1394 }
1395
1396 int
1397 start_tls(smtp_t *smtp, config_t *cfg) {
1398
1399     int ret;
1400     /* initialize SSL */
1401     if(init_ssl(cfg)) {
1402 #ifdef DEBUG
1403         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): failed to initialize ssl session.");
1404 #endif
1405         return 1;
1406     }
1407
1408     smtp->sock->ssl = SSL_new(SSLContext);
1409     SSL_set_fd(smtp->sock->ssl, smtp->sock->fd);
1410     if((ret = SSL_connect(smtp->sock->ssl)) <= 0) {
1411         socket_perror("connect", smtp->sock, ret);
1412 #ifdef DEBUG
1413         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): failed to connect ssl session.");
1414 #endif
1415         return 1;
1416     }
1417 #ifdef VERIFY_CERT
1418     /* verify the server certificate */
1419     if(verify_cert(smtp->sock->ssl)) {
1420         return 1;
1421     }
1422 #endif
1423     smtp->sock->use_ssl = 1;
1424 #ifdef DEBUG
1425     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): SSL support enabled.");
1426 #endif
1427     return 0;
1428 }
1429 #endif
1430