blob: 70ac35dae470635f11e27a34e0dbf9a3a37dad97 [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 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci5da708a2015-09-01 17:33:23 +020013 */
14
Michal Vaskoc14e3c82016-01-11 16:14:30 +010015#define _GNU_SOURCE
16
17#include <string.h>
18#include <poll.h>
Michal Vaskof0537d82016-01-29 14:42:38 +010019#include <unistd.h>
Michal Vaskoc14e3c82016-01-11 16:14:30 +010020
21#include <openssl/ssl.h>
22#include <openssl/evp.h>
23#include <openssl/err.h>
24#include <openssl/x509v3.h>
Michal Vaskoe2713da2016-08-22 16:06:40 +020025#include <openssl/x509.h>
Michal Vaskoc14e3c82016-01-11 16:14:30 +010026
Michal Vasko11d142a2016-01-19 15:58:24 +010027#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010028#include "session_server_ch.h"
29#include "libnetconf.h"
Radek Krejci5da708a2015-09-01 17:33:23 +020030
Michal Vasko3031aae2016-01-27 16:07:18 +010031struct nc_server_tls_opts tls_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010032pthread_mutex_t tls_ch_opts_lock = PTHREAD_MUTEX_INITIALIZER;
Michal Vaskoc14e3c82016-01-11 16:14:30 +010033extern struct nc_server_opts server_opts;
Michal Vaskoc61c4492016-01-25 11:13:34 +010034
Michal Vasko5c2f7952016-01-22 13:16:31 +010035static pthread_key_t verify_key;
36static pthread_once_t verify_once = PTHREAD_ONCE_INIT;
37
Michal Vaskoc14e3c82016-01-11 16:14:30 +010038static char *
Michal Vasko18aeb5d2017-02-17 09:23:56 +010039asn1time_to_str(const ASN1_TIME *t)
Michal Vasko086311b2016-01-08 09:53:11 +010040{
Michal Vaskoc14e3c82016-01-11 16:14:30 +010041 char *cp;
42 BIO *bio;
43 int n;
Radek Krejci5da708a2015-09-01 17:33:23 +020044
Michal Vaskoc14e3c82016-01-11 16:14:30 +010045 if (!t) {
46 return NULL;
47 }
48 bio = BIO_new(BIO_s_mem());
49 if (!bio) {
50 return NULL;
51 }
52 ASN1_TIME_print(bio, t);
53 n = BIO_pending(bio);
54 cp = malloc(n + 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +010055 if (!cp) {
56 ERRMEM;
57 BIO_free(bio);
58 return NULL;
59 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +010060 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);
Michal Vasko4eb3c312016-03-01 14:09:37 +010077 if (!*str) {
78 ERRMEM;
79 return;
80 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +010081 for (i = 0; i < dig_len - 1; ++i) {
82 sprintf((*str) + (i * 3), "%02x:", digest[i]);
83 }
84 sprintf((*str) + (i * 3), "%02x", digest[i]);
85}
86
87/* return NULL - SSL error can be retrieved */
88static X509 *
89base64der_to_cert(const char *in)
90{
91 X509 *out;
92 char *buf;
93 BIO *bio;
94
95 if (in == NULL) {
96 return NULL;
97 }
98
99 if (asprintf(&buf, "%s%s%s", "-----BEGIN CERTIFICATE-----\n", in, "\n-----END CERTIFICATE-----") == -1) {
100 return NULL;
101 }
102 bio = BIO_new_mem_buf(buf, strlen(buf));
103 if (!bio) {
104 free(buf);
105 return NULL;
106 }
107
108 out = PEM_read_bio_X509(bio, NULL, NULL, NULL);
109 if (!out) {
110 free(buf);
111 BIO_free(bio);
112 return NULL;
113 }
114
115 free(buf);
116 BIO_free(bio);
117 return out;
118}
119
120/* return NULL - either errno or SSL error */
121static X509 *
122pem_to_cert(const char *path)
123{
124 FILE *fp;
125 X509 *out;
126
127 fp = fopen(path, "r");
128 if (!fp) {
129 return NULL;
130 }
131
132 out = PEM_read_X509(fp, NULL, NULL, NULL);
133 fclose(fp);
134 return out;
135}
136
Michal Vasko6e08cb72017-02-02 11:15:29 +0100137static EVP_PKEY *
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100138base64der_to_privatekey(const char *in, int rsa)
139{
140 EVP_PKEY *out;
141 char *buf;
142 BIO *bio;
143
144 if (in == NULL) {
145 return NULL;
146 }
147
148 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) {
149 return NULL;
150 }
151 bio = BIO_new_mem_buf(buf, strlen(buf));
152 if (!bio) {
153 free(buf);
154 return NULL;
155 }
156
157 out = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
158 if (!out) {
159 free(buf);
160 BIO_free(bio);
161 return NULL;
162 }
163
164 free(buf);
165 BIO_free(bio);
166 return out;
Michal Vasko6e08cb72017-02-02 11:15:29 +0100167}
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100168
169static int
170cert_pubkey_match(X509 *cert1, X509 *cert2)
171{
172 ASN1_BIT_STRING *bitstr1, *bitstr2;
173
174 bitstr1 = X509_get0_pubkey_bitstr(cert1);
175 bitstr2 = X509_get0_pubkey_bitstr(cert2);
176
177 if (!bitstr1 || !bitstr2 || (bitstr1->length != bitstr2->length) ||
178 memcmp(bitstr1->data, bitstr2->data, bitstr1->length)) {
179 return 0;
180 }
181
182 return 1;
183}
184
185static int
186nc_tls_ctn_get_username_from_cert(X509 *client_cert, NC_TLS_CTN_MAPTYPE map_type, char **username)
187{
188 STACK_OF(GENERAL_NAME) *san_names;
189 GENERAL_NAME *san_name;
190 ASN1_OCTET_STRING *ip;
191 int i, san_count;
192 char *subject, *common_name;
193
194 if (map_type == NC_TLS_CTN_COMMON_NAME) {
195 subject = X509_NAME_oneline(X509_get_subject_name(client_cert), NULL, 0);
196 common_name = strstr(subject, "CN=");
197 if (!common_name) {
Michal Vaskod083db62016-01-19 10:31:29 +0100198 WRN("Certificate does not include the commonName field.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100199 free(subject);
200 return 1;
201 }
202 common_name += 3;
203 if (strchr(common_name, '/')) {
204 *strchr(common_name, '/') = '\0';
205 }
206 *username = strdup(common_name);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100207 if (!*username) {
208 ERRMEM;
209 return 1;
210 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100211 free(subject);
212 } else {
213 /* retrieve subjectAltName's rfc822Name (email), dNSName and iPAddress values */
214 san_names = X509_get_ext_d2i(client_cert, NID_subject_alt_name, NULL, NULL);
215 if (!san_names) {
Michal Vaskod083db62016-01-19 10:31:29 +0100216 WRN("Certificate has no SANs or failed to retrieve them.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100217 return 1;
218 }
219
220 san_count = sk_GENERAL_NAME_num(san_names);
221 for (i = 0; i < san_count; ++i) {
222 san_name = sk_GENERAL_NAME_value(san_names, i);
223
224 /* rfc822Name (email) */
225 if ((map_type == NC_TLS_CTN_SAN_ANY || map_type == NC_TLS_CTN_SAN_RFC822_NAME) &&
226 san_name->type == GEN_EMAIL) {
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100227#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100228 *username = strdup((char *)ASN1_STRING_data(san_name->d.rfc822Name));
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100229#else
230 *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.rfc822Name));
231#endif
Michal Vasko4eb3c312016-03-01 14:09:37 +0100232 if (!*username) {
233 ERRMEM;
234 return 1;
235 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100236 break;
237 }
238
239 /* dNSName */
240 if ((map_type == NC_TLS_CTN_SAN_ANY || map_type == NC_TLS_CTN_SAN_DNS_NAME) &&
241 san_name->type == GEN_DNS) {
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100242#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100243 *username = strdup((char *)ASN1_STRING_data(san_name->d.dNSName));
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100244#else
245 *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.dNSName));
246#endif
Michal Vasko4eb3c312016-03-01 14:09:37 +0100247 if (!*username) {
248 ERRMEM;
249 return 1;
250 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100251 break;
252 }
253
254 /* iPAddress */
255 if ((map_type == NC_TLS_CTN_SAN_ANY || map_type == NC_TLS_CTN_SAN_IP_ADDRESS) &&
256 san_name->type == GEN_IPADD) {
257 ip = san_name->d.iPAddress;
258 if (ip->length == 4) {
259 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 +0100260 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100261 sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
262 return -1;
263 }
264 break;
265 } else if (ip->length == 16) {
266 if (asprintf(username, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
267 ip->data[0], ip->data[1], ip->data[2], ip->data[3], ip->data[4], ip->data[5],
268 ip->data[6], ip->data[7], ip->data[8], ip->data[9], ip->data[10], ip->data[11],
269 ip->data[12], ip->data[13], ip->data[14], ip->data[15]) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100270 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100271 sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
272 return -1;
273 }
274 break;
275 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100276 WRN("SAN IP address in an unknown format (length is %d).", ip->length);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100277 }
278 }
279 }
280 sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
281
282 if (i < san_count) {
283 switch (map_type) {
284 case NC_TLS_CTN_SAN_RFC822_NAME:
Michal Vaskod083db62016-01-19 10:31:29 +0100285 WRN("Certificate does not include the SAN rfc822Name field.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100286 break;
287 case NC_TLS_CTN_SAN_DNS_NAME:
Michal Vaskod083db62016-01-19 10:31:29 +0100288 WRN("Certificate does not include the SAN dNSName field.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100289 break;
290 case NC_TLS_CTN_SAN_IP_ADDRESS:
Michal Vaskod083db62016-01-19 10:31:29 +0100291 WRN("Certificate does not include the SAN iPAddress field.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100292 break;
293 case NC_TLS_CTN_SAN_ANY:
Michal Vaskod083db62016-01-19 10:31:29 +0100294 WRN("Certificate does not include any relevant SAN fields.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100295 break;
296 default:
297 break;
298 }
299 return 1;
300 }
301 }
302
303 return 0;
304}
305
306/* return: 0 - OK, 1 - no match, -1 - error */
307static int
Michal Vaskoc61c4492016-01-25 11:13:34 +0100308nc_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 +0100309{
310 char *digest_md5 = NULL, *digest_sha1 = NULL, *digest_sha224 = NULL;
311 char *digest_sha256 = NULL, *digest_sha384 = NULL, *digest_sha512 = NULL;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100312 unsigned char *buf = malloc(64);
313 unsigned int buf_len = 64;
314 int ret = 0;
Michal Vasko5e3f3392016-01-20 11:13:01 +0100315 struct nc_ctn *ctn;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100316
Michal Vasko4eb3c312016-03-01 14:09:37 +0100317 if (!buf) {
318 ERRMEM;
319 return -1;
320 }
321
Michal Vaskoc61c4492016-01-25 11:13:34 +0100322 if (!ctn_first || !cert || !map_type || !name) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100323 free(buf);
324 return -1;
325 }
326
Michal Vaskoc61c4492016-01-25 11:13:34 +0100327 for (ctn = ctn_first; ctn; ctn = ctn->next) {
Michal Vasko3cf4aaa2017-02-01 15:03:36 +0100328 /* first make sure the entry is valid */
329 if (!ctn->fingerprint || !ctn->map_type || ((ctn->map_type == NC_TLS_CTN_SPECIFIED) && !ctn->name)) {
330 VRB("Cert verify CTN: entry with id %u not valid, skipping.", ctn->id);
331 continue;
332 }
333
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100334 /* MD5 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100335 if (!strncmp(ctn->fingerprint, "01", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100336 if (!digest_md5) {
337 if (X509_digest(cert, EVP_md5(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100338 ERR("Calculating MD5 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100339 ret = -1;
340 goto cleanup;
341 }
342 digest_to_str(buf, buf_len, &digest_md5);
343 }
344
Michal Vasko5e3f3392016-01-20 11:13:01 +0100345 if (!strcasecmp(ctn->fingerprint + 3, digest_md5)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100346 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100347 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100348 *map_type = ctn->map_type;
349 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
350 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100351 }
352 break;
353 }
354
355 /* SHA-1 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100356 } else if (!strncmp(ctn->fingerprint, "02", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100357 if (!digest_sha1) {
358 if (X509_digest(cert, EVP_sha1(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100359 ERR("Calculating SHA-1 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100360 ret = -1;
361 goto cleanup;
362 }
363 digest_to_str(buf, buf_len, &digest_sha1);
364 }
365
Michal Vasko5e3f3392016-01-20 11:13:01 +0100366 if (!strcasecmp(ctn->fingerprint + 3, digest_sha1)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100367 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100368 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100369 *map_type = ctn->map_type;
370 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
371 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100372 }
373 break;
374 }
375
376 /* SHA-224 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100377 } else if (!strncmp(ctn->fingerprint, "03", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100378 if (!digest_sha224) {
379 if (X509_digest(cert, EVP_sha224(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100380 ERR("Calculating SHA-224 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100381 ret = -1;
382 goto cleanup;
383 }
384 digest_to_str(buf, buf_len, &digest_sha224);
385 }
386
Michal Vasko5e3f3392016-01-20 11:13:01 +0100387 if (!strcasecmp(ctn->fingerprint + 3, digest_sha224)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100388 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100389 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100390 *map_type = ctn->map_type;
391 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
392 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100393 }
394 break;
395 }
396
397 /* SHA-256 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100398 } else if (!strncmp(ctn->fingerprint, "04", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100399 if (!digest_sha256) {
400 if (X509_digest(cert, EVP_sha256(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100401 ERR("Calculating SHA-256 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100402 ret = -1;
403 goto cleanup;
404 }
405 digest_to_str(buf, buf_len, &digest_sha256);
406 }
407
Michal Vasko5e3f3392016-01-20 11:13:01 +0100408 if (!strcasecmp(ctn->fingerprint + 3, digest_sha256)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100409 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100410 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100411 *map_type = ctn->map_type;
412 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
413 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100414 }
415 break;
416 }
417
418 /* SHA-384 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100419 } else if (!strncmp(ctn->fingerprint, "05", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100420 if (!digest_sha384) {
421 if (X509_digest(cert, EVP_sha384(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100422 ERR("Calculating SHA-384 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100423 ret = -1;
424 goto cleanup;
425 }
426 digest_to_str(buf, buf_len, &digest_sha384);
427 }
428
Michal Vasko5e3f3392016-01-20 11:13:01 +0100429 if (!strcasecmp(ctn->fingerprint + 3, digest_sha384)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100430 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100431 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100432 *map_type = ctn->map_type;
433 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
434 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100435 }
436 break;
437 }
438
439 /* SHA-512 */
Michal Vasko5e3f3392016-01-20 11:13:01 +0100440 } else if (!strncmp(ctn->fingerprint, "06", 2)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100441 if (!digest_sha512) {
442 if (X509_digest(cert, EVP_sha512(), buf, &buf_len) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100443 ERR("Calculating SHA-512 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100444 ret = -1;
445 goto cleanup;
446 }
447 digest_to_str(buf, buf_len, &digest_sha512);
448 }
449
Michal Vasko5e3f3392016-01-20 11:13:01 +0100450 if (!strcasecmp(ctn->fingerprint + 3, digest_sha512)) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100451 /* we got ourselves a winner! */
Michal Vaskod083db62016-01-19 10:31:29 +0100452 VRB("Cert verify CTN: entry with a matching fingerprint found.");
Michal Vasko5e3f3392016-01-20 11:13:01 +0100453 *map_type = ctn->map_type;
454 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
455 *name = ctn->name;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100456 }
457 break;
458 }
459
460 /* unknown */
461 } else {
Michal Vasko5e3f3392016-01-20 11:13:01 +0100462 WRN("Unknown fingerprint algorithm used (%s), skipping.", ctn->fingerprint);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100463 }
464 }
465
Michal Vasko5e3f3392016-01-20 11:13:01 +0100466 if (!ctn) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100467 ret = 1;
468 }
469
470cleanup:
471 free(digest_md5);
472 free(digest_sha1);
473 free(digest_sha224);
474 free(digest_sha256);
475 free(digest_sha384);
476 free(digest_sha512);
477 free(buf);
478 return ret;
479}
480
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100481#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
482
483static int
484nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
485{
486 X509_STORE_CTX *store_ctx;
487 X509_OBJECT *obj;
488 X509_NAME *subject;
489 X509_NAME *issuer;
490 X509 *cert;
491 X509_CRL *crl;
492 X509_REVOKED *revoked;
493 STACK_OF(X509) *cert_stack;
494 EVP_PKEY *pubkey;
495 struct nc_session* session;
496 struct nc_server_tls_opts *opts;
497 const ASN1_INTEGER *serial;
498 int i, n, rc, depth;
499 char *cp;
500 const char *username = NULL;
501 NC_TLS_CTN_MAPTYPE map_type = 0;
502 const ASN1_TIME *last_update = NULL, *next_update = NULL;
503
504 /* get the thread session */
505 session = pthread_getspecific(verify_key);
506 if (!session) {
507 ERRINT;
508 return 0;
509 }
510
511 opts = session->data;
512
513 /* get the last certificate, that is the peer (client) certificate */
514 if (!session->opts.server.client_cert) {
515 cert_stack = X509_STORE_CTX_get1_chain(x509_ctx);
516 session->opts.server.client_cert = sk_X509_value(cert_stack, sk_X509_num(cert_stack) - 1);
517 X509_up_ref(session->opts.server.client_cert);
518 sk_X509_pop_free(cert_stack, X509_free);
519 }
520
521 /* standard certificate verification failed, so a trusted client cert must match to continue */
522 if (!preverify_ok) {
523 subject = X509_get_subject_name(session->opts.server.client_cert);
524 cert_stack = X509_STORE_CTX_get1_certs(x509_ctx, subject);
525 if (cert_stack) {
526 for (i = 0; i < sk_X509_num(cert_stack); ++i) {
527 if (cert_pubkey_match(session->opts.server.client_cert, sk_X509_value(cert_stack, i))) {
528 /* we are just overriding the failed standard certificate verification (preverify_ok == 0),
529 * this callback will be called again with the same current certificate and preverify_ok == 1 */
530 VRB("Cert verify: fail (%s), but the client certificate is trusted, continuing.",
531 X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
532 X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
533 sk_X509_pop_free(cert_stack, X509_free);
534 return 1;
535 }
536 }
537 sk_X509_pop_free(cert_stack, X509_free);
538 }
539
540 ERR("Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
541 return 0;
542 }
543
544 /* print cert verify info */
545 depth = X509_STORE_CTX_get_error_depth(x509_ctx);
546 VRB("Cert verify: depth %d.", depth);
547
548 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
549 subject = X509_get_subject_name(cert);
550 issuer = X509_get_issuer_name(cert);
551
552 cp = X509_NAME_oneline(subject, NULL, 0);
553 VRB("Cert verify: subject: %s.", cp);
554 OPENSSL_free(cp);
555 cp = X509_NAME_oneline(issuer, NULL, 0);
556 VRB("Cert verify: issuer: %s.", cp);
557 OPENSSL_free(cp);
558
559 /* check for revocation if set */
560 if (opts->crl_store) {
561 /* try to retrieve a CRL corresponding to the _subject_ of
562 * the current certificate in order to verify it's integrity */
563 store_ctx = X509_STORE_CTX_new();
564 obj = X509_OBJECT_new();
565 X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
566 rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, subject, obj);
567 X509_STORE_CTX_free(store_ctx);
568 crl = X509_OBJECT_get0_X509_CRL(obj);
569 if (rc > 0 && crl) {
570 cp = X509_NAME_oneline(subject, NULL, 0);
571 VRB("Cert verify CRL: issuer: %s.", cp);
572 OPENSSL_free(cp);
573
574 last_update = X509_CRL_get0_lastUpdate(crl);
575 next_update = X509_CRL_get0_nextUpdate(crl);
576 cp = asn1time_to_str(last_update);
577 VRB("Cert verify CRL: last update: %s.", cp);
578 free(cp);
579 cp = asn1time_to_str(next_update);
580 VRB("Cert verify CRL: next update: %s.", cp);
581 free(cp);
582
583 /* verify the signature on this CRL */
584 pubkey = X509_get_pubkey(cert);
585 if (X509_CRL_verify(crl, pubkey) <= 0) {
586 ERR("Cert verify CRL: invalid signature.");
587 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
588 X509_OBJECT_free(obj);
589 if (pubkey) {
590 EVP_PKEY_free(pubkey);
591 }
592 return 0;
593 }
594 if (pubkey) {
595 EVP_PKEY_free(pubkey);
596 }
597
598 /* check date of CRL to make sure it's not expired */
599 if (!next_update) {
600 ERR("Cert verify CRL: invalid nextUpdate field.");
601 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
602 X509_OBJECT_free(obj);
603 return 0;
604 }
605 if (X509_cmp_current_time(next_update) < 0) {
606 ERR("Cert verify CRL: expired - revoking all certificates.");
607 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
608 X509_OBJECT_free(obj);
609 return 0;
610 }
611 X509_OBJECT_free(obj);
612 }
613
614 /* try to retrieve a CRL corresponding to the _issuer_ of
615 * the current certificate in order to check for revocation */
616 store_ctx = X509_STORE_CTX_new();
617 obj = X509_OBJECT_new();
618 X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
619 rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj);
620 X509_STORE_CTX_free(store_ctx);
621 crl = X509_OBJECT_get0_X509_CRL(obj);
622 if (rc > 0 && crl) {
623 /* check if the current certificate is revoked by this CRL */
624 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
625 for (i = 0; i < n; i++) {
626 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
627 serial = X509_REVOKED_get0_serialNumber(revoked);
628 if (ASN1_INTEGER_cmp(serial, X509_get_serialNumber(cert)) == 0) {
629 cp = X509_NAME_oneline(issuer, NULL, 0);
630 ERR("Cert verify CRL: certificate with serial %ld (0x%lX) revoked per CRL from issuer %s.", serial, serial, cp);
631 OPENSSL_free(cp);
632 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
633 X509_OBJECT_free(obj);
634 return 0;
635 }
636 }
637 X509_OBJECT_free(obj);
638 }
639 }
640
641 /* cert-to-name already successful */
642 if (session->username) {
643 return 1;
644 }
645
646 /* cert-to-name */
647 rc = nc_tls_cert_to_name(opts->ctn, cert, &map_type, &username);
648
649 if (rc) {
650 if (rc == -1) {
651 /* fatal error */
652 depth = 0;
653 }
654 /* rc == 1 is a normal CTN fail (no match found) */
655 goto fail;
656 }
657
658 /* cert-to-name match, now to extract the specific field from the peer cert */
659 if (map_type == NC_TLS_CTN_SPECIFIED) {
660 session->username = lydict_insert(server_opts.ctx, username, 0);
661 } else {
662 rc = nc_tls_ctn_get_username_from_cert(session->opts.server.client_cert, map_type, &cp);
663 if (rc) {
664 if (rc == -1) {
665 depth = 0;
666 }
667 goto fail;
668 }
669 session->username = lydict_insert_zc(server_opts.ctx, cp);
670 }
671
672 VRB("Cert verify CTN: new client username recognized as \"%s\".", session->username);
673
674 if (server_opts.user_verify_clb && !server_opts.user_verify_clb(session)) {
675 VRB("Cert verify: user verify callback revoked authorization.");
676 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
677 return 0;
678 }
679
680 return 1;
681
682fail:
683 if (depth > 0) {
684 VRB("Cert verify CTN: cert fail, cert-to-name will continue on the next cert in chain.");
685 return 1;
686 }
687
688 VRB("Cert-to-name unsuccessful, dropping the new client.");
689 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
690 return 0;
691}
692
693#else
694
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100695static int
696nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
697{
698 X509_STORE_CTX store_ctx;
699 X509_OBJECT obj;
700 X509_NAME *subject;
701 X509_NAME *issuer;
702 X509 *cert;
703 X509_CRL *crl;
704 X509_REVOKED *revoked;
705 STACK_OF(X509) *cert_stack;
706 EVP_PKEY *pubkey;
707 struct nc_session* session;
Michal Vasko3031aae2016-01-27 16:07:18 +0100708 struct nc_server_tls_opts *opts;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100709 long serial;
710 int i, n, rc, depth;
711 char *cp;
712 const char *username = NULL;
713 NC_TLS_CTN_MAPTYPE map_type = 0;
714 ASN1_TIME *last_update = NULL, *next_update = NULL;
715
Michal Vasko6d292992016-01-18 09:42:38 +0100716 /* get the thread session */
Michal Vasko5c2f7952016-01-22 13:16:31 +0100717 session = pthread_getspecific(verify_key);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100718 if (!session) {
719 ERRINT;
720 return 0;
721 }
722
Michal Vasko2cc4c682016-03-01 09:16:48 +0100723 opts = session->data;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100724
725 /* get the last certificate, that is the peer (client) certificate */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200726 if (!session->opts.server.client_cert) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100727 cert_stack = X509_STORE_CTX_get1_chain(x509_ctx);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100728 while ((cert = sk_X509_pop(cert_stack))) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200729 X509_free(session->opts.server.client_cert);
730 session->opts.server.client_cert = cert;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100731 }
732 sk_X509_pop_free(cert_stack, X509_free);
733 }
734
735 /* standard certificate verification failed, so a trusted client cert must match to continue */
736 if (!preverify_ok) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200737 subject = X509_get_subject_name(session->opts.server.client_cert);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100738 cert_stack = X509_STORE_get1_certs(x509_ctx, subject);
739 if (cert_stack) {
740 for (i = 0; i < sk_X509_num(cert_stack); ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200741 if (cert_pubkey_match(session->opts.server.client_cert, sk_X509_value(cert_stack, i))) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100742 /* we are just overriding the failed standard certificate verification (preverify_ok == 0),
743 * this callback will be called again with the same current certificate and preverify_ok == 1 */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100744 VRB("Cert verify: fail (%s), but the client certificate is trusted, continuing.",
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100745 X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
746 X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
747 sk_X509_pop_free(cert_stack, X509_free);
748 return 1;
749 }
750 }
751 sk_X509_pop_free(cert_stack, X509_free);
752 }
753
754 ERR("Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
755 return 0;
756 }
757
758 /* print cert verify info */
759 depth = X509_STORE_CTX_get_error_depth(x509_ctx);
Michal Vaskod083db62016-01-19 10:31:29 +0100760 VRB("Cert verify: depth %d.", depth);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100761
762 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
763 subject = X509_get_subject_name(cert);
764 issuer = X509_get_issuer_name(cert);
765
766 cp = X509_NAME_oneline(subject, NULL, 0);
Michal Vaskod083db62016-01-19 10:31:29 +0100767 VRB("Cert verify: subject: %s.", cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100768 OPENSSL_free(cp);
769 cp = X509_NAME_oneline(issuer, NULL, 0);
Michal Vaskod083db62016-01-19 10:31:29 +0100770 VRB("Cert verify: issuer: %s.", cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100771 OPENSSL_free(cp);
772
773 /* check for revocation if set */
Michal Vaskoc61c4492016-01-25 11:13:34 +0100774 if (opts->crl_store) {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100775 /* try to retrieve a CRL corresponding to the _subject_ of
776 * the current certificate in order to verify it's integrity */
777 memset((char *)&obj, 0, sizeof(obj));
Michal Vaskoc61c4492016-01-25 11:13:34 +0100778 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100779 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
780 X509_STORE_CTX_cleanup(&store_ctx);
781 crl = obj.data.crl;
782 if (rc > 0 && crl) {
783 cp = X509_NAME_oneline(subject, NULL, 0);
Michal Vaskod083db62016-01-19 10:31:29 +0100784 VRB("Cert verify CRL: issuer: %s.", cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100785 OPENSSL_free(cp);
786
787 last_update = X509_CRL_get_lastUpdate(crl);
788 next_update = X509_CRL_get_nextUpdate(crl);
789 cp = asn1time_to_str(last_update);
Michal Vaskod083db62016-01-19 10:31:29 +0100790 VRB("Cert verify CRL: last update: %s.", cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100791 free(cp);
792 cp = asn1time_to_str(next_update);
Michal Vaskod083db62016-01-19 10:31:29 +0100793 VRB("Cert verify CRL: next update: %s.", cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100794 free(cp);
795
796 /* verify the signature on this CRL */
797 pubkey = X509_get_pubkey(cert);
798 if (X509_CRL_verify(crl, pubkey) <= 0) {
799 ERR("Cert verify CRL: invalid signature.");
800 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
801 X509_OBJECT_free_contents(&obj);
802 if (pubkey) {
803 EVP_PKEY_free(pubkey);
804 }
805 return 0;
806 }
807 if (pubkey) {
808 EVP_PKEY_free(pubkey);
809 }
810
811 /* check date of CRL to make sure it's not expired */
812 if (!next_update) {
813 ERR("Cert verify CRL: invalid nextUpdate field.");
814 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
815 X509_OBJECT_free_contents(&obj);
816 return 0;
817 }
818 if (X509_cmp_current_time(next_update) < 0) {
819 ERR("Cert verify CRL: expired - revoking all certificates.");
820 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
821 X509_OBJECT_free_contents(&obj);
822 return 0;
823 }
824 X509_OBJECT_free_contents(&obj);
825 }
826
827 /* try to retrieve a CRL corresponding to the _issuer_ of
Michal Vaskob48aa812016-01-18 14:13:09 +0100828 * the current certificate in order to check for revocation */
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100829 memset((char *)&obj, 0, sizeof(obj));
Michal Vaskoc61c4492016-01-25 11:13:34 +0100830 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100831 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
832 X509_STORE_CTX_cleanup(&store_ctx);
833 crl = obj.data.crl;
834 if (rc > 0 && crl) {
835 /* check if the current certificate is revoked by this CRL */
836 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
837 for (i = 0; i < n; i++) {
838 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
839 if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
840 serial = ASN1_INTEGER_get(revoked->serialNumber);
841 cp = X509_NAME_oneline(issuer, NULL, 0);
Michal Vaskod083db62016-01-19 10:31:29 +0100842 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 +0100843 OPENSSL_free(cp);
844 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
845 X509_OBJECT_free_contents(&obj);
846 return 0;
847 }
848 }
849 X509_OBJECT_free_contents(&obj);
850 }
851 }
852
853 /* cert-to-name already successful */
854 if (session->username) {
855 return 1;
856 }
857
858 /* cert-to-name */
Michal Vaskoc61c4492016-01-25 11:13:34 +0100859 rc = nc_tls_cert_to_name(opts->ctn, cert, &map_type, &username);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100860
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100861 if (rc) {
862 if (rc == -1) {
863 /* fatal error */
864 depth = 0;
865 }
866 /* rc == 1 is a normal CTN fail (no match found) */
867 goto fail;
868 }
869
870 /* cert-to-name match, now to extract the specific field from the peer cert */
871 if (map_type == NC_TLS_CTN_SPECIFIED) {
872 session->username = lydict_insert(server_opts.ctx, username, 0);
873 } else {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200874 rc = nc_tls_ctn_get_username_from_cert(session->opts.server.client_cert, map_type, &cp);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100875 if (rc) {
876 if (rc == -1) {
877 depth = 0;
878 }
879 goto fail;
880 }
881 session->username = lydict_insert_zc(server_opts.ctx, cp);
882 }
883
884 VRB("Cert verify CTN: new client username recognized as \"%s\".", session->username);
Michal Vasko7060bcf2016-11-28 14:48:11 +0100885
886 if (server_opts.user_verify_clb && !server_opts.user_verify_clb(session)) {
887 VRB("Cert verify: user verify callback revoked authorization.");
888 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
889 return 0;
890 }
891
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100892 return 1;
893
894fail:
895 if (depth > 0) {
896 VRB("Cert verify CTN: cert fail, cert-to-name will continue on the next cert in chain.");
897 return 1;
898 }
899
900 VRB("Cert-to-name unsuccessful, dropping the new client.");
901 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
902 return 0;
903}
904
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100905#endif
906
Michal Vasko3031aae2016-01-27 16:07:18 +0100907static int
Michal Vasko4c1fb492017-01-30 14:31:07 +0100908nc_server_tls_set_server_cert(const char *name, struct nc_server_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100909{
Michal Vasko4c1fb492017-01-30 14:31:07 +0100910 if (!name) {
Michal Vaskoa8748792016-11-22 14:34:26 +0100911 if (opts->server_cert) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100912 lydict_remove(server_opts.ctx, opts->server_cert);
Michal Vaskoa8748792016-11-22 14:34:26 +0100913 }
914 opts->server_cert = NULL;
915 return 0;
916 }
917
Michal Vaskoe2713da2016-08-22 16:06:40 +0200918 if (opts->server_cert) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100919 lydict_remove(server_opts.ctx, opts->server_cert);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100920 }
Michal Vasko4c1fb492017-01-30 14:31:07 +0100921 opts->server_cert = lydict_insert(server_opts.ctx, name, 0);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100922
923 return 0;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100924}
925
926API int
Michal Vasko4c1fb492017-01-30 14:31:07 +0100927nc_server_tls_endpt_set_server_cert(const char *endpt_name, const char *name)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100928{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100929 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100930 struct nc_endpt *endpt;
931
Michal Vasko2e6defd2016-10-07 15:48:15 +0200932 if (!endpt_name) {
933 ERRARG("endpt_name");
934 return -1;
935 }
936
Michal Vasko51e514d2016-02-02 15:51:52 +0100937 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100938 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100939 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100940 return -1;
941 }
Michal Vasko4c1fb492017-01-30 14:31:07 +0100942 ret = nc_server_tls_set_server_cert(name, endpt->opts.tls);
Michal Vasko51e514d2016-02-02 15:51:52 +0100943 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100944 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100945
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100946 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100947}
948
949API int
Michal Vasko4c1fb492017-01-30 14:31:07 +0100950nc_server_tls_ch_client_set_server_cert(const char *client_name, const char *name)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100951{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100952 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200953 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100954
Michal Vasko2e6defd2016-10-07 15:48:15 +0200955 if (!client_name) {
Michal Vasko9bf49542016-11-23 13:50:15 +0100956 ERRARG("client_name");
Michal Vasko2e6defd2016-10-07 15:48:15 +0200957 return -1;
958 }
959
960 /* LOCK */
961 client = nc_server_ch_client_lock(client_name, NC_TI_OPENSSL, NULL);
962 if (!client) {
963 return -1;
964 }
965
Michal Vasko4c1fb492017-01-30 14:31:07 +0100966 ret = nc_server_tls_set_server_cert(name, client->opts.tls);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200967
968 /* UNLOCK */
969 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100970
971 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100972}
973
Michal Vasko4c1fb492017-01-30 14:31:07 +0100974API void
975nc_server_tls_set_server_cert_clb(int (*cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data,
976 char **privkey_path, char **privkey_data, int *privkey_data_rsa),
977 void *user_data, void (*free_user_data)(void *user_data))
Michal Vaskoc61c4492016-01-25 11:13:34 +0100978{
Michal Vasko4c1fb492017-01-30 14:31:07 +0100979 if (!cert_clb) {
980 ERRARG("cert_clb");
981 return;
Michal Vaskoa8748792016-11-22 14:34:26 +0100982 }
983
Michal Vasko4c1fb492017-01-30 14:31:07 +0100984 server_opts.server_cert_clb = cert_clb;
985 server_opts.server_cert_data = user_data;
986 server_opts.server_cert_data_free = free_user_data;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100987}
988
989static int
Michal Vasko4c1fb492017-01-30 14:31:07 +0100990nc_server_tls_add_trusted_cert_list(const char *name, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +0100991{
Michal Vasko4c1fb492017-01-30 14:31:07 +0100992 if (!name) {
993 ERRARG("name");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100994 return -1;
995 }
996
Michal Vasko4c1fb492017-01-30 14:31:07 +0100997 ++opts->trusted_cert_list_count;
998 opts->trusted_cert_lists = nc_realloc(opts->trusted_cert_lists, opts->trusted_cert_list_count * sizeof *opts->trusted_cert_lists);
999 if (!opts->trusted_cert_lists) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001000 ERRMEM;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001001 return -1;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001002 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001003 opts->trusted_cert_lists[opts->trusted_cert_list_count - 1] = lydict_insert(server_opts.ctx, name, 0);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001004
1005 return 0;
1006}
1007
1008API int
Michal Vasko4c1fb492017-01-30 14:31:07 +01001009nc_server_tls_endpt_add_trusted_cert_list(const char *endpt_name, const char *name)
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001010{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001011 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001012 struct nc_endpt *endpt;
1013
Michal Vasko2e6defd2016-10-07 15:48:15 +02001014 if (!endpt_name) {
1015 ERRARG("endpt_name");
1016 return -1;
1017 }
1018
Michal Vasko51e514d2016-02-02 15:51:52 +01001019 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001020 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001021 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001022 return -1;
1023 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001024 ret = nc_server_tls_add_trusted_cert_list(name, endpt->opts.tls);
Michal Vasko51e514d2016-02-02 15:51:52 +01001025 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001026 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001027
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001028 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001029}
1030
1031API int
Michal Vasko4c1fb492017-01-30 14:31:07 +01001032nc_server_tls_ch_client_add_trusted_cert_list(const char *client_name, const char *name)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001033{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001034 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001035 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001036
Michal Vasko2e6defd2016-10-07 15:48:15 +02001037 if (!client_name) {
Michal Vasko9bf49542016-11-23 13:50:15 +01001038 ERRARG("client_name");
Michal Vasko2e6defd2016-10-07 15:48:15 +02001039 return -1;
1040 }
1041
1042 /* LOCK */
1043 client = nc_server_ch_client_lock(client_name, NC_TI_OPENSSL, NULL);
1044 if (!client) {
1045 return -1;
1046 }
1047
Michal Vasko4c1fb492017-01-30 14:31:07 +01001048 ret = nc_server_tls_add_trusted_cert_list(name, client->opts.tls);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001049
1050 /* UNLOCK */
1051 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001052
1053 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001054}
1055
Michal Vasko4c1fb492017-01-30 14:31:07 +01001056API void
1057nc_server_tls_set_trusted_cert_list_clb(int (*cert_list_clb)(const char *name, void *user_data, char ***cert_paths,
1058 int *cert_path_count, char ***cert_data, int *cert_data_count),
1059 void *user_data, void (*free_user_data)(void *user_data))
Michal Vaskoc61c4492016-01-25 11:13:34 +01001060{
Michal Vasko4c1fb492017-01-30 14:31:07 +01001061 if (!cert_list_clb) {
1062 ERRARG("cert_list_clb");
1063 return;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001064 }
1065
Michal Vasko4c1fb492017-01-30 14:31:07 +01001066 server_opts.trusted_cert_list_clb = cert_list_clb;
1067 server_opts.trusted_cert_list_data = user_data;
1068 server_opts.trusted_cert_list_data_free = free_user_data;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001069}
1070
1071static int
Michal Vasko4c1fb492017-01-30 14:31:07 +01001072nc_server_tls_del_trusted_cert_list(const char *name, struct nc_server_tls_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +02001073{
1074 uint16_t i;
1075
1076 if (!name) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001077 for (i = 0; i < opts->trusted_cert_list_count; ++i) {
1078 lydict_remove(server_opts.ctx, opts->trusted_cert_lists[i]);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001079 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001080 free(opts->trusted_cert_lists);
1081 opts->trusted_cert_lists = NULL;
1082 opts->trusted_cert_list_count = 0;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001083 return 0;
1084 } else {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001085 for (i = 0; i < opts->trusted_cert_list_count; ++i) {
1086 if (!strcmp(opts->trusted_cert_lists[i], name)) {
1087 lydict_remove(server_opts.ctx, opts->trusted_cert_lists[i]);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001088
Michal Vasko4c1fb492017-01-30 14:31:07 +01001089 --opts->trusted_cert_list_count;
1090 if (i < opts->trusted_cert_list_count - 1) {
1091 memmove(opts->trusted_cert_lists + i, opts->trusted_cert_lists + i + 1,
1092 (opts->trusted_cert_list_count - i) * sizeof *opts->trusted_cert_lists);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001093 }
1094 return 0;
1095 }
1096 }
1097 }
1098
Michal Vasko4c1fb492017-01-30 14:31:07 +01001099 ERR("Certificate list \"%s\" not found.", name);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001100 return -1;
1101}
1102
1103API int
Michal Vasko4c1fb492017-01-30 14:31:07 +01001104nc_server_tls_endpt_del_trusted_cert_list(const char *endpt_name, const char *name)
Michal Vaskoe2713da2016-08-22 16:06:40 +02001105{
1106 int ret;
1107 struct nc_endpt *endpt;
1108
Michal Vasko2e6defd2016-10-07 15:48:15 +02001109 if (!endpt_name) {
1110 ERRARG("endpt_name");
1111 return -1;
1112 }
1113
Michal Vaskoe2713da2016-08-22 16:06:40 +02001114 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001115 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001116 if (!endpt) {
1117 return -1;
1118 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001119 ret = nc_server_tls_del_trusted_cert_list(name, endpt->opts.tls);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001120 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001121 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001122
1123 return ret;
1124}
1125
1126API int
Michal Vasko4c1fb492017-01-30 14:31:07 +01001127nc_server_tls_ch_client_del_trusted_cert_list(const char *client_name, const char *name)
Michal Vaskoe2713da2016-08-22 16:06:40 +02001128{
1129 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001130 struct nc_ch_client *client;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001131
Michal Vasko2e6defd2016-10-07 15:48:15 +02001132 if (!client_name) {
Michal Vasko9bf49542016-11-23 13:50:15 +01001133 ERRARG("client_name");
Michal Vasko2e6defd2016-10-07 15:48:15 +02001134 return -1;
1135 }
1136
1137 /* LOCK */
1138 client = nc_server_ch_client_lock(client_name, NC_TI_OPENSSL, NULL);
1139 if (!client) {
1140 return -1;
1141 }
1142
Michal Vasko4c1fb492017-01-30 14:31:07 +01001143 ret = nc_server_tls_del_trusted_cert_list(name, client->opts.tls);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001144
1145 /* UNLOCK */
1146 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001147
1148 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001149}
1150
1151static int
Michal Vasko96830e32016-02-01 10:54:18 +01001152nc_server_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001153{
Michal Vasko96830e32016-02-01 10:54:18 +01001154 if (!ca_file && !ca_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001155 ERRARG("ca_file and ca_dir");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001156 return -1;
1157 }
1158
Michal Vasko96830e32016-02-01 10:54:18 +01001159 if (ca_file) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001160 if (opts->trusted_ca_file) {
1161 lydict_remove(server_opts.ctx, opts->trusted_ca_file);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001162 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001163 opts->trusted_ca_file = lydict_insert(server_opts.ctx, ca_file, 0);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001164 }
1165
Michal Vasko96830e32016-02-01 10:54:18 +01001166 if (ca_dir) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001167 if (opts->trusted_ca_dir) {
1168 lydict_remove(server_opts.ctx, opts->trusted_ca_dir);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001169 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001170 opts->trusted_ca_dir = lydict_insert(server_opts.ctx, ca_dir, 0);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001171 }
1172
1173 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001174}
1175
Michal Vaskoc61c4492016-01-25 11:13:34 +01001176API int
Michal Vasko96830e32016-02-01 10:54:18 +01001177nc_server_tls_endpt_set_trusted_ca_paths(const char *endpt_name, const char *ca_file, const char *ca_dir)
Michal Vasko086311b2016-01-08 09:53:11 +01001178{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001179 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001180 struct nc_endpt *endpt;
1181
Michal Vasko2e6defd2016-10-07 15:48:15 +02001182 if (!endpt_name) {
1183 ERRARG("endpt_name");
1184 return -1;
1185 }
1186
Michal Vasko51e514d2016-02-02 15:51:52 +01001187 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001188 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001189 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001190 return -1;
1191 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001192 ret = nc_server_tls_set_trusted_ca_paths(ca_file, ca_dir, endpt->opts.tls);
Michal Vasko51e514d2016-02-02 15:51:52 +01001193 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001194 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001195
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001196 return ret;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001197}
1198
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001199API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001200nc_server_tls_ch_client_set_trusted_ca_paths(const char *client_name, const char *ca_file, const char *ca_dir)
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001201{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001202 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001203 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001204
Michal Vasko2e6defd2016-10-07 15:48:15 +02001205 if (!client_name) {
Michal Vasko9bf49542016-11-23 13:50:15 +01001206 ERRARG("client_name");
Michal Vasko2e6defd2016-10-07 15:48:15 +02001207 return -1;
1208 }
1209
1210 /* LOCK */
1211 client = nc_server_ch_client_lock(client_name, NC_TI_OPENSSL, NULL);
1212 if (!client) {
1213 return -1;
1214 }
1215
1216 ret = nc_server_tls_set_trusted_ca_paths(ca_file, ca_dir, client->opts.tls);
1217
1218 /* UNLOCK */
1219 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001220
1221 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001222}
1223
Michal Vaskoc61c4492016-01-25 11:13:34 +01001224static int
Michal Vasko96830e32016-02-01 10:54:18 +01001225nc_server_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001226{
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001227 X509_LOOKUP *lookup;
1228
Michal Vasko96830e32016-02-01 10:54:18 +01001229 if (!crl_file && !crl_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001230 ERRARG("crl_file and crl_dir");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001231 return -1;
1232 }
1233
Michal Vaskoc61c4492016-01-25 11:13:34 +01001234 if (!opts->crl_store) {
1235 opts->crl_store = X509_STORE_new();
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001236 }
1237
Michal Vasko96830e32016-02-01 10:54:18 +01001238 if (crl_file) {
Michal Vaskoc61c4492016-01-25 11:13:34 +01001239 lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file());
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001240 if (!lookup) {
Michal Vaskod083db62016-01-19 10:31:29 +01001241 ERR("Failed to add a lookup method.");
Michal Vaskob48aa812016-01-18 14:13:09 +01001242 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001243 }
1244
Michal Vasko96830e32016-02-01 10:54:18 +01001245 if (X509_LOOKUP_load_file(lookup, crl_file, X509_FILETYPE_PEM) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +01001246 ERR("Failed to add a revocation lookup file (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskob48aa812016-01-18 14:13:09 +01001247 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001248 }
1249 }
1250
Michal Vasko96830e32016-02-01 10:54:18 +01001251 if (crl_dir) {
Michal Vaskoc61c4492016-01-25 11:13:34 +01001252 lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir());
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001253 if (!lookup) {
Michal Vaskod083db62016-01-19 10:31:29 +01001254 ERR("Failed to add a lookup method.");
Michal Vaskob48aa812016-01-18 14:13:09 +01001255 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001256 }
1257
Michal Vasko96830e32016-02-01 10:54:18 +01001258 if (X509_LOOKUP_add_dir(lookup, crl_dir, X509_FILETYPE_PEM) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +01001259 ERR("Failed to add a revocation lookup directory (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskob48aa812016-01-18 14:13:09 +01001260 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001261 }
1262 }
1263
1264 return 0;
Michal Vaskob48aa812016-01-18 14:13:09 +01001265
1266fail:
Michal Vaskob48aa812016-01-18 14:13:09 +01001267 return -1;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001268}
1269
Michal Vaskoc61c4492016-01-25 11:13:34 +01001270API int
Michal Vasko96830e32016-02-01 10:54:18 +01001271nc_server_tls_endpt_set_crl_paths(const char *endpt_name, const char *crl_file, const char *crl_dir)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001272{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001273 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001274 struct nc_endpt *endpt;
1275
Michal Vasko2e6defd2016-10-07 15:48:15 +02001276 if (!endpt_name) {
1277 ERRARG("endpt_name");
1278 return -1;
1279 }
1280
Michal Vasko51e514d2016-02-02 15:51:52 +01001281 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001282 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001283 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001284 return -1;
1285 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001286 ret = nc_server_tls_set_crl_paths(crl_file, crl_dir, endpt->opts.tls);
Michal Vasko51e514d2016-02-02 15:51:52 +01001287 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001288 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001289
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001290 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001291}
1292
1293API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001294nc_server_tls_ch_client_set_crl_paths(const char *client_name, const char *crl_file, const char *crl_dir)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001295{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001296 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001297 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001298
Michal Vasko2e6defd2016-10-07 15:48:15 +02001299 if (!client_name) {
Michal Vasko9bf49542016-11-23 13:50:15 +01001300 ERRARG("client_name");
Michal Vasko2e6defd2016-10-07 15:48:15 +02001301 return -1;
1302 }
1303
1304 /* LOCK */
1305 client = nc_server_ch_client_lock(client_name, NC_TI_OPENSSL, NULL);
1306 if (!client) {
1307 return -1;
1308 }
1309
1310 ret = nc_server_tls_set_crl_paths(crl_file, crl_dir, client->opts.tls);
1311
1312 /* UNLOCK */
1313 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001314
1315 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001316}
1317
1318static void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001319nc_server_tls_clear_crls(struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001320{
Michal Vaskoc61c4492016-01-25 11:13:34 +01001321 if (!opts->crl_store) {
Michal Vaskoc61c4492016-01-25 11:13:34 +01001322 return;
1323 }
1324
1325 X509_STORE_free(opts->crl_store);
1326 opts->crl_store = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001327}
1328
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001329API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001330nc_server_tls_endpt_clear_crls(const char *endpt_name)
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001331{
Michal Vasko3031aae2016-01-27 16:07:18 +01001332 struct nc_endpt *endpt;
1333
Michal Vasko2e6defd2016-10-07 15:48:15 +02001334 if (!endpt_name) {
1335 ERRARG("endpt_name");
1336 return;
1337 }
1338
Michal Vasko51e514d2016-02-02 15:51:52 +01001339 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001340 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001341 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001342 return;
1343 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001344 nc_server_tls_clear_crls(endpt->opts.tls);
Michal Vasko51e514d2016-02-02 15:51:52 +01001345 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001346 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001347}
1348
Michal Vaskoc61c4492016-01-25 11:13:34 +01001349API void
Michal Vasko2e6defd2016-10-07 15:48:15 +02001350nc_server_tls_ch_client_clear_crls(const char *client_name)
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001351{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001352 struct nc_ch_client *client;
1353
1354 if (!client_name) {
Michal Vasko9bf49542016-11-23 13:50:15 +01001355 ERRARG("client_name");
Michal Vasko2e6defd2016-10-07 15:48:15 +02001356 return;
1357 }
1358
1359 /* LOCK */
1360 client = nc_server_ch_client_lock(client_name, NC_TI_OPENSSL, NULL);
1361 if (!client) {
1362 return;
1363 }
1364
1365 nc_server_tls_clear_crls(client->opts.tls);
1366
1367 /* UNLOCK */
1368 nc_server_ch_client_unlock(client);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001369}
1370
1371static int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001372nc_server_tls_add_ctn(uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name,
1373 struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001374{
Michal Vasko5e3f3392016-01-20 11:13:01 +01001375 struct nc_ctn *ctn, *new;
1376
Michal Vaskoc61c4492016-01-25 11:13:34 +01001377 if (!opts->ctn) {
Michal Vasko5e3f3392016-01-20 11:13:01 +01001378 /* the first item */
Michal Vasko3cf4aaa2017-02-01 15:03:36 +01001379 opts->ctn = new = calloc(1, sizeof *new);
1380 if (!new) {
1381 ERRMEM;
1382 return -1;
1383 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001384 } else if (opts->ctn->id > id) {
Michal Vasko5e3f3392016-01-20 11:13:01 +01001385 /* insert at the beginning */
Michal Vasko3cf4aaa2017-02-01 15:03:36 +01001386 new = calloc(1, sizeof *new);
1387 if (!new) {
1388 ERRMEM;
1389 return -1;
1390 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001391 new->next = opts->ctn;
1392 opts->ctn = new;
Michal Vasko5e3f3392016-01-20 11:13:01 +01001393 } else {
Michal Vasko3cf4aaa2017-02-01 15:03:36 +01001394 for (ctn = opts->ctn; ctn->next && ctn->next->id < id; ctn = ctn->next);
Michal Vasko276f9d92017-02-02 11:15:55 +01001395 if (ctn->id == id) {
Michal Vasko3cf4aaa2017-02-01 15:03:36 +01001396 /* it exists already */
Michal Vasko276f9d92017-02-02 11:15:55 +01001397 new = ctn;
Michal Vasko3cf4aaa2017-02-01 15:03:36 +01001398 } else {
1399 /* insert after ctn */
1400 new = calloc(1, sizeof *new);
1401 if (!new) {
1402 ERRMEM;
1403 return -1;
1404 }
1405 new->next = ctn->next;
1406 ctn->next = new;
1407 }
1408 }
1409
1410 new->id = id;
1411 if (fingerprint) {
1412 if (new->fingerprint) {
1413 lydict_remove(server_opts.ctx, new->fingerprint);
1414 }
1415 new->fingerprint = lydict_insert(server_opts.ctx, fingerprint, 0);
1416 }
1417 if (map_type) {
1418 new->map_type = map_type;
1419 }
1420 if (name) {
1421 if (new->name) {
1422 lydict_remove(server_opts.ctx, new->name);
1423 }
1424 new->name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko5e3f3392016-01-20 11:13:01 +01001425 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001426
1427 return 0;
1428}
1429
1430API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001431nc_server_tls_endpt_add_ctn(const char *endpt_name, uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type,
1432 const char *name)
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001433{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001434 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001435 struct nc_endpt *endpt;
1436
Michal Vasko2e6defd2016-10-07 15:48:15 +02001437 if (!endpt_name) {
1438 ERRARG("endpt_name");
1439 return -1;
1440 }
1441
Michal Vasko51e514d2016-02-02 15:51:52 +01001442 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001443 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001444 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001445 return -1;
1446 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001447 ret = nc_server_tls_add_ctn(id, fingerprint, map_type, name, endpt->opts.tls);
Michal Vasko51e514d2016-02-02 15:51:52 +01001448 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001449 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001450
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001451 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001452}
1453
1454API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001455nc_server_tls_ch_client_add_ctn(const char *client_name, uint32_t id, const char *fingerprint,
1456 NC_TLS_CTN_MAPTYPE map_type, const char *name)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001457{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001458 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001459 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001460
Michal Vasko2e6defd2016-10-07 15:48:15 +02001461 if (!client_name) {
Michal Vasko9bf49542016-11-23 13:50:15 +01001462 ERRARG("client_name");
Michal Vasko2e6defd2016-10-07 15:48:15 +02001463 return -1;
1464 }
1465
1466 /* LOCK */
1467 client = nc_server_ch_client_lock(client_name, NC_TI_OPENSSL, NULL);
1468 if (!client) {
1469 return -1;
1470 }
1471
1472 ret = nc_server_tls_add_ctn(id, fingerprint, map_type, name, client->opts.tls);
1473
1474 /* UNLOCK */
1475 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001476
1477 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001478}
1479
1480static int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001481nc_server_tls_del_ctn(int64_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name,
1482 struct nc_server_tls_opts *opts)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001483{
Michal Vasko5e3f3392016-01-20 11:13:01 +01001484 struct nc_ctn *ctn, *next, *prev;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001485 int ret = -1;
1486
Michal Vasko1a38c862016-01-15 15:50:07 +01001487 if ((id < 0) && !fingerprint && !map_type && !name) {
Michal Vaskoc61c4492016-01-25 11:13:34 +01001488 ctn = opts->ctn;
Michal Vasko5e3f3392016-01-20 11:13:01 +01001489 while (ctn) {
1490 lydict_remove(server_opts.ctx, ctn->fingerprint);
1491 lydict_remove(server_opts.ctx, ctn->name);
1492
1493 next = ctn->next;
1494 free(ctn);
1495 ctn = next;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001496
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001497 ret = 0;
1498 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001499 opts->ctn = NULL;
Michal Vasko1a38c862016-01-15 15:50:07 +01001500 } else {
Michal Vasko5e3f3392016-01-20 11:13:01 +01001501 prev = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001502 ctn = opts->ctn;
Michal Vasko5e3f3392016-01-20 11:13:01 +01001503 while (ctn) {
1504 if (((id < 0) || (ctn->id == id))
1505 && (!fingerprint || !strcmp(ctn->fingerprint, fingerprint))
1506 && (!map_type || (ctn->map_type == map_type))
1507 && (!name || (ctn->name && !strcmp(ctn->name, name)))) {
Michal Vasko5e3f3392016-01-20 11:13:01 +01001508 lydict_remove(server_opts.ctx, ctn->fingerprint);
1509 lydict_remove(server_opts.ctx, ctn->name);
Michal Vasko1a38c862016-01-15 15:50:07 +01001510
Michal Vasko5e3f3392016-01-20 11:13:01 +01001511 if (prev) {
1512 prev->next = ctn->next;
1513 next = ctn->next;
1514 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +01001515 opts->ctn = ctn->next;
Michal Vasko5e3f3392016-01-20 11:13:01 +01001516 next = ctn->next;
1517 }
1518 free(ctn);
1519 ctn = next;
Michal Vasko1a38c862016-01-15 15:50:07 +01001520
1521 ret = 0;
Michal Vasko5e3f3392016-01-20 11:13:01 +01001522 } else {
1523 prev = ctn;
1524 ctn = ctn->next;
Michal Vasko1a38c862016-01-15 15:50:07 +01001525 }
1526 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001527 }
1528
1529 return ret;
1530}
1531
Michal Vaskoc61c4492016-01-25 11:13:34 +01001532API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001533nc_server_tls_endpt_del_ctn(const char *endpt_name, int64_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type,
1534 const char *name)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001535{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001536 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001537 struct nc_endpt *endpt;
1538
Michal Vasko2e6defd2016-10-07 15:48:15 +02001539 if (!endpt_name) {
1540 ERRARG("endpt_name");
1541 return -1;
1542 }
1543
Michal Vasko51e514d2016-02-02 15:51:52 +01001544 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001545 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001546 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001547 return -1;
1548 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001549 ret = nc_server_tls_del_ctn(id, fingerprint, map_type, name, endpt->opts.tls);
Michal Vasko51e514d2016-02-02 15:51:52 +01001550 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001551 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001552
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001553 return ret;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001554}
1555
1556API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001557nc_server_tls_ch_client_del_ctn(const char *client_name, int64_t id, const char *fingerprint,
1558 NC_TLS_CTN_MAPTYPE map_type, const char *name)
Michal Vaskoc61c4492016-01-25 11:13:34 +01001559{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001560 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001561 struct nc_ch_client *client;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001562
Michal Vasko2e6defd2016-10-07 15:48:15 +02001563 if (!client_name) {
Michal Vasko9bf49542016-11-23 13:50:15 +01001564 ERRARG("client_name");
Michal Vasko2e6defd2016-10-07 15:48:15 +02001565 return -1;
1566 }
1567
1568 /* LOCK */
1569 client = nc_server_ch_client_lock(client_name, NC_TI_OPENSSL, NULL);
1570 if (!client) {
1571 return -1;
1572 }
1573
1574 ret = nc_server_tls_del_ctn(id, fingerprint, map_type, name, client->opts.tls);
1575
1576 /* UNLOCK */
1577 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001578
1579 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001580}
Michal Vaskoc61c4492016-01-25 11:13:34 +01001581
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001582static int
Michal Vaskof585ac72016-11-25 15:16:38 +01001583nc_server_tls_get_ctn(uint32_t *id, char **fingerprint, NC_TLS_CTN_MAPTYPE *map_type, char **name,
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001584 struct nc_server_tls_opts *opts)
1585{
1586 struct nc_ctn *ctn;
1587 int ret = -1;
1588
1589 for (ctn = opts->ctn; ctn; ctn = ctn->next) {
1590 if (id && *id && (*id != ctn->id)) {
1591 continue;
1592 }
Michal Vasko3cf4aaa2017-02-01 15:03:36 +01001593 if (fingerprint && *fingerprint && (!ctn->fingerprint || strcmp(*fingerprint, ctn->fingerprint))) {
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001594 continue;
1595 }
Michal Vasko3cf4aaa2017-02-01 15:03:36 +01001596 if (map_type && *map_type && (!ctn->map_type || (*map_type != ctn->map_type))) {
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001597 continue;
1598 }
Michal Vasko3cf4aaa2017-02-01 15:03:36 +01001599 if (name && *name && (!ctn->name || strcmp(*name, ctn->name))) {
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001600 continue;
1601 }
1602
1603 /* first match, good enough */
1604 if (id && !(*id)) {
1605 *id = ctn->id;
1606 }
Michal Vasko3cf4aaa2017-02-01 15:03:36 +01001607 if (fingerprint && !(*fingerprint) && ctn->fingerprint) {
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001608 *fingerprint = strdup(ctn->fingerprint);
1609 }
Michal Vasko3cf4aaa2017-02-01 15:03:36 +01001610 if (map_type && !(*map_type) && ctn->map_type) {
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001611 *map_type = ctn->map_type;
1612 }
Michal Vasko3cf4aaa2017-02-01 15:03:36 +01001613 if (name && !(*name) && ctn->name && ctn->name) {
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001614 *name = strdup(ctn->name);
1615 }
1616
1617 ret = 0;
1618 break;
1619 }
1620
1621 return ret;
1622}
1623
1624API int
Michal Vaskof585ac72016-11-25 15:16:38 +01001625nc_server_tls_endpt_get_ctn(const char *endpt_name, uint32_t *id, char **fingerprint, NC_TLS_CTN_MAPTYPE *map_type,
1626 char **name)
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001627{
1628 int ret;
1629 struct nc_endpt *endpt;
1630
1631 if (!endpt_name) {
1632 ERRARG("endpt_name");
1633 return -1;
1634 }
1635
1636 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001637 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001638 if (!endpt) {
1639 return -1;
1640 }
1641 ret = nc_server_tls_get_ctn(id, fingerprint, map_type, name, endpt->opts.tls);
1642 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +01001643 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001644
1645 return ret;
1646}
1647
1648API int
Michal Vaskof585ac72016-11-25 15:16:38 +01001649nc_server_tls_ch_client_get_ctn(const char *client_name, uint32_t *id, char **fingerprint, NC_TLS_CTN_MAPTYPE *map_type,
1650 char **name)
Michal Vaskodf5e6af2016-11-23 13:50:56 +01001651{
1652 int ret;
1653 struct nc_ch_client *client;
1654
1655 if (!client_name) {
1656 ERRARG("client_name");
1657 return -1;
1658 }
1659
1660 /* LOCK */
1661 client = nc_server_ch_client_lock(client_name, NC_TI_OPENSSL, NULL);
1662 if (!client) {
1663 return -1;
1664 }
1665
1666 ret = nc_server_tls_get_ctn(id, fingerprint, map_type, name, client->opts.tls);
1667
1668 /* UNLOCK */
1669 nc_server_ch_client_unlock(client);
1670
1671 return ret;
1672}
1673
Michal Vasko709598e2016-11-28 14:48:32 +01001674API const X509 *
1675nc_session_get_client_cert(const struct nc_session *session)
1676{
1677 if (!session || (session->side != NC_SERVER)) {
1678 ERRARG("session");
1679 return NULL;
1680 }
1681
1682 return session->opts.server.client_cert;
1683}
1684
Michal Vasko7060bcf2016-11-28 14:48:11 +01001685API void
1686nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *session))
1687{
1688 server_opts.user_verify_clb = verify_clb;
1689}
1690
Michal Vasko3031aae2016-01-27 16:07:18 +01001691void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001692nc_server_tls_clear_opts(struct nc_server_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +01001693{
Michal Vasko4c1fb492017-01-30 14:31:07 +01001694 lydict_remove(server_opts.ctx, opts->server_cert);
1695 nc_server_tls_del_trusted_cert_list(NULL, opts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001696 lydict_remove(server_opts.ctx, opts->trusted_ca_file);
1697 lydict_remove(server_opts.ctx, opts->trusted_ca_dir);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001698 nc_server_tls_clear_crls(opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001699 nc_server_tls_del_ctn(-1, NULL, 0, NULL, opts);
Michal Vasko086311b2016-01-08 09:53:11 +01001700}
Michal Vasko9e036d52016-01-08 10:49:26 +01001701
Michal Vasko6d292992016-01-18 09:42:38 +01001702static void
1703nc_tls_make_verify_key(void)
1704{
Michal Vasko5c2f7952016-01-22 13:16:31 +01001705 pthread_key_create(&verify_key, NULL);
Michal Vasko6d292992016-01-18 09:42:38 +01001706}
1707
Michal Vasko4c1fb492017-01-30 14:31:07 +01001708static int
1709nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, const char *cert_name)
1710{
1711 char *cert_path = NULL, *cert_data = NULL, *privkey_path = NULL, *privkey_data = NULL;
1712 int privkey_data_rsa = 1, ret = 0;
Michal Vasko6e08cb72017-02-02 11:15:29 +01001713 X509 *cert = NULL;
1714 EVP_PKEY *pkey = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001715
1716 if (!cert_name) {
1717 ERR("Server certificate not set.");
1718 return -1;
1719 } else if (!server_opts.server_cert_clb) {
1720 ERR("Callback for retrieving the server certificate is not set.");
1721 return -1;
1722 }
1723
1724 if (server_opts.server_cert_clb(cert_name, server_opts.server_cert_data, &cert_path, &cert_data, &privkey_path,
1725 &privkey_data, &privkey_data_rsa)) {
1726 ERR("Server certificate callback failed.");
1727 return -1;
1728 }
1729
1730 /* load the certificate */
1731 if (cert_path) {
1732 if (SSL_CTX_use_certificate_file(tls_ctx, cert_path, SSL_FILETYPE_PEM) != 1) {
1733 ERR("Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
1734 ret = -1;
1735 goto cleanup;
1736 }
1737 } else {
Michal Vasko6e08cb72017-02-02 11:15:29 +01001738 cert = base64der_to_cert(cert_data);
Michal Vasko4c1fb492017-01-30 14:31:07 +01001739 if (!cert || (SSL_CTX_use_certificate(tls_ctx, cert) != 1)) {
1740 ERR("Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
1741 ret = -1;
1742 goto cleanup;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001743 }
1744 }
1745
1746 /* load the private key */
1747 if (privkey_path) {
1748 if (SSL_CTX_use_PrivateKey_file(tls_ctx, privkey_path, SSL_FILETYPE_PEM) != 1) {
1749 ERR("Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
1750 ret = -1;
1751 goto cleanup;
1752 }
1753 } else {
Michal Vasko6e08cb72017-02-02 11:15:29 +01001754 pkey = base64der_to_privatekey(privkey_data, privkey_data_rsa);
Michal Vasko4c1fb492017-01-30 14:31:07 +01001755 if (!pkey || (SSL_CTX_use_PrivateKey(tls_ctx, pkey) != 1)) {
1756 ERR("Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
1757 ret = -1;
1758 goto cleanup;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001759 }
1760 }
1761
1762cleanup:
Michal Vasko6e08cb72017-02-02 11:15:29 +01001763 X509_free(cert);
1764 EVP_PKEY_free(pkey);
Michal Vasko4c1fb492017-01-30 14:31:07 +01001765 free(cert_path);
1766 free(cert_data);
1767 free(privkey_path);
1768 free(privkey_data);
1769 return ret;
1770}
1771
1772static void
1773tls_store_add_trusted_cert(X509_STORE *cert_store, const char *cert_path, const char *cert_data)
1774{
1775 X509 *cert;
1776
1777 if (cert_path) {
1778 cert = pem_to_cert(cert_path);
1779 } else {
1780 cert = base64der_to_cert(cert_data);
1781 }
1782
1783 if (!cert) {
1784 if (cert_path) {
1785 ERR("Loading a trusted certificate (path \"%s\") failed (%s).", cert_path,
1786 ERR_reason_error_string(ERR_get_error()));
1787 } else {
1788 ERR("Loading a trusted certificate (data \"%s\") failed (%s).", cert_data,
1789 ERR_reason_error_string(ERR_get_error()));
1790 }
1791 return;
1792 }
1793
1794 /* add the trusted certificate */
1795 if (X509_STORE_add_cert(cert_store, cert) != 1) {
1796 ERR("Adding a trusted certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
1797 X509_free(cert);
1798 return;
1799 }
1800 X509_free(cert);
1801}
1802
1803static int
1804nc_tls_store_set_trusted_certs(X509_STORE *cert_store, const char **trusted_cert_lists, uint16_t trusted_cert_list_count)
1805{
1806 uint16_t i;
1807 int j;
1808 char **cert_paths, **cert_data;
1809 int cert_path_count, cert_data_count;
1810
1811 if (!server_opts.trusted_cert_list_clb) {
1812 ERR("Callback for retrieving trusted certificate lists is not set.");
1813 return -1;
1814 }
1815
1816 for (i = 0; i < trusted_cert_list_count; ++i) {
1817 cert_paths = cert_data = NULL;
1818 cert_path_count = cert_data_count = 0;
1819 if (server_opts.trusted_cert_list_clb(trusted_cert_lists[i], server_opts.trusted_cert_list_data,
1820 &cert_paths, &cert_path_count, &cert_data, &cert_data_count)) {
1821 ERR("Trusted certificate list callback for \"%s\" failed.", trusted_cert_lists[i]);
1822 return -1;
1823 }
1824
1825 for (j = 0; j < cert_path_count; ++j) {
1826 tls_store_add_trusted_cert(cert_store, cert_paths[j], NULL);
1827 free(cert_paths[j]);
1828 }
1829 free(cert_paths);
1830
1831 for (j = 0; j < cert_data_count; ++j) {
1832 tls_store_add_trusted_cert(cert_store, NULL, cert_data[j]);
1833 free(cert_data[j]);
1834 }
1835 free(cert_data);
1836 }
1837
1838 return 0;
1839}
1840
Michal Vasko3031aae2016-01-27 16:07:18 +01001841int
Michal Vasko0190bc32016-03-02 15:47:49 +01001842nc_accept_tls_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001843{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001844 X509_STORE *cert_store;
1845 SSL_CTX *tls_ctx;
1846 X509_LOOKUP *lookup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001847 struct nc_server_tls_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001848 int ret;
1849 struct timespec ts_timeout, ts_cur;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001850
Michal Vasko2cc4c682016-03-01 09:16:48 +01001851 opts = session->data;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001852
Michal Vaskoe2713da2016-08-22 16:06:40 +02001853 /* SSL_CTX */
Michal Vasko18aeb5d2017-02-17 09:23:56 +01001854#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
1855 tls_ctx = SSL_CTX_new(TLS_server_method());
1856#else
Michal Vaskoe2713da2016-08-22 16:06:40 +02001857 tls_ctx = SSL_CTX_new(TLSv1_2_server_method());
Michal Vasko18aeb5d2017-02-17 09:23:56 +01001858#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001859 if (!tls_ctx) {
1860 ERR("Failed to create TLS context.");
1861 goto error;
1862 }
1863 SSL_CTX_set_verify(tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_tlsclb_verify);
Michal Vasko4c1fb492017-01-30 14:31:07 +01001864 if (nc_tls_ctx_set_server_cert_key(tls_ctx, opts->server_cert)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001865 goto error;
1866 }
1867
1868 /* X509_STORE, managed (freed) with the context */
1869 cert_store = X509_STORE_new();
1870 SSL_CTX_set_cert_store(tls_ctx, cert_store);
1871
Michal Vasko4c1fb492017-01-30 14:31:07 +01001872 if (nc_tls_store_set_trusted_certs(cert_store, opts->trusted_cert_lists, opts->trusted_cert_list_count)) {
1873 goto error;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001874 }
1875
1876 if (opts->trusted_ca_file) {
1877 lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_file());
1878 if (!lookup) {
1879 ERR("Failed to add a lookup method.");
1880 goto error;
1881 }
1882
1883 if (X509_LOOKUP_load_file(lookup, opts->trusted_ca_file, X509_FILETYPE_PEM) != 1) {
1884 ERR("Failed to add a trusted cert file (%s).", ERR_reason_error_string(ERR_get_error()));
1885 goto error;
1886 }
1887 }
1888
1889 if (opts->trusted_ca_dir) {
1890 lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_hash_dir());
1891 if (!lookup) {
1892 ERR("Failed to add a lookup method.");
1893 goto error;
1894 }
1895
1896 if (X509_LOOKUP_add_dir(lookup, opts->trusted_ca_dir, X509_FILETYPE_PEM) != 1) {
1897 ERR("Failed to add a trusted cert directory (%s).", ERR_reason_error_string(ERR_get_error()));
1898 goto error;
1899 }
1900 }
1901
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001902 session->ti_type = NC_TI_OPENSSL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001903 session->ti.tls = SSL_new(tls_ctx);
1904
Michal Vasko4c1fb492017-01-30 14:31:07 +01001905 /* context can be freed already, trusted certs must be freed manually */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001906 SSL_CTX_free(tls_ctx);
1907 tls_ctx = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001908
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001909 if (!session->ti.tls) {
Michal Vaskod083db62016-01-19 10:31:29 +01001910 ERR("Failed to create TLS structure from context.");
Michal Vaskoe2713da2016-08-22 16:06:40 +02001911 goto error;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001912 }
1913
1914 SSL_set_fd(session->ti.tls, sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001915 sock = -1;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001916 SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
1917
Michal Vasko6d292992016-01-18 09:42:38 +01001918 /* store session on per-thread basis */
Michal Vasko5c2f7952016-01-22 13:16:31 +01001919 pthread_once(&verify_once, nc_tls_make_verify_key);
1920 pthread_setspecific(verify_key, session);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001921
Michal Vasko36c7be82017-02-22 13:37:59 +01001922 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001923 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001924 nc_addtimespec(&ts_timeout, timeout);
1925 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001926 while (((ret = SSL_accept(session->ti.tls)) == -1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) {
1927 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001928 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001929 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001930 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1931 ERR("SSL_accept timeout.");
1932 return 0;
1933 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001934 }
1935 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001936
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001937 if (ret != 1) {
1938 switch (SSL_get_error(session->ti.tls, ret)) {
1939 case SSL_ERROR_SYSCALL:
Michal Vaskod083db62016-01-19 10:31:29 +01001940 ERR("SSL_accept failed (%s).", strerror(errno));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001941 break;
1942 case SSL_ERROR_SSL:
Michal Vaskod083db62016-01-19 10:31:29 +01001943 ERR("SSL_accept failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001944 break;
1945 default:
Michal Vaskod083db62016-01-19 10:31:29 +01001946 ERR("SSL_accept failed.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001947 break;
1948 }
1949 return -1;
1950 }
1951
Michal Vasko1a38c862016-01-15 15:50:07 +01001952 return 1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001953
1954error:
1955 if (sock > -1) {
1956 close(sock);
1957 }
1958 SSL_CTX_free(tls_ctx);
1959 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001960}