Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@ if(ENABLE_SSH_TLS)
list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBSSH_LIBRARIES})
include_directories(${LIBSSH_INCLUDE_DIRS})

# check if libssh version is >= 0.12.0
if(LIBSSH_VERSION VERSION_GREATER_EQUAL "0.12.0")
set(HAVE_LIBSSH_0_12 TRUE)
endif()

# dependencies - libcurl
find_package(CURL 7.30.0 REQUIRED)
if(TARGET CURL::libcurl)
Expand Down
3 changes: 3 additions & 0 deletions src/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,7 @@
/* Comply with the O-RAN WG11 security spec (client cert Extended Key Usage and Key Usage checks). */
#cmakedefine NC_COMPLY_WITH_ORAN_WG11

/* Support for libssh >= 0.12.0 callback API */
#cmakedefine HAVE_LIBSSH_0_12

#endif /* NC_CONFIG_H_ */
10 changes: 10 additions & 0 deletions src/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,7 @@ nc_session_free_transport(struct nc_session *session, int *multisession)
}
}
ssh_channel_free(session->ti.libssh.channel);
free(session->ti.libssh.channel_cb);
}

if (session->ti.libssh.next) {
Expand All @@ -965,6 +966,7 @@ nc_session_free_transport(struct nc_session *session, int *multisession)
/* free starting SSH NETCONF session (channel will be freed in ssh_free()) */
free(siter->username);
free(siter->host);
free(siter->ti.libssh.channel_cb);
if (!(siter->flags & NC_SESSION_SHAREDCTX)) {
ly_ctx_destroy((struct ly_ctx *)siter->ctx);
}
Expand All @@ -982,6 +984,9 @@ nc_session_free_transport(struct nc_session *session, int *multisession)
sock = -1;
#endif

/* free heap-allocated callback data (libssh >= 0.12) */
free(session->ti.libssh.cb_data);

/* closes sock if set */
ssh_free(session->ti.libssh.session);
} else {
Expand All @@ -994,6 +999,11 @@ nc_session_free_transport(struct nc_session *session, int *multisession)
/* there are still multiple sessions, keep the ring list */
siter->ti.libssh.next = session->ti.libssh.next;
}
/* transfer cb_data to a remaining session so it's freed when the SSH session is freed */
if (session->ti.libssh.cb_data) {
siter->ti.libssh.cb_data = session->ti.libssh.cb_data;
session->ti.libssh.cb_data = NULL;
}
}

/* SESSION IO UNLOCK */
Expand Down
7 changes: 7 additions & 0 deletions src/session_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,10 @@ struct nc_server_opts {
void *interactive_auth_data;
void (*interactive_auth_data_free)(void *data);

int (*interactive_auth_clb_v2)(const struct nc_session *session, ssh_message msg, int is_response, void *user_data);
void *interactive_auth_v2_data;
void (*interactive_auth_v2_data_free)(void *data);

int (*user_verify_clb)(const struct nc_session *session);

/**
Expand Down Expand Up @@ -896,6 +900,9 @@ struct nc_session {
struct {
ssh_channel channel;
ssh_session session;
struct ssh_channel_callbacks_struct *channel_cb; /**< channel callbacks used in the
callback-based auth (libssh >= 0.12) */
void *cb_data; /**< heap-allocated nc_ssh_cb_data (libssh >= 0.12) */
struct nc_session *next; /**< pointer to the next NETCONF session on the same
SSH session, but different SSH channel. If no such session exists, it is NULL.
otherwise there is a ring list of the NETCONF sessions */
Expand Down
42 changes: 27 additions & 15 deletions src/session_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -1379,6 +1379,11 @@ nc_server_destroy(void)
}
server_opts.interactive_auth_data = NULL;
server_opts.interactive_auth_data_free = NULL;
if (server_opts.interactive_auth_v2_data && server_opts.interactive_auth_v2_data_free) {
server_opts.interactive_auth_v2_data_free(server_opts.interactive_auth_v2_data);
}
server_opts.interactive_auth_v2_data = NULL;
server_opts.interactive_auth_v2_data_free = NULL;
#endif /* NC_ENABLED_SSH_TLS */

