2
* This file is part of libESMTP, a library for submission of RFC 2822
3
* formatted electronic mail messages using the SMTP protocol described
6
* Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
8
* This library is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Lesser General Public
10
* License as published by the Free Software Foundation; either
11
* version 2.1 of the License, or (at your option) any later version.
13
* This library is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* Lesser General Public License for more details.
18
* You should have received a copy of the GNU Lesser General Public
19
* License along with this library; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
/* Support for the SMTP STARTTLS verb.
33
/* This stuff doesn't belong here */
35
#include <sys/types.h>
39
#include <missing.h> /* declarations for missing library functions */
42
#include "libesmtp-private.h"
48
static SSL_CTX *starttls_ctx;
49
static smtp_starttls_passwordcb_t ctx_password_cb;
50
static void *ctx_password_cb_arg;
55
static pthread_mutex_t starttls_mutex = PTHREAD_MUTEX_INITIALIZER;
56
static pthread_mutex_t *openssl_mutex;
59
openssl_mutexcb (int mode, int n,
60
const char *file __attribute__ ((unused)),
61
int line __attribute__ ((unused)))
63
if (mode & CRYPTO_LOCK)
64
pthread_mutex_lock (&openssl_mutex[n]);
66
pthread_mutex_unlock (&openssl_mutex[n]);
77
/* Set up mutexes for the OpenSSL library */
78
if (openssl_mutex == NULL)
80
pthread_mutexattr_t attr;
83
openssl_mutex = malloc (sizeof (pthread_mutex_t) * CRYPTO_num_locks ());
84
if (openssl_mutex == NULL)
86
pthread_mutexattr_init (&attr);
87
for (n = 0; n < CRYPTO_num_locks (); n++)
88
pthread_mutex_init (&openssl_mutex[n], &attr);
89
pthread_mutexattr_destroy (&attr);
90
CRYPTO_set_locking_callback (openssl_mutexcb);
94
SSL_load_error_strings ();
99
/* This stuff is crude and doesn't belong here */
105
return getenv ("HOME");
109
user_pathname (char buf[], size_t buflen, const char *tail)
114
snprintf (buf, buflen, "%s/.authenticate/%s", home, tail);
118
/* Check file exists, is a regular file and contains something */
120
check_file (const char *file)
124
if (stat (file, &st) < 0)
126
/* File must be regular and contain something */
127
if (!S_ISREG (st.st_mode) && st.st_size > 0)
129
/* For now this check is paranoid. The way I figure it, the passwords
130
on the private keys will be intensely annoying, so people will
131
remove them. Therefore make them protect the files using very
132
restrictive file permissions. Only the owner should be able to
133
read or write the certificates and the user the app is running as
134
should own the files. */
135
if ((st.st_mode & (S_IXUSR | S_IRWXG | S_IRWXO)) || st.st_uid != getuid ())
140
/* Check directory exists */
142
check_directory (const char *file)
146
if (stat (file, &st) < 0)
148
/* File must be a directory */
149
if (!S_ISDIR (st.st_mode))
151
/* Paranoia - only permit owner rwx permissions */
152
if ((st.st_mode & (S_IRWXG | S_IRWXO)) || st.st_uid != getuid ())
159
/* Unusually this API does not require a smtp_session_t. The data
162
N.B. If this API is not called and OpenSSL requires a password, it
163
will supply a default callback which prompts on the user's tty.
164
This is likely to be undesired behaviour, so the app should
165
supply a callback using this function.
168
smtp_starttls_set_password_cb (smtp_starttls_passwordcb_t cb, void *arg)
170
SMTPAPI_CHECK_ARGS (cb != NULL, 0);
173
pthread_mutex_lock (&starttls_mutex);
175
ctx_password_cb = cb;
176
ctx_password_cb_arg = arg;
178
pthread_mutex_unlock (&starttls_mutex);
184
starttls_create_ctx (void)
189
char *keyfile, *cafile, *capath;
191
/* The decision not to support SSL v2 and v3 but instead to use only
192
TLSv1 is deliberate. RFC 2487 is not clear on what versions of
193
SSL and TLS are to be supported but the implication is that
194
TLS and not SSL is to be used. Servers typically support SSL as
195
well as TLS because some versions of Netscape do not support TLS.
196
I am assuming that all deployed servers support the full set of
197
protocols including TLSv1. */
198
ctx = SSL_CTX_new (TLSv1_client_method ());
200
/* Load our keys and certificates. To avoid messing with configuration
201
variables etc, use fixed paths for the certificate store. These are
204
~/.authenticate/private/smtp-starttls.pem
205
the user's certificate and private key
206
~/.authenticate/ca.pem
207
the user's trusted CA list
209
the user's hashed CA directory
211
Host specific stuff follows the same structure except that its
212
below ~/.authenticate/host.name
214
It probably makes sense to check that the directories and/or files
215
are readable only by the user who owns them.
217
This structure will certainly change. I'm on a voyage of discovery
218
here! Eventually, this code and the SASL stuff will become a
219
separate library used by libESMTP (libAUTH?). The general idea is
220
that ~/.authenticate will be used to store authentication
221
information for the user (eventually there might be a
222
({/usr{/local}}/etc/authenticate for system wide stuff - CA lists
223
and CRLs for example). The "private" subdirectory is just to
224
separate out private info from others. There might be a "public"
225
directory too. Since the CA list is global (I think) I've put them
226
below .authenticate for now. Within the "private" and "public"
227
directories, certificates and other authentication data are named
228
according to their purpose (hence smtp-starttls.pem). Symlinks can
229
be used to avoid duplication where authentication tokens are shared
230
for several purposes. My reasoning here is that libESMTP (or any
231
client layered over the hypothetical libAUTH) will always want the
232
same authentication behaviour for a given service, regardless of
233
the application using it.
235
XXX - The above isn't quite enough. Per-host directories are
236
required, e.g. a different smtp-starttls.pem might be needed for
237
different servers. This will not affect the trusted CAs though.
239
XXX - (this comment belongs in auth-client.c) Ideally, the
240
~/.authenticate hierarchy will be able to store SASL passwords
241
if required, preferably encrypted. Then the application would
242
not necessarily have to supply usernames and passwords via the
243
libESMTP API to be able to authenticate to a server.
246
/* Client certificate policy: if a client certificate is found at
247
~/.authenticate/private/smtp-starttls.pem, it is presented to the
248
server if it requests it. The server may use the certificate to
249
perform authentication at its own discretion. */
250
if (ctx_password_cb != NULL)
252
SSL_CTX_set_default_passwd_cb (ctx, ctx_password_cb);
253
SSL_CTX_set_default_passwd_cb_userdata (ctx, ctx_password_cb_arg);
255
keyfile = user_pathname (buf, sizeof buf, "private/smtp-starttls.pem");
256
if (check_file (keyfile))
258
if (!SSL_CTX_use_certificate_file (ctx, keyfile, SSL_FILETYPE_PEM))
260
/* FIXME: set an error code */
263
if (!SSL_CTX_use_PrivateKey_file (ctx, keyfile, SSL_FILETYPE_PEM))
265
/* FIXME: set an error code */
270
/* Server certificate policy: check the server certificate against the
271
trusted CA list to a depth of 1. */
272
cafile = user_pathname (buf, sizeof buf, "ca.pem");
273
if (!check_file (cafile))
275
capath = user_pathname (buf2, sizeof buf2, "ca");
276
if (!check_directory (capath))
278
/* Load the CAs we trust */
279
if (cafile != NULL || capath != NULL)
280
SSL_CTX_load_verify_locations (ctx, cafile, capath);
282
SSL_CTX_set_default_verify_paths (ctx);
284
/* FIXME: load a source of randomness */
290
starttls_create_ssl (smtp_session_t session)
297
ssl = SSL_new (session->starttls_ctx);
299
/* Client certificate policy: if a host specific client certificate
300
is found at ~/.authenticate/host.name/private/smtp-starttls.pem,
301
it is presented to the server if it requests it. */
303
/* FIXME: when the default client certificate is loaded a passowrd may be
304
required. Then the code below might ask for one too. It
305
will be annoying when two passwords are needed and only one
306
is necessary. Also, the default certificate will only need
307
the password when the SSL_CTX is created. A host specific
308
certificate's password will be needed for every SMTP session
309
within an application. This needs a solution. */
311
/* FIXME: in protocol.c, record the canonic name of the host returned
312
by getaddrinfo. Use that instead of session->host. */
313
snprintf (buf2, sizeof buf2, "%s/private/smtp-starttls.pem", session->host);
314
keyfile = user_pathname (buf, sizeof buf, buf2);
315
if (check_file (keyfile))
317
if (!SSL_use_certificate_file (ssl, keyfile, SSL_FILETYPE_PEM))
319
/* FIXME: set an error code */
322
if (!SSL_use_PrivateKey_file (ssl, keyfile, SSL_FILETYPE_PEM))
324
/* FIXME: set an error code */
332
/* App calls this to allow libESMTP to use an SSL_CTX it has already
333
initialised. NULL means use a default created by libESMTP.
334
If called at all, libESMTP assumes the application has initialised
335
openssl. Otherwise, libESMTP will initialise OpenSSL before calling
336
any of the SSL APIs. */
338
smtp_starttls_set_ctx (smtp_session_t session, SSL_CTX *ctx)
340
SMTPAPI_CHECK_ARGS (session != NULL, 0);
342
tls_init = 1; /* Assume app has set up openssl */
343
session->starttls_ctx = ctx;
347
/* how == 0: disabled, 1: if possible, 2: required */
349
smtp_starttls_enable (smtp_session_t session, enum starttls_option how)
351
SMTPAPI_CHECK_ARGS (session != NULL, 0);
353
session->starttls_enabled = how;
354
if (how == Starttls_REQUIRED)
355
session->required_extensions |= EXT_STARTTLS;
357
session->required_extensions &= ~EXT_STARTTLS;
362
select_starttls (smtp_session_t session)
364
if (session->using_tls || session->authenticated)
366
/* FIXME: if the server has reported the TLS extension in a previous
367
session promote Starttls_ENABLED to Starttls_REQUIRED.
368
If this session does not offer STARTTLS, this will force
369
protocol.c to report the extension as not available and QUIT
370
as reccommended in RFC 2487. */
372
session->starttls_enabled = Starttls_REQUIRED; */
373
if (!(session->extensions & EXT_STARTTLS))
375
if (!session->starttls_enabled)
378
pthread_mutex_lock (&starttls_mutex);
380
if (starttls_ctx == NULL && starttls_init ())
381
starttls_ctx = starttls_create_ctx ();
383
pthread_mutex_unlock (&starttls_mutex);
385
session->starttls_ctx = starttls_ctx;
386
return session->starttls_ctx != NULL;
390
check_acceptable_security (smtp_session_t session, SSL *ssl)
398
/* Check certificate validity.
400
vfy_result = SSL_get_verify_result (ssl);
401
if (vfy_result != X509_V_OK)
404
if (session->event_cb != NULL)
405
(*session->event_cb) (session, SMTP_EV_INVALID_PEER_CERTIFICATE,
406
session->event_cb_arg, vfy_result, &ok);
411
/* Check cipher strength. Since we use only TLSv1 the cipher should
412
never be this weak. */
413
bits = SSL_get_cipher_bits (ssl, NULL);
417
if (session->event_cb != NULL)
418
(*session->event_cb) (session, SMTP_EV_WEAK_CIPHER,
419
session->event_cb_arg, bits, &ok);
424
/* Check server credentials stored in the certificate.
426
cert = SSL_get_peer_certificate (ssl);
429
/* FIXME: should retrieve each subjectAltName of type dNSName
430
and compare them to the remote host name. If any one
431
of them matches, accept the certificate. */
432
X509_NAME_get_text_by_NID (X509_get_subject_name (cert),
433
NID_commonName, buf, sizeof buf);
436
/* FIXME: in protocol.c, record the canonic name of the host returned
437
by getaddrinfo. Use that instead of session->host. */
439
/* FIXME: the host name in the certificate may have wild cards
440
present. Use something better than strcasecmp! */
441
if (strcasecmp (session->host, buf) != 0)
444
if (session->event_cb != NULL)
445
(*session->event_cb) (session, SMTP_EV_WRONG_PEER_CERTIFICATE,
446
session->event_cb_arg, &ok);
454
if (session->event_cb != NULL)
455
(*session->event_cb) (session, SMTP_EV_NO_PEER_CERTIFICATE,
456
session->event_cb_arg, &ok);
461
/* Since the criteria for rejecting a certificate may be at the
462
user's discretion, a config file might be needed to configure
463
the client TLS (and SASL) options.
465
Yuck! libESMTP has got on without bloody config files until now :(
472
cmd_starttls (siobuf_t conn, smtp_session_t session)
474
sio_write (conn, "STARTTLS\r\n", -1);
475
session->cmd_state = -1;
479
rsp_starttls (siobuf_t conn, smtp_session_t session)
486
code = read_smtp_response (conn, session, &session->mta_status, NULL);
488
&& sio_set_tlsclient_ssl (conn, (ssl = starttls_create_ssl (session))))
490
session->using_tls = 1;
492
/* Forget what we know about the server and reset protocol state.
494
session->extensions = 0;
495
destroy_auth_mechanisms (session);
497
if (!check_acceptable_security (session, ssl))
498
session->rsp_state = S_quit;
501
if (session->event_cb != NULL)
502
(*session->event_cb) (session, SMTP_EV_STARTTLS_OK,
503
session->event_cb_arg,
504
ssl, SSL_get_cipher (ssl),
505
SSL_get_cipher_bits (ssl, NULL));
506
cert = SSL_get_certificate (ssl);
509
/* Copy the common name [typically email address] from the
510
client certificate and use it to prime the SASL EXTERNAL
512
X509_NAME_get_text_by_NID (X509_get_subject_name (cert),
513
NID_commonName, buf, sizeof buf);
515
if (session->auth_context != NULL)
516
auth_set_external_id (session->auth_context, buf);
519
/* Next state is EHLO */
520
session->rsp_state = S_ehlo;
524
session->rsp_state = S_quit;
530
#include "libesmtp-private.h"
532
/* Fudge the declaration. The idea is that all builds of the library
533
export the same API, but that the unsupported features always fail.
534
This prototype is declared only if <openssl/ssl.h> is included.
535
Strict ANSI compiles require prototypes, so here it is! */
536
int smtp_starttls_set_ctx (smtp_session_t session, SSL_CTX *ctx);
539
smtp_starttls_set_ctx (smtp_session_t session,
540
SSL_CTX *ctx __attribute__ ((unused)))
542
SMTPAPI_CHECK_ARGS (session != (smtp_session_t) 0, 0);
548
smtp_starttls_enable (smtp_session_t session,
549
enum starttls_option how __attribute__ ((unused)))
551
SMTPAPI_CHECK_ARGS (session != (smtp_session_t) 0, 0);
557
smtp_starttls_set_password_cb (smtp_starttls_passwordcb_t cb
558
__attribute__ ((unused)),
559
void *arg __attribute__ ((unused)))