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