fe509d05c3dd461e5e0d79620070abdeb5d97444
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 <openssl/ssl.h>
41 #include <openssl/bio.h>
42 #include <openssl/x509.h>
43 #include <openssl/err.h>
45 /*
46 * data types
47 */
49 struct sdb_ssl_client {
50 SSL_CTX *ctx;
51 sdb_ssl_options_t opts;
52 };
54 struct sdb_ssl_server {
55 SSL_CTX *ctx;
56 sdb_ssl_options_t opts;
57 };
59 struct sdb_ssl_session {
60 SSL *ssl;
61 };
63 /*
64 * private helper functions
65 */
67 /* log all pending SSL errors */
68 static void
69 ssl_log(int prio, const char *prefix, ...)
70 {
71 char msg[1024];
72 va_list ap;
74 va_start(ap, prefix);
75 vsnprintf(msg, sizeof(msg), prefix, ap);
76 msg[sizeof(msg) - 1] = '\0';
77 va_end(ap);
79 while (42) {
80 unsigned long e = ERR_get_error();
81 if (! e)
82 break;
83 sdb_log(prio, "%s: %s", msg, ERR_reason_error_string(e));
84 }
85 } /* ssl_log */
87 static void
88 ssl_log_err(int prio, SSL *ssl, int status, const char *prefix, ...)
89 {
90 int err = SSL_get_error(ssl, status);
91 char msg[1024];
92 va_list ap;
94 va_start(ap, prefix);
95 vsnprintf(msg, sizeof(msg), prefix, ap);
96 msg[sizeof(msg) - 1] = '\0';
97 va_end(ap);
99 errno = 0;
100 switch (err) {
101 case SSL_ERROR_NONE:
102 sdb_log(prio, "%s: success", msg);
103 break;
104 case SSL_ERROR_ZERO_RETURN:
105 errno = ECONNRESET;
106 break;
107 case SSL_ERROR_WANT_READ:
108 case SSL_ERROR_WANT_WRITE:
109 errno = EWOULDBLOCK;
110 break;
111 case SSL_ERROR_WANT_CONNECT:
112 case SSL_ERROR_WANT_ACCEPT:
113 sdb_log(prio, "%s: connection not set up", msg);
114 break;
115 case SSL_ERROR_WANT_X509_LOOKUP:
116 sdb_log(prio, "%s: application error", msg);
117 break;
118 case SSL_ERROR_SYSCALL:
119 if (ERR_peek_error())
120 return ssl_log(prio, msg);
121 if (! status)
122 sdb_log(prio, "%s: unexpected end-of-file", msg);
123 else if (! errno)
124 errno = EIO;
125 case SSL_ERROR_SSL:
126 return ssl_log(prio, msg);
127 default:
128 sdb_log(prio, "%s: unkown SSL error %d", msg, err);
129 break;
130 }
132 if (errno) {
133 char errbuf[1024];
134 sdb_log(prio, "%s: %s", msg,
135 sdb_strerror(errno, errbuf, sizeof(errbuf)));
136 }
137 } /* ssl_log_err */
139 static int
140 copy_options(sdb_ssl_options_t *dst, const sdb_ssl_options_t *src)
141 {
142 sdb_ssl_options_t tmp;
143 sdb_ssl_options_t def = SDB_SSL_DEFAULT_OPTIONS;
145 if (src)
146 tmp = *src;
147 else
148 tmp = def;
150 if (! tmp.ca_file)
151 tmp.ca_file = def.ca_file;
152 if (! tmp.key_file)
153 tmp.key_file = def.key_file;
154 if (! tmp.cert_file)
155 tmp.cert_file = def.cert_file;
157 dst->ca_file = strdup(tmp.ca_file);
158 dst->key_file = strdup(tmp.key_file);
159 dst->cert_file = strdup(tmp.cert_file);
160 if ((! dst->ca_file) || (! dst->key_file) || (! dst->cert_file))
161 return -1;
162 if (tmp.crl_file) {
163 dst->crl_file = strdup(tmp.crl_file);
164 if (! dst->crl_file)
165 return -1;
166 }
167 return 0;
168 } /* copy_options */
170 /*
171 * public API
172 */
174 void
175 sdb_ssl_init(void)
176 {
177 SSL_load_error_strings();
178 OpenSSL_add_ssl_algorithms();
179 } /* sdb_ssl_init */
181 void
182 sdb_ssl_shutdown(void)
183 {
184 ERR_free_strings();
185 } /* sdb_ssl_shutdown */
187 sdb_ssl_client_t *
188 sdb_ssl_client_create(const sdb_ssl_options_t *opts)
189 {
190 sdb_ssl_client_t *client;
192 client = calloc(1, sizeof(*client));
193 if (! client)
194 return NULL;
196 if (copy_options(&client->opts, opts)) {
197 sdb_ssl_client_destroy(client);
198 return NULL;
199 }
201 client->ctx = SSL_CTX_new(SSLv23_client_method());
202 if (! client->ctx) {
203 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL context");
204 sdb_ssl_client_destroy(client);
205 return NULL;
206 }
208 if (! SSL_CTX_load_verify_locations(client->ctx,
209 client->opts.ca_file, NULL)) {
210 ssl_log(SDB_LOG_ERR, "ssl: Failed to load CA file '%s'",
211 client->opts.ca_file);
212 sdb_ssl_client_destroy(client);
213 return NULL;
214 }
215 if (! SSL_CTX_use_certificate_file(client->ctx,
216 client->opts.cert_file, SSL_FILETYPE_PEM)) {
217 ssl_log(SDB_LOG_ERR, "ssl: Failed to load cert file '%s'",
218 client->opts.cert_file);
219 sdb_ssl_client_destroy(client);
220 return NULL;
221 }
222 if (! SSL_CTX_use_PrivateKey_file(client->ctx,
223 client->opts.key_file, SSL_FILETYPE_PEM)) {
224 ssl_log(SDB_LOG_ERR, "ssl: Failed to load key file '%s'",
225 client->opts.key_file);
226 sdb_ssl_client_destroy(client);
227 return NULL;
228 }
229 if (! SSL_CTX_check_private_key(client->ctx)) {
230 ssl_log(SDB_LOG_ERR, "ssl: Failed to verify key (%s)",
231 client->opts.key_file);
232 sdb_ssl_client_destroy(client);
233 return NULL;
234 }
236 SSL_CTX_set_mode(client->ctx, SSL_MODE_AUTO_RETRY);
237 SSL_CTX_set_verify(client->ctx, SSL_VERIFY_PEER, NULL);
238 SSL_CTX_set_verify_depth(client->ctx, 1);
239 return client;
240 } /* sdb_ssl_client_create */
242 void
243 sdb_ssl_client_destroy(sdb_ssl_client_t *client)
244 {
245 if (! client)
246 return;
248 if (client->ctx)
249 SSL_CTX_free(client->ctx);
250 sdb_ssl_free_options(&client->opts);
251 free(client);
252 } /* sdb_ssl_client_destroy */
254 sdb_ssl_session_t *
255 sdb_ssl_client_connect(sdb_ssl_client_t *client, int fd)
256 {
257 sdb_ssl_session_t *session;
258 int status;
259 BIO *bio;
261 if ((! client) || (fd < 0))
262 return NULL;
264 session = calloc(1, sizeof(*session));
265 if (! session)
266 return NULL;
268 bio = BIO_new_socket(fd, BIO_NOCLOSE);
269 if (! bio) {
270 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL socket");
271 sdb_ssl_session_destroy(session);
272 return NULL;
273 }
274 session->ssl = SSL_new(client->ctx);
275 if (! session->ssl) {
276 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL object");
277 sdb_ssl_session_destroy(session);
278 return NULL;
279 }
280 SSL_set_bio(session->ssl, bio, bio);
282 if ((status = SSL_connect(session->ssl)) <= 0) {
283 ssl_log_err(SDB_LOG_ERR, session->ssl, status,
284 "ssl: Failed to initialize SSL session");
285 sdb_ssl_session_destroy(session);
286 return NULL;
287 }
288 if (SSL_get_verify_result(session->ssl) != X509_V_OK) {
289 sdb_log(SDB_LOG_ERR, "Failed to verify SSL connection");
290 sdb_ssl_session_destroy(session);
291 return NULL;
292 }
293 return session;
294 } /* sdb_ssl_client_connect */
296 sdb_ssl_server_t *
297 sdb_ssl_server_create(const sdb_ssl_options_t *opts)
298 {
299 sdb_ssl_server_t *server;
301 server = calloc(1, sizeof(*server));
302 if (! server)
303 return NULL;
305 if (copy_options(&server->opts, opts)) {
306 sdb_ssl_server_destroy(server);
307 return NULL;
308 }
310 server->ctx = SSL_CTX_new(SSLv23_server_method());
311 if (! server->ctx) {
312 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL context");
313 sdb_ssl_server_destroy(server);
314 return NULL;
315 }
317 /* Recommendation documented at
318 * https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ */
319 if (! SSL_CTX_set_cipher_list(server->ctx,
320 "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:"
321 "DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:"
322 "!aNULL:!MD5:!DSS")) {
323 sdb_log(SDB_LOG_ERR, "ssl: Invalid cipher list");
324 sdb_ssl_server_destroy(server);
325 return NULL;
326 }
328 if (! SSL_CTX_load_verify_locations(server->ctx,
329 server->opts.ca_file, NULL)) {
330 ssl_log(SDB_LOG_ERR, "Failed to load CA file %s",
331 server->opts.ca_file);
332 return NULL;
333 }
334 SSL_CTX_set_client_CA_list(server->ctx,
335 SSL_load_client_CA_file(server->opts.ca_file));
337 if (! SSL_CTX_use_certificate_file(server->ctx,
338 server->opts.cert_file, SSL_FILETYPE_PEM)) {
339 ssl_log(SDB_LOG_ERR, "Failed to load SSL cert file %s",
340 server->opts.cert_file);
341 return NULL;
342 }
343 if (! SSL_CTX_use_PrivateKey_file(server->ctx,
344 server->opts.key_file, SSL_FILETYPE_PEM)) {
345 ssl_log(SDB_LOG_ERR, "Failed to load SSL key file %s",
346 server->opts.key_file);
347 return NULL;
348 }
349 if (! SSL_CTX_check_private_key(server->ctx)) {
350 ssl_log(SDB_LOG_ERR, "Failed to verify SSL private key");
351 return NULL;
352 }
354 SSL_CTX_set_mode(server->ctx, SSL_MODE_AUTO_RETRY);
355 SSL_CTX_set_verify(server->ctx,
356 SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
357 SSL_CTX_set_verify_depth(server->ctx, 1);
359 /* TODO: handle server->opts.crl_file */
360 return server;
361 } /* sdb_ssl_server_create */
363 void
364 sdb_ssl_server_destroy(sdb_ssl_server_t *server)
365 {
366 if (! server)
367 return;
369 if (server->ctx)
370 SSL_CTX_free(server->ctx);
371 sdb_ssl_free_options(&server->opts);
372 free(server);
373 } /* sdb_ssl_server_destroy */
375 sdb_ssl_session_t *
376 sdb_ssl_server_accept(sdb_ssl_server_t *server, int fd)
377 {
378 sdb_ssl_session_t *session;
379 int status;
380 BIO *bio;
382 if ((! server) || (fd < 0))
383 return NULL;
385 session = calloc(1, sizeof(*session));
386 if (! session)
387 return NULL;
389 bio = BIO_new_socket(fd, BIO_NOCLOSE);
390 if (! bio) {
391 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL socket");
392 sdb_ssl_session_destroy(session);
393 return NULL;
394 }
395 session->ssl = SSL_new(server->ctx);
396 if (! session->ssl) {
397 ssl_log(SDB_LOG_ERR, "ssl: Failed to create SSL object");
398 sdb_ssl_session_destroy(session);
399 return NULL;
400 }
401 SSL_set_bio(session->ssl, bio, bio);
403 while (42) {
404 if ((status = SSL_accept(session->ssl)) <= 0) {
405 if (SSL_get_error(session->ssl, status) == SSL_ERROR_WANT_READ)
406 continue;
408 ssl_log_err(SDB_LOG_ERR, session->ssl, status,
409 "ssl: Failed to initialize SSL session");
410 sdb_ssl_session_destroy(session);
411 return NULL;
412 }
413 break;
414 }
415 if (SSL_get_verify_result(session->ssl) != X509_V_OK) {
416 sdb_log(SDB_LOG_ERR, "Failed to verify SSL connection");
417 sdb_ssl_session_destroy(session);
418 return NULL;
419 }
420 return session;
421 } /* sdb_ssl_server_accept */
423 void
424 sdb_ssl_session_destroy(sdb_ssl_session_t *session)
425 {
426 if (! session)
427 return;
429 if (session->ssl) {
430 SSL_shutdown(session->ssl);
431 SSL_clear(session->ssl);
432 SSL_free(session->ssl);
433 }
434 free(session);
435 } /* sdb_ssl_session_destroy */
437 char *
438 sdb_ssl_session_peer(sdb_ssl_session_t *session)
439 {
440 X509 *x509;
441 X509_NAME *name;
443 char *peer = NULL;
444 char p[1024];
446 if (! session)
447 return NULL;
449 x509 = SSL_get_peer_certificate(session->ssl);
450 if (! x509)
451 return NULL;
452 name = X509_get_subject_name(x509);
453 if (! name) {
454 X509_free(x509);
455 return NULL;
456 }
458 if (X509_NAME_get_text_by_NID(name, NID_commonName, p, sizeof(p)) > 0)
459 peer = strdup(p);
461 X509_free(x509);
462 return peer;
463 } /* sdb_ssl_session_peer */
465 ssize_t
466 sdb_ssl_session_write(sdb_ssl_session_t *session, const void *buf, size_t n)
467 {
468 int status;
470 if (! session)
471 return -1;
473 status = SSL_write(session->ssl, buf, (int)n);
474 if (status) {
475 if ((status < 0) && (errno != EAGAIN))
476 ssl_log_err(SDB_LOG_ERR, session->ssl, status, "ssl: Write error");
477 return (ssize_t)status;
478 }
480 status = SSL_get_error(session->ssl, status);
481 if (status == SSL_ERROR_ZERO_RETURN)
482 return 0;
484 if ((status == SSL_ERROR_WANT_READ) || (status == SSL_ERROR_WANT_WRITE)) {
485 errno = EWOULDBLOCK;
486 return -1;
487 }
488 errno = ECONNRESET;
489 return -1;
490 } /* sdb_ssl_session_write */
492 ssize_t
493 sdb_ssl_session_read(sdb_ssl_session_t *session, void *buf, size_t n)
494 {
495 int status;
497 if (! session)
498 return -1;
500 status = SSL_read(session->ssl, buf, (int)n);
501 if (status) {
502 if ((status < 0) && (errno != EAGAIN))
503 ssl_log_err(SDB_LOG_ERR, session->ssl, status, "ssl: Read error");
504 return (ssize_t)status;
505 }
507 status = SSL_get_error(session->ssl, status);
508 if (status == SSL_ERROR_ZERO_RETURN)
509 return 0;
511 if ((status == SSL_ERROR_WANT_READ) || (status == SSL_ERROR_WANT_WRITE)) {
512 errno = EWOULDBLOCK;
513 return -1;
514 }
515 errno = ECONNRESET;
516 return -1;
517 } /* sdb_ssl_session_read */
519 void
520 sdb_ssl_free_options(sdb_ssl_options_t *opts)
521 {
522 if (! opts)
523 return;
525 if (opts->ca_file)
526 free(opts->ca_file);
527 if (opts->key_file)
528 free(opts->key_file);
529 if (opts->cert_file)
530 free(opts->cert_file);
531 if (opts->crl_file)
532 free(opts->crl_file);
534 opts->ca_file = opts->key_file = opts->cert_file = opts->crl_file = NULL;
535 } /* sdb_ssl_free_options */
537 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */