blob: 649614b1050211e1c9a746e8d2dec78e0d2abf79 [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 Vaskodae48322016-02-25 14:49:48 +010025#include <openssl/x509.h>
Radek Krejci9f03b482015-10-22 16:02:10 +020026
Michal Vaskoe22c6732016-01-29 11:03:02 +010027#include "session_client.h"
28#include "session_client_ch.h"
Radek Krejci9f03b482015-10-22 16:02:10 +020029#include "libnetconf.h"
Michal Vasko11d4cdb2015-10-29 11:42:52 +010030
Michal Vaskodaf9a092016-02-09 10:42:05 +010031extern struct nc_client_opts client_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +010032static struct nc_client_tls_opts tls_opts;
33static struct nc_client_tls_opts tls_ch_opts;
34
35static int tlsauth_ch;
Radek Krejci9f03b482015-10-22 16:02:10 +020036
Radek Krejci9f03b482015-10-22 16:02:10 +020037static int
Michal Vasko11d4cdb2015-10-29 11:42:52 +010038tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +020039{
Michal Vasko11d4cdb2015-10-29 11:42:52 +010040 X509_STORE_CTX store_ctx;
41 X509_OBJECT obj;
42 X509_NAME *subject, *issuer;
43 X509 *cert;
44 X509_CRL *crl;
45 X509_REVOKED *revoked;
46 EVP_PKEY *pubkey;
47 int i, n, rc;
48 ASN1_TIME *next_update = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +010049 struct nc_client_tls_opts *opts;
Radek Krejci9f03b482015-10-22 16:02:10 +020050
Michal Vasko11d4cdb2015-10-29 11:42:52 +010051 if (!preverify_ok) {
52 return 0;
53 }
Radek Krejci9f03b482015-10-22 16:02:10 +020054
Michal Vasko3031aae2016-01-27 16:07:18 +010055 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
56
57 if (!opts->crl_store) {
58 /* nothing to check */
59 return 1;
60 }
61
Michal Vasko11d4cdb2015-10-29 11:42:52 +010062 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
63 subject = X509_get_subject_name(cert);
64 issuer = X509_get_issuer_name(cert);
65
66 /* try to retrieve a CRL corresponding to the _subject_ of
67 * the current certificate in order to verify it's integrity */
68 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +010069 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +010070 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
71 X509_STORE_CTX_cleanup(&store_ctx);
72 crl = obj.data.crl;
73 if (rc > 0 && crl) {
74 next_update = X509_CRL_get_nextUpdate(crl);
75
76 /* verify the signature on this CRL */
77 pubkey = X509_get_pubkey(cert);
78 if (X509_CRL_verify(crl, pubkey) <= 0) {
79 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
80 X509_OBJECT_free_contents(&obj);
81 if (pubkey) {
82 EVP_PKEY_free(pubkey);
83 }
84 return 0; /* fail */
85 }
86 if (pubkey) {
87 EVP_PKEY_free(pubkey);
88 }
89
90 /* check date of CRL to make sure it's not expired */
91 if (!next_update) {
92 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
93 X509_OBJECT_free_contents(&obj);
94 return 0; /* fail */
95 }
96 if (X509_cmp_current_time(next_update) < 0) {
97 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
98 X509_OBJECT_free_contents(&obj);
99 return 0; /* fail */
100 }
101 X509_OBJECT_free_contents(&obj);
102 }
103
104 /* try to retrieve a CRL corresponding to the _issuer_ of
105 * the current certificate in order to check for revocation */
106 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100107 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100108 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
109 X509_STORE_CTX_cleanup(&store_ctx);
110 crl = obj.data.crl;
111 if (rc > 0 && crl) {
112 /* check if the current certificate is revoked by this CRL */
113 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
114 for (i = 0; i < n; i++) {
115 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
116 if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
117 ERR("Certificate revoked!");
118 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
119 X509_OBJECT_free_contents(&obj);
120 return 0; /* fail */
121 }
122 }
123 X509_OBJECT_free_contents(&obj);
124 }
125
126 return 1; /* success */
127}
128
Michal Vaskoe22c6732016-01-29 11:03:02 +0100129static void
130_nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
131{
132 free(opts->cert_path);
133 free(opts->key_path);
134 free(opts->ca_file);
135 free(opts->ca_dir);
136 SSL_CTX_free(opts->tls_ctx);
137
138 free(opts->crl_file);
139 free(opts->crl_dir);
140 X509_STORE_free(opts->crl_store);
141}
142
Michal Vaskob7558c52016-02-26 15:04:19 +0100143void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100144nc_client_tls_destroy_opts(void)
145{
146 _nc_client_tls_destroy_opts(&tls_opts);
147 _nc_client_tls_destroy_opts(&tls_ch_opts);
148}
149
Michal Vasko3031aae2016-01-27 16:07:18 +0100150static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100151_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 +0100152{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100153 if (!client_cert) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200154 ERRARG("client_cert");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100155 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100156 }
157
Michal Vasko3031aae2016-01-27 16:07:18 +0100158 free(opts->cert_path);
159 free(opts->key_path);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100160
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100161 opts->cert_path = strdup(client_cert);
162 if (!opts->cert_path) {
163 ERRMEM;
164 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100165 }
166
Michal Vasko3031aae2016-01-27 16:07:18 +0100167 if (client_key) {
168 opts->key_path = strdup(client_key);
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100169 if (!opts->key_path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100170 ERRMEM;
171 return -1;
172 }
173 } else {
174 opts->key_path = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100175 }
176
Michal Vasko3031aae2016-01-27 16:07:18 +0100177 opts->tls_ctx_change = 1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100178
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100179 return 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100180}
181
Michal Vasko3031aae2016-01-27 16:07:18 +0100182API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100183nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100184{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100185 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100186}
187
188API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100189nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko3031aae2016-01-27 16:07:18 +0100190{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100191 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_ch_opts);
192}
193
194static void
195_nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts)
196{
197 if (!client_cert && !client_key) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200198 ERRARG("client_cert and client_key");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100199 return;
200 }
201
202 if (client_cert) {
203 *client_cert = opts->cert_path;
204 }
205 if (client_key) {
206 *client_key = opts->key_path;
207 }
208}
209
210API void
211nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key)
212{
213 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_opts);
214}
215
216API void
217nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key)
218{
219 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100220}
221
222static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100223_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 +0100224{
Michal Vasko3031aae2016-01-27 16:07:18 +0100225 if (!ca_file && !ca_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200226 ERRARG("ca_file and ca_dir");
Michal Vasko3031aae2016-01-27 16:07:18 +0100227 return -1;
228 }
229
Michal Vasko3031aae2016-01-27 16:07:18 +0100230 free(opts->ca_file);
231 free(opts->ca_dir);
232
233 if (ca_file) {
234 opts->ca_file = strdup(ca_file);
235 if (!opts->ca_file) {
236 ERRMEM;
237 return -1;
238 }
239 } else {
240 opts->ca_file = NULL;
241 }
242
243 if (ca_dir) {
244 opts->ca_dir = strdup(ca_dir);
245 if (!opts->ca_dir) {
246 ERRMEM;
247 return -1;
248 }
249 } else {
250 opts->ca_dir = NULL;
251 }
252
253 opts->tls_ctx_change = 1;
254
255 return 0;
256}
257
258API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100259nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100260{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100261 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100262}
263
264API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100265nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100266{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100267 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
268}
269
270static void
271_nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts)
272{
273 if (!ca_file && !ca_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200274 ERRARG("ca_file and ca_dir");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100275 return;
276 }
277
278 if (ca_file) {
279 *ca_file = opts->ca_file;
280 }
281 if (ca_dir) {
282 *ca_dir = opts->ca_dir;
283 }
284}
285
286API void
287nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
288{
289 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
290}
291
292API void
293nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
294{
295 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100296}
297
298static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100299_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 +0100300{
Michal Vasko3031aae2016-01-27 16:07:18 +0100301 if (!crl_file && !crl_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200302 ERRARG("crl_file and crl_dir");
Michal Vasko3031aae2016-01-27 16:07:18 +0100303 return -1;
304 }
305
Michal Vasko3031aae2016-01-27 16:07:18 +0100306 free(opts->crl_file);
307 free(opts->crl_dir);
308
309 if (crl_file) {
310 opts->crl_file = strdup(crl_file);
311 if (!opts->crl_file) {
312 ERRMEM;
313 return -1;
314 }
315 } else {
316 opts->crl_file = NULL;
317 }
318
319 if (crl_dir) {
320 opts->crl_dir = strdup(crl_dir);
321 if (!opts->crl_dir) {
322 ERRMEM;
323 return -1;
324 }
325 } else {
326 opts->crl_dir = NULL;
327 }
328
329 opts->crl_store_change = 1;
330
331 return 0;
332}
333
334API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100335nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100336{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100337 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100338}
339
340API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100341nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100342{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100343 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts);
344}
345
346static void
347_nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts)
348{
349 if (!crl_file && !crl_dir) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200350 ERRARG("crl_file and crl_dir");
Michal Vaskoe22c6732016-01-29 11:03:02 +0100351 return;
352 }
353
354 if (crl_file) {
355 *crl_file = opts->crl_file;
356 }
357 if (crl_dir) {
358 *crl_dir = opts->crl_dir;
359 }
360}
361
362API void
363nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir)
364{
365 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts);
366}
367
368API void
369nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir)
370{
371 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100372}
373
374API int
375nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
376{
377 return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL);
378}
379
380API int
381nc_client_tls_ch_del_bind(const char *address, uint16_t port)
382{
383 return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
384}
385
Michal Vasko3031aae2016-01-27 16:07:18 +0100386static int
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100387nc_client_tls_update_opts(struct nc_client_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100388{
389 char *key;
390 X509_LOOKUP *lookup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100391
392 if (!opts->tls_ctx || opts->tls_ctx_change) {
Michal Vaskoe22c6732016-01-29 11:03:02 +0100393 SSL_CTX_free(opts->tls_ctx);
Michal Vasko3031aae2016-01-27 16:07:18 +0100394
395 /* prepare global SSL context, allow only mandatory TLS 1.2 */
396 if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()))) {
397 ERR("Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
398 return -1;
399 }
400 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
401
402 /* get peer certificate */
403 if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
404 ERR("Loading the client certificate from \'%s\' failed (%s).", opts->cert_path, ERR_reason_error_string(ERR_get_error()));
405 return -1;
406 }
407
408 /* if the file with private key not specified, expect that the private key is stored with the certificate */
409 if (!opts->key_path) {
410 key = opts->cert_path;
411 } else {
412 key = opts->key_path;
413 }
414 if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
415 ERR("Loading the client priavte key from \'%s\' failed (%s).", key, ERR_reason_error_string(ERR_get_error()));
416 return -1;
417 }
418
419 if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
420 ERR("Failed to load the locations of trusted CA certificates (%s).", ERR_reason_error_string(ERR_get_error()));
421 return -1;
422 }
423 }
424
425 if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
426 /* set the revocation store with the correct paths for the callback */
427 X509_STORE_free(opts->crl_store);
428
429 opts->crl_store = X509_STORE_new();
430 if (!opts->crl_store) {
431 ERR("Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
432 return -1;
433 }
434 opts->crl_store->cache = 0;
435
436 if (opts->crl_file) {
437 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
438 ERR("Failed to add lookup method to CRL checking.");
439 return -1;
440 }
441 if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
442 ERR("Failed to add the revocation lookup file \"%s\".", opts->crl_file);
443 return -1;
444 }
445 }
446
447 if (opts->crl_dir) {
448 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
449 ERR("Failed to add lookup method to CRL checking.");
450 return -1;
451 }
452 if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
453 ERR("Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
454 return -1;
455 }
456 }
457 }
458
459 return 0;
Radek Krejci9f03b482015-10-22 16:02:10 +0200460}
461
462API struct nc_session *
Michal Vasko9bee18d2015-12-08 14:41:42 +0100463nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200464{
Radek Krejci9f03b482015-10-22 16:02:10 +0200465 struct nc_session *session = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +0100466 int sock, verify, ret;
467 uint32_t elapsed_usec = 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100468
Michal Vasko3031aae2016-01-27 16:07:18 +0100469 if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200470 ERRINIT;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100471 return NULL;
472 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200473
474 /* process parameters */
475 if (!host || strisempty(host)) {
476 host = "localhost";
477 }
478
479 if (!port) {
480 port = NC_PORT_TLS;
481 }
482
Michal Vasko3031aae2016-01-27 16:07:18 +0100483 /* create/update TLS structures */
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100484 if (nc_client_tls_update_opts(&tls_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100485 return NULL;
486 }
487
Radek Krejci9f03b482015-10-22 16:02:10 +0200488 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100489 session = calloc(1, sizeof *session);
Radek Krejci9f03b482015-10-22 16:02:10 +0200490 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100491 ERRMEM;
Radek Krejci9f03b482015-10-22 16:02:10 +0200492 return NULL;
493 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100494 session->status = NC_STATUS_STARTING;
495 session->side = NC_CLIENT;
Radek Krejci9f03b482015-10-22 16:02:10 +0200496
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100497 /* transport lock */
498 session->ti_lock = malloc(sizeof *session->ti_lock);
499 if (!session->ti_lock) {
500 ERRMEM;
501 goto fail;
502 }
503 pthread_mutex_init(session->ti_lock, NULL);
504
505 /* fill the session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100506 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100507 if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100508 ERR("Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100509 goto fail;
510 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200511
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100512 /* create and assign socket */
Michal Vaskof05562c2016-01-20 12:06:43 +0100513 sock = nc_sock_connect(host, port);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100514 if (sock == -1) {
515 goto fail;
516 }
517 SSL_set_fd(session->ti.tls, sock);
518
519 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
520 SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
521
522 /* connect and perform the handshake */
Michal Vasko3031aae2016-01-27 16:07:18 +0100523 tlsauth_ch = 0;
Michal Vasko0190bc32016-03-02 15:47:49 +0100524 while (((ret = SSL_connect(session->ti.tls)) == -1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) {
525 usleep(NC_TIMEOUT_STEP);
526 elapsed_usec += NC_TIMEOUT_STEP;
527 if (elapsed_usec / 1000 >= NC_TRANSPORT_TIMEOUT) {
528 ERR("SSL_connect timeout.");
529 goto fail;
530 }
531 }
532 if (ret != 1) {
533 switch (SSL_get_error(session->ti.tls, ret)) {
534 case SSL_ERROR_SYSCALL:
535 ERR("SSL_connect failed (%s).", strerror(errno));
536 break;
537 case SSL_ERROR_SSL:
538 ERR("SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
539 break;
540 default:
541 ERR("SSL_connect failed.");
542 break;
543 }
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100544 goto fail;
545 }
546
547 /* check certificate verification result */
548 verify = SSL_get_verify_result(session->ti.tls);
549 switch (verify) {
550 case X509_V_OK:
551 VRB("Server certificate successfully verified.");
552 break;
553 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100554 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Radek Krejci9f03b482015-10-22 16:02:10 +0200555 }
556
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100557 /* assign context (dicionary needed for handshake) */
558 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +0100559 if (client_opts.schema_searchpath) {
560 ctx = ly_ctx_new(client_opts.schema_searchpath);
561 } else {
562 ctx = ly_ctx_new(SCHEMAS_DIR);
563 }
Michal Vaskoe035b8e2016-03-11 10:10:03 +0100564 /* definitely should not happen, but be ready */
565 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
566 /* that's just it */
567 goto fail;
568 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100569 } else {
570 session->flags |= NC_SESSION_SHAREDCTX;
571 }
572 session->ctx = ctx;
573
Radek Krejci9f03b482015-10-22 16:02:10 +0200574 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200575 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100576 goto fail;
Radek Krejci9f03b482015-10-22 16:02:10 +0200577 }
Michal Vaskoad611702015-12-03 13:41:51 +0100578 session->status = NC_STATUS_RUNNING;
Radek Krejci9f03b482015-10-22 16:02:10 +0200579
Michal Vaskoef578332016-01-25 13:20:09 +0100580 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100581 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100582 }
583
584 /* store information into session and the dictionary */
585 session->host = lydict_insert(ctx, host, 0);
586 session->port = port;
Michal Vasko9bee18d2015-12-08 14:41:42 +0100587 session->username = lydict_insert(ctx, "certificate-based", 0);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100588
Radek Krejci9f03b482015-10-22 16:02:10 +0200589 return session;
590
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100591fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100592 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200593 return NULL;
594}
595
596API struct nc_session *
597nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
598{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100599 struct nc_session *session;
Radek Krejci9f03b482015-10-22 16:02:10 +0200600
Michal Vasko45e53ae2016-04-07 11:46:03 +0200601 if (!tls) {
602 ERRARG("tls");
603 return NULL;
604 } else if (!SSL_is_init_finished(tls)) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100605 ERR("Supplied TLS session is not fully connected!");
606 return NULL;
607 }
608
609 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100610 session = calloc(1, sizeof *session);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100611 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100612 ERRMEM;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100613 return NULL;
614 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100615 session->status = NC_STATUS_STARTING;
616 session->side = NC_CLIENT;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100617
618 /* transport lock */
619 session->ti_lock = malloc(sizeof *session->ti_lock);
620 if (!session->ti_lock) {
621 ERRMEM;
622 goto fail;
623 }
624 pthread_mutex_init(session->ti_lock, NULL);
625
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100626 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100627 session->ti.tls = tls;
628
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100629 /* assign context (dicionary needed for handshake) */
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100630 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +0100631 if (client_opts.schema_searchpath) {
632 ctx = ly_ctx_new(client_opts.schema_searchpath);
633 } else {
634 ctx = ly_ctx_new(SCHEMAS_DIR);
635 }
Michal Vaskoe035b8e2016-03-11 10:10:03 +0100636 /* definitely should not happen, but be ready */
637 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
638 /* that's just it */
639 goto fail;
640 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100641 } else {
642 session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100643 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100644 session->ctx = ctx;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100645
646 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200647 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100648 goto fail;
649 }
Michal Vaskoad611702015-12-03 13:41:51 +0100650 session->status = NC_STATUS_RUNNING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100651
Michal Vaskoef578332016-01-25 13:20:09 +0100652 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100653 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100654 }
655
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100656 return session;
657
658fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100659 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200660 return NULL;
661}
662
Michal Vasko3031aae2016-01-27 16:07:18 +0100663struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +0100664nc_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 +0100665{
Michal Vasko0190bc32016-03-02 15:47:49 +0100666 int verify, ret, elapsed_usec = 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100667 SSL *tls;
668 struct nc_session *session;
669
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100670 if (nc_client_tls_update_opts(&tls_ch_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100671 close(sock);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100672 return NULL;
673 }
674
Michal Vasko3031aae2016-01-27 16:07:18 +0100675 if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
Michal Vasko80cad7f2015-12-08 14:42:27 +0100676 ERR("Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
677 close(sock);
678 return NULL;
679 }
680
681 SSL_set_fd(tls, sock);
682
683 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
684 SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
685
686 /* connect and perform the handshake */
Michal Vasko3031aae2016-01-27 16:07:18 +0100687 tlsauth_ch = 1;
Michal Vasko0190bc32016-03-02 15:47:49 +0100688 while (((ret = SSL_connect(tls)) == -1) && (SSL_get_error(tls, ret) == SSL_ERROR_WANT_READ)) {
689 usleep(NC_TIMEOUT_STEP);
690 elapsed_usec += NC_TIMEOUT_STEP;
691 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
692 ERR("SSL_connect timeout.");
693 SSL_free(tls);
694 return NULL;
695 }
696 }
697 if (ret != 1) {
698 switch (SSL_get_error(tls, ret)) {
699 case SSL_ERROR_SYSCALL:
700 ERR("SSL_connect failed (%s).", strerror(errno));
701 break;
702 case SSL_ERROR_SSL:
703 ERR("SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
704 break;
705 default:
706 ERR("SSL_connect failed.");
707 break;
708 }
Michal Vasko80cad7f2015-12-08 14:42:27 +0100709 SSL_free(tls);
710 return NULL;
711 }
712
713 /* check certificate verification result */
714 verify = SSL_get_verify_result(tls);
715 switch (verify) {
716 case X509_V_OK:
717 VRB("Server certificate successfully verified.");
718 break;
719 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100720 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Michal Vasko80cad7f2015-12-08 14:42:27 +0100721 }
722
723 session = nc_connect_libssl(tls, ctx);
724 if (session) {
Michal Vasko4282fae2016-02-18 10:03:42 +0100725 session->flags |= NC_SESSION_CALLHOME;
726
Michal Vasko80cad7f2015-12-08 14:42:27 +0100727 /* store information into session and the dictionary */
Michal Vasko3031aae2016-01-27 16:07:18 +0100728 session->host = lydict_insert(session->ctx, host, 0);
Michal Vasko80cad7f2015-12-08 14:42:27 +0100729 session->port = port;
730 session->username = lydict_insert(session->ctx, "certificate-based", 0);
731 }
732
733 return session;
734}