Index: mail/cclient/src/c-client/mail.h =================================================================== --- mail/cclient/src/c-client/mail.h +++ mail/cclient/src/c-client/mail.h @@ -177,6 +177,10 @@ #define SET_EXTERNALAUTHID (long) 230 #define GET_SSLCAPATH (long) 231 #define SET_SSLCAPATH (long) 232 +#define GET_SSLPROTOCOLS (long) 295 +#define SET_SSLPROTOCOLS (long) 296 +#define GET_SSLCIPHERLIST (long) 297 +#define SET_SSLCIPHERLIST (long) 298 /* 3xx: TCP/IP */ #define GET_OPENTIMEOUT (long) 300 Index: mail/cclient/src/osdep/unix/env_unix.c =================================================================== --- mail/cclient/src/osdep/unix/env_unix.c +++ mail/cclient/src/osdep/unix/env_unix.c @@ -74,6 +74,8 @@ /* black box default home directory */ static char *blackBoxDefaultHome = NIL; static char *sslCApath = NIL; /* non-standard CA path */ +static char *sslProtocols = NIL ; /* list of allowed SSL protocols */ +static char *sslCipherList = NIL; /* list of allowed SSL ciphers */ static short anonymous = NIL; /* is anonymous */ static short blackBox = NIL; /* is a black box */ static short closedBox = NIL; /* is a closed box (uses chroot() jail) */ @@ -347,6 +349,20 @@ case GET_SSLCAPATH: ret = (void *) sslCApath; break; + case SET_SSLCIPHERLIST: /* this can be set null */ + if (sslCipherList) fs_give ((void **) &sslCipherList); + sslCipherList = value ? cpystr ((char *) value) : value; + break; + case GET_SSLCIPHERLIST: + ret = (void *) sslCipherList; + break; + case SET_SSLPROTOCOLS: /* this can be set null */ + if (sslProtocols) fs_give ((void **) &sslProtocols); + sslProtocols = value ? cpystr ((char *) value) : value; + break; + case GET_SSLPROTOCOLS: + ret = (void *) sslProtocols; + break; case SET_LISTMAXLEVEL: list_max_level = (long) value; case GET_LISTMAXLEVEL: @@ -862,6 +878,8 @@ } myHomeDir = cpystr (home); /* set home directory */ } + sslProtocols = cpystr("ALL -SSLv2"); /* default protocols */ + sslCipherList = cpystr ("ALL:!LOW"); /* default cipher list */ if (allowuserconfig) { /* allow user config files */ dorc (strcat (strcpy (tmp,myHomeDir),"/.mminit"),T); @@ -1757,6 +1775,10 @@ */ else if (!compare_cstring (s,"set CA-certificate-path")) sslCApath = cpystr (k); + else if (!compare_cstring (s,"set ssl-cipher-list")) + sslCipherList = cpystr (k); + else if (!compare_cstring (s,"set ssl-protocols")) + sslProtocols = cpystr (k); else if (!compare_cstring (s,"set disable-plaintext")) disablePlaintext = atoi (k); else if (!compare_cstring (s,"set allowed-login-attempts")) Index: mail/cclient/src/osdep/unix/ssl_unix.c =================================================================== --- mail/cclient/src/osdep/unix/ssl_unix.c +++ mail/cclient/src/osdep/unix/ssl_unix.c @@ -38,7 +38,6 @@ #undef crypt #define SSLBUFLEN 8192 -#define SSLCIPHERLIST "ALL:!LOW" /* SSL I/O stream */ @@ -677,6 +676,123 @@ return NIL; } +/** + * Define the SSL Protocol options for next function + */ +#define SSL_PROTOCOL_NONE (0) +#ifdef SSL_OP_NO_SSLv2 +# define SSL_PROTOCOL_SSLV2 (1<<0) +#else +# define SSL_PROTOCOL_SSLV2 0 +#endif +#ifdef SSL_OP_NO_SSLv3 +# define SSL_PROTOCOL_SSLV3 (1<<1) +#else +# define SSL_PROTOCOL_SSLV3 0 +#endif +#ifdef SSL_OP_NO_TLSv1 +# define SSL_PROTOCOL_TLSV1 (1<<2) +#else +# define SSL_PROTOCOL_TLSV1 0 +#endif +#ifdef SSL_OP_NO_TLSv1_1 +# define SSL_PROTOCOL_TLSV1_1 (1<<3) +#else +# define SSL_PROTOCOL_TLSV1_1 0 +#endif +#ifdef SSL_OP_NO_TLSv1_2 +# define SSL_PROTOCOL_TLSV1_2 (1<<4) +#else +# define SSL_PROTOCOL_TLSV1_2 0 +#endif + +#define SSL_PROTOCOL_ALL (SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1|SSL_PROTOCOL_TLSV1_1|SSL_PROTOCOL_TLSV1_2) +typedef int ssl_proto_t; + +/* Supporting function for parse of set ssl-protocol option */ +static const char * +ssl_cmd_protocol_parse(const char *arg, ssl_proto_t *options) +{ + ssl_proto_t thisopt; + char *larg = NULL, *w; + static char errstr[2048]; /* Not thread safe ! */ + + *options = SSL_PROTOCOL_NONE; + if (arg == NULL) + return NULL; + + larg = alloca(strlen(arg)+1); + if (larg == NULL) + return ("Not enought memory while parsing SSL protocol option"); + + strcpy(larg, arg); + + while ((w = strsep(&larg, " \t")) != NULL) { + char action = '\0'; + + if (*w == '\0') + continue; + + if ((*w == '+') || (*w == '-')) + action = *(w++); + + if (strcasecmp(w, "SSLv2") == 0) { +#ifdef OPENSSL_NO_SSL2 + if (action != '-') { + return "SSLv2 not supported by this version of OpenSSL"; + } +#endif + thisopt = SSL_PROTOCOL_SSLV2; + } else if (strcasecmp(w, "SSLv3") == 0) { +#ifndef SSL_OP_NO_SSLv3 + if (action != '-') { + return "SSLv3 not supported by this version of OpenSSL"; + } +#else + thisopt = SSL_PROTOCOL_SSLV3; +#endif + } else if (strcasecmp(w, "TLSv1") == 0) { +#ifndef SSL_OP_NO_TLSv1 + if (action != '-') { + return "TLSv1 not supported by this version of OpenSSL"; + } +#else + thisopt = SSL_PROTOCOL_TLSV1; +#endif + } else if (strcasecmp(w, "TLSv1.1") == 0) { +#ifndef SSL_OP_NO_TLSv1_1 + if (action != '-') { + return "TLSv1.1 not supported by this version of OpenSSL"; + } +#else + thisopt = SSL_PROTOCOL_TLSV1_1; +#endif + } else if (strcasecmp(w, "TLSv1.2") == 0) { +#ifndef SSL_OP_NO_TLSv1_2 + if (action != '-') { + return "TLSv1.2 not supported by this version of OpenSSL"; + } +#else + thisopt = SSL_PROTOCOL_TLSV1_2; +#endif + } else if (strcasecmp(w, "all") == 0) { + thisopt = SSL_PROTOCOL_ALL; + } else { + snprintf(errstr, sizeof(errstr), "Illegal SSL protocol configured: '%s'", w); + return errstr; + } + + if (action == '-') + *options &= ~thisopt; + else if (action == '+') + *options |= thisopt; + else + *options = thisopt; + } + + return NULL; +} + /* Init server for SSL * Accepts: server name */ @@ -702,17 +818,52 @@ if (stat (key,&sbuf)) strcpy (key,cert); } /* create context */ - if (!(stream->context = SSL_CTX_new (start_tls ? - TLSv1_server_method () : - SSLv23_server_method ()))) + if (!(stream->context = SSL_CTX_new (SSLv23_server_method ()))) syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s", tcp_clienthost ()); else { /* set context options */ + char *s; + const char *errstr; + ssl_proto_t protocol; + SSL_CTX_set_options (stream->context,SSL_OP_ALL); + /* set protocols */ + if ((s = (char *) mail_parameters (NIL,GET_SSLPROTOCOLS,NIL)) != NULL) { + errstr = ssl_cmd_protocol_parse(s, &protocol); +#ifdef SSL_OP_NO_SSLv2 + if (errstr != NULL || !(protocol & SSL_PROTOCOL_SSLV2)) { + SSL_CTX_set_options(stream->context, SSL_OP_NO_SSLv2); + } +#endif +#ifdef SSL_OP_NO_SSLv3 + if (errstr != NULL || !(protocol & SSL_PROTOCOL_SSLV3)) { + SSL_CTX_set_options(stream->context, SSL_OP_NO_SSLv3); + } +#endif +#ifdef SSL_OP_NO_TLSv1 + if (errstr != NULL || !(protocol & SSL_PROTOCOL_TLSV1)) { + SSL_CTX_set_options(stream->context, SSL_OP_NO_TLSv1); + } +#endif +#ifdef SSL_OP_NO_TLSv1_1 + if (errstr != NULL || !(protocol & SSL_PROTOCOL_TLSV1_1)) { + SSL_CTX_set_options(stream->context, SSL_OP_NO_TLSv1_1); + } +#endif +#ifdef SSL_OP_NO_TLSv1_2 + if (errstr != NULL || !(protocol & SSL_PROTOCOL_TLSV1_2)) { + SSL_CTX_set_options(stream->context, SSL_OP_NO_TLSv1_2); + } +#endif + } + if (s != NULL && errstr != NULL) + syslog (LOG_ALERT,"Unable to set protocol (host=%.80s): %s", + tcp_clienthost (), errstr); /* set cipher list */ - if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST)) + else if ((s = (char *) mail_parameters (NIL,GET_SSLCIPHERLIST,NIL)) != NULL && !SSL_CTX_set_cipher_list (stream->context,s)) syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s", - SSLCIPHERLIST,tcp_clienthost ()); + s,tcp_clienthost ()); + /* want to send client certificate? */ /* load certificate */ else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert)) syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s",