3 * $Id: smtpauth.c,v 1.14 2009/06/12 08:55:50 taizo Exp $
4 * Copyright (C) 2009 HDE, Inc.
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)
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.
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.
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>
37 #include <sys/ioctl.h>
43 #include <openssl/err.h>
44 #include <openssl/md5.h>
50 #define EHLO_CMD ("EHLO ") /* ESMTP ehlo command */
51 #define AUTH_CMD ("AUTH ") /* ESMTP auth command */
52 #define QUIT_CMD ("QUIT ") /* ESMTP quit command */
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"
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
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
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);
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);
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);
96 bin2hex(char *out, const unsigned char *in, int in_len) {
97 static const char hex[17] = "0123456789abcdef";
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];
107 make_digest(char *md5str, unsigned char *digest) {
108 bin2hex(md5str, digest, 16);
113 md5_hex_hmac(char *hexdigest, unsigned char *text, unsigned int text_len, unsigned char *key, unsigned int key_len) {
115 unsigned char digest[16];
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]);
126 hmac_md5(unsigned char *text, unsigned int text_len, unsigned char *key, unsigned int key_len, unsigned char *digest) {
129 unsigned char k_ipad[64];
130 unsigned char k_opad[64];
133 memset(k_ipad, 0, sizeof(k_ipad));
134 memset(k_opad, 0, sizeof(k_opad));
140 MD5_Update(&tctx, key, key_len);
141 MD5_Final(k_ipad, &tctx);
142 MD5_Final(k_opad, &tctx);
145 MD5Update(&tctx, key, key_len);
146 MD5Final(k_ipad, &tctx);
147 MD5Final(k_opad, &tctx);
150 memcpy(k_ipad, key, key_len);
151 memcpy(k_opad, key, key_len);
154 for(cnt=0; cnt<64; cnt++) {
161 MD5_Update(&context, k_ipad, 64);
162 MD5_Update(&context, text, text_len);
163 MD5_Final(digest, &context);
166 MD5_Update(&context, k_opad, 64);
167 MD5_Update(&context, digest, 16);
168 MD5_Final(digest, &context);
171 MD5Update(&context, k_ipad, 64);
172 MD5Update(&context, text, text_len);
173 MD5Final(digest, &context);
176 MD5Update(&context, k_opad, 64);
177 MD5Update(&context, digest, 16);
178 MD5Final(digest, &context);
184 extract_token(const char *str, const char *token, char *value, int len) {
186 char *p = NULL, *q = NULL;
188 memset(value,0x00,sizeof(char)*len);
189 if((p = strstr(str, token)) != NULL) {
192 if((q = strchr(p + 1, '\"')) == NULL)
194 strncpy(value, p + 1, q - p - 1 >= len ? len - 1 : q - p - 1);
197 if((q = strchr(p, ',')) == NULL)
199 strncpy(value, p, q - p >= len ? len - 1 : q - p);
203 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): str=%s", str);
210 digest_md5(char *response, unsigned char *text, unsigned int text_len, const char *login, const char *passwd) {
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];
218 unsigned char random[16];
221 unsigned char digest[16];
222 char hexA1[33], hexA2[33], resp[33];
226 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): text=%s", text);
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);
234 snprintf((char *)random, sizeof(random), "%ld", (long int)rand());
235 bin2hex(cnonce, random, 8);
238 sprintf(uri, "smtp/%s", realm);
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);
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);
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);
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);
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);
283 make_digest(hexA1, digest);
286 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): A1=%s", hexA1);
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);
297 MD5_Final(digest, &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);
305 MD5Final(digest, &ctx);
307 make_digest(hexA2, digest);
310 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): A2=%s", hexA2);
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);
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);
340 make_digest(resp, digest);
343 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): resp=%s", resp);
346 sprintf(response, "charset=utf-8,username=\"%s\",realm=\"%s\",nonce=\"%s\","
347 "nc=00000001,cnonce=\"%s\",digest-uri=\"%s\",qop=%s,"
349 login, realm, nonce, cnonce, uri, qop, resp);
352 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): response:%s", response);
353 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): text:%s", text);
359 smtp_auth(config_t *cfg) {
362 struct sockaddr_in addr;
372 int avail_auth_type = 0;
374 struct utsname h_name[1];
378 struct sockaddr_in taddr;
380 struct ifconf ifconf;
381 struct ifreq *ifr, ifreq;
382 unsigned char *ifptr;
389 if(!global.password) {
390 global.password = getpass("Password:");
391 if(!global.password) {
394 if(!*global.password) {
395 global.password = NULL;
398 global.password = strdup(global.password);
400 cfg->password = strdup(global.password);
403 assert(cfg->username != NULL);
404 assert(cfg->password != NULL);
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;
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);
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);
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) {
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);
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));
450 if(((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr == taddr.sin_addr.s_addr) {
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);
462 ifptr += sizeof(struct ifreq);
466 addr.sin_addr.s_addr = *((int *)he->h_addr_list[0]);
467 s = socket(PF_INET, SOCK_STREAM, 0);
469 if(cfg->conn_timeout > 0) {
470 set_timeout(cfg->conn_timeout);
472 if(connect(s, (struct sockaddr *) &addr, sizeof(addr))) {
474 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): connection error = %s",strerror(errno));
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);
484 if(cfg->conn_timeout > 0) {
491 if(start_tls(smtp, cfg)) {
493 strcpy(msgbuf, "Error: start_tls");
494 smtp->error_message = malloc(strlen(msgbuf) + 1);
495 strcpy(smtp->error_message, msgbuf);
502 /* CLAIM: we now have a TCP connection to the remote SMTP server */
503 if(cfg->timeout > 0) {
504 set_timeout(cfg->timeout);
506 rc = socket_read(smtp->sock, rbuf, sizeof(rbuf));
507 if(cfg->timeout > 0) {
512 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (banner): %m");
515 strcpy(msgbuf, RESP_SYNCERROR);
516 smtp->error_message = malloc(strlen(msgbuf) + 1);
517 strcpy(smtp->error_message, msgbuf);
521 c = strpbrk(rbuf, "\r\n");
526 if(strncmp(rbuf, "220 ", sizeof("220 ")-1)) {
528 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): unexpected response during initial handshake: %s", rbuf);
531 strcpy(msgbuf, RESP_UNEXPECTED);
532 smtp->error_message = malloc(strlen(msgbuf) + 1);
533 strcpy(smtp->error_message, msgbuf);
537 if((uname(h_name)) < 0){
538 myhostname = "localhost.localdomain";
540 myhostname = h_name->nodename;
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;
551 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", EHLO_CMD, myhostname);
553 if(cfg->timeout > 0) {
554 set_timeout(cfg->timeout);
556 rc = retry_writev(smtp->sock, iov, 3);
557 memset(iov, 0, sizeof(iov));
558 if(cfg->timeout > 0) {
563 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): writev: %m");
566 strcpy(msgbuf, RESP_IERROR);
567 smtp->error_message = malloc(strlen(msgbuf) + 1);
568 strcpy(smtp->error_message, msgbuf);
572 /* read and parse the EHLO response */
573 if(cfg->timeout > 0) {
574 set_timeout(cfg->timeout);
576 rc = socket_read(smtp->sock, rbuf, sizeof(rbuf));
577 if(cfg->timeout > 0) {
582 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
585 strcpy(msgbuf, RESP_IERROR);
586 smtp->error_message = malloc(strlen(msgbuf) + 1);
587 strcpy(smtp->error_message, msgbuf);
591 if((tbuf = strstr(rbuf, "250-STARTTLS"))) {
593 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): STARTTLS not supported.");
597 if((tbuf = strstr(rbuf, "250-AUTH"))) {
598 if(strncmp(tbuf, "250", sizeof("250")-1) == 0) {
601 if(*p == '-' || *p == ' ') p++;
602 if(strncasecmp(p, "AUTH", sizeof("AUTH")-1) == 0) {
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;
616 if(avail_auth_type == 0) {
618 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp authentication is not implemented: %s", rbuf);
621 strcpy(msgbuf, RESP_UNEXPECTED);
622 smtp->error_message = malloc(strlen(msgbuf) + 1);
623 strcpy(smtp->error_message, msgbuf);
627 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth_type: %d", avail_auth_type);
630 /* build the AUTH command */
631 if(avail_auth_type & AUTH_CRAM_MD5) {
632 auth = auth_cram_md5(smtp->sock,&global);
634 else if((avail_auth_type & AUTH_LOGIN) != 0) {
635 auth = auth_login(smtp->sock,&global);
637 else if((avail_auth_type & AUTH_PLAIN) != 0) {
638 auth = auth_plain(smtp->sock,&global);
640 else if((avail_auth_type & AUTH_DIGEST_MD5) != 0) {
641 auth = auth_digest_md5(smtp->sock,&global);
645 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp authentication is not implemented: %s", rbuf);
648 strcpy(msgbuf, RESP_UNEXPECTED);
649 smtp->error_message = malloc(strlen(msgbuf) + 1);
650 strcpy(smtp->error_message, msgbuf);
655 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth) auth: [%d]", auth);
659 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth) rejected: [%s]", global.username);
662 strcpy(msgbuf, RESP_CREDERROR);
663 smtp->error_message = malloc(strlen(msgbuf) + 1);
664 strcpy(smtp->error_message, msgbuf);
668 smtp_quit(smtp->sock,&global);
672 smtp_quit(smtp->sock,&global);
675 else if(smtp->error == 2)
682 smtp_quit(socket_t *sock, config_t *cfg) {
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;
693 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", QUIT_CMD);
695 if(cfg->timeout > 0) {
696 set_timeout(cfg->timeout);
698 rc = retry_writev(sock, iov, 2);
699 memset(iov, 0, sizeof(iov));
700 if(cfg->timeout > 0) {
705 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): quit writev: %m");
708 (void)socket_close(sock);
714 auth_cram_md5(socket_t *sock, config_t *cfg) {
722 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH CRAM-MD5");
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;
732 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "CRAM-MD5");
734 if(cfg->timeout > 0) {
735 set_timeout(cfg->timeout);
737 rc = retry_writev(sock, iov, 3);
738 memset(iov, 0, sizeof(iov));
739 if(cfg->timeout > 0) {
744 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): cram-md5 writev: %m");
749 if(cfg->timeout > 0) {
750 set_timeout(cfg->timeout);
752 rc = socket_read(sock, rbuf, sizeof(rbuf));
753 if(cfg->timeout > 0) {
758 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
764 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
766 if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
769 unsigned char *challenge;
771 unsigned char hexdigest[33];
773 challenge = malloc(strlen(rbuf + 4) + 1);
774 challengelen = base64_decode((char *)challenge, rbuf + 4, -1);
775 challenge[challengelen] = '\0';
777 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): challenge=%s", challenge);
780 snprintf(buf, sizeof(buf), "%s", cfg->password);
781 md5_hex_hmac((char *)hexdigest, challenge, challengelen, (unsigned char*)buf, strlen(cfg->password));
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));
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;
796 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", response64);
798 if(cfg->timeout > 0) {
799 set_timeout(cfg->timeout);
801 rc = retry_writev(sock, iov, 2);
802 memset(iov, 0, sizeof(iov));
803 if(cfg->timeout > 0) {
808 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): cram-md5 writev: %m");
813 if(cfg->timeout > 0) {
814 set_timeout(cfg->timeout);
816 rc = socket_read(sock, rbuf, sizeof(rbuf));
817 if(cfg->timeout > 0) {
822 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
828 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
830 if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
832 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
839 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems cram-md5 mech is not implemented.");
848 auth_login(socket_t *sock, config_t *cfg) {
853 //char buf[RESP_LEN];
857 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH LOGIN");
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;
867 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "LOGIN");
869 if(cfg->timeout > 0) {
870 set_timeout(cfg->timeout);
872 rc = retry_writev(sock, iov, 3);
873 memset(iov, 0, sizeof(iov));
874 if(cfg->timeout > 0) {
879 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
884 if(cfg->timeout > 0) {
885 set_timeout(cfg->timeout);
887 rc = socket_read(sock, rbuf, sizeof(rbuf));
888 if(cfg->timeout > 0) {
893 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
899 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
901 if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
902 buf = malloc(sizeof(char)*128);
903 base64_encode(buf, cfg->username, strlen(cfg->username));
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;
911 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
913 if(cfg->timeout > 0) {
914 set_timeout(cfg->timeout);
916 rc = retry_writev(sock, iov, 2);
917 memset(iov, 0, sizeof(iov));
918 if(cfg->timeout > 0) {
923 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
928 if(cfg->timeout > 0) {
929 set_timeout(cfg->timeout);
931 rc = socket_read(sock, rbuf, sizeof(rbuf));
932 if(cfg->timeout > 0) {
937 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
943 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
945 if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
946 buf = malloc(sizeof(char)*128);
947 base64_encode(buf, cfg->password, strlen(cfg->password));
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;
955 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
957 if(cfg->timeout > 0) {
958 set_timeout(cfg->timeout);
960 rc = retry_writev(sock, iov, 2);
961 memset(iov, 0, sizeof(iov));
962 if(cfg->timeout > 0) {
967 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
972 if(cfg->timeout > 0) {
973 set_timeout(cfg->timeout);
975 rc = socket_read(sock, rbuf, sizeof(rbuf));
976 if(cfg->timeout > 0) {
981 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
987 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
989 if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
991 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
997 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems login mech is not implemented.");
1003 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems login mech is not implemented.");
1012 auth_plain(socket_t *sock, config_t *cfg) {
1014 struct iovec iov[3];
1016 char rbuf[RESP_LEN];
1017 //char buf[RESP_LEN];
1023 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH PLAIN");
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] == '^') {
1032 buf = malloc(sizeof(char)*128);
1033 base64_encode(buf, phrase, len);
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;
1045 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s %s", AUTH_CMD, "PLAIN", buf);
1047 if(cfg->timeout > 0) {
1048 set_timeout(cfg->timeout);
1050 rc = retry_writev(sock, iov, 4);
1051 memset(iov, 0, sizeof(iov));
1053 if(cfg->timeout > 0) {
1058 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): plain writev: %m");
1063 if(cfg->timeout > 0) {
1064 set_timeout(cfg->timeout);
1066 rc = socket_read(sock, rbuf, sizeof(rbuf));
1067 if(cfg->timeout > 0) {
1072 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
1077 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
1078 if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
1080 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
1089 auth_digest_md5(socket_t *sock, config_t *cfg) {
1091 struct iovec iov[3];
1093 char rbuf[RESP_LEN];
1097 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH DIGEST-MD5");
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;
1108 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "DIGEST-MD5");
1110 if(cfg->timeout > 0) {
1111 set_timeout(cfg->timeout);
1113 rc = retry_writev(sock, iov, 3);
1114 memset(iov, 0, sizeof(iov));
1115 if(cfg->timeout > 0) {
1120 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): digest-md5 writev: %m");
1125 if(cfg->timeout > 0) {
1126 set_timeout(cfg->timeout);
1128 rc = socket_read(sock, rbuf, sizeof(rbuf));
1129 if(cfg->timeout > 0) {
1134 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
1140 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
1142 if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
1147 unsigned char hexdigest[256];
1149 challenge = malloc(strlen(rbuf + 4) + 1);
1150 challengelen = base64_decode(challenge, rbuf + 4, -1);
1151 challenge[challengelen] = '\0';
1153 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): challenge=%s", challenge);
1156 digest_md5((char *)hexdigest, (unsigned char*)challenge, challengelen, cfg->username, cfg->password);
1158 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): hexdigest=%s", hexdigest);
1161 response = malloc(sizeof(char)*256);
1162 snprintf(response, 256, "%s", hexdigest);
1164 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): response=%s", response);
1166 response64 = malloc((strlen(response) + 3) * 2 + 1);
1167 base64_encode(response64, response, strlen(response));
1170 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): response64=%s", response64);
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;
1179 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", response64);
1181 if(cfg->timeout > 0) {
1182 set_timeout(cfg->timeout);
1184 rc = retry_writev(sock, iov, 2);
1185 memset(iov, 0, sizeof(iov));
1186 if(cfg->timeout > 0) {
1191 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): digest-md5 writev: %m");
1196 if(cfg->timeout > 0) {
1197 set_timeout(cfg->timeout);
1199 rc = socket_read(sock, rbuf, sizeof(rbuf));
1200 if(cfg->timeout > 0) {
1205 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
1211 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
1213 if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
1216 buf = malloc(strlen(rbuf + 4) + 1);
1217 buflen = base64_decode(buf, rbuf + 4, -1);
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;
1226 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
1228 if(cfg->timeout > 0) {
1229 set_timeout(cfg->timeout);
1231 rc = retry_writev(sock, iov, 2);
1232 memset(iov, 0, sizeof(iov));
1233 if(cfg->timeout > 0) {
1238 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): digest-md5 writev: %m");
1243 if(cfg->timeout > 0) {
1244 set_timeout(cfg->timeout);
1246 rc = socket_read(sock, rbuf, sizeof(rbuf));
1247 if(cfg->timeout > 0) {
1252 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
1258 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
1260 if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
1262 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
1268 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems digest-md5 mech is not implemented.");
1275 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems digest-md5 mech is not implemented.");
1283 SSL_CTX *SSLContext = 0;
1287 verify_cert(SSL *ssl) {
1295 cert = SSL_get_peer_certificate(ssl);
1298 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: no server certificate.");
1303 err = SSL_get_verify_result(ssl);
1304 if(err == X509_V_OK) {
1309 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: can't verify certificate: %s (%d).",
1310 X509_verify_cert_error_string(err), err);
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);
1325 fprintf(stderr," to: %s\n", buf);
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')) {
1334 fprintf(stderr, "\nFine, but don't say I didn't warn you!\n\n");
1341 init_ssl(config_t *conf) {
1346 if(!conf->certfile) {
1348 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSLCertificateFile not defined.");
1352 SSL_load_error_strings();
1354 if(conf->use_tlsv1 && !conf->use_sslv2 && !conf->use_sslv3)
1355 method = TLSv1_client_method();
1357 method = SSLv23_client_method();
1359 SSLContext = SSL_CTX_new(method);
1361 if(access(conf->certfile, F_OK)) {
1362 if(errno != ENOENT) {
1364 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSLCertificateFile is not accessible.");
1369 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Warning: SSLCertificateFile doesn't exist, can't verify server certificates.");
1371 } else if(!SSL_CTX_load_verify_locations(SSLContext, conf->certfile, NULL)) {
1373 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSL_CTX_load_verify_locations: %s.",ERR_error_string(ERR_get_error(), 0));
1375 SSL_CTX_free(SSLContext);
1379 if(!conf->use_sslv2) {
1380 options |= SSL_OP_NO_SSLv2;
1382 if(!conf->use_sslv3) {
1383 options |= SSL_OP_NO_SSLv3;
1385 if(!conf->use_tlsv1) {
1386 options |= SSL_OP_NO_TLSv1;
1389 SSL_CTX_set_options(SSLContext, options);
1391 /* we check the result of the verification after SSL_connect() */
1392 SSL_CTX_set_verify(SSLContext, SSL_VERIFY_NONE, 0);
1397 start_tls(smtp_t *smtp, config_t *cfg) {
1400 /* initialize SSL */
1403 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): failed to initialize ssl session.");
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);
1413 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): failed to connect ssl session.");
1418 /* verify the server certificate */
1419 if(verify_cert(smtp->sock->ssl)) {
1423 smtp->sock->use_ssl = 1;
1425 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): SSL support enabled.");