* New version 2.26
[alpine.git] / imap / src / osdep / unix / ssl_unix.c
blob0fd93a7e1252c8a9ba8f63a4a44d5a232ade5a63
1 /* ========================================================================
2 * Copyright 2019-2022 Eduardo Chappa
3 * Copyright 2008-2009 Mark Crispin
4 * ========================================================================
5 */
7 /*
8 * Program: SSL authentication/encryption module
10 * Author: Mark Crispin
12 * Date: 22 September 1998
13 * Last Edited: 8 November 2009
15 * Previous versions of this file were
17 * Copyright 1988-2008 University of Washington
19 * Licensed under the Apache License, Version 2.0 (the "License");
20 * you may not use this file except in compliance with the License.
21 * You may obtain a copy of the License at
23 * http://www.apache.org/licenses/LICENSE-2.0
27 #define crypt ssl_private_crypt
28 #define STRING OPENSSL_STRING
29 #include <x509v3.h>
30 #include <ssl.h>
31 #include <err.h>
32 #include <pem.h>
33 #include <buffer.h>
34 #include <bio.h>
35 #include <crypto.h>
36 #include <rand.h>
37 #ifdef OPENSSL_1_1_0
38 #include <rsa.h>
39 #include <bn.h>
40 #endif /* OPENSSL_1_1_0 */
41 #undef STRING
42 #undef crypt
44 #define SSLBUFLEN 8192
47 * PCI auditing compliance, disable:
48 * SSLv2
49 * anonymous D-H (no certificate
50 * export encryption ciphers (40 and 56 bits)
51 * low encryption cipher suites (40 and 56 bits, excluding export)
52 * null encryption (disabling implied by "ALL")
54 * UW imapd just disables low-grade and null ("ALL:!LOW"). This setting
55 * will break clients that attempt to use the newly-prohibited mechanisms.
57 * I question the value of disabling SSLv2, as opposed to disabling the SSL
58 * ports (e.g., 993 for IMAP, 995 for POP3) and using TLS exclusively.
61 #define SSLCIPHERLIST "ALL:!SSLv2:!ADH:!EXP:!LOW"
63 /* SSL I/O stream */
65 typedef struct ssl_stream {
66 TCPSTREAM *tcpstream; /* TCP stream */
67 SSL_CTX *context; /* SSL context */
68 SSL *con; /* SSL connection */
69 int ictr; /* input counter */
70 char *iptr; /* input pointer */
71 char ibuf[SSLBUFLEN]; /* input buffer */
72 } SSLSTREAM;
74 #include "sslio.h"
76 /* Function prototypes */
77 long ssl_disable_mask(int ssl_version, int direction);
78 const SSL_METHOD *ssl_connect_mthd(int flag, int *minv, int *maxv);
79 static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
80 static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags);
81 static int ssl_open_verify (int ok,X509_STORE_CTX *ctx);
82 static char *ssl_validate_cert (X509 *cert,char *host);
83 static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat);
84 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
85 long *contd);
86 static long ssl_abort (SSLSTREAM *stream);
88 #ifdef OPENSSL_1_1_0
89 #define SSL_CTX_TYPE SSL_CTX
90 #else
91 #define SSL_CTX_TYPE SSL
92 #endif /* OPENSSL_1_1_0 */
94 static RSA *ssl_genkey (SSL_CTX_TYPE *con,int export,int keylength);
96 typedef struct ssl_versions_s {
97 char *name;
98 int version;
99 } SSL_VERSIONS_S;
102 pith_ssl_encryption_version(char *s)
104 SSL_VERSIONS_S ssl_versions[] = {
105 { "no_min", 0 },
106 { "ssl3", SSL3_VERSION },
107 { "tls1", TLS1_VERSION },
108 { "tls1_1", TLS1_1_VERSION },
109 { "tls1_2", TLS1_2_VERSION },
110 #ifdef TLS1_3_VERSION
111 { "tls1_3", TLS1_3_VERSION },
112 #endif /* TLS1_3_VERSION */
113 { "no_max", 0 }, /* set this last in the list */
114 { NULL, 0 },
116 int i;
118 if (s == NULL || *s == '\0')
119 return -1;
121 for (i = 0; ssl_versions[i].name != NULL; i++)
122 if (strcmp(ssl_versions[i].name, s) == 0)
123 break;
125 if (strcmp(s, "no_max") == 0) i--;
127 return ssl_versions[i].name != NULL ? ssl_versions[i].version : -1;
132 /* Secure Sockets Layer network driver dispatch */
134 static struct ssl_driver ssldriver = {
135 ssl_open, /* open connection */
136 ssl_aopen, /* open preauthenticated connection */
137 ssl_getline, /* get a line */
138 ssl_getbuffer, /* get a buffer */
139 ssl_soutr, /* output pushed data */
140 ssl_sout, /* output string */
141 ssl_close, /* close connection */
142 ssl_host, /* return host name */
143 ssl_remotehost, /* return remote host name */
144 ssl_port, /* return port number */
145 ssl_localhost, /* return local host name */
146 ssl_getsize /* return needed number of bytes */
148 /* non-NIL if doing SSL primary I/O */
149 static SSLSTDIOSTREAM *sslstdio = NIL;
150 static char *start_tls = NIL; /* non-NIL if start TLS requested */
152 /* One-time SSL initialization */
154 static int sslonceonly = 0;
156 void ssl_onceonlyinit (void)
158 if (!sslonceonly++) { /* only need to call it once */
159 int fd;
160 char tmp[MAILTMPLEN];
161 struct stat sbuf;
162 /* if system doesn't have /dev/urandom */
163 if (stat ("/dev/urandom",&sbuf)) {
164 strcpy(tmp, "SSLXXXXXX");
165 while ((fd = mkstemp(tmp)) < 0) sleep (1);
166 fstat (fd,&sbuf); /* get information about the file */
167 close (fd); /* flush descriptor */
168 unlink (tmp); /* don't need the file */
169 /* not great but it'll have to do */
170 sprintf (tmp + strlen (tmp),"%.80s%lx%.80s%lx%lx%lx%lx%lx",
171 tcp_serveraddr (),(unsigned long) tcp_serverport (),
172 tcp_clientaddr (),(unsigned long) tcp_clientport (),
173 (unsigned long) sbuf.st_ino,(unsigned long) time (0),
174 (unsigned long) gethostid (),(unsigned long) getpid ());
175 RAND_seed (tmp,strlen (tmp));
177 /* apply runtime linkage */
178 mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
179 mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
180 #ifdef OPENSSL_1_1_0
181 OPENSSL_init_ssl(0, NULL);
182 #else
183 SSL_library_init (); /* add all algorithms */
184 #endif /* OPENSSL_1_1_0 */
188 /* SSL open
189 * Accepts: host name
190 * contact service name
191 * contact port number
192 * Returns: SSL stream if success else NIL
195 SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
197 TCPSTREAM *stream = tcp_open (host,service,port);
198 return stream ? ssl_start (stream,host,port) : NIL;
202 /* SSL authenticated open
203 * Accepts: host name
204 * service name
205 * returned user name buffer
206 * Returns: SSL stream if success else NIL
209 SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
211 return NIL; /* don't use this mechanism with SSL */
214 typedef struct ssl_disable_s {
215 int version;
216 long disable_code;
217 } SSL_DISABLE_S;
219 SSL_DISABLE_S ssl_disable[] = {
220 {SSL2_VERSION, SSL_OP_NO_SSLv2},
221 {SSL3_VERSION, SSL_OP_NO_SSLv3},
222 {TLS1_VERSION, SSL_OP_NO_TLSv1},
223 {TLS1_1_VERSION, SSL_OP_NO_TLSv1_1},
224 {TLS1_2_VERSION, SSL_OP_NO_TLSv1_2},
225 #ifdef TLS1_3_VERSION
226 {TLS1_3_VERSION, SSL_OP_NO_TLSv1_3},
227 #endif /* TLS1_3_VERSION */
228 {0, 0}
231 #define NUMBER_SSL_VERSIONS (sizeof(ssl_disable)/sizeof(ssl_disable[0]) - 1)
233 /* returns the mask to disable a specific version.
234 * If version not found, returns 0.
236 * Arguments: version, and direction.
237 * If direction is -1, returns mask to disable versions less than given version.
238 * If direction is +1, returns mask to disable versions bigger than given version.
240 long ssl_disable_mask(int ssl_version, int direction)
242 int rv = 0;
243 int i;
244 for(i = 0; ssl_disable[i].version != 0
245 && ssl_disable[i].version != ssl_version; i++);
246 if(i == 0
247 || i == NUMBER_SSL_VERSIONS - 1
248 || ssl_disable[i].version == 0)
249 return rv;
250 i += direction; /* move in the direction */
251 for(; i >= 0 && i <= NUMBER_SSL_VERSIONS - 1; i += direction)
252 rv |= ssl_disable[i].disable_code;
253 return rv;
257 /* ssl_connect_mthd: returns a context pointer to the connection to
258 * a ssl server
260 const SSL_METHOD *ssl_connect_mthd(int flag, int *minv, int *maxv)
262 int client_request;
263 client_request = (flag & NET_TRYTLS1) ? TLS1_VERSION
264 : (flag & NET_TRYTLS1_1) ? TLS1_1_VERSION
265 : (flag & NET_TRYTLS1_2) ? TLS1_2_VERSION
266 #ifdef TLS1_3_VERSION
267 : (flag & NET_TRYTLS1_3) ? TLS1_3_VERSION
268 #else
269 : (flag & NET_TRYTLS1_3) ? -2
270 #endif
271 : 0;
273 *minv = *(int *) mail_parameters(NULL, GET_ENCRYPTION_RANGE_MIN, NULL);
274 *maxv = *(int *) mail_parameters(NULL, GET_ENCRYPTION_RANGE_MAX, NULL);
277 * if no special request, negotiate the maximum the client is configured
278 * to negotiate
280 if(client_request == 0)
281 client_request = *maxv;
283 if(client_request < *minv || client_request > *maxv)
284 return NIL; /* out of range? bail out */
286 /* Some Linux distributors seem to believe that it is ok to disable some of
287 * these methods for their users, so we have to test that every requested
288 * method has actually been compiled in into their openssl/libressl library.
289 * Oh well...
291 #ifndef OPENSSL_1_1_0
292 if(client_request == SSL3_VERSION)
293 #ifndef OPENSSL_NO_SSL3_METHOD
294 return SSLv3_client_method();
295 #else
296 return NIL;
297 #endif /* OPENSSL_NO_SSL3_METHOD */
298 else if(client_request == TLS1_VERSION)
299 #ifndef OPENSSL_NO_TLS1_METHOD
300 return TLSv1_client_method();
301 #else
302 return NIL;
303 #endif /* OPENSSL_NO_TLS1_METHOD */
304 else if(client_request == TLS1_1_VERSION)
305 #ifndef OPENSSL_NO_TLS1_1_METHOD
306 return TLSv1_1_client_method();
307 #else
308 return NIL;
309 #endif /* OPENSSL_NO_TLS1_1_METHOD */
310 else if(client_request == TLS1_2_VERSION)
311 #ifndef OPENSSL_NO_TLS1_2_METHOD
312 return TLSv1_2_client_method();
313 #else
314 return NIL;
315 #endif /* OPENSSL_NO_TLS1_2_METHOD */
316 #ifdef TLS1_3_VERSION /* this is only reachable if TLS1_3 support exists */
317 else if(client_request == TLS1_3_VERSION)
318 #ifndef OPENSSL_NO_TLS1_3_METHOD
319 return TLS_client_method();
320 #else
321 return NIL;
322 #endif /* #ifndef OPENSSL_NO_TLS1_2_METHOD */
323 #endif /* TLS1_3_VERSION */
324 #endif /* ifndef OPENSSL_1_1_0 */
326 return SSLv23_client_method();
329 /* Start SSL/TLS negotiations
330 * Accepts: open TCP stream of session
331 * user's host name
332 * flags
333 * Returns: SSL stream if success else NIL
336 static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
338 char *reason,tmp[MAILTMPLEN];
339 sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
340 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
341 void *data = (*bn) (BLOCK_SENSITIVE,NIL);
342 SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
343 sizeof (SSLSTREAM));
344 stream->tcpstream = tstream; /* bind TCP stream */
345 /* do the work */
346 reason = ssl_start_work (stream,host,flags);
347 (*bn) (BLOCK_NONSENSITIVE,data);
348 if (reason) { /* failed? */
349 ssl_close (stream); /* failed to do SSL */
350 stream = NIL; /* no stream returned */
351 switch (*reason) { /* analyze reason */
352 case '*': /* certificate failure */
353 ++reason; /* skip over certificate failure indication */
354 /* pass to error callback */
355 if (sf) (*sf) (host,reason,flags);
356 else { /* no error callback, build error message */
357 sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
358 mm_log (tmp,ERROR);
360 case '\0': /* user answered no to certificate callback */
361 if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
362 stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
363 sizeof (SSLSTREAM));
364 break;
365 default: /* non-certificate failure */
366 if (flags & NET_TRYSSL); /* no error output if tryssl */
367 /* pass to error callback */
368 else if (sf) (*sf) (host,reason,flags);
369 else { /* no error callback, build error message */
370 sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
371 mm_log (tmp,ERROR);
373 break;
376 return stream;
379 /* Start SSL/TLS negotiations worker routine
380 * Accepts: SSL stream
381 * user's host name
382 * flags
383 * Returns: NIL if success, else error reason
386 /* evil but I had no choice */
387 static char *ssl_last_error = NIL;
388 static char *ssl_last_host = NIL;
390 static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
392 BIO *bio;
393 X509 *cert;
394 unsigned long sl,tl;
395 int minv, maxv;
396 long masklow, maskhigh;
397 char *s,*t,*err,tmp[MAILTMPLEN], buf[256];
398 char *CAfile, *CApath, *ciphers;
399 sslcertificatequery_t scq =
400 (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
401 sslclientcert_t scc =
402 (sslclientcert_t) mail_parameters (NIL,GET_SSLCLIENTCERT,NIL);
403 sslclientkey_t sck =
404 (sslclientkey_t) mail_parameters (NIL,GET_SSLCLIENTKEY,NIL);
405 if (ssl_last_error) fs_give ((void **) &ssl_last_error);
406 ssl_last_host = host;
407 if (!(stream->context = SSL_CTX_new (ssl_connect_mthd(flags, &minv, &maxv))))
408 return "SSL context failed";
409 SSL_CTX_set_options (stream->context,0);
410 masklow = ssl_disable_mask(minv, -1);
411 maskhigh = ssl_disable_mask(maxv, 1);
412 SSL_CTX_set_options(stream->context, masklow|maskhigh);
413 /* disable certificate validation? */
414 if (flags & NET_NOVALIDATECERT)
415 SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL);
416 else SSL_CTX_set_verify (stream->context,SSL_VERIFY_PEER,ssl_open_verify);
417 /* set cipher list */
418 ciphers = (char *) mail_parameters (NIL,GET_SSLCIPHERS,NIL);
419 if(ciphers != NIL
420 && *ciphers != '\0'
421 && !SSL_CTX_set_cipher_list (stream->context,ciphers))
422 return "No listed ciphers recognized";
423 /* if a non-standard path desired */
424 CAfile = (char *) mail_parameters (NIL,GET_SSLCAFILE,NIL);
425 CApath = (char *) mail_parameters (NIL,GET_SSLCAPATH,NIL);
426 if (CAfile != NIL || CApath != NIL)
427 SSL_CTX_load_verify_locations (stream->context, CAfile, CApath);
428 else /* set default paths to CAs... */
429 SSL_CTX_set_default_verify_paths (stream->context);
430 /* Load app certificates */
431 CAfile = (char *) mail_parameters (NIL,GET_SSLAPPCAFILE,NIL);
432 CApath = (char *) mail_parameters (NIL,GET_SSLAPPCAPATH,NIL);
433 if (CAfile != NIL || CApath != NIL)
434 SSL_CTX_load_verify_locations (stream->context, CAfile, CApath);
435 /* want to send client certificate? */
436 if (scc && (s = (*scc) ()) && (sl = strlen (s))) {
437 if ((cert = PEM_read_bio_X509 (bio = BIO_new_mem_buf (s,sl),NIL,NIL,NIL)) != NULL) {
438 SSL_CTX_use_certificate (stream->context,cert);
439 X509_free (cert);
441 BIO_free (bio);
442 if (!cert) return "SSL client certificate failed";
443 /* want to supply private key? */
444 if ((t = (sck ? (*sck) () : s)) && (tl = strlen (t))) {
445 EVP_PKEY *key;
446 if ((key = PEM_read_bio_PrivateKey (bio = BIO_new_mem_buf (t,tl),
447 NIL,NIL,"")) != NULL) {
448 SSL_CTX_use_PrivateKey (stream->context,key);
449 EVP_PKEY_free (key);
451 BIO_free (bio);
452 memset (t,0,tl); /* erase key */
454 if (s != t) memset (s,0,sl);/* erase certificate if different from key */
457 /* create connection */
458 if (!(stream->con = (SSL *) SSL_new (stream->context)))
459 return "SSL connection failed";
460 if (host && !SSL_set_tlsext_host_name(stream->con, host)){
461 return "Server Name Identification (SNI) failed";
463 bio = BIO_new_socket (stream->tcpstream->tcpsi,BIO_NOCLOSE);
464 SSL_set_bio (stream->con,bio,bio);
465 SSL_set_connect_state (stream->con);
466 if (SSL_in_init (stream->con)) SSL_total_renegotiations (stream->con);
467 /* now negotiate SSL */
468 if (SSL_write (stream->con,"",0) < 0)
469 return ssl_last_error ? ssl_last_error : "SSL negotiation failed";
470 /* need to validate host names? */
471 cert = SSL_get_peer_certificate (stream->con);
472 if (!(flags & NET_NOVALIDATECERT) &&
473 (err = ssl_validate_cert (cert, host))) {
474 /* application callback */
475 X509_NAME_oneline (X509_get_subject_name(cert), buf, sizeof(buf));
476 if (scq) return (*scq) (err,host,cert ? buf : "???") ? NIL : "";
477 /* error message to return via mm_log() */
478 sprintf (tmp,"*%.128s: %.255s",err,cert ? buf : "???");
479 X509_free(cert);
480 return ssl_last_error = cpystr (tmp);
482 X509_free(cert);
483 return NIL;
486 /* SSL certificate verification callback
487 * Accepts: error flag
488 * X509 context
489 * Returns: error flag
492 static int ssl_open_verify (int ok,X509_STORE_CTX *ctx)
494 char *err,cert[256],tmp[MAILTMPLEN];
495 sslcertificatequery_t scq =
496 (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
497 if (!ok) { /* in case failure */
498 err = (char *) X509_verify_cert_error_string
499 (X509_STORE_CTX_get_error (ctx));
500 X509_NAME_oneline (X509_get_subject_name
501 (X509_STORE_CTX_get_current_cert (ctx)),cert,255);
502 if (!scq) { /* mm_log() error message if no callback */
503 sprintf (tmp,"*%.128s: %.255s",err,cert);
504 ssl_last_error = cpystr (tmp);
506 /* ignore error if application says to */
507 else if ((*scq) (err,ssl_last_host,cert)) ok = T;
508 /* application wants punt */
509 else ssl_last_error = cpystr ("");
511 return ok;
515 /* SSL validate certificate
516 * Accepts: certificate
517 * host to validate against
518 * Returns: NIL if validated, else string of error message
521 static char *ssl_validate_cert (X509 *cert,char *host)
523 int i,j,n, m = 0;
524 char *s=NULL,*t,*ret = NIL;
525 void *ext = NIL;
526 GENERAL_NAME *name;
527 X509_NAME *cname;
528 X509_NAME_ENTRY *e;
529 char buf[256];
530 /* make sure have a certificate */
531 if (!cert) return "No certificate from server";
532 /* Method 1: locate CN */
533 #ifndef OPENSSL_1_1_0
534 if (cert->name == NIL)
535 ret = "No name in certificate";
536 else if ((s = strstr (cert->name,"/CN=")) != NIL) {
537 m++; /* count that we tried this method */
538 if ((t = strchr (s += 4,'/')) != NIL) *t = '\0';
539 /* host name matches pattern? */
540 ret = ssl_compare_hostnames (host,s) ? NIL :
541 "Server name does not match certificate";
542 if (t) *t = '/'; /* restore smashed delimiter */
543 /* if mismatch, see if in extensions */
544 if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
545 (n = sk_GENERAL_NAME_num (ext)))
546 /* older versions of OpenSSL use "ia5" instead of dNSName */
547 for (i = 0; ret && (i < n); i++)
548 if ((name = sk_GENERAL_NAME_value (ext,i)) &&
549 (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
550 ssl_compare_hostnames (host,s)) ret = NIL;
551 if(ext) GENERAL_NAMES_free(ext);
553 #endif /* OPENSSL_1_1_0 */
554 /* Method 2, use cname */
555 if(m == 0 || ret != NIL){
556 cname = X509_get_subject_name(cert);
557 for(j = 0, ret = NIL; j < X509_NAME_entry_count(cname) && ret == NIL; j++){
558 if((e = X509_NAME_get_entry(cname, j)) != NULL){
559 X509_NAME_get_text_by_OBJ(cname, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
560 s = (char *) buf;
562 else s = NIL;
563 if (s != NIL) {
564 /* host name matches pattern? */
565 ret = ssl_compare_hostnames (host,s) ? NIL :
566 "Server name does not match certificate";
567 ext = NIL;
568 /* if mismatch, see if in extensions */
569 if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
570 (n = sk_GENERAL_NAME_num (ext)))
571 /* older versions of OpenSSL use "ia5" instead of dNSName */
572 for (i = 0; ret && (i < n); i++)
573 if ((name = sk_GENERAL_NAME_value (ext,i)) &&
574 (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
575 ssl_compare_hostnames (host,s)) ret = NIL;
576 if(ext) GENERAL_NAMES_free(ext);
581 if (ret == NIL
582 #ifndef OPENSSL_1_1_0
583 && !cert->name
584 #endif /* OPENSSL_1_1_0 */
585 && !X509_get_subject_name(cert))
586 ret = "No name in certificate";
588 if (ret == NIL && s == NIL)
589 ret = "Unable to locate common name in certificate";
591 return ret;
594 /* Case-independent wildcard pattern match
595 * Accepts: base string
596 * pattern string
597 * Returns: T if pattern matches base, else NIL
600 static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat)
602 long ret = NIL;
603 switch (*pat) {
604 case '*': /* wildcard */
605 if (pat[1]) { /* there must be a pattern suffix */
606 /* there is, scan base against it */
607 do if (ssl_compare_hostnames (s,pat+1)) ret = LONGT;
608 while (!ret && (*s != '.') && *s++);
610 break;
611 case '\0': /* end of pattern */
612 if (!*s) ret = LONGT; /* success if base is also at end */
613 break;
614 default: /* non-wildcard, recurse if match */
615 if (!compare_uchar (*pat,*s)) ret = ssl_compare_hostnames (s+1,pat+1);
616 break;
618 return ret;
621 /* SSL receive line
622 * Accepts: SSL stream
623 * Returns: text line string or NIL if failure
626 char *ssl_getline (SSLSTREAM *stream)
628 unsigned long n,contd;
629 char *ret = ssl_getline_work (stream,&n,&contd);
630 if (ret && contd) { /* got a line needing continuation? */
631 STRINGLIST *stl = mail_newstringlist ();
632 STRINGLIST *stc = stl;
633 do { /* collect additional lines */
634 stc->text.data = (unsigned char *) ret;
635 stc->text.size = n;
636 stc = stc->next = mail_newstringlist ();
637 ret = ssl_getline_work (stream,&n,&contd);
638 } while (ret && contd);
639 if (ret) { /* stash final part of line on list */
640 stc->text.data = (unsigned char *) ret;
641 stc->text.size = n;
642 /* determine how large a buffer we need */
643 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
644 ret = fs_get (n + 1); /* copy parts into buffer */
645 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
646 memcpy (ret + n,stc->text.data,stc->text.size);
647 ret[n] = '\0';
649 mail_free_stringlist (&stl);/* either way, done with list */
651 return ret;
654 char *ssl_getsize (SSLSTREAM *stream, unsigned long size)
656 char *ret = NIL;
657 unsigned long got = 0L, need = size, n;
659 do {
660 if(!ssl_getdata (stream)) return ret; /* return what we have */
661 n = stream->ictr < need ? stream->ictr : need;
662 fs_resize((void **) &ret, (got + n + 1)*sizeof(char));
663 memcpy(ret + got, stream->iptr, n);
664 ret[got+n] = '\0';
665 got += n;
666 need -= n;
667 stream->iptr += n;
668 stream->ictr -= n;
669 } while (need > 0);
671 return ret;
673 /* SSL receive line or partial line
674 * Accepts: SSL stream
675 * pointer to return size
676 * pointer to return continuation flag
677 * Returns: text line string, size and continuation flag, or NIL if failure
680 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
681 long *contd)
683 unsigned long n;
684 char *s,*ret,c,d;
685 *contd = NIL; /* assume no continuation */
686 /* make sure have data */
687 if (!ssl_getdata (stream)) return NIL;
688 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
689 d = *stream->iptr++; /* slurp another character */
690 if ((c == '\015') && (d == '\012')) {
691 ret = (char *) fs_get (n--);
692 memcpy (ret,s,*size = n); /* copy into a free storage string */
693 ret[n] = '\0'; /* tie off string with null */
694 return ret;
697 /* copy partial string from buffer */
698 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
699 /* get more data from the net */
700 if (!ssl_getdata (stream)) fs_give ((void **) &ret);
701 /* special case of newline broken by buffer */
702 else if ((c == '\015') && (*stream->iptr == '\012')) {
703 stream->iptr++; /* eat the line feed */
704 stream->ictr--;
705 ret[*size = --n] = '\0'; /* tie off string with null */
707 else *contd = LONGT; /* continuation needed */
708 return ret;
711 /* SSL receive buffer
712 * Accepts: SSL stream
713 * size in bytes
714 * buffer to read into
715 * Returns: T if success, NIL otherwise
718 long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
720 unsigned long n;
721 while (size > 0) { /* until request satisfied */
722 if (!ssl_getdata (stream)) return NIL;
723 n = min (size,stream->ictr);/* number of bytes to transfer */
724 /* do the copy */
725 memcpy (buffer,stream->iptr,n);
726 buffer += n; /* update pointer */
727 stream->iptr += n;
728 size -= n; /* update # of bytes to do */
729 stream->ictr -= n;
731 buffer[0] = '\0'; /* tie off string */
732 return T;
735 /* SSL receive data
736 * Accepts: TCP/IP stream
737 * Returns: T if success, NIL otherwise
740 long ssl_getdata (SSLSTREAM *stream)
742 int i,sock;
743 fd_set fds,efds;
744 struct timeval tmo;
745 tcptimeout_t tmoh = (tcptimeout_t) mail_parameters (NIL,GET_TIMEOUT,NIL);
746 long ttmo_read = (long) mail_parameters (NIL,GET_READTIMEOUT,NIL);
747 time_t t = time (0);
748 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
749 if (!stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return NIL;
750 /* tcp_unix should have prevented this */
751 if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
752 (*bn) (BLOCK_TCPREAD,NIL);
753 while (stream->ictr < 1) { /* if nothing in the buffer */
754 time_t tl = time (0); /* start of request */
755 time_t now = tl;
756 int ti = ttmo_read ? now + ttmo_read : 0;
757 if (SSL_pending (stream->con)) i = 1;
758 else {
759 if (tcpdebug) mm_log ("Reading SSL data",TCPDEBUG);
760 tmo.tv_usec = 0;
761 FD_ZERO (&fds); /* initialize selection vector */
762 FD_ZERO (&efds); /* handle errors too */
763 FD_SET (sock,&fds); /* set bit in selection vector */
764 FD_SET (sock,&efds); /* set bit in error selection vector */
765 errno = NIL; /* block and read */
766 do { /* block under timeout */
767 tmo.tv_sec = ti ? ti - now : 0;
768 i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
769 now = time (0); /* fake timeout if interrupt & time expired */
770 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
771 } while ((i < 0) && (errno == EINTR));
773 if (i) { /* non-timeout result from select? */
774 errno = 0; /* just in case */
775 if (i > 0) /* read what we can */
776 while (((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) < 0) &&
777 ((errno == EINTR) ||
778 (SSL_get_error (stream->con,i) == SSL_ERROR_WANT_READ)));
779 if (i <= 0) { /* error seen? */
780 if (tcpdebug) {
781 char *s,tmp[MAILTMPLEN];
782 if (i) sprintf (s = tmp,"SSL data read I/O error %d SSL error %d",
783 errno,SSL_get_error (stream->con,i));
784 else s = "SSL data read end of file";
785 mm_log (s,TCPDEBUG);
787 return ssl_abort (stream);
789 stream->iptr = stream->ibuf;/* point at TCP buffer */
790 stream->ictr = i; /* set new byte count */
791 if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG);
793 /* timeout, punt unless told not to */
794 else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->tcpstream->host)) {
795 if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG);
796 return ssl_abort (stream);
799 (*bn) (BLOCK_NONE,NIL);
800 return T;
803 /* SSL send string as record
804 * Accepts: SSL stream
805 * string pointer
806 * Returns: T if success else NIL
809 long ssl_soutr (SSLSTREAM *stream,char *string)
811 return ssl_sout (stream,string,(unsigned long) strlen (string));
815 /* SSL send string
816 * Accepts: SSL stream
817 * string pointer
818 * byte count
819 * Returns: T if success else NIL
822 long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
824 long i;
825 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
826 if (!stream->con) return NIL;
827 (*bn) (BLOCK_TCPWRITE,NIL);
828 if (tcpdebug) mm_log ("Writing to SSL",TCPDEBUG);
829 /* until request satisfied */
830 for (i = 0; size > 0; string += i,size -= i)
831 /* write as much as we can */
832 if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) < 0) {
833 if (tcpdebug) {
834 char tmp[MAILTMPLEN];
835 sprintf (tmp,"SSL data write I/O error %d SSL error %d",
836 errno,SSL_get_error (stream->con,i));
837 mm_log (tmp,TCPDEBUG);
839 return ssl_abort (stream);/* write failed */
841 if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
842 (*bn) (BLOCK_NONE,NIL);
843 return LONGT; /* all done */
846 /* SSL close
847 * Accepts: SSL stream
850 void ssl_close (SSLSTREAM *stream)
852 ssl_abort (stream); /* nuke the stream */
853 fs_give ((void **) &stream); /* flush the stream */
857 /* SSL abort stream
858 * Accepts: SSL stream
859 * Returns: NIL always
862 static long ssl_abort (SSLSTREAM *stream)
864 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
865 if (stream->con) { /* close SSL connection */
866 SSL_shutdown (stream->con);
867 SSL_free (stream->con);
868 stream->con = NIL;
870 if (stream->context) { /* clean up context */
871 SSL_CTX_free (stream->context);
872 stream->context = NIL;
874 if (stream->tcpstream) { /* close TCP stream */
875 tcp_close (stream->tcpstream);
876 stream->tcpstream = NIL;
878 (*bn) (BLOCK_NONE,NIL);
879 return NIL;
882 /* SSL get host name
883 * Accepts: SSL stream
884 * Returns: host name for this stream
887 char *ssl_host (SSLSTREAM *stream)
889 return stream ? tcp_host (stream->tcpstream) : "UNKNOWN";
893 /* SSL get remote host name
894 * Accepts: SSL stream
895 * Returns: host name for this stream
898 char *ssl_remotehost (SSLSTREAM *stream)
900 return tcp_remotehost (stream->tcpstream);
904 /* SSL return port for this stream
905 * Accepts: SSL stream
906 * Returns: port number for this stream
909 unsigned long ssl_port (SSLSTREAM *stream)
911 return tcp_port (stream->tcpstream);
915 /* SSL get local host name
916 * Accepts: SSL stream
917 * Returns: local host name
920 char *ssl_localhost (SSLSTREAM *stream)
922 return tcp_localhost (stream->tcpstream);
925 /* Start TLS
926 * Accepts: /etc/services service name
927 * Returns: cpystr'd error string if TLS failed, else NIL for success
930 char *ssl_start_tls (char *server)
932 char tmp[MAILTMPLEN];
933 struct stat sbuf;
934 if (sslstdio) return cpystr ("Already in an SSL session");
935 if (start_tls) return cpystr ("TLS already started");
936 if (server) { /* build specific certificate/key file name */
937 sprintf (tmp,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
938 if (stat (tmp,&sbuf)) { /* use non-specific name if no specific file */
939 sprintf (tmp,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
940 if (stat (tmp,&sbuf)) return cpystr ("Server certificate not installed");
942 start_tls = server; /* switch to STARTTLS mode */
944 return NIL;
947 /* Init server for SSL
948 * Accepts: server name
951 void ssl_server_init (char *server)
953 char cert[MAILTMPLEN],key[MAILTMPLEN];
954 unsigned long i;
955 struct stat sbuf;
956 SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
957 sizeof (SSLSTREAM));
958 ssl_onceonlyinit (); /* make sure algorithms added */
959 #ifdef OPENSSL_1_1_0
960 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
961 OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
962 #else
963 ERR_load_crypto_strings ();
964 SSL_load_error_strings ();
965 #endif /* OPENSSL_1_1_0 */
966 /* build specific certificate/key file names */
967 sprintf (cert,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
968 sprintf (key,"%s/%s-%s.pem",SSL_KEY_DIRECTORY,server,tcp_serveraddr ());
969 /* use non-specific name if no specific cert */
970 if (stat (cert,&sbuf)) sprintf (cert,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
971 if (stat (key,&sbuf)) { /* use non-specific name if no specific key */
972 sprintf (key,"%s/%s.pem",SSL_KEY_DIRECTORY,server);
973 /* use cert file as fallback for key */
974 if (stat (key,&sbuf)) strcpy (key,cert);
976 /* create context */
977 #ifdef OPENSSL_1_1_0
978 if (!(stream->context = SSL_CTX_new (start_tls ?
979 TLS_server_method () :
980 SSLv23_server_method ())))
981 #else
982 if (!(stream->context = SSL_CTX_new (start_tls ?
983 TLSv1_server_method () :
984 SSLv23_server_method ())))
985 #endif /* OPENSSL_1_1_0 */
986 syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s",
987 tcp_clienthost ());
988 else { /* set context options */
989 SSL_CTX_set_options (stream->context,SSL_OP_ALL);
990 /* set cipher list */
991 if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST))
992 syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s",
993 SSLCIPHERLIST,tcp_clienthost ());
994 /* load certificate */
995 else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert))
996 syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s",
997 cert,tcp_clienthost ());
998 /* load key */
999 else if (!(SSL_CTX_use_RSAPrivateKey_file (stream->context,key,
1000 SSL_FILETYPE_PEM)))
1001 syslog (LOG_ALERT,"Unable to load private key from %.80s, host=%.80s",
1002 key,tcp_clienthost ());
1004 else { /* generate key if needed */
1005 #ifdef OPENSSL_1_1_0
1006 if (0)
1007 ssl_genkey(stream->context, 0, 0);
1008 #else
1009 if (SSL_CTX_need_tmp_RSA (stream->context))
1010 SSL_CTX_set_tmp_rsa_callback (stream->context,ssl_genkey);
1011 #endif /* OPENSSL_1_1_0 */
1012 /* create new SSL connection */
1013 if (!(stream->con = SSL_new (stream->context)))
1014 syslog (LOG_ALERT,"Unable to create SSL connection, host=%.80s",
1015 tcp_clienthost ());
1016 else { /* set file descriptor */
1017 SSL_set_fd (stream->con,0);
1018 /* all OK if accepted */
1019 if (SSL_accept (stream->con) < 0)
1020 syslog (LOG_INFO,"Unable to accept SSL connection, host=%.80s",
1021 tcp_clienthost ());
1022 else { /* server set up */
1023 sslstdio = (SSLSTDIOSTREAM *)
1024 memset (fs_get (sizeof(SSLSTDIOSTREAM)),0,sizeof (SSLSTDIOSTREAM));
1025 sslstdio->sslstream = stream;
1026 /* available space in output buffer */
1027 sslstdio->octr = SSLBUFLEN;
1028 /* current output buffer pointer */
1029 sslstdio->optr = sslstdio->obuf;
1030 /* allow plaintext if disable value was 2 */
1031 if ((long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) > 1)
1032 mail_parameters (NIL,SET_DISABLEPLAINTEXT,NIL);
1033 /* unhide PLAIN SASL authenticator */
1034 mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"PLAIN");
1035 mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"LOGIN");
1036 return;
1041 while ((i = ERR_get_error ()) != 0L) /* SSL failure */
1042 syslog (LOG_ERR,"SSL error status: %.80s",ERR_error_string (i,NIL));
1043 ssl_close (stream); /* punt stream */
1044 exit (1); /* punt this program too */
1047 /* Generate one-time key for server
1048 * Accepts: SSL connection
1049 * export flag
1050 * keylength
1051 * Returns: generated key, always
1054 static RSA *ssl_genkey (SSL_CTX_TYPE *con,int export,int keylength)
1056 unsigned long i;
1057 static RSA *key = NIL;
1058 if (!key) { /* if don't have a key already */
1059 /* generate key */
1060 #ifdef OPENSSL_1_1_0
1061 BIGNUM *e = BN_new();
1062 if (!RSA_generate_key_ex (key, export ? keylength : 1024, e,NIL)) {
1063 #else
1064 if (!(key = RSA_generate_key (export ? keylength : 1024,RSA_F4,NIL,NIL))) {
1065 #endif /* OPENSSL_1_1_0 */
1066 syslog (LOG_ALERT,"Unable to generate temp key, host=%.80s",
1067 tcp_clienthost ());
1068 while ((i = ERR_get_error ()) != 0L)
1069 syslog (LOG_ALERT,"SSL error status: %s",ERR_error_string (i,NIL));
1070 exit (1);
1072 #ifdef OPENSSL_1_1_0
1073 BN_free(e);
1074 e = NULL;
1075 #endif /* OPENSSL_1_1_0 */
1077 return key;
1080 /* Wait for stdin input
1081 * Accepts: timeout in seconds
1082 * Returns: T if have input on stdin, else NIL
1085 long ssl_server_input_wait (long seconds)
1087 int i,sock;
1088 fd_set fds,efd;
1089 struct timeval tmo;
1090 SSLSTREAM *stream;
1091 if (!sslstdio) return server_input_wait (seconds);
1092 /* input available in buffer */
1093 if (((stream = sslstdio->sslstream)->ictr > 0) ||
1094 !stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return LONGT;
1095 /* sock ought to be 0 always */
1096 if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
1097 /* input available from SSL */
1098 if (SSL_pending (stream->con) &&
1099 ((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) > 0)) {
1100 stream->iptr = stream->ibuf;/* point at TCP buffer */
1101 stream->ictr = i; /* set new byte count */
1102 return LONGT;
1104 FD_ZERO (&fds); /* initialize selection vector */
1105 FD_ZERO (&efd); /* initialize selection vector */
1106 FD_SET (sock,&fds); /* set bit in selection vector */
1107 FD_SET (sock,&efd); /* set bit in selection vector */
1108 tmo.tv_sec = seconds; tmo.tv_usec = 0;
1109 /* see if input available from the socket */
1110 return select (sock+1,&fds,0,&efd,&tmo) ? LONGT : NIL;
1113 #include "sslstdio.c"