aboutsummaryrefslogtreecommitdiff
path: root/ssh-agent.c
diff options
context:
space:
mode:
Diffstat (limited to 'ssh-agent.c')
-rw-r--r--ssh-agent.c653
1 files changed, 423 insertions, 230 deletions
diff --git a/ssh-agent.c b/ssh-agent.c
index e081413b8..48a47d45a 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-agent.c,v 1.257 2020/03/06 18:28:27 markus Exp $ */
+/* $OpenBSD: ssh-agent.c,v 1.278 2021/04/03 06:18:41 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -77,6 +77,7 @@
#include "xmalloc.h"
#include "ssh.h"
+#include "ssh2.h"
#include "sshbuf.h"
#include "sshkey.h"
#include "authfd.h"
@@ -92,8 +93,8 @@
#include "ssh-pkcs11.h"
#include "sk-api.h"
-#ifndef DEFAULT_PROVIDER_WHITELIST
-# define DEFAULT_PROVIDER_WHITELIST "/usr/lib*/*,/usr/local/lib*/*"
+#ifndef DEFAULT_ALLOWED_PROVIDERS
+# define DEFAULT_ALLOWED_PROVIDERS "/usr/lib*/*,/usr/local/lib*/*"
#endif
/* Maximum accepted message length */
@@ -102,12 +103,12 @@
#define AGENT_RBUF_LEN (4096)
typedef enum {
- AUTH_UNUSED,
- AUTH_SOCKET,
- AUTH_CONNECTION
+ AUTH_UNUSED = 0,
+ AUTH_SOCKET = 1,
+ AUTH_CONNECTION = 2,
} sock_type;
-typedef struct {
+typedef struct socket_entry {
int fd;
sock_type type;
struct sshbuf *input;
@@ -149,8 +150,8 @@ pid_t cleanup_pid = 0;
char socket_name[PATH_MAX];
char socket_dir[PATH_MAX];
-/* PKCS#11/Security key path whitelist */
-static char *provider_whitelist;
+/* Pattern-list of allowed PKCS#11/Security key paths */
+static char *allowed_providers;
/* locking */
#define LOCK_SIZE 32
@@ -163,19 +164,23 @@ u_char lock_salt[LOCK_SALT_SIZE];
extern char *__progname;
/* Default lifetime in seconds (0 == forever) */
-static long lifetime = 0;
+static int lifetime = 0;
static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
+/* Refuse signing of non-SSH messages for web-origin FIDO keys */
+static int restrict_websafe = 1;
+
static void
close_socket(SocketEntry *e)
{
close(e->fd);
- e->fd = -1;
- e->type = AUTH_UNUSED;
sshbuf_free(e->input);
sshbuf_free(e->output);
sshbuf_free(e->request);
+ memset(e, '\0', sizeof(*e));
+ e->fd = -1;
+ e->type = AUTH_UNUSED;
}
static void
@@ -211,15 +216,16 @@ lookup_identity(struct sshkey *key)
/* Check confirmation of keysign request */
static int
-confirm_key(Identity *id)
+confirm_key(Identity *id, const char *extra)
{
char *p;
int ret = -1;
p = sshkey_fingerprint(id->key, fingerprint_hash, SSH_FP_DEFAULT);
if (p != NULL &&
- ask_permission("Allow use of key %s?\nKey fingerprint %s.",
- id->comment, p))
+ ask_permission("Allow use of key %s?\nKey fingerprint %s.%s%s",
+ id->comment, p,
+ extra == NULL ? "" : "\n", extra == NULL ? "" : extra))
ret = 0;
free(p);
@@ -234,7 +240,7 @@ send_status(SocketEntry *e, int success)
if ((r = sshbuf_put_u32(e->output, 1)) != 0 ||
(r = sshbuf_put_u8(e->output, success ?
SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "compose");
}
/* send list of supported public keys to 'client' */
@@ -245,22 +251,23 @@ process_request_identities(SocketEntry *e)
struct sshbuf *msg;
int r;
+ debug2_f("entering");
+
if ((msg = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
+ fatal_f("sshbuf_new failed");
if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
(r = sshbuf_put_u32(msg, idtab->nentries)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "compose");
TAILQ_FOREACH(id, &idtab->idlist, next) {
- if ((r = sshkey_puts_opts(id->key, msg, SSHKEY_SERIALIZE_INFO))
- != 0 ||
+ if ((r = sshkey_puts_opts(id->key, msg,
+ SSHKEY_SERIALIZE_INFO)) != 0 ||
(r = sshbuf_put_cstring(msg, id->comment)) != 0) {
- error("%s: put key/comment: %s", __func__,
- ssh_err(r));
+ error_fr(r, "compose key/comment");
continue;
}
}
if ((r = sshbuf_put_stringb(e->output, msg)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "enqueue");
sshbuf_free(msg);
}
@@ -282,70 +289,213 @@ agent_decode_alg(struct sshkey *key, u_int flags)
return NULL;
}
+/*
+ * Attempt to parse the contents of a buffer as a SSH publickey userauth
+ * request, checking its contents for consistency and matching the embedded
+ * key against the one that is being used for signing.
+ * Note: does not modify msg buffer.
+ * Optionally extract the username and session ID from the request.
+ */
+static int
+parse_userauth_request(struct sshbuf *msg, const struct sshkey *expected_key,
+ char **userp, struct sshbuf **sess_idp)
+{
+ struct sshbuf *b = NULL, *sess_id = NULL;
+ char *user = NULL, *service = NULL, *method = NULL, *pkalg = NULL;
+ int r;
+ u_char t, sig_follows;
+ struct sshkey *mkey = NULL;
+
+ if (userp != NULL)
+ *userp = NULL;
+ if (sess_idp != NULL)
+ *sess_idp = NULL;
+ if ((b = sshbuf_fromb(msg)) == NULL)
+ fatal_f("sshbuf_fromb");
+
+ /* SSH userauth request */
+ if ((r = sshbuf_froms(b, &sess_id)) != 0)
+ goto out;
+ if (sshbuf_len(sess_id) == 0) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if ((r = sshbuf_get_u8(b, &t)) != 0 || /* SSH2_MSG_USERAUTH_REQUEST */
+ (r = sshbuf_get_cstring(b, &user, NULL)) != 0 || /* server user */
+ (r = sshbuf_get_cstring(b, &service, NULL)) != 0 || /* service */
+ (r = sshbuf_get_cstring(b, &method, NULL)) != 0 || /* method */
+ (r = sshbuf_get_u8(b, &sig_follows)) != 0 || /* sig-follows */
+ (r = sshbuf_get_cstring(b, &pkalg, NULL)) != 0 || /* alg */
+ (r = sshkey_froms(b, &mkey)) != 0) /* key */
+ goto out;
+ if (t != SSH2_MSG_USERAUTH_REQUEST ||
+ sig_follows != 1 ||
+ strcmp(service, "ssh-connection") != 0 ||
+ !sshkey_equal(expected_key, mkey) ||
+ sshkey_type_from_name(pkalg) != expected_key->type) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (strcmp(method, "publickey") != 0) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (sshbuf_len(b) != 0) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ /* success */
+ r = 0;
+ debug3_f("well formed userauth");
+ if (userp != NULL) {
+ *userp = user;
+ user = NULL;
+ }
+ if (sess_idp != NULL) {
+ *sess_idp = sess_id;
+ sess_id = NULL;
+ }
+ out:
+ sshbuf_free(b);
+ sshbuf_free(sess_id);
+ free(user);
+ free(service);
+ free(method);
+ free(pkalg);
+ sshkey_free(mkey);
+ return r;
+}
+
+/*
+ * Attempt to parse the contents of a buffer as a SSHSIG signature request.
+ * Note: does not modify buffer.
+ */
+static int
+parse_sshsig_request(struct sshbuf *msg)
+{
+ int r;
+ struct sshbuf *b;
+
+ if ((b = sshbuf_fromb(msg)) == NULL)
+ fatal_f("sshbuf_fromb");
+
+ if ((r = sshbuf_cmp(b, 0, "SSHSIG", 6)) != 0 ||
+ (r = sshbuf_consume(b, 6)) != 0 ||
+ (r = sshbuf_get_cstring(b, NULL, NULL)) != 0 || /* namespace */
+ (r = sshbuf_get_string_direct(b, NULL, NULL)) != 0 || /* reserved */
+ (r = sshbuf_get_cstring(b, NULL, NULL)) != 0 || /* hashalg */
+ (r = sshbuf_get_string_direct(b, NULL, NULL)) != 0) /* H(msg) */
+ goto out;
+ if (sshbuf_len(b) != 0) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ /* success */
+ r = 0;
+ out:
+ sshbuf_free(b);
+ return r;
+}
+
+/*
+ * This function inspects a message to be signed by a FIDO key that has a
+ * web-like application string (i.e. one that does not begin with "ssh:".
+ * It checks that the message is one of those expected for SSH operations
+ * (pubkey userauth, sshsig, CA key signing) to exclude signing challenges
+ * for the web.
+ */
+static int
+check_websafe_message_contents(struct sshkey *key, struct sshbuf *data)
+{
+ if (parse_userauth_request(data, key, NULL, NULL) == 0) {
+ debug_f("signed data matches public key userauth request");
+ return 1;
+ }
+ if (parse_sshsig_request(data) == 0) {
+ debug_f("signed data matches SSHSIG signature request");
+ return 1;
+ }
+
+ /* XXX check CA signature operation */
+
+ error("web-origin key attempting to sign non-SSH message");
+ return 0;
+}
+
/* ssh2 only */
static void
process_sign_request2(SocketEntry *e)
{
- const u_char *data;
u_char *signature = NULL;
- size_t dlen, slen = 0;
+ size_t slen = 0;
u_int compat = 0, flags;
int r, ok = -1;
char *fp = NULL;
- struct sshbuf *msg;
+ struct sshbuf *msg = NULL, *data = NULL;
struct sshkey *key = NULL;
struct identity *id;
struct notifier_ctx *notifier = NULL;
- if ((msg = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
+ debug_f("entering");
+
+ if ((msg = sshbuf_new()) == NULL || (data = sshbuf_new()) == NULL)
+ fatal_f("sshbuf_new failed");
if ((r = sshkey_froms(e->request, &key)) != 0 ||
- (r = sshbuf_get_string_direct(e->request, &data, &dlen)) != 0 ||
+ (r = sshbuf_get_stringb(e->request, data)) != 0 ||
(r = sshbuf_get_u32(e->request, &flags)) != 0) {
- error("%s: couldn't parse request: %s", __func__, ssh_err(r));
+ error_fr(r, "parse");
goto send;
}
if ((id = lookup_identity(key)) == NULL) {
- verbose("%s: %s key not found", __func__, sshkey_type(key));
+ verbose_f("%s key not found", sshkey_type(key));
goto send;
}
- if (id->confirm && confirm_key(id) != 0) {
- verbose("%s: user refused key", __func__);
+ if (id->confirm && confirm_key(id, NULL) != 0) {
+ verbose_f("user refused key");
goto send;
}
- if (sshkey_is_sk(id->key) &&
- (id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
- if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
- SSH_FP_DEFAULT)) == NULL)
- fatal("%s: fingerprint failed", __func__);
- notifier = notify_start(0,
- "Confirm user presence for key %s %s",
- sshkey_type(id->key), fp);
+ if (sshkey_is_sk(id->key)) {
+ if (strncmp(id->key->sk_application, "ssh:", 4) != 0 &&
+ !check_websafe_message_contents(key, data)) {
+ /* error already logged */
+ goto send;
+ }
+ if ((id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
+ if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
+ SSH_FP_DEFAULT)) == NULL)
+ fatal_f("fingerprint failed");
+ notifier = notify_start(0,
+ "Confirm user presence for key %s %s",
+ sshkey_type(id->key), fp);
+ }
}
+ /* XXX support PIN required FIDO keys */
if ((r = sshkey_sign(id->key, &signature, &slen,
- data, dlen, agent_decode_alg(key, flags),
- id->sk_provider, compat)) != 0) {
- error("%s: sshkey_sign: %s", __func__, ssh_err(r));
+ sshbuf_ptr(data), sshbuf_len(data), agent_decode_alg(key, flags),
+ id->sk_provider, NULL, compat)) != 0) {
+ error_fr(r, "sshkey_sign");
goto send;
}
/* Success */
ok = 0;
send:
- notify_complete(notifier);
- sshkey_free(key);
- free(fp);
+ notify_complete(notifier, "User presence confirmed");
+
if (ok == 0) {
if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 ||
(r = sshbuf_put_string(msg, signature, slen)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "compose");
} else if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "compose failure");
if ((r = sshbuf_put_stringb(e->output, msg)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "enqueue");
+ sshbuf_free(data);
sshbuf_free(msg);
+ sshkey_free(key);
+ free(fp);
free(signature);
}
@@ -357,24 +507,24 @@ process_remove_identity(SocketEntry *e)
struct sshkey *key = NULL;
Identity *id;
+ debug2_f("entering");
if ((r = sshkey_froms(e->request, &key)) != 0) {
- error("%s: get key: %s", __func__, ssh_err(r));
+ error_fr(r, "parse key");
goto done;
}
if ((id = lookup_identity(key)) == NULL) {
- debug("%s: key not found", __func__);
+ debug_f("key not found");
goto done;
}
/* We have this key, free it. */
if (idtab->nentries < 1)
- fatal("%s: internal error: nentries %d",
- __func__, idtab->nentries);
+ fatal_f("internal error: nentries %d", idtab->nentries);
TAILQ_REMOVE(&idtab->idlist, id, next);
free_identity(id);
idtab->nentries--;
- sshkey_free(key);
success = 1;
done:
+ sshkey_free(key);
send_status(e, success);
}
@@ -383,6 +533,7 @@ process_remove_all_identities(SocketEntry *e)
{
Identity *id;
+ debug2_f("entering");
/* Loop over all identities and clear the keys. */
for (id = TAILQ_FIRST(&idtab->idlist); id;
id = TAILQ_FIRST(&idtab->idlist)) {
@@ -423,125 +574,171 @@ reaper(void)
return (deadline - now);
}
-static void
-process_add_identity(SocketEntry *e)
+static int
+parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp)
{
- Identity *id;
- int success = 0, confirm = 0;
- u_int seconds = 0, maxsign;
- char *fp, *comment = NULL, *ext_name = NULL, *sk_provider = NULL;
- char canonical_provider[PATH_MAX];
- time_t death = 0;
- struct sshkey *k = NULL;
- u_char ctype;
- int r = SSH_ERR_INTERNAL_ERROR;
+ char *ext_name = NULL;
+ int r;
- if ((r = sshkey_private_deserialize(e->request, &k)) != 0 ||
- k == NULL ||
- (r = sshbuf_get_cstring(e->request, &comment, NULL)) != 0) {
- error("%s: decode private key: %s", __func__, ssh_err(r));
- goto err;
+ if ((r = sshbuf_get_cstring(m, &ext_name, NULL)) != 0) {
+ error_fr(r, "parse constraint extension");
+ goto out;
}
- while (sshbuf_len(e->request)) {
- if ((r = sshbuf_get_u8(e->request, &ctype)) != 0) {
- error("%s: buffer error: %s", __func__, ssh_err(r));
- goto err;
+ debug_f("constraint ext %s", ext_name);
+ if (strcmp(ext_name, "sk-provider@openssh.com") == 0) {
+ if (sk_providerp == NULL) {
+ error_f("%s not valid here", ext_name);
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (*sk_providerp != NULL) {
+ error_f("%s already set", ext_name);
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if ((r = sshbuf_get_cstring(m, sk_providerp, NULL)) != 0) {
+ error_fr(r, "parse %s", ext_name);
+ goto out;
+ }
+ } else {
+ error_f("unsupported constraint \"%s\"", ext_name);
+ r = SSH_ERR_FEATURE_UNSUPPORTED;
+ goto out;
+ }
+ /* success */
+ r = 0;
+ out:
+ free(ext_name);
+ return r;
+}
+
+static int
+parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp,
+ u_int *secondsp, int *confirmp, char **sk_providerp)
+{
+ u_char ctype;
+ int r;
+ u_int seconds, maxsign = 0;
+
+ while (sshbuf_len(m)) {
+ if ((r = sshbuf_get_u8(m, &ctype)) != 0) {
+ error_fr(r, "parse constraint type");
+ goto out;
}
switch (ctype) {
case SSH_AGENT_CONSTRAIN_LIFETIME:
- if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) {
- error("%s: bad lifetime constraint: %s",
- __func__, ssh_err(r));
- goto err;
+ if (*deathp != 0) {
+ error_f("lifetime already set");
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if ((r = sshbuf_get_u32(m, &seconds)) != 0) {
+ error_fr(r, "parse lifetime constraint");
+ goto out;
}
- death = monotime() + seconds;
+ *deathp = monotime() + seconds;
+ *secondsp = seconds;
break;
case SSH_AGENT_CONSTRAIN_CONFIRM:
- confirm = 1;
+ if (*confirmp != 0) {
+ error_f("confirm already set");
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ *confirmp = 1;
break;
case SSH_AGENT_CONSTRAIN_MAXSIGN:
- if ((r = sshbuf_get_u32(e->request, &maxsign)) != 0) {
- error("%s: bad maxsign constraint: %s",
- __func__, ssh_err(r));
- goto err;
+ if (k == NULL) {
+ error_f("maxsign not valid here");
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (maxsign != 0) {
+ error_f("maxsign already set");
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if ((r = sshbuf_get_u32(m, &maxsign)) != 0) {
+ error_fr(r, "parse maxsign constraint");
+ goto out;
}
if ((r = sshkey_enable_maxsign(k, maxsign)) != 0) {
- error("%s: cannot enable maxsign: %s",
- __func__, ssh_err(r));
- goto err;
+ error_fr(r, "enable maxsign");
+ goto out;
}
break;
case SSH_AGENT_CONSTRAIN_EXTENSION:
- if ((r = sshbuf_get_cstring(e->request,
- &ext_name, NULL)) != 0) {
- error("%s: cannot parse extension: %s",
- __func__, ssh_err(r));
- goto err;
- }
- debug("%s: constraint ext %s", __func__, ext_name);
- if (strcmp(ext_name, "sk-provider@openssh.com") == 0) {
- if (sk_provider != NULL) {
- error("%s already set", ext_name);
- goto err;
- }
- if ((r = sshbuf_get_cstring(e->request,
- &sk_provider, NULL)) != 0) {
- error("%s: cannot parse %s: %s",
- __func__, ext_name, ssh_err(r));
- goto err;
- }
- } else {
- error("%s: unsupported constraint \"%s\"",
- __func__, ext_name);
- goto err;
- }
- free(ext_name);
+ if ((r = parse_key_constraint_extension(m,
+ sk_providerp)) != 0)
+ goto out; /* error already logged */
break;
default:
- error("%s: Unknown constraint %d", __func__, ctype);
- err:
- free(sk_provider);
- free(ext_name);
- sshbuf_reset(e->request);
- free(comment);
- sshkey_free(k);
- goto send;
+ error_f("Unknown constraint %d", ctype);
+ r = SSH_ERR_FEATURE_UNSUPPORTED;
+ goto out;
}
}
+ /* success */
+ r = 0;
+ out:
+ return r;
+}
+
+static void
+process_add_identity(SocketEntry *e)
+{
+ Identity *id;
+ int success = 0, confirm = 0;
+ char *fp, *comment = NULL, *sk_provider = NULL;
+ char canonical_provider[PATH_MAX];
+ time_t death = 0;
+ u_int seconds = 0;
+ struct sshkey *k = NULL;
+ int r = SSH_ERR_INTERNAL_ERROR;
+
+ debug2_f("entering");
+ if ((r = sshkey_private_deserialize(e->request, &k)) != 0 ||
+ k == NULL ||
+ (r = sshbuf_get_cstring(e->request, &comment, NULL)) != 0) {
+ error_fr(r, "parse");
+ goto out;
+ }
+ if (parse_key_constraints(e->request, k, &death, &seconds, &confirm,
+ &sk_provider) != 0) {
+ error_f("failed to parse constraints");
+ sshbuf_reset(e->request);
+ goto out;
+ }
+
if (sk_provider != NULL) {
if (!sshkey_is_sk(k)) {
error("Cannot add provider: %s is not an "
"authenticator-hosted key", sshkey_type(k));
- free(sk_provider);
- goto send;
+ goto out;
}
if (strcasecmp(sk_provider, "internal") == 0) {
- debug("%s: internal provider", __func__);
+ debug_f("internal provider");
} else {
if (realpath(sk_provider, canonical_provider) == NULL) {
verbose("failed provider \"%.100s\": "
"realpath: %s", sk_provider,
strerror(errno));
- free(sk_provider);
- goto send;
+ goto out;
}
free(sk_provider);
sk_provider = xstrdup(canonical_provider);
if (match_pattern_list(sk_provider,
- provider_whitelist, 0) != 1) {
+ allowed_providers, 0) != 1) {
error("Refusing add key: "
- "provider %s not whitelisted", sk_provider);
- free(sk_provider);
- goto send;
+ "provider %s not allowed", sk_provider);
+ goto out;
}
}
}
if ((r = sshkey_shield_private(k)) != 0) {
- error("%s: shield private key: %s", __func__, ssh_err(r));
- goto err;
+ error_fr(r, "shield private");
+ goto out;
}
-
- success = 1;
if (lifetime && !death)
death = monotime() + lifetime;
if ((id = lookup_identity(k)) == NULL) {
@@ -555,6 +752,7 @@ process_add_identity(SocketEntry *e)
free(id->comment);
free(id->sk_provider);
}
+ /* success */
id->key = k;
id->comment = comment;
id->death = death;
@@ -563,12 +761,20 @@ process_add_identity(SocketEntry *e)
if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT,
SSH_FP_DEFAULT)) == NULL)
- fatal("%s: sshkey_fingerprint failed", __func__);
- debug("%s: add %s %s \"%.100s\" (life: %u) (confirm: %u) "
- "(provider: %s)", __func__, sshkey_ssh_name(k), fp, comment,
- seconds, confirm, sk_provider == NULL ? "none" : sk_provider);
+ fatal_f("sshkey_fingerprint failed");
+ debug_f("add %s %s \"%.100s\" (life: %u) (confirm: %u) "
+ "(provider: %s)", sshkey_ssh_name(k), fp, comment, seconds,
+ confirm, sk_provider == NULL ? "none" : sk_provider);
free(fp);
-send:
+ /* transferred */
+ k = NULL;
+ comment = NULL;
+ sk_provider = NULL;
+ success = 1;
+ out:
+ free(sk_provider);
+ free(comment);
+ sshkey_free(k);
send_status(e, success);
}
@@ -582,13 +788,14 @@ process_lock_agent(SocketEntry *e, int lock)
static u_int fail_count = 0;
size_t pwlen;
+ debug2_f("entering");
/*
* This is deliberately fatal: the user has requested that we lock,
* but we can't parse their request properly. The only safe thing to
* do is abort.
*/
if ((r = sshbuf_get_cstring(e->request, &passwd, &pwlen)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "parse");
if (pwlen == 0) {
debug("empty password not supported");
} else if (locked && !lock) {
@@ -631,11 +838,11 @@ no_identities(SocketEntry *e)
int r;
if ((msg = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
+ fatal_f("sshbuf_new failed");
if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
(r = sshbuf_put_u32(msg, 0)) != 0 ||
(r = sshbuf_put_stringb(e->output, msg)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "compose");
sshbuf_free(msg);
}
@@ -646,51 +853,33 @@ process_add_smartcard_key(SocketEntry *e)
char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
char **comments = NULL;
int r, i, count = 0, success = 0, confirm = 0;
- u_int seconds;
+ u_int seconds = 0;
time_t death = 0;
- u_char type;
struct sshkey **keys = NULL, *k;
Identity *id;
+ debug2_f("entering");
if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
(r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) {
- error("%s: buffer error: %s", __func__, ssh_err(r));
+ error_fr(r, "parse");
goto send;
}
-
- while (sshbuf_len(e->request)) {
- if ((r = sshbuf_get_u8(e->request, &type)) != 0) {
- error("%s: buffer error: %s", __func__, ssh_err(r));
- goto send;
- }
- switch (type) {
- case SSH_AGENT_CONSTRAIN_LIFETIME:
- if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) {
- error("%s: buffer error: %s",
- __func__, ssh_err(r));
- goto send;
- }
- death = monotime() + seconds;
- break;
- case SSH_AGENT_CONSTRAIN_CONFIRM:
- confirm = 1;
- break;
- default:
- error("%s: Unknown constraint type %d", __func__, type);
- goto send;
- }
+ if (parse_key_constraints(e->request, NULL, &death, &seconds, &confirm,
+ NULL) != 0) {
+ error_f("failed to parse constraints");
+ goto send;
}
if (realpath(provider, canonical_provider) == NULL) {
verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
provider, strerror(errno));
goto send;
}
- if (match_pattern_list(canonical_provider, provider_whitelist, 0) != 1) {
+ if (match_pattern_list(canonical_provider, allowed_providers, 0) != 1) {
verbose("refusing PKCS#11 add of \"%.100s\": "
- "provider not whitelisted", canonical_provider);
+ "provider not allowed", canonical_provider);
goto send;
}
- debug("%s: add %.100s", __func__, canonical_provider);
+ debug_f("add %.100s", canonical_provider);
if (lifetime && !death)
death = monotime() + lifetime;
@@ -714,6 +903,7 @@ process_add_smartcard_key(SocketEntry *e)
idtab->nentries++;
success = 1;
}
+ /* XXX update constraints for existing keys */
sshkey_free(keys[i]);
free(comments[i]);
}
@@ -732,9 +922,10 @@ process_remove_smartcard_key(SocketEntry *e)
int r, success = 0;
Identity *id, *nxt;
+ debug2_f("entering");
if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
(r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) {
- error("%s: buffer error: %s", __func__, ssh_err(r));
+ error_fr(r, "parse");
goto send;
}
free(pin);
@@ -745,7 +936,7 @@ process_remove_smartcard_key(SocketEntry *e)
goto send;
}
- debug("%s: remove %.100s", __func__, canonical_provider);
+ debug_f("remove %.100s", canonical_provider);
for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) {
nxt = TAILQ_NEXT(id, next);
/* Skip file--based keys */
@@ -760,15 +951,17 @@ process_remove_smartcard_key(SocketEntry *e)
if (pkcs11_del_provider(canonical_provider) == 0)
success = 1;
else
- error("%s: pkcs11_del_provider failed", __func__);
+ error_f("pkcs11_del_provider failed");
send:
free(provider);
send_status(e, success);
}
#endif /* ENABLE_PKCS11 */
-/* dispatch incoming messages */
-
+/*
+ * dispatch incoming message.
+ * returns 1 on success, 0 for incomplete messages or -1 on error.
+ */
static int
process_message(u_int socknum)
{
@@ -778,10 +971,8 @@ process_message(u_int socknum)
int r;
SocketEntry *e;
- if (socknum >= sockets_alloc) {
- fatal("%s: socket number %u >= allocated %u",
- __func__, socknum, sockets_alloc);
- }
+ if (socknum >= sockets_alloc)
+ fatal_f("sock %u >= allocated %u", socknum, sockets_alloc);
e = &sockets[socknum];
if (sshbuf_len(e->input) < 5)
@@ -789,8 +980,8 @@ process_message(u_int socknum)
cp = sshbuf_ptr(e->input);
msg_len = PEEK_U32(cp);
if (msg_len > AGENT_MAX_LEN) {
- debug("%s: socket %u (fd=%d) message too long %u > %u",
- __func__, socknum, e->fd, msg_len, AGENT_MAX_LEN);
+ debug_f("socket %u (fd=%d) message too long %u > %u",
+ socknum, e->fd, msg_len, AGENT_MAX_LEN);
return -1;
}
if (sshbuf_len(e->input) < msg_len + 4)
@@ -802,13 +993,13 @@ process_message(u_int socknum)
(r = sshbuf_get_u8(e->request, &type)) != 0) {
if (r == SSH_ERR_MESSAGE_INCOMPLETE ||
r == SSH_ERR_STRING_TOO_LARGE) {
- debug("%s: buffer error: %s", __func__, ssh_err(r));
+ error_fr(r, "parse");
return -1;
}
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "parse");
}
- debug("%s: socket %u (fd=%d) type %d", __func__, socknum, e->fd, type);
+ debug_f("socket %u (fd=%d) type %d", socknum, e->fd, type);
/* check whether agent is locked */
if (locked && type != SSH_AGENTC_UNLOCK) {
@@ -822,7 +1013,7 @@ process_message(u_int socknum)
/* send a fail message for all other request types */
send_status(e, 0);
}
- return 0;
+ return 1;
}
switch (type) {
@@ -866,7 +1057,7 @@ process_message(u_int socknum)
send_status(e, 0);
break;
}
- return 0;
+ return 1;
}
static void
@@ -874,6 +1065,8 @@ new_socket(sock_type type, int fd)
{
u_int i, old_alloc, new_alloc;
+ debug_f("type = %s", type == AUTH_CONNECTION ? "CONNECTION" :
+ (type == AUTH_SOCKET ? "SOCKET" : "UNKNOWN"));
set_nonblock(fd);
if (fd > max_fd)
@@ -882,28 +1075,25 @@ new_socket(sock_type type, int fd)
for (i = 0; i < sockets_alloc; i++)
if (sockets[i].type == AUTH_UNUSED) {
sockets[i].fd = fd;
- if ((sockets[i].input = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- if ((sockets[i].output = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- if ((sockets[i].request = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
+ if ((sockets[i].input = sshbuf_new()) == NULL ||
+ (sockets[i].output = sshbuf_new()) == NULL ||
+ (sockets[i].request = sshbuf_new()) == NULL)
+ fatal_f("sshbuf_new failed");
sockets[i].type = type;
return;
}
old_alloc = sockets_alloc;
new_alloc = sockets_alloc + 10;
- sockets = xreallocarray(sockets, new_alloc, sizeof(sockets[0]));
+ sockets = xrecallocarray(sockets, old_alloc, new_alloc,
+ sizeof(sockets[0]));
for (i = old_alloc; i < new_alloc; i++)
sockets[i].type = AUTH_UNUSED;
sockets_alloc = new_alloc;
sockets[old_alloc].fd = fd;
- if ((sockets[old_alloc].input = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- if ((sockets[old_alloc].output = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- if ((sockets[old_alloc].request = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
+ if ((sockets[old_alloc].input = sshbuf_new()) == NULL ||
+ (sockets[old_alloc].output = sshbuf_new()) == NULL ||
+ (sockets[old_alloc].request = sshbuf_new()) == NULL)
+ fatal_f("sshbuf_new failed");
sockets[old_alloc].type = type;
}
@@ -948,16 +1138,20 @@ handle_conn_read(u_int socknum)
if (len == -1) {
if (errno == EAGAIN || errno == EINTR)
return 0;
- error("%s: read error on socket %u (fd %d): %s",
- __func__, socknum, sockets[socknum].fd,
- strerror(errno));
+ error_f("read error on socket %u (fd %d): %s",
+ socknum, sockets[socknum].fd, strerror(errno));
}
return -1;
}
if ((r = sshbuf_put(sockets[socknum].input, buf, len)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "compose");
explicit_bzero(buf, sizeof(buf));
- process_message(socknum);
+ for (;;) {
+ if ((r = process_message(socknum)) == -1)
+ return -1;
+ else if (r == 0)
+ break;
+ }
return 0;
}
@@ -975,14 +1169,13 @@ handle_conn_write(u_int socknum)
if (len == -1) {
if (errno == EAGAIN || errno == EINTR)
return 0;
- error("%s: read error on socket %u (fd %d): %s",
- __func__, socknum, sockets[socknum].fd,
- strerror(errno));
+ error_f("read error on socket %u (fd %d): %s",
+ socknum, sockets[socknum].fd, strerror(errno));
}
return -1;
}
if ((r = sshbuf_consume(sockets[socknum].output, len)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "consume");
return 0;
}
@@ -1004,7 +1197,7 @@ after_poll(struct pollfd *pfd, size_t npfd, u_int maxfds)
break;
}
if (socknum >= sockets_alloc) {
- error("%s: no socket for fd %d", __func__, pfd[i].fd);
+ error_f("no socket for fd %d", pfd[i].fd);
continue;
}
/* Process events */
@@ -1065,7 +1258,7 @@ prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds)
}
if (npfd != *npfdp &&
(pfd = recallocarray(pfd, *npfdp, npfd, sizeof(*pfd))) == NULL)
- fatal("%s: recallocarray failed", __func__);
+ fatal_f("recallocarray failed");
*pfdp = pfd;
*npfdp = npfd;
@@ -1092,12 +1285,10 @@ prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds)
if ((r = sshbuf_check_reserve(sockets[i].input,
AGENT_RBUF_LEN)) == 0 &&
(r = sshbuf_check_reserve(sockets[i].output,
- AGENT_MAX_LEN)) == 0)
+ AGENT_MAX_LEN)) == 0)
pfd[j].events = POLLIN;
- else if (r != SSH_ERR_NO_BUFFER_SPACE) {
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
- }
+ else if (r != SSH_ERR_NO_BUFFER_SPACE)
+ fatal_fr(r, "reserve");
if (sshbuf_len(sockets[i].output) > 0)
pfd[j].events |= POLLOUT;
j++;
@@ -1126,7 +1317,7 @@ cleanup_socket(void)
{
if (cleanup_pid != 0 && getpid() != cleanup_pid)
return;
- debug("%s: cleanup", __func__);
+ debug_f("cleanup");
if (socket_name[0])
unlink(socket_name);
if (socket_dir[0])
@@ -1170,7 +1361,9 @@ usage(void)
{
fprintf(stderr,
"usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n"
- " [-P provider_whitelist] [-t life] [command [arg ...]]\n"
+ " [-P allowed_providers] [-t life]\n"
+ " ssh-agent [-a bind_address] [-E fingerprint_hash] [-P allowed_providers]\n"
+ " [-t life] command [arg ...]\n"
" ssh-agent [-c | -s] -k\n");
exit(1);
}
@@ -1179,7 +1372,7 @@ int
main(int ac, char **av)
{
int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0;
- int sock, fd, ch, result, saved_errno;
+ int sock, ch, result, saved_errno;
char *shell, *format, *pidstr, *agentsocket = NULL;
#ifdef HAVE_SETRLIMIT
struct rlimit rlim;
@@ -1212,7 +1405,7 @@ main(int ac, char **av)
__progname = ssh_get_progname(av[0]);
seed_rng();
- while ((ch = getopt(ac, av, "cDdksE:a:P:t:")) != -1) {
+ while ((ch = getopt(ac, av, "cDdksE:a:O:P:t:")) != -1) {
switch (ch) {
case 'E':
fingerprint_hash = ssh_digest_alg_by_name(optarg);
@@ -1227,10 +1420,16 @@ main(int ac, char **av)
case 'k':
k_flag++;
break;
+ case 'O':
+ if (strcmp(optarg, "no-restrict-websafe") == 0)
+ restrict_websafe = 0;
+ else
+ fatal("Unknown -O option");
+ break;
case 'P':
- if (provider_whitelist != NULL)
+ if (allowed_providers != NULL)
fatal("-P option already specified");
- provider_whitelist = xstrdup(optarg);
+ allowed_providers = xstrdup(optarg);
break;
case 's':
if (c_flag)
@@ -1266,8 +1465,8 @@ main(int ac, char **av)
if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag))
usage();
- if (provider_whitelist == NULL)
- provider_whitelist = xstrdup(DEFAULT_PROVIDER_WHITELIST);
+ if (allowed_providers == NULL)
+ allowed_providers = xstrdup(DEFAULT_ALLOWED_PROVIDERS);
if (ac == 0 && !c_flag && !s_flag) {
shell = getenv("SHELL");
@@ -1393,14 +1592,8 @@ main(int ac, char **av)
}
(void)chdir("/");
- if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
- /* XXX might close listen socket */
- (void)dup2(fd, STDIN_FILENO);
- (void)dup2(fd, STDOUT_FILENO);
- (void)dup2(fd, STDERR_FILENO);
- if (fd > 2)
- close(fd);
- }
+ if (stdfd_devnull(1, 1, 1) == -1)
+ error_f("stdfd_devnull failed");
#ifdef HAVE_SETRLIMIT
/* deny core dumps, since memory contains unencrypted private keys */