blob: f95fd46063db81a6aa0a474d96a1d434a4255893 [file] [log] [blame]
Radek Krejci9f03b482015-10-22 16:02:10 +02001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_client_tls.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @author Michal Vasko <mvasko@cesnet.cz>
5 * @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 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01009 * @copyright
Radek Krejci9f03b482015-10-22 16:02:10 +020010 * Copyright (c) 2015 CESNET, z.s.p.o.
11 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * This source code is licensed under BSD 3-Clause License (the "License").
13 * You may not use this file except in compliance with the License.
14 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010015 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010016 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci9f03b482015-10-22 16:02:10 +020017 */
18
Michal Vaskoba9f3582023-02-22 10:26:32 +010019#define _GNU_SOURCE /* pthread_rwlock_t, strdup */
20
Radek Krejci9f03b482015-10-22 16:02:10 +020021#include <assert.h>
22#include <errno.h>
Radek Krejci9f03b482015-10-22 16:02:10 +020023#include <string.h>
24#include <unistd.h>
25
26#include <libyang/libyang.h>
Michal Vasko086311b2016-01-08 09:53:11 +010027#include <openssl/err.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020028#include <openssl/ossl_typ.h>
Michal Vaskodae48322016-02-25 14:49:48 +010029#include <openssl/x509.h>
Radek Krejci9f03b482015-10-22 16:02:10 +020030
Michal Vaskob83a3fa2021-05-26 09:53:42 +020031#include "libnetconf.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010032#include "session_client.h"
33#include "session_client_ch.h"
Michal Vasko11d4cdb2015-10-29 11:42:52 +010034
Rosen Penev4f552d62019-06-26 16:10:43 -070035#if OPENSSL_VERSION_NUMBER < 0x10100000L
36#define X509_STORE_CTX_get_by_subject X509_STORE_get_by_subject
37#endif
38
Radek Krejci62aa0642017-05-25 16:33:49 +020039struct nc_client_context *nc_client_context_location(void);
40
41#define client_opts nc_client_context_location()->opts
42#define tls_opts nc_client_context_location()->tls_opts
43#define tls_ch_opts nc_client_context_location()->tls_ch_opts
Michal Vasko3031aae2016-01-27 16:07:18 +010044
45static int tlsauth_ch;
Radek Krejci9f03b482015-10-22 16:02:10 +020046
Michal Vasko18aeb5d2017-02-17 09:23:56 +010047#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
48
49static int
50tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
51{
52 X509_STORE_CTX *store_ctx;
53 X509_OBJECT *obj;
54 X509_NAME *subject, *issuer;
55 X509 *cert;
56 X509_CRL *crl;
57 X509_REVOKED *revoked;
58 EVP_PKEY *pubkey;
59 int i, n, rc;
60 const ASN1_TIME *next_update = NULL;
61 struct nc_client_tls_opts *opts;
62
63 if (!preverify_ok) {
64 return 0;
65 }
66
67 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
68
69 if (!opts->crl_store) {
70 /* nothing to check */
71 return 1;
72 }
73
74 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
75 subject = X509_get_subject_name(cert);
76 issuer = X509_get_issuer_name(cert);
77
78 /* try to retrieve a CRL corresponding to the _subject_ of
79 * the current certificate in order to verify it's integrity */
80 store_ctx = X509_STORE_CTX_new();
81 obj = X509_OBJECT_new();
82 X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
Rosen Penev4f552d62019-06-26 16:10:43 -070083 rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, subject, obj);
Michal Vasko18aeb5d2017-02-17 09:23:56 +010084 X509_STORE_CTX_free(store_ctx);
85 crl = X509_OBJECT_get0_X509_CRL(obj);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020086 if ((rc > 0) && crl) {
Michal Vasko18aeb5d2017-02-17 09:23:56 +010087 next_update = X509_CRL_get0_nextUpdate(crl);
88
89 /* verify the signature on this CRL */
90 pubkey = X509_get_pubkey(cert);
91 if (X509_CRL_verify(crl, pubkey) <= 0) {
92 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
93 X509_OBJECT_free(obj);
94 if (pubkey) {
95 EVP_PKEY_free(pubkey);
96 }
97 return 0; /* fail */
98 }
99 if (pubkey) {
100 EVP_PKEY_free(pubkey);
101 }
102
103 /* check date of CRL to make sure it's not expired */
104 if (!next_update) {
105 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
106 X509_OBJECT_free(obj);
107 return 0; /* fail */
108 }
109 if (X509_cmp_current_time(next_update) < 0) {
110 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
111 X509_OBJECT_free(obj);
112 return 0; /* fail */
113 }
114 X509_OBJECT_free(obj);
115 }
116
117 /* try to retrieve a CRL corresponding to the _issuer_ of
118 * the current certificate in order to check for revocation */
119 store_ctx = X509_STORE_CTX_new();
120 obj = X509_OBJECT_new();
121 X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
Rosen Penev4f552d62019-06-26 16:10:43 -0700122 rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj);
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100123 X509_STORE_CTX_free(store_ctx);
124 crl = X509_OBJECT_get0_X509_CRL(obj);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200125 if ((rc > 0) && crl) {
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100126 /* check if the current certificate is revoked by this CRL */
127 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
128 for (i = 0; i < n; i++) {
129 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
130 if (ASN1_INTEGER_cmp(X509_REVOKED_get0_serialNumber(revoked), X509_get_serialNumber(cert)) == 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200131 ERR(NULL, "Certificate revoked!");
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100132 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
133 X509_OBJECT_free(obj);
134 return 0; /* fail */
135 }
136 }
137 X509_OBJECT_free(obj);
138 }
139
140 return 1; /* success */
141}
142
143#else
144
Radek Krejci9f03b482015-10-22 16:02:10 +0200145static int
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100146tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200147{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100148 X509_STORE_CTX store_ctx;
149 X509_OBJECT obj;
150 X509_NAME *subject, *issuer;
151 X509 *cert;
152 X509_CRL *crl;
153 X509_REVOKED *revoked;
154 EVP_PKEY *pubkey;
155 int i, n, rc;
156 ASN1_TIME *next_update = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100157 struct nc_client_tls_opts *opts;
Radek Krejci9f03b482015-10-22 16:02:10 +0200158
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100159 if (!preverify_ok) {
160 return 0;
161 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200162
Michal Vasko3031aae2016-01-27 16:07:18 +0100163 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
164
165 if (!opts->crl_store) {
166 /* nothing to check */
167 return 1;
168 }
169
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100170 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
171 subject = X509_get_subject_name(cert);
172 issuer = X509_get_issuer_name(cert);
173
174 /* try to retrieve a CRL corresponding to the _subject_ of
175 * the current certificate in order to verify it's integrity */
176 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100177 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Rosen Penev4f552d62019-06-26 16:10:43 -0700178 rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100179 X509_STORE_CTX_cleanup(&store_ctx);
180 crl = obj.data.crl;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200181 if ((rc > 0) && crl) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100182 next_update = X509_CRL_get_nextUpdate(crl);
183
184 /* verify the signature on this CRL */
185 pubkey = X509_get_pubkey(cert);
186 if (X509_CRL_verify(crl, pubkey) <= 0) {
187 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
188 X509_OBJECT_free_contents(&obj);
189 if (pubkey) {
190 EVP_PKEY_free(pubkey);
191 }
192 return 0; /* fail */
193 }
194 if (pubkey) {
195 EVP_PKEY_free(pubkey);
196 }
197
198 /* check date of CRL to make sure it's not expired */
199 if (!next_update) {
200 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
201 X509_OBJECT_free_contents(&obj);
202 return 0; /* fail */
203 }
204 if (X509_cmp_current_time(next_update) < 0) {
205 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
206 X509_OBJECT_free_contents(&obj);
207 return 0; /* fail */
208 }
209 X509_OBJECT_free_contents(&obj);
210 }
211
212 /* try to retrieve a CRL corresponding to the _issuer_ of
213 * the current certificate in order to check for revocation */
214 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100215 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Rosen Penev4f552d62019-06-26 16:10:43 -0700216 rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100217 X509_STORE_CTX_cleanup(&store_ctx);
218 crl = obj.data.crl;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200219 if ((rc > 0) && crl) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100220 /* check if the current certificate is revoked by this CRL */
221 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
222 for (i = 0; i < n; i++) {
223 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
224 if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200225 ERR(NULL, "Certificate revoked!");
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100226 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
227 X509_OBJECT_free_contents(&obj);
228 return 0; /* fail */
229 }
230 }
231 X509_OBJECT_free_contents(&obj);
232 }
233
234 return 1; /* success */
235}
236
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100237#endif
238
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400239void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100240_nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
241{
242 free(opts->cert_path);
243 free(opts->key_path);
244 free(opts->ca_file);
245 free(opts->ca_dir);
246 SSL_CTX_free(opts->tls_ctx);
247
248 free(opts->crl_file);
249 free(opts->crl_dir);
250 X509_STORE_free(opts->crl_store);
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200251
252 memset(opts, 0, sizeof *opts);
Michal Vaskoe22c6732016-01-29 11:03:02 +0100253}
254
Michal Vaskob7558c52016-02-26 15:04:19 +0100255void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100256nc_client_tls_destroy_opts(void)
257{
258 _nc_client_tls_destroy_opts(&tls_opts);
259 _nc_client_tls_destroy_opts(&tls_ch_opts);
260}
261
Michal Vasko3031aae2016-01-27 16:07:18 +0100262static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100263_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 +0100264{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100265 if (!client_cert) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200266 ERRARG("client_cert");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100267 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100268 }
269
Michal Vasko3031aae2016-01-27 16:07:18 +0100270 free(opts->cert_path);
271 free(opts->key_path);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100272
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100273 opts->cert_path = strdup(client_cert);
274 if (!opts->cert_path) {
275 ERRMEM;
276 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100277 }
278
Michal Vasko3031aae2016-01-27 16:07:18 +0100279 if (client_key) {
280 opts->key_path = strdup(client_key);
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100281 if (!opts->key_path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100282 ERRMEM;
283 return -1;
284 }
285 } else {
286 opts->key_path = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100287 }
288
Michal Vasko3031aae2016-01-27 16:07:18 +0100289 opts->tls_ctx_change = 1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100290
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100291 return 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100292}
293
Michal Vasko3031aae2016-01-27 16:07:18 +0100294API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100295nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100296{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100297 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100298}
299
300API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100301nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko3031aae2016-01-27 16:07:18 +0100302{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100303 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_ch_opts);
304}
305
306static void
307_nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts)
308{
309 if (!client_cert && !client_key) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200310 ERRARG("client_cert and client_key");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100311 return;
312 }
313
314 if (client_cert) {
315 *client_cert = opts->cert_path;
316 }
317 if (client_key) {
318 *client_key = opts->key_path;
319 }
320}
321
322API void
323nc_client_tls_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_opts);
326}
327
328API void
329nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key)
330{
331 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100332}
333
334static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100335_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 +0100336{
Michal Vasko3031aae2016-01-27 16:07:18 +0100337 if (!ca_file && !ca_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200338 ERRARG("ca_file and ca_dir");
Michal Vasko3031aae2016-01-27 16:07:18 +0100339 return -1;
340 }
341
Michal Vasko3031aae2016-01-27 16:07:18 +0100342 free(opts->ca_file);
343 free(opts->ca_dir);
344
345 if (ca_file) {
346 opts->ca_file = strdup(ca_file);
347 if (!opts->ca_file) {
348 ERRMEM;
349 return -1;
350 }
351 } else {
352 opts->ca_file = NULL;
353 }
354
355 if (ca_dir) {
356 opts->ca_dir = strdup(ca_dir);
357 if (!opts->ca_dir) {
358 ERRMEM;
359 return -1;
360 }
361 } else {
362 opts->ca_dir = NULL;
363 }
364
365 opts->tls_ctx_change = 1;
366
367 return 0;
368}
369
370API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100371nc_client_tls_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_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100374}
375
376API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100377nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100378{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100379 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
380}
381
382static void
383_nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts)
384{
385 if (!ca_file && !ca_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200386 ERRARG("ca_file and ca_dir");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100387 return;
388 }
389
390 if (ca_file) {
391 *ca_file = opts->ca_file;
392 }
393 if (ca_dir) {
394 *ca_dir = opts->ca_dir;
395 }
396}
397
398API void
399nc_client_tls_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_opts);
402}
403
404API void
405nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
406{
407 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100408}
409
410static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100411_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 +0100412{
Michal Vasko3031aae2016-01-27 16:07:18 +0100413 if (!crl_file && !crl_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200414 ERRARG("crl_file and crl_dir");
Michal Vasko3031aae2016-01-27 16:07:18 +0100415 return -1;
416 }
417
Michal Vasko3031aae2016-01-27 16:07:18 +0100418 free(opts->crl_file);
419 free(opts->crl_dir);
420
421 if (crl_file) {
422 opts->crl_file = strdup(crl_file);
423 if (!opts->crl_file) {
424 ERRMEM;
425 return -1;
426 }
427 } else {
428 opts->crl_file = NULL;
429 }
430
431 if (crl_dir) {
432 opts->crl_dir = strdup(crl_dir);
433 if (!opts->crl_dir) {
434 ERRMEM;
435 return -1;
436 }
437 } else {
438 opts->crl_dir = NULL;
439 }
440
441 opts->crl_store_change = 1;
442
443 return 0;
444}
445
446API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100447nc_client_tls_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_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100450}
451
452API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100453nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100454{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100455 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts);
456}
457
458static void
459_nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts)
460{
461 if (!crl_file && !crl_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200462 ERRARG("crl_file and crl_dir");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100463 return;
464 }
465
466 if (crl_file) {
467 *crl_file = opts->crl_file;
468 }
469 if (crl_dir) {
470 *crl_dir = opts->crl_dir;
471 }
472}
473
474API void
475nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir)
476{
477 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts);
478}
479
480API void
481nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir)
482{
483 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100484}
485
486API int
487nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
488{
Michal Vasko9d4cca52022-09-07 11:19:57 +0200489 return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_OPENSSL);
490}
491
492API int
493nc_client_tls_ch_add_bind_hostname_listen(const char *address, uint16_t port, const char *hostname)
494{
495 return nc_client_ch_add_bind_listen(address, port, hostname, NC_TI_OPENSSL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100496}
497
498API int
499nc_client_tls_ch_del_bind(const char *address, uint16_t port)
500{
501 return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
502}
503
Michal Vasko3031aae2016-01-27 16:07:18 +0100504static int
Michal Vasko55ce24a2022-09-07 09:24:43 +0200505nc_client_tls_update_opts(struct nc_client_tls_opts *opts, const char *peername)
Michal Vasko3031aae2016-01-27 16:07:18 +0100506{
Michal Vasko9af52322022-07-25 14:39:30 +0200507 int rc = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +0100508 char *key;
509 X509_LOOKUP *lookup;
Michal Vasko9af52322022-07-25 14:39:30 +0200510 X509_VERIFY_PARAM *vpm = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100511
512 if (!opts->tls_ctx || opts->tls_ctx_change) {
Michal Vaskoe22c6732016-01-29 11:03:02 +0100513 SSL_CTX_free(opts->tls_ctx);
Michal Vasko3031aae2016-01-27 16:07:18 +0100514
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100515#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
516 /* prepare global SSL context, highest available method is negotiated autmatically */
517 if (!(opts->tls_ctx = SSL_CTX_new(TLS_client_method())))
518#else
Michal Vasko3031aae2016-01-27 16:07:18 +0100519 /* prepare global SSL context, allow only mandatory TLS 1.2 */
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100520 if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method())))
521#endif
522 {
Michal Vasko05532772021-06-03 12:12:38 +0200523 ERR(NULL, "Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko9af52322022-07-25 14:39:30 +0200524 rc = -1;
525 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100526 }
527 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
528
529 /* get peer certificate */
530 if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200531 ERR(NULL, "Loading the client certificate from \'%s\' failed (%s).", opts->cert_path,
532 ERR_reason_error_string(ERR_get_error()));
Michal Vasko9af52322022-07-25 14:39:30 +0200533 rc = -1;
534 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100535 }
536
537 /* if the file with private key not specified, expect that the private key is stored with the certificate */
538 if (!opts->key_path) {
539 key = opts->cert_path;
540 } else {
541 key = opts->key_path;
542 }
543 if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
Michal Vasko9af52322022-07-25 14:39:30 +0200544 ERR(NULL, "Loading the client private key from \'%s\' failed (%s).", key,
Michal Vasko05532772021-06-03 12:12:38 +0200545 ERR_reason_error_string(ERR_get_error()));
Michal Vasko9af52322022-07-25 14:39:30 +0200546 rc = -1;
547 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100548 }
549
550 if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
Michal Vasko05532772021-06-03 12:12:38 +0200551 ERR(NULL, "Failed to load the locations of trusted CA certificates (%s).",
552 ERR_reason_error_string(ERR_get_error()));
Michal Vasko9af52322022-07-25 14:39:30 +0200553 rc = -1;
554 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100555 }
Michal Vasko9af52322022-07-25 14:39:30 +0200556
557#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
Michal Vasko55ce24a2022-09-07 09:24:43 +0200558 if (peername) {
559 /* server identity (hostname) verification */
560 vpm = X509_VERIFY_PARAM_new();
561 if (!X509_VERIFY_PARAM_set1_host(vpm, peername, 0)) {
562 ERR(NULL, "Failed to set expected server hostname (%s).", ERR_reason_error_string(ERR_get_error()));
563 rc = -1;
564 goto cleanup;
565 }
566 if (!SSL_CTX_set1_param(opts->tls_ctx, vpm)) {
567 ERR(NULL, "Failed to set verify params (%s).", ERR_reason_error_string(ERR_get_error()));
568 rc = -1;
569 goto cleanup;
570 }
Michal Vasko9af52322022-07-25 14:39:30 +0200571 }
572#endif
Michal Vasko3031aae2016-01-27 16:07:18 +0100573 }
574
575 if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
576 /* set the revocation store with the correct paths for the callback */
577 X509_STORE_free(opts->crl_store);
578
579 opts->crl_store = X509_STORE_new();
580 if (!opts->crl_store) {
Michal Vasko05532772021-06-03 12:12:38 +0200581 ERR(NULL, "Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko9af52322022-07-25 14:39:30 +0200582 rc = -1;
583 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100584 }
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100585
586#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
587 /* whaveter this does... */
Michal Vasko3031aae2016-01-27 16:07:18 +0100588 opts->crl_store->cache = 0;
Michal Vasko18aeb5d2017-02-17 09:23:56 +0100589#endif
Michal Vasko3031aae2016-01-27 16:07:18 +0100590
591 if (opts->crl_file) {
592 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
Michal Vasko05532772021-06-03 12:12:38 +0200593 ERR(NULL, "Failed to add lookup method to CRL checking.");
Michal Vasko9af52322022-07-25 14:39:30 +0200594 rc = -1;
595 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100596 }
597 if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200598 ERR(NULL, "Failed to add the revocation lookup file \"%s\".", opts->crl_file);
Michal Vasko9af52322022-07-25 14:39:30 +0200599 rc = -1;
600 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100601 }
602 }
603
604 if (opts->crl_dir) {
605 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
Michal Vasko05532772021-06-03 12:12:38 +0200606 ERR(NULL, "Failed to add lookup method to CRL checking.");
Michal Vasko9af52322022-07-25 14:39:30 +0200607 rc = -1;
608 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100609 }
610 if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200611 ERR(NULL, "Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
Michal Vasko9af52322022-07-25 14:39:30 +0200612 rc = -1;
613 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100614 }
615 }
616 }
617
Michal Vasko9af52322022-07-25 14:39:30 +0200618cleanup:
619 X509_VERIFY_PARAM_free(vpm);
620 return rc;
621}
622
623static int
Michal Vaskoa82745b2022-09-07 11:48:12 +0200624nc_client_tls_connect_check(int connect_ret, SSL *tls, const char *peername)
Michal Vasko9af52322022-07-25 14:39:30 +0200625{
626 int verify;
627
628 /* check certificate verification result */
629 verify = SSL_get_verify_result(tls);
630 switch (verify) {
631 case X509_V_OK:
632 if (connect_ret == 1) {
Michal Vasko7ffcd142022-09-07 09:24:22 +0200633 VRB(NULL, "Server certificate verified (domain \"%s\").", peername);
Michal Vasko9af52322022-07-25 14:39:30 +0200634 }
635 break;
636 default:
637 ERR(NULL, "Server certificate error (%s).", X509_verify_cert_error_string(verify));
638 }
639
640 /* check TLS connection result */
641 if (connect_ret != 1) {
642 switch (SSL_get_error(tls, connect_ret)) {
643 case SSL_ERROR_SYSCALL:
Michal Vasko7ffcd142022-09-07 09:24:22 +0200644 ERR(NULL, "SSL connect to \"%s\" failed (%s).", peername, errno ? strerror(errno) : "unexpected EOF");
Michal Vasko9af52322022-07-25 14:39:30 +0200645 break;
646 case SSL_ERROR_SSL:
Michal Vasko7ffcd142022-09-07 09:24:22 +0200647 ERR(NULL, "SSL connect to \"%s\" failed (%s).", peername, ERR_reason_error_string(ERR_get_error()));
Michal Vasko9af52322022-07-25 14:39:30 +0200648 break;
649 default:
Michal Vasko7ffcd142022-09-07 09:24:22 +0200650 ERR(NULL, "SSL connect to \"%s\" failed.", peername);
Michal Vasko9af52322022-07-25 14:39:30 +0200651 break;
652 }
653 }
654
655 return connect_ret;
Radek Krejci9f03b482015-10-22 16:02:10 +0200656}
657
658API struct nc_session *
Michal Vasko9bee18d2015-12-08 14:41:42 +0100659nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200660{
Radek Krejci9f03b482015-10-22 16:02:10 +0200661 struct nc_session *session = NULL;
Michal Vasko9af52322022-07-25 14:39:30 +0200662 int sock, ret;
roman6ece9c52022-06-22 09:29:17 +0200663 struct timespec ts_timeout;
Michal Vasko66032bc2019-01-22 15:03:12 +0100664 char *ip_host = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100665
Michal Vasko3031aae2016-01-27 16:07:18 +0100666 if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200667 ERRINIT;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100668 return NULL;
669 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200670
671 /* process parameters */
672 if (!host || strisempty(host)) {
673 host = "localhost";
674 }
675
676 if (!port) {
677 port = NC_PORT_TLS;
678 }
679
Michal Vasko3031aae2016-01-27 16:07:18 +0100680 /* create/update TLS structures */
Michal Vasko9af52322022-07-25 14:39:30 +0200681 if (nc_client_tls_update_opts(&tls_opts, host)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100682 return NULL;
683 }
684
Radek Krejci9f03b482015-10-22 16:02:10 +0200685 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200686 session = nc_new_session(NC_CLIENT, 0);
Radek Krejci9f03b482015-10-22 16:02:10 +0200687 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100688 ERRMEM;
Radek Krejci9f03b482015-10-22 16:02:10 +0200689 return NULL;
690 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100691 session->status = NC_STATUS_STARTING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100692
693 /* fill the session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100694 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100695 if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
Michal Vasko05532772021-06-03 12:12:38 +0200696 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 +0100697 goto fail;
698 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200699
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100700 /* create and assign socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200701 sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100702 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200703 ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100704 goto fail;
705 }
706 SSL_set_fd(session->ti.tls, sock);
707
708 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
709 SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
710
711 /* connect and perform the handshake */
Michal Vaskod8a74192023-02-06 15:51:50 +0100712 nc_timeouttime_get(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko3031aae2016-01-27 16:07:18 +0100713 tlsauth_ch = 0;
Michal Vaskod51a2642021-09-03 13:03:24 +0200714 while (((ret = SSL_connect(session->ti.tls)) != 1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) {
Michal Vasko0190bc32016-03-02 15:47:49 +0100715 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +0100716 if (nc_timeouttime_cur_diff(&ts_timeout) < 1) {
Michal Vasko7ffcd142022-09-07 09:24:22 +0200717 ERR(NULL, "SSL connect timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +0100718 goto fail;
719 }
720 }
Michal Vasko3cd55a72022-09-06 15:52:39 +0200721
722 /* check for errors */
Michal Vaskoa82745b2022-09-07 11:48:12 +0200723 if (nc_client_tls_connect_check(ret, session->ti.tls, host) != 1) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100724 goto fail;
725 }
726
Michal Vasko78939072022-12-12 07:43:18 +0100727 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200728 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100729 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200730 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100731
Radek Krejci9f03b482015-10-22 16:02:10 +0200732 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200733 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100734 goto fail;
Radek Krejci9f03b482015-10-22 16:02:10 +0200735 }
Michal Vaskoad611702015-12-03 13:41:51 +0100736 session->status = NC_STATUS_RUNNING;
Radek Krejci9f03b482015-10-22 16:02:10 +0200737
Michal Vaskoef578332016-01-25 13:20:09 +0100738 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100739 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100740 }
741
742 /* store information into session and the dictionary */
Michal Vasko93224072021-11-09 12:14:28 +0100743 session->host = ip_host;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100744 session->port = port;
Michal Vasko93224072021-11-09 12:14:28 +0100745 session->username = strdup("certificate-based");
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100746
Radek Krejci9f03b482015-10-22 16:02:10 +0200747 return session;
748
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100749fail:
Michal Vasko66032bc2019-01-22 15:03:12 +0100750 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100751 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200752 return NULL;
753}
754
755API struct nc_session *
756nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
757{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100758 struct nc_session *session;
Radek Krejci9f03b482015-10-22 16:02:10 +0200759
Michal Vasko45e53ae2016-04-07 11:46:03 +0200760 if (!tls) {
761 ERRARG("tls");
762 return NULL;
763 } else if (!SSL_is_init_finished(tls)) {
Michal Vasko05532772021-06-03 12:12:38 +0200764 ERR(NULL, "Supplied TLS session is not fully connected!");
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100765 return NULL;
766 }
767
768 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200769 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100770 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100771 ERRMEM;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100772 return NULL;
773 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100774 session->status = NC_STATUS_STARTING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100775 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100776 session->ti.tls = tls;
777
Michal Vasko78939072022-12-12 07:43:18 +0100778 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200779 goto fail;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100780 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200781 ctx = session->ctx;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100782
783 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200784 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100785 goto fail;
786 }
Michal Vaskoad611702015-12-03 13:41:51 +0100787 session->status = NC_STATUS_RUNNING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100788
Michal Vaskoef578332016-01-25 13:20:09 +0100789 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100790 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100791 }
792
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100793 return session;
794
795fail:
Michal Vaskob604ed52022-01-24 09:56:14 +0100796 session->ti_type = NC_TI_NONE;
Michal Vaskoa42a7f02021-07-02 08:43:12 +0200797 session->ti.tls = NULL;
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100798 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200799 return NULL;
800}
801
Michal Vasko3031aae2016-01-27 16:07:18 +0100802struct nc_session *
Michal Vasko9d4cca52022-09-07 11:19:57 +0200803nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout, const char *peername)
Michal Vasko80cad7f2015-12-08 14:42:27 +0100804{
Michal Vasko3cd55a72022-09-06 15:52:39 +0200805 int ret;
Michal Vaskoc64c38c2021-07-02 08:43:53 +0200806 SSL *tls = NULL;
807 struct nc_session *session = NULL;
roman6ece9c52022-06-22 09:29:17 +0200808 struct timespec ts_timeout;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100809
Michal Vasko9d4cca52022-09-07 11:19:57 +0200810 /* create/update TLS structures with explicit expected peername, if any set, the host is just the IP */
811 if (nc_client_tls_update_opts(&tls_ch_opts, peername)) {
Michal Vaskoc64c38c2021-07-02 08:43:53 +0200812 goto cleanup;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100813 }
814
Michal Vasko3031aae2016-01-27 16:07:18 +0100815 if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
Michal Vasko05532772021-06-03 12:12:38 +0200816 ERR(NULL, "Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vaskoc64c38c2021-07-02 08:43:53 +0200817 goto cleanup;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100818 }
819
820 SSL_set_fd(tls, sock);
821
822 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
823 SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
824
825 /* connect and perform the handshake */
Michal Vasko36c7be82017-02-22 13:37:59 +0100826 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100827 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100828 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100829 tlsauth_ch = 1;
Michal Vasko0190bc32016-03-02 15:47:49 +0100830 while (((ret = SSL_connect(tls)) == -1) && (SSL_get_error(tls, ret) == SSL_ERROR_WANT_READ)) {
831 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +0100832 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
Michal Vasko7ffcd142022-09-07 09:24:22 +0200833 ERR(NULL, "SSL connect timeout.");
roman6ece9c52022-06-22 09:29:17 +0200834 goto cleanup;
Michal Vasko0190bc32016-03-02 15:47:49 +0100835 }
836 }
Michal Vasko80cad7f2015-12-08 14:42:27 +0100837
Michal Vasko3cd55a72022-09-06 15:52:39 +0200838 /* check for errors */
Michal Vaskoa82745b2022-09-07 11:48:12 +0200839 if (nc_client_tls_connect_check(ret, tls, peername ? peername : host) != 1) {
Michal Vasko3cd55a72022-09-06 15:52:39 +0200840 goto cleanup;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100841 }
842
Michal Vaskoc64c38c2021-07-02 08:43:53 +0200843 /* connect */
Michal Vasko80cad7f2015-12-08 14:42:27 +0100844 session = nc_connect_libssl(tls, ctx);
Michal Vaskoc64c38c2021-07-02 08:43:53 +0200845 if (!session) {
846 goto cleanup;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100847 }
848
Michal Vaskoc64c38c2021-07-02 08:43:53 +0200849 session->flags |= NC_SESSION_CALLHOME;
850
851 /* store information into session and the dictionary */
Michal Vasko93224072021-11-09 12:14:28 +0100852 session->host = strdup(host);
Michal Vaskoc64c38c2021-07-02 08:43:53 +0200853 session->port = port;
Michal Vasko93224072021-11-09 12:14:28 +0100854 session->username = strdup("certificate-based");
Michal Vaskoc64c38c2021-07-02 08:43:53 +0200855
856cleanup:
857 if (!session) {
858 SSL_free(tls);
859 close(sock);
860 }
Michal Vasko80cad7f2015-12-08 14:42:27 +0100861 return session;
862}