/* $Id: ssh_authagent.c,v 1.6 2001/02/11 03:35:09 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 support for FreSSH. * * TODO: * * - Add support for SSHv2 authentication agents. */ #include #include #include #include #include #include #include #include "sshd.h" #include "ssh_buffer.h" #include "ssh_authagent.h" #include "ssh_environ.h" /* * ssh_authagent_opensock: * * Open a connection to the authentication agent. */ int ssh_authagent_opensock(void) { struct sockaddr_un sun; const char *path; size_t len; int s; path = getenv(SSH_ENVVAR_AUTHAGENT_SOCKNAME); if (path == NULL) return (-1); sun.sun_family = AF_LOCAL; strncpy(sun.sun_path, path, sizeof(sun.sun_path)); sun.sun_path[sizeof(sun.sun_path) - 1] = '\0'; sun.sun_len = len = SUN_LEN(&sun) + 1; s = socket(PF_LOCAL, SOCK_STREAM, 0); if (s < 0) return (-1); /* Set close-on-exec. */ if (fcntl(s, F_SETFD, 1) < 0) { (void) close(s); return (-1); } if (connect(s, (struct sockaddr *) &sun, len) < 0) { (void) close(s); return (-1); } return (s); } /* * ssh_authagent_closesock: * * Close a connection to the authentication agent. */ void ssh_authagent_closesock(int s) { if (s != -1) (void) close(s); } /* * ssh_authagent_ispresent: * * Check to see if the authentication agent is present. */ int ssh_authagent_ispresent(void) { int s, rv; s = ssh_authagent_opensock(); if (s < 0) rv = 0; else { ssh_authagent_closesock(s); rv = 1; } return (rv); } /* * ssh_authagent_opensession: * * Open a session with the authentication agent. */ void * ssh_authagent_opensession(ssh_context_t *context) { struct authagent_state *cookie; cookie = calloc(1, sizeof(*cookie)); if (cookie == NULL) return (NULL); cookie->sock = ssh_authagent_opensock(); if (cookie->sock == -1) { free(cookie); return (NULL); } return (cookie); } /* * ssh_authagent_closesession: * * Close an authentication agent session. */ void ssh_authagent_closesession(ssh_context_t *context, void *arg) { struct authagent_state *cookie = arg; buf_cleanup(&cookie->buf); cookie->count = 0; ssh_authagent_closesock(cookie->sock); free(cookie); } /* * ssh_authagent_request: * * Send a request to the authentication agent and get a reply. */ int ssh_authagent_request(int s, struct ssh_buf *req, struct ssh_buf *repl) { u_int8_t pktdat[64]; u_int32_t pktlen; size_t len, rv; pktlen = htonl(buf_alllen(req)); if (write(s, &pktlen, sizeof(pktlen)) != sizeof(pktlen)) { SSH_ERROR("authagent_request: unable to send req len: %s\n", strerror(errno)); return (-1); } if (write(s, buf_alldata(req), buf_alllen(req)) != buf_alllen(req)) { SSH_ERROR("authagent_request: unable to send req: %s\n", strerror(errno)); return (-1); } /* * The first data back from the agent will be the length * of the reply packet. */ for (len = 0; len < sizeof(pktlen); len += rv) { rv = read(s, (caddr_t)&pktlen + len, sizeof(pktlen) - len); if (rv <= 0) { SSH_ERROR("authagent_request: reading reply len " "from agent socket: %s\n", strerror(errno)); return (-1); } } pktlen = ntohl(pktlen); if (pktlen > 256 * 1024) { SSH_ERROR("authagent_request: absurd reply length: %d\n", pktlen); return (-1); } /* * Now read the rest of the reply packet. */ for (len = 0; len < pktlen; len += rv) { rv = read(s, pktdat, pktlen > sizeof(pktdat) ? sizeof(pktdat) : pktlen); if (rv <= 0) { SSH_ERROR("authagent_request: reading reply from agent " "socket: %s\n", strerror(errno)); return (-1); } buf_put_nbytes(repl, rv, pktdat); } buf_rewind(repl); return (0); } /* * ssh_authagent_v1_getcount: * * Get the number of identities served by the agent. This * function actually contacts the agent to pull the indenties * over into a local buffer. */ int ssh_authagent_v1_getcount(ssh_context_t *context, void *arg) { struct authagent_state *cookie = arg; struct ssh_buf req; u_int8_t replcode; memset(&req, 0, sizeof(req)); buf_put_byte(&req, SSH_AGENT_V1_RSA_ID_REQUEST); buf_reset(&cookie->buf); if (ssh_authagent_request(cookie->sock, &req, &cookie->buf) != 0) { buf_cleanup(&req); buf_cleanup(&cookie->buf); return (0); } buf_cleanup(&req); if (buf_get_int8(&cookie->buf, &replcode) != 0) { SSH_ERROR("Unable to get auth agent reply code\n"); buf_cleanup(&cookie->buf); return (0); } switch (replcode) { case SSH_AGENT_V1_FAILURE: SSH_ERROR("Auth agent reports FAILURE\n"); buf_cleanup(&cookie->buf); return (0); case SSH_AGENT_V1_RSA_ID_REPLY: /* Hooray, we got an answer. */ break; default: SSH_ERROR("Auth agent sent unknown reply: 0x%02x\n", replcode); buf_cleanup(&cookie->buf); return (0); } if (buf_get_int32(&cookie->buf, &cookie->count) != 0) { SSH_ERROR("Unable to get identity count\n"); cookie->count = 0; buf_cleanup(&cookie->buf); } if (cookie->count > 1024) { SSH_ERROR("Absurd number of identities from agent: %d\n", cookie->count); cookie->count = 0; buf_cleanup(&cookie->buf); } return (cookie->count); } /* * ssh_authagent_v1_getkey: * * Get the next identity from the agent. Returns NULL * if we're all out. */ int ssh_authagent_v1_getkey(ssh_context_t *context, void *arg, ssh_RSA **keyp, char **commp) { struct authagent_state *cookie = arg; ssh_RSA *key; u_int8_t *comment; u_int32_t keybits, commlen; if (cookie->count == 0) return (-1); key = ssh_rsa_new(); if (key == NULL) { SSH_ERROR("authagent_getkey: unable to allocate new " "RSA struct.\n"); return (-1); } if (buf_get_int32(&cookie->buf, &keybits) != 0) { SSH_ERROR("authagent_getkey: unable to get keybits\n"); goto bad; } if (buf_get_bignum(&cookie->buf, &ssh_rsa_epart(key)) != 0) { SSH_ERROR("authagent_getkey: unable to get epart: %s\n", strerror(errno)); goto bad; } if (buf_get_bignum(&cookie->buf, &ssh_rsa_npart(key)) != 0) { SSH_ERROR("authagent_getkey: unable to get npart: %s\n", strerror(errno)); goto bad; } if (buf_get_int32(&cookie->buf, &commlen) != 0) { SSH_ERROR("authagent_getkey: unable to get comment len: %s\n", strerror(errno)); goto bad; } comment = NULL; if (buf_get_nbytes(&cookie->buf, commlen, &comment) != 0) { SSH_ERROR("authagent_getkey: unable to get comment: %s\n", strerror(errno)); goto bad; } comment[commlen] = '\0'; if (keybits != bignum_num_bits(ssh_rsa_npart(key))) SSH_ERROR("WARNING: %d bit key from agent is actually " "%d bits\n", keybits, bignum_num_bits(ssh_rsa_npart(key))); /* * Okay, have the key -- return it, and decrement the number * of keys remaining in our buffer. */ *keyp = key; *commp = (char *) comment; cookie->count--; return (0); bad: ssh_rsa_free(&key); /* * We've failed to unpack a key from the agent. We need to zap * the identity buffer, because the buffer is out of sync now. */ buf_cleanup(&cookie->buf); cookie->count = 0; return (-1); } /* * ssh_authagent_v1_rsachallenge: * * Given an RSA authentication challenge, get the * response from the agent. */ int ssh_authagent_v1_rsachallenge(ssh_context_t *context, void *arg, u_int8_t *session_id, ssh_RSA *key, ssh_BIGNUM *encr_chal, u_int8_t *response) { struct authagent_state *cookie = arg; struct ssh_buf req; struct ssh_buf repl; u_int8_t replcode; memset(&req, 0, sizeof(req)); memset(&repl, 0, sizeof(repl)); buf_put_byte(&req, SSH_AGENT_V1_RSA_CHALLENGE); buf_put_int32(&req, bignum_num_bits(ssh_rsa_npart(key))); buf_put_bignum(&req, ssh_rsa_epart(key)); buf_put_bignum(&req, ssh_rsa_npart(key)); buf_put_bignum(&req, encr_chal); buf_put_nbytes(&req, 16, session_id); buf_put_int32(&req, 1 /* response type */); if (ssh_authagent_request(cookie->sock, &req, &repl) != 0) { buf_cleanup(&req); buf_cleanup(&repl); return (-1); } buf_cleanup(&req); if (buf_get_int8(&repl, &replcode) != 0) { SSH_ERROR("Unable to get auth agent reply code\n"); buf_cleanup(&repl); return (-1); } switch (replcode) { case SSH_AGENT_V1_FAILURE: SSH_ERROR("Auth agent reports FAILURE\n"); buf_cleanup(&repl); return (-1); case SSH_AGENT_V1_RSA_RESPONSE: /* Hooray, we got an answer. */ break; default: SSH_ERROR("Auth agent sent unknown reply: 0x%02x\n", replcode); buf_cleanup(&repl); return (-1); } if (buf_len(&repl) < 16) { SSH_ERROR("Short RSA reposonse from agent\n"); buf_cleanup(&repl); return (-1); } memcpy(response, buf_data(&repl), 16); buf_cleanup(&repl); return (0); }