]> andersk Git - openssh.git/blobdiff - ssh-agent.c
- djm@cvs.openbsd.org 2010/01/30 02:54:53
[openssh.git] / ssh-agent.c
index 3b5d06c5117ef9cb71abe29f558787eadd87f47f..df3a87d9a53021252a4856f0834ab05438805e04 100644 (file)
@@ -1,5 +1,4 @@
-/*     $OpenBSD: ssh-agent.c,v 1.81 2002/02/05 15:50:12 stevesk Exp $  */
-
+/* $OpenBSD: ssh-agent.c,v 1.162 2009/09/01 14:43:17 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: ssh-agent.c,v 1.81 2002/02/05 15:50:12 stevesk Exp $");
 
-#if defined(HAVE_SYS_QUEUE_H) && !defined(HAVE_BOGUS_SYS_QUEUE_H)
-#include <sys/queue.h>
-#else
-#include "openbsd-compat/fake-queue.h"
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
 #endif
-
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+#include "openbsd-compat/sys-queue.h"
 
 #include <openssl/evp.h>
 #include <openssl/md5.h>
+#include "openbsd-compat/openssl-compat.h"
 
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "xmalloc.h"
 #include "ssh.h"
 #include "rsa.h"
 #include "buffer.h"
-#include "bufaux.h"
-#include "xmalloc.h"
-#include "getput.h"
 #include "key.h"
 #include "authfd.h"
 #include "compat.h"
 #include "log.h"
+#include "misc.h"
 
 #ifdef SMARTCARD
-#include <openssl/engine.h>
 #include "scard.h"
 #endif
 
+#if defined(HAVE_SYS_PRCTL_H)
+#include <sys/prctl.h> /* For prctl() and PR_SET_DUMPABLE */
+#endif
+
 typedef enum {
        AUTH_UNUSED,
        AUTH_SOCKET,
@@ -75,6 +95,7 @@ typedef struct {
        sock_type type;
        Buffer input;
        Buffer output;
+       Buffer request;
 } SocketEntry;
 
 u_int sockets_alloc = 0;
@@ -84,6 +105,8 @@ typedef struct identity {
        TAILQ_ENTRY(identity) next;
        Key *key;
        char *comment;
+       u_int death;
+       u_int confirm;
 } Identity;
 
 typedef struct {
@@ -98,21 +121,37 @@ int max_fd = 0;
 
 /* pid of shell == parent of agent */
 pid_t parent_pid = -1;
+u_int parent_alive_interval = 0;
 
 /* pathname and directory for AUTH_SOCKET */
-char socket_name[1024];
-char socket_dir[1024];
+char socket_name[MAXPATHLEN];
+char socket_dir[MAXPATHLEN];
+
+/* locking */
+int locked = 0;
+char *lock_passwd = NULL;
 
-#ifdef HAVE___PROGNAME
 extern char *__progname;
-#else
-char *__progname;
-#endif
+
+/* Default lifetime (0 == forever) */
+static int lifetime = 0;
+
+static void
+close_socket(SocketEntry *e)
+{
+       close(e->fd);
+       e->fd = -1;
+       e->type = AUTH_UNUSED;
+       buffer_free(&e->input);
+       buffer_free(&e->output);
+       buffer_free(&e->request);
+}
 
 static void
 idtab_init(void)
 {
        int i;
+
        for (i = 0; i <=2; i++) {
                TAILQ_INIT(&idtable[i].idlist);
                idtable[i].nentries = 0;
@@ -128,6 +167,14 @@ idtab_lookup(int version)
        return &idtable[version];
 }
 
+static void
+free_identity(Identity *id)
+{
+       key_free(id->key);
+       xfree(id->comment);
+       xfree(id);
+}
+
 /* return matching private key for given public key */
 static Identity *
 lookup_identity(Key *key, int version)
@@ -142,12 +189,20 @@ lookup_identity(Key *key, int version)
        return (NULL);
 }
 
-static void
-free_identity(Identity *id)
+/* Check confirmation of keysign request */
+static int
+confirm_key(Identity *id)
 {
-       key_free(id->key);
-       xfree(id->comment);
-       xfree(id);
+       char *p;
+       int ret = -1;
+
+       p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX);
+       if (ask_permission("Allow use of key %s?\nKey fingerprint %s.",
+           id->comment, p))
+               ret = 0;
+       xfree(p);
+
+       return (ret);
 }
 
 /* send list of supported public keys to 'client' */
@@ -155,8 +210,8 @@ static void
 process_request_identities(SocketEntry *e, int version)
 {
        Idtab *tab = idtab_lookup(version);
-       Buffer msg;
        Identity *id;
+       Buffer msg;
 
        buffer_init(&msg);
        buffer_put_char(&msg, (version == 1) ?
@@ -185,35 +240,35 @@ process_request_identities(SocketEntry *e, int version)
 static void
 process_authentication_challenge1(SocketEntry *e)
 {
-       Identity *id;
-       Key *key;
+       u_char buf[32], mdbuf[16], session_id[16];
+       u_int response_type;
        BIGNUM *challenge;
+       Identity *id;
        int i, len;
        Buffer msg;
        MD5_CTX md;
-       u_char buf[32], mdbuf[16], session_id[16];
-       u_int response_type;
+       Key *key;
 
        buffer_init(&msg);
        key = key_new(KEY_RSA1);
        if ((challenge = BN_new()) == NULL)
                fatal("process_authentication_challenge1: BN_new failed");
 
-       buffer_get_int(&e->input);                              /* ignored */
-       buffer_get_bignum(&e->input, key->rsa->e);
-       buffer_get_bignum(&e->input, key->rsa->n);
-       buffer_get_bignum(&e->input, challenge);
+       (void) buffer_get_int(&e->request);                     /* ignored */
+       buffer_get_bignum(&e->request, key->rsa->e);
+       buffer_get_bignum(&e->request, key->rsa->n);
+       buffer_get_bignum(&e->request, challenge);
 
        /* Only protocol 1.1 is supported */
-       if (buffer_len(&e->input) == 0)
+       if (buffer_len(&e->request) == 0)
                goto failure;
-       buffer_get(&e->input, session_id, 16);
-       response_type = buffer_get_int(&e->input);
+       buffer_get(&e->request, session_id, 16);
+       response_type = buffer_get_int(&e->request);
        if (response_type != 1)
                goto failure;
 
        id = lookup_identity(key, 1);
-       if (id != NULL) {
+       if (id != NULL && (!id->confirm || confirm_key(id) == 0)) {
                Key *private = id->key;
                /* Decrypt the challenge using the private key. */
                if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0)
@@ -222,7 +277,7 @@ process_authentication_challenge1(SocketEntry *e)
                /* The response is MD5 of decrypted challenge plus session id. */
                len = BN_num_bytes(challenge);
                if (len <= 0 || len > 32) {
-                       log("process_authentication_challenge: bad challenge length %d", len);
+                       logit("process_authentication_challenge: bad challenge length %d", len);
                        goto failure;
                }
                memset(buf, 0, 32);
@@ -254,30 +309,31 @@ send:
 static void
 process_sign_request2(SocketEntry *e)
 {
-       extern int datafellows;
-       Key *key;
        u_char *blob, *data, *signature = NULL;
        u_int blen, dlen, slen = 0;
-       int flags;
+       extern int datafellows;
+       int odatafellows;
+       int ok = -1, flags;
        Buffer msg;
-       int ok = -1;
+       Key *key;
 
        datafellows = 0;
 
-       blob = buffer_get_string(&e->input, &blen);
-       data = buffer_get_string(&e->input, &dlen);
+       blob = buffer_get_string(&e->request, &blen);
+       data = buffer_get_string(&e->request, &dlen);
 
-       flags = buffer_get_int(&e->input);
+       flags = buffer_get_int(&e->request);
+       odatafellows = datafellows;
        if (flags & SSH_AGENT_OLD_SIGNATURE)
                datafellows = SSH_BUG_SIGBLOB;
 
        key = key_from_blob(blob, blen);
        if (key != NULL) {
                Identity *id = lookup_identity(key, 2);
-               if (id != NULL)
+               if (id != NULL && (!id->confirm || confirm_key(id) == 0))
                        ok = key_sign(id->key, &signature, &slen, data, dlen);
+               key_free(key);
        }
-       key_free(key);
        buffer_init(&msg);
        if (ok == 0) {
                buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE);
@@ -293,31 +349,31 @@ process_sign_request2(SocketEntry *e)
        xfree(blob);
        if (signature != NULL)
                xfree(signature);
+       datafellows = odatafellows;
 }
 
 /* shared */
 static void
 process_remove_identity(SocketEntry *e, int version)
 {
+       u_int blen, bits;
+       int success = 0;
        Key *key = NULL;
        u_char *blob;
-       u_int blen;
-       u_int bits;
-       int success = 0;
 
        switch (version) {
        case 1:
                key = key_new(KEY_RSA1);
-               bits = buffer_get_int(&e->input);
-               buffer_get_bignum(&e->input, key->rsa->e);
-               buffer_get_bignum(&e->input, key->rsa->n);
+               bits = buffer_get_int(&e->request);
+               buffer_get_bignum(&e->request, key->rsa->e);
+               buffer_get_bignum(&e->request, key->rsa->n);
 
                if (bits != key_size(key))
-                       log("Warning: identity keysize mismatch: actual %d, announced %d",
+                       logit("Warning: identity keysize mismatch: actual %u, announced %u",
                            key_size(key), bits);
                break;
        case 2:
-               blob = buffer_get_string(&e->input, &blen);
+               blob = buffer_get_string(&e->request, &blen);
                key = key_from_blob(blob, blen);
                xfree(blob);
                break;
@@ -327,7 +383,7 @@ process_remove_identity(SocketEntry *e, int version)
                if (id != NULL) {
                        /*
                         * We have this key.  Free the old key.  Since we
-                        * don\'t want to leave empty slots in the middle of
+                        * don't want to leave empty slots in the middle of
                         * the array, we actually free the key there and move
                         * all the entries between the empty slot and the end
                         * of the array.
@@ -368,135 +424,250 @@ process_remove_all_identities(SocketEntry *e, int version)
        /* Send success. */
        buffer_put_int(&e->output, 1);
        buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
-       return;
+}
+
+/* removes expired keys and returns number of seconds until the next expiry */
+static u_int
+reaper(void)
+{
+       u_int deadline = 0, now = time(NULL);
+       Identity *id, *nxt;
+       int version;
+       Idtab *tab;
+
+       for (version = 1; version < 3; version++) {
+               tab = idtab_lookup(version);
+               for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) {
+                       nxt = TAILQ_NEXT(id, next);
+                       if (id->death == 0)
+                               continue;
+                       if (now >= id->death) {
+                               debug("expiring key '%s'", id->comment);
+                               TAILQ_REMOVE(&tab->idlist, id, next);
+                               free_identity(id);
+                               tab->nentries--;
+                       } else
+                               deadline = (deadline == 0) ? id->death :
+                                   MIN(deadline, id->death);
+               }
+       }
+       if (deadline == 0 || deadline <= now)
+               return 0;
+       else
+               return (deadline - now);
 }
 
 static void
 process_add_identity(SocketEntry *e, int version)
 {
-       Key *k = NULL;
-       char *type_name;
-       char *comment;
-       int type, success = 0;
        Idtab *tab = idtab_lookup(version);
+       Identity *id;
+       int type, success = 0, death = 0, confirm = 0;
+       char *type_name, *comment;
+       Key *k = NULL;
 
        switch (version) {
        case 1:
                k = key_new_private(KEY_RSA1);
-               buffer_get_int(&e->input);                      /* ignored */
-               buffer_get_bignum(&e->input, k->rsa->n);
-               buffer_get_bignum(&e->input, k->rsa->e);
-               buffer_get_bignum(&e->input, k->rsa->d);
-               buffer_get_bignum(&e->input, k->rsa->iqmp);
+               (void) buffer_get_int(&e->request);             /* ignored */
+               buffer_get_bignum(&e->request, k->rsa->n);
+               buffer_get_bignum(&e->request, k->rsa->e);
+               buffer_get_bignum(&e->request, k->rsa->d);
+               buffer_get_bignum(&e->request, k->rsa->iqmp);
 
                /* SSH and SSL have p and q swapped */
-               buffer_get_bignum(&e->input, k->rsa->q);        /* p */
-               buffer_get_bignum(&e->input, k->rsa->p);        /* q */
+               buffer_get_bignum(&e->request, k->rsa->q);      /* p */
+               buffer_get_bignum(&e->request, k->rsa->p);      /* q */
 
                /* Generate additional parameters */
                rsa_generate_additional_parameters(k->rsa);
                break;
        case 2:
-               type_name = buffer_get_string(&e->input, NULL);
+               type_name = buffer_get_string(&e->request, NULL);
                type = key_type_from_name(type_name);
                xfree(type_name);
                switch (type) {
                case KEY_DSA:
                        k = key_new_private(type);
-                       buffer_get_bignum2(&e->input, k->dsa->p);
-                       buffer_get_bignum2(&e->input, k->dsa->q);
-                       buffer_get_bignum2(&e->input, k->dsa->g);
-                       buffer_get_bignum2(&e->input, k->dsa->pub_key);
-                       buffer_get_bignum2(&e->input, k->dsa->priv_key);
+                       buffer_get_bignum2(&e->request, k->dsa->p);
+                       buffer_get_bignum2(&e->request, k->dsa->q);
+                       buffer_get_bignum2(&e->request, k->dsa->g);
+                       buffer_get_bignum2(&e->request, k->dsa->pub_key);
+                       buffer_get_bignum2(&e->request, k->dsa->priv_key);
                        break;
                case KEY_RSA:
                        k = key_new_private(type);
-                       buffer_get_bignum2(&e->input, k->rsa->n);
-                       buffer_get_bignum2(&e->input, k->rsa->e);
-                       buffer_get_bignum2(&e->input, k->rsa->d);
-                       buffer_get_bignum2(&e->input, k->rsa->iqmp);
-                       buffer_get_bignum2(&e->input, k->rsa->p);
-                       buffer_get_bignum2(&e->input, k->rsa->q);
+                       buffer_get_bignum2(&e->request, k->rsa->n);
+                       buffer_get_bignum2(&e->request, k->rsa->e);
+                       buffer_get_bignum2(&e->request, k->rsa->d);
+                       buffer_get_bignum2(&e->request, k->rsa->iqmp);
+                       buffer_get_bignum2(&e->request, k->rsa->p);
+                       buffer_get_bignum2(&e->request, k->rsa->q);
 
                        /* Generate additional parameters */
                        rsa_generate_additional_parameters(k->rsa);
                        break;
                default:
-                       buffer_clear(&e->input);
+                       buffer_clear(&e->request);
+                       goto send;
+               }
+               break;
+       }
+       /* enable blinding */
+       switch (k->type) {
+       case KEY_RSA:
+       case KEY_RSA1:
+               if (RSA_blinding_on(k->rsa, NULL) != 1) {
+                       error("process_add_identity: RSA_blinding_on failed");
+                       key_free(k);
                        goto send;
                }
                break;
        }
-       comment = buffer_get_string(&e->input, NULL);
+       comment = buffer_get_string(&e->request, NULL);
        if (k == NULL) {
                xfree(comment);
                goto send;
        }
+       while (buffer_len(&e->request)) {
+               switch ((type = buffer_get_char(&e->request))) {
+               case SSH_AGENT_CONSTRAIN_LIFETIME:
+                       death = time(NULL) + buffer_get_int(&e->request);
+                       break;
+               case SSH_AGENT_CONSTRAIN_CONFIRM:
+                       confirm = 1;
+                       break;
+               default:
+                       error("process_add_identity: "
+                           "Unknown constraint type %d", type);
+                       xfree(comment);
+                       key_free(k);
+                       goto send;
+               }
+       }
        success = 1;
-       if (lookup_identity(k, version) == NULL) {
-               Identity *id = xmalloc(sizeof(Identity));
+       if (lifetime && !death)
+               death = time(NULL) + lifetime;
+       if ((id = lookup_identity(k, version)) == NULL) {
+               id = xmalloc(sizeof(Identity));
                id->key = k;
-               id->comment = comment;
                TAILQ_INSERT_TAIL(&tab->idlist, id, next);
                /* Increment the number of identities. */
                tab->nentries++;
        } else {
                key_free(k);
-               xfree(comment);
+               xfree(id->comment);
        }
+       id->comment = comment;
+       id->death = death;
+       id->confirm = confirm;
 send:
        buffer_put_int(&e->output, 1);
        buffer_put_char(&e->output,
            success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
 }
 
+/* XXX todo: encrypt sensitive data with passphrase */
+static void
+process_lock_agent(SocketEntry *e, int lock)
+{
+       int success = 0;
+       char *passwd;
+
+       passwd = buffer_get_string(&e->request, NULL);
+       if (locked && !lock && strcmp(passwd, lock_passwd) == 0) {
+               locked = 0;
+               memset(lock_passwd, 0, strlen(lock_passwd));
+               xfree(lock_passwd);
+               lock_passwd = NULL;
+               success = 1;
+       } else if (!locked && lock) {
+               locked = 1;
+               lock_passwd = xstrdup(passwd);
+               success = 1;
+       }
+       memset(passwd, 0, strlen(passwd));
+       xfree(passwd);
+
+       buffer_put_int(&e->output, 1);
+       buffer_put_char(&e->output,
+           success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
+}
+
+static void
+no_identities(SocketEntry *e, u_int type)
+{
+       Buffer msg;
+
+       buffer_init(&msg);
+       buffer_put_char(&msg,
+           (type == SSH_AGENTC_REQUEST_RSA_IDENTITIES) ?
+           SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER);
+       buffer_put_int(&msg, 0);
+       buffer_put_int(&e->output, buffer_len(&msg));
+       buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
+       buffer_free(&msg);
+}
 
 #ifdef SMARTCARD
 static void
-process_add_smartcard_key (SocketEntry *e)
+process_add_smartcard_key(SocketEntry *e)
 {
+       char *sc_reader_id = NULL, *pin;
+       int i, type, version, success = 0, death = 0, confirm = 0;
+       Key **keys, *k;
+       Identity *id;
        Idtab *tab;
-       Key *n = NULL, *k = NULL;
-       char *sc_reader_id = NULL;
-       int success = 0;
 
-       sc_reader_id = buffer_get_string(&e->input, NULL);
-       k = sc_get_key(sc_reader_id);
-       xfree(sc_reader_id);
+       sc_reader_id = buffer_get_string(&e->request, NULL);
+       pin = buffer_get_string(&e->request, NULL);
 
-       if (k == NULL) {
-               error("sc_get_pubkey failed");
-               goto send;
+       while (buffer_len(&e->request)) {
+               switch ((type = buffer_get_char(&e->request))) {
+               case SSH_AGENT_CONSTRAIN_LIFETIME:
+                       death = time(NULL) + buffer_get_int(&e->request);
+                       break;
+               case SSH_AGENT_CONSTRAIN_CONFIRM:
+                       confirm = 1;
+                       break;
+               default:
+                       error("process_add_smartcard_key: "
+                           "Unknown constraint type %d", type);
+                       xfree(sc_reader_id);
+                       xfree(pin);
+                       goto send;
+               }
        }
-       success = 1;
+       if (lifetime && !death)
+               death = time(NULL) + lifetime;
 
-       tab = idtab_lookup(1);
-       k->type = KEY_RSA1;
-       if (lookup_identity(k, 1) == NULL) {
-               Identity *id = xmalloc(sizeof(Identity));
-               n = key_new(KEY_RSA1);
-               BN_copy(n->rsa->n, k->rsa->n);
-               BN_copy(n->rsa->e, k->rsa->e);
-               RSA_set_method(n->rsa, sc_get_engine());
-               id->key = n;
-               id->comment = xstrdup("rsa1 smartcard");
-               TAILQ_INSERT_TAIL(&tab->idlist, id, next);
-               tab->nentries++;
+       keys = sc_get_keys(sc_reader_id, pin);
+       xfree(sc_reader_id);
+       xfree(pin);
+
+       if (keys == NULL || keys[0] == NULL) {
+               error("sc_get_keys failed");
+               goto send;
        }
-       k->type = KEY_RSA;
-       tab = idtab_lookup(2);
-       if (lookup_identity(k, 2) == NULL) {
-               Identity *id = xmalloc(sizeof(Identity));
-               n = key_new(KEY_RSA);
-               BN_copy(n->rsa->n, k->rsa->n);
-               BN_copy(n->rsa->e, k->rsa->e);
-               RSA_set_method(n->rsa, sc_get_engine());
-               id->key = n;
-               id->comment = xstrdup("rsa smartcard");
-               TAILQ_INSERT_TAIL(&tab->idlist, id, next);
-               tab->nentries++;
+       for (i = 0; keys[i] != NULL; i++) {
+               k = keys[i];
+               version = k->type == KEY_RSA1 ? 1 : 2;
+               tab = idtab_lookup(version);
+               if (lookup_identity(k, version) == NULL) {
+                       id = xmalloc(sizeof(Identity));
+                       id->key = k;
+                       id->comment = sc_get_key_label(k);
+                       id->death = death;
+                       id->confirm = confirm;
+                       TAILQ_INSERT_TAIL(&tab->idlist, id, next);
+                       tab->nentries++;
+                       success = 1;
+               } else {
+                       key_free(k);
+               }
+               keys[i] = NULL;
        }
-       key_free(k);
+       xfree(keys);
 send:
        buffer_put_int(&e->output, 1);
        buffer_put_char(&e->output,
@@ -506,39 +677,37 @@ send:
 static void
 process_remove_smartcard_key(SocketEntry *e)
 {
-       Key *k = NULL;
-       int success = 0;
-       char *sc_reader_id = NULL;
+       char *sc_reader_id = NULL, *pin;
+       int i, version, success = 0;
+       Key **keys, *k = NULL;
+       Identity *id;
+       Idtab *tab;
 
-       sc_reader_id = buffer_get_string(&e->input, NULL);
-       k = sc_get_key(sc_reader_id);
+       sc_reader_id = buffer_get_string(&e->request, NULL);
+       pin = buffer_get_string(&e->request, NULL);
+       keys = sc_get_keys(sc_reader_id, pin);
        xfree(sc_reader_id);
+       xfree(pin);
 
-       if (k == NULL) {
-               error("sc_get_pubkey failed");
-       } else {
-               Identity *id;
-               k->type = KEY_RSA1;
-               id = lookup_identity(k, 1);
-               if (id != NULL) {
-                       Idtab *tab = idtab_lookup(1);
+       if (keys == NULL || keys[0] == NULL) {
+               error("sc_get_keys failed");
+               goto send;
+       }
+       for (i = 0; keys[i] != NULL; i++) {
+               k = keys[i];
+               version = k->type == KEY_RSA1 ? 1 : 2;
+               if ((id = lookup_identity(k, version)) != NULL) {
+                       tab = idtab_lookup(version);
                        TAILQ_REMOVE(&tab->idlist, id, next);
-                       free_identity(id);
                        tab->nentries--;
-                       success = 1;
-               }
-               k->type = KEY_RSA;
-               id = lookup_identity(k, 2);
-               if (id != NULL) {
-                       Idtab *tab = idtab_lookup(2);
-                       TAILQ_REMOVE(&tab->idlist, id, next);
                        free_identity(id);
-                       tab->nentries--;
                        success = 1;
                }
                key_free(k);
+               keys[i] = NULL;
        }
-
+       xfree(keys);
+send:
        buffer_put_int(&e->output, 1);
        buffer_put_char(&e->output,
            success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
@@ -550,26 +719,50 @@ process_remove_smartcard_key(SocketEntry *e)
 static void
 process_message(SocketEntry *e)
 {
-       u_int msg_len;
-       u_int type;
+       u_int msg_len, type;
        u_char *cp;
+
        if (buffer_len(&e->input) < 5)
                return;         /* Incomplete message. */
        cp = buffer_ptr(&e->input);
-       msg_len = GET_32BIT(cp);
+       msg_len = get_u32(cp);
        if (msg_len > 256 * 1024) {
-               shutdown(e->fd, SHUT_RDWR);
-               close(e->fd);
-               e->type = AUTH_UNUSED;
+               close_socket(e);
                return;
        }
        if (buffer_len(&e->input) < msg_len + 4)
                return;
+
+       /* move the current input to e->request */
        buffer_consume(&e->input, 4);
-       type = buffer_get_char(&e->input);
+       buffer_clear(&e->request);
+       buffer_append(&e->request, buffer_ptr(&e->input), msg_len);
+       buffer_consume(&e->input, msg_len);
+       type = buffer_get_char(&e->request);
+
+       /* check wheter agent is locked */
+       if (locked && type != SSH_AGENTC_UNLOCK) {
+               buffer_clear(&e->request);
+               switch (type) {
+               case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
+               case SSH2_AGENTC_REQUEST_IDENTITIES:
+                       /* send empty lists */
+                       no_identities(e, type);
+                       break;
+               default:
+                       /* send a fail message for all other request types */
+                       buffer_put_int(&e->output, 1);
+                       buffer_put_char(&e->output, SSH_AGENT_FAILURE);
+               }
+               return;
+       }
 
        debug("type %d", type);
        switch (type) {
+       case SSH_AGENTC_LOCK:
+       case SSH_AGENTC_UNLOCK:
+               process_lock_agent(e, type == SSH_AGENTC_LOCK);
+               break;
        /* ssh1 */
        case SSH_AGENTC_RSA_CHALLENGE:
                process_authentication_challenge1(e);
@@ -578,6 +771,7 @@ process_message(SocketEntry *e)
                process_request_identities(e, 1);
                break;
        case SSH_AGENTC_ADD_RSA_IDENTITY:
+       case SSH_AGENTC_ADD_RSA_ID_CONSTRAINED:
                process_add_identity(e, 1);
                break;
        case SSH_AGENTC_REMOVE_RSA_IDENTITY:
@@ -594,6 +788,7 @@ process_message(SocketEntry *e)
                process_request_identities(e, 2);
                break;
        case SSH2_AGENTC_ADD_IDENTITY:
+       case SSH2_AGENTC_ADD_ID_CONSTRAINED:
                process_add_identity(e, 2);
                break;
        case SSH2_AGENTC_REMOVE_IDENTITY:
@@ -604,6 +799,7 @@ process_message(SocketEntry *e)
                break;
 #ifdef SMARTCARD
        case SSH_AGENTC_ADD_SMARTCARD_KEY:
+       case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED:
                process_add_smartcard_key(e);
                break;
        case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
@@ -613,7 +809,7 @@ process_message(SocketEntry *e)
        default:
                /* Unknown message.  Respond with failure. */
                error("Unknown message %d", type);
-               buffer_clear(&e->input);
+               buffer_clear(&e->request);
                buffer_put_int(&e->output, 1);
                buffer_put_char(&e->output, SSH_AGENT_FAILURE);
                break;
@@ -623,9 +819,9 @@ process_message(SocketEntry *e)
 static void
 new_socket(sock_type type, int fd)
 {
-       u_int i, old_alloc;
-       if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
-               error("fcntl O_NONBLOCK: %s", strerror(errno));
+       u_int i, old_alloc, new_alloc;
+
+       set_nonblock(fd);
 
        if (fd > max_fd)
                max_fd = fd;
@@ -633,30 +829,32 @@ new_socket(sock_type type, int fd)
        for (i = 0; i < sockets_alloc; i++)
                if (sockets[i].type == AUTH_UNUSED) {
                        sockets[i].fd = fd;
-                       sockets[i].type = type;
                        buffer_init(&sockets[i].input);
                        buffer_init(&sockets[i].output);
+                       buffer_init(&sockets[i].request);
+                       sockets[i].type = type;
                        return;
                }
        old_alloc = sockets_alloc;
-       sockets_alloc += 10;
-       if (sockets)
-               sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
-       else
-               sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
-       for (i = old_alloc; i < sockets_alloc; i++)
+       new_alloc = sockets_alloc + 10;
+       sockets = xrealloc(sockets, new_alloc, sizeof(sockets[0]));
+       for (i = old_alloc; i < new_alloc; i++)
                sockets[i].type = AUTH_UNUSED;
-       sockets[old_alloc].type = type;
+       sockets_alloc = new_alloc;
        sockets[old_alloc].fd = fd;
        buffer_init(&sockets[old_alloc].input);
        buffer_init(&sockets[old_alloc].output);
+       buffer_init(&sockets[old_alloc].request);
+       sockets[old_alloc].type = type;
 }
 
 static int
-prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, int *nallocp)
+prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp,
+    struct timeval **tvpp)
 {
-       u_int i, sz;
+       u_int i, sz, deadline;
        int n = 0;
+       static struct timeval tv;
 
        for (i = 0; i < sockets_alloc; i++) {
                switch (sockets[i].type) {
@@ -700,19 +898,32 @@ prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, int *nallocp)
                        break;
                }
        }
+       deadline = reaper();
+       if (parent_alive_interval != 0)
+               deadline = (deadline == 0) ? parent_alive_interval :
+                   MIN(deadline, parent_alive_interval);
+       if (deadline == 0) {
+               *tvpp = NULL;
+       } else {
+               tv.tv_sec = deadline;
+               tv.tv_usec = 0;
+               *tvpp = &tv;
+       }
        return (1);
 }
 
 static void
 after_select(fd_set *readset, fd_set *writeset)
 {
-       u_int i;
-       int len, sock;
+       struct sockaddr_un sunaddr;
        socklen_t slen;
        char buf[1024];
-       struct sockaddr_un sunaddr;
+       int len, sock;
+       u_int i, orig_alloc;
+       uid_t euid;
+       gid_t egid;
 
-       for (i = 0; i < sockets_alloc; i++)
+       for (i = 0, orig_alloc = sockets_alloc; i < orig_alloc; i++)
                switch (sockets[i].type) {
                case AUTH_UNUSED:
                        break;
@@ -720,51 +931,52 @@ after_select(fd_set *readset, fd_set *writeset)
                        if (FD_ISSET(sockets[i].fd, readset)) {
                                slen = sizeof(sunaddr);
                                sock = accept(sockets[i].fd,
-                                   (struct sockaddr *) &sunaddr, &slen);
+                                   (struct sockaddr *)&sunaddr, &slen);
                                if (sock < 0) {
                                        error("accept from AUTH_SOCKET: %s",
                                            strerror(errno));
                                        break;
                                }
+                               if (getpeereid(sock, &euid, &egid) < 0) {
+                                       error("getpeereid %d failed: %s",
+                                           sock, strerror(errno));
+                                       close(sock);
+                                       break;
+                               }
+                               if ((euid != 0) && (getuid() != euid)) {
+                                       error("uid mismatch: "
+                                           "peer euid %u != uid %u",
+                                           (u_int) euid, (u_int) getuid());
+                                       close(sock);
+                                       break;
+                               }
                                new_socket(AUTH_CONNECTION, sock);
                        }
                        break;
                case AUTH_CONNECTION:
                        if (buffer_len(&sockets[i].output) > 0 &&
                            FD_ISSET(sockets[i].fd, writeset)) {
-                               do {
-                                       len = write(sockets[i].fd,
-                                           buffer_ptr(&sockets[i].output),
-                                           buffer_len(&sockets[i].output));
-                                       if (len == -1 && (errno == EAGAIN ||
-                                           errno == EINTR))
-                                               continue;
-                                       break;
-                               } while (1);
+                               len = write(sockets[i].fd,
+                                   buffer_ptr(&sockets[i].output),
+                                   buffer_len(&sockets[i].output));
+                               if (len == -1 && (errno == EAGAIN ||
+                                   errno == EWOULDBLOCK ||
+                                   errno == EINTR))
+                                       continue;
                                if (len <= 0) {
-                                       shutdown(sockets[i].fd, SHUT_RDWR);
-                                       close(sockets[i].fd);
-                                       sockets[i].type = AUTH_UNUSED;
-                                       buffer_free(&sockets[i].input);
-                                       buffer_free(&sockets[i].output);
+                                       close_socket(&sockets[i]);
                                        break;
                                }
                                buffer_consume(&sockets[i].output, len);
                        }
                        if (FD_ISSET(sockets[i].fd, readset)) {
-                               do {
-                                       len = read(sockets[i].fd, buf, sizeof(buf));
-                                       if (len == -1 && (errno == EAGAIN ||
-                                           errno == EINTR))
-                                               continue;
-                                       break;
-                               } while (1);
+                               len = read(sockets[i].fd, buf, sizeof(buf));
+                               if (len == -1 && (errno == EAGAIN ||
+                                   errno == EWOULDBLOCK ||
+                                   errno == EINTR))
+                                       continue;
                                if (len <= 0) {
-                                       shutdown(sockets[i].fd, SHUT_RDWR);
-                                       close(sockets[i].fd);
-                                       sockets[i].type = AUTH_UNUSED;
-                                       buffer_free(&sockets[i].input);
-                                       buffer_free(&sockets[i].output);
+                                       close_socket(&sockets[i]);
                                        break;
                                }
                                buffer_append(&sockets[i].input, buf, len);
@@ -777,7 +989,7 @@ after_select(fd_set *readset, fd_set *writeset)
 }
 
 static void
-cleanup_socket(void *p)
+cleanup_socket(void)
 {
        if (socket_name[0])
                unlink(socket_name);
@@ -785,74 +997,85 @@ cleanup_socket(void *p)
                rmdir(socket_dir);
 }
 
-static void
+void
 cleanup_exit(int i)
 {
-       cleanup_socket(NULL);
-       exit(i);
+       cleanup_socket();
+       _exit(i);
 }
 
+/*ARGSUSED*/
 static void
 cleanup_handler(int sig)
 {
-       cleanup_socket(NULL);
+       cleanup_socket();
        _exit(2);
 }
 
 static void
-check_parent_exists(int sig)
+check_parent_exists(void)
 {
-       int save_errno = errno;
-
        if (parent_pid != -1 && kill(parent_pid, 0) < 0) {
                /* printf("Parent has died - Authentication agent exiting.\n"); */
-               cleanup_handler(sig); /* safe */
+               cleanup_socket();
+               _exit(2);
        }
-       signal(SIGALRM, check_parent_exists);
-       alarm(10);
-       errno = save_errno;
 }
 
 static void
 usage(void)
 {
-       fprintf(stderr, "Usage: %s [options] [command [args ...]]\n",
+       fprintf(stderr, "usage: %s [options] [command [arg ...]]\n",
            __progname);
        fprintf(stderr, "Options:\n");
        fprintf(stderr, "  -c          Generate C-shell commands on stdout.\n");
        fprintf(stderr, "  -s          Generate Bourne shell commands on stdout.\n");
        fprintf(stderr, "  -k          Kill the current agent.\n");
        fprintf(stderr, "  -d          Debug mode.\n");
+       fprintf(stderr, "  -a socket   Bind agent socket to given name.\n");
+       fprintf(stderr, "  -t life     Default identity lifetime (seconds).\n");
        exit(1);
 }
 
 int
 main(int ac, char **av)
 {
-       int sock, c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0, ch, nalloc;
+       int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0;
+       int sock, fd, ch, result, saved_errno;
+       u_int nalloc;
+       char *shell, *format, *pidstr, *agentsocket = NULL;
+       fd_set *readsetp = NULL, *writesetp = NULL;
        struct sockaddr_un sunaddr;
 #ifdef HAVE_SETRLIMIT
        struct rlimit rlim;
 #endif
-#ifdef HAVE_CYGWIN
        int prev_mask;
-#endif
-       pid_t pid;
-       char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
        extern int optind;
-       fd_set *readsetp = NULL, *writesetp = NULL;
+       extern char *optarg;
+       pid_t pid;
+       char pidstrbuf[1 + 3 * sizeof pid];
+       struct timeval *tvp = NULL;
+       size_t len;
+
+       /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
+       sanitise_stdfd();
+
+       /* drop */
+       setegid(getgid());
+       setgid(getgid());
+
+#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
+       /* Disable ptrace on Linux without sgid bit */
+       prctl(PR_SET_DUMPABLE, 0);
+#endif
 
        SSLeay_add_all_algorithms();
 
-       __progname = get_progname(av[0]);
+       __progname = ssh_get_progname(av[0]);
        init_rng();
        seed_rng();
 
-#ifdef __GNU_LIBRARY__
-       while ((ch = getopt(ac, av, "+cdks")) != -1) {
-#else /* __GNU_LIBRARY__ */
-       while ((ch = getopt(ac, av, "cdks")) != -1) {
-#endif /* __GNU_LIBRARY__ */
+       while ((ch = getopt(ac, av, "cdksa:t:")) != -1) {
                switch (ch) {
                case 'c':
                        if (s_flag)
@@ -872,6 +1095,15 @@ main(int ac, char **av)
                                usage();
                        d_flag++;
                        break;
+               case 'a':
+                       agentsocket = optarg;
+                       break;
+               case 't':
+                       if ((lifetime = convtime(optarg)) == -1) {
+                               fprintf(stderr, "Invalid lifetime\n");
+                               usage();
+                       }
+                       break;
                default:
                        usage();
                }
@@ -882,22 +1114,26 @@ main(int ac, char **av)
        if (ac > 0 && (c_flag || k_flag || s_flag || d_flag))
                usage();
 
-       if (ac == 0 && !c_flag && !k_flag && !s_flag && !d_flag) {
+       if (ac == 0 && !c_flag && !s_flag) {
                shell = getenv("SHELL");
-               if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
+               if (shell != NULL && (len = strlen(shell)) > 2 &&
+                   strncmp(shell + len - 3, "csh", 3) == 0)
                        c_flag = 1;
        }
        if (k_flag) {
+               const char *errstr = NULL;
+
                pidstr = getenv(SSH_AGENTPID_ENV_NAME);
                if (pidstr == NULL) {
                        fprintf(stderr, "%s not set, cannot kill agent\n",
                            SSH_AGENTPID_ENV_NAME);
                        exit(1);
                }
-               pid = atoi(pidstr);
-               if (pid < 1) {
-                       fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
-                           SSH_AGENTPID_ENV_NAME, pidstr);
+               pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr);
+               if (errstr) {
+                       fprintf(stderr,
+                           "%s=\"%s\", which is not a good PID: %s\n",
+                           SSH_AGENTPID_ENV_NAME, pidstr, errstr);
                        exit(1);
                }
                if (kill(pid, SIGTERM) == -1) {
@@ -907,19 +1143,25 @@ main(int ac, char **av)
                format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
                printf(format, SSH_AUTHSOCKET_ENV_NAME);
                printf(format, SSH_AGENTPID_ENV_NAME);
-               printf("echo Agent pid %d killed;\n", pid);
+               printf("echo Agent pid %ld killed;\n", (long)pid);
                exit(0);
        }
        parent_pid = getpid();
 
-       /* Create private directory for agent socket */
-       strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
-       if (mkdtemp(socket_dir) == NULL) {
-               perror("mkdtemp: private socket dir");
-               exit(1);
+       if (agentsocket == NULL) {
+               /* Create private directory for agent socket */
+               strlcpy(socket_dir, "/tmp/ssh-XXXXXXXXXX", sizeof socket_dir);
+               if (mkdtemp(socket_dir) == NULL) {
+                       perror("mkdtemp: private socket dir");
+                       exit(1);
+               }
+               snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir,
+                   (long)parent_pid);
+       } else {
+               /* Try to use specified agent socket */
+               socket_dir[0] = '\0';
+               strlcpy(socket_name, agentsocket, sizeof socket_name);
        }
-       snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
-           parent_pid);
 
        /*
         * Create socket early so it will exist before command gets run from
@@ -928,25 +1170,21 @@ main(int ac, char **av)
        sock = socket(AF_UNIX, SOCK_STREAM, 0);
        if (sock < 0) {
                perror("socket");
+               *socket_name = '\0'; /* Don't unlink any existing file */
                cleanup_exit(1);
        }
        memset(&sunaddr, 0, sizeof(sunaddr));
        sunaddr.sun_family = AF_UNIX;
        strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
-#ifdef HAVE_CYGWIN
        prev_mask = umask(0177);
-#endif
-       if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) {
+       if (bind(sock, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) {
                perror("bind");
-#ifdef HAVE_CYGWIN
+               *socket_name = '\0'; /* Don't unlink any existing file */
                umask(prev_mask);
-#endif
                cleanup_exit(1);
        }
-#ifdef HAVE_CYGWIN
        umask(prev_mask);
-#endif
-       if (listen(sock, 5) < 0) {
+       if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
                perror("listen");
                cleanup_exit(1);
        }
@@ -960,7 +1198,7 @@ main(int ac, char **av)
                format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
                printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
                    SSH_AUTHSOCKET_ENV_NAME);
-               printf("echo Agent pid %d;\n", parent_pid);
+               printf("echo Agent pid %ld;\n", (long)parent_pid);
                goto skip;
        }
        pid = fork();
@@ -970,14 +1208,14 @@ main(int ac, char **av)
        }
        if (pid != 0) {         /* Parent - execute the given command. */
                close(sock);
-               snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
+               snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid);
                if (ac == 0) {
                        format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
                        printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
                            SSH_AUTHSOCKET_ENV_NAME);
                        printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
                            SSH_AGENTPID_ENV_NAME);
-                       printf("echo Agent pid %d;\n", pid);
+                       printf("echo Agent pid %ld;\n", (long)pid);
                        exit(0);
                }
                if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 ||
@@ -998,9 +1236,14 @@ main(int ac, char **av)
        }
 
        (void)chdir("/");
-       close(0);
-       close(1);
-       close(2);
+       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);
+       }
 
 #ifdef HAVE_SETRLIMIT
        /* deny core dumps, since memory contains unencrypted private keys */
@@ -1012,12 +1255,9 @@ main(int ac, char **av)
 #endif
 
 skip:
-       fatal_add_cleanup(cleanup_socket, NULL);
        new_socket(AUTH_SOCKET, sock);
-       if (ac > 0) {
-               signal(SIGALRM, check_parent_exists);
-               alarm(10);
-       }
+       if (ac > 0)
+               parent_alive_interval = 10;
        idtab_init();
        if (!d_flag)
                signal(SIGINT, SIG_IGN);
@@ -1027,13 +1267,18 @@ skip:
        nalloc = 0;
 
        while (1) {
-               prepare_select(&readsetp, &writesetp, &max_fd, &nalloc);
-               if (select(max_fd + 1, readsetp, writesetp, NULL, NULL) < 0) {
-                       if (errno == EINTR)
+               prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp);
+               result = select(max_fd + 1, readsetp, writesetp, NULL, tvp);
+               saved_errno = errno;
+               if (parent_alive_interval != 0)
+                       check_parent_exists();
+               (void) reaper();        /* remove expired keys */
+               if (result < 0) {
+                       if (saved_errno == EINTR)
                                continue;
-                       fatal("select: %s", strerror(errno));
-               }
-               after_select(readsetp, writesetp);
+                       fatal("select: %s", strerror(saved_errno));
+               } else if (result > 0)
+                       after_select(readsetp, writesetp);
        }
        /* NOTREACHED */
 }
This page took 0.095714 seconds and 4 git commands to generate.