blob: a7c28116d31cb0dd792e399e596e685e2372e120 [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 Vasko086311b2016-01-08 09:53:11 +010024#include <openssl/err.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020025#include <openssl/ossl_typ.h>
Michal Vaskodae48322016-02-25 14:49:48 +010026#include <openssl/x509.h>
Radek Krejci9f03b482015-10-22 16:02:10 +020027
Michal Vaskob83a3fa2021-05-26 09:53:42 +020028#include "libnetconf.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010029#include "session_client.h"
30#include "session_client_ch.h"
Michal Vasko11d4cdb2015-10-29 11:42:52 +010031
Rosen Penev4f552d62019-06-26 16:10:43 -070032#if OPENSSL_VERSION_NUMBER < 0x10100000L
33#define X509_STORE_CTX_get_by_subject X509_STORE_get_by_subject
34#endif
35
Radek Krejci62aa0642017-05-25 16:33:49 +020036struct nc_client_context *nc_client_context_location(void);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020037int nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx);
Radek Krejci62aa0642017-05-25 16:33:49 +020038
39#define client_opts nc_client_context_location()->opts
40#define tls_opts nc_client_context_location()->tls_opts
41#define tls_ch_opts nc_client_context_location()->tls_ch_opts
Michal Vasko3031aae2016-01-27 16:07:18 +010042
43static int tlsauth_ch;
Radek Krejci9f03b482015-10-22 16:02:10 +020044
Michal Vasko18aeb5d2017-02-17 09:23:56 +010045#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
46
47static int
48tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
49{
50 X509_STORE_CTX *store_ctx;
51 X509_OBJECT *obj;
52 X509_NAME *subject, *issuer;
53 X509 *cert;
54 X509_CRL *crl;
55 X509_REVOKED *revoked;
56 EVP_PKEY *pubkey;
57 int i, n, rc;
58 const ASN1_TIME *next_update = NULL;
59 struct nc_client_tls_opts *opts;
60
61 if (!preverify_ok) {
62 return 0;
63 }
64
65 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
66
67 if (!opts->crl_store) {
68 /* nothing to check */
69 return 1;
70 }
71
72 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
73 subject = X509_get_subject_name(cert);
74 issuer = X509_get_issuer_name(cert);
75
76 /* try to retrieve a CRL corresponding to the _subject_ of
77 * the current certificate in order to verify it's integrity */
78 store_ctx = X509_STORE_CTX_new();
79 obj = X509_OBJECT_new();
80 X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
Rosen Penev4f552d62019-06-26 16:10:43 -070081 rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, subject, obj);
Michal Vasko18aeb5d2017-02-17 09:23:56 +010082 X509_STORE_CTX_free(store_ctx);
83 crl = X509_OBJECT_get0_X509_CRL(obj);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020084 if ((rc > 0) && crl) {
Michal Vasko18aeb5d2017-02-17 09:23:56 +010085 next_update = X509_CRL_get0_nextUpdate(crl);
86
87 /* verify the signature on this CRL */
88 pubkey = X509_get_pubkey(cert);
89 if (X509_CRL_verify(crl, pubkey) <= 0) {
90 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
91 X509_OBJECT_free(obj);
92 if (pubkey) {
93 EVP_PKEY_free(pubkey);
94 }
95 return 0; /* fail */
96 }
97 if (pubkey) {
98 EVP_PKEY_free(pubkey);
99 }
100
101 /* check date of CRL to make sure it's not expired */
102 if (!next_update) {
103 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
104 X509_OBJECT_free(obj);
105 return 0; /* fail */
106 }
107 if (X509_cmp_current_time(next_update) < 0) {
108 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
109 X509_OBJECT_free(obj);
110 return 0; /* fail */
111 }
112 X509_OBJECT_free(obj);
113 }
114
115 /* try to retrieve a CRL corresponding to the _issuer_ of
116 * the current certificate in order to check for revocation */
117 store_ctx = X509_STORE_CTX_new();
118 obj = X509_OBJECT_new();
119 X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
Rosen Penev4f552d62019-06-26 16:10:43 -0700120 rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj);
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100121 X509_STORE_CTX_free(store_ctx);
122 crl = X509_OBJECT_get0_X509_CRL(obj);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200123 if ((rc > 0) && crl) {
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100124 /* check if the current certificate is revoked by this CRL */
125 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
126 for (i = 0; i < n; i++) {
127 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
128 if (ASN1_INTEGER_cmp(X509_REVOKED_get0_serialNumber(revoked), X509_get_serialNumber(cert)) == 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200129 ERR(NULL, "Certificate revoked!");
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100130 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
131 X509_OBJECT_free(obj);
132 return 0; /* fail */
133 }
134 }
135 X509_OBJECT_free(obj);
136 }
137
138 return 1; /* success */
139}
140
141#else
142
Radek Krejci9f03b482015-10-22 16:02:10 +0200143static int
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100144tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200145{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100146 X509_STORE_CTX store_ctx;
147 X509_OBJECT obj;
148 X509_NAME *subject, *issuer;
149 X509 *cert;
150 X509_CRL *crl;
151 X509_REVOKED *revoked;
152 EVP_PKEY *pubkey;
153 int i, n, rc;
154 ASN1_TIME *next_update = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100155 struct nc_client_tls_opts *opts;
Radek Krejci9f03b482015-10-22 16:02:10 +0200156
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100157 if (!preverify_ok) {
158 return 0;
159 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200160
Michal Vasko3031aae2016-01-27 16:07:18 +0100161 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
162
163 if (!opts->crl_store) {
164 /* nothing to check */
165 return 1;
166 }
167
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100168 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
169 subject = X509_get_subject_name(cert);
170 issuer = X509_get_issuer_name(cert);
171
172 /* try to retrieve a CRL corresponding to the _subject_ of
173 * the current certificate in order to verify it's integrity */
174 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100175 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Rosen Penev4f552d62019-06-26 16:10:43 -0700176 rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100177 X509_STORE_CTX_cleanup(&store_ctx);
178 crl = obj.data.crl;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200179 if ((rc > 0) && crl) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100180 next_update = X509_CRL_get_nextUpdate(crl);
181
182 /* verify the signature on this CRL */
183 pubkey = X509_get_pubkey(cert);
184 if (X509_CRL_verify(crl, pubkey) <= 0) {
185 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
186 X509_OBJECT_free_contents(&obj);
187 if (pubkey) {
188 EVP_PKEY_free(pubkey);
189 }
190 return 0; /* fail */
191 }
192 if (pubkey) {
193 EVP_PKEY_free(pubkey);
194 }
195
196 /* check date of CRL to make sure it's not expired */
197 if (!next_update) {
198 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
199 X509_OBJECT_free_contents(&obj);
200 return 0; /* fail */
201 }
202 if (X509_cmp_current_time(next_update) < 0) {
203 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
204 X509_OBJECT_free_contents(&obj);
205 return 0; /* fail */
206 }
207 X509_OBJECT_free_contents(&obj);
208 }
209
210 /* try to retrieve a CRL corresponding to the _issuer_ of
211 * the current certificate in order to check for revocation */
212 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100213 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Rosen Penev4f552d62019-06-26 16:10:43 -0700214 rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100215 X509_STORE_CTX_cleanup(&store_ctx);
216 crl = obj.data.crl;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200217 if ((rc > 0) && crl) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100218 /* check if the current certificate is revoked by this CRL */
219 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
220 for (i = 0; i < n; i++) {
221 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
222 if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200223 ERR(NULL, "Certificate revoked!");
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100224 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
225 X509_OBJECT_free_contents(&obj);
226 return 0; /* fail */
227 }
228 }
229 X509_OBJECT_free_contents(&obj);
230 }
231
232 return 1; /* success */
233}
234
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100235#endif
236
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400237void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100238_nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
239{
240 free(opts->cert_path);
241 free(opts->key_path);
242 free(opts->ca_file);
243 free(opts->ca_dir);
244 SSL_CTX_free(opts->tls_ctx);
245
246 free(opts->crl_file);
247 free(opts->crl_dir);
248 X509_STORE_free(opts->crl_store);
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200249
250 memset(opts, 0, sizeof *opts);
Michal Vaskoe22c6732016-01-29 11:03:02 +0100251}
252
Michal Vaskob7558c52016-02-26 15:04:19 +0100253void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100254nc_client_tls_destroy_opts(void)
255{
256 _nc_client_tls_destroy_opts(&tls_opts);
257 _nc_client_tls_destroy_opts(&tls_ch_opts);
258}
259
Michal Vasko3031aae2016-01-27 16:07:18 +0100260static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100261_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 +0100262{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100263 if (!client_cert) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200264 ERRARG("client_cert");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100265 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100266 }
267
Michal Vasko3031aae2016-01-27 16:07:18 +0100268 free(opts->cert_path);
269 free(opts->key_path);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100270
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100271 opts->cert_path = strdup(client_cert);
272 if (!opts->cert_path) {
273 ERRMEM;
274 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100275 }
276
Michal Vasko3031aae2016-01-27 16:07:18 +0100277 if (client_key) {
278 opts->key_path = strdup(client_key);
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100279 if (!opts->key_path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100280 ERRMEM;
281 return -1;
282 }
283 } else {
284 opts->key_path = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100285 }
286
Michal Vasko3031aae2016-01-27 16:07:18 +0100287 opts->tls_ctx_change = 1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100288
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100289 return 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100290}
291
Michal Vasko3031aae2016-01-27 16:07:18 +0100292API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100293nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100294{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100295 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100296}
297
298API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100299nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko3031aae2016-01-27 16:07:18 +0100300{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100301 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_ch_opts);
302}
303
304static void
305_nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts)
306{
307 if (!client_cert && !client_key) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200308 ERRARG("client_cert and client_key");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100309 return;
310 }
311
312 if (client_cert) {
313 *client_cert = opts->cert_path;
314 }
315 if (client_key) {
316 *client_key = opts->key_path;
317 }
318}
319
320API void
321nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key)
322{
323 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_opts);
324}
325
326API void
327nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key)
328{
329 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100330}
331
332static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100333_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 +0100334{
Michal Vasko3031aae2016-01-27 16:07:18 +0100335 if (!ca_file && !ca_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200336 ERRARG("ca_file and ca_dir");
Michal Vasko3031aae2016-01-27 16:07:18 +0100337 return -1;
338 }
339
Michal Vasko3031aae2016-01-27 16:07:18 +0100340 free(opts->ca_file);
341 free(opts->ca_dir);
342
343 if (ca_file) {
344 opts->ca_file = strdup(ca_file);
345 if (!opts->ca_file) {
346 ERRMEM;
347 return -1;
348 }
349 } else {
350 opts->ca_file = NULL;
351 }
352
353 if (ca_dir) {
354 opts->ca_dir = strdup(ca_dir);
355 if (!opts->ca_dir) {
356 ERRMEM;
357 return -1;
358 }
359 } else {
360 opts->ca_dir = NULL;
361 }
362
363 opts->tls_ctx_change = 1;
364
365 return 0;
366}
367
368API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100369nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100370{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100371 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100372}
373
374API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100375nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100376{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100377 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
378}
379
380static void
381_nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts)
382{
383 if (!ca_file && !ca_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200384 ERRARG("ca_file and ca_dir");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100385 return;
386 }
387
388 if (ca_file) {
389 *ca_file = opts->ca_file;
390 }
391 if (ca_dir) {
392 *ca_dir = opts->ca_dir;
393 }
394}
395
396API void
397nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
398{
399 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
400}
401
402API void
403nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
404{
405 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100406}
407
408static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100409_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 +0100410{
Michal Vasko3031aae2016-01-27 16:07:18 +0100411 if (!crl_file && !crl_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200412 ERRARG("crl_file and crl_dir");
Michal Vasko3031aae2016-01-27 16:07:18 +0100413 return -1;
414 }
415
Michal Vasko3031aae2016-01-27 16:07:18 +0100416 free(opts->crl_file);
417 free(opts->crl_dir);
418
419 if (crl_file) {
420 opts->crl_file = strdup(crl_file);
421 if (!opts->crl_file) {
422 ERRMEM;
423 return -1;
424 }
425 } else {
426 opts->crl_file = NULL;
427 }
428
429 if (crl_dir) {
430 opts->crl_dir = strdup(crl_dir);
431 if (!opts->crl_dir) {
432 ERRMEM;
433 return -1;
434 }
435 } else {
436 opts->crl_dir = NULL;
437 }
438
439 opts->crl_store_change = 1;
440
441 return 0;
442}
443
444API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100445nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100446{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100447 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100448}
449
450API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100451nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100452{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100453 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts);
454}
455
456static void
457_nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts)
458{
459 if (!crl_file && !crl_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200460 ERRARG("crl_file and crl_dir");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100461 return;
462 }
463
464 if (crl_file) {
465 *crl_file = opts->crl_file;
466 }
467 if (crl_dir) {
468 *crl_dir = opts->crl_dir;
469 }
470}
471
472API void
473nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir)
474{
475 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts);
476}
477
478API void
479nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir)
480{
481 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100482}
483
484API int
485nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
486{
487 return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL);
488}
489
490API int
491nc_client_tls_ch_del_bind(const char *address, uint16_t port)
492{
493 return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
494}
495
Michal Vasko3031aae2016-01-27 16:07:18 +0100496static int
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100497nc_client_tls_update_opts(struct nc_client_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100498{
499 char *key;
500 X509_LOOKUP *lookup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100501
502 if (!opts->tls_ctx || opts->tls_ctx_change) {
Michal Vaskoe22c6732016-01-29 11:03:02 +0100503 SSL_CTX_free(opts->tls_ctx);
Michal Vasko3031aae2016-01-27 16:07:18 +0100504
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100505#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
506 /* prepare global SSL context, highest available method is negotiated autmatically */
507 if (!(opts->tls_ctx = SSL_CTX_new(TLS_client_method())))
508#else
Michal Vasko3031aae2016-01-27 16:07:18 +0100509 /* prepare global SSL context, allow only mandatory TLS 1.2 */
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100510 if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method())))
511#endif
512 {
Michal Vasko05532772021-06-03 12:12:38 +0200513 ERR(NULL, "Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko3031aae2016-01-27 16:07:18 +0100514 return -1;
515 }
516 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
517
518 /* get peer certificate */
519 if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200520 ERR(NULL, "Loading the client certificate from \'%s\' failed (%s).", opts->cert_path,
521 ERR_reason_error_string(ERR_get_error()));
Michal Vasko3031aae2016-01-27 16:07:18 +0100522 return -1;
523 }
524
525 /* if the file with private key not specified, expect that the private key is stored with the certificate */
526 if (!opts->key_path) {
527 key = opts->cert_path;
528 } else {
529 key = opts->key_path;
530 }
531 if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200532 ERR(NULL, "Loading the client priavte key from \'%s\' failed (%s).", key,
533 ERR_reason_error_string(ERR_get_error()));
Michal Vasko3031aae2016-01-27 16:07:18 +0100534 return -1;
535 }
536
537 if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
Michal Vasko05532772021-06-03 12:12:38 +0200538 ERR(NULL, "Failed to load the locations of trusted CA certificates (%s).",
539 ERR_reason_error_string(ERR_get_error()));
Michal Vasko3031aae2016-01-27 16:07:18 +0100540 return -1;
541 }
542 }
543
544 if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
545 /* set the revocation store with the correct paths for the callback */
546 X509_STORE_free(opts->crl_store);
547
548 opts->crl_store = X509_STORE_new();
549 if (!opts->crl_store) {
Michal Vasko05532772021-06-03 12:12:38 +0200550 ERR(NULL, "Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko3031aae2016-01-27 16:07:18 +0100551 return -1;
552 }
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100553
554#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
555 /* whaveter this does... */
Michal Vasko3031aae2016-01-27 16:07:18 +0100556 opts->crl_store->cache = 0;
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100557#endif
Michal Vasko3031aae2016-01-27 16:07:18 +0100558
559 if (opts->crl_file) {
560 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
Michal Vasko05532772021-06-03 12:12:38 +0200561 ERR(NULL, "Failed to add lookup method to CRL checking.");
Michal Vasko3031aae2016-01-27 16:07:18 +0100562 return -1;
563 }
564 if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200565 ERR(NULL, "Failed to add the revocation lookup file \"%s\".", opts->crl_file);
Michal Vasko3031aae2016-01-27 16:07:18 +0100566 return -1;
567 }
568 }
569
570 if (opts->crl_dir) {
571 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
Michal Vasko05532772021-06-03 12:12:38 +0200572 ERR(NULL, "Failed to add lookup method to CRL checking.");
Michal Vasko3031aae2016-01-27 16:07:18 +0100573 return -1;
574 }
575 if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200576 ERR(NULL, "Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
Michal Vasko3031aae2016-01-27 16:07:18 +0100577 return -1;
578 }
579 }
580 }
581
582 return 0;
Radek Krejci9f03b482015-10-22 16:02:10 +0200583}
584
585API struct nc_session *
Michal Vasko9bee18d2015-12-08 14:41:42 +0100586nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200587{
Radek Krejci9f03b482015-10-22 16:02:10 +0200588 struct nc_session *session = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +0100589 int sock, verify, ret;
Michal Vasko36c7be82017-02-22 13:37:59 +0100590 struct timespec ts_timeout, ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +0100591 char *ip_host = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100592
Michal Vasko3031aae2016-01-27 16:07:18 +0100593 if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200594 ERRINIT;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100595 return NULL;
596 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200597
598 /* process parameters */
599 if (!host || strisempty(host)) {
600 host = "localhost";
601 }
602
603 if (!port) {
604 port = NC_PORT_TLS;
605 }
606
Michal Vasko3031aae2016-01-27 16:07:18 +0100607 /* create/update TLS structures */
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100608 if (nc_client_tls_update_opts(&tls_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100609 return NULL;
610 }
611
Radek Krejci9f03b482015-10-22 16:02:10 +0200612 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200613 session = nc_new_session(NC_CLIENT, 0);
Radek Krejci9f03b482015-10-22 16:02:10 +0200614 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100615 ERRMEM;
Radek Krejci9f03b482015-10-22 16:02:10 +0200616 return NULL;
617 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100618 session->status = NC_STATUS_STARTING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100619
620 /* fill the session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100621 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100622 if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
Michal Vasko05532772021-06-03 12:12:38 +0200623 ERR(NULL, "Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100624 goto fail;
625 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200626
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100627 /* create and assign socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200628 sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100629 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200630 ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100631 goto fail;
632 }
633 SSL_set_fd(session->ti.tls, sock);
634
635 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
636 SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
637
638 /* connect and perform the handshake */
Michal Vaskoe852c8b2017-10-05 10:02:20 +0200639 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100640 nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko3031aae2016-01-27 16:07:18 +0100641 tlsauth_ch = 0;
Michal Vasko0190bc32016-03-02 15:47:49 +0100642 while (((ret = SSL_connect(session->ti.tls)) == -1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) {
643 usleep(NC_TIMEOUT_STEP);
Michal Vaskoe852c8b2017-10-05 10:02:20 +0200644 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100645 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200646 ERR(NULL, "SSL_connect timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +0100647 goto fail;
648 }
649 }
650 if (ret != 1) {
651 switch (SSL_get_error(session->ti.tls, ret)) {
652 case SSL_ERROR_SYSCALL:
Michal Vasko05532772021-06-03 12:12:38 +0200653 ERR(NULL, "SSL_connect failed (%s).", strerror(errno));
Michal Vasko0190bc32016-03-02 15:47:49 +0100654 break;
655 case SSL_ERROR_SSL:
Michal Vasko05532772021-06-03 12:12:38 +0200656 ERR(NULL, "SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko0190bc32016-03-02 15:47:49 +0100657 break;
658 default:
Michal Vasko05532772021-06-03 12:12:38 +0200659 ERR(NULL, "SSL_connect failed.");
Michal Vasko0190bc32016-03-02 15:47:49 +0100660 break;
661 }
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100662 goto fail;
663 }
664
665 /* check certificate verification result */
666 verify = SSL_get_verify_result(session->ti.tls);
667 switch (verify) {
668 case X509_V_OK:
Michal Vasko05532772021-06-03 12:12:38 +0200669 VRB(NULL, "Server certificate successfully verified.");
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100670 break;
671 default:
Michal Vasko05532772021-06-03 12:12:38 +0200672 WRN(NULL, "Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Radek Krejci9f03b482015-10-22 16:02:10 +0200673 }
674
Radek Krejcifd5b6682017-06-13 15:52:53 +0200675 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
676 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100677 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200678 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100679
Radek Krejci9f03b482015-10-22 16:02:10 +0200680 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200681 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100682 goto fail;
Radek Krejci9f03b482015-10-22 16:02:10 +0200683 }
Michal Vaskoad611702015-12-03 13:41:51 +0100684 session->status = NC_STATUS_RUNNING;
Radek Krejci9f03b482015-10-22 16:02:10 +0200685
Michal Vaskoef578332016-01-25 13:20:09 +0100686 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100687 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100688 }
689
690 /* store information into session and the dictionary */
Michal Vasko77367452021-02-16 16:32:18 +0100691 lydict_insert_zc(ctx, ip_host, &session->host);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100692 session->port = port;
Michal Vasko77367452021-02-16 16:32:18 +0100693 lydict_insert(ctx, "certificate-based", 0, &session->username);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100694
Radek Krejci9f03b482015-10-22 16:02:10 +0200695 return session;
696
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100697fail:
Michal Vasko66032bc2019-01-22 15:03:12 +0100698 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100699 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200700 return NULL;
701}
702
703API struct nc_session *
704nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
705{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100706 struct nc_session *session;
Radek Krejci9f03b482015-10-22 16:02:10 +0200707
Michal Vasko45e53ae2016-04-07 11:46:03 +0200708 if (!tls) {
709 ERRARG("tls");
710 return NULL;
711 } else if (!SSL_is_init_finished(tls)) {
Michal Vasko05532772021-06-03 12:12:38 +0200712 ERR(NULL, "Supplied TLS session is not fully connected!");
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100713 return NULL;
714 }
715
716 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200717 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100718 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100719 ERRMEM;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100720 return NULL;
721 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100722 session->status = NC_STATUS_STARTING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100723 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100724 session->ti.tls = tls;
725
Radek Krejcifd5b6682017-06-13 15:52:53 +0200726 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
727 goto fail;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100728 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200729 ctx = session->ctx;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100730
731 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200732 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100733 goto fail;
734 }
Michal Vaskoad611702015-12-03 13:41:51 +0100735 session->status = NC_STATUS_RUNNING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100736
Michal Vaskoef578332016-01-25 13:20:09 +0100737 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100738 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100739 }
740
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100741 return session;
742
743fail:
Michal Vaskoa42a7f02021-07-02 08:43:12 +0200744 session->ti.tls = NULL;
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100745 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200746 return NULL;
747}
748
Michal Vasko3031aae2016-01-27 16:07:18 +0100749struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +0100750nc_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 +0100751{
Michal Vasko36c7be82017-02-22 13:37:59 +0100752 int verify, ret;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100753 SSL *tls;
754 struct nc_session *session;
Michal Vasko36c7be82017-02-22 13:37:59 +0100755 struct timespec ts_timeout, ts_cur;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100756
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100757 if (nc_client_tls_update_opts(&tls_ch_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100758 close(sock);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100759 return NULL;
760 }
761
Michal Vasko3031aae2016-01-27 16:07:18 +0100762 if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
Michal Vasko05532772021-06-03 12:12:38 +0200763 ERR(NULL, "Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko80cad7f2015-12-08 14:42:27 +0100764 close(sock);
765 return NULL;
766 }
767
768 SSL_set_fd(tls, sock);
769
770 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
771 SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
772
773 /* connect and perform the handshake */
Michal Vasko36c7be82017-02-22 13:37:59 +0100774 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +0200775 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100776 nc_addtimespec(&ts_timeout, timeout);
777 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100778 tlsauth_ch = 1;
Michal Vasko0190bc32016-03-02 15:47:49 +0100779 while (((ret = SSL_connect(tls)) == -1) && (SSL_get_error(tls, ret) == SSL_ERROR_WANT_READ)) {
780 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +0100781 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +0200782 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100783 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200784 ERR(NULL, "SSL_connect timeout.");
Michal Vasko36c7be82017-02-22 13:37:59 +0100785 SSL_free(tls);
786 return NULL;
787 }
Michal Vasko0190bc32016-03-02 15:47:49 +0100788 }
789 }
790 if (ret != 1) {
791 switch (SSL_get_error(tls, ret)) {
792 case SSL_ERROR_SYSCALL:
Michal Vasko05532772021-06-03 12:12:38 +0200793 ERR(NULL, "SSL_connect failed (%s).", strerror(errno));
Michal Vasko0190bc32016-03-02 15:47:49 +0100794 break;
795 case SSL_ERROR_SSL:
Michal Vasko05532772021-06-03 12:12:38 +0200796 ERR(NULL, "SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko0190bc32016-03-02 15:47:49 +0100797 break;
798 default:
Michal Vasko05532772021-06-03 12:12:38 +0200799 ERR(NULL, "SSL_connect failed.");
Michal Vasko0190bc32016-03-02 15:47:49 +0100800 break;
801 }
Michal Vasko80cad7f2015-12-08 14:42:27 +0100802 SSL_free(tls);
803 return NULL;
804 }
805
806 /* check certificate verification result */
807 verify = SSL_get_verify_result(tls);
808 switch (verify) {
809 case X509_V_OK:
Michal Vasko05532772021-06-03 12:12:38 +0200810 VRB(NULL, "Server certificate successfully verified.");
Michal Vasko80cad7f2015-12-08 14:42:27 +0100811 break;
812 default:
Michal Vasko05532772021-06-03 12:12:38 +0200813 WRN(NULL, "Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Michal Vasko80cad7f2015-12-08 14:42:27 +0100814 }
815
816 session = nc_connect_libssl(tls, ctx);
817 if (session) {
Michal Vasko4282fae2016-02-18 10:03:42 +0100818 session->flags |= NC_SESSION_CALLHOME;
819
Michal Vasko80cad7f2015-12-08 14:42:27 +0100820 /* store information into session and the dictionary */
Michal Vasko77367452021-02-16 16:32:18 +0100821 lydict_insert(session->ctx, host, 0, &session->host);
Michal Vasko80cad7f2015-12-08 14:42:27 +0100822 session->port = port;
Michal Vasko77367452021-02-16 16:32:18 +0100823 lydict_insert(session->ctx, "certificate-based", 0, &session->username);
Michal Vasko80cad7f2015-12-08 14:42:27 +0100824 }
825
826 return session;
827}