* Update to version 2.19.1
[alpine.git] / imap / src / osdep / unix / ssl_unix.c
blobd77ed7197dd18378261a19dc60a11671d0282854
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 #undef STRING
37 #undef crypt
39 #define SSLBUFLEN 8192
42 * PCI auditing compliance, disable:
43 * SSLv2
44 * anonymous D-H (no certificate
45 * export encryption ciphers (40 and 56 bits)
46 * low encryption cipher suites (40 and 56 bits, excluding export)
47 * null encryption (disabling implied by "ALL")
49 * UW imapd just disables low-grade and null ("ALL:!LOW"). This setting
50 * will break clients that attempt to use the newly-prohibited mechanisms.
52 * I question the value of disabling SSLv2, as opposed to disabling the SSL
53 * ports (e.g., 993 for IMAP, 995 for POP3) and using TLS exclusively.
56 #define SSLCIPHERLIST "ALL:!SSLv2:!ADH:!EXP:!LOW"
58 /* SSL I/O stream */
60 typedef struct ssl_stream {
61 TCPSTREAM *tcpstream; /* TCP stream */
62 SSL_CTX *context; /* SSL context */
63 SSL *con; /* SSL connection */
64 int ictr; /* input counter */
65 char *iptr; /* input pointer */
66 char ibuf[SSLBUFLEN]; /* input buffer */
67 } SSLSTREAM;
69 #include "sslio.h"
71 /* Function prototypes */
72 const SSL_METHOD *ssl_connect_mthd(int flag);
73 static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
74 static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags);
75 static int ssl_open_verify (int ok,X509_STORE_CTX *ctx);
76 static char *ssl_validate_cert (X509 *cert,char *host);
77 static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat);
78 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
79 long *contd);
80 static long ssl_abort (SSLSTREAM *stream);
81 static RSA *ssl_genkey (SSL *con,int export,int keylength);
84 /* Secure Sockets Layer network driver dispatch */
86 static struct ssl_driver ssldriver = {
87 ssl_open, /* open connection */
88 ssl_aopen, /* open preauthenticated connection */
89 ssl_getline, /* get a line */
90 ssl_getbuffer, /* get a buffer */
91 ssl_soutr, /* output pushed data */
92 ssl_sout, /* output string */
93 ssl_close, /* close connection */
94 ssl_host, /* return host name */
95 ssl_remotehost, /* return remote host name */
96 ssl_port, /* return port number */
97 ssl_localhost /* return local host name */
99 /* non-NIL if doing SSL primary I/O */
100 static SSLSTDIOSTREAM *sslstdio = NIL;
101 static char *start_tls = NIL; /* non-NIL if start TLS requested */
103 /* One-time SSL initialization */
105 static int sslonceonly = 0;
107 void ssl_onceonlyinit (void)
109 if (!sslonceonly++) { /* only need to call it once */
110 int fd;
111 char tmp[MAILTMPLEN];
112 struct stat sbuf;
113 /* if system doesn't have /dev/urandom */
114 if (stat ("/dev/urandom",&sbuf)) {
115 strcpy(tmp, "SSLXXXXXX");
116 while ((fd = mkstemp(tmp)) < 0) sleep (1);
117 unlink (tmp); /* don't need the file */
118 fstat (fd,&sbuf); /* get information about the file */
119 close (fd); /* flush descriptor */
120 /* not great but it'll have to do */
121 sprintf (tmp + strlen (tmp),"%.80s%lx%.80s%lx%lx%lx%lx%lx",
122 tcp_serveraddr (),(unsigned long) tcp_serverport (),
123 tcp_clientaddr (),(unsigned long) tcp_clientport (),
124 (unsigned long) sbuf.st_ino,(unsigned long) time (0),
125 (unsigned long) gethostid (),(unsigned long) getpid ());
126 RAND_seed (tmp,strlen (tmp));
128 /* apply runtime linkage */
129 mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
130 mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
131 SSL_library_init (); /* add all algorithms */
135 /* SSL open
136 * Accepts: host name
137 * contact service name
138 * contact port number
139 * Returns: SSL stream if success else NIL
142 SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
144 TCPSTREAM *stream = tcp_open (host,service,port);
145 return stream ? ssl_start (stream,host,port) : NIL;
149 /* SSL authenticated open
150 * Accepts: host name
151 * service name
152 * returned user name buffer
153 * Returns: SSL stream if success else NIL
156 SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
158 return NIL; /* don't use this mechanism with SSL */
161 /* ssl_connect_mthd: returns a context pointer to the connection to
162 * a ssl server
164 const SSL_METHOD *ssl_connect_mthd(int flag)
166 if(flag & NET_TRYTLS1)
167 return TLSv1_client_method();
168 #ifdef TLSV1_2
169 else if(flag & NET_TRYTLS1_1)
170 return TLSv1_1_client_method();
171 else if(flag & NET_TRYTLS1_2)
172 return TLSv1_2_client_method();
173 #endif
174 else if(flag & NET_TRYDTLS1)
175 return DTLSv1_client_method();
176 else return SSLv23_client_method();
179 /* Start SSL/TLS negotiations
180 * Accepts: open TCP stream of session
181 * user's host name
182 * flags
183 * Returns: SSL stream if success else NIL
186 static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
188 char *reason,tmp[MAILTMPLEN];
189 sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
190 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
191 void *data = (*bn) (BLOCK_SENSITIVE,NIL);
192 SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
193 sizeof (SSLSTREAM));
194 stream->tcpstream = tstream; /* bind TCP stream */
195 /* do the work */
196 reason = ssl_start_work (stream,host,flags);
197 (*bn) (BLOCK_NONSENSITIVE,data);
198 if (reason) { /* failed? */
199 ssl_close (stream); /* failed to do SSL */
200 stream = NIL; /* no stream returned */
201 switch (*reason) { /* analyze reason */
202 case '*': /* certificate failure */
203 ++reason; /* skip over certificate failure indication */
204 /* pass to error callback */
205 if (sf) (*sf) (host,reason,flags);
206 else { /* no error callback, build error message */
207 sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
208 mm_log (tmp,ERROR);
210 case '\0': /* user answered no to certificate callback */
211 if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
212 stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
213 sizeof (SSLSTREAM));
214 break;
215 default: /* non-certificate failure */
216 if (flags & NET_TRYSSL); /* no error output if tryssl */
217 /* pass to error callback */
218 else if (sf) (*sf) (host,reason,flags);
219 else { /* no error callback, build error message */
220 sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
221 mm_log (tmp,ERROR);
223 break;
226 return stream;
229 /* Start SSL/TLS negotiations worker routine
230 * Accepts: SSL stream
231 * user's host name
232 * flags
233 * Returns: NIL if success, else error reason
236 /* evil but I had no choice */
237 static char *ssl_last_error = NIL;
238 static char *ssl_last_host = NIL;
240 static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
242 BIO *bio;
243 X509 *cert;
244 unsigned long sl,tl;
245 char *s,*t,*err,tmp[MAILTMPLEN];
246 sslcertificatequery_t scq =
247 (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
248 sslclientcert_t scc =
249 (sslclientcert_t) mail_parameters (NIL,GET_SSLCLIENTCERT,NIL);
250 sslclientkey_t sck =
251 (sslclientkey_t) mail_parameters (NIL,GET_SSLCLIENTKEY,NIL);
252 if (ssl_last_error) fs_give ((void **) &ssl_last_error);
253 ssl_last_host = host;
254 if (!(stream->context = SSL_CTX_new (ssl_connect_mthd(flags))))
255 return "SSL context failed";
256 SSL_CTX_set_options (stream->context,0);
257 /* disable certificate validation? */
258 if (flags & NET_NOVALIDATECERT)
259 SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL);
260 else SSL_CTX_set_verify (stream->context,SSL_VERIFY_PEER,ssl_open_verify);
261 /* set default paths to CAs... */
262 SSL_CTX_set_default_verify_paths (stream->context);
263 /* ...unless a non-standard path desired */
264 if (s = (char *) mail_parameters (NIL,GET_SSLCAPATH,NIL))
265 SSL_CTX_load_verify_locations (stream->context,NIL,s);
266 /* want to send client certificate? */
267 if (scc && (s = (*scc) ()) && (sl = strlen (s))) {
268 if (cert = PEM_read_bio_X509 (bio = BIO_new_mem_buf (s,sl),NIL,NIL,NIL)) {
269 SSL_CTX_use_certificate (stream->context,cert);
270 X509_free (cert);
272 BIO_free (bio);
273 if (!cert) return "SSL client certificate failed";
274 /* want to supply private key? */
275 if ((t = (sck ? (*sck) () : s)) && (tl = strlen (t))) {
276 EVP_PKEY *key;
277 if (key = PEM_read_bio_PrivateKey (bio = BIO_new_mem_buf (t,tl),
278 NIL,NIL,"")) {
279 SSL_CTX_use_PrivateKey (stream->context,key);
280 EVP_PKEY_free (key);
282 BIO_free (bio);
283 memset (t,0,tl); /* erase key */
285 if (s != t) memset (s,0,sl);/* erase certificate if different from key */
288 /* create connection */
289 if (!(stream->con = (SSL *) SSL_new (stream->context)))
290 return "SSL connection failed";
291 bio = BIO_new_socket (stream->tcpstream->tcpsi,BIO_NOCLOSE);
292 SSL_set_bio (stream->con,bio,bio);
293 SSL_set_connect_state (stream->con);
294 if (SSL_in_init (stream->con)) SSL_total_renegotiations (stream->con);
295 /* now negotiate SSL */
296 if (SSL_write (stream->con,"",0) < 0)
297 return ssl_last_error ? ssl_last_error : "SSL negotiation failed";
298 /* need to validate host names? */
299 if (!(flags & NET_NOVALIDATECERT) &&
300 (err = ssl_validate_cert (cert = SSL_get_peer_certificate (stream->con),
301 host))) {
302 /* application callback */
303 if (scq) return (*scq) (err,host,cert ? cert->name : "???") ? NIL : "";
304 /* error message to return via mm_log() */
305 sprintf (tmp,"*%.128s: %.255s",err,cert ? cert->name : "???");
306 return ssl_last_error = cpystr (tmp);
308 return NIL;
311 /* SSL certificate verification callback
312 * Accepts: error flag
313 * X509 context
314 * Returns: error flag
317 static int ssl_open_verify (int ok,X509_STORE_CTX *ctx)
319 char *err,cert[256],tmp[MAILTMPLEN];
320 sslcertificatequery_t scq =
321 (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
322 if (!ok) { /* in case failure */
323 err = (char *) X509_verify_cert_error_string
324 (X509_STORE_CTX_get_error (ctx));
325 X509_NAME_oneline (X509_get_subject_name
326 (X509_STORE_CTX_get_current_cert (ctx)),cert,255);
327 if (!scq) { /* mm_log() error message if no callback */
328 sprintf (tmp,"*%.128s: %.255s",err,cert);
329 ssl_last_error = cpystr (tmp);
331 /* ignore error if application says to */
332 else if ((*scq) (err,ssl_last_host,cert)) ok = T;
333 /* application wants punt */
334 else ssl_last_error = cpystr ("");
336 return ok;
340 /* SSL validate certificate
341 * Accepts: certificate
342 * host to validate against
343 * Returns: NIL if validated, else string of error message
346 static char *ssl_validate_cert (X509 *cert,char *host)
348 int i,n;
349 char *s,*t,*ret;
350 void *ext;
351 GENERAL_NAME *name;
352 /* make sure have a certificate */
353 if (!cert) ret = "No certificate from server";
354 /* and that it has a name */
355 else if (!cert->name) ret = "No name in certificate";
356 /* locate CN */
357 else if (s = strstr (cert->name,"/CN=")) {
358 if (t = strchr (s += 4,'/')) *t = '\0';
359 /* host name matches pattern? */
360 ret = ssl_compare_hostnames (host,s) ? NIL :
361 "Server name does not match certificate";
362 if (t) *t = '/'; /* restore smashed delimiter */
363 /* if mismatch, see if in extensions */
364 if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
365 (n = sk_GENERAL_NAME_num (ext)))
366 /* older versions of OpenSSL use "ia5" instead of dNSName */
367 for (i = 0; ret && (i < n); i++)
368 if ((name = sk_GENERAL_NAME_value (ext,i)) &&
369 (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
370 ssl_compare_hostnames (host,s)) ret = NIL;
372 else ret = "Unable to locate common name in certificate";
373 return ret;
376 /* Case-independent wildcard pattern match
377 * Accepts: base string
378 * pattern string
379 * Returns: T if pattern matches base, else NIL
382 static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat)
384 long ret = NIL;
385 switch (*pat) {
386 case '*': /* wildcard */
387 if (pat[1]) { /* there must be a pattern suffix */
388 /* there is, scan base against it */
389 do if (ssl_compare_hostnames (s,pat+1)) ret = LONGT;
390 while (!ret && (*s != '.') && *s++);
392 break;
393 case '\0': /* end of pattern */
394 if (!*s) ret = LONGT; /* success if base is also at end */
395 break;
396 default: /* non-wildcard, recurse if match */
397 if (!compare_uchar (*pat,*s)) ret = ssl_compare_hostnames (s+1,pat+1);
398 break;
400 return ret;
403 /* SSL receive line
404 * Accepts: SSL stream
405 * Returns: text line string or NIL if failure
408 char *ssl_getline (SSLSTREAM *stream)
410 unsigned long n,contd;
411 char *ret = ssl_getline_work (stream,&n,&contd);
412 if (ret && contd) { /* got a line needing continuation? */
413 STRINGLIST *stl = mail_newstringlist ();
414 STRINGLIST *stc = stl;
415 do { /* collect additional lines */
416 stc->text.data = (unsigned char *) ret;
417 stc->text.size = n;
418 stc = stc->next = mail_newstringlist ();
419 ret = ssl_getline_work (stream,&n,&contd);
420 } while (ret && contd);
421 if (ret) { /* stash final part of line on list */
422 stc->text.data = (unsigned char *) ret;
423 stc->text.size = n;
424 /* determine how large a buffer we need */
425 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
426 ret = fs_get (n + 1); /* copy parts into buffer */
427 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
428 memcpy (ret + n,stc->text.data,stc->text.size);
429 ret[n] = '\0';
431 mail_free_stringlist (&stl);/* either way, done with list */
433 return ret;
436 /* SSL receive line or partial line
437 * Accepts: SSL stream
438 * pointer to return size
439 * pointer to return continuation flag
440 * Returns: text line string, size and continuation flag, or NIL if failure
443 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
444 long *contd)
446 unsigned long n;
447 char *s,*ret,c,d;
448 *contd = NIL; /* assume no continuation */
449 /* make sure have data */
450 if (!ssl_getdata (stream)) return NIL;
451 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
452 d = *stream->iptr++; /* slurp another character */
453 if ((c == '\015') && (d == '\012')) {
454 ret = (char *) fs_get (n--);
455 memcpy (ret,s,*size = n); /* copy into a free storage string */
456 ret[n] = '\0'; /* tie off string with null */
457 return ret;
460 /* copy partial string from buffer */
461 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
462 /* get more data from the net */
463 if (!ssl_getdata (stream)) fs_give ((void **) &ret);
464 /* special case of newline broken by buffer */
465 else if ((c == '\015') && (*stream->iptr == '\012')) {
466 stream->iptr++; /* eat the line feed */
467 stream->ictr--;
468 ret[*size = --n] = '\0'; /* tie off string with null */
470 else *contd = LONGT; /* continuation needed */
471 return ret;
474 /* SSL receive buffer
475 * Accepts: SSL stream
476 * size in bytes
477 * buffer to read into
478 * Returns: T if success, NIL otherwise
481 long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
483 unsigned long n;
484 while (size > 0) { /* until request satisfied */
485 if (!ssl_getdata (stream)) return NIL;
486 n = min (size,stream->ictr);/* number of bytes to transfer */
487 /* do the copy */
488 memcpy (buffer,stream->iptr,n);
489 buffer += n; /* update pointer */
490 stream->iptr += n;
491 size -= n; /* update # of bytes to do */
492 stream->ictr -= n;
494 buffer[0] = '\0'; /* tie off string */
495 return T;
498 /* SSL receive data
499 * Accepts: TCP/IP stream
500 * Returns: T if success, NIL otherwise
503 long ssl_getdata (SSLSTREAM *stream)
505 int i,sock;
506 fd_set fds,efds;
507 struct timeval tmo;
508 tcptimeout_t tmoh = (tcptimeout_t) mail_parameters (NIL,GET_TIMEOUT,NIL);
509 long ttmo_read = (long) mail_parameters (NIL,GET_READTIMEOUT,NIL);
510 time_t t = time (0);
511 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
512 if (!stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return NIL;
513 /* tcp_unix should have prevented this */
514 if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
515 (*bn) (BLOCK_TCPREAD,NIL);
516 while (stream->ictr < 1) { /* if nothing in the buffer */
517 time_t tl = time (0); /* start of request */
518 time_t now = tl;
519 int ti = ttmo_read ? now + ttmo_read : 0;
520 if (SSL_pending (stream->con)) i = 1;
521 else {
522 if (tcpdebug) mm_log ("Reading SSL data",TCPDEBUG);
523 tmo.tv_usec = 0;
524 FD_ZERO (&fds); /* initialize selection vector */
525 FD_ZERO (&efds); /* handle errors too */
526 FD_SET (sock,&fds); /* set bit in selection vector */
527 FD_SET (sock,&efds); /* set bit in error selection vector */
528 errno = NIL; /* block and read */
529 do { /* block under timeout */
530 tmo.tv_sec = ti ? ti - now : 0;
531 i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
532 now = time (0); /* fake timeout if interrupt & time expired */
533 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
534 } while ((i < 0) && (errno == EINTR));
536 if (i) { /* non-timeout result from select? */
537 errno = 0; /* just in case */
538 if (i > 0) /* read what we can */
539 while (((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) < 0) &&
540 ((errno == EINTR) ||
541 (SSL_get_error (stream->con,i) == SSL_ERROR_WANT_READ)));
542 if (i <= 0) { /* error seen? */
543 if (tcpdebug) {
544 char *s,tmp[MAILTMPLEN];
545 if (i) sprintf (s = tmp,"SSL data read I/O error %d SSL error %d",
546 errno,SSL_get_error (stream->con,i));
547 else s = "SSL data read end of file";
548 mm_log (s,TCPDEBUG);
550 return ssl_abort (stream);
552 stream->iptr = stream->ibuf;/* point at TCP buffer */
553 stream->ictr = i; /* set new byte count */
554 if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG);
556 /* timeout, punt unless told not to */
557 else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->tcpstream->host)) {
558 if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG);
559 return ssl_abort (stream);
562 (*bn) (BLOCK_NONE,NIL);
563 return T;
566 /* SSL send string as record
567 * Accepts: SSL stream
568 * string pointer
569 * Returns: T if success else NIL
572 long ssl_soutr (SSLSTREAM *stream,char *string)
574 return ssl_sout (stream,string,(unsigned long) strlen (string));
578 /* SSL send string
579 * Accepts: SSL stream
580 * string pointer
581 * byte count
582 * Returns: T if success else NIL
585 long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
587 long i;
588 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
589 if (!stream->con) return NIL;
590 (*bn) (BLOCK_TCPWRITE,NIL);
591 if (tcpdebug) mm_log ("Writing to SSL",TCPDEBUG);
592 /* until request satisfied */
593 for (i = 0; size > 0; string += i,size -= i)
594 /* write as much as we can */
595 if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) < 0) {
596 if (tcpdebug) {
597 char tmp[MAILTMPLEN];
598 sprintf (tmp,"SSL data write I/O error %d SSL error %d",
599 errno,SSL_get_error (stream->con,i));
600 mm_log (tmp,TCPDEBUG);
602 return ssl_abort (stream);/* write failed */
604 if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
605 (*bn) (BLOCK_NONE,NIL);
606 return LONGT; /* all done */
609 /* SSL close
610 * Accepts: SSL stream
613 void ssl_close (SSLSTREAM *stream)
615 ssl_abort (stream); /* nuke the stream */
616 fs_give ((void **) &stream); /* flush the stream */
620 /* SSL abort stream
621 * Accepts: SSL stream
622 * Returns: NIL always
625 static long ssl_abort (SSLSTREAM *stream)
627 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
628 if (stream->con) { /* close SSL connection */
629 SSL_shutdown (stream->con);
630 SSL_free (stream->con);
631 stream->con = NIL;
633 if (stream->context) { /* clean up context */
634 SSL_CTX_free (stream->context);
635 stream->context = NIL;
637 if (stream->tcpstream) { /* close TCP stream */
638 tcp_close (stream->tcpstream);
639 stream->tcpstream = NIL;
641 (*bn) (BLOCK_NONE,NIL);
642 return NIL;
645 /* SSL get host name
646 * Accepts: SSL stream
647 * Returns: host name for this stream
650 char *ssl_host (SSLSTREAM *stream)
652 return stream ? tcp_host (stream->tcpstream) : "UNKNOWN";
656 /* SSL get remote host name
657 * Accepts: SSL stream
658 * Returns: host name for this stream
661 char *ssl_remotehost (SSLSTREAM *stream)
663 return tcp_remotehost (stream->tcpstream);
667 /* SSL return port for this stream
668 * Accepts: SSL stream
669 * Returns: port number for this stream
672 unsigned long ssl_port (SSLSTREAM *stream)
674 return tcp_port (stream->tcpstream);
678 /* SSL get local host name
679 * Accepts: SSL stream
680 * Returns: local host name
683 char *ssl_localhost (SSLSTREAM *stream)
685 return tcp_localhost (stream->tcpstream);
688 /* Start TLS
689 * Accepts: /etc/services service name
690 * Returns: cpystr'd error string if TLS failed, else NIL for success
693 char *ssl_start_tls (char *server)
695 char tmp[MAILTMPLEN];
696 struct stat sbuf;
697 if (sslstdio) return cpystr ("Already in an SSL session");
698 if (start_tls) return cpystr ("TLS already started");
699 if (server) { /* build specific certificate/key file name */
700 sprintf (tmp,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
701 if (stat (tmp,&sbuf)) { /* use non-specific name if no specific file */
702 sprintf (tmp,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
703 if (stat (tmp,&sbuf)) return cpystr ("Server certificate not installed");
705 start_tls = server; /* switch to STARTTLS mode */
707 return NIL;
710 /* Init server for SSL
711 * Accepts: server name
714 void ssl_server_init (char *server)
716 char cert[MAILTMPLEN],key[MAILTMPLEN];
717 unsigned long i;
718 struct stat sbuf;
719 SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
720 sizeof (SSLSTREAM));
721 ssl_onceonlyinit (); /* make sure algorithms added */
722 ERR_load_crypto_strings ();
723 SSL_load_error_strings ();
724 /* build specific certificate/key file names */
725 sprintf (cert,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
726 sprintf (key,"%s/%s-%s.pem",SSL_KEY_DIRECTORY,server,tcp_serveraddr ());
727 /* use non-specific name if no specific cert */
728 if (stat (cert,&sbuf)) sprintf (cert,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
729 if (stat (key,&sbuf)) { /* use non-specific name if no specific key */
730 sprintf (key,"%s/%s.pem",SSL_KEY_DIRECTORY,server);
731 /* use cert file as fallback for key */
732 if (stat (key,&sbuf)) strcpy (key,cert);
734 /* create context */
735 if (!(stream->context = SSL_CTX_new (start_tls ?
736 TLSv1_server_method () :
737 SSLv23_server_method ())))
738 syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s",
739 tcp_clienthost ());
740 else { /* set context options */
741 SSL_CTX_set_options (stream->context,SSL_OP_ALL);
742 /* set cipher list */
743 if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST))
744 syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s",
745 SSLCIPHERLIST,tcp_clienthost ());
746 /* load certificate */
747 else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert))
748 syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s",
749 cert,tcp_clienthost ());
750 /* load key */
751 else if (!(SSL_CTX_use_RSAPrivateKey_file (stream->context,key,
752 SSL_FILETYPE_PEM)))
753 syslog (LOG_ALERT,"Unable to load private key from %.80s, host=%.80s",
754 key,tcp_clienthost ());
756 else { /* generate key if needed */
757 if (SSL_CTX_need_tmp_RSA (stream->context))
758 SSL_CTX_set_tmp_rsa_callback (stream->context,ssl_genkey);
759 /* create new SSL connection */
760 if (!(stream->con = SSL_new (stream->context)))
761 syslog (LOG_ALERT,"Unable to create SSL connection, host=%.80s",
762 tcp_clienthost ());
763 else { /* set file descriptor */
764 SSL_set_fd (stream->con,0);
765 /* all OK if accepted */
766 if (SSL_accept (stream->con) < 0)
767 syslog (LOG_INFO,"Unable to accept SSL connection, host=%.80s",
768 tcp_clienthost ());
769 else { /* server set up */
770 sslstdio = (SSLSTDIOSTREAM *)
771 memset (fs_get (sizeof(SSLSTDIOSTREAM)),0,sizeof (SSLSTDIOSTREAM));
772 sslstdio->sslstream = stream;
773 /* available space in output buffer */
774 sslstdio->octr = SSLBUFLEN;
775 /* current output buffer pointer */
776 sslstdio->optr = sslstdio->obuf;
777 /* allow plaintext if disable value was 2 */
778 if ((long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) > 1)
779 mail_parameters (NIL,SET_DISABLEPLAINTEXT,NIL);
780 /* unhide PLAIN SASL authenticator */
781 mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"PLAIN");
782 mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"LOGIN");
783 return;
788 while (i = ERR_get_error ()) /* SSL failure */
789 syslog (LOG_ERR,"SSL error status: %.80s",ERR_error_string (i,NIL));
790 ssl_close (stream); /* punt stream */
791 exit (1); /* punt this program too */
794 /* Generate one-time key for server
795 * Accepts: SSL connection
796 * export flag
797 * keylength
798 * Returns: generated key, always
801 static RSA *ssl_genkey (SSL *con,int export,int keylength)
803 unsigned long i;
804 static RSA *key = NIL;
805 if (!key) { /* if don't have a key already */
806 /* generate key */
807 if (!(key = RSA_generate_key (export ? keylength : 1024,RSA_F4,NIL,NIL))) {
808 syslog (LOG_ALERT,"Unable to generate temp key, host=%.80s",
809 tcp_clienthost ());
810 while (i = ERR_get_error ())
811 syslog (LOG_ALERT,"SSL error status: %s",ERR_error_string (i,NIL));
812 exit (1);
815 return key;
818 /* Wait for stdin input
819 * Accepts: timeout in seconds
820 * Returns: T if have input on stdin, else NIL
823 long ssl_server_input_wait (long seconds)
825 int i,sock;
826 fd_set fds,efd;
827 struct timeval tmo;
828 SSLSTREAM *stream;
829 if (!sslstdio) return server_input_wait (seconds);
830 /* input available in buffer */
831 if (((stream = sslstdio->sslstream)->ictr > 0) ||
832 !stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return LONGT;
833 /* sock ought to be 0 always */
834 if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
835 /* input available from SSL */
836 if (SSL_pending (stream->con) &&
837 ((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) > 0)) {
838 stream->iptr = stream->ibuf;/* point at TCP buffer */
839 stream->ictr = i; /* set new byte count */
840 return LONGT;
842 FD_ZERO (&fds); /* initialize selection vector */
843 FD_ZERO (&efd); /* initialize selection vector */
844 FD_SET (sock,&fds); /* set bit in selection vector */
845 FD_SET (sock,&efd); /* set bit in selection vector */
846 tmo.tv_sec = seconds; tmo.tv_usec = 0;
847 /* see if input available from the socket */
848 return select (sock+1,&fds,0,&efd,&tmo) ? LONGT : NIL;
851 #include "sslstdio.c"