blob: 6a045c9bd59f771cb69a28541a9c9f3c26bef01b [file] [log] [blame]
Radek Krejci5da708a2015-09-01 17:33:23 +02001/**
Michal Vasko086311b2016-01-08 09:53:11 +01002 * \file session_server_tls.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 TLS server session manipulation functions
Radek Krejci5da708a2015-09-01 17:33:23 +02005 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 */
22
Michal Vaskoc14e3c82016-01-11 16:14:30 +010023#define _GNU_SOURCE
24
25#include <string.h>
26#include <poll.h>
27
28#include <openssl/ssl.h>
29#include <openssl/evp.h>
30#include <openssl/err.h>
31#include <openssl/x509v3.h>
32
Michal Vasko1a38c862016-01-15 15:50:07 +010033#include "libnetconf.h"
Michal Vasko11d142a2016-01-19 15:58:24 +010034#include "session_server.h"
Radek Krejci5da708a2015-09-01 17:33:23 +020035
Michal Vasko3031aae2016-01-27 16:07:18 +010036struct nc_server_tls_opts tls_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010037pthread_mutex_t tls_ch_opts_lock = PTHREAD_MUTEX_INITIALIZER;
Michal Vaskoc14e3c82016-01-11 16:14:30 +010038extern struct nc_server_opts server_opts;
Michal Vaskoc61c4492016-01-25 11:13:34 +010039
Michal Vasko5c2f7952016-01-22 13:16:31 +010040static pthread_key_t verify_key;
41static pthread_once_t verify_once = PTHREAD_ONCE_INIT;
42
Michal Vaskoc14e3c82016-01-11 16:14:30 +010043static char *
44asn1time_to_str(ASN1_TIME *t)
Michal Vasko086311b2016-01-08 09:53:11 +010045{
Michal Vaskoc14e3c82016-01-11 16:14:30 +010046 char *cp;
47 BIO *bio;
48 int n;
Radek Krejci5da708a2015-09-01 17:33:23 +020049
Michal Vaskoc14e3c82016-01-11 16:14:30 +010050 if (!t) {
51 return NULL;
52 }
53 bio = BIO_new(BIO_s_mem());
54 if (!bio) {
55 return NULL;
56 }
57 ASN1_TIME_print(bio, t);
58 n = BIO_pending(bio);
59 cp = malloc(n + 1);
60 n = BIO_read(bio, cp, n);
61 if (n < 0) {
62 BIO_free(bio);
63 free(cp);
64 return NULL;
65 }
66 cp[n] = '\0';
67 BIO_free(bio);
68 return cp;
69}
70
71static void
72digest_to_str(const unsigned char *digest, unsigned int dig_len, char **str)
73{
74 unsigned int i;
75
76 *str = malloc(dig_len * 3);
77 for (i = 0; i < dig_len - 1; ++i) {
78 sprintf((*str) + (i * 3), "%02x:", digest[i]);
79 }
80 sprintf((*str) + (i * 3), "%02x", digest[i]);
81}
82
83/* return NULL - SSL error can be retrieved */
84static X509 *
85base64der_to_cert(const char *in)
86{
87 X509 *out;
88 char *buf;
89 BIO *bio;
90
91 if (in == NULL) {
92 return NULL;
93 }
94
95 if (asprintf(&buf, "%s%s%s", "-----BEGIN CERTIFICATE-----\n", in, "\n-----END CERTIFICATE-----") == -1) {
96 return NULL;
97 }
98 bio = BIO_new_mem_buf(buf, strlen(buf));
99 if (!bio) {
100 free(buf);
101 return NULL;
102 }
103
104 out = PEM_read_bio_X509(bio, NULL, NULL, NULL);
105 if (!out) {
106 free(buf);
107 BIO_free(bio);
108 return NULL;
109 }
110
111 free(buf);
112 BIO_free(bio);
113 return out;
114}
115
116/* return NULL - either errno or SSL error */
117static X509 *
118pem_to_cert(const char *path)
119{
120 FILE *fp;
121 X509 *out;
122
123 fp = fopen(path, "r");
124 if (!fp) {
125 return NULL;
126 }
127
128 out = PEM_read_X509(fp, NULL, NULL, NULL);
129 fclose(fp);
130 return out;
131}
132
133static EVP_PKEY *
134base64der_to_privatekey(const char *in, int rsa)
135{
136 EVP_PKEY *out;
137 char *buf;
138 BIO *bio;
139
140 if (in == NULL) {
141 return NULL;
142 }
143
144 if (asprintf(&buf, "%s%s%s%s%s%s%s", "-----BEGIN ", (rsa ? "RSA" : "DSA"), " PRIVATE KEY-----\n", in, "\n-----END ", (rsa ? "RSA" : "DSA"), " PRIVATE KEY-----") == -1) {
145 return NULL;
146 }
147 bio = BIO_new_mem_buf(buf, strlen(buf));
148 if (!bio) {
149 free(buf);
150 return NULL;
151 }
152
153 out = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
154 if (!out) {
155 free(buf);
156 BIO_free(bio);
157 return NULL;
158 }
159
160 free(buf);
161 BIO_free(bio);
162 return out;
163}
164
165static int
166cert_pubkey_match(X509 *cert1, X509 *cert2)
167{
168 ASN1_BIT_STRING *bitstr1, *bitstr2;
169
170 bitstr1 = X509_get0_pubkey_bitstr(cert1);
171 bitstr2 = X509_get0_pubkey_bitstr(cert2);
172
173 if (!bitstr1 || !bitstr2 || (bitstr1->length != bitstr2->length) ||
174 memcmp(bitstr1->data, bitstr2->data, bitstr1->length)) {
175 return 0;
176 }
177
178 return 1;
179}
180
181static int
182nc_tls_ctn_get_username_from_cert(X509 *client_cert, NC_TLS_CTN_MAPTYPE map_type, char **username)
183{
184 STACK_OF(GENERAL_NAME) *san_names;
185 GENERAL_NAME *san_name;
186 ASN1_OCTET_STRING *ip;
187 int i, san_count;
188 char *subject, *common_name;
189
190 if (map_type == NC_TLS_CTN_COMMON_NAME) {
191 subject = X509_NAME_oneline(X509_get_subject_name(client_cert), NULL, 0);
192 common_name = strstr(subject, "CN=");
193 if (!common_name) {
Michal Vaskod083db62016-01-19 10:31:29 +0100194 WRN("Certificate does not include the commonName field.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100195 free(subject);
196 return 1;
197 }
198 common_name += 3;
199 if (strchr(common_name, '/')) {
200 *strchr(common_name, '/') = '\0';
201 }
202 *username = strdup(common_name);
203 free(subject);
204 } else {
205 /* retrieve subjectAltName's rfc822Name (email), dNSName and iPAddress values */
206 san_names = X509_get_ext_d2i(client_cert, NID_subject_alt_name, NULL, NULL);
207 if (!san_names) {
Michal Vaskod083db62016-01-19 10:31:29 +0100208 WRN("Certificate has no SANs or failed to retrieve them.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100209 return 1;
210 }
211
212 san_count = sk_GENERAL_NAME_num(san_names);
213 for (i = 0; i < san_count; ++i) {
214 san_name = sk_GENERAL_NAME_value(san_names, i);
215
216 /* rfc822Name (email) */
217 if ((map_type == NC_TLS_CTN_SAN_ANY || map_type == NC_TLS_CTN_SAN_RFC822_NAME) &&
218 san_name->type == GEN_EMAIL) {
219 *username = strdup((char *)ASN1_STRING_data(san_name->d.rfc822Name));
220 break;
221 }
222
223 /* dNSName */
224 if ((map_type == NC_TLS_CTN_SAN_ANY || map_type == NC_TLS_CTN_SAN_DNS_NAME) &&
225 san_name->type == GEN_DNS) {
226 *username = strdup((char *)ASN1_STRING_data(san_name->d.dNSName));
227 break;
228 }
229
230 /* iPAddress */
231 if ((map_type == NC_TLS_CTN_SAN_ANY || map_type == NC_TLS_CTN_SAN_IP_ADDRESS) &&
232 san_name->type == GEN_IPADD) {
233 ip = san_name->d.iPAddress;
234 if (ip->length == 4) {
235 if (asprintf(username, "%d.%d.%d.%d", ip->data[0], ip->data[1], ip->data[2], ip->data[3]) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100236 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100237 sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
238 return -1;
239 }
240 break;
241 } else if (ip->length == 16) {
242 if (asprintf(username, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
243 ip->data[0], ip->data[1], ip->data[2], ip->data[3], ip->data[4], ip->data[5],
244 ip->data[6], ip->data[7], ip->data[8], ip->data[9], ip->data[10], ip->data[11],
245 ip->data[12], ip->data[13], ip->data[14], ip->data[15]) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100246 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100247 sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
248 return -1;
249 }
250 break;
251 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100252 WRN("SAN IP address in an unknown format (length is %d).", ip->length);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100253 }
254 }
255 }
256 sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
257
258 if (i < san_count) {
259 switch (map_type) {
260 case NC_TLS_CTN_SAN_RFC822_NAME:
Michal Vaskod083db62016-01-19 10:31:29 +0100261 WRN("Certificate does not include the SAN rfc822Name field.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100262 break;
263 case NC_TLS_CTN_SAN_DNS_NAME:
Michal Vaskod083db62016-01-19 10:31:29 +0100264 WRN("Certificate does not include the SAN dNSName field.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100265 break;
266 case NC_TLS_CTN_SAN_IP_ADDRESS:
Michal Vaskod083db62016-01-19 10:31:29 +0100267 WRN("Certificate does not include the SAN iPAddress field.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100268 break;
269 case NC_TLS_CTN_SAN_ANY:
Michal Vaskod083db62016-01-19 10:31:29 +0100270 WRN("Certificate does not include any relevant SAN fields.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100271 break;
272 default:
273 break;
274 }
275 return 1;
276 }
277 }
278
279 return 0;
280}
281
282/* return: 0 - OK, 1 - no match, -1 - error */
283static int
Michal Vaskoc61c4492016-01-25 11:13:34 +0100284nc_tls_cert_to_name(struct nc_ctn *ctn_first, X509 *cert, NC_TLS_CTN_MAPTYPE *map_type, const char **name)
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100285{
286 char *digest_md5 = NULL, *digest_sha1 = NULL, *digest_sha224 = NULL;
287 char *digest_sha256 = NULL, *digest_sha384 = NULL, *digest_sha512 = NULL;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100288 unsigned char *buf = malloc(64);
289 unsigned int buf_len = 64;
290 int ret = 0;
Michal Vasko5e3f3392016-01-20 11:13:01 +0100291 struct nc_ctn *ctn;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100292
Michal Vaskoc61c4492016-01-25 11:13:34 +0100293 if (!ctn_first || !cert || !map_type || !name) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100294 free(buf);
295 return -1;
296 }
297
Michal Vaskoc61c4492016-01-25 11:13:34 +0100298 for (ctn = ctn_first; ctn; ctn = ctn->next) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100299 /* MD5 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100300 if (!strncmp(ctn->fingerprint, "01", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100301 if (!digest_md5) {
302 if (X509_digest(cert, EVP_md5(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100303 ERR("Calculating MD5 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100304 ret = -1;
305 goto cleanup;
306 }
307 digest_to_str(buf, buf_len, &digest_md5);
308 }
309
Michal Vasko5e3f3392016-01-20 11:13:01 +0100310 if (!strcasecmp(ctn->fingerprint + 3, digest_md5)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100311 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100312 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100313 *map_type = ctn->map_type;
314 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
315 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100316 }
317 break;
318 }
319
320 /* SHA-1 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100321 } else if (!strncmp(ctn->fingerprint, "02", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100322 if (!digest_sha1) {
323 if (X509_digest(cert, EVP_sha1(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100324 ERR("Calculating SHA-1 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100325 ret = -1;
326 goto cleanup;
327 }
328 digest_to_str(buf, buf_len, &digest_sha1);
329 }
330
Michal Vasko5e3f3392016-01-20 11:13:01 +0100331 if (!strcasecmp(ctn->fingerprint + 3, digest_sha1)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100332 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100333 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100334 *map_type = ctn->map_type;
335 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
336 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100337 }
338 break;
339 }
340
341 /* SHA-224 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100342 } else if (!strncmp(ctn->fingerprint, "03", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100343 if (!digest_sha224) {
344 if (X509_digest(cert, EVP_sha224(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100345 ERR("Calculating SHA-224 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100346 ret = -1;
347 goto cleanup;
348 }
349 digest_to_str(buf, buf_len, &digest_sha224);
350 }
351
Michal Vasko5e3f3392016-01-20 11:13:01 +0100352 if (!strcasecmp(ctn->fingerprint + 3, digest_sha224)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100353 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100354 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100355 *map_type = ctn->map_type;
356 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
357 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100358 }
359 break;
360 }
361
362 /* SHA-256 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100363 } else if (!strncmp(ctn->fingerprint, "04", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100364 if (!digest_sha256) {
365 if (X509_digest(cert, EVP_sha256(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100366 ERR("Calculating SHA-256 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100367 ret = -1;
368 goto cleanup;
369 }
370 digest_to_str(buf, buf_len, &digest_sha256);
371 }
372
Michal Vasko5e3f3392016-01-20 11:13:01 +0100373 if (!strcasecmp(ctn->fingerprint + 3, digest_sha256)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100374 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100375 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100376 *map_type = ctn->map_type;
377 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
378 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100379 }
380 break;
381 }
382
383 /* SHA-384 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100384 } else if (!strncmp(ctn->fingerprint, "05", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100385 if (!digest_sha384) {
386 if (X509_digest(cert, EVP_sha384(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100387 ERR("Calculating SHA-384 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100388 ret = -1;
389 goto cleanup;
390 }
391 digest_to_str(buf, buf_len, &digest_sha384);
392 }
393
Michal Vasko5e3f3392016-01-20 11:13:01 +0100394 if (!strcasecmp(ctn->fingerprint + 3, digest_sha384)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100395 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100396 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100397 *map_type = ctn->map_type;
398 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
399 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100400 }
401 break;
402 }
403
404 /* SHA-512 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100405 } else if (!strncmp(ctn->fingerprint, "06", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100406 if (!digest_sha512) {
407 if (X509_digest(cert, EVP_sha512(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100408 ERR("Calculating SHA-512 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100409 ret = -1;
410 goto cleanup;
411 }
412 digest_to_str(buf, buf_len, &digest_sha512);
413 }
414
Michal Vasko5e3f3392016-01-20 11:13:01 +0100415 if (!strcasecmp(ctn->fingerprint + 3, digest_sha512)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100416 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100417 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100418 *map_type = ctn->map_type;
419 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
420 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100421 }
422 break;
423 }
424
425 /* unknown */
426 } else {
Michal Vasko5e3f3392016-01-20 11:13:01 +0100427 WRN("Unknown fingerprint algorithm used (%s), skipping.", ctn->fingerprint);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100428 }
429 }
430
Michal Vasko5e3f3392016-01-20 11:13:01 +0100431 if (!ctn) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100432 ret = 1;
433 }
434
435cleanup:
436 free(digest_md5);
437 free(digest_sha1);
438 free(digest_sha224);
439 free(digest_sha256);
440 free(digest_sha384);
441 free(digest_sha512);
442 free(buf);
443 return ret;
444}
445
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100446static int
447nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
448{
449 X509_STORE_CTX store_ctx;
450 X509_OBJECT obj;
451 X509_NAME *subject;
452 X509_NAME *issuer;
453 X509 *cert;
454 X509_CRL *crl;
455 X509_REVOKED *revoked;
456 STACK_OF(X509) *cert_stack;
457 EVP_PKEY *pubkey;
458 struct nc_session* session;
Michal Vasko3031aae2016-01-27 16:07:18 +0100459 struct nc_server_tls_opts *opts;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100460 long serial;
461 int i, n, rc, depth;
462 char *cp;
463 const char *username = NULL;
464 NC_TLS_CTN_MAPTYPE map_type = 0;
465 ASN1_TIME *last_update = NULL, *next_update = NULL;
466
Michal Vasko6d292992016-01-18 09:42:38 +0100467 /* get the thread session */
Michal Vasko5c2f7952016-01-22 13:16:31 +0100468 session = pthread_getspecific(verify_key);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100469 if (!session) {
470 ERRINT;
471 return 0;
472 }
473
Michal Vasko3031aae2016-01-27 16:07:18 +0100474 opts = session->ti_opts;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100475
476 /* get the last certificate, that is the peer (client) certificate */
Michal Vasko06e22432016-01-15 10:30:06 +0100477 if (!session->tls_cert) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100478 cert_stack = X509_STORE_CTX_get1_chain(x509_ctx);
479 /* TODO all that is needed, but function X509_up_ref not present in older OpenSSL versions
480 session->cert = sk_X509_value(cert_stack, sk_X509_num(cert_stack) - 1);
481 X509_up_ref(session->cert);
482 sk_X509_pop_free(cert_stack, X509_free); */
483 while ((cert = sk_X509_pop(cert_stack))) {
Michal Vasko06e22432016-01-15 10:30:06 +0100484 X509_free(session->tls_cert);
485 session->tls_cert = cert;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100486 }
487 sk_X509_pop_free(cert_stack, X509_free);
488 }
489
490 /* standard certificate verification failed, so a trusted client cert must match to continue */
491 if (!preverify_ok) {
Michal Vasko06e22432016-01-15 10:30:06 +0100492 subject = X509_get_subject_name(session->tls_cert);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100493 cert_stack = X509_STORE_get1_certs(x509_ctx, subject);
494 if (cert_stack) {
495 for (i = 0; i < sk_X509_num(cert_stack); ++i) {
Michal Vasko06e22432016-01-15 10:30:06 +0100496 if (cert_pubkey_match(session->tls_cert, sk_X509_value(cert_stack, i))) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100497 /* we are just overriding the failed standard certificate verification (preverify_ok == 0),
498 * this callback will be called again with the same current certificate and preverify_ok == 1 */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100499 VRB("Cert verify: fail (%s), but the client certificate is trusted, continuing.",
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100500 X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
501 X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
502 sk_X509_pop_free(cert_stack, X509_free);
503 return 1;
504 }
505 }
506 sk_X509_pop_free(cert_stack, X509_free);
507 }
508
509 ERR("Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
510 return 0;
511 }
512
513 /* print cert verify info */
514 depth = X509_STORE_CTX_get_error_depth(x509_ctx);
Michal Vaskod083db62016-01-19 10:31:29 +0100515 VRB("Cert verify: depth %d.", depth);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100516
517 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
518 subject = X509_get_subject_name(cert);
519 issuer = X509_get_issuer_name(cert);
520
521 cp = X509_NAME_oneline(subject, NULL, 0);
Michal Vaskod083db62016-01-19 10:31:29 +0100522 VRB("Cert verify: subject: %s.", cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100523 OPENSSL_free(cp);
524 cp = X509_NAME_oneline(issuer, NULL, 0);
Michal Vaskod083db62016-01-19 10:31:29 +0100525 VRB("Cert verify: issuer: %s.", cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100526 OPENSSL_free(cp);
527
528 /* check for revocation if set */
Michal Vaskoc61c4492016-01-25 11:13:34 +0100529 if (opts->crl_store) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100530 /* try to retrieve a CRL corresponding to the _subject_ of
531 * the current certificate in order to verify it's integrity */
532 memset((char *)&obj, 0, sizeof(obj));
Michal Vaskoc61c4492016-01-25 11:13:34 +0100533 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100534 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
535 X509_STORE_CTX_cleanup(&store_ctx);
536 crl = obj.data.crl;
537 if (rc > 0 && crl) {
538 cp = X509_NAME_oneline(subject, NULL, 0);
Michal Vaskod083db62016-01-19 10:31:29 +0100539 VRB("Cert verify CRL: issuer: %s.", cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100540 OPENSSL_free(cp);
541
542 last_update = X509_CRL_get_lastUpdate(crl);
543 next_update = X509_CRL_get_nextUpdate(crl);
544 cp = asn1time_to_str(last_update);
Michal Vaskod083db62016-01-19 10:31:29 +0100545 VRB("Cert verify CRL: last update: %s.", cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100546 free(cp);
547 cp = asn1time_to_str(next_update);
Michal Vaskod083db62016-01-19 10:31:29 +0100548 VRB("Cert verify CRL: next update: %s.", cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100549 free(cp);
550
551 /* verify the signature on this CRL */
552 pubkey = X509_get_pubkey(cert);
553 if (X509_CRL_verify(crl, pubkey) <= 0) {
554 ERR("Cert verify CRL: invalid signature.");
555 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
556 X509_OBJECT_free_contents(&obj);
557 if (pubkey) {
558 EVP_PKEY_free(pubkey);
559 }
560 return 0;
561 }
562 if (pubkey) {
563 EVP_PKEY_free(pubkey);
564 }
565
566 /* check date of CRL to make sure it's not expired */
567 if (!next_update) {
568 ERR("Cert verify CRL: invalid nextUpdate field.");
569 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
570 X509_OBJECT_free_contents(&obj);
571 return 0;
572 }
573 if (X509_cmp_current_time(next_update) < 0) {
574 ERR("Cert verify CRL: expired - revoking all certificates.");
575 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
576 X509_OBJECT_free_contents(&obj);
577 return 0;
578 }
579 X509_OBJECT_free_contents(&obj);
580 }
581
582 /* try to retrieve a CRL corresponding to the _issuer_ of
Michal Vaskob48aa812016-01-18 14:13:09 +0100583 * the current certificate in order to check for revocation */
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100584 memset((char *)&obj, 0, sizeof(obj));
Michal Vaskoc61c4492016-01-25 11:13:34 +0100585 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100586 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
587 X509_STORE_CTX_cleanup(&store_ctx);
588 crl = obj.data.crl;
589 if (rc > 0 && crl) {
590 /* check if the current certificate is revoked by this CRL */
591 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
592 for (i = 0; i < n; i++) {
593 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
594 if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
595 serial = ASN1_INTEGER_get(revoked->serialNumber);
596 cp = X509_NAME_oneline(issuer, NULL, 0);
Michal Vaskod083db62016-01-19 10:31:29 +0100597 ERR("Cert verify CRL: certificate with serial %ld (0x%lX) revoked per CRL from issuer %s.", serial, serial, cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100598 OPENSSL_free(cp);
599 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
600 X509_OBJECT_free_contents(&obj);
601 return 0;
602 }
603 }
604 X509_OBJECT_free_contents(&obj);
605 }
606 }
607
608 /* cert-to-name already successful */
609 if (session->username) {
610 return 1;
611 }
612
613 /* cert-to-name */
Michal Vaskoc61c4492016-01-25 11:13:34 +0100614 rc = nc_tls_cert_to_name(opts->ctn, cert, &map_type, &username);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100615
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100616 if (rc) {
617 if (rc == -1) {
618 /* fatal error */
619 depth = 0;
620 }
621 /* rc == 1 is a normal CTN fail (no match found) */
622 goto fail;
623 }
624
625 /* cert-to-name match, now to extract the specific field from the peer cert */
626 if (map_type == NC_TLS_CTN_SPECIFIED) {
Michal Vasko5e3f3392016-01-20 11:13:01 +0100627 nc_ctx_lock(-1, NULL);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100628 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko5e3f3392016-01-20 11:13:01 +0100629 nc_ctx_unlock();
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100630 } else {
Michal Vasko06e22432016-01-15 10:30:06 +0100631 rc = nc_tls_ctn_get_username_from_cert(session->tls_cert, map_type, &cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100632 if (rc) {
633 if (rc == -1) {
634 depth = 0;
635 }
636 goto fail;
637 }
Michal Vasko5e3f3392016-01-20 11:13:01 +0100638 nc_ctx_lock(-1, NULL);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100639 session->username = lydict_insert_zc(server_opts.ctx, cp);
Michal Vasko5e3f3392016-01-20 11:13:01 +0100640 nc_ctx_unlock();
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100641 }
642
643 VRB("Cert verify CTN: new client username recognized as \"%s\".", session->username);
644 return 1;
645
646fail:
647 if (depth > 0) {
648 VRB("Cert verify CTN: cert fail, cert-to-name will continue on the next cert in chain.");
649 return 1;
650 }
651
652 VRB("Cert-to-name unsuccessful, dropping the new client.");
653 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
654 return 0;
655}
656
Michal Vasko3031aae2016-01-27 16:07:18 +0100657API int
658nc_server_tls_add_endpt_listen(const char *name, const char *address, uint16_t port)
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100659{
Michal Vasko3031aae2016-01-27 16:07:18 +0100660 return nc_server_add_endpt_listen(name, address, port, NC_TI_OPENSSL);
661}
662
663API int
664nc_server_tls_del_endpt(const char *name)
665{
666 return nc_server_del_endpt(name, NC_TI_OPENSSL);
667}
668
669static int
670nc_server_tls_set_cert(const char *cert, struct nc_server_tls_opts *opts)
671{
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100672 X509 *x509_cert;
673
674 if (!cert) {
675 ERRARG;
676 return -1;
677 }
678
Michal Vaskoc61c4492016-01-25 11:13:34 +0100679 if (!opts->tls_ctx) {
680 opts->tls_ctx = SSL_CTX_new(TLSv1_2_server_method());
681 if (!opts->tls_ctx) {
Michal Vaskod083db62016-01-19 10:31:29 +0100682 ERR("Failed to create TLS context.");
Michal Vaskob48aa812016-01-18 14:13:09 +0100683 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100684 }
Michal Vaskoc61c4492016-01-25 11:13:34 +0100685 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_tlsclb_verify);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100686 }
687
688 x509_cert = base64der_to_cert(cert);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100689 if (!x509_cert || (SSL_CTX_use_certificate(opts->tls_ctx, x509_cert) != 1)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100690 ERR("Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100691 X509_free(x509_cert);
Michal Vaskob48aa812016-01-18 14:13:09 +0100692 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100693 }
694 X509_free(x509_cert);
695
696 return 0;
Michal Vaskob48aa812016-01-18 14:13:09 +0100697
698fail:
Michal Vaskoc61c4492016-01-25 11:13:34 +0100699 return -1;
700}
701
702API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100703nc_server_tls_endpt_set_cert(const char *endpt_name, const char *cert)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100704{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100705 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100706 struct nc_endpt *endpt;
707
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100708 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100709 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100710 return -1;
711 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100712 ret = nc_server_tls_set_cert(cert, endpt->ti_opts);
713 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100714
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100715 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100716}
717
718API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100719nc_server_tls_ch_set_cert(const char *cert)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100720{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100721 int ret;
722
723 /* OPTS LOCK */
724 pthread_mutex_lock(&tls_ch_opts_lock);
725 ret = nc_server_tls_set_cert(cert, &tls_ch_opts);
726 /* OPTS UNLOCK */
727 pthread_mutex_unlock(&tls_ch_opts_lock);
728
729 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100730}
731
732static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100733nc_server_tls_set_cert_path(const char *cert_path, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100734{
Michal Vaskoc61c4492016-01-25 11:13:34 +0100735 if (!cert_path) {
736 ERRARG;
737 return -1;
738 }
739
Michal Vaskoc61c4492016-01-25 11:13:34 +0100740 if (!opts->tls_ctx) {
741 opts->tls_ctx = SSL_CTX_new(TLSv1_2_server_method());
742 if (!opts->tls_ctx) {
743 ERR("Failed to create TLS context.");
744 goto fail;
745 }
746 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_tlsclb_verify);
747 }
748
749 if (SSL_CTX_use_certificate_file(opts->tls_ctx, cert_path, SSL_FILETYPE_PEM) != 1) {
750 ERR("Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
751 goto fail;
752 }
753
Michal Vaskoc61c4492016-01-25 11:13:34 +0100754 return 0;
755
756fail:
Michal Vaskob48aa812016-01-18 14:13:09 +0100757 return -1;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100758}
759
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100760API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100761nc_server_tls_set_endpt_cert_path(const char *endpt_name, const char *cert_path)
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100762{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100763 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100764 struct nc_endpt *endpt;
765
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100766 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100767 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100768 return -1;
769 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100770 ret = nc_server_tls_set_cert_path(cert_path, endpt->ti_opts);
771 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100772
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100773 return ret;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100774}
775
776API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100777nc_server_tls_ch_set_cert_path(const char *cert_path)
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100778{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100779 int ret;
780
781 /* OPTS LOCK */
782 pthread_mutex_lock(&tls_ch_opts_lock);
783 ret = nc_server_tls_set_cert_path(cert_path, &tls_ch_opts);
784 /* OPTS UNLOCK */
785 pthread_mutex_unlock(&tls_ch_opts_lock);
786
787 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100788}
789
790static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100791nc_server_tls_set_key(const char *privkey, int is_rsa, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100792{
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100793 EVP_PKEY *key;;
794
795 if (!privkey) {
796 ERRARG;
797 return -1;
798 }
799
Michal Vaskoc61c4492016-01-25 11:13:34 +0100800 if (!opts->tls_ctx) {
801 opts->tls_ctx = SSL_CTX_new(TLSv1_2_server_method());
802 if (!opts->tls_ctx) {
Michal Vaskod083db62016-01-19 10:31:29 +0100803 ERR("Failed to create TLS context.");
Michal Vaskob48aa812016-01-18 14:13:09 +0100804 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100805 }
Michal Vaskoc61c4492016-01-25 11:13:34 +0100806 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_tlsclb_verify);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100807 }
808
809 key = base64der_to_privatekey(privkey, is_rsa);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100810 if (!key || (SSL_CTX_use_PrivateKey(opts->tls_ctx, key) != 1)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100811 ERR("Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100812 EVP_PKEY_free(key);
Michal Vaskob48aa812016-01-18 14:13:09 +0100813 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100814 }
815 EVP_PKEY_free(key);
816
817 return 0;
Michal Vaskob48aa812016-01-18 14:13:09 +0100818
819fail:
Michal Vaskoc61c4492016-01-25 11:13:34 +0100820 return -1;
821}
822
823API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100824nc_server_tls_endpt_set_key(const char *endpt_name, const char *privkey, int is_rsa)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100825{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100826 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100827 struct nc_endpt *endpt;
828
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100829 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100830 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100831 return -1;
832 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100833 ret = nc_server_tls_set_key(privkey, is_rsa, endpt->ti_opts);
834 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100835
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100836 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100837}
838
839API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100840nc_server_tls_ch_set_key(const char *privkey, int is_rsa)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100841{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100842 int ret;
843
844 /* OPTS LOCK */
845 pthread_mutex_lock(&tls_ch_opts_lock);
846 ret = nc_server_tls_set_key(privkey, is_rsa, &tls_ch_opts);
847 /* OPTS UNLOCK */
848 pthread_mutex_unlock(&tls_ch_opts_lock);
849
850 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100851}
852
853static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100854nc_server_tls_set_key_path(const char *privkey_path, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100855{
Michal Vaskoc61c4492016-01-25 11:13:34 +0100856 if (!privkey_path) {
857 ERRARG;
858 return -1;
859 }
860
Michal Vaskoc61c4492016-01-25 11:13:34 +0100861 if (!opts->tls_ctx) {
862 opts->tls_ctx = SSL_CTX_new(TLSv1_2_server_method());
863 if (!opts->tls_ctx) {
864 ERR("Failed to create TLS context.");
865 goto fail;
866 }
867 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_tlsclb_verify);
868 }
869
870 if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, privkey_path, SSL_FILETYPE_PEM) != 1) {
871 ERR("Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
872 goto fail;
873 }
874
Michal Vaskoc61c4492016-01-25 11:13:34 +0100875 return 0;
876
877fail:
Michal Vaskob48aa812016-01-18 14:13:09 +0100878 return -1;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100879}
880
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100881API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100882nc_server_tls_endpt_set_key_path(const char *endpt_name, const char *privkey_path)
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100883{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100884 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100885 struct nc_endpt *endpt;
886
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100887 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100888 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100889 return -1;
890 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100891 ret = nc_server_tls_set_key_path(privkey_path, endpt->ti_opts);
892 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100893
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100894 return ret;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100895}
896
897API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100898nc_server_tls_ch_set_key_path(const char *privkey_path)
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100899{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100900 int ret;
901
902 /* OPTS LOCK */
903 pthread_mutex_lock(&tls_ch_opts_lock);
904 ret = nc_server_tls_set_key_path(privkey_path, &tls_ch_opts);
905 /* OPTS UNLOCK */
906 pthread_mutex_unlock(&tls_ch_opts_lock);
907
908 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100909}
910
911static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100912nc_server_tls_add_trusted_cert(const char *cert, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100913{
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100914 X509_STORE *cert_store;
915 X509 *x509_cert;
916
917 if (!cert) {
918 ERRARG;
919 return -1;
920 }
921
Michal Vaskoc61c4492016-01-25 11:13:34 +0100922 if (!opts->tls_ctx) {
923 opts->tls_ctx = SSL_CTX_new(TLSv1_2_server_method());
924 if (!opts->tls_ctx) {
Michal Vaskod083db62016-01-19 10:31:29 +0100925 ERR("Failed to create TLS context.");
Michal Vaskob48aa812016-01-18 14:13:09 +0100926 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100927 }
Michal Vaskoc61c4492016-01-25 11:13:34 +0100928 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_tlsclb_verify);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100929 }
930
Michal Vaskoc61c4492016-01-25 11:13:34 +0100931 cert_store = SSL_CTX_get_cert_store(opts->tls_ctx);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100932 if (!cert_store) {
933 cert_store = X509_STORE_new();
Michal Vaskoc61c4492016-01-25 11:13:34 +0100934 SSL_CTX_set_cert_store(opts->tls_ctx, cert_store);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100935 }
936
937 x509_cert = base64der_to_cert(cert);
938 if (!x509_cert || (X509_STORE_add_cert(cert_store, x509_cert) != 1)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100939 ERR("Adding a trusted certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100940 X509_free(x509_cert);
Michal Vaskob48aa812016-01-18 14:13:09 +0100941 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100942 }
943 X509_free(x509_cert);
944
945 return 0;
Michal Vaskob48aa812016-01-18 14:13:09 +0100946
947fail:
Michal Vaskob48aa812016-01-18 14:13:09 +0100948 return -1;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100949}
950
951API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100952nc_server_tls_endpt_add_trusted_cert(const char *endpt_name, const char *cert)
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100953{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100954 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100955 struct nc_endpt *endpt;
956
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100957 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100958 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100959 return -1;
960 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100961 ret = nc_server_tls_add_trusted_cert(cert, endpt->ti_opts);
962 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100963
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100964 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100965}
966
967API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100968nc_server_tls_ch_add_trusted_cert(const char *cert)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100969{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100970 int ret;
971
972 /* OPTS LOCK */
973 pthread_mutex_lock(&tls_ch_opts_lock);
974 ret = nc_server_tls_add_trusted_cert(cert, &tls_ch_opts);
975 /* OPTS UNLOCK */
976 pthread_mutex_unlock(&tls_ch_opts_lock);
977
978 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100979}
980
981static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100982nc_server_tls_add_trusted_cert_path(const char *cert_path, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100983{
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100984 X509_STORE *cert_store;
985 X509 *x509_cert;
986
987 if (!cert_path) {
988 ERRARG;
989 return -1;
990 }
991
Michal Vaskoc61c4492016-01-25 11:13:34 +0100992 if (!opts->tls_ctx) {
993 opts->tls_ctx = SSL_CTX_new(TLSv1_2_server_method());
994 if (!opts->tls_ctx) {
Michal Vaskod083db62016-01-19 10:31:29 +0100995 ERR("Failed to create TLS context.");
Michal Vaskob48aa812016-01-18 14:13:09 +0100996 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100997 }
Michal Vaskoc61c4492016-01-25 11:13:34 +0100998 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_tlsclb_verify);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100999 }
1000
Michal Vaskoc61c4492016-01-25 11:13:34 +01001001 cert_store = SSL_CTX_get_cert_store(opts->tls_ctx);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001002 if (!cert_store) {
1003 cert_store = X509_STORE_new();
Michal Vaskoc61c4492016-01-25 11:13:34 +01001004 SSL_CTX_set_cert_store(opts->tls_ctx, cert_store);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001005 }
1006
1007 errno = 0;
1008 x509_cert = pem_to_cert(cert_path);
1009 if (!x509_cert || (X509_STORE_add_cert(cert_store, x509_cert) != 1)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001010 ERR("Adding a trusted certificate failed (%s).",
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001011 (errno ? strerror(errno) : ERR_reason_error_string(ERR_get_error())));
1012 X509_free(x509_cert);
Michal Vaskob48aa812016-01-18 14:13:09 +01001013 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001014 }
1015 X509_free(x509_cert);
1016
1017 return 0;
Michal Vaskob48aa812016-01-18 14:13:09 +01001018
1019fail:
Michal Vaskob48aa812016-01-18 14:13:09 +01001020 return -1;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001021}
1022
1023API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001024nc_server_tls_endpt_add_trusted_cert_path(const char *endpt_name, const char *cert_path)
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001025{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001026 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001027 struct nc_endpt *endpt;
1028
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001029 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001030 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001031 return -1;
1032 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001033 ret = nc_server_tls_add_trusted_cert_path(cert_path, endpt->ti_opts);
1034 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +01001035
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001036 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001037}
1038
1039API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001040nc_server_tls_ch_add_trusted_cert_path(const char *cert_path)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001041{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001042 int ret;
1043
1044 /* OPTS LOCK */
1045 pthread_mutex_lock(&tls_ch_opts_lock);
1046 ret = nc_server_tls_add_trusted_cert_path(cert_path, &tls_ch_opts);
1047 /* OPTS UNLOCK */
1048 pthread_mutex_unlock(&tls_ch_opts_lock);
1049
1050 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001051}
1052
1053static int
Michal Vasko3031aae2016-01-27 16:07:18 +01001054nc_server_tls_set_trusted_cacert_locations(const char *cacert_file_path, const char *cacert_dir_path, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001055{
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001056 X509_STORE *cert_store;
1057 X509_LOOKUP *lookup;
1058
1059 if (!cacert_file_path && !cacert_dir_path) {
1060 ERRARG;
1061 return -1;
1062 }
1063
Michal Vaskoc61c4492016-01-25 11:13:34 +01001064 if (!opts->tls_ctx) {
1065 opts->tls_ctx = SSL_CTX_new(TLSv1_2_server_method());
1066 if (!opts->tls_ctx) {
Michal Vaskod083db62016-01-19 10:31:29 +01001067 ERR("Failed to create TLS context.");
Michal Vaskob48aa812016-01-18 14:13:09 +01001068 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001069 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001070 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_tlsclb_verify);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001071 }
1072
Michal Vaskoc61c4492016-01-25 11:13:34 +01001073 cert_store = SSL_CTX_get_cert_store(opts->tls_ctx);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001074 if (!cert_store) {
1075 cert_store = X509_STORE_new();
Michal Vaskoc61c4492016-01-25 11:13:34 +01001076 SSL_CTX_set_cert_store(opts->tls_ctx, cert_store);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001077 }
1078
1079 if (cacert_file_path) {
1080 lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_file());
1081 if (!lookup) {
Michal Vaskod083db62016-01-19 10:31:29 +01001082 ERR("Failed to add a lookup method.");
Michal Vaskob48aa812016-01-18 14:13:09 +01001083 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001084 }
1085
1086 if (X509_LOOKUP_load_file(lookup, cacert_file_path, X509_FILETYPE_PEM) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +01001087 ERR("Failed to add a trusted cert file (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskob48aa812016-01-18 14:13:09 +01001088 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001089 }
1090 }
1091
1092 if (cacert_dir_path) {
1093 lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_hash_dir());
1094 if (!lookup) {
Michal Vaskod083db62016-01-19 10:31:29 +01001095 ERR("Failed to add a lookup method.");
Michal Vaskob48aa812016-01-18 14:13:09 +01001096 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001097 }
1098
1099 if (X509_LOOKUP_add_dir(lookup, cacert_dir_path, X509_FILETYPE_PEM) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +01001100 ERR("Failed to add a trusted cert directory (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskob48aa812016-01-18 14:13:09 +01001101 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001102 }
1103 }
1104
1105 return 0;
Michal Vaskob48aa812016-01-18 14:13:09 +01001106
1107fail:
Michal Vaskob48aa812016-01-18 14:13:09 +01001108 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001109}
1110
Michal Vaskoc61c4492016-01-25 11:13:34 +01001111API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001112nc_server_tls_endpt_set_trusted_cacert_locations(const char *endpt_name, const char *cacert_file_path, const char *cacert_dir_path)
Michal Vasko086311b2016-01-08 09:53:11 +01001113{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001114 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001115 struct nc_endpt *endpt;
1116
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001117 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001118 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001119 return -1;
1120 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001121 ret = nc_server_tls_set_trusted_cacert_locations(cacert_file_path, cacert_dir_path, endpt->ti_opts);
1122 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +01001123
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001124 return ret;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001125}
1126
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001127API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001128nc_server_tls_ch_set_trusted_cacert_locations(const char *cacert_file_path, const char *cacert_dir_path)
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001129{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001130 int ret;
1131
1132 /* OPTS LOCK */
1133 pthread_mutex_lock(&tls_ch_opts_lock);
1134 ret = nc_server_tls_set_trusted_cacert_locations(cacert_file_path, cacert_dir_path, &tls_ch_opts);
1135 /* OPTS UNLOCK */
1136 pthread_mutex_unlock(&tls_ch_opts_lock);
1137
1138 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001139}
1140
1141static void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001142nc_server_tls_clear_certs(struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001143{
Michal Vaskoc61c4492016-01-25 11:13:34 +01001144 if (!opts->tls_ctx) {
Michal Vaskoc61c4492016-01-25 11:13:34 +01001145 return;
1146 }
1147
1148 SSL_CTX_free(opts->tls_ctx);
1149 opts->tls_ctx = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001150}
1151
1152API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001153nc_server_tls_endpt_clear_certs(const char *endpt_name)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001154{
Michal Vasko3031aae2016-01-27 16:07:18 +01001155 struct nc_endpt *endpt;
1156
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001157 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001158 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001159 return;
1160 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001161 nc_server_tls_clear_certs(endpt->ti_opts);
1162 nc_server_endpt_unlock(endpt);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001163}
1164
1165API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001166nc_server_tls_ch_clear_certs(void)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001167{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001168 /* OPTS LOCK */
1169 pthread_mutex_lock(&tls_ch_opts_lock);
1170 nc_server_tls_clear_certs(&tls_ch_opts);
1171 /* OPTS UNLOCK */
1172 pthread_mutex_unlock(&tls_ch_opts_lock);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001173}
1174
1175static int
Michal Vasko3031aae2016-01-27 16:07:18 +01001176nc_server_tls_set_crl_locations(const char *crl_file_path, const char *crl_dir_path, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001177{
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001178 X509_LOOKUP *lookup;
1179
1180 if (!crl_file_path && !crl_dir_path) {
1181 ERRARG;
1182 return -1;
1183 }
1184
Michal Vaskoc61c4492016-01-25 11:13:34 +01001185 if (!opts->crl_store) {
1186 opts->crl_store = X509_STORE_new();
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001187 }
1188
1189 if (crl_file_path) {
Michal Vaskoc61c4492016-01-25 11:13:34 +01001190 lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file());
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001191 if (!lookup) {
Michal Vaskod083db62016-01-19 10:31:29 +01001192 ERR("Failed to add a lookup method.");
Michal Vaskob48aa812016-01-18 14:13:09 +01001193 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001194 }
1195
1196 if (X509_LOOKUP_load_file(lookup, crl_file_path, X509_FILETYPE_PEM) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +01001197 ERR("Failed to add a revocation lookup file (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskob48aa812016-01-18 14:13:09 +01001198 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001199 }
1200 }
1201
1202 if (crl_dir_path) {
Michal Vaskoc61c4492016-01-25 11:13:34 +01001203 lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir());
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001204 if (!lookup) {
Michal Vaskod083db62016-01-19 10:31:29 +01001205 ERR("Failed to add a lookup method.");
Michal Vaskob48aa812016-01-18 14:13:09 +01001206 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001207 }
1208
1209 if (X509_LOOKUP_add_dir(lookup, crl_dir_path, X509_FILETYPE_PEM) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +01001210 ERR("Failed to add a revocation lookup directory (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskob48aa812016-01-18 14:13:09 +01001211 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001212 }
1213 }
1214
1215 return 0;
Michal Vaskob48aa812016-01-18 14:13:09 +01001216
1217fail:
Michal Vaskob48aa812016-01-18 14:13:09 +01001218 return -1;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001219}
1220
Michal Vaskoc61c4492016-01-25 11:13:34 +01001221API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001222nc_server_tls_endpt_set_crl_locations(const char *endpt_name, const char *crl_file_path, const char *crl_dir_path)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001223{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001224 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001225 struct nc_endpt *endpt;
1226
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001227 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001228 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001229 return -1;
1230 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001231 ret = nc_server_tls_set_crl_locations(crl_file_path, crl_dir_path, endpt->ti_opts);
1232 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +01001233
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001234 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001235}
1236
1237API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001238nc_server_tls_ch_set_crl_locations(const char *crl_file_path, const char *crl_dir_path)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001239{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001240 int ret;
1241
1242 /* OPTS LOCK */
1243 pthread_mutex_lock(&tls_ch_opts_lock);
1244 ret = nc_server_tls_set_crl_locations(crl_file_path, crl_dir_path, &tls_ch_opts);
1245 /* OPTS UNLOCK */
1246 pthread_mutex_unlock(&tls_ch_opts_lock);
1247
1248 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001249}
1250
1251static void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001252nc_server_tls_clear_crls(struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001253{
Michal Vaskoc61c4492016-01-25 11:13:34 +01001254 if (!opts->crl_store) {
Michal Vaskoc61c4492016-01-25 11:13:34 +01001255 return;
1256 }
1257
1258 X509_STORE_free(opts->crl_store);
1259 opts->crl_store = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001260}
1261
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001262API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001263nc_server_tls_endpt_clear_crls(const char *endpt_name)
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001264{
Michal Vasko3031aae2016-01-27 16:07:18 +01001265 struct nc_endpt *endpt;
1266
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001267 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001268 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001269 return;
1270 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001271 nc_server_tls_clear_crls(endpt->ti_opts);
1272 nc_server_endpt_unlock(endpt);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001273}
1274
Michal Vaskoc61c4492016-01-25 11:13:34 +01001275API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001276nc_server_tls_ch_clear_crls(void)
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001277{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001278 /* OPTS LOCK */
1279 pthread_mutex_lock(&tls_ch_opts_lock);
1280 nc_server_tls_clear_crls(&tls_ch_opts);
1281 /* OPTS UNLOCK */
1282 pthread_mutex_unlock(&tls_ch_opts_lock);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001283}
1284
1285static int
Michal Vasko3031aae2016-01-27 16:07:18 +01001286nc_server_tls_add_ctn(uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001287{
Michal Vasko5e3f3392016-01-20 11:13:01 +01001288 struct nc_ctn *ctn, *new;
1289
Michal Vasko1a38c862016-01-15 15:50:07 +01001290 if (!fingerprint || !map_type || ((map_type == NC_TLS_CTN_SPECIFIED) && !name)
1291 || ((map_type != NC_TLS_CTN_SPECIFIED) && name)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001292 ERRARG;
1293 return -1;
1294 }
1295
Michal Vasko5e3f3392016-01-20 11:13:01 +01001296 new = malloc(sizeof *new);
1297
1298 nc_ctx_lock(-1, NULL);
1299 new->fingerprint = lydict_insert(server_opts.ctx, fingerprint, 0);
1300 new->name = lydict_insert(server_opts.ctx, name, 0);
1301 nc_ctx_unlock();
1302 new->id = id;
1303 new->map_type = map_type;
1304 new->next = NULL;
1305
Michal Vaskoc61c4492016-01-25 11:13:34 +01001306 if (!opts->ctn) {
Michal Vasko5e3f3392016-01-20 11:13:01 +01001307 /* the first item */
Michal Vaskoc61c4492016-01-25 11:13:34 +01001308 opts->ctn = new;
1309 } else if (opts->ctn->id > id) {
Michal Vasko5e3f3392016-01-20 11:13:01 +01001310 /* insert at the beginning */
Michal Vaskoc61c4492016-01-25 11:13:34 +01001311 new->next = opts->ctn;
1312 opts->ctn = new;
Michal Vasko5e3f3392016-01-20 11:13:01 +01001313 } else {
Michal Vaskoc61c4492016-01-25 11:13:34 +01001314 for (ctn = opts->ctn; ctn->next && ctn->next->id <= id; ctn = ctn->next);
Michal Vasko5e3f3392016-01-20 11:13:01 +01001315 /* insert after ctn */
1316 new->next = ctn->next;
1317 ctn->next = new;
1318 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001319
1320 return 0;
1321}
1322
1323API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001324nc_server_tls_endpt_add_ctn(const char *endpt_name, uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name)
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001325{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001326 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001327 struct nc_endpt *endpt;
1328
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001329 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001330 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001331 return -1;
1332 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001333 ret = nc_server_tls_add_ctn(id, fingerprint, map_type, name, endpt->ti_opts);
1334 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +01001335
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001336 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001337}
1338
1339API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001340nc_server_tls_ch_add_ctn(uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001341{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001342 int ret;
1343
1344 /* OPTS LOCK */
1345 pthread_mutex_lock(&tls_ch_opts_lock);
1346 ret = nc_server_tls_add_ctn(id, fingerprint, map_type, name, &tls_ch_opts);
1347 /* OPTS UNLOCK */
1348 pthread_mutex_unlock(&tls_ch_opts_lock);
1349
1350 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001351}
1352
1353static int
Michal Vasko3031aae2016-01-27 16:07:18 +01001354nc_server_tls_del_ctn(int64_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001355{
Michal Vasko5e3f3392016-01-20 11:13:01 +01001356 struct nc_ctn *ctn, *next, *prev;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001357 int ret = -1;
1358
Michal Vasko1a38c862016-01-15 15:50:07 +01001359 if ((id < 0) && !fingerprint && !map_type && !name) {
Michal Vaskoc61c4492016-01-25 11:13:34 +01001360 ctn = opts->ctn;
Michal Vasko11d142a2016-01-19 15:58:24 +01001361 nc_ctx_lock(-1, NULL);
Michal Vasko5e3f3392016-01-20 11:13:01 +01001362 while (ctn) {
1363 lydict_remove(server_opts.ctx, ctn->fingerprint);
1364 lydict_remove(server_opts.ctx, ctn->name);
1365
1366 next = ctn->next;
1367 free(ctn);
1368 ctn = next;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001369
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001370 ret = 0;
1371 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001372 nc_ctx_unlock();
Michal Vasko3031aae2016-01-27 16:07:18 +01001373 opts->ctn = NULL;
Michal Vasko1a38c862016-01-15 15:50:07 +01001374 } else {
Michal Vasko5e3f3392016-01-20 11:13:01 +01001375 prev = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001376 ctn = opts->ctn;
Michal Vasko5e3f3392016-01-20 11:13:01 +01001377 while (ctn) {
1378 if (((id < 0) || (ctn->id == id))
1379 && (!fingerprint || !strcmp(ctn->fingerprint, fingerprint))
1380 && (!map_type || (ctn->map_type == map_type))
1381 && (!name || (ctn->name && !strcmp(ctn->name, name)))) {
Michal Vasko11d142a2016-01-19 15:58:24 +01001382 nc_ctx_lock(-1, NULL);
Michal Vasko5e3f3392016-01-20 11:13:01 +01001383 lydict_remove(server_opts.ctx, ctn->fingerprint);
1384 lydict_remove(server_opts.ctx, ctn->name);
Michal Vasko11d142a2016-01-19 15:58:24 +01001385 nc_ctx_unlock();
Michal Vasko1a38c862016-01-15 15:50:07 +01001386
Michal Vasko5e3f3392016-01-20 11:13:01 +01001387 if (prev) {
1388 prev->next = ctn->next;
1389 next = ctn->next;
1390 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +01001391 opts->ctn = ctn->next;
Michal Vasko5e3f3392016-01-20 11:13:01 +01001392 next = ctn->next;
1393 }
1394 free(ctn);
1395 ctn = next;
Michal Vasko1a38c862016-01-15 15:50:07 +01001396
1397 ret = 0;
Michal Vasko5e3f3392016-01-20 11:13:01 +01001398 } else {
1399 prev = ctn;
1400 ctn = ctn->next;
Michal Vasko1a38c862016-01-15 15:50:07 +01001401 }
1402 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001403 }
1404
1405 return ret;
1406}
1407
Michal Vaskoc61c4492016-01-25 11:13:34 +01001408API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001409nc_server_tls_endpt_del_ctn(const char *endpt_name, int64_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001410{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001411 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001412 struct nc_endpt *endpt;
1413
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001414 endpt = nc_server_endpt_lock(endpt_name, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001415 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001416 return -1;
1417 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001418 ret = nc_server_tls_del_ctn(id, fingerprint, map_type, name, endpt->ti_opts);
1419 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +01001420
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001421 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001422}
1423
1424API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001425nc_server_tls_ch_del_ctn(int64_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001426{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001427 int ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001428
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001429 /* OPTS LOCK */
1430 pthread_mutex_lock(&tls_ch_opts_lock);
1431 ret = nc_server_tls_del_ctn(id, fingerprint, map_type, name, &tls_ch_opts);
1432 /* OPTS UNLOCK */
1433 pthread_mutex_unlock(&tls_ch_opts_lock);
1434
1435 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001436}
Michal Vaskoc61c4492016-01-25 11:13:34 +01001437
Michal Vasko3031aae2016-01-27 16:07:18 +01001438void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001439nc_server_tls_clear_opts(struct nc_server_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +01001440{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001441 nc_server_tls_clear_certs(opts);
1442 nc_server_tls_clear_crls(opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001443 nc_server_tls_del_ctn(-1, NULL, 0, NULL, opts);
Michal Vasko086311b2016-01-08 09:53:11 +01001444}
Michal Vasko9e036d52016-01-08 10:49:26 +01001445
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001446API void
1447nc_server_tls_ch_clear_opts(void)
1448{
1449 /* OPTS LOCK */
1450 pthread_mutex_lock(&tls_ch_opts_lock);
1451 nc_server_tls_clear_opts(&tls_ch_opts);
1452 /* OPTS UNLOCK */
1453 pthread_mutex_unlock(&tls_ch_opts_lock);
1454}
1455
Michal Vasko6d292992016-01-18 09:42:38 +01001456static void
1457nc_tls_make_verify_key(void)
1458{
Michal Vasko5c2f7952016-01-22 13:16:31 +01001459 pthread_key_create(&verify_key, NULL);
Michal Vasko6d292992016-01-18 09:42:38 +01001460}
1461
Michal Vasko3031aae2016-01-27 16:07:18 +01001462API int
1463nc_connect_callhome_tls(const char *host, uint16_t port, int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001464{
Michal Vasko3031aae2016-01-27 16:07:18 +01001465 return nc_connect_callhome(host, port, NC_TI_OPENSSL, timeout, session);
1466}
1467
1468int
1469nc_accept_tls_session(struct nc_session *session, int sock, int timeout)
1470{
1471 struct nc_server_tls_opts *opts;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001472 struct pollfd pfd;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001473 struct timespec old_ts, new_ts;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001474 int ret, elapsed = 0;
1475
Michal Vasko3031aae2016-01-27 16:07:18 +01001476 opts = session->ti_opts;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001477
1478 pfd.fd = sock;
1479 pfd.events = POLLIN;
1480 pfd.revents = 0;
1481
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001482 if (timeout > 0) {
1483 clock_gettime(CLOCK_MONOTONIC_RAW, &old_ts);
1484 }
1485
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001486 /* poll for a new connection */
1487 errno = 0;
1488 ret = poll(&pfd, 1, timeout);
1489 if (!ret) {
1490 /* we timeouted */
1491 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +01001492 return 0;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001493 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +01001494 ERR("poll failed (%s).", strerror(errno));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001495 close(sock);
1496 return -1;
1497 }
1498
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001499 if (timeout > 0) {
1500 /* decrease timeout */
1501 clock_gettime(CLOCK_MONOTONIC_RAW, &new_ts);
1502
1503 elapsed = (new_ts.tv_sec - old_ts.tv_sec) * 1000;
1504 elapsed += (new_ts.tv_nsec - old_ts.tv_nsec) / 1000000;
1505 }
1506
Michal Vaskob48aa812016-01-18 14:13:09 +01001507 /* data waiting, prepare session */
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001508 session->ti_type = NC_TI_OPENSSL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001509
Michal Vaskoc61c4492016-01-25 11:13:34 +01001510 session->ti.tls = SSL_new(opts->tls_ctx);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001511
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001512 if (!session->ti.tls) {
Michal Vaskod083db62016-01-19 10:31:29 +01001513 ERR("Failed to create TLS structure from context.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001514 close(sock);
1515 return -1;
1516 }
1517
1518 SSL_set_fd(session->ti.tls, sock);
1519 SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
1520
Michal Vasko6d292992016-01-18 09:42:38 +01001521 /* store session on per-thread basis */
Michal Vasko5c2f7952016-01-22 13:16:31 +01001522 pthread_once(&verify_once, nc_tls_make_verify_key);
1523 pthread_setspecific(verify_key, session);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001524
1525 ret = SSL_accept(session->ti.tls);
Michal Vaskob48aa812016-01-18 14:13:09 +01001526
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001527 if (ret != 1) {
1528 switch (SSL_get_error(session->ti.tls, ret)) {
1529 case SSL_ERROR_SYSCALL:
Michal Vaskod083db62016-01-19 10:31:29 +01001530 ERR("SSL_accept failed (%s).", strerror(errno));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001531 break;
1532 case SSL_ERROR_SSL:
Michal Vaskod083db62016-01-19 10:31:29 +01001533 ERR("SSL_accept failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001534 break;
1535 default:
Michal Vaskod083db62016-01-19 10:31:29 +01001536 ERR("SSL_accept failed.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001537 break;
1538 }
1539 return -1;
1540 }
1541
Michal Vasko1a38c862016-01-15 15:50:07 +01001542 return 1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001543}