blob: e54b292a882154615e69db7bafb55c44fba546ae [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) {
Michal Vasko29af44b2016-10-13 10:59:55 +0200515 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100516 goto fail;
517 }
518 SSL_set_fd(session->ti.tls, sock);
519
520 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
521 SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
522
523 /* connect and perform the handshake */
Michal Vasko3031aae2016-01-27 16:07:18 +0100524 tlsauth_ch = 0;
Michal Vasko0190bc32016-03-02 15:47:49 +0100525 while (((ret = SSL_connect(session->ti.tls)) == -1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) {
526 usleep(NC_TIMEOUT_STEP);
527 elapsed_usec += NC_TIMEOUT_STEP;
528 if (elapsed_usec / 1000 >= NC_TRANSPORT_TIMEOUT) {
529 ERR("SSL_connect timeout.");
530 goto fail;
531 }
532 }
533 if (ret != 1) {
534 switch (SSL_get_error(session->ti.tls, ret)) {
535 case SSL_ERROR_SYSCALL:
536 ERR("SSL_connect failed (%s).", strerror(errno));
537 break;
538 case SSL_ERROR_SSL:
539 ERR("SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
540 break;
541 default:
542 ERR("SSL_connect failed.");
543 break;
544 }
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100545 goto fail;
546 }
547
548 /* check certificate verification result */
549 verify = SSL_get_verify_result(session->ti.tls);
550 switch (verify) {
551 case X509_V_OK:
552 VRB("Server certificate successfully verified.");
553 break;
554 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100555 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Radek Krejci9f03b482015-10-22 16:02:10 +0200556 }
557
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100558 /* assign context (dicionary needed for handshake) */
559 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +0100560 if (client_opts.schema_searchpath) {
561 ctx = ly_ctx_new(client_opts.schema_searchpath);
562 } else {
563 ctx = ly_ctx_new(SCHEMAS_DIR);
564 }
Michal Vaskoe035b8e2016-03-11 10:10:03 +0100565 /* definitely should not happen, but be ready */
566 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
567 /* that's just it */
568 goto fail;
569 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100570 } else {
571 session->flags |= NC_SESSION_SHAREDCTX;
572 }
573 session->ctx = ctx;
574
Radek Krejci9f03b482015-10-22 16:02:10 +0200575 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200576 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100577 goto fail;
Radek Krejci9f03b482015-10-22 16:02:10 +0200578 }
Michal Vaskoad611702015-12-03 13:41:51 +0100579 session->status = NC_STATUS_RUNNING;
Radek Krejci9f03b482015-10-22 16:02:10 +0200580
Michal Vaskoef578332016-01-25 13:20:09 +0100581 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100582 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100583 }
584
585 /* store information into session and the dictionary */
586 session->host = lydict_insert(ctx, host, 0);
587 session->port = port;
Michal Vasko9bee18d2015-12-08 14:41:42 +0100588 session->username = lydict_insert(ctx, "certificate-based", 0);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100589
Radek Krejci9f03b482015-10-22 16:02:10 +0200590 return session;
591
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100592fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100593 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200594 return NULL;
595}
596
597API struct nc_session *
598nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
599{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100600 struct nc_session *session;
Radek Krejci9f03b482015-10-22 16:02:10 +0200601
Michal Vasko45e53ae2016-04-07 11:46:03 +0200602 if (!tls) {
603 ERRARG("tls");
604 return NULL;
605 } else if (!SSL_is_init_finished(tls)) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100606 ERR("Supplied TLS session is not fully connected!");
607 return NULL;
608 }
609
610 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100611 session = calloc(1, sizeof *session);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100612 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100613 ERRMEM;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100614 return NULL;
615 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100616 session->status = NC_STATUS_STARTING;
617 session->side = NC_CLIENT;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100618
619 /* transport lock */
620 session->ti_lock = malloc(sizeof *session->ti_lock);
621 if (!session->ti_lock) {
622 ERRMEM;
623 goto fail;
624 }
625 pthread_mutex_init(session->ti_lock, NULL);
626
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100627 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100628 session->ti.tls = tls;
629
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100630 /* assign context (dicionary needed for handshake) */
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100631 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +0100632 if (client_opts.schema_searchpath) {
633 ctx = ly_ctx_new(client_opts.schema_searchpath);
634 } else {
635 ctx = ly_ctx_new(SCHEMAS_DIR);
636 }
Michal Vaskoe035b8e2016-03-11 10:10:03 +0100637 /* definitely should not happen, but be ready */
638 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
639 /* that's just it */
640 goto fail;
641 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100642 } else {
643 session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100644 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100645 session->ctx = ctx;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100646
647 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200648 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100649 goto fail;
650 }
Michal Vaskoad611702015-12-03 13:41:51 +0100651 session->status = NC_STATUS_RUNNING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100652
Michal Vaskoef578332016-01-25 13:20:09 +0100653 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100654 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100655 }
656
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100657 return session;
658
659fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100660 nc_session_free(session, NULL);
Radek Krejci9f03b482015-10-22 16:02:10 +0200661 return NULL;
662}
663
Michal Vasko3031aae2016-01-27 16:07:18 +0100664struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +0100665nc_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 +0100666{
Michal Vasko0190bc32016-03-02 15:47:49 +0100667 int verify, ret, elapsed_usec = 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100668 SSL *tls;
669 struct nc_session *session;
670
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100671 if (nc_client_tls_update_opts(&tls_ch_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100672 close(sock);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100673 return NULL;
674 }
675
Michal Vasko3031aae2016-01-27 16:07:18 +0100676 if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
Michal Vasko80cad7f2015-12-08 14:42:27 +0100677 ERR("Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
678 close(sock);
679 return NULL;
680 }
681
682 SSL_set_fd(tls, sock);
683
684 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
685 SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
686
687 /* connect and perform the handshake */
Michal Vasko3031aae2016-01-27 16:07:18 +0100688 tlsauth_ch = 1;
Michal Vasko0190bc32016-03-02 15:47:49 +0100689 while (((ret = SSL_connect(tls)) == -1) && (SSL_get_error(tls, ret) == SSL_ERROR_WANT_READ)) {
690 usleep(NC_TIMEOUT_STEP);
691 elapsed_usec += NC_TIMEOUT_STEP;
692 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
693 ERR("SSL_connect timeout.");
694 SSL_free(tls);
695 return NULL;
696 }
697 }
698 if (ret != 1) {
699 switch (SSL_get_error(tls, ret)) {
700 case SSL_ERROR_SYSCALL:
701 ERR("SSL_connect failed (%s).", strerror(errno));
702 break;
703 case SSL_ERROR_SSL:
704 ERR("SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
705 break;
706 default:
707 ERR("SSL_connect failed.");
708 break;
709 }
Michal Vasko80cad7f2015-12-08 14:42:27 +0100710 SSL_free(tls);
711 return NULL;
712 }
713
714 /* check certificate verification result */
715 verify = SSL_get_verify_result(tls);
716 switch (verify) {
717 case X509_V_OK:
718 VRB("Server certificate successfully verified.");
719 break;
720 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100721 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Michal Vasko80cad7f2015-12-08 14:42:27 +0100722 }
723
724 session = nc_connect_libssl(tls, ctx);
725 if (session) {
Michal Vasko4282fae2016-02-18 10:03:42 +0100726 session->flags |= NC_SESSION_CALLHOME;
727
Michal Vasko80cad7f2015-12-08 14:42:27 +0100728 /* store information into session and the dictionary */
Michal Vasko3031aae2016-01-27 16:07:18 +0100729 session->host = lydict_insert(session->ctx, host, 0);
Michal Vasko80cad7f2015-12-08 14:42:27 +0100730 session->port = port;
731 session->username = lydict_insert(session->ctx, "certificate-based", 0);
732 }
733
734 return session;
735}