Merging upstream version 3.5.5 (Closes: #1098233).
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
c86ae7dcba
commit
6af28b7e8e
144 changed files with 43534 additions and 11497 deletions
|
@ -1,9 +1,11 @@
|
|||
/**
|
||||
* \file config.h
|
||||
* \author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* \brief libnetconf2 various configuration settings.
|
||||
* @file config.h
|
||||
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 various configuration settings.
|
||||
*
|
||||
* Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2024 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
|
@ -33,14 +35,29 @@
|
|||
#cmakedefine HAVE_SHADOW
|
||||
|
||||
/*
|
||||
* Support for crypt.h
|
||||
* Support for terminal in/out
|
||||
*/
|
||||
#cmakedefine HAVE_CRYPT
|
||||
#cmakedefine HAVE_TERMIOS
|
||||
|
||||
/*
|
||||
* Location of installed basic YANG modules on the system
|
||||
* Support for keyboard-interactive SSH authentication method
|
||||
*/
|
||||
#define NC_YANG_DIR "@YANG_MODULE_DIR@"
|
||||
#cmakedefine HAVE_LIBPAM
|
||||
|
||||
/*
|
||||
* Use MbedTLS as TLS back-end
|
||||
*/
|
||||
#cmakedefine HAVE_MBEDTLS
|
||||
|
||||
/*
|
||||
* Location of installed YANG modules on the system
|
||||
*/
|
||||
#define NC_SERVER_SEARCH_DIR "@YANG_MODULE_DIR@"
|
||||
|
||||
/*
|
||||
* Location of installed YANG modules on the system
|
||||
*/
|
||||
#define NC_CLIENT_SEARCH_DIR "@CLIENT_SEARCH_DIR@"
|
||||
|
||||
/*
|
||||
* Inactive read timeout
|
||||
|
|
403
src/io.c
403
src/io.c
|
@ -1,9 +1,11 @@
|
|||
/**
|
||||
* \file io.c
|
||||
* \author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* \brief libnetconf2 - input/output functions
|
||||
* @file io.c
|
||||
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 - input/output functions
|
||||
*
|
||||
* Copyright (c) 2015 CESNET, z.s.p.o.
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2024 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
|
@ -13,27 +15,30 @@
|
|||
*/
|
||||
|
||||
#define _GNU_SOURCE /* asprintf, signals */
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <poll.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef NC_ENABLED_TLS
|
||||
# include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "libnetconf.h"
|
||||
#include "config.h"
|
||||
#include "log_p.h"
|
||||
#include "messages_p.h"
|
||||
#include "netconf.h"
|
||||
#include "session.h"
|
||||
#include "session_p.h"
|
||||
#include "session_wrapper.h"
|
||||
|
||||
const char *nc_msgtype2str[] = {
|
||||
"error",
|
||||
|
@ -49,49 +54,13 @@ const char *nc_msgtype2str[] = {
|
|||
|
||||
#define BUFFERSIZE 512
|
||||
|
||||
#ifdef NC_ENABLED_TLS
|
||||
|
||||
static char *
|
||||
nc_ssl_error_get_reasons(void)
|
||||
{
|
||||
unsigned int e;
|
||||
int reason_size, reason_len;
|
||||
char *reasons = NULL;
|
||||
|
||||
reason_size = 1;
|
||||
reason_len = 0;
|
||||
while ((e = ERR_get_error())) {
|
||||
if (reason_len) {
|
||||
/* add "; " */
|
||||
reason_size += 2;
|
||||
reasons = nc_realloc(reasons, reason_size);
|
||||
if (!reasons) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
reason_len += sprintf(reasons + reason_len, "; ");
|
||||
}
|
||||
reason_size += strlen(ERR_reason_error_string(e));
|
||||
reasons = nc_realloc(reasons, reason_size);
|
||||
if (!reasons) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e));
|
||||
}
|
||||
|
||||
return reasons;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static ssize_t
|
||||
nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_timeout, struct timespec *ts_act_timeout)
|
||||
nc_read(struct nc_session *session, char *buf, uint32_t count, uint32_t inact_timeout, struct timespec *ts_act_timeout)
|
||||
{
|
||||
size_t readd = 0;
|
||||
uint32_t readd = 0;
|
||||
ssize_t r = -1;
|
||||
int fd, interrupted;
|
||||
struct timespec ts_cur, ts_inact_timeout;
|
||||
struct timespec ts_inact_timeout;
|
||||
|
||||
assert(session);
|
||||
assert(buf);
|
||||
|
@ -104,8 +73,7 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time
|
|||
return 0;
|
||||
}
|
||||
|
||||
nc_gettimespec_mono(&ts_inact_timeout);
|
||||
nc_addtimespec(&ts_inact_timeout, inact_timeout);
|
||||
nc_timeouttime_get(&ts_inact_timeout, inact_timeout);
|
||||
do {
|
||||
interrupted = 0;
|
||||
switch (session->ti_type) {
|
||||
|
@ -139,8 +107,8 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time
|
|||
}
|
||||
break;
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
case NC_TI_LIBSSH:
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
case NC_TI_SSH:
|
||||
/* read via libssh */
|
||||
r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
|
||||
if (r == SSH_AGAIN) {
|
||||
|
@ -161,48 +129,15 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time
|
|||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef NC_ENABLED_TLS
|
||||
case NC_TI_OPENSSL:
|
||||
/* read via OpenSSL */
|
||||
ERR_clear_error();
|
||||
r = SSL_read(session->ti.tls, buf + readd, count - readd);
|
||||
if (r <= 0) {
|
||||
int e;
|
||||
char *reasons;
|
||||
|
||||
switch (e = SSL_get_error(session->ti.tls, r)) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
r = 0;
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
ERR(session, "Communication socket unexpectedly closed (OpenSSL).");
|
||||
session->status = NC_STATUS_INVALID;
|
||||
session->term_reason = NC_SESSION_TERM_DROPPED;
|
||||
return -1;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
ERR(session, "SSL socket error (%s).", errno ? strerror(errno) : "unexpected EOF");
|
||||
session->status = NC_STATUS_INVALID;
|
||||
session->term_reason = NC_SESSION_TERM_OTHER;
|
||||
return -1;
|
||||
case SSL_ERROR_SSL:
|
||||
reasons = nc_ssl_error_get_reasons();
|
||||
ERR(session, "SSL error (%s).", reasons);
|
||||
free(reasons);
|
||||
session->status = NC_STATUS_INVALID;
|
||||
session->term_reason = NC_SESSION_TERM_OTHER;
|
||||
return -1;
|
||||
default:
|
||||
ERR(session, "Unknown SSL error occured (err code %d).", e);
|
||||
session->status = NC_STATUS_INVALID;
|
||||
session->term_reason = NC_SESSION_TERM_OTHER;
|
||||
return -1;
|
||||
}
|
||||
case NC_TI_TLS:
|
||||
r = nc_tls_read_wrap(session, (unsigned char *)buf + readd, count - readd);
|
||||
if (r < 0) {
|
||||
/* non-recoverable error */
|
||||
return r;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
}
|
||||
|
||||
if (r == 0) {
|
||||
|
@ -210,9 +145,8 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time
|
|||
if (!interrupted) {
|
||||
usleep(NC_TIMEOUT_STEP);
|
||||
}
|
||||
nc_gettimespec_mono(&ts_cur);
|
||||
if ((nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) || (nc_difftimespec(&ts_cur, ts_act_timeout) < 1)) {
|
||||
if (nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) {
|
||||
if ((nc_timeouttime_cur_diff(&ts_inact_timeout) < 1) || (nc_timeouttime_cur_diff(ts_act_timeout) < 1)) {
|
||||
if (nc_timeouttime_cur_diff(&ts_inact_timeout) < 1) {
|
||||
ERR(session, "Inactive read timeout elapsed.");
|
||||
} else {
|
||||
ERR(session, "Active read timeout elapsed.");
|
||||
|
@ -226,8 +160,7 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time
|
|||
readd += r;
|
||||
|
||||
/* reset inactive timeout */
|
||||
nc_gettimespec_mono(&ts_inact_timeout);
|
||||
nc_addtimespec(&ts_inact_timeout, inact_timeout);
|
||||
nc_timeouttime_get(&ts_inact_timeout, inact_timeout);
|
||||
}
|
||||
|
||||
} while (readd < count);
|
||||
|
@ -249,10 +182,7 @@ nc_read_chunk(struct nc_session *session, size_t len, uint32_t inact_timeout, st
|
|||
}
|
||||
|
||||
*chunk = malloc((len + 1) * sizeof **chunk);
|
||||
if (!*chunk) {
|
||||
ERRMEM;
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!*chunk, -1);
|
||||
|
||||
r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
|
||||
if (r <= 0) {
|
||||
|
@ -282,10 +212,7 @@ nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint
|
|||
size = BUFFERSIZE;
|
||||
}
|
||||
chunk = malloc((size + 1) * sizeof *chunk);
|
||||
if (!chunk) {
|
||||
ERRMEM;
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!chunk, -1);
|
||||
|
||||
len = strlen(endtag);
|
||||
while (1) {
|
||||
|
@ -301,10 +228,7 @@ nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint
|
|||
/* get more memory */
|
||||
size = size + BUFFERSIZE;
|
||||
chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
|
||||
if (!chunk) {
|
||||
ERRMEM;
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!chunk, -1);
|
||||
}
|
||||
|
||||
/* get another character */
|
||||
|
@ -362,8 +286,7 @@ nc_read_msg_io(struct nc_session *session, int io_timeout, struct ly_in **msg, i
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
nc_gettimespec_mono(&ts_act_timeout);
|
||||
nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
|
||||
nc_timeouttime_get(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
|
||||
|
||||
if (!io_locked) {
|
||||
/* SESSION IO LOCK */
|
||||
|
@ -428,11 +351,7 @@ nc_read_msg_io(struct nc_session *session, int io_timeout, struct ly_in **msg, i
|
|||
|
||||
/* realloc message buffer, remember to count terminating null byte */
|
||||
data = nc_realloc(data, len + chunk_len + 1);
|
||||
if (!data) {
|
||||
ERRMEM;
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
NC_CHECK_ERRMEM_GOTO(!data, ret = -1, cleanup);
|
||||
memcpy(data + len, chunk, chunk_len);
|
||||
len += chunk_len;
|
||||
data[len] = '\0';
|
||||
|
@ -469,7 +388,6 @@ cleanup:
|
|||
static int
|
||||
nc_read_poll(struct nc_session *session, int io_timeout)
|
||||
{
|
||||
sigset_t sigmask, origmask;
|
||||
int ret = -2;
|
||||
struct pollfd fds;
|
||||
|
||||
|
@ -479,8 +397,13 @@ nc_read_poll(struct nc_session *session, int io_timeout)
|
|||
}
|
||||
|
||||
switch (session->ti_type) {
|
||||
#ifdef NC_ENABLED_SSH
|
||||
case NC_TI_LIBSSH:
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
case NC_TI_SSH:
|
||||
if (io_timeout == -1) {
|
||||
/* BUG libssh 0.11.0 replaces timeout -1 with 0 for non-blocking sessions */
|
||||
io_timeout = INT_MAX;
|
||||
}
|
||||
|
||||
/* EINTR is handled, it resumes waiting */
|
||||
ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
|
||||
if (ret == SSH_ERROR) {
|
||||
|
@ -501,10 +424,8 @@ nc_read_poll(struct nc_session *session, int io_timeout)
|
|||
fds.revents = 0;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef NC_ENABLED_TLS
|
||||
case NC_TI_OPENSSL:
|
||||
ret = SSL_pending(session->ti.tls);
|
||||
case NC_TI_TLS:
|
||||
ret = nc_tls_get_num_pending_bytes_wrap(session->ti.tls.session);
|
||||
if (ret) {
|
||||
/* some buffered TLS data available */
|
||||
ret = 1;
|
||||
|
@ -512,8 +433,8 @@ nc_read_poll(struct nc_session *session, int io_timeout)
|
|||
break;
|
||||
}
|
||||
|
||||
fds.fd = SSL_get_fd(session->ti.tls);
|
||||
#endif
|
||||
fds.fd = nc_tls_get_fd_wrap(session);
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
/* fallthrough */
|
||||
case NC_TI_FD:
|
||||
case NC_TI_UNIX:
|
||||
|
@ -526,11 +447,7 @@ nc_read_poll(struct nc_session *session, int io_timeout)
|
|||
fds.events = POLLIN;
|
||||
fds.revents = 0;
|
||||
|
||||
sigfillset(&sigmask);
|
||||
pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
|
||||
ret = poll(&fds, 1, io_timeout);
|
||||
pthread_sigmask(SIG_SETMASK, &origmask, NULL);
|
||||
|
||||
ret = nc_poll(&fds, 1, io_timeout);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -600,7 +517,7 @@ nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct ly_in **m
|
|||
|
||||
/* does not really log, only fatal errors */
|
||||
int
|
||||
nc_session_is_connected(struct nc_session *session)
|
||||
nc_session_is_connected(const struct nc_session *session)
|
||||
{
|
||||
int ret;
|
||||
struct pollfd fds;
|
||||
|
@ -612,15 +529,13 @@ nc_session_is_connected(struct nc_session *session)
|
|||
case NC_TI_UNIX:
|
||||
fds.fd = session->ti.unixsock.sock;
|
||||
break;
|
||||
#ifdef NC_ENABLED_SSH
|
||||
case NC_TI_LIBSSH:
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
case NC_TI_SSH:
|
||||
return ssh_is_connected(session->ti.libssh.session);
|
||||
#endif
|
||||
#ifdef NC_ENABLED_TLS
|
||||
case NC_TI_OPENSSL:
|
||||
fds.fd = SSL_get_fd(session->ti.tls);
|
||||
case NC_TI_TLS:
|
||||
fds.fd = nc_tls_get_fd_wrap(session);
|
||||
break;
|
||||
#endif
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -632,11 +547,8 @@ nc_session_is_connected(struct nc_session *session)
|
|||
fds.events = POLLIN;
|
||||
fds.revents = 0;
|
||||
|
||||
errno = 0;
|
||||
while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR)) {}
|
||||
|
||||
ret = nc_poll(&fds, 1, 0);
|
||||
if (ret == -1) {
|
||||
ERR(session, "poll failed (%s).", strerror(errno));
|
||||
return 0;
|
||||
} else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
|
||||
return 0;
|
||||
|
@ -646,21 +558,26 @@ nc_session_is_connected(struct nc_session *session)
|
|||
}
|
||||
|
||||
#define WRITE_BUFSIZE (2 * BUFFERSIZE)
|
||||
struct wclb_arg {
|
||||
struct nc_wclb_arg {
|
||||
struct nc_session *session;
|
||||
char buf[WRITE_BUFSIZE];
|
||||
size_t len;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Write to a NETCONF session.
|
||||
*
|
||||
* @param[in] session Session to write to.
|
||||
* @param[in] buf Buffer to write.
|
||||
* @param[in] count Count of bytes from @p buf to write.
|
||||
* @return Number of bytes written.
|
||||
* @return -1 on error.
|
||||
*/
|
||||
static int
|
||||
nc_write(struct nc_session *session, const void *buf, size_t count)
|
||||
nc_write(struct nc_session *session, const void *buf, uint32_t count)
|
||||
{
|
||||
int c, fd, interrupted;
|
||||
size_t written = 0;
|
||||
|
||||
#ifdef NC_ENABLED_TLS
|
||||
unsigned long e;
|
||||
#endif
|
||||
uint32_t written = 0;
|
||||
|
||||
if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
|
||||
return -1;
|
||||
|
@ -674,7 +591,7 @@ nc_write(struct nc_session *session, const void *buf, size_t count)
|
|||
return -1;
|
||||
}
|
||||
|
||||
DBG(session, "Sending message:\n%.*s\n", count, buf);
|
||||
DBG(session, "Sending message:\n%.*s\n", (int)count, buf);
|
||||
|
||||
do {
|
||||
interrupted = 0;
|
||||
|
@ -689,13 +606,13 @@ nc_write(struct nc_session *session, const void *buf, size_t count)
|
|||
c = 0;
|
||||
interrupted = 1;
|
||||
} else if (c < 0) {
|
||||
ERR(session, "socket error (%s).", strerror(errno));
|
||||
ERR(session, "Socket error (%s).", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
case NC_TI_LIBSSH:
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
case NC_TI_SSH:
|
||||
if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
|
||||
if (ssh_channel_is_closed(session->ti.libssh.channel)) {
|
||||
ERR(session, "SSH channel unexpectedly closed.");
|
||||
|
@ -712,36 +629,14 @@ nc_write(struct nc_session *session, const void *buf, size_t count)
|
|||
return -1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef NC_ENABLED_TLS
|
||||
case NC_TI_OPENSSL:
|
||||
c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
|
||||
if (c < 1) {
|
||||
char *reasons;
|
||||
|
||||
switch ((e = SSL_get_error(session->ti.tls, c))) {
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
ERR(session, "SSL connection was properly closed.");
|
||||
return -1;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
case SSL_ERROR_WANT_READ:
|
||||
c = 0;
|
||||
break;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
ERR(session, "SSL socket error (%s).", strerror(errno));
|
||||
return -1;
|
||||
case SSL_ERROR_SSL:
|
||||
reasons = nc_ssl_error_get_reasons();
|
||||
ERR(session, "SSL error (%s).", reasons);
|
||||
free(reasons);
|
||||
return -1;
|
||||
default:
|
||||
ERR(session, "Unknown SSL error occured (err code %d).", e);
|
||||
return -1;
|
||||
}
|
||||
case NC_TI_TLS:
|
||||
c = nc_tls_write_wrap(session, (const unsigned char *)(buf + written), count - written);
|
||||
if (c < 0) {
|
||||
/* possible client dc, or some socket/TLS communication error */
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
default:
|
||||
ERRINT;
|
||||
return -1;
|
||||
|
@ -758,30 +653,47 @@ nc_write(struct nc_session *session, const void *buf, size_t count)
|
|||
return written;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write the start tag and the message part of a chunked-framing NETCONF message.
|
||||
*
|
||||
* @param[in] session Session to write to.
|
||||
* @param[in] buf Message buffer to write.
|
||||
* @param[in] count Count of bytes from @p buf to write.
|
||||
* @return Number of bytes written.
|
||||
* @return -1 on error.
|
||||
*/
|
||||
static int
|
||||
nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
|
||||
nc_write_starttag_and_msg(struct nc_session *session, const void *buf, uint32_t count)
|
||||
{
|
||||
int ret = 0, c;
|
||||
int ret = 0, r;
|
||||
char chunksize[24];
|
||||
|
||||
// warning: ‘%zu’ directive writing between 4 and 20 bytes into a region of size 18 [-Wformat-overflow=]
|
||||
if (session->version == NC_VERSION_11) {
|
||||
sprintf(chunksize, "\n#%zu\n", count);
|
||||
ret = nc_write(session, chunksize, strlen(chunksize));
|
||||
if (ret == -1) {
|
||||
r = sprintf(chunksize, "\n#%" PRIu32 "\n", count);
|
||||
|
||||
r = nc_write(session, chunksize, r);
|
||||
if (r == -1) {
|
||||
return -1;
|
||||
}
|
||||
ret += r;
|
||||
}
|
||||
|
||||
c = nc_write(session, buf, count);
|
||||
if (c == -1) {
|
||||
r = nc_write(session, buf, count);
|
||||
if (r == -1) {
|
||||
return -1;
|
||||
}
|
||||
ret += c;
|
||||
ret += r;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write the end tag part of a chunked-framing NETCONF message.
|
||||
*
|
||||
* @param[in] session Session to write to.
|
||||
* @return Number of bytes written.
|
||||
* @return -1 on error.
|
||||
*/
|
||||
static int
|
||||
nc_write_endtag(struct nc_session *session)
|
||||
{
|
||||
|
@ -796,8 +708,15 @@ nc_write_endtag(struct nc_session *session)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Flush all the data buffered for writing.
|
||||
*
|
||||
* @param[in] warg Write callback structure to flush.
|
||||
* @return Number of written bytes.
|
||||
* @return -1 on error.
|
||||
*/
|
||||
static int
|
||||
nc_write_clb_flush(struct wclb_arg *warg)
|
||||
nc_write_clb_flush(struct nc_wclb_arg *warg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -810,12 +729,22 @@ nc_write_clb_flush(struct wclb_arg *warg)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write callback buffering the data in a write structure.
|
||||
*
|
||||
* @param[in] arg Write structure used for buffering.
|
||||
* @param[in] buf Buffer to write.
|
||||
* @param[in] count Count of bytes to write from @p buf.
|
||||
* @param[in] xmlcontent Whether the data are actually printed as part of an XML in which case they need to be encoded.
|
||||
* @return Number of written bytes.
|
||||
* @return -1 on error.
|
||||
*/
|
||||
static ssize_t
|
||||
nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
|
||||
nc_write_clb(void *arg, const void *buf, uint32_t count, int xmlcontent)
|
||||
{
|
||||
int ret = 0, c;
|
||||
size_t l;
|
||||
struct wclb_arg *warg = (struct wclb_arg *)arg;
|
||||
ssize_t ret = 0, c;
|
||||
uint32_t l;
|
||||
struct nc_wclb_arg *warg = arg;
|
||||
|
||||
if (!buf) {
|
||||
c = nc_write_clb_flush(warg);
|
||||
|
@ -895,6 +824,9 @@ nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write print callback used by libyang.
|
||||
*/
|
||||
static ssize_t
|
||||
nc_write_xmlclb(void *arg, const void *buf, size_t count)
|
||||
{
|
||||
|
@ -921,7 +853,7 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
|
|||
struct nc_server_notif *notif;
|
||||
struct nc_server_reply *reply;
|
||||
char *buf;
|
||||
struct wclb_arg arg;
|
||||
struct nc_wclb_arg arg;
|
||||
const char **capabilities;
|
||||
uint32_t *sid = NULL, i, wd = 0;
|
||||
LY_ERR lyrc;
|
||||
|
@ -954,11 +886,7 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
|
|||
/* <rpc> open */
|
||||
count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%" PRIu64 "\"%s>",
|
||||
NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
|
||||
if (count == -1) {
|
||||
ERRMEM;
|
||||
ret = NC_MSG_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup);
|
||||
nc_write_clb((void *)&arg, buf, count, 0);
|
||||
free(buf);
|
||||
|
||||
|
@ -969,7 +897,7 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
|
|||
}
|
||||
|
||||
/* rpc data */
|
||||
if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, op, LYD_XML, LYD_PRINT_SHRINK)) {
|
||||
if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, op, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_KEEPEMPTYCONT)) {
|
||||
ret = NC_MSG_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -991,27 +919,21 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
|
|||
rpc_envp = va_arg(ap, struct lyd_node_opaq *);
|
||||
reply = va_arg(ap, struct nc_server_reply *);
|
||||
|
||||
if (!rpc_envp) {
|
||||
/* can be NULL if replying with a malformed-message error */
|
||||
nc_write_clb((void *)&arg, "<rpc-reply xmlns=\"" NC_NS_BASE "\">", 18 + strlen(NC_NS_BASE) + 2, 0);
|
||||
|
||||
assert(reply->type == NC_RPL_ERROR);
|
||||
if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_server_reply_error *)reply)->err, LYD_XML,
|
||||
LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)) {
|
||||
/* build a rpc-reply opaque node that can be simply printed */
|
||||
if (rpc_envp) {
|
||||
if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns,
|
||||
&reply_envp)) {
|
||||
ERRINT;
|
||||
ret = NC_MSG_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, NULL, NC_NS_BASE,
|
||||
&reply_envp)) {
|
||||
ERRINT;
|
||||
ret = NC_MSG_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* build a rpc-reply opaque node that can be simply printed */
|
||||
if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns,
|
||||
&reply_envp)) {
|
||||
ERRINT;
|
||||
ret = NC_MSG_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
switch (reply->type) {
|
||||
|
@ -1060,7 +982,9 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
|
|||
}
|
||||
|
||||
/* temporary */
|
||||
((struct lyd_node_opaq *)reply_envp)->attr = rpc_envp->attr;
|
||||
if (rpc_envp) {
|
||||
((struct lyd_node_opaq *)reply_envp)->attr = rpc_envp->attr;
|
||||
}
|
||||
|
||||
/* print */
|
||||
lyrc = lyd_print_clb(nc_write_xmlclb, (void *)&arg, reply_envp, LYD_XML, LYD_PRINT_SHRINK | wd);
|
||||
|
@ -1117,11 +1041,7 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
|
|||
sid = va_arg(ap, uint32_t *);
|
||||
|
||||
count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
|
||||
if (count == -1) {
|
||||
ERRMEM;
|
||||
ret = NC_MSG_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup);
|
||||
nc_write_clb((void *)&arg, buf, count, 0);
|
||||
free(buf);
|
||||
for (i = 0; capabilities[i]; i++) {
|
||||
|
@ -1130,12 +1050,8 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
|
|||
nc_write_clb((void *)&arg, "</capability>", 13, 0);
|
||||
}
|
||||
if (sid) {
|
||||
count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
|
||||
if (count == -1) {
|
||||
ERRMEM;
|
||||
ret = NC_MSG_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
count = asprintf(&buf, "</capabilities><session-id>%" PRIu32 "</session-id></hello>", *sid);
|
||||
NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup);
|
||||
nc_write_clb((void *)&arg, buf, count, 0);
|
||||
free(buf);
|
||||
} else {
|
||||
|
@ -1179,7 +1095,7 @@ nc_realloc(void *ptr, size_t size)
|
|||
}
|
||||
|
||||
struct passwd *
|
||||
nc_getpwuid(uid_t uid, struct passwd *pwd_buf, char **buf, size_t *buf_size)
|
||||
nc_getpw(uid_t uid, const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size)
|
||||
{
|
||||
struct passwd *pwd = NULL;
|
||||
long sys_size;
|
||||
|
@ -1197,16 +1113,21 @@ nc_getpwuid(uid_t uid, struct passwd *pwd_buf, char **buf, size_t *buf_size)
|
|||
|
||||
/* allocate some buffer */
|
||||
*buf = nc_realloc(*buf, *buf_size);
|
||||
if (!*buf) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!*buf, NULL);
|
||||
|
||||
ret = getpwuid_r(uid, pwd_buf, *buf, *buf_size, &pwd);
|
||||
if (username) {
|
||||
ret = getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd);
|
||||
} else {
|
||||
ret = getpwuid_r(uid, pwd_buf, *buf, *buf_size, &pwd);
|
||||
}
|
||||
} while (ret && (ret == ERANGE));
|
||||
|
||||
if (ret) {
|
||||
ERR(NULL, "Retrieving UID \"%lu\" passwd entry failed (%s).", (unsigned long int)uid, strerror(ret));
|
||||
if (username) {
|
||||
ERR(NULL, "Retrieving username \"%s\" passwd entry failed (%s).", username, strerror(ret));
|
||||
} else {
|
||||
ERR(NULL, "Retrieving UID \"%lu\" passwd entry failed (%s).", (unsigned long)uid, strerror(ret));
|
||||
}
|
||||
}
|
||||
return pwd;
|
||||
}
|
||||
|
|
669
src/libnetconf.h
669
src/libnetconf.h
|
@ -1,669 +0,0 @@
|
|||
/**
|
||||
* @file libnetconf.h
|
||||
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 main internal header.
|
||||
*
|
||||
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef NC_LIBNETCONF_H_
|
||||
#define NC_LIBNETCONF_H_
|
||||
|
||||
#include "config.h"
|
||||
#include "log_p.h"
|
||||
#include "messages_p.h"
|
||||
#include "netconf.h"
|
||||
#include "session_p.h"
|
||||
|
||||
/* Tests whether string is empty or non-empty. */
|
||||
#define strisempty(str) ((str)[0] == '\0')
|
||||
#define strnonempty(str) ((str)[0] != '\0')
|
||||
|
||||
/**
|
||||
* @mainpage About
|
||||
*
|
||||
* libnetconf2 is a NETCONF library in C handling NETCONF authentication and all NETCONF
|
||||
* RPC communication both server and client-side. Note that NETCONF datastore implementation
|
||||
* is not a part of this library. The library supports both NETCONF 1.0
|
||||
* ([RFC 4741](https://tools.ietf.org/html/rfc4741)) as well as NETCONF 1.1
|
||||
* ([RFC 6241](https://tools.ietf.org/html/rfc6241)).
|
||||
*
|
||||
* @section about-features Main Features
|
||||
*
|
||||
* - Creating SSH ([RFC 4742](https://tools.ietf.org/html/rfc4742), [RFC 6242](https://tools.ietf.org/html/rfc6242)),
|
||||
* using [libssh](https://www.libssh.org/), or TLS ([RFC 7589](https://tools.ietf.org/html/rfc7589)),
|
||||
* using [OpenSSL](https://www.openssl.org/), authenticated NETCONF sessions.
|
||||
* - Creating NETCONF sessions with a pre-established transport protocol
|
||||
* (using this mechanism the communication can be tunneled through sshd(8), for instance).
|
||||
* - Creating NETCONF Call Home sessions ([RFC 8071](https://tools.ietf.org/html/rfc8071)).
|
||||
* - Creating, sending, receiving, and replying to RPCs ([RFC 4741](https://tools.ietf.org/html/rfc4741),
|
||||
* [RFC 6241](https://tools.ietf.org/html/rfc6241)).
|
||||
* - Creating, sending and receiving NETCONF Event Notifications ([RFC 5277](https://tools.ietf.org/html/rfc5277)),
|
||||
*
|
||||
* @section about-license License
|
||||
*
|
||||
* Copyright (c) 2015-2021 CESNET, z.s.p.o.
|
||||
*
|
||||
* (The BSD 3-Clause License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name of the Company nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page howto How To ...
|
||||
*
|
||||
* - @subpage howtoinit
|
||||
* - @subpage howtoclient
|
||||
* - @subpage howtoserver
|
||||
* - @subpage howtoclientcomm
|
||||
* - @subpage howtoservercomm
|
||||
* - @subpage howtotimeouts
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page howtoinit Init and Thread-safety Information
|
||||
*
|
||||
* Before working with the library, it must be initialized using ::nc_client_init()
|
||||
* or ::nc_server_init(). Based on how the library was compiled, also _libssh_ and/or
|
||||
* _libssh_/_libcrypto_ are initialized (for multi-threaded use) too. To prevent
|
||||
* any reachable memory at the end of your application, there are complementary
|
||||
* destroy functions (::nc_server_destroy() and ::nc_client_destroy() available. If your
|
||||
* application is multi-threaded, call the destroy functions in the main thread,
|
||||
* after all the other threads have ended. In every other thread you should call
|
||||
* ::nc_thread_destroy() just before it exits.
|
||||
*
|
||||
* If _libnetconf2_ is used in accordance with this information, there should
|
||||
* not be memory leaks of any kind at program exit. For thread-safety details
|
||||
* of _libssh_, _libssl_, and _libcrypto_, please refer to the corresponding project
|
||||
* documentation. _libnetconf2_ thread-safety information is below.
|
||||
*
|
||||
* Client
|
||||
* ------
|
||||
*
|
||||
* Optionally, a client can specify two alternative ways to get schemas needed when connecting
|
||||
* with a server. The primary way is to read local files in searchpath (and its subdirectories)
|
||||
* specified via ::nc_client_set_schema_searchpath(). Alternatively, _libnetconf2_ can use callback
|
||||
* provided via ::nc_client_set_schema_callback(). If these ways do not succeed and the server
|
||||
* implements NETCONF \<get-schema\> operation, the schema is retrieved from the server and stored
|
||||
* localy into the searchpath (if specified) for a future use. If none of these methods succeed to
|
||||
* load particular schema, the data from this schema are ignored during the communication with the
|
||||
* server.
|
||||
*
|
||||
* Besides the mentioned setters, there are many other @ref howtoclientssh "SSH", @ref howtoclienttls "TLS"
|
||||
* and @ref howtoclientch "Call Home" getter/setter functions to manipulate with various settings. All these
|
||||
* settings are internally placed in a thread-specific context so they are independent and
|
||||
* initialized to the default values within each new thread. However, the context can be shared among
|
||||
* the threads using ::nc_client_get_thread_context() and ::nc_client_set_thread_context() functions. In such
|
||||
* a case, be careful and avoid concurrent execution of the mentioned setters/getters and functions
|
||||
* creating connection (no matter if it is a standard NETCONF connection or Call Home).
|
||||
*
|
||||
* In the client, it is thread-safe to work with distinguish NETCONF sessions since the client
|
||||
* settings are thread-specific as described above.
|
||||
*
|
||||
* Server
|
||||
* ------
|
||||
*
|
||||
* Server is __FULLY__ thread-safe meaning you can set all the (thread-shared in contrast to
|
||||
* client) options simultaneously while listening for or accepting new sessions or
|
||||
* polling the existing ones. It is even safe to poll one session in several
|
||||
* pollsession structures or one pollsession structure in several threads. Generally,
|
||||
* servers can use more threads without any problems as long as they keep their workflow sane
|
||||
* (behavior such as freeing sessions only after no thread uses them or similar).
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_client.h__.
|
||||
*
|
||||
* - ::nc_client_init()
|
||||
* - ::nc_client_destroy()
|
||||
*
|
||||
* - ::nc_client_set_schema_searchpath()
|
||||
* - ::nc_client_get_schema_searchpath()
|
||||
* - ::nc_client_set_schema_callback()
|
||||
* - ::nc_client_get_schema_callback()
|
||||
*
|
||||
* - ::nc_client_set_thread_context()
|
||||
* - ::nc_client_get_thread_context()
|
||||
*
|
||||
* Available in __nc_server.h__.
|
||||
*
|
||||
* - ::nc_server_init()
|
||||
* - ::nc_server_destroy()
|
||||
*
|
||||
* Available in both __nc_client.h__ and __nc_server.h__.
|
||||
*
|
||||
* - ::nc_thread_destroy()
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page howtoclient Client sessions
|
||||
*
|
||||
* To connect to a NETCONF server, a NETCONF session must be established,
|
||||
* which requires a working transport session. It is possible to create
|
||||
* NETCONF sessions with SSH (using _libssh_) or TLS (using _libssl/libcrypto_)
|
||||
* as the underlying transport protocol. It is also possible to establish
|
||||
* the transport protocol outside _libnetconf2_ and then provide these file
|
||||
* descriptors (FD) for full NETCONF session creation.
|
||||
*
|
||||
* There are a lot of options for both an SSH and a TLS client. All of them
|
||||
* have setters and getters so that there is no need to duplicate them in
|
||||
* a client.
|
||||
*
|
||||
* @anchor howtoclientssh
|
||||
* SSH
|
||||
* ===
|
||||
*
|
||||
* Connecting to a server using SSH does not strictly require to set any
|
||||
* options, there are sensible default values for all the basic ones.
|
||||
* Except all the SSH options, optionally some authetication callbacks can be set,
|
||||
* which are particulary useful in automated clients (passwords cannot be
|
||||
* asked a user) or simply if any additional information is retrieved some
|
||||
* other way than from standard terminal input.
|
||||
*
|
||||
* Having the default options or changing any unsuitable ones, there are 2 functions
|
||||
* to use for a new server connection. ::nc_connect_ssh() is the standard function
|
||||
* that creates sessions using the set options. If there are some options, which
|
||||
* cannot be changed with the provided API, there is ::nc_connect_libssh() available.
|
||||
* It requires a _libssh_ session, in which all the SSH options can be modified
|
||||
* and even the connection established. This allows for full customization and
|
||||
* should fit any specific situation.
|
||||
*
|
||||
* New NETCONF sessions can also be created on existing authenticated SSH sessions.
|
||||
* There is a new SSH channel needed, on which the NETCONF session is then created.
|
||||
* Use ::nc_connect_ssh_channel() for this purpose.
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_client.h__.
|
||||
*
|
||||
* - ::nc_client_ssh_set_auth_hostkey_check_clb()
|
||||
* - ::nc_client_ssh_get_auth_hostkey_check_clb()
|
||||
* - ::nc_client_ssh_set_auth_password_clb()
|
||||
* - ::nc_client_ssh_get_auth_password_clb()
|
||||
* - ::nc_client_ssh_set_auth_interactive_clb()
|
||||
* - ::nc_client_ssh_get_auth_interactive_clb()
|
||||
* - ::nc_client_ssh_set_auth_privkey_passphrase_clb()
|
||||
* - ::nc_client_ssh_get_auth_privkey_passphrase_clb()
|
||||
* - ::nc_client_ssh_add_keypair()
|
||||
* - ::nc_client_ssh_del_keypair()
|
||||
* - ::nc_client_ssh_get_keypair_count()
|
||||
* - ::nc_client_ssh_get_keypair()
|
||||
* - ::nc_client_ssh_set_auth_pref()
|
||||
* - ::nc_client_ssh_get_auth_pref()
|
||||
* - ::nc_client_ssh_set_username()
|
||||
* - ::nc_client_ssh_get_username()
|
||||
*
|
||||
* - ::nc_connect_ssh()
|
||||
* - ::nc_connect_libssh()
|
||||
* - ::nc_connect_ssh_channel()
|
||||
*
|
||||
* @anchor howtoclienttls
|
||||
* TLS
|
||||
* ===
|
||||
*
|
||||
* To connect to a server using TLS, there must be some client identification
|
||||
* options set. Client must specify its certificate with a private key using
|
||||
* ::nc_client_tls_set_cert_key_paths(). Also, the Certificate Authority of
|
||||
* a server certificate must be considered trusted. Paths to all the trusted
|
||||
* CA certificates can be set by ::nc_client_tls_set_trusted_ca_paths().
|
||||
*
|
||||
* Then there are again 2 functions for connecting, ::nc_connect_tls() being
|
||||
* the standard way of connecting. ::nc_connect_libssl() again enables
|
||||
* to customize the TLS session in every way _libssl_ allows.
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_client.h__.
|
||||
*
|
||||
* - ::nc_client_tls_set_cert_key_paths()
|
||||
* - ::nc_client_tls_get_cert_key_paths()
|
||||
* - ::nc_client_tls_set_trusted_ca_paths()
|
||||
* - ::nc_client_tls_get_trusted_ca_paths()
|
||||
* - ::nc_client_tls_set_crl_paths()
|
||||
* - ::nc_client_tls_get_crl_paths()
|
||||
*
|
||||
* - ::nc_connect_tls()
|
||||
* - ::nc_connect_libssl()
|
||||
*
|
||||
*
|
||||
* FD and UNIX socket
|
||||
* ==================
|
||||
*
|
||||
* If you authenticated the connection using some tunneling software, you
|
||||
* can pass its file descriptors to _libnetconf2_ using ::nc_connect_inout(),
|
||||
* which will continue to establish a full NETCONF session. To connect locally
|
||||
* on a UNIX socket avoiding all cryptography use ::nc_connect_unix().
|
||||
*
|
||||
* Funtions List
|
||||
* -------------
|
||||
*
|
||||
* Available in __nc_client.h__.
|
||||
*
|
||||
* - ::nc_connect_inout()
|
||||
* - ::nc_connect_unix()
|
||||
*
|
||||
*
|
||||
* @anchor howtoclientch
|
||||
* Call Home
|
||||
* =========
|
||||
*
|
||||
* Call Home needs the same options set as standard SSH or TLS and the functions
|
||||
* reflect it exactly. However, to accept a connection, the client must first
|
||||
* specify addresses and ports, which to listen on by ::nc_client_ssh_ch_add_bind_listen()
|
||||
* and ::nc_client_tls_ch_add_bind_listen(). Then connections can be
|
||||
* accepted using ::nc_accept_callhome().
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_client.h__.
|
||||
*
|
||||
* - ::nc_client_ssh_ch_set_auth_hostkey_check_clb()
|
||||
* - ::nc_client_ssh_ch_set_auth_password_clb()
|
||||
* - ::nc_client_ssh_ch_set_auth_interactive_clb()
|
||||
* - ::nc_client_ssh_ch_set_auth_privkey_passphrase_clb()
|
||||
* - ::nc_client_ssh_ch_add_bind_listen()
|
||||
* - ::nc_client_ssh_ch_del_bind()
|
||||
* - ::nc_client_ssh_ch_add_keypair()
|
||||
* - ::nc_client_ssh_ch_del_keypair()
|
||||
* - ::nc_client_ssh_ch_get_keypair_count()
|
||||
* - ::nc_client_ssh_ch_get_keypair()
|
||||
* - ::nc_client_ssh_ch_set_auth_pref()
|
||||
* - ::nc_client_ssh_ch_get_auth_pref()
|
||||
* - ::nc_client_ssh_ch_set_username()
|
||||
* - ::nc_client_ssh_ch_get_username()
|
||||
*
|
||||
* - ::nc_client_tls_ch_add_bind_listen()
|
||||
* - ::nc_client_tls_ch_del_bind()
|
||||
* - ::nc_client_tls_ch_set_cert_key_paths()
|
||||
* - ::nc_client_tls_ch_get_cert_key_paths()
|
||||
* - ::nc_client_tls_ch_set_trusted_ca_paths()
|
||||
* - ::nc_client_tls_ch_get_trusted_ca_paths()
|
||||
* - ::nc_client_tls_ch_set_crl_paths()
|
||||
* - ::nc_client_tls_ch_get_crl_paths()
|
||||
*
|
||||
* - ::nc_accept_callhome()
|
||||
*
|
||||
*
|
||||
* Cleanup
|
||||
* =======
|
||||
*
|
||||
* These options and the schema searchpath are stored in dynamically
|
||||
* allocated memory. They are freed as a part of [destroying the client](@ref howtoinit).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page howtoserver Server sessions
|
||||
*
|
||||
* Init
|
||||
* ====
|
||||
*
|
||||
* Server takes an argument for its [initialization function](@ref howtoinit).
|
||||
* In it, you set the server context, which determines what modules it
|
||||
* supports and what capabilities to advertise. Few capabilities that
|
||||
* cannot be learnt from the context are set with separate functions
|
||||
* ::nc_server_set_capab_withdefaults() and generally ::nc_server_set_capability().
|
||||
* Timeout for receiving the _hello_ message on a new session can be set
|
||||
* by ::nc_server_set_hello_timeout() and the timeout for disconnecting
|
||||
* an inactive session by ::nc_server_set_idle_timeout().
|
||||
*
|
||||
* Context does not only determine server modules, but its overall
|
||||
* functionality as well. For every RPC the server should support,
|
||||
* an nc_rpc_clb callback should be set on that node in the context using ::nc_set_rpc_callback().
|
||||
* Server then calls these as appropriate [during poll](@ref howtoservercomm).
|
||||
*
|
||||
* Just like in the [client](@ref howtoclient), you can let _libnetconf2_
|
||||
* establish SSH or TLS transport or do it yourself and only provide the file
|
||||
* descriptors of the connection.
|
||||
*
|
||||
* Server options can be only set, there are no getters.
|
||||
*
|
||||
* To be able to accept any connections, endpoints must first be added
|
||||
* with ::nc_server_add_endpt() and configured with ::nc_server_endpt_set_address()
|
||||
* and ::nc_server_endpt_set_port(). For unix sockets, ::nc_server_endpt_set_perms()
|
||||
* is available to set the unix socket file permissions, and ::nc_server_endpt_set_port()
|
||||
* is invalid.
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_server.h__.
|
||||
*
|
||||
* - ::nc_server_set_capab_withdefaults()
|
||||
* - ::nc_server_set_capability()
|
||||
* - ::nc_server_set_hello_timeout()
|
||||
* - ::nc_server_set_idle_timeout()
|
||||
*
|
||||
* - ::nc_server_add_endpt()
|
||||
* - ::nc_server_del_endpt()
|
||||
* - ::nc_server_endpt_set_address()
|
||||
* - ::nc_server_endpt_set_port()
|
||||
* - ::nc_server_endpt_set_perms()
|
||||
*
|
||||
*
|
||||
* SSH
|
||||
* ===
|
||||
*
|
||||
* To successfully accept an SSH session you must set at least the host key using
|
||||
* ::nc_server_ssh_endpt_add_hostkey(), which are ordered. This way you simply add
|
||||
* some hostkey identifier, but the key itself will be retrieved always when needed
|
||||
* by calling the callback set by ::nc_server_ssh_set_hostkey_clb().
|
||||
*
|
||||
* There are also some other optional settings. Note that authorized
|
||||
* public keys are set for the server as a whole, not endpoint-specifically.
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_server.h__.
|
||||
*
|
||||
* - ::nc_server_ssh_endpt_add_hostkey()
|
||||
* - ::nc_server_ssh_endpt_del_hostkey()
|
||||
* - ::nc_server_ssh_endpt_mov_hostkey()
|
||||
* - ::nc_server_ssh_endpt_mod_hostkey()
|
||||
* - ::nc_server_ssh_endpt_set_auth_methods()
|
||||
* - ::nc_server_ssh_endpt_set_auth_attempts()
|
||||
* - ::nc_server_ssh_endpt_set_auth_timeout()
|
||||
*
|
||||
* - ::nc_server_ssh_set_hostkey_clb()
|
||||
*
|
||||
* - ::nc_server_ssh_add_authkey()
|
||||
* - ::nc_server_ssh_add_authkey_path()
|
||||
* - ::nc_server_ssh_del_authkey()
|
||||
*
|
||||
*
|
||||
* TLS
|
||||
* ===
|
||||
*
|
||||
* TLS works with endpoints too, but its options differ
|
||||
* significantly from the SSH ones, especially in the _cert-to-name_
|
||||
* options that TLS uses to derive usernames from client certificates.
|
||||
* So, after starting listening on an endpoint you need to set the server
|
||||
* certificate (::nc_server_tls_endpt_set_server_cert()). Its actual content
|
||||
* together with the matching private key will be loaded using a callback
|
||||
* from ::nc_server_tls_set_server_cert_clb(). Additional certificates needed
|
||||
* for the client to verify the server's certificate chain can be loaded using
|
||||
* a callback from ::nc_server_tls_set_server_cert_chain_clb().
|
||||
*
|
||||
* To accept client certificates, they must first be considered trusted,
|
||||
* which you have three ways of achieving. You can add each of their Certificate Authority
|
||||
* certificates to the trusted ones or mark a specific client certificate
|
||||
* as trusted. Lastly, you can set paths with all the trusted CA certificates
|
||||
* with ::nc_server_tls_endpt_set_trusted_ca_paths(). Adding specific certificates
|
||||
* is also performed only as an arbitrary identificator and later retrieved from
|
||||
* callback set by ::nc_server_tls_set_trusted_cert_list_clb(). But, you can add
|
||||
* certficates as whole lists, not one-by-one.
|
||||
*
|
||||
* Then, from each trusted client certificate a username must be derived
|
||||
* for the NETCONF session. This is accomplished by finding a matching
|
||||
* _cert-to-name_ entry. They are added using ::nc_server_tls_endpt_add_ctn().
|
||||
*
|
||||
* If you need to remove trusted certificates, you can do so with ::nc_server_tls_endpt_del_trusted_cert_list().
|
||||
* To clear all Certificate Revocation Lists use ::nc_server_tls_endpt_clear_crls().
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_server.h__.
|
||||
*
|
||||
* - ::nc_server_tls_endpt_set_server_cert()
|
||||
* - ::nc_server_tls_endpt_add_trusted_cert_list()
|
||||
* - ::nc_server_tls_endpt_del_trusted_cert_list()
|
||||
* - ::nc_server_tls_endpt_set_trusted_ca_paths()
|
||||
* - ::nc_server_tls_endpt_set_crl_paths()
|
||||
* - ::nc_server_tls_endpt_clear_crls()
|
||||
* - ::nc_server_tls_endpt_add_ctn()
|
||||
* - ::nc_server_tls_endpt_del_ctn()
|
||||
* - ::nc_server_tls_endpt_get_ctn()
|
||||
*
|
||||
* - ::nc_server_tls_set_server_cert_clb()
|
||||
* - ::nc_server_tls_set_server_cert_chain_clb()
|
||||
* - ::nc_server_tls_set_trusted_cert_list_clb()
|
||||
*
|
||||
* FD
|
||||
* ==
|
||||
*
|
||||
* If you used a tunneling software, which does its own authentication,
|
||||
* you can accept a NETCONF session on its file descriptors with
|
||||
* ::nc_accept_inout().
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_server.h__.
|
||||
*
|
||||
* - ::nc_accept_inout()
|
||||
*
|
||||
*
|
||||
* Call Home
|
||||
* =========
|
||||
*
|
||||
* _Call Home_ works with endpoints just like standard sessions, but
|
||||
* the options are organized a bit differently and endpoints are added
|
||||
* for CH clients. However, one important difference is that
|
||||
* once all the mandatory options are set, _libnetconf2_ __will not__
|
||||
* immediately start connecting to a client. It will do so only after
|
||||
* calling ::nc_connect_ch_client_dispatch() in a separate thread.
|
||||
*
|
||||
* Lastly, monitoring of these sessions is up to the application.
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_server.h__.
|
||||
*
|
||||
* - ::nc_server_ch_add_client()
|
||||
* - ::nc_server_ch_del_client()
|
||||
* - ::nc_server_ch_is_client()
|
||||
* - ::nc_server_ch_client_add_endpt()
|
||||
* - ::nc_server_ch_client_del_endpt()
|
||||
* - ::nc_server_ch_client_is_endpt()
|
||||
* - ::nc_server_ch_client_endpt_set_address()
|
||||
* - ::nc_server_ch_client_endpt_set_port()
|
||||
* - ::nc_server_ch_client_endpt_enable_keepalives()
|
||||
* - ::nc_server_ch_client_endpt_set_keepalives()
|
||||
* - ::nc_server_ch_client_set_conn_type()
|
||||
* - ::nc_server_ch_client_periodic_set_period()
|
||||
* - ::nc_server_ch_client_periodic_set_anchor_time()
|
||||
* - ::nc_server_ch_client_periodic_set_idle_timeout()
|
||||
* - ::nc_server_ch_client_set_start_with()
|
||||
* - ::nc_server_ch_client_set_max_attempts()
|
||||
* - ::nc_connect_ch_client_dispatch()
|
||||
*
|
||||
* - ::nc_server_ssh_ch_client_endpt_add_hostkey()
|
||||
* - ::nc_server_ssh_ch_client_endpt_del_hostkey()
|
||||
* - ::nc_server_ssh_ch_client_endpt_mov_hostkey()
|
||||
* - ::nc_server_ssh_ch_client_endpt_set_auth_methods()
|
||||
* - ::nc_server_ssh_ch_client_endpt_get_auth_methods()
|
||||
* - ::nc_server_ssh_ch_client_endpt_set_auth_attempts()
|
||||
* - ::nc_server_ssh_ch_client_endpt_set_auth_timeout()
|
||||
*
|
||||
* - ::nc_server_tls_ch_client_endpt_set_server_cert()
|
||||
* - ::nc_server_tls_ch_client_endpt_add_trusted_cert_list()
|
||||
* - ::nc_server_tls_ch_client_endpt_del_trusted_cert_list()
|
||||
* - ::nc_server_tls_ch_client_endpt_set_trusted_ca_paths()
|
||||
* - ::nc_server_tls_ch_client_endpt_set_crl_paths()
|
||||
* - ::nc_server_tls_ch_client_endpt_clear_crls()
|
||||
* - ::nc_server_tls_ch_client_endpt_add_ctn()
|
||||
* - ::nc_server_tls_ch_client_endpt_del_ctn()
|
||||
* - ::nc_server_tls_ch_client_endpt_get_ctn()
|
||||
*
|
||||
*
|
||||
* Connecting And Cleanup
|
||||
* ======================
|
||||
*
|
||||
* When accepting connections with ::nc_accept(), all the endpoints are examined
|
||||
* and the first with a pending connection is used. To remove all CH clients,
|
||||
* endpoints, and free any used dynamic memory, [destroy](@ref howtoinit) the server.
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_server.h__.
|
||||
*
|
||||
* - ::nc_accept()
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page howtoclientcomm Client communication
|
||||
*
|
||||
* To send RPCs on a session, you simply create an RPC, send it using ::nc_send_rpc(),
|
||||
* and then wait for a reply using ::nc_recv_reply(). If you are subscribed, there are 2 ways
|
||||
* of receiving notifications. Either you wait for them the same way
|
||||
* as for standard replies with ::nc_recv_notif() or you create a dispatcher
|
||||
* with ::nc_recv_notif_dispatch() that asynchronously (in a separate thread)
|
||||
* reads notifications and passes them to your callback.
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_client.h__.
|
||||
*
|
||||
* - ::nc_rpc_act_generic()
|
||||
* - ::nc_rpc_act_generic_xml()
|
||||
* - ::nc_rpc_getconfig()
|
||||
* - ::nc_rpc_edit()
|
||||
* - ::nc_rpc_copy()
|
||||
* - ::nc_rpc_delete()
|
||||
* - ::nc_rpc_lock()
|
||||
* - ::nc_rpc_unlock()
|
||||
* - ::nc_rpc_get()
|
||||
* - ::nc_rpc_kill()
|
||||
* - ::nc_rpc_commit()
|
||||
* - ::nc_rpc_discard()
|
||||
* - ::nc_rpc_cancel()
|
||||
* - ::nc_rpc_validate()
|
||||
* - ::nc_rpc_getschema()
|
||||
* - ::nc_rpc_subscribe()
|
||||
* - ::nc_rpc_getdata()
|
||||
* - ::nc_rpc_editdata()
|
||||
* - ::nc_rpc_establishsub()
|
||||
* - ::nc_rpc_modifysub()
|
||||
* - ::nc_rpc_deletesub()
|
||||
* - ::nc_rpc_killsub()
|
||||
* - ::nc_rpc_establishpush_periodic()
|
||||
* - ::nc_rpc_establishpush_onchange()
|
||||
* - ::nc_rpc_modifypush_periodic()
|
||||
* - ::nc_rpc_modifypush_onchange()
|
||||
* - ::nc_rpc_resyncsub()
|
||||
*
|
||||
* - ::nc_send_rpc()
|
||||
* - ::nc_recv_reply()
|
||||
* - ::nc_recv_notif()
|
||||
* - ::nc_recv_notif_dispatch()
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page howtoservercomm Server communication
|
||||
*
|
||||
* Once at least one session is established, an nc_pollsession structure
|
||||
* should be created with ::nc_ps_new(), filled with the session using
|
||||
* ::nc_ps_add_session() and finally polled with ::nc_ps_poll(). Based on
|
||||
* the return value from the poll, further actions can be taken. More
|
||||
* sessions can be polled at the same time and any requests received on
|
||||
* the sessions are [handled internally](@ref howtoserver).
|
||||
*
|
||||
* If an SSH NETCONF session asks for a new channel, you can accept
|
||||
* this request with ::nc_ps_accept_ssh_channel() or ::nc_session_accept_ssh_channel()
|
||||
* depending on the structure you want to use as the argument.
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_server.h__.
|
||||
*
|
||||
* - ::nc_ps_new()
|
||||
* - ::nc_ps_add_session()
|
||||
* - ::nc_ps_del_session()
|
||||
* - ::nc_ps_session_count()
|
||||
* - ::nc_ps_free()
|
||||
*
|
||||
* - ::nc_ps_poll()
|
||||
* - ::nc_ps_clear()
|
||||
* - ::nc_ps_accept_ssh_channel()
|
||||
* - ::nc_session_accept_ssh_channel()
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page howtotimeouts Timeouts
|
||||
*
|
||||
* There are several timeouts which are used throughout _libnetconf2_ to
|
||||
* assure that it will never indefinitely hang on any operation. Normally,
|
||||
* you should not need to worry about them much necause they are set by
|
||||
* default to reasonable values for common systems. However, if your
|
||||
* platform is not common (embedded, ...), adjusting these timeouts may
|
||||
* save a lot of debugging and time.
|
||||
*
|
||||
* Compile Options
|
||||
* ---------------
|
||||
*
|
||||
* You can adjust active and inactive read timeout using `cmake` variables.
|
||||
* For details look into `README.md`.
|
||||
*
|
||||
* API Functions
|
||||
* -------------
|
||||
*
|
||||
* Once a new connection is established including transport protocol negotiations,
|
||||
* _hello_ message is exchanged. You can set how long will the server wait for
|
||||
* receiving this message from a client before dropping it.
|
||||
*
|
||||
* Having a NETCONF session working, it may not communicate for a longer time.
|
||||
* To free up some resources, it is possible to adjust the maximum idle period
|
||||
* of a session before it is disconnected. In _Call Home_, for both a persistent
|
||||
* and periodic connection can this idle timeout be specified separately for each
|
||||
* client using corresponding functions.
|
||||
*
|
||||
* Lastly, SSH user authentication timeout can be also modified. It is the time
|
||||
* a client has to successfully authenticate after connecting before it is disconnected.
|
||||
*
|
||||
* Functions List
|
||||
* --------------
|
||||
*
|
||||
* Available in __nc_server.h__.
|
||||
*
|
||||
* - ::nc_server_set_hello_timeout()
|
||||
* - ::nc_server_get_hello_timeout()
|
||||
* - ::nc_server_set_idle_timeout()
|
||||
* - ::nc_server_get_idle_timeout()
|
||||
* - ::nc_server_ch_client_periodic_set_idle_timeout()
|
||||
* - ::nc_server_ssh_ch_client_endpt_set_auth_timeout()
|
||||
* - ::nc_server_ssh_ch_client_endpt_set_auth_timeout()
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup misc Miscellaneous
|
||||
* @brief Miscellaneous macros, types, structure and functions for a generic use by both server and client applications.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup client Client
|
||||
* @brief NETCONF client functionality.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup server Server
|
||||
* @brief NETCONF server functionality.
|
||||
*/
|
||||
|
||||
#endif /* NC_LIBNETCONF_H_ */
|
113
src/log.c
113
src/log.c
|
@ -1,9 +1,11 @@
|
|||
/**
|
||||
* \file log.c
|
||||
* \author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* \brief libnetconf2 - log functions
|
||||
* @file log.c
|
||||
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 - log functions
|
||||
*
|
||||
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2024 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
|
@ -12,31 +14,38 @@
|
|||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* pthread_rwlock_t */
|
||||
|
||||
#include "log_p.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
#include <libssh/libssh.h>
|
||||
#endif
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
#include "compat.h"
|
||||
#include "libnetconf.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "session_p.h"
|
||||
|
||||
#define NC_MSG_SIZE 256
|
||||
|
||||
/**
|
||||
* @brief libnetconf verbose level variable
|
||||
*/
|
||||
volatile uint8_t verbose_level = 0;
|
||||
ATOMIC_T verbose_level = 0;
|
||||
|
||||
void (*depr_print_clb)(NC_VERB_LEVEL level, const char *msg);
|
||||
void (*print_clb)(const struct nc_session *session, NC_VERB_LEVEL level, const char *msg);
|
||||
|
||||
API void
|
||||
nc_verbosity(NC_VERB_LEVEL level)
|
||||
{
|
||||
verbose_level = level;
|
||||
ATOMIC_STORE_RELAXED(verbose_level, level);
|
||||
ly_log_level((LY_LOG_LEVEL)level);
|
||||
}
|
||||
|
||||
|
@ -51,98 +60,100 @@ struct {
|
|||
{NC_VERB_DEBUG_LOWLVL, "[DBL]"}
|
||||
};
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
static void
|
||||
nc_libssh_log_cb(int priority, const char *UNUSED(function), const char *buffer, void *UNUSED(userdata))
|
||||
{
|
||||
static char last_msg[NC_MSG_SIZE] = {0};
|
||||
static struct timespec last_print = {0}, cur_time;
|
||||
|
||||
/* check for repeated messages and do not print them */
|
||||
if (!strncmp(last_msg, buffer, NC_MSG_SIZE - 1)) {
|
||||
nc_realtime_get(&cur_time);
|
||||
if (last_print.tv_sec && (nc_time_diff(&cur_time, &last_print) < 1000)) {
|
||||
/* print another repeated message only after 1s */
|
||||
return;
|
||||
}
|
||||
|
||||
last_print = cur_time;
|
||||
} else {
|
||||
/* store the last message */
|
||||
strncpy(last_msg, buffer, NC_MSG_SIZE - 1);
|
||||
memset(&last_print, 0, sizeof last_print);
|
||||
}
|
||||
|
||||
/* print the message */
|
||||
nc_log_printf(NULL, priority, "SSH: %s", buffer);
|
||||
}
|
||||
|
||||
API void
|
||||
nc_libssh_thread_verbosity(int level)
|
||||
{
|
||||
ssh_set_log_callback(nc_libssh_log_cb);
|
||||
ssh_set_log_level(level);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
static void
|
||||
prv_vprintf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, va_list args)
|
||||
nc_log_vprintf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, va_list args)
|
||||
{
|
||||
#define PRV_MSG_INIT_SIZE 256
|
||||
va_list args2;
|
||||
char *prv_msg;
|
||||
char *msg;
|
||||
void *mem;
|
||||
int req_len;
|
||||
|
||||
prv_msg = malloc(PRV_MSG_INIT_SIZE);
|
||||
if (!prv_msg) {
|
||||
msg = malloc(NC_MSG_SIZE);
|
||||
if (!msg) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_copy(args2, args);
|
||||
|
||||
req_len = vsnprintf(prv_msg, PRV_MSG_INIT_SIZE - 1, format, args);
|
||||
req_len = vsnprintf(msg, NC_MSG_SIZE - 1, format, args);
|
||||
if (req_len == -1) {
|
||||
goto cleanup;
|
||||
} else if (req_len >= PRV_MSG_INIT_SIZE - 1) {
|
||||
} else if (req_len >= NC_MSG_SIZE - 1) {
|
||||
/* the length is not enough */
|
||||
++req_len;
|
||||
mem = realloc(prv_msg, req_len);
|
||||
mem = realloc(msg, req_len);
|
||||
if (!mem) {
|
||||
goto cleanup;
|
||||
}
|
||||
prv_msg = mem;
|
||||
msg = mem;
|
||||
|
||||
/* now print the full message */
|
||||
req_len = vsnprintf(prv_msg, req_len, format, args2);
|
||||
req_len = vsnprintf(msg, req_len, format, args2);
|
||||
if (req_len == -1) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (print_clb) {
|
||||
print_clb(session, level, prv_msg);
|
||||
} else if (depr_print_clb) {
|
||||
depr_print_clb(level, prv_msg);
|
||||
print_clb(session, level, msg);
|
||||
} else if (session && session->id) {
|
||||
fprintf(stderr, "Session %u %s: %s\n", session->id, verb[level].label, prv_msg);
|
||||
fprintf(stderr, "Session %" PRIu32 " %s: %s\n", session->id, verb[level].label, msg);
|
||||
} else {
|
||||
fprintf(stderr, "%s: %s\n", verb[level].label, prv_msg);
|
||||
fprintf(stderr, "%s: %s\n", verb[level].label, msg);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(prv_msg);
|
||||
#undef PRV_MSG_INIT_SIZE
|
||||
free(msg);
|
||||
}
|
||||
|
||||
void
|
||||
prv_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...)
|
||||
nc_log_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
prv_vprintf(session, level, format, ap);
|
||||
nc_log_vprintf(session, level, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void
|
||||
nc_ly_log_clb(LY_LOG_LEVEL lvl, const char *msg, const char *UNUSED(path))
|
||||
{
|
||||
if (print_clb) {
|
||||
print_clb(NULL, (NC_VERB_LEVEL)lvl, msg);
|
||||
} else if (depr_print_clb) {
|
||||
depr_print_clb((NC_VERB_LEVEL)lvl, msg);
|
||||
}
|
||||
}
|
||||
|
||||
API void
|
||||
nc_set_print_clb(void (*clb)(NC_VERB_LEVEL, const char *))
|
||||
{
|
||||
print_clb = NULL;
|
||||
depr_print_clb = clb;
|
||||
ly_set_log_clb(nc_ly_log_clb, 1);
|
||||
}
|
||||
|
||||
API void
|
||||
nc_set_print_clb_session(void (*clb)(const struct nc_session *, NC_VERB_LEVEL, const char *))
|
||||
{
|
||||
print_clb = clb;
|
||||
depr_print_clb = NULL;
|
||||
ly_set_log_clb(nc_ly_log_clb, 1);
|
||||
}
|
||||
|
|
16
src/log.h
16
src/log.h
|
@ -3,6 +3,7 @@
|
|||
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* @brief libnetconf2 logger
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
|
@ -49,7 +50,7 @@ typedef enum NC_VERB_LEVEL {
|
|||
*/
|
||||
void nc_verbosity(NC_VERB_LEVEL level);
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/**
|
||||
* @brief Set libssh verbosity level.
|
||||
|
@ -68,20 +69,13 @@ void nc_verbosity(NC_VERB_LEVEL level);
|
|||
*/
|
||||
void nc_libssh_thread_verbosity(int level);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Deprecated, use ::nc_set_print_clb_session() instead.
|
||||
*
|
||||
* @param[in] clb Callback that is called for every message.
|
||||
*/
|
||||
void nc_set_print_clb(void (*clb)(NC_VERB_LEVEL, const char *));
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
/**
|
||||
* @brief Set libnetconf print callback.
|
||||
*
|
||||
* This callback is set for libnetconf2 and also libyang that is used internally. libyang
|
||||
* callback can be set explicitly, but must be done so after calling this function.
|
||||
* The callback is not set per-session, it is a global resource. It might be called with
|
||||
* a NULL session parameter.
|
||||
*
|
||||
* @param[in] clb Callback that is called for every message.
|
||||
*/
|
||||
|
|
69
src/log_p.h
69
src/log_p.h
|
@ -1,9 +1,11 @@
|
|||
/**
|
||||
* @file log.h
|
||||
* @file log_p.h
|
||||
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 logger
|
||||
*
|
||||
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2024 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
|
@ -15,8 +17,9 @@
|
|||
#ifndef NC_LOG_PRIVATE_H_
|
||||
#define NC_LOG_PRIVATE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "log.h"
|
||||
|
||||
/*
|
||||
|
@ -30,25 +33,67 @@
|
|||
* @param[in] level Verbose level
|
||||
* @param[in] format Formatting string
|
||||
*/
|
||||
void prv_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...);
|
||||
void nc_log_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...);
|
||||
|
||||
/**
|
||||
* @brief Verbose level variable
|
||||
*/
|
||||
extern volatile uint8_t verbose_level;
|
||||
extern ATOMIC_T verbose_level;
|
||||
|
||||
/*
|
||||
* Verbose printing macros
|
||||
*/
|
||||
#define ERR(session, format, args ...) prv_printf(session,NC_VERB_ERROR,format,##args)
|
||||
#define WRN(session, format, args ...) if(verbose_level>=NC_VERB_WARNING){prv_printf(session,NC_VERB_WARNING,format,##args);}
|
||||
#define VRB(session, format, args ...) if(verbose_level>=NC_VERB_VERBOSE){prv_printf(session,NC_VERB_VERBOSE,format,##args);}
|
||||
#define DBG(session, format, args ...) if(verbose_level>=NC_VERB_DEBUG){prv_printf(session,NC_VERB_DEBUG,format,##args);}
|
||||
#define DBL(session, format, args ...) if(verbose_level>=NC_VERB_DEBUG_LOWLVL){prv_printf(session,NC_VERB_DEBUG_LOWLVL,format,##args);}
|
||||
#define ERR(session, ...) nc_log_printf(session, NC_VERB_ERROR, __VA_ARGS__)
|
||||
#define WRN(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_WARNING){nc_log_printf(session, NC_VERB_WARNING, __VA_ARGS__);}
|
||||
#define VRB(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_VERBOSE){nc_log_printf(session, NC_VERB_VERBOSE, __VA_ARGS__);}
|
||||
#define DBG(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_DEBUG){nc_log_printf(session, NC_VERB_DEBUG, __VA_ARGS__);}
|
||||
#define DBL(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_DEBUG_LOWLVL){nc_log_printf(session, NC_VERB_DEBUG_LOWLVL, __VA_ARGS__);}
|
||||
|
||||
#define ERRMEM ERR(NULL, "%s: memory reallocation failed (%s:%d).", __func__, __FILE__, __LINE__)
|
||||
#define ERRARG(arg) ERR(NULL, "%s: invalid argument (%s).", __func__, arg)
|
||||
#define ERRINIT ERR(NULL, "%s: libnetconf2 not initialized.", __func__)
|
||||
#define ERRINITSRV ERR(NULL, "%s: server not initialized.", __func__)
|
||||
#define ERRINT ERR(NULL, "%s: internal error (%s:%d).", __func__, __FILE__, __LINE__)
|
||||
#define ERRARG(session, ARG) ERR(session, "Invalid argument %s (%s()).", #ARG, __func__)
|
||||
|
||||
#define NC_CHECK_SRV_INIT_RET(RET) if (!ATOMIC_LOAD_RELAXED(server_opts.new_session_id)) {ERRINITSRV; return (RET);}
|
||||
#define NC_CHECK_ERRMEM_RET(COND, RET) if ((COND)) {ERRMEM; return (RET);}
|
||||
#define NC_CHECK_ERRMEM_GOTO(COND, RET, GOTO) if ((COND)) {ERRMEM; RET; goto GOTO;}
|
||||
|
||||
#define GETMACRO1(_1, NAME, ...) NAME
|
||||
#define GETMACRO2(_1, _2, NAME, ...) NAME
|
||||
#define GETMACRO3(_1, _2, _3, NAME, ...) NAME
|
||||
#define GETMACRO4(_1, _2, _3, _4, NAME, ...) NAME
|
||||
#define GETMACRO5(_1, _2, _3, _4, _5, NAME, ...) NAME
|
||||
#define GETMACRO6(_1, _2, _3, _4, _5, _6, NAME, ...) NAME
|
||||
#define GETMACRO7(_1, _2, _3, _4, _5, _6, _7, NAME, ...) NAME
|
||||
#define GETMACRO8(_1, _2, _3, _4, _5, _6, _7, _8, NAME, ...) NAME
|
||||
|
||||
#define NC_CHECK_ARG_RET1(session, ARG, RETVAL) if (!(ARG)) {ERRARG(session, ARG);return RETVAL;}
|
||||
#define NC_CHECK_ARG_RET2(session, ARG1, ARG2, RETVAL)\
|
||||
NC_CHECK_ARG_RET1(session, ARG1, RETVAL);\
|
||||
NC_CHECK_ARG_RET1(session, ARG2, RETVAL)
|
||||
#define NC_CHECK_ARG_RET3(session, ARG1, ARG2, ARG3, RETVAL)\
|
||||
NC_CHECK_ARG_RET2(session, ARG1, ARG2, RETVAL);\
|
||||
NC_CHECK_ARG_RET1(session, ARG3, RETVAL)
|
||||
#define NC_CHECK_ARG_RET4(session, ARG1, ARG2, ARG3, ARG4, RETVAL)\
|
||||
NC_CHECK_ARG_RET3(session, ARG1, ARG2, ARG3, RETVAL);\
|
||||
NC_CHECK_ARG_RET1(session, ARG4, RETVAL)
|
||||
#define NC_CHECK_ARG_RET5(session, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL)\
|
||||
NC_CHECK_ARG_RET4(session, ARG1, ARG2, ARG3, ARG4, RETVAL);\
|
||||
NC_CHECK_ARG_RET1(session, ARG5, RETVAL)
|
||||
#define NC_CHECK_ARG_RET6(session, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, RETVAL)\
|
||||
NC_CHECK_ARG_RET5(session, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL);\
|
||||
NC_CHECK_ARG_RET1(session, ARG6, RETVAL)
|
||||
#define NC_CHECK_ARG_RET7(session, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, RETVAL)\
|
||||
NC_CHECK_ARG_RET6(session, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, RETVAL);\
|
||||
NC_CHECK_ARG_RET1(session, ARG7, RETVAL)
|
||||
|
||||
/**
|
||||
* @brief Function's parameters checking macro
|
||||
*
|
||||
* @param session Session that is logged.
|
||||
* @param ... Parameters of the function to check. The last parameter is the value that is returned on error.
|
||||
*/
|
||||
#define NC_CHECK_ARG_RET(session, ...) GETMACRO8(__VA_ARGS__, NC_CHECK_ARG_RET7, NC_CHECK_ARG_RET6, NC_CHECK_ARG_RET5,\
|
||||
NC_CHECK_ARG_RET4, NC_CHECK_ARG_RET3, NC_CHECK_ARG_RET2, NC_CHECK_ARG_RET1, DUMMY) (session, __VA_ARGS__)
|
||||
|
||||
#endif /* NC_LOG_PRIVATE_H_ */
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/**
|
||||
* \file messages.c
|
||||
* \author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* \brief libnetconf2 - NETCONF messages functions
|
||||
* @file messages.c
|
||||
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* @brief libnetconf2 - NETCONF messages functions
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
|
@ -12,15 +13,21 @@
|
|||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* pthread_rwlock_t, strdup */
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#include "libnetconf.h"
|
||||
#include "compat.h"
|
||||
#include "config.h"
|
||||
#include "log_p.h"
|
||||
#include "messages_client.h"
|
||||
#include "messages_p.h"
|
||||
#include "netconf.h"
|
||||
|
||||
const char *rpcedit_dfltop2str[] = {NULL, "merge", "replace", "none"};
|
||||
const char *rpcedit_testopt2str[] = {NULL, "test-then-set", "set", "test-only"};
|
||||
|
@ -29,10 +36,7 @@ const char *rpcedit_erropt2str[] = {NULL, "stop-on-error", "continue-on-error",
|
|||
API NC_RPC_TYPE
|
||||
nc_rpc_get_type(const struct nc_rpc *rpc)
|
||||
{
|
||||
if (!rpc) {
|
||||
ERRARG("rpc");
|
||||
return 0;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, rpc, 0);
|
||||
|
||||
return rpc->type;
|
||||
}
|
||||
|
@ -42,16 +46,14 @@ nc_rpc_act_generic(const struct lyd_node *data, NC_PARAMTYPE paramtype)
|
|||
{
|
||||
struct nc_rpc_act_generic *rpc;
|
||||
|
||||
if (!data || data->next || (data->prev != data)) {
|
||||
ERRARG("data");
|
||||
NC_CHECK_ARG_RET(NULL, data, NULL);
|
||||
if (data->next || (data->prev != data)) {
|
||||
ERR(NULL, "nc_rpc_act_generic missing data");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_ACT_GENERIC;
|
||||
rpc->has_data = 1;
|
||||
|
@ -73,16 +75,10 @@ nc_rpc_act_generic_xml(const char *xml_str, NC_PARAMTYPE paramtype)
|
|||
{
|
||||
struct nc_rpc_act_generic *rpc;
|
||||
|
||||
if (!xml_str) {
|
||||
ERRARG("xml_str");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, xml_str, NULL);
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_ACT_GENERIC;
|
||||
rpc->has_data = 0;
|
||||
|
@ -101,10 +97,7 @@ nc_rpc_getconfig(NC_DATASTORE source, const char *filter, NC_WD_MODE wd_mode, NC
|
|||
{
|
||||
struct nc_rpc_getconfig *rpc;
|
||||
|
||||
if (!source) {
|
||||
ERRARG("source");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, source, NULL);
|
||||
|
||||
if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
|
||||
ERR(NULL, "Filter is neither an XML subtree nor an XPath expression (invalid first char '%c').", filter[0]);
|
||||
|
@ -112,10 +105,7 @@ nc_rpc_getconfig(NC_DATASTORE source, const char *filter, NC_WD_MODE wd_mode, NC
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_GETCONFIG;
|
||||
rpc->source = source;
|
||||
|
@ -136,13 +126,7 @@ nc_rpc_edit(NC_DATASTORE target, NC_RPC_EDIT_DFLTOP default_op, NC_RPC_EDIT_TEST
|
|||
{
|
||||
struct nc_rpc_edit *rpc;
|
||||
|
||||
if (!target) {
|
||||
ERRARG("target");
|
||||
return NULL;
|
||||
} else if (!edit_content) {
|
||||
ERRARG("edit_content");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, target, edit_content, NULL);
|
||||
|
||||
if (edit_content[0] && (edit_content[0] != '<') && !isalpha(edit_content[0])) {
|
||||
ERR(NULL, "<edit-config> content is neither a URL nor an XML config (invalid first char '%c').", edit_content[0]);
|
||||
|
@ -150,10 +134,7 @@ nc_rpc_edit(NC_DATASTORE target, NC_RPC_EDIT_DFLTOP default_op, NC_RPC_EDIT_TEST
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_EDIT;
|
||||
rpc->target = target;
|
||||
|
@ -176,13 +157,7 @@ nc_rpc_copy(NC_DATASTORE target, const char *url_trg, NC_DATASTORE source, const
|
|||
{
|
||||
struct nc_rpc_copy *rpc;
|
||||
|
||||
if (!target) {
|
||||
ERRARG("target");
|
||||
return NULL;
|
||||
} else if (!source) {
|
||||
ERRARG("source");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, target, source, NULL);
|
||||
|
||||
if (url_or_config_src && url_or_config_src[0] && (url_or_config_src[0] != '<') && !isalpha(url_or_config_src[0])) {
|
||||
ERR(NULL, "<copy-config> source is neither a URL nor an XML config (invalid first char '%c').", url_or_config_src[0]);
|
||||
|
@ -190,10 +165,7 @@ nc_rpc_copy(NC_DATASTORE target, const char *url_trg, NC_DATASTORE source, const
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_COPY;
|
||||
rpc->target = target;
|
||||
|
@ -219,16 +191,10 @@ nc_rpc_delete(NC_DATASTORE target, const char *url, NC_PARAMTYPE paramtype)
|
|||
{
|
||||
struct nc_rpc_delete *rpc;
|
||||
|
||||
if (!target) {
|
||||
ERRARG("target");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, target, NULL);
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_DELETE;
|
||||
rpc->target = target;
|
||||
|
@ -247,16 +213,10 @@ nc_rpc_lock(NC_DATASTORE target)
|
|||
{
|
||||
struct nc_rpc_lock *rpc;
|
||||
|
||||
if (!target) {
|
||||
ERRARG("target");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, target, NULL);
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_LOCK;
|
||||
rpc->target = target;
|
||||
|
@ -269,16 +229,10 @@ nc_rpc_unlock(NC_DATASTORE target)
|
|||
{
|
||||
struct nc_rpc_lock *rpc;
|
||||
|
||||
if (!target) {
|
||||
ERRARG("target");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, target, NULL);
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_UNLOCK;
|
||||
rpc->target = target;
|
||||
|
@ -297,10 +251,7 @@ nc_rpc_get(const char *filter, NC_WD_MODE wd_mode, NC_PARAMTYPE paramtype)
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_GET;
|
||||
if (filter && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
|
||||
|
@ -319,16 +270,10 @@ nc_rpc_kill(uint32_t session_id)
|
|||
{
|
||||
struct nc_rpc_kill *rpc;
|
||||
|
||||
if (!session_id) {
|
||||
ERRARG("session_id");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, session_id, NULL);
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_KILL;
|
||||
rpc->sid = session_id;
|
||||
|
@ -343,10 +288,7 @@ nc_rpc_commit(int confirmed, uint32_t confirm_timeout, const char *persist, cons
|
|||
struct nc_rpc_commit *rpc;
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_COMMIT;
|
||||
rpc->confirmed = confirmed;
|
||||
|
@ -372,10 +314,7 @@ nc_rpc_discard(void)
|
|||
struct nc_rpc *rpc;
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_DISCARD;
|
||||
|
||||
|
@ -388,10 +327,7 @@ nc_rpc_cancel(const char *persist_id, NC_PARAMTYPE paramtype)
|
|||
struct nc_rpc_cancel *rpc;
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_CANCEL;
|
||||
if (persist_id && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
|
||||
|
@ -409,10 +345,7 @@ nc_rpc_validate(NC_DATASTORE source, const char *url_or_config, NC_PARAMTYPE par
|
|||
{
|
||||
struct nc_rpc_validate *rpc;
|
||||
|
||||
if (!source) {
|
||||
ERRARG("source");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, source, NULL);
|
||||
|
||||
if (url_or_config && url_or_config[0] && (url_or_config[0] != '<') && !isalpha(url_or_config[0])) {
|
||||
ERR(NULL, "<validate> source is neither a URL nor an XML config (invalid first char '%c').", url_or_config[0]);
|
||||
|
@ -420,10 +353,7 @@ nc_rpc_validate(NC_DATASTORE source, const char *url_or_config, NC_PARAMTYPE par
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_VALIDATE;
|
||||
rpc->source = source;
|
||||
|
@ -442,16 +372,10 @@ nc_rpc_getschema(const char *identifier, const char *version, const char *format
|
|||
{
|
||||
struct nc_rpc_getschema *rpc;
|
||||
|
||||
if (!identifier) {
|
||||
ERRARG("identifier");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, identifier, NULL);
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_GETSCHEMA;
|
||||
if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
|
||||
|
@ -486,10 +410,7 @@ nc_rpc_subscribe(const char *stream_name, const char *filter, const char *start_
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_SUBSCRIBE;
|
||||
if (stream_name && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
|
||||
|
@ -525,19 +446,15 @@ nc_rpc_getdata(const char *datastore, const char *filter, const char *config_fil
|
|||
struct nc_rpc_getdata *rpc = NULL;
|
||||
int i;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, datastore, NULL);
|
||||
|
||||
if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
|
||||
ERR(NULL, "Filter is neither an XML subtree nor an XPath expression (invalid first char '%c').", filter[0]);
|
||||
return NULL;
|
||||
} else if (!datastore) {
|
||||
ERRARG("datastore");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rpc = calloc(1, sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
|
||||
|
||||
rpc->type = NC_RPC_GETDATA;
|
||||
|
@ -558,16 +475,10 @@ nc_rpc_getdata(const char *datastore, const char *filter, const char *config_fil
|
|||
}
|
||||
if (origin_filter && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
|
||||
rpc->origin_filter = malloc(origin_filter_count * sizeof *rpc->origin_filter);
|
||||
if (!rpc->origin_filter) {
|
||||
ERRMEM;
|
||||
goto error;
|
||||
}
|
||||
NC_CHECK_ERRMEM_GOTO(!rpc->origin_filter, , error);
|
||||
for (i = 0; i < origin_filter_count; ++i) {
|
||||
rpc->origin_filter[i] = strdup(origin_filter[i]);
|
||||
if (!rpc->origin_filter[i]) {
|
||||
ERRMEM;
|
||||
goto error;
|
||||
}
|
||||
NC_CHECK_ERRMEM_GOTO(!rpc->origin_filter[i], , error);
|
||||
++rpc->origin_filter_count;
|
||||
}
|
||||
} else {
|
||||
|
@ -591,13 +502,7 @@ nc_rpc_editdata(const char *datastore, NC_RPC_EDIT_DFLTOP default_op, const char
|
|||
{
|
||||
struct nc_rpc_editdata *rpc;
|
||||
|
||||
if (!datastore) {
|
||||
ERRARG("datastore");
|
||||
return NULL;
|
||||
} else if (!edit_content) {
|
||||
ERRARG("edit_content");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, datastore, edit_content, NULL);
|
||||
|
||||
if (edit_content[0] && (edit_content[0] != '<') && !isalpha(edit_content[0])) {
|
||||
ERR(NULL, "<edit-data> content is neither a URL nor an XML config (invalid first char '%c').", edit_content[0]);
|
||||
|
@ -605,10 +510,7 @@ nc_rpc_editdata(const char *datastore, NC_RPC_EDIT_DFLTOP default_op, const char
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_EDITDATA;
|
||||
if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
|
||||
|
@ -633,10 +535,7 @@ nc_rpc_establishsub(const char *filter, const char *stream_name, const char *sta
|
|||
{
|
||||
struct nc_rpc_establishsub *rpc;
|
||||
|
||||
if (!stream_name) {
|
||||
ERRARG("stream_name");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, stream_name, NULL);
|
||||
|
||||
if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
|
||||
ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').",
|
||||
|
@ -645,10 +544,7 @@ nc_rpc_establishsub(const char *filter, const char *stream_name, const char *sta
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_ESTABLISHSUB;
|
||||
if (filter && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
|
||||
|
@ -686,10 +582,7 @@ nc_rpc_modifysub(uint32_t id, const char *filter, const char *stop_time, NC_PARA
|
|||
{
|
||||
struct nc_rpc_modifysub *rpc;
|
||||
|
||||
if (!id) {
|
||||
ERRARG("id");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, id, NULL);
|
||||
|
||||
if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
|
||||
ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').",
|
||||
|
@ -698,10 +591,7 @@ nc_rpc_modifysub(uint32_t id, const char *filter, const char *stop_time, NC_PARA
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_MODIFYSUB;
|
||||
rpc->id = id;
|
||||
|
@ -725,16 +615,10 @@ nc_rpc_deletesub(uint32_t id)
|
|||
{
|
||||
struct nc_rpc_deletesub *rpc;
|
||||
|
||||
if (!id) {
|
||||
ERRARG("id");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, id, NULL);
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_DELETESUB;
|
||||
rpc->id = id;
|
||||
|
@ -747,16 +631,10 @@ nc_rpc_killsub(uint32_t id)
|
|||
{
|
||||
struct nc_rpc_killsub *rpc;
|
||||
|
||||
if (!id) {
|
||||
ERRARG("id");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, id, NULL);
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_KILLSUB;
|
||||
rpc->id = id;
|
||||
|
@ -770,13 +648,7 @@ nc_rpc_establishpush_periodic(const char *datastore, const char *filter, const c
|
|||
{
|
||||
struct nc_rpc_establishpush *rpc;
|
||||
|
||||
if (!datastore) {
|
||||
ERRARG("datastore");
|
||||
return NULL;
|
||||
} else if (!period) {
|
||||
ERRARG("period");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, datastore, period, NULL);
|
||||
|
||||
if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
|
||||
ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').",
|
||||
|
@ -785,10 +657,7 @@ nc_rpc_establishpush_periodic(const char *datastore, const char *filter, const c
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_ESTABLISHPUSH;
|
||||
if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
|
||||
|
@ -829,11 +698,9 @@ nc_rpc_establishpush_onchange(const char *datastore, const char *filter, const c
|
|||
{
|
||||
struct nc_rpc_establishpush *rpc;
|
||||
uint32_t i;
|
||||
void *tmp;
|
||||
|
||||
if (!datastore) {
|
||||
ERRARG("datastore");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, datastore, NULL);
|
||||
|
||||
if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
|
||||
ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').",
|
||||
|
@ -842,10 +709,7 @@ nc_rpc_establishpush_onchange(const char *datastore, const char *filter, const c
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_ESTABLISHPUSH;
|
||||
if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
|
||||
|
@ -874,7 +738,15 @@ nc_rpc_establishpush_onchange(const char *datastore, const char *filter, const c
|
|||
if (excluded_change && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
|
||||
rpc->excluded_change = NULL;
|
||||
for (i = 0; excluded_change[i]; ++i) {
|
||||
rpc->excluded_change = realloc(rpc->excluded_change, (i + 2) * sizeof *rpc->excluded_change);
|
||||
tmp = realloc(rpc->excluded_change, (i + 2) * sizeof *rpc->excluded_change);
|
||||
if (!tmp) {
|
||||
/* in case we fail to alloc, just free all the excluded changes, but return the rpc anyways */
|
||||
ERRMEM;
|
||||
free(rpc->excluded_change);
|
||||
rpc->excluded_change = NULL;
|
||||
break;
|
||||
}
|
||||
rpc->excluded_change = tmp;
|
||||
rpc->excluded_change[i] = strdup(excluded_change[i]);
|
||||
rpc->excluded_change[i + 1] = NULL;
|
||||
}
|
||||
|
@ -892,13 +764,7 @@ nc_rpc_modifypush_periodic(uint32_t id, const char *datastore, const char *filte
|
|||
{
|
||||
struct nc_rpc_modifypush *rpc;
|
||||
|
||||
if (!id) {
|
||||
ERRARG("id");
|
||||
return NULL;
|
||||
} else if (!datastore) {
|
||||
ERRARG("datastore");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, id, datastore, NULL);
|
||||
|
||||
if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
|
||||
ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').",
|
||||
|
@ -907,10 +773,7 @@ nc_rpc_modifypush_periodic(uint32_t id, const char *datastore, const char *filte
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_MODIFYPUSH;
|
||||
rpc->id = id;
|
||||
|
@ -947,13 +810,7 @@ nc_rpc_modifypush_onchange(uint32_t id, const char *datastore, const char *filte
|
|||
{
|
||||
struct nc_rpc_modifypush *rpc;
|
||||
|
||||
if (!id) {
|
||||
ERRARG("id");
|
||||
return NULL;
|
||||
} else if (!datastore) {
|
||||
ERRARG("datastore");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, id, datastore, NULL);
|
||||
|
||||
if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
|
||||
ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').",
|
||||
|
@ -962,10 +819,7 @@ nc_rpc_modifypush_onchange(uint32_t id, const char *datastore, const char *filte
|
|||
}
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_MODIFYPUSH;
|
||||
rpc->id = id;
|
||||
|
@ -996,16 +850,10 @@ nc_rpc_resyncsub(uint32_t id)
|
|||
{
|
||||
struct nc_rpc_resyncsub *rpc;
|
||||
|
||||
if (!id) {
|
||||
ERRARG("id");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, id, NULL);
|
||||
|
||||
rpc = malloc(sizeof *rpc);
|
||||
if (!rpc) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!rpc, NULL);
|
||||
|
||||
rpc->type = NC_RPC_RESYNCSUB;
|
||||
rpc->id = id;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2's public functions and structures of NETCONF client messages.
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* @brief libnetconf2's private functions and structures of NETCONF messages.
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2021 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
|
@ -15,10 +16,13 @@
|
|||
#ifndef NC_MESSAGES_P_H_
|
||||
#define NC_MESSAGES_P_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#include "messages_client.h"
|
||||
#include "messages_server.h"
|
||||
#include "netconf.h"
|
||||
|
||||
extern const char *rpcedit_dfltop2str[];
|
||||
extern const char *rpcedit_testopt2str[];
|
||||
|
@ -65,6 +69,7 @@ struct nc_rpc {
|
|||
struct nc_rpc_act_generic {
|
||||
NC_RPC_TYPE type; /**< NC_RPC_ACT_GENERIC */
|
||||
int has_data; /**< 1 for content.data, 0 for content.xml_str */
|
||||
|
||||
union {
|
||||
struct lyd_node *data; /**< parsed RPC data */
|
||||
char *xml_str; /**< raw XML string */
|
||||
|
@ -220,6 +225,7 @@ struct nc_rpc_establishpush {
|
|||
char *stop;
|
||||
char *encoding;
|
||||
int periodic;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t period;
|
||||
|
@ -241,6 +247,7 @@ struct nc_rpc_modifypush {
|
|||
char *filter; /**< XML subtree (starts with '<'), an XPath (starts with '/'), or reference (start with alpha) */
|
||||
char *stop;
|
||||
int periodic;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t period;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/**
|
||||
* \file messages_server.c
|
||||
* \author Michal Vasko <mvasko@cesnet.cz>
|
||||
* \brief libnetconf2 - server NETCONF messages functions
|
||||
* @file messages_server.c
|
||||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 - server NETCONF messages functions
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2015 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
|
@ -12,6 +13,8 @@
|
|||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* pthread_rwlock_t, strdup */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
|
@ -21,10 +24,11 @@
|
|||
#include <libyang/libyang.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "libnetconf.h"
|
||||
#include "session_server.h"
|
||||
|
||||
extern struct nc_server_opts server_opts;
|
||||
#include "config.h"
|
||||
#include "log_p.h"
|
||||
#include "messages_p.h"
|
||||
#include "messages_server.h"
|
||||
#include "netconf.h"
|
||||
|
||||
API struct nc_server_reply *
|
||||
nc_server_reply_ok(void)
|
||||
|
@ -32,10 +36,7 @@ nc_server_reply_ok(void)
|
|||
struct nc_server_reply *ret;
|
||||
|
||||
ret = malloc(sizeof *ret);
|
||||
if (!ret) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!ret, NULL);
|
||||
|
||||
ret->type = NC_RPL_OK;
|
||||
return ret;
|
||||
|
@ -46,16 +47,15 @@ nc_server_reply_data(struct lyd_node *data, NC_WD_MODE wd, NC_PARAMTYPE paramtyp
|
|||
{
|
||||
struct nc_server_reply_data *ret;
|
||||
|
||||
if (!data) {
|
||||
ERRARG("data");
|
||||
NC_CHECK_ARG_RET(NULL, data, NULL);
|
||||
|
||||
if (!(data->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
|
||||
ERR(NULL, "nc_server_reply_data bad data");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = malloc(sizeof *ret);
|
||||
if (!ret) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!ret, NULL);
|
||||
|
||||
ret->type = NC_RPL_DATA;
|
||||
ret->wd = wd;
|
||||
|
@ -80,16 +80,10 @@ nc_server_reply_err(struct lyd_node *err)
|
|||
{
|
||||
struct nc_server_reply_error *ret;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, NULL);
|
||||
|
||||
ret = malloc(sizeof *ret);
|
||||
if (!ret) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!ret, NULL);
|
||||
|
||||
ret->type = NC_RPL_ERROR;
|
||||
ret->err = err;
|
||||
|
@ -101,11 +95,10 @@ nc_server_reply_add_err(struct nc_server_reply *reply, struct lyd_node *err)
|
|||
{
|
||||
struct nc_server_reply_error *err_rpl;
|
||||
|
||||
if (!reply || (reply->type != NC_RPL_ERROR)) {
|
||||
ERRARG("reply");
|
||||
return -1;
|
||||
} else if (!err) {
|
||||
ERRARG("err");
|
||||
NC_CHECK_ARG_RET(NULL, reply, err, -1);
|
||||
|
||||
if (reply->type != NC_RPL_ERROR) {
|
||||
ERR(NULL, "nc_server_reply_add_err() bad reply type");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -119,8 +112,10 @@ nc_server_reply_get_last_err(const struct nc_server_reply *reply)
|
|||
{
|
||||
struct nc_server_reply_error *err_rpl;
|
||||
|
||||
if (!reply || (reply->type != NC_RPL_ERROR)) {
|
||||
ERRARG("reply");
|
||||
NC_CHECK_ARG_RET(NULL, reply, NULL);
|
||||
|
||||
if (reply->type != NC_RPL_ERROR) {
|
||||
ERR(NULL, "nc_server_reply_get_last_err() bad reply type");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -270,10 +265,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
|
|||
const char *arg1, *arg2;
|
||||
uint32_t sid;
|
||||
|
||||
if (!tag) {
|
||||
ERRARG("tag");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, tag, NULL);
|
||||
|
||||
/* rpc-error */
|
||||
if (lyd_new_opaq2(NULL, ctx, "rpc-error", NULL, NULL, NC_NS_BASE, &err)) {
|
||||
|
@ -291,7 +283,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
|
|||
case NC_ERR_OP_NOT_SUPPORTED:
|
||||
type = (NC_ERR_TYPE)va_arg(ap, int); /* NC_ERR_TYPE enum is automatically promoted to int */
|
||||
if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) {
|
||||
ERRARG("type");
|
||||
ERRARG(NULL, "type");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
@ -304,7 +296,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
|
|||
case NC_ERR_UNKNOWN_ATTR:
|
||||
type = (NC_ERR_TYPE)va_arg(ap, int);
|
||||
if (type == NC_ERR_TYPE_TRAN) {
|
||||
ERRARG("type");
|
||||
ERRARG(NULL, "type");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
@ -313,14 +305,14 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
|
|||
case NC_ERR_UNKNOWN_ELEM:
|
||||
type = (NC_ERR_TYPE)va_arg(ap, int);
|
||||
if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) {
|
||||
ERRARG("type");
|
||||
ERRARG(NULL, "type");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case NC_ERR_UNKNOWN_NS:
|
||||
type = (NC_ERR_TYPE)va_arg(ap, int);
|
||||
if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) {
|
||||
ERRARG("type");
|
||||
ERRARG(NULL, "type");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
@ -334,7 +326,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
|
|||
case NC_ERR_OP_FAILED:
|
||||
type = (NC_ERR_TYPE)va_arg(ap, int);
|
||||
if (type == NC_ERR_TYPE_TRAN) {
|
||||
ERRARG("type");
|
||||
ERRARG(NULL, "type");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
@ -342,7 +334,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
|
|||
type = NC_ERR_TYPE_RPC;
|
||||
break;
|
||||
default:
|
||||
ERRARG("tag");
|
||||
ERRARG(NULL, "tag");
|
||||
goto fail;
|
||||
}
|
||||
if (lyd_new_opaq2(err, NULL, "error-type", nc_err_type2str(type), NULL, NC_NS_BASE, NULL)) {
|
||||
|
@ -419,7 +411,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
|
|||
nc_err_set_msg(err, "A message could not be handled because it failed to be parsed correctly.", "en");
|
||||
break;
|
||||
default:
|
||||
ERRARG("tag");
|
||||
ERRARG(NULL, "tag");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -466,7 +458,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
|
|||
nc_err_set_sid(err, sid);
|
||||
break;
|
||||
default:
|
||||
ERRARG("tag");
|
||||
ERRARG(NULL, "tag");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -484,10 +476,7 @@ nc_err_get_type(const struct lyd_node *err)
|
|||
{
|
||||
struct lyd_node *match;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return 0;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, 0);
|
||||
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-type", &match);
|
||||
if (match) {
|
||||
|
@ -502,10 +491,7 @@ nc_err_get_tag(const struct lyd_node *err)
|
|||
{
|
||||
struct lyd_node *match;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return 0;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, 0);
|
||||
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-tag", &match);
|
||||
if (match) {
|
||||
|
@ -520,13 +506,7 @@ nc_err_set_app_tag(struct lyd_node *err, const char *error_app_tag)
|
|||
{
|
||||
struct lyd_node *match;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return -1;
|
||||
} else if (!error_app_tag) {
|
||||
ERRARG("error_app_tag");
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, error_app_tag, -1);
|
||||
|
||||
/* remove previous node */
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &match);
|
||||
|
@ -546,10 +526,7 @@ nc_err_get_app_tag(const struct lyd_node *err)
|
|||
{
|
||||
struct lyd_node *match;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, NULL);
|
||||
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &match);
|
||||
if (match) {
|
||||
|
@ -564,13 +541,7 @@ nc_err_set_path(struct lyd_node *err, const char *error_path)
|
|||
{
|
||||
struct lyd_node *match;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return -1;
|
||||
} else if (!error_path) {
|
||||
ERRARG("error_path");
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, error_path, -1);
|
||||
|
||||
/* remove previous node */
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &match);
|
||||
|
@ -590,10 +561,7 @@ nc_err_get_path(const struct lyd_node *err)
|
|||
{
|
||||
struct lyd_node *match;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return 0;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, NULL);
|
||||
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &match);
|
||||
if (match) {
|
||||
|
@ -609,20 +577,15 @@ nc_err_set_msg(struct lyd_node *err, const char *error_message, const char *lang
|
|||
struct lyd_node *match;
|
||||
struct lyd_attr *attr;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return -1;
|
||||
} else if (!error_message) {
|
||||
ERRARG("error_message");
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, error_message, -1);
|
||||
|
||||
/* remove previous message */
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-message", &match);
|
||||
if (match) {
|
||||
lyd_free_tree(match);
|
||||
/* Change the value of error-message and keep order of elements to comply with appendix-B in RFC 6241. */
|
||||
lydict_remove(LYD_CTX(err), ((struct lyd_node_opaq *)match)->value);
|
||||
lydict_insert(LYD_CTX(err), error_message, 0, &(((struct lyd_node_opaq *)match)->value));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lyd_new_opaq2(err, NULL, "error-message", error_message, NULL, NC_NS_BASE, &match)) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -639,10 +602,7 @@ nc_err_get_msg(const struct lyd_node *err)
|
|||
{
|
||||
struct lyd_node *match;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, NULL);
|
||||
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-message", &match);
|
||||
if (match) {
|
||||
|
@ -658,10 +618,7 @@ nc_err_set_sid(struct lyd_node *err, uint32_t session_id)
|
|||
struct lyd_node *match, *info;
|
||||
char buf[22];
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, -1);
|
||||
|
||||
/* find error-info */
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
|
||||
|
@ -688,13 +645,7 @@ nc_err_add_bad_attr(struct lyd_node *err, const char *attr_name)
|
|||
{
|
||||
struct lyd_node *info;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return -1;
|
||||
} else if (!attr_name) {
|
||||
ERRARG("attr_name");
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, attr_name, -1);
|
||||
|
||||
/* find error-info */
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
|
||||
|
@ -714,13 +665,7 @@ nc_err_add_bad_elem(struct lyd_node *err, const char *elem_name)
|
|||
{
|
||||
struct lyd_node *info;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return -1;
|
||||
} else if (!elem_name) {
|
||||
ERRARG("elem_name");
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, elem_name, -1);
|
||||
|
||||
/* find error-info */
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
|
||||
|
@ -740,13 +685,7 @@ nc_err_add_bad_ns(struct lyd_node *err, const char *ns_name)
|
|||
{
|
||||
struct lyd_node *info;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return -1;
|
||||
} else if (!ns_name) {
|
||||
ERRARG("ns_name");
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, ns_name, -1);
|
||||
|
||||
/* find error-info */
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
|
||||
|
@ -766,13 +705,7 @@ nc_err_add_info_other(struct lyd_node *err, struct lyd_node *other)
|
|||
{
|
||||
struct lyd_node *info;
|
||||
|
||||
if (!err) {
|
||||
ERRARG("err");
|
||||
return -1;
|
||||
} else if (!other) {
|
||||
ERRARG("other");
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, err, other, -1);
|
||||
|
||||
/* find error-info */
|
||||
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
|
||||
|
@ -837,13 +770,7 @@ nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramt
|
|||
struct lyd_node *elem;
|
||||
int found;
|
||||
|
||||
if (!event) {
|
||||
ERRARG("event");
|
||||
return NULL;
|
||||
} else if (!eventtime) {
|
||||
ERRARG("eventtime");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, event, eventtime, NULL);
|
||||
|
||||
/* check that there is a notification */
|
||||
found = 0;
|
||||
|
@ -855,11 +782,13 @@ nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramt
|
|||
LYD_TREE_DFS_END(event, elem);
|
||||
}
|
||||
if (!found) {
|
||||
ERRARG("event");
|
||||
ERRARG(NULL, "event");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ntf = malloc(sizeof *ntf);
|
||||
NC_CHECK_ERRMEM_RET(!ntf, NULL);
|
||||
|
||||
if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
|
||||
ntf->eventtime = strdup(eventtime);
|
||||
if (lyd_dup_single(event, NULL, LYD_DUP_RECURSIVE, &ntf->ntf)) {
|
||||
|
@ -892,10 +821,7 @@ nc_server_notif_free(struct nc_server_notif *notif)
|
|||
API const char *
|
||||
nc_server_notif_get_time(const struct nc_server_notif *notif)
|
||||
{
|
||||
if (!notif) {
|
||||
ERRARG("notif");
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, notif, NULL);
|
||||
|
||||
return notif->eventtime;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2's functions and structures of server NETCONF messages.
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2015-2021 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
|
@ -19,9 +20,11 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#include "netconf.h"
|
||||
#include "session.h"
|
||||
|
||||
|
@ -95,7 +98,7 @@ struct nc_server_reply *nc_server_reply_ok(void);
|
|||
/**
|
||||
* @brief Create a DATA rpc-reply object.
|
||||
*
|
||||
* @param[in] data Reply data tree. This tree must be valid according to
|
||||
* @param[in] data Reply data tree pointing to the RPC/action itself. This tree must be valid according to
|
||||
* the RPC output of the RPC this is a reply to.
|
||||
* @param[in] wd with-default mode if applicable
|
||||
* @param[in] paramtype Determines how the @p data parameter is treated.
|
||||
|
@ -106,7 +109,7 @@ struct nc_server_reply *nc_server_reply_data(struct lyd_node *data, NC_WD_MODE w
|
|||
/**
|
||||
* @brief Create an ERROR rpc-reply object.
|
||||
*
|
||||
* @param[in] err Errors as opaque data node tree. It will be freed with the returned object.
|
||||
* @param[in] err Errors created by nc_err(). It will be freed with the returned object.
|
||||
* @return rpc-reply object, NULL on error.
|
||||
*/
|
||||
struct nc_server_reply *nc_server_reply_err(struct lyd_node *err);
|
||||
|
@ -115,7 +118,7 @@ struct nc_server_reply *nc_server_reply_err(struct lyd_node *err);
|
|||
* @brief Add another error opaque data node tree to an ERROR rpc-reply object.
|
||||
*
|
||||
* @param[in] reply ERROR reply to add to.
|
||||
* @param[in] err Error as opaque data node tree. It will be freed with the returned object.
|
||||
* @param[in] err Error created by nc_err(). It will be freed with the returned object.
|
||||
* @return 0 on success, -1 on errror.
|
||||
*/
|
||||
int nc_server_reply_add_err(struct nc_server_reply *reply, struct lyd_node *err);
|
||||
|
@ -165,6 +168,7 @@ const struct lyd_node *nc_server_reply_get_last_err(const struct nc_server_reply
|
|||
* - #NC_ERR_DATA_MISSING
|
||||
* - #NC_ERR_MALFORMED_MSG
|
||||
* - no additional arguments
|
||||
* @param[in] ... Additional arguments depending on the @p tag used.
|
||||
* @return Opaque data node tree representing the error.
|
||||
*/
|
||||
struct lyd_node *nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...);
|
||||
|
@ -293,11 +297,11 @@ void nc_server_reply_free(struct nc_server_reply *reply);
|
|||
* @brief Create Event Notification object to be sent to the subscribed client(s).
|
||||
*
|
||||
* @param[in] event Notification data tree (valid as LYD_OPT_NOTIF) from libyang. The tree is directly used in created
|
||||
* object, so the caller is supposed to not free the tree on its own, but only via freeng the created object.
|
||||
* object, so the caller is supposed to not free the tree on its own, but only via freeing the created object.
|
||||
* @param[in] eventtime YANG dateTime format value of the time when the event was generated by the event source.
|
||||
* Caller can use nc_timespec2datetime() to create the value from a timespec value.
|
||||
* @param[in] paramtype How to further manage data parameters.
|
||||
* @return Newly created structure of the Event Notification object to be sent to the clients via nc_server_send_notif()
|
||||
* @return Newly created structure of the Event Notification object to be sent to the clients via nc_server_notif_send()
|
||||
* and freed using nc_server_notif_free().
|
||||
*/
|
||||
struct nc_server_notif *nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramtype);
|
||||
|
@ -306,8 +310,8 @@ struct nc_server_notif *nc_server_notif_new(struct lyd_node *event, char *eventt
|
|||
* @brief Send NETCONF Event Notification via the session.
|
||||
*
|
||||
* @param[in] session NETCONF session where the Event Notification will be written.
|
||||
* @param[in] notif NETCOFN Notification object to send via specified session. Object can be created by
|
||||
* nc_notif_new() function.
|
||||
* @param[in] notif NETCONF Notification object to send via specified session. Object can be created by
|
||||
* nc_server_notif_new() function.
|
||||
* @param[in] timeout Timeout for writing in milliseconds. Use negative value for infinite
|
||||
* waiting and 0 for return if data cannot be sent immediately.
|
||||
* @return #NC_MSG_NOTIF on success,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* @brief libnetconf2's general public functions and structures definitions.
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
|
@ -19,8 +20,6 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/**
|
||||
* @addtogroup misc
|
||||
* @{
|
||||
|
|
4707
src/server_config.c
Normal file
4707
src/server_config.c
Normal file
File diff suppressed because it is too large
Load diff
1442
src/server_config.h
Normal file
1442
src/server_config.h
Normal file
File diff suppressed because it is too large
Load diff
445
src/server_config_ks.c
Normal file
445
src/server_config_ks.c
Normal file
|
@ -0,0 +1,445 @@
|
|||
/**
|
||||
* @file server_config_ks.c
|
||||
* @author Roman Janota <janota@cesnet.cz>
|
||||
* @brief libnetconf2 keystore configuration functions
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2023 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "log_p.h"
|
||||
#include "server_config_p.h"
|
||||
#include "session_p.h"
|
||||
|
||||
/**
|
||||
* @brief Get the pointer to an asymmetric key structure based on node's location in the YANG data.
|
||||
*
|
||||
* @param[in] node Node from which the asymmetric key containing this node is derived.
|
||||
* @param[out] askey Asymmetric key containing the node.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
static int
|
||||
nc_server_config_get_asymmetric_key(const struct lyd_node *node, struct nc_asymmetric_key **askey)
|
||||
{
|
||||
uint16_t i;
|
||||
const char *askey_name;
|
||||
struct nc_keystore *ks;
|
||||
const char *node_name = LYD_NAME(node);
|
||||
|
||||
assert(node && askey);
|
||||
|
||||
while (node) {
|
||||
if (!strcmp(LYD_NAME(node), "asymmetric-key")) {
|
||||
break;
|
||||
}
|
||||
node = lyd_parent(node);
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
ERR(NULL, "Node \"%s\" is not contained in an asymmetric-key subtree.", node_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
askey_name = lyd_get_value(node);
|
||||
|
||||
ks = &server_opts.keystore;
|
||||
for (i = 0; i < ks->asym_key_count; i++) {
|
||||
if (!strcmp(ks->asym_keys[i].name, askey_name)) {
|
||||
*askey = &ks->asym_keys[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ERR(NULL, "Asymmetric key \"%s\" was not found.", askey_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the pointer to a certificate structure based on node's location in the YANG data.
|
||||
*
|
||||
* @param[in] node Node from which the certificate containing this node is derived.
|
||||
* @param[out] cert Certificate containing the node.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
static int
|
||||
nc_server_config_get_certificate(const struct lyd_node *node, struct nc_certificate **cert)
|
||||
{
|
||||
uint16_t i;
|
||||
const char *cert_name;
|
||||
struct nc_asymmetric_key *askey;
|
||||
const char *node_name = LYD_NAME(node);
|
||||
|
||||
assert(node && cert);
|
||||
|
||||
if (nc_server_config_get_asymmetric_key(node, &askey)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (node) {
|
||||
if (!strcmp(LYD_NAME(node), "certificate")) {
|
||||
break;
|
||||
}
|
||||
node = lyd_parent(node);
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
ERR(NULL, "Node \"%s\" is not contained in a certificate subtree.", node_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
cert_name = lyd_get_value(node);
|
||||
|
||||
for (i = 0; i < askey->cert_count; i++) {
|
||||
if (!strcmp(askey->certs[i].name, cert_name)) {
|
||||
*cert = &askey->certs[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ERR(NULL, "Certificate \"%s\" was not found.", cert_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
nc_server_config_ks_del_asymmetric_key_cert(struct nc_asymmetric_key *key, struct nc_certificate *cert)
|
||||
{
|
||||
free(cert->name);
|
||||
free(cert->data);
|
||||
|
||||
key->cert_count--;
|
||||
if (!key->cert_count) {
|
||||
free(key->certs);
|
||||
key->certs = NULL;
|
||||
} else if (cert != &key->certs[key->cert_count]) {
|
||||
memcpy(cert, &key->certs[key->cert_count], sizeof *key->certs);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nc_server_config_ks_del_asymmetric_key(struct nc_asymmetric_key *key)
|
||||
{
|
||||
uint16_t i, cert_count;
|
||||
struct nc_keystore *ks = &server_opts.keystore;
|
||||
|
||||
free(key->name);
|
||||
free(key->pubkey_data);
|
||||
free(key->privkey_data);
|
||||
|
||||
cert_count = key->cert_count;
|
||||
for (i = 0; i < cert_count; i++) {
|
||||
nc_server_config_ks_del_asymmetric_key_cert(key, &key->certs[i]);
|
||||
}
|
||||
|
||||
ks->asym_key_count--;
|
||||
if (!ks->asym_key_count) {
|
||||
free(ks->asym_keys);
|
||||
ks->asym_keys = NULL;
|
||||
} else if (key != &ks->asym_keys[ks->asym_key_count]) {
|
||||
memcpy(key, &ks->asym_keys[ks->asym_key_count], sizeof *ks->asym_keys);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ks_asymmetric_keys(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
struct nc_keystore *ks = &server_opts.keystore;
|
||||
uint16_t i, asym_key_count;
|
||||
|
||||
(void) node;
|
||||
|
||||
if (op == NC_OP_DELETE) {
|
||||
asym_key_count = ks->asym_key_count;
|
||||
for (i = 0; i < asym_key_count; i++) {
|
||||
nc_server_config_ks_del_asymmetric_key(&ks->asym_keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nc_server_config_ks_keystore(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
(void) node;
|
||||
|
||||
if (op == NC_OP_DELETE) {
|
||||
nc_server_config_ks_asymmetric_keys(NULL, NC_OP_DELETE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ks_create_asymmetric_key(const struct lyd_node *node)
|
||||
{
|
||||
struct nc_keystore *ks = &server_opts.keystore;
|
||||
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
|
||||
return nc_server_config_realloc(lyd_get_value(node), (void **)&ks->asym_keys, sizeof *ks->asym_keys, &ks->asym_key_count);
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ks_asymmetric_key(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
int ret = 0;
|
||||
struct nc_asymmetric_key *key;
|
||||
|
||||
assert(!strcmp(LYD_NAME(node), "asymmetric-key"));
|
||||
|
||||
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
|
||||
ret = nc_server_config_ks_create_asymmetric_key(node);
|
||||
} else {
|
||||
if (nc_server_config_get_asymmetric_key(node, &key)) {
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
nc_server_config_ks_del_asymmetric_key(key);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ks_public_key_format(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
struct nc_asymmetric_key *key;
|
||||
const char *format;
|
||||
|
||||
(void) op;
|
||||
|
||||
assert(!strcmp(LYD_NAME(node), "public-key-format"));
|
||||
|
||||
if (nc_server_config_get_asymmetric_key(node, &key)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
format = ((struct lyd_node_term *)node)->value.ident->name;
|
||||
if (!strcmp(format, "ssh-public-key-format")) {
|
||||
key->pubkey_type = NC_PUBKEY_FORMAT_SSH;
|
||||
} else if (!strcmp(format, "subject-public-key-info-format")) {
|
||||
key->pubkey_type = NC_PUBKEY_FORMAT_X509;
|
||||
} else {
|
||||
ERR(NULL, "Public key format (%s) not supported.", format);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ks_public_key(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
struct nc_asymmetric_key *key;
|
||||
|
||||
(void) op;
|
||||
|
||||
assert(!strcmp(LYD_NAME(node), "public-key"));
|
||||
|
||||
if (nc_server_config_get_asymmetric_key(node, &key)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* replace the pubkey */
|
||||
free(key->pubkey_data);
|
||||
key->pubkey_data = strdup(lyd_get_value(node));
|
||||
NC_CHECK_ERRMEM_RET(!key->pubkey_data, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ks_private_key_format(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
struct nc_asymmetric_key *key;
|
||||
const char *format;
|
||||
enum nc_privkey_format privkey_type;
|
||||
|
||||
(void) op;
|
||||
|
||||
assert(!strcmp(LYD_NAME(node), "private-key-format"));
|
||||
|
||||
if (nc_server_config_get_asymmetric_key(node, &key)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
format = ((struct lyd_node_term *)node)->value.ident->name;
|
||||
if (!format) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
privkey_type = nc_server_config_get_private_key_type(format);
|
||||
if (privkey_type == NC_PRIVKEY_FORMAT_UNKNOWN) {
|
||||
return 1;
|
||||
}
|
||||
key->privkey_type = privkey_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ks_cleartext_private_key(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
struct nc_asymmetric_key *key;
|
||||
|
||||
assert(!strcmp(LYD_NAME(node), "cleartext-private-key"));
|
||||
|
||||
if (nc_server_config_get_asymmetric_key(node, &key)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
|
||||
/* replace the privkey */
|
||||
free(key->privkey_data);
|
||||
key->privkey_data = strdup(lyd_get_value(node));
|
||||
NC_CHECK_ERRMEM_RET(!key->privkey_data, 1);
|
||||
} else if (op == NC_OP_DELETE) {
|
||||
free(key->privkey_data);
|
||||
key->privkey_data = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ks_create_certificate(const struct lyd_node *node, struct nc_asymmetric_key *key)
|
||||
{
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
|
||||
return nc_server_config_realloc(lyd_get_value(node), (void **)&key->certs, sizeof *key->certs, &key->cert_count);
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ks_certificate(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
int ret = 0;
|
||||
struct nc_asymmetric_key *key;
|
||||
struct nc_certificate *cert;
|
||||
|
||||
assert(!strcmp(LYD_NAME(node), "certificate"));
|
||||
|
||||
if (nc_server_config_get_asymmetric_key(node, &key)) {
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
|
||||
ret = nc_server_config_ks_create_certificate(node, key);
|
||||
} else {
|
||||
if (nc_server_config_get_certificate(node, &cert)) {
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
nc_server_config_ks_del_asymmetric_key_cert(key, cert);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ks_cert_data(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
struct nc_certificate *cert;
|
||||
|
||||
assert(!strcmp(LYD_NAME(node), "cert-data"));
|
||||
|
||||
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
|
||||
if (nc_server_config_get_certificate(node, &cert)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* replace the cert data */
|
||||
free(cert->data);
|
||||
cert->data = strdup(lyd_get_value(node));
|
||||
NC_CHECK_ERRMEM_RET(!cert->data, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nc_server_config_parse_keystore(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
const char *name = LYD_NAME(node);
|
||||
int ret = 0;
|
||||
|
||||
if (!strcmp(name, "keystore")) {
|
||||
ret = nc_server_config_ks_keystore(node, op);
|
||||
} else if (!strcmp(name, "asymmetric-keys")) {
|
||||
ret = nc_server_config_ks_asymmetric_keys(node, op);
|
||||
} else if (!strcmp(name, "asymmetric-key")) {
|
||||
ret = nc_server_config_ks_asymmetric_key(node, op);
|
||||
} else if (!strcmp(name, "public-key-format")) {
|
||||
ret = nc_server_config_ks_public_key_format(node, op);
|
||||
} else if (!strcmp(name, "public-key")) {
|
||||
ret = nc_server_config_ks_public_key(node, op);
|
||||
} else if (!strcmp(name, "private-key-format")) {
|
||||
ret = nc_server_config_ks_private_key_format(node, op);
|
||||
} else if (!strcmp(name, "cleartext-private-key")) {
|
||||
ret = nc_server_config_ks_cleartext_private_key(node, op);
|
||||
} else if (!strcmp(name, "certificate")) {
|
||||
ret = nc_server_config_ks_certificate(node, op);
|
||||
} else if (!strcmp(name, "cert-data")) {
|
||||
ret = nc_server_config_ks_cert_data(node, op);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
ERR(NULL, "Configuring (%s) failed.", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nc_server_config_fill_keystore(const struct lyd_node *data, enum nc_operation op)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t prev_lo;
|
||||
struct lyd_node *tree;
|
||||
|
||||
/* silently search for nodes, some of them may not be present */
|
||||
prev_lo = ly_log_options(0);
|
||||
|
||||
ret = lyd_find_path(data, "/ietf-keystore:keystore", 0, &tree);
|
||||
if (ret || (tree->flags & LYD_DEFAULT)) {
|
||||
/* not found */
|
||||
ret = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (nc_server_config_parse_tree(tree, op, NC_MODULE_KEYSTORE)) {
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
/* reset the logging options back to what they were */
|
||||
ly_log_options(prev_lo);
|
||||
return ret;
|
||||
}
|
169
src/server_config_p.h
Normal file
169
src/server_config_p.h
Normal file
|
@ -0,0 +1,169 @@
|
|||
/**
|
||||
* @file server_config_p.h
|
||||
* @author Roman Janota <janota@cesnet.cz>
|
||||
* @brief libnetconf2 server configuration
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2023 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef NC_CONFIG_SERVER_P_H_
|
||||
#define NC_CONFIG_SERVER_P_H_
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "session_p.h"
|
||||
|
||||
/**
|
||||
* Enumeration of ietf-netconf-server's modules/trees (top-level containers)
|
||||
*/
|
||||
typedef enum {
|
||||
NC_MODULE_NETCONF_SERVER,
|
||||
NC_MODULE_KEYSTORE,
|
||||
NC_MODULE_TRUSTSTORE,
|
||||
NC_MODULE_LIBNETCONF2_NETCONF_SERVER
|
||||
} NC_MODULE;
|
||||
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/**
|
||||
* @brief Get private key type from YANG identity stored in a string.
|
||||
*
|
||||
* @param[in] format Value of the YANG identityref.
|
||||
* @return Private key format on success, NC_PRIVKEY_FORMAT_UNKNOWN otherwise.
|
||||
*/
|
||||
enum nc_privkey_format nc_server_config_get_private_key_type(const char *format);
|
||||
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
/**
|
||||
* @brief Compares the nth-parent name.
|
||||
*
|
||||
* @param[in] node Node of which nth-parent to compare.
|
||||
* @param[in] parent_count Count of parents.
|
||||
* @param[in] parent_name Expected name of the parent.
|
||||
* @return 1 if the name matches, 0 otherwise.
|
||||
*/
|
||||
int equal_parent_name(const struct lyd_node *node, uint16_t parent_count, const char *parent_name);
|
||||
|
||||
/**
|
||||
* @brief Generic realloc function for arrays of structures representing YANG lists whose first member is the key (char *)
|
||||
*
|
||||
* @param[in] key_value Value of the key, which will be assigned to the first member of the given struct.
|
||||
* @param[in] size Size of a member of the array.
|
||||
* @param[in,out] ptr Pointer to the beginning of the given array, which will be reallocated.
|
||||
* @param[in,out] count Count of members in the array, incremented at the end.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_server_config_realloc(const char *key_value, void **ptr, size_t size, uint16_t *count);
|
||||
|
||||
/**
|
||||
* @brief Recursively parse the given tree and apply it's data to the server's configuration.
|
||||
*
|
||||
* @param[in] node YANG data tree.
|
||||
* @param[in] parent_op Operation of the parent.
|
||||
* @param[in] module Module for which to parse the data - either ietf-netconf-server, ietf-keystore or ietf-truststore
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_server_config_parse_tree(const struct lyd_node *node, enum nc_operation parent_op, NC_MODULE module);
|
||||
|
||||
/**
|
||||
* @brief Configures the listen subtree in the ietf-netconf-server module.
|
||||
*
|
||||
* @param[in] node Listen YANG data node.
|
||||
* @param[in] op Operation to be done on the subtree. Only does something if the operation is NC_OP_DELETE.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_server_config_listen(const struct lyd_node *node, enum nc_operation op);
|
||||
|
||||
/**
|
||||
* @brief Configures the Call Home subtree in the ietf-netconf-server module.
|
||||
*
|
||||
* @param[in] node call-home YANG data node.
|
||||
* @param[in] op Operation to be done on the subtree. Only does something if the operation is NC_OP_DELETE.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_server_config_ch(const struct lyd_node *node, enum nc_operation op);
|
||||
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/** KEYSTORE **/
|
||||
|
||||
/**
|
||||
* @brief Checks if keystore tree is present in the data and if yes, tries to apply it's data.
|
||||
*
|
||||
* @param[in] data YANG data tree.
|
||||
* @param[in] op Operation saying what to do with the top-level node.
|
||||
* @return 0 either if keystore is not present or if it is and application was successful, 1 on error.
|
||||
*/
|
||||
int nc_server_config_fill_keystore(const struct lyd_node *data, enum nc_operation op);
|
||||
|
||||
/**
|
||||
* @brief Parse the given node, which belongs to the ietf-keystore subtree, and apply it's data to the server's configuration.
|
||||
*
|
||||
* @param[in] node YANG data node.
|
||||
* @param[in] op Operation saying what to do with the node.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_server_config_parse_keystore(const struct lyd_node *node, enum nc_operation op);
|
||||
|
||||
/**
|
||||
* @brief Configures the keystore subtree in the ietf-keystore module.
|
||||
*
|
||||
* @param[in] node Keystore YANG data node.
|
||||
* @param[in] op Operation to be done on the subtree. Only does something if the operation is NC_OP_DELETE.
|
||||
* @return 0.
|
||||
*/
|
||||
int nc_server_config_ks_keystore(const struct lyd_node *node, enum nc_operation op);
|
||||
|
||||
/** TRUSTSTORE **/
|
||||
|
||||
/**
|
||||
* @brief Checks if truststore tree is present in the data and if yes, tries to apply it's data.
|
||||
*
|
||||
* @param[in] data YANG data tree.
|
||||
* @param[in] op Operation saying what to do with the top-level node.
|
||||
* @return 0 either if truststore is not present or if it is and application was successful, 1 on error.
|
||||
*/
|
||||
int nc_server_config_fill_truststore(const struct lyd_node *data, enum nc_operation op);
|
||||
|
||||
/**
|
||||
* @brief Parse the given node, which belongs to the ietf-truststore subtree, and apply it's data to the server's configuration.
|
||||
*
|
||||
* @param[in] node YANG data node.
|
||||
* @param[in] op Operation saying what to do with the node.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_server_config_parse_truststore(const struct lyd_node *node, enum nc_operation op);
|
||||
|
||||
/**
|
||||
* @brief Configures the truststore subtree in the ietf-truststore module.
|
||||
*
|
||||
* @param[in] node Truststore YANG data node.
|
||||
* @param[in] op Operation to be done on the subtree. Only does something if the operation is NC_OP_DELETE.
|
||||
* @return 0.
|
||||
*/
|
||||
int nc_server_config_ts_truststore(const struct lyd_node *node, enum nc_operation op);
|
||||
|
||||
/** LIBNETCONF2-NETCONF-SERVER **/
|
||||
|
||||
/**
|
||||
* @brief Configures the ln2-netconf-server subtree in the libnetconf2-netconf-server module.
|
||||
*
|
||||
* @param[in] node Optional ln2-netconf-server YANG data node.
|
||||
* @param[in] op Operation to be done on the subtree. Only does something if the operation is NC_OP_DELETE.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_server_config_ln2_netconf_server(const struct lyd_node *node, enum nc_operation op);
|
||||
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
#endif /* NC_CONFIG_SERVER_P_H_ */
|
600
src/server_config_ts.c
Normal file
600
src/server_config_ts.c
Normal file
|
@ -0,0 +1,600 @@
|
|||
/**
|
||||
* @file server_config_ts.c
|
||||
* @author Roman Janota <janota@cesnet.cz>
|
||||
* @brief libnetconf2 truststore configuration functions
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2023 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "log_p.h"
|
||||
#include "server_config_p.h"
|
||||
#include "session_p.h"
|
||||
|
||||
/**
|
||||
* @brief Get the pointer to a certificate bag structure based on node's location in the YANG data.
|
||||
*
|
||||
* @param[in] node Node from which the certificate bag containing this node is derived.
|
||||
* @param[out] cbag Certificate bag containing the node.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
static int
|
||||
nc_server_config_get_certificate_bag(const struct lyd_node *node, struct nc_certificate_bag **cbag)
|
||||
{
|
||||
uint16_t i;
|
||||
const char *cbag_name;
|
||||
struct nc_truststore *ts;
|
||||
const char *node_name = LYD_NAME(node);
|
||||
|
||||
assert(node && cbag);
|
||||
|
||||
while (node) {
|
||||
if (!strcmp(LYD_NAME(node), "certificate-bag")) {
|
||||
break;
|
||||
}
|
||||
node = lyd_parent(node);
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
ERR(NULL, "Node \"%s\" is not contained in a certificate-bag subtree.", node_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
cbag_name = lyd_get_value(node);
|
||||
|
||||
ts = &server_opts.truststore;
|
||||
for (i = 0; i < ts->cert_bag_count; i++) {
|
||||
if (!strcmp(ts->cert_bags[i].name, cbag_name)) {
|
||||
*cbag = &ts->cert_bags[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ERR(NULL, "Certificate bag \"%s\" was not found.", cbag_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the pointer to a certificate structure based on node's location in the YANG data.
|
||||
*
|
||||
* @param[in] node Node from which the certificate containing this node is derived.
|
||||
* @param[out] cert Certificate containing the node.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
static int
|
||||
nc_server_config_get_certificate(const struct lyd_node *node, struct nc_certificate **cert)
|
||||
{
|
||||
uint16_t i;
|
||||
const char *cert_name;
|
||||
struct nc_certificate_bag *cbag;
|
||||
const char *node_name = LYD_NAME(node);
|
||||
|
||||
assert(node && cert);
|
||||
|
||||
if (nc_server_config_get_certificate_bag(node, &cbag)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (node) {
|
||||
if (!strcmp(LYD_NAME(node), "certificate")) {
|
||||
break;
|
||||
}
|
||||
node = lyd_parent(node);
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
ERR(NULL, "Node \"%s\" is not contained in a certificate subtree.", node_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
cert_name = lyd_get_value(node);
|
||||
|
||||
for (i = 0; i < cbag->cert_count; i++) {
|
||||
if (!strcmp(cbag->certs[i].name, cert_name)) {
|
||||
*cert = &cbag->certs[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ERR(NULL, "Certificate \"%s\" was not found.", cert_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the pointer to a public key bag structure based on node's location in the YANG data.
|
||||
*
|
||||
* @param[in] node Node from which the public key bag containing this node is derived.
|
||||
* @param[out] pbag Public key bag containing the node.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
static int
|
||||
nc_server_config_get_public_key_bag(const struct lyd_node *node, struct nc_public_key_bag **pbag)
|
||||
{
|
||||
uint16_t i;
|
||||
const char *pbag_name;
|
||||
struct nc_truststore *ts;
|
||||
const char *node_name = LYD_NAME(node);
|
||||
|
||||
assert(node && pbag);
|
||||
|
||||
while (node) {
|
||||
if (!strcmp(LYD_NAME(node), "public-key-bag")) {
|
||||
break;
|
||||
}
|
||||
node = lyd_parent(node);
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
ERR(NULL, "Node \"%s\" is not contained in a public-key-bag subtree.", node_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
pbag_name = lyd_get_value(node);
|
||||
|
||||
ts = &server_opts.truststore;
|
||||
for (i = 0; i < ts->pub_bag_count; i++) {
|
||||
if (!strcmp(ts->pub_bags[i].name, pbag_name)) {
|
||||
*pbag = &ts->pub_bags[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ERR(NULL, "Public key bag \"%s\" was not found.", pbag_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the pointer to a public key structure based on node's location in the YANG data.
|
||||
*
|
||||
* @param[in] node Node from which the public key containing this node is derived.
|
||||
* @param[out] pkey Public key containing the node.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
static int
|
||||
nc_server_config_get_public_key(const struct lyd_node *node, struct nc_public_key **pkey)
|
||||
{
|
||||
uint16_t i;
|
||||
const char *pkey_name;
|
||||
struct nc_public_key_bag *pbag;
|
||||
const char *node_name = LYD_NAME(node);
|
||||
|
||||
assert(node && pkey);
|
||||
|
||||
if (nc_server_config_get_public_key_bag(node, &pbag)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (node) {
|
||||
if (!strcmp(LYD_NAME(node), "public-key")) {
|
||||
if (lyd_child(node)) {
|
||||
/* check if it's not the leaf public-key, only case about the list */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
node = lyd_parent(node);
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
ERR(NULL, "Node \"%s\" is not contained in a public-key subtree.", node_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
pkey_name = lyd_get_value(node);
|
||||
|
||||
for (i = 0; i < pbag->pubkey_count; i++) {
|
||||
if (!strcmp(pbag->pubkeys[i].name, pkey_name)) {
|
||||
*pkey = &pbag->pubkeys[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ERR(NULL, "Public key \"%s\" was not found.", pkey_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
nc_server_config_ts_del_certificate(struct nc_certificate_bag *cbag, struct nc_certificate *cert)
|
||||
{
|
||||
free(cert->name);
|
||||
free(cert->data);
|
||||
|
||||
cbag->cert_count--;
|
||||
if (!cbag->cert_count) {
|
||||
free(cbag->certs);
|
||||
cbag->certs = NULL;
|
||||
} else if (cert != &cbag->certs[cbag->cert_count]) {
|
||||
memcpy(cert, &cbag->certs[cbag->cert_count], sizeof *cbag->certs);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nc_server_config_ts_del_public_key(struct nc_public_key_bag *pbag, struct nc_public_key *pkey)
|
||||
{
|
||||
free(pkey->name);
|
||||
free(pkey->data);
|
||||
|
||||
pbag->pubkey_count--;
|
||||
if (!pbag->pubkey_count) {
|
||||
free(pbag->pubkeys);
|
||||
pbag->pubkeys = NULL;
|
||||
} else if (pkey != &pbag->pubkeys[pbag->pubkey_count]) {
|
||||
memcpy(pkey, &pbag->pubkeys[pbag->pubkey_count], sizeof *pbag->pubkeys);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nc_server_config_ts_del_certificate_bag(struct nc_certificate_bag *cbag)
|
||||
{
|
||||
uint16_t i, cert_count;
|
||||
struct nc_truststore *ts = &server_opts.truststore;
|
||||
|
||||
free(cbag->name);
|
||||
|
||||
cert_count = cbag->cert_count;
|
||||
for (i = 0; i < cert_count; i++) {
|
||||
nc_server_config_ts_del_certificate(cbag, &cbag->certs[i]);
|
||||
}
|
||||
|
||||
ts->cert_bag_count--;
|
||||
if (!ts->cert_bag_count) {
|
||||
free(ts->cert_bags);
|
||||
ts->cert_bags = NULL;
|
||||
} else if (cbag != &ts->cert_bags[ts->cert_bag_count]) {
|
||||
memcpy(cbag, &ts->cert_bags[ts->cert_bag_count], sizeof *ts->cert_bags);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nc_server_config_ts_del_public_key_bag(struct nc_public_key_bag *pbag)
|
||||
{
|
||||
uint16_t i, pubkey_count;
|
||||
struct nc_truststore *ts = &server_opts.truststore;
|
||||
|
||||
free(pbag->name);
|
||||
|
||||
pubkey_count = pbag->pubkey_count;
|
||||
for (i = 0; i < pubkey_count; i++) {
|
||||
nc_server_config_ts_del_public_key(pbag, &pbag->pubkeys[i]);
|
||||
}
|
||||
|
||||
ts->pub_bag_count--;
|
||||
if (!ts->pub_bag_count) {
|
||||
free(ts->pub_bags);
|
||||
ts->pub_bags = NULL;
|
||||
} else if (pbag != &ts->pub_bags[ts->pub_bag_count]) {
|
||||
memcpy(pbag, &ts->pub_bags[ts->pub_bag_count], sizeof *ts->pub_bags);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_certificate_bags(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
uint16_t i, cert_bag_count;
|
||||
struct nc_truststore *ts = &server_opts.truststore;
|
||||
|
||||
(void) node;
|
||||
|
||||
if (op == NC_OP_DELETE) {
|
||||
cert_bag_count = ts->cert_bag_count;
|
||||
for (i = 0; i < cert_bag_count; i++) {
|
||||
nc_server_config_ts_del_certificate_bag(&ts->cert_bags[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_public_key_bags(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
uint16_t i, pub_bag_count;
|
||||
struct nc_truststore *ts = &server_opts.truststore;
|
||||
|
||||
(void) node;
|
||||
|
||||
if (op == NC_OP_DELETE) {
|
||||
pub_bag_count = ts->pub_bag_count;
|
||||
for (i = 0; i < pub_bag_count; i++) {
|
||||
nc_server_config_ts_del_public_key_bag(&ts->pub_bags[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nc_server_config_ts_truststore(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
(void) node;
|
||||
|
||||
if (op == NC_OP_DELETE) {
|
||||
nc_server_config_ts_certificate_bags(NULL, NC_OP_DELETE);
|
||||
nc_server_config_ts_public_key_bags(NULL, NC_OP_DELETE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_create_certificate_bag(const struct lyd_node *node)
|
||||
{
|
||||
struct nc_truststore *ts = &server_opts.truststore;
|
||||
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
|
||||
return nc_server_config_realloc(lyd_get_value(node), (void **)&ts->cert_bags, sizeof *ts->cert_bags, &ts->cert_bag_count);
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_certificate_bag(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
struct nc_certificate_bag *bag;
|
||||
|
||||
assert(!strcmp(LYD_NAME(node), "certificate-bag"));
|
||||
|
||||
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
|
||||
if (nc_server_config_ts_create_certificate_bag(node)) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (nc_server_config_get_certificate_bag(node, &bag)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
nc_server_config_ts_del_certificate_bag(bag);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_create_certificate(const struct lyd_node *node, struct nc_certificate_bag *bag)
|
||||
{
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
|
||||
return nc_server_config_realloc(lyd_get_value(node), (void **)&bag->certs, sizeof *bag->certs, &bag->cert_count);
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_certificate(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
struct nc_certificate_bag *bag;
|
||||
struct nc_certificate *cert;
|
||||
|
||||
assert(!strcmp(LYD_NAME(node), "certificate"));
|
||||
|
||||
if (nc_server_config_get_certificate_bag(node, &bag)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
|
||||
if (nc_server_config_ts_create_certificate(node, bag)) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (nc_server_config_get_certificate(node, &cert)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
nc_server_config_ts_del_certificate(bag, cert);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_cert_data(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
struct nc_certificate *cert;
|
||||
|
||||
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
|
||||
if (nc_server_config_get_certificate(node, &cert)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(cert->data);
|
||||
cert->data = strdup(lyd_get_value(node));
|
||||
NC_CHECK_ERRMEM_RET(!cert->data, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_create_public_key_bag(const struct lyd_node *node)
|
||||
{
|
||||
struct nc_truststore *ts = &server_opts.truststore;
|
||||
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
|
||||
return nc_server_config_realloc(lyd_get_value(node), (void **)&ts->pub_bags, sizeof *ts->pub_bags, &ts->pub_bag_count);
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_public_key_bag(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
struct nc_public_key_bag *pbag;
|
||||
|
||||
assert(!strcmp(LYD_NAME(node), "public-key-bag"));
|
||||
|
||||
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
|
||||
if (nc_server_config_ts_create_public_key_bag(node)) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (nc_server_config_get_public_key_bag(node, &pbag)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
nc_server_config_ts_del_public_key_bag(pbag);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_create_public_key(const struct lyd_node *node, struct nc_public_key_bag *bag)
|
||||
{
|
||||
node = lyd_child(node);
|
||||
assert(!strcmp(LYD_NAME(node), "name"));
|
||||
|
||||
return nc_server_config_realloc(lyd_get_value(node), (void **)&bag->pubkeys, sizeof *bag->pubkeys, &bag->pubkey_count);
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_public_key(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
int ret = 0;
|
||||
struct nc_public_key_bag *bag;
|
||||
struct nc_public_key *pkey;
|
||||
|
||||
if (nc_server_config_get_public_key_bag(node, &bag)) {
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (equal_parent_name(node, 1, "public-key-bag")) {
|
||||
/* public-key list */
|
||||
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
|
||||
ret = nc_server_config_ts_create_public_key(node, bag);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
if (nc_server_config_get_public_key(node, &pkey)) {
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
nc_server_config_ts_del_public_key(bag, pkey);
|
||||
}
|
||||
} else {
|
||||
/* public-key leaf */
|
||||
if (nc_server_config_get_public_key(node, &pkey)) {
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* replace the public key */
|
||||
free(pkey->data);
|
||||
pkey->data = strdup(lyd_get_value(node));
|
||||
NC_CHECK_ERRMEM_GOTO(!pkey->data, ret = 1, cleanup);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nc_server_config_ts_public_key_format(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
const char *format;
|
||||
struct nc_public_key *pkey;
|
||||
|
||||
(void) op;
|
||||
|
||||
if (nc_server_config_get_public_key(node, &pkey)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
format = ((struct lyd_node_term *)node)->value.ident->name;
|
||||
if (!strcmp(format, "ssh-public-key-format")) {
|
||||
pkey->type = NC_PUBKEY_FORMAT_SSH;
|
||||
} else if (!strcmp(format, "subject-public-key-info-format")) {
|
||||
pkey->type = NC_PUBKEY_FORMAT_X509;
|
||||
} else {
|
||||
ERR(NULL, "Public key format (%s) not supported.", format);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nc_server_config_parse_truststore(const struct lyd_node *node, enum nc_operation op)
|
||||
{
|
||||
const char *name = LYD_NAME(node);
|
||||
int ret = 0;
|
||||
|
||||
if (!strcmp(name, "truststore")) {
|
||||
ret = nc_server_config_ts_truststore(node, op);
|
||||
} else if (!strcmp(name, "certificate-bags")) {
|
||||
ret = nc_server_config_ts_certificate_bags(node, op);
|
||||
} else if (!strcmp(name, "certificate-bag")) {
|
||||
ret = nc_server_config_ts_certificate_bag(node, op);
|
||||
} else if (!strcmp(name, "certificate")) {
|
||||
ret = nc_server_config_ts_certificate(node, op);
|
||||
} else if (!strcmp(name, "cert-data")) {
|
||||
ret = nc_server_config_ts_cert_data(node, op);
|
||||
} else if (!strcmp(name, "public-key-bags")) {
|
||||
ret = nc_server_config_ts_public_key_bags(node, op);
|
||||
} else if (!strcmp(name, "public-key-bag")) {
|
||||
ret = nc_server_config_ts_public_key_bag(node, op);
|
||||
} else if (!strcmp(name, "public-key")) {
|
||||
ret = nc_server_config_ts_public_key(node, op);
|
||||
} else if (!strcmp(name, "public-key-format")) {
|
||||
ret = nc_server_config_ts_public_key_format(node, op);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
ERR(NULL, "Configuring (%s) failed.", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nc_server_config_fill_truststore(const struct lyd_node *data, enum nc_operation op)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t prev_lo;
|
||||
struct lyd_node *tree;
|
||||
|
||||
/* silently search for nodes, some of them may not be present */
|
||||
prev_lo = ly_log_options(0);
|
||||
|
||||
ret = lyd_find_path(data, "/ietf-truststore:truststore", 0, &tree);
|
||||
if (ret || (tree->flags & LYD_DEFAULT)) {
|
||||
/* not found */
|
||||
ret = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (nc_server_config_parse_tree(tree, op, NC_MODULE_TRUSTSTORE)) {
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
/* reset the logging options back to what they were */
|
||||
ly_log_options(prev_lo);
|
||||
return ret;
|
||||
}
|
1366
src/server_config_util.c
Normal file
1366
src/server_config_util.c
Normal file
File diff suppressed because it is too large
Load diff
169
src/server_config_util.h
Normal file
169
src/server_config_util.h
Normal file
|
@ -0,0 +1,169 @@
|
|||
/**
|
||||
* @file server_config_util.h
|
||||
* @author Roman Janota <janota@cesnet.cz>
|
||||
* @brief libnetconf2 server configuration utlities header
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2023 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef NC_SERVER_CONFIG_UTIL_H_
|
||||
#define NC_SERVER_CONFIG_UTIL_H_
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#include "session_p.h"
|
||||
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/* private key's pkcs8 header */
|
||||
#define NC_PKCS8_PRIVKEY_HEADER "-----BEGIN PRIVATE KEY-----\n"
|
||||
|
||||
/* private key's pkcs8 footer */
|
||||
#define NC_PKCS8_PRIVKEY_FOOTER "\n-----END PRIVATE KEY-----\n"
|
||||
|
||||
/* private key's openssh header */
|
||||
#define NC_OPENSSH_PRIVKEY_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----\n"
|
||||
|
||||
/* private key's openssh footer */
|
||||
#define NC_OPENSSH_PRIVKEY_FOOTER "\n-----END OPENSSH PRIVATE KEY-----\n"
|
||||
|
||||
/* private key's pkcs1 rsa header */
|
||||
#define NC_PKCS1_RSA_PRIVKEY_HEADER "-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
|
||||
/* private key's pkcs1 rsa footer */
|
||||
#define NC_PKCS1_RSA_PRIVKEY_FOOTER "\n-----END RSA PRIVATE KEY-----\n"
|
||||
|
||||
/* private key's sec1 ec header */
|
||||
#define NC_SEC1_EC_PRIVKEY_HEADER "-----BEGIN EC PRIVATE KEY-----\n"
|
||||
|
||||
/* private key's sec1 ec footer */
|
||||
#define NC_SEC1_EC_PRIVKEY_FOOTER "\n-----END EC PRIVATE KEY-----\n"
|
||||
|
||||
/* private key's header when getting an EC/RSA privkey from file using libssh */
|
||||
#define NC_LIBSSH_PRIVKEY_HEADER "-----BEGIN PRIVATE KEY-----\n"
|
||||
|
||||
/* private key's footer when getting an EC/RSA privkey from file using libssh */
|
||||
#define NC_LIBSSH_PRIVKEY_FOOTER "\n-----END PRIVATE KEY-----\n"
|
||||
|
||||
/* public key's ssh2 header */
|
||||
#define NC_SSH2_PUBKEY_HEADER "---- BEGIN SSH2 PUBLIC KEY ----\n"
|
||||
|
||||
/* public key's SubjectPublicKeyInfo format header */
|
||||
#define NC_SUBJECT_PUBKEY_INFO_HEADER "-----BEGIN PUBLIC KEY-----\n"
|
||||
|
||||
/* public key's SubjectPublicKeyInfo format footer */
|
||||
#define NC_SUBJECT_PUBKEY_INFO_FOOTER "\n-----END PUBLIC KEY-----\n"
|
||||
|
||||
/* certificate's PEM format header */
|
||||
#define NC_PEM_CERTIFICATE_HEADER "-----BEGIN CERTIFICATE-----\n"
|
||||
|
||||
/* certificate's PEM format footer */
|
||||
#define NC_PEM_CERTIFICATE_FOOTER "\n-----END CERTIFICATE-----\n"
|
||||
|
||||
typedef enum {
|
||||
NC_ALG_HOSTKEY,
|
||||
NC_ALG_KEY_EXCHANGE,
|
||||
NC_ALG_ENCRYPTION,
|
||||
NC_ALG_MAC
|
||||
} NC_ALG_TYPE;
|
||||
|
||||
/**
|
||||
* @brief Gets asymmetric key pair from private key (and optionally public key) file(s).
|
||||
*
|
||||
* @param[in] privkey_path Path to private key.
|
||||
* @param[in] pubkey_path Optional path to public key. If not set, PK will be generated from private key.
|
||||
* @param[in] wanted_pubkey_type Wanted public key format to be generated (SPKI/SSH)
|
||||
* @param[out] privkey Base64 encoded private key.
|
||||
* @param[out] privkey_type Type of the private key. (RSA, EC, etc)
|
||||
* @param[out] pubkey Base64 encoded public key.
|
||||
* @return 0 on success, non-zero otherwise.
|
||||
*/
|
||||
int nc_server_config_util_get_asym_key_pair(const char *privkey_path, const char *pubkey_path, enum nc_pubkey_format wanted_pubkey_type,
|
||||
char **privkey, enum nc_privkey_format *privkey_type, char **pubkey);
|
||||
|
||||
/**
|
||||
* @brief Gets public key from a file and converts it to the SSH format if need be.
|
||||
*
|
||||
* @param[in] pubkey_path Path to the public key.
|
||||
* @param[out] pubkey Base64 encoded public key.
|
||||
*
|
||||
* @return 0 on success, non-zero otherwise.
|
||||
*/
|
||||
int nc_server_config_util_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey);
|
||||
|
||||
/**
|
||||
* @brief Gets a certificate from a file.
|
||||
*
|
||||
* @param[in] cert_path Path to the certificate.
|
||||
* @param[out] cert Base64 PEM encoded certificate data.
|
||||
*
|
||||
* @return 0 on success, non-zero otherwise.
|
||||
*/
|
||||
int nc_server_config_util_read_certificate(const char *cert_path, char **cert);
|
||||
|
||||
/**
|
||||
* @brief Converts private key format to its associated identityref value.
|
||||
*
|
||||
* @param[in] format Private key format.
|
||||
*
|
||||
* @return Identityref on success, NULL on failure.
|
||||
*/
|
||||
const char *nc_server_config_util_privkey_format_to_identityref(enum nc_privkey_format format);
|
||||
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
/**
|
||||
* @brief Creates YANG data nodes in a path and gives the final node a value.
|
||||
*
|
||||
* @param[in] ctx libyang context.
|
||||
* @param[in, out] tree The YANG data tree where the insertion will happen. On success
|
||||
* this is set to the top level container.
|
||||
* @param[in] value Value assigned to the final node in the path.
|
||||
* @param[in] path_fmt Format of the path.
|
||||
* @param[in] ... Parameters for the path format, essentially representing the lists' keys.
|
||||
* @return 0 on success, 1 otherwise.
|
||||
*/
|
||||
int nc_server_config_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...);
|
||||
|
||||
/**
|
||||
* @brief Creates a YANG data node by appending it to a specified parent node.
|
||||
*
|
||||
* @param[in] ctx libyang context.
|
||||
* @param[in] parent_path Path to the parent node.
|
||||
* @param[in] child_name Name of the parent's child node to be created.
|
||||
* @param[in] value Value given to the child node.
|
||||
* @param[out] tree YANG data tree where the insertion will happen. On success
|
||||
* this is set to the top level container.
|
||||
* @return 0 on success, 1 otherwise.
|
||||
*/
|
||||
int nc_server_config_append(const struct ly_ctx *ctx, const char *parent_path, const char *child_name,
|
||||
const char *value, struct lyd_node **tree);
|
||||
|
||||
/**
|
||||
* @brief Deletes a subtree from the YANG data.
|
||||
*
|
||||
* @param tree YANG data from which the subtree will be deleted.
|
||||
* @param[in] path_fmt Format of the path. The last node will be the top level node of the deleted tree.
|
||||
* @param[in] ... Parameters for the path format, essentially representing the lists' keys.
|
||||
* @return 0 on success, non-zero otherwise.
|
||||
*/
|
||||
int nc_server_config_delete(struct lyd_node **tree, const char *path_fmt, ...);
|
||||
|
||||
/**
|
||||
* @brief Deletes a subtree from the YANG data, but doesn't return an error if the node doesn't exist.
|
||||
*
|
||||
* @param tree YANG data from which the subtree will be deleted.
|
||||
* @param[in] path_fmt Format of the path. The last node will be the top level node of the deleted tree.
|
||||
* @param[in] ... Parameters for the path format, essentially representing the lists' keys.
|
||||
* @return 0 on success, non-zero otherwise.
|
||||
*/
|
||||
int nc_server_config_check_delete(struct lyd_node **tree, const char *path_fmt, ...);
|
||||
|
||||
#endif /* NC_CONFIG_NEW_H_ */
|
798
src/server_config_util_ssh.c
Normal file
798
src/server_config_util_ssh.c
Normal file
|
@ -0,0 +1,798 @@
|
|||
/**
|
||||
* @file server_config_util_ssh.c
|
||||
* @author Roman Janota <janota@cesnet.cz>
|
||||
* @brief libnetconf2 server SSH configuration utilities
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2023 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "server_config_util.h"
|
||||
|
||||
#include <crypt.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "config.h"
|
||||
#include "log_p.h"
|
||||
#include "server_config.h"
|
||||
#include "session_p.h"
|
||||
|
||||
static int
|
||||
_nc_server_config_add_ssh_hostkey(const struct ly_ctx *ctx, const char *tree_path,
|
||||
const char *privkey_path, const char *pubkey_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *pubkey = NULL, *privkey = NULL;
|
||||
enum nc_privkey_format privkey_type;
|
||||
const char *privkey_format, *pubkey_format = "ietf-crypto-types:ssh-public-key-format";
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, tree_path, privkey_path, config, 1);
|
||||
|
||||
/* get the keys as a string from the given files */
|
||||
ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_SSH, &privkey,
|
||||
&privkey_type, &pubkey);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* get privkey identityref value */
|
||||
privkey_format = nc_server_config_util_privkey_format_to_identityref(privkey_type);
|
||||
if (!privkey_format) {
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "inline-definition/public-key-format", pubkey_format, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "inline-definition/public-key", pubkey, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "inline-definition/private-key-format", privkey_format, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "inline-definition/cleartext-private-key", privkey, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete keystore choice nodes if present */
|
||||
ret = nc_server_config_check_delete(config, "%s/central-keystore-reference", tree_path);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(privkey);
|
||||
free(pubkey);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ssh_hostkey(const struct ly_ctx *ctx, const char *endpt_name, const char *hostkey_name,
|
||||
const char *privkey_path, const char *pubkey_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, hostkey_name, privkey_path, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/"
|
||||
"server-identity/host-key[name='%s']/public-key", endpt_name, hostkey_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_ssh_hostkey(ctx, path, privkey_path, pubkey_path, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_ssh_hostkey(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name,
|
||||
const char *hostkey_name, const char *privkey_path, const char *pubkey_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, hostkey_name, privkey_path, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/"
|
||||
"host-key[name='%s']/public-key", client_name, endpt_name, hostkey_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_ssh_hostkey(ctx, path, privkey_path, pubkey_path, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ssh_hostkey(const struct ly_ctx *ctx, const char *endpt_name, const char *hostkey_name,
|
||||
struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, config, 1);
|
||||
|
||||
if (hostkey_name) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/"
|
||||
"ssh/ssh-server-parameters/server-identity/host-key[name='%s']", endpt_name, hostkey_name);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/"
|
||||
"ssh/ssh-server-parameters/server-identity/host-key", endpt_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_ssh_hostkey(const char *client_name, const char *endpt_name,
|
||||
const char *hostkey_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1);
|
||||
|
||||
if (hostkey_name) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/"
|
||||
"host-key[name='%s']", client_name, endpt_name, hostkey_name);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/"
|
||||
"host-key", client_name, endpt_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ssh_keystore_ref(const struct ly_ctx *ctx, const char *endpt_name, const char *hostkey_name,
|
||||
const char *keystore_reference, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, hostkey_name, keystore_reference, config, 1);
|
||||
|
||||
ret = nc_server_config_create(ctx, config, keystore_reference, "/ietf-netconf-server:netconf-server/listen/"
|
||||
"endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/host-key[name='%s']/public-key/"
|
||||
"central-keystore-reference", endpt_name, hostkey_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete inline definition nodes if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/"
|
||||
"endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/host-key[name='%s']/public-key/"
|
||||
"inline-definition", endpt_name, hostkey_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_ssh_keystore_ref(const struct ly_ctx *ctx, const char *client_name,
|
||||
const char *endpt_name, const char *hostkey_name, const char *keystore_reference, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, hostkey_name, keystore_reference, config, 1);
|
||||
|
||||
ret = nc_server_config_create(ctx, config, keystore_reference, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/"
|
||||
"host-key[name='%s']/public-key/central-keystore-reference", client_name, endpt_name, hostkey_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete inline definition nodes if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/"
|
||||
"host-key[name='%s']/public-key/inline-definition", client_name, endpt_name, hostkey_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ssh_keystore_ref(const char *endpt_name, const char *hostkey_name,
|
||||
struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/"
|
||||
"endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/host-key[name='%s']/public-key/"
|
||||
"central-keystore-reference", endpt_name, hostkey_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_ssh_keystore_ref(const char *client_name, const char *endpt_name,
|
||||
const char *hostkey_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, hostkey_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/"
|
||||
"host-key[name='%s']/public-key/central-keystore-reference", client_name, endpt_name, hostkey_name);
|
||||
}
|
||||
|
||||
static int
|
||||
_nc_server_config_add_ssh_user_pubkey(const struct ly_ctx *ctx, const char *tree_path, const char *pubkey_path,
|
||||
struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *pubkey = NULL;
|
||||
const char *pubkey_format = "ietf-crypto-types:ssh-public-key-format";
|
||||
|
||||
/* get pubkey data */
|
||||
ret = nc_server_config_util_get_ssh_pubkey_file(pubkey_path, &pubkey);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "public-key-format", pubkey_format, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "public-key", pubkey, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(pubkey);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ssh_user_pubkey(const struct ly_ctx *ctx, const char *endpt_name,
|
||||
const char *user_name, const char *pubkey_name, const char *pubkey_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, pubkey_name, pubkey_path, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/inline-definition/"
|
||||
"public-key[name='%s']", endpt_name, user_name, pubkey_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_ssh_user_pubkey(ctx, path, pubkey_path, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete truststore reference if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/central-truststore-reference",
|
||||
endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete use system auth if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/"
|
||||
"libnetconf2-netconf-server:use-system-keys", endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_ssh_user_pubkey(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name,
|
||||
const char *user_name, const char *pubkey_name, const char *pubkey_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, pubkey_name, pubkey_path, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/"
|
||||
"endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
|
||||
"users/user[name='%s']/public-keys/inline-definition/public-key[name='%s']", client_name,
|
||||
endpt_name, user_name, pubkey_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_ssh_user_pubkey(ctx, path, pubkey_path, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete truststore reference if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/"
|
||||
"public-keys/central-truststore-reference", client_name, endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete use system auth if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/"
|
||||
"libnetconf2-netconf-server:use-system-keys", client_name, endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ssh_user_pubkey(const char *endpt_name, const char *user_name,
|
||||
const char *pubkey_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, user_name, config, 1);
|
||||
|
||||
if (pubkey_name) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/inline-definition/"
|
||||
"public-key[name='%s']", endpt_name, user_name, pubkey_name);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/inline-definition/"
|
||||
"public-key", endpt_name, user_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_ssh_user_pubkey(const char *client_name, const char *endpt_name,
|
||||
const char *user_name, const char *pubkey_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, user_name, config, 1);
|
||||
|
||||
if (pubkey_name) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
|
||||
"users/user[name='%s']/public-keys/inline-definition/public-key[name='%s']", client_name,
|
||||
endpt_name, user_name, pubkey_name);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
|
||||
"users/user[name='%s']/public-keys/inline-definition/public-key", client_name,
|
||||
endpt_name, user_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ssh_user_authkey(const struct ly_ctx *ctx, const char *endpt_name,
|
||||
const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/"
|
||||
"client-authentication/users/user[name='%s']/public-keys", endpt_name, user_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = nc_server_config_append(ctx, path, "libnetconf2-netconf-server:use-system-keys", NULL, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete inline definition nodes if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/inline-definition",
|
||||
endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete truststore reference if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/central-truststore-reference",
|
||||
endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_ssh_user_authkey(const struct ly_ctx *ctx, const char *client_name,
|
||||
const char *endpt_name, const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/"
|
||||
"endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users"
|
||||
"/user[name='%s']/public-keys", client_name, endpt_name, user_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = nc_server_config_append(ctx, path, "libnetconf2-netconf-server:use-system-keys", NULL, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete inline definition nodes if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/"
|
||||
"public-keys/inline-definition", client_name, endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete truststore reference if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/"
|
||||
"public-keys/central-truststore-reference", client_name, endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ssh_user_authkey(const char *endpt_name, const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, user_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/"
|
||||
"public-keys/libnetconf2-netconf-server:use-system-keys", endpt_name, user_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_ch_del_ssh_user_authkey(const char *client_name, const char *endpt_name,
|
||||
const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, user_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/"
|
||||
"endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/"
|
||||
"public-keys/libnetconf2-netconf-server:use-system-keys", client_name, endpt_name, user_name);
|
||||
}
|
||||
|
||||
static int
|
||||
_nc_server_config_add_ssh_user_password(const struct ly_ctx *ctx, const char *tree_path,
|
||||
const char *password, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *hashed_pw = NULL;
|
||||
const char *salt = "$6$idsizuippipk$";
|
||||
struct crypt_data cdata = {0};
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, tree_path, password, config, 1);
|
||||
|
||||
hashed_pw = crypt_r(password, salt, &cdata);
|
||||
if (!hashed_pw) {
|
||||
ERR(NULL, "Hashing password failed (%s).", strerror(errno));
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "password", hashed_pw, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ssh_user_password(const struct ly_ctx *ctx, const char *endpt_name,
|
||||
const char *user_name, const char *password, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, password, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/"
|
||||
"client-authentication/users/user[name='%s']", endpt_name, user_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_ssh_user_password(ctx, path, password, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_ssh_user_password(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name,
|
||||
const char *user_name, const char *password, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, password, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/"
|
||||
"endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
|
||||
"users/user[name='%s']", client_name, endpt_name, user_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_ssh_user_password(ctx, path, password, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ssh_user_password(const char *endpt_name, const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, user_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/password", endpt_name, user_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_ssh_user_password(const char *client_name, const char *endpt_name,
|
||||
const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, user_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
|
||||
"users/user[name='%s']/password", client_name, endpt_name, user_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ssh_user_interactive(const struct ly_ctx *ctx, const char *endpt_name,
|
||||
const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/"
|
||||
"client-authentication/users/user[name='%s']/libnetconf2-netconf-server:keyboard-interactive", endpt_name, user_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = nc_server_config_append(ctx, path, "use-system-auth", NULL, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_ssh_user_interactive(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name,
|
||||
const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/"
|
||||
"endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/"
|
||||
"libnetconf2-netconf-server:keyboard-interactive", client_name, endpt_name, user_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = nc_server_config_append(ctx, path, "use-system-auth", NULL, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ssh_user_interactive(const char *endpt_name, const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, user_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/"
|
||||
"libnetconf2-netconf-server:keyboard-interactive", endpt_name, user_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_ssh_user_interactive(const char *client_name, const char *endpt_name,
|
||||
const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, user_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/"
|
||||
"endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/"
|
||||
"libnetconf2-netconf-server:keyboard-interactive", client_name, endpt_name, user_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ssh_user(const char *endpt_name,
|
||||
const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, config, 1);
|
||||
|
||||
if (user_name) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']", endpt_name, user_name);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user", endpt_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_ssh_user(const char *client_name, const char *endpt_name,
|
||||
const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1);
|
||||
|
||||
if (user_name) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/"
|
||||
"endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']", client_name,
|
||||
endpt_name, user_name);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/"
|
||||
"endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user", client_name, endpt_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ssh_endpoint_client_ref(const struct ly_ctx *ctx, const char *endpt_name,
|
||||
const char *referenced_endpt, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, referenced_endpt, config, 1);
|
||||
|
||||
return nc_server_config_create(ctx, config, referenced_endpt, "/ietf-netconf-server:netconf-server/listen/endpoints/"
|
||||
"endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/libnetconf2-netconf-server:endpoint-reference",
|
||||
endpt_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ssh_endpoint_client_ref(const char *endpt_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/"
|
||||
"ssh/ssh-server-parameters/client-authentication/libnetconf2-netconf-server:endpoint-reference", endpt_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ssh_truststore_ref(const struct ly_ctx *ctx, const char *endpt_name, const char *user_name,
|
||||
const char *truststore_reference, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, truststore_reference, config, 1);
|
||||
|
||||
ret = nc_server_config_create(ctx, config, truststore_reference, "/ietf-netconf-server:netconf-server/listen/"
|
||||
"endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/"
|
||||
"central-truststore-reference", endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete inline definition nodes if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/inline-definition",
|
||||
endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete use system auth if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/"
|
||||
"libnetconf2-netconf-server:use-system-keys", endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_ssh_truststore_ref(const struct ly_ctx *ctx, const char *client_name,
|
||||
const char *endpt_name, const char *user_name, const char *truststore_reference, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, truststore_reference, config, 1);
|
||||
|
||||
ret = nc_server_config_create(ctx, config, truststore_reference, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
|
||||
"users/user[name='%s']/public-keys/central-truststore-reference", client_name, endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete inline definition nodes if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/"
|
||||
"public-keys/inline-definition", client_name, endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete use system auth if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/"
|
||||
"ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/"
|
||||
"libnetconf2-netconf-server:use-system-keys", client_name, endpt_name, user_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ssh_truststore_ref(const char *endpt_name, const char *user_name,
|
||||
struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, user_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/"
|
||||
"endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/"
|
||||
"central-truststore-reference", endpt_name, user_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_ssh_truststore_ref(const char *client_name, const char *endpt_name,
|
||||
const char *user_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, user_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
|
||||
"users/user[name='%s']/public-keys/central-truststore-reference", client_name, endpt_name, user_name);
|
||||
}
|
790
src/server_config_util_tls.c
Normal file
790
src/server_config_util_tls.c
Normal file
|
@ -0,0 +1,790 @@
|
|||
/**
|
||||
* @file server_config_util_tls.c
|
||||
* @author Roman Janota <janota@cesnet.cz>
|
||||
* @brief libnetconf2 server TLS configuration utilities
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2023 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "server_config_util.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "config.h"
|
||||
#include "log_p.h"
|
||||
#include "server_config.h"
|
||||
#include "session.h"
|
||||
#include "session_p.h"
|
||||
|
||||
static int
|
||||
_nc_server_config_add_tls_server_cert(const struct ly_ctx *ctx, const char *tree_path, const char *privkey_path,
|
||||
const char *pubkey_path, const char *cert_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *privkey = NULL, *pubkey = NULL, *cert = NULL;
|
||||
enum nc_privkey_format privkey_type;
|
||||
const char *privkey_format, *pubkey_format = "ietf-crypto-types:subject-public-key-info-format";
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, tree_path, privkey_path, cert_path, config, 1);
|
||||
|
||||
/* get the keys as a string from the given files */
|
||||
ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_X509, &privkey, &privkey_type, &pubkey);
|
||||
if (ret) {
|
||||
ERR(NULL, "Getting keys from file(s) failed.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* get cert data from file */
|
||||
ret = nc_server_config_util_read_certificate(cert_path, &cert);
|
||||
if (ret) {
|
||||
ERR(NULL, "Getting certificate from file \"%s\" failed.", cert_path);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* get privkey identityref value */
|
||||
privkey_format = nc_server_config_util_privkey_format_to_identityref(privkey_type);
|
||||
if (!privkey_format) {
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "inline-definition/public-key-format", pubkey_format, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "inline-definition/public-key", pubkey, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "inline-definition/private-key-format", privkey_format, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "inline-definition/cleartext-private-key", privkey, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "inline-definition/cert-data", cert, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete keystore if present */
|
||||
ret = nc_server_config_check_delete(config, "%s/central-keystore-reference", tree_path);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(privkey);
|
||||
free(pubkey);
|
||||
free(cert);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_tls_server_cert(const struct ly_ctx *ctx, const char *endpt_name, const char *privkey_path,
|
||||
const char *pubkey_path, const char *cert_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, privkey_path, cert_path, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/"
|
||||
"tls/tls-server-parameters/server-identity/certificate", endpt_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_tls_server_cert(ctx, path, privkey_path, pubkey_path,
|
||||
cert_path, config);
|
||||
if (ret) {
|
||||
ERR(NULL, "Creating new TLS server certificate YANG data failed.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_tls_server_cert(const char *endpt_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/"
|
||||
"tls/tls-server-parameters/server-identity/certificate/inline-definition", endpt_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_tls_server_cert(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name,
|
||||
const char *privkey_path, const char *pubkey_path, const char *cert_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, privkey_path, cert_path, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/server-identity/"
|
||||
"certificate", client_name, endpt_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_tls_server_cert(ctx, path, privkey_path, pubkey_path,
|
||||
cert_path, config);
|
||||
if (ret) {
|
||||
ERR(NULL, "Creating new CH TLS server certificate YANG data failed.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_tls_server_cert(const char *client_name, const char *endpt_name,
|
||||
struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/server-identity/"
|
||||
"certificate/inline-definition", client_name, endpt_name);
|
||||
}
|
||||
|
||||
static int
|
||||
_nc_server_config_add_tls_keystore_ref(const struct ly_ctx *ctx, const char *tree_path, const char *asym_key_ref,
|
||||
const char *cert_ref, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* create asymmetric key pair reference */
|
||||
ret = nc_server_config_append(ctx, tree_path, "central-keystore-reference/asymmetric-key", asym_key_ref, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* create cert reference, this cert has to belong to the asym key */
|
||||
ret = nc_server_config_append(ctx, tree_path, "central-keystore-reference/certificate", cert_ref, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete inline definition if present */
|
||||
ret = nc_server_config_check_delete(config, "%s/inline-definition", tree_path);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_tls_keystore_ref(const struct ly_ctx *ctx, const char *endpt_name, const char *asym_key_ref,
|
||||
const char *cert_ref, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, asym_key_ref, cert_ref, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/"
|
||||
"tls/tls-server-parameters/server-identity/certificate", endpt_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_tls_keystore_ref(ctx, path, asym_key_ref, cert_ref, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_tls_keystore_ref(const char *endpt_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/"
|
||||
"tls/tls-server-parameters/server-identity/certificate/central-keystore-reference", endpt_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_tls_keystore_ref(const struct ly_ctx *ctx, const char *client_name,
|
||||
const char *endpt_name, const char *asym_key_ref, const char *cert_ref, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, asym_key_ref, cert_ref, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/"
|
||||
"endpoint[name='%s']/tls/tls-server-parameters/server-identity/certificate", client_name, endpt_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_tls_keystore_ref(ctx, path, asym_key_ref, cert_ref, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_tls_keystore_ref(const char *client_name, const char *endpt_name,
|
||||
struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/tls-server-parameters/server-identity/certificate/"
|
||||
"central-keystore-reference", client_name, endpt_name);
|
||||
}
|
||||
|
||||
static int
|
||||
_nc_server_config_add_tls_client_cert(const struct ly_ctx *ctx, const char *tree_path,
|
||||
const char *cert_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *cert = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, tree_path, cert_path, config, 1);
|
||||
|
||||
ret = nc_server_config_util_read_certificate(cert_path, &cert);
|
||||
if (ret) {
|
||||
ERR(NULL, "Getting certificate from file \"%s\" failed.", cert_path);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "cert-data", cert, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(cert);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_tls_client_cert(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name,
|
||||
const char *cert_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_name, cert_path, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/tls-server-parameters/"
|
||||
"client-authentication/ee-certs/inline-definition/certificate[name='%s']", endpt_name, cert_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_tls_client_cert(ctx, path, cert_path, config);
|
||||
if (ret) {
|
||||
ERR(NULL, "Creating new TLS client certificate YANG data failed.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete truststore if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/"
|
||||
"tls/tls-server-parameters/client-authentication/ee-certs/central-truststore-reference", endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_tls_client_cert(const char *endpt_name, const char *cert_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, config, 1);
|
||||
|
||||
if (cert_name) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"tls-server-parameters/client-authentication/ee-certs/inline-definition/"
|
||||
"certificate[name='%s']", endpt_name, cert_name);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"tls-server-parameters/client-authentication/ee-certs/inline-definition/"
|
||||
"certificate", endpt_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_tls_client_cert(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name,
|
||||
const char *cert_name, const char *cert_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, cert_name, cert_path, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ee-certs/"
|
||||
"inline-definition/certificate[name='%s']", client_name, endpt_name, cert_name) == -1;
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_tls_client_cert(ctx, path, cert_path, config);
|
||||
if (ret) {
|
||||
ERR(NULL, "Creating new CH TLS client certificate YANG data failed.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete truststore if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/tls-server-parameters/"
|
||||
"client-authentication/ee-certs/central-truststore-reference", client_name, endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_tls_client_cert(const char *client_name, const char *endpt_name,
|
||||
const char *cert_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1);
|
||||
|
||||
if (cert_name) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ee-certs/"
|
||||
"inline-definition/certificate[name='%s']", client_name, endpt_name, cert_name);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ee-certs/"
|
||||
"inline-definition/certificate", client_name, endpt_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_tls_client_cert_truststore_ref(const struct ly_ctx *ctx, const char *endpt_name,
|
||||
const char *cert_bag_ref, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_bag_ref, config, 1);
|
||||
|
||||
ret = nc_server_config_create(ctx, config, cert_bag_ref, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"tls-server-parameters/client-authentication/ee-certs/central-truststore-reference", endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete inline definition if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"tls-server-parameters/client-authentication/ee-certs/inline-definition", endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_tls_client_cert_truststore_ref(const char *endpt_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"tls-server-parameters/client-authentication/ee-certs/central-truststore-reference", endpt_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_tls_client_cert_truststore_ref(const struct ly_ctx *ctx, const char *client_name,
|
||||
const char *endpt_name, const char *cert_bag_ref, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, cert_bag_ref, config, 1);
|
||||
|
||||
ret = nc_server_config_create(ctx, config, cert_bag_ref, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/"
|
||||
"client-authentication/ee-certs/central-truststore-reference", client_name, endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete inline definition if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/"
|
||||
"tls-server-parameters/client-authentication/ee-certs/inline-definition", client_name, endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_tls_client_cert_truststore_ref(const char *client_name, const char *endpt_name,
|
||||
struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/"
|
||||
"client-authentication/ee-certs/central-truststore-reference", client_name, endpt_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_tls_ca_cert(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name,
|
||||
const char *cert_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_name, cert_path, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/tls-server-parameters/"
|
||||
"client-authentication/ca-certs/inline-definition/certificate[name='%s']", endpt_name, cert_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_tls_client_cert(ctx, path, cert_path, config);
|
||||
if (ret) {
|
||||
ERR(NULL, "Creating new TLS client certificate authority YANG data failed.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete truststore if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/"
|
||||
"tls/tls-server-parameters/client-authentication/ca-certs/central-truststore-reference", endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_tls_ca_cert(const char *endpt_name, const char *cert_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, config, 1);
|
||||
|
||||
if (cert_name) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"tls-server-parameters/client-authentication/ca-certs/inline-definition/"
|
||||
"certificate[name='%s']", endpt_name, cert_name);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"tls-server-parameters/client-authentication/ca-certs/inline-definition/"
|
||||
"certificate", endpt_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_tls_ca_cert(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name,
|
||||
const char *cert_name, const char *cert_path, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, cert_name, cert_path, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ca-certs/"
|
||||
"inline-definition/certificate[name='%s']", client_name, endpt_name, cert_name);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_tls_client_cert(ctx, path, cert_path, config);
|
||||
if (ret) {
|
||||
ERR(NULL, "Creating new CH TLS client certificate authority YANG data failed.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete truststore if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/tls-server-parameters/"
|
||||
"client-authentication/ca-certs/central-truststore-reference", client_name, endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_tls_ca_cert(const char *client_name, const char *endpt_name,
|
||||
const char *cert_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1);
|
||||
|
||||
if (cert_name) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ca-certs/"
|
||||
"inline-definition/certificate[name='%s']", client_name, endpt_name, cert_name);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ca-certs/"
|
||||
"inline-definition/certificate", client_name, endpt_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_tls_ca_cert_truststore_ref(const struct ly_ctx *ctx, const char *endpt_name,
|
||||
const char *cert_bag_ref, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_bag_ref, config, 1);
|
||||
|
||||
ret = nc_server_config_create(ctx, config, cert_bag_ref, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"tls-server-parameters/client-authentication/ca-certs/central-truststore-reference", endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete inline definition if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"tls-server-parameters/client-authentication/ca-certs/inline-definition", endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_tls_ca_cert_truststore_ref(const char *endpt_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"tls-server-parameters/client-authentication/ca-certs/central-truststore-reference", endpt_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_tls_ca_cert_truststore_ref(const struct ly_ctx *ctx, const char *client_name,
|
||||
const char *endpt_name, const char *cert_bag_ref, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, cert_bag_ref, config, 1);
|
||||
|
||||
ret = nc_server_config_create(ctx, config, cert_bag_ref, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/"
|
||||
"client-authentication/ca-certs/central-truststore-reference", client_name, endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* delete inline definition if present */
|
||||
ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/"
|
||||
"client-authentication/ca-certs/inline-definition", client_name, endpt_name);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_tls_ca_cert_truststore_ref(const char *client_name, const char *endpt_name,
|
||||
struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
|
||||
"netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/"
|
||||
"client-authentication/ca-certs/central-truststore-reference", client_name, endpt_name);
|
||||
}
|
||||
|
||||
static const char *
|
||||
nc_server_config_tls_maptype2str(NC_TLS_CTN_MAPTYPE map_type)
|
||||
{
|
||||
switch (map_type) {
|
||||
case NC_TLS_CTN_SPECIFIED:
|
||||
return "ietf-x509-cert-to-name:specified";
|
||||
case NC_TLS_CTN_SAN_RFC822_NAME:
|
||||
return "ietf-x509-cert-to-name:san-rfc822-name";
|
||||
case NC_TLS_CTN_SAN_DNS_NAME:
|
||||
return "ietf-x509-cert-to-name:san-dns-name";
|
||||
case NC_TLS_CTN_SAN_IP_ADDRESS:
|
||||
return "ietf-x509-cert-to-name:san-ip-address";
|
||||
case NC_TLS_CTN_SAN_ANY:
|
||||
return "ietf-x509-cert-to-name:san-any";
|
||||
case NC_TLS_CTN_COMMON_NAME:
|
||||
return "ietf-x509-cert-to-name:common-name";
|
||||
case NC_TLS_CTN_UNKNOWN:
|
||||
default:
|
||||
ERR(NULL, "Unknown CTN mapping type.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
_nc_server_config_add_tls_ctn(const struct ly_ctx *ctx, const char *tree_path, const char *fingerprint,
|
||||
NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
const char *map;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, tree_path, name, config, 1);
|
||||
|
||||
if (fingerprint) {
|
||||
/* optional */
|
||||
ret = nc_server_config_append(ctx, tree_path, "fingerprint", fingerprint, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* get map str */
|
||||
map = nc_server_config_tls_maptype2str(map_type);
|
||||
if (!map) {
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "map-type", map, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = nc_server_config_append(ctx, tree_path, "name", name, config);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_tls_ctn(const struct ly_ctx *ctx, const char *endpt_name, uint32_t id, const char *fingerprint,
|
||||
NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, id, name, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/netconf-server-parameters/"
|
||||
"client-identity-mappings/cert-to-name[id='%" PRIu32 "']", endpt_name, id);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_tls_ctn(ctx, path, fingerprint, map_type, name, config);
|
||||
if (ret) {
|
||||
ERR(NULL, "Creating new TLS cert-to-name YANG data failed.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_tls_ctn(const char *endpt_name, uint32_t id, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, config, 1);
|
||||
|
||||
if (id) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"netconf-server-parameters/client-identity-mappings/cert-to-name[id='%" PRIu32 "']", endpt_name, id);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/"
|
||||
"netconf-server-parameters/client-identity-mappings/cert-to-name", endpt_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_ch_tls_ctn(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name,
|
||||
uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config)
|
||||
{
|
||||
int ret = 0;
|
||||
char *path = NULL;
|
||||
|
||||
NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, id, name, config, 1);
|
||||
|
||||
ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/netconf-server-parameters/client-identity-mappings/"
|
||||
"cert-to-name[id='%" PRIu32 "']", client_name, endpt_name, id);
|
||||
NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
|
||||
|
||||
ret = _nc_server_config_add_tls_ctn(ctx, path, fingerprint, map_type, name, config);
|
||||
if (ret) {
|
||||
ERR(NULL, "Creating new CH TLS cert-to-name YANG data failed.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_ch_tls_ctn(const char *client_name, const char *endpt_name,
|
||||
uint32_t id, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1);
|
||||
|
||||
if (id) {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/netconf-server-parameters/client-identity-mappings/"
|
||||
"cert-to-name[id='%u']", client_name, endpt_name, id);
|
||||
} else {
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
|
||||
"endpoints/endpoint[name='%s']/tls/netconf-server-parameters/client-identity-mappings/"
|
||||
"cert-to-name", client_name, endpt_name);
|
||||
}
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_add_tls_endpoint_client_ref(const struct ly_ctx *ctx, const char *endpt_name, const char *referenced_endpt, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, referenced_endpt, config, 1);
|
||||
|
||||
return nc_server_config_create(ctx, config, referenced_endpt, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/tls-server-parameters/"
|
||||
"client-authentication/libnetconf2-netconf-server:endpoint-reference", endpt_name);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_server_config_del_tls_endpoint_client_ref(const char *endpt_name, struct lyd_node **config)
|
||||
{
|
||||
NC_CHECK_ARG_RET(NULL, endpt_name, config, 1);
|
||||
|
||||
return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/tls-server-parameters/"
|
||||
"client-authentication/libnetconf2-netconf-server:endpoint-reference", endpt_name);
|
||||
}
|
1319
src/session.c
1319
src/session.c
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,11 @@
|
|||
/**
|
||||
* \file session.h
|
||||
* \author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* \brief libnetconf2 session manipulation
|
||||
* @file session.h
|
||||
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 session manipulation
|
||||
*
|
||||
* Copyright (c) 2015 CESNET, z.s.p.o.
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
|
@ -21,7 +23,7 @@ extern "C" {
|
|||
|
||||
#include "netconf.h"
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/**
|
||||
* @brief Enumeration of NETCONF SSH authentication methods
|
||||
|
@ -32,9 +34,16 @@ typedef enum {
|
|||
NC_SSH_AUTH_INTERACTIVE = 0x04 /**< interactive SSH authentication */
|
||||
} NC_SSH_AUTH_TYPE;
|
||||
|
||||
#endif /* NC_ENABLED_SSH */
|
||||
|
||||
#ifdef NC_ENABLED_TLS
|
||||
/**
|
||||
* @brief Enumeration of host key checking and known_hosts entry adding modes
|
||||
*/
|
||||
typedef enum {
|
||||
NC_SSH_KNOWNHOSTS_ASK = 0, /**< add a known_hosts entry, but with a prompt */
|
||||
NC_SSH_KNOWNHOSTS_STRICT, /**< do not add a known_hosts entry and the server's host key must be present in the configured known_hosts file */
|
||||
NC_SSH_KNOWNHOSTS_ACCEPT_NEW, /**< add a known_hosts entry without a prompt */
|
||||
NC_SSH_KNOWNHOSTS_ACCEPT, /**< add a known_hosts entry without a prompt and allow connections to servers which changed their host key */
|
||||
NC_SSH_KNOWNHOSTS_SKIP /**< do not add a known_hosts entry and skip all host key checks */
|
||||
} NC_SSH_KNOWNHOSTS_MODE;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of cert-to-name mapping types
|
||||
|
@ -49,7 +58,17 @@ typedef enum {
|
|||
NC_TLS_CTN_COMMON_NAME /**< common name as username */
|
||||
} NC_TLS_CTN_MAPTYPE;
|
||||
|
||||
#endif /* NC_ENABLED_TLS */
|
||||
/**
|
||||
* @brief Enumeration of TLS versions.
|
||||
*/
|
||||
typedef enum {
|
||||
NC_TLS_VERSION_10 = 1, /**< TLS1.0 */
|
||||
NC_TLS_VERSION_11 = 2, /**< TLS1.1 */
|
||||
NC_TLS_VERSION_12 = 4, /**< TLS1.2 */
|
||||
NC_TLS_VERSION_13 = 8 /**< TLS1.3 */
|
||||
} NC_TLS_VERSION;
|
||||
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
/**
|
||||
* @brief Enumeration of possible session statuses
|
||||
|
@ -70,12 +89,11 @@ typedef enum {
|
|||
NC_TI_FD, /**< file descriptors - use standard input/output, transport protocol is implemented
|
||||
outside the current application */
|
||||
NC_TI_UNIX, /**< unix socket */
|
||||
#ifdef NC_ENABLED_SSH
|
||||
NC_TI_LIBSSH, /**< libssh - use libssh library, only for NETCONF over SSH transport */
|
||||
#endif
|
||||
#ifdef NC_ENABLED_TLS
|
||||
NC_TI_OPENSSL /**< OpenSSL - use OpenSSL library, only for NETCONF over TLS transport */
|
||||
#endif
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
NC_TI_SSH, /**< SSH - use libssh library, only for NETCONF over SSH transport */
|
||||
|
||||
NC_TI_TLS /**< TLS - use either OpenSSL or MbedTLS library, only for NETCONF over TLS transport */
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
} NC_TRANSPORT_IMPL;
|
||||
|
||||
/**
|
||||
|
@ -96,16 +114,6 @@ typedef enum {
|
|||
NC_CH_RANDOM
|
||||
} NC_CH_START_WITH;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of SSH key types.
|
||||
*/
|
||||
typedef enum {
|
||||
NC_SSH_KEY_UNKNOWN = 0,
|
||||
NC_SSH_KEY_DSA,
|
||||
NC_SSH_KEY_RSA,
|
||||
NC_SSH_KEY_ECDSA
|
||||
} NC_SSH_KEY_TYPE;
|
||||
|
||||
/**
|
||||
* @brief NETCONF session object
|
||||
*/
|
||||
|
@ -183,6 +191,18 @@ const char *nc_session_get_host(const struct nc_session *session);
|
|||
*/
|
||||
uint16_t nc_session_get_port(const struct nc_session *session);
|
||||
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/**
|
||||
* @brief Get the SSH banner sent by the peer.
|
||||
*
|
||||
* @param[in] session Session to get the banner from.
|
||||
* @return SSH banner on success, NULL on error.
|
||||
*/
|
||||
const char *nc_session_ssh_get_banner(const struct nc_session *session);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get session path (unix socket only).
|
||||
*
|
||||
|
@ -197,7 +217,7 @@ const char *nc_session_get_path(const struct nc_session *session);
|
|||
* @param[in] session Session to get the information from.
|
||||
* @return Session context.
|
||||
*/
|
||||
struct ly_ctx *nc_session_get_ctx(const struct nc_session *session);
|
||||
const struct ly_ctx *nc_session_get_ctx(const struct nc_session *session);
|
||||
|
||||
/**
|
||||
* @brief Assign arbitrary data to a session.
|
||||
|
@ -215,6 +235,14 @@ void nc_session_set_data(struct nc_session *session, void *data);
|
|||
*/
|
||||
void *nc_session_get_data(const struct nc_session *session);
|
||||
|
||||
/**
|
||||
* @brief Learn whether a session was created using Call Home or not.
|
||||
*
|
||||
* @param[in] session Session to get the information from.
|
||||
* @return 0 if a standard session, non-zero if a Call Home session.
|
||||
*/
|
||||
int nc_session_is_callhome(const struct nc_session *session);
|
||||
|
||||
/**
|
||||
* @brief Free the NETCONF session object.
|
||||
*
|
||||
|
@ -223,20 +251,6 @@ void *nc_session_get_data(const struct nc_session *session);
|
|||
*/
|
||||
void nc_session_free(struct nc_session *session, void (*data_free)(void *));
|
||||
|
||||
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
|
||||
|
||||
/**
|
||||
* @brief Free all the dynamically allocated thread-specific libssl/libcrypto
|
||||
* resources.
|
||||
*
|
||||
* This function should be called only if init (nc_client_init(), respectively nc_server_init()) was called.
|
||||
* Call it in every thread your application creates just before the thread exits. In the last thread
|
||||
* (usually the main one) call nc_client_destroy(), respectively nc_server_destroy().
|
||||
*/
|
||||
void nc_thread_destroy(void);
|
||||
|
||||
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
1923
src/session_client.c
1923
src/session_client.c
File diff suppressed because it is too large
Load diff
|
@ -3,6 +3,7 @@
|
|||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 session client manipulation
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
|
@ -21,18 +22,14 @@ extern "C" {
|
|||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
# include <libssh/libssh.h>
|
||||
#endif
|
||||
|
||||
#ifdef NC_ENABLED_TLS
|
||||
# include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
#include "messages_client.h"
|
||||
#include "netconf.h"
|
||||
#include "session.h"
|
||||
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
# include <libssh/libssh.h>
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
/**
|
||||
* @addtogroup client
|
||||
* @{
|
||||
|
@ -82,6 +79,26 @@ int nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data);
|
|||
*/
|
||||
ly_module_imp_clb nc_client_get_schema_callback(void **user_data);
|
||||
|
||||
/**
|
||||
* @brief Enable/disable loading of all the YANG modules supported by
|
||||
* the server when a new session is created. If disabled, it is expected
|
||||
* that users update the context themselves and load the YANG modules
|
||||
* that are planned to be used. Otherwise even basic RPCs may fail to be sent!
|
||||
*
|
||||
* @param[in] enabled Whether context autofill is enabled or disabled.
|
||||
*/
|
||||
void nc_client_set_new_session_context_autofill(int enabled);
|
||||
|
||||
/**
|
||||
* @brief Set client session context to support schema-mount, if possible.
|
||||
*
|
||||
* Performed automatically unless ::nc_client_set_new_session_context_autofill() was disabled.
|
||||
*
|
||||
* @param[in] session NETCONF session to update.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_client_set_new_session_context_schema_mount(struct nc_session *session);
|
||||
|
||||
/**
|
||||
* @brief Use the provided thread-specific client's context in the current thread.
|
||||
*
|
||||
|
@ -105,13 +122,15 @@ void nc_client_set_thread_context(void *context);
|
|||
void *nc_client_get_thread_context(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize libssh and/or libssl/libcrypto for use in the client.
|
||||
* @brief Initialize client for establishing connections.
|
||||
*
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
void nc_client_init(void);
|
||||
int nc_client_init(void);
|
||||
|
||||
/**
|
||||
* @brief Destroy all libssh and/or libssl/libcrypto dynamic memory and
|
||||
* the client options, for both SSH and TLS, and for Call Home too.
|
||||
* the client options, for both SSH and TLS, and for Call Home too.
|
||||
*/
|
||||
void nc_client_destroy(void);
|
||||
|
||||
|
@ -134,13 +153,9 @@ void nc_client_destroy(void);
|
|||
*
|
||||
* @param[in] fdin Input file descriptor for reading (clear) data from NETCONF server.
|
||||
* @param[in] fdout Output file descriptor for writing (clear) data for NETCONF server.
|
||||
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
|
||||
* (ignoring what is actually supported by the server side). If not set,
|
||||
* YANG context is created for the session using \<get-schema\> (if supported
|
||||
* by the server side) or/and by searching for YANG schemas in the searchpath
|
||||
* (see nc_client_schema_searchpath()). In every case except not providing context
|
||||
* to connect to a server supporting \<get-schema\> it is possible that
|
||||
* the session context will not include all the models supported by the server.
|
||||
* @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created.
|
||||
* Any YANG modules not present in the context and supported by the server are loaded using \<get-schema\>
|
||||
* (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()).
|
||||
* @return Created NETCONF session object or NULL in case of error.
|
||||
*/
|
||||
struct nc_session *nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx);
|
||||
|
@ -153,20 +168,16 @@ struct nc_session *nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx);
|
|||
* by sending and processing NETCONF \<hello\> messages.
|
||||
*
|
||||
* @param[in] address Path to the unix socket.
|
||||
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
|
||||
* (ignoring what is actually supported by the server side). If not set,
|
||||
* YANG context is created for the session using \<get-schema\> (if supported
|
||||
* by the server side) or/and by searching for YANG schemas in the searchpath
|
||||
* (see nc_client_schema_searchpath()). In every case except not providing context
|
||||
* to connect to a server supporting \<get-schema\> it is possible that
|
||||
* the session context will not include all the models supported by the server.
|
||||
* @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created.
|
||||
* Any YANG modules not present in the context and supported by the server are loaded using \<get-schema\>
|
||||
* (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()).
|
||||
* @return Created NETCONF session object or NULL in case of error.
|
||||
*/
|
||||
struct nc_session *nc_connect_unix(const char *address, struct ly_ctx *ctx);
|
||||
|
||||
/** @} Client Session */
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/**
|
||||
* @defgroup client_ssh Client SSH
|
||||
|
@ -177,28 +188,25 @@ struct nc_session *nc_connect_unix(const char *address, struct ly_ctx *ctx);
|
|||
*/
|
||||
|
||||
/**
|
||||
* @brief Set SSH authentication hostkey check (knownhosts) callback.
|
||||
* @brief Set the behaviour of checking the host key and adding/reading entries to/from the known_hosts file.
|
||||
*
|
||||
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
|
||||
* freeing the private data when necessary (the private data can be obtained by
|
||||
* nc_client_ssh_get_auth_hostkey_check_clb()).
|
||||
* The default mode is ::NC_SSH_KNOWNHOSTS_ASK.
|
||||
*
|
||||
* @param[in] auth_hostkey_check Function to call, returns 0 on success, non-zero in error.
|
||||
* If NULL, the default callback is set.
|
||||
* @param[in] priv Optional private data to be passed to the callback function.
|
||||
* @param[in] mode Server host key checking mode.
|
||||
*/
|
||||
void nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
|
||||
void *priv);
|
||||
void nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode);
|
||||
|
||||
/**
|
||||
* @brief Get currently set SSH authentication hostkey check (knownhosts) callback and its private data previously set
|
||||
* by nc_client_ssh_set_auth_hostkey_check_clb().
|
||||
* @brief Set the path to the known_hosts file.
|
||||
*
|
||||
* @param[out] auth_hostkey_check Currently set callback, NULL in case of the default callback.
|
||||
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
|
||||
* Repetetive calling replaces the value. If the given file doesn't exist and the process has sufficient
|
||||
* rights, it gets created whenever the file is needed, otherwise an error occurs. If NULL is passed or the
|
||||
* path isn't set, the default known_hosts file will be used.
|
||||
*
|
||||
* @param[in] path Path to the known_hosts file.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
void nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
|
||||
void **priv);
|
||||
int nc_client_ssh_set_knownhosts_path(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Set SSH password authentication callback.
|
||||
|
@ -354,15 +362,11 @@ const char *nc_client_ssh_get_username(void);
|
|||
* they are supposed to use nc_connect_libssh().
|
||||
*
|
||||
* @param[in] host Hostname or address (both Ipv4 and IPv6 are accepted) of the target server.
|
||||
* 'localhost' is used by default if NULL is specified.
|
||||
* 'localhost' is used by default if NULL is specified.
|
||||
* @param[in] port Port number of the target server. Default value 830 is used if 0 is specified.
|
||||
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
|
||||
* (ignoring what is actually supported by the server side). If not set,
|
||||
* YANG context is created for the session using \<get-schema\> (if supported
|
||||
* by the server side) or/and by searching for YANG schemas in the searchpath
|
||||
* (see nc_client_schema_searchpath()). In every case except not providing context
|
||||
* to connect to a server supporting \<get-schema\> it is possible that
|
||||
* the session context will not include all the models supported by the server.
|
||||
* @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created.
|
||||
* Any YANG modules not present in the context and supported by the server are loaded using \<get-schema\>
|
||||
* (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()).
|
||||
* @return Created NETCONF session object or NULL on error.
|
||||
*/
|
||||
struct nc_session *nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx);
|
||||
|
@ -375,15 +379,11 @@ struct nc_session *nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx
|
|||
* set and connected only the host and the username must be set/is detected. Or the @p ssh_session
|
||||
* can already be authenticated in which case it is used directly.
|
||||
*
|
||||
* @param[in] ssh_session libssh structure representing SSH session object. After passing it
|
||||
* to libnetconf2 this way, it is fully managed by it (including freeing!).
|
||||
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
|
||||
* (ignoring what is actually supported by the server side). If not set,
|
||||
* YANG context is created for the session using \<get-schema\> (if supported
|
||||
* by the server side) or/and by searching for YANG schemas in the searchpath
|
||||
* (see nc_client_schema_searchpath()). In every case except not providing context
|
||||
* to connect to a server supporting \<get-schema\> it is possible that
|
||||
* the session context will not include all the models supported by the server.
|
||||
* @param[in] ssh_session libssh structure representing SSH session object. It is fully managed by the created session
|
||||
* including freeing it.
|
||||
* @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created.
|
||||
* Any YANG modules not present in the context and supported by the server are loaded using \<get-schema\>
|
||||
* (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()).
|
||||
* @return Created NETCONF session object or NULL on error.
|
||||
*/
|
||||
struct nc_session *nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx);
|
||||
|
@ -392,24 +392,16 @@ struct nc_session *nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx
|
|||
* @brief Create another NETCONF session on existing SSH session using separated SSH channel.
|
||||
*
|
||||
* @param[in] session Existing NETCONF session. The session has to be created on SSH transport layer using libssh -
|
||||
* it has to be created by nc_connect_ssh(), nc_connect_libssh() or nc_connect_ssh_channel().
|
||||
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
|
||||
* (ignoring what is actually supported by the server side). If not set,
|
||||
* YANG context is created for the session using \<get-schema\> (if supported
|
||||
* by the server side) or/and by searching for YANG schemas in the searchpath
|
||||
* (see nc_client_schema_searchpath()). In every case except not providing context
|
||||
* to connect to a server supporting \<get-schema\> it is possible that
|
||||
* the session context will not include all the models supported by the server.
|
||||
* it has to be created by nc_connect_ssh(), nc_connect_libssh() or nc_connect_ssh_channel().
|
||||
* @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created.
|
||||
* Any YANG modules not present in the context and supported by the server are loaded using \<get-schema\>
|
||||
* (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()).
|
||||
* @return Created NETCONF session object or NULL on error.
|
||||
*/
|
||||
struct nc_session *nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx);
|
||||
|
||||
/** @} Client SSH */
|
||||
|
||||
#endif /* NC_ENABLED_SSH */
|
||||
|
||||
#ifdef NC_ENABLED_TLS
|
||||
|
||||
/**
|
||||
* @defgroup client_tls Client TLS
|
||||
* @ingroup client
|
||||
|
@ -458,19 +450,12 @@ int nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir);
|
|||
void nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir);
|
||||
|
||||
/**
|
||||
* @brief Set client Certificate Revocation List paths.
|
||||
*
|
||||
* @param[in] crl_file Location of the CRL certificate file used to check for revocated certificates.
|
||||
* @param[in] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
|
||||
* @return 0 on success, -1 on error.
|
||||
* @brief Deprecated.
|
||||
*/
|
||||
int nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir);
|
||||
|
||||
/**
|
||||
* @brief Get client Certificate Revocation List paths.
|
||||
*
|
||||
* @param[out] crl_file Location of the CRL certificate file used to check for revocated certificates.
|
||||
* @param[out] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
|
||||
* @brief Deprecated.
|
||||
*/
|
||||
void nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir);
|
||||
|
||||
|
@ -478,42 +463,26 @@ void nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir);
|
|||
* @brief Connect to the NETCONF server using TLS transport (via libssl)
|
||||
*
|
||||
* TLS session is created with the certificates set using nc_client_tls_* functions, which must be called beforehand!
|
||||
* If the caller needs to use specific TLS session properties, they are supposed to use nc_connect_libssl().
|
||||
* If the caller needs to use specific TLS session properties, they are supposed to use ::nc_connect_libssl().
|
||||
*
|
||||
* @param[in] host Hostname or address (both Ipv4 and IPv6 are accepted) of the target server.
|
||||
* 'localhost' is used by default if NULL is specified.
|
||||
* 'localhost' is used by default if NULL is specified. It is verified by TLS when connecting to it.
|
||||
* @param[in] port Port number of the target server. Default value 6513 is used if 0 is specified.
|
||||
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
|
||||
* (ignoring what is actually supported by the server side). If not set,
|
||||
* YANG context is created for the session using \<get-schema\> (if supported
|
||||
* by the server side) or/and by searching for YANG schemas in the searchpath
|
||||
* (see nc_client_schema_searchpath()). In every case except not providing context
|
||||
* to connect to a server supporting \<get-schema\> it is possible that
|
||||
* the session context will not include all the models supported by the server.
|
||||
* @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created.
|
||||
* Any YANG modules not present in the context and supported by the server are loaded using \<get-schema\>
|
||||
* (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()).
|
||||
* @return Created NETCONF session object or NULL on error.
|
||||
*/
|
||||
struct nc_session *nc_connect_tls(const char *host, uint16_t port, struct ly_ctx *ctx);
|
||||
|
||||
/**
|
||||
* @brief Connect to the NETCONF server using the provided TLS (libssl) session.
|
||||
*
|
||||
* The TLS session supplied is expected to be fully connected and authenticated!
|
||||
*
|
||||
* @param[in] tls libssl structure representing the TLS session object.
|
||||
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
|
||||
* (ignoring what is actually supported by the server side). If not set,
|
||||
* YANG context is created for the session using \<get-schema\> (if supported
|
||||
* by the server side) or/and by searching for YANG schemas in the searchpath
|
||||
* (see nc_client_schema_searchpath()). In every case except not providing context
|
||||
* to connect to a server supporting \<get-schema\> it is possible that
|
||||
* the session context will not include all the models supported by the server.
|
||||
* @return Created NETCONF session object or NULL on error.
|
||||
* @brief Deprecated. Should not be needed.
|
||||
*/
|
||||
struct nc_session *nc_connect_libssl(SSL *tls, struct ly_ctx *ctx);
|
||||
struct nc_session *nc_connect_libssl(void *tls, struct ly_ctx *ctx);
|
||||
|
||||
/** @} Client TLS */
|
||||
|
||||
#endif /* NC_ENABLED_TLS */
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
/**
|
||||
* @addtogroup client_session
|
||||
|
@ -572,9 +541,9 @@ NC_MSG_TYPE nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64
|
|||
* @brief Receive NETCONF Notification.
|
||||
*
|
||||
* @param[in] session NETCONF session from which the function gets data. It must be the
|
||||
* client side session object.
|
||||
* client side session object.
|
||||
* @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite
|
||||
* waiting and 0 for immediate return if data are not available on the wire.
|
||||
* waiting and 0 for immediate return if data are not available on the wire.
|
||||
* @param[out] envp NETCONF notification XML envelopes.
|
||||
* @param[out] op Parsed NETCONF notification data.
|
||||
* @return #NC_MSG_NOTIF for success,
|
||||
|
@ -584,18 +553,44 @@ NC_MSG_TYPE nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64
|
|||
*/
|
||||
NC_MSG_TYPE nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op);
|
||||
|
||||
/**
|
||||
* @brief Callback for receiving notifications in a separate thread.
|
||||
*
|
||||
* @param[in] session NC session that received the notification.
|
||||
* @param[in] envp Notification envelope data tree.
|
||||
* @param[in] op Notification body data tree.
|
||||
* @param[in] user_data Arbitrary user data passed to the callback.
|
||||
*/
|
||||
typedef void (*nc_notif_dispatch_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Receive NETCONF Notifications in a separate thread until the session is terminated
|
||||
* or \<notificationComplete\> is received.
|
||||
* or \<notificationComplete\> is received.
|
||||
*
|
||||
* @param[in] session Netconf session to read notifications from.
|
||||
* @param[in] notif_clb Function that is called for every received notification (including
|
||||
* \<notificationComplete\>). Parameters are the session the notification was received on
|
||||
* and the notification data.
|
||||
* \<notificationComplete\>). Parameters are the session the notification was received on
|
||||
* and the notification data.
|
||||
* @return 0 if the thread was successfully created, -1 on error.
|
||||
*/
|
||||
int nc_recv_notif_dispatch(struct nc_session *session,
|
||||
void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op));
|
||||
int nc_recv_notif_dispatch(struct nc_session *session, nc_notif_dispatch_clb notif_clb);
|
||||
|
||||
/**
|
||||
* @brief Receive NETCONF Notifications in a separate thread until the session is terminated
|
||||
* or \<notificationComplete\> is received. Similar to ::nc_recv_notif_dispatch() but allows
|
||||
* to set arbitrary user data that can be freed as well.
|
||||
*
|
||||
* @param[in] session Netconf session to read notifications from.
|
||||
* @param[in] notif_clb Callback that is called for every received notification (including
|
||||
* \<notificationComplete\>). Parameters are the session the notification was received on
|
||||
* and the notification data.
|
||||
* @param[in] user_data Arbitrary user data.
|
||||
* @param[in] free_data Callback for freeing the user data after notif thread exit.
|
||||
* @return 0 if the thread was successfully created, -1 on error.
|
||||
*/
|
||||
int nc_recv_notif_dispatch_data(struct nc_session *session, nc_notif_dispatch_clb notif_clb, void *user_data,
|
||||
void (*free_data)(void *));
|
||||
|
||||
/**
|
||||
* @brief Send NETCONF RPC message via the session.
|
||||
|
@ -603,7 +598,7 @@ int nc_recv_notif_dispatch(struct nc_session *session,
|
|||
* @param[in] session NETCONF session where the RPC will be written.
|
||||
* @param[in] rpc NETCONF RPC object to send via the specified session.
|
||||
* @param[in] timeout Timeout for writing in milliseconds. Use negative value for infinite
|
||||
* waiting and 0 for return if data cannot be sent immediately.
|
||||
* waiting and 0 for return if data cannot be sent immediately.
|
||||
* @param[out] msgid If RPC was successfully sent, this is it's message ID.
|
||||
* @return #NC_MSG_RPC on success,
|
||||
* #NC_MSG_WOULDBLOCK in case of a busy session, and
|
||||
|
@ -613,7 +608,7 @@ NC_MSG_TYPE nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int time
|
|||
|
||||
/**
|
||||
* @brief Make a session not strict when sending RPCs and receiving RPC replies. In other words,
|
||||
* it will silently skip unknown nodes without an error.
|
||||
* it will silently skip unknown nodes without an error.
|
||||
*
|
||||
* Generally, no such data should be worked with, so use this function only when you know what you
|
||||
* are doing and you understand the consequences.
|
||||
|
@ -622,6 +617,58 @@ NC_MSG_TYPE nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int time
|
|||
*/
|
||||
void nc_client_session_set_not_strict(struct nc_session *session);
|
||||
|
||||
/**
|
||||
* @brief Callback for monitoring client sessions.
|
||||
*
|
||||
* This callback is called whenever the client finds out that a session was disconnected by the server.
|
||||
*
|
||||
* @param[in] session Disconnected session. The session will not be freed, so it is safe to call nc_session_free() on it.
|
||||
* @param[in] user_data Arbitrary user data passed to the callback.
|
||||
*/
|
||||
typedef void (*nc_client_monitoring_clb)(struct nc_session *session, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Start a thread that monitors client sessions.
|
||||
*
|
||||
* Once the thread is running, new sessions will be monitored automatically.
|
||||
*
|
||||
* Note that once you start the monitoring thread, any other client thread that
|
||||
* calls ::nc_session_free() needs to share the same thread context (or be the same thread)
|
||||
* as the thread that called this function (see ::nc_client_set_thread_context()).
|
||||
*
|
||||
* @param[in] monitoring_clb Callback called whenever a session is terminated.
|
||||
* @param[in] user_data Arbitrary user data passed to the callback.
|
||||
* @param[in] free_data Callback for freeing the user data after monitoring thread exits.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_client_monitoring_thread_start(nc_client_monitoring_clb monitoring_clb, void *user_data, void (*free_data)(void *));
|
||||
|
||||
/**
|
||||
* @brief Stop the client session monitoring thread.
|
||||
*/
|
||||
void nc_client_monitoring_thread_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Enable or disable TCP keepalives. Only affects new sessions.
|
||||
*
|
||||
* Client-side TCP keepalives have the following default values:
|
||||
* - idle time: 1 second
|
||||
* - max probes: 10
|
||||
* - probe interval: 5 seconds
|
||||
*
|
||||
* @param[in] enable Whether to enable or disable TCP keepalives.
|
||||
*/
|
||||
void nc_client_enable_tcp_keepalives(int enable);
|
||||
|
||||
/**
|
||||
* @brief Set TCP keepalive options.
|
||||
*
|
||||
* @param[in] idle_time Time in seconds before the first keepalive probe is sent. If 0, the default value 1 is used.
|
||||
* @param[in] max_probes Maximum number of keepalive probes to send before considering the connection dead. If 0, the default value 10 is used.
|
||||
* @param[in] probe_interval Time in seconds between individual keepalive probes. If 0, the default value 5 is used.
|
||||
*/
|
||||
void nc_client_set_tcp_keepalives(uint16_t idle_time, uint16_t max_probes, uint16_t probe_interval);
|
||||
|
||||
/** @} Client Session */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 Call Home session client manipulation
|
||||
*
|
||||
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
|
@ -21,15 +22,15 @@ extern "C" {
|
|||
|
||||
#include <libyang/libyang.h>
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
# include <libssh/libssh.h>
|
||||
#endif
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
#include "messages_client.h"
|
||||
#include "netconf.h"
|
||||
#include "session.h"
|
||||
|
||||
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/**
|
||||
* @defgroup client_ch Client-side Call Home
|
||||
|
@ -43,8 +44,10 @@ extern "C" {
|
|||
* @brief Accept a Call Home connection on any of the listening binds.
|
||||
*
|
||||
* @param[in] timeout Timeout for receiving a new connection in milliseconds, 0 for
|
||||
* non-blocking call, -1 for infinite waiting.
|
||||
* @param[in] ctx Session context to use. Can be NULL.
|
||||
* non-blocking call, -1 for infinite waiting.
|
||||
* @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created.
|
||||
* Any YANG modules not present in the context and supported by the server are loaded using \<get-schema\>
|
||||
* (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()).
|
||||
* @param[out] session New session.
|
||||
* @return 1 on success, 0 on timeout, -1 on error.
|
||||
*/
|
||||
|
@ -52,10 +55,6 @@ int nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **sess
|
|||
|
||||
/** @} Client-side Call Home */
|
||||
|
||||
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
|
||||
/**
|
||||
* @defgroup client_ch_ssh Client-side Call Home on SSH
|
||||
* @ingroup client_ch
|
||||
|
@ -65,28 +64,26 @@ int nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **sess
|
|||
*/
|
||||
|
||||
/**
|
||||
* @brief Set SSH Call Home authentication hostkey check (knownhosts) callback.
|
||||
* @brief Set SSH Call Home behaviour of checking the host key and adding/reading entries to/from the known_hosts file.
|
||||
*
|
||||
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
|
||||
* freeing the private data when necessary (the private data can be obtained by
|
||||
* nc_client_ssh_ch_get_auth_hostkey_check_clb()).
|
||||
* The default mode is ::NC_SSH_KNOWNHOSTS_ASK.
|
||||
*
|
||||
* @param[in] auth_hostkey_check Function to call, returns 0 on success, non-zero in error.
|
||||
* If NULL, the default callback is set.
|
||||
* @param[in] priv Optional private data to be passed to the callback function.
|
||||
* @param[in] mode Server host key checking mode.
|
||||
*/
|
||||
void nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
|
||||
void *priv);
|
||||
void nc_client_ssh_ch_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode);
|
||||
|
||||
/**
|
||||
* @brief Get currently set SSH Call Home authentication hostkey check (knownhosts) callback and its private data
|
||||
* previously set by nc_client_ssh_ch_set_auth_hostkey_check_clb().
|
||||
* @brief Set SSH Call Home path to the known_hosts file for connections.
|
||||
*
|
||||
* @param[out] auth_hostkey_check Currently set callback, NULL in case of the default callback.
|
||||
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
|
||||
* Repetetive calling replaces the value. If the given file doesn't exist and the process has sufficient
|
||||
* rights, it gets created whenever the file is needed, otherwise an error occurs. If NULL is passed or the
|
||||
* path isn't set, the default known_hosts file will be used.
|
||||
*
|
||||
* @param[in] path Path to the known_hosts file.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
void nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
|
||||
void **priv);
|
||||
int nc_client_ssh_ch_set_knownhosts_path(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Set SSH Call Home password authentication callback.
|
||||
*
|
||||
|
@ -95,7 +92,7 @@ void nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(cons
|
|||
* nc_client_ssh_ch_get_auth_password_clb()).
|
||||
*
|
||||
* @param[in] auth_password Function to call, returns the password for username\@hostname.
|
||||
* If NULL, the default callback is set.
|
||||
* If NULL, the default callback is set.
|
||||
* @param[in] priv Optional private data to be passed to the callback function.
|
||||
*/
|
||||
void nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
|
||||
|
@ -119,8 +116,7 @@ void nc_client_ssh_ch_get_auth_password_clb(char *(**auth_password)(const char *
|
|||
* nc_client_ssh_ch_get_auth_interactive_clb()).
|
||||
*
|
||||
* @param[in] auth_interactive Function to call for every question, returns the answer for
|
||||
* authentication name with instruction and echoing prompt.
|
||||
* If NULL, the default callback is set.
|
||||
* authentication name with instruction and echoing prompt. If NULL, the default callback is set.
|
||||
* @param[in] priv Optional private data to be passed to the callback function.
|
||||
*/
|
||||
void nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
|
||||
|
@ -145,8 +141,8 @@ void nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const
|
|||
* freeing the private data when necessary (the private data can be obtained by
|
||||
* nc_client_ssh_ch_get_auth_privkey_passphrase_clb()).
|
||||
*
|
||||
* @param[in] auth_privkey_passphrase Function to call for every question, returns
|
||||
* the passphrase for the specific private key.
|
||||
* @param[in] auth_privkey_passphrase Function to call for every question, returns the passphrase for the specific
|
||||
* private key.
|
||||
* @param[in] priv Optional private data to be passed to the callback function.
|
||||
*/
|
||||
void nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
|
||||
|
@ -254,10 +250,6 @@ const char *nc_client_ssh_ch_get_username(void);
|
|||
|
||||
/** @} Client-side Call Home on SSH */
|
||||
|
||||
#endif /* NC_ENABLED_SSH */
|
||||
|
||||
#ifdef NC_ENABLED_TLS
|
||||
|
||||
/**
|
||||
* @defgroup client_ch_tls Client-side Call Home on TLS
|
||||
* @ingroup client_ch
|
||||
|
@ -275,6 +267,16 @@ const char *nc_client_ssh_ch_get_username(void);
|
|||
*/
|
||||
int nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port);
|
||||
|
||||
/**
|
||||
* @brief Add a new client bind and start listening on it for TLS Call Home connections coming from the specified hostname.
|
||||
*
|
||||
* @param[in] address IP address to bind to.
|
||||
* @param[in] port Port to bind to.
|
||||
* @param[in] hostname Expected server hostname, verified by TLS when connecting to it. If NULL, the check is skipped.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_client_tls_ch_add_bind_hostname_listen(const char *address, uint16_t port, const char *hostname);
|
||||
|
||||
/**
|
||||
* @brief Remove a TLS listening client bind.
|
||||
*
|
||||
|
@ -289,7 +291,7 @@ int nc_client_tls_ch_del_bind(const char *address, uint16_t port);
|
|||
*
|
||||
* @param[in] client_cert Path to the file containing the client certificate.
|
||||
* @param[in] client_key Path to the file containing the private key for the @p client_cert.
|
||||
* If NULL, key is expected to be stored with @p client_cert.
|
||||
* If NULL, key is expected to be stored with @p client_cert.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key);
|
||||
|
@ -298,8 +300,7 @@ int nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *cli
|
|||
* @brief Get client Call Home authentication identity - a certificate and a private key.
|
||||
*
|
||||
* @param[out] client_cert Path to the file containing the client certificate. Can be NULL.
|
||||
* @param[out] client_key Path to the file containing the private key for the @p client_cert.
|
||||
* Can be NULL.
|
||||
* @param[out] client_key Path to the file containing the private key for the @p client_cert. Can be NULL.
|
||||
*/
|
||||
void nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key);
|
||||
|
||||
|
@ -307,9 +308,9 @@ void nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **
|
|||
* @brief Set client Call Home trusted CA certificates.
|
||||
*
|
||||
* @param[in] ca_file Location of the CA certificate file used to verify server certificates.
|
||||
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
|
||||
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
|
||||
* @param[in] ca_dir Location of the CA certificates directory used to verify the server certificates.
|
||||
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
|
||||
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir);
|
||||
|
@ -317,35 +318,24 @@ int nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_di
|
|||
/**
|
||||
* @brief Get client Call Home trusted CA certificates.
|
||||
*
|
||||
* @param[out] ca_file Location of the CA certificate file used to verify server certificates.
|
||||
* Can be NULL.
|
||||
* @param[out] ca_dir Location of the CA certificates directory used to verify the server certificates.
|
||||
* Can be NULL.
|
||||
* @param[out] ca_file Location of the CA certificate file used to verify server certificates. Can be NULL.
|
||||
* @param[out] ca_dir Location of the CA certificates directory used to verify the server certificates. Can be NULL.
|
||||
*/
|
||||
void nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir);
|
||||
|
||||
/**
|
||||
* @brief Set client Call Home Certificate Revocation Lists.
|
||||
*
|
||||
* @param[in] crl_file Location of the CRL certificate file used to check for revocated certificates.
|
||||
* @param[in] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
|
||||
* @return 0 on success, -1 on error.
|
||||
* @brief Deprecated.
|
||||
*/
|
||||
int nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir);
|
||||
|
||||
/**
|
||||
* @brief Get client Call Home Certificate Revocation Lists.
|
||||
*
|
||||
* @param[out] crl_file Location of the CRL certificate file used to check for revocated certificates.
|
||||
* Can be NULL.
|
||||
* @param[out] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
|
||||
* Can be NULL.
|
||||
* @brief Deprecated.
|
||||
*/
|
||||
void nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir);
|
||||
|
||||
/** @} Client-side Call Home on TLS */
|
||||
|
||||
#endif /* NC_ENABLED_TLS */
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,11 +1,12 @@
|
|||
/**
|
||||
* \file session_client_tls.c
|
||||
* \author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* \author Michal Vasko <mvasko@cesnet.cz>
|
||||
* \brief libnetconf2 - TLS specific session client transport functions
|
||||
* @file session_client_tls.c
|
||||
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 - TLS specific session client transport functions
|
||||
*
|
||||
* This source is compiled only with libssl.
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2015 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
|
@ -15,225 +16,30 @@
|
|||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* pthread_rwlock_t, strdup */
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ossl_typ.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#include "libnetconf.h"
|
||||
#include "compat.h"
|
||||
#include "config.h"
|
||||
#include "log_p.h"
|
||||
#include "session_client.h"
|
||||
#include "session_client_ch.h"
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
#define X509_STORE_CTX_get_by_subject X509_STORE_get_by_subject
|
||||
#endif
|
||||
#include "session_p.h"
|
||||
#include "session_wrapper.h"
|
||||
|
||||
struct nc_client_context *nc_client_context_location(void);
|
||||
int nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx);
|
||||
|
||||
#define client_opts nc_client_context_location()->opts
|
||||
#define tls_opts nc_client_context_location()->tls_opts
|
||||
#define tls_ch_opts nc_client_context_location()->tls_ch_opts
|
||||
|
||||
static int tlsauth_ch;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
|
||||
|
||||
static int
|
||||
tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
||||
{
|
||||
X509_STORE_CTX *store_ctx;
|
||||
X509_OBJECT *obj;
|
||||
X509_NAME *subject, *issuer;
|
||||
X509 *cert;
|
||||
X509_CRL *crl;
|
||||
X509_REVOKED *revoked;
|
||||
EVP_PKEY *pubkey;
|
||||
int i, n, rc;
|
||||
const ASN1_TIME *next_update = NULL;
|
||||
struct nc_client_tls_opts *opts;
|
||||
|
||||
if (!preverify_ok) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
|
||||
|
||||
if (!opts->crl_store) {
|
||||
/* nothing to check */
|
||||
return 1;
|
||||
}
|
||||
|
||||
cert = X509_STORE_CTX_get_current_cert(x509_ctx);
|
||||
subject = X509_get_subject_name(cert);
|
||||
issuer = X509_get_issuer_name(cert);
|
||||
|
||||
/* try to retrieve a CRL corresponding to the _subject_ of
|
||||
* the current certificate in order to verify it's integrity */
|
||||
store_ctx = X509_STORE_CTX_new();
|
||||
obj = X509_OBJECT_new();
|
||||
X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
|
||||
rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, subject, obj);
|
||||
X509_STORE_CTX_free(store_ctx);
|
||||
crl = X509_OBJECT_get0_X509_CRL(obj);
|
||||
if ((rc > 0) && crl) {
|
||||
next_update = X509_CRL_get0_nextUpdate(crl);
|
||||
|
||||
/* verify the signature on this CRL */
|
||||
pubkey = X509_get_pubkey(cert);
|
||||
if (X509_CRL_verify(crl, pubkey) <= 0) {
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
|
||||
X509_OBJECT_free(obj);
|
||||
if (pubkey) {
|
||||
EVP_PKEY_free(pubkey);
|
||||
}
|
||||
return 0; /* fail */
|
||||
}
|
||||
if (pubkey) {
|
||||
EVP_PKEY_free(pubkey);
|
||||
}
|
||||
|
||||
/* check date of CRL to make sure it's not expired */
|
||||
if (!next_update) {
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
|
||||
X509_OBJECT_free(obj);
|
||||
return 0; /* fail */
|
||||
}
|
||||
if (X509_cmp_current_time(next_update) < 0) {
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
|
||||
X509_OBJECT_free(obj);
|
||||
return 0; /* fail */
|
||||
}
|
||||
X509_OBJECT_free(obj);
|
||||
}
|
||||
|
||||
/* try to retrieve a CRL corresponding to the _issuer_ of
|
||||
* the current certificate in order to check for revocation */
|
||||
store_ctx = X509_STORE_CTX_new();
|
||||
obj = X509_OBJECT_new();
|
||||
X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
|
||||
rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj);
|
||||
X509_STORE_CTX_free(store_ctx);
|
||||
crl = X509_OBJECT_get0_X509_CRL(obj);
|
||||
if ((rc > 0) && crl) {
|
||||
/* check if the current certificate is revoked by this CRL */
|
||||
n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
|
||||
for (i = 0; i < n; i++) {
|
||||
revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
|
||||
if (ASN1_INTEGER_cmp(X509_REVOKED_get0_serialNumber(revoked), X509_get_serialNumber(cert)) == 0) {
|
||||
ERR(NULL, "Certificate revoked!");
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
|
||||
X509_OBJECT_free(obj);
|
||||
return 0; /* fail */
|
||||
}
|
||||
}
|
||||
X509_OBJECT_free(obj);
|
||||
}
|
||||
|
||||
return 1; /* success */
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int
|
||||
tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
||||
{
|
||||
X509_STORE_CTX store_ctx;
|
||||
X509_OBJECT obj;
|
||||
X509_NAME *subject, *issuer;
|
||||
X509 *cert;
|
||||
X509_CRL *crl;
|
||||
X509_REVOKED *revoked;
|
||||
EVP_PKEY *pubkey;
|
||||
int i, n, rc;
|
||||
ASN1_TIME *next_update = NULL;
|
||||
struct nc_client_tls_opts *opts;
|
||||
|
||||
if (!preverify_ok) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
|
||||
|
||||
if (!opts->crl_store) {
|
||||
/* nothing to check */
|
||||
return 1;
|
||||
}
|
||||
|
||||
cert = X509_STORE_CTX_get_current_cert(x509_ctx);
|
||||
subject = X509_get_subject_name(cert);
|
||||
issuer = X509_get_issuer_name(cert);
|
||||
|
||||
/* try to retrieve a CRL corresponding to the _subject_ of
|
||||
* the current certificate in order to verify it's integrity */
|
||||
memset((char *)&obj, 0, sizeof obj);
|
||||
X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
|
||||
rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
|
||||
X509_STORE_CTX_cleanup(&store_ctx);
|
||||
crl = obj.data.crl;
|
||||
if ((rc > 0) && crl) {
|
||||
next_update = X509_CRL_get_nextUpdate(crl);
|
||||
|
||||
/* verify the signature on this CRL */
|
||||
pubkey = X509_get_pubkey(cert);
|
||||
if (X509_CRL_verify(crl, pubkey) <= 0) {
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
|
||||
X509_OBJECT_free_contents(&obj);
|
||||
if (pubkey) {
|
||||
EVP_PKEY_free(pubkey);
|
||||
}
|
||||
return 0; /* fail */
|
||||
}
|
||||
if (pubkey) {
|
||||
EVP_PKEY_free(pubkey);
|
||||
}
|
||||
|
||||
/* check date of CRL to make sure it's not expired */
|
||||
if (!next_update) {
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
|
||||
X509_OBJECT_free_contents(&obj);
|
||||
return 0; /* fail */
|
||||
}
|
||||
if (X509_cmp_current_time(next_update) < 0) {
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
|
||||
X509_OBJECT_free_contents(&obj);
|
||||
return 0; /* fail */
|
||||
}
|
||||
X509_OBJECT_free_contents(&obj);
|
||||
}
|
||||
|
||||
/* try to retrieve a CRL corresponding to the _issuer_ of
|
||||
* the current certificate in order to check for revocation */
|
||||
memset((char *)&obj, 0, sizeof obj);
|
||||
X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
|
||||
rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
|
||||
X509_STORE_CTX_cleanup(&store_ctx);
|
||||
crl = obj.data.crl;
|
||||
if ((rc > 0) && crl) {
|
||||
/* check if the current certificate is revoked by this CRL */
|
||||
n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
|
||||
for (i = 0; i < n; i++) {
|
||||
revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
|
||||
if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
|
||||
ERR(NULL, "Certificate revoked!");
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
|
||||
X509_OBJECT_free_contents(&obj);
|
||||
return 0; /* fail */
|
||||
}
|
||||
}
|
||||
X509_OBJECT_free_contents(&obj);
|
||||
}
|
||||
|
||||
return 1; /* success */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
_nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
|
||||
{
|
||||
|
@ -241,12 +47,6 @@ _nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
|
|||
free(opts->key_path);
|
||||
free(opts->ca_file);
|
||||
free(opts->ca_dir);
|
||||
SSL_CTX_free(opts->tls_ctx);
|
||||
|
||||
free(opts->crl_file);
|
||||
free(opts->crl_dir);
|
||||
X509_STORE_free(opts->crl_store);
|
||||
|
||||
memset(opts, 0, sizeof *opts);
|
||||
}
|
||||
|
||||
|
@ -260,32 +60,21 @@ nc_client_tls_destroy_opts(void)
|
|||
static int
|
||||
_nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key, struct nc_client_tls_opts *opts)
|
||||
{
|
||||
if (!client_cert) {
|
||||
ERRARG("client_cert");
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ARG_RET(NULL, client_cert, -1);
|
||||
|
||||
free(opts->cert_path);
|
||||
free(opts->key_path);
|
||||
|
||||
opts->cert_path = strdup(client_cert);
|
||||
if (!opts->cert_path) {
|
||||
ERRMEM;
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!opts->cert_path, -1);
|
||||
|
||||
if (client_key) {
|
||||
opts->key_path = strdup(client_key);
|
||||
if (!opts->key_path) {
|
||||
ERRMEM;
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!opts->key_path, -1);
|
||||
} else {
|
||||
opts->key_path = NULL;
|
||||
}
|
||||
|
||||
opts->tls_ctx_change = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -305,7 +94,7 @@ static void
|
|||
_nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts)
|
||||
{
|
||||
if (!client_cert && !client_key) {
|
||||
ERRARG("client_cert and client_key");
|
||||
ERRARG(NULL, "client_cert and client_key");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -333,7 +122,7 @@ static int
|
|||
_nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, struct nc_client_tls_opts *opts)
|
||||
{
|
||||
if (!ca_file && !ca_dir) {
|
||||
ERRARG("ca_file and ca_dir");
|
||||
ERRARG(NULL, "ca_file and ca_dir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -342,26 +131,18 @@ _nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, str
|
|||
|
||||
if (ca_file) {
|
||||
opts->ca_file = strdup(ca_file);
|
||||
if (!opts->ca_file) {
|
||||
ERRMEM;
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!opts->ca_file, -1);
|
||||
} else {
|
||||
opts->ca_file = NULL;
|
||||
}
|
||||
|
||||
if (ca_dir) {
|
||||
opts->ca_dir = strdup(ca_dir);
|
||||
if (!opts->ca_dir) {
|
||||
ERRMEM;
|
||||
return -1;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!opts->ca_dir, -1);
|
||||
} else {
|
||||
opts->ca_dir = NULL;
|
||||
}
|
||||
|
||||
opts->tls_ctx_change = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -381,7 +162,7 @@ static void
|
|||
_nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts)
|
||||
{
|
||||
if (!ca_file && !ca_dir) {
|
||||
ERRARG("ca_file and ca_dir");
|
||||
ERRARG(NULL, "ca_file and ca_dir");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -405,200 +186,197 @@ nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
|
|||
_nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
|
||||
}
|
||||
|
||||
static int
|
||||
_nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct nc_client_tls_opts *opts)
|
||||
API int
|
||||
nc_client_tls_set_crl_paths(const char *UNUSED(crl_file), const char *UNUSED(crl_dir))
|
||||
{
|
||||
if (!crl_file && !crl_dir) {
|
||||
ERRARG("crl_file and crl_dir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(opts->crl_file);
|
||||
free(opts->crl_dir);
|
||||
|
||||
if (crl_file) {
|
||||
opts->crl_file = strdup(crl_file);
|
||||
if (!opts->crl_file) {
|
||||
ERRMEM;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
opts->crl_file = NULL;
|
||||
}
|
||||
|
||||
if (crl_dir) {
|
||||
opts->crl_dir = strdup(crl_dir);
|
||||
if (!opts->crl_dir) {
|
||||
ERRMEM;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
opts->crl_dir = NULL;
|
||||
}
|
||||
|
||||
opts->crl_store_change = 1;
|
||||
|
||||
return 0;
|
||||
ERR(NULL, "nc_client_tls_set_crl_paths() is deprecated, do not use it.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir)
|
||||
nc_client_tls_ch_set_crl_paths(const char *UNUSED(crl_file), const char *UNUSED(crl_dir))
|
||||
{
|
||||
return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_opts);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir)
|
||||
{
|
||||
return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts);
|
||||
}
|
||||
|
||||
static void
|
||||
_nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts)
|
||||
{
|
||||
if (!crl_file && !crl_dir) {
|
||||
ERRARG("crl_file and crl_dir");
|
||||
return;
|
||||
}
|
||||
|
||||
if (crl_file) {
|
||||
*crl_file = opts->crl_file;
|
||||
}
|
||||
if (crl_dir) {
|
||||
*crl_dir = opts->crl_dir;
|
||||
}
|
||||
ERR(NULL, "nc_client_tls_ch_set_crl_paths() is deprecated, do not use it.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
API void
|
||||
nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir)
|
||||
nc_client_tls_get_crl_paths(const char **UNUSED(crl_file), const char **UNUSED(crl_dir))
|
||||
{
|
||||
_nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts);
|
||||
ERR(NULL, "nc_client_tls_get_crl_paths() is deprecated, do not use it.");
|
||||
return;
|
||||
}
|
||||
|
||||
API void
|
||||
nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir)
|
||||
nc_client_tls_ch_get_crl_paths(const char **UNUSED(crl_file), const char **UNUSED(crl_dir))
|
||||
{
|
||||
_nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts);
|
||||
ERR(NULL, "nc_client_tls_ch_get_crl_paths() is deprecated, do not use it.");
|
||||
return;
|
||||
}
|
||||
|
||||
API int
|
||||
nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
|
||||
{
|
||||
return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL);
|
||||
return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_TLS);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_client_tls_ch_add_bind_hostname_listen(const char *address, uint16_t port, const char *hostname)
|
||||
{
|
||||
return nc_client_ch_add_bind_listen(address, port, hostname, NC_TI_TLS);
|
||||
}
|
||||
|
||||
API int
|
||||
nc_client_tls_ch_del_bind(const char *address, uint16_t port)
|
||||
{
|
||||
return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
|
||||
return nc_client_ch_del_bind(address, port, NC_TI_TLS);
|
||||
}
|
||||
|
||||
static int
|
||||
nc_client_tls_update_opts(struct nc_client_tls_opts *opts)
|
||||
nc_client_tls_connect_check(int connect_ret, void *tls_session, const char *peername)
|
||||
{
|
||||
char *key;
|
||||
X509_LOOKUP *lookup;
|
||||
uint32_t verify;
|
||||
char *err;
|
||||
|
||||
if (!opts->tls_ctx || opts->tls_ctx_change) {
|
||||
SSL_CTX_free(opts->tls_ctx);
|
||||
/* check certificate verification result */
|
||||
verify = nc_tls_get_verify_result_wrap(tls_session);
|
||||
if (!verify && (connect_ret == 1)) {
|
||||
VRB(NULL, "Server certificate verified (domain \"%s\").", peername);
|
||||
} else if (verify) {
|
||||
err = nc_tls_verify_error_string_wrap(verify);
|
||||
ERR(NULL, "Server certificate error (%s).", err);
|
||||
free(err);
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
|
||||
/* prepare global SSL context, highest available method is negotiated autmatically */
|
||||
if (!(opts->tls_ctx = SSL_CTX_new(TLS_client_method())))
|
||||
#else
|
||||
/* prepare global SSL context, allow only mandatory TLS 1.2 */
|
||||
if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method())))
|
||||
#endif
|
||||
{
|
||||
ERR(NULL, "Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
|
||||
return -1;
|
||||
}
|
||||
SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
|
||||
/* check TLS connection result */
|
||||
if (connect_ret != 1) {
|
||||
nc_client_tls_print_connect_err_wrap(connect_ret, peername, tls_session);
|
||||
}
|
||||
|
||||
/* get peer certificate */
|
||||
if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
|
||||
ERR(NULL, "Loading the client certificate from \'%s\' failed (%s).", opts->cert_path,
|
||||
ERR_reason_error_string(ERR_get_error()));
|
||||
return -1;
|
||||
}
|
||||
return connect_ret;
|
||||
}
|
||||
|
||||
/* if the file with private key not specified, expect that the private key is stored with the certificate */
|
||||
if (!opts->key_path) {
|
||||
key = opts->cert_path;
|
||||
} else {
|
||||
key = opts->key_path;
|
||||
}
|
||||
if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
|
||||
ERR(NULL, "Loading the client priavte key from \'%s\' failed (%s).", key,
|
||||
ERR_reason_error_string(ERR_get_error()));
|
||||
return -1;
|
||||
}
|
||||
static void *
|
||||
nc_client_tls_session_new(int sock, const char *host, int timeout, struct nc_client_tls_opts *opts, void **out_tls_cfg, struct nc_tls_ctx *tls_ctx)
|
||||
{
|
||||
int ret = 0, sock_tmp = sock;
|
||||
struct timespec ts_timeout;
|
||||
void *tls_session, *tls_cfg, *cli_cert, *cli_pkey, *cert_store, *crl_store;
|
||||
|
||||
if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
|
||||
ERR(NULL, "Failed to load the locations of trusted CA certificates (%s).",
|
||||
ERR_reason_error_string(ERR_get_error()));
|
||||
return -1;
|
||||
tls_session = tls_cfg = cli_cert = cli_pkey = cert_store = crl_store = NULL;
|
||||
|
||||
/* prepare TLS context from which a session will be created */
|
||||
tls_cfg = nc_tls_config_new_wrap(NC_CLIENT);
|
||||
if (!tls_cfg) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* opaque CA/CRL certificate store */
|
||||
cert_store = nc_tls_cert_store_new_wrap();
|
||||
if (!cert_store) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* load client's key and certificate */
|
||||
if (nc_client_tls_load_cert_key_wrap(opts->cert_path, opts->key_path, &cli_cert, &cli_pkey)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* load trusted CA certificates */
|
||||
if (nc_client_tls_load_trusted_certs_wrap(cert_store, opts->ca_file, opts->ca_dir)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* load CRLs from set certificates' extensions */
|
||||
if (nc_session_tls_crl_from_cert_ext_fetch(cli_cert, cert_store, &crl_store)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* set client's verify mode flags */
|
||||
nc_client_tls_set_verify_wrap(tls_cfg);
|
||||
|
||||
/* init TLS context and store data which may be needed later in it */
|
||||
if (nc_tls_init_ctx_wrap(cli_cert, cli_pkey, cert_store, crl_store, tls_ctx)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* memory is managed by context now */
|
||||
cli_cert = cli_pkey = cert_store = crl_store = NULL;
|
||||
|
||||
/* setup config from ctx */
|
||||
if (nc_tls_setup_config_from_ctx_wrap(tls_ctx, NC_CLIENT, tls_cfg)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* session from config */
|
||||
tls_session = nc_tls_session_new_wrap(tls_cfg);
|
||||
if (!tls_session) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* set session fd */
|
||||
nc_tls_set_fd_wrap(tls_session, sock, tls_ctx);
|
||||
|
||||
sock = -1;
|
||||
|
||||
/* set session hostname to check against in the server cert */
|
||||
if (nc_client_tls_set_hostname_wrap(tls_session, host)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* handshake */
|
||||
if (timeout > -1) {
|
||||
nc_timeouttime_get(&ts_timeout, timeout);
|
||||
}
|
||||
while ((ret = nc_client_tls_handshake_step_wrap(tls_session, sock_tmp)) == 0) {
|
||||
usleep(NC_TIMEOUT_STEP);
|
||||
if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
|
||||
ERR(NULL, "SSL connect timeout.");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
|
||||
/* set the revocation store with the correct paths for the callback */
|
||||
X509_STORE_free(opts->crl_store);
|
||||
|
||||
opts->crl_store = X509_STORE_new();
|
||||
if (!opts->crl_store) {
|
||||
ERR(NULL, "Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
|
||||
/* whaveter this does... */
|
||||
opts->crl_store->cache = 0;
|
||||
#endif
|
||||
|
||||
if (opts->crl_file) {
|
||||
if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
|
||||
ERR(NULL, "Failed to add lookup method to CRL checking.");
|
||||
return -1;
|
||||
}
|
||||
if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
|
||||
ERR(NULL, "Failed to add the revocation lookup file \"%s\".", opts->crl_file);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->crl_dir) {
|
||||
if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
|
||||
ERR(NULL, "Failed to add lookup method to CRL checking.");
|
||||
return -1;
|
||||
}
|
||||
if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
|
||||
ERR(NULL, "Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* check if handshake was ok */
|
||||
if (nc_client_tls_connect_check(ret, tls_session, host) != 1) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
*out_tls_cfg = tls_cfg;
|
||||
return tls_session;
|
||||
|
||||
fail:
|
||||
if (sock > -1) {
|
||||
close(sock);
|
||||
}
|
||||
|
||||
nc_tls_session_destroy_wrap(tls_session);
|
||||
nc_tls_cert_destroy_wrap(cli_cert);
|
||||
nc_tls_privkey_destroy_wrap(cli_pkey);
|
||||
nc_tls_cert_store_destroy_wrap(cert_store);
|
||||
nc_tls_crl_store_destroy_wrap(crl_store);
|
||||
nc_tls_config_destroy_wrap(tls_cfg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
API struct nc_session *
|
||||
nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
|
||||
{
|
||||
struct nc_session *session = NULL;
|
||||
int sock, verify, ret;
|
||||
unsigned long tls_err;
|
||||
struct timespec ts_timeout, ts_cur;
|
||||
const char *peername;
|
||||
int sock;
|
||||
char *ip_host = NULL;
|
||||
void *tls_cfg = NULL;
|
||||
struct nc_tls_ctx tls_ctx = {0};
|
||||
|
||||
if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
|
||||
ERRINIT;
|
||||
if (!tls_opts.cert_path) {
|
||||
ERR(NULL, "Client certificate not set.");
|
||||
return NULL;
|
||||
} else if (!tls_opts.ca_file && !tls_opts.ca_dir) {
|
||||
ERR(NULL, "Certificate authority certificates not set.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* process parameters */
|
||||
if (!host || strisempty(host)) {
|
||||
if (!host || (host[0] == '\0')) {
|
||||
host = "localhost";
|
||||
}
|
||||
|
||||
|
@ -606,94 +384,33 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
|
|||
port = NC_PORT_TLS;
|
||||
}
|
||||
|
||||
/* create/update TLS structures */
|
||||
if (nc_client_tls_update_opts(&tls_opts)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* prepare session structure */
|
||||
session = nc_new_session(NC_CLIENT, 0);
|
||||
if (!session) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!session, NULL);
|
||||
session->status = NC_STATUS_STARTING;
|
||||
|
||||
/* fill the session */
|
||||
session->ti_type = NC_TI_OPENSSL;
|
||||
if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
|
||||
ERR(NULL, "Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* create and assign socket */
|
||||
sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
|
||||
sock = nc_sock_connect(NULL, 0, host, port, -1, &client_opts.ka, NULL, &ip_host);
|
||||
if (sock == -1) {
|
||||
ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
SSL_set_fd(session->ti.tls, sock);
|
||||
|
||||
/* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
|
||||
SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
|
||||
/* server identity (hostname) verification */
|
||||
if (!SSL_set1_host(session->ti.tls, host)) {
|
||||
ERR(NULL, "Failed to set expected server hostname.");
|
||||
/* fill the session */
|
||||
session->ti_type = NC_TI_TLS;
|
||||
if (!(session->ti.tls.session = nc_client_tls_session_new(sock, host, NC_TRANSPORT_TIMEOUT, &tls_opts, &tls_cfg, &tls_ctx))) {
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
session->ti.tls.config = tls_cfg;
|
||||
|
||||
/* connect and perform the handshake */
|
||||
nc_gettimespec_mono(&ts_timeout);
|
||||
nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
|
||||
tlsauth_ch = 0;
|
||||
while (((ret = SSL_connect(session->ti.tls)) != 1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) {
|
||||
usleep(NC_TIMEOUT_STEP);
|
||||
nc_gettimespec_mono(&ts_cur);
|
||||
if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
|
||||
ERR(NULL, "SSL_connect timeout.");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (ret != 1) {
|
||||
switch (SSL_get_error(session->ti.tls, ret)) {
|
||||
case SSL_ERROR_SYSCALL:
|
||||
ERR(NULL, "SSL_connect failed (%s).", errno ? strerror(errno) : "unexpected EOF");
|
||||
break;
|
||||
case SSL_ERROR_SSL:
|
||||
tls_err = ERR_get_error();
|
||||
ERR(NULL, "SSL_connect failed (%s).", ERR_reason_error_string(tls_err));
|
||||
break;
|
||||
default:
|
||||
ERR(NULL, "SSL_connect failed.");
|
||||
break;
|
||||
}
|
||||
/* memory belongs to session */
|
||||
memcpy(&session->ti.tls.ctx, &tls_ctx, sizeof tls_ctx);
|
||||
memset(&tls_ctx, 0, sizeof tls_ctx);
|
||||
|
||||
if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* check certificate verification result */
|
||||
verify = SSL_get_verify_result(session->ti.tls);
|
||||
switch (verify) {
|
||||
case X509_V_OK:
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
|
||||
peername = SSL_get0_peername(session->ti.tls);
|
||||
VRB(NULL, "Server certificate successfully verified (domain \"%s\").", peername ? peername : "<unknown>");
|
||||
#else
|
||||
(void)peername;
|
||||
VRB(NULL, "Server certificate successfully verified.");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
WRN(NULL, "Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
|
||||
}
|
||||
|
||||
if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
ctx = session->ctx;
|
||||
|
||||
/* NETCONF handshake */
|
||||
if (nc_handshake_io(session) != NC_MSG_HELLO) {
|
||||
goto fail;
|
||||
|
@ -704,46 +421,58 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/* store information into session and the dictionary */
|
||||
lydict_insert_zc(ctx, ip_host, &session->host);
|
||||
/* start monitoring the session if the monitoring thread is running */
|
||||
if (nc_client_monitoring_session_start(session)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* store information into session */
|
||||
session->host = ip_host;
|
||||
session->port = port;
|
||||
lydict_insert(ctx, "certificate-based", 0, &session->username);
|
||||
session->username = strdup("certificate-based");
|
||||
|
||||
return session;
|
||||
|
||||
fail:
|
||||
free(ip_host);
|
||||
nc_session_free(session, NULL);
|
||||
nc_tls_ctx_destroy_wrap(&tls_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
API struct nc_session *
|
||||
nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
|
||||
nc_connect_libssl(void *UNUSED(tls), struct ly_ctx *UNUSED(ctx))
|
||||
{
|
||||
struct nc_session *session;
|
||||
ERR(NULL, "nc_connect_libssl() is deprecated, do not use it.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!tls) {
|
||||
ERRARG("tls");
|
||||
return NULL;
|
||||
} else if (!SSL_is_init_finished(tls)) {
|
||||
ERR(NULL, "Supplied TLS session is not fully connected!");
|
||||
return NULL;
|
||||
}
|
||||
struct nc_session *
|
||||
nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout, const char *peername)
|
||||
{
|
||||
struct nc_session *session = NULL;
|
||||
void *tls_cfg = NULL;
|
||||
struct nc_tls_ctx tls_ctx = {0};
|
||||
|
||||
/* prepare session structure */
|
||||
session = nc_new_session(NC_CLIENT, 0);
|
||||
if (!session) {
|
||||
ERRMEM;
|
||||
return NULL;
|
||||
}
|
||||
NC_CHECK_ERRMEM_RET(!session, NULL);
|
||||
session->status = NC_STATUS_STARTING;
|
||||
session->ti_type = NC_TI_OPENSSL;
|
||||
session->ti.tls = tls;
|
||||
|
||||
if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
|
||||
/* fill the session */
|
||||
session->ti_type = NC_TI_TLS;
|
||||
if (!(session->ti.tls.session = nc_client_tls_session_new(sock, peername, timeout, &tls_ch_opts, &tls_cfg, &tls_ctx))) {
|
||||
goto fail;
|
||||
}
|
||||
session->ti.tls.config = tls_cfg;
|
||||
|
||||
/* memory belongs to session */
|
||||
memcpy(&session->ti.tls.ctx, &tls_ctx, sizeof tls_ctx);
|
||||
memset(&tls_ctx, 0, sizeof tls_ctx);
|
||||
|
||||
if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
ctx = session->ctx;
|
||||
|
||||
/* NETCONF handshake */
|
||||
if (nc_handshake_io(session) != NC_MSG_HELLO) {
|
||||
|
@ -755,94 +484,22 @@ nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
session->flags |= NC_SESSION_CALLHOME;
|
||||
|
||||
/* start monitoring the session if the monitoring thread is running */
|
||||
if (nc_client_monitoring_session_start(session)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* store information into session */
|
||||
session->host = strdup(host);
|
||||
session->port = port;
|
||||
session->username = strdup("certificate-based");
|
||||
|
||||
return session;
|
||||
|
||||
fail:
|
||||
session->ti.tls = NULL;
|
||||
nc_session_free(session, NULL);
|
||||
nc_tls_ctx_destroy_wrap(&tls_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct nc_session *
|
||||
nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout)
|
||||
{
|
||||
int verify, ret;
|
||||
SSL *tls = NULL;
|
||||
struct nc_session *session = NULL;
|
||||
struct timespec ts_timeout, ts_cur;
|
||||
|
||||
if (nc_client_tls_update_opts(&tls_ch_opts)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
|
||||
ERR(NULL, "Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
SSL_set_fd(tls, sock);
|
||||
|
||||
/* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
|
||||
SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
|
||||
|
||||
/* connect and perform the handshake */
|
||||
if (timeout > -1) {
|
||||
nc_gettimespec_mono(&ts_timeout);
|
||||
nc_addtimespec(&ts_timeout, timeout);
|
||||
}
|
||||
tlsauth_ch = 1;
|
||||
while (((ret = SSL_connect(tls)) == -1) && (SSL_get_error(tls, ret) == SSL_ERROR_WANT_READ)) {
|
||||
usleep(NC_TIMEOUT_STEP);
|
||||
if (timeout > -1) {
|
||||
nc_gettimespec_mono(&ts_cur);
|
||||
if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
|
||||
ERR(NULL, "SSL_connect timeout.");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret != 1) {
|
||||
switch (SSL_get_error(tls, ret)) {
|
||||
case SSL_ERROR_SYSCALL:
|
||||
ERR(NULL, "SSL_connect failed (%s).", strerror(errno));
|
||||
break;
|
||||
case SSL_ERROR_SSL:
|
||||
ERR(NULL, "SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
|
||||
break;
|
||||
default:
|
||||
ERR(NULL, "SSL_connect failed.");
|
||||
break;
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* check certificate verification result */
|
||||
verify = SSL_get_verify_result(tls);
|
||||
switch (verify) {
|
||||
case X509_V_OK:
|
||||
VRB(NULL, "Server certificate successfully verified.");
|
||||
break;
|
||||
default:
|
||||
WRN(NULL, "Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
|
||||
}
|
||||
|
||||
/* connect */
|
||||
session = nc_connect_libssl(tls, ctx);
|
||||
if (!session) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
session->flags |= NC_SESSION_CALLHOME;
|
||||
|
||||
/* store information into session and the dictionary */
|
||||
lydict_insert(session->ctx, host, 0, &session->host);
|
||||
session->port = port;
|
||||
lydict_insert(session->ctx, "certificate-based", 0, &session->username);
|
||||
|
||||
cleanup:
|
||||
if (!session) {
|
||||
SSL_free(tls);
|
||||
close(sock);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
|
2014
src/session_mbedtls.c
Normal file
2014
src/session_mbedtls.c
Normal file
File diff suppressed because it is too large
Load diff
1494
src/session_openssl.c
Normal file
1494
src/session_openssl.c
Normal file
File diff suppressed because it is too large
Load diff
1012
src/session_p.h
1012
src/session_p.h
File diff suppressed because it is too large
Load diff
4131
src/session_server.c
4131
src/session_server.c
File diff suppressed because it is too large
Load diff
|
@ -3,6 +3,7 @@
|
|||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 session server manipulation
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
|
@ -21,20 +22,17 @@ extern "C" {
|
|||
|
||||
#include <libyang/libyang.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef NC_ENABLED_TLS
|
||||
# include <openssl/x509.h>
|
||||
#endif
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
# include <libssh/callbacks.h>
|
||||
# include <libssh/libssh.h>
|
||||
# include <libssh/server.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "netconf.h"
|
||||
#include "session.h"
|
||||
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
# include <libssh/callbacks.h>
|
||||
# include <libssh/libssh.h>
|
||||
# include <libssh/server.h>
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
/**
|
||||
* @defgroup server_session Server Session
|
||||
* @ingroup server
|
||||
|
@ -57,6 +55,21 @@ extern "C" {
|
|||
*/
|
||||
typedef struct nc_server_reply *(*nc_rpc_clb)(struct lyd_node *rpc, struct nc_session *session);
|
||||
|
||||
/**
|
||||
* @brief Callback for certificate expiration notification.
|
||||
*
|
||||
* This callback is called when a certificate expiration notification is generated.
|
||||
* It is up to the user to decide what to do with the notification.
|
||||
*
|
||||
* In case an error occurs and you wish to terminate the notification thread,
|
||||
* call nc_server_notif_cert_expiration_thread_stop().
|
||||
*
|
||||
* @param[in] expiration_time Expiration time of the certificate obtained via ly_time_time2str().
|
||||
* @param[in] xpath Xpath of the certificate. Can be used to create the notification data.
|
||||
* @param[in] user_data Arbitrary user data.
|
||||
*/
|
||||
typedef void (*nc_cert_exp_notif_clb)(const char *expiration_time, const char *xpath, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Set the termination reason for a session. Use only in #nc_rpc_clb callbacks.
|
||||
*
|
||||
|
@ -85,47 +98,48 @@ void nc_session_set_status(struct nc_session *session, NC_STATUS status);
|
|||
* @brief Set a global nc_rpc_clb that is called if the particular RPC request is
|
||||
* received and the private field in the corresponding RPC schema node is NULL.
|
||||
*
|
||||
* If this callback is set, the default callbacks for "get-schema" and "close-session" are not used.
|
||||
*
|
||||
* @param[in] clb An user-defined nc_rpc_clb function callback, NULL to default.
|
||||
*/
|
||||
void nc_set_global_rpc_clb(nc_rpc_clb clb);
|
||||
|
||||
/**
|
||||
* @brief Default RPC callback used for "ietf-netconf-monitoring:get-schema" RPC if no other specific
|
||||
* or global callback is set.
|
||||
*
|
||||
* @param[in] rpc Received RPC.
|
||||
* @param[in] session NC session @p rpc was received on.
|
||||
* @return Server reply.
|
||||
*/
|
||||
struct nc_server_reply *nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *session);
|
||||
|
||||
/**
|
||||
* @brief Default RPC callback used for "ietf-netconf:close-session" RPC if no other specific
|
||||
* or global callback is set.
|
||||
*
|
||||
* @param[in] rpc Received RPC.
|
||||
* @param[in] session NC session @p rpc was received on.
|
||||
* @return Server reply.
|
||||
*/
|
||||
struct nc_server_reply *nc_clb_default_close_session(struct lyd_node *rpc, struct nc_session *session);
|
||||
|
||||
/** @} Server Session */
|
||||
|
||||
/**
|
||||
* @addtogroup server
|
||||
* @defgroup server_functions Server Functions
|
||||
* @ingroup server
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize libssh and/or libssl/libcrypto and the server using a libyang context.
|
||||
* @brief Initialize libssh and/or libssl/libcrypto and the server.
|
||||
*
|
||||
* The context is not modified internally, only its dictionary is used for holding
|
||||
* all the strings, which is thread-safe. Reading models is considered thread-safe
|
||||
* as models cannot be removed and are rarely modified (augments or deviations).
|
||||
* Must be called before other nc_server* functions.
|
||||
*
|
||||
* If the RPC callbacks on schema nodes (mentioned in @ref howtoserver) are modified after
|
||||
* server initialization with that particular context, they will be called (changes
|
||||
* will take effect). However, there could be race conditions as the access to
|
||||
* these callbacks is not thread-safe.
|
||||
*
|
||||
* Server capabilities are generated based on its content. Changing the context
|
||||
* in ways that result in changed capabilities (adding models, changing features)
|
||||
* is discouraged after sessions are established as it is not possible to change
|
||||
* capabilities of a session.
|
||||
*
|
||||
* This context can safely be destroyed only after calling the last libnetconf2
|
||||
* function in an application.
|
||||
*
|
||||
* Supported RPCs of models in the context are expected to have their callback
|
||||
* in the corresponding RPC schema node set to a nc_rpc_clb function callback using nc_set_rpc_callback().
|
||||
* This callback is called by nc_ps_poll() if the particular RPC request is
|
||||
* received. Callbacks for ietf-netconf:get-schema (supporting YANG and YIN format
|
||||
* only) and ietf-netconf:close-session are set internally if left unset.
|
||||
*
|
||||
* @param[in] ctx Core NETCONF server context.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_init(struct ly_ctx *ctx);
|
||||
int nc_server_init(void);
|
||||
|
||||
/**
|
||||
* @brief Destroy any dynamically allocated libssh and/or libssl/libcrypto and
|
||||
|
@ -133,6 +147,23 @@ int nc_server_init(struct ly_ctx *ctx);
|
|||
*/
|
||||
void nc_server_destroy(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize a context which can serve as a default server context.
|
||||
*
|
||||
* Loads the default modules ietf-netconf and ietf-netconf-monitoring and their enabled features - ietf-netconf
|
||||
* enabled features are : writable-running, candidate, rollback-on-error, validate, startup, url, xpath, confirmed-commit and
|
||||
* ietf-netconf-monitoring has no features.
|
||||
*
|
||||
* If ctx is :
|
||||
* - NULL: a new context will be created and if the call is successful you have to free it,
|
||||
* - non NULL: context will be searched for the two modules and their features
|
||||
* and if anything is missing, it will be implemented.
|
||||
*
|
||||
* @param[in,out] ctx Optional context in which the modules will be loaded. Created if ctx is null.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_init_ctx(struct ly_ctx **ctx);
|
||||
|
||||
/**
|
||||
* @brief Set the with-defaults capability extra parameters.
|
||||
*
|
||||
|
@ -154,8 +185,8 @@ int nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported);
|
|||
*
|
||||
* At least one argument must be non-NULL.
|
||||
*
|
||||
* @param[in,out] basic_mode basic-mode parameter.
|
||||
* @param[in,out] also_supported also-supported parameter.
|
||||
* @param[out] basic_mode basic-mode parameter.
|
||||
* @param[out] also_supported also-supported parameter.
|
||||
*/
|
||||
void nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported);
|
||||
|
||||
|
@ -181,36 +212,6 @@ int nc_server_set_capability(const char *value);
|
|||
void nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
|
||||
void (*free_user_data)(void *user_data));
|
||||
|
||||
/**
|
||||
* @brief Set server timeout for receiving a hello message.
|
||||
*
|
||||
* @param[in] hello_timeout Hello message timeout. 0 for infinite waiting.
|
||||
*/
|
||||
void nc_server_set_hello_timeout(uint16_t hello_timeout);
|
||||
|
||||
/**
|
||||
* @brief get server timeout for receiving a hello message.
|
||||
*
|
||||
* @return Hello message timeout, 0 is infinite.
|
||||
*/
|
||||
uint16_t nc_server_get_hello_timeout(void);
|
||||
|
||||
/**
|
||||
* @brief Set server timeout for dropping an idle session.
|
||||
*
|
||||
* @param[in] idle_timeout Idle session timeout. 0 to never drop a session
|
||||
* because of inactivity.
|
||||
*/
|
||||
void nc_server_set_idle_timeout(uint16_t idle_timeout);
|
||||
|
||||
/**
|
||||
* @brief Get server timeout for dropping an idle session.
|
||||
*
|
||||
* @return Idle session timeout, 0 for for never dropping
|
||||
* a session because of inactivity.
|
||||
*/
|
||||
uint16_t nc_server_get_idle_timeout(void);
|
||||
|
||||
/**
|
||||
* @brief Get all the server capabilities including all the schemas.
|
||||
*
|
||||
|
@ -218,9 +219,9 @@ uint16_t nc_server_get_idle_timeout(void);
|
|||
* server options.
|
||||
*
|
||||
* @param[in] ctx Context to read most capabilities from.
|
||||
* @return Array of capabilities stored in the @p ctx dictionary, NULL on error.
|
||||
* @return Array of capabilities, NULL on error.
|
||||
*/
|
||||
const char **nc_server_get_cpblts(struct ly_ctx *ctx);
|
||||
char **nc_server_get_cpblts(const struct ly_ctx *ctx);
|
||||
|
||||
/**
|
||||
* @brief Get the server capabilities including the schemas with the specified YANG version.
|
||||
|
@ -231,11 +232,11 @@ const char **nc_server_get_cpblts(struct ly_ctx *ctx);
|
|||
* @param[in] ctx Context to read most capabilities from.
|
||||
* @param[in] version YANG version of the schemas to be included in result, with
|
||||
* LYS_VERSION_UNDEF the result is the same as from nc_server_get_cpblts().
|
||||
* @return Array of capabilities stored in the @p ctx dictionary, NULL on error.
|
||||
* @return Array of capabilities, NULL on error.
|
||||
*/
|
||||
const char **nc_server_get_cpblts_version(struct ly_ctx *ctx, LYS_VERSION version);
|
||||
char **nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version);
|
||||
|
||||
/** @} Server */
|
||||
/** @} Server Functions */
|
||||
|
||||
/**
|
||||
* @addtogroup server_session
|
||||
|
@ -245,14 +246,18 @@ const char **nc_server_get_cpblts_version(struct ly_ctx *ctx, LYS_VERSION versio
|
|||
/**
|
||||
* @brief Accept a new session on a pre-established transport session.
|
||||
*
|
||||
* For detailed description, look at ::nc_accept().
|
||||
*
|
||||
* @param[in] fdin File descriptor to read (unencrypted) XML data from.
|
||||
* @param[in] fdout File descriptor to write (unencrypted) XML data to.
|
||||
* @param[in] username NETCONF username as provided by the transport protocol.
|
||||
* @param[in] ctx Context for the session to use.
|
||||
* @param[out] session New session on success.
|
||||
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
|
||||
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
|
||||
*/
|
||||
NC_MSG_TYPE nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session);
|
||||
NC_MSG_TYPE nc_accept_inout(int fdin, int fdout, const char *username, const struct ly_ctx *ctx,
|
||||
struct nc_session **session);
|
||||
|
||||
/**
|
||||
* @brief Create an empty structure for polling sessions.
|
||||
|
@ -298,6 +303,26 @@ int nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session);
|
|||
*/
|
||||
struct nc_session *nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx);
|
||||
|
||||
/**
|
||||
* @brief Callback for finding a session in a pollsession structure.
|
||||
*
|
||||
* @param[in] session Considered NETCONF session.
|
||||
* @param[in] cb_data User data.
|
||||
* @return 0 if the session does not match.
|
||||
* @return non-zero if the session matches and should be returned.
|
||||
*/
|
||||
typedef int (*nc_ps_session_match_cb)(struct nc_session *session, void *cb_data);
|
||||
|
||||
/**
|
||||
* @brief Find a session in a pollsession structure using a matching callback.
|
||||
*
|
||||
* @param[in] ps Pollsession structure to read from.
|
||||
* @param[in] match_cb Matching callback to use.
|
||||
* @param[in] cb_data User data passed to @p cb.
|
||||
* @return Found session, NULL if none matched.
|
||||
*/
|
||||
struct nc_session *nc_ps_find_session(const struct nc_pollsession *ps, nc_ps_session_match_cb match_cb, void *cb_data);
|
||||
|
||||
/**
|
||||
* @brief Learn the number of sessions in a pollsession structure.
|
||||
*
|
||||
|
@ -317,10 +342,10 @@ uint16_t nc_ps_session_count(struct nc_pollsession *ps);
|
|||
#define NC_PSPOLL_SESSION_ERROR 0x0040 /**< Some session was terminated incorrectly (not by a \<close-session\> or \<kill-session\> RPC). */
|
||||
#define NC_PSPOLL_ERROR 0x0080 /**< Other fatal errors (they are printed). */
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
# define NC_PSPOLL_SSH_MSG 0x00100 /**< SSH message received (and processed, if relevant, only with SSH support). */
|
||||
# define NC_PSPOLL_SSH_CHANNEL 0x0200 /**< New SSH channel opened on an existing session (only with SSH support). */
|
||||
#endif
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
/**
|
||||
* @brief Poll sessions and process any received RPCs.
|
||||
|
@ -353,33 +378,10 @@ void nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *));
|
|||
/** @} Server Session */
|
||||
|
||||
/**
|
||||
* @addtogroup server
|
||||
* @addtogroup server_functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Add a new endpoint.
|
||||
*
|
||||
* Before the endpoint can accept any connections, its address and port must
|
||||
* be set via nc_server_endpt_set_address() and nc_server_endpt_set_port().
|
||||
*
|
||||
* @param[in] name Arbitrary unique endpoint name.
|
||||
* @param[in] ti Transport protocol to use.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti);
|
||||
|
||||
/**
|
||||
* @brief Stop listening on and remove an endpoint.
|
||||
*
|
||||
* @param[in] name Endpoint name. NULL matches all endpoints.
|
||||
* @param[in] ti Endpoint transport protocol. NULL matches any protocol.
|
||||
* Redundant to set if @p name is set, endpoint names are
|
||||
* unique disregarding their protocol.
|
||||
* @return 0 on success, -1 on not finding any match.
|
||||
*/
|
||||
int nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti);
|
||||
|
||||
/**
|
||||
* @brief Get the number of currently configured listening endpoints.
|
||||
* Note that an ednpoint without address and/or port will be included
|
||||
|
@ -390,75 +392,27 @@ int nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti);
|
|||
int nc_server_endpt_count(void);
|
||||
|
||||
/**
|
||||
* @brief Check if an endpoint exists.
|
||||
* @brief Create a new UNIX socket endpoint and start listening.
|
||||
*
|
||||
* @param[in] name Endpoint name.
|
||||
* @return 0 if does not exists, non-zero otherwise.
|
||||
*/
|
||||
int nc_server_is_endpt(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Change endpoint listening address.
|
||||
*
|
||||
* On error the previous listening socket (if any) is left untouched.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] address New listening address.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_endpt_set_address(const char *endpt_name, const char *address);
|
||||
|
||||
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
|
||||
|
||||
/**
|
||||
* @brief Change endpoint listening port.
|
||||
*
|
||||
* This is only valid on SSH/TLS transport endpoint.
|
||||
* On error the previous listening socket (if any) is left untouched.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] port New listening port.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_endpt_set_port(const char *endpt_name, uint16_t port);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Change endpoint permissions.
|
||||
*
|
||||
* This is only valid on UNIX transport endpoint.
|
||||
* On error the previous listening socket (if any) is left untouched.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] endpt_name Arbitrary unique identifier of the endpoint.
|
||||
* @param[in] unix_socket_path Path to the listening socket.
|
||||
* @param[in] mode New mode, -1 to use default.
|
||||
* @param[in] uid New uid, -1 to use default.
|
||||
* @param[in] gid New gid, -1 to use default.
|
||||
* @return 0 on success, -1 on error.
|
||||
*
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid);
|
||||
int nc_server_add_endpt_unix_socket_listen(const char *endpt_name, const char *unix_socket_path, mode_t mode, uid_t uid, gid_t gid);
|
||||
|
||||
/**
|
||||
* @brief Change endpoint keepalives state. Affects only new connections.
|
||||
* @brief Deletes a UNIX socket endpoint.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] enable Whether to enable or disable keepalives.
|
||||
* @return 0 on success, -1 on error.
|
||||
* @param[in] endpt_name Identifier of the endpoint.
|
||||
* Has no effect if the endpoint doesn't exist or if its transport is not UNIX socket.
|
||||
*/
|
||||
int nc_server_endpt_enable_keepalives(const char *endpt_name, int enable);
|
||||
void nc_server_del_endpt_unix_socket(const char *endpt_name);
|
||||
|
||||
/**
|
||||
* @brief Change endpoint keepalives parameters. Affects only new connections.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] idle_time Keepalive idle time in seconds, 1 by default, -1 to keep previous value.
|
||||
* @param[in] max_probes Keepalive max probes sent, 10 by default, -1 to keep previous value.
|
||||
* @param[in] probe_interval Keepalive probe interval in seconds, 5 by default, -1 to keep previous value.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval);
|
||||
|
||||
/** @} Server */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @addtogroup server_session
|
||||
|
@ -472,15 +426,26 @@ int nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int ma
|
|||
* is used for waiting for transport-related data, which means this call can block
|
||||
* for much longer that @p timeout, but only with slow/faulty/malicious clients.
|
||||
*
|
||||
* Server capabilities are generated based on the content of @p ctx. The context must
|
||||
* not be destroyed before the accepted NETCONF session is freed. Basic usable context may
|
||||
* be created by calling ::nc_server_init_ctx().
|
||||
*
|
||||
* Supported RPCs of models in the context are expected to have their callback
|
||||
* in the corresponding RPC schema node set to a nc_rpc_clb function callback using ::nc_set_rpc_callback().
|
||||
* This callback is called by ::nc_ps_poll() if the particular RPC request is
|
||||
* received. Callbacks for ietf-netconf:get-schema (supporting YANG and YIN format
|
||||
* only) and ietf-netconf:close-session are set internally if left unset.
|
||||
*
|
||||
* @param[in] timeout Timeout for receiving a new connection in milliseconds, 0 for
|
||||
* non-blocking call, -1 for infinite waiting.
|
||||
* non-blocking call, -1 for infinite waiting.
|
||||
* @param[in] ctx Context for the session to use.
|
||||
* @param[out] session New session.
|
||||
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
|
||||
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
|
||||
*/
|
||||
NC_MSG_TYPE nc_accept(int timeout, struct nc_session **session);
|
||||
NC_MSG_TYPE nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session);
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/**
|
||||
* @brief Accept a new NETCONF session on an SSH session of a running NETCONF @p orig_session.
|
||||
|
@ -516,172 +481,77 @@ NC_MSG_TYPE nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_sessio
|
|||
*/
|
||||
|
||||
/**
|
||||
* @brief Add an authorized client SSH public key. This public key can be used for
|
||||
* publickey authentication (for any SSH connection, even Call Home) afterwards.
|
||||
* @brief Set the format of the path to authorized_keys files.
|
||||
*
|
||||
* @param[in] pubkey_base64 Authorized public key binary content encoded in base64.
|
||||
* @param[in] type Authorized public key SSH type.
|
||||
* @param[in] username Username that the client with the public key must use.
|
||||
* @return 0 on success, -1 on error.
|
||||
* This path format will be set globally for all clients wishing to authenticate via the
|
||||
* SSH Public Key system authentication.
|
||||
*
|
||||
* @param[in] path Path to authorized_keys files. The path may contain the following tokens:
|
||||
* - %u - replaced by the username of the user trying to authenticate,
|
||||
* - %h - replaced by the home directory of the user trying to authenticate,
|
||||
* - %U - replaced by the UID of the user trying to authenticate,
|
||||
* - %% - a literal '%'.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username);
|
||||
int nc_server_ssh_set_authkey_path_format(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Add an authorized client SSH public key. This public key can be used for
|
||||
* publickey authentication (for any SSH connection, even Call Home) afterwards.
|
||||
* @brief Keyboard interactive authentication callback.
|
||||
*
|
||||
* @param[in] pubkey_path Path to the public key.
|
||||
* @param[in] username Username that the client with the public key must use.
|
||||
* @return 0 on success, -1 on error.
|
||||
* The callback has to handle sending interactive challenges and receiving responses by itself.
|
||||
* An example callback may fit the following description:
|
||||
* Prepare all prompts for the user and send them via `ssh_message_auth_interactive_request()`.
|
||||
* Get the answers either by calling `ssh_message_get()` or `nc_server_ssh_kbdint_get_nanswers()`.
|
||||
* Return value based on your authentication logic and user answers retrieved by
|
||||
* calling `ssh_userauth_kbdint_getanswer()`.
|
||||
*
|
||||
* @param[in] session NETCONF session.
|
||||
* @param[in] ssh_sess libssh session.
|
||||
* @param[in] msg SSH message that contains the interactive request and which expects a reply with prompts.
|
||||
* @param[in] user_data Arbitrary user data.
|
||||
* @return 0 for successful authentication, non-zero to deny the user.
|
||||
*/
|
||||
int nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username);
|
||||
typedef int (*nc_server_ssh_interactive_auth_clb)(const struct nc_session *session,
|
||||
ssh_session ssh_sess, ssh_message msg, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Remove an authorized client SSH public key.
|
||||
* @brief Set the callback for SSH interactive authentication.
|
||||
*
|
||||
* @param[in] pubkey_path Path to an authorized public key. NULL matches all the keys.
|
||||
* @param[in] pubkey_base64 Authorized public key content. NULL matches any key.
|
||||
* @param[in] type Authorized public key type. 0 matches all types.
|
||||
* @param[in] username Username for an authorized public key. NULL matches all the usernames.
|
||||
* @return 0 on success, -1 on not finding any match.
|
||||
*/
|
||||
int nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
|
||||
const char *username);
|
||||
|
||||
/**
|
||||
* @brief Set the callback for SSH password authentication. If none is set, local system users are used.
|
||||
*
|
||||
* @param[in] passwd_auth_clb Callback that should authenticate the user. Username can be directly obtained from @p session.
|
||||
* Zero return indicates success, non-zero an error.
|
||||
* @param[in] user_data Optional arbitrary user data that will be passed to @p passwd_auth_clb.
|
||||
* @param[in] auth_clb Keyboard interactive authentication callback. This callback is only called once per authentication.
|
||||
* @param[in] user_data Optional arbitrary user data that will be passed to @p interactive_auth_clb.
|
||||
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
|
||||
*/
|
||||
void nc_server_ssh_set_passwd_auth_clb(int (*passwd_auth_clb)(const struct nc_session *session, const char *password,
|
||||
void *user_data), void *user_data, void (*free_user_data)(void *user_data));
|
||||
void nc_server_ssh_set_interactive_auth_clb(nc_server_ssh_interactive_auth_clb auth_clb, void *user_data, void (*free_user_data)(void *user_data));
|
||||
|
||||
/**
|
||||
* @brief Set the callback for SSH interactive authentication. If none is set, local system users are used.
|
||||
* @brief Get the number of answers to Keyboard interactive authentication prompts.
|
||||
*
|
||||
* @param[in] interactive_auth_clb Callback that should authenticate the user.
|
||||
* Zero return indicates success, non-zero an error.
|
||||
* @param[in] user_data Optional arbitrary user data that will be passed to @p passwd_auth_clb.
|
||||
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
|
||||
* The actual answers can later be retrieved by calling `ssh_userauth_kbdint_getanswer()` on
|
||||
* the @p libssh_session.
|
||||
*
|
||||
* @param[in] session NETCONF session.
|
||||
* @param[in] libssh_session libssh session.
|
||||
*
|
||||
* @return Non-negative number of answers on success, -1 on configurable authentication timeout,
|
||||
* disconnect or other error.
|
||||
*/
|
||||
void nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session,
|
||||
const ssh_message msg, void *user_data), void *user_data, void (*free_user_data)(void *user_data));
|
||||
int nc_server_ssh_kbdint_get_nanswers(const struct nc_session *session, ssh_session libssh_session);
|
||||
|
||||
/**
|
||||
* @brief Set the callback for SSH public key authentication. If none is set, local system users are used.
|
||||
* @brief Set the name of the PAM configuration file.
|
||||
*
|
||||
* @param[in] pubkey_auth_clb Callback that should authenticate the user.
|
||||
* Zero return indicates success, non-zero an error.
|
||||
* @param[in] user_data Optional arbitrary user data that will be passed to @p passwd_auth_clb.
|
||||
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
|
||||
*/
|
||||
void nc_server_ssh_set_pubkey_auth_clb(int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key,
|
||||
void *user_data), void *user_data, void (*free_user_data)(void *user_data));
|
||||
|
||||
/**
|
||||
* @brief Set the callback for retrieving host keys. Any RSA, DSA, and ECDSA keys can be added. However,
|
||||
* a maximum of one key of each type will be used during SSH authentication, later keys replacing
|
||||
* the earlier ones.
|
||||
* This filename will be set globally for all clients wishing to authenticate via the
|
||||
* SSH Keyboard Interactive authentication method.
|
||||
*
|
||||
* @param[in] hostkey_clb Callback that should return the key itself. Zero return indicates success, non-zero
|
||||
* an error. On success exactly ONE of @p privkey_path or @p privkey_data is expected
|
||||
* to be set. The one set will be freed.
|
||||
* - @p privkey_path expects a PEM file,
|
||||
* - @p privkey_data expects a base-64 encoded ANS.1 DER data,
|
||||
* - @p privkey_type type of the key in @p privkey_data. Use ::NC_SSH_KEY_UNKNOWN for
|
||||
* PKCS#8 key that includes the information about the key in its data.
|
||||
* @param[in] user_data Optional arbitrary user data that will be passed to @p hostkey_clb.
|
||||
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
|
||||
*/
|
||||
void nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
|
||||
char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data, void (*free_user_data)(void *user_data));
|
||||
|
||||
/**
|
||||
* @brief Add endpoint SSH host keys the server will identify itself with. Only the name is set, the key itself
|
||||
* wil be retrieved using a callback.
|
||||
* @param[in] filename Name of the PAM configuration file. The file needs to be located in
|
||||
* the default PAM directory (usually /etc/pam.d/).
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] name Arbitrary name of the host key.
|
||||
* @param[in] idx Optional index where to add the key. -1 adds at the end.
|
||||
* @return 0 on success, -1 on error.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx);
|
||||
|
||||
/**
|
||||
* @brief Delete endpoint SSH host key. Their order is preserved.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] name Name of the host key. NULL matches all the keys, but if @p idx != -1 then this must be NULL.
|
||||
* @param[in] idx Index of the hostkey. -1 matches all indices, but if @p name != NULL then this must be -1.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx);
|
||||
|
||||
/**
|
||||
* @brief Move endpoint SSH host key.
|
||||
*
|
||||
* @param[in] endpt_name Exisitng endpoint name.
|
||||
* @param[in] key_mov Name of the host key that will be moved.
|
||||
* @param[in] key_after Name of the key that will preceed @p key_mov. NULL if @p key_mov is to be moved at the beginning.
|
||||
* @return 0 in success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after);
|
||||
|
||||
/**
|
||||
* @brief Modify endpoint SSH host key.
|
||||
*
|
||||
* @param[in] endpt_name Exisitng endpoint name.
|
||||
* @param[in] name Name of an existing host key.
|
||||
* @param[in] new_name New name of the host key @p name.
|
||||
* @return 0 in success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_endpt_mod_hostkey(const char *endpt_name, const char *name, const char *new_name);
|
||||
|
||||
/**
|
||||
* @brief Set endpoint accepted SSH authentication methods. All (publickey, password, interactive)
|
||||
* are supported by default.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] auth_methods Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods);
|
||||
|
||||
/**
|
||||
* @brief Get endpoint accepted SSH authentication methods.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @return Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
|
||||
*/
|
||||
int nc_server_ssh_endpt_get_auth_methods(const char *endpt_name);
|
||||
|
||||
/**
|
||||
* @brief Set endpoint SSH authentication attempts of every client. 3 by default.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] auth_attempts Failed authentication attempts before a client is dropped.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts);
|
||||
|
||||
/**
|
||||
* @brief Set endpoint SSH authentication timeout. 30 seconds by default.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] auth_timeout Number of seconds before an unauthenticated client is dropped.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout);
|
||||
int nc_server_ssh_set_pam_conf_filename(const char *filename);
|
||||
|
||||
/** @} Server SSH */
|
||||
|
||||
#endif /* NC_ENABLED_SSH */
|
||||
|
||||
#ifdef NC_ENABLED_TLS
|
||||
|
||||
/**
|
||||
* @defgroup server_tls Server TLS
|
||||
* @ingroup server
|
||||
|
@ -690,179 +560,19 @@ int nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_t
|
|||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Set the server TLS certificate. Only the name is set, the certificate itself
|
||||
* wil be retrieved using a callback.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] name Arbitrary certificate name.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_tls_endpt_set_server_cert(const char *endpt_name, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Set the callback for retrieving server certificate and matching private key.
|
||||
*
|
||||
* @param[in] cert_clb Callback that should return the certificate and the key itself. Zero return indicates success,
|
||||
* non-zero an error. On success exactly ONE of @p cert_path or @p cert_data and ONE of
|
||||
* @p privkey_path and @p privkey_data is expected to be set. Those set will be freed.
|
||||
* - @p cert_path expects a PEM file,
|
||||
* - @p cert_data expects a base-64 encoded ASN.1 DER data,
|
||||
* - @p privkey_path expects a PEM file,
|
||||
* - @p privkey_data expects a base-64 encoded ANS.1 DER data,
|
||||
* - @p privkey_type type of the key in @p privkey_data.
|
||||
* @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb.
|
||||
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
|
||||
*/
|
||||
void nc_server_tls_set_server_cert_clb(int (*cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data,
|
||||
char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data,
|
||||
void (*free_user_data)(void *user_data));
|
||||
|
||||
/**
|
||||
* @brief Set the callback for retrieving server certificate chain
|
||||
*
|
||||
* @param[in] cert_chain_clb Callback that should return all the certificates of the chain. Zero return indicates success,
|
||||
* non-zero an error. On success, @p cert_paths and @p cert_data are expected to be set or left
|
||||
* NULL. Both will be (deeply) freed.
|
||||
* - @p cert_paths expect an array of PEM files,
|
||||
* - @p cert_path_count number of @p cert_paths array members,
|
||||
* - @p cert_data expect an array of base-64 encoded ASN.1 DER cert data,
|
||||
* - @p cert_data_count number of @p cert_data array members.
|
||||
* @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb.
|
||||
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
|
||||
*/
|
||||
void nc_server_tls_set_server_cert_chain_clb(int (*cert_chain_clb)(const char *name, void *user_data, char ***cert_paths,
|
||||
int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data));
|
||||
|
||||
/**
|
||||
* @brief Add a trusted certificate list. Can be both a CA or a client one. Can be
|
||||
* safely used together with nc_server_tls_endpt_set_trusted_ca_paths().
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] name Arbitary name identifying this certificate list.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_tls_endpt_add_trusted_cert_list(const char *endpt_name, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Set the callback for retrieving trusted certificates.
|
||||
*
|
||||
* @param[in] cert_list_clb Callback that should return all the certificates of a list. Zero return indicates success,
|
||||
* non-zero an error. On success, @p cert_paths and @p cert_data are expected to be set or left
|
||||
* NULL. Both will be (deeply) freed.
|
||||
* - @p cert_paths expect an array of PEM files,
|
||||
* - @p cert_path_count number of @p cert_paths array members,
|
||||
* - @p cert_data expect an array of base-64 encoded ASN.1 DER cert data,
|
||||
* - @p cert_data_count number of @p cert_data array members.
|
||||
* @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb.
|
||||
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
|
||||
*/
|
||||
void nc_server_tls_set_trusted_cert_list_clb(int (*cert_list_clb)(const char *name, void *user_data, char ***cert_paths,
|
||||
int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data));
|
||||
|
||||
/**
|
||||
* @brief Remove a trusted certificate.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] name Name of the certificate list to delete. NULL deletes all the lists.
|
||||
* @return 0 on success, -1 on not found.
|
||||
*/
|
||||
int nc_server_tls_endpt_del_trusted_cert_list(const char *endpt_name, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Set trusted Certificate Authority certificate locations. There can only be
|
||||
* one file and one directory, they are replaced if already set. Can be safely
|
||||
* used with nc_server_tls_endpt_add_trusted_cert() or its _path variant.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] ca_file Path to a trusted CA cert store file in PEM format. Can be NULL.
|
||||
* @param[in] ca_dir Path to a trusted CA cert store hashed directory (c_rehash utility
|
||||
* can be used to create hashes) with PEM files. Can be NULL.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_tls_endpt_set_trusted_ca_paths(const char *endpt_name, const char *ca_file, const char *ca_dir);
|
||||
|
||||
/**
|
||||
* @brief Set Certificate Revocation List locations. There can only be one file
|
||||
* and one directory, they are replaced if already set.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] crl_file Path to a CRL store file in PEM format. Can be NULL.
|
||||
* @param[in] crl_dir Path to a CRL store hashed directory (c_rehash utility
|
||||
* can be used to create hashes) with PEM files. Can be NULL.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_tls_endpt_set_crl_paths(const char *endpt_name, const char *crl_file, const char *crl_dir);
|
||||
|
||||
/**
|
||||
* @brief Destroy and clean CRLs. Certificates, private keys, and CTN entries are
|
||||
* not affected.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
*/
|
||||
void nc_server_tls_endpt_clear_crls(const char *endpt_name);
|
||||
|
||||
/**
|
||||
* @brief Add a cert-to-name entry.
|
||||
*
|
||||
* It is possible to add an entry step-by-step, specifying first only @p ip and in later calls
|
||||
* @p fingerprint, @p map_type, and optionally @p name spearately.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] id Priority of the entry. It must be unique. If already exists, the entry with this id
|
||||
* is modified.
|
||||
* @param[in] fingerprint Matching certificate fingerprint. If NULL, kept temporarily unset.
|
||||
* @param[in] map_type Type of username-certificate mapping. If 0, kept temporarily unset.
|
||||
* @param[in] name Specific username used only if @p map_type == NC_TLS_CTN_SPECIFED.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_tls_endpt_add_ctn(const char *endpt_name, uint32_t id, const char *fingerprint,
|
||||
NC_TLS_CTN_MAPTYPE map_type, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Remove a cert-to-name entry.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in] id Priority of the entry. -1 matches all the priorities.
|
||||
* @param[in] fingerprint Fingerprint fo the entry. NULL matches all the fingerprints.
|
||||
* @param[in] map_type Mapping type of the entry. 0 matches all the mapping types.
|
||||
* @param[in] name Specific username for the entry. NULL matches all the usernames.
|
||||
* @return 0 on success, -1 on not finding any match.
|
||||
*/
|
||||
int nc_server_tls_endpt_del_ctn(const char *endpt_name, int64_t id, const char *fingerprint,
|
||||
NC_TLS_CTN_MAPTYPE map_type, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Get a cert-to-name entry.
|
||||
*
|
||||
* If a parameter is NULL, it is ignored. If its dereferenced value is NULL,
|
||||
* it is filled and returned. If the value is set, it is used as a filter.
|
||||
* Returns first matching entry.
|
||||
*
|
||||
* @param[in] endpt_name Existing endpoint name.
|
||||
* @param[in,out] id Priority of the entry.
|
||||
* @param[in,out] fingerprint Fingerprint fo the entry.
|
||||
* @param[in,out] map_type Mapping type of the entry.
|
||||
* @param[in,out] name Specific username for the entry.
|
||||
* @return 0 on success, -1 on not finding any match.
|
||||
*/
|
||||
int nc_server_tls_endpt_get_ctn(const char *endpt_name, uint32_t *id, char **fingerprint, NC_TLS_CTN_MAPTYPE *map_type,
|
||||
char **name);
|
||||
|
||||
/**
|
||||
* @brief Get client certificate.
|
||||
*
|
||||
* @param[in] session Session to get the information from.
|
||||
* @return Const session client certificate.
|
||||
*/
|
||||
const X509 *nc_session_get_client_cert(const struct nc_session *session);
|
||||
const void *nc_session_get_client_cert(const struct nc_session *session);
|
||||
|
||||
/**
|
||||
* @brief Set TLS authentication additional verify callback.
|
||||
*
|
||||
* Server will always perform cert-to-name based on its configuration. Only after it passes
|
||||
* and this callback is set, it is also called. It should return exactly what OpenSSL
|
||||
* verify callback meaning 1 for success, 0 to deny the user.
|
||||
* and this callback is set, it is also called. It should return non-zero for success, 0 to deny the user.
|
||||
*
|
||||
* @param[in] verify_clb Additional user verify callback.
|
||||
*/
|
||||
|
@ -870,7 +580,7 @@ void nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *ses
|
|||
|
||||
/** @} Server TLS */
|
||||
|
||||
#endif /* NC_ENABLED_TLS */
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
/**
|
||||
* @addtogroup server_session
|
||||
|
@ -883,7 +593,7 @@ void nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *ses
|
|||
* @param[in] session Session to get the information from.
|
||||
* @return Session start time.
|
||||
*/
|
||||
time_t nc_session_get_start_time(const struct nc_session *session);
|
||||
struct timespec nc_session_get_start_time(const struct nc_session *session);
|
||||
|
||||
/**
|
||||
* @brief Increase session notification subscription flag count.
|
||||
|
@ -912,14 +622,34 @@ void nc_session_dec_notif_status(struct nc_session *session);
|
|||
*/
|
||||
int nc_session_get_notif_status(const struct nc_session *session);
|
||||
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/**
|
||||
* @brief Learn whether a session was created using Call Home or not.
|
||||
* Works only for server sessions.
|
||||
* @brief Start the certificate expiration notification thread.
|
||||
*
|
||||
* @param[in] session Session to get the information from.
|
||||
* @return 0 if a standard session, non-zero if a Call Home session.
|
||||
* The thread will periodically check the expiration time of all certificates in the configuration.
|
||||
* When a notification is about to be generated, the callback @p cert_exp_notif_clb is called.
|
||||
* The times of when these notifications are generated are based on the expiration times of certificates
|
||||
* in the configuration and on the values of intervals set in the configuration. For more information,
|
||||
* see the libnetconf2-netconf-server YANG module.
|
||||
*
|
||||
* @param[in] cert_exp_notif_clb The callback to be called when a notification is generated.
|
||||
* @param[in] user_data Arbitrary user data to pass to the callback.
|
||||
* @param[in] free_data Optional callback to free the user data.
|
||||
*
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_session_is_callhome(const struct nc_session *session);
|
||||
int nc_server_notif_cert_expiration_thread_start(nc_cert_exp_notif_clb cert_exp_notif_clb,
|
||||
void *user_data, void (*free_data)(void *));
|
||||
|
||||
/**
|
||||
* @brief Stop the certificate expiration notification thread.
|
||||
*
|
||||
* @param[in] wait Boolean representing whether to block and wait for the thread to finish.
|
||||
*/
|
||||
void nc_server_notif_cert_expiration_thread_stop(int wait);
|
||||
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
/** @} Server Session */
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* @author Michal Vasko <mvasko@cesnet.cz>
|
||||
* @brief libnetconf2 Call Home session server manipulation
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
|
@ -21,11 +22,12 @@ extern "C" {
|
|||
|
||||
#include <libyang/libyang.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "netconf.h"
|
||||
#include "session.h"
|
||||
|
||||
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/**
|
||||
* @defgroup server_ch Server-side Call Home
|
||||
|
@ -35,21 +37,15 @@ extern "C" {
|
|||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Add a new Call Home client.
|
||||
*
|
||||
* @param[in] name Arbitrary unique client name.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ch_add_client(const char *name);
|
||||
/** @} Server-side Call Home */
|
||||
|
||||
/**
|
||||
* @brief Drop any connections, stop connecting and remove a client.
|
||||
* @defgroup server_ch_functions Server-side Call Home Functions
|
||||
* @ingroup server_ch
|
||||
*
|
||||
* @param[in] name Client name. NULL matches all the clients.
|
||||
* @return 0 on success, -1 on not finding any match.
|
||||
* @brief Server-side Call Home functions.
|
||||
* @{
|
||||
*/
|
||||
int nc_server_ch_del_client(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Check if a Call Home client exists.
|
||||
|
@ -59,28 +55,6 @@ int nc_server_ch_del_client(const char *name);
|
|||
*/
|
||||
int nc_server_ch_is_client(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Add a new Call Home client endpoint.
|
||||
*
|
||||
* @param[in] client_name Existing client name.
|
||||
* @param[in] endpt_name Arbitrary unique (within the client) endpoint name.
|
||||
* @param[in] ti Transport protocol to use.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti);
|
||||
|
||||
/**
|
||||
* @brief Remove a Call Home client endpoint.
|
||||
*
|
||||
* @param[in] client_name Existing client name.
|
||||
* @param[in] endpt_name Existing endpoint of @p client_name. NULL matches all endpoints.
|
||||
* @param[in] ti Client transport protocol. NULL matches any protocol.
|
||||
* Redundant to set if @p endpt_name is set, client names are
|
||||
* unique disregarding their protocol.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti);
|
||||
|
||||
/**
|
||||
* @brief Check if an endpoint of a Call Home client exists.
|
||||
*
|
||||
|
@ -91,343 +65,65 @@ int nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_nam
|
|||
int nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name);
|
||||
|
||||
/**
|
||||
* @brief Change Call Home client endpoint listening address.
|
||||
* @brief Callback for getting a locked context for new Call Home sessions.
|
||||
*
|
||||
* On error the previous listening socket (if any) is left untouched.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of @p client_name.
|
||||
* @param[in] address New listening address.
|
||||
* @return 0 on success, -1 on error.
|
||||
* @param[in] cb_data Arbitrary ctx callback data.
|
||||
* @return Context for the session to use during its lifetime;
|
||||
* @return NULL on error and session fails to be created.
|
||||
*/
|
||||
int nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address);
|
||||
typedef const struct ly_ctx *(*nc_server_ch_session_acquire_ctx_cb)(void *cb_data);
|
||||
|
||||
/**
|
||||
* @brief Change Call Home client endpoint listening port.
|
||||
* @brief Callback for releasing a locked context for Call Home sessions.
|
||||
*
|
||||
* On error the previous listening socket (if any) is left untouched.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of @p client_name.
|
||||
* @param[in] port New listening port.
|
||||
* @return 0 on success, -1 on error.
|
||||
* @param[in] cb_data Arbitrary ctx callback data.
|
||||
*/
|
||||
int nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port);
|
||||
typedef void (*nc_server_ch_session_release_ctx_cb)(void *cb_data);
|
||||
|
||||
/**
|
||||
* @brief Change Call Home client endpoint keepalives state. Affects only new connections.
|
||||
* @brief Callback for new Call Home sessions.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of @p client_name.
|
||||
* @param[in] enable Whether to enable or disable keepalives.
|
||||
* @return 0 on success, -1 on error.
|
||||
* @param[in] client_name Name of the CH client which established the session.
|
||||
* @param[in] new_session New established CH session, the pointer is internally discarded afterwards.
|
||||
* @param[in] user_data Arbitrary new session callback data.
|
||||
* @return 0 on success;
|
||||
* @return non-zero on error and @p new_session is freed.
|
||||
*/
|
||||
int nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable);
|
||||
typedef int (*nc_server_ch_new_session_cb)(const char *client_name, struct nc_session *new_session, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Change Call Home client endpoint keepalives parameters. Affects only new connections.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of @p client_name.
|
||||
* @param[in] idle_time Keepalive idle time in seconds, 1 by default, -1 to keep previous value.
|
||||
* @param[in] max_probes Keepalive max probes sent, 10 by default, -1 to keep previous value.
|
||||
* @param[in] probe_interval Keepalive probe interval in seconds, 5 by default, -1 to keep previous value.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time,
|
||||
int max_probes, int probe_interval);
|
||||
|
||||
/**
|
||||
* @brief Set Call Home client connection type.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] conn_type Call Home connection type.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type);
|
||||
|
||||
/**
|
||||
* @brief Set Call Home client periodic connection period for reconnecting.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] period Call Home periodic connection period in minutes.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period);
|
||||
|
||||
/**
|
||||
* @brief Set Call Home client periodic connection period anchor time.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] anchor_time Call Home periodic connection anchor time for the period.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time);
|
||||
|
||||
/**
|
||||
* @brief Set Call Home client periodic connection idle timeout.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] idle_timeout Call Home periodic idle timeout.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout);
|
||||
|
||||
/**
|
||||
* @brief Set Call Home client start-with policy.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] start_with Call Home client start-with.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with);
|
||||
|
||||
/**
|
||||
* @brief Set Call Home client overall max attempts.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] max_attempts Call Home overall max reconnect attempts.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts);
|
||||
|
||||
/**
|
||||
* @brief Establish a Call Home connection with a listening NETCONF client.
|
||||
* @brief Dispatch a thread connecting to a listening NETCONF client and creating Call Home sessions.
|
||||
*
|
||||
* @param[in] client_name Existing client name.
|
||||
* @param[out] session_clb Function that is called for every established session on the client. @p new_session
|
||||
* pointer is internally discarded afterwards. If the callback returns non-zero, the @p new_session is freed.
|
||||
* @param[in] acquire_ctx_cb Callback for acquiring new session context.
|
||||
* @param[in] release_ctx_cb Callback for releasing session context.
|
||||
* @param[in] ctx_cb_data Arbitrary user data passed to @p acquire_ctx_cb and @p release_ctx_cb.
|
||||
* @param[in] new_session_cb Callback called for every established session on the client.
|
||||
* @param[in] new_session_cb_data Arbitrary user data passed to @p new_session_cb.
|
||||
* @return 0 if the thread was successfully created, -1 on error.
|
||||
*/
|
||||
int nc_connect_ch_client_dispatch(const char *client_name,
|
||||
int (*session_clb)(const char *client_name, struct nc_session *new_session));
|
||||
|
||||
/** @} Server-side Call Home */
|
||||
|
||||
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
|
||||
|
||||
#ifdef NC_ENABLED_SSH
|
||||
int nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
|
||||
nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb,
|
||||
void *new_session_cb_data);
|
||||
|
||||
/**
|
||||
* @defgroup server_ch_ssh Server-side Call Home on SSH
|
||||
* @ingroup server_ch
|
||||
* @brief Set callbacks and their data for Call Home threads.
|
||||
*
|
||||
* @brief SSH settings for the Call Home functionality
|
||||
* @{
|
||||
* If set, Call Home threads will be dispatched automatically upon creation of new Call Home clients.
|
||||
*
|
||||
* @param[in] acquire_ctx_cb Callback for acquiring new session context.
|
||||
* @param[in] release_ctx_cb Callback for releasing session context.
|
||||
* @param[in] ctx_cb_data Arbitrary user data passed to @p acquire_ctx_cb and @p release_ctx_cb.
|
||||
* @param[in] new_session_cb Callback called for every established Call Home session.
|
||||
* @param[in] new_session_cb_data Arbitrary user data passed to @p new_session_cb.
|
||||
*/
|
||||
void nc_server_ch_set_dispatch_data(nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
|
||||
nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb,
|
||||
void *new_session_cb_data);
|
||||
|
||||
/**
|
||||
* @brief Add Call Home SSH host keys the server will identify itself with. Only the name is set, the key itself
|
||||
* wil be retrieved using a callback.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] name Arbitrary name of the host key.
|
||||
* @param[in] idx Optional index where to add the key. -1 adds at the end.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_ch_client_endpt_add_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx);
|
||||
/** @} Server-side Call Home Functions */
|
||||
|
||||
/**
|
||||
* @brief Delete Call Home SSH host keys. Their order is preserved.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] name Name of the host key. NULL matches all the keys, but if @p idx != -1 then this must be NULL.
|
||||
* @param[in] idx Index of the hostkey. -1 matches all indices, but if @p name != NULL then this must be -1.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_ch_client_endpt_del_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx);
|
||||
|
||||
/**
|
||||
* @brief Move Call Home SSH host key.
|
||||
*
|
||||
* @param[in] client_name Exisitng Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] key_mov Name of the host key that will be moved.
|
||||
* @param[in] key_after Name of the key that will preceed @p key_mov. NULL if @p key_mov is to be moved at the beginning.
|
||||
* @return 0 in success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov,
|
||||
const char *key_after);
|
||||
|
||||
/**
|
||||
* @brief Set accepted Call Home SSH authentication methods. All (publickey, password, interactive)
|
||||
* are supported by default.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] auth_methods Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_ch_client_endpt_set_auth_methods(const char *client_name, const char *endpt_name, int auth_methods);
|
||||
|
||||
/**
|
||||
* @brief Get accepted Call Home SSH authentication methods.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @return Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
|
||||
*/
|
||||
int nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name);
|
||||
|
||||
/**
|
||||
* @brief Set Call Home SSH authentication attempts of every client. 3 by default.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] auth_attempts Failed authentication attempts before a client is dropped.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_ch_client_endpt_set_auth_attempts(const char *client_name, const char *endpt_name, uint16_t auth_attempts);
|
||||
|
||||
/**
|
||||
* @brief Set Call Home SSH authentication timeout. 30 seconds by default.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] auth_timeout Number of seconds before an unauthenticated client is dropped.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_ssh_ch_client_endpt_set_auth_timeout(const char *client_name, const char *endpt_name, uint16_t auth_timeout);
|
||||
|
||||
/** @} Server-side Call Home on SSH */
|
||||
|
||||
#endif /* NC_ENABLED_SSH */
|
||||
|
||||
#ifdef NC_ENABLED_TLS
|
||||
|
||||
/**
|
||||
* @defgroup server_ch_tls Server-side Call Home on TLS
|
||||
* @ingroup server_ch
|
||||
*
|
||||
* @brief TLS settings for the Call Home functionality
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Set the server Call Home TLS certificate. Only the name is set, the certificate itself
|
||||
* wil be retrieved using a callback.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] name Arbitrary certificate name.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_tls_ch_client_endpt_set_server_cert(const char *client_name, const char *endpt_name, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Add a Call Home trusted certificate list. Can be both a CA or a client one.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] name Arbitary name identifying this certificate list.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_tls_ch_client_endpt_add_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Remove a set Call Home trusted certificate list. CRLs and CTN entries are not affected.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] name Name of the certificate list to delete. NULL deletes all the lists.
|
||||
* @return 0 on success, -1 on not found.
|
||||
*/
|
||||
int nc_server_tls_ch_client_endpt_del_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Set trusted Call Home Certificate Authority certificate locations. There
|
||||
* can only be one file and one directory, they are replaced if already set.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] ca_file Path to a trusted CA cert store file in PEM format.
|
||||
* Can be NULL.
|
||||
* @param[in] ca_dir Path to a trusted CA cert store hashed directory
|
||||
* (c_rehash utility can be used to create hashes)
|
||||
* with PEM files. Can be NULL.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_tls_ch_client_endpt_set_trusted_ca_paths(const char *client_name, const char *endpt_name, const char *ca_file,
|
||||
const char *ca_dir);
|
||||
|
||||
/**
|
||||
* @brief Set Call Home Certificate Revocation List locations. There can only be
|
||||
* one file and one directory, they are replaced if already set.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] crl_file Path to a CRL store file in PEM format. Can be NULL.
|
||||
* @param[in] crl_dir Path to a CRL store hashed directory (c_rehash utility
|
||||
* can be used to create hashes) with PEM files. Can be NULL.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_tls_ch_client_endpt_set_crl_paths(const char *client_name, const char *endpt_name, const char *crl_file,
|
||||
const char *crl_dir);
|
||||
|
||||
/**
|
||||
* @brief Destroy and clean Call Home CRLs. Call Home certificates, private keys,
|
||||
* and CTN entries are not affected.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
*/
|
||||
void nc_server_tls_ch_client_endpt_clear_crls(const char *client_name, const char *endpt_name);
|
||||
|
||||
/**
|
||||
* @brief Add a cert-to-name entry.
|
||||
*
|
||||
* It is possible to add an entry step-by-step, specifying first only @p ip and in later calls
|
||||
* @p fingerprint, @p map_type, and optionally @p name spearately.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] id Priority of the entry. It must be unique. If already exists, the entry with this id
|
||||
* is modified.
|
||||
* @param[in] fingerprint Matching certificate fingerprint. If NULL, kept temporarily unset.
|
||||
* @param[in] map_type Type of username-certificate mapping. If 0, kept temporarily unset.
|
||||
* @param[in] name Specific username used only if @p map_type == NC_TLS_CTN_SPECIFED.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_server_tls_ch_client_endpt_add_ctn(const char *client_name, const char *endpt_name, uint32_t id,
|
||||
const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Remove a Call Home cert-to-name entry.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in] id Priority of the entry. -1 matches all the priorities.
|
||||
* @param[in] fingerprint Fingerprint fo the entry. NULL matches all the fingerprints.
|
||||
* @param[in] map_type Mapping type of the entry. 0 matches all the mapping types.
|
||||
* @param[in] name Specific username for the entry. NULL matches all the usernames.
|
||||
* @return 0 on success, -1 on not finding any match.
|
||||
*/
|
||||
int nc_server_tls_ch_client_endpt_del_ctn(const char *client_name, const char *endpt_name, int64_t id,
|
||||
const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Get a Call Home cert-to-name entry.
|
||||
*
|
||||
* If a parameter is NULL, it is ignored. If its dereferenced value is NULL,
|
||||
* it is filled and returned. If the value is set, it is used as a filter.
|
||||
* Returns first matching entry.
|
||||
*
|
||||
* @param[in] client_name Existing Call Home client name.
|
||||
* @param[in] endpt_name Existing endpoint name of the client.
|
||||
* @param[in,out] id Priority of the entry.
|
||||
* @param[in,out] fingerprint Fingerprint fo the entry.
|
||||
* @param[in,out] map_type Mapping type of the entry.
|
||||
* @param[in,out] name Specific username for the entry.
|
||||
* @return 0 on success, -1 on not finding any match.
|
||||
*/
|
||||
int nc_server_tls_ch_client_endpt_get_ctn(const char *client_name, const char *endpt_name, uint32_t *id, char **fingerprint,
|
||||
NC_TLS_CTN_MAPTYPE *map_type, char **name);
|
||||
|
||||
/** @} Server-side Call Home on TLS */
|
||||
|
||||
#endif /* NC_ENABLED_TLS */
|
||||
#endif /* NC_ENABLED_SSH_TLS */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
742
src/session_wrapper.h
Normal file
742
src/session_wrapper.h
Normal file
|
@ -0,0 +1,742 @@
|
|||
/**
|
||||
* @file session_wrapper.h
|
||||
* @author Roman Janota <janota@cesnet.cz>
|
||||
* @brief libnetconf2 - header for wrapped TLS library function calls (currently OpenSSL and MbedTLS)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2024 CESNET, z.s.p.o.
|
||||
*
|
||||
* This source code is licensed under BSD 3-Clause License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _SESSION_WRAPPER_H_
|
||||
#define _SESSION_WRAPPER_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_MBEDTLS
|
||||
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/pk.h>
|
||||
#include <mbedtls/ssl.h>
|
||||
#include <mbedtls/x509_crl.h>
|
||||
#include <mbedtls/x509_crt.h>
|
||||
|
||||
/**
|
||||
* @brief Context from which a TLS session may be created.
|
||||
*/
|
||||
struct nc_tls_ctx {
|
||||
int *sock; /**< Socket FD. */
|
||||
mbedtls_entropy_context *entropy; /**< Entropy. */
|
||||
mbedtls_ctr_drbg_context *ctr_drbg; /**< Random bit generator. */
|
||||
mbedtls_x509_crt *cert; /**< Certificate. */
|
||||
mbedtls_pk_context *pkey; /**< Private key. */
|
||||
mbedtls_x509_crt *cert_store; /**< CA certificates store. */
|
||||
mbedtls_x509_crl *crl_store; /**< CRL store. */
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
/**
|
||||
* @brief Context from which a TLS session may be created.
|
||||
*/
|
||||
struct nc_tls_ctx {
|
||||
X509 *cert; /**< Certificate. */
|
||||
EVP_PKEY *pkey; /**< Private key. */
|
||||
X509_STORE *cert_store; /**< CA certificate store. */
|
||||
X509_STORE *crl_store; /**< CRL store. */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Server side TLS verify callback data.
|
||||
*/
|
||||
struct nc_tls_verify_cb_data {
|
||||
struct nc_session *session; /**< NETCONF session. */
|
||||
struct nc_server_tls_opts *opts; /**< TLS server options. */
|
||||
void *chain; /**< Certificate chain used to verify the client cert. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Creates a new TLS session from the given configuration.
|
||||
*
|
||||
* @param[in] tls_cfg TLS configuration.
|
||||
* @return New TLS session on success, NULL on fail.
|
||||
*/
|
||||
void * nc_tls_session_new_wrap(void *tls_cfg);
|
||||
|
||||
/**
|
||||
* @brief Destroys a TLS session.
|
||||
*
|
||||
* @param[in] tls_session TLS session to destroy.
|
||||
*/
|
||||
void nc_tls_session_destroy_wrap(void *tls_session);
|
||||
|
||||
/**
|
||||
* @brief Creates a new TLS configuration.
|
||||
*
|
||||
* @param[in] side Side of the TLS connection.
|
||||
* @return New TLS configuration on success, NULL on fail.
|
||||
*/
|
||||
void * nc_tls_config_new_wrap(int side);
|
||||
|
||||
/**
|
||||
* @brief Destroys a TLS configuration.
|
||||
*
|
||||
* @param[in] tls_cfg TLS configuration to destroy.
|
||||
*/
|
||||
void nc_tls_config_destroy_wrap(void *tls_cfg);
|
||||
|
||||
/**
|
||||
* @brief Creates a new TLS certificate.
|
||||
*
|
||||
* @return New TLS certificate on success, NULL on fail.
|
||||
*/
|
||||
void * nc_tls_cert_new_wrap(void);
|
||||
|
||||
/**
|
||||
* @brief Destroys a TLS certificate.
|
||||
*
|
||||
* @param[in] cert TLS certificate to destroy.
|
||||
*/
|
||||
void nc_tls_cert_destroy_wrap(void *cert);
|
||||
|
||||
/**
|
||||
* @brief Destroys a TLS private key.
|
||||
*
|
||||
* @param[in] pkey TLS private key to destroy.
|
||||
*/
|
||||
void nc_tls_privkey_destroy_wrap(void *pkey);
|
||||
|
||||
/**
|
||||
* @brief Creates a new TLS certificate store.
|
||||
*
|
||||
* @return New TLS certificate store on success, NULL on fail.
|
||||
*/
|
||||
void * nc_tls_cert_store_new_wrap(void);
|
||||
|
||||
/**
|
||||
* @brief Destroys a TLS certificate store.
|
||||
*
|
||||
* @param[in] cert_store TLS certificate store to destroy.
|
||||
*/
|
||||
void nc_tls_cert_store_destroy_wrap(void *cert_store);
|
||||
|
||||
/**
|
||||
* @brief Creates a new CRL store.
|
||||
*
|
||||
* @return New CRL store on success, NULL on fail.
|
||||
*/
|
||||
void * nc_tls_crl_store_new_wrap(void);
|
||||
|
||||
/**
|
||||
* @brief Destroys a CRL store.
|
||||
*
|
||||
* @param[in] crl_store CRL store to destroy.
|
||||
*/
|
||||
void nc_tls_crl_store_destroy_wrap(void *crl_store);
|
||||
|
||||
/**
|
||||
* @brief Converts PEM certificate data to a certificate.
|
||||
*
|
||||
* @param[in] cert_data PEM certificate data.
|
||||
* @return New certificate on success, NULL on fail.
|
||||
*/
|
||||
void * nc_tls_pem_to_cert_wrap(const char *cert_data);
|
||||
|
||||
/**
|
||||
* @brief Adds a certificate to a certificate store.
|
||||
*
|
||||
* @param[in] cert Certificate to add.
|
||||
* @param[in] cert_store Certificate store to add the certificate to.
|
||||
* @return 0 on success and the memory belongs to cert_store, non-zero on fail.
|
||||
*/
|
||||
int nc_tls_add_cert_to_store_wrap(void *cert, void *cert_store);
|
||||
|
||||
/**
|
||||
* @brief Converts PEM private key data to a private key.
|
||||
*
|
||||
* @param[in] privkey_data PEM private key data.
|
||||
* @return New private key on success, NULL on fail.
|
||||
*/
|
||||
void * nc_tls_pem_to_privkey_wrap(const char *privkey_data);
|
||||
|
||||
/**
|
||||
* @brief Parses and adds a CRL to a CRL store.
|
||||
*
|
||||
* @param[in] crl_data CRL data.
|
||||
* @param[in] size Size of the CRL data.
|
||||
* @param[in] crl_store CRL store to add the CRL to.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *crl_store);
|
||||
|
||||
/**
|
||||
* @brief Sets the TLS version.
|
||||
*
|
||||
* @param[in] tls_cfg TLS configuration.
|
||||
* @param[in] tls_versions Bit-field of supported TLS versions.
|
||||
*
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions);
|
||||
|
||||
/**
|
||||
* @brief Set TLS server's verify flags, verify cb and its data.
|
||||
*
|
||||
* @param[in] tls_cfg TLS configuration.
|
||||
* @param[in] cb_data Verify callback data.
|
||||
*/
|
||||
void nc_server_tls_set_verify_wrap(void *tls_cfg, struct nc_tls_verify_cb_data *cb_data);
|
||||
|
||||
/**
|
||||
* @brief Set TLS client's verify flags.
|
||||
*
|
||||
* @param[in] tls_cfg TLS configuration.
|
||||
*/
|
||||
void nc_client_tls_set_verify_wrap(void *tls_cfg);
|
||||
|
||||
/**
|
||||
* @brief Verify the certificate.
|
||||
*
|
||||
* @param[in] cert Certificate to verify.
|
||||
* @param[in] depth Certificate depth.
|
||||
* @param[in] trusted Boolean flag representing whether the certificate is trusted.
|
||||
* @param[in] cb_data Data for the verify callback.
|
||||
* @return 0 on success, 1 on verify fail, -1 on fatal error.
|
||||
*/
|
||||
int nc_server_tls_verify_cert(void *cert, int depth, int trusted, struct nc_tls_verify_cb_data *cb_data);
|
||||
|
||||
/**
|
||||
* @brief Check if the peer certificate matches any configured ee certs.
|
||||
*
|
||||
* @param[in] peer_cert Peer certificate.
|
||||
* @param[in] opts TLS options.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_server_tls_opts *opts);
|
||||
|
||||
/**
|
||||
* @brief Get the subject of the certificate.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
* @return Subject of the certificate on success, NULL on fail.
|
||||
*/
|
||||
char * nc_server_tls_get_subject_wrap(void *cert);
|
||||
|
||||
/**
|
||||
* @brief Get the issuer of the certificate.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
* @return Issuer of the certificate on success, NULL on fail.
|
||||
*/
|
||||
char * nc_server_tls_get_issuer_wrap(void *cert);
|
||||
|
||||
/**
|
||||
* @brief Get the Subject Alternative Names of the certificate.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
* @return SANs on success, NULL on fail.
|
||||
*/
|
||||
void * nc_tls_get_sans_wrap(void *cert);
|
||||
|
||||
/**
|
||||
* @brief Destroy the SANs.
|
||||
*
|
||||
* @param[in] sans SANs to destroy.
|
||||
*/
|
||||
void nc_tls_sans_destroy_wrap(void *sans);
|
||||
|
||||
/**
|
||||
* @brief Get the number of SANs.
|
||||
*
|
||||
* @param[in] sans SANs.
|
||||
* @return Number of SANs.
|
||||
*/
|
||||
int nc_tls_get_num_sans_wrap(void *sans);
|
||||
|
||||
#ifdef NC_ENABLED_SSH_TLS
|
||||
|
||||
/**
|
||||
* @brief Get the SAN value and type in the context of CTN.
|
||||
*
|
||||
* @param[in] sans SANs.
|
||||
* @param[in] idx Index of the SAN.
|
||||
* @param[out] san_value SAN value.
|
||||
* @param[out] san_type SAN type.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN_MAPTYPE *san_type);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get the number of certificates in a certificate chain.
|
||||
*
|
||||
* @param[in] chain Certificate chain.
|
||||
* @return Number of certificates in the chain.
|
||||
*/
|
||||
int nc_tls_get_num_certs_wrap(void *chain);
|
||||
|
||||
/**
|
||||
* @brief Get a certificate from a certificate chain.
|
||||
*
|
||||
* @param[in] chain Certificate chain.
|
||||
* @param[in] idx Index of the certificate to get.
|
||||
* @param[out] cert Certificate.
|
||||
*/
|
||||
void nc_tls_get_cert_wrap(void *chain, int idx, void **cert);
|
||||
|
||||
/**
|
||||
* @brief Compare two certificates.
|
||||
*
|
||||
* @param[in] cert1 Certificate 1.
|
||||
* @param[in] cert2 Certificate 2.
|
||||
* @return 1 if the certificates match, 0 otherwise.
|
||||
*/
|
||||
int nc_server_tls_certs_match_wrap(void *cert1, void *cert2);
|
||||
|
||||
/**
|
||||
* @brief Get the MD5 digest of the certificate.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
* @param[out] buf Buffer for the digest.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_server_tls_md5_wrap(void *cert, unsigned char *buf);
|
||||
|
||||
/**
|
||||
* @brief Get the SHA1 digest of the certificate.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
* @param[out] buf Buffer for the digest.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_server_tls_sha1_wrap(void *cert, unsigned char *buf);
|
||||
|
||||
/**
|
||||
* @brief Get the SHA224 digest of the certificate.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
* @param[out] buf Buffer for the digest.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_server_tls_sha224_wrap(void *cert, unsigned char *buf);
|
||||
|
||||
/**
|
||||
* @brief Get the SHA256 digest of the certificate.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
* @param[out] buf Buffer for the digest.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_server_tls_sha256_wrap(void *cert, unsigned char *buf);
|
||||
|
||||
/**
|
||||
* @brief Get the SHA384 digest of the certificate.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
* @param[out] buf Buffer for the digest.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_server_tls_sha384_wrap(void *cert, unsigned char *buf);
|
||||
|
||||
/**
|
||||
* @brief Get the SHA512 digest of the certificate.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
* @param[out] buf Buffer for the digest.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_server_tls_sha512_wrap(void *cert, unsigned char *buf);
|
||||
|
||||
/**
|
||||
* @brief Set the FD for a TLS session.
|
||||
*
|
||||
* @param[in] tls_session TLS session.
|
||||
* @param[in] sock Socket FD.
|
||||
* @param[in] tls_ctx TLS context.
|
||||
*/
|
||||
void nc_tls_set_fd_wrap(void *tls_session, int sock, struct nc_tls_ctx *tls_ctx);
|
||||
|
||||
/**
|
||||
* @brief Perform a server-side step of the TLS handshake.
|
||||
*
|
||||
* @param[in] tls_session TLS session.
|
||||
* @return 1 on success, 0 if the handshake is not finished, negative number on error.
|
||||
*/
|
||||
int nc_server_tls_handshake_step_wrap(void *tls_session);
|
||||
|
||||
/**
|
||||
* @brief Perform a client-side step of the TLS handshake.
|
||||
*
|
||||
* @param[in] tls_session TLS session.
|
||||
* @param[in] sock Socket FD.
|
||||
* @return 1 on success, 0 if the handshake is not finished, negative number on error.
|
||||
*/
|
||||
int nc_client_tls_handshake_step_wrap(void *tls_session, int sock);
|
||||
|
||||
/**
|
||||
* @brief Destroy a TLS context.
|
||||
*
|
||||
* @param[in] tls_ctx TLS context.
|
||||
*/
|
||||
void nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *tls_ctx);
|
||||
|
||||
/**
|
||||
* @brief Load client's certificate and a private key.
|
||||
*
|
||||
* @param[in] cert_path Path to the certificate.
|
||||
* @param[in] key_path Path to the private key.
|
||||
* @param[out] cert Certificate.
|
||||
* @param[out] pkey Private key.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, void **cert, void **pkey);
|
||||
|
||||
/**
|
||||
* @brief Load client's trusted certificates.
|
||||
*
|
||||
* @param[in] cert_store Certificate store.
|
||||
* @param[in] file_path Path to the file with trusted certificates.
|
||||
* @param[in] dir_path Path to the directory with trusted certificates.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, const char *dir_path);
|
||||
|
||||
/**
|
||||
* @brief Set the hostname for the TLS session.
|
||||
*
|
||||
* @param[in] tls_session TLS session.
|
||||
* @param[in] hostname Hostname.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname);
|
||||
|
||||
/**
|
||||
* @brief Initialize a TLS context.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
* @param[in] pkey Private key.
|
||||
* @param[in] cert_store Certificate store.
|
||||
* @param[in] crl_store CRL store.
|
||||
* @param[in,out] tls_ctx TLS context.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_tls_init_ctx_wrap(void *cert, void *pkey, void *cert_store, void *crl_store, struct nc_tls_ctx *tls_ctx);
|
||||
|
||||
/**
|
||||
* @brief Setup a TLS configuration from a TLS context.
|
||||
*
|
||||
* @param[in] tls_ctx TLS context.
|
||||
* @param[in] side Side of the TLS connection.
|
||||
* @param[in,out] tls_cfg TLS configuration.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tls_cfg);
|
||||
|
||||
/**
|
||||
* @brief Get the error code from a TLS session's verification.
|
||||
*
|
||||
* @param[in] tls_session TLS session.
|
||||
* @return Error code, 0 indicates success.
|
||||
*/
|
||||
uint32_t nc_tls_get_verify_result_wrap(void *tls_session);
|
||||
|
||||
/**
|
||||
* @brief Get the error string from a TLS session's verification.
|
||||
*
|
||||
* @param[in] err_code Error code.
|
||||
* @return Error string.
|
||||
*/
|
||||
char * nc_tls_verify_error_string_wrap(uint32_t err_code);
|
||||
|
||||
/**
|
||||
* @brief Print the TLS session's connection error.
|
||||
*
|
||||
* @param[in] connect_ret Error code.
|
||||
* @param[in] peername Peername.
|
||||
* @param[in] tls_session TLS session.
|
||||
*/
|
||||
void nc_client_tls_print_connect_err_wrap(int connect_ret, const char *peername, void *tls_session);
|
||||
|
||||
/**
|
||||
* @brief Print the TLS session's accept error.
|
||||
*
|
||||
* @param[in] accept_ret Error code.
|
||||
* @param[in] tls_session TLS session.
|
||||
*/
|
||||
void nc_server_tls_print_accept_err_wrap(int accept_ret, void *tls_session);
|
||||
|
||||
/**
|
||||
* @brief Checks if the DER data is a SubjectPublicKeyInfo public key.
|
||||
*
|
||||
* @param[in] der DER data.
|
||||
* @param[in] len Length of the DER data.
|
||||
*
|
||||
* @return 1 if the data is a SubjectPublicKeyInfo public key, 0 if not, -1 on error.
|
||||
*/
|
||||
int nc_tls_is_der_subpubkey_wrap(unsigned char *der, long len);
|
||||
|
||||
/**
|
||||
* @brief Decodes base64 to binary.
|
||||
*
|
||||
* @param[in] base64 Base64 string.
|
||||
* @param[out] bin Binary result, memory managed by the caller.
|
||||
* @return Length of the binary data on success, -1 on error.
|
||||
*/
|
||||
int nc_base64_decode_wrap(const char *base64, unsigned char **bin);
|
||||
|
||||
/**
|
||||
* @brief Encodes binary to base64.
|
||||
*
|
||||
* @param[in] bin Binary data.
|
||||
* @param[in] len Length of the binary data.
|
||||
* @param[out] base64 NULL terminated Base64 result, memory managed by the caller.
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64);
|
||||
|
||||
/**
|
||||
* @brief Reads data from a TLS session.
|
||||
*
|
||||
* @param[in] session NETCONF session.
|
||||
* @param[out] buf Buffer for the data.
|
||||
* @param[in] size Size of the buffer.
|
||||
* @return Number of bytes read on success, -1 on error.
|
||||
*/
|
||||
int nc_tls_read_wrap(struct nc_session *session, unsigned char *buf, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Writes data to a TLS session.
|
||||
*
|
||||
* @param[in] session NETCONF session.
|
||||
* @param[in] buf Data to write.
|
||||
* @param[in] size Size of the data.
|
||||
* @return Number of bytes written on success, -1 on error.
|
||||
*/
|
||||
int nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Get the number of pending bytes in a TLS session.
|
||||
*
|
||||
* @param[in] tls_session TLS session.
|
||||
* @return Number of pending bytes.
|
||||
*/
|
||||
int nc_tls_get_num_pending_bytes_wrap(void *tls_session);
|
||||
|
||||
/**
|
||||
* @brief Get the file descriptor of a TLS session.
|
||||
*
|
||||
* @param[in] session NETCONF session.
|
||||
* @return File descriptor, -1 on error.
|
||||
*/
|
||||
int nc_tls_get_fd_wrap(const struct nc_session *session);
|
||||
|
||||
/**
|
||||
* @brief Close a TLS session.
|
||||
*
|
||||
* @param[in] tls_session TLS session.
|
||||
*/
|
||||
void nc_tls_close_notify_wrap(void *tls_session);
|
||||
|
||||
/**
|
||||
* @brief Import a private key from a file.
|
||||
*
|
||||
* @param[in] privkey_path Path to the private key file.
|
||||
* @return Imported private key on success, NULL on fail.
|
||||
*/
|
||||
void * nc_tls_import_privkey_file_wrap(const char *privkey_path);
|
||||
|
||||
/**
|
||||
* @brief Import a certificate from a file.
|
||||
*
|
||||
* @param[in] cert_path Path to the certificate file.
|
||||
* @return Imported certificate on success, NULL on fail.
|
||||
*/
|
||||
void * nc_tls_import_cert_file_wrap(const char *cert_path);
|
||||
|
||||
/**
|
||||
* @brief Export a private key to a PEM string.
|
||||
*
|
||||
* @param[in] pkey Private key.
|
||||
* @return PEM string on success, NULL on fail.
|
||||
*/
|
||||
char * nc_tls_export_privkey_pem_wrap(void *pkey);
|
||||
|
||||
/**
|
||||
* @brief Export a certificate to a PEM string.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
* @return PEM string on success, NULL on fail.
|
||||
*/
|
||||
char * nc_tls_export_cert_pem_wrap(void *cert);
|
||||
|
||||
/**
|
||||
* @brief Export a public key to a PEM string.
|
||||
*
|
||||
* @param[in] pkey Public key.
|
||||
* @return PEM string on success, NULL on fail.
|
||||
*/
|
||||
char * nc_tls_export_pubkey_pem_wrap(void *pkey);
|
||||
|
||||
/**
|
||||
* @brief Check if a private key is RSA.
|
||||
*
|
||||
* @param[in] pkey Private key.
|
||||
* @return 1 if the private key is RSA, 0 if not.
|
||||
*/
|
||||
int nc_tls_privkey_is_rsa_wrap(void *pkey);
|
||||
|
||||
/**
|
||||
* @brief Get the RSA public key parameters from a private key.
|
||||
*
|
||||
* @param[in] pkey Private key.
|
||||
* @param[out] e Exponent.
|
||||
* @param[out] n Modulus.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n);
|
||||
|
||||
/**
|
||||
* @brief Destroy an MPI.
|
||||
*
|
||||
* @param[in] mpi MPI.
|
||||
*/
|
||||
void nc_tls_destroy_mpi_wrap(void *mpi);
|
||||
|
||||
/**
|
||||
* @brief Check if a private key is EC.
|
||||
*
|
||||
* @param[in] pkey Private key.
|
||||
* @return 1 if the private key is EC, 0 if not.
|
||||
*/
|
||||
int nc_tls_privkey_is_ec_wrap(void *pkey);
|
||||
|
||||
/**
|
||||
* @brief Get the group name of an EC private key.
|
||||
*
|
||||
* @param[in] pkey Private key.
|
||||
* @return Group name on success, NULL on fail.
|
||||
*/
|
||||
char * nc_tls_get_ec_group_wrap(void *pkey);
|
||||
|
||||
/**
|
||||
* @brief Get the EC public key parameters from a private key.
|
||||
*
|
||||
* @param[in] pkey Private key.
|
||||
* @param[out] q Public key point.
|
||||
* @param[out] q_grp Public key group.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_tls_get_ec_pubkey_params_wrap(void *pkey, void **q, void **q_grp);
|
||||
|
||||
/**
|
||||
* @brief Convert an EC point to binary.
|
||||
*
|
||||
* @param[in] q EC point.
|
||||
* @param[in] q_grp EC group.
|
||||
* @param[out] bin Binary point.
|
||||
* @param[out] bin_len Length of the binary point.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_tls_ec_point_to_bin_wrap(void *q, void *q_grp, unsigned char **bin, int *bin_len);
|
||||
|
||||
/**
|
||||
* @brief Destroy an EC point.
|
||||
*
|
||||
* @param[in] p EC point.
|
||||
*/
|
||||
void nc_tls_ec_point_destroy_wrap(void *p);
|
||||
|
||||
/**
|
||||
* @brief Destroy an EC group.
|
||||
*
|
||||
* @param[in] grp EC group.
|
||||
*/
|
||||
void nc_tls_ec_group_destroy_wrap(void *grp);
|
||||
|
||||
/**
|
||||
* @brief Convert an MPI to binary.
|
||||
*
|
||||
* @param[in] mpi MPI.
|
||||
* @param[out] bin Binary buffer.
|
||||
* @param[out] bin_len Length of the binary.
|
||||
* @return 0 on success, 1 on error.
|
||||
*/
|
||||
int nc_tls_mpi2bin_wrap(void *mpi, unsigned char **bin, int *bin_len);
|
||||
|
||||
/**
|
||||
* @brief Import a public key from a file.
|
||||
*
|
||||
* @param[in] pubkey_path Path to the public key file.
|
||||
* @return Imported public key on success, NULL on fail.
|
||||
*/
|
||||
void * nc_tls_import_pubkey_file_wrap(const char *pubkey_path);
|
||||
|
||||
/**
|
||||
* @brief Get all the URIs from the CRLDistributionPoints x509v3 extensions.
|
||||
*
|
||||
* @param[in] leaf_cert Server/client certificate.
|
||||
* @param[in] cert_store Certificate store.
|
||||
* @param[out] uris URIs to download the CRLs from.
|
||||
* @param[out] uri_count Number of URIs found.
|
||||
* @return 0 on success, non-zero on fail.
|
||||
*/
|
||||
int nc_server_tls_get_crl_distpoint_uris_wrap(void *leaf_cert, void *cert_store, char ***uris, int *uri_count);
|
||||
|
||||
/**
|
||||
* @brief Process a cipher suite so that it can be set by the underlying TLS lib.
|
||||
*
|
||||
* @param[in] cipher Cipher suite identity value.
|
||||
* @param[out] out Processed cipher suite.
|
||||
* @return 0 on success, 1 on fail.
|
||||
*/
|
||||
int nc_tls_process_cipher_suite_wrap(const char *cipher, char **out);
|
||||
|
||||
/**
|
||||
* @brief Append a cipher suite to the list of cipher suites.
|
||||
*
|
||||
* @param[in] opts TLS options.
|
||||
* @param[in] cipher_suite Cipher suite to append.
|
||||
* @return 0 on success, 1 on fail.
|
||||
*/
|
||||
int nc_tls_append_cipher_suite_wrap(struct nc_server_tls_opts *opts, const char *cipher_suite);
|
||||
|
||||
/**
|
||||
* @brief Set the list of cipher suites for the TLS configuration.
|
||||
*
|
||||
* @param[in] tls_cfg TLS configuration.
|
||||
* @param[in] cipher_suites List of cipher suites.
|
||||
*/
|
||||
void nc_server_tls_set_cipher_suites_wrap(void *tls_cfg, void *cipher_suites);
|
||||
|
||||
/**
|
||||
* @brief Get the certificate's expiration time.
|
||||
*
|
||||
* @param[in] cert Certificate.
|
||||
*
|
||||
* @return Calendar time of the expiration (it is in GMT) or -1 on error.
|
||||
*/
|
||||
time_t nc_tls_get_cert_exp_time_wrap(void *cert);
|
||||
|
||||
/**
|
||||
* @brief Set the session to log TLS secrets for.
|
||||
*
|
||||
* @param[in] session Session to log secrets for.
|
||||
*/
|
||||
void nc_tls_keylog_session_wrap(void *session);
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue