* Define TLS_client_version to be the highest TLSv1*_client_method
[alpine.git] / imap / src / osdep / nt / ssl_nt.c
blob5b39f63b6aec102c22723e383d698d68ae70791f
1 /* ========================================================================
2 * Copyright 2018 Eduardo Chappa
3 * Copyright 2008-2009 Mark Crispin
4 * ========================================================================
5 */
7 /*
8 * Program: SSL authentication/encryption module for Windows 9x and NT
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
26 #ifdef ENABLE_WINDOWS_LIBRESSL
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 #ifndef TLS_client_method
38 #ifdef TLSv1_2_client_method
39 #define TLS_client_method TLSv1_2_client_method
40 #elif defined(TLSv1_1_client_method)
41 #define TLS_client_method TLSv1_1_client_method
42 #else
43 #define TLS_client_method TLSv1_client_method
44 #endif /* TLSv1_2_client_method */
45 #endif /* TLS_client_method */
46 #ifdef OPENSSL_1_1_0
47 #include <rsa.h>
48 #include <bn.h>
49 #ifdef TLSv1_client_method
50 #undef TLSv1_client_method
51 #endif /* TLSv1_client_method */
52 #ifdef TLSv1_1_client_method
53 #undef TLSv1_1_client_method
54 #endif /* TLSv1_1_client_method */
55 #ifdef TLSv1_2_client_method
56 #undef TLSv1_2_client_method
57 #endif /* TLSv1_2_client_method */
58 #ifdef DTLSv1_client_method
59 #undef DTLSv1_client_method
60 #endif /* DTLSv1_client_method */
61 #ifdef DTLSv1_2_client_method
62 #undef DTLSv1_2_client_method
63 #endif /* DTLSv1_2_client_method */
64 #define TLSv1_client_method TLS_client_method
65 #define TLSv1_1_client_method TLS_client_method
66 #define TLSv1_2_client_method TLS_client_method
67 #define DTLSv1_client_method DTLS_client_method
68 #define DTLSv1_2_client_method DTLS_client_method
69 #endif /* OPENSSL_1_1_0 */
70 #ifndef DTLSv1_2_client_method
71 #define DTLSv1_2_client_method DTLSv1_client_method
72 #endif /* DTLSv1_2_client_method */
73 #undef STRING
74 #undef crypt
76 #define SSLBUFLEN 8192
79 * PCI auditing compliance, disable:
80 * SSLv2
81 * anonymous D-H (no certificate
82 * export encryption ciphers (40 and 56 bits)
83 * low encryption cipher suites (40 and 56 bits, excluding export)
84 * null encryption (disabling implied by "ALL")
86 * UW imapd just disables low-grade and null ("ALL:!LOW"). This setting
87 * will break clients that attempt to use the newly-prohibited mechanisms.
89 * I question the value of disabling SSLv2, as opposed to disabling the SSL
90 * ports (e.g., 993 for IMAP, 995 for POP3) and using TLS exclusively.
93 #define SSLCIPHERLIST "ALL:!SSLv2:!ADH:!EXP:!LOW"
95 /* SSL I/O stream */
97 typedef struct ssl_stream {
98 TCPSTREAM *tcpstream; /* TCP stream */
99 SSL_CTX *context; /* SSL context */
100 SSL *con; /* SSL connection */
101 int ictr; /* input counter */
102 char *iptr; /* input pointer */
103 char ibuf[SSLBUFLEN]; /* input buffer */
104 } SSLSTREAM;
106 #include "sslio.h"
108 /* Function prototypes */
109 const SSL_METHOD *ssl_connect_mthd(int flag);
110 static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
111 static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags);
112 static int ssl_open_verify (int ok,X509_STORE_CTX *ctx);
113 static char *ssl_validate_cert (X509 *cert,char *host);
114 static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat);
115 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
116 long *contd);
117 static long ssl_abort (SSLSTREAM *stream);
119 #ifdef OPENSSL_1_1_0
120 #define SSL_CTX_TYPE SSL_CTX
121 #else
122 #define SSL_CTX_TYPE SSL
123 #endif /* OPENSSL_1_1_0 */
125 static RSA *ssl_genkey (SSL_CTX_TYPE *con,int export,int keylength);
128 /* Secure Sockets Layer network driver dispatch */
130 static struct ssl_driver ssldriver = {
131 ssl_open, /* open connection */
132 ssl_aopen, /* open preauthenticated connection */
133 ssl_getline, /* get a line */
134 ssl_getbuffer, /* get a buffer */
135 ssl_soutr, /* output pushed data */
136 ssl_sout, /* output string */
137 ssl_close, /* close connection */
138 ssl_host, /* return host name */
139 ssl_remotehost, /* return remote host name */
140 ssl_port, /* return port number */
141 ssl_localhost /* return local host name */
143 /* non-NIL if doing SSL primary I/O */
144 static SSLSTDIOSTREAM *sslstdio = NIL;
145 static char *start_tls = NIL; /* non-NIL if start TLS requested */
147 /* One-time SSL initialization */
149 static int sslonceonly = 0;
151 void ssl_onceonlyinit (void)
153 if (!sslonceonly++) { /* only need to call it once */
154 int fd;
155 char tmp[MAILTMPLEN];
156 struct stat sbuf;
157 /* if system doesn't have /dev/urandom */
158 if (stat ("/dev/urandom",&sbuf)) {
159 strcpy(tmp, "SSLXXXXXX");
160 fd = fopen(tmp,"a");
161 unlink (tmp); /* don't need the file */
162 fstat (fd,&sbuf); /* get information about the file */
163 close (fd); /* flush descriptor */
164 /* not great but it'll have to do */
165 sprintf (tmp + strlen (tmp),"%.80s%lx%.80s%lx%lx%lx%lx%lx",
166 tcp_serveraddr (),(unsigned long) tcp_serverport (),
167 tcp_clientaddr (),(unsigned long) tcp_clientport (),
168 (unsigned long) sbuf.st_ino,(unsigned long) time (0),
169 (unsigned long) gethostid (),(unsigned long) getpid ());
170 RAND_seed (tmp,strlen (tmp));
172 /* apply runtime linkage */
173 mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
174 mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
175 #ifdef OPENSSL_1_1_0
176 OPENSSL_init_ssl(0, NULL);
177 #else
178 SSL_library_init (); /* add all algorithms */
179 #endif /* OPENSSL_1_1_0 */
183 /* SSL open
184 * Accepts: host name
185 * contact service name
186 * contact port number
187 * Returns: SSL stream if success else NIL
190 SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
192 TCPSTREAM *stream = tcp_open (host,service,port);
193 return stream ? ssl_start (stream,host,port) : NIL;
197 /* SSL authenticated open
198 * Accepts: host name
199 * service name
200 * returned user name buffer
201 * Returns: SSL stream if success else NIL
204 SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
206 return NIL; /* don't use this mechanism with SSL */
209 /* ssl_connect_mthd: returns a context pointer to the connection to
210 * a ssl server
212 const SSL_METHOD *ssl_connect_mthd(int flag)
214 if (flag & NET_TRYTLS1)
215 #ifndef OPENSSL_NO_TLS1_METHOD
216 return TLSv1_client_method();
217 #else
218 return TLS_client_method();
219 #endif /* OPENSSL_NO_TLS1_METHOD */
221 else if(flag & NET_TRYTLS1_1)
222 #ifndef OPENSSL_NO_TLS1_1_METHOD
223 return TLSv1_1_client_method();
224 #else
225 return TLS_client_method();
226 #endif /* OPENSSL_NO_TLS1_1_METHOD */
228 else if(flag & NET_TRYTLS1_2)
229 #ifndef OPENSSL_NO_TLS1_2_METHOD
230 return TLSv1_2_client_method();
231 #else
232 return TLS_client_method();
233 #endif /* OPENSSL_NO_TLS1_2_METHOD */
235 else if(flag & NET_TRYTLS1_3)
236 return TLS_client_method();
238 else if(flag & NET_TRYDTLS1)
239 #ifndef OPENSSL_NO_DTLS1_METHOD
240 return DTLSv1_client_method();
241 #else
242 return DTLS_client_method();
243 #endif /* OPENSSL_NO_DTLS1_METHOD */
245 else if(flag & NET_TRYDTLS1_2)
246 #ifndef OPENSSL_NO_DTLS1_METHOD
247 return DTLSv1_2_client_method();
248 #else
249 return DTLS_client_method();
250 #endif /* OPENSSL_NO_DTLS1_METHOD */
252 else return SSLv23_client_method();
255 /* Start SSL/TLS negotiations
256 * Accepts: open TCP stream of session
257 * user's host name
258 * flags
259 * Returns: SSL stream if success else NIL
262 static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
264 char *reason,tmp[MAILTMPLEN];
265 sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
266 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
267 void *data = (*bn) (BLOCK_SENSITIVE,NIL);
268 SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
269 sizeof (SSLSTREAM));
270 stream->tcpstream = tstream; /* bind TCP stream */
271 /* do the work */
272 reason = ssl_start_work (stream,host,flags);
273 (*bn) (BLOCK_NONSENSITIVE,data);
274 if (reason) { /* failed? */
275 ssl_close (stream); /* failed to do SSL */
276 stream = NIL; /* no stream returned */
277 switch (*reason) { /* analyze reason */
278 case '*': /* certificate failure */
279 ++reason; /* skip over certificate failure indication */
280 /* pass to error callback */
281 if (sf) (*sf) (host,reason,flags);
282 else { /* no error callback, build error message */
283 sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
284 mm_log (tmp,ERROR);
286 case '\0': /* user answered no to certificate callback */
287 if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
288 stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
289 sizeof (SSLSTREAM));
290 break;
291 default: /* non-certificate failure */
292 if (flags & NET_TRYSSL); /* no error output if tryssl */
293 /* pass to error callback */
294 else if (sf) (*sf) (host,reason,flags);
295 else { /* no error callback, build error message */
296 sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
297 mm_log (tmp,ERROR);
299 break;
302 return stream;
305 /* Start SSL/TLS negotiations worker routine
306 * Accepts: SSL stream
307 * user's host name
308 * flags
309 * Returns: NIL if success, else error reason
312 /* evil but I had no choice */
313 static char *ssl_last_error = NIL;
314 static char *ssl_last_host = NIL;
316 static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
318 BIO *bio;
319 X509 *cert;
320 unsigned long sl,tl;
321 char *s,*t,*err,tmp[MAILTMPLEN], buf[256];
322 sslcertificatequery_t scq =
323 (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
324 sslclientcert_t scc =
325 (sslclientcert_t) mail_parameters (NIL,GET_SSLCLIENTCERT,NIL);
326 sslclientkey_t sck =
327 (sslclientkey_t) mail_parameters (NIL,GET_SSLCLIENTKEY,NIL);
328 if (ssl_last_error) fs_give ((void **) &ssl_last_error);
329 ssl_last_host = host;
330 if (!(stream->context = SSL_CTX_new (ssl_connect_mthd(flags))))
331 return "SSL context failed";
332 SSL_CTX_set_options (stream->context,0);
333 /* disable certificate validation? */
334 if (flags & NET_NOVALIDATECERT)
335 SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL);
336 else SSL_CTX_set_verify (stream->context,SSL_VERIFY_PEER,ssl_open_verify);
337 /* set default paths to CAs... */
338 SSL_CTX_set_default_verify_paths (stream->context);
339 /* ...unless a non-standard path desired */
340 if ((s = (char *) mail_parameters (NIL,GET_SSLCAPATH,NIL)) != NULL)
341 SSL_CTX_load_verify_locations (stream->context,NIL,s);
342 /* want to send client certificate? */
343 if (scc && (s = (*scc) ()) && (sl = strlen (s))) {
344 if ((cert = PEM_read_bio_X509 (bio = BIO_new_mem_buf (s,sl),NIL,NIL,NIL)) != NULL) {
345 SSL_CTX_use_certificate (stream->context,cert);
346 X509_free (cert);
348 BIO_free (bio);
349 if (!cert) return "SSL client certificate failed";
350 /* want to supply private key? */
351 if ((t = (sck ? (*sck) () : s)) && (tl = strlen (t))) {
352 EVP_PKEY *key;
353 if ((key = PEM_read_bio_PrivateKey (bio = BIO_new_mem_buf (t,tl),
354 NIL,NIL,"")) != NULL) {
355 SSL_CTX_use_PrivateKey (stream->context,key);
356 EVP_PKEY_free (key);
358 BIO_free (bio);
359 memset (t,0,tl); /* erase key */
361 if (s != t) memset (s,0,sl);/* erase certificate if different from key */
364 /* create connection */
365 if (!(stream->con = (SSL *) SSL_new (stream->context)))
366 return "SSL connection failed";
367 bio = BIO_new_socket (stream->tcpstream->tcpsi,BIO_NOCLOSE);
368 SSL_set_bio (stream->con,bio,bio);
369 SSL_set_connect_state (stream->con);
370 if (SSL_in_init (stream->con)) SSL_total_renegotiations (stream->con);
371 /* now negotiate SSL */
372 if (SSL_write (stream->con,"",0) < 0)
373 return ssl_last_error ? ssl_last_error : "SSL negotiation failed";
374 /* need to validate host names? */
375 if (!(flags & NET_NOVALIDATECERT) &&
376 (err = ssl_validate_cert (cert = SSL_get_peer_certificate (stream->con),
377 host))) {
378 /* application callback */
379 X509_NAME_oneline (X509_get_subject_name(cert), buf, sizeof(buf));
380 if (scq) return (*scq) (err,host,cert ? buf : "???") ? NIL : "";
381 /* error message to return via mm_log() */
382 sprintf (tmp,"*%.128s: %.255s",err,cert ? buf : "???");
383 return ssl_last_error = cpystr (tmp);
385 return NIL;
388 /* SSL certificate verification callback
389 * Accepts: error flag
390 * X509 context
391 * Returns: error flag
394 static int ssl_open_verify (int ok,X509_STORE_CTX *ctx)
396 char *err,cert[256],tmp[MAILTMPLEN];
397 sslcertificatequery_t scq =
398 (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
399 if (!ok) { /* in case failure */
400 err = (char *) X509_verify_cert_error_string
401 (X509_STORE_CTX_get_error (ctx));
402 X509_NAME_oneline (X509_get_subject_name
403 (X509_STORE_CTX_get_current_cert (ctx)),cert,255);
404 if (!scq) { /* mm_log() error message if no callback */
405 sprintf (tmp,"*%.128s: %.255s",err,cert);
406 ssl_last_error = cpystr (tmp);
408 /* ignore error if application says to */
409 else if ((*scq) (err,ssl_last_host,cert)) ok = T;
410 /* application wants punt */
411 else ssl_last_error = cpystr ("");
413 return ok;
417 /* SSL validate certificate
418 * Accepts: certificate
419 * host to validate against
420 * Returns: NIL if validated, else string of error message
423 static char *ssl_validate_cert (X509 *cert,char *host)
425 int i,n;
426 char *s=NULL,*t,*ret;
427 void *ext;
428 GENERAL_NAME *name;
429 X509_NAME *cname;
430 X509_NAME_ENTRY *e;
431 char buf[256];
432 /* make sure have a certificate */
433 if (!cert) ret = "No certificate from server";
434 /* and that it has a name */
435 else if (!(cname = X509_get_subject_name(cert))) ret = "No name in certificate";
436 /* locate CN */
437 else{
438 if((e = X509_NAME_get_entry(cname, X509_NAME_entry_count(cname)-1)) != NULL){
439 X509_NAME_get_text_by_OBJ(cname, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
440 s = (char *) buf;
442 else s = NULL;
444 if (s != NULL) {
445 /* host name matches pattern? */
446 ret = ssl_compare_hostnames (host,s) ? NIL :
447 "Server name does not match certificate";
448 /* if mismatch, see if in extensions */
449 if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
450 (n = sk_GENERAL_NAME_num (ext)))
451 /* older versions of OpenSSL use "ia5" instead of dNSName */
452 for (i = 0; ret && (i < n); i++)
453 if ((name = sk_GENERAL_NAME_value (ext,i)) &&
454 (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
455 ssl_compare_hostnames (host,s)) ret = NIL;
457 else ret = "Unable to locate common name in certificate";
458 return ret;
461 /* Case-independent wildcard pattern match
462 * Accepts: base string
463 * pattern string
464 * Returns: T if pattern matches base, else NIL
467 static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat)
469 long ret = NIL;
470 switch (*pat) {
471 case '*': /* wildcard */
472 if (pat[1]) { /* there must be a pattern suffix */
473 /* there is, scan base against it */
474 do if (ssl_compare_hostnames (s,pat+1)) ret = LONGT;
475 while (!ret && (*s != '.') && *s++);
477 break;
478 case '\0': /* end of pattern */
479 if (!*s) ret = LONGT; /* success if base is also at end */
480 break;
481 default: /* non-wildcard, recurse if match */
482 if (!compare_uchar (*pat,*s)) ret = ssl_compare_hostnames (s+1,pat+1);
483 break;
485 return ret;
488 /* SSL receive line
489 * Accepts: SSL stream
490 * Returns: text line string or NIL if failure
493 char *ssl_getline (SSLSTREAM *stream)
495 unsigned long n,contd;
496 char *ret = ssl_getline_work (stream,&n,&contd);
497 if (ret && contd) { /* got a line needing continuation? */
498 STRINGLIST *stl = mail_newstringlist ();
499 STRINGLIST *stc = stl;
500 do { /* collect additional lines */
501 stc->text.data = (unsigned char *) ret;
502 stc->text.size = n;
503 stc = stc->next = mail_newstringlist ();
504 ret = ssl_getline_work (stream,&n,&contd);
505 } while (ret && contd);
506 if (ret) { /* stash final part of line on list */
507 stc->text.data = (unsigned char *) ret;
508 stc->text.size = n;
509 /* determine how large a buffer we need */
510 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
511 ret = fs_get (n + 1); /* copy parts into buffer */
512 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
513 memcpy (ret + n,stc->text.data,stc->text.size);
514 ret[n] = '\0';
516 mail_free_stringlist (&stl);/* either way, done with list */
518 return ret;
521 /* SSL receive line or partial line
522 * Accepts: SSL stream
523 * pointer to return size
524 * pointer to return continuation flag
525 * Returns: text line string, size and continuation flag, or NIL if failure
528 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
529 long *contd)
531 unsigned long n;
532 char *s,*ret,c,d;
533 *contd = NIL; /* assume no continuation */
534 /* make sure have data */
535 if (!ssl_getdata (stream)) return NIL;
536 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
537 d = *stream->iptr++; /* slurp another character */
538 if ((c == '\015') && (d == '\012')) {
539 ret = (char *) fs_get (n--);
540 memcpy (ret,s,*size = n); /* copy into a free storage string */
541 ret[n] = '\0'; /* tie off string with null */
542 return ret;
545 /* copy partial string from buffer */
546 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
547 /* get more data from the net */
548 if (!ssl_getdata (stream)) fs_give ((void **) &ret);
549 /* special case of newline broken by buffer */
550 else if ((c == '\015') && (*stream->iptr == '\012')) {
551 stream->iptr++; /* eat the line feed */
552 stream->ictr--;
553 ret[*size = --n] = '\0'; /* tie off string with null */
555 else *contd = LONGT; /* continuation needed */
556 return ret;
559 /* SSL receive buffer
560 * Accepts: SSL stream
561 * size in bytes
562 * buffer to read into
563 * Returns: T if success, NIL otherwise
566 long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
568 unsigned long n;
569 while (size > 0) { /* until request satisfied */
570 if (!ssl_getdata (stream)) return NIL;
571 n = min (size,stream->ictr);/* number of bytes to transfer */
572 /* do the copy */
573 memcpy (buffer,stream->iptr,n);
574 buffer += n; /* update pointer */
575 stream->iptr += n;
576 size -= n; /* update # of bytes to do */
577 stream->ictr -= n;
579 buffer[0] = '\0'; /* tie off string */
580 return T;
583 /* SSL receive data
584 * Accepts: TCP/IP stream
585 * Returns: T if success, NIL otherwise
588 #ifdef FD_SETSIZE
589 #undef FD_SETSIZE
590 #endif /* FD_SETSIZE */
591 #define FD_SETSIZE 16384
593 long ssl_getdata (SSLSTREAM *stream)
595 int i,sock;
596 fd_set fds,efds;
597 struct timeval tmo;
598 tcptimeout_t tmoh = (tcptimeout_t) mail_parameters (NIL,GET_TIMEOUT,NIL);
599 long ttmo_read = (long) mail_parameters (NIL,GET_READTIMEOUT,NIL);
600 time_t t = time (0);
601 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
602 if (!stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return NIL;
603 /* tcp_unix should have prevented this */
604 if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
605 (*bn) (BLOCK_TCPREAD,NIL);
606 while (stream->ictr < 1) { /* if nothing in the buffer */
607 time_t tl = time (0); /* start of request */
608 time_t now = tl;
609 int ti = ttmo_read ? now + ttmo_read : 0;
610 if (SSL_pending (stream->con)) i = 1;
611 else {
612 if (tcpdebug) mm_log ("Reading SSL data",TCPDEBUG);
613 tmo.tv_usec = 0;
614 FD_ZERO (&fds); /* initialize selection vector */
615 FD_ZERO (&efds); /* handle errors too */
616 FD_SET (sock,&fds); /* set bit in selection vector */
617 FD_SET (sock,&efds); /* set bit in error selection vector */
618 errno = NIL; /* block and read */
619 do { /* block under timeout */
620 tmo.tv_sec = ti ? ti - now : 0;
621 i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
622 now = time (0); /* fake timeout if interrupt & time expired */
623 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
624 } while ((i < 0) && (errno == EINTR));
626 if (i) { /* non-timeout result from select? */
627 errno = 0; /* just in case */
628 if (i > 0) /* read what we can */
629 while (((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) < 0) &&
630 ((errno == EINTR) ||
631 (SSL_get_error (stream->con,i) == SSL_ERROR_WANT_READ)));
632 if (i <= 0) { /* error seen? */
633 if (tcpdebug) {
634 char *s,tmp[MAILTMPLEN];
635 if (i) sprintf (s = tmp,"SSL data read I/O error %d SSL error %d",
636 errno,SSL_get_error (stream->con,i));
637 else s = "SSL data read end of file";
638 mm_log (s,TCPDEBUG);
640 return ssl_abort (stream);
642 stream->iptr = stream->ibuf;/* point at TCP buffer */
643 stream->ictr = i; /* set new byte count */
644 if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG);
646 /* timeout, punt unless told not to */
647 else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->tcpstream->host)) {
648 if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG);
649 return ssl_abort (stream);
652 (*bn) (BLOCK_NONE,NIL);
653 return T;
656 /* SSL send string as record
657 * Accepts: SSL stream
658 * string pointer
659 * Returns: T if success else NIL
662 long ssl_soutr (SSLSTREAM *stream,char *string)
664 return ssl_sout (stream,string,(unsigned long) strlen (string));
668 /* SSL send string
669 * Accepts: SSL stream
670 * string pointer
671 * byte count
672 * Returns: T if success else NIL
675 long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
677 long i;
678 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
679 if (!stream->con) return NIL;
680 (*bn) (BLOCK_TCPWRITE,NIL);
681 if (tcpdebug) mm_log ("Writing to SSL",TCPDEBUG);
682 /* until request satisfied */
683 for (i = 0; size > 0; string += i,size -= i)
684 /* write as much as we can */
685 if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) < 0) {
686 if (tcpdebug) {
687 char tmp[MAILTMPLEN];
688 sprintf (tmp,"SSL data write I/O error %d SSL error %d",
689 errno,SSL_get_error (stream->con,i));
690 mm_log (tmp,TCPDEBUG);
692 return ssl_abort (stream);/* write failed */
694 if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
695 (*bn) (BLOCK_NONE,NIL);
696 return LONGT; /* all done */
699 /* SSL close
700 * Accepts: SSL stream
703 void ssl_close (SSLSTREAM *stream)
705 ssl_abort (stream); /* nuke the stream */
706 fs_give ((void **) &stream); /* flush the stream */
710 /* SSL abort stream
711 * Accepts: SSL stream
712 * Returns: NIL always
715 static long ssl_abort (SSLSTREAM *stream)
717 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
718 if (stream->con) { /* close SSL connection */
719 SSL_shutdown (stream->con);
720 SSL_free (stream->con);
721 stream->con = NIL;
723 if (stream->context) { /* clean up context */
724 SSL_CTX_free (stream->context);
725 stream->context = NIL;
727 if (stream->tcpstream) { /* close TCP stream */
728 tcp_close (stream->tcpstream);
729 stream->tcpstream = NIL;
731 (*bn) (BLOCK_NONE,NIL);
732 return NIL;
735 /* SSL get host name
736 * Accepts: SSL stream
737 * Returns: host name for this stream
740 char *ssl_host (SSLSTREAM *stream)
742 return stream ? tcp_host (stream->tcpstream) : "UNKNOWN";
746 /* SSL get remote host name
747 * Accepts: SSL stream
748 * Returns: host name for this stream
751 char *ssl_remotehost (SSLSTREAM *stream)
753 return tcp_remotehost (stream->tcpstream);
757 /* SSL return port for this stream
758 * Accepts: SSL stream
759 * Returns: port number for this stream
762 unsigned long ssl_port (SSLSTREAM *stream)
764 return tcp_port (stream->tcpstream);
768 /* SSL get local host name
769 * Accepts: SSL stream
770 * Returns: local host name
773 char *ssl_localhost (SSLSTREAM *stream)
775 return tcp_localhost (stream->tcpstream);
778 #ifdef SSL_CERT_DIRECTORY
779 #undef SSL_CERT_DIRECTORY
780 #endif /* SSL_CERT_DIRECTORY */
781 #define SSL_CERT_DIRECTORY "C:\\\\libressl\\ssl\\certs"
783 #ifdef SSL_KEY_DIRECTORY
784 #undef SSL_KEY_DIRECTORY
785 #endif /* SSL_KEY_DIRECTORY */
786 #define SSL_KEY_DIRECTORY "C:\\\\libressl\\ssl\\private"
789 /* Start TLS
790 * Accepts: /etc/services service name
791 * Returns: cpystr'd error string if TLS failed, else NIL for success
793 char *ssl_start_tls (char *server)
795 char tmp[MAILTMPLEN];
796 struct stat sbuf;
797 if (sslstdio) return cpystr ("Already in an SSL session");
798 if (start_tls) return cpystr ("TLS already started");
799 if (server) { /* build specific certificate/key file name */
800 sprintf (tmp,"%s\\%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
801 if (stat (tmp,&sbuf)) { /* use non-specific name if no specific file */
802 sprintf (tmp,"%s\\%s.pem",SSL_CERT_DIRECTORY,server);
803 if (stat (tmp,&sbuf)) return cpystr ("Server certificate not installed");
805 start_tls = server; /* switch to STARTTLS mode */
807 return NIL;
810 /* Init server for SSL
811 * Accepts: server name
814 void ssl_server_init (char *server)
816 char cert[MAILTMPLEN],key[MAILTMPLEN];
817 unsigned long i;
818 struct stat sbuf;
819 SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
820 sizeof (SSLSTREAM));
821 ssl_onceonlyinit (); /* make sure algorithms added */
822 #ifdef OPENSSL_1_1_0
823 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
824 OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
825 #else
826 ERR_load_crypto_strings ();
827 SSL_load_error_strings ();
828 #endif /* OPENSSL_1_1_0 */
829 /* build specific certificate/key file names */
830 sprintf (cert,"%s\\%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
831 sprintf (key,"%s\\%s-%s.pem",SSL_KEY_DIRECTORY,server,tcp_serveraddr ());
832 /* use non-specific name if no specific cert */
833 if (stat (cert,&sbuf)) sprintf (cert,"%s\\%s.pem",SSL_CERT_DIRECTORY,server);
834 if (stat (key,&sbuf)) { /* use non-specific name if no specific key */
835 sprintf (key,"%s\\%s.pem",SSL_KEY_DIRECTORY,server);
836 /* use cert file as fallback for key */
837 if (stat (key,&sbuf)) strcpy (key,cert);
839 /* create context */
840 #ifdef OPENSSL_1_1_0
841 if (!(stream->context = SSL_CTX_new (start_tls ?
842 TLS_server_method () :
843 SSLv23_server_method ())))
844 #else
845 if (!(stream->context = SSL_CTX_new (start_tls ?
846 TLSv1_server_method () :
847 SSLv23_server_method ())))
848 #endif /* OPENSSL_1_1_0 */
849 syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s",
850 tcp_clienthost ());
851 else { /* set context options */
852 SSL_CTX_set_options (stream->context,SSL_OP_ALL);
853 /* set cipher list */
854 if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST))
855 syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s",
856 SSLCIPHERLIST,tcp_clienthost ());
857 /* load certificate */
858 else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert))
859 syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s",
860 cert,tcp_clienthost ());
861 /* load key */
862 else if (!(SSL_CTX_use_RSAPrivateKey_file (stream->context,key,
863 SSL_FILETYPE_PEM)))
864 syslog (LOG_ALERT,"Unable to load private key from %.80s, host=%.80s",
865 key,tcp_clienthost ());
867 else { /* generate key if needed */
868 #ifdef OPENSSL_1_1_0
869 if (0)
870 ssl_genkey(stream->context, 0, 0);
871 #else
872 if (SSL_CTX_need_tmp_RSA (stream->context))
873 SSL_CTX_set_tmp_rsa_callback (stream->context,ssl_genkey);
874 #endif /* OPENSSL_1_1_0 */
875 /* create new SSL connection */
876 if (!(stream->con = SSL_new (stream->context)))
877 syslog (LOG_ALERT,"Unable to create SSL connection, host=%.80s",
878 tcp_clienthost ());
879 else { /* set file descriptor */
880 SSL_set_fd (stream->con,0);
881 /* all OK if accepted */
882 if (SSL_accept (stream->con) < 0)
883 syslog (LOG_INFO,"Unable to accept SSL connection, host=%.80s",
884 tcp_clienthost ());
885 else { /* server set up */
886 sslstdio = (SSLSTDIOSTREAM *)
887 memset (fs_get (sizeof(SSLSTDIOSTREAM)),0,sizeof (SSLSTDIOSTREAM));
888 sslstdio->sslstream = stream;
889 /* available space in output buffer */
890 sslstdio->octr = SSLBUFLEN;
891 /* current output buffer pointer */
892 sslstdio->optr = sslstdio->obuf;
893 /* allow plaintext if disable value was 2 */
894 if ((long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) > 1)
895 mail_parameters (NIL,SET_DISABLEPLAINTEXT,NIL);
896 /* unhide PLAIN SASL authenticator */
897 mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"PLAIN");
898 mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"LOGIN");
899 return;
904 while ((i = ERR_get_error ()) != 0L) /* SSL failure */
905 syslog (LOG_ERR,"SSL error status: %.80s",ERR_error_string (i,NIL));
906 ssl_close (stream); /* punt stream */
907 exit (1); /* punt this program too */
910 /* Generate one-time key for server
911 * Accepts: SSL connection
912 * export flag
913 * keylength
914 * Returns: generated key, always
917 static RSA *ssl_genkey (SSL_CTX_TYPE *con,int export,int keylength)
919 unsigned long i;
920 static RSA *key = NIL;
921 if (!key) { /* if don't have a key already */
922 /* generate key */
923 #ifdef OPENSSL_1_1_0
924 BIGNUM *e = BN_new();
925 if (!RSA_generate_key_ex (key, export ? keylength : 1024, e,NIL)) {
926 #else
927 if (!(key = RSA_generate_key (export ? keylength : 1024,RSA_F4,NIL,NIL))) {
928 #endif /* OPENSSL_1_1_0 */
929 syslog (LOG_ALERT,"Unable to generate temp key, host=%.80s",
930 tcp_clienthost ());
931 while ((i = ERR_get_error ()) != 0L)
932 syslog (LOG_ALERT,"SSL error status: %s",ERR_error_string (i,NIL));
933 exit (1);
935 #ifdef OPENSSL_1_1_0
936 BN_free(e);
937 e = NULL;
938 #endif /* OPENSSL_1_1_0 */
940 return key;
943 /* Wait for stdin input
944 * Accepts: timeout in seconds
945 * Returns: T if have input on stdin, else NIL
948 long ssl_server_input_wait (long seconds)
950 int i,sock;
951 fd_set fds,efd;
952 struct timeval tmo;
953 SSLSTREAM *stream;
954 if (!sslstdio) return server_input_wait (seconds);
955 /* input available in buffer */
956 if (((stream = sslstdio->sslstream)->ictr > 0) ||
957 !stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return LONGT;
958 /* sock ought to be 0 always */
959 if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
960 /* input available from SSL */
961 if (SSL_pending (stream->con) &&
962 ((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) > 0)) {
963 stream->iptr = stream->ibuf;/* point at TCP buffer */
964 stream->ictr = i; /* set new byte count */
965 return LONGT;
967 FD_ZERO (&fds); /* initialize selection vector */
968 FD_ZERO (&efd); /* initialize selection vector */
969 FD_SET (sock,&fds); /* set bit in selection vector */
970 FD_SET (sock,&efd); /* set bit in selection vector */
971 tmo.tv_sec = seconds; tmo.tv_usec = 0;
972 /* see if input available from the socket */
973 return select (sock+1,&fds,0,&efd,&tmo) ? LONGT : NIL;
976 #include "sslstdio.c"
977 #else /* ENABLE_WINDOWS_LIBRESSL */
978 #define SECURITY_WIN32
979 #include <sspi.h>
980 #include <schannel.h>
983 #define SSLBUFLEN 8192
986 /* SSL I/O stream */
988 typedef struct ssl_stream {
989 TCPSTREAM *tcpstream; /* TCP stream */
990 CredHandle cred; /* SSL credentials */
991 CtxtHandle context; /* SSL context */
992 /* stream encryption sizes */
993 SecPkgContext_StreamSizes sizes;
994 size_t bufsize;
995 int ictr; /* input counter */
996 char *iptr; /* input pointer */
997 int iextractr; /* extra input counter */
998 char *iextraptr; /* extra input pointer */
999 char *ibuf; /* input buffer */
1000 char *obuf; /* output buffer */
1001 } SSLSTREAM;
1003 #include "sslio.h"
1006 /* Function prototypes */
1008 static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
1009 static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
1010 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
1011 long *contd);
1012 static long ssl_abort (SSLSTREAM *stream);
1014 /* Secure Sockets Layer network driver dispatch */
1016 static struct ssl_driver ssldriver = {
1017 ssl_open, /* open connection */
1018 ssl_aopen, /* open preauthenticated connection */
1019 ssl_getline, /* get a line */
1020 ssl_getbuffer, /* get a buffer */
1021 ssl_soutr, /* output pushed data */
1022 ssl_sout, /* output string */
1023 ssl_close, /* close connection */
1024 ssl_host, /* return host name */
1025 ssl_remotehost, /* return remote host name */
1026 ssl_port, /* return port number */
1027 ssl_localhost /* return local host name */
1030 /* security function table */
1031 static SecurityFunctionTable *sft = NIL;
1032 static unsigned long ssltsz = 0;/* SSL maximum token length */
1035 /* Define crypt32.dll stuff here in case a pre-IE5 Win9x system */
1037 typedef DWORD (CALLBACK *CNTS) (DWORD,PCERT_NAME_BLOB,DWORD,LPSTR,DWORD);
1038 typedef BOOL (CALLBACK *CGCC) (HCERTCHAINENGINE,PCCERT_CONTEXT,LPFILETIME,
1039 HCERTSTORE,PCERT_CHAIN_PARA,DWORD,LPVOID,
1040 PCCERT_CHAIN_CONTEXT *);
1041 typedef BOOL (CALLBACK *CVCCP) (LPCSTR,PCCERT_CHAIN_CONTEXT,
1042 PCERT_CHAIN_POLICY_PARA,
1043 PCERT_CHAIN_POLICY_STATUS);
1044 typedef VOID (CALLBACK *CFCC) (PCCERT_CHAIN_CONTEXT);
1045 typedef BOOL (CALLBACK *CFCCX) (PCCERT_CONTEXT);
1047 static CNTS certNameToStr = NIL;
1048 static CGCC certGetCertificateChain = NIL;
1049 static CVCCP certVerifyCertificateChainPolicy = NIL;
1050 static CFCC certFreeCertificateChain = NIL;
1051 static CFCCX certFreeCertificateContext = NIL;
1053 /* One-time SSL initialization */
1055 static int sslonceonly = 0;
1057 void ssl_onceonlyinit (void)
1059 if (!sslonceonly++) { /* only need to call it once */
1060 HINSTANCE lib;
1061 FARPROC pi;
1062 ULONG np;
1063 SecPkgInfo *pp;
1064 int i;
1065 /* get security library */
1066 if (((lib = LoadLibrary ("schannel.dll")) ||
1067 (lib = LoadLibrary ("security.dll"))) &&
1068 (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) &&
1069 (sft = (SecurityFunctionTable *) pi ()) &&
1070 !(sft->EnumerateSecurityPackages (&np,&pp))) {
1071 /* look for an SSL package */
1072 for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
1073 /* note maximum token size and name */
1074 ssltsz = pp[i].cbMaxToken;
1075 /* apply runtime linkage */
1076 mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
1077 mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
1078 if ((lib = LoadLibrary ("crypt32.dll")) &&
1079 (certGetCertificateChain = (CGCC)
1080 GetProcAddress (lib,"CertGetCertificateChain")) &&
1081 (certVerifyCertificateChainPolicy = (CVCCP)
1082 GetProcAddress (lib,"CertVerifyCertificateChainPolicy")) &&
1083 (certFreeCertificateChain = (CFCC)
1084 GetProcAddress (lib,"CertFreeCertificateChain")) &&
1085 (certFreeCertificateContext = (CFCCX)
1086 GetProcAddress (lib,"CertFreeCertificateContext")))
1087 certNameToStr = (CNTS) GetProcAddress (lib,"CertNameToStrA");
1088 return; /* all done */
1094 /* SSL open
1095 * Accepts: host name
1096 * contact service name
1097 * contact port number
1098 * Returns: SSL stream if success else NIL
1101 SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
1103 TCPSTREAM *stream = tcp_open (host,service,port);
1104 return stream ? ssl_start (stream,host,port) : NIL;
1108 /* SSL authenticated open
1109 * Accepts: host name
1110 * service name
1111 * returned user name buffer
1112 * Returns: SSL stream if success else NIL
1115 SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
1117 return NIL; /* don't use this mechanism with SSL */
1120 /* Start SSL/TLS negotiations
1121 * Accepts: open TCP stream of session
1122 * user's host name
1123 * flags
1124 * Returns: SSL stream if success else NIL
1127 static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
1129 SECURITY_STATUS e;
1130 ULONG a;
1131 TimeStamp t;
1132 SecBuffer ibuf[2],obuf[1];
1133 SecBufferDesc ibufs,obufs;
1134 SCHANNEL_CRED tlscred;
1135 CERT_CONTEXT *cert = NIL;
1136 CERT_CHAIN_PARA chparam;
1137 CERT_CHAIN_CONTEXT *chain;
1138 SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy;
1139 CERT_CHAIN_POLICY_PARA polparam;
1140 CERT_CHAIN_POLICY_STATUS status;
1141 char tmp[MAILTMPLEN],certname[256];
1142 char *reason = NIL;
1143 ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
1144 ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
1145 ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
1146 ISC_REQ_MANUAL_CRED_VALIDATION;
1147 LPSTR usage[] = {
1148 szOID_PKIX_KP_SERVER_AUTH,
1149 szOID_SERVER_GATED_CRYPTO,
1150 szOID_SGC_NETSCAPE
1152 PWSTR whost = NIL;
1153 char *buf = (char *) fs_get (ssltsz);
1154 unsigned long size = 0;
1155 sslcertificatequery_t scq =
1156 (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
1157 sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
1158 SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
1159 sizeof (SSLSTREAM));
1160 stream->tcpstream = tstream; /* bind TCP stream */
1161 /* initialize TLS credential */
1162 memset (&tlscred,0,sizeof (SCHANNEL_CRED));
1163 tlscred.dwVersion = SCHANNEL_CRED_VERSION;
1164 tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
1166 /* acquire credentials */
1167 if (sft->AcquireCredentialsHandle
1168 (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
1169 &tlscred : NIL,NIL,NIL,&stream->cred,&t)
1170 != SEC_E_OK) reason = "Acquire credentials handle failed";
1171 else while (!reason) { /* negotiate security context */
1172 /* initialize buffers */
1173 ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
1174 ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
1175 obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
1176 ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
1177 ibuf[1].BufferType = SECBUFFER_EMPTY;
1178 /* initialize buffer descriptors */
1179 ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
1180 ibufs.cBuffers = 2; obufs.cBuffers = 1;
1181 ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
1182 /* negotiate security */
1183 e = sft->InitializeSecurityContext
1184 (&stream->cred,size ? &stream->context : NIL,host,req,0,
1185 SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
1186 /* have an output buffer we need to send? */
1187 if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
1188 if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
1189 reason = "Unexpected TCP output disconnect";
1190 /* free the buffer */
1191 sft->FreeContextBuffer (obuf[0].pvBuffer);
1193 if (!reason) switch (e) { /* negotiation state */
1194 case SEC_I_INCOMPLETE_CREDENTIALS:
1195 break; /* server wants client auth */
1196 case SEC_I_CONTINUE_NEEDED:
1197 if (size) { /* continue, read any data? */
1198 /* yes, anything regurgiated back to us? */
1199 if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
1200 /* yes, set this as the new data */
1201 memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
1202 size = ibuf[1].cbBuffer;
1203 break;
1205 size = 0; /* otherwise, read more stuff from server */
1207 case SEC_E_INCOMPLETE_MESSAGE:
1208 /* need to read more data from server */
1209 if (!tcp_getdata (stream->tcpstream))
1210 reason = "Unexpected TCP input disconnect";
1211 else {
1212 memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
1213 size += stream->tcpstream->ictr;
1214 /* empty it from TCP's buffers */
1215 stream->tcpstream->iptr += stream->tcpstream->ictr;
1216 stream->tcpstream->ictr = 0;
1218 break;
1220 case SEC_E_OK: /* success, any data to be regurgitated? */
1221 if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
1222 /* yes, set this as the new data */
1223 memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
1224 buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
1225 stream->tcpstream->ictr = ibuf[1].cbBuffer;
1227 if (certNameToStr && !(flags & NET_NOVALIDATECERT)) {
1228 /* need validation, make wchar of host */
1229 if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) &&
1230 (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) &&
1231 MultiByteToWideChar (CP_ACP,0,host,-1,whost,size)))
1232 fatal ("Can't make wchar of host name!");
1233 /* get certificate */
1234 if ((sft->QueryContextAttributes
1235 (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) !=
1236 SEC_E_OK) || !cert) {
1237 reason = "*Unable to get certificate";
1238 strcpy (certname,"<no certificate>");
1240 else { /* get certificate subject name */
1241 (*certNameToStr) (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
1242 &cert->pCertInfo->Subject,CERT_X500_NAME_STR,
1243 certname,255);
1244 /* build certificate chain */
1245 memset (&chparam,0,sizeof (chparam));
1246 chparam.cbSize = sizeof (chparam);
1247 chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
1248 chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage;
1249 chparam.RequestedUsage.Usage.cUsageIdentifier =
1250 sizeof (usage) / sizeof (LPSTR);
1251 if (!(*certGetCertificateChain)
1252 (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain))
1253 reason = ssl_analyze_status (GetLastError (),tmp);
1254 else { /* validate certificate chain */
1255 memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
1256 policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
1257 policy.dwAuthType = AUTHTYPE_SERVER;
1258 policy.fdwChecks = NIL;
1259 policy.pwszServerName = whost;
1260 memset (&polparam,0,sizeof (polparam));
1261 polparam.cbSize = sizeof (polparam);
1262 polparam.pvExtraPolicyPara = &policy;
1263 memset (&status,0,sizeof (status));
1264 status.cbSize = sizeof (status);
1265 if (!(*certVerifyCertificateChainPolicy)
1266 (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status))
1267 reason = ssl_analyze_status (GetLastError (),tmp);
1268 else if (status.dwError)
1269 reason = ssl_analyze_status (status.dwError,tmp);
1270 (*certFreeCertificateChain) (chain);
1272 (*certFreeCertificateContext) (cert);
1274 if (whost) fs_give ((void **) &whost);
1276 if (reason) { /* got an error? */
1277 /* application callback */
1278 if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason,
1279 host,certname) ? NIL : "";
1280 else if (*certname) { /* error message to return via mm_log() */
1281 sprintf (buf,"*%.128s: %.255s",
1282 (*reason == '*') ? reason + 1 : reason,certname);
1283 reason = buf;
1287 if (reason ||
1288 (reason = ssl_analyze_status
1289 (sft->QueryContextAttributes
1290 (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf)))
1291 break; /* error in certificate or getting sizes */
1292 fs_give ((void **) &buf); /* flush temporary buffer */
1293 /* make maximum-sized buffers */
1294 stream->bufsize = stream->sizes.cbHeader +
1295 stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
1296 if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
1297 fatal ("cbMaximumMessage is less than SSLBUFLEN!");
1298 else if (stream->sizes.cbMaximumMessage < 16384) {
1299 sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
1300 (long) stream->sizes.cbMaximumMessage);
1301 mm_log (tmp,NIL);
1303 stream->ibuf = (char *) fs_get (stream->bufsize);
1304 stream->obuf = (char *) fs_get (stream->bufsize);
1305 return stream;
1306 default:
1307 reason = ssl_analyze_status (e,buf);
1310 ssl_close (stream); /* failed to do SSL */
1311 stream = NIL; /* no stream returned */
1312 switch (*reason) { /* analyze reason */
1313 case '*': /* certificate failure */
1314 ++reason; /* skip over certificate failure indication */
1315 /* pass to error callback */
1316 if (sf) (*sf) (host,reason,flags);
1317 else { /* no error callback, build error message */
1318 sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
1319 mm_log (tmp,ERROR);
1321 case '\0': /* user answered no to certificate callback */
1322 if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
1323 stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
1324 sizeof (SSLSTREAM));
1325 break;
1326 default: /* non-certificate failure */
1327 if (flags & NET_TRYSSL); /* no error output if tryssl */
1328 /* pass to error callback */
1329 else if (sf) (*sf) (host,reason,flags);
1330 else { /* no error callback, build error message */
1331 sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
1332 mm_log (tmp,ERROR);
1334 break;
1336 fs_give ((void **) &buf); /* flush temporary buffer */
1337 return stream;
1340 /* Generate error text from SSL error code
1341 * Accepts: SSL status
1342 * scratch buffer
1343 * Returns: text if error status, else NIL
1346 static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
1348 switch (err) {
1349 case SEC_E_OK: /* no error */
1350 case SEC_I_CONTINUE_NEEDED:
1351 case SEC_I_INCOMPLETE_CREDENTIALS:
1352 case SEC_E_INCOMPLETE_MESSAGE:
1353 return NIL;
1354 case SEC_E_NO_AUTHENTICATING_AUTHORITY:
1355 mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL);
1356 return "*No authority could be contacted for authentication";
1357 case SEC_E_WRONG_PRINCIPAL:
1358 mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL);
1359 case CERT_E_CN_NO_MATCH:
1360 return "*Server name does not match certificate";
1361 case SEC_E_UNTRUSTED_ROOT:
1362 mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL);
1363 case CERT_E_UNTRUSTEDROOT:
1364 return "*Self-signed certificate or untrusted authority";
1365 case SEC_E_CERT_EXPIRED:
1366 mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL);
1367 case CERT_E_EXPIRED:
1368 return "*Certificate has expired";
1369 case CERT_E_REVOKED:
1370 return "*Certificate revoked";
1371 case SEC_E_INVALID_TOKEN:
1372 return "Invalid token, probably not an SSL server";
1373 case SEC_E_UNSUPPORTED_FUNCTION:
1374 return "SSL not supported on this machine - upgrade your system software";
1376 sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err);
1377 return buf;
1380 /* SSL receive line
1381 * Accepts: SSL stream
1382 * Returns: text line string or NIL if failure
1385 char *ssl_getline (SSLSTREAM *stream)
1387 unsigned long n,contd;
1388 char *ret = ssl_getline_work (stream,&n,&contd);
1389 if (ret && contd) { /* got a line needing continuation? */
1390 STRINGLIST *stl = mail_newstringlist ();
1391 STRINGLIST *stc = stl;
1392 do { /* collect additional lines */
1393 stc->text.data = (unsigned char *) ret;
1394 stc->text.size = n;
1395 stc = stc->next = mail_newstringlist ();
1396 ret = ssl_getline_work (stream,&n,&contd);
1397 } while (ret && contd);
1398 if (ret) { /* stash final part of line on list */
1399 stc->text.data = (unsigned char *) ret;
1400 stc->text.size = n;
1401 /* determine how large a buffer we need */
1402 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
1403 ret = fs_get (n + 1); /* copy parts into buffer */
1404 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
1405 memcpy (ret + n,stc->text.data,stc->text.size);
1406 ret[n] = '\0';
1408 mail_free_stringlist (&stl);/* either way, done with list */
1410 return ret;
1413 /* SSL receive line or partial line
1414 * Accepts: SSL stream
1415 * pointer to return size
1416 * pointer to return continuation flag
1417 * Returns: text line string, size and continuation flag, or NIL if failure
1420 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
1421 long *contd)
1423 unsigned long n;
1424 char *s,*ret,c,d;
1425 *contd = NIL; /* assume no continuation */
1426 /* make sure have data */
1427 if (!ssl_getdata (stream)) return NIL;
1428 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
1429 d = *stream->iptr++; /* slurp another character */
1430 if ((c == '\015') && (d == '\012')) {
1431 ret = (char *) fs_get (n--);
1432 memcpy (ret,s,*size = n); /* copy into a free storage string */
1433 ret[n] = '\0'; /* tie off string with null */
1434 return ret;
1437 /* copy partial string from buffer */
1438 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
1439 /* get more data from the net */
1440 if (!ssl_getdata (stream)) fs_give ((void **) &ret);
1441 /* special case of newline broken by buffer */
1442 else if ((c == '\015') && (*stream->iptr == '\012')) {
1443 stream->iptr++; /* eat the line feed */
1444 stream->ictr--;
1445 ret[*size = --n] = '\0'; /* tie off string with null */
1447 else *contd = LONGT; /* continuation needed */
1448 return ret;
1451 /* SSL receive buffer
1452 * Accepts: SSL stream
1453 * size in bytes
1454 * buffer to read into
1455 * Returns: T if success, NIL otherwise
1458 long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
1460 unsigned long n;
1461 while (size > 0) { /* until request satisfied */
1462 if (!ssl_getdata (stream)) return NIL;
1463 n = min (size,stream->ictr);/* number of bytes to transfer */
1464 /* do the copy */
1465 memcpy (buffer,stream->iptr,n);
1466 buffer += n; /* update pointer */
1467 stream->iptr += n;
1468 size -= n; /* update # of bytes to do */
1469 stream->ictr -= n;
1471 buffer[0] = '\0'; /* tie off string */
1472 return T;
1475 /* SSL receive data
1476 * Accepts: TCP/IP stream
1477 * Returns: T if success, NIL otherwise
1480 long ssl_getdata (SSLSTREAM *stream)
1482 while (stream->ictr < 1) { /* decrypted buffer empty? */
1483 SECURITY_STATUS status;
1484 SecBuffer buf[4];
1485 SecBufferDesc msg;
1486 size_t i;
1487 size_t n = 0; /* initially no bytes to decrypt */
1488 if (!stream->tcpstream) return NIL;
1489 do { /* yes, make sure have data from TCP */
1490 if (stream->iextractr) { /* have previous unread data? */
1491 memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
1492 n += stream->iextractr; /* update number of bytes read */
1493 stream->iextractr = 0; /* no more extra data */
1495 else { /* read from TCP */
1496 if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
1497 /* maximum amount of data to copy */
1498 if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
1499 fatal ("incomplete SecBuffer exceeds maximum buffer size");
1500 /* do the copy */
1501 memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
1502 stream->tcpstream->iptr += i;
1503 stream->tcpstream->ictr -= i;
1504 n += i; /* update number of bytes to decrypt */
1506 buf[0].cbBuffer = n; /* first SecBuffer gets data */
1507 buf[0].pvBuffer = stream->ibuf;
1508 buf[0].BufferType = SECBUFFER_DATA;
1509 /* subsequent ones are for spares */
1510 buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
1511 SECBUFFER_EMPTY;
1512 msg.ulVersion = SECBUFFER_VERSION;
1513 msg.cBuffers = 4; /* number of SecBuffers */
1514 msg.pBuffers = buf; /* first SecBuffer */
1516 } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4)
1517 (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
1518 switch (status) {
1519 case SEC_E_OK: /* won */
1520 case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
1521 /* hunt for a buffer */
1522 for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
1523 if (i < 4) { /* found a buffer? */
1524 /* yes, set up pointer and counter */
1525 stream->iptr = buf[i].pvBuffer;
1526 stream->ictr = buf[i].cbBuffer;
1527 /* any unprocessed data? */
1528 while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
1529 /* yes, note for next time around */
1530 stream->iextraptr = buf[i].pvBuffer;
1531 stream->iextractr = buf[i].cbBuffer;
1534 break;
1535 default: /* anything else means we've lost */
1536 return ssl_abort (stream);
1539 return LONGT;
1542 /* SSL send string as record
1543 * Accepts: SSL stream
1544 * string pointer
1545 * Returns: T if success else NIL
1548 long ssl_soutr (SSLSTREAM *stream,char *string)
1550 return ssl_sout (stream,string,(unsigned long) strlen (string));
1554 /* SSL send string
1555 * Accepts: SSL stream
1556 * string pointer
1557 * byte count
1558 * Returns: T if success else NIL
1561 long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
1563 SecBuffer buf[4];
1564 SecBufferDesc msg;
1565 char *s;
1566 size_t n;
1567 if (!stream->tcpstream) return NIL;
1568 /* until request satisfied */
1569 for (s = stream->ibuf,n = 0; size;) {
1570 /* header */
1571 buf[0].BufferType = SECBUFFER_STREAM_HEADER;
1572 memset (buf[0].pvBuffer = stream->obuf,0,
1573 buf[0].cbBuffer = stream->sizes.cbHeader);
1574 /* message (up to maximum size) */
1575 buf[1].BufferType = SECBUFFER_DATA;
1576 memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
1577 buf[1].cbBuffer = min (size,SSLBUFLEN));
1578 /* trailer */
1579 buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
1580 memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
1581 buf[2].cbBuffer = stream->sizes.cbTrailer);
1582 /* spare */
1583 buf[3].BufferType = SECBUFFER_EMPTY;
1584 msg.ulVersion = SECBUFFER_VERSION;
1585 msg.cBuffers = 4; /* number of SecBuffers */
1586 msg.pBuffers = buf; /* first SecBuffer */
1587 string += buf[1].cbBuffer;
1588 size -= buf[1].cbBuffer; /* this many bytes processed */
1589 /* encrypt and send message */
1590 if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3)
1591 (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
1592 !tcp_sout (stream->tcpstream,stream->obuf,
1593 buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
1594 return ssl_abort (stream);/* encryption or sending failed */
1596 return LONGT;
1599 /* SSL close
1600 * Accepts: SSL stream
1603 void ssl_close (SSLSTREAM *stream)
1605 ssl_abort (stream); /* nuke the stream */
1606 fs_give ((void **) &stream); /* flush the stream */
1610 /* SSL abort stream
1611 * Accepts: SSL stream
1612 * Returns: NIL always
1615 static long ssl_abort (SSLSTREAM *stream)
1617 if (stream->tcpstream) { /* close TCP stream */
1618 sft->DeleteSecurityContext (&stream->context);
1619 sft->FreeCredentialHandle (&stream->cred);
1620 tcp_close (stream->tcpstream);
1621 stream->tcpstream = NIL;
1623 if (stream->ibuf) fs_give ((void **) &stream->ibuf);
1624 if (stream->obuf) fs_give ((void **) &stream->obuf);
1625 return NIL;
1628 /* SSL get host name
1629 * Accepts: SSL stream
1630 * Returns: host name for this stream
1633 char *ssl_host (SSLSTREAM *stream)
1635 return stream ? tcp_host (stream->tcpstream) : "UNKNOWN";
1639 /* SSL get remote host name
1640 * Accepts: SSL stream
1641 * Returns: host name for this stream
1644 char *ssl_remotehost (SSLSTREAM *stream)
1646 return tcp_remotehost (stream->tcpstream);
1650 /* SSL return port for this stream
1651 * Accepts: SSL stream
1652 * Returns: port number for this stream
1655 unsigned long ssl_port (SSLSTREAM *stream)
1657 return tcp_port (stream->tcpstream);
1661 /* SSL get local host name
1662 * Accepts: SSL stream
1663 * Returns: local host name
1666 char *ssl_localhost (SSLSTREAM *stream)
1668 return tcp_localhost (stream->tcpstream);
1671 #include "ssl_none.c" /* currently no server support */
1673 #endif /* ENABLE_WINDOWS_LIBRESSL */