diff -Naur libssh2-1.0-clean/include/libssh2.h libssh2-1.0/include/libssh2.h --- libssh2-1.0-clean/include/libssh2.h 2008-12-26 22:35:14.000000000 +0100 +++ libssh2-1.0/include/libssh2.h 2009-01-23 23:58:32.062500000 +0100 @@ -339,6 +339,10 @@ LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, const char *password, unsigned int password_len, LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))); #define libssh2_userauth_password(session, username, password) libssh2_userauth_password_ex((session), (username), strlen(username), (password), strlen(password), NULL) +LIBSSH2_API int libssh2_userauth_publickey_fromagent_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, + const char *publickey); +#define libssh2_userauth_publickey_fromagent(session, username, publickey) \ + libssh2_userauth_publickey_fromagent_ex((session), (username), strlen(username), (publickey)) LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, const char *publickey, const char *privatekey, const char *passphrase); @@ -352,6 +356,11 @@ #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)) +LIBSSH2_API int libssh2_userauth_sign_with_agent(LIBSSH2_SESSION * session, const char *username, + unsigned int username_len, + unsigned char **signature, + unsigned long *signature_len); + /* * response_callback is provided with filled by library prompts array, * but client must allocate and fill individual responses. Responses diff -Naur libssh2-1.0-clean/src/agent.c libssh2-1.0/src/agent.c --- libssh2-1.0-clean/src/agent.c 1970-01-01 01:00:00.000000000 +0100 +++ libssh2-1.0/src/agent.c 2009-01-24 00:56:10.562500000 +0100 @@ -0,0 +1,476 @@ +#include "libssh2_priv.h" + +#include +#ifndef WIN32 +#include +#include +#else +#include +#include +#endif + +#define SSH2_AGENTC_REQUEST_IDENTITIES 11 +#define SSH2_AGENT_IDENTITIES_ANSWER 12 +#define SSH_AGENTC_SIGN_REQUEST 13 +#define SSH2_AGENT_SIGN_RESPONSE 14 + +#ifdef LIBSSH2DEBUG +#define UNPRINTABLE_CHAR '.' +static void +debugdump(LIBSSH2_SESSION * session, + const char *desc, unsigned char *ptr, unsigned long size) +{ + size_t i; + size_t c; + FILE *stream = stdout; + unsigned int width = 0x10; + + if (!(session->showmask & (1 << LIBSSH2_DBG_TRANS))) { + /* not asked for, bail out */ + return; + } + + fprintf(stream, "=> %s (%d bytes)\n", desc, (int) size); + + for(i = 0; i < size; i += width) { + + fprintf(stream, "%04lx: ", (long)i); + + /* hex not disabled, show it */ + for(c = 0; c < width; c++) { + if (i + c < size) + fprintf(stream, "%02x ", ptr[i + c]); + else + fputs(" ", stream); + } + + for(c = 0; (c < width) && (i + c < size); c++) { + fprintf(stream, "%c", + (ptr[i + c] >= 0x20) && + (ptr[i + c] < 0x80) ? ptr[i + c] : UNPRINTABLE_CHAR); + } + fputc('\n', stream); /* newline */ + } + fflush(stream); +} +#else +#define debugdump(a,x,y,z) +#endif + +#ifndef WIN32 +static int get_agent_socket(LIBSSH2_SESSION *session) +{ + struct sockaddr_un sunaddr; + int sock; + char *sock_name; + + sock_name = getenv("SSH_AUTH_SOCK"); + if (sock_name == NULL) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "no SSH_AUTH_SOCK set."); + return -1; + } + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "using %s as agent socket", sock_name); + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "method: %.*s", + (int)session->userauth_pblc_method_len, session->userauth_pblc_method); + *session->userauth_pblc_b = 0x01; + + sunaddr.sun_family = AF_UNIX; + strcpy(sunaddr.sun_path, sock_name); + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "sock < 0. Returning."); + return -1; + } + + /* TODO: connect may return EINPROGRESS, check it. */ + if (connect(sock, (struct sockaddr *)&sunaddr, sizeof sunaddr) < 0) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "connect < 0. Returning."); + close(sock); + return -1; + } + return sock; +} + +#else /*WIN32*/ +#define AGENT_COPYDATA_ID 0x804e50ba /* Pageant private ID */ +#define AGENT_MAX_MSGLEN 8192 + +/* {{{ agent_exists + * Checks if Pageant is running + */ +int agent_exists(void) +{ + HWND hwnd; + hwnd = FindWindow("Pageant", "Pageant"); + if (!hwnd) { + return FALSE; + } else { + return TRUE; + } +} + +/* }}} */ + +/* {{{ agent_query + * Queries Pageant + */ +int agent_query(LIBSSH2_SESSION *session, + void *in, int inlen, + void **out, int *outlen, + void (*callback)(void *, void *, int), void *callback_ctx) +{ + HWND hwnd; + char *mapname; + HANDLE filemap; + unsigned char *p, *ret; + int id, retlen; + COPYDATASTRUCT cds; + + *out = NULL; + *outlen = 0; + + hwnd = FindWindow("Pageant", "Pageant"); + if (!hwnd) + return 1; /* *out == NULL, so failure */ + mapname = LIBSSH2_ALLOC(session, 32); + snprintf(mapname, 32, "PageantRequest%08x", (unsigned)GetCurrentThreadId()); + filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, + 0, AGENT_MAX_MSGLEN, mapname); + if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) + return 1; /* *out == NULL, so failure */ + p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); + libssh2_htonu32(p, inlen); + memcpy(p+4, in, inlen); + cds.dwData = AGENT_COPYDATA_ID; + cds.cbData = 1 + strlen(mapname); + cds.lpData = mapname; + + id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds); + if (id > 0) { + retlen = libssh2_ntohu32(p); + ret = LIBSSH2_ALLOC(session, retlen); + if (ret) { + memcpy(ret, p+4, retlen); + *out = ret; + *outlen = retlen; + } + } + UnmapViewOfFile(p); + CloseHandle(filemap); + return 1; +} + +/* }}} */ +#endif /*WIN32*/ + +/* {{{ libssh2_userauth_sign_with_agent + * Signs request with agent + */ +int +libssh2_userauth_sign_with_agent(LIBSSH2_SESSION *session, + const char *username, + unsigned int username_len, + unsigned char **signature, + unsigned long *signature_len) +{ +#ifndef WIN32 + int sock; + unsigned char reqlen[4]; +#endif + unsigned char agbuf[4*1024]; + unsigned char *resbuf; + unsigned char *p; + int len; + char *pub_key; + int pub_key_len; + + /*libssh2_trace(session, 0xFFFFFFFF);*/ + + if (session->agent_state == libssh2_NB_state_idle) { +#ifdef WIN32 + if(!agent_exists()) + return -1; +#else + sock = get_agent_socket(session); + if (sock < 0) + return -1; +#endif + session->agent_state = libssh2_NB_state_created; + } + + /* Get public keys from agent. */ + if(session->agent_state == libssh2_NB_state_created) { + if(libssh2_agent_read_publickey(session, NULL, NULL, &pub_key, + &pub_key_len, + session->agent_current_key)) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Unable to read cached agent public key"); + return -1; + } + + /* Sign */ + agbuf[0] = SSH_AGENTC_SIGN_REQUEST; + p = agbuf + 1; + + libssh2_htonu32(p, pub_key_len); + p += 4; + libssh2_htonu32(p, session->userauth_pblc_method_len); + p += 4; + memcpy(p, session->userauth_pblc_method, + session->userauth_pblc_method_len); + p += session->userauth_pblc_method_len; + memcpy(p, pub_key+11, pub_key_len-11); + p += pub_key_len-11; + + len = 4 + session->session_id_len + 1 + 4 + username_len + + 4 + strlen("ssh-connection") + 4 + strlen("publickey") + + 1 + 4 + session->userauth_pblc_method_len + + 4 + pub_key_len - 11 + 4 + session->userauth_pblc_method_len; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "len: %d", len); + + libssh2_htonu32(p, len); + p += 4; + + libssh2_htonu32(p, session->session_id_len); + p += 4; + memcpy(p, session->session_id, session->session_id_len); + p += session->session_id_len; + + *p = SSH_MSG_USERAUTH_REQUEST; + p++; + + libssh2_htonu32(p, username_len); + p += 4; + memcpy(p, username, username_len); + p += username_len; + libssh2_htonu32(p, strlen("ssh-connection")); + p += 4; + memcpy(p, "ssh-connection", strlen("ssh-connection")); + p += strlen("ssh-connection"); + libssh2_htonu32(p, strlen("publickey")); + p += 4; + memcpy(p, "publickey", strlen("publickey")); + p += strlen("publickey"); + *p = 1; + p += 1; + + libssh2_htonu32(p, session->userauth_pblc_method_len); + p += 4; + memcpy(p, session->userauth_pblc_method, + session->userauth_pblc_method_len); + p += session->userauth_pblc_method_len; + + libssh2_htonu32(p, pub_key_len); + p += 4; + libssh2_htonu32(p, session->userauth_pblc_method_len); + p += 4; + memcpy(p, session->userauth_pblc_method, + session->userauth_pblc_method_len); + p += session->userauth_pblc_method_len; + memcpy(p, pub_key+11, pub_key_len-11); + p += pub_key_len-11; + + /* Flags. */ + libssh2_htonu32(p, 0); + p += 4; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "len = %d", p - agbuf); +#ifdef WIN32 + if(agent_query(session, agbuf, p-agbuf, &resbuf, &len, NULL, NULL)!=1) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Agent query failed"); + return -1; + } +#else + libssh2_htonu32(reqlen, p - agbuf); + write(sock, reqlen, 4); + len = write(sock, agbuf, p - agbuf); + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "ret: %d, len: %d", + len, p - agbuf); + session->agent_state = libssh2_NB_state_sent2; + } + + if(session->agent_state = libssh2_NB_state_sent2) { + read(sock, agbuf, 4); + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "%02X %02X %02X %02X", + agbuf[0], agbuf[1], agbuf[2], agbuf[3]); + len = libssh2_ntohu32(agbuf); + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "ret len: %d", len); + resbuf = LIBSSH2_ALLOC(session, len); + read(sock, resbuf, len); +#endif + if(resbuf[0] != SSH2_AGENT_SIGN_RESPONSE) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Agent admitted failure to sign using the key."); + return -1; + } + + debugdump(session, "response", resbuf, len); + p=resbuf+5+4+libssh2_ntohu32(resbuf+5); + *signature_len = libssh2_ntohu32(p); + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "sign_len: %ld", + *signature_len); + *signature = LIBSSH2_ALLOC(session, *signature_len); + if (!*signature) { + return -1; + } + + memcpy(*signature, p+4, *signature_len); +#ifndef WIN32 + close(sock); +#endif + LIBSSH2_FREE(session, pub_key); + LIBSSH2_FREE(session, resbuf); + + libssh2_trace(session, 0); + return 0; + } + return 0; +} + +/* }}} */ + +/* {{{ libssh2_agent_get_all_keys + * Gets all public keys from agent + */ +int +libssh2_agent_get_all_keys(LIBSSH2_SESSION * session, unsigned char **pubkeysdata, + unsigned long *pubkeysdata_len, int *pubkeys_cnt) +{ +#ifndef WIN32 + int sock; + unsigned char reqlen[4]; +#endif + unsigned char agbuf[4]; + unsigned char *resbuf; + int len; + char *pub_keys; + int pub_keys_len; + int pub_keys_cnt; + + /*libssh2_trace(session, 0xFFFFFFFF);*/ + + if (session->agent_state == libssh2_NB_state_idle) { +#ifdef WIN32 + if(!agent_exists()) + return -1; +#else + sock = get_agent_socket(session); + if (sock < 0) + return -1; +#endif + session->agent_state = libssh2_NB_state_created; + } + + if (session->agent_state == libssh2_NB_state_created) { + memset(agbuf, 0, sizeof(agbuf)); + agbuf[0] = SSH2_AGENTC_REQUEST_IDENTITIES; +#ifdef WIN32 + if(agent_query(session, agbuf, 1, &resbuf, &len, NULL, NULL)!=1) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Agent query failed"); + return -1; + } +#else + libssh2_htonu32(reqlen, 1); + write(sock, reqlen, 4); + write(sock, agbuf, 1); + session->agent_state = libssh2_NB_state_sent1; + } + + if (session->agent_state == libssh2_NB_state_sent1) { + read(sock, agbuf, 4); + len = libssh2_ntohu32(agbuf); + resbuf = LIBSSH2_ALLOC(session, len); + read(sock, resbuf, len); +#endif + debugdump(session, "pubkey from agent", resbuf, len); + + if (resbuf[0] != SSH2_AGENT_IDENTITIES_ANSWER) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Bad agent reply: %d", resbuf[0]); + return -1; + } + pub_keys_cnt = libssh2_ntohu32(resbuf+1); + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Found %d keys", pub_keys_cnt); + if (pub_keys_cnt == 0) { + /* No key found. */ + return -1; + } + + pub_keys_len = len-5; + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Total keys length: %d", pub_keys_len); + pub_keys = LIBSSH2_ALLOC(session, pub_keys_len); + memcpy(pub_keys, resbuf+5, pub_keys_len); + + *pubkeysdata_len = pub_keys_len; + *pubkeysdata = pub_keys; + *pubkeys_cnt = pub_keys_cnt; + + LIBSSH2_FREE(session, resbuf); + } + return 0; +} + +/* }}} */ + +/* {{{ libssh2_agent_read_publickey + * Gets public key from data cached earlier + */ +int +libssh2_agent_read_publickey(LIBSSH2_SESSION * session, unsigned char **method, + unsigned long *method_len, + unsigned char **pubkeydata, + unsigned long *pubkeydata_len, + int pubkey_number) +{ + unsigned char *pub_key; + int pub_key_len; + int com_len; + unsigned char *data = session->agent_keys; + int i; + + if(!data) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "No cached public keys"); + return -1; + } + + pub_key_len = libssh2_ntohu32(data); + for(i=0; iagent_keys > session->agent_keys_len) { + return -1; + } + com_len = libssh2_ntohu32(data); + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "comment len = %d", + com_len); + data += 4 + com_len; + if(data + 4 - session->agent_keys > session->agent_keys_len) { + return -1; + } + pub_key_len = libssh2_ntohu32(data); + if(data + 4 + pub_key_len - session->agent_keys > session->agent_keys_len) { + return -1; + } + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "new key len = %d", + pub_key_len); + } + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "key offset = %d", + data-session->agent_keys); + pub_key = LIBSSH2_ALLOC(session, pub_key_len); + memcpy(pub_key, data+4, pub_key_len); + + if(method!=NULL && method_len!=NULL) { + *method_len = libssh2_ntohu32(pub_key); + *method = pub_key+4; + } + + *pubkeydata_len = pub_key_len; + *pubkeydata = pub_key; + + return 0; +} + +/* }}} */ \ No newline at end of file diff -Naur libssh2-1.0-clean/src/libssh2_priv.h libssh2-1.0/src/libssh2_priv.h --- libssh2-1.0-clean/src/libssh2_priv.h 2008-12-20 00:18:16.000000000 +0100 +++ libssh2-1.0/src/libssh2_priv.h 2009-01-24 00:01:11.859375000 +0100 @@ -893,6 +893,13 @@ char *scpSend_err_msg; long scpSend_err_len; LIBSSH2_CHANNEL *scpSend_channel; + + /* State variables used in libssh2_userauth_sign_with_agent */ + libssh2_nonblocking_states agent_state; + unsigned char *agent_keys; + int agent_keys_len; + int agent_keys_cnt; + int agent_current_key; }; /* session.state bits */ diff -Naur libssh2-1.0-clean/src/userauth.c libssh2-1.0/src/userauth.c --- libssh2-1.0-clean/src/userauth.c 2008-11-28 00:12:42.000000000 +0100 +++ libssh2-1.0/src/userauth.c 2009-01-24 00:53:24.343750000 +0100 @@ -816,6 +816,53 @@ /* }}} */ +/* {{{ libssh2_userauth_publickey_fromagent_ex + * Authenticate using agent with optional public key found in named file + */ +LIBSSH2_API int +libssh2_userauth_publickey_fromagent_ex(LIBSSH2_SESSION * session, + const char *username, + unsigned int username_len, + const char *publickey) +{ + if(publickey==NULL) { + /* Get keys from agent */ + int ret; + int i; + + if(libssh2_agent_get_all_keys(session, &session->agent_keys, + &session->agent_keys_len, + &session->agent_keys_cnt)) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Unable to get public keys from agent"); + return -1; + } + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Read %d keys from agent", session->agent_keys_cnt); + for(i=0; iagent_keys_cnt; i++) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Trying to auth with key #%d", i); + session->agent_current_key=i; + ret=libssh2_userauth_publickey_fromfile_ex(session, username, + username_len, publickey, NULL, NULL); + if(ret==0) { + session->agent_keys_cnt=0; + session->agent_keys_len=0; + LIBSSH2_FREE(session, session->agent_keys); + return ret; + } + } + //TODO: if needed... + //} else { + // /* Use key from file */ + // return libssh2_userauth_publickey_fromfile_ex(session, username, + // username_len, publickey, NULL, NULL); + } + return -1; +} + +/* }}} */ + /* {{{ libssh2_userauth_publickey_fromfile_ex * Authenticate using a keypair found in the named files */ @@ -841,11 +888,24 @@ memset(&session->userauth_pblc_packet_requirev_state, 0, sizeof(session->userauth_pblc_packet_requirev_state)); - if (libssh2_file_read_publickey - (session, &session->userauth_pblc_method, - &session->userauth_pblc_method_len, &pubkeydata, &pubkeydata_len, - publickey)) { - return -1; + if(publickey!=NULL) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "reading public key from file %s", publickey); + if (libssh2_file_read_publickey + (session, &session->userauth_pblc_method, + &session->userauth_pblc_method_len, &pubkeydata, + &pubkeydata_len, publickey)) { + return -1; + } + } else { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "reading public key from agent"); + if (libssh2_agent_read_publickey + (session, &session->userauth_pblc_method, + &session->userauth_pblc_method_len, &pubkeydata, + &pubkeydata_len, session->agent_current_key)) { + return -1; + } } /* @@ -995,6 +1055,10 @@ LIBSSH2_FREE(session, session->userauth_pblc_data); session->userauth_pblc_data = NULL; + if (privatekey != NULL) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "reading private key from file %s", privatekey); + /* FIXME: indent original code begin */ if (libssh2_file_read_privatekey (session, &privkeyobj, &abstract, session->userauth_pblc_method, session->userauth_pblc_method_len, privatekey, passphrase)) { @@ -1031,6 +1095,24 @@ if (privkeyobj->dtor) { privkeyobj->dtor(session, &abstract); } + /* FIXME: indent original code end */ + } else { + /* Getting sig from agent. */ + int ret; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "reading private key from agent"); + + *session->userauth_pblc_b = 0x01; + + ret = libssh2_userauth_sign_with_agent(session, + username, username_len, + &sig, &sig_len); + if (ret == -1) { + session->userauth_pblc_state = libssh2_NB_state_idle; + return -1; + } + } /* * If this function was restarted, pubkeydata_len might still be 0