blob: 71bb134dd9e373ed4e0d31816c467ff92105191e [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
Radek Krejci62aa0642017-05-25 16:33:49 +020032struct nc_client_context *nc_client_context_location(void);
Radek Krejcifd5b6682017-06-13 15:52:53 +020033int nc_session_new_ctx( struct nc_session *session, struct ly_ctx *ctx);
Radek Krejci62aa0642017-05-25 16:33:49 +020034
35#define client_opts nc_client_context_location()->opts
36#define tls_opts nc_client_context_location()->tls_opts
37#define tls_ch_opts nc_client_context_location()->tls_ch_opts
Michal Vasko3031aae2016-01-27 16:07:18 +010038
39static int tlsauth_ch;
Radek Krejci9f03b482015-10-22 16:02:10 +020040
Michal Vasko18aeb5d2017-02-17 09:23:56 +010041#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
42
43static int
44tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
45{
46 X509_STORE_CTX *store_ctx;
47 X509_OBJECT *obj;
48 X509_NAME *subject, *issuer;
49 X509 *cert;
50 X509_CRL *crl;
51 X509_REVOKED *revoked;
52 EVP_PKEY *pubkey;
53 int i, n, rc;
54 const ASN1_TIME *next_update = NULL;
55 struct nc_client_tls_opts *opts;
56
57 if (!preverify_ok) {
58 return 0;
59 }
60
61 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
62
63 if (!opts->crl_store) {
64 /* nothing to check */
65 return 1;
66 }
67
68 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
69 subject = X509_get_subject_name(cert);
70 issuer = X509_get_issuer_name(cert);
71
72 /* try to retrieve a CRL corresponding to the _subject_ of
73 * the current certificate in order to verify it's integrity */
74 store_ctx = X509_STORE_CTX_new();
75 obj = X509_OBJECT_new();
76 X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
77 rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, subject, obj);
78 X509_STORE_CTX_free(store_ctx);
79 crl = X509_OBJECT_get0_X509_CRL(obj);
80 if (rc > 0 && crl) {
81 next_update = X509_CRL_get0_nextUpdate(crl);
82
83 /* verify the signature on this CRL */
84 pubkey = X509_get_pubkey(cert);
85 if (X509_CRL_verify(crl, pubkey) <= 0) {
86 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
87 X509_OBJECT_free(obj);
88 if (pubkey) {
89 EVP_PKEY_free(pubkey);
90 }
91 return 0; /* fail */
92 }
93 if (pubkey) {
94 EVP_PKEY_free(pubkey);
95 }
96
97 /* check date of CRL to make sure it's not expired */
98 if (!next_update) {
99 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
100 X509_OBJECT_free(obj);
101 return 0; /* fail */
102 }
103 if (X509_cmp_current_time(next_update) < 0) {
104 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
105 X509_OBJECT_free(obj);
106 return 0; /* fail */
107 }
108 X509_OBJECT_free(obj);
109 }
110
111 /* try to retrieve a CRL corresponding to the _issuer_ of
112 * the current certificate in order to check for revocation */
113 store_ctx = X509_STORE_CTX_new();
114 obj = X509_OBJECT_new();
115 X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
116 rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj);
117 X509_STORE_CTX_free(store_ctx);
118 crl = X509_OBJECT_get0_X509_CRL(obj);
119 if (rc > 0 && crl) {
120 /* check if the current certificate is revoked by this CRL */
121 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
122 for (i = 0; i < n; i++) {
123 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
124 if (ASN1_INTEGER_cmp(X509_REVOKED_get0_serialNumber(revoked), X509_get_serialNumber(cert)) == 0) {
125 ERR("Certificate revoked!");
126 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
127 X509_OBJECT_free(obj);
128 return 0; /* fail */
129 }
130 }
131 X509_OBJECT_free(obj);
132 }
133
134 return 1; /* success */
135}
136
137#else
138
Radek Krejci9f03b482015-10-22 16:02:10 +0200139static int
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100140tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200141{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100142 X509_STORE_CTX store_ctx;
143 X509_OBJECT obj;
144 X509_NAME *subject, *issuer;
145 X509 *cert;
146 X509_CRL *crl;
147 X509_REVOKED *revoked;
148 EVP_PKEY *pubkey;
149 int i, n, rc;
150 ASN1_TIME *next_update = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100151 struct nc_client_tls_opts *opts;
Radek Krejci9f03b482015-10-22 16:02:10 +0200152
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100153 if (!preverify_ok) {
154 return 0;
155 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200156
Michal Vasko3031aae2016-01-27 16:07:18 +0100157 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
158
159 if (!opts->crl_store) {
160 /* nothing to check */
161 return 1;
162 }
163
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100164 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
165 subject = X509_get_subject_name(cert);
166 issuer = X509_get_issuer_name(cert);
167
168 /* try to retrieve a CRL corresponding to the _subject_ of
169 * the current certificate in order to verify it's integrity */
170 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100171 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100172 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
173 X509_STORE_CTX_cleanup(&store_ctx);
174 crl = obj.data.crl;
175 if (rc > 0 && crl) {
176 next_update = X509_CRL_get_nextUpdate(crl);
177
178 /* verify the signature on this CRL */
179 pubkey = X509_get_pubkey(cert);
180 if (X509_CRL_verify(crl, pubkey) <= 0) {
181 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
182 X509_OBJECT_free_contents(&obj);
183 if (pubkey) {
184 EVP_PKEY_free(pubkey);
185 }
186 return 0; /* fail */
187 }
188 if (pubkey) {
189 EVP_PKEY_free(pubkey);
190 }
191
192 /* check date of CRL to make sure it's not expired */
193 if (!next_update) {
194 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
195 X509_OBJECT_free_contents(&obj);
196 return 0; /* fail */
197 }
198 if (X509_cmp_current_time(next_update) < 0) {
199 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
200 X509_OBJECT_free_contents(&obj);
201 return 0; /* fail */
202 }
203 X509_OBJECT_free_contents(&obj);
204 }
205
206 /* try to retrieve a CRL corresponding to the _issuer_ of
207 * the current certificate in order to check for revocation */
208 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100209 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100210 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
211 X509_STORE_CTX_cleanup(&store_ctx);
212 crl = obj.data.crl;
213 if (rc > 0 && crl) {
214 /* check if the current certificate is revoked by this CRL */
215 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
216 for (i = 0; i < n; i++) {
217 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
218 if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
219 ERR("Certificate revoked!");
220 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
221 X509_OBJECT_free_contents(&obj);
222 return 0; /* fail */
223 }
224 }
225 X509_OBJECT_free_contents(&obj);
226 }
227
228 return 1; /* success */
229}
230
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100231#endif
232
Michal Vaskoe22c6732016-01-29 11:03:02 +0100233static void
234_nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
235{
236 free(opts->cert_path);
237 free(opts->key_path);
238 free(opts->ca_file);
239 free(opts->ca_dir);
240 SSL_CTX_free(opts->tls_ctx);
241
242 free(opts->crl_file);
243 free(opts->crl_dir);
244 X509_STORE_free(opts->crl_store);
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200245
246 memset(opts, 0, sizeof *opts);
Michal Vaskoe22c6732016-01-29 11:03:02 +0100247}
248
Michal Vaskob7558c52016-02-26 15:04:19 +0100249void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100250nc_client_tls_destroy_opts(void)
251{
252 _nc_client_tls_destroy_opts(&tls_opts);
253 _nc_client_tls_destroy_opts(&tls_ch_opts);
254}
255
Michal Vasko3031aae2016-01-27 16:07:18 +0100256static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100257_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 +0100258{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100259 if (!client_cert) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200260 ERRARG("client_cert");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100261 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100262 }
263
Michal Vasko3031aae2016-01-27 16:07:18 +0100264 free(opts->cert_path);
265 free(opts->key_path);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100266
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100267 opts->cert_path = strdup(client_cert);
268 if (!opts->cert_path) {
269 ERRMEM;
270 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100271 }
272
Michal Vasko3031aae2016-01-27 16:07:18 +0100273 if (client_key) {
274 opts->key_path = strdup(client_key);
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100275 if (!opts->key_path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100276 ERRMEM;
277 return -1;
278 }
279 } else {
280 opts->key_path = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100281 }
282
Michal Vasko3031aae2016-01-27 16:07:18 +0100283 opts->tls_ctx_change = 1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100284
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100285 return 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100286}
287
Michal Vasko3031aae2016-01-27 16:07:18 +0100288API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100289nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100290{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100291 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100292}
293
294API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100295nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko3031aae2016-01-27 16:07:18 +0100296{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100297 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_ch_opts);
298}
299
300static void
301_nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts)
302{
303 if (!client_cert && !client_key) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200304 ERRARG("client_cert and client_key");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100305 return;
306 }
307
308 if (client_cert) {
309 *client_cert = opts->cert_path;
310 }
311 if (client_key) {
312 *client_key = opts->key_path;
313 }
314}
315
316API void
317nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key)
318{
319 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_opts);
320}
321
322API void
323nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key)
324{
325 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100326}
327
328static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100329_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 +0100330{
Michal Vasko3031aae2016-01-27 16:07:18 +0100331 if (!ca_file && !ca_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200332 ERRARG("ca_file and ca_dir");
Michal Vasko3031aae2016-01-27 16:07:18 +0100333 return -1;
334 }
335
Michal Vasko3031aae2016-01-27 16:07:18 +0100336 free(opts->ca_file);
337 free(opts->ca_dir);
338
339 if (ca_file) {
340 opts->ca_file = strdup(ca_file);
341 if (!opts->ca_file) {
342 ERRMEM;
343 return -1;
344 }
345 } else {
346 opts->ca_file = NULL;
347 }
348
349 if (ca_dir) {
350 opts->ca_dir = strdup(ca_dir);
351 if (!opts->ca_dir) {
352 ERRMEM;
353 return -1;
354 }
355 } else {
356 opts->ca_dir = NULL;
357 }
358
359 opts->tls_ctx_change = 1;
360
361 return 0;
362}
363
364API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100365nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100366{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100367 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100368}
369
370API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100371nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100372{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100373 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
374}
375
376static void
377_nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts)
378{
379 if (!ca_file && !ca_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200380 ERRARG("ca_file and ca_dir");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100381 return;
382 }
383
384 if (ca_file) {
385 *ca_file = opts->ca_file;
386 }
387 if (ca_dir) {
388 *ca_dir = opts->ca_dir;
389 }
390}
391
392API void
393nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
394{
395 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
396}
397
398API void
399nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
400{
401 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100402}
403
404static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100405_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 +0100406{
Michal Vasko3031aae2016-01-27 16:07:18 +0100407 if (!crl_file && !crl_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200408 ERRARG("crl_file and crl_dir");
Michal Vasko3031aae2016-01-27 16:07:18 +0100409 return -1;
410 }
411
Michal Vasko3031aae2016-01-27 16:07:18 +0100412 free(opts->crl_file);
413 free(opts->crl_dir);
414
415 if (crl_file) {
416 opts->crl_file = strdup(crl_file);
417 if (!opts->crl_file) {
418 ERRMEM;
419 return -1;
420 }
421 } else {
422 opts->crl_file = NULL;
423 }
424
425 if (crl_dir) {
426 opts->crl_dir = strdup(crl_dir);
427 if (!opts->crl_dir) {
428 ERRMEM;
429 return -1;
430 }
431 } else {
432 opts->crl_dir = NULL;
433 }
434
435 opts->crl_store_change = 1;
436
437 return 0;
438}
439
440API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100441nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100442{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100443 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100444}
445
446API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100447nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100448{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100449 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts);
450}
451
452static void
453_nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts)
454{
455 if (!crl_file && !crl_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200456 ERRARG("crl_file and crl_dir");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100457 return;
458 }
459
460 if (crl_file) {
461 *crl_file = opts->crl_file;
462 }
463 if (crl_dir) {
464 *crl_dir = opts->crl_dir;
465 }
466}
467
468API void
469nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir)
470{
471 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts);
472}
473
474API void
475nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir)
476{
477 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100478}
479
480API int
481nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
482{
483 return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL);
484}
485
486API int
487nc_client_tls_ch_del_bind(const char *address, uint16_t port)
488{
489 return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
490}
491
Michal Vasko3031aae2016-01-27 16:07:18 +0100492static int
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100493nc_client_tls_update_opts(struct nc_client_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100494{
495 char *key;
496 X509_LOOKUP *lookup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100497
498 if (!opts->tls_ctx || opts->tls_ctx_change) {
Michal Vaskoe22c6732016-01-29 11:03:02 +0100499 SSL_CTX_free(opts->tls_ctx);
Michal Vasko3031aae2016-01-27 16:07:18 +0100500
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100501#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
502 /* prepare global SSL context, highest available method is negotiated autmatically */
503 if (!(opts->tls_ctx = SSL_CTX_new(TLS_client_method())))
504#else
Michal Vasko3031aae2016-01-27 16:07:18 +0100505 /* prepare global SSL context, allow only mandatory TLS 1.2 */
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100506 if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method())))
507#endif
508 {
Michal Vasko3031aae2016-01-27 16:07:18 +0100509 ERR("Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
510 return -1;
511 }
512 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
513
514 /* get peer certificate */
515 if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
516 ERR("Loading the client certificate from \'%s\' failed (%s).", opts->cert_path, ERR_reason_error_string(ERR_get_error()));
517 return -1;
518 }
519
520 /* if the file with private key not specified, expect that the private key is stored with the certificate */
521 if (!opts->key_path) {
522 key = opts->cert_path;
523 } else {
524 key = opts->key_path;
525 }
526 if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
527 ERR("Loading the client priavte key from \'%s\' failed (%s).", key, ERR_reason_error_string(ERR_get_error()));
528 return -1;
529 }
530
531 if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
532 ERR("Failed to load the locations of trusted CA certificates (%s).", ERR_reason_error_string(ERR_get_error()));
533 return -1;
534 }
535 }
536
537 if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
538 /* set the revocation store with the correct paths for the callback */
539 X509_STORE_free(opts->crl_store);
540
541 opts->crl_store = X509_STORE_new();
542 if (!opts->crl_store) {
543 ERR("Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
544 return -1;
545 }
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100546
547#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
548 /* whaveter this does... */
Michal Vasko3031aae2016-01-27 16:07:18 +0100549 opts->crl_store->cache = 0;
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100550#endif
Michal Vasko3031aae2016-01-27 16:07:18 +0100551
552 if (opts->crl_file) {
553 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
554 ERR("Failed to add lookup method to CRL checking.");
555 return -1;
556 }
557 if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
558 ERR("Failed to add the revocation lookup file \"%s\".", opts->crl_file);
559 return -1;
560 }
561 }
562
563 if (opts->crl_dir) {
564 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
565 ERR("Failed to add lookup method to CRL checking.");
566 return -1;
567 }
568 if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
569 ERR("Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
570 return -1;
571 }
572 }
573 }
574
575 return 0;
Radek Krejci9f03b482015-10-22 16:02:10 +0200576}
577
578API struct nc_session *
Michal Vasko9bee18d2015-12-08 14:41:42 +0100579nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200580{
Radek Krejci9f03b482015-10-22 16:02:10 +0200581 struct nc_session *session = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +0100582 int sock, verify, ret;
Michal Vasko36c7be82017-02-22 13:37:59 +0100583 struct timespec ts_timeout, ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +0100584 char *ip_host = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100585
Michal Vasko3031aae2016-01-27 16:07:18 +0100586 if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200587 ERRINIT;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100588 return NULL;
589 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200590
591 /* process parameters */
592 if (!host || strisempty(host)) {
593 host = "localhost";
594 }
595
596 if (!port) {
597 port = NC_PORT_TLS;
598 }
599
Michal Vasko3031aae2016-01-27 16:07:18 +0100600 /* create/update TLS structures */
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100601 if (nc_client_tls_update_opts(&tls_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100602 return NULL;
603 }
604
Radek Krejci9f03b482015-10-22 16:02:10 +0200605 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200606 session = nc_new_session(NC_CLIENT, 0);
Radek Krejci9f03b482015-10-22 16:02:10 +0200607 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100608 ERRMEM;
Radek Krejci9f03b482015-10-22 16:02:10 +0200609 return NULL;
610 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100611 session->status = NC_STATUS_STARTING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100612
613 /* fill the session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100614 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100615 if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100616 ERR("Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100617 goto fail;
618 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200619
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100620 /* create and assign socket */
Michal Vasko66032bc2019-01-22 15:03:12 +0100621 sock = nc_sock_connect(host, port, -1, NULL, &ip_host);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100622 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +0200623 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100624 goto fail;
625 }
626 SSL_set_fd(session->ti.tls, sock);
627
628 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
629 SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
630
631 /* connect and perform the handshake */
Michal Vaskoe852c8b2017-10-05 10:02:20 +0200632 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100633 nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko3031aae2016-01-27 16:07:18 +0100634 tlsauth_ch = 0;
Michal Vasko0190bc32016-03-02 15:47:49 +0100635 while (((ret = SSL_connect(session->ti.tls)) == -1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) {
636 usleep(NC_TIMEOUT_STEP);
Michal Vaskoe852c8b2017-10-05 10:02:20 +0200637 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100638 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +0100639 ERR("SSL_connect timeout.");
640 goto fail;
641 }
642 }
643 if (ret != 1) {
644 switch (SSL_get_error(session->ti.tls, ret)) {
645 case SSL_ERROR_SYSCALL:
646 ERR("SSL_connect failed (%s).", strerror(errno));
647 break;
648 case SSL_ERROR_SSL:
649 ERR("SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
650 break;
651 default:
652 ERR("SSL_connect failed.");
653 break;
654 }
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100655 goto fail;
656 }
657
658 /* check certificate verification result */
659 verify = SSL_get_verify_result(session->ti.tls);
660 switch (verify) {
661 case X509_V_OK:
662 VRB("Server certificate successfully verified.");
663 break;
664 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100665 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Radek Krejci9f03b482015-10-22 16:02:10 +0200666 }
667
Radek Krejcifd5b6682017-06-13 15:52:53 +0200668 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
669 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100670 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200671 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100672
Radek Krejci9f03b482015-10-22 16:02:10 +0200673 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200674 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100675 goto fail;
Radek Krejci9f03b482015-10-22 16:02:10 +0200676 }
Michal Vaskoad611702015-12-03 13:41:51 +0100677 session->status = NC_STATUS_RUNNING;
Radek Krejci9f03b482015-10-22 16:02:10 +0200678
Michal Vaskoef578332016-01-25 13:20:09 +0100679 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100680 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100681 }
682
683 /* store information into session and the dictionary */
Michal Vasko66032bc2019-01-22 15:03:12 +0100684 session->host = lydict_insert_zc(ctx, ip_host);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100685 session->port = port;
Michal Vasko9bee18d2015-12-08 14:41:42 +0100686 session->username = lydict_insert(ctx, "certificate-based", 0);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100687
Radek Krejci9f03b482015-10-22 16:02:10 +0200688 return session;
689
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100690fail:
Michal Vasko66032bc2019-01-22 15:03:12 +0100691 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100692 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200693 return NULL;
694}
695
696API struct nc_session *
697nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
698{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100699 struct nc_session *session;
Radek Krejci9f03b482015-10-22 16:02:10 +0200700
Michal Vasko45e53ae2016-04-07 11:46:03 +0200701 if (!tls) {
702 ERRARG("tls");
703 return NULL;
704 } else if (!SSL_is_init_finished(tls)) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100705 ERR("Supplied TLS session is not fully connected!");
706 return NULL;
707 }
708
709 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200710 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100711 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100712 ERRMEM;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100713 return NULL;
714 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100715 session->status = NC_STATUS_STARTING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100716 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100717 session->ti.tls = tls;
718
Radek Krejcifd5b6682017-06-13 15:52:53 +0200719 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
720 goto fail;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100721 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200722 ctx = session->ctx;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100723
724 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200725 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100726 goto fail;
727 }
Michal Vaskoad611702015-12-03 13:41:51 +0100728 session->status = NC_STATUS_RUNNING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100729
Michal Vaskoef578332016-01-25 13:20:09 +0100730 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100731 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100732 }
733
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100734 return session;
735
736fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100737 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200738 return NULL;
739}
740
Michal Vasko3031aae2016-01-27 16:07:18 +0100741struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +0100742nc_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 +0100743{
Michal Vasko36c7be82017-02-22 13:37:59 +0100744 int verify, ret;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100745 SSL *tls;
746 struct nc_session *session;
Michal Vasko36c7be82017-02-22 13:37:59 +0100747 struct timespec ts_timeout, ts_cur;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100748
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100749 if (nc_client_tls_update_opts(&tls_ch_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100750 close(sock);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100751 return NULL;
752 }
753
Michal Vasko3031aae2016-01-27 16:07:18 +0100754 if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
Michal Vasko80cad7f2015-12-08 14:42:27 +0100755 ERR("Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
756 close(sock);
757 return NULL;
758 }
759
760 SSL_set_fd(tls, sock);
761
762 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
763 SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
764
765 /* connect and perform the handshake */
Michal Vasko36c7be82017-02-22 13:37:59 +0100766 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +0200767 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100768 nc_addtimespec(&ts_timeout, timeout);
769 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100770 tlsauth_ch = 1;
Michal Vasko0190bc32016-03-02 15:47:49 +0100771 while (((ret = SSL_connect(tls)) == -1) && (SSL_get_error(tls, ret) == SSL_ERROR_WANT_READ)) {
772 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +0100773 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +0200774 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100775 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
776 ERR("SSL_connect timeout.");
777 SSL_free(tls);
778 return NULL;
779 }
Michal Vasko0190bc32016-03-02 15:47:49 +0100780 }
781 }
782 if (ret != 1) {
783 switch (SSL_get_error(tls, ret)) {
784 case SSL_ERROR_SYSCALL:
785 ERR("SSL_connect failed (%s).", strerror(errno));
786 break;
787 case SSL_ERROR_SSL:
788 ERR("SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
789 break;
790 default:
791 ERR("SSL_connect failed.");
792 break;
793 }
Michal Vasko80cad7f2015-12-08 14:42:27 +0100794 SSL_free(tls);
795 return NULL;
796 }
797
798 /* check certificate verification result */
799 verify = SSL_get_verify_result(tls);
800 switch (verify) {
801 case X509_V_OK:
802 VRB("Server certificate successfully verified.");
803 break;
804 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100805 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Michal Vasko80cad7f2015-12-08 14:42:27 +0100806 }
807
808 session = nc_connect_libssl(tls, ctx);
809 if (session) {
Michal Vasko4282fae2016-02-18 10:03:42 +0100810 session->flags |= NC_SESSION_CALLHOME;
811
Michal Vasko80cad7f2015-12-08 14:42:27 +0100812 /* store information into session and the dictionary */
Michal Vasko3031aae2016-01-27 16:07:18 +0100813 session->host = lydict_insert(session->ctx, host, 0);
Michal Vasko80cad7f2015-12-08 14:42:27 +0100814 session->port = port;
815 session->username = lydict_insert(session->ctx, "certificate-based", 0);
816 }
817
818 return session;
819}