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 B64(c) (isascii(c) ? base64val[(int)(c)] : -1)
71 extern int retry_writev(socket_t *socket, struct iovec *iov, int iovcnt);
72 extern int socket_read(socket_t * sock, char *buf, size_t len);
73 extern void socket_perror(const char *func, socket_t *sock, int ret);
75 void md5_hex_hmac(char *hexdigest, unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len);
76 void hmac_md5(unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len, unsigned char *digest);
77 int start_tls(smtp_t *smtp, config_t * cfg);
80 int smtp_quit(socket_t *socket, config_t * cfg);
81 int auth_cram_md5(socket_t *socket, config_t * cfg);
82 int auth_login(socket_t *socket, config_t * cfg);
83 int auth_plain(socket_t *socket, config_t * cfg);
86 static const char base64char[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
87 static const char base64val[128] = {
88 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
89 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
90 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
91 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
92 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
93 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
94 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
95 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
100 md5_hex_hmac(char *hexdigest, unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len) {
102 unsigned char digest[16];
105 hmac_md5(text, text_len, key, key_len, digest);
106 for(cnt=0; cnt<16; cnt++) {
107 sprintf(hexdigest + 2 * cnt, "%02x", digest[cnt]);
113 hmac_md5(unsigned char* text, unsigned int text_len, unsigned char* key, unsigned int key_len, unsigned char *digest) {
116 unsigned char k_ipad[64];
117 unsigned char k_opad[64];
120 memset(k_ipad, 0, sizeof(k_ipad));
121 memset(k_opad, 0, sizeof(k_opad));
127 MD5_Update(&tctx, key, key_len);
128 MD5_Final(k_ipad, &tctx);
129 MD5_Final(k_opad, &tctx);
132 MD5Update(&tctx, key, key_len);
133 MD5Final(k_ipad, &tctx);
134 MD5Final(k_opad, &tctx);
137 memcpy(k_ipad, key, key_len);
138 memcpy(k_opad, key, key_len);
141 for(cnt=0; cnt<64; cnt++) {
148 MD5_Update(&context, k_ipad, 64);
149 MD5_Update(&context, text, text_len);
150 MD5_Final(digest, &context);
153 MD5_Update(&context, k_opad, 64);
154 MD5_Update(&context, digest, 16);
155 MD5_Final(digest, &context);
158 MD5Update(&context, k_ipad, 64);
159 MD5Update(&context, text, text_len);
160 MD5Final(digest, &context);
163 MD5Update(&context, k_opad, 64);
164 MD5Update(&context, digest, 16);
165 MD5Final(digest, &context);
171 base64_encode(char *out, const char *in, int inlen) {
173 const char *inp = in;
177 *outp++ = base64char[(inp[0] >> 2) & 0x3f];
178 *outp++ = base64char[((inp[0] & 0x03) << 4) | ((inp[1] >> 4) & 0x0f)];
179 *outp++ = base64char[((inp[1] & 0x0f) << 2) | ((inp[2] >> 6) & 0x03)];
180 *outp++ = base64char[inp[2] & 0x3f];
185 *outp++ = base64char[(inp[0] >> 2) & 0x3f];
187 *outp++ = base64char[(inp[0] & 0x03) << 4];
191 *outp++ = base64char[((inp[0] & 0x03) << 4) | ((inp[1] >> 4) & 0x0f)];
192 *outp++ = base64char[((inp[1] & 0x0f) << 2)];
201 base64_decode(char *out, const char *in, int inlen) {
203 const char *inp = in;
210 while(inlen >=4 && *inp != '\0') {
213 if( B64(buf[0]) == -1) break;
217 if(B64(buf[1]) == -1) break;
221 if(buf[2] != '=' && B64(buf[2]) == -1) break;
225 if(buf[3] != '=' && B64(buf[3]) == -1) break;
227 *outp++ = ((B64(buf[0]) << 2) & 0xfc) | ((B64(buf[1]) >> 4) & 0x03);
229 *outp++ = ((B64(buf[1]) & 0x0f) << 4) | ((B64(buf[2]) >> 2) & 0x0f);
231 *outp++ = ((B64(buf[2]) & 0x03) << 6) | (B64(buf[3]) & 0x3f);
240 smtp_auth(config_t * cfg) {
243 struct sockaddr_in addr;
253 int avail_auth_type = 0;
255 struct utsname h_name[1];
259 struct sockaddr_in taddr;
261 struct ifconf ifconf;
262 struct ifreq *ifr, ifreq;
263 unsigned char *ifptr;
270 if(!global.password) {
271 global.password = getpass("Password:");
272 if(!global.password) {
275 if(!*global.password) {
276 global.password = NULL;
279 global.password = strdup(global.password);
281 cfg->password = strdup(global.password);
284 assert(cfg->username != NULL);
285 assert(cfg->password != NULL);
287 smtp = calloc(1, sizeof(smtp_t));
288 smtp->sock = calloc(1, sizeof(socket_t));
289 smtp->buf = calloc(1, sizeof(buffer_t));
290 smtp->buf->sock = smtp->sock;
294 /* open connection to SMTP server */
295 memset(&addr, 0, sizeof(addr));
296 addr.sin_port = htons(cfg->port);
297 addr.sin_family = AF_INET;
298 he = gethostbyname(cfg->host);
301 strcpy(msgbuf, "Error: resolving hostname ");
302 strcat(msgbuf, cfg->host);
303 smtp->error_message = malloc(strlen(msgbuf) + 1);
304 strcpy(smtp->error_message, msgbuf);
308 if((sd = socket(PF_INET, SOCK_DGRAM, 0)) != -1) {
309 bzero(&ifconf, sizeof(struct ifconf));
310 bzero(&ifreq, sizeof(struct ifreq));
311 iflen = 10 * sizeof(struct ifreq);
312 ifptr = malloc(iflen);
313 ifconf.ifc_len = iflen;
314 ifconf.ifc_ifcu.ifcu_req = (struct ifreq *)ifptr;
315 if(ioctl(sd, SIOCGIFCONF, &ifconf) != -1) {
316 for(iflen=sizeof(struct ifreq); iflen<=ifconf.ifc_len; iflen+=sizeof(struct ifreq)) {
317 ifr = (struct ifreq *)ifptr;
318 strcpy(ifreq.ifr_ifrn.ifrn_name, ifr->ifr_name);
319 if(ioctl(sd, SIOCGIFADDR, &ifreq) != -1) {
321 while(he->h_addr_list[n]) {
322 if(he->h_addrtype == AF_INET) {
323 memset((char*)&taddr, 0, sizeof(taddr));
324 memcpy((char*)&taddr.sin_addr, he->h_addr_list[n], he->h_length);
326 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): my ip: %s",
327 inet_ntoa(((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr));
328 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp ip: %s",
329 inet_ntoa(taddr.sin_addr));
331 if(((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr == taddr.sin_addr.s_addr) {
333 strcpy(msgbuf, "Error: this host is specified. ");
334 strcat(msgbuf, inet_ntoa(taddr.sin_addr));
335 smtp->error_message = malloc(strlen(msgbuf) + 1);
336 strcpy(smtp->error_message, msgbuf);
343 ifptr += sizeof(struct ifreq);
347 addr.sin_addr.s_addr = *((int *)he->h_addr_list[0]);
348 s = socket(PF_INET, SOCK_STREAM, 0);
349 if(connect(s, (struct sockaddr *) &addr, sizeof(addr))) {
351 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): connection error = %s",strerror(errno));
354 strcpy(msgbuf, "Error: connecting to ");
355 strcat(msgbuf, inet_ntoa(addr.sin_addr));
356 smtp->error_message = malloc(strlen(msgbuf) + 1);
357 strcpy(smtp->error_message, msgbuf);
365 if(start_tls(smtp, cfg)) {
367 strcpy(msgbuf, "Error: start_tls");
368 smtp->error_message = malloc(strlen(msgbuf) + 1);
369 strcpy(smtp->error_message, msgbuf);
376 /* CLAIM: we now have a TCP connection to the remote SMTP server */
379 rc = socket_read(smtp->sock, rbuf, sizeof(rbuf));
383 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (banner): %m");
386 strcpy(msgbuf, RESP_SYNCERROR);
387 smtp->error_message = malloc(strlen(msgbuf) + 1);
388 strcpy(smtp->error_message, msgbuf);
392 c = strpbrk(rbuf, "\r\n");
397 if(strncmp(rbuf, "220 ", sizeof("220 ")-1)) {
399 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): unexpected response during initial handshake: %s", rbuf);
402 strcpy(msgbuf, RESP_UNEXPECTED);
403 smtp->error_message = malloc(strlen(msgbuf) + 1);
404 strcpy(smtp->error_message, msgbuf);
408 if((uname(h_name)) < 0){
409 myhostname = "localhost.localdomain";
411 myhostname = h_name->nodename;
414 iov[0].iov_base = EHLO_CMD;
415 iov[0].iov_len = sizeof(EHLO_CMD) - 1;
416 iov[1].iov_base = myhostname;
417 iov[1].iov_len = strlen(myhostname);
418 iov[2].iov_base = "\r\n";
419 iov[2].iov_len = sizeof("\r\n") - 1;
422 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", EHLO_CMD, myhostname);
425 rc = retry_writev(smtp->sock, iov, 3);
426 memset(iov, 0, sizeof(iov));
430 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): writev: %m");
433 strcpy(msgbuf, RESP_IERROR);
434 smtp->error_message = malloc(strlen(msgbuf) + 1);
435 strcpy(smtp->error_message, msgbuf);
439 /* read and parse the EHLO response */
441 rc = socket_read(smtp->sock, rbuf, sizeof(rbuf));
445 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
448 strcpy(msgbuf, RESP_IERROR);
449 smtp->error_message = malloc(strlen(msgbuf) + 1);
450 strcpy(smtp->error_message, msgbuf);
454 if((tbuf = strstr(rbuf, "250-AUTH"))) {
455 if(strncmp(tbuf, "250", sizeof("250")-1) == 0) {
458 if(*p == '-' || *p == ' ') p++;
459 if(strncasecmp(p, "AUTH", sizeof("AUTH")-1) == 0) {
461 if(strcasestr(p, "PLAIN"))
462 avail_auth_type |= AUTH_PLAIN;
463 if(strcasestr(p, "LOGIN"))
464 avail_auth_type |= AUTH_LOGIN;
465 if(strcasestr(p, "CRAM-MD5"))
466 avail_auth_type |= AUTH_CRAM_MD5;
467 if(strcasestr(p, "DIGEST-MD5"))
468 avail_auth_type |= AUTH_DIGEST_MD5;
473 if(avail_auth_type == 0) {
475 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp authentication is not implemented: %s", rbuf);
478 strcpy(msgbuf, RESP_UNEXPECTED);
479 smtp->error_message = malloc(strlen(msgbuf) + 1);
480 strcpy(smtp->error_message, msgbuf);
484 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth_type: %d", avail_auth_type);
487 /* build the AUTH command */
488 if(avail_auth_type & AUTH_CRAM_MD5) {
489 auth = auth_cram_md5(smtp->sock,&global);
491 else if((avail_auth_type & AUTH_LOGIN) != 0) {
492 auth = auth_login(smtp->sock,&global);
494 else if((avail_auth_type & AUTH_PLAIN) != 0) {
495 auth = auth_plain(smtp->sock,&global);
499 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): smtp authentication is not implemented: %s", rbuf);
502 strcpy(msgbuf, RESP_UNEXPECTED);
503 smtp->error_message = malloc(strlen(msgbuf) + 1);
504 strcpy(smtp->error_message, msgbuf);
509 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth) auth: [%d]", auth);
513 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth) rejected: [%s]", global.username);
516 strcpy(msgbuf, RESP_CREDERROR);
517 smtp->error_message = malloc(strlen(msgbuf) + 1);
518 strcpy(smtp->error_message, msgbuf);
522 smtp_quit(smtp->sock,&global);
526 smtp_quit(smtp->sock,&global);
529 else if(smtp->error == 2)
536 smtp_quit(socket_t *socket, config_t *cfg) {
541 iov[0].iov_base = QUIT_CMD;
542 iov[0].iov_len = sizeof(QUIT_CMD) - 1;
543 iov[1].iov_base = "\r\n";
544 iov[1].iov_len = sizeof("\r\n") - 1;
547 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", QUIT_CMD);
550 rc = retry_writev(socket, iov, 2);
551 memset(iov, 0, sizeof(iov));
555 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): quit writev: %m");
558 (void)close(socket->fd);
565 auth_cram_md5(socket_t *socket, config_t *cfg) {
573 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH CRAM-MD5");
575 iov[0].iov_base = AUTH_CMD;
576 iov[0].iov_len = sizeof(AUTH_CMD) - 1;
577 iov[1].iov_base = "CRAM-MD5";
578 iov[1].iov_len = strlen("CRAM-MD5");
579 iov[2].iov_base = "\r\n";
580 iov[2].iov_len = sizeof("\r\n") - 1;
583 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "CRAM-MD5");
586 rc = retry_writev(socket, iov, 3);
587 memset(iov, 0, sizeof(iov));
591 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): cram-md5 writev: %m");
597 rc = socket_read(socket, rbuf, sizeof(rbuf));
601 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
607 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
609 if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
614 unsigned char hexdigest[33];
616 challenge = malloc(strlen(rbuf + 4) + 1);
617 challengelen = base64_decode(challenge, rbuf + 4, -1);
618 challenge[challengelen] = '\0';
620 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): challenge=%s", challenge);
623 snprintf(buf, sizeof(buf), "%s", cfg->password);
624 md5_hex_hmac(hexdigest, challenge, challengelen, buf, strlen(cfg->password));
627 response = malloc(sizeof(char)*128);
628 sprintf(response, "%s %s", cfg->username, hexdigest);
629 response64 = malloc((strlen(response) + 3) * 2 + 1);
630 base64_encode(response64, response, strlen(response));
633 iov[0].iov_base = response64;
634 iov[0].iov_len = strlen(response64);
635 iov[1].iov_base = "\r\n";
636 iov[1].iov_len = sizeof("\r\n") - 1;
639 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", response64);
642 rc = retry_writev(socket, iov, 2);
643 memset(iov, 0, sizeof(iov));
647 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): cram-md5 writev: %m");
653 rc = socket_read(socket, rbuf, sizeof(rbuf));
657 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
663 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
665 if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
667 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
674 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems cram-md5 mech is not implemented.");
683 auth_login(socket_t *socket, config_t *cfg) {
688 //char buf[RESP_LEN];
692 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH LOGIN");
694 iov[0].iov_base = AUTH_CMD;
695 iov[0].iov_len = sizeof(AUTH_CMD) - 1;
696 iov[1].iov_base = "LOGIN";
697 iov[1].iov_len = strlen("LOGIN");
698 iov[2].iov_base = "\r\n";
699 iov[2].iov_len = sizeof("\r\n") - 1;
702 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s", AUTH_CMD, "LOGIN");
705 rc = retry_writev(socket, iov, 3);
706 memset(iov, 0, sizeof(iov));
710 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
716 rc = socket_read(socket, rbuf, sizeof(rbuf));
720 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
726 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
728 if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
729 buf = malloc(sizeof(char)*128);
730 base64_encode(buf, cfg->username, strlen(cfg->username));
732 iov[0].iov_base = buf;
733 iov[0].iov_len = strlen(buf);
734 iov[1].iov_base = "\r\n";
735 iov[1].iov_len = sizeof("\r\n") - 1;
738 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
741 rc = retry_writev(socket, iov, 2);
742 memset(iov, 0, sizeof(iov));
746 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): login writev: %m");
752 rc = socket_read(socket, rbuf, sizeof(rbuf));
756 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
762 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
764 if(strncmp(rbuf, "334 ", sizeof("334 ")-1) == 0) {
765 buf = malloc(sizeof(char)*128);
766 base64_encode(buf, cfg->password, strlen(cfg->password));
768 iov[0].iov_base = buf;
769 iov[0].iov_len = strlen(buf);
770 iov[1].iov_base = "\r\n";
771 iov[1].iov_len = sizeof("\r\n") - 1;
774 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s", buf);
777 rc = retry_writev(socket, iov, 2);
778 memset(iov, 0, sizeof(iov));
782 syslog(LOG_WARNING, "pam_smtpauth(smtpauth): login writev: %m");
788 rc = socket_read(socket, rbuf, sizeof(rbuf));
792 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
798 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
800 if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
802 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
808 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems login mech is not implemented.");
814 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): it seems login mech is not implemented.");
823 auth_plain(socket_t *socket, config_t *cfg) {
828 //char buf[RESP_LEN];
834 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): AUTH PLAIN");
836 sprintf(phrase,"%s^%s^%s", cfg->username, cfg->username, cfg->password);
837 len = strlen(phrase);
838 for(cnt=len-1; cnt>=0; cnt--) {
839 if(phrase[cnt] == '^') {
843 buf = malloc(sizeof(char)*128);
844 base64_encode(buf, phrase, len);
846 iov[0].iov_base = AUTH_CMD;
847 iov[0].iov_len = sizeof(AUTH_CMD) - 1;
848 iov[1].iov_base = "PLAIN ";
849 iov[1].iov_len = strlen("PLAIN ");
850 iov[2].iov_base = buf;
851 iov[2].iov_len = strlen(buf);
852 iov[3].iov_base = "\r\n";
853 iov[3].iov_len = sizeof("\r\n") - 1;
856 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): sending %s%s %s", AUTH_CMD, "PLAIN", buf);
859 rc = retry_writev(socket, iov, 4);
860 memset(iov, 0, sizeof(iov));
865 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): plain writev: %m");
871 rc = socket_read(socket, rbuf, sizeof(rbuf));
875 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %m");
880 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): read (response): %s",rbuf);
881 if(strncmp(rbuf, "235 ", sizeof("235 ")-1) != 0) {
883 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): auth failure.");
892 SSL_CTX *SSLContext = 0;
895 /* this gets called when a certificate is to be verified */
897 verify_cert(SSL *ssl) {
905 cert = SSL_get_peer_certificate(ssl);
908 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: no server certificate.");
913 err = SSL_get_verify_result(ssl);
914 if(err == X509_V_OK) {
919 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: can't verify certificate: %s (%d).",
920 X509_verify_cert_error_string(err), err);
922 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
923 fprintf(stderr,"\nSubject: %s\n", buf);
924 X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
925 fprintf(stderr,"Issuer: %s\n", buf);
926 bio = BIO_new(BIO_s_mem());
927 ASN1_TIME_print(bio, X509_get_notBefore(cert));
928 memset(buf, 0, sizeof(buf));
929 BIO_read(bio, buf, sizeof(buf) - 1);
930 fprintf(stderr,"Valid from: %s\n", buf);
931 ASN1_TIME_print(bio, X509_get_notAfter(cert));
932 memset(buf, 0, sizeof(buf));
933 BIO_read(bio, buf, sizeof(buf) - 1);
935 fprintf(stderr," to: %s\n", buf);
938 "\nThere is no way to verify this certificate.\n"
939 " It is possible that a hostile attacker has replaced the server certificate.\n"
940 " Continue at your own risk!\n"
941 "\nAccept this certificate anyway? [no]: ");
942 if(fgets(buf, sizeof(buf), stdin) && (buf[0] == 'y' || buf[0] == 'Y')) {
944 fprintf(stderr, "\nFine, but don't say I didn't warn you!\n\n");
951 init_ssl(config_t *conf) {
956 if(!conf->certfile) {
958 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSLCertificateFile not defined.");
962 SSL_load_error_strings();
964 if(conf->use_tlsv1 && !conf->use_sslv2 && !conf->use_sslv3)
965 method = TLSv1_client_method();
967 method = SSLv23_client_method();
969 SSLContext = SSL_CTX_new(method);
971 if(access(conf->certfile, F_OK)) {
972 if(errno != ENOENT) {
974 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSLCertificateFile is not accessible.");
979 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Warning: SSLCertificateFile doesn't exist, can't verify server certificates.");
981 } else if(!SSL_CTX_load_verify_locations(SSLContext, conf->certfile, NULL)) {
983 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): Error: SSL_CTX_load_verify_locations: %s.",ERR_error_string(ERR_get_error(), 0));
985 SSL_CTX_free(SSLContext);
989 if(!conf->use_sslv2) {
990 options |= SSL_OP_NO_SSLv2;
992 if(!conf->use_sslv3) {
993 options |= SSL_OP_NO_SSLv3;
995 if(!conf->use_tlsv1) {
996 options |= SSL_OP_NO_TLSv1;
999 SSL_CTX_set_options(SSLContext, options);
1001 /* we check the result of the verification after SSL_connect() */
1002 SSL_CTX_set_verify(SSLContext, SSL_VERIFY_NONE, 0);
1007 start_tls(smtp_t *smtp, config_t *cfg) {
1010 /* initialize SSL */
1013 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): failed to initialize ssl session.");
1018 smtp->sock->ssl = SSL_new(SSLContext);
1019 SSL_set_fd(smtp->sock->ssl, smtp->sock->fd);
1020 if((ret = SSL_connect(smtp->sock->ssl)) <= 0) {
1021 socket_perror("connect", smtp->sock, ret);
1023 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): failed to connect ssl session.");
1028 /* verify the server certificate */
1029 if(verify_cert(smtp->sock->ssl)) {
1033 smtp->sock->use_ssl = 1;
1035 syslog(LOG_DEBUG, "pam_smtpauth(smtpauth): SSL support enabled.");