session server ssh UPDATE add kbdint helper API
Made a static function public. This new API helps get client's answers
to kbdint prompts. Also added a more detailed documentation to kbdint
callback setter. Helps resolve #462.
diff --git a/src/io.c b/src/io.c
index d51278f..15123e5 100644
--- a/src/io.c
+++ b/src/io.c
@@ -582,7 +582,7 @@
/* 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;
diff --git a/src/session_p.h b/src/session_p.h
index 4b828f1..f99e30e 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -735,7 +735,6 @@
struct nc_pam_thread_arg {
ssh_message msg; /**< libssh message */
struct nc_session *session; /**< NETCONF session */
- uint16_t auth_timeout; /**< Authentication timeout. */
};
/**
@@ -1116,6 +1115,6 @@
* @param[in] session Session to check.
* @return 1 if connected, 0 if not.
*/
-int nc_session_is_connected(struct nc_session *session);
+int nc_session_is_connected(const struct nc_session *session);
#endif /* NC_SESSION_PRIVATE_H_ */
diff --git a/src/session_server.h b/src/session_server.h
index 344c7bd..d386b62 100644
--- a/src/session_server.h
+++ b/src/session_server.h
@@ -483,15 +483,46 @@
int nc_server_ssh_set_authkey_path_format(const char *path);
/**
- * @brief Set the callback for SSH interactive authentication. If not set, local PAM-based authentication is used.
+ * @brief Keyboard interactive authentication callback.
*
- * @param[in] interactive_auth_clb Callback that should authenticate the user.
- * Zero return indicates success, non-zero an 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.
+ */
+typedef int (*nc_server_ssh_interactive_auth_clb)(const struct nc_session *session,
+ ssh_session ssh_sess, ssh_message msg, void *user_data);
+
+/**
+ * @brief Set the callback for SSH interactive authentication.
+ *
+ * @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_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session,
- ssh_session ssh_sess, ssh_message msg, 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 Get the number of answers to Keyboard interactive authentication prompts.
+ *
+ * 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.
+ */
+int nc_server_ssh_kbdint_get_nanswers(const struct nc_session *session, ssh_session libssh_session);
/**
* @brief Set the name of the PAM configuration file.
diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c
index 12989de..8f86fae 100644
--- a/src/session_server_ssh.c
+++ b/src/session_server_ssh.c
@@ -461,13 +461,15 @@
return auth_ret;
}
-/* get answers to kbdint prompts on the given libssh session and return the number of them, -1 on timeout/dc */
-static int
-nc_server_ssh_kbdint_get_nanswers(struct nc_session *session, ssh_session libssh_session, uint16_t auth_timeout)
+API int
+nc_server_ssh_kbdint_get_nanswers(const struct nc_session *session, ssh_session libssh_session)
{
int ret = 0;
struct timespec ts_timeout = {0};
ssh_message reply = NULL;
+ uint16_t auth_timeout = *((uint16_t *)session->data);
+
+ NC_CHECK_ARG_RET(NULL, session, libssh_session, -1);
if (auth_timeout) {
nc_timeouttime_get(&ts_timeout, auth_timeout * 1000);
@@ -523,10 +525,8 @@
ssh_message reply = NULL;
struct nc_pam_thread_arg *clb_data = appdata_ptr;
ssh_session libssh_session;
- uint16_t auth_timeout;
libssh_session = clb_data->session->ti.libssh.session;
- auth_timeout = clb_data->auth_timeout;
/* PAM_MAX_NUM_MSG == 32 by default */
if ((n_messages <= 0) || (n_messages >= PAM_MAX_NUM_MSG)) {
@@ -598,7 +598,7 @@
goto cleanup;
}
- n_answers = nc_server_ssh_kbdint_get_nanswers(clb_data->session, libssh_session, auth_timeout);
+ n_answers = nc_server_ssh_kbdint_get_nanswers(clb_data->session, libssh_session);
if (n_answers < 0) {
/* timeout or dc */
r = PAM_CONV_ERR;
@@ -642,7 +642,7 @@
* @return PAM error otherwise.
*/
static int
-nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t auth_timeout, ssh_message ssh_msg)
+nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, ssh_message ssh_msg)
{
pam_handle_t *pam_h = NULL;
int ret;
@@ -652,7 +652,6 @@
/* structure holding callback's data */
clb_data.msg = ssh_msg;
clb_data.session = session;
- clb_data.auth_timeout = auth_timeout;
/* PAM conversation structure holding the callback and it's data */
conv.conv = nc_pam_conv_clb;
@@ -835,13 +834,12 @@
*
* @param[in] session Session to authenticate on.
* @param[in] client Client to authenticate.
- * @param[in] auth_timeout Authentication timeout.
* @param[in] msg SSH message that originally requested kbdint authentication.
*
* @return 0 on success, non-zero otherwise.
*/
static int
-nc_server_ssh_system_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t auth_timeout, ssh_message msg)
+nc_server_ssh_system_auth(struct nc_session *session, struct nc_auth_client *client, ssh_message msg)
{
int ret = 0, n_answers;
const char *name = "Keyboard-Interactive Authentication";
@@ -868,7 +866,7 @@
}
/* get the reply */
- n_answers = nc_server_ssh_kbdint_get_nanswers(session, session->ti.libssh.session, auth_timeout);
+ n_answers = nc_server_ssh_kbdint_get_nanswers(session, session->ti.libssh.session);
if (n_answers < 0) {
/* timeout or dc */
ret = 1;
@@ -895,7 +893,7 @@
#endif
static int
-nc_sshcb_auth_kbdint(struct nc_session *session, struct nc_auth_client *client, uint16_t auth_timeout, ssh_message msg)
+nc_sshcb_auth_kbdint(struct nc_session *session, struct nc_auth_client *client, ssh_message msg)
{
int auth_ret = 1;
@@ -904,16 +902,15 @@
} else {
#ifdef HAVE_LIBPAM
/* authenticate using PAM */
- if (!nc_pam_auth(session, client, auth_timeout, msg)) {
+ if (!nc_pam_auth(session, client, msg)) {
auth_ret = 0;
}
#elif defined (HAVE_SHADOW)
/* authenticate using locally configured users */
- if (!nc_server_ssh_system_auth(session, client, auth_timeout, msg)) {
+ if (!nc_server_ssh_system_auth(session, client, msg)) {
auth_ret = 0;
}
#else
- (void) auth_timeout;
ERR(NULL, "Keyboard-interactive method not supported.");
#endif
}
@@ -1506,7 +1503,7 @@
} else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
ret = nc_sshcb_auth_pubkey(session, auth_client, msg);
} else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
- ret = nc_sshcb_auth_kbdint(session, auth_client, opts->auth_timeout, msg);
+ ret = nc_sshcb_auth_kbdint(session, auth_client, msg);
}
if (!ret) {
@@ -1780,8 +1777,11 @@
goto cleanup;
}
- /* authenticate */
- if ((rc = nc_accept_ssh_session_auth(session, opts)) != 1) {
+ /* authenticate, store auth_timeout in session so we can retrieve it in kb interactive API */
+ session->data = &opts->auth_timeout;
+ rc = nc_accept_ssh_session_auth(session, opts);
+ session->data = NULL;
+ if (rc != 1) {
goto cleanup;
}