userauth: derive publickey from private

Pass a NULL pointer for the publickey parameter of
libssh2_userauth_publickey_fromfile and
libssh2_userauth_hostbased_fromfile functions.  In this case, the
functions recompute the public key from the private key file data.

This is work done by Jean-Louis CHARTON
<Jean-Louis.CHARTON@oikialog.com>, then adapted by Mark Smith and
slightly edited further by me Daniel.

WARNING: this does leave the feature NOT WORKING when libssh2 is built
to use libgcrypt instead of OpenSSL simply due to lack of
implementation.
This commit is contained in:
Mark Smith 2010-12-18 23:38:08 +01:00 committed by Daniel Stenberg
parent 5ee38702a0
commit 5b1a7ec2f1
5 changed files with 348 additions and 25 deletions

View File

@ -572,4 +572,17 @@ _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
return ret;
}
int
_libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
unsigned char **method,
size_t *method_len,
unsigned char **pubkeydata,
size_t *pubkeydata_len,
const char *privatekey,
const char *passphrase)
{
return -1; /* not yet supported; interpreted by userauth.c to call
libssh2_error */
}
#endif /* LIBSSH2_LIBGCRYPT */

View File

@ -207,3 +207,11 @@ int _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
#define _libssh2_bn_bytes(bn) (gcry_mpi_get_nbits (bn) / 8 + ((gcry_mpi_get_nbits (bn) % 8 == 0) ? 0 : 1))
#define _libssh2_bn_bits(bn) gcry_mpi_get_nbits (bn)
#define _libssh2_bn_free(bn) gcry_mpi_release(bn)
int _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
unsigned char **method,
size_t *method_len,
unsigned char **pubkeydata,
size_t *pubkeydata_len,
const char *privatekey,
const char *passphrase);

View File

