OSDN Git Service

append DEBUG macro.
[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 #include <stdio.h>
22 #include <unistd.h>
23 #include <assert.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <syslog.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/utsname.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <signal.h>
33 #include <netdb.h>
34 #include <sys/ioctl.h>
35 #include <net/if.h>
36
37 #include "global.h"
38 #include "md5.h"
39 #include "smtpauth.h"
40
41 #define EHLO_CMD ("EHLO ")      /* ESMTP ehlo command */
42 #define AUTH_CMD ("AUTH ")      /* ESMTP auth command */
43 #define QUIT_CMD ("QUIT ")      /* ESMTP quit command */
44
45 #define RESP_LEN 1000
46 #define RESP_IERROR      "internal error"
47 #define RESP_UNAVAILABLE "remote authentication server is currently unavailable"
48 #define RESP_UNEXPECTED  "unexpected response from remote authentication server"
49 #define RESP_SYNCERROR   "error synchronizing with remote authentication server"
50 #define RESP_CREDERROR   "remote authentication server rejected your credentials"
51
52 #define AUTH_NG 0
53 #define AUTH_OK 1
54
55 #define AUTH_PLAIN      1 << 0
56 #define AUTH_LOGIN      1 << 1
57 #define AUTH_CRAM_MD5   1 << 2
58 #define AUTH_DIGEST_MD5 1 << 3
59
60 #define B64(c)  (isascii(c) ? base64val[(int)(c)] : -1)
61
62 void
63 md5_hex_hmac(char *hexdigest, unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len);
64 void
65 hmac_md5(unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len, unsigned char *digest);
66
67 config_t global;
68 static const char base64char[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
69 static const char base64val[128] = {
70     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
71     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
72     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
73     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
74     -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
75     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
76     -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
77     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
78 };
79
80
81 void
82 md5_hex_hmac(char *hexdigest, unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len) {
83
84     unsigned char digest[16];
85     int cnt;
86
87     hmac_md5(text, text_len, key, key_len, digest);
88     for(cnt=0; cnt<16; cnt++) {
89         sprintf(hexdigest + 2 * cnt, "%02x", digest[cnt]);
90     }
91 }
92
93
94 void
95 hmac_md5(unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len, unsigned char *digest) {
96
97     MD5_CTX context;
98     unsigned char k_ipad[64];
99     unsigned char k_opad[64];
100     int cnt;
101
102     memset(k_ipad, 0, sizeof k_ipad);
103     memset(k_opad, 0, sizeof k_opad);
104     if(key_len > 64) {
105         MD5_CTX tctx;
106
107         MD5Init(&tctx);
108         MD5Update(&tctx, key, key_len);
109         MD5Final(k_ipad, &tctx);
110         MD5Final(k_opad, &tctx);
111     } else {
112         memcpy(k_ipad, key, key_len);
113         memcpy(k_opad, key, key_len);
114     }
115
116     for(cnt=0; cnt<64; cnt++) {
117         k_ipad[cnt] ^= 0x36;
118         k_opad[cnt] ^= 0x5c;
119     }
120
121     MD5Init(&context);
122     MD5Update(&context, k_ipad, 64);
123     MD5Update(&context, text, text_len);
124     MD5Final(digest, &context);
125
126     MD5Init(&context);
127     MD5Update(&context, k_opad, 64);
128     MD5Update(&context, digest, 16);
129     MD5Final(digest, &context);
130 }
131
132
133 void
134 base64_encode(char *out, const char *in, int inlen) {
135
136     const char *inp = in;
137     char *outp = out;
138
139     while(inlen >= 3) {
140         *outp++ = base64char[(inp[0] >> 2) & 0x3f];
141         *outp++ = base64char[((inp[0] & 0x03) << 4) | ((inp[1] >> 4) & 0x0f)];
142         *outp++ = base64char[((inp[1] & 0x0f) << 2) | ((inp[2] >> 6) & 0x03)];
143         *outp++ = base64char[inp[2] & 0x3f];
144         inp += 3;
145         inlen -= 3;
146     }
147     if(inlen > 0) {
148         *outp++ = base64char[(inp[0] >> 2) & 0x3f];
149         if(inlen == 1) {
150             *outp++ = base64char[(inp[0] & 0x03) << 4];
151             *outp++ = '=';
152         }
153         else {
154             *outp++ = base64char[((inp[0] & 0x03) << 4) | ((inp[1] >> 4) & 0x0f)];
155             *outp++ = base64char[((inp[1] & 0x0f) << 2)];
156         }
157         *outp++ = '=';
158     }
159     *outp = '\0';
160 }
161
162
163 int
164 base64_decode(char *out, const char *in, int inlen) {
165
166     const char *inp = in;
167     char *outp = out;
168     char buf[4];
169
170     if( inlen < 0) {
171         inlen = 2100000000;
172     }
173     while(inlen >=4 && *inp != '\0') {
174         buf[0] = *inp++;
175         inlen--;
176         if( B64(buf[0]) == -1) break;
177
178         buf[1] = *inp++;
179         inlen--;
180         if(B64(buf[1]) == -1) break;
181
182         buf[2] = *inp++;
183         inlen--;
184         if(buf[2] != '=' && B64(buf[2]) == -1) break;
185
186         buf[3] = *inp++;
187         inlen--;
188         if(buf[3] != '=' && B64(buf[3]) ==  -1) break;
189
190         *outp++ = ((B64(buf[0]) << 2) & 0xfc) | ((B64(buf[1]) >> 4) & 0x03);
191         if(buf[2] != '=') {
192            *outp++ = ((B64(buf[1]) & 0x0f) << 4) | ((B64(buf[2]) >> 2) & 0x0f);
193            if(buf[3] != '=') {
194                 *outp++ = ((B64(buf[2]) & 0x03) << 6) | (B64(buf[3]) & 0x3f);
195            }
196         }
197     }
198     return outp - out;
199 }
200
201
202 int
203 retry_writev(int fd, struct iovec *iov, int iovcnt) {
204
205     int n;
206     int cnt;
207     int written;
208     static int iov_max;
209
210     iov_max = 8192;
211     written = 0;
212
213     for(;;) {
214         while(iovcnt && iov[0].iov_len == 0) {
215             iov++;
216             iovcnt--;
217         }
218         if(!iovcnt) {
219             return written;
220         }
221         n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
222         if (n == -1) {
223             if(errno == EINVAL && iov_max > 10) {
224                 iov_max /= 2;
225                 continue;
226             }
227             if(errno == EINTR) {
228                 continue;
229             }
230             return -1;
231         } else {
232             written += n;
233         }
234
235         for(cnt=0; cnt<iovcnt; cnt++) {
236             if((int)iov[cnt].iov_len > n) {
237                 iov[cnt].iov_base = (char *)iov[cnt].iov_base + n;
238                 iov[cnt].iov_len -= n;
239                 break;
240             }
241             n -= iov[cnt].iov_len;
242             iov[cnt].iov_len = 0;
243         }
244
245         if(cnt == iovcnt) {
246             return written;
247         }
248     }
249 }
250
251
252 smtp_t *
253 smtp_auth (config_t * cfg) {
254
255     int s;
256     struct sockaddr_in addr;
257     struct hostent *he;
258     smtp_t *smtp;
259     char msgbuf[256];
260
261     struct iovec iov[5];
262     char *c;
263     int rc;
264     char rbuf[RESP_LEN];
265     int auth = 0;
266     int avail_auth_type = 0;
267     char *tbuf;
268     struct utsname  h_name[1];
269     char *myhostname;
270
271     int                n;
272     struct sockaddr_in taddr;
273     int                sd;
274     struct ifconf      ifconf;
275     struct ifreq       *ifr, ifreq;
276     unsigned char      *ifptr;
277     int                iflen;
278
279
280     if(!cfg->password) {
281         if(!global.password) {
282             global.password = getpass("Password:");
283             if(!global.password) {
284                 return 0;
285             }
286             if(!*global.password) {
287                 global.password = NULL;
288                 goto bail;
289             }
290             global.password = strdup(global.password);
291         }
292         cfg->password = strdup(global.password);
293     }
294
295     assert(cfg->username != NULL);
296     assert(cfg->password != NULL);
297
298     smtp = calloc(1, sizeof (smtp_t));
299     smtp->sock = calloc(1, sizeof (socket_t));
300     smtp->buf = calloc(1, sizeof (buffer_t));
301     smtp->buf->sock = smtp->sock;
302     smtp->sock->fd = -1;
303     smtp->error = 0;
304
305     /* open connection to SMTP server */
306     memset(&addr, 0, sizeof (addr));
307     addr.sin_port = htons(cfg->port);
308     addr.sin_family = AF_INET;
309     he = gethostbyname(cfg->host);
310     if(!he) {
311         smtp->error = 1;
312         strcpy(msgbuf, "Error: resolving hostname ");
313         strcat(msgbuf, cfg->host);
314         smtp->error_message = malloc(strlen(msgbuf) + 1);
315         strcpy(smtp->error_message, msgbuf);
316         goto bail;
317     }
318
319     if((sd = socket(PF_INET, SOCK_DGRAM, 0)) != -1) {
320         bzero(&ifconf, sizeof(struct ifconf));
321         bzero(&ifreq, sizeof(struct ifreq));
322         iflen = 10 * sizeof(struct ifreq);
323         ifptr = malloc(iflen);
324         ifconf.ifc_len = iflen;
325         ifconf.ifc_ifcu.ifcu_req = (struct ifreq *)ifptr;
326         if(ioctl(sd, SIOCGIFCONF, &ifconf) != -1) {
327             for(iflen=sizeof(struct ifreq); iflen<=ifconf.ifc_len; iflen+=sizeof(struct ifreq)) {
328                 ifr = (struct ifreq *)ifptr;
329                 strcpy(ifreq.ifr_ifrn.ifrn_name, ifr->ifr_name);
330                 if(ioctl(sd, SIOCGIFADDR, &ifreq) != -1) {
331                     n = 0;
332                     while(he->h_addr_list[n]) {
333                         if(he->h_addrtype == AF_INET) {
334                             memset((char*)&taddr, 0, sizeof(taddr));
335                             memcpy((char*)&taddr.sin_addr, he->h_addr_list[n], he->h_length);
336 #ifdef DEBUG
337                             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): my ip: %s",
338                               inet_ntoa(((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr));
339                             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp ip: %s",
340                               inet_ntoa(taddr.sin_addr));
341 #endif
342                             if(((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr == taddr.sin_addr.s_addr) {
343                                 smtp->error = 1;
344                                 strcpy(msgbuf, "Error: this host is specified. ");
345                                 strcat(msgbuf, inet_ntoa(taddr.sin_addr));
346                                 smtp->error_message = malloc(strlen(msgbuf) + 1);
347                                 strcpy(smtp->error_message, msgbuf);
348                                 goto bail;
349                             }
350                         }
351                         n++;
352                     }
353                 }
354                 ifptr += sizeof(struct ifreq);
355             }
356         }
357     }
358
359
360     addr.sin_addr.s_addr = *((int *)he->h_addr_list[0]);
361     s = socket(PF_INET, SOCK_STREAM, 0);
362     if(connect (s, (struct sockaddr *) &addr, sizeof (addr))) {
363         smtp->error = 1;
364         strcpy(msgbuf, "Error: connecting to ");
365         strcat(msgbuf, inet_ntoa(addr.sin_addr));
366         smtp->error_message = malloc(strlen(msgbuf) + 1);
367         strcpy(smtp->error_message, msgbuf);
368         goto bail;
369     }
370     smtp->sock->fd = s;
371
372     /* CLAIM: we now have a TCP connection to the remote SMTP server */
373     alarm(cfg->timeout);
374     rc = read(s, rbuf, sizeof(rbuf));
375     alarm(0);
376     if (rc == -1) {
377 #ifdef DEBUG
378         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (banner): %m");
379 #endif
380         smtp->error = 1;
381         strcpy(msgbuf, RESP_SYNCERROR);
382         smtp->error_message = malloc(strlen(msgbuf) + 1);
383         strcpy(smtp->error_message, msgbuf);
384         goto bail;
385     }
386     rbuf[rc] = '\0';
387     c = strpbrk(rbuf, "\r\n");
388     if(c != NULL) {
389         *c = '\0';
390     }
391
392     if(strncmp(rbuf, "220 ", sizeof("220 ")-1)) {
393 #ifdef DEBUG
394         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): unexpected response during initial handshake: %s", rbuf);
395 #endif
396         smtp->error = 1;
397         strcpy(msgbuf, RESP_UNEXPECTED);
398         smtp->error_message = malloc(strlen(msgbuf) + 1);
399         strcpy(smtp->error_message, msgbuf);
400         goto bail;
401     }
402
403     if((uname(h_name)) < 0){
404         myhostname = "localhost.localdomain";
405     } else {
406         myhostname = h_name->nodename;
407     }
408
409     iov[0].iov_base = EHLO_CMD;
410     iov[0].iov_len  = sizeof(EHLO_CMD) - 1;
411     iov[1].iov_base = myhostname;
412     iov[1].iov_len  = strlen(myhostname);
413     iov[2].iov_base = "\r\n";
414     iov[2].iov_len  = sizeof("\r\n") - 1;
415
416 #ifdef DEBUG
417     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", EHLO_CMD, myhostname);
418 #endif
419     alarm(cfg->timeout);
420     rc = retry_writev(s, iov, 3);
421     memset(iov, 0, sizeof(iov));
422     alarm(0);
423     if(rc == -1) {
424 #ifdef DEBUG
425         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): writev: %m");
426 #endif
427         smtp->error = 1;
428         strcpy(msgbuf, RESP_IERROR);
429         smtp->error_message = malloc(strlen(msgbuf) + 1);
430         strcpy(smtp->error_message, msgbuf);
431         goto bail;
432     }
433
434     /* read and parse the EHLO response */
435     alarm(cfg->timeout);
436     rc = read(s, rbuf, sizeof(rbuf));
437     alarm(0);
438     if(rc == -1) {
439 #ifdef DEBUG
440         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
441 #endif
442         smtp->error = 1;
443         strcpy(msgbuf, RESP_IERROR);
444         smtp->error_message = malloc(strlen(msgbuf) + 1);
445         strcpy(smtp->error_message, msgbuf);
446         goto bail;
447     }
448
449     if(tbuf = strstr(rbuf, "250-AUTH")) {
450         if(strncmp(tbuf, "250", sizeof("250")-1) == 0) {
451             char *p = tbuf;
452             p += 3;
453             if (*p == '-' || *p == ' ') p++;
454             if (strncasecmp(p, "AUTH", sizeof("AUTH")-1) == 0) {
455                 p += 5;
456                 if (strcasestr(p, "PLAIN"))
457                     avail_auth_type |= AUTH_PLAIN;
458                 if (strcasestr(p, "LOGIN"))
459                     avail_auth_type |= AUTH_LOGIN;
460                 if (strcasestr(p, "CRAM-MD5"))
461                     avail_auth_type |= AUTH_CRAM_MD5;
462                 if (strcasestr(p, "DIGEST-MD5"))
463                     avail_auth_type |= AUTH_DIGEST_MD5;
464             }
465         }
466     }
467
468     if (avail_auth_type == 0) {
469 #ifdef DEBUG
470         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp authentication is not implemented: %s", rbuf);
471 #endif
472         smtp->error = 1;
473         strcpy(msgbuf, RESP_UNEXPECTED);
474         smtp->error_message = malloc(strlen(msgbuf) + 1);
475         strcpy(smtp->error_message, msgbuf);
476         goto bail;
477     }
478 #ifdef DEBUG
479     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth_type: %d", avail_auth_type);
480 #endif
481
482     /* build the AUTH command */
483     if (avail_auth_type & AUTH_CRAM_MD5) {
484         auth = auth_cram_md5(s,&global);
485     }
486     else if ((avail_auth_type & AUTH_LOGIN) != 0) {
487         auth = auth_login(s,&global);
488     }
489     else if ((avail_auth_type & AUTH_PLAIN) != 0) {
490         auth = auth_plain(s,&global);
491     }
492     else {
493 #ifdef DEBUG
494         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp authentication is not implemented: %s", rbuf);
495 #endif
496         smtp->error = 1;
497         strcpy(msgbuf, RESP_UNEXPECTED);
498         smtp->error_message = malloc(strlen(msgbuf) + 1);
499         strcpy(smtp->error_message, msgbuf);
500         goto bail;
501     }
502
503 #ifdef DEBUG
504     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth) auth: [%d]", auth);
505 #endif
506     if(auth == 0) {
507 #ifdef DEBUG
508         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth) rejected: [%s]", global.username);
509 #endif
510         smtp->error = 2;
511         strcpy(msgbuf, RESP_CREDERROR);
512         smtp->error_message = malloc(strlen(msgbuf) + 1);
513         strcpy(smtp->error_message, msgbuf);
514         goto bail;
515     }
516
517     smtp_quit(s,&global);
518     return smtp;
519
520     bail:
521         smtp_quit(s,&global);
522         if(smtp->error == 1)
523             return smtp;
524         else if(smtp->error == 2)
525             return smtp;
526 }
527
528
529 int
530 smtp_quit(int s, config_t * cfg) {
531
532     struct iovec iov[3];
533     int rc;
534
535     iov[0].iov_base = QUIT_CMD;
536     iov[0].iov_len  = sizeof(QUIT_CMD) - 1;
537     iov[1].iov_base = "\r\n";
538     iov[1].iov_len  = sizeof("\r\n") - 1;
539
540 #ifdef DEBUG
541     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", QUIT_CMD);
542 #endif
543     alarm(cfg->timeout);
544     rc = retry_writev(s, iov, 2);
545     memset(iov, 0, sizeof(iov));
546     alarm(0);
547     if(rc == -1) {
548 #ifdef DEBUG
549         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): quit writev: %m");
550 #endif
551     }
552     (void)close(s);
553 }
554
555
556
557 int
558 auth_cram_md5(int s, config_t * cfg) {
559
560     struct iovec iov[3];
561     int rc;
562     char rbuf[RESP_LEN];
563     char buf[RESP_LEN];
564
565 #ifdef DEBUG
566     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH CRAM-MD5");
567 #endif
568     iov[0].iov_base = AUTH_CMD;
569     iov[0].iov_len  = sizeof(AUTH_CMD) - 1;
570     iov[1].iov_base = "CRAM-MD5";
571     iov[1].iov_len  = strlen("CRAM-MD5");
572     iov[2].iov_base = "\r\n";
573     iov[2].iov_len  = sizeof("\r\n") - 1;
574
575 #ifdef DEBUG
576     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "CRAM-MD5");
577 #endif
578     alarm(cfg->timeout);
579     rc = retry_writev(s, iov, 3);
580     memset(iov, 0, sizeof(iov));
581     alarm(0);
582     if(rc == -1) {
583 #ifdef DEBUG
584         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): cram-md5 writev: %m");
585 #endif
586         return AUTH_NG;
587     }
588
589     alarm(cfg->timeout);
590     rc = read(s, rbuf, sizeof(rbuf));
591     alarm(0);
592     if(rc == -1) {
593 #ifdef DEBUG
594         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
595 #endif
596         return AUTH_NG;
597     }
598
599 #ifdef DEBUG
600     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
601 #endif
602     if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
603         char *response;
604         char *response64;
605         char *challenge;
606         int challengelen;
607         unsigned char hexdigest[33];
608
609         challenge = malloc(strlen(rbuf + 4) + 1);
610         challengelen = base64_decode(challenge, rbuf + 4, -1);
611         challenge[challengelen] = '\0';
612 #ifdef DEBUG
613         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): challenge=%s", challenge);
614 #endif
615
616         snprintf(buf, sizeof(buf), "%s", cfg->password);
617         md5_hex_hmac(hexdigest, challenge, challengelen, buf, strlen(cfg->password));
618         free(challenge);
619
620         response = malloc(sizeof(char)*128);
621         sprintf(response, "%s %s", cfg->username, hexdigest);
622         response64 = malloc((strlen(response) + 3) * 2 + 1);
623         base64_encode(response64, response, strlen(response));
624         free(response);
625
626         iov[0].iov_base = response64;
627         iov[0].iov_len  = strlen(response64);
628         iov[1].iov_base = "\r\n";
629         iov[1].iov_len  = sizeof("\r\n") - 1;
630
631 #ifdef DEBUG
632         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", response64);
633 #endif
634         alarm(cfg->timeout);
635         rc = retry_writev(s, iov, 2);
636         memset(iov, 0, sizeof(iov));
637         alarm(0);
638         if(rc == -1) {
639 #ifdef DEBUG
640             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): cram-md5 writev: %m");
641 #endif
642             return AUTH_NG;
643         }
644
645         alarm(cfg->timeout);
646         rc = read(s, rbuf, sizeof(rbuf));
647         alarm(0);
648         if(rc == -1) {
649 #ifdef DEBUG
650             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
651 #endif
652             return AUTH_NG;
653         }
654
655 #ifdef DEBUG
656         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
657 #endif
658         if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
659 #ifdef DEBUG
660             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
661 #endif
662             return AUTH_NG;
663         }
664         free(response64);
665     } else {
666 #ifdef DEBUG
667         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems cram-md5 mech is not implemented.");
668 #endif
669         return AUTH_NG;
670     }
671     return AUTH_OK;
672 }
673
674
675 int
676 auth_login(int s, config_t * cfg) {
677
678     struct iovec iov[3];
679     int rc;
680     char rbuf[RESP_LEN];
681     //char buf[RESP_LEN];
682     char *buf;
683
684 #ifdef DEBUG
685     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH LOGIN");
686 #endif
687     iov[0].iov_base = AUTH_CMD;
688     iov[0].iov_len  = sizeof(AUTH_CMD) - 1;
689     iov[1].iov_base = "LOGIN";
690     iov[1].iov_len  = strlen("LOGIN");
691     iov[2].iov_base = "\r\n";
692     iov[2].iov_len  = sizeof("\r\n") - 1;
693
694 #ifdef DEBUG
695     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "LOGIN");
696 #endif
697     alarm(cfg->timeout);
698     rc = retry_writev(s, iov, 3);
699     memset(iov, 0, sizeof(iov));
700     alarm(0);
701     if(rc == -1) {
702 #ifdef DEBUG
703         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
704 #endif
705         return AUTH_NG;
706     }
707
708     alarm(cfg->timeout);
709     rc = read(s, rbuf, sizeof(rbuf));
710     alarm(0);
711     if(rc == -1) {
712 #ifdef DEBUG
713         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
714 #endif
715         return AUTH_NG;
716     }
717
718 #ifdef DEBUG
719     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
720 #endif
721     if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
722         buf = malloc(sizeof(char)*128);
723         base64_encode(buf, cfg->username, strlen(cfg->username));
724
725         iov[0].iov_base = buf;
726         iov[0].iov_len  = strlen(buf);
727         iov[1].iov_base = "\r\n";
728         iov[1].iov_len  = sizeof("\r\n") - 1;
729
730 #ifdef DEBUG
731         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
732 #endif
733         alarm(cfg->timeout);
734         rc = retry_writev(s, iov, 2);
735         memset(iov, 0, sizeof(iov));
736         alarm(0);
737         if(rc == -1) {
738 #ifdef DEBUG
739             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
740 #endif
741             return AUTH_NG;
742         }
743
744         alarm(cfg->timeout);
745         rc = read(s, rbuf, sizeof(rbuf));
746         alarm(0);
747         if(rc == -1) {
748 #ifdef DEBUG
749             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
750 #endif
751             return AUTH_NG;
752         }
753
754 #ifdef DEBUG
755         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
756 #endif
757         if (strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
758             buf = malloc(sizeof(char)*128);
759             base64_encode(buf, cfg->password, strlen(cfg->password));
760
761             iov[0].iov_base = buf;
762             iov[0].iov_len  = strlen(buf);
763             iov[1].iov_base = "\r\n";
764             iov[1].iov_len  = sizeof("\r\n") - 1;
765
766 #ifdef DEBUG
767             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
768 #endif
769             alarm(cfg->timeout);
770             rc = retry_writev(s, iov, 2);
771             memset(iov, 0, sizeof(iov));
772             alarm(0);
773             if(rc == -1) {
774 #ifdef DEBUG
775                 syslog(LOG_WARNING, "pam_smtpauth(smtpauth): login writev: %m");
776 #endif
777                 return AUTH_NG;
778             }
779
780             alarm(cfg->timeout);
781             rc = read(s, rbuf, sizeof(rbuf));
782             alarm(0);
783             if (rc == -1) {
784 #ifdef DEBUG
785                 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
786 #endif
787                 return AUTH_NG;
788             }
789
790 #ifdef DEBUG
791             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
792 #endif
793             if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
794 #ifdef DEBUG
795                 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
796 #endif
797                 return AUTH_NG;
798             }
799         } else {
800 #ifdef DEBUG
801             syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems login mech is not implemented.");
802 #endif
803             return AUTH_NG;
804         }
805     } else {
806 #ifdef DEBUG
807         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems login mech is not implemented.");
808 #endif
809         return AUTH_NG;
810     }
811     return AUTH_OK;
812 }
813
814
815 int
816 auth_plain(int s, config_t * cfg) {
817
818     struct iovec iov[3];
819     int rc;
820     char rbuf[RESP_LEN];
821     //char buf[RESP_LEN];
822     char *buf;
823     int cnt, len;
824     char phrase[512];
825
826 #ifdef DEBUG
827     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH PLAIN");
828 #endif
829     sprintf(phrase,"%s^%s^%s", cfg->username, cfg->username, cfg->password);
830     len = strlen(phrase);
831     for(cnt=len-1; cnt>=0; cnt--) {
832         if(phrase[cnt] == '^') {
833             phrase[cnt] = '\0';
834         }
835     }
836     buf = malloc(sizeof(char)*128);
837     base64_encode(buf, phrase, len);
838
839     iov[0].iov_base = AUTH_CMD;
840     iov[0].iov_len  = sizeof(AUTH_CMD) - 1;
841     iov[1].iov_base = "PLAIN ";
842     iov[1].iov_len  = strlen("PLAIN ");
843     iov[2].iov_base = buf;
844     iov[2].iov_len  = strlen(buf);
845     iov[3].iov_base = "\r\n";
846     iov[3].iov_len  = sizeof("\r\n") - 1;
847
848 #ifdef DEBUG
849     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s %s", AUTH_CMD, "PLAIN", buf);
850 #endif
851     alarm(cfg->timeout);
852     rc = retry_writev(s, iov, 4);
853     memset(iov, 0, sizeof(iov));
854     free(buf);
855     alarm(0);
856     if(rc == -1) {
857 #ifdef DEBUG
858         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): plain writev: %m");
859 #endif
860         return AUTH_NG;
861     }
862
863     alarm(cfg->timeout);
864     rc = read(s, rbuf, sizeof(rbuf));
865     alarm(0);
866     if(rc == -1) {
867 #ifdef DEBUG
868         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
869 #endif
870         return AUTH_NG;
871     }
872
873     syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
874     if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
875 #ifdef DEBUG
876         syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
877 #endif
878         return AUTH_NG;
879     }
880     return AUTH_OK;
881 }
882