SSH: added agent based authentication
CURLSSH_AUTH_AGENT is a new auth type for SSH
This commit is contained in:
Родитель
021e89b8c6
Коммит
e351972bc8
|
@ -631,6 +631,7 @@ typedef enum {
|
|||
#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */
|
||||
#define CURLSSH_AUTH_HOST (1<<2) /* host key files */
|
||||
#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */
|
||||
#define CURLSSH_AUTH_AGENT (1<<4) /* agent (ssh-agent, pageant...) */
|
||||
#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY
|
||||
|
||||
#define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */
|
||||
|
|
114
lib/ssh.c
114
lib/ssh.c
|
@ -324,7 +324,8 @@ static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc)
|
|||
static LIBSSH2_FREE_FUNC(my_libssh2_free)
|
||||
{
|
||||
(void)abstract; /* arg not used */
|
||||
free(ptr);
|
||||
if(ptr) /* ssh2 agent sometimes call free with null ptr */
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -345,6 +346,9 @@ static void state(struct connectdata *conn, sshstate nowstate)
|
|||
"SSH_AUTH_PKEY",
|
||||
"SSH_AUTH_PASS_INIT",
|
||||
"SSH_AUTH_PASS",
|
||||
"SSH_AUTH_AGENT_INIT",
|
||||
"SSH_AUTH_AGENT_LIST",
|
||||
"SSH_AUTH_AGENT",
|
||||
"SSH_AUTH_HOST_INIT",
|
||||
"SSH_AUTH_HOST",
|
||||
"SSH_AUTH_KEY_INIT",
|
||||
|
@ -893,12 +897,97 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
|
|||
state(conn, SSH_AUTH_HOST);
|
||||
}
|
||||
else {
|
||||
state(conn, SSH_AUTH_KEY_INIT);
|
||||
state(conn, SSH_AUTH_AGENT_INIT);
|
||||
}
|
||||
break;
|
||||
|
||||
case SSH_AUTH_HOST:
|
||||
state(conn, SSH_AUTH_KEY_INIT);
|
||||
state(conn, SSH_AUTH_AGENT_INIT);
|
||||
break;
|
||||
|
||||
case SSH_AUTH_AGENT_INIT:
|
||||
#ifdef HAVE_LIBSSH2_AGENT_API
|
||||
if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
|
||||
&& (strstr(sshc->authlist, "publickey") != NULL)) {
|
||||
|
||||
/* Connect to the ssh-agent */
|
||||
/* The agent could be shared by a curl thread i believe
|
||||
but nothing obvious as keys can be added/removed at any time */
|
||||
if(!sshc->ssh_agent) {
|
||||
sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
|
||||
if(!sshc->ssh_agent) {
|
||||
infof(data, "Could not create agent object\n");
|
||||
|
||||
state(conn, SSH_AUTH_KEY_INIT);
|
||||
}
|
||||
}
|
||||
|
||||
rc = libssh2_agent_connect(sshc->ssh_agent);
|
||||
if(rc == LIBSSH2_ERROR_EAGAIN)
|
||||
break;
|
||||
if(rc < 0) {
|
||||
infof(data, "Failure connecting to agent\n");
|
||||
state(conn, SSH_AUTH_KEY_INIT);
|
||||
}
|
||||
else {
|
||||
state(conn, SSH_AUTH_AGENT_LIST);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif /* HAVE_LIBSSH2_AGENT_API */
|
||||
state(conn, SSH_AUTH_KEY_INIT);
|
||||
break;
|
||||
|
||||
case SSH_AUTH_AGENT_LIST:
|
||||
rc = libssh2_agent_list_identities(sshc->ssh_agent);
|
||||
|
||||
if(rc == LIBSSH2_ERROR_EAGAIN)
|
||||
break;
|
||||
if(rc < 0) {
|
||||
infof(data, "Failure requesting identities to agent\n");
|
||||
state(conn, SSH_AUTH_KEY_INIT);
|
||||
}
|
||||
else {
|
||||
state(conn, SSH_AUTH_AGENT);
|
||||
sshc->sshagent_prev_identity = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case SSH_AUTH_AGENT:
|
||||
/* as prev_identity evolves only after an identity user auth finished we
|
||||
can safely request it again as long as EAGAIN is returned here or by
|
||||
libssh2_agent_userauth */
|
||||
rc = libssh2_agent_get_identity(sshc->ssh_agent,
|
||||
&sshc->sshagent_identity,
|
||||
sshc->sshagent_prev_identity);
|
||||
if(rc == LIBSSH2_ERROR_EAGAIN)
|
||||
break;
|
||||
|
||||
if(rc == 0) {
|
||||
rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user,
|
||||
sshc->sshagent_identity);
|
||||
|
||||
if(rc < 0) {
|
||||
if(rc != LIBSSH2_ERROR_EAGAIN) {
|
||||
/* tried and failed? go to next identity */
|
||||
sshc->sshagent_prev_identity = sshc->sshagent_identity;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(rc < 0)
|
||||
infof(data, "Failure requesting identities to agent\n");
|
||||
else if(rc == 1)
|
||||
infof(data, "No identity would match\n");
|
||||
|
||||
if(rc == LIBSSH2_ERROR_NONE) {
|
||||
sshc->authed = TRUE;
|
||||
infof(data, "Agent based authentication successful\n");
|
||||
state(conn, SSH_AUTH_DONE);
|
||||
}
|
||||
else
|
||||
state(conn, SSH_AUTH_KEY_INIT);
|
||||
break;
|
||||
|
||||
case SSH_AUTH_KEY_INIT:
|
||||
|
@ -2357,6 +2446,25 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBSSH2_AGENT_API
|
||||
if(sshc->ssh_agent) {
|
||||
rc = libssh2_agent_disconnect(sshc->ssh_agent);
|
||||
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
||||
break;
|
||||
}
|
||||
else if(rc < 0) {
|
||||
infof(data, "Failed to disconnect from libssh2 agent\n");
|
||||
}
|
||||
libssh2_agent_free (sshc->ssh_agent);
|
||||
sshc->ssh_agent = NULL;
|
||||
|
||||
/* NB: there is no need to free identities, they are part of internal
|
||||
agent stuff */
|
||||
sshc->sshagent_identity = NULL;
|
||||
sshc->sshagent_prev_identity = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(sshc->ssh_session) {
|
||||
rc = libssh2_session_free(sshc->ssh_session);
|
||||
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
||||
|
|
|
@ -44,6 +44,9 @@ typedef enum {
|
|||
SSH_AUTH_PKEY,
|
||||
SSH_AUTH_PASS_INIT,
|
||||
SSH_AUTH_PASS,
|
||||
SSH_AUTH_AGENT_INIT,/* initialize then wait for connection to agent */
|
||||
SSH_AUTH_AGENT_LIST,/* ask for list then wait for entire list to come */
|
||||
SSH_AUTH_AGENT, /* attempt one key at a time */
|
||||
SSH_AUTH_HOST_INIT,
|
||||
SSH_AUTH_HOST,
|
||||
SSH_AUTH_KEY_INIT,
|
||||
|
@ -139,6 +142,12 @@ struct ssh_conn {
|
|||
LIBSSH2_SFTP_HANDLE *sftp_handle;
|
||||
int orig_waitfor; /* default READ/WRITE bits wait for */
|
||||
|
||||
#ifdef HAVE_LIBSSH2_AGENT_API
|
||||
LIBSSH2_AGENT *ssh_agent; /* proxy to ssh-agent/pageant */
|
||||
struct libssh2_agent_publickey *sshagent_identity,
|
||||
*sshagent_prev_identity;
|
||||
#endif
|
||||
|
||||
/* note that HAVE_LIBSSH2_KNOWNHOST_API is a define set in the libssh2.h
|
||||
header */
|
||||
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
|
||||
|
|
Загрузка…
Ссылка в новой задаче