2 title: 'Debian: disabling SSLv3 in courier server'
9 Debian wheezy (currently oldstable) ships courier-0.68 which was probably released on 2012.
10 "Probably", because, head over to the [Courier website](http://www.courier-mta.org/) and
11 try to find the NEWS/ChangeLog.
15 Anyway. Courier-0.68 has built-in openssl support on Debian and it initializes a SSL
16 context the following way:
19 ctx=SSL_CTX_new(protocol && strcmp(protocol, "SSL3") == 0
21 protocol && strcmp(protocol, "SSL23") == 0
26 SSL_CTX_set_options(ctx, SSL_OP_ALL);
29 ssl_cipher_list="SSLv3:TLSv1:HIGH:!LOW:!MEDIUM:!EXP:!NULL:!aNULL@STRENGTH";
31 SSL_CTX_set_cipher_list(ctx, ssl_cipher_list);
34 #### Some clarifications:
36 * `SSLv3_method` would **only** allow SSLv3
37 * `TLSv1_method` would **only** allow TLSv1.0
38 * `SSLv23_method` is "the general-purpose version-flexible SSL/TLS method"
40 So, if we do not want to limit ourselves to TLSv1.0, i.e. allow TLSv1.0, TLSv1.1 and TLSv1.2,
41 we have to limit our protocol version support through other means.
43 Openssl-1.0.1 (remember, we're on Debian wheezy) does neither come with `TLS_method()` the
44 generic TLS-only method, nor does it come with `SSL_CTX_set_min_proto_version` and we cannot
45 disable SSLv3 with the cipher list if we also want to allow TLSv1.0.
47 ## Solution 1: Upgrading to Jessie
49 What if we upgrade to Debian jessie (current stable)? Jessie ships courier-0.73, so let's see
50 how SSL context intitialization looks there:
55 method=((!protocol || !*protocol)
57 strcmp(protocol, "SSL3") == 0
59 strcmp(protocol, "SSL23") == 0
61 strcmp(protocol, "TLSv1") == 0
63 #ifdef HAVE_TLSV1_1_METHOD
64 strcmp(protocol, "TLSv1.1") == 0
67 #ifdef HAVE_TLSV1_2_METHOD
68 strcmp(protocol, "TLSv1.2") == 0
75 method=SSLv23_method();
76 options|=SSL_OP_NO_SSLv2;
79 ctx=SSL_CTX_new(method);
82 SSL_CTX_set_options(ctx, options);
85 ssl_cipher_list="SSLv3:TLSv1:HIGH:!LOW:!MEDIUM:!EXP:!NULL:!aNULL@STRENGTH";
87 SSL_CTX_set_cipher_list(ctx, ssl_cipher_list);
90 Jessie also comes with openssl-1.0.1, so the situation would not improve for our undertaking
91 by upgrading to jessie.
93 ## Solution 2: Augmenting SSL_CTX_new
95 What I came up with, is augmenting `SSL_CTX_new` and setting the desired `SSL_OP_NO_SSLv3` on
96 each newly created SSL context.
101 #include <openssl/ssl.h>
104 static void *new_sym;
105 static void *opt_sym;
111 lib = dlopen("libssl.so", RTLD_LAZY|RTLD_LOCAL);
113 fprintf(stderr, "dlopen: %s\n", dlerror());
120 *(void **) &new_sym = dlsym(lib, "SSL_CTX_new");
121 if ((error = dlerror())) {
122 fprintf(stderr, "dlsym: %s\n", error);
129 *(void **) &opt_sym = dlsym(lib, "SSL_CTX_ctrl");
130 if ((error = dlerror())) {
131 fprintf(stderr, "dlsym: %s\n", error);
138 SSL_CTX *SSL_CTX_new(const SSL_METHOD *m)
144 ctx = ((SSL_CTX *(*)(const SSL_METHOD*))new_sym)(m);
147 ((long (*)(SSL_CTX *, int, long, void*))opt_sym)(ctx, SSL_CTRL_OPTIONS, SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3, NULL);
153 This is the source of a tiny shared library pre-defining our `SSL_CTX_new`; to build like e.g.
155 `gcc -ldl -fPIC -shared -o preload.so preload.c`
157 It does the following:
159 * augments `SSL_CTX_new` with our own version,
160 i.e. whenever courier calls `SSL_CTX_new` our own version gets called
161 * when it's called the first time, it
163 * fetches the addresses of the original `SSL_CTX_new` and `SSL_CTX_ctrl`
164 (which is the actual function `SSL_CTX_set_options` calls)
165 * calls the original `SSL_CTX_new` to actually create the SSL context
166 * calls `SSL_CTX_ctrl` on the new context with the options we want to set (`SSL_OP_NO_SSLv3`)
167 * returns the context to the caller
171 Courier config files are basically shell scripts which set a environment variables, so we'll
172 enable it as follows:
176 cat >>esmtpd >>esmtpd-msa >>esmtpd-ssl \
177 >>pop3d >>pop3d-ssl \
178 >>imapd >>imapd-ssl \
180 LD_PRELOAD=/path/to/preload.so
184 Then restart each courier service.
188 Last, we have to verify that our solution actually works:
192 -CApath /etc/ssl/certs/ \
194 -connect localhost:143 \
197 139690858608296:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:s3_pkt.c:1261:SSL alert number 40
198 139690858608296:error:1409E0E5:SSL routines:SSL3_WRITE_BYTES:ssl handshake failure:s3_pkt.c:599:
201 A bunch of errors. "Good"! :)
203 Let's see if we can still connect with TLSv1+:
207 -CApath /etc/ssl/certs/ \
209 -connect localhost:143 \
218 . OK CAPABILITY completed
219 * BYE Courier-IMAP server shutting down
220 LOGOUT OK LOGOUT completed
223 Awesome. Mission accomplished.
227 Here's my cipher list for the interested:
230 openssl ciphers -v 'HIGH+aRSA:+kEDH:+kRSA:+SHA:+3DES:!kSRP' \
231 | awk '{ printf "%-28s %-8s %-8s %-18s %-16s\n",$1,$2,$3,$5,$6 }'
234 Which results in the following ciphers:
237 ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Enc=AESGCM(256) Mac=AEAD
238 ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Enc=AES(256) Mac=SHA384
239 ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Enc=AESGCM(128) Mac=AEAD
240 ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Enc=AES(128) Mac=SHA256
241 DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH Enc=AESGCM(256) Mac=AEAD
242 DHE-RSA-AES256-SHA256 TLSv1.2 Kx=DH Enc=AES(256) Mac=SHA256
243 DHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=DH Enc=AESGCM(128) Mac=AEAD
244 DHE-RSA-AES128-SHA256 TLSv1.2 Kx=DH Enc=AES(128) Mac=SHA256
245 AES256-GCM-SHA384 TLSv1.2 Kx=RSA Enc=AESGCM(256) Mac=AEAD
246 AES256-SHA256 TLSv1.2 Kx=RSA Enc=AES(256) Mac=SHA256
247 AES128-GCM-SHA256 TLSv1.2 Kx=RSA Enc=AESGCM(128) Mac=AEAD
248 AES128-SHA256 TLSv1.2 Kx=RSA Enc=AES(128) Mac=SHA256
249 ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Enc=AES(256) Mac=SHA1
250 ECDHE-RSA-AES128-SHA SSLv3 Kx=ECDH Enc=AES(128) Mac=SHA1
251 DHE-RSA-AES256-SHA SSLv3 Kx=DH Enc=AES(256) Mac=SHA1
252 DHE-RSA-CAMELLIA256-SHA SSLv3 Kx=DH Enc=Camellia(256) Mac=SHA1
253 DHE-RSA-AES128-SHA SSLv3 Kx=DH Enc=AES(128) Mac=SHA1
254 DHE-RSA-CAMELLIA128-SHA SSLv3 Kx=DH Enc=Camellia(128) Mac=SHA1
255 AES256-SHA SSLv3 Kx=RSA Enc=AES(256) Mac=SHA1
256 CAMELLIA256-SHA SSLv3 Kx=RSA Enc=Camellia(256) Mac=SHA1
257 AES128-SHA SSLv3 Kx=RSA Enc=AES(128) Mac=SHA1
258 CAMELLIA128-SHA SSLv3 Kx=RSA Enc=Camellia(128) Mac=SHA1
259 ECDHE-RSA-DES-CBC3-SHA SSLv3 Kx=ECDH Enc=3DES(168) Mac=SHA1
260 EDH-RSA-DES-CBC3-SHA SSLv3 Kx=DH Enc=3DES(168) Mac=SHA1
261 DES-CBC3-SHA SSLv3 Kx=RSA Enc=3DES(168) Mac=SHA1
264 Note, that courier actually does **not** support `ECDH` key exchange, but I didn't exclude it for
265 the sake of simplicity for using the same cipher list for every server (e.g. web etc.)