/* $Id: ssh-agent.c,v 1.11.2.1 2001/02/11 04:16:14 tls Exp $ */ /* * Copyright (c) 2001 Jason R. Thorpe. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Authentication agent for FreSSH. * * TODO: * * - Add support for the SSHv2 authentication agent protocol. */ #include #include #include #include #include #include #include #include #include #include #include "sshd.h" #include "ssh_buffer.h" #include "ssh_authagent.h" #include "ssh_environ.h" #include "ssh_defines.h" struct sockev { struct sockev *sev_next; void (*sev_handler)(struct sockev *); struct ssh_buf *sev_buf; int sev_sock; int sev_state; u_int32_t sev_pktlen; }; struct sockev *sockev_list; #define SOCKEV_STATE_INITIAL 0 #define SOCKEV_STATE_GETREQUEST 1 #define SOCKEV_STATE_SENDREPLY 2 #define SOCKEV_READ(sev) \ ((sev)->sev_state == SOCKEV_STATE_INITIAL || \ (sev)->sev_state == SOCKEV_STATE_GETREQUEST) #define SOCKEV_WRITE(sev) \ ((sev)->sev_state == SOCKEV_STATE_SENDREPLY) void sockev_add(int, void (*)(struct sockev *)); void sockev_remove(struct sockev *); void sockev_doaccept(struct sockev *); void sockev_domsg(struct sockev *); typedef void (*agent_message_handler)(struct sockev *); void agent_v1_rsa_id_request(struct sockev *); void agent_v1_rsa_challenge(struct sockev *); void agent_v1_add_rsa_id(struct sockev *); void agent_v1_remove_rsa_id(struct sockev *); void agent_v1_remove_all_rsa_id(struct sockev *); void agent_v1_send_failure(struct sockev *); const agent_message_handler agent_incoming_message_table[] = { NULL, /* 0 */ agent_v1_rsa_id_request, NULL, /* SSH_AGENT_V1_RSA_ID_REPLY */ agent_v1_rsa_challenge, NULL, /* SSH_AGENT_V1_RSA_RESPONSE */ NULL, /* SSH_AGENT_V1_FAILURE */ NULL, /* SSH_AGENT_V1_SUCCESS */ agent_v1_add_rsa_id, agent_v1_remove_rsa_id, agent_v1_remove_all_rsa_id, }; const int agent_incoming_message_table_size = sizeof(agent_incoming_message_table) / sizeof(agent_incoming_message_table[0]); #define CODE_IS_VALID(code) \ ((code) < agent_incoming_message_table_size && \ agent_incoming_message_table[(code)] != NULL) struct auth_id { struct auth_id *id_next; ssh_RSA *id_key; char *id_comm; }; struct auth_id *auth_id_list; int auth_id_count; int auth_id_add(ssh_RSA *, char *); struct auth_id *auth_id_lookup(ssh_BIGNUM *, ssh_BIGNUM *); int auth_id_delete(ssh_BIGNUM *, ssh_BIGNUM *); void auth_id_delete_all(void); /* We'd like to use __progname if we have it. */ #define FRESSH_AGENT "fressh-agent" #define FRESSH_AGENT_DEFAULT 0 #define FRESSH_AGENT_CSH 1 #define FRESSH_AGENT_BOURNE 2 int main(int, char *[]); void serverloop(void); void cleanup(void); void exit_on_signal(int); void usage(void); char sockname[MAXPATHLEN+1]; char sockdirname[MAXPATHLEN+1]; int listensock; sigset_t critsigs; sig_atomic_t time_to_die; int main(int argc, char *argv[]) { struct sockaddr_un sun; struct sigaction sa; struct rlimit rlim; char *envval, pidstr[16]; int ch, shtype, kflag, fd, Dflag; pid_t pid; shtype = FRESSH_AGENT_DEFAULT; kflag = 0; Dflag = 0; while ((ch = getopt(argc, argv, "cksD")) != -1) { switch (ch) { case 'c': if (shtype != FRESSH_AGENT_DEFAULT) usage(); shtype = FRESSH_AGENT_CSH; break; case 'k': kflag = 1; break; case 's': if (shtype != FRESSH_AGENT_DEFAULT) usage(); shtype = FRESSH_AGENT_BOURNE; break; case 'D': Dflag = 1; break; default: usage(); } } argc -= optind; argv += optind; /* * If have command arguments specified, no flags should * be specified. */ if (argc != 0 && (kflag || shtype != FRESSH_AGENT_DEFAULT)) usage(); if (shtype == FRESSH_AGENT_DEFAULT) { envval = getenv("SHELL"); if (envval != NULL && strcmp(envval + strlen(envval) - 3, "csh") == 0) shtype = FRESSH_AGENT_CSH; else shtype = FRESSH_AGENT_BOURNE; } /* * Deal with the KILL command first -- it's easy. */ if (kflag) { if (argc != 0) usage(); envval = getenv(SSH_ENVVAR_AUTHAGENT_PID); if (envval == NULL) { fprintf(stderr, "The %s environment variable is not " "set, unable to kill agent\n", SSH_ENVVAR_AUTHAGENT_PID); exit(1); } pid = atoi(envval); if (pid < 1) { fprintf(stderr, "The %s environment variable does not " "contain a valid\n" "process ID, unable to kill agent\n", SSH_ENVVAR_AUTHAGENT_PID); exit(1); } if (kill(pid, SIGTERM) < 0) { fprintf(stderr, "Unable to send SIGTERM to PID %d: %s\n", pid, strerror(errno)); exit(1); } if (shtype == FRESSH_AGENT_CSH) { printf("unsetenv %s;\n", SSH_ENVVAR_AUTHAGENT_SOCKNAME); printf("unsetenv %s;\n", SSH_ENVVAR_AUTHAGENT_PID); } else { printf("unset %s;\n", SSH_ENVVAR_AUTHAGENT_SOCKNAME); printf("unset %s;\n", SSH_ENVVAR_AUTHAGENT_PID); } printf("echo %s authentication agent %d killed;\n", SSHD_REV, pid); exit(0); } /* * Don't allow core dumps to happen. We have private * keys in-core, and we don't want to leak them. */ rlim.rlim_cur = rlim.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &rlim) < 0) { fprintf(stderr, "Unable to disable core dumps: %s\n", strerror(errno)); exit(1); } listensock = -1; /* * Register our cleanup routine. */ if (atexit(cleanup) < 0) { fprintf(stderr, "Unable to register cleanup routine: %s\n", strerror(errno)); exit (1); } /* * Create the communication socket. * * NOTE: All of the uses of strcpy(), etc. here are safe; everyhing * is guaranteed to total less than MAXPATHLEN. */ pid = getpid(); strcpy(sockdirname, "/tmp/fressh-XXXXXXXX"); if (mkdtemp(sockdirname) == NULL) { fprintf(stderr, "Unable to create socket directory: %s\n", strerror(errno)); sockdirname[0] = '\0'; exit(1); } sprintf(sockname, "%s/authagent.%d", sockdirname, pid); listensock = socket(PF_LOCAL, SOCK_STREAM, 0); if (listensock < 0) { fprintf(stderr, "Unable to create listen socket: %s\n", strerror(errno)); exit(1); } memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_LOCAL; strncpy(sun.sun_path, sockname, sizeof(sun.sun_path)); sun.sun_path[sizeof(sun.sun_path) - 1] = '\0'; if (strcmp(sun.sun_path, sockname) != 0) { fprintf(stderr, "Socket name doesn't fit!\n"); exit(1); } sun.sun_len = SUN_LEN(&sun); if (bind(listensock, (struct sockaddr *) &sun, sizeof(sun)) < 0) { fprintf(stderr, "Unable to bind listen socket: %s\n", strerror(errno)); exit(1); } if (listen(listensock, 5) < 0) { fprintf(stderr, "Unable to listen on socket: %s\n", strerror(errno)); exit(1); } if (Dflag) goto parent_code; switch ((pid = fork())) { case -1: fprintf(stderr, "Fork failed: %s\n", strerror(errno)); exit(1); case 0: /* Child -- handled below. */ break; default: /* * Parent: If we have no commands, print the environment * variables for the shell to evaluate and exit. Otherwise, * set the environment as appopriate and execute the * command. */ (void) close(listensock); parent_code: sprintf(pidstr, "%d", pid); /* this is safe */ if (argc == 0) { if (shtype == FRESSH_AGENT_CSH) { printf("setenv %s %s;\n", SSH_ENVVAR_AUTHAGENT_SOCKNAME, sockname); printf("setenv %s %s;\n", SSH_ENVVAR_AUTHAGENT_PID, pidstr); } else { printf("%s=%s; export %s;\n", SSH_ENVVAR_AUTHAGENT_SOCKNAME, sockname, SSH_ENVVAR_AUTHAGENT_SOCKNAME); printf("%s=%s; export %s;\n", SSH_ENVVAR_AUTHAGENT_PID, pidstr, SSH_ENVVAR_AUTHAGENT_PID); } printf("echo %s authentication agent PID is %s;\n", SSHD_REV, pidstr); /* * Flush stdout -- if we're being eval'd, * we might need this so that the shell * notices the output (we use _exit(), * and thus the stdio buffers aren't * flushed automatically on exit. */ fflush(stdout); if (Dflag) goto child_code; _exit(0); } if (setenv(SSH_ENVVAR_AUTHAGENT_SOCKNAME, sockname, 1) < 0) { fprintf(stderr, "Unable to set %s environment variable: %s\n", SSH_ENVVAR_AUTHAGENT_SOCKNAME, strerror(errno)); exit(1); } if (setenv(SSH_ENVVAR_AUTHAGENT_PID, pidstr, 1) < 0) { fprintf(stderr, "Unable to set %s environment variable: %s\n", SSH_ENVVAR_AUTHAGENT_PID, strerror(errno)); exit(1); } (void) execvp(argv[0], argv); fprintf(stderr, "Unable to exec `%s': %s\n", argv[0], strerror(errno)); exit(1); } child_code: /* * Ignore SIGINT, SIGPIPE. */ sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGINT, &sa, NULL) < 0) { fprintf(stderr, "Unable to ignore SIGINT: %s\n", strerror(errno)); exit(1); } if (sigaction(SIGPIPE, &sa, NULL) < 0) { fprintf(stderr, "Unable to ignore SIGPIPE: %s\n", strerror(errno)); exit(1); } /* * Exit on SIGHUP and SIGTERM. */ sa.sa_handler = exit_on_signal; if (sigaction(SIGHUP, &sa, NULL) < 0) { fprintf(stderr, "Unable to register SIGHUP handler: %s\n", strerror(errno)); exit(1); } if (sigaction(SIGTERM, &sa, NULL) < 0) { fprintf(stderr, "Unable to register SIGTERM handler: %s\n", strerror(errno)); exit(1); } /* * Block SIGHUP and SIGTERM during critical sections. */ sigemptyset(&critsigs); sigaddset(&critsigs, SIGHUP); sigaddset(&critsigs, SIGTERM); if (Dflag) goto after_detach; /* * Detach from the controlling terminal. */ if (setsid() < 0) { fprintf(stderr, "Unable to detach from controlling terminal: %s\n", strerror(errno)); exit(1); } /* * Redirect stdin/stdout/stderr to /dev/null. */ fd = open("/dev/null", O_RDWR, 0); if (fd < 0) { fprintf(stderr, "Unable to open /dev/null: %s\n", strerror(errno)); exit(1); } if (dup2(fd, fileno(stdin)) < 0) { fprintf(stderr, "Unable to redirect stdin to /dev/null: %s\n", strerror(errno)); exit(1); } if (dup2(fd, fileno(stdout)) < 0) { fprintf(stderr, "Unable to redirect stdout to /dev/null: %s\n", strerror(errno)); exit(1); } if (dup2(fd, fileno(stderr)) < 0) { fprintf(stderr, "Unable to redirect stderr to /dev/null: %s\n", strerror(errno)); exit(1); } after_detach: /* * NO MORE OUTPUT FROM HERE ON! * * Actually, we go ahead and still output to stderr, * which aids in debugging if you e.g. use ktruss(1) * to attach to an already running agent. */ /* * Create a socklist entry for the listen socket. */ sockev_add(listensock, sockev_doaccept); /* * Enter the server loop. */ serverloop(); /* This isn't ever reached. */ fprintf(stderr, "%s: (pid %d) SERVER LOOP RETURNED\n", FRESSH_AGENT, getpid()); exit(666); } void exit_on_signal(int sig) { time_to_die = 1; } void usage(void) { fprintf(stderr, "%s Authentication Agent (SSHv1)\n", SSHD_REV); fprintf(stderr, "Usage: %s [command [args ...]]\n", FRESSH_AGENT); fprintf(stderr, " %s [ -c | -s ]\n", FRESSH_AGENT); fprintf(stderr, " %s -k\n", FRESSH_AGENT); exit(1); } /* * serverloop: * * This is the server loop. This is as simple as possible, so that * can can replace it easily with a loop based on poll() on systems * that have it. */ void serverloop(void) { struct sockev *sev, *nextsev; fd_set readset, writeset; int rv, maxsock; for (;;) { if (time_to_die) { fprintf(stderr, "%s: (pid %d) signalled to exit.\n", FRESSH_AGENT, getpid()); exit(0); } FD_ZERO(&readset); FD_ZERO(&writeset); maxsock = 0; for (sev = sockev_list; sev != NULL; sev = sev->sev_next) { if (SOCKEV_READ(sev)) FD_SET(sev->sev_sock, &readset); if (SOCKEV_WRITE(sev)) FD_SET(sev->sev_sock, &writeset); if (sev->sev_sock > maxsock) maxsock = sev->sev_sock; } rv = select(maxsock + 1, &readset, &writeset, NULL, NULL); if (rv < 0) { if (errno == EINTR) continue; fprintf(stderr, "%s: (pid %d) SELECT FAILED: %s\n", FRESSH_AGENT, getpid(), strerror(errno)); exit(1); } for (sev = sockev_list; sev != NULL; sev = nextsev) { nextsev = sev->sev_next; if (FD_ISSET(sev->sev_sock, &readset) || FD_ISSET(sev->sev_sock, &writeset)) { (*sev->sev_handler)(sev); } } } } /***************************************************************************** * Utility routines. *****************************************************************************/ /* * cleanup: * * We sweep up on exit. */ void cleanup(void) { struct sockev *sev; auth_id_delete_all(); while ((sev = sockev_list) != NULL) sockev_remove(sev); if (listensock != -1) (void) close(listensock); if (sockname[0] != '\0') (void) unlink(sockname); if (sockdirname[0] != '\0') (void) rmdir(sockdirname); } /* * sockev_add: * * Add a socket event handler. */ void sockev_add(int sock, void (*handler)(struct sockev *)) { struct sockev *sev; /* * XXX Should handle this better. */ if (sock >= FD_SETSIZE) { (void) close(sock); return; } if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { (void) close(sock); return; } sev = calloc(1, sizeof(*sev)); if (sev == NULL) { (void) close(sock); return; } sev->sev_sock = sock; sev->sev_handler = handler; sev->sev_state = SOCKEV_STATE_INITIAL; sev->sev_buf = buf_alloc(NULL, 64); if (sev == NULL) { (void) close(sock); free(sev); } sev->sev_next = sockev_list; sockev_list = sev; } /* * sockev_remove: * * Remove a socket event handler. */ void sockev_remove(struct sockev *sev) { struct sockev *lsev; if (sev == sockev_list) { sockev_list = sev->sev_next; } else { for (lsev = sockev_list; lsev->sev_next == sev && lsev != NULL; lsev = lsev->sev_next) { /* Search it out... */ ; } if (lsev == NULL) { /* IMPOSSIBLE */ exit(1); } lsev->sev_next = sev->sev_next; } (void) close(sev->sev_sock); buf_cleanup(sev->sev_buf); free(sev->sev_buf); free(sev); } /* * sockev_doaccept: * * Socket event handler for the listen socket -- accept a connection. */ void sockev_doaccept(struct sockev *sev) { struct sockaddr_un sun; socklen_t len; int sock; len = sizeof(sun); sock = accept(sev->sev_sock, (struct sockaddr *) &sun, &len); if (sock < 0) { /* Oops, just return. */ return; } sockev_add(sock, sockev_domsg); } /* * sockev_domsg: * * Socket event handler for connection sockets -- message * processing. */ void sockev_domsg(struct sockev *sev) { size_t rv, size; switch (sev->sev_state) { case SOCKEV_STATE_INITIAL: /* * Initial state -- we're expecting to read the * packet length. */ size = sizeof(&sev->sev_pktlen) - buf_len(sev->sev_buf); rv = read(sev->sev_sock, buf_data(sev->sev_buf) + buf_len(sev->sev_buf), size); if (rv <= 0) { sockev_remove(sev); return; } if (buf_adjlen(sev->sev_buf, rv) != 0) { sockev_remove(sev); return; } /* * If there are 4 bytes in the packet, we have the * length. */ if (buf_len(sev->sev_buf) == sizeof(sev->sev_pktlen)) { if (buf_get_int32(sev->sev_buf, &sev->sev_pktlen) != 0) { sockev_remove(sev); return; } if (sev->sev_pktlen > 256 * 1024) { /* Absurd length. */ sockev_remove(sev); return; } /* * Have the packet length -- make sure the * buffer has enough space. */ if (buf_grow(sev->sev_buf, sev->sev_pktlen + sizeof(sev->sev_pktlen)) != 0) { sockev_remove(sev); return; } sev->sev_state = SOCKEV_STATE_GETREQUEST; } return; case SOCKEV_STATE_GETREQUEST: /* * Grab a block of data. */ size = sev->sev_pktlen - buf_len(sev->sev_buf); rv = read(sev->sev_sock, buf_data(sev->sev_buf) + buf_len(sev->sev_buf), size); if (rv <= 0) { sockev_remove(sev); return; } if (buf_adjlen(sev->sev_buf, rv) != 0) { sockev_remove(sev); return; } if (buf_len(sev->sev_buf) == sev->sev_pktlen) { u_int8_t code; /* * Have the entire packet. Validate the * code and perform the request. */ if (buf_get_int8(sev->sev_buf, &code) != 0) { sockev_remove(sev); return; } if (CODE_IS_VALID(code) == 0) { sockev_remove(sev); return; } (*agent_incoming_message_table[code])(sev); sev->sev_state = SOCKEV_STATE_SENDREPLY; } return; case SOCKEV_STATE_SENDREPLY: /* * The packet length and code are already stuffed * into the buffer; just blast it out. */ rv = write(sev->sev_sock, buf_data(sev->sev_buf), buf_len(sev->sev_buf)); if (rv <= 0) { sockev_remove(sev); return; } if (buf_get_skip(sev->sev_buf, rv) != 0) { sockev_remove(sev); return; } if (buf_len(sev->sev_buf) == 0) { /* * Cool, all done. Return to the initial * state -- the client may have more requests * to send us. */ buf_clear(sev->sev_buf); sev->sev_state = SOCKEV_STATE_INITIAL; return; } return; default: /* * Totally bogus state -- this can't happen, but * just kill the sockev. */ sockev_remove(sev); return; } } /***************************************************************************** * SSHv1 authentication agent protocol handlers *****************************************************************************/ void agent_v1_rsa_id_request(struct sockev *sev) { struct ssh_buf *repl; struct auth_id *id; repl = buf_alloc(NULL, 16); /* Number of keys. */ if (buf_put_int32(repl, auth_id_count) != 0) goto bad; /* * ...and now the size, epart, npart, and comment of each key. */ for (id = auth_id_list; id != NULL; id = id->id_next) { if (buf_put_int32(repl, bignum_num_bits(ssh_rsa_npart(id->id_key))) != 0) goto bad; if (buf_put_bignum(repl, ssh_rsa_epart(id->id_key)) != 0) goto bad; if (buf_put_bignum(repl, ssh_rsa_npart(id->id_key)) != 0) goto bad; if (buf_put_binstr(repl, id->id_comm, strlen(id->id_comm)) != 0) goto bad; } buf_reset(sev->sev_buf); if (buf_put_int32(sev->sev_buf, buf_alllen(repl) + 1) != 0) goto bad; if (buf_put_int8(sev->sev_buf, SSH_AGENT_V1_RSA_ID_REPLY) != 0) goto bad; if (buf_append(sev->sev_buf, repl) != 0) goto bad; buf_rewind(sev->sev_buf); buf_cleanup(repl); free(repl); return; bad: agent_v1_send_failure(sev); } void agent_v1_rsa_challenge(struct sockev *sev) { ssh_BIGNUM *challenge = NULL, *epart = NULL, *npart = NULL; struct auth_id *id; u_int32_t keybits, resptype; u_int8_t *session_id = NULL; u_int8_t *encr_chal = NULL, *chal, response[16]; ssh_MD5 md5ctx; int nuke = 0; int chal_len = 0, len; if (buf_get_int32(sev->sev_buf, &keybits) != 0) goto nuke; if (buf_get_bignum(sev->sev_buf, &epart) != 0) goto nuke; if (buf_get_bignum(sev->sev_buf, &npart) != 0) goto nuke; if (buf_get_bignum(sev->sev_buf, &challenge) != 0) goto nuke; /* * SSHv1 agent protocol 1.0 didn't include a session ID, * and was insecure. We don't use it. We only support * SSHv1 agent protocol 1.1. */ if (buf_len(sev->sev_buf) == 0) goto bad; if (buf_get_nbytes(sev->sev_buf, 16, &session_id) != 0) goto nuke; if (buf_get_int32(sev->sev_buf, &resptype) != 0) goto nuke; if (resptype != 1) goto bad; id = auth_id_lookup(epart, npart); if (id == NULL) goto bad; chal_len = bignum_num_bytes(challenge); encr_chal = malloc(chal_len); if (encr_chal == NULL) goto bad; chal = malloc(chal_len); if (chal == NULL) goto bad; len = bignum_bn2bin(challenge, encr_chal); if (len == 0 || len > chal_len) goto bad; if (ssh_rsa_private_decrypt(len, encr_chal, chal, id->id_key) < 0) goto bad; ssh_md5_init(&md5ctx); ssh_md5_update(&md5ctx, chal, 32); ssh_md5_update(&md5ctx, session_id, 16); ssh_md5_final(response, &md5ctx); buf_reset(sev->sev_buf); if (buf_put_int32(sev->sev_buf, sizeof(response) + 1) != 0) goto bad; if (buf_put_int8(sev->sev_buf, SSH_AGENT_V1_RSA_RESPONSE) != 0) goto bad; if (buf_put_nbytes(sev->sev_buf, sizeof(response), response) != 0) goto bad; buf_rewind(sev->sev_buf); bignum_free(epart); bignum_free(npart); bignum_free(challenge); free(encr_chal); memset(chal, 0, chal_len); free(chal); memset(session_id, 0, 16); free(session_id); return; nuke: nuke = 1; bad: if (epart != NULL) bignum_free(epart); if (epart != NULL) bignum_free(npart); if (challenge != NULL) bignum_free(challenge); if (encr_chal != NULL) free(encr_chal); if (chal != NULL) { memset(chal, 0, chal_len); free(chal); } if (session_id != NULL) { memset(session_id, 0, 16); free(session_id); } memset(chal, 0, sizeof(chal)); if (nuke) sockev_remove(sev); else agent_v1_send_failure(sev); } void agent_v1_add_rsa_id(struct sockev *sev) { ssh_RSA *key = NULL; char *comment; u_int32_t keybits; int nuke = 0; key = ssh_rsa_new(); if (key == NULL) goto bad; if (buf_get_int32(sev->sev_buf, &keybits) != 0) goto nuke; if (buf_get_bignum(sev->sev_buf, &ssh_rsa_npart(key)) != 0) goto nuke; if (buf_get_bignum(sev->sev_buf, &ssh_rsa_epart(key)) != 0) goto nuke; if (buf_get_bignum(sev->sev_buf, &ssh_rsa_dpart(key)) != 0) goto nuke; if (buf_get_bignum(sev->sev_buf, &ssh_rsa_iqmppart(key)) != 0) goto nuke; /* * The order is the same order as the F-secure key file * format -- p and q are actually swapped. */ if (buf_get_bignum(sev->sev_buf, &ssh_rsa_qpart(key)) != 0) goto nuke; if (buf_get_bignum(sev->sev_buf, &ssh_rsa_ppart(key)) != 0) goto nuke; comment = NULL; if (buf_get_binstr(sev->sev_buf, (u_int8_t **)&comment, NULL) != 0) goto nuke; /* Compute dmp1 and dmq1. */ if (ssh_key_fixup(key) != 0) goto bad; if (auth_id_add(key, comment) != 0) goto bad; buf_reset(sev->sev_buf); if (buf_put_int32(sev->sev_buf, 1) != 0) goto bad; if (buf_put_int8(sev->sev_buf, SSH_AGENT_V1_SUCCESS) != 0) goto bad; buf_rewind(sev->sev_buf); return; nuke: nuke = 1; bad: if (key != NULL) ssh_rsa_free(&key); if (nuke) sockev_remove(sev); else agent_v1_send_failure(sev); } void agent_v1_remove_rsa_id(struct sockev *sev) { ssh_BIGNUM *epart = NULL, *npart = NULL; u_int32_t keybits; int nuke = 0; if (buf_get_int32(sev->sev_buf, &keybits) != 0) goto nuke; if (buf_get_bignum(sev->sev_buf, &epart) != 0) goto nuke; if (buf_get_bignum(sev->sev_buf, &npart) != 0) goto nuke; if (auth_id_delete(epart, npart) != 0) goto bad; bignum_free(epart); bignum_free(npart); buf_reset(sev->sev_buf); if (buf_put_int32(sev->sev_buf, 1) != 0) goto bad; if (buf_put_int8(sev->sev_buf, SSH_AGENT_V1_SUCCESS) != 0) goto bad; buf_rewind(sev->sev_buf); return; nuke: nuke = 1; bad: if (epart != NULL) bignum_free(epart); if (npart != NULL) bignum_free(npart); if (nuke) sockev_remove(sev); else agent_v1_send_failure(sev); } void agent_v1_remove_all_rsa_id(struct sockev *sev) { auth_id_delete_all(); buf_reset(sev->sev_buf); if (buf_put_int32(sev->sev_buf, 1) != 0) goto bad; if (buf_put_int8(sev->sev_buf, SSH_AGENT_V1_SUCCESS) != 0) goto bad; buf_rewind(sev->sev_buf); return; bad: agent_v1_send_failure(sev); } void agent_v1_send_failure(struct sockev *sev) { buf_reset(sev->sev_buf); if (buf_put_int32(sev->sev_buf, 1) != 0) goto bad; if (buf_put_int8(sev->sev_buf, SSH_AGENT_V1_FAILURE) != 0) goto bad; buf_rewind(sev->sev_buf); return; bad: sockev_remove(sev); } /***************************************************************************** * Identity database routines *****************************************************************************/ int auth_id_add(ssh_RSA *key, char *comment) { struct auth_id *id; id = auth_id_lookup(ssh_rsa_epart(key), ssh_rsa_npart(key)); if (id != NULL) { ssh_rsa_free(&id->id_key); free(id->id_comm); } else { id = malloc(sizeof(*id)); if (id == NULL) goto bad; id->id_next = auth_id_list; auth_id_list = id; auth_id_count++; } id->id_key = key; id->id_comm = comment; return (0); bad: return (-1); } struct auth_id * auth_id_lookup(ssh_BIGNUM *epart, ssh_BIGNUM *npart) { struct auth_id *id; for (id = auth_id_list; id != NULL; id = id->id_next) { if (bignum_compare(epart, ssh_rsa_epart(id->id_key)) == 0 && bignum_compare(npart, ssh_rsa_npart(id->id_key)) == 0) break; } return (id); } int auth_id_delete(ssh_BIGNUM *epart, ssh_BIGNUM *npart) { struct auth_id *id, *lid; id = auth_id_lookup(epart, npart); if (id == NULL) goto bad; if (id == auth_id_list) auth_id_list = id->id_next; else { for (lid = auth_id_list; lid->id_next != id; lid = lid->id_next) /* Search it out... */ ; lid->id_next = id->id_next; } auth_id_count--; ssh_rsa_free(&id->id_key); free(id->id_comm); free(id); return (0); bad: return (-1); } void auth_id_delete_all(void) { struct auth_id *id; while ((id = auth_id_list) != NULL) { auth_id_list = id->id_next; ssh_rsa_free(&id->id_key); free(id->id_comm); free(id); } auth_id_count = 0; }