blob: b845ba1987c049c3a8d24a41864ec2a17812846b [file] [log] [blame]
Radek Krejci9f03b482015-10-22 16:02:10 +02001/**
Michal Vasko086311b2016-01-08 09:53:11 +01002 * \file session_client_tls.c
Radek Krejci9f03b482015-10-22 16:02:10 +02003 * \author Radek Krejci <rkrejci@cesnet.cz>
Michal Vasko11d4cdb2015-10-29 11:42:52 +01004 * \author Michal Vasko <mvasko@cesnet.cz>
Michal Vasko086311b2016-01-08 09:53:11 +01005 * \brief libnetconf2 - TLS specific session client transport functions
Radek Krejci9f03b482015-10-22 16:02:10 +02006 *
Michal Vasko11d4cdb2015-10-29 11:42:52 +01007 * This source is compiled only with libssl.
Radek Krejci9f03b482015-10-22 16:02:10 +02008 *
9 * Copyright (c) 2015 CESNET, z.s.p.o.
10 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010011 * This source code is licensed under BSD 3-Clause License (the "License").
12 * You may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010014 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010015 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci9f03b482015-10-22 16:02:10 +020016 */
17
18#include <assert.h>
19#include <errno.h>
Radek Krejci9f03b482015-10-22 16:02:10 +020020#include <string.h>
21#include <unistd.h>
22
23#include <libyang/libyang.h>
Michal Vasko18aeb5d2017-02-17 09:23:56 +010024#include <openssl/ossl_typ.h>
Michal Vasko086311b2016-01-08 09:53:11 +010025#include <openssl/err.h>
Michal Vaskodae48322016-02-25 14:49:48 +010026#include <openssl/x509.h>
Radek Krejci9f03b482015-10-22 16:02:10 +020027
Michal Vaskoe22c6732016-01-29 11:03:02 +010028#include "session_client.h"
29#include "session_client_ch.h"
Radek Krejci9f03b482015-10-22 16:02:10 +020030#include "libnetconf.h"
Michal Vasko11d4cdb2015-10-29 11:42:52 +010031
Michal Vaskodaf9a092016-02-09 10:42:05 +010032extern struct nc_client_opts client_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +010033static struct nc_client_tls_opts tls_opts;
34static struct nc_client_tls_opts tls_ch_opts;
35
36static int tlsauth_ch;
Radek Krejci9f03b482015-10-22 16:02:10 +020037
Michal Vasko18aeb5d2017-02-17 09:23:56 +010038#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
39
40static int
41tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
42{
43 X509_STORE_CTX *store_ctx;
44 X509_OBJECT *obj;
45 X509_NAME *subject, *issuer;
46 X509 *cert;
47 X509_CRL *crl;
48 X509_REVOKED *revoked;
49 EVP_PKEY *pubkey;
50 int i, n, rc;
51 const ASN1_TIME *next_update = NULL;
52 struct nc_client_tls_opts *opts;
53
54 if (!preverify_ok) {
55 return 0;
56 }
57
58 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
59
60 if (!opts->crl_store) {
61 /* nothing to check */
62 return 1;
63 }
64
65 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
66 subject = X509_get_subject_name(cert);
67 issuer = X509_get_issuer_name(cert);
68
69 /* try to retrieve a CRL corresponding to the _subject_ of
70 * the current certificate in order to verify it's integrity */
71 store_ctx = X509_STORE_CTX_new();
72 obj = X509_OBJECT_new();
73 X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
74 rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, subject, obj);
75 X509_STORE_CTX_free(store_ctx);
76 crl = X509_OBJECT_get0_X509_CRL(obj);
77 if (rc > 0 && crl) {
78 next_update = X509_CRL_get0_nextUpdate(crl);
79
80 /* verify the signature on this CRL */
81 pubkey = X509_get_pubkey(cert);
82 if (X509_CRL_verify(crl, pubkey) <= 0) {
83 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
84 X509_OBJECT_free(obj);
85 if (pubkey) {
86 EVP_PKEY_free(pubkey);
87 }
88 return 0; /* fail */
89 }
90 if (pubkey) {
91 EVP_PKEY_free(pubkey);
92 }
93
94 /* check date of CRL to make sure it's not expired */
95 if (!next_update) {
96 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
97 X509_OBJECT_free(obj);
98 return 0; /* fail */
99 }
100 if (X509_cmp_current_time(next_update) < 0) {
101 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
102 X509_OBJECT_free(obj);
103 return 0; /* fail */
104 }
105 X509_OBJECT_free(obj);
106 }
107
108 /* try to retrieve a CRL corresponding to the _issuer_ of
109 * the current certificate in order to check for revocation */
110 store_ctx = X509_STORE_CTX_new();
111 obj = X509_OBJECT_new();
112 X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
113 rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj);
114 X509_STORE_CTX_free(store_ctx);
115 crl = X509_OBJECT_get0_X509_CRL(obj);
116 if (rc > 0 && crl) {
117 /* check if the current certificate is revoked by this CRL */
118 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
119 for (i = 0; i < n; i++) {
120 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
121 if (ASN1_INTEGER_cmp(X509_REVOKED_get0_serialNumber(revoked), X509_get_serialNumber(cert)) == 0) {
122 ERR("Certificate revoked!");
123 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
124 X509_OBJECT_free(obj);
125 return 0; /* fail */
126 }
127 }
128 X509_OBJECT_free(obj);
129 }
130
131 return 1; /* success */
132}
133
134#else
135
Radek Krejci9f03b482015-10-22 16:02:10 +0200136static int
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100137tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200138{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100139 X509_STORE_CTX store_ctx;
140 X509_OBJECT obj;
141 X509_NAME *subject, *issuer;
142 X509 *cert;
143 X509_CRL *crl;
144 X509_REVOKED *revoked;
145 EVP_PKEY *pubkey;
146 int i, n, rc;
147 ASN1_TIME *next_update = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100148 struct nc_client_tls_opts *opts;
Radek Krejci9f03b482015-10-22 16:02:10 +0200149
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100150 if (!preverify_ok) {
151 return 0;
152 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200153
Michal Vasko3031aae2016-01-27 16:07:18 +0100154 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
155
156 if (!opts->crl_store) {
157 /* nothing to check */
158 return 1;
159 }
160
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100161 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
162 subject = X509_get_subject_name(cert);
163 issuer = X509_get_issuer_name(cert);
164
165 /* try to retrieve a CRL corresponding to the _subject_ of
166 * the current certificate in order to verify it's integrity */
167 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100168 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100169 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
170 X509_STORE_CTX_cleanup(&store_ctx);
171 crl = obj.data.crl;
172 if (rc > 0 && crl) {
173 next_update = X509_CRL_get_nextUpdate(crl);
174
175 /* verify the signature on this CRL */
176 pubkey = X509_get_pubkey(cert);
177 if (X509_CRL_verify(crl, pubkey) <= 0) {
178 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
179 X509_OBJECT_free_contents(&obj);
180 if (pubkey) {
181 EVP_PKEY_free(pubkey);
182 }
183 return 0; /* fail */
184 }
185 if (pubkey) {
186 EVP_PKEY_free(pubkey);
187 }
188
189 /* check date of CRL to make sure it's not expired */
190 if (!next_update) {
191 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
192 X509_OBJECT_free_contents(&obj);
193 return 0; /* fail */
194 }
195 if (X509_cmp_current_time(next_update) < 0) {
196 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
197 X509_OBJECT_free_contents(&obj);
198 return 0; /* fail */
199 }
200 X509_OBJECT_free_contents(&obj);
201 }
202
203 /* try to retrieve a CRL corresponding to the _issuer_ of
204 * the current certificate in order to check for revocation */
205 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100206 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100207 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
208 X509_STORE_CTX_cleanup(&store_ctx);
209 crl = obj.data.crl;
210 if (rc > 0 && crl) {
211 /* check if the current certificate is revoked by this CRL */
212 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
213 for (i = 0; i < n; i++) {
214 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
215 if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
216 ERR("Certificate revoked!");
217 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
218 X509_OBJECT_free_contents(&obj);
219 return 0; /* fail */
220 }
221 }
222 X509_OBJECT_free_contents(&obj);
223 }
224
225 return 1; /* success */
226}
227
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100228#endif
229
Michal Vaskoe22c6732016-01-29 11:03:02 +0100230static void
231_nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
232{
233 free(opts->cert_path);
234 free(opts->key_path);
235 free(opts->ca_file);
236 free(opts->ca_dir);
237 SSL_CTX_free(opts->tls_ctx);
238
239 free(opts->crl_file);
240 free(opts->crl_dir);
241 X509_STORE_free(opts->crl_store);
242}
243
Michal Vaskob7558c52016-02-26 15:04:19 +0100244void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100245nc_client_tls_destroy_opts(void)
246{
247 _nc_client_tls_destroy_opts(&tls_opts);
248 _nc_client_tls_destroy_opts(&tls_ch_opts);
249}
250
Michal Vasko3031aae2016-01-27 16:07:18 +0100251static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100252_nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key, struct nc_client_tls_opts *opts)
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100253{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100254 if (!client_cert) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200255 ERRARG("client_cert");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100256 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100257 }
258
Michal Vasko3031aae2016-01-27 16:07:18 +0100259 free(opts->cert_path);
260 free(opts->key_path);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100261
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100262 opts->cert_path = strdup(client_cert);
263 if (!opts->cert_path) {
264 ERRMEM;
265 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100266 }
267
Michal Vasko3031aae2016-01-27 16:07:18 +0100268 if (client_key) {
269 opts->key_path = strdup(client_key);
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100270 if (!opts->key_path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100271 ERRMEM;
272 return -1;
273 }
274 } else {
275 opts->key_path = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100276 }
277
Michal Vasko3031aae2016-01-27 16:07:18 +0100278 opts->tls_ctx_change = 1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100279
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100280 return 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100281}
282
Michal Vasko3031aae2016-01-27 16:07:18 +0100283API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100284nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100285{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100286 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100287}
288
289API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100290nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko3031aae2016-01-27 16:07:18 +0100291{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100292 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_ch_opts);
293}
294
295static void
296_nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts)
297{
298 if (!client_cert && !client_key) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200299 ERRARG("client_cert and client_key");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100300 return;
301 }
302
303 if (client_cert) {
304 *client_cert = opts->cert_path;
305 }
306 if (client_key) {
307 *client_key = opts->key_path;
308 }
309}
310
311API void
312nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key)
313{
314 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_opts);
315}
316
317API void
318nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key)
319{
320 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100321}
322
323static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100324_nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, struct nc_client_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100325{
Michal Vasko3031aae2016-01-27 16:07:18 +0100326 if (!ca_file && !ca_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200327 ERRARG("ca_file and ca_dir");
Michal Vasko3031aae2016-01-27 16:07:18 +0100328 return -1;
329 }
330
Michal Vasko3031aae2016-01-27 16:07:18 +0100331 free(opts->ca_file);
332 free(opts->ca_dir);
333
334 if (ca_file) {
335 opts->ca_file = strdup(ca_file);
336 if (!opts->ca_file) {
337 ERRMEM;
338 return -1;
339 }
340 } else {
341 opts->ca_file = NULL;
342 }
343
344 if (ca_dir) {
345 opts->ca_dir = strdup(ca_dir);
346 if (!opts->ca_dir) {
347 ERRMEM;
348 return -1;
349 }
350 } else {
351 opts->ca_dir = NULL;
352 }
353
354 opts->tls_ctx_change = 1;
355
356 return 0;
357}
358
359API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100360nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100361{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100362 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100363}
364
365API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100366nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100367{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100368 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
369}
370
371static void
372_nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts)
373{
374 if (!ca_file && !ca_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200375 ERRARG("ca_file and ca_dir");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100376 return;
377 }
378
379 if (ca_file) {
380 *ca_file = opts->ca_file;
381 }
382 if (ca_dir) {
383 *ca_dir = opts->ca_dir;
384 }
385}
386
387API void
388nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
389{
390 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
391}
392
393API void
394nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
395{
396 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100397}
398
399static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100400_nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct nc_client_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100401{
Michal Vasko3031aae2016-01-27 16:07:18 +0100402 if (!crl_file && !crl_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200403 ERRARG("crl_file and crl_dir");
Michal Vasko3031aae2016-01-27 16:07:18 +0100404 return -1;
405 }
406
Michal Vasko3031aae2016-01-27 16:07:18 +0100407 free(opts->crl_file);
408 free(opts->crl_dir);
409
410 if (crl_file) {
411 opts->crl_file = strdup(crl_file);
412 if (!opts->crl_file) {
413 ERRMEM;
414 return -1;
415 }
416 } else {
417 opts->crl_file = NULL;
418 }
419
420 if (crl_dir) {
421 opts->crl_dir = strdup(crl_dir);
422 if (!opts->crl_dir) {
423 ERRMEM;
424 return -1;
425 }
426 } else {
427 opts->crl_dir = NULL;
428 }
429
430 opts->crl_store_change = 1;
431
432 return 0;
433}
434
435API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100436nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100437{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100438 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100439}
440
441API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100442nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100443{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100444 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts);
445}
446
447static void
448_nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts)
449{
450 if (!crl_file && !crl_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200451 ERRARG("crl_file and crl_dir");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100452 return;
453 }
454
455 if (crl_file) {
456 *crl_file = opts->crl_file;
457 }
458 if (crl_dir) {
459 *crl_dir = opts->crl_dir;
460 }
461}
462
463API void
464nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir)
465{
466 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts);
467}
468
469API void
470nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir)
471{
472 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100473}
474
475API int
476nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
477{
478 return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL);
479}
480
481API int
482nc_client_tls_ch_del_bind(const char *address, uint16_t port)
483{
484 return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
485}
486
Michal Vasko3031aae2016-01-27 16:07:18 +0100487static int
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100488nc_client_tls_update_opts(struct nc_client_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100489{
490 char *key;
491 X509_LOOKUP *lookup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100492
493 if (!opts->tls_ctx || opts->tls_ctx_change) {
Michal Vaskoe22c6732016-01-29 11:03:02 +0100494 SSL_CTX_free(opts->tls_ctx);
Michal Vasko3031aae2016-01-27 16:07:18 +0100495
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100496#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
497 /* prepare global SSL context, highest available method is negotiated autmatically */
498 if (!(opts->tls_ctx = SSL_CTX_new(TLS_client_method())))
499#else
Michal Vasko3031aae2016-01-27 16:07:18 +0100500 /* prepare global SSL context, allow only mandatory TLS 1.2 */
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100501 if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method())))
502#endif
503 {
Michal Vasko3031aae2016-01-27 16:07:18 +0100504 ERR("Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
505 return -1;
506 }
507 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
508
509 /* get peer certificate */
510 if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
511 ERR("Loading the client certificate from \'%s\' failed (%s).", opts->cert_path, ERR_reason_error_string(ERR_get_error()));
512 return -1;
513 }
514
515 /* if the file with private key not specified, expect that the private key is stored with the certificate */
516 if (!opts->key_path) {
517 key = opts->cert_path;
518 } else {
519 key = opts->key_path;
520 }
521 if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
522 ERR("Loading the client priavte key from \'%s\' failed (%s).", key, ERR_reason_error_string(ERR_get_error()));
523 return -1;
524 }
525
526 if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
527 ERR("Failed to load the locations of trusted CA certificates (%s).", ERR_reason_error_string(ERR_get_error()));
528 return -1;
529 }
530 }
531
532 if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
533 /* set the revocation store with the correct paths for the callback */
534 X509_STORE_free(opts->crl_store);
535
536 opts->crl_store = X509_STORE_new();
537 if (!opts->crl_store) {
538 ERR("Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
539 return -1;
540 }
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100541
542#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
543 /* whaveter this does... */
Michal Vasko3031aae2016-01-27 16:07:18 +0100544 opts->crl_store->cache = 0;
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100545#endif
Michal Vasko3031aae2016-01-27 16:07:18 +0100546
547 if (opts->crl_file) {
548 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
549 ERR("Failed to add lookup method to CRL checking.");
550 return -1;
551 }
552 if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
553 ERR("Failed to add the revocation lookup file \"%s\".", opts->crl_file);
554 return -1;
555 }
556 }
557
558 if (opts->crl_dir) {
559 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
560 ERR("Failed to add lookup method to CRL checking.");
561 return -1;
562 }
563 if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
564 ERR("Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
565 return -1;
566 }
567 }
568 }
569
570 return 0;
Radek Krejci9f03b482015-10-22 16:02:10 +0200571}
572
573API struct nc_session *
Michal Vasko9bee18d2015-12-08 14:41:42 +0100574nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200575{
Radek Krejci9f03b482015-10-22 16:02:10 +0200576 struct nc_session *session = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +0100577 int sock, verify, ret;
Michal Vasko36c7be82017-02-22 13:37:59 +0100578 struct timespec ts_timeout, ts_cur;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100579
Michal Vasko3031aae2016-01-27 16:07:18 +0100580 if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200581 ERRINIT;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100582 return NULL;
583 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200584
585 /* process parameters */
586 if (!host || strisempty(host)) {
587 host = "localhost";
588 }
589
590 if (!port) {
591 port = NC_PORT_TLS;
592 }
593
Michal Vasko3031aae2016-01-27 16:07:18 +0100594 /* create/update TLS structures */
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100595 if (nc_client_tls_update_opts(&tls_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100596 return NULL;
597 }
598
Radek Krejci9f03b482015-10-22 16:02:10 +0200599 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100600 session = calloc(1, sizeof *session);
Radek Krejci9f03b482015-10-22 16:02:10 +0200601 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100602 ERRMEM;
Radek Krejci9f03b482015-10-22 16:02:10 +0200603 return NULL;
604 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100605 session->status = NC_STATUS_STARTING;
606 session->side = NC_CLIENT;
Radek Krejci9f03b482015-10-22 16:02:10 +0200607
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100608 /* transport lock */
609 session->ti_lock = malloc(sizeof *session->ti_lock);
610 if (!session->ti_lock) {
611 ERRMEM;
612 goto fail;
613 }
614 pthread_mutex_init(session->ti_lock, NULL);
615
616 /* fill the session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100617 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100618 if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100619 ERR("Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100620 goto fail;
621 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200622
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100623 /* create and assign socket */
Michal Vaskof05562c2016-01-20 12:06:43 +0100624 sock = nc_sock_connect(host, port);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100625 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +0200626 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100627 goto fail;
628 }
629 SSL_set_fd(session->ti.tls, sock);
630
631 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
632 SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
633
634 /* connect and perform the handshake */
Michal Vasko36c7be82017-02-22 13:37:59 +0100635 nc_gettimespec(&ts_timeout);
636 nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko3031aae2016-01-27 16:07:18 +0100637 tlsauth_ch = 0;
Michal Vasko0190bc32016-03-02 15:47:49 +0100638 while (((ret = SSL_connect(session->ti.tls)) == -1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) {
639 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +0100640 nc_gettimespec(&ts_cur);
641 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +0100642 ERR("SSL_connect timeout.");
643 goto fail;
644 }
645 }
646 if (ret != 1) {
647 switch (SSL_get_error(session->ti.tls, ret)) {
648 case SSL_ERROR_SYSCALL:
649 ERR("SSL_connect failed (%s).", strerror(errno));
650 break;
651 case SSL_ERROR_SSL:
652 ERR("SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
653 break;
654 default:
655 ERR("SSL_connect failed.");
656 break;
657 }
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100658 goto fail;
659 }
660
661 /* check certificate verification result */
662 verify = SSL_get_verify_result(session->ti.tls);
663 switch (verify) {
664 case X509_V_OK:
665 VRB("Server certificate successfully verified.");
666 break;
667 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100668 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Radek Krejci9f03b482015-10-22 16:02:10 +0200669 }
670
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100671 /* assign context (dicionary needed for handshake) */
672 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +0100673 if (client_opts.schema_searchpath) {
674 ctx = ly_ctx_new(client_opts.schema_searchpath);
675 } else {
676 ctx = ly_ctx_new(SCHEMAS_DIR);
677 }
Michal Vaskoe035b8e2016-03-11 10:10:03 +0100678 /* definitely should not happen, but be ready */
679 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
680 /* that's just it */
681 goto fail;
682 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100683 } else {
684 session->flags |= NC_SESSION_SHAREDCTX;
685 }
686 session->ctx = ctx;
687
Radek Krejci9f03b482015-10-22 16:02:10 +0200688 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200689 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100690 goto fail;
Radek Krejci9f03b482015-10-22 16:02:10 +0200691 }
Michal Vaskoad611702015-12-03 13:41:51 +0100692 session->status = NC_STATUS_RUNNING;
Radek Krejci9f03b482015-10-22 16:02:10 +0200693
Michal Vaskoef578332016-01-25 13:20:09 +0100694 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100695 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100696 }
697
698 /* store information into session and the dictionary */
699 session->host = lydict_insert(ctx, host, 0);
700 session->port = port;
Michal Vasko9bee18d2015-12-08 14:41:42 +0100701 session->username = lydict_insert(ctx, "certificate-based", 0);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100702
Radek Krejci9f03b482015-10-22 16:02:10 +0200703 return session;
704
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100705fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100706 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200707 return NULL;
708}
709
710API struct nc_session *
711nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
712{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100713 struct nc_session *session;
Radek Krejci9f03b482015-10-22 16:02:10 +0200714
Michal Vasko45e53ae2016-04-07 11:46:03 +0200715 if (!tls) {
716 ERRARG("tls");
717 return NULL;
718 } else if (!SSL_is_init_finished(tls)) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100719 ERR("Supplied TLS session is not fully connected!");
720 return NULL;
721 }
722
723 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100724 session = calloc(1, sizeof *session);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100725 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100726 ERRMEM;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100727 return NULL;
728 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100729 session->status = NC_STATUS_STARTING;
730 session->side = NC_CLIENT;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100731
732 /* transport lock */
733 session->ti_lock = malloc(sizeof *session->ti_lock);
734 if (!session->ti_lock) {
735 ERRMEM;
736 goto fail;
737 }
738 pthread_mutex_init(session->ti_lock, NULL);
739
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100740 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100741 session->ti.tls = tls;
742
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100743 /* assign context (dicionary needed for handshake) */
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100744 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +0100745 if (client_opts.schema_searchpath) {
746 ctx = ly_ctx_new(client_opts.schema_searchpath);
747 } else {
748 ctx = ly_ctx_new(SCHEMAS_DIR);
749 }
Michal Vaskoe035b8e2016-03-11 10:10:03 +0100750 /* definitely should not happen, but be ready */
751 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
752 /* that's just it */
753 goto fail;
754 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100755 } else {
756 session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100757 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100758 session->ctx = ctx;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100759
760 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200761 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100762 goto fail;
763 }
Michal Vaskoad611702015-12-03 13:41:51 +0100764 session->status = NC_STATUS_RUNNING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100765
Michal Vaskoef578332016-01-25 13:20:09 +0100766 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100767 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100768 }
769
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100770 return session;
771
772fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100773 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200774 return NULL;
775}
776
Michal Vasko3031aae2016-01-27 16:07:18 +0100777struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +0100778nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout)
Michal Vasko80cad7f2015-12-08 14:42:27 +0100779{
Michal Vasko36c7be82017-02-22 13:37:59 +0100780 int verify, ret;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100781 SSL *tls;
782 struct nc_session *session;
Michal Vasko36c7be82017-02-22 13:37:59 +0100783 struct timespec ts_timeout, ts_cur;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100784
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100785 if (nc_client_tls_update_opts(&tls_ch_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100786 close(sock);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100787 return NULL;
788 }
789
Michal Vasko3031aae2016-01-27 16:07:18 +0100790 if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
Michal Vasko80cad7f2015-12-08 14:42:27 +0100791 ERR("Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
792 close(sock);
793 return NULL;
794 }
795
796 SSL_set_fd(tls, sock);
797
798 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
799 SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
800
801 /* connect and perform the handshake */
Michal Vasko36c7be82017-02-22 13:37:59 +0100802 if (timeout > -1) {
803 nc_gettimespec(&ts_timeout);
804 nc_addtimespec(&ts_timeout, timeout);
805 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100806 tlsauth_ch = 1;
Michal Vasko0190bc32016-03-02 15:47:49 +0100807 while (((ret = SSL_connect(tls)) == -1) && (SSL_get_error(tls, ret) == SSL_ERROR_WANT_READ)) {
808 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +0100809 if (timeout > -1) {
810 nc_gettimespec(&ts_cur);
811 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
812 ERR("SSL_connect timeout.");
813 SSL_free(tls);
814 return NULL;
815 }
Michal Vasko0190bc32016-03-02 15:47:49 +0100816 }
817 }
818 if (ret != 1) {
819 switch (SSL_get_error(tls, ret)) {
820 case SSL_ERROR_SYSCALL:
821 ERR("SSL_connect failed (%s).", strerror(errno));
822 break;
823 case SSL_ERROR_SSL:
824 ERR("SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
825 break;
826 default:
827 ERR("SSL_connect failed.");
828 break;
829 }
Michal Vasko80cad7f2015-12-08 14:42:27 +0100830 SSL_free(tls);
831 return NULL;
832 }
833
834 /* check certificate verification result */
835 verify = SSL_get_verify_result(tls);
836 switch (verify) {
837 case X509_V_OK:
838 VRB("Server certificate successfully verified.");
839 break;
840 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100841 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Michal Vasko80cad7f2015-12-08 14:42:27 +0100842 }
843
844 session = nc_connect_libssl(tls, ctx);
845 if (session) {
Michal Vasko4282fae2016-02-18 10:03:42 +0100846 session->flags |= NC_SESSION_CALLHOME;
847
Michal Vasko80cad7f2015-12-08 14:42:27 +0100848 /* store information into session and the dictionary */
Michal Vasko3031aae2016-01-27 16:07:18 +0100849 session->host = lydict_insert(session->ctx, host, 0);
Michal Vasko80cad7f2015-12-08 14:42:27 +0100850 session->port = port;
851 session->username = lydict_insert(session->ctx, "certificate-based", 0);
852 }
853
854 return session;
855}