diff --git a/include/libssh/pki.h b/include/libssh/pki.h index fa0a81e6..ec0ce9af 100644 --- a/include/libssh/pki.h +++ b/include/libssh/pki.h @@ -28,7 +28,9 @@ #ifdef HAVE_OPENSSL_ECDSA_H #include #endif - +#ifdef HAVE_LIBCRYPTO +#include +#endif #include "libssh/crypto.h" #ifdef HAVE_OPENSSL_ED25519 /* If using OpenSSL implementation, define the signature lenght which would be @@ -46,6 +48,7 @@ #define SSH_KEY_FLAG_EMPTY 0x0 #define SSH_KEY_FLAG_PUBLIC 0x0001 #define SSH_KEY_FLAG_PRIVATE 0x0002 +#define SSH_KEY_FLAG_PKCS11_URI 0x0004 struct ssh_key_struct { enum ssh_keytypes_e type; @@ -63,6 +66,7 @@ struct ssh_key_struct { #elif defined(HAVE_LIBCRYPTO) DSA *dsa; RSA *rsa; + EVP_PKEY *key; /* Saving the OpenSSL context here to save time while converting*/ # if defined(HAVE_OPENSSL_ECC) EC_KEY *ecdsa; # else diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h index d365a2dd..71418fdc 100644 --- a/include/libssh/pki_priv.h +++ b/include/libssh/pki_priv.h @@ -43,6 +43,12 @@ int bcrypt_pbkdf(const char *pass, /* Magic defined in OpenSSH/PROTOCOL.key */ #define OPENSSH_AUTH_MAGIC "openssh-key-v1" +/* Determine type of ssh key. */ +enum ssh_key_e { + SSH_KEY_PUBLIC = 0, + SSH_KEY_PRIVATE +}; + int pki_key_ecdsa_nid_from_name(const char *name); const char *pki_key_ecdsa_nid_to_name(int nid); const char *ssh_key_signature_to_char(enum ssh_keytypes_e type, @@ -156,4 +162,7 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key, ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, const char *passphrase, ssh_auth_callback auth_fn, void *auth_data); +/* URI Function */ +int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type); + #endif /* PKI_PRIV_H_ */ diff --git a/src/auth.c b/src/auth.c index f2eeee0b..ac6c9b14 100644 --- a/src/auth.c +++ b/src/auth.c @@ -1055,12 +1055,28 @@ int ssh_userauth_publickey_auto(ssh_session session, while (state->it != NULL) { const char *privkey_file = state->it->data; char pubkey_file[1024] = {0}; + if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY) { SSH_LOG(SSH_LOG_DEBUG, "Trying to authenticate with %s", privkey_file); state->privkey = NULL; state->pubkey = NULL; - snprintf(pubkey_file, sizeof(pubkey_file), "%s.pub", privkey_file); + + if (ssh_pki_is_uri(privkey_file)) { + char *pub_uri_from_priv = NULL; + SSH_LOG(SSH_LOG_INFO, + "Authenticating with PKCS #11 URI."); + pub_uri_from_priv = ssh_pki_export_pub_uri_from_priv_uri(privkey_file); + if (pub_uri_from_priv == NULL) { + return SSH_ERROR; + } else { + snprintf(pubkey_file, sizeof(pubkey_file), "%s", + pub_uri_from_priv); + SAFE_FREE(pub_uri_from_priv); + } + } else { + snprintf(pubkey_file, sizeof(pubkey_file), "%s.pub", privkey_file); + } rc = ssh_pki_import_pubkey_file(pubkey_file, &state->pubkey); if (rc == SSH_ERROR) { diff --git a/src/pki.c b/src/pki.c index 24ffd25d..ae470ffc 100644 --- a/src/pki.c +++ b/src/pki.c @@ -4,6 +4,7 @@ * * Copyright (c) 2010 by Aris Adamantiadis * Copyright (c) 2011-2013 Andreas Schneider + * Copyright (c) 2019 Sahana Prasad * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -864,6 +865,13 @@ int ssh_pki_import_privkey_file(const char *filename, return SSH_ERROR; } +#ifdef WITH_PKCS11_URI + if (ssh_pki_is_uri(filename)) { + rc = pki_uri_import(filename, pkey, SSH_KEY_PRIVATE); + return rc; + } +#endif + file = fopen(filename, "rb"); if (file == NULL) { SSH_LOG(SSH_LOG_WARN, @@ -1639,6 +1647,13 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey) return SSH_ERROR; } +#ifdef WITH_PKCS11_URI + if (ssh_pki_is_uri(filename)) { + rc = pki_uri_import(filename, pkey, SSH_KEY_PUBLIC); + return rc; + } +#endif + file = fopen(filename, "rb"); if (file == NULL) { SSH_LOG(SSH_LOG_WARN, "Error opening %s: %s", diff --git a/src/pki_crypto.c b/src/pki_crypto.c index e2a3d3bf..000afd35 100644 --- a/src/pki_crypto.c +++ b/src/pki_crypto.c @@ -5,6 +5,7 @@ * * Copyright (c) 2003-2009 by Aris Adamantiadis * Copyright (c) 2009-2013 by Andreas Schneider + * Copyright (c) 2019 by Sahana Prasad * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -32,6 +33,8 @@ #include #include #include +#include +#include #include #include "libcrypto-compat.h" @@ -297,6 +300,10 @@ ssh_key pki_key_dup(const ssh_key key, int demote) return NULL; } +#ifdef WITH_PKCS11_URI + new->key = key->key; +#endif + new->type = key->type; new->type_c = key->type_c; if (demote) { @@ -1993,6 +2000,13 @@ static EVP_PKEY *pki_key_to_pkey(ssh_key key) { EVP_PKEY *pkey = NULL; +#ifdef WITH_PKCS11_URI + if (key->flags & SSH_KEY_FLAG_PKCS11_URI) { + pkey = key->key; + return pkey; + } +#endif + switch(key->type) { case SSH_KEYTYPE_DSS: case SSH_KEYTYPE_DSS_CERT01: @@ -2489,4 +2503,157 @@ ssh_signature pki_do_sign_hash(const ssh_key privkey, } #endif /* HAVE_OPENSSL_ED25519 */ +/** + * @internal + * + * @brief Populate the public/private ssh_key from the engine with + * PKCS#11 URIs as the look up. + * + * @param[in] uri_name The PKCS#11 URI + * @param[in] nkey The ssh-key context for + * the key loaded from the engine. + * @param[in] key_type The type of the key used. Public/Private. + * + * @return SSH_OK if ssh-key is valid; SSH_ERROR otherwise. + */ +int pki_uri_import(const char *uri_name, + ssh_key *nkey, + enum ssh_key_e key_type) +{ + ENGINE *engine = NULL; + EVP_PKEY *pkey = NULL; + RSA *rsa = NULL; + ssh_key key = NULL; + enum ssh_keytypes_e type = SSH_KEYTYPE_UNKNOWN; +#ifdef HAVE_OPENSSL_ECC + EC_KEY *ecdsa = NULL; +#else + void *ecdsa = NULL; +#endif + int ok; + + ENGINE_load_builtin_engines(); + + engine = ENGINE_by_id("pkcs11"); + if (engine == NULL) { + SSH_LOG(SSH_LOG_WARN, + "Could not load the engine: %s", + ERR_error_string(ERR_get_error(),NULL)); + return SSH_ERROR; + } + SSH_LOG(SSH_LOG_INFO, "Engine loaded successfully"); + + ok = ENGINE_init(engine); + if (!ok) { + SSH_LOG(SSH_LOG_WARN, + "Could not initialize the engine: %s", + ERR_error_string(ERR_get_error(),NULL)); + ENGINE_free(engine); + return SSH_ERROR; + } + + SSH_LOG(SSH_LOG_INFO, "Engine init success"); + + switch (key_type) { + case SSH_KEY_CMP_PRIVATE: + pkey = ENGINE_load_private_key(engine, uri_name, NULL, NULL); + if (pkey == NULL) { + SSH_LOG(SSH_LOG_WARN, + "Could not load key: %s", + ERR_error_string(ERR_get_error(),NULL)); + goto fail; + } + break; + case SSH_KEY_CMP_PUBLIC: + pkey = ENGINE_load_public_key(engine, uri_name, NULL, NULL); + if (pkey == NULL) { + SSH_LOG(SSH_LOG_WARN, + "Could not load key: %s", + ERR_error_string(ERR_get_error(),NULL)); + goto fail; + } + break; + default: + SSH_LOG(SSH_LOG_WARN, + "Invalid key type: %d", key_type); + goto fail; + } + + key = ssh_key_new(); + if (key == NULL) { + goto fail; + } + + switch (EVP_PKEY_base_id(pkey)) { + case EVP_PKEY_RSA: + rsa = EVP_PKEY_get1_RSA(pkey); + if (rsa == NULL) { + SSH_LOG(SSH_LOG_WARN, + "Parsing pub key: %s", + ERR_error_string(ERR_get_error(),NULL)); + goto fail; + } + type = SSH_KEYTYPE_RSA; + break; + case EVP_PKEY_EC: +#ifdef HAVE_OPENSSL_ECC + ecdsa = EVP_PKEY_get1_EC_KEY(pkey); + if (ecdsa == NULL) { + SSH_LOG(SSH_LOG_WARN, + "Parsing pub key: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + /* pki_privatekey_type_from_string always returns P256 for ECDSA + * keys, so we need to figure out the correct type here */ + type = pki_key_ecdsa_to_key_type(ecdsa); + if (type == SSH_KEYTYPE_UNKNOWN) { + SSH_LOG(SSH_LOG_WARN, "Invalid pub key."); + goto fail; + } + + break; +#endif + default: + SSH_LOG(SSH_LOG_WARN, "Unknown or invalid public key type %d", + EVP_PKEY_base_id(pkey)); + goto fail; + } + + key->key = pkey; + key->type = type; + key->type_c = ssh_key_type_to_char(type); + if (key_type == SSH_KEY_PRIVATE) { + key->flags = SSH_KEY_FLAG_PUBLIC | SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PKCS11_URI; + } else { + key->flags = SSH_KEY_FLAG_PUBLIC | SSH_KEY_FLAG_PKCS11_URI; + } + key->rsa = rsa; + key->ecdsa = ecdsa; +#ifdef HAVE_OPENSSL_ECC + if (is_ecdsa_key_type(key->type)) { + key->ecdsa_nid = pki_key_ecdsa_to_nid(key->ecdsa); + } +#endif + + *nkey = key; + ENGINE_finish(engine); + ENGINE_free(engine); + + return SSH_OK; + +fail: + ENGINE_finish(engine); + ENGINE_free(engine); + EVP_PKEY_free(pkey); + ssh_key_free(key); + RSA_free(rsa); +#ifdef HAVE_OPENSSL_ECC + EC_KEY_free(ecdsa); +#endif + + return SSH_ERROR; +} + #endif /* _PKI_CRYPTO_H */ diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c index 4bfcb9f6..23572683 100644 --- a/src/pki_gcrypt.c +++ b/src/pki_gcrypt.c @@ -2457,4 +2457,13 @@ int pki_verify_data_signature(ssh_signature signature, return SSH_OK; } +int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type) +{ + (void) uri_name; + (void) key; + (void) key_type; + SSH_LOG(SSH_LOG_WARN, + "gcrypt does not support PKCS #11"); + return SSH_ERROR; +} #endif /* HAVE_LIBGCRYPT */ diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c index dd1a513e..08905cd9 100644 --- a/src/pki_mbedcrypto.c +++ b/src/pki_mbedcrypto.c @@ -1591,4 +1591,14 @@ int pki_key_generate_dss(ssh_key key, int parameter) (void) parameter; return SSH_ERROR; } + +int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type) +{ + (void) uri_name; + (void) key; + (void) key_type; + SSH_LOG(SSH_LOG_WARN, + "mbedcrypto does not support PKCS #11"); + return SSH_ERROR; +} #endif /* HAVE_LIBMBEDCRYPTO */