blob: aac3cbc7845fb2721ab7fcc548d8235077c7d5b6 [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 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 * 3. Neither the name of the Company nor the names of its contributors
21 * may be used to endorse or promote products derived from this
22 * software without specific prior written permission.
23 *
24 */
25
26#include <assert.h>
27#include <errno.h>
Radek Krejci9f03b482015-10-22 16:02:10 +020028#include <string.h>
29#include <unistd.h>
30
31#include <libyang/libyang.h>
Michal Vasko086311b2016-01-08 09:53:11 +010032#include <openssl/err.h>
Radek Krejci9f03b482015-10-22 16:02:10 +020033
Michal Vaskoe22c6732016-01-29 11:03:02 +010034#include "session_client.h"
35#include "session_client_ch.h"
Radek Krejci9f03b482015-10-22 16:02:10 +020036#include "libnetconf.h"
Michal Vasko11d4cdb2015-10-29 11:42:52 +010037
Michal Vasko3031aae2016-01-27 16:07:18 +010038static struct nc_client_tls_opts tls_opts;
39static struct nc_client_tls_opts tls_ch_opts;
40
41static int tlsauth_ch;
Radek Krejci9f03b482015-10-22 16:02:10 +020042
Radek Krejci9f03b482015-10-22 16:02:10 +020043static int
Michal Vasko11d4cdb2015-10-29 11:42:52 +010044tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +020045{
Michal Vasko11d4cdb2015-10-29 11:42:52 +010046 X509_STORE_CTX store_ctx;
47 X509_OBJECT obj;
48 X509_NAME *subject, *issuer;
49 X509 *cert;
50 X509_CRL *crl;
51 X509_REVOKED *revoked;
52 EVP_PKEY *pubkey;
53 int i, n, rc;
54 ASN1_TIME *next_update = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +010055 struct nc_client_tls_opts *opts;
Radek Krejci9f03b482015-10-22 16:02:10 +020056
Michal Vasko11d4cdb2015-10-29 11:42:52 +010057 if (!preverify_ok) {
58 return 0;
59 }
Radek Krejci9f03b482015-10-22 16:02:10 +020060
Michal Vasko3031aae2016-01-27 16:07:18 +010061 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
62
63 if (!opts->crl_store) {
64 /* nothing to check */
65 return 1;
66 }
67
Michal Vasko11d4cdb2015-10-29 11:42:52 +010068 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
69 subject = X509_get_subject_name(cert);
70 issuer = X509_get_issuer_name(cert);
71
72 /* try to retrieve a CRL corresponding to the _subject_ of
73 * the current certificate in order to verify it's integrity */
74 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +010075 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +010076 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
77 X509_STORE_CTX_cleanup(&store_ctx);
78 crl = obj.data.crl;
79 if (rc > 0 && crl) {
80 next_update = X509_CRL_get_nextUpdate(crl);
81
82 /* verify the signature on this CRL */
83 pubkey = X509_get_pubkey(cert);
84 if (X509_CRL_verify(crl, pubkey) <= 0) {
85 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
86 X509_OBJECT_free_contents(&obj);
87 if (pubkey) {
88 EVP_PKEY_free(pubkey);
89 }
90 return 0; /* fail */
91 }
92 if (pubkey) {
93 EVP_PKEY_free(pubkey);
94 }
95
96 /* check date of CRL to make sure it's not expired */
97 if (!next_update) {
98 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
99 X509_OBJECT_free_contents(&obj);
100 return 0; /* fail */
101 }
102 if (X509_cmp_current_time(next_update) < 0) {
103 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
104 X509_OBJECT_free_contents(&obj);
105 return 0; /* fail */
106 }
107 X509_OBJECT_free_contents(&obj);
108 }
109
110 /* try to retrieve a CRL corresponding to the _issuer_ of
111 * the current certificate in order to check for revocation */
112 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100113 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100114 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
115 X509_STORE_CTX_cleanup(&store_ctx);
116 crl = obj.data.crl;
117 if (rc > 0 && crl) {
118 /* check if the current certificate is revoked by this CRL */
119 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
120 for (i = 0; i < n; i++) {
121 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
122 if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
123 ERR("Certificate revoked!");
124 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
125 X509_OBJECT_free_contents(&obj);
126 return 0; /* fail */
127 }
128 }
129 X509_OBJECT_free_contents(&obj);
130 }
131
132 return 1; /* success */
133}
134
Michal Vaskoe22c6732016-01-29 11:03:02 +0100135static void
136_nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
137{
138 free(opts->cert_path);
139 free(opts->key_path);
140 free(opts->ca_file);
141 free(opts->ca_dir);
142 SSL_CTX_free(opts->tls_ctx);
143
144 free(opts->crl_file);
145 free(opts->crl_dir);
146 X509_STORE_free(opts->crl_store);
147}
148
149API void
150nc_client_tls_destroy_opts(void)
151{
152 _nc_client_tls_destroy_opts(&tls_opts);
153 _nc_client_tls_destroy_opts(&tls_ch_opts);
154}
155
Michal Vasko3031aae2016-01-27 16:07:18 +0100156static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100157_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 +0100158{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100159 if (!client_cert) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100160 ERRARG;
161 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100162 }
163
Michal Vasko3031aae2016-01-27 16:07:18 +0100164 free(opts->cert_path);
165 free(opts->key_path);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100166
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100167 opts->cert_path = strdup(client_cert);
168 if (!opts->cert_path) {
169 ERRMEM;
170 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100171 }
172
Michal Vasko3031aae2016-01-27 16:07:18 +0100173 if (client_key) {
174 opts->key_path = strdup(client_key);
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100175 if (!opts->key_path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100176 ERRMEM;
177 return -1;
178 }
179 } else {
180 opts->key_path = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100181 }
182
Michal Vasko3031aae2016-01-27 16:07:18 +0100183 opts->tls_ctx_change = 1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100184
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100185 return 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100186}
187
Michal Vasko3031aae2016-01-27 16:07:18 +0100188API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100189nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100190{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100191 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100192}
193
194API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100195nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko3031aae2016-01-27 16:07:18 +0100196{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100197 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_ch_opts);
198}
199
200static void
201_nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts)
202{
203 if (!client_cert && !client_key) {
204 ERRARG;
205 return;
206 }
207
208 if (client_cert) {
209 *client_cert = opts->cert_path;
210 }
211 if (client_key) {
212 *client_key = opts->key_path;
213 }
214}
215
216API void
217nc_client_tls_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_opts);
220}
221
222API void
223nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key)
224{
225 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100226}
227
228static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100229_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 +0100230{
Michal Vasko3031aae2016-01-27 16:07:18 +0100231 if (!ca_file && !ca_dir) {
232 ERRARG;
233 return -1;
234 }
235
Michal Vasko3031aae2016-01-27 16:07:18 +0100236 free(opts->ca_file);
237 free(opts->ca_dir);
238
239 if (ca_file) {
240 opts->ca_file = strdup(ca_file);
241 if (!opts->ca_file) {
242 ERRMEM;
243 return -1;
244 }
245 } else {
246 opts->ca_file = NULL;
247 }
248
249 if (ca_dir) {
250 opts->ca_dir = strdup(ca_dir);
251 if (!opts->ca_dir) {
252 ERRMEM;
253 return -1;
254 }
255 } else {
256 opts->ca_dir = NULL;
257 }
258
259 opts->tls_ctx_change = 1;
260
261 return 0;
262}
263
264API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100265nc_client_tls_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_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100268}
269
270API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100271nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100272{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100273 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
274}
275
276static void
277_nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts)
278{
279 if (!ca_file && !ca_dir) {
280 ERRARG;
281 return;
282 }
283
284 if (ca_file) {
285 *ca_file = opts->ca_file;
286 }
287 if (ca_dir) {
288 *ca_dir = opts->ca_dir;
289 }
290}
291
292API void
293nc_client_tls_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_opts);
296}
297
298API void
299nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
300{
301 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100302}
303
304static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100305_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 +0100306{
Michal Vasko3031aae2016-01-27 16:07:18 +0100307 if (!crl_file && !crl_dir) {
308 ERRARG;
309 return -1;
310 }
311
Michal Vasko3031aae2016-01-27 16:07:18 +0100312 free(opts->crl_file);
313 free(opts->crl_dir);
314
315 if (crl_file) {
316 opts->crl_file = strdup(crl_file);
317 if (!opts->crl_file) {
318 ERRMEM;
319 return -1;
320 }
321 } else {
322 opts->crl_file = NULL;
323 }
324
325 if (crl_dir) {
326 opts->crl_dir = strdup(crl_dir);
327 if (!opts->crl_dir) {
328 ERRMEM;
329 return -1;
330 }
331 } else {
332 opts->crl_dir = NULL;
333 }
334
335 opts->crl_store_change = 1;
336
337 return 0;
338}
339
340API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100341nc_client_tls_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_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100344}
345
346API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100347nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100348{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100349 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts);
350}
351
352static void
353_nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts)
354{
355 if (!crl_file && !crl_dir) {
356 ERRARG;
357 return;
358 }
359
360 if (crl_file) {
361 *crl_file = opts->crl_file;
362 }
363 if (crl_dir) {
364 *crl_dir = opts->crl_dir;
365 }
366}
367
368API void
369nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir)
370{
371 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts);
372}
373
374API void
375nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir)
376{
377 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100378}
379
380API int
381nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
382{
383 return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL);
384}
385
386API int
387nc_client_tls_ch_del_bind(const char *address, uint16_t port)
388{
389 return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
390}
391
Michal Vasko3031aae2016-01-27 16:07:18 +0100392static int
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100393nc_client_tls_update_opts(struct nc_client_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100394{
395 char *key;
396 X509_LOOKUP *lookup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100397
398 if (!opts->tls_ctx || opts->tls_ctx_change) {
Michal Vaskoe22c6732016-01-29 11:03:02 +0100399 SSL_CTX_free(opts->tls_ctx);
Michal Vasko3031aae2016-01-27 16:07:18 +0100400
401 /* prepare global SSL context, allow only mandatory TLS 1.2 */
402 if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()))) {
403 ERR("Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
404 return -1;
405 }
406 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
407
408 /* get peer certificate */
409 if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
410 ERR("Loading the client certificate from \'%s\' failed (%s).", opts->cert_path, ERR_reason_error_string(ERR_get_error()));
411 return -1;
412 }
413
414 /* if the file with private key not specified, expect that the private key is stored with the certificate */
415 if (!opts->key_path) {
416 key = opts->cert_path;
417 } else {
418 key = opts->key_path;
419 }
420 if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
421 ERR("Loading the client priavte key from \'%s\' failed (%s).", key, ERR_reason_error_string(ERR_get_error()));
422 return -1;
423 }
424
425 if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
426 ERR("Failed to load the locations of trusted CA certificates (%s).", ERR_reason_error_string(ERR_get_error()));
427 return -1;
428 }
429 }
430
431 if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
432 /* set the revocation store with the correct paths for the callback */
433 X509_STORE_free(opts->crl_store);
434
435 opts->crl_store = X509_STORE_new();
436 if (!opts->crl_store) {
437 ERR("Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
438 return -1;
439 }
440 opts->crl_store->cache = 0;
441
442 if (opts->crl_file) {
443 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
444 ERR("Failed to add lookup method to CRL checking.");
445 return -1;
446 }
447 if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
448 ERR("Failed to add the revocation lookup file \"%s\".", opts->crl_file);
449 return -1;
450 }
451 }
452
453 if (opts->crl_dir) {
454 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
455 ERR("Failed to add lookup method to CRL checking.");
456 return -1;
457 }
458 if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
459 ERR("Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
460 return -1;
461 }
462 }
463 }
464
465 return 0;
Radek Krejci9f03b482015-10-22 16:02:10 +0200466}
467
468API struct nc_session *
Michal Vasko9bee18d2015-12-08 14:41:42 +0100469nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200470{
Radek Krejci9f03b482015-10-22 16:02:10 +0200471 struct nc_session *session = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100472 int sock, verify;
473
Michal Vasko3031aae2016-01-27 16:07:18 +0100474 if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100475 ERRARG;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100476 return NULL;
477 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200478
479 /* process parameters */
480 if (!host || strisempty(host)) {
481 host = "localhost";
482 }
483
484 if (!port) {
485 port = NC_PORT_TLS;
486 }
487
Michal Vasko3031aae2016-01-27 16:07:18 +0100488 /* create/update TLS structures */
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100489 if (nc_client_tls_update_opts(&tls_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100490 return NULL;
491 }
492
Radek Krejci9f03b482015-10-22 16:02:10 +0200493 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100494 session = calloc(1, sizeof *session);
Radek Krejci9f03b482015-10-22 16:02:10 +0200495 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100496 ERRMEM;
Radek Krejci9f03b482015-10-22 16:02:10 +0200497 return NULL;
498 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100499 session->status = NC_STATUS_STARTING;
500 session->side = NC_CLIENT;
Radek Krejci9f03b482015-10-22 16:02:10 +0200501
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100502 /* transport lock */
503 session->ti_lock = malloc(sizeof *session->ti_lock);
504 if (!session->ti_lock) {
505 ERRMEM;
506 goto fail;
507 }
508 pthread_mutex_init(session->ti_lock, NULL);
509
510 /* fill the session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100511 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100512 if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100513 ERR("Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100514 goto fail;
515 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200516
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100517 /* create and assign socket */
Michal Vaskof05562c2016-01-20 12:06:43 +0100518 sock = nc_sock_connect(host, port);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100519 if (sock == -1) {
520 goto fail;
521 }
522 SSL_set_fd(session->ti.tls, sock);
523
524 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
525 SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
526
527 /* connect and perform the handshake */
Michal Vasko3031aae2016-01-27 16:07:18 +0100528 tlsauth_ch = 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100529 if (SSL_connect(session->ti.tls) != 1) {
530 ERR("Connecting over TLS failed (%s).", ERR_reason_error_string(ERR_get_error()));
531 goto fail;
532 }
533
534 /* check certificate verification result */
535 verify = SSL_get_verify_result(session->ti.tls);
536 switch (verify) {
537 case X509_V_OK:
538 VRB("Server certificate successfully verified.");
539 break;
540 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100541 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Radek Krejci9f03b482015-10-22 16:02:10 +0200542 }
543
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100544 /* assign context (dicionary needed for handshake) */
545 if (!ctx) {
546 ctx = ly_ctx_new(SCHEMAS_DIR);
547 } else {
548 session->flags |= NC_SESSION_SHAREDCTX;
549 }
550 session->ctx = ctx;
551
Radek Krejci9f03b482015-10-22 16:02:10 +0200552 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100553 if (nc_handshake(session)) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100554 goto fail;
Radek Krejci9f03b482015-10-22 16:02:10 +0200555 }
Michal Vaskoad611702015-12-03 13:41:51 +0100556 session->status = NC_STATUS_RUNNING;
Radek Krejci9f03b482015-10-22 16:02:10 +0200557
Michal Vaskoef578332016-01-25 13:20:09 +0100558 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100559 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100560 }
561
562 /* store information into session and the dictionary */
563 session->host = lydict_insert(ctx, host, 0);
564 session->port = port;
Michal Vasko9bee18d2015-12-08 14:41:42 +0100565 session->username = lydict_insert(ctx, "certificate-based", 0);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100566
Radek Krejci9f03b482015-10-22 16:02:10 +0200567 return session;
568
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100569fail:
Radek Krejci9f03b482015-10-22 16:02:10 +0200570 nc_session_free(session);
571 return NULL;
572}
573
574API struct nc_session *
575nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
576{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100577 struct nc_session *session;
Radek Krejci9f03b482015-10-22 16:02:10 +0200578
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100579 /* check TLS session status */
580 if (!tls || !SSL_is_init_finished(tls)) {
581 ERR("Supplied TLS session is not fully connected!");
582 return NULL;
583 }
584
585 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100586 session = calloc(1, sizeof *session);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100587 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100588 ERRMEM;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100589 return NULL;
590 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100591 session->status = NC_STATUS_STARTING;
592 session->side = NC_CLIENT;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100593
594 /* transport lock */
595 session->ti_lock = malloc(sizeof *session->ti_lock);
596 if (!session->ti_lock) {
597 ERRMEM;
598 goto fail;
599 }
600 pthread_mutex_init(session->ti_lock, NULL);
601
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100602 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100603 session->ti.tls = tls;
604
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100605 /* assign context (dicionary needed for handshake) */
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100606 if (!ctx) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100607 ctx = ly_ctx_new(SCHEMAS_DIR);
608 } else {
609 session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100610 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100611 session->ctx = ctx;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100612
613 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100614 if (nc_handshake(session)) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100615 goto fail;
616 }
Michal Vaskoad611702015-12-03 13:41:51 +0100617 session->status = NC_STATUS_RUNNING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100618
Michal Vaskoef578332016-01-25 13:20:09 +0100619 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100620 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100621 }
622
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100623 return session;
624
625fail:
626 nc_session_free(session);
Radek Krejci9f03b482015-10-22 16:02:10 +0200627 return NULL;
628}
629
Michal Vasko3031aae2016-01-27 16:07:18 +0100630struct nc_session *
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100631nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx)
Michal Vasko80cad7f2015-12-08 14:42:27 +0100632{
Michal Vasko3031aae2016-01-27 16:07:18 +0100633 int verify;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100634 SSL *tls;
635 struct nc_session *session;
636
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100637 if (nc_client_tls_update_opts(&tls_ch_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100638 close(sock);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100639 return NULL;
640 }
641
Michal Vasko3031aae2016-01-27 16:07:18 +0100642 if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
Michal Vasko80cad7f2015-12-08 14:42:27 +0100643 ERR("Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
644 close(sock);
645 return NULL;
646 }
647
648 SSL_set_fd(tls, sock);
649
650 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
651 SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
652
653 /* connect and perform the handshake */
Michal Vasko3031aae2016-01-27 16:07:18 +0100654 tlsauth_ch = 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100655 if (SSL_connect(tls) != 1) {
656 ERR("Connecting over TLS failed (%s).", ERR_reason_error_string(ERR_get_error()));
657 SSL_free(tls);
658 return NULL;
659 }
660
661 /* check certificate verification result */
662 verify = SSL_get_verify_result(tls);
663 switch (verify) {
664 case X509_V_OK:
665 VRB("Server certificate successfully verified.");
666 break;
667 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100668 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Michal Vasko80cad7f2015-12-08 14:42:27 +0100669 }
670
671 session = nc_connect_libssl(tls, ctx);
672 if (session) {
673 /* store information into session and the dictionary */
Michal Vasko3031aae2016-01-27 16:07:18 +0100674 session->host = lydict_insert(session->ctx, host, 0);
Michal Vasko80cad7f2015-12-08 14:42:27 +0100675 session->port = port;
676 session->username = lydict_insert(session->ctx, "certificate-based", 0);
677 }
678
679 return session;
680}