diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index e6f08472..577ddb71 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -425,8 +425,10 @@ LIBSSH_API enum ssh_keytypes_e ssh_key_type_from_name(const char *name); LIBSSH_API int ssh_key_is_public(ssh_key k); LIBSSH_API int ssh_key_is_private(ssh_key k); -LIBSSH_API int ssh_pki_import_privkey_base64(ssh_key key, ssh_session session, - const char *b64_key, const char *passphrase); +LIBSSH_API int ssh_pki_import_privkey_base64(ssh_session session, + const char *b64_key, + const char *passphrase, + ssh_key *pkey); LIBSSH_API int ssh_userauth_pki_pubkey(ssh_session session, const char *username, ssh_string publickey, ssh_key privatekey); LIBSSH_API void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len); diff --git a/include/libssh/pki.h b/include/libssh/pki.h index 463f614f..32e084f4 100644 --- a/include/libssh/pki.h +++ b/include/libssh/pki.h @@ -59,4 +59,7 @@ ssh_public_key ssh_pki_convert_key_to_publickey(ssh_key key); enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey); +ssh_key pki_private_key_from_base64(ssh_session session, + const char *b64_key, + const char *passphrase); #endif /* PKI_H_ */ diff --git a/src/legacy.c b/src/legacy.c index 76bcf503..678b0658 100644 --- a/src/legacy.c +++ b/src/legacy.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include void buffer_free(ssh_buffer buffer){ ssh_buffer_free(buffer); @@ -234,6 +236,33 @@ char *string_to_char(ssh_string str){ return ssh_string_to_char(str); } +ssh_private_key privatekey_from_base64(ssh_session session, + const char *b64_pkey, + int type, + const char *passphrase) { + ssh_private_key privkey; + ssh_key key; + + (void) type; /* unused */ + + key = pki_private_key_from_base64(session, b64_pkey, passphrase); + if (key == NULL) { + return NULL; + } + + privkey = malloc(sizeof(struct ssh_private_key_struct)); + if (privkey == NULL) { + ssh_key_free(key); + return NULL; + } + + privkey->type = key->type; + privkey->dsa_priv = key->dsa; + privkey->rsa_priv = key->rsa; + + return privkey; +} + /**************************************************************************** * SERVER SUPPORT ****************************************************************************/ diff --git a/src/pki.c b/src/pki.c index c3947ae5..1966ca9d 100644 --- a/src/pki.c +++ b/src/pki.c @@ -247,32 +247,30 @@ ssh_public_key ssh_pki_convert_key_to_publickey(ssh_key key) { * * @return SSH_ERROR in case of error, SSH_OK otherwise */ -int ssh_pki_import_privkey_base64(ssh_key key, ssh_session session, - const char *b64_key, const char *passphrase) { - ssh_private_key priv; +int ssh_pki_import_privkey_base64(ssh_session session, + const char *b64_key, + const char *passphrase, + ssh_key *pkey) { + ssh_key key; - if(key == NULL || session == NULL) { + if (pkey == NULL || session == NULL) { return SSH_ERROR; } - if(b64_key == NULL || !*b64_key) { + if (b64_key == NULL || !*b64_key) { return SSH_ERROR; } - priv = privatekey_from_base64(session, b64_key, 0, passphrase); - if(priv == NULL) { + ssh_log(session, SSH_LOG_RARE, "Trying to decode privkey passphrase=%s", + passphrase ? "true" : "false"); + + key = pki_private_key_from_base64(session, b64_key, passphrase); + if (key == NULL) { return SSH_ERROR; } - ssh_key_clean(key); + *pkey = key; - key->dsa = priv->dsa_priv; - key->rsa = priv->rsa_priv; - key->type = priv->type; - key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; - key->type_c = ssh_type_to_char(key->type); - - SAFE_FREE(priv); return SSH_OK; } diff --git a/src/pki_crypto.c b/src/pki_crypto.c index c6b92244..db29a2eb 100644 --- a/src/pki_crypto.c +++ b/src/pki_crypto.c @@ -36,6 +36,7 @@ #include "libssh/libssh.h" #include "libssh/session.h" #include "libssh/callbacks.h" +#include "libssh/pki.h" static int pem_get_password(char *buf, int size, int rwflag, void *userdata) { ssh_session session = userdata; @@ -66,4 +67,100 @@ static int pem_get_password(char *buf, int size, int rwflag, void *userdata) { return 0; } +ssh_key pki_private_key_from_base64(ssh_session session, + const char *b64_key, + const char *passphrase) { + BIO *mem = NULL; + DSA *dsa = NULL; + RSA *rsa = NULL; + ssh_key key; + enum ssh_keytypes_e type; + + /* needed for openssl initialization */ + if (ssh_init() < 0) { + return NULL; + } + + type = pki_privatekey_type_from_string(b64_key); + if (type == SSH_KEYTYPE_UNKNOWN) { + ssh_set_error(session, SSH_FATAL, "Unknown or invalid private key."); + return NULL; + } + + mem = BIO_new_mem_buf((void*)b64_key, -1); + + switch (type) { + case SSH_KEYTYPE_DSS: + if (passphrase == NULL) { + if (session->common.callbacks && session->common.callbacks->auth_function) { + dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, pem_get_password, session); + } else { + /* openssl uses its own callback to get the passphrase here */ + dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, NULL, NULL); + } + } else { + dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, NULL, (void *) passphrase); + } + + BIO_free(mem); + + if (dsa == NULL) { + ssh_set_error(session, SSH_FATAL, + "Parsing private key: %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + if (passphrase == NULL) { + if (session->common.callbacks && session->common.callbacks->auth_function) { + rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, pem_get_password, session); + } else { + /* openssl uses its own callback to get the passphrase here */ + rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL); + } + } else { + rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, (void *) passphrase); + } + + BIO_free(mem); + + if (rsa == NULL) { + ssh_set_error(session, SSH_FATAL, + "Parsing private key: %s", + ERR_error_string(ERR_get_error(),NULL)); + return NULL; + } + + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + BIO_free(mem); + ssh_set_error(session, SSH_FATAL, + "Unkown or invalid private key type %d", type); + return NULL; + } + + key = ssh_key_new(); + if (key == NULL) { + goto fail; + } + + key->type = type; + key->type_c = ssh_key_type_to_char(type); + key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; + key->dsa = dsa; + key->rsa = rsa; + + return key; +fail: + ssh_key_free(key); + DSA_free(dsa); + RSA_free(rsa); + + return NULL; +} + #endif /* _PKI_CRYPTO_H */ diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c index 480478a7..f160a805 100644 --- a/src/pki_gcrypt.c +++ b/src/pki_gcrypt.c @@ -55,6 +55,7 @@ #include "libssh/wrapper.h" #include "libssh/misc.h" #include "libssh/keys.h" +#include "libssh/pki.h" /*todo: remove this include */ #include "libssh/string.h" @@ -861,42 +862,104 @@ error: return rc; } -#endif /* HAVE_LIBGCRYPT */ -#ifdef HAVE_LIBCRYPTO -static int pem_get_password(char *buf, int size, int rwflag, void *userdata) { - ssh_session session = userdata; +ssh_key pki_private_key_from_base64(ssh_session session, + const char *b64_key, + const char *passphrase) { + ssh_auth_callback auth_cb = NULL; + void *auth_ud = NULL; - /* unused flag */ - (void) rwflag; - if(buf==NULL) - return 0; - memset(buf,'\0',size); - ssh_log(session, SSH_LOG_RARE, - "Trying to call external authentication function"); + gcry_sexp_t dsa = NULL; + gcry_sexp_t rsa = NULL; + ssh_key key = NULL; + enum ssh_keytypes_e type; + int valid; - if (session && session->common.callbacks && session->common.callbacks->auth_function) { - if (session->common.callbacks->auth_function("Passphrase for private key:", buf, size, 0, 0, - session->common.callbacks->userdata) < 0) { - return 0; + /* needed for gcrypt initialization */ + if (ssh_init() < 0) { + return NULL; } - return strlen(buf); - } + type = pki_privatekey_type_from_string(b64_key); + if (type == SSH_KEYTYPE_UNKNOWN) { + ssh_set_error(session, SSH_FATAL, "Unknown or invalid private key."); + return NULL; + } - return 0; -} -#endif /* HAVE_LIBCRYPTO */ + switch (type) { + case SSH_KEYTYPE_DSS: + if (passphrase == NULL) { + if (session->common.callbacks && + session->common.callbacks->auth_function) { + auth_cb = session->common.callbacks->auth_function; + auth_ud = session->common.callbacks->userdata; -static int privatekey_type_from_string(const char *pkey) { - if (strncmp(pkey, DSA_HEADER_BEGIN, strlen(DSA_HEADER_BEGIN)) == 0) { - return SSH_KEYTYPE_DSS; - } - if (strncmp(pkey, RSA_HEADER_BEGIN, strlen(RSA_HEADER_BEGIN)) == 0) { - return SSH_KEYTYPE_RSA; - } - return 0; + valid = b64decode_dsa_privatekey(b64_key, &dsa, auth_cb, + auth_ud, "Passphrase for private key:"); + } else { + valid = b64decode_dsa_privatekey(b64_key, &dsa, NULL, NULL, + NULL); + } + } else { + valid = b64decode_dsa_privatekey(b64_key, &dsa, NULL, (void *) + passphrase, NULL); + } + + if (!valid) { + ssh_set_error(session, SSH_FATAL, "Parsing private key"); + goto fail; + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + if (passphrase == NULL) { + if (session->common.callbacks && + session->common.callbacks->auth_function) { + auth_cb = session->common.callbacks->auth_function; + auth_ud = session->common.callbacks->userdata; + valid = b64decode_rsa_privatekey(b64_key, &rsa, auth_cb, + auth_ud, "Passphrase for private key:"); + } else { + valid = b64decode_rsa_privatekey(b64_key, &rsa, NULL, NULL, + NULL); + } + } else { + valid = b64decode_rsa_privatekey(b64_key, &rsa, NULL, + (void *)passphrase, NULL); + } + + if (!valid) { + ssh_set_error(session,SSH_FATAL, "Parsing private key"); + goto fail; + } + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + ssh_set_error(session, SSH_FATAL, + "Unkown or invalid private key type %d", type); + return NULL; + } + + key = ssh_key_new(); + if (key == NULL) { + goto fail; + } + + key->type = type; + key->type_c = ssh_key_type_to_char(type); + key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; + key->dsa = dsa; + key->rsa = rsa; + + return key; +fail: + ssh_key_free(key); + gcry_sexp_release(dsa); + gcry_sexp_release(rsa); + + return NULL; } +#endif /* HAVE_LIBGCRYPT */ /** * @addtogroup libssh_auth @@ -966,156 +1029,6 @@ ssh_private_key privatekey_from_file(ssh_session session, const char *filename, return privkey; } -ssh_private_key privatekey_from_base64(ssh_session session, const char *b64_pkey, - int type, const char *passphrase) { - ssh_private_key privkey = NULL; -#ifdef HAVE_LIBGCRYPT - ssh_auth_callback auth_cb = NULL; - void *auth_ud = NULL; - - gcry_sexp_t dsa = NULL; - gcry_sexp_t rsa = NULL; - int valid; -#elif defined HAVE_LIBCRYPTO - BIO *mem = NULL; - DSA *dsa = NULL; - RSA *rsa = NULL; -#endif - /* TODO Implement to read both DSA and RSA at once. */ - - if(b64_pkey == NULL || !*b64_pkey) { - return NULL; - } - - /* needed for openssl initialization */ - if (ssh_init() < 0) { - return NULL; - } - - ssh_log(session, SSH_LOG_RARE, "Trying to read privkey type=%s, passphase=%s, authcb=%s", - type ? type == SSH_KEYTYPE_DSS ? "ssh-dss" : "ssh-rsa": "unknown", - passphrase ? "true" : "false", - session->common.callbacks && session->common.callbacks->auth_function ? "true" : "false"); - - if (type == 0) { - type = privatekey_type_from_string(b64_pkey); - if (type == 0) { - ssh_set_error(session, SSH_FATAL, "Invalid private key."); - return NULL; - } - } - switch (type) { - case SSH_KEYTYPE_DSS: -#ifdef HAVE_LIBGCRYPT - if (passphrase == NULL) { - if (session->common.callbacks && session->common.callbacks->auth_function) { - auth_cb = session->common.callbacks->auth_function; - auth_ud = session->common.callbacks->userdata; - - valid = b64decode_dsa_privatekey(b64_pkey, &dsa, auth_cb, auth_ud, - "Passphrase for private key:"); - } else { /* authcb */ - valid = b64decode_dsa_privatekey(b64_pkey, &dsa, NULL, NULL, NULL); - } /* authcb */ - } else { /* passphrase */ - valid = b64decode_dsa_privatekey(b64_pkey, &dsa, NULL, - (void *) passphrase, NULL); - } - - if (!valid) { - ssh_set_error(session, SSH_FATAL, "Parsing private key"); - return NULL; - } -#elif defined HAVE_LIBCRYPTO - mem = BIO_new_mem_buf((void*)b64_pkey, -1); - if (passphrase == NULL) { - if (session->common.callbacks && session->common.callbacks->auth_function) { - dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, pem_get_password, session); - } else { /* authcb */ - /* openssl uses its own callback to get the passphrase here */ - dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, NULL, NULL); - } /* authcb */ - } else { /* passphrase */ - dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, NULL, (void *) passphrase); - } - - BIO_free(mem); - - if (dsa == NULL) { - ssh_set_error(session, SSH_FATAL, - "Parsing private key: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NULL; - } -#endif - break; - case SSH_KEYTYPE_RSA: -#ifdef HAVE_LIBGCRYPT - if (passphrase == NULL) { - if (session->common.callbacks && session->common.callbacks->auth_function) { - auth_cb = session->common.callbacks->auth_function; - auth_ud = session->common.callbacks->userdata; - valid = b64decode_rsa_privatekey(b64_pkey, &rsa, auth_cb, auth_ud, - "Passphrase for private key:"); - } else { /* authcb */ - valid = b64decode_rsa_privatekey(b64_pkey, &rsa, NULL, NULL, NULL); - } /* authcb */ - } else { /* passphrase */ - valid = b64decode_rsa_privatekey(b64_pkey, &rsa, NULL, - (void *) passphrase, NULL); - } - - if (!valid) { - ssh_set_error(session,SSH_FATAL, "Parsing private key"); - return NULL; - } -#elif defined HAVE_LIBCRYPTO - mem = BIO_new_mem_buf((void*)b64_pkey, -1); - if (passphrase == NULL) { - if (session->common.callbacks && session->common.callbacks->auth_function) { - rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, pem_get_password, session); - } else { /* authcb */ - /* openssl uses its own callback to get the passphrase here */ - rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL); - } /* authcb */ - } else { /* passphrase */ - rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, (void *) passphrase); - } - - BIO_free(mem); - - if (rsa == NULL) { - ssh_set_error(session, SSH_FATAL, - "Parsing private key: %s", - ERR_error_string(ERR_get_error(),NULL)); - return NULL; - } -#endif - break; - default: - ssh_set_error(session, SSH_FATAL, "Invalid private key type %d", type); - return NULL; - } /* switch */ - - privkey = malloc(sizeof(struct ssh_private_key_struct)); - if (privkey == NULL) { -#ifdef HAVE_LIBGCRYPT - gcry_sexp_release(dsa); - gcry_sexp_release(rsa); -#elif defined HAVE_LIBCRYPTO - DSA_free(dsa); - RSA_free(rsa); -#endif - return NULL; - } - ZERO_STRUCTP(privkey); - privkey->type = type; - privkey->dsa_priv = dsa; - privkey->rsa_priv = rsa; - - return privkey; -} - /** * @brief returns the type of a private key * @param[in] privatekey the private key handle diff --git a/tests/unittests/torture_pki.c b/tests/unittests/torture_pki.c index 760c1cec..8e1d07de 100644 --- a/tests/unittests/torture_pki.c +++ b/tests/unittests/torture_pki.c @@ -103,13 +103,7 @@ static void torture_pki_import_privkey_base64_RSA(void **state) { key_str = read_file(LIBSSH_RSA_TESTKEY); assert_true(key_str != NULL); - key = ssh_key_new(); - assert_true(key != NULL); - /* - int ssh_pki_import_privkey_base64(ssh_key key, ssh_session session, - const char *b64_key, const char *passphrase) - */ - rc = ssh_pki_import_privkey_base64(key, session, key_str, passphrase); + rc = ssh_pki_import_privkey_base64(session, key_str, passphrase, &key); assert_true(rc == 0); free(key_str); @@ -130,7 +124,7 @@ static void torture_pki_import_privkey_base64_NULL_key(void **state) { assert_true(key != NULL); /* test if it returns -1 if key is NULL */ - rc = ssh_pki_import_privkey_base64(NULL, session, key_str, passphrase); + rc = ssh_pki_import_privkey_base64(session, key_str, passphrase, NULL); assert_true(rc == -1); free(key_str); @@ -141,18 +135,15 @@ static void torture_pki_import_privkey_base64_NULL_session(void **state) { ssh_session session = *state; int rc; char *key_str; - ssh_key key; + ssh_key key = NULL; const char *passphrase = LIBSSH_PASSPHRASE; key_str = read_file(LIBSSH_RSA_TESTKEY); assert_true(key_str != NULL); - key = ssh_key_new(); - assert_true(key != NULL); - /* test if it returns -1 if session is NULL */ (void)session; - rc = ssh_pki_import_privkey_base64(key, NULL, key_str, passphrase); + rc = ssh_pki_import_privkey_base64(NULL, key_str, passphrase, &key); assert_true(rc == -1); free(key_str); @@ -163,17 +154,14 @@ static void torture_pki_import_privkey_base64_NULL_str(void **state) { ssh_session session = *state; int rc; char *key_str; - ssh_key key; + ssh_key key = NULL; const char *passphrase = LIBSSH_PASSPHRASE; key_str = read_file(LIBSSH_RSA_TESTKEY); assert_true(key_str != NULL); - key = ssh_key_new(); - assert_true(key != NULL); - /* test if it returns -1 if key_str is NULL */ - rc = ssh_pki_import_privkey_base64(key, session, NULL, passphrase); + rc = ssh_pki_import_privkey_base64(session, NULL, passphrase, &key); assert_true(rc == -1); free(key_str); @@ -190,13 +178,7 @@ static void torture_pki_import_privkey_base64_DSA(void **state) { key_str = read_file(LIBSSH_DSA_TESTKEY); assert_true(key_str != NULL); - key = ssh_key_new(); - assert_true(key != NULL); - /* - int ssh_pki_import_privkey_base64(ssh_key key, ssh_session session, - const char *b64_key, const char *passphrase) - */ - rc = ssh_pki_import_privkey_base64(key, session, key_str, passphrase); + rc = ssh_pki_import_privkey_base64(session, key_str, passphrase, &key); assert_true(rc == 0); free(key_str); @@ -207,26 +189,24 @@ static void torture_pki_import_privkey_base64_passphrase(void **state) { ssh_session session = *state; int rc; char *key_str; - ssh_key key; + ssh_key key = NULL; const char *passphrase = LIBSSH_PASSPHRASE; key_str = read_file(LIBSSH_RSA_TESTKEY); assert_true(key_str != NULL); - key = ssh_key_new(); - assert_true(key != NULL); - - rc = ssh_pki_import_privkey_base64(key, session, key_str, passphrase); + rc = ssh_pki_import_privkey_base64(session, key_str, passphrase, &key); assert_true(rc == 0); + ssh_key_free(key); /* test if it returns -1 if passphrase is wrong */ - rc = ssh_pki_import_privkey_base64(key, session, key_str, "wrong passphrase !!"); + rc = ssh_pki_import_privkey_base64(session, key_str, "wrong passphrase !!", &key); assert_true(rc == -1); #ifndef HAVE_LIBCRYPTO /* test if it returns -1 if passphrase is NULL */ /* libcrypto asks for a passphrase, so skip this test */ - rc = ssh_pki_import_privkey_base64(key, session, key_str, NULL); + rc = ssh_pki_import_privkey_base64(session, key_str, NULL, &key); assert_true(rc == -1); #endif @@ -236,22 +216,22 @@ static void torture_pki_import_privkey_base64_passphrase(void **state) { key_str = read_file(LIBSSH_DSA_TESTKEY); assert_true(key_str != NULL); - rc = ssh_pki_import_privkey_base64(key, session, key_str, passphrase); + rc = ssh_pki_import_privkey_base64(session, key_str, passphrase, &key); assert_true(rc == 0); + ssh_key_free(key); /* test if it returns -1 if passphrase is wrong */ - rc = ssh_pki_import_privkey_base64(key, session, key_str, "wrong passphrase !!"); + rc = ssh_pki_import_privkey_base64(session, key_str, "wrong passphrase !!", &key); assert_true(rc == -1); #ifndef HAVE_LIBCRYPTO /* test if it returns -1 if passphrase is NULL */ /* libcrypto asks for a passphrase, so skip this test */ - rc = ssh_pki_import_privkey_base64(key, session, key_str, NULL); + rc = ssh_pki_import_privkey_base64(session, key_str, NULL, &key); assert_true(rc == -1); #endif free(key_str); - ssh_key_free(key); } static void torture_pki_pki_publickey_from_privatekey_RSA(void **state) { @@ -265,10 +245,7 @@ static void torture_pki_pki_publickey_from_privatekey_RSA(void **state) { key_str = read_file(LIBSSH_RSA_TESTKEY); assert_true(key_str != NULL); - key = ssh_key_new(); - assert_true(key != NULL); - - rc = ssh_pki_import_privkey_base64(key, session, key_str, passphrase); + rc = ssh_pki_import_privkey_base64(session, key_str, passphrase, &key); assert_true(rc == 0); pubkey = ssh_pki_publickey_from_privatekey(key); @@ -290,10 +267,7 @@ static void torture_pki_pki_publickey_from_privatekey_DSA(void **state) { key_str = read_file(LIBSSH_DSA_TESTKEY); assert_true(key_str != NULL); - key = ssh_key_new(); - assert_true(key != NULL); - - rc = ssh_pki_import_privkey_base64(key, session, key_str, passphrase); + rc = ssh_pki_import_privkey_base64(session, key_str, passphrase, &key); assert_true(rc == 0); pubkey = ssh_pki_publickey_from_privatekey(key);