* Rewrite support for specific SSL encryption protocols, including
[alpine.git] / imap / src / osdep / unix / ssl_unix.c
blob11454cbdf15f2abfca6461adf47c1ec0b61c679c
1 /* ========================================================================
2 * Copyright 2008-2009 Mark Crispin
3 * ========================================================================
4 */
6 /*
7 * Program: SSL authentication/encryption module
9 * Author: Mark Crispin
11 * Date: 22 September 1998
12 * Last Edited: 8 November 2009
14 * Previous versions of this file were
16 * Copyright 1988-2008 University of Washington
18 * Licensed under the Apache License, Version 2.0 (the "License");
19 * you may not use this file except in compliance with the License.
20 * You may obtain a copy of the License at
22 * http://www.apache.org/licenses/LICENSE-2.0
26 #define crypt ssl_private_crypt
27 #define STRING OPENSSL_STRING
28 #include <x509v3.h>
29 #include <ssl.h>
30 #include <err.h>
31 #include <pem.h>
32 #include <buffer.h>
33 #include <bio.h>
34 #include <crypto.h>
35 #include <rand.h>
36 #ifdef OPENSSL_1_1_0
37 #include <rsa.h>
38 #include <bn.h>
39 #endif /* OPENSSL_1_1_0 */
40 #undef STRING
41 #undef crypt
43 #define SSLBUFLEN 8192
46 * PCI auditing compliance, disable:
47 * SSLv2
48 * anonymous D-H (no certificate
49 * export encryption ciphers (40 and 56 bits)
50 * low encryption cipher suites (40 and 56 bits, excluding export)
51 * null encryption (disabling implied by "ALL")
53 * UW imapd just disables low-grade and null ("ALL:!LOW"). This setting
54 * will break clients that attempt to use the newly-prohibited mechanisms.
56 * I question the value of disabling SSLv2, as opposed to disabling the SSL
57 * ports (e.g., 993 for IMAP, 995 for POP3) and using TLS exclusively.
60 #define SSLCIPHERLIST "ALL:!SSLv2:!ADH:!EXP:!LOW"
62 /* SSL I/O stream */
64 typedef struct ssl_stream {
65 TCPSTREAM *tcpstream; /* TCP stream */
66 SSL_CTX *context; /* SSL context */
67 SSL *con; /* SSL connection */
68 int ictr; /* input counter */
69 char *iptr; /* input pointer */
70 char ibuf[SSLBUFLEN]; /* input buffer */
71 } SSLSTREAM;
73 #include "sslio.h"
75 /* Function prototypes */
76 int ssl_disable_mask(int ssl_version, int direction);
77 const SSL_METHOD *ssl_connect_mthd(int flag, int *min, int *max);
78 static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
79 static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags);
80 static int ssl_open_verify (int ok,X509_STORE_CTX *ctx);
81 static char *ssl_validate_cert (X509 *cert,char *host);
82 static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat);
83 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
84 long *contd);
85 static long ssl_abort (SSLSTREAM *stream);
87 #ifdef OPENSSL_1_1_0
88 #define SSL_CTX_TYPE SSL_CTX
89 #else
90 #define SSL_CTX_TYPE SSL
91 #endif /* OPENSSL_1_1_0 */
93 static RSA *ssl_genkey (SSL_CTX_TYPE *con,int export,int keylength);
96 /* Secure Sockets Layer network driver dispatch */
98 static struct ssl_driver ssldriver = {
99 ssl_open, /* open connection */
100 ssl_aopen, /* open preauthenticated connection */
101 ssl_getline, /* get a line */
102 ssl_getbuffer, /* get a buffer */
103 ssl_soutr, /* output pushed data */
104 ssl_sout, /* output string */
105 ssl_close, /* close connection */
106 ssl_host, /* return host name */
107 ssl_remotehost, /* return remote host name */
108 ssl_port, /* return port number */
109 ssl_localhost /* return local host name */
111 /* non-NIL if doing SSL primary I/O */
112 static SSLSTDIOSTREAM *sslstdio = NIL;
113 static char *start_tls = NIL; /* non-NIL if start TLS requested */
115 /* One-time SSL initialization */
117 static int sslonceonly = 0;
119 void ssl_onceonlyinit (void)
121 if (!sslonceonly++) { /* only need to call it once */
122 int fd;
123 char tmp[MAILTMPLEN];
124 struct stat sbuf;
125 /* if system doesn't have /dev/urandom */
126 if (stat ("/dev/urandom",&sbuf)) {
127 strcpy(tmp, "SSLXXXXXX");
128 while ((fd = mkstemp(tmp)) < 0) sleep (1);
129 unlink (tmp); /* don't need the file */
130 fstat (fd,&sbuf); /* get information about the file */
131 close (fd); /* flush descriptor */
132 /* not great but it'll have to do */
133 sprintf (tmp + strlen (tmp),"%.80s%lx%.80s%lx%lx%lx%lx%lx",
134 tcp_serveraddr (),(unsigned long) tcp_serverport (),
135 tcp_clientaddr (),(unsigned long) tcp_clientport (),
136 (unsigned long) sbuf.st_ino,(unsigned long) time (0),
137 (unsigned long) gethostid (),(unsigned long) getpid ());
138 RAND_seed (tmp,strlen (tmp));
140 /* apply runtime linkage */
141 mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
142 mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
143 #ifdef OPENSSL_1_1_0
144 OPENSSL_init_ssl(0, NULL);
145 #else
146 SSL_library_init (); /* add all algorithms */
147 #endif /* OPENSSL_1_1_0 */
151 /* SSL open
152 * Accepts: host name
153 * contact service name
154 * contact port number
155 * Returns: SSL stream if success else NIL
158 SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
160 TCPSTREAM *stream = tcp_open (host,service,port);
161 return stream ? ssl_start (stream,host,port) : NIL;
165 /* SSL authenticated open
166 * Accepts: host name
167 * service name
168 * returned user name buffer
169 * Returns: SSL stream if success else NIL
172 SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
174 return NIL; /* don't use this mechanism with SSL */
177 typedef struct ssl_disable_s {
178 int version;
179 int disable_code;
180 } SSL_DISABLE_S;
182 SSL_DISABLE_S ssl_disable[] = {
183 {SSL2_VERSION, SSL_OP_NO_SSLv2},
184 {SSL3_VERSION, SSL_OP_NO_SSLv3},
185 {TLS1_VERSION, SSL_OP_NO_TLSv1},
186 {TLS1_1_VERSION, SSL_OP_NO_TLSv1_1},
187 {TLS1_2_VERSION, SSL_OP_NO_TLSv1_2},
188 #ifdef TLS1_3_VERSION
189 {TLS1_3_VERSION, SSL_OP_NO_TLSv1_3},
190 #endif /* TLS1_3_VERSION */
191 {0, 0}
194 #define NUMBER_SSL_VERSIONS (sizeof(ssl_disable)/sizeof(ssl_disable[0]) - 1)
196 /* returns the mask to disable a specific version.
197 * If version not found, returns 0.
199 * Arguments: version, and direction.
200 * If direction is -1, returns mask to disable versions less than given version.
201 * If direction is +1, returns mask to disable versions bigger than given version.
203 int ssl_disable_mask(int ssl_version, int direction)
205 int rv = 0;
206 int i;
207 for(i = 0; ssl_disable[i].version != 0
208 && ssl_disable[i].version != ssl_version; i++);
209 if(i == 0
210 || i == NUMBER_SSL_VERSIONS - 1
211 || ssl_disable[i].version == 0)
212 return rv;
213 i += direction; /* move in the direction */
214 for(; i >= 0 && i <= NUMBER_SSL_VERSIONS - 1; i += direction)
215 rv |= ssl_disable[i].disable_code;
216 return rv;
220 /* ssl_connect_mthd: returns a context pointer to the connection to
221 * a ssl server
223 const SSL_METHOD *ssl_connect_mthd(int flag, int *min, int *max)
225 char tmp[10000];
226 int client_request;
227 client_request = (flag & NET_TRYTLS1) ? TLS1_VERSION
228 : (flag & NET_TRYTLS1_1) ? TLS1_1_VERSION
229 : (flag & NET_TRYTLS1_2) ? TLS1_2_VERSION
230 #ifdef TLS1_3_VERSION
231 : (flag & NET_TRYTLS1_3) ? TLS1_3_VERSION
232 #else
233 : (flag & NET_TRYTLS1_3) ? INT_MAX
234 #endif
235 : 0L;
237 *min = *(int *) mail_parameters(NULL, GET_ENCRYPTION_RANGE_MIN, NULL);
238 *max = *(int *) mail_parameters(NULL, GET_ENCRYPTION_RANGE_MAX, NULL);
241 * if no special request, negotiate the maximum the client is configured
242 * to negotiate
244 if(client_request == 0)
245 client_request = *max;
247 if(client_request < *min || client_request > *max)
248 return NIL; /* out of range? bail out */
250 #ifndef OPENSSL_1_1_0
251 if(client_request == SSL3_VERSION)
252 return SSLv3_client_method();
253 if(client_request == TLS1_VERSION)
254 return TLSv1_client_method();
255 else if(client_request == TLS1_1_VERSION)
256 return TLSv1_1_client_method();
257 else if(client_request == TLS1_2_VERSION)
258 return TLSv1_2_client_method();
259 #ifdef TLS1_3_VERSION /* this is only reachable if TLS1_3 support exists */
260 else if(client_request == TLS1_3_VERSION)
261 return TLS_client_method();
262 #endif /* TLS1_3_VERSION */
263 #endif /* ifndef OPENSSL_1_1_0 */
265 return SSLv23_client_method();
268 /* Start SSL/TLS negotiations
269 * Accepts: open TCP stream of session
270 * user's host name
271 * flags
272 * Returns: SSL stream if success else NIL
275 static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
277 char *reason,tmp[MAILTMPLEN];
278 sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
279 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
280 void *data = (*bn) (BLOCK_SENSITIVE,NIL);
281 SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
282 sizeof (SSLSTREAM));
283 stream->tcpstream = tstream; /* bind TCP stream */
284 /* do the work */
285 reason = ssl_start_work (stream,host,flags);
286 (*bn) (BLOCK_NONSENSITIVE,data);
287 if (reason) { /* failed? */
288 ssl_close (stream); /* failed to do SSL */
289 stream = NIL; /* no stream returned */
290 switch (*reason) { /* analyze reason */
291 case '*': /* certificate failure */
292 ++reason; /* skip over certificate failure indication */
293 /* pass to error callback */
294 if (sf) (*sf) (host,reason,flags);
295 else { /* no error callback, build error message */
296 sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
297 mm_log (tmp,ERROR);
299 case '\0': /* user answered no to certificate callback */
300 if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
301 stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
302 sizeof (SSLSTREAM));
303 break;
304 default: /* non-certificate failure */
305 if (flags & NET_TRYSSL); /* no error output if tryssl */
306 /* pass to error callback */
307 else if (sf) (*sf) (host,reason,flags);
308 else { /* no error callback, build error message */
309 sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
310 mm_log (tmp,ERROR);
312 break;
315 return stream;
318 /* Start SSL/TLS negotiations worker routine
319 * Accepts: SSL stream
320 * user's host name
321 * flags
322 * Returns: NIL if success, else error reason
325 /* evil but I had no choice */
326 static char *ssl_last_error = NIL;
327 static char *ssl_last_host = NIL;
329 static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
331 BIO *bio;
332 X509 *cert;
333 unsigned long sl,tl;
334 int min, max;
335 char *s,*t,*err,tmp[MAILTMPLEN], buf[256];
336 sslcertificatequery_t scq =
337 (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
338 sslclientcert_t scc =
339 (sslclientcert_t) mail_parameters (NIL,GET_SSLCLIENTCERT,NIL);
340 sslclientkey_t sck =
341 (sslclientkey_t) mail_parameters (NIL,GET_SSLCLIENTKEY,NIL);
342 if (ssl_last_error) fs_give ((void **) &ssl_last_error);
343 ssl_last_host = host;
344 if (!(stream->context = SSL_CTX_new (ssl_connect_mthd(flags, &min, &max))))
345 return "SSL context failed";
346 SSL_CTX_set_options (stream->context,0);
347 #ifdef OPENSSL_1_1_0
348 if(stream->context != NIL &&
349 ((min != 0 && SSL_CTX_set_min_proto_version(stream->context, min) == 0) ||
350 (max != 0 && SSL_CTX_set_max_proto_version(stream->context, max) == 0)))
351 return "SSL set protocol version Failed";
352 #else
353 { int masklow, maskhigh;
354 masklow = ssl_disable_mask(min, -1);
355 maskhigh = ssl_disable_mask(max, 1);
356 SSL_CTX_set_options(stream->context, masklow|maskhigh);
358 #endif /* OPENSSL_1_1_0 */
359 /* disable certificate validation? */
360 if (flags & NET_NOVALIDATECERT)
361 SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL);
362 else SSL_CTX_set_verify (stream->context,SSL_VERIFY_PEER,ssl_open_verify);
363 /* set default paths to CAs... */
364 SSL_CTX_set_default_verify_paths (stream->context);
365 /* ...unless a non-standard path desired */
366 if ((s = (char *) mail_parameters (NIL,GET_SSLCAPATH,NIL)) != NULL)
367 SSL_CTX_load_verify_locations (stream->context,NIL,s);
368 /* want to send client certificate? */
369 if (scc && (s = (*scc) ()) && (sl = strlen (s))) {
370 if ((cert = PEM_read_bio_X509 (bio = BIO_new_mem_buf (s,sl),NIL,NIL,NIL)) != NULL) {
371 SSL_CTX_use_certificate (stream->context,cert);
372 X509_free (cert);
374 BIO_free (bio);
375 if (!cert) return "SSL client certificate failed";
376 /* want to supply private key? */
377 if ((t = (sck ? (*sck) () : s)) && (tl = strlen (t))) {
378 EVP_PKEY *key;
379 if ((key = PEM_read_bio_PrivateKey (bio = BIO_new_mem_buf (t,tl),
380 NIL,NIL,"")) != NULL) {
381 SSL_CTX_use_PrivateKey (stream->context,key);
382 EVP_PKEY_free (key);
384 BIO_free (bio);
385 memset (t,0,tl); /* erase key */
387 if (s != t) memset (s,0,sl);/* erase certificate if different from key */
390 /* create connection */
391 if (!(stream->con = (SSL *) SSL_new (stream->context)))
392 return "SSL connection failed";
393 if (host && !SSL_set_tlsext_host_name(stream->con, host)){
394 return "Server Name Identification (SNI) failed";
396 bio = BIO_new_socket (stream->tcpstream->tcpsi,BIO_NOCLOSE);
397 SSL_set_bio (stream->con,bio,bio);
398 SSL_set_connect_state (stream->con);
399 if (SSL_in_init (stream->con)) SSL_total_renegotiations (stream->con);
400 /* now negotiate SSL */
401 if (SSL_write (stream->con,"",0) < 0)
402 return ssl_last_error ? ssl_last_error : "SSL negotiation failed";
403 /* need to validate host names? */
404 if (!(flags & NET_NOVALIDATECERT) &&
405 (err = ssl_validate_cert (cert = SSL_get_peer_certificate (stream->con),
406 host))) {
407 /* application callback */
408 X509_NAME_oneline (X509_get_subject_name(cert), buf, sizeof(buf));
409 if (scq) return (*scq) (err,host,cert ? buf : "???") ? NIL : "";
410 /* error message to return via mm_log() */
411 sprintf (tmp,"*%.128s: %.255s",err,cert ? buf : "???");
412 return ssl_last_error = cpystr (tmp);
414 return NIL;
417 /* SSL certificate verification callback
418 * Accepts: error flag
419 * X509 context
420 * Returns: error flag
423 static int ssl_open_verify (int ok,X509_STORE_CTX *ctx)
425 char *err,cert[256],tmp[MAILTMPLEN];
426 sslcertificatequery_t scq =
427 (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
428 if (!ok) { /* in case failure */
429 err = (char *) X509_verify_cert_error_string
430 (X509_STORE_CTX_get_error (ctx));
431 X509_NAME_oneline (X509_get_subject_name
432 (X509_STORE_CTX_get_current_cert (ctx)),cert,255);
433 if (!scq) { /* mm_log() error message if no callback */
434 sprintf (tmp,"*%.128s: %.255s",err,cert);
435 ssl_last_error = cpystr (tmp);
437 /* ignore error if application says to */
438 else if ((*scq) (err,ssl_last_host,cert)) ok = T;
439 /* application wants punt */
440 else ssl_last_error = cpystr ("");
442 return ok;
446 /* SSL validate certificate
447 * Accepts: certificate
448 * host to validate against
449 * Returns: NIL if validated, else string of error message
452 static char *ssl_validate_cert (X509 *cert,char *host)
454 int i,n;
455 char *s=NULL,*t,*ret;
456 void *ext;
457 GENERAL_NAME *name;
458 X509_NAME *cname;
459 X509_NAME_ENTRY *e;
460 char buf[256];
461 /* make sure have a certificate */
462 if (!cert) ret = "No certificate from server";
463 /* and that it has a name */
464 else if (!(cname = X509_get_subject_name(cert))) ret = "No name in certificate";
465 /* locate CN */
466 else{
467 if((e = X509_NAME_get_entry(cname, X509_NAME_entry_count(cname)-1)) != NULL){
468 X509_NAME_get_text_by_OBJ(cname, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
469 s = (char *) buf;
471 else s = NULL;
473 if (s != NULL) {
474 /* host name matches pattern? */
475 ret = ssl_compare_hostnames (host,s) ? NIL :
476 "Server name does not match certificate";
477 /* if mismatch, see if in extensions */
478 if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
479 (n = sk_GENERAL_NAME_num (ext)))
480 /* older versions of OpenSSL use "ia5" instead of dNSName */
481 for (i = 0; ret && (i < n); i++)
482 if ((name = sk_GENERAL_NAME_value (ext,i)) &&
483 (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
484 ssl_compare_hostnames (host,s)) ret = NIL;
486 else ret = "Unable to locate common name in certificate";
487 return ret;
490 /* Case-independent wildcard pattern match
491 * Accepts: base string
492 * pattern string
493 * Returns: T if pattern matches base, else NIL
496 static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat)
498 long ret = NIL;
499 switch (*pat) {
500 case '*': /* wildcard */
501 if (pat[1]) { /* there must be a pattern suffix */
502 /* there is, scan base against it */
503 do if (ssl_compare_hostnames (s,pat+1)) ret = LONGT;
504 while (!ret && (*s != '.') && *s++);
506 break;
507 case '\0': /* end of pattern */
508 if (!*s) ret = LONGT; /* success if base is also at end */
509 break;
510 default: /* non-wildcard, recurse if match */
511 if (!compare_uchar (*pat,*s)) ret = ssl_compare_hostnames (s+1,pat+1);
512 break;
514 return ret;
517 /* SSL receive line
518 * Accepts: SSL stream
519 * Returns: text line string or NIL if failure
522 char *ssl_getline (SSLSTREAM *stream)
524 unsigned long n,contd;
525 char *ret = ssl_getline_work (stream,&n,&contd);
526 if (ret && contd) { /* got a line needing continuation? */
527 STRINGLIST *stl = mail_newstringlist ();
528 STRINGLIST *stc = stl;
529 do { /* collect additional lines */
530 stc->text.data = (unsigned char *) ret;
531 stc->text.size = n;
532 stc = stc->next = mail_newstringlist ();
533 ret = ssl_getline_work (stream,&n,&contd);
534 } while (ret && contd);
535 if (ret) { /* stash final part of line on list */
536 stc->text.data = (unsigned char *) ret;
537 stc->text.size = n;
538 /* determine how large a buffer we need */
539 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
540 ret = fs_get (n + 1); /* copy parts into buffer */
541 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
542 memcpy (ret + n,stc->text.data,stc->text.size);
543 ret[n] = '\0';
545 mail_free_stringlist (&stl);/* either way, done with list */
547 return ret;
550 /* SSL receive line or partial line
551 * Accepts: SSL stream
552 * pointer to return size
553 * pointer to return continuation flag
554 * Returns: text line string, size and continuation flag, or NIL if failure
557 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
558 long *contd)
560 unsigned long n;
561 char *s,*ret,c,d;
562 *contd = NIL; /* assume no continuation */
563 /* make sure have data */
564 if (!ssl_getdata (stream)) return NIL;
565 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
566 d = *stream->iptr++; /* slurp another character */
567 if ((c == '\015') && (d == '\012')) {
568 ret = (char *) fs_get (n--);
569 memcpy (ret,s,*size = n); /* copy into a free storage string */
570 ret[n] = '\0'; /* tie off string with null */
571 return ret;
574 /* copy partial string from buffer */
575 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
576 /* get more data from the net */
577 if (!ssl_getdata (stream)) fs_give ((void **) &ret);
578 /* special case of newline broken by buffer */
579 else if ((c == '\015') && (*stream->iptr == '\012')) {
580 stream->iptr++; /* eat the line feed */
581 stream->ictr--;
582 ret[*size = --n] = '\0'; /* tie off string with null */
584 else *contd = LONGT; /* continuation needed */
585 return ret;
588 /* SSL receive buffer
589 * Accepts: SSL stream
590 * size in bytes
591 * buffer to read into
592 * Returns: T if success, NIL otherwise
595 long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
597 unsigned long n;
598 while (size > 0) { /* until request satisfied */
599 if (!ssl_getdata (stream)) return NIL;
600 n = min (size,stream->ictr);/* number of bytes to transfer */
601 /* do the copy */
602 memcpy (buffer,stream->iptr,n);
603 buffer += n; /* update pointer */
604 stream->iptr += n;
605 size -= n; /* update # of bytes to do */
606 stream->ictr -= n;
608 buffer[0] = '\0'; /* tie off string */
609 return T;
612 /* SSL receive data
613 * Accepts: TCP/IP stream
614 * Returns: T if success, NIL otherwise
617 long ssl_getdata (SSLSTREAM *stream)
619 int i,sock;
620 fd_set fds,efds;
621 struct timeval tmo;
622 tcptimeout_t tmoh = (tcptimeout_t) mail_parameters (NIL,GET_TIMEOUT,NIL);
623 long ttmo_read = (long) mail_parameters (NIL,GET_READTIMEOUT,NIL);
624 time_t t = time (0);
625 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
626 if (!stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return NIL;
627 /* tcp_unix should have prevented this */
628 if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
629 (*bn) (BLOCK_TCPREAD,NIL);
630 while (stream->ictr < 1) { /* if nothing in the buffer */
631 time_t tl = time (0); /* start of request */
632 time_t now = tl;
633 int ti = ttmo_read ? now + ttmo_read : 0;
634 if (SSL_pending (stream->con)) i = 1;
635 else {
636 if (tcpdebug) mm_log ("Reading SSL data",TCPDEBUG);
637 tmo.tv_usec = 0;
638 FD_ZERO (&fds); /* initialize selection vector */
639 FD_ZERO (&efds); /* handle errors too */
640 FD_SET (sock,&fds); /* set bit in selection vector */
641 FD_SET (sock,&efds); /* set bit in error selection vector */
642 errno = NIL; /* block and read */
643 do { /* block under timeout */
644 tmo.tv_sec = ti ? ti - now : 0;
645 i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
646 now = time (0); /* fake timeout if interrupt & time expired */
647 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
648 } while ((i < 0) && (errno == EINTR));
650 if (i) { /* non-timeout result from select? */
651 errno = 0; /* just in case */
652 if (i > 0) /* read what we can */
653 while (((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) < 0) &&
654 ((errno == EINTR) ||
655 (SSL_get_error (stream->con,i) == SSL_ERROR_WANT_READ)));
656 if (i <= 0) { /* error seen? */
657 if (tcpdebug) {
658 char *s,tmp[MAILTMPLEN];
659 if (i) sprintf (s = tmp,"SSL data read I/O error %d SSL error %d",
660 errno,SSL_get_error (stream->con,i));
661 else s = "SSL data read end of file";
662 mm_log (s,TCPDEBUG);
664 return ssl_abort (stream);
666 stream->iptr = stream->ibuf;/* point at TCP buffer */
667 stream->ictr = i; /* set new byte count */
668 if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG);
670 /* timeout, punt unless told not to */
671 else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->tcpstream->host)) {
672 if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG);
673 return ssl_abort (stream);
676 (*bn) (BLOCK_NONE,NIL);
677 return T;
680 /* SSL send string as record
681 * Accepts: SSL stream
682 * string pointer
683 * Returns: T if success else NIL
686 long ssl_soutr (SSLSTREAM *stream,char *string)
688 return ssl_sout (stream,string,(unsigned long) strlen (string));
692 /* SSL send string
693 * Accepts: SSL stream
694 * string pointer
695 * byte count
696 * Returns: T if success else NIL
699 long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
701 long i;
702 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
703 if (!stream->con) return NIL;
704 (*bn) (BLOCK_TCPWRITE,NIL);
705 if (tcpdebug) mm_log ("Writing to SSL",TCPDEBUG);
706 /* until request satisfied */
707 for (i = 0; size > 0; string += i,size -= i)
708 /* write as much as we can */
709 if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) < 0) {
710 if (tcpdebug) {
711 char tmp[MAILTMPLEN];
712 sprintf (tmp,"SSL data write I/O error %d SSL error %d",
713 errno,SSL_get_error (stream->con,i));
714 mm_log (tmp,TCPDEBUG);
716 return ssl_abort (stream);/* write failed */
718 if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
719 (*bn) (BLOCK_NONE,NIL);
720 return LONGT; /* all done */
723 /* SSL close
724 * Accepts: SSL stream
727 void ssl_close (SSLSTREAM *stream)
729 ssl_abort (stream); /* nuke the stream */
730 fs_give ((void **) &stream); /* flush the stream */
734 /* SSL abort stream
735 * Accepts: SSL stream
736 * Returns: NIL always
739 static long ssl_abort (SSLSTREAM *stream)
741 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
742 if (stream->con) { /* close SSL connection */
743 SSL_shutdown (stream->con);
744 SSL_free (stream->con);
745 stream->con = NIL;
747 if (stream->context) { /* clean up context */
748 SSL_CTX_free (stream->context);
749 stream->context = NIL;
751 if (stream->tcpstream) { /* close TCP stream */
752 tcp_close (stream->tcpstream);
753 stream->tcpstream = NIL;
755 (*bn) (BLOCK_NONE,NIL);
756 return NIL;
759 /* SSL get host name
760 * Accepts: SSL stream
761 * Returns: host name for this stream
764 char *ssl_host (SSLSTREAM *stream)
766 return stream ? tcp_host (stream->tcpstream) : "UNKNOWN";
770 /* SSL get remote host name
771 * Accepts: SSL stream
772 * Returns: host name for this stream
775 char *ssl_remotehost (SSLSTREAM *stream)
777 return tcp_remotehost (stream->tcpstream);
781 /* SSL return port for this stream
782 * Accepts: SSL stream
783 * Returns: port number for this stream
786 unsigned long ssl_port (SSLSTREAM *stream)
788 return tcp_port (stream->tcpstream);
792 /* SSL get local host name
793 * Accepts: SSL stream
794 * Returns: local host name
797 char *ssl_localhost (SSLSTREAM *stream)
799 return tcp_localhost (stream->tcpstream);
802 /* Start TLS
803 * Accepts: /etc/services service name
804 * Returns: cpystr'd error string if TLS failed, else NIL for success
807 char *ssl_start_tls (char *server)
809 char tmp[MAILTMPLEN];
810 struct stat sbuf;
811 if (sslstdio) return cpystr ("Already in an SSL session");
812 if (start_tls) return cpystr ("TLS already started");
813 if (server) { /* build specific certificate/key file name */
814 sprintf (tmp,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
815 if (stat (tmp,&sbuf)) { /* use non-specific name if no specific file */
816 sprintf (tmp,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
817 if (stat (tmp,&sbuf)) return cpystr ("Server certificate not installed");
819 start_tls = server; /* switch to STARTTLS mode */
821 return NIL;
824 /* Init server for SSL
825 * Accepts: server name
828 void ssl_server_init (char *server)
830 char cert[MAILTMPLEN],key[MAILTMPLEN];
831 unsigned long i;
832 struct stat sbuf;
833 SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
834 sizeof (SSLSTREAM));
835 ssl_onceonlyinit (); /* make sure algorithms added */
836 #ifdef OPENSSL_1_1_0
837 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
838 OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
839 #else
840 ERR_load_crypto_strings ();
841 SSL_load_error_strings ();
842 #endif /* OPENSSL_1_1_0 */
843 /* build specific certificate/key file names */
844 sprintf (cert,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
845 sprintf (key,"%s/%s-%s.pem",SSL_KEY_DIRECTORY,server,tcp_serveraddr ());
846 /* use non-specific name if no specific cert */
847 if (stat (cert,&sbuf)) sprintf (cert,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
848 if (stat (key,&sbuf)) { /* use non-specific name if no specific key */
849 sprintf (key,"%s/%s.pem",SSL_KEY_DIRECTORY,server);
850 /* use cert file as fallback for key */
851 if (stat (key,&sbuf)) strcpy (key,cert);
853 /* create context */
854 #ifdef OPENSSL_1_1_0
855 if (!(stream->context = SSL_CTX_new (start_tls ?
856 TLS_server_method () :
857 SSLv23_server_method ())))
858 #else
859 if (!(stream->context = SSL_CTX_new (start_tls ?
860 TLSv1_server_method () :
861 SSLv23_server_method ())))
862 #endif /* OPENSSL_1_1_0 */
863 syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s",
864 tcp_clienthost ());
865 else { /* set context options */
866 SSL_CTX_set_options (stream->context,SSL_OP_ALL);
867 /* set cipher list */
868 if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST))
869 syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s",
870 SSLCIPHERLIST,tcp_clienthost ());
871 /* load certificate */
872 else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert))
873 syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s",
874 cert,tcp_clienthost ());
875 /* load key */
876 else if (!(SSL_CTX_use_RSAPrivateKey_file (stream->context,key,
877 SSL_FILETYPE_PEM)))
878 syslog (LOG_ALERT,"Unable to load private key from %.80s, host=%.80s",
879 key,tcp_clienthost ());
881 else { /* generate key if needed */
882 #ifdef OPENSSL_1_1_0
883 if (0)
884 ssl_genkey(stream->context, 0, 0);
885 #else
886 if (SSL_CTX_need_tmp_RSA (stream->context))
887 SSL_CTX_set_tmp_rsa_callback (stream->context,ssl_genkey);
888 #endif /* OPENSSL_1_1_0 */
889 /* create new SSL connection */
890 if (!(stream->con = SSL_new (stream->context)))
891 syslog (LOG_ALERT,"Unable to create SSL connection, host=%.80s",
892 tcp_clienthost ());
893 else { /* set file descriptor */
894 SSL_set_fd (stream->con,0);
895 /* all OK if accepted */
896 if (SSL_accept (stream->con) < 0)
897 syslog (LOG_INFO,"Unable to accept SSL connection, host=%.80s",
898 tcp_clienthost ());
899 else { /* server set up */
900 sslstdio = (SSLSTDIOSTREAM *)
901 memset (fs_get (sizeof(SSLSTDIOSTREAM)),0,sizeof (SSLSTDIOSTREAM));
902 sslstdio->sslstream = stream;
903 /* available space in output buffer */
904 sslstdio->octr = SSLBUFLEN;
905 /* current output buffer pointer */
906 sslstdio->optr = sslstdio->obuf;
907 /* allow plaintext if disable value was 2 */
908 if ((long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) > 1)
909 mail_parameters (NIL,SET_DISABLEPLAINTEXT,NIL);
910 /* unhide PLAIN SASL authenticator */
911 mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"PLAIN");
912 mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"LOGIN");
913 return;
918 while ((i = ERR_get_error ()) != 0L) /* SSL failure */
919 syslog (LOG_ERR,"SSL error status: %.80s",ERR_error_string (i,NIL));
920 ssl_close (stream); /* punt stream */
921 exit (1); /* punt this program too */
924 /* Generate one-time key for server
925 * Accepts: SSL connection
926 * export flag
927 * keylength
928 * Returns: generated key, always
931 static RSA *ssl_genkey (SSL_CTX_TYPE *con,int export,int keylength)
933 unsigned long i;
934 static RSA *key = NIL;
935 if (!key) { /* if don't have a key already */
936 /* generate key */
937 #ifdef OPENSSL_1_1_0
938 BIGNUM *e = BN_new();
939 if (!RSA_generate_key_ex (key, export ? keylength : 1024, e,NIL)) {
940 #else
941 if (!(key = RSA_generate_key (export ? keylength : 1024,RSA_F4,NIL,NIL))) {
942 #endif /* OPENSSL_1_1_0 */
943 syslog (LOG_ALERT,"Unable to generate temp key, host=%.80s",
944 tcp_clienthost ());
945 while ((i = ERR_get_error ()) != 0L)
946 syslog (LOG_ALERT,"SSL error status: %s",ERR_error_string (i,NIL));
947 exit (1);
949 #ifdef OPENSSL_1_1_0
950 BN_free(e);
951 e = NULL;
952 #endif /* OPENSSL_1_1_0 */
954 return key;
957 /* Wait for stdin input
958 * Accepts: timeout in seconds
959 * Returns: T if have input on stdin, else NIL
962 long ssl_server_input_wait (long seconds)
964 int i,sock;
965 fd_set fds,efd;
966 struct timeval tmo;
967 SSLSTREAM *stream;
968 if (!sslstdio) return server_input_wait (seconds);
969 /* input available in buffer */
970 if (((stream = sslstdio->sslstream)->ictr > 0) ||
971 !stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return LONGT;
972 /* sock ought to be 0 always */
973 if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
974 /* input available from SSL */
975 if (SSL_pending (stream->con) &&
976 ((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) > 0)) {
977 stream->iptr = stream->ibuf;/* point at TCP buffer */
978 stream->ictr = i; /* set new byte count */
979 return LONGT;
981 FD_ZERO (&fds); /* initialize selection vector */
982 FD_ZERO (&efd); /* initialize selection vector */
983 FD_SET (sock,&fds); /* set bit in selection vector */
984 FD_SET (sock,&efd); /* set bit in selection vector */
985 tmo.tv_sec = seconds; tmo.tv_usec = 0;
986 /* see if input available from the socket */
987 return select (sock+1,&fds,0,&efd,&tmo) ? LONGT : NIL;
990 #include "sslstdio.c"