]> andersk Git - gssapi-openssh.git/blobdiff - openssh/ssh-agent.c
Import of OpenSSH 4.9p1
[gssapi-openssh.git] / openssh / ssh-agent.c
index c05c61468c93e22ce0d62413b72acf12aadbb414..6f8727b33066b5279c1705c992a8bb1a008712a3 100644 (file)
@@ -1,3 +1,4 @@
+/* $OpenBSD: ssh-agent.c,v 1.157 2007/09/25 23:48:57 canacar Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  */
 
 #include "includes.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"
-RCSID("$OpenBSD: ssh-agent.c,v 1.111 2003/06/12 19:12:03 markus Exp $");
 
 #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 "readpass.h"
 #include "misc.h"
 
 #ifdef SMARTCARD
 #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,
@@ -94,20 +121,17 @@ 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;
@@ -169,23 +193,15 @@ lookup_identity(Key *key, int version)
 static int
 confirm_key(Identity *id)
 {
-       char *p, prompt[1024];
+       char *p;
        int ret = -1;
 
        p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX);
-       snprintf(prompt, sizeof(prompt), "Allow use of key %s?\n"
-           "Key fingerprint %s.", id->comment, p);
+       if (ask_permission("Allow use of key %s?\nKey fingerprint %s.",
+           id->comment, p))
+               ret = 0;
        xfree(p);
-       p = read_passphrase(prompt, RP_ALLOW_EOF);
-       if (p != NULL) {
-               /*
-                * Accept empty responses and responses consisting 
-                * of the word "yes" as affirmative.
-                */
-               if (*p == '\0' || *p == '\n' || strcasecmp(p, "yes") == 0)
-                       ret = 0;
-               xfree(p);
-       }
+
        return (ret);
 }
 
@@ -314,8 +330,8 @@ process_sign_request2(SocketEntry *e)
                Identity *id = lookup_identity(key, 2);
                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);
@@ -364,7 +380,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.
@@ -407,10 +423,11 @@ process_remove_all_identities(SocketEntry *e, int version)
        buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
 }
 
-static void
+/* removes expired keys and returns number of seconds until the next expiry */
+static u_int
 reaper(void)
 {
-       u_int now = time(NULL);
+       u_int deadline = 0, now = time(NULL);
        Identity *id, *nxt;
        int version;
        Idtab *tab;
@@ -419,19 +436,29 @@ reaper(void)
                tab = idtab_lookup(version);
                for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) {
                        nxt = TAILQ_NEXT(id, next);
-                       if (id->death != 0 && now >= id->death) {
+                       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)
 {
        Idtab *tab = idtab_lookup(version);
+       Identity *id;
        int type, success = 0, death = 0, confirm = 0;
        char *type_name, *comment;
        Key *k = NULL;
@@ -514,19 +541,19 @@ process_add_identity(SocketEntry *e, int version)
        }
        if (lifetime && !death)
                death = time(NULL) + lifetime;
-       if (lookup_identity(k, version) == NULL) {
-               Identity *id = xmalloc(sizeof(Identity));
+       if ((id = lookup_identity(k, version)) == NULL) {
+               id = xmalloc(sizeof(Identity));
                id->key = k;
-               id->comment = comment;
-               id->death = death;
-               id->confirm = confirm;
                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,
@@ -684,13 +711,10 @@ process_message(SocketEntry *e)
        u_int msg_len, type;
        u_char *cp;
 
-       /* kill dead keys */
-       reaper();
-
        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) {
                close_socket(e);
                return;
@@ -784,10 +808,9 @@ process_message(SocketEntry *e)
 static void
 new_socket(sock_type type, int fd)
 {
-       u_int i, old_alloc;
+       u_int i, old_alloc, new_alloc;
 
-       if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
-               error("fcntl O_NONBLOCK: %s", strerror(errno));
+       set_nonblock(fd);
 
        if (fd > max_fd)
                max_fd = fd;
@@ -795,32 +818,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) {
@@ -864,6 +887,17 @@ 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);
 }
 
@@ -886,7 +920,7 @@ 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));
@@ -948,7 +982,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);
@@ -956,38 +990,35 @@ 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);
        }
-       mysignal(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");
@@ -1003,25 +1034,33 @@ int
 main(int ac, char **av)
 {
        int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0;
-       int sock, fd,  ch, nalloc;
+       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
        extern int optind;
        extern char *optarg;
        pid_t pid;
        char pidstrbuf[1 + 3 * sizeof pid];
+       struct timeval *tvp = NULL;
+
+       /* 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 = ssh_get_progname(av[0]);
@@ -1069,20 +1108,24 @@ main(int ac, char **av)
 
        if (ac == 0 && !c_flag && !s_flag) {
                shell = getenv("SHELL");
-               if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
+               if (shell != NULL &&
+                   strncmp(shell + strlen(shell) - 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) {
@@ -1099,7 +1142,7 @@ main(int ac, char **av)
 
        if (agentsocket == NULL) {
                /* Create private directory for agent socket */
-               strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
+               strlcpy(socket_dir, "/tmp/ssh-XXXXXXXXXX", sizeof socket_dir);
                if (mkdtemp(socket_dir) == NULL) {
                        perror("mkdtemp: private socket dir");
                        exit(1);
@@ -1119,25 +1162,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, 128) < 0) {
+       if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
                perror("listen");
                cleanup_exit(1);
        }
@@ -1208,12 +1247,9 @@ main(int ac, char **av)
 #endif
 
 skip:
-       fatal_add_cleanup(cleanup_socket, NULL);
        new_socket(AUTH_SOCKET, sock);
-       if (ac > 0) {
-               mysignal(SIGALRM, check_parent_exists);
-               alarm(10);
-       }
+       if (ac > 0)
+               parent_alive_interval = 10;
        idtab_init();
        if (!d_flag)
                signal(SIGINT, SIG_IGN);
@@ -1223,13 +1259,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.057614 seconds and 4 git commands to generate.