/* hidden UNIX socket paths */
Expand Down Expand Up @@ -2317,7 +2322,9 @@ nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mon
uint16_t idle_timeout;

#ifdef NC_ENABLED_SSH_TLS
#ifndef HAVE_LIBSSH_0_12
ssh_message ssh_msg;
#endif /* !HAVE_LIBSSH_0_12 */
struct nc_session *new;
#endif /* NC_ENABLED_SSH_TLS */

Expand All @@ -2341,36 +2348,41 @@ nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mon
switch (session->ti_type) {
#ifdef NC_ENABLED_SSH_TLS
case NC_TI_SSH:
#ifndef HAVE_LIBSSH_0_12
ssh_msg = ssh_message_get(session->ti.libssh.session);
if (ssh_msg) {
if (nc_session_ssh_msg(session, NULL, ssh_msg, NULL)) {
ssh_message_reply_default(ssh_msg);
}
if (session->ti.libssh.next) {
for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
(new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
/* new NETCONF SSH channel */
ret = NC_PSPOLL_SSH_CHANNEL;
break;
}
}
if (new != session) {
ssh_message_free(ssh_msg);
ssh_message_free(ssh_msg);
}
#endif /* !HAVE_LIBSSH_0_12 */

/* check for new SSH channels (in callback mode, ssh_message_get returns NULL) */
if (session->ti.libssh.next) {
for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
(new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
/* new NETCONF SSH channel */
ret = NC_PSPOLL_SSH_CHANNEL;
break;
}
}
if (!ret) {
/* just some SSH message */
ret = NC_PSPOLL_SSH_MSG;
if (new != session) {
break;
}
ssh_message_free(ssh_msg);
}

#ifndef HAVE_LIBSSH_0_12
if (ssh_msg) {
/* just some SSH message */
ret = NC_PSPOLL_SSH_MSG;
/* break because 1) we don't want to return anything here ORred with NC_PSPOLL_RPC
* and 2) we don't want to delay openning a new channel by waiting for a RPC to get processed
*/
break;
}
#endif /* !HAVE_LIBSSH_0_12 */

r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
if (r == SSH_EOF) {
Expand Down
36 changes: 36 additions & 0 deletions src/session_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,42 @@ typedef int (*nc_server_ssh_interactive_auth_clb)(const struct nc_session *sessi
*/
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 Keyboard interactive authentication callback (v2, for libssh >= 0.12).
*
* This callback is invoked by the callback-based auth path (libssh >= 0.12) and
* is called multiple times: once for the initial request (is_response == 0) and
* once for each client response (is_response == 1). It must NOT call
* `ssh_message_get()` — the answers are already available in the libssh session
* via `ssh_userauth_kbdint_getanswer()`.
*
* An example callback may fit the following description:
* - If is_response == 0: prepare prompts, send them via
* `ssh_message_auth_interactive_request()`, return SSH_AUTH_INFO.
* - If is_response == 1: read answers via `ssh_userauth_kbdint_getanswer()`,
* validate them, return SSH_AUTH_SUCCESS or SSH_AUTH_DENIED.
*
* @param[in] session NETCONF session.
* @param[in] msg SSH message that contains the interactive request or response.
* @param[in] is_response 0 for the initial request, 1 for the client's response.
* @param[in] user_data Arbitrary user data.
* @return SSH_AUTH_INFO if prompts were sent (waiting for client response),
* SSH_AUTH_SUCCESS for successful authentication,
* SSH_AUTH_DENIED to deny the user.
*/
typedef int (*nc_server_ssh_interactive_auth_clb_v2)(const struct nc_session *session,
ssh_message msg, int is_response, void *user_data);

/**
* @brief Set the v2 callback for SSH interactive authentication (libssh >= 0.12).
*
* @param[in] auth_clb Keyboard interactive authentication callback (v2).
* @param[in] user_data Optional arbitrary user data that will be passed to @p 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_interactive_auth_clb_v2(nc_server_ssh_interactive_auth_clb_v2 auth_clb, void *user_data,
void (*free_user_data)(void *user_data));

/**
* @brief Get the number of answers to Keyboard interactive authentication prompts.
*
Expand Down
Loading