Code

SSL utils: Fixed a memory leak in a couple of error conditions.
[sysdb.git] / src / utils / ssl.c
1 /*
2  * SysDB - src/utils/ssl.c
3  * Copyright (C) 2015 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif
32 #include "utils/ssl.h"
33 #include "utils/error.h"
35 #include <errno.h>
37 #include <stdlib.h>
38 #include <string.h>
40 #include <pthread.h>
42 #include <openssl/ssl.h>
43 #include <openssl/bio.h>
44 #include <openssl/crypto.h>
45 #include <openssl/x509.h>
46 #include <openssl/err.h>
48 /*
49  * data types
50  */
52 struct sdb_ssl_client {
53         SSL_CTX *ctx;
54         sdb_ssl_options_t opts;
55 };
57 struct sdb_ssl_server {
58         SSL_CTX *ctx;
59         sdb_ssl_options_t opts;
60 };
62 struct sdb_ssl_session {
63         SSL *ssl;
64 };
66 /*
67  * OpenSSL helper functions
68  */
70 static pthread_mutex_t *mutexes = NULL;
71 static int mutexes_num = 0;
73 static void
74 locking_callback(int mode, int n, const char *file, int line)
75 {
76         if (! mutexes) {
77                 sdb_log(SDB_LOG_EMERG, "ssl: CRYPTO_lock called from %s:%d "
78                                 "before initializing SSL", file, line);
79                 return;
80         }
81         if (n >= mutexes_num) {
82                 sdb_log(SDB_LOG_EMERG, "ssl: CRYPTO_lock called from %s:%d "
83                                 "for mutex %d but only %d are available",
84                                 file, line, n, mutexes_num);
85                 return;
86         }
88         if (mode & CRYPTO_LOCK)
89                 pthread_mutex_lock(&mutexes[n]);
90         else
91                 pthread_mutex_unlock(&mutexes[n]);
92 } /* locking_callback */
94 static void
95 threadid_callback(CRYPTO_THREADID *id)
96 {
97         CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
98 } /* threadid_callback */
100 /*
101  * private helper functions
102  */
104 /* log all pending SSL errors */
105 static void
106 ssl_log(int prio, const char *prefix, ...)
108         char msg[1024];
109         va_list ap;
111         va_start(ap, prefix);
112         vsnprintf(msg, sizeof(msg), prefix, ap);
113         msg[sizeof(msg) - 1] = '\0';
114         va_end(ap);
116         while (42) {
117                 unsigned long e = ERR_get_error();
118                 if (! e)
119                         break;
120                 sdb_log(prio, "%s: %s", msg, ERR_reason_error_string(e));
121         }
122 } /* ssl_log */
124 static void
125 ssl_log_err(int prio, SSL *ssl, int status, const char *prefix, ...)
127         int err = SSL_get_error(ssl, status);
128         char msg[1024];
129         va_list ap;
131         va_start(ap, prefix);
132         vsnprintf(msg, sizeof(msg), prefix, ap);
133         msg[sizeof(msg) - 1] = '\0';
134         va_end(ap);
136         errno = 0;
137         switch (err) {
138                 case SSL_ERROR_NONE:
139                         sdb_log(prio, "%s: success", msg);
140                         break;
141                 case SSL_ERROR_ZERO_RETURN:
142                         errno = ECONNRESET;
143                         break;
144                 case SSL_ERROR_WANT_READ:
145                 case SSL_ERROR_WANT_WRITE:
146                         errno = EWOULDBLOCK;
147                         break;
148                 case SSL_ERROR_WANT_CONNECT:
149                 case SSL_ERROR_WANT_ACCEPT:
150                         sdb_log(prio, "%s: connection not set up", msg);
151                         break;
152                 case SSL_ERROR_WANT_X509_LOOKUP:
153                         sdb_log(prio, "%s: application error", msg);
154                         break;
155                 case SSL_ERROR_SYSCALL:
156                         if (ERR_peek_error())
157                                 return ssl_log(prio, msg);
158                         if (! status)
159                                 sdb_log(prio, "%s: unexpected end-of-file", msg);
160                         else if (! errno)
161                                 errno = EIO;
162                 case SSL_ERROR_SSL:
163                         return ssl_log(prio, msg);
164                 default:
165                         sdb_log(prio, "%s: unkown SSL error %d", msg, err);
166                         break;
167         }
169         if (errno) {
170                 char errbuf[1024];
171                 sdb_log(prio, "%s: %s", msg,
172                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
173         }
174 } /* ssl_log_err */
176 static int
177 copy_options(sdb_ssl_options_t *dst, const sdb_ssl_options_t *src)
179         sdb_ssl_options_t tmp;
180         sdb_ssl_options_t def = SDB_SSL_DEFAULT_OPTIONS;
182         if (src)
183                 tmp = *src;
184         else
185                 tmp = def;
187         if (! tmp.ca_file)
188                 tmp.ca_file = def.ca_file;
189         if (! tmp.key_file)
190                 tmp.key_file = def.key_file;
191         if (! tmp.cert_file)
192                 tmp.cert_file = def.cert_file;
194         dst->ca_file = strdup(tmp.ca_file);
195         dst->key_file = strdup(tmp.key_file);
196         dst->cert_file = strdup(tmp.cert_file);
197         if ((! dst->ca_file) || (! dst->key_file) || (! dst->cert_file))
198                 return -1;
199         if (tmp.crl_file) {
200                 dst->crl_file = strdup(tmp.crl_file);
201                 if (! dst->crl_file)
202                         return -1;
203         }
204         return 0;
205 } /* copy_options */
207 /*
208  * public API
209  */
211 int
212 sdb_ssl_init(void)
214         int i;
216         SSL_load_error_strings();
217         OpenSSL_add_ssl_algorithms();
219         mutexes = calloc(CRYPTO_num_locks(), sizeof(*mutexes));
220         if (! mutexes) {
221                 char errbuf[1024];
222                 sdb_log(SDB_LOG_ERR, "ssl: Failed to allocate mutexes: %s",
223                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
224                 return -1;
225         }
226         for (i = 0; i < CRYPTO_num_locks(); ++i) {
227                 errno = pthread_mutex_init(&mutexes[i], /* attr = */ NULL);
228                 if (errno) {
229                         char errbuf[1024];
230                         sdb_log(SDB_LOG_ERR, "ssl: Failed to initialize mutex: %s",
231                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
232                         return -1;
233                 }
234                 mutexes_num = i + 1;
235         }
237         CRYPTO_set_locking_callback(locking_callback);
238         CRYPTO_THREADID_set_callback(threadid_callback);
239         return 0;
240 } /* sdb_ssl_init */
242 void
243 sdb_ssl_shutdown(void)
245         int i;
247         ERR_free_strings();
249         for (i = 0; i < mutexes_num; ++i)
250                 pthread_mutex_destroy(&mutexes[i]);
251         if (mutexes)
252                 free(mutexes);
253         mutexes = NULL;
254         mutexes_num = 0;
255 } /* sdb_ssl_shutdown */
257 sdb_ssl_client_t *
258 sdb_ssl_client_create(const sdb_ssl_options_t *opts)
260         sdb_ssl_client_t *client;
262         client = calloc(1, sizeof(*client));
263         if (! client)
264                 return NULL;
266         if (copy_options(&client->opts, opts)) {
267                 sdb_ssl_client_destroy(client);
268                 return NULL;
269         }
271         client->ctx = SSL_CTX_new(SSLv23_client_method());
272         if (! client->ctx) {
273                 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL context");
274                 sdb_ssl_client_destroy(client);
275                 return NULL;
276         }
278         if (! SSL_CTX_load_verify_locations(client->ctx,
279                                 client->opts.ca_file, NULL)) {
280                 ssl_log(SDB_LOG_ERR, "ssl: Failed to load CA file '%s'",
281                                 client->opts.ca_file);
282                 sdb_ssl_client_destroy(client);
283                 return NULL;
284         }
285         if (! SSL_CTX_use_certificate_file(client->ctx,
286                                 client->opts.cert_file, SSL_FILETYPE_PEM)) {
287                 ssl_log(SDB_LOG_ERR, "ssl: Failed to load cert file '%s'",
288                                 client->opts.cert_file);
289                 sdb_ssl_client_destroy(client);
290                 return NULL;
291         }
292         if (! SSL_CTX_use_PrivateKey_file(client->ctx,
293                                 client->opts.key_file, SSL_FILETYPE_PEM)) {
294                 ssl_log(SDB_LOG_ERR, "ssl: Failed to load key file '%s'",
295                                 client->opts.key_file);
296                 sdb_ssl_client_destroy(client);
297                 return NULL;
298         }
299         if (! SSL_CTX_check_private_key(client->ctx)) {
300                 ssl_log(SDB_LOG_ERR, "ssl: Failed to verify key (%s)",
301                                 client->opts.key_file);
302                 sdb_ssl_client_destroy(client);
303                 return NULL;
304         }
306         SSL_CTX_set_mode(client->ctx, SSL_MODE_AUTO_RETRY);
307         SSL_CTX_set_verify(client->ctx, SSL_VERIFY_PEER, NULL);
308         SSL_CTX_set_verify_depth(client->ctx, 1);
309         return client;
310 } /* sdb_ssl_client_create */
312 void
313 sdb_ssl_client_destroy(sdb_ssl_client_t *client)
315         if (! client)
316                 return;
318         if (client->ctx)
319                 SSL_CTX_free(client->ctx);
320         sdb_ssl_free_options(&client->opts);
321         free(client);
322 } /* sdb_ssl_client_destroy */
324 sdb_ssl_session_t *
325 sdb_ssl_client_connect(sdb_ssl_client_t *client, int fd)
327         sdb_ssl_session_t *session;
328         int status;
329         BIO *bio;
331         if ((! client) || (fd < 0))
332                 return NULL;
334         session = calloc(1, sizeof(*session));
335         if (! session)
336                 return NULL;
338         bio = BIO_new_socket(fd, BIO_NOCLOSE);
339         if (! bio) {
340                 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL socket");
341                 sdb_ssl_session_destroy(session);
342                 return NULL;
343         }
344         session->ssl = SSL_new(client->ctx);
345         if (! session->ssl) {
346                 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL object");
347                 sdb_ssl_session_destroy(session);
348                 return NULL;
349         }
350         SSL_set_bio(session->ssl, bio, bio);
352         if ((status = SSL_connect(session->ssl)) <= 0) {
353                 ssl_log_err(SDB_LOG_ERR, session->ssl, status,
354                                 "ssl: Failed to initialize SSL session");
355                 sdb_ssl_session_destroy(session);
356                 return NULL;
357         }
358         if (SSL_get_verify_result(session->ssl) != X509_V_OK) {
359                 sdb_log(SDB_LOG_ERR, "Failed to verify SSL connection");
360                 sdb_ssl_session_destroy(session);
361                 return NULL;
362         }
363         return session;
364 } /* sdb_ssl_client_connect */
366 sdb_ssl_server_t *
367 sdb_ssl_server_create(const sdb_ssl_options_t *opts)
369         sdb_ssl_server_t *server;
371         server = calloc(1, sizeof(*server));
372         if (! server)
373                 return NULL;
375         if (copy_options(&server->opts, opts)) {
376                 sdb_ssl_server_destroy(server);
377                 return NULL;
378         }
380         server->ctx = SSL_CTX_new(SSLv23_server_method());
381         if (! server->ctx) {
382                 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL context");
383                 sdb_ssl_server_destroy(server);
384                 return NULL;
385         }
387         /* Recommendation documented at
388          * https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ */
389         if (! SSL_CTX_set_cipher_list(server->ctx,
390                                 "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:"
391                                 "DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:"
392                                 "!aNULL:!MD5:!DSS")) {
393                 sdb_log(SDB_LOG_ERR, "ssl: Invalid cipher list");
394                 sdb_ssl_server_destroy(server);
395                 return NULL;
396         }
398         if (! SSL_CTX_load_verify_locations(server->ctx,
399                                 server->opts.ca_file, NULL)) {
400                 ssl_log(SDB_LOG_ERR, "Failed to load CA file %s",
401                                 server->opts.ca_file);
402                 sdb_ssl_server_destroy(server);
403                 return NULL;
404         }
405         SSL_CTX_set_client_CA_list(server->ctx,
406                         SSL_load_client_CA_file(server->opts.ca_file));
408         if (! SSL_CTX_use_certificate_file(server->ctx,
409                                 server->opts.cert_file, SSL_FILETYPE_PEM)) {
410                 ssl_log(SDB_LOG_ERR, "Failed to load SSL cert file %s",
411                                 server->opts.cert_file);
412                 sdb_ssl_server_destroy(server);
413                 return NULL;
414         }
415         if (! SSL_CTX_use_PrivateKey_file(server->ctx,
416                                 server->opts.key_file, SSL_FILETYPE_PEM)) {
417                 ssl_log(SDB_LOG_ERR, "Failed to load SSL key file %s",
418                                 server->opts.key_file);
419                 sdb_ssl_server_destroy(server);
420                 return NULL;
421         }
422         if (! SSL_CTX_check_private_key(server->ctx)) {
423                 ssl_log(SDB_LOG_ERR, "Failed to verify SSL private key");
424                 sdb_ssl_server_destroy(server);
425                 return NULL;
426         }
428         SSL_CTX_set_mode(server->ctx, SSL_MODE_AUTO_RETRY);
429         SSL_CTX_set_verify(server->ctx,
430                         SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
431         SSL_CTX_set_verify_depth(server->ctx, 1);
433         /* TODO: handle server->opts.crl_file */
434         return server;
435 } /* sdb_ssl_server_create */
437 void
438 sdb_ssl_server_destroy(sdb_ssl_server_t *server)
440         if (! server)
441                 return;
443         if (server->ctx)
444                 SSL_CTX_free(server->ctx);
445         sdb_ssl_free_options(&server->opts);
446         free(server);
447 } /* sdb_ssl_server_destroy */
449 sdb_ssl_session_t *
450 sdb_ssl_server_accept(sdb_ssl_server_t *server, int fd)
452         sdb_ssl_session_t *session;
453         int status;
454         BIO *bio;
456         if ((! server) || (fd < 0))
457                 return NULL;
459         session = calloc(1, sizeof(*session));
460         if (! session)
461                 return NULL;
463         bio = BIO_new_socket(fd, BIO_NOCLOSE);
464         if (! bio) {
465                 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL socket");
466                 sdb_ssl_session_destroy(session);
467                 return NULL;
468         }
469         session->ssl = SSL_new(server->ctx);
470         if (! session->ssl) {
471                 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL object");
472                 sdb_ssl_session_destroy(session);
473                 return NULL;
474         }
475         SSL_set_bio(session->ssl, bio, bio);
477         while (42) {
478                 if ((status = SSL_accept(session->ssl)) <= 0) {
479                         if (SSL_get_error(session->ssl, status) == SSL_ERROR_WANT_READ)
480                                 continue;
482                         ssl_log_err(SDB_LOG_ERR, session->ssl, status,
483                                         "ssl: Failed to initialize SSL session");
484                         sdb_ssl_session_destroy(session);
485                         return NULL;
486                 }
487                 break;
488         }
489         if (SSL_get_verify_result(session->ssl) != X509_V_OK) {
490                 sdb_log(SDB_LOG_ERR, "Failed to verify SSL connection");
491                 sdb_ssl_session_destroy(session);
492                 return NULL;
493         }
494         return session;
495 } /* sdb_ssl_server_accept */
497 void
498 sdb_ssl_session_destroy(sdb_ssl_session_t *session)
500         if (! session)
501                 return;
503         if (session->ssl) {
504                 SSL_shutdown(session->ssl);
505                 SSL_clear(session->ssl);
506                 SSL_free(session->ssl);
507         }
508         free(session);
509 } /* sdb_ssl_session_destroy */
511 char *
512 sdb_ssl_session_peer(sdb_ssl_session_t *session)
514         X509 *x509;
515         X509_NAME *name;
517         char *peer = NULL;
518         char p[1024];
520         if (! session)
521                 return NULL;
523         x509 = SSL_get_peer_certificate(session->ssl);
524         if (! x509)
525                 return NULL;
526         name = X509_get_subject_name(x509);
527         if (! name) {
528                 X509_free(x509);
529                 return NULL;
530         }
532         if (X509_NAME_get_text_by_NID(name, NID_commonName, p, sizeof(p)) > 0)
533                 peer = strdup(p);
535         X509_free(x509);
536         return peer;
537 } /* sdb_ssl_session_peer */
539 ssize_t
540 sdb_ssl_session_write(sdb_ssl_session_t *session, const void *buf, size_t n)
542         int status;
544         if (! session)
545                 return -1;
547         status = SSL_write(session->ssl, buf, (int)n);
548         if (status) {
549                 if ((status < 0) && (errno != EAGAIN))
550                         ssl_log_err(SDB_LOG_ERR, session->ssl, status, "ssl: Write error");
551                 return (ssize_t)status;
552         }
554         status = SSL_get_error(session->ssl, status);
555         if (status == SSL_ERROR_ZERO_RETURN)
556                 return 0;
558         if ((status == SSL_ERROR_WANT_READ) || (status == SSL_ERROR_WANT_WRITE)) {
559                 errno = EWOULDBLOCK;
560                 return -1;
561         }
562         errno = ECONNRESET;
563         return -1;
564 } /* sdb_ssl_session_write */
566 ssize_t
567 sdb_ssl_session_read(sdb_ssl_session_t *session, void *buf, size_t n)
569         int status;
571         if (! session)
572                 return -1;
574         status = SSL_read(session->ssl, buf, (int)n);
575         if (status) {
576                 if ((status < 0) && (errno != EAGAIN))
577                         ssl_log_err(SDB_LOG_ERR, session->ssl, status, "ssl: Read error");
578                 return (ssize_t)status;
579         }
581         status = SSL_get_error(session->ssl, status);
582         if (status == SSL_ERROR_ZERO_RETURN)
583                 return 0;
585         if ((status == SSL_ERROR_WANT_READ) || (status == SSL_ERROR_WANT_WRITE)) {
586                 errno = EWOULDBLOCK;
587                 return -1;
588         }
589         errno = ECONNRESET;
590         return -1;
591 } /* sdb_ssl_session_read */
593 void
594 sdb_ssl_free_options(sdb_ssl_options_t *opts)
596         if (! opts)
597                 return;
599         if (opts->ca_file)
600                 free(opts->ca_file);
601         if (opts->key_file)
602                 free(opts->key_file);
603         if (opts->cert_file)
604                 free(opts->cert_file);
605         if (opts->crl_file)
606                 free(opts->crl_file);
608         opts->ca_file = opts->key_file = opts->cert_file = opts->crl_file = NULL;
609 } /* sdb_ssl_free_options */
611 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */