diff --git a/README b/README index 11ac1b7d..62eeb329 100644 --- a/README +++ b/README @@ -4,6 +4,8 @@ libssh2 - SSH2 library Version 0.7 ----------- + Added libssh2_userauth_hostbased_fromfile_ex() for authenticating from hostkey. + Fixed extended data identification in libssh2_channel_read(). Fixed window adjust code. Hadn't acknowledged adjustments correctly. diff --git a/include/libssh2.h b/include/libssh2.h index 8b4b7fe4..4b9f507e 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -67,7 +67,7 @@ typedef long long libssh2_int64_t; #endif #define LIBSSH2_VERSION "0.6" -#define LIBSSH2_APINO 200502091118 +#define LIBSSH2_APINO 200502132140 /* Part of every banner, user specified or not */ #define LIBSSH2_SSH_BANNER "SSH-2.0-libssh2_" LIBSSH2_VERSION @@ -230,6 +230,13 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, char *passphrase); #define libssh2_userauth_publickey_fromfile(session, username, publickey, privatekey, passphrase) \ libssh2_userauth_publickey_fromfile_ex((session), (username), strlen(username), (publickey), (privatekey), (passphrase)) +LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, char *username, int username_len, + char *publickey, char *privatekey, + char *passphrase, + char *hostname, int hostname_len, + char *local_username, int local_username_len); +#define libssh2_userauth_hostbased_fromfile(session, username, publickey, privatekey, passphrase, hostname) \ + libssh2_userauth_hostbased_fromfile_ex((session), (username), strlen(username), (publickey), (privatekey), (passphrase), (hostname), strlen(hostname), (username), strlen(username)) /* Channel API */ #define LIBSSH2_CHANNEL_WINDOW_DEFAULT 65536 diff --git a/src/userauth.c b/src/userauth.c index 06b4fafd..d6ea27ef 100644 --- a/src/userauth.c +++ b/src/userauth.c @@ -326,6 +326,137 @@ static int libssh2_file_read_privatekey(LIBSSH2_SESSION *session, LIBSSH2_HOSTKE } /* }}} */ +/* {{{ libssh2_userauth_hostbased_fromfile_ex + * Authenticate using a keypair found in the named files + */ +LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, char *username, int username_len, + char *publickey, char *privatekey, + char *passphrase, + char *hostname, int hostname_len, + char *local_username, int local_username_len) +{ + LIBSSH2_HOSTKEY_METHOD *privkeyobj; + void *abstract; + unsigned char buf[5]; + struct iovec datavec[4]; + unsigned char *method, *pubkeydata, *packet, *s, *sig; + unsigned long method_len, pubkeydata_len, packet_len, sig_len; + + if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) { + return -1; + } + + packet_len = username_len + method_len + hostname_len + local_username_len + pubkeydata_len + 48; + /* packet_type(1) + username_len(4) + servicename_len(4) + service_name(14)"ssh-connection" + + * authmethod_len(4) + authmethod(9)"hostbased" + method_len(4) + pubkeydata_len(4) + + * local_username_len(4) + */ + /* Preallocate space for an overall length, method name again, + * and the signature, which won't be any larger than the size of the publickeydata itself */ + s = packet = LIBSSH2_ALLOC(session, packet_len + 4 + (4 + method_len) + (4 + pubkeydata_len)); + + *(s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(s, username_len); s += 4; + memcpy(s, username, username_len); s += username_len; + + libssh2_htonu32(s, 14); s += 4; + memcpy(s, "ssh-connection", 14); s += 14; + + libssh2_htonu32(s, 9); s += 4; + memcpy(s, "hostbased", 9); s += 9; + + libssh2_htonu32(s, method_len); s += 4; + memcpy(s, method, method_len); s += method_len; + + libssh2_htonu32(s, pubkeydata_len); s += 4; + memcpy(s, pubkeydata, pubkeydata_len); s += pubkeydata_len; + + libssh2_htonu32(s, hostname_len); s += 4; + memcpy(s, hostname, hostname_len); s += hostname_len; + + libssh2_htonu32(s, local_username_len); s += 4; + memcpy(s, local_username, local_username_len); s += local_username_len; + + if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, method, method_len, privatekey, passphrase)) { + LIBSSH2_FREE(session, method); + LIBSSH2_FREE(session, packet); + return -1; + } + + 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 = packet; + datavec[2].iov_len = packet_len; + + if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { + LIBSSH2_FREE(session, method); + LIBSSH2_FREE(session, packet); + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + return -1; + } + + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + + if (sig_len > pubkeydata_len ) { + /* Should *NEVER* happen, but...well.. better safe than sorry */ + packet = LIBSSH2_REALLOC(session, packet, packet_len + 4 + (4 + method_len) + (4 + sig_len)); /* PK sigblob */ + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating additional space for userauth-hostbased packet", 0); + LIBSSH2_FREE(session, method); + return -1; + } + } + + s = packet + packet_len; + + libssh2_htonu32(s, 4 + method_len + 4 + sig_len); s += 4; + + libssh2_htonu32(s, method_len); s += 4; + memcpy(s, method, method_len); s += method_len; + LIBSSH2_FREE(session, method); + + libssh2_htonu32(s, sig_len); s += 4; + memcpy(s, sig, sig_len); s += sig_len; + LIBSSH2_FREE(session, sig); + + if (libssh2_packet_write(session, packet, s - packet)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-hostbased request", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + while (1) { + unsigned char *data; + unsigned long data_len; + + if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_SUCCESS, &data, &data_len, 1) == 0) { + /* We are us and we've proved it. */ + LIBSSH2_FREE(session, data); + session->state |= LIBSSH2_STATE_AUTHENTICATED; + return 0; + } + + if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_FAILURE, &data, &data_len, 0) == 0) { + /* This public key is not allowed for this user on this server */ + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0); + return -1; + } + /* TODO: Timeout? */ + } + + return 0; +} +/* }}} */ + /* {{{ libssh2_userauth_publickey_fromfile_ex * Authenticate using a keypair found in the named files */