793 lines · 23.3 KB · C
RawDownload
/* * Copyright (c) 2009 by Daiki Ueno * Copyright (C) 2010 by Daniel Stenberg * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * *   Redistributions of source code must retain the above *   copyright notice, this list of conditions and the *   following disclaimer. * *   Redistributions in binary form must reproduce the above *   copyright notice, this list of conditions and the following *   disclaimer in the documentation and/or other materials *   provided with the distribution. * *   Neither the name of the copyright holder nor the names *   of any other contributors may be used to endorse or *   promote products derived from this software without *   specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ #include "libssh2_priv.h"#include "misc.h"#include <errno.h>#ifdef HAVE_SYS_UN_H#include <sys/un.h>#else/* Use the existence of sys/un.h as a test if Unix domain socket is   supported.  winsock*.h define PF_UNIX/AF_UNIX but do not actually   support them. */#undef PF_UNIX#endif#include "userauth.h"#include "session.h" /* Requests from client to agent for protocol 1 key operations */#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1#define SSH_AGENTC_RSA_CHALLENGE 3#define SSH_AGENTC_ADD_RSA_IDENTITY 7#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24 /* Requests from client to agent for protocol 2 key operations */#define SSH2_AGENTC_REQUEST_IDENTITIES 11#define SSH2_AGENTC_SIGN_REQUEST 13#define SSH2_AGENTC_ADD_IDENTITY 17#define SSH2_AGENTC_REMOVE_IDENTITY 18#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 /* Key-type independent requests from client to agent */#define SSH_AGENTC_ADD_SMARTCARD_KEY 20#define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21#define SSH_AGENTC_LOCK 22#define SSH_AGENTC_UNLOCK 23#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 /* Generic replies from agent to client */#define SSH_AGENT_FAILURE 5#define SSH_AGENT_SUCCESS 6 /* Replies from agent to client for protocol 1 key operations */#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2#define SSH_AGENT_RSA_RESPONSE 4 /* Replies from agent to client for protocol 2 key operations */#define SSH2_AGENT_IDENTITIES_ANSWER 12#define SSH2_AGENT_SIGN_RESPONSE 14 /* Key constraint identifiers */#define SSH_AGENT_CONSTRAIN_LIFETIME 1#define SSH_AGENT_CONSTRAIN_CONFIRM 2 /* non-blocking mode on agent connection is not yet implemented, but   for future use. */typedef enum {    agent_NB_state_init = 0,    agent_NB_state_request_created,    agent_NB_state_request_length_sent,    agent_NB_state_request_sent,    agent_NB_state_response_length_received,    agent_NB_state_response_received} agent_nonblocking_states; typedef struct agent_transaction_ctx {    unsigned char *request;    size_t request_len;    unsigned char *response;    size_t response_len;    agent_nonblocking_states state;} *agent_transaction_ctx_t; typedef int (*agent_connect_func)(LIBSSH2_AGENT *agent);typedef int (*agent_transact_func)(LIBSSH2_AGENT *agent,                                   agent_transaction_ctx_t transctx);typedef int (*agent_disconnect_func)(LIBSSH2_AGENT *agent); struct agent_publickey {    struct list_node node;     /* this is the struct we expose externally */    struct libssh2_agent_publickey external;}; struct agent_ops {    agent_connect_func connect;    agent_transact_func transact;    agent_disconnect_func disconnect;}; struct _LIBSSH2_AGENT{    LIBSSH2_SESSION *session;  /* the session this "belongs to" */     libssh2_socket_t fd;     struct agent_ops *ops;     struct agent_transaction_ctx transctx;    struct agent_publickey *identity;    struct list_head head;              /* list of public keys */}; #ifdef PF_UNIXstatic intagent_connect_unix(LIBSSH2_AGENT *agent){    const char *path;    struct sockaddr_un s_un;     path = getenv("SSH_AUTH_SOCK");    if (!path)        return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,                              "no auth sock variable");     agent->fd = socket(PF_UNIX, SOCK_STREAM, 0);    if (agent->fd < 0)        return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_SOCKET,                              "failed creating socket");     s_un.sun_family = AF_UNIX;    strncpy (s_un.sun_path, path, sizeof s_un.sun_path);    if (connect(agent->fd, (struct sockaddr*)(&s_un), sizeof s_un) != 0) {        close (agent->fd);        return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,                              "failed connecting with agent");    }     return LIBSSH2_ERROR_NONE;} static intagent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx){    unsigned char buf[4];    int rc;     /* Send the length of the request */    if (transctx->state == agent_NB_state_request_created) {        _libssh2_htonu32(buf, transctx->request_len);        rc = LIBSSH2_SEND_FD(agent->session, agent->fd, buf, sizeof buf, 0);        if (rc == -EAGAIN)            return LIBSSH2_ERROR_EAGAIN;        else if (rc < 0)            return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,                                  "agent send failed");        transctx->state = agent_NB_state_request_length_sent;    }     /* Send the request body */    if (transctx->state == agent_NB_state_request_length_sent) {        rc = LIBSSH2_SEND_FD(agent->session, agent->fd, transctx->request,                           transctx->request_len, 0);        if (rc == -EAGAIN)            return LIBSSH2_ERROR_EAGAIN;        else if (rc < 0)            return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,                                  "agent send failed");        transctx->state = agent_NB_state_request_sent;    }     /* Receive the length of a response */    if (transctx->state == agent_NB_state_request_sent) {        rc = LIBSSH2_RECV_FD(agent->session, agent->fd, buf, sizeof buf, 0);        if (rc < 0) {            if (rc == -EAGAIN)                return LIBSSH2_ERROR_EAGAIN;            return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_RECV,                                  "agent recv failed");        }        transctx->response_len = _libssh2_ntohu32(buf);        transctx->response = LIBSSH2_ALLOC(agent->session,                                           transctx->response_len);        if (!transctx->response)            return LIBSSH2_ERROR_ALLOC;         transctx->state = agent_NB_state_response_length_received;    }     /* Receive the response body */    if (transctx->state == agent_NB_state_response_length_received) {        rc = LIBSSH2_RECV_FD(agent->session, agent->fd, transctx->response,                           transctx->response_len, 0);        if (rc < 0) {            if (rc == -EAGAIN)                return LIBSSH2_ERROR_EAGAIN;            return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,                                  "agent recv failed");        }        transctx->state = agent_NB_state_response_received;    }     return 0;} static intagent_disconnect_unix(LIBSSH2_AGENT *agent){    int ret;    ret = close(agent->fd);     if(ret == -1)        return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT,                              "failed closing the agent socket");    return LIBSSH2_ERROR_NONE;} struct agent_ops agent_ops_unix = {    agent_connect_unix,    agent_transact_unix,    agent_disconnect_unix};#endif  /* PF_UNIX */ #ifdef WIN32/* Code to talk to Pageant was taken from PuTTY. * * Portions copyright Robert de Bath, Joris van Rantwijk, Delian * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas * Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, * Markus Kuhn, Colin Watson, and CORE SDI S.A. */#define PAGEANT_COPYDATA_ID 0x804e50ba   /* random goop */#define PAGEANT_MAX_MSGLEN  8192 static intagent_connect_pageant(LIBSSH2_AGENT *agent){    HWND hwnd;    hwnd = FindWindow("Pageant", "Pageant");    if (!hwnd)        return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,                              "failed connecting agent");    agent->fd = 0;         /* Mark as the connection has been established */    return LIBSSH2_ERROR_NONE;} static intagent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx){    HWND hwnd;    char mapname[23];    HANDLE filemap;    unsigned char *p;    unsigned char *p2;    int id;    COPYDATASTRUCT cds;     if (!transctx || 4 + transctx->request_len > PAGEANT_MAX_MSGLEN)        return _libssh2_error(agent->session, LIBSSH2_ERROR_INVAL,                              "illegal input");     hwnd = FindWindow("Pageant", "Pageant");    if (!hwnd)        return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,                              "found no pageant");     sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId());    filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,                                0, PAGEANT_MAX_MSGLEN, mapname);     if (filemap == NULL || filemap == INVALID_HANDLE_VALUE)        return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,                              "failed setting up pageant filemap");     p2 = p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);    _libssh2_store_str(&p2, (const char *)transctx->request,                       transctx->request_len);     cds.dwData = PAGEANT_COPYDATA_ID;    cds.cbData = 1 + strlen(mapname);    cds.lpData = mapname;     id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);    if (id > 0) {        transctx->response_len = _libssh2_ntohu32(p);        if (transctx->response_len > PAGEANT_MAX_MSGLEN) {            UnmapViewOfFile(p);            CloseHandle(filemap);            return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,                                  "agent setup fail");        }        transctx->response = LIBSSH2_ALLOC(agent->session,                                           transctx->response_len);        if (!transctx->response) {            UnmapViewOfFile(p);            CloseHandle(filemap);            return _libssh2_error(agent->session, LIBSSH2_ERROR_ALLOC,                                  "agent malloc");        }        memcpy(transctx->response, p + 4, transctx->response_len);    }     UnmapViewOfFile(p);    CloseHandle(filemap);    return 0;} static intagent_disconnect_pageant(LIBSSH2_AGENT *agent){    agent->fd = LIBSSH2_INVALID_SOCKET;    return 0;} struct agent_ops agent_ops_pageant = {    agent_connect_pageant,    agent_transact_pageant,    agent_disconnect_pageant};#endif  /* WIN32 */ static struct {    const char *name;    struct agent_ops *ops;} supported_backends[] = {#ifdef WIN32    {"Pageant", &agent_ops_pageant},#endif  /* WIN32 */#ifdef PF_UNIX    {"Unix", &agent_ops_unix},#endif  /* PF_UNIX */    {NULL, NULL}}; static intagent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,           const unsigned char *data, size_t data_len, void **abstract){    LIBSSH2_AGENT *agent = (LIBSSH2_AGENT *) (*abstract);    agent_transaction_ctx_t transctx = &agent->transctx;    struct agent_publickey *identity = agent->identity;    ssize_t len = 1 + 4 + identity->external.blob_len + 4 + data_len + 4;    ssize_t method_len;    unsigned char *s;    int rc;     /* Create a request to sign the data */    if (transctx->state == agent_NB_state_init) {        s = transctx->request = LIBSSH2_ALLOC(session, len);        if (!transctx->request)            return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,                                  "out of memory");         *s++ = SSH2_AGENTC_SIGN_REQUEST;        /* key blob */        _libssh2_store_str(&s, (const char *)identity->external.blob,                           identity->external.blob_len);        /* data */        _libssh2_store_str(&s, (const char *)data, data_len);         /* flags */        _libssh2_store_u32(&s, 0);         transctx->request_len = s - transctx->request;        transctx->state = agent_NB_state_request_created;    }     /* Make sure to be re-called as a result of EAGAIN. */    if (*transctx->request != SSH2_AGENTC_SIGN_REQUEST)        return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,                              "illegal request");     if (!agent->ops)        /* if no agent has been connected, bail out */        return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,                              "agent not connected");     rc = agent->ops->transact(agent, transctx);    if (rc) {        goto error;    }    LIBSSH2_FREE(session, transctx->request);    transctx->request = NULL;     len = transctx->response_len;    s = transctx->response;    len--;    if (len < 0) {        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;        goto error;    }    if (*s != SSH2_AGENT_SIGN_RESPONSE) {        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;        goto error;    }    s++;     /* Skip the entire length of the signature */    len -= 4;    if (len < 0) {        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;        goto error;    }    s += 4;     /* Skip signing method */    len -= 4;    if (len < 0) {        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;        goto error;    }    method_len = _libssh2_ntohu32(s);    s += 4;    len -= method_len;    if (len < 0) {        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;        goto error;    }    s += method_len;     /* Read the signature */    len -= 4;    if (len < 0) {        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;        goto error;    }    *sig_len = _libssh2_ntohu32(s);    s += 4;    len -= *sig_len;    if (len < 0) {        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;        goto error;    }     *sig = LIBSSH2_ALLOC(session, *sig_len);    if (!*sig) {        rc = LIBSSH2_ERROR_ALLOC;        goto error;    }    memcpy(*sig, s, *sig_len);   error:    LIBSSH2_FREE(session, transctx->request);    transctx->request = NULL;     LIBSSH2_FREE(session, transctx->response);    transctx->response = NULL;     return _libssh2_error(session, rc, "agent sign failure");} static intagent_list_identities(LIBSSH2_AGENT *agent){    agent_transaction_ctx_t transctx = &agent->transctx;    ssize_t len, num_identities;    unsigned char *s;    int rc;    unsigned char c = SSH2_AGENTC_REQUEST_IDENTITIES;     /* Create a request to list identities */    if (transctx->state == agent_NB_state_init) {        transctx->request = &c;        transctx->request_len = 1;        transctx->state = agent_NB_state_request_created;    }     /* Make sure to be re-called as a result of EAGAIN. */    if (*transctx->request != SSH2_AGENTC_REQUEST_IDENTITIES)        return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,

Lines 1–500 of 793.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t