fdb170d2b2da42b588dbaec40b33bd2afde5ab8a
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, ...)
107 {
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, ...)
126 {
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)
178 {
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)
213 {
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)
244 {
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)
259 {
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)
314 {
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)
326 {
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)
368 {
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)
439 {
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)
451 {
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)
499 {
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)
513 {
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)
541 {
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)
568 {
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)
595 {
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 : */