blob: becb934453550047843a71b899eda50136b4d343 [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 Vaskodaf9a092016-02-09 10:42:05 +010038extern struct nc_client_opts client_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +010039static struct nc_client_tls_opts tls_opts;
40static struct nc_client_tls_opts tls_ch_opts;
41
42static int tlsauth_ch;
Radek Krejci9f03b482015-10-22 16:02:10 +020043
Radek Krejci9f03b482015-10-22 16:02:10 +020044static int
Michal Vasko11d4cdb2015-10-29 11:42:52 +010045tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +020046{
Michal Vasko11d4cdb2015-10-29 11:42:52 +010047 X509_STORE_CTX store_ctx;
48 X509_OBJECT obj;
49 X509_NAME *subject, *issuer;
50 X509 *cert;
51 X509_CRL *crl;
52 X509_REVOKED *revoked;
53 EVP_PKEY *pubkey;
54 int i, n, rc;
55 ASN1_TIME *next_update = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +010056 struct nc_client_tls_opts *opts;
Radek Krejci9f03b482015-10-22 16:02:10 +020057
Michal Vasko11d4cdb2015-10-29 11:42:52 +010058 if (!preverify_ok) {
59 return 0;
60 }
Radek Krejci9f03b482015-10-22 16:02:10 +020061
Michal Vasko3031aae2016-01-27 16:07:18 +010062 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
63
64 if (!opts->crl_store) {
65 /* nothing to check */
66 return 1;
67 }
68
Michal Vasko11d4cdb2015-10-29 11:42:52 +010069 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
70 subject = X509_get_subject_name(cert);
71 issuer = X509_get_issuer_name(cert);
72
73 /* try to retrieve a CRL corresponding to the _subject_ of
74 * the current certificate in order to verify it's integrity */
75 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +010076 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +010077 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
78 X509_STORE_CTX_cleanup(&store_ctx);
79 crl = obj.data.crl;
80 if (rc > 0 && crl) {
81 next_update = X509_CRL_get_nextUpdate(crl);
82
83 /* verify the signature on this CRL */
84 pubkey = X509_get_pubkey(cert);
85 if (X509_CRL_verify(crl, pubkey) <= 0) {
86 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
87 X509_OBJECT_free_contents(&obj);
88 if (pubkey) {
89 EVP_PKEY_free(pubkey);
90 }
91 return 0; /* fail */
92 }
93 if (pubkey) {
94 EVP_PKEY_free(pubkey);
95 }
96
97 /* check date of CRL to make sure it's not expired */
98 if (!next_update) {
99 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
100 X509_OBJECT_free_contents(&obj);
101 return 0; /* fail */
102 }
103 if (X509_cmp_current_time(next_update) < 0) {
104 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
105 X509_OBJECT_free_contents(&obj);
106 return 0; /* fail */
107 }
108 X509_OBJECT_free_contents(&obj);
109 }
110
111 /* try to retrieve a CRL corresponding to the _issuer_ of
112 * the current certificate in order to check for revocation */
113 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100114 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100115 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
116 X509_STORE_CTX_cleanup(&store_ctx);
117 crl = obj.data.crl;
118 if (rc > 0 && crl) {
119 /* check if the current certificate is revoked by this CRL */
120 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
121 for (i = 0; i < n; i++) {
122 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
123 if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
124 ERR("Certificate revoked!");
125 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
126 X509_OBJECT_free_contents(&obj);
127 return 0; /* fail */
128 }
129 }
130 X509_OBJECT_free_contents(&obj);
131 }
132
133 return 1; /* success */
134}
135
Michal Vaskoe22c6732016-01-29 11:03:02 +0100136static void
137_nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
138{
139 free(opts->cert_path);
140 free(opts->key_path);
141 free(opts->ca_file);
142 free(opts->ca_dir);
143 SSL_CTX_free(opts->tls_ctx);
144
145 free(opts->crl_file);
146 free(opts->crl_dir);
147 X509_STORE_free(opts->crl_store);
148}
149
150API void
151nc_client_tls_destroy_opts(void)
152{
153 _nc_client_tls_destroy_opts(&tls_opts);
154 _nc_client_tls_destroy_opts(&tls_ch_opts);
155}
156
Michal Vasko3031aae2016-01-27 16:07:18 +0100157static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100158_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 +0100159{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100160 if (!client_cert) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100161 ERRARG;
162 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100163 }
164
Michal Vasko3031aae2016-01-27 16:07:18 +0100165 free(opts->cert_path);
166 free(opts->key_path);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100167
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100168 opts->cert_path = strdup(client_cert);
169 if (!opts->cert_path) {
170 ERRMEM;
171 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100172 }
173
Michal Vasko3031aae2016-01-27 16:07:18 +0100174 if (client_key) {
175 opts->key_path = strdup(client_key);
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100176 if (!opts->key_path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100177 ERRMEM;
178 return -1;
179 }
180 } else {
181 opts->key_path = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100182 }
183
Michal Vasko3031aae2016-01-27 16:07:18 +0100184 opts->tls_ctx_change = 1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100185
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100186 return 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100187}
188
Michal Vasko3031aae2016-01-27 16:07:18 +0100189API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100190nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100191{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100192 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100193}
194
195API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100196nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko3031aae2016-01-27 16:07:18 +0100197{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100198 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_ch_opts);
199}
200
201static void
202_nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts)
203{
204 if (!client_cert && !client_key) {
205 ERRARG;
206 return;
207 }
208
209 if (client_cert) {
210 *client_cert = opts->cert_path;
211 }
212 if (client_key) {
213 *client_key = opts->key_path;
214 }
215}
216
217API void
218nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key)
219{
220 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_opts);
221}
222
223API void
224nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key)
225{
226 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100227}
228
229static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100230_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 +0100231{
Michal Vasko3031aae2016-01-27 16:07:18 +0100232 if (!ca_file && !ca_dir) {
233 ERRARG;
234 return -1;
235 }
236
Michal Vasko3031aae2016-01-27 16:07:18 +0100237 free(opts->ca_file);
238 free(opts->ca_dir);
239
240 if (ca_file) {
241 opts->ca_file = strdup(ca_file);
242 if (!opts->ca_file) {
243 ERRMEM;
244 return -1;
245 }
246 } else {
247 opts->ca_file = NULL;
248 }
249
250 if (ca_dir) {
251 opts->ca_dir = strdup(ca_dir);
252 if (!opts->ca_dir) {
253 ERRMEM;
254 return -1;
255 }
256 } else {
257 opts->ca_dir = NULL;
258 }
259
260 opts->tls_ctx_change = 1;
261
262 return 0;
263}
264
265API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100266nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100267{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100268 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100269}
270
271API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100272nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100273{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100274 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
275}
276
277static void
278_nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts)
279{
280 if (!ca_file && !ca_dir) {
281 ERRARG;
282 return;
283 }
284
285 if (ca_file) {
286 *ca_file = opts->ca_file;
287 }
288 if (ca_dir) {
289 *ca_dir = opts->ca_dir;
290 }
291}
292
293API void
294nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
295{
296 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
297}
298
299API void
300nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
301{
302 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100303}
304
305static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100306_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 +0100307{
Michal Vasko3031aae2016-01-27 16:07:18 +0100308 if (!crl_file && !crl_dir) {
309 ERRARG;
310 return -1;
311 }
312
Michal Vasko3031aae2016-01-27 16:07:18 +0100313 free(opts->crl_file);
314 free(opts->crl_dir);
315
316 if (crl_file) {
317 opts->crl_file = strdup(crl_file);
318 if (!opts->crl_file) {
319 ERRMEM;
320 return -1;
321 }
322 } else {
323 opts->crl_file = NULL;
324 }
325
326 if (crl_dir) {
327 opts->crl_dir = strdup(crl_dir);
328 if (!opts->crl_dir) {
329 ERRMEM;
330 return -1;
331 }
332 } else {
333 opts->crl_dir = NULL;
334 }
335
336 opts->crl_store_change = 1;
337
338 return 0;
339}
340
341API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100342nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100343{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100344 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100345}
346
347API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100348nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100349{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100350 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts);
351}
352
353static void
354_nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts)
355{
356 if (!crl_file && !crl_dir) {
357 ERRARG;
358 return;
359 }
360
361 if (crl_file) {
362 *crl_file = opts->crl_file;
363 }
364 if (crl_dir) {
365 *crl_dir = opts->crl_dir;
366 }
367}
368
369API void
370nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir)
371{
372 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts);
373}
374
375API void
376nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir)
377{
378 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100379}
380
381API int
382nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
383{
384 return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL);
385}
386
387API int
388nc_client_tls_ch_del_bind(const char *address, uint16_t port)
389{
390 return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
391}
392
Michal Vasko3031aae2016-01-27 16:07:18 +0100393static int
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100394nc_client_tls_update_opts(struct nc_client_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100395{
396 char *key;
397 X509_LOOKUP *lookup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100398
399 if (!opts->tls_ctx || opts->tls_ctx_change) {
Michal Vaskoe22c6732016-01-29 11:03:02 +0100400 SSL_CTX_free(opts->tls_ctx);
Michal Vasko3031aae2016-01-27 16:07:18 +0100401
402 /* prepare global SSL context, allow only mandatory TLS 1.2 */
403 if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()))) {
404 ERR("Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
405 return -1;
406 }
407 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
408
409 /* get peer certificate */
410 if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
411 ERR("Loading the client certificate from \'%s\' failed (%s).", opts->cert_path, ERR_reason_error_string(ERR_get_error()));
412 return -1;
413 }
414
415 /* if the file with private key not specified, expect that the private key is stored with the certificate */
416 if (!opts->key_path) {
417 key = opts->cert_path;
418 } else {
419 key = opts->key_path;
420 }
421 if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
422 ERR("Loading the client priavte key from \'%s\' failed (%s).", key, ERR_reason_error_string(ERR_get_error()));
423 return -1;
424 }
425
426 if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
427 ERR("Failed to load the locations of trusted CA certificates (%s).", ERR_reason_error_string(ERR_get_error()));
428 return -1;
429 }
430 }
431
432 if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
433 /* set the revocation store with the correct paths for the callback */
434 X509_STORE_free(opts->crl_store);
435
436 opts->crl_store = X509_STORE_new();
437 if (!opts->crl_store) {
438 ERR("Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
439 return -1;
440 }
441 opts->crl_store->cache = 0;
442
443 if (opts->crl_file) {
444 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
445 ERR("Failed to add lookup method to CRL checking.");
446 return -1;
447 }
448 if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
449 ERR("Failed to add the revocation lookup file \"%s\".", opts->crl_file);
450 return -1;
451 }
452 }
453
454 if (opts->crl_dir) {
455 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
456 ERR("Failed to add lookup method to CRL checking.");
457 return -1;
458 }
459 if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
460 ERR("Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
461 return -1;
462 }
463 }
464 }
465
466 return 0;
Radek Krejci9f03b482015-10-22 16:02:10 +0200467}
468
469API struct nc_session *
Michal Vasko9bee18d2015-12-08 14:41:42 +0100470nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200471{
Radek Krejci9f03b482015-10-22 16:02:10 +0200472 struct nc_session *session = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100473 int sock, verify;
474
Michal Vasko3031aae2016-01-27 16:07:18 +0100475 if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100476 ERRARG;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100477 return NULL;
478 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200479
480 /* process parameters */
481 if (!host || strisempty(host)) {
482 host = "localhost";
483 }
484
485 if (!port) {
486 port = NC_PORT_TLS;
487 }
488
Michal Vasko3031aae2016-01-27 16:07:18 +0100489 /* create/update TLS structures */
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100490 if (nc_client_tls_update_opts(&tls_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100491 return NULL;
492 }
493
Radek Krejci9f03b482015-10-22 16:02:10 +0200494 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100495 session = calloc(1, sizeof *session);
Radek Krejci9f03b482015-10-22 16:02:10 +0200496 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100497 ERRMEM;
Radek Krejci9f03b482015-10-22 16:02:10 +0200498 return NULL;
499 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100500 session->status = NC_STATUS_STARTING;
501 session->side = NC_CLIENT;
Radek Krejci9f03b482015-10-22 16:02:10 +0200502
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100503 /* transport lock */
504 session->ti_lock = malloc(sizeof *session->ti_lock);
505 if (!session->ti_lock) {
506 ERRMEM;
507 goto fail;
508 }
509 pthread_mutex_init(session->ti_lock, NULL);
510
511 /* fill the session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100512 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100513 if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100514 ERR("Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100515 goto fail;
516 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200517
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100518 /* create and assign socket */
Michal Vaskof05562c2016-01-20 12:06:43 +0100519 sock = nc_sock_connect(host, port);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100520 if (sock == -1) {
521 goto fail;
522 }
523 SSL_set_fd(session->ti.tls, sock);
524
525 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
526 SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
527
528 /* connect and perform the handshake */
Michal Vasko3031aae2016-01-27 16:07:18 +0100529 tlsauth_ch = 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100530 if (SSL_connect(session->ti.tls) != 1) {
531 ERR("Connecting over TLS failed (%s).", ERR_reason_error_string(ERR_get_error()));
532 goto fail;
533 }
534
535 /* check certificate verification result */
536 verify = SSL_get_verify_result(session->ti.tls);
537 switch (verify) {
538 case X509_V_OK:
539 VRB("Server certificate successfully verified.");
540 break;
541 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100542 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Radek Krejci9f03b482015-10-22 16:02:10 +0200543 }
544
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100545 /* assign context (dicionary needed for handshake) */
546 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +0100547 if (client_opts.schema_searchpath) {
548 ctx = ly_ctx_new(client_opts.schema_searchpath);
549 } else {
550 ctx = ly_ctx_new(SCHEMAS_DIR);
551 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100552 } else {
553 session->flags |= NC_SESSION_SHAREDCTX;
554 }
555 session->ctx = ctx;
556
Radek Krejci9f03b482015-10-22 16:02:10 +0200557 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100558 if (nc_handshake(session)) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100559 goto fail;
Radek Krejci9f03b482015-10-22 16:02:10 +0200560 }
Michal Vaskoad611702015-12-03 13:41:51 +0100561 session->status = NC_STATUS_RUNNING;
Radek Krejci9f03b482015-10-22 16:02:10 +0200562
Michal Vaskoef578332016-01-25 13:20:09 +0100563 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100564 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100565 }
566
567 /* store information into session and the dictionary */
568 session->host = lydict_insert(ctx, host, 0);
569 session->port = port;
Michal Vasko9bee18d2015-12-08 14:41:42 +0100570 session->username = lydict_insert(ctx, "certificate-based", 0);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100571
Radek Krejci9f03b482015-10-22 16:02:10 +0200572 return session;
573
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100574fail:
Radek Krejci9f03b482015-10-22 16:02:10 +0200575 nc_session_free(session);
576 return NULL;
577}
578
579API struct nc_session *
580nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
581{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100582 struct nc_session *session;
Radek Krejci9f03b482015-10-22 16:02:10 +0200583
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100584 /* check TLS session status */
585 if (!tls || !SSL_is_init_finished(tls)) {
586 ERR("Supplied TLS session is not fully connected!");
587 return NULL;
588 }
589
590 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100591 session = calloc(1, sizeof *session);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100592 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100593 ERRMEM;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100594 return NULL;
595 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100596 session->status = NC_STATUS_STARTING;
597 session->side = NC_CLIENT;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100598
599 /* transport lock */
600 session->ti_lock = malloc(sizeof *session->ti_lock);
601 if (!session->ti_lock) {
602 ERRMEM;
603 goto fail;
604 }
605 pthread_mutex_init(session->ti_lock, NULL);
606
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100607 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100608 session->ti.tls = tls;
609
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100610 /* assign context (dicionary needed for handshake) */
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100611 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +0100612 if (client_opts.schema_searchpath) {
613 ctx = ly_ctx_new(client_opts.schema_searchpath);
614 } else {
615 ctx = ly_ctx_new(SCHEMAS_DIR);
616 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100617 } else {
618 session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100619 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100620 session->ctx = ctx;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100621
622 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100623 if (nc_handshake(session)) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100624 goto fail;
625 }
Michal Vaskoad611702015-12-03 13:41:51 +0100626 session->status = NC_STATUS_RUNNING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100627
Michal Vaskoef578332016-01-25 13:20:09 +0100628 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100629 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100630 }
631
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100632 return session;
633
634fail:
635 nc_session_free(session);
Radek Krejci9f03b482015-10-22 16:02:10 +0200636 return NULL;
637}
638
Michal Vasko3031aae2016-01-27 16:07:18 +0100639struct nc_session *
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100640nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx)
Michal Vasko80cad7f2015-12-08 14:42:27 +0100641{
Michal Vasko3031aae2016-01-27 16:07:18 +0100642 int verify;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100643 SSL *tls;
644 struct nc_session *session;
645
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100646 if (nc_client_tls_update_opts(&tls_ch_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100647 close(sock);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100648 return NULL;
649 }
650
Michal Vasko3031aae2016-01-27 16:07:18 +0100651 if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
Michal Vasko80cad7f2015-12-08 14:42:27 +0100652 ERR("Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
653 close(sock);
654 return NULL;
655 }
656
657 SSL_set_fd(tls, sock);
658
659 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
660 SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
661
662 /* connect and perform the handshake */
Michal Vasko3031aae2016-01-27 16:07:18 +0100663 tlsauth_ch = 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100664 if (SSL_connect(tls) != 1) {
665 ERR("Connecting over TLS failed (%s).", ERR_reason_error_string(ERR_get_error()));
666 SSL_free(tls);
667 return NULL;
668 }
669
670 /* check certificate verification result */
671 verify = SSL_get_verify_result(tls);
672 switch (verify) {
673 case X509_V_OK:
674 VRB("Server certificate successfully verified.");
675 break;
676 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100677 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Michal Vasko80cad7f2015-12-08 14:42:27 +0100678 }
679
680 session = nc_connect_libssl(tls, ctx);
681 if (session) {
Michal Vasko4282fae2016-02-18 10:03:42 +0100682 session->flags |= NC_SESSION_CALLHOME;
683
Michal Vasko80cad7f2015-12-08 14:42:27 +0100684 /* store information into session and the dictionary */
Michal Vasko3031aae2016-01-27 16:07:18 +0100685 session->host = lydict_insert(session->ctx, host, 0);
Michal Vasko80cad7f2015-12-08 14:42:27 +0100686 session->port = port;
687 session->username = lydict_insert(session->ctx, "certificate-based", 0);
688 }
689
690 return session;
691}