From f34cd24f8073e87d09159b8a6c8e2fa48cd17227 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Sun, 28 Feb 2010 22:51:21 +0100 Subject: Fixed and added support for several identity files. --- libssh/auth.c | 251 +++++++++++++++++++++++++----------------------------- libssh/keyfiles.c | 146 +++++++++++++++++++++++++++++++ libssh/misc.c | 46 ++++++++++ libssh/options.c | 75 +++++++--------- libssh/session.c | 47 +++++++++- 5 files changed, 383 insertions(+), 182 deletions(-) (limited to 'libssh') diff --git a/libssh/auth.c b/libssh/auth.c index b89f57d0..c73afb05 100644 --- a/libssh/auth.c +++ b/libssh/auth.c @@ -35,6 +35,7 @@ #include "libssh/buffer.h" #include "libssh/agent.h" #include "libssh/keyfiles.h" +#include "libssh/misc.h" #include "libssh/packet.h" #include "libssh/session.h" #include "libssh/keys.h" @@ -902,42 +903,6 @@ error: return rc; } -#ifdef _MSC_VER -static const char privKey_1[] = "SSH_DIR/identity"; -static const char pubKey_1[] = "SSH_DIR/identity.pub"; -static const char privKey_2[] = "SSH_DIR/id_dsa"; -static const char pubKey_2[] = "SSH_DIR/id_dsa.pub"; -static const char privKey_3[] = "SSH_DIR/id_rsa"; -static const char pubKey_3[] = "SSH_DIR/id_rsa.pub"; -/** Used different var to allow const char[] declaration */ -static struct ssh_keys_struct keytab[] = { - { privKey_1, pubKey_1}, - { privKey_2, pubKey_2}, - { privKey_3, pubKey_3}, - {0} -}; -#else -/* This requires GCC extensions */ -static struct ssh_keys_struct keytab[] = { - { - .privatekey = "SSH_DIR/identity", - .publickey = "SSH_DIR/identity.pub" - }, - { - .privatekey = "SSH_DIR/id_dsa", - .publickey = "SSH_DIR/id_dsa.pub", - }, - { - .privatekey = "SSH_DIR/id_rsa", - .publickey = "SSH_DIR/id_rsa.pub", - }, - { - .privatekey = NULL, - .publickey = NULL - } -}; -#endif - /** * @brief Tries to automatically authenticate with public key and "none" * @@ -961,13 +926,10 @@ static struct ssh_keys_struct keytab[] = { * @see ssh_options_set() */ int ssh_userauth_autopubkey(ssh_session session, const char *passphrase) { - struct ssh_public_key_struct *publickey; - ssh_string pubkey; + struct ssh_iterator *it; ssh_private_key privkey; - char *privkeyfile = NULL; - char *id = NULL; - size_t size; - unsigned int i = 0; + ssh_public_key pubkey; + ssh_string pubkey_string; int type = 0; int rc; @@ -983,41 +945,40 @@ int ssh_userauth_autopubkey(ssh_session session, const char *passphrase) { /* Try authentication with ssh-agent first */ #ifndef _WIN32 if (agent_is_running(session)) { + char *privkey_file = NULL; + ssh_log(session, SSH_LOG_RARE, "Trying to authenticate with SSH agent keys as user: %s", session->username); - for (publickey = agent_get_first_ident(session, &privkeyfile); - publickey != NULL; - publickey = agent_get_next_ident(session, &privkeyfile)) { + for (pubkey = agent_get_first_ident(session, &privkey_file); + pubkey != NULL; + pubkey = agent_get_next_ident(session, &privkey_file)) { - ssh_log(session, SSH_LOG_RARE, "Trying identity %s", privkeyfile); + ssh_log(session, SSH_LOG_RARE, "Trying identity %s", privkey_file); - pubkey = publickey_to_string(publickey); - if (pubkey) { - rc = ssh_userauth_offer_pubkey(session, NULL, publickey->type, pubkey); - string_free(pubkey); + pubkey_string = publickey_to_string(pubkey); + if (pubkey_string) { + rc = ssh_userauth_offer_pubkey(session, NULL, pubkey->type, pubkey_string); + string_free(pubkey_string); if (rc == SSH_AUTH_ERROR) { - SAFE_FREE(id); - SAFE_FREE(privkeyfile); - publickey_free(publickey); + SAFE_FREE(privkey_file); + publickey_free(pubkey); leave_function(); return rc; } else if (rc != SSH_AUTH_SUCCESS) { ssh_log(session, SSH_LOG_PROTOCOL, "Public key refused by server"); - SAFE_FREE(id); - SAFE_FREE(privkeyfile); - publickey_free(publickey); + SAFE_FREE(privkey_file); + publickey_free(pubkey); continue; } ssh_log(session, SSH_LOG_PROTOCOL, "Public key accepted"); /* pubkey accepted by server ! */ - rc = ssh_userauth_agent_pubkey(session, NULL, publickey); + rc = ssh_userauth_agent_pubkey(session, NULL, pubkey); if (rc == SSH_AUTH_ERROR) { - SAFE_FREE(id); - SAFE_FREE(privkeyfile); - publickey_free(publickey); + SAFE_FREE(privkey_file); + publickey_free(pubkey); leave_function(); return rc; @@ -1025,100 +986,129 @@ int ssh_userauth_autopubkey(ssh_session session, const char *passphrase) { ssh_log(session, SSH_LOG_RARE, "Server accepted public key but refused the signature ;" " It might be a bug of libssh"); - SAFE_FREE(id); - SAFE_FREE(privkeyfile); - publickey_free(publickey); + SAFE_FREE(privkey_file); + publickey_free(pubkey); continue; } /* auth success */ ssh_log(session, SSH_LOG_PROTOCOL, "Authentication using %s success", - privkeyfile); - SAFE_FREE(id); - SAFE_FREE(privkeyfile); - publickey_free(publickey); + privkey_file); + SAFE_FREE(privkey_file); + publickey_free(pubkey); leave_function(); return SSH_AUTH_SUCCESS; } /* if pubkey */ - SAFE_FREE(id); - SAFE_FREE(privkeyfile); - publickey_free(publickey); + SAFE_FREE(privkey_file); + publickey_free(pubkey); } /* for each privkey */ } /* if agent is running */ #endif - size = ARRAY_SIZE(keytab); - if (session->identity) { - ssh_log(session, SSH_LOG_PROTOCOL, - "Trying identity file %s", session->identity); + for (it = ssh_list_get_iterator(session->identity); + it != NULL; + it = it->next) { + char *privkey_file = NULL; + int privkey_open = 0; - id = malloc(strlen(session->identity) + 1 + 4); - if (id == NULL) { - leave_function(); - return SSH_AUTH_ERROR; + privkey_file = dir_expand_dup(session, it->data, 1); + if (privkey_file == NULL) { + continue; } - sprintf(id, "%s.pub", session->identity); - keytab[size - 1].privatekey = session->identity; - keytab[size - 1].publickey = id; - } + ssh_log(session, SSH_LOG_PROTOCOL, "Trying to read privatekey %s", privkey_file); + + rc = ssh_try_publickey_from_file(session, privkey_file, &pubkey_string, &type); + if (rc == 1) { + char *publickey_file; + size_t len; - for (i = 0, pubkey = try_publickey_from_file(session, keytab[i], - &privkeyfile, &type); - i < size; - pubkey = try_publickey_from_file(session, keytab[i++], - &privkeyfile, &type)) { - if (pubkey == NULL) { + privkey = privatekey_from_file(session, privkey_file, type, passphrase); + if (privkey == NULL) { + ssh_log(session, SSH_LOG_RARE, + "Reading private key %s failed (bad passphrase ?)", + privkey_file); + SAFE_FREE(privkey_file); + leave_function(); + return SSH_AUTH_ERROR; + } + privkey_open = 1; + + pubkey = publickey_from_privatekey(privkey); + if (pubkey == NULL) { + SAFE_FREE(privkey_file); + privatekey_free(privkey); + ssh_set_error_oom(session); + leave_function(); + return SSH_AUTH_ERROR; + } + + pubkey_string = publickey_to_string(pubkey); + type = pubkey->type; + publickey_free(pubkey); + if (pubkey_string == NULL) { + SAFE_FREE(privkey_file); + ssh_set_error_oom(session); + leave_function(); + return SSH_AUTH_ERROR; + } + + len = strlen(privkey_file) + 5; + publickey_file = malloc(len); + if (publickey_file == NULL) { + SAFE_FREE(privkey_file); + ssh_set_error_oom(session); + leave_function(); + return SSH_AUTH_ERROR; + } + snprintf(publickey_file, len, "%s.pub", privkey_file); + rc = ssh_publickey_to_file(session, publickey_file, pubkey_string, type); + if (rc < 0) { + ssh_log(session, SSH_LOG_PACKET, + "Could not write public key to file: %s", publickey_file); + } + SAFE_FREE(publickey_file); + } else if (rc < 0) { + SAFE_FREE(privkey_file); continue; } - rc = ssh_userauth_offer_pubkey(session, NULL, type, pubkey); + rc = ssh_userauth_offer_pubkey(session, NULL, type, pubkey_string); if (rc == SSH_AUTH_ERROR){ - if (id != NULL) { - keytab[size - 1].privatekey = NULL; - keytab[size - 1].publickey = NULL; - SAFE_FREE(id); - } - string_free(pubkey); - SAFE_FREE(privkeyfile); + SAFE_FREE(privkey_file); + string_free(pubkey_string); ssh_log(session, SSH_LOG_RARE, "Publickey authentication error"); leave_function(); return rc; } else { if (rc != SSH_AUTH_SUCCESS){ ssh_log(session, SSH_LOG_PROTOCOL, "Publickey refused by server"); - string_free(pubkey); - pubkey = NULL; - SAFE_FREE(privkeyfile); - privkeyfile = NULL; + SAFE_FREE(privkey_file); + string_free(pubkey_string); continue; } } /* Public key accepted by server! */ - ssh_log(session, SSH_LOG_PROTOCOL, "Trying to read privatekey %s", privkeyfile); - privkey = privatekey_from_file(session, privkeyfile, type, passphrase); - if (privkey == NULL) { - ssh_log(session, SSH_LOG_RARE, - "Reading private key %s failed (bad passphrase ?)", - privkeyfile); - string_free(pubkey); - pubkey = NULL; - SAFE_FREE(privkeyfile); - privkeyfile = NULL; - continue; /* continue the loop with other pubkey */ + if (!privkey_open) { + ssh_log(session, SSH_LOG_PROTOCOL, "Trying to read privatekey %s", + privkey_file); + privkey = privatekey_from_file(session, privkey_file, type, passphrase); + if (privkey == NULL) { + ssh_log(session, SSH_LOG_RARE, + "Reading private key %s failed (bad passphrase ?)", + privkey_file); + SAFE_FREE(privkey_file); + string_free(pubkey_string); + continue; /* continue the loop with other pubkey */ + } } - rc = ssh_userauth_pubkey(session, NULL, pubkey, privkey); + rc = ssh_userauth_pubkey(session, NULL, pubkey_string, privkey); if (rc == SSH_AUTH_ERROR) { - if (id != NULL) { - keytab[size - 1].privatekey = NULL; - keytab[size - 1].publickey = NULL; - SAFE_FREE(id); - } - string_free(pubkey); - SAFE_FREE(privkeyfile); + SAFE_FREE(privkey_file); + string_free(pubkey_string); privatekey_free(privkey); leave_function(); return rc; @@ -1126,10 +1116,8 @@ int ssh_userauth_autopubkey(ssh_session session, const char *passphrase) { if (rc != SSH_AUTH_SUCCESS){ ssh_log(session, SSH_LOG_RARE, "The server accepted the public key but refused the signature"); - string_free(pubkey); - pubkey = NULL; - SAFE_FREE(privkeyfile); - privkeyfile = NULL; + SAFE_FREE(privkey_file); + string_free(pubkey_string); privatekey_free(privkey); continue; } @@ -1137,28 +1125,19 @@ int ssh_userauth_autopubkey(ssh_session session, const char *passphrase) { /* auth success */ ssh_log(session, SSH_LOG_PROTOCOL, - "Successfully authenticated using %s", privkeyfile); - string_free(pubkey); + "Successfully authenticated using %s", privkey_file); + SAFE_FREE(privkey_file); + string_free(pubkey_string); privatekey_free(privkey); - SAFE_FREE(privkeyfile); - if (id != NULL) { - keytab[size - 1].privatekey = NULL; - keytab[size - 1].publickey = NULL; - SAFE_FREE(id); - } leave_function(); return SSH_AUTH_SUCCESS; } + /* at this point, pubkey is NULL and so is privkeyfile */ ssh_log(session, SSH_LOG_PROTOCOL, "Tried every public key, none matched"); ssh_set_error(session,SSH_NO_ERROR,"No public key matched"); - if (id) { - keytab[size - 1].privatekey = NULL; - keytab[size - 1].publickey = NULL; - SAFE_FREE(id); - } leave_function(); return SSH_AUTH_DENIED; diff --git a/libssh/keyfiles.c b/libssh/keyfiles.c index ab2050ed..40c60304 100644 --- a/libssh/keyfiles.c +++ b/libssh/keyfiles.c @@ -887,6 +887,79 @@ void privatekey_free(ssh_private_key prv) { SAFE_FREE(prv); } +/** + * @brief Write a public key to a file. + * + * @param[in] session The ssh session to use. + * + * @param[in] file The filename to write the key into. + * + * @param[in] pubkey The public key to write. + * + * @param[in] type The type of the public key. + * + * @return 0 on success, -1 on error. + */ +int ssh_publickey_to_file(ssh_session session, const char *file, + ssh_string pubkey, int type) { + FILE *fp; + char *user; + char buffer[1024]; + char host[256]; + unsigned char *pubkey_64; + size_t len; + int rc; + + pubkey_64 = bin_to_base64(pubkey->string, string_len(pubkey)); + if (pubkey_64 == NULL) { + return -1; + } + + user = ssh_get_local_username(session); + if (user == NULL) { + SAFE_FREE(pubkey_64); + return -1; + } + + rc = gethostname(host, sizeof(host)); + if (rc < 0) { + SAFE_FREE(user); + SAFE_FREE(pubkey_64); + return -1; + } + + snprintf(buffer, sizeof(buffer), "%s %s %s@%s\n", + ssh_type_to_char(type), + pubkey_64, + user, + host); + + SAFE_FREE(pubkey_64); + SAFE_FREE(user); + + ssh_log(session, SSH_LOG_RARE, "Trying to write public key file: %s", file); + ssh_log(session, SSH_LOG_PACKET, "public key file content: %s", buffer); + + fp = fopen(file, "w+"); + if (fp == NULL) { + ssh_set_error(session, SSH_REQUEST_DENIED, + "Error opening %s: %s", file, strerror(errno)); + return -1; + } + + len = strlen(buffer); + if (fwrite(buffer, len, 1, fp) != 1 || ferror(fp)) { + ssh_set_error(session, SSH_REQUEST_DENIED, + "Unable to write to %s", file); + fclose(fp); + unlink(file); + return -1; + } + + fclose(fp); + return 0; +} + /** \brief Retrieve a public key from a file * \param session the SSH session * \param filename Filename of the key @@ -964,6 +1037,79 @@ ssh_string publickey_from_file(ssh_session session, const char *filename, return str; } +/** + * @brief Try to read the public key from a given file. + * + * @param[in] session The ssh session to use. + * + * @param[in] keyfile The name of the private keyfile. + * + * @param[out] publickey A ssh_string to store the public key. + * + * @param[out] type A pointer to an integer to store the type. + * + * @return 0 on success, -1 on error or the private key doesn't + * exist, 1 if the public key doesn't exist. + */ +int ssh_try_publickey_from_file(ssh_session session, const char *keyfile, + ssh_string *publickey, int *type) { + char *pubkey_file; + size_t len; + ssh_string pubkey_string; + int pubkey_type; + + if (session == NULL || keyfile == NULL || publickey == NULL || type == NULL) { + return -1; + } + + if (session->sshdir == NULL) { + if (ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL) < 0) { + return -1; + } + } + + ssh_log(session, SSH_LOG_PACKET, "Trying to open privatekey %s", keyfile); + if (!ssh_file_readaccess_ok(keyfile)) { + ssh_log(session, SSH_LOG_PACKET, "Failed to open privatekey %s", keyfile); + return -1; + } + + len = strlen(keyfile) + 5; + pubkey_file = malloc(len); + if (pubkey_file == NULL) { + return -1; + } + snprintf(pubkey_file, len, "%s.pub", keyfile); + + ssh_log(session, SSH_LOG_PACKET, "Trying to open publickey %s", + pubkey_file); + if (!ssh_file_readaccess_ok(pubkey_file)) { + ssh_log(session, SSH_LOG_PACKET, "Failed to open publickey %s", + pubkey_file); + return 1; + } + + ssh_log(session, SSH_LOG_PACKET, "Success opening public and private key"); + + /* + * We are sure both the private and public key file is readable. We return + * the public as a string, and the private filename as an argument + */ + pubkey_string = publickey_from_file(session, pubkey_file, &pubkey_type); + if (pubkey_string == NULL) { + ssh_log(session, SSH_LOG_PACKET, + "Wasn't able to open public key file %s: %s", + pubkey_file, + ssh_get_error(session)); + return -1; + } + + *publickey = pubkey_string; + *type = pubkey_type; + + return 0; +} + ssh_string try_publickey_from_file(ssh_session session, struct ssh_keys_struct keytab, char **privkeyfile, int *type) { char *public; diff --git a/libssh/misc.c b/libssh/misc.c index 95b20306..22fb1a65 100644 --- a/libssh/misc.c +++ b/libssh/misc.c @@ -158,6 +158,52 @@ uint64_t ntohll(uint64_t a) { #endif } +#ifdef _WIN32 +char *ssh_get_local_username(ssh_session session) { + DWORD size = 0; + char *user; + + /* get the size */ + GetUserName(NULL, &size); + + user = malloc(size); + if (user == NULL) { + ssh_set_error_oom(session); + return NULL; + } + + if (GetUserName(user, &size)) { + return user; + } + + return NULL; +} +#else +char *ssh_get_local_username(ssh_session session) { + struct passwd pwd; + struct passwd *pwdbuf; + char buf[NSS_BUFLEN_PASSWD]; + char *name; + int rc; + + rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); + if (rc != 0) { + ssh_set_error(session, SSH_FATAL, + "Couldn't retrieve information for current user!"); + return NULL; + } + + name = strdup(pwd.pw_name); + + if (name == NULL) { + ssh_set_error_oom(session); + return NULL; + } + + return name; +} +#endif + /** * @brief Check if libssh is the required version or get the version * string. diff --git a/libssh/options.c b/libssh/options.c index 7a06fad9..a7d62415 100644 --- a/libssh/options.c +++ b/libssh/options.c @@ -83,10 +83,29 @@ int ssh_options_copy(ssh_session src, ssh_session *dest) { } if (src->identity) { - new->identity = strdup(src->identity); + struct ssh_iterator *it; + + new->identity = ssh_list_new(); if (new->identity == NULL) { return -1; } + + it = ssh_list_get_iterator(src->identity); + while (it) { + char *id; + int rc; + + id = strdup((char *) it->data); + if (id == NULL) { + return -1; + } + + rc = ssh_list_append(new->identity, id); + if (rc < 0) { + return -1; + } + it = it->next; + } } if (src->sshdir) { @@ -124,29 +143,6 @@ int ssh_options_copy(ssh_session src, ssh_session *dest) { return 0; } -#ifndef _WIN32 -static char *get_username_from_uid(ssh_session session, uid_t uid){ - struct passwd *pwd = NULL; - char *name; - - pwd = getpwuid(uid); - - if (pwd == NULL) { - ssh_set_error(session, SSH_FATAL, "uid %d doesn't exist !", uid); - return NULL; - } - - name = strdup(pwd->pw_name); - - if (name == NULL) { - ssh_set_error_oom(session); - return NULL; - } - - return name; -} -#endif - int ssh_options_set_algo(ssh_session session, int algo, const char *list) { if (!verify_existing_algo(algo, list)) { @@ -376,6 +372,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, const void *value) { char *p, *q; long int i; + int rc; if (session == NULL) { return -1; @@ -442,27 +439,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, case SSH_OPTIONS_USER: SAFE_FREE(session->username); if (value == NULL) { /* set default username */ -#ifdef _WIN32 - DWORD size = 0; - GetUserName(NULL, &size); //Get Size - q = malloc(size); - if (q == NULL) { - ssh_set_error_oom(session); - return -1; - } - if (GetUserName(q, &size)) { - session->username = q; - } else { - SAFE_FREE(q); - return -1; - } -#else /* _WIN32 */ - q = get_username_from_uid(session, getuid()); + q = ssh_get_local_username(session); if (q == NULL) { return -1; } session->username = q; -#endif /* _WIN32 */ } else { /* username provided */ session->username = strdup(value); if (session->username == NULL) { @@ -489,14 +470,18 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, } break; case SSH_OPTIONS_IDENTITY: - + case SSH_OPTIONS_ADD_IDENTITY: if (value == NULL) { ssh_set_error_invalid(session, __FUNCTION__); return -1; } - SAFE_FREE(session->identity); - session->identity = dir_expand_dup(session, value, 1); - if (session->identity == NULL) { + q = dir_expand_dup(session, value, 1); + if (q == NULL) { + return -1; + } + rc = ssh_list_prepend(session->identity, q); + if (rc < 0) { + SAFE_FREE(q); return -1; } break; diff --git a/libssh/session.c b/libssh/session.c index a39a68c5..e6d9fc7d 100644 --- a/libssh/session.c +++ b/libssh/session.c @@ -54,6 +54,8 @@ */ ssh_session ssh_new(void) { ssh_session session; + char *id; + int rc; session = malloc(sizeof (struct ssh_session_struct)); if (session == NULL) { @@ -103,6 +105,39 @@ ssh_session ssh_new(void) { goto err; } #endif /* _WIN32 */ + + session->identity = ssh_list_new(); + if (session->identity == NULL) { + goto err; + } + + id = strdup("SSH_DIR/id_rsa"); + if (id == NULL) { + goto err; + } + rc = ssh_list_append(session->identity, id); + if (rc == SSH_ERROR) { + goto err; + } + + id = strdup("SSH_DIR/id_dsa"); + if (id == NULL) { + goto err; + } + rc = ssh_list_append(session->identity, id); + if (rc == SSH_ERROR) { + goto err; + } + + id = strdup("SSH_DIR/identity"); + if (id == NULL) { + goto err; + } + rc = ssh_list_append(session->identity, id); + if (rc == SSH_ERROR) { + goto err; + } + return session; err: @@ -173,10 +208,20 @@ void ssh_free(ssh_session session) { ssh_list_free(session->ssh_message_list); } + if (session->identity) { + char *id; + + for (id = ssh_list_pop_head(char *, session->identity); + id != NULL; + id = ssh_list_pop_head(char *, session->identity)) { + SAFE_FREE(id); + } + ssh_list_free(session->identity); + } + /* options */ SAFE_FREE(session->username); SAFE_FREE(session->host); - SAFE_FREE(session->identity); SAFE_FREE(session->sshdir); SAFE_FREE(session->knownhosts); -- cgit v1.2.3