diff --git a/include/libssh2.h b/include/libssh2.h index 807f6a27..f771eca0 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -178,6 +178,11 @@ typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE unsigned int length; } LIBSSH2_USERAUTH_KBDINT_RESPONSE; +/* 'publickey' authentication callback */ +#define LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC(name) \ + int name(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, \ + const unsigned char *data, size_t data_len, void **abstract) + /* 'keyboard-interactive' authentication callback */ #define LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC(name_) \ void name_(const char* name, int name_len, const char* instruction, \ @@ -436,6 +441,14 @@ libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, strlen(username), (publickey), \ (privatekey), (passphrase)) +LIBSSH2_API int +libssh2_userauth_publickey(LIBSSH2_SESSION *session, + const char *username, + const unsigned char *pubkeydata, + size_t pubkeydata_len, + LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)), + void **abstract); + LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, const char *username, diff --git a/src/userauth.c b/src/userauth.c index ffd1db1f..1bf333ee 100644 --- a/src/userauth.c +++ b/src/userauth.c @@ -615,6 +615,45 @@ file_read_privatekey(LIBSSH2_SESSION * session, return 0; } +struct privkey_file { + const char *filename; + const char *passphrase; +}; + +static int +sign_fromfile(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, + const unsigned char *data, size_t data_len, void **abstract) +{ + struct privkey_file *privkey_file = (struct privkey_file *) (*abstract); + const LIBSSH2_HOSTKEY_METHOD *privkeyobj; + void *hostkey_abstract; + struct iovec datavec; + + if (file_read_privatekey(session, &privkeyobj, &hostkey_abstract, + session->userauth_pblc_method, + session->userauth_pblc_method_len, + privkey_file->filename, + privkey_file->passphrase)) { + return -1; + } + + datavec.iov_base = (unsigned char *)data; + datavec.iov_len = data_len; + + if (privkeyobj->signv(session, sig, sig_len, 1, &datavec, + &hostkey_abstract)) { + if (privkeyobj->dtor) { + privkeyobj->dtor(session, abstract); + } + return -1; + } + + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &hostkey_abstract); + } + return 0; +} + /* userauth_hostbased_fromfile @@ -887,19 +926,15 @@ libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, -/* - * userauth_publickey_fromfile - * Authenticate using a keypair found in the named files - */ static int -userauth_publickey_fromfile(LIBSSH2_SESSION *session, - const char *username, - unsigned int username_len, - const char *publickey, - const char *privatekey, - const char *passphrase) +userauth_publickey(LIBSSH2_SESSION *session, + const char *username, + unsigned int username_len, + const unsigned char *pubkeydata, + unsigned long pubkeydata_len, + LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)), + void *abstract) { - unsigned long pubkeydata_len = 0; unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PK_OK, 0 @@ -907,17 +942,20 @@ userauth_publickey_fromfile(LIBSSH2_SESSION *session, int rc; if (session->userauth_pblc_state == libssh2_NB_state_idle) { - unsigned char *pubkeydata; - /* Zero the whole thing out */ memset(&session->userauth_pblc_packet_requirev_state, 0, sizeof(session->userauth_pblc_packet_requirev_state)); - if (file_read_publickey(session, &session->userauth_pblc_method, - &session->userauth_pblc_method_len, - &pubkeydata, &pubkeydata_len,publickey)) { + session->userauth_pblc_method_len = _libssh2_ntohu32(pubkeydata); + session->userauth_pblc_method = + LIBSSH2_ALLOC(session, session->userauth_pblc_method_len); + if (!session->userauth_pblc_method) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for public key data", 0); return -1; } + memcpy(session->userauth_pblc_method, pubkeydata + 4, + session->userauth_pblc_method_len); /* * 45 = packet_type(1) + username_len(4) + servicename_len(4) + @@ -942,7 +980,6 @@ userauth_publickey_fromfile(LIBSSH2_SESSION *session, if (!session->userauth_pblc_packet) { LIBSSH2_FREE(session, session->userauth_pblc_method); session->userauth_pblc_method = NULL; - LIBSSH2_FREE(session, pubkeydata); return -1; } @@ -977,7 +1014,6 @@ userauth_publickey_fromfile(LIBSSH2_SESSION *session, session->userauth_pblc_s += 4; memcpy(session->userauth_pblc_s, pubkeydata, pubkeydata_len); session->userauth_pblc_s += pubkeydata_len; - LIBSSH2_FREE(session, pubkeydata); _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Attempting publickey authentication"); @@ -1005,13 +1041,6 @@ userauth_publickey_fromfile(LIBSSH2_SESSION *session, } if (session->userauth_pblc_state == libssh2_NB_state_sent) { - const LIBSSH2_HOSTKEY_METHOD *privkeyobj; - void *abstract; - unsigned char buf[5]; - struct iovec datavec[4]; - unsigned char *sig; - unsigned long sig_len; - rc = _libssh2_packet_requirev(session, reply_codes, &session->userauth_pblc_data, &session->userauth_pblc_data_len, 0, @@ -1065,44 +1094,45 @@ userauth_publickey_fromfile(LIBSSH2_SESSION *session, LIBSSH2_FREE(session, session->userauth_pblc_data); session->userauth_pblc_data = NULL; - if (file_read_privatekey(session, &privkeyobj, &abstract, - session->userauth_pblc_method, - session->userauth_pblc_method_len, - privatekey, passphrase)) { - LIBSSH2_FREE(session, session->userauth_pblc_method); - session->userauth_pblc_method = NULL; - LIBSSH2_FREE(session, session->userauth_pblc_packet); - session->userauth_pblc_packet = NULL; - session->userauth_pblc_state = libssh2_NB_state_idle; - return -1; - } - *session->userauth_pblc_b = 0x01; + session->userauth_pblc_state = libssh2_NB_state_sent1; + } - _libssh2_htonu32(buf, session->session_id_len); - datavec[0].iov_base = buf; - datavec[0].iov_len = 4; - datavec[1].iov_base = session->session_id; - datavec[1].iov_len = session->session_id_len; - datavec[2].iov_base = session->userauth_pblc_packet; - datavec[2].iov_len = session->userauth_pblc_packet_len; + if (session->userauth_pblc_state == libssh2_NB_state_sent1) { + unsigned char *buf, *s; + unsigned char *sig; + unsigned long sig_len; - if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { + s = buf = LIBSSH2_ALLOC(session, 4 + session->session_id_len + + session->userauth_pblc_packet_len); + if (!buf) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for userauth-publickey " + "signed data", 0); + return -1; + } + + _libssh2_htonu32(s, session->session_id_len); + s += 4; + memcpy (s, session->session_id, session->session_id_len); + s += session->session_id_len; + memcpy (s, session->userauth_pblc_packet, + session->userauth_pblc_packet_len); + s += session->userauth_pblc_packet_len; + + rc = sign_callback(session, &sig, &sig_len, buf, s - buf, abstract); + LIBSSH2_FREE(session, buf); + if (rc == PACKET_EAGAIN) { + return rc; + } else if (rc) { LIBSSH2_FREE(session, session->userauth_pblc_method); session->userauth_pblc_method = NULL; LIBSSH2_FREE(session, session->userauth_pblc_packet); session->userauth_pblc_packet = NULL; - if (privkeyobj->dtor) { - privkeyobj->dtor(session, &abstract); - } session->userauth_pblc_state = libssh2_NB_state_idle; return -1; } - if (privkeyobj->dtor) { - privkeyobj->dtor(session, &abstract); - } - /* * If this function was restarted, pubkeydata_len might still be 0 * which will cause an unnecessary but harmless realloc here. @@ -1157,10 +1187,10 @@ userauth_publickey_fromfile(LIBSSH2_SESSION *session, _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Attempting publickey authentication -- phase 2"); - session->userauth_pblc_state = libssh2_NB_state_sent1; + session->userauth_pblc_state = libssh2_NB_state_sent2; } - if (session->userauth_pblc_state == libssh2_NB_state_sent1) { + if (session->userauth_pblc_state == libssh2_NB_state_sent2) { rc = _libssh2_transport_write(session, session->userauth_pblc_packet, session->userauth_pblc_s - session->userauth_pblc_packet); @@ -1177,7 +1207,7 @@ userauth_publickey_fromfile(LIBSSH2_SESSION *session, LIBSSH2_FREE(session, session->userauth_pblc_packet); session->userauth_pblc_packet = NULL; - session->userauth_pblc_state = libssh2_NB_state_sent2; + session->userauth_pblc_state = libssh2_NB_state_sent3; } /* PK_OK is no longer valid */ @@ -1216,6 +1246,43 @@ userauth_publickey_fromfile(LIBSSH2_SESSION *session, return -1; } +/* + * userauth_publickey_fromfile + * Authenticate using a keypair found in the named files + */ +static int +userauth_publickey_fromfile(LIBSSH2_SESSION *session, + const char *username, + unsigned int username_len, + const char *publickey, + const char *privatekey, + const char *passphrase) +{ + unsigned char *pubkeydata = NULL; + unsigned long pubkeydata_len = 0; + struct privkey_file privkey_file; + void *abstract = &privkey_file; + int rc; + + privkey_file.filename = privatekey; + privkey_file.passphrase = passphrase; + + if (session->userauth_pblc_state == libssh2_NB_state_idle) { + if (file_read_publickey(session, &session->userauth_pblc_method, + &session->userauth_pblc_method_len, + &pubkeydata, &pubkeydata_len, publickey)) { + return -1; + } + } + + rc = userauth_publickey(session, username, username_len, + pubkeydata, pubkeydata_len, + sign_fromfile, &abstract); + LIBSSH2_FREE(session, pubkeydata); + + return rc; +} + /* libssh2_userauth_publickey_fromfile_ex * Authenticate using a keypair found in the named files */ @@ -1235,6 +1302,25 @@ libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, return rc; } +/* libssh2_userauth_publickey_ex + * Authenticate using an external callback function + */ +LIBSSH2_API int +libssh2_userauth_publickey(LIBSSH2_SESSION *session, + const char *user, + const unsigned char *pubkeydata, + size_t pubkeydata_len, + LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)), + void **abstract) +{ + int rc; + BLOCK_ADJUST(rc, session, + userauth_publickey(session, user, strlen(user), + pubkeydata, pubkeydata_len, + sign_callback, abstract)); + return rc; +} + /*