version 1.213, 2015/01/08 10:14:08 |
version 1.214, 2015/01/14 20:05:27 |
|
|
#include "pathnames.h" |
#include "pathnames.h" |
#include "uidswap.h" |
#include "uidswap.h" |
#include "hostfile.h" |
#include "hostfile.h" |
|
#include "ssherr.h" |
|
|
#ifdef GSSAPI |
#ifdef GSSAPI |
#include "ssh-gss.h" |
#include "ssh-gss.h" |
|
|
} while (0) |
} while (0) |
|
|
while ((alg = strsep(&avail, ",")) && *alg != '\0') { |
while ((alg = strsep(&avail, ",")) && *alg != '\0') { |
if ((ktype = key_type_from_name(alg)) == KEY_UNSPEC) |
if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) |
fatal("%s: unknown alg %s", __func__, alg); |
fatal("%s: unknown alg %s", __func__, alg); |
if (lookup_key_in_hostkeys_by_type(hostkeys, |
if (lookup_key_in_hostkeys_by_type(hostkeys, |
key_type_plain(ktype), NULL)) |
sshkey_type_plain(ktype), NULL)) |
ALG_APPEND(first, alg); |
ALG_APPEND(first, alg); |
else |
else |
ALG_APPEND(last, alg); |
ALG_APPEND(last, alg); |
|
|
* Authenticate user |
* Authenticate user |
*/ |
*/ |
|
|
typedef struct Authctxt Authctxt; |
typedef struct cauthctxt Authctxt; |
typedef struct Authmethod Authmethod; |
typedef struct cauthmethod Authmethod; |
typedef struct identity Identity; |
typedef struct identity Identity; |
typedef struct idlist Idlist; |
typedef struct idlist Idlist; |
|
|
struct identity { |
struct identity { |
TAILQ_ENTRY(identity) next; |
TAILQ_ENTRY(identity) next; |
AuthenticationConnection *ac; /* set if agent supports key */ |
int agent_fd; /* >=0 if agent supports key */ |
Key *key; /* public/private key */ |
struct sshkey *key; /* public/private key */ |
char *filename; /* comment for agent-only keys */ |
char *filename; /* comment for agent-only keys */ |
int tried; |
int tried; |
int isprivate; /* key points to the private key */ |
int isprivate; /* key points to the private key */ |
|
|
}; |
}; |
TAILQ_HEAD(idlist, identity); |
TAILQ_HEAD(idlist, identity); |
|
|
struct Authctxt { |
struct cauthctxt { |
const char *server_user; |
const char *server_user; |
const char *local_user; |
const char *local_user; |
const char *host; |
const char *host; |
const char *service; |
const char *service; |
Authmethod *method; |
struct cauthmethod *method; |
sig_atomic_t success; |
sig_atomic_t success; |
char *authlist; |
char *authlist; |
|
int attempt; |
/* pubkey */ |
/* pubkey */ |
Idlist keys; |
struct idlist keys; |
AuthenticationConnection *agent; |
int agent_fd; |
/* hostbased */ |
/* hostbased */ |
Sensitive *sensitive; |
Sensitive *sensitive; |
/* kbd-interactive */ |
/* kbd-interactive */ |
|
|
/* generic */ |
/* generic */ |
void *methoddata; |
void *methoddata; |
}; |
}; |
struct Authmethod { |
|
|
struct cauthmethod { |
char *name; /* string to compare against server's list */ |
char *name; /* string to compare against server's list */ |
int (*userauth)(Authctxt *authctxt); |
int (*userauth)(Authctxt *authctxt); |
void (*cleanup)(Authctxt *authctxt); |
void (*cleanup)(Authctxt *authctxt); |
|
|
key->type, pktype); |
key->type, pktype); |
goto done; |
goto done; |
} |
} |
fp = key_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); |
fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); |
debug2("input_userauth_pk_ok: fp %s", fp); |
debug2("input_userauth_pk_ok: fp %s", fp); |
free(fp); |
free(fp); |
|
|
|
|
} |
} |
|
|
static int |
static int |
identity_sign(Identity *id, u_char **sigp, u_int *lenp, |
identity_sign(struct identity *id, u_char **sigp, size_t *lenp, |
u_char *data, u_int datalen) |
const u_char *data, size_t datalen, u_int compat) |
{ |
{ |
Key *prv; |
Key *prv; |
int ret; |
int ret; |
|
|
/* the agent supports this key */ |
/* the agent supports this key */ |
if (id->ac) |
if (id->agent_fd) |
return (ssh_agent_sign(id->ac, id->key, sigp, lenp, |
return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp, |
data, datalen)); |
data, datalen, compat); |
|
|
/* |
/* |
* we have already loaded the private key or |
* we have already loaded the private key or |
* the private key is stored in external hardware |
* the private key is stored in external hardware |
*/ |
*/ |
if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)) |
if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)) |
return (key_sign(id->key, sigp, lenp, data, datalen)); |
return (sshkey_sign(id->key, sigp, lenp, data, datalen, |
|
compat)); |
/* load the private key from the file */ |
/* load the private key from the file */ |
if ((prv = load_identity_file(id->filename, id->userprovided)) == NULL) |
if ((prv = load_identity_file(id->filename, id->userprovided)) == NULL) |
return (-1); |
return (-1); /* XXX return decent error code */ |
ret = key_sign(prv, sigp, lenp, data, datalen); |
ret = sshkey_sign(prv, sigp, lenp, data, datalen, compat); |
key_free(prv); |
sshkey_free(prv); |
return (ret); |
return (ret); |
} |
} |
|
|
|
|
{ |
{ |
Buffer b; |
Buffer b; |
u_char *blob, *signature; |
u_char *blob, *signature; |
u_int bloblen, slen; |
u_int bloblen; |
|
size_t slen; |
u_int skip = 0; |
u_int skip = 0; |
int ret = -1; |
int ret = -1; |
int have_sig = 1; |
int have_sig = 1; |
|
|
|
|
/* generate signature */ |
/* generate signature */ |
ret = identity_sign(id, &signature, &slen, |
ret = identity_sign(id, &signature, &slen, |
buffer_ptr(&b), buffer_len(&b)); |
buffer_ptr(&b), buffer_len(&b), datafellows); |
if (ret == -1) { |
if (ret != 0) { |
free(blob); |
free(blob); |
buffer_free(&b); |
buffer_free(&b); |
return 0; |
return 0; |
|
|
{ |
{ |
Key *private; |
Key *private; |
char prompt[300], *passphrase; |
char prompt[300], *passphrase; |
int perm_ok = 0, quit, i; |
int r, perm_ok = 0, quit, i; |
struct stat st; |
struct stat st; |
|
|
if (stat(filename, &st) < 0) { |
if (stat(filename, &st) < 0) { |
|
|
filename, strerror(errno)); |
filename, strerror(errno)); |
return NULL; |
return NULL; |
} |
} |
private = key_load_private_type(KEY_UNSPEC, filename, "", NULL, &perm_ok); |
snprintf(prompt, sizeof prompt, |
if (!perm_ok) { |
"Enter passphrase for key '%.100s': ", filename); |
if (private != NULL) |
for (i = 0; i <= options.number_of_password_prompts; i++) { |
key_free(private); |
if (i == 0) |
return NULL; |
passphrase = ""; |
} |
else { |
if (private == NULL) { |
|
if (options.batch_mode) |
|
return NULL; |
|
snprintf(prompt, sizeof prompt, |
|
"Enter passphrase for key '%.100s': ", filename); |
|
for (i = 0; i < options.number_of_password_prompts; i++) { |
|
passphrase = read_passphrase(prompt, 0); |
passphrase = read_passphrase(prompt, 0); |
if (strcmp(passphrase, "") != 0) { |
if (*passphrase == '\0') { |
private = key_load_private_type(KEY_UNSPEC, |
|
filename, passphrase, NULL, NULL); |
|
quit = 0; |
|
} else { |
|
debug2("no passphrase given, try next key"); |
debug2("no passphrase given, try next key"); |
|
free(passphrase); |
|
break; |
|
} |
|
} |
|
switch ((r = sshkey_load_private_type(KEY_UNSPEC, filename, |
|
passphrase, &private, NULL, &perm_ok))) { |
|
case 0: |
|
break; |
|
case SSH_ERR_KEY_WRONG_PASSPHRASE: |
|
if (options.batch_mode) { |
quit = 1; |
quit = 1; |
|
break; |
} |
} |
|
debug2("bad passphrase given, try again..."); |
|
break; |
|
case SSH_ERR_SYSTEM_ERROR: |
|
if (errno == ENOENT) { |
|
debug2("Load key \"%s\": %s", |
|
filename, ssh_err(r)); |
|
quit = 1; |
|
break; |
|
} |
|
/* FALLTHROUGH */ |
|
default: |
|
error("Load key \"%s\": %s", filename, ssh_err(r)); |
|
quit = 1; |
|
break; |
|
} |
|
if (i > 0) { |
explicit_bzero(passphrase, strlen(passphrase)); |
explicit_bzero(passphrase, strlen(passphrase)); |
free(passphrase); |
free(passphrase); |
if (private != NULL || quit) |
|
break; |
|
debug2("bad passphrase given, try again..."); |
|
} |
} |
|
if (private != NULL || quit) |
|
break; |
} |
} |
return private; |
return private; |
} |
} |
|
|
static void |
static void |
pubkey_prepare(Authctxt *authctxt) |
pubkey_prepare(Authctxt *authctxt) |
{ |
{ |
Identity *id, *id2, *tmp; |
struct identity *id, *id2, *tmp; |
Idlist agent, files, *preferred; |
struct idlist agent, files, *preferred; |
Key *key; |
struct sshkey *key; |
AuthenticationConnection *ac; |
int agent_fd, i, r, found; |
char *comment; |
size_t j; |
int i, found; |
struct ssh_identitylist *idlist; |
|
|
TAILQ_INIT(&agent); /* keys from the agent */ |
TAILQ_INIT(&agent); /* keys from the agent */ |
TAILQ_INIT(&files); /* keys from the config file */ |
TAILQ_INIT(&files); /* keys from the config file */ |
|
|
if (id2->key == NULL || |
if (id2->key == NULL || |
(id2->key->flags & SSHKEY_FLAG_EXT) == 0) |
(id2->key->flags & SSHKEY_FLAG_EXT) == 0) |
continue; |
continue; |
if (key_equal(id->key, id2->key)) { |
if (sshkey_equal(id->key, id2->key)) { |
TAILQ_REMOVE(&files, id, next); |
TAILQ_REMOVE(&files, id, next); |
TAILQ_INSERT_TAIL(preferred, id, next); |
TAILQ_INSERT_TAIL(preferred, id, next); |
found = 1; |
found = 1; |
|
|
} |
} |
} |
} |
/* list of keys supported by the agent */ |
/* list of keys supported by the agent */ |
if ((ac = ssh_get_authentication_connection())) { |
if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { |
for (key = ssh_get_first_identity(ac, &comment, 2); |
if (r != SSH_ERR_AGENT_NOT_PRESENT) |
key != NULL; |
debug("%s: ssh_get_authentication_socket: %s", |
key = ssh_get_next_identity(ac, &comment, 2)) { |
__func__, ssh_err(r)); |
|
} else if ((r = ssh_fetch_identitylist(agent_fd, 2, &idlist)) != 0) { |
|
if (r != SSH_ERR_AGENT_NO_IDENTITIES) |
|
debug("%s: ssh_fetch_identitylist: %s", |
|
__func__, ssh_err(r)); |
|
} else { |
|
for (j = 0; j < idlist->nkeys; j++) { |
found = 0; |
found = 0; |
TAILQ_FOREACH(id, &files, next) { |
TAILQ_FOREACH(id, &files, next) { |
/* agent keys from the config file are preferred */ |
/* |
if (key_equal(key, id->key)) { |
* agent keys from the config file are |
key_free(key); |
* preferred |
free(comment); |
*/ |
|
if (sshkey_equal(idlist->keys[j], id->key)) { |
TAILQ_REMOVE(&files, id, next); |
TAILQ_REMOVE(&files, id, next); |
TAILQ_INSERT_TAIL(preferred, id, next); |
TAILQ_INSERT_TAIL(preferred, id, next); |
id->ac = ac; |
id->agent_fd = agent_fd; |
found = 1; |
found = 1; |
break; |
break; |
} |
} |
} |
} |
if (!found && !options.identities_only) { |
if (!found && !options.identities_only) { |
id = xcalloc(1, sizeof(*id)); |
id = xcalloc(1, sizeof(*id)); |
id->key = key; |
/* XXX "steals" key/comment from idlist */ |
id->filename = comment; |
id->key = idlist->keys[j]; |
id->ac = ac; |
id->filename = idlist->comments[j]; |
|
idlist->keys[j] = NULL; |
|
idlist->comments[j] = NULL; |
|
id->agent_fd = agent_fd; |
TAILQ_INSERT_TAIL(&agent, id, next); |
TAILQ_INSERT_TAIL(&agent, id, next); |
} |
} |
} |
} |
|
ssh_free_identitylist(idlist); |
/* append remaining agent keys */ |
/* append remaining agent keys */ |
for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) { |
for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) { |
TAILQ_REMOVE(&agent, id, next); |
TAILQ_REMOVE(&agent, id, next); |
TAILQ_INSERT_TAIL(preferred, id, next); |
TAILQ_INSERT_TAIL(preferred, id, next); |
} |
} |
authctxt->agent = ac; |
authctxt->agent_fd = agent_fd; |
} |
} |
/* append remaining keys from the config file */ |
/* append remaining keys from the config file */ |
for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) { |
for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) { |
|
|
{ |
{ |
Identity *id; |
Identity *id; |
|
|
if (authctxt->agent != NULL) |
if (authctxt->agent_fd != -1) |
ssh_close_authentication_connection(authctxt->agent); |
ssh_close_authentication_socket(authctxt->agent_fd); |
for (id = TAILQ_FIRST(&authctxt->keys); id; |
for (id = TAILQ_FIRST(&authctxt->keys); id; |
id = TAILQ_FIRST(&authctxt->keys)) { |
id = TAILQ_FIRST(&authctxt->keys)) { |
TAILQ_REMOVE(&authctxt->keys, id, next); |
TAILQ_REMOVE(&authctxt->keys, id, next); |
if (id->key) |
if (id->key) |
key_free(id->key); |
sshkey_free(id->key); |
free(id->filename); |
free(id->filename); |
free(id); |
free(id); |
} |
} |