@ -225,17 +225,17 @@ aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
return 0;
switch (ctx->key_len) {
case 16:
aes_cipher = EVP_aes_128_ecb();
break;
case 24:
aes_cipher = EVP_aes_192_ecb();
break;
case 32:
aes_cipher = EVP_aes_256_ecb();
break;
default:
return 0;
case 16:
aes_cipher = EVP_aes_128_ecb();
break;
case 24:
aes_cipher = EVP_aes_192_ecb();
break;
case 32:
aes_cipher = EVP_aes_256_ecb();
break;
default:
return 0;
}
c->aes_ctx = malloc(sizeof(EVP_CIPHER_CTX));
if (c->aes_ctx == NULL)
@ -515,4 +515,274 @@ libssh2_md5(const unsigned char *message, unsigned long len,
EVP_DigestFinal(&ctx, out, NULL);
}
static unsigned char *
write_bn(unsigned char *buf, const BIGNUM *bn, int bn_bytes)
{
unsigned char *p = buf;
/* Left space for bn size which will be written below. */
p += 4;
*p = 0;
BN_bn2bin(bn, p + 1);
if (!(*(p + 1) & 0x80)) {
memmove(p, p + 1, --bn_bytes);
}
_libssh2_htonu32(p - 4, bn_bytes); /* Post write bn size. */
return p + bn_bytes;
}
static unsigned char *
gen_publickey_from_rsa(LIBSSH2_SESSION *session, RSA *rsa,
size_t *key_len)
{
int e_bytes, n_bytes;
unsigned long len;
unsigned char* key;
unsigned char* p;
e_bytes = BN_num_bytes(rsa->e) + 1;
n_bytes = BN_num_bytes(rsa->n) + 1;
/* Key form is "ssh-rsa" + e + n. */
len = 4 + 7 + 4 + e_bytes + 4 + n_bytes;
key = LIBSSH2_ALLOC(session, len);
if (key == NULL) {
return NULL;
}
/* Process key encoding. */
p = key;
_libssh2_htonu32(p, 7); /* Key type. */
p += 4;
memcpy(p, "ssh-rsa", 7);
p += 7;
p = write_bn(p, rsa->e, e_bytes);
p = write_bn(p, rsa->n, n_bytes);
*key_len = (size_t)(p - key);
return key;
}
static unsigned char *
gen_publickey_from_dsa(LIBSSH2_SESSION* session, DSA *dsa,
size_t *key_len)
{
int p_bytes, q_bytes, g_bytes, k_bytes;
unsigned long len;
unsigned char* key;
unsigned char* p;
p_bytes = BN_num_bytes(dsa->p) + 1;
q_bytes = BN_num_bytes(dsa->q) + 1;
g_bytes = BN_num_bytes(dsa->g) + 1;
k_bytes = BN_num_bytes(dsa->pub_key) + 1;
/* Key form is "ssh-dss" + p + q + g + pub_key. */
len = 4 + 7 + 4 + p_bytes + 4 + q_bytes + 4 + g_bytes + 4 + k_bytes;
key = LIBSSH2_ALLOC(session, len);
if (key == NULL) {
return NULL;
}
/* Process key encoding. */
p = key;
_libssh2_htonu32(p, 7); /* Key type. */
p += 4;
memcpy(p, "ssh-dss", 7);
p += 7;
p = write_bn(p, dsa->p, p_bytes);
p = write_bn(p, dsa->q, q_bytes);
p = write_bn(p, dsa->g, g_bytes);
p = write_bn(p, dsa->pub_key, k_bytes);
*key_len = (size_t)(p - key);
return key;
}
static int
gen_publickey_from_rsa_evp(LIBSSH2_SESSION *session,
unsigned char **method,
size_t *method_len,
unsigned char **pubkeydata,
size_t *pubkeydata_len,
EVP_PKEY *pk)
{
RSA* rsa = NULL;
unsigned char* key;
unsigned char* method_buf = NULL;
size_t key_len;
_libssh2_debug(session,
LIBSSH2_TRACE_AUTH,
"Computing public key from RSA private key envelop");
rsa = EVP_PKEY_get1_RSA(pk);
if (rsa == NULL) {
/* Assume memory allocation error... what else could it be ? */
goto __alloc_error;
}
method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-rsa. */
if (method_buf == NULL) {
goto __alloc_error;
}
key = gen_publickey_from_rsa(session, rsa, &key_len);
if (key == NULL) {
goto __alloc_error;
}
RSA_free(rsa);
memcpy(method_buf, "ssh-rsa", 7);
*method = method_buf;
*method_len = 7;
*pubkeydata = key;
*pubkeydata_len = key_len;
return 0;
__alloc_error:
if (rsa != NULL) {
RSA_free(rsa);
}
if (method_buf != NULL) {
LIBSSH2_FREE(session, method_buf);
}
_libssh2_error(session,
LIBSSH2_ERROR_ALLOC,
"Unable to allocate memory for private key data");
return -1;
}
static int
gen_publickey_from_dsa_evp(LIBSSH2_SESSION *session,
unsigned char **method,
size_t *method_len,
unsigned char **pubkeydata,
size_t *pubkeydata_len,
EVP_PKEY *pk)
{
DSA* dsa = NULL;
unsigned char* key;
unsigned char* method_buf = NULL;
size_t key_len;
_libssh2_debug(session,
LIBSSH2_TRACE_AUTH,
"Computing public key from DSA private key envelop");
dsa = EVP_PKEY_get1_DSA(pk);
if (dsa == NULL) {
/* Assume memory allocation error... what else could it be ? */
goto __alloc_error;
}
method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-dss. */
if (method_buf == NULL) {
goto __alloc_error;
}
key = gen_publickey_from_dsa(session, dsa, &key_len);
if (key == NULL) {
goto __alloc_error;
}
DSA_free(dsa);
memcpy(method_buf, "ssh-dss", 7);
*method = method_buf;
*method_len = 7;
*pubkeydata = key;
*pubkeydata_len = key_len;
return 0;
__alloc_error:
if (dsa != NULL) {
DSA_free(dsa);
}
if (method_buf != NULL) {
LIBSSH2_FREE(session, method_buf);
}
_libssh2_error(session,
LIBSSH2_ERROR_ALLOC,
"Unable to allocate memory for private key data");
return -1;
}
int
_libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
unsigned char **method,
size_t *method_len,
unsigned char **pubkeydata,
size_t *pubkeydata_len,
const char *privatekey,
const char *passphrase)
{
int st;
BIO* bp;
EVP_PKEY* pk;
_libssh2_debug(session,
LIBSSH2_TRACE_AUTH,
"Computing public key from private key file: %s",
privatekey);
bp = BIO_new_file(privatekey, "r");
if (bp == NULL) {
_libssh2_error(session,
LIBSSH2_ERROR_FILE,
"Unable to open private key file");
return -1;
}
if (!EVP_get_cipherbyname("des")) {
/* If this cipher isn't loaded it's a pretty good indication that none
* are. I have *NO DOUBT* that there's a better way to deal with this
* ($#&%#$(%$#( Someone buy me an OpenSSL manual and I'll read up on
* it.
*/
OpenSSL_add_all_ciphers();
}
BIO_reset(bp);
pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void*)passphrase);
BIO_free(bp);
if (pk == NULL) {
_libssh2_error(session,
LIBSSH2_ERROR_FILE,
"Wrong passphrase or invalid/unrecognized "
"private key file format");
return -1;
}
switch (pk->type) {
case EVP_PKEY_RSA :
st = gen_publickey_from_rsa_evp(
session, method, method_len, pubkeydata, pubkeydata_len, pk);
break;
case EVP_PKEY_DSA :
st = gen_publickey_from_dsa_evp(
session, method, method_len, pubkeydata, pubkeydata_len, pk);
break;
default :
st = -1;
_libssh2_error(session,
LIBSSH2_ERROR_FILE,
"Unsupported private key file format");
break;
}
EVP_PKEY_free(pk);
return st;
}
#endif /* !LIBSSH2_LIBGCRYPT */

View File

@ -241,3 +241,11 @@ int _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
const EVP_CIPHER *_libssh2_EVP_aes_128_ctr(void);
const EVP_CIPHER *_libssh2_EVP_aes_192_ctr(void);
const EVP_CIPHER *_libssh2_EVP_aes_256_ctr(void);
int _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
unsigned char **method,
size_t *method_len,
unsigned char **pubkeydata,
size_t *pubkeydata_len,
const char *privatekey,
const char *passphrase);

View File

@ -655,13 +655,25 @@ userauth_hostbased_fromfile(LIBSSH2_SESSION *session,
memset(&session->userauth_host_packet_requirev_state, 0,
sizeof(session->userauth_host_packet_requirev_state));
rc = file_read_publickey(session, &session->userauth_host_method,
&session->userauth_host_method_len,
&pubkeydata, &pubkeydata_len,
publickey);
if(rc)
/* Note: file_read_publickey() calls _libssh2_error() */
return rc;
if (publickey) {
rc = file_read_publickey(session, &session->userauth_host_method,
&session->userauth_host_method_len,
&pubkeydata, &pubkeydata_len, publickey);
if(rc)
/* Note: file_read_publickey() calls _libssh2_error() */
return rc;
}
else {
/* Compute public key from private key. */
if (_libssh2_pub_priv_keyfile(session,
&session->userauth_host_method,
&session->userauth_host_method_len,
&pubkeydata, &pubkeydata_len,
privatekey, passphrase))
return _libssh2_error(session, LIBSSH2_ERROR_FILE,
"Unable to extract public key "
"from private key file");
}
/*
* 52 = packet_type(1) + username_len(4) + servicename_len(4) +
@ -932,7 +944,7 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session,
else if (session->userauth_pblc_method_len !=
_libssh2_ntohu32(pubkeydata))
return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
"Invalid public key");
"Invalid public key");
/*
* 45 = packet_type(1) + username_len(4) + servicename_len(4) +
@ -1221,11 +1233,24 @@ userauth_publickey_fromfile(LIBSSH2_SESSION *session,
privkey_file.passphrase = passphrase;
if (session->userauth_pblc_state == libssh2_NB_state_idle) {
rc = file_read_publickey(session, &session->userauth_pblc_method,
&session->userauth_pblc_method_len,
&pubkeydata, &pubkeydata_len, publickey);
if(rc)
return rc;
if (publickey) {
rc = file_read_publickey(session, &session->userauth_pblc_method,
&session->userauth_pblc_method_len,
&pubkeydata, &pubkeydata_len,publickey);
if(rc)
return rc;
}
else {
/* Compute public key from private key. */
if (_libssh2_pub_priv_keyfile(session,
&session->userauth_pblc_method,
&session->userauth_pblc_method_len,
&pubkeydata, &pubkeydata_len,
privatekey, passphrase))
return _libssh2_error(session, LIBSSH2_ERROR_FILE,
"Unable to extract public key "
"from private key file");
}
}
rc = _libssh2_userauth_publickey(session, username, username_len,
@ -1643,4 +1668,3 @@ libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session,
response_callback));
return rc;
}