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;
     }