diff --git a/README.md b/README.md index 3d4d965f..b31da069 100644 --- a/README.md +++ b/README.md @@ -247,3 +247,38 @@ Tests can be run by the make's `test` target: $ make test ``` +## Supported YANG modules + +### Server + +The *libnetconf2* NETCONF server has two APIs that load YANG modules into the context. The first API is [nc_server_init_ctx](https://netopeer.liberouter.org/doc/libnetconf2/master/html/group__server__functions.html#ga35cccf2dbe9204abe01ccb4b93db7438), which loads the following YANG modules with their features: + +- **ietf-netconf**: writable-running, candidate, rollback-on-error, validate, startup, url, xpath, confirmed-commit, +- **ietf-netconf-monitoring**: no features. + +The second API is [nc_server_config_load_modules](https://netopeer.liberouter.org/doc/libnetconf2/master/html/group__server__config__functions.html#ga3760b87e3ab4309514e9ad82c4c09cdb). Supported features (marked by ✔) are loaded into the context by this API. The only exception is the feature `local-users-supported`, which is by default loaded, but can be disabled, which will influence the behaviour of the SSH authentication (see the *libnetconf2* [documentation](https://netopeer.liberouter.org/doc/libnetconf2/master/html/howtoserver.html)). + +- **iana-crypt-hash**: crypt-hash-md5 ✔, crypt-hash-sha-256 ✔, crypt-hash-sha-512 ✔, +- **ietf-netconf-server**: ssh-listen ✔, tls-listen ✔, ssh-call-home ✔, tls-call-home ✔, central-netconf-server-supported ✔, +- **iana-ssh-encryption-algs**: no features, +- **iana-ssh-key-exchange-algs**: no features, +- **iana-ssh-mac-algs**: no features, +- **iana-ssh-public-key-algs**: no features, +- **iana-tls-cipher-suite-algs**: no features, +- **ietf-crypto-types**: cleartext-passwords ✔, cleartext-private-keys ✔, private-key-encryption ✘, csr-generation ✘, p10-csr-format ✘, certificate-expiration-notification ✘, encrypted-passwords ✘, hidden-symmetric-keys ✘, encrypted-symmetric-keys ✘, hidden-private-keys ✘, encrypted-private-keys ✘, one-symmetric-key-format ✘, one-asymmetric-key-format ✘, symmetrically-encrypted-value-format ✘, asymmetrically-encrypted-value-format ✘, cms-enveloped-data-format ✘, cms-encrypted-data-format ✘, cleartext-symmetric-keys ✘, +- **ietf-keystore**: central-keystore-supported ✔, inline-definitions-supported ✔, asymmetric-keys ✔, symmetric-keys ✘, +- **ietf-netconf-server**: ssh-listen ✔, tls-listen ✔, ssh-call-home ✔, tls-call-home ✔, central-netconf-server-supported ✔, +- **ietf-ssh-common**: transport-params ✔, ssh-x509-certs ✘, public-key-generation ✘, +- **ietf-ssh-server**: local-users-supported **?**, local-user-auth-publickey ✔, local-user-auth-password ✔, local-user-auth-none ✔, ssh-server-keepalives ✘, local-user-auth-hostbased ✘, +- **ietf-tcp-client**: tcp-client-keepalives ✔, proxy-connect ✘, socks5-gss-api ✘, socks5-username-password ✘, local-binding-supported ✘, +- **ietf-tcp-common**: transport-params ✔, ssh-x509-certs ✘, public-key-generation ✘, +- **ietf-tcp-server**: tcp-server-keepalives ✔, +- **ietf-tls-common**: tls10 ✔, tls11 ✔, tls12 ✔, tls13 ✔, hello-params ✔, public-key-generation ✘, +- **ietf-tls-server**: server-ident-x509-cert ✔, client-auth-supported ✔, client-auth-x509-cert ✔, tls-server-keepalives ✘, server-ident-raw-public-key ✘, server-ident-tls12-psk ✘, server-ident-tls13-epsk ✘, client-auth-raw-public-key ✘, client-auth-tls12-psk ✘, client-auth-tls13-epsk ✘, +- **ietf-truststore**: central-truststore-supported ✔, inline-definitions-supported ✔, certificates ✔, public-keys ✔, +- **ietf-x509-cert-to-name**: no features, +- **libnetconf2-netconf-server**: no features. + +### Client + +Currently no client specific YANG modules are supported. diff --git a/doc/libnetconf.doc b/doc/libnetconf.doc index 6194db29..e255aa1b 100644 --- a/doc/libnetconf.doc +++ b/doc/libnetconf.doc @@ -386,10 +386,16 @@ * To successfully accept an SSH session you must configure at least one host key. * You may create this data yourself or by using ::nc_server_config_add_ssh_hostkey(). * - * On top of that, each SSH endpoint can define it's own authorized clients and their authentication methods. + * It is important to decide whether the users that can connect to the SSH server should be obtained from the configuration or from the system. + * If the YANG feature *local-users-supported* is enabled (the default), then the authorized users are derived from the configuration. + * When a client connects to the server, he must be found in the configuration and he must authenticate to **all** of his configured authentication methods. + * If the feature is disabled, then the system will be used to try to authenticate the client via one of the three + * methods - publickey, keyboard-interactive or password (only one of them has to succeed). + * + * If the local users are supported then each SSH endpoint can define it's own authorized clients and their authentication methods. * For example if you wish to create an SSH user that can authenticate using a password, use ::nc_server_config_add_ssh_user_password(). * Another option for authorized clients is to reference another endpoint's clients, however be careful not to create a cyclic reference - * (see ::nc_server_config_add_ssh_endpoint_client_ref()). An authorized client MUST authenticate to all of it's configured authentication methods. + * (see ::nc_server_config_add_ssh_endpoint_client_ref()). * * \anchor ln2doc_pubkey * The Public Key authentication method is supported. If you wish to use this method, you need to specify the given user's @@ -398,7 +404,8 @@ * in the ietf-trustore module's YANG data and then reference them (truststore-reference). The final option is to set the global * path to file with public keys. This path may contain special tokens, see ::nc_server_ssh_set_authkey_path_format(). * If the path is set and the use-system-keys container is present in the data for the client wishing to authenticate, - * then the keys from the file will be used for authentication. + * then the keys from the file will be used for authentication. If the YANG feature *local-users-supported* is disabled, + * then it's neccessary to set the path format using ::nc_server_ssh_set_authkey_path_format(). * * \anchor ln2doc_kbdint * The Keyboard Interactive authentication method is also supported. It can be done in three ways. diff --git a/src/server_config.h b/src/server_config.h index 6112f777..63d4784c 100644 --- a/src/server_config.h +++ b/src/server_config.h @@ -60,6 +60,10 @@ extern "C" { * iana-ssh-mac-algs, iana-ssh-public-key-algs, ietf-keystore, ietf-ssh-server, ietf-truststore, * ietf-tls-server and libnetconf2-netconf-server. * + * Note that the SSH authentication depends on the value of the 'local-users-supported' feature in the ietf-ssh-server module. + * If the feature, and its dependent if-features, are disabled, the SSH authentication will use the system users. + * Otherwise, the SSH authentication will use the local users from the configuration (the default). + * * @param[in, out] ctx Optional context in which the modules will be implemented. Created if *ctx is null. * @return 0 on success, 1 on error. */ diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c index f7fc74dc..b481ee32 100644 --- a/src/session_server_ssh.c +++ b/src/session_server_ssh.c @@ -48,8 +48,15 @@ extern struct nc_server_opts server_opts; +/** + * @brief Stores the private key data as a temporary file. + * + * @param[in] in Private key data. + * @param[in] privkey_format String representation of the private key format. + * @return Path to the created temporary file or NULL on fail. + */ static char * -base64der_privkey_to_tmp_file(const char *in, const char *privkey_format) +nc_server_ssh_privkey_data_to_tmp_file(const char *in, const char *privkey_format) { char path[12] = "/tmp/XXXXXX"; int fd, written; @@ -110,6 +117,13 @@ base64der_privkey_to_tmp_file(const char *in, const char *privkey_format) return strdup(path); } +/** + * @brief Get asymmetric key from the keystore. + * + * @param[in] referenced_name Name of the asymmetric key in the keystore. + * @param[out] askey Referenced asymmetric key. + * @return 0 on success, 1 on error. + */ static int nc_server_ssh_ks_ref_get_key(const char *referenced_name, struct nc_asymmetric_key **askey) { @@ -142,6 +156,14 @@ nc_server_ssh_ks_ref_get_key(const char *referenced_name, struct nc_asymmetric_k return 0; } +/** + * @brief Get public keys from the truststore. + * + * @param[in] referenced_name Name of the public key bag in the truststore. + * @param[out] pubkeys Referenced public keys. + * @param[out] pubkey_count Referenced public key count. + * @return 0 on success, 1 on error. + */ static int nc_server_ssh_ts_ref_get_keys(const char *referenced_name, struct nc_public_key **pubkeys, uint16_t *pubkey_count) { @@ -177,6 +199,12 @@ nc_server_ssh_ts_ref_get_keys(const char *referenced_name, struct nc_public_key return 0; } +/** + * @brief Convert UID to string. + * + * @param[in] uid UID to convert. + * @return UID converted to string or NULL on fail. + */ static char * nc_server_ssh_uid_to_str(uid_t uid) { @@ -194,6 +222,16 @@ nc_server_ssh_uid_to_str(uid_t uid) return uid_str; } +/** + * @brief Append a character or a string to a string. + * + * @param[in] src_c Source character. + * @param[in] src_str Source string. + * @param[in,out] size Size of the destination string. + * @param[out] idx Index of the next character to write. + * @param[out] dst Destination string. + * @return 0 on success, 1 on error. + */ static int nc_server_ssh_str_append(const char src_c, const char *src_str, int *size, int *idx, char **dst) { @@ -230,6 +268,13 @@ nc_server_ssh_str_append(const char src_c, const char *src_str, int *size, int * return 0; } +/** + * @brief Get the path to the system public keys from format set by an API. + * + * @param[in] username Username. + * @param[out] out_path Path to the system public keys. + * @return 0 on success, 1 on error. + */ static int nc_server_ssh_get_system_keys_path(const char *username, char **out_path) { @@ -239,6 +284,11 @@ nc_server_ssh_get_system_keys_path(const char *username, char **out_path) struct passwd *pw, pw_buf; size_t buf_len = 0; + if (!path_fmt) { + ERR(NULL, "System public keys path format not set."); + return 1; + } + /* check if the path format contains any tokens */ if (strstr(path_fmt, "%h") || strstr(path_fmt, "%U") || strstr(path_fmt, "%u") || strstr(path_fmt, "%%")) { /* get pw */ @@ -305,11 +355,18 @@ nc_server_ssh_get_system_keys_path(const char *username, char **out_path) return ret; } -/* reads public keys from authorized_keys-like file */ +/** + * @brief Read public keys from the authorized keys file. + * + * @param[in] path Path to the authorized keys file. + * @param[out] pubkeys Public keys. + * @param[out] pubkey_count Public key count. + * @return 0 on success, 1 on error. + */ static int nc_server_ssh_read_authorized_keys_file(const char *path, struct nc_public_key **pubkeys, uint16_t *pubkey_count) { - int ret = 0, line_num = 0; + int ret = 0, rc, line_num = 0; FILE *f = NULL; char *line = NULL, *ptr, *ptr2; size_t n; @@ -360,8 +417,8 @@ nc_server_ssh_read_authorized_keys_file(const char *path, struct nc_public_key * /* add the key */ *pubkeys = nc_realloc(*pubkeys, (*pubkey_count + 1) * sizeof **pubkeys); NC_CHECK_ERRMEM_GOTO(!(*pubkeys), ret = 1, cleanup); - ret = asprintf(&(*pubkeys)[*pubkey_count].name, "authorized_key_%" PRIu16, *pubkey_count); - NC_CHECK_ERRMEM_GOTO(ret == -1, (*pubkeys)[*pubkey_count].name = NULL; ret = 1, cleanup); + rc = asprintf(&(*pubkeys)[*pubkey_count].name, "authorized_key_%" PRIu16, *pubkey_count); + NC_CHECK_ERRMEM_GOTO(rc == -1, (*pubkeys)[*pubkey_count].name = NULL; ret = 1, cleanup); (*pubkeys)[*pubkey_count].type = NC_PUBKEY_FORMAT_SSH; (*pubkeys)[*pubkey_count].data = strdup(ptr); NC_CHECK_ERRMEM_GOTO(!(*pubkeys)[*pubkey_count].data, ret = 1, cleanup); @@ -378,6 +435,14 @@ nc_server_ssh_read_authorized_keys_file(const char *path, struct nc_public_key * return ret; } +/** + * @brief Get user's public keys from the system. + * + * @param[in] username Username. + * @param[out] pubkeys User's public keys. + * @param[out] pubkey_count Public key count. + * @return 0 on success, non-zero on error. + */ static int nc_server_ssh_get_system_keys(const char *username, struct nc_public_key **pubkeys, uint16_t *pubkey_count) { @@ -403,20 +468,168 @@ nc_server_ssh_get_system_keys(const char *username, struct nc_public_key **pubke return ret; } +#ifdef HAVE_SHADOW + +/** + * @brief Get the user's /etc/passwd entry. + * + * @param[in] username Username. + * @param[out] pwd_buf Buffer for the passwd structure. + * @param[out] buf Buffer for the pwd's strings. + * @param[out] buf_size Size of the buffer. + * @return User's passwd entry or NULL on error. + */ +static struct passwd * +nc_server_ssh_getpwnam(const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size) +{ + struct passwd *pwd = NULL; + char *mem; + int r = 0; + + do { + r = getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd); + if (pwd) { + /* entry found */ + break; + } + + if (r == ERANGE) { + /* small buffer, enlarge */ + *buf_size <<= 2; + mem = realloc(*buf, *buf_size); + if (!mem) { + ERRMEM; + return NULL; + } + *buf = mem; + } + } while (r == ERANGE); + + return pwd; +} + +/** + * @brief Get the user's /etc/shadow entry. + * + * @param[in] username Username. + * @param[out] spwd_buf Buffer for the spwd structure. + * @param[out] buf Buffer for the spwd's strings. + * @param[out] buf_size Size of the buffer. + * @return User's shadow entry or NULL on error. + */ +static struct spwd * +nc_server_ssh_getspnam(const char *username, struct spwd *spwd_buf, char **buf, size_t *buf_size) +{ + struct spwd *spwd = NULL; + char *mem; + int r = 0; + + do { +# ifndef __QNXNTO__ + r = getspnam_r(username, spwd_buf, *buf, *buf_size, &spwd); +# else + spwd = getspnam_r(username, spwd_buf, *buf, *buf_size); + r = errno; +# endif + if (spwd) { + /* entry found */ + break; + } + + if (r == ERANGE) { + /* small buffer, enlarge */ + *buf_size <<= 2; + mem = realloc(*buf, *buf_size); + if (!mem) { + ERRMEM; + return NULL; + } + *buf = mem; + } + } while (r == ERANGE); + + return spwd; +} + +/** + * @brief Get the user's hashed password from the system. + * + * @param[in] username Username. + * @return User's hashed password or NULL on error. + */ +static char * +nc_server_ssh_get_pwd_hash(const char *username) +{ + struct passwd *pwd, pwd_buf; + struct spwd *spwd, spwd_buf; + char *pass_hash = NULL, *buf = NULL; + size_t buf_size = 256; + + buf = malloc(buf_size); + NC_CHECK_ERRMEM_GOTO(!buf, , error); + + pwd = nc_server_ssh_getpwnam(username, &pwd_buf, &buf, &buf_size); + if (!pwd) { + VRB(NULL, "User \"%s\" not found in the system.", username); + goto error; + } + + if (!strcmp(pwd->pw_passwd, "x")) { + spwd = nc_server_ssh_getspnam(username, &spwd_buf, &buf, &buf_size); + if (!spwd) { + VRB(NULL, "Failed to retrieve the shadow entry for \"%s\".", username); + goto error; + } else if ((spwd->sp_expire > -1) && (spwd->sp_expire <= (time(NULL) / (60 * 60 * 24)))) { + WRN(NULL, "User \"%s\" account has expired.", username); + goto error; + } + + pass_hash = spwd->sp_pwdp; + } else { + pass_hash = pwd->pw_passwd; + } + + if (!pass_hash) { + ERR(NULL, "No password could be retrieved for \"%s\".", username); + goto error; + } + + /* check the hash structure for special meaning */ + if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) { + VRB(NULL, "User \"%s\" is not allowed to authenticate using a password.", username); + goto error; + } + if (!strcmp(pass_hash, "*NP*")) { + VRB(NULL, "Retrieving password for \"%s\" from a NIS+ server not supported.", username); + goto error; + } + + pass_hash = strdup(pass_hash); + free(buf); + return pass_hash; + +error: + free(buf); + return NULL; +} + +#endif + /** - * @brief Compare hashed password with a cleartext password for a match. + * @brief Compare stored hashed password with a cleartext received password. * - * @param[in] pass_hash Hashed password. - * @param[in] pass_clear Cleartext password. - * @return 0 on match. - * @return non-zero if not a match. + * @param[in] stored_pw Hashed stored password. + * @param[in] received_pw Cleartext received password. + * @return 0 on match, non-zero otherwise. */ static int -auth_password_compare_pwd(const char *stored_pw, const char *received_pw) +nc_server_ssh_compare_password(const char *stored_pw, const char *received_pw) { char *received_pw_hash = NULL; struct crypt_data cdata = {0}; + NC_CHECK_ARG_RET(NULL, stored_pw, received_pw, 1); + if (!stored_pw[0]) { if (!received_pw[0]) { WRN(NULL, "User authentication successful with an empty password!"); @@ -442,27 +655,6 @@ auth_password_compare_pwd(const char *stored_pw, const char *received_pw) return strcmp(received_pw_hash, stored_pw); } -static int -nc_sshcb_auth_password(struct nc_session *session, struct nc_auth_client *auth_client, ssh_message msg) -{ - int auth_ret = 1; - - if (!auth_client->password) { - VRB(session, "User \"%s\" does not have password method configured, but a request was received.", auth_client->username); - } else { - auth_ret = auth_password_compare_pwd(auth_client->password, ssh_message_auth_password(msg)); - } - - if (auth_ret) { - ++session->opts.server.ssh_auth_attempts; - VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username, - session->opts.server.ssh_auth_attempts); - ssh_message_reply_default(msg); - } - - return auth_ret; -} - API int nc_server_ssh_kbdint_get_nanswers(const struct nc_session *session, ssh_session libssh_session) { @@ -639,12 +831,13 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo * @brief Handles authentication via Linux PAM. * * @param[in] session NETCONF session. + * @param[in] username Username of the client to auhtenticate. * @param[in] ssh_msg SSH message with a keyboard-interactive authentication request. * @return PAM_SUCCESS on success; * @return PAM error otherwise. */ static int -nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, ssh_message ssh_msg) +nc_server_ssh_auth_kbdint_pam(struct nc_session *session, const char *username, ssh_message ssh_msg) { pam_handle_t *pam_h = NULL; int ret; @@ -666,7 +859,7 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, ssh_messa } /* initialize PAM and see if the given configuration file exists */ - ret = pam_start(server_opts.pam_config_name, client->username, &conv, &pam_h); + ret = pam_start(server_opts.pam_config_name, username, &conv, &pam_h); if (ret != PAM_SUCCESS) { ERR(session, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; @@ -696,7 +889,7 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, ssh_messa VRB(session, "PAM warning occurred (%s).", pam_strerror(pam_h, ret)); ret = pam_chauthtok(pam_h, PAM_CHANGE_EXPIRED_AUTHTOK); if (ret == PAM_SUCCESS) { - VRB(session, "The authentication token of user \"%s\" updated successfully.", client->username); + VRB(session, "The authentication token of user \"%s\" updated successfully.", username); } else { ERR(session, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; @@ -713,229 +906,74 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, ssh_messa #elif defined (HAVE_SHADOW) -static struct passwd * -nc_server_ssh_getpwnam(const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size) +/** + * @brief Authenticate using credentials stored in the system. + * + * @param[in] session Session to authenticate on. + * @param[in] username Username of the client to authenticate. + * @param[in] msg SSH message that originally requested kbdint authentication. + * + * @return 0 on success, non-zero otherwise. + */ +static int +nc_server_ssh_auth_kbdint_system(struct nc_session *session, const char *username, ssh_message msg) { - struct passwd *pwd = NULL; - char *mem; - int r = 0; + int ret = 0, n_answers; + const char *name = "Keyboard-Interactive Authentication"; + const char *instruction = "Please enter your authentication token"; + char *prompt = NULL, *pw = NULL, *received_pw = NULL; + char echo[] = {0}; - do { - r = getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd); - if (pwd) { - /* entry found */ - break; - } + /* try to get the client's pw hash from the system */ + pw = nc_server_ssh_get_pwd_hash(username); + if (!pw) { + ret = 1; + goto cleanup; + } - if (r == ERANGE) { - /* small buffer, enlarge */ - *buf_size <<= 2; - mem = realloc(*buf, *buf_size); - if (!mem) { - ERRMEM; - return NULL; - } - *buf = mem; - } - } while (r == ERANGE); + ret = asprintf(&prompt, "%s's password:", username); + NC_CHECK_ERRMEM_GOTO(ret == -1, prompt = NULL; ret = 1, cleanup); - return pwd; + /* send the password prompt to the client */ + ret = ssh_message_auth_interactive_request(msg, name, instruction, 1, (const char **) &prompt, echo); + if (ret) { + ERR(session, "Failed to send an authentication request to client \"%s\".", username); + goto cleanup; + } + + /* get the reply */ + n_answers = nc_server_ssh_kbdint_get_nanswers(session, session->ti.libssh.session); + if (n_answers < 0) { + /* timeout or dc */ + ret = 1; + goto cleanup; + } else if (n_answers != 1) { + /* only expecting a single answer */ + ERR(session, "Unexpected amount of answers in system auth. Expected 1, got \"%d\".", n_answers); + ret = 1; + goto cleanup; + } + received_pw = strdup(ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0)); + NC_CHECK_ERRMEM_GOTO(!received_pw, ret = 1, cleanup); + + /* cmp the passwords */ + ret = nc_server_ssh_compare_password(pw, received_pw); + +cleanup: + free(pw); + free(received_pw); + free(prompt); + return ret; } -static struct spwd * -nc_server_ssh_getspnam(const char *username, struct spwd *spwd_buf, char **buf, size_t *buf_size) +#endif + +API 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)) { - struct spwd *spwd = NULL; - char *mem; - int r = 0; - - do { -# ifndef __QNXNTO__ - r = getspnam_r(username, spwd_buf, *buf, *buf_size, &spwd); -# else - spwd = getspnam_r(username, spwd_buf, *buf, *buf_size); -# endif - if (spwd) { - /* entry found */ - break; - } - - if (r == ERANGE) { - /* small buffer, enlarge */ - *buf_size <<= 2; - mem = realloc(*buf, *buf_size); - if (!mem) { - ERRMEM; - return NULL; - } - *buf = mem; - } - } while (r == ERANGE); - - return spwd; -} - -static char * -nc_server_ssh_get_pwd_hash(const char *username) -{ - struct passwd *pwd, pwd_buf; - struct spwd *spwd, spwd_buf; - char *pass_hash = NULL, *buf = NULL; - size_t buf_size = 256; - - buf = malloc(buf_size); - NC_CHECK_ERRMEM_GOTO(!buf, , error); - - pwd = nc_server_ssh_getpwnam(username, &pwd_buf, &buf, &buf_size); - if (!pwd) { - VRB(NULL, "User \"%s\" not found locally.", username); - goto error; - } - - if (!strcmp(pwd->pw_passwd, "x")) { - spwd = nc_server_ssh_getspnam(username, &spwd_buf, &buf, &buf_size); - if (!spwd) { - VRB(NULL, "Failed to retrieve the shadow entry for \"%s\".", username); - goto error; - } else if ((spwd->sp_expire > -1) && (spwd->sp_expire <= (time(NULL) / (60 * 60 * 24)))) { - WRN(NULL, "User \"%s\" account has expired.", username); - goto error; - } - - pass_hash = spwd->sp_pwdp; - } else { - pass_hash = pwd->pw_passwd; - } - - if (!pass_hash) { - ERR(NULL, "No password could be retrieved for \"%s\".", username); - goto error; - } - - /* check the hash structure for special meaning */ - if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) { - VRB(NULL, "User \"%s\" is not allowed to authenticate using a password.", username); - goto error; - } - if (!strcmp(pass_hash, "*NP*")) { - VRB(NULL, "Retrieving password for \"%s\" from a NIS+ server not supported.", username); - goto error; - } - - pass_hash = strdup(pass_hash); - free(buf); - return pass_hash; - -error: - free(buf); - return NULL; -} - -/** - * @brief Authenticate using locally stored credentials. - * - * @param[in] session Session to authenticate on. - * @param[in] client Client to authenticate. - * @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, ssh_message msg) -{ - int ret = 0, n_answers; - const char *name = "Keyboard-Interactive Authentication"; - const char *instruction = "Please enter your authentication token"; - char *prompt = NULL, *local_pw = NULL, *received_pw = NULL; - char echo[] = {0}; - - /* try to get the client's locally stored pw hash */ - local_pw = nc_server_ssh_get_pwd_hash(client->username); - if (!local_pw) { - ERR(session, "Unable to get %s's credentials.", client->username); - ret = 1; - goto cleanup; - } - - ret = asprintf(&prompt, "%s's password:", client->username); - NC_CHECK_ERRMEM_GOTO(ret == -1, prompt = NULL; ret = 1, cleanup); - - /* send the password prompt to the client */ - ret = ssh_message_auth_interactive_request(msg, name, instruction, 1, (const char **) &prompt, echo); - if (ret) { - ERR(session, "Failed to send an authentication request to client \"%s\".", client->username); - goto cleanup; - } - - /* get the reply */ - n_answers = nc_server_ssh_kbdint_get_nanswers(session, session->ti.libssh.session); - if (n_answers < 0) { - /* timeout or dc */ - ret = 1; - goto cleanup; - } else if (n_answers != 1) { - /* only expecting a single answer */ - ERR(session, "Unexpected amount of answers in system auth. Expected 1, got \"%d\".", n_answers); - ret = 1; - goto cleanup; - } - received_pw = strdup(ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0)); - NC_CHECK_ERRMEM_GOTO(!received_pw, ret = 1, cleanup); - - /* cmp the pw hashes */ - ret = auth_password_compare_pwd(local_pw, received_pw); - -cleanup: - free(local_pw); - free(received_pw); - free(prompt); - return ret; -} - -#endif - -static int -nc_sshcb_auth_kbdint(struct nc_session *session, struct nc_auth_client *client, ssh_message msg) -{ - int auth_ret = 1; - - if (!client->kb_int_enabled) { - VRB(session, "User \"%s\" does not have Keyboard-interactive method configured, but a request was received.", client->username); - } else if (server_opts.interactive_auth_clb) { - auth_ret = server_opts.interactive_auth_clb(session, session->ti.libssh.session, msg, server_opts.interactive_auth_data); - } else { -#ifdef HAVE_LIBPAM - /* authenticate using PAM */ - 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, msg)) { - auth_ret = 0; - } -#else - ERR(NULL, "Keyboard-interactive method not supported."); -#endif - } - - /* Authenticate message based on outcome */ - if (auth_ret) { - ++session->opts.server.ssh_auth_attempts; - VRB(session, "Failed user \"%s\" authentication attempt (#%d).", client->username, - session->opts.server.ssh_auth_attempts); - ssh_message_reply_default(msg); - } - - return auth_ret; -} - -API 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)) -{ - /* CONFIG LOCK */ - pthread_rwlock_wrlock(&server_opts.config_lock); + /* CONFIG LOCK */ + pthread_rwlock_wrlock(&server_opts.config_lock); server_opts.interactive_auth_clb = interactive_auth_clb; server_opts.interactive_auth_data = user_data; @@ -1003,10 +1041,13 @@ nc_server_ssh_set_authkey_path_format(const char *path) return ret; } -/* - * Get the public key type from binary data stored in buffer. - * The data is in the form of: 4 bytes = data length, then data of data length - * and the data is in network byte order. The key has to be in the SSH2 format. +/** + * @brief Get the public key type from binary data. + * + * @param[in] buffer Binary key data, which is in the form of: 4 bytes = data length, then data of data length. + * Data is in network byte order. The key has to be in the SSH2 format. + * @param[out] len Length of the key type. + * @return Pointer to where the key type starts in the buffer and is of the length @p len . */ static const char * nc_server_ssh_get_pubkey_type(const unsigned char *buffer, uint32_t *len) @@ -1089,116 +1130,207 @@ nc_server_ssh_create_ssh_pubkey(const char *base64, ssh_key *key) * @return Authorized key username, NULL if no match was found. */ static int -auth_pubkey_compare_key(ssh_key key, struct nc_auth_client *auth_client) +nc_server_ssh_auth_pubkey_compare_key(ssh_key key, struct nc_public_key *pubkeys, uint16_t pubkey_count) { - uint16_t i, pubkey_count = 0; + uint16_t i; int ret = 0; ssh_key new_key = NULL; - struct nc_public_key *pubkeys = NULL; - - /* get the correct public key storage */ - if (auth_client->store == NC_STORE_LOCAL) { - pubkeys = auth_client->pubkeys; - pubkey_count = auth_client->pubkey_count; - } else if (auth_client->store == NC_STORE_TRUSTSTORE) { - ret = nc_server_ssh_ts_ref_get_keys(auth_client->ts_ref, &pubkeys, &pubkey_count); - if (ret) { - ERR(NULL, "Error getting \"%s\"'s public keys from the truststore.", auth_client->username); - goto cleanup; - } - } else if (auth_client->store == NC_STORE_SYSTEM) { - ret = nc_server_ssh_get_system_keys(auth_client->username, &pubkeys, &pubkey_count); - if (ret) { - ERR(NULL, "Failed to retrieve public keys of user \"%s\" from the system.", auth_client->username); - goto cleanup; - } - } else { - ERRINT; - return 1; - } /* try to compare all of the client's keys with the key received in the SSH message */ for (i = 0; i < pubkey_count; i++) { /* create the SSH key from the data */ if (nc_server_ssh_create_ssh_pubkey(pubkeys[i].data, &new_key)) { + /* skip */ ssh_key_free(new_key); continue; } /* compare the keys */ ret = ssh_key_cmp(key, new_key, SSH_KEY_CMP_PUBLIC); + ssh_key_free(new_key); if (!ret) { + /* found a match */ break; - } else { - WRN(NULL, "User's \"%s\" public key doesn't match, trying another.", auth_client->username); - ssh_key_free(new_key); } } if (i == pubkey_count) { ret = 1; - goto cleanup; } -cleanup: - if (!ret) { - /* only free a key if everything was ok, it would have already been freed otherwise */ - ssh_key_free(new_key); - } - - if ((auth_client->store == NC_STORE_SYSTEM) && pubkeys) { - for (i = 0; i < pubkey_count; i++) { - free(pubkeys[i].name); - free(pubkeys[i].data); - } - free(pubkeys); - } return ret; } -static void -nc_sshcb_auth_none(struct nc_session *session, struct nc_auth_client *auth_client, ssh_message msg) +/** + * @brief Handle authentication request for the None method. + * + * @param[in] local_users_supported Whether the server supports local users. + * @param[in] auth_client Configured client's authentication data. + * @param[in] msg libssh message. + * @return 0 if the authentication was successful, -1 if not (@p msg already replied to). + */ +static int +nc_server_ssh_auth_none(int local_users_supported, struct nc_auth_client *auth_client, ssh_message msg) { - if (auth_client->none_enabled && !auth_client->password && !auth_client->pubkey_count && !auth_client->kb_int_enabled) { - /* only authenticate the client if he supports none and no other method */ - session->flags |= NC_SESSION_SSH_AUTHENTICATED; - VRB(session, "User \"%s\" authenticated.", session->username); - ssh_message_auth_reply_success(msg, 0); + assert(!local_users_supported || auth_client); + + if (local_users_supported && auth_client->none_enabled) { + /* success */ + return 0; } + /* reply and return -1 so that this does not get counted as an unsuccessful authentication attempt */ ssh_message_reply_default(msg); + return -1; } +/** + * @brief Handle authentication request for the Password method. + * + * @param[in] session NETCONF session. + * @param[in] local_users_supported Whether the server supports local users. + * @param[in] auth_client Configured client's authentication data. + * @param[in] msg libssh message. + * @return 0 if the authentication was successful, 1 if not (@p msg not yet replied to). + */ static int -nc_sshcb_auth_pubkey(struct nc_session *session, struct nc_auth_client *auth_client, ssh_message msg) +nc_server_ssh_auth_password(struct nc_session *session, int local_users_supported, + struct nc_auth_client *auth_client, ssh_message msg) +{ + int rc; + char *password = NULL; + + assert(!local_users_supported || auth_client); + + if (local_users_supported) { + /* obtain pw from config */ + password = auth_client->password; + if (!password) { + VRB(session, "User \"%s\" does not have password method configured, but a request was received.", session->username); + return 1; + } + } else { +#ifdef HAVE_SHADOW + /* obtain pw from system, this one needs to be free'd */ + password = nc_server_ssh_get_pwd_hash(session->username); + if (!password) { + return 1; + } +#else + ERR(session, "Obtaining password from system not supported."); + return 1; +#endif + } + + /* compare the passwords */ + rc = nc_server_ssh_compare_password(password, ssh_message_auth_password(msg)); + + if (!local_users_supported) { + free(password); + } + + return rc ? 1 : 0; +} + +/** + * @brief Handle authentication request for the Publickey method. + * + * @param[in] session NETCONF session. + * @param[in] local_users_supported Whether the server supports local users. + * @param[in] auth_client Configured client's authentication data. + * @param[in] msg libssh message. + * @return 0 if the authentication was successful, 1 if not and the @p msg not yet replied to, -1 if not and @p msg was replied to. + */ +static int +nc_server_ssh_auth_pubkey(struct nc_session *session, int local_users_supported, + struct nc_auth_client *auth_client, ssh_message msg) { int signature_state, ret = 0; + struct nc_public_key *pubkeys = NULL; + uint16_t pubkey_count = 0, i; - if (auth_pubkey_compare_key(ssh_message_auth_pubkey(msg), auth_client)) { + assert(!local_users_supported || auth_client); + + /* get the public keys */ + if (!local_users_supported || (auth_client->store == NC_STORE_SYSTEM)) { + /* system user or the user has 'use system keys' configured, these need to be free'd */ + ret = nc_server_ssh_get_system_keys(session->username, &pubkeys, &pubkey_count); + if (ret) { + goto cleanup; + } + } else if (auth_client->store == NC_STORE_LOCAL) { + pubkeys = auth_client->pubkeys; + pubkey_count = auth_client->pubkey_count; + } else if (auth_client->store == NC_STORE_TRUSTSTORE) { + ret = nc_server_ssh_ts_ref_get_keys(auth_client->ts_ref, &pubkeys, &pubkey_count); + if (ret) { + goto cleanup; + } + } else { + ERRINT; + return 1; + } + + /* compare the received pubkey with the authorized ones */ + if (nc_server_ssh_auth_pubkey_compare_key(ssh_message_auth_pubkey(msg), pubkeys, pubkey_count)) { VRB(session, "User \"%s\" tried to use an unknown (unauthorized) public key.", session->username); ret = 1; - goto fail; + goto cleanup; } signature_state = ssh_message_auth_publickey_state(msg); if (signature_state == SSH_PUBLICKEY_STATE_NONE) { /* accepting only the use of a public key */ ssh_message_auth_reply_pk_ok_simple(msg); - ret = 1; + ret = -1; + } + +cleanup: + if (!local_users_supported || (auth_client->store == NC_STORE_SYSTEM)) { + for (i = 0; i < pubkey_count; i++) { + free(pubkeys[i].name); + free(pubkeys[i].data); + } + free(pubkeys); } return ret; +} -fail: - ++session->opts.server.ssh_auth_attempts; - VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username, - session->opts.server.ssh_auth_attempts); - ssh_message_reply_default(msg); +static int +nc_server_ssh_auth_kbdint(struct nc_session *session, int local_users_supported, struct nc_auth_client *auth_client, ssh_message msg) +{ + int rc; - return ret; + assert(!local_users_supported || auth_client); + + if (local_users_supported && !auth_client->kb_int_enabled) { + VRB(session, "User \"%s\" does not have Keyboard-interactive method configured, but a request was received.", session->username); + } else if (server_opts.interactive_auth_clb) { + rc = server_opts.interactive_auth_clb(session, session->ti.libssh.session, msg, server_opts.interactive_auth_data); + } else { +#ifdef HAVE_LIBPAM + /* authenticate using PAM */ + rc = nc_server_ssh_auth_kbdint_pam(session, session->username, msg); +#elif defined (HAVE_SHADOW) + /* authenticate using the system */ + rc = nc_server_ssh_auth_kbdint_system(session, session->username, msg); +#else + ERR(NULL, "Keyboard-interactive method not supported."); +#endif + } + + return rc ? 1 : 0; } +/** + * @brief Handle SSH channel open request. + * + * @param[in] session NETCONF session. + * @param[in] msg libssh message. + * @return 0 on success, -1 on failure. + */ static int -nc_sshcb_channel_open(struct nc_session *session, ssh_message msg) +nc_server_ssh_channel_open(struct nc_session *session, ssh_message msg) { ssh_channel chan; @@ -1228,8 +1360,16 @@ nc_sshcb_channel_open(struct nc_session *session, ssh_message msg) return 0; } +/** + * @brief Handle SSH channel request subsystem request. + * + * @param[in] session NETCONF session. + * @param[in] channel Requested SSH channel. + * @param[in] subsystem Name of the requested subsystem. + * @return 0 on success, -1 on failure. + */ static int -nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem) +nc_server_ssh_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem) { struct nc_session *new_session; @@ -1278,15 +1418,155 @@ nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, cons return 0; } -int -nc_session_ssh_msg(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_message msg, struct nc_auth_state *state) +/** + * @brief Handle NETCONF SSH authentication. + * + * @param[in] session NETCONF session. + * @param[in] opts SSH server options. + * @param[in] msg libssh message. + * @param[in] method Type of the authentication method. + * @param[in] str_method String representation of the authentication method. + * @param[in] local_users_supported Whether the server supports local users. + * @param[in,out] state Authentication state. + * @return 1 in case of a fatal error, 0 otherwise. + */ +static int +nc_server_ssh_auth(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_message msg, + int method, const char *str_method, int local_users_supported, struct nc_auth_state *state) { - const char *str_type, *str_subtype = NULL, *username; - int subtype, type, libssh_auth_methods = 0, ret = 0; + const char *username; + int libssh_auth_methods = 0, ret = 0; uint16_t i; struct nc_auth_client *auth_client = NULL; struct nc_endpt *referenced_endpt; + /* save the username, do not let the client change it */ + username = ssh_message_auth_user(msg); + assert(username); + + if (local_users_supported) { + /* get the locally configured user */ + for (i = 0; i < opts->client_count; i++) { + if (!strcmp(opts->auth_clients[i].username, username)) { + auth_client = &opts->auth_clients[i]; + break; + } + } + + if (!auth_client) { + if (opts->referenced_endpt_name) { + /* client not known by the endpt, but it references another one so try it */ + if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { + ERRINT; + return 1; + } + + return nc_server_ssh_auth(session, referenced_endpt->opts.ssh, msg, method, + str_method, local_users_supported, state); + } + + /* user not known, set his authentication methods to public key only so that + * there is no interaction and it will simply be denied */ + ERR(NULL, "User \"%s\" not known by the server.", username); + ssh_set_auth_methods(session->ti.libssh.session, SSH_AUTH_METHOD_PUBLICKEY); + ssh_message_reply_default(msg); + return 0; + } + } + + if (!session->username) { + session->username = strdup(username); + NC_CHECK_ERRMEM_RET(!session->username, 1); + + /* configure and count accepted auth methods */ + if (local_users_supported) { + if (((auth_client->store == NC_STORE_LOCAL) && (auth_client->pubkey_count)) || + (auth_client->store == NC_STORE_TRUSTSTORE) || (auth_client->store == NC_STORE_SYSTEM)) { + /* either locally configured pubkeys, or truststore or system (need to check for + * pubkey count, because NC_STORE_LOCAL is the default enum value) */ + state->auth_method_count++; + libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY; + } + if (auth_client->password) { + state->auth_method_count++; + libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD; + } + if (auth_client->kb_int_enabled) { + state->auth_method_count++; + libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE; + } + if (auth_client->none_enabled) { + state->auth_method_count++; + libssh_auth_methods |= SSH_AUTH_METHOD_NONE; + } + } else { + /* no local users meaning pw, pubkey and kbdint methods are supported, method count is set to 1, + * because only one method is needed for successul auth */ + state->auth_method_count = 1; + libssh_auth_methods = SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_INTERACTIVE; + } + + ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods); + } else { + if (strcmp(username, session->username)) { + /* changing username not allowed */ + ERR(session, "User \"%s\" changed its username to \"%s\".", session->username, username); + session->status = NC_STATUS_INVALID; + session->term_reason = NC_SESSION_TERM_OTHER; + return 1; + } + } + + /* try authenticating, if local users are supported, then the configured user must authenticate via all of his + * configured auth methods, otherwise for system users just one is needed, + * 0 return indicates success, 1 fail (msg not yet replied to), -1 fail (msg was replied to) */ + if (method == SSH_AUTH_METHOD_NONE) { + ret = nc_server_ssh_auth_none(local_users_supported, auth_client, msg); + } else if (method == SSH_AUTH_METHOD_PASSWORD) { + ret = nc_server_ssh_auth_password(session, local_users_supported, auth_client, msg); + } else if (method == SSH_AUTH_METHOD_PUBLICKEY) { + ret = nc_server_ssh_auth_pubkey(session, local_users_supported, auth_client, msg); + } else if (method == SSH_AUTH_METHOD_INTERACTIVE) { + ret = nc_server_ssh_auth_kbdint(session, local_users_supported, auth_client, msg); + } else { + ++session->opts.server.ssh_auth_attempts; + VRB(session, "Authentication method \"%s\" not supported.", str_method); + ssh_message_reply_default(msg); + return 0; + } + + if (!ret) { + state->auth_success_count++; + + if (state->auth_success_count < state->auth_method_count) { + /* success, but he needs to do another method */ + VRB(session, "User \"%s\" partially authenticated, but still needs to authenticate via the rest of his configured methods.", username); + ssh_message_auth_reply_success(msg, 1); + } else { + /* authenticated */ + ssh_message_auth_reply_success(msg, 0); + session->flags |= NC_SESSION_SSH_AUTHENTICATED; + VRB(session, "User \"%s\" authenticated.", username); + } + } else if (ret == 1) { + /* failed attempt, msg wasnt yet replied to */ + ++session->opts.server.ssh_auth_attempts; + VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username, + session->opts.server.ssh_auth_attempts); + ssh_message_reply_default(msg); + } + + return 0; +} + +int +nc_session_ssh_msg(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_message msg, struct nc_auth_state *state) +{ + const char *str_type, *str_subtype = NULL; + int subtype, type, rc, local_users_supported; + const struct ly_ctx *ctx; + struct lys_module *mod; + type = ssh_message_type(msg); subtype = ssh_message_subtype(msg); @@ -1425,119 +1705,38 @@ nc_session_ssh_msg(struct nc_session *session, struct nc_server_ssh_opts *opts, return 1; } - /* save the username, do not let the client change it */ - username = ssh_message_auth_user(msg); - assert(username); - - for (i = 0; i < opts->client_count; i++) { - if (!strcmp(opts->auth_clients[i].username, username)) { - auth_client = &opts->auth_clients[i]; - break; - } - } - - if (!auth_client) { - if (opts->referenced_endpt_name) { - /* client not known by the endpt, but it references another one so try it */ - if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { - ERRINT; - return 1; - } - - return nc_session_ssh_msg(session, referenced_endpt->opts.ssh, msg, state); - } - - /* user not known, set his authentication methods to public key only so that - * there is no interaction and it will simply be denied */ - ERR(NULL, "User \"%s\" not known by the server.", username); - ssh_set_auth_methods(session->ti.libssh.session, SSH_AUTH_METHOD_PUBLICKEY); - ssh_message_reply_default(msg); - return 0; - } - - if (!session->username) { - session->username = strdup(username); - - /* configure and count accepted auth methods */ - if (auth_client->store == NC_STORE_LOCAL) { - if (auth_client->pubkey_count) { - libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY; - } - } else if (auth_client->store == NC_STORE_TRUSTSTORE) { - if (auth_client->ts_ref) { - libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY; - } - } else if (auth_client->store == NC_STORE_SYSTEM) { - libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY; - } - if (auth_client->password) { - state->auth_method_count++; - libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD; - } - if (auth_client->kb_int_enabled) { - state->auth_method_count++; - libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE; - } - if (auth_client->none_enabled) { - libssh_auth_methods |= SSH_AUTH_METHOD_NONE; - } - - if (libssh_auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { - state->auth_method_count++; - } - - ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods); - } else { - if (strcmp(username, session->username)) { - /* changing username not allowed */ - ERR(session, "User \"%s\" changed its username to \"%s\".", session->username, username); - session->status = NC_STATUS_INVALID; - session->term_reason = NC_SESSION_TERM_OTHER; - return 1; - } + /* get libyang ctx from session and ietf-ssh-server yang model from the ctx */ + ctx = nc_session_get_ctx(session); + mod = ly_ctx_get_module_latest(ctx, "ietf-ssh-server"); + if (!mod) { + ERRINT; + return 1; } - /* try authenticating, the user must authenticate via all of his configured auth methods */ - if (subtype == SSH_AUTH_METHOD_NONE) { - nc_sshcb_auth_none(session, auth_client, msg); - ret = 1; - } else if (subtype == SSH_AUTH_METHOD_PASSWORD) { - ret = nc_sshcb_auth_password(session, auth_client, msg); - } 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, msg); + /* check if local-users-supported feature is enabled */ + rc = lys_feature_value(mod, "local-users-supported"); + if (!rc) { + /* using users from the YANG data */ + local_users_supported = 1; + } else if (rc == LY_ENOT) { + /* using users from the system */ + local_users_supported = 0; } else { - VRB(session, "Authentication method \"%s\" not supported.", str_subtype); - ssh_message_reply_default(msg); - return 0; - } - - if (!ret) { - state->auth_success_count++; - } - - if (!ret && (state->auth_success_count < state->auth_method_count)) { - /* success, but he needs to do another method */ - VRB(session, "User \"%s\" partially authenticated, but still needs to authenticate via the rest of his configured methods.", username); - ssh_message_auth_reply_success(msg, 1); - } else if (!ret && (state->auth_success_count == state->auth_method_count)) { - /* authenticated */ - ssh_message_auth_reply_success(msg, 0); - session->flags |= NC_SESSION_SSH_AUTHENTICATED; - VRB(session, "User \"%s\" authenticated.", username); + ERRINT; + return 1; } - return 0; + /* authenticate */ + return nc_server_ssh_auth(session, opts, msg, subtype, str_subtype, local_users_supported, state); } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) { if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) { - if (nc_sshcb_channel_open(session, msg)) { + if (nc_server_ssh_channel_open(session, msg)) { ssh_message_reply_default(msg); } return 0; } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) { - if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg), + if (nc_server_ssh_channel_subsystem(session, ssh_message_channel_request_channel(msg), ssh_message_channel_request_subsystem(msg))) { ssh_message_reply_default(msg); } else { @@ -1621,7 +1820,7 @@ nc_ssh_bind_add_hostkeys(ssh_bind sbind, struct nc_server_ssh_opts *opts, uint16 } } - privkey_path = base64der_privkey_to_tmp_file(key->privkey_data, nc_privkey_format_to_str(key->privkey_type)); + privkey_path = nc_server_ssh_privkey_data_to_tmp_file(key->privkey_data, nc_privkey_format_to_str(key->privkey_type)); if (!privkey_path) { ERR(NULL, "Temporarily storing a host key into a file failed (%s).", strerror(errno)); return -1;