ssh-agent.c revision 255767
1255767Sdes/* $OpenBSD: ssh-agent.c,v 1.177 2013/07/20 01:50:20 djm Exp $ */ 257429Smarkm/* 357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 557429Smarkm * All rights reserved 657429Smarkm * The authentication agent program. 765674Skris * 865674Skris * As far as I am concerned, the code I have written for this software 965674Skris * can be used freely for any purpose. Any derived versions of this 1065674Skris * software must be clearly marked as such, and if the derived work is 1165674Skris * incompatible with the protocol description in the RFC file, it must be 1265674Skris * called by a name other than "ssh" or "Secure Shell". 1365674Skris * 1492559Sdes * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 1565674Skris * 1665674Skris * Redistribution and use in source and binary forms, with or without 1765674Skris * modification, are permitted provided that the following conditions 1865674Skris * are met: 1965674Skris * 1. Redistributions of source code must retain the above copyright 2065674Skris * notice, this list of conditions and the following disclaimer. 2165674Skris * 2. Redistributions in binary form must reproduce the above copyright 2265674Skris * notice, this list of conditions and the following disclaimer in the 2365674Skris * documentation and/or other materials provided with the distribution. 2465674Skris * 2565674Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2665674Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2765674Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2865674Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2965674Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3065674Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3165674Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3265674Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3365674Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3465674Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3557429Smarkm */ 3657429Smarkm 3757429Smarkm#include "includes.h" 38162856Sdes__RCSID("$FreeBSD: head/crypto/openssh/ssh-agent.c 255767 2013-09-21 21:36:09Z des $"); 39162856Sdes 40162856Sdes#include <sys/types.h> 41162856Sdes#include <sys/param.h> 42162856Sdes#include <sys/resource.h> 43162856Sdes#include <sys/stat.h> 44162856Sdes#include <sys/socket.h> 45162856Sdes#ifdef HAVE_SYS_TIME_H 46162856Sdes# include <sys/time.h> 47162856Sdes#endif 48162856Sdes#ifdef HAVE_SYS_UN_H 49162856Sdes# include <sys/un.h> 50162856Sdes#endif 51106130Sdes#include "openbsd-compat/sys-queue.h" 5257429Smarkm 5376262Sgreen#include <openssl/evp.h> 5476262Sgreen#include <openssl/md5.h> 55181111Sdes#include "openbsd-compat/openssl-compat.h" 5676262Sgreen 57162856Sdes#include <errno.h> 58162856Sdes#include <fcntl.h> 59162856Sdes#ifdef HAVE_PATHS_H 60162856Sdes# include <paths.h> 61162856Sdes#endif 62162856Sdes#include <signal.h> 63162856Sdes#include <stdarg.h> 64162856Sdes#include <stdio.h> 65162856Sdes#include <stdlib.h> 66162856Sdes#include <time.h> 67162856Sdes#include <string.h> 68162856Sdes#include <unistd.h> 69162856Sdes 70162856Sdes#include "xmalloc.h" 7157429Smarkm#include "ssh.h" 7257429Smarkm#include "rsa.h" 7357429Smarkm#include "buffer.h" 7465674Skris#include "key.h" 7565674Skris#include "authfd.h" 7669591Sgreen#include "compat.h" 7776262Sgreen#include "log.h" 78113911Sdes#include "misc.h" 7957429Smarkm 80204917Sdes#ifdef ENABLE_PKCS11 81204917Sdes#include "ssh-pkcs11.h" 8292559Sdes#endif 8392559Sdes 84128460Sdes#if defined(HAVE_SYS_PRCTL_H) 85128460Sdes#include <sys/prctl.h> /* For prctl() and PR_SET_DUMPABLE */ 86128460Sdes#endif 87128460Sdes 8892559Sdestypedef enum { 8992559Sdes AUTH_UNUSED, 9092559Sdes AUTH_SOCKET, 9192559Sdes AUTH_CONNECTION 9292559Sdes} sock_type; 9392559Sdes 9457429Smarkmtypedef struct { 9557429Smarkm int fd; 9692559Sdes sock_type type; 9757429Smarkm Buffer input; 9857429Smarkm Buffer output; 9998684Sdes Buffer request; 10057429Smarkm} SocketEntry; 10157429Smarkm 10276262Sgreenu_int sockets_alloc = 0; 10357429SmarkmSocketEntry *sockets = NULL; 10457429Smarkm 10592559Sdestypedef struct identity { 10692559Sdes TAILQ_ENTRY(identity) next; 10765674Skris Key *key; 10857429Smarkm char *comment; 109204917Sdes char *provider; 110255767Sdes time_t death; 111113911Sdes u_int confirm; 11257429Smarkm} Identity; 11357429Smarkm 11465674Skristypedef struct { 11565674Skris int nentries; 11692559Sdes TAILQ_HEAD(idqueue, identity) idlist; 11765674Skris} Idtab; 11857429Smarkm 11965674Skris/* private key table, one per protocol version */ 12065674SkrisIdtab idtable[3]; 12165674Skris 12257429Smarkmint max_fd = 0; 12357429Smarkm 12457429Smarkm/* pid of shell == parent of agent */ 12560576Skrispid_t parent_pid = -1; 126255767Sdestime_t parent_alive_interval = 0; 12757429Smarkm 12857429Smarkm/* pathname and directory for AUTH_SOCKET */ 129162856Sdeschar socket_name[MAXPATHLEN]; 130162856Sdeschar socket_dir[MAXPATHLEN]; 13157429Smarkm 13298684Sdes/* locking */ 13398684Sdesint locked = 0; 13498684Sdeschar *lock_passwd = NULL; 13598684Sdes 13657429Smarkmextern char *__progname; 13757429Smarkm 138255767Sdes/* Default lifetime in seconds (0 == forever) */ 139255767Sdesstatic long lifetime = 0; 140113911Sdes 141226103Sdes/* 142226103Sdes * Client connection count; incremented in new_socket() and decremented in 143226103Sdes * close_socket(). When it reaches 0, ssh-agent will exit. Since it is 144226103Sdes * normally initialized to 1, it will never reach 0. However, if the -x 145226103Sdes * option is specified, it is initialized to 0 in main(); in that case, 146226103Sdes * ssh-agent will exit as soon as it has had at least one client but no 147226103Sdes * longer has any. 148226103Sdes */ 149226103Sdesstatic int xcount = 1; 150226103Sdes 15192559Sdesstatic void 152106130Sdesclose_socket(SocketEntry *e) 153106130Sdes{ 154226103Sdes int last = 0; 155226103Sdes 156226103Sdes if (e->type == AUTH_CONNECTION) { 157226103Sdes debug("xcount %d -> %d", xcount, xcount - 1); 158226103Sdes if (--xcount == 0) 159226103Sdes last = 1; 160226103Sdes } 161106130Sdes close(e->fd); 162106130Sdes e->fd = -1; 163106130Sdes e->type = AUTH_UNUSED; 164106130Sdes buffer_free(&e->input); 165106130Sdes buffer_free(&e->output); 166106130Sdes buffer_free(&e->request); 167226103Sdes if (last) 168226103Sdes cleanup_exit(0); 169106130Sdes} 170106130Sdes 171106130Sdesstatic void 17265674Skrisidtab_init(void) 17357429Smarkm{ 17465674Skris int i; 17599063Sdes 17692559Sdes for (i = 0; i <=2; i++) { 17792559Sdes TAILQ_INIT(&idtable[i].idlist); 17865674Skris idtable[i].nentries = 0; 17965674Skris } 18065674Skris} 18165674Skris 18265674Skris/* return private key table for requested protocol version */ 18392559Sdesstatic Idtab * 18465674Skrisidtab_lookup(int version) 18565674Skris{ 18665674Skris if (version < 1 || version > 2) 18765674Skris fatal("internal error, bad protocol version %d", version); 18865674Skris return &idtable[version]; 18965674Skris} 19065674Skris 19198684Sdesstatic void 19298684Sdesfree_identity(Identity *id) 19398684Sdes{ 19498684Sdes key_free(id->key); 195255767Sdes free(id->provider); 196255767Sdes free(id->comment); 197255767Sdes free(id); 19898684Sdes} 19998684Sdes 20065674Skris/* return matching private key for given public key */ 20192559Sdesstatic Identity * 20292559Sdeslookup_identity(Key *key, int version) 20365674Skris{ 20492559Sdes Identity *id; 20592559Sdes 20665674Skris Idtab *tab = idtab_lookup(version); 20792559Sdes TAILQ_FOREACH(id, &tab->idlist, next) { 20892559Sdes if (key_equal(key, id->key)) 20992559Sdes return (id); 21065674Skris } 21192559Sdes return (NULL); 21265674Skris} 21365674Skris 214113911Sdes/* Check confirmation of keysign request */ 215113911Sdesstatic int 216113911Sdesconfirm_key(Identity *id) 217113911Sdes{ 218147005Sdes char *p; 219113911Sdes int ret = -1; 220113911Sdes 221113911Sdes p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); 222147005Sdes if (ask_permission("Allow use of key %s?\nKey fingerprint %s.", 223147005Sdes id->comment, p)) 224147005Sdes ret = 0; 225255767Sdes free(p); 226147005Sdes 227113911Sdes return (ret); 228113911Sdes} 229113911Sdes 23065674Skris/* send list of supported public keys to 'client' */ 23192559Sdesstatic void 23265674Skrisprocess_request_identities(SocketEntry *e, int version) 23365674Skris{ 23465674Skris Idtab *tab = idtab_lookup(version); 23599063Sdes Identity *id; 23657429Smarkm Buffer msg; 23757429Smarkm 23857429Smarkm buffer_init(&msg); 23965674Skris buffer_put_char(&msg, (version == 1) ? 24065674Skris SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); 24165674Skris buffer_put_int(&msg, tab->nentries); 24292559Sdes TAILQ_FOREACH(id, &tab->idlist, next) { 24376262Sgreen if (id->key->type == KEY_RSA1) { 24465674Skris buffer_put_int(&msg, BN_num_bits(id->key->rsa->n)); 24565674Skris buffer_put_bignum(&msg, id->key->rsa->e); 24665674Skris buffer_put_bignum(&msg, id->key->rsa->n); 24765674Skris } else { 24876262Sgreen u_char *blob; 24976262Sgreen u_int blen; 25076262Sgreen key_to_blob(id->key, &blob, &blen); 25165674Skris buffer_put_string(&msg, blob, blen); 252255767Sdes free(blob); 25365674Skris } 25465674Skris buffer_put_cstring(&msg, id->comment); 25557429Smarkm } 25657429Smarkm buffer_put_int(&e->output, buffer_len(&msg)); 25757429Smarkm buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); 25857429Smarkm buffer_free(&msg); 25957429Smarkm} 26057429Smarkm 26165674Skris/* ssh1 only */ 26292559Sdesstatic void 26365674Skrisprocess_authentication_challenge1(SocketEntry *e) 26457429Smarkm{ 26599063Sdes u_char buf[32], mdbuf[16], session_id[16]; 26699063Sdes u_int response_type; 26799063Sdes BIGNUM *challenge; 26892559Sdes Identity *id; 26965674Skris int i, len; 27057429Smarkm Buffer msg; 27157429Smarkm MD5_CTX md; 27299063Sdes Key *key; 27357429Smarkm 27457429Smarkm buffer_init(&msg); 27576262Sgreen key = key_new(KEY_RSA1); 27692559Sdes if ((challenge = BN_new()) == NULL) 27792559Sdes fatal("process_authentication_challenge1: BN_new failed"); 27865674Skris 27999063Sdes (void) buffer_get_int(&e->request); /* ignored */ 28098684Sdes buffer_get_bignum(&e->request, key->rsa->e); 28198684Sdes buffer_get_bignum(&e->request, key->rsa->n); 28298684Sdes buffer_get_bignum(&e->request, challenge); 28357429Smarkm 28465674Skris /* Only protocol 1.1 is supported */ 28598684Sdes if (buffer_len(&e->request) == 0) 28665674Skris goto failure; 28798684Sdes buffer_get(&e->request, session_id, 16); 28898684Sdes response_type = buffer_get_int(&e->request); 28965674Skris if (response_type != 1) 29065674Skris goto failure; 29157429Smarkm 29292559Sdes id = lookup_identity(key, 1); 293113911Sdes if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { 29492559Sdes Key *private = id->key; 29565674Skris /* Decrypt the challenge using the private key. */ 29672397Skris if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0) 29772397Skris goto failure; 29857429Smarkm 29965674Skris /* The response is MD5 of decrypted challenge plus session id. */ 30065674Skris len = BN_num_bytes(challenge); 30165674Skris if (len <= 0 || len > 32) { 302124211Sdes logit("process_authentication_challenge: bad challenge length %d", len); 30365674Skris goto failure; 30465674Skris } 30565674Skris memset(buf, 0, 32); 30665674Skris BN_bn2bin(challenge, buf + 32 - len); 30765674Skris MD5_Init(&md); 30865674Skris MD5_Update(&md, buf, 32); 30965674Skris MD5_Update(&md, session_id, 16); 31065674Skris MD5_Final(mdbuf, &md); 31157429Smarkm 31265674Skris /* Send the response. */ 31365674Skris buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); 31465674Skris for (i = 0; i < 16; i++) 31565674Skris buffer_put_char(&msg, mdbuf[i]); 31665674Skris goto send; 31765674Skris } 31857429Smarkm 31965674Skrisfailure: 32065674Skris /* Unknown identity or protocol error. Send failure. */ 32157429Smarkm buffer_put_char(&msg, SSH_AGENT_FAILURE); 32257429Smarkmsend: 32357429Smarkm buffer_put_int(&e->output, buffer_len(&msg)); 32465674Skris buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); 32565674Skris key_free(key); 32665674Skris BN_clear_free(challenge); 32765674Skris buffer_free(&msg); 32865674Skris} 32965674Skris 33065674Skris/* ssh2 only */ 33192559Sdesstatic void 33265674Skrisprocess_sign_request2(SocketEntry *e) 33365674Skris{ 33476262Sgreen u_char *blob, *data, *signature = NULL; 33576262Sgreen u_int blen, dlen, slen = 0; 33699063Sdes extern int datafellows; 337181111Sdes int odatafellows; 33899063Sdes int ok = -1, flags; 33965674Skris Buffer msg; 34099063Sdes Key *key; 34165674Skris 34265674Skris datafellows = 0; 34376262Sgreen 34498684Sdes blob = buffer_get_string(&e->request, &blen); 34598684Sdes data = buffer_get_string(&e->request, &dlen); 34665674Skris 34798684Sdes flags = buffer_get_int(&e->request); 348181111Sdes odatafellows = datafellows; 34969591Sgreen if (flags & SSH_AGENT_OLD_SIGNATURE) 35069591Sgreen datafellows = SSH_BUG_SIGBLOB; 35169591Sgreen 35276262Sgreen key = key_from_blob(blob, blen); 35365674Skris if (key != NULL) { 35492559Sdes Identity *id = lookup_identity(key, 2); 355113911Sdes if (id != NULL && (!id->confirm || confirm_key(id) == 0)) 35692559Sdes ok = key_sign(id->key, &signature, &slen, data, dlen); 357162856Sdes key_free(key); 35865674Skris } 35965674Skris buffer_init(&msg); 36065674Skris if (ok == 0) { 36165674Skris buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE); 36265674Skris buffer_put_string(&msg, signature, slen); 36365674Skris } else { 36465674Skris buffer_put_char(&msg, SSH_AGENT_FAILURE); 36565674Skris } 36665674Skris buffer_put_int(&e->output, buffer_len(&msg)); 36757429Smarkm buffer_append(&e->output, buffer_ptr(&msg), 36865674Skris buffer_len(&msg)); 36957429Smarkm buffer_free(&msg); 370255767Sdes free(data); 371255767Sdes free(blob); 372255767Sdes free(signature); 373181111Sdes datafellows = odatafellows; 37457429Smarkm} 37557429Smarkm 37665674Skris/* shared */ 37792559Sdesstatic void 37865674Skrisprocess_remove_identity(SocketEntry *e, int version) 37957429Smarkm{ 38099063Sdes u_int blen, bits; 38199063Sdes int success = 0; 38292559Sdes Key *key = NULL; 38376262Sgreen u_char *blob; 38457429Smarkm 38592559Sdes switch (version) { 38665674Skris case 1: 38776262Sgreen key = key_new(KEY_RSA1); 38898684Sdes bits = buffer_get_int(&e->request); 38998684Sdes buffer_get_bignum(&e->request, key->rsa->e); 39098684Sdes buffer_get_bignum(&e->request, key->rsa->n); 39157429Smarkm 39265674Skris if (bits != key_size(key)) 393124211Sdes logit("Warning: identity keysize mismatch: actual %u, announced %u", 39476262Sgreen key_size(key), bits); 39565674Skris break; 39665674Skris case 2: 39798684Sdes blob = buffer_get_string(&e->request, &blen); 39876262Sgreen key = key_from_blob(blob, blen); 399255767Sdes free(blob); 40065674Skris break; 40165674Skris } 40265674Skris if (key != NULL) { 40392559Sdes Identity *id = lookup_identity(key, version); 40492559Sdes if (id != NULL) { 40557429Smarkm /* 40657429Smarkm * We have this key. Free the old key. Since we 407157019Sdes * don't want to leave empty slots in the middle of 40876262Sgreen * the array, we actually free the key there and move 40976262Sgreen * all the entries between the empty slot and the end 41076262Sgreen * of the array. 41157429Smarkm */ 41265674Skris Idtab *tab = idtab_lookup(version); 41376262Sgreen if (tab->nentries < 1) 41476262Sgreen fatal("process_remove_identity: " 41576262Sgreen "internal error: tab->nentries %d", 41676262Sgreen tab->nentries); 41792559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 41892559Sdes free_identity(id); 41965674Skris tab->nentries--; 42065674Skris success = 1; 42157429Smarkm } 42265674Skris key_free(key); 42365674Skris } 42457429Smarkm buffer_put_int(&e->output, 1); 42565674Skris buffer_put_char(&e->output, 42665674Skris success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 42757429Smarkm} 42857429Smarkm 42992559Sdesstatic void 43065674Skrisprocess_remove_all_identities(SocketEntry *e, int version) 43157429Smarkm{ 43265674Skris Idtab *tab = idtab_lookup(version); 43392559Sdes Identity *id; 43457429Smarkm 43557429Smarkm /* Loop over all identities and clear the keys. */ 43692559Sdes for (id = TAILQ_FIRST(&tab->idlist); id; 43792559Sdes id = TAILQ_FIRST(&tab->idlist)) { 43892559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 43992559Sdes free_identity(id); 44057429Smarkm } 44157429Smarkm 44257429Smarkm /* Mark that there are no identities. */ 44365674Skris tab->nentries = 0; 44457429Smarkm 44557429Smarkm /* Send success. */ 44657429Smarkm buffer_put_int(&e->output, 1); 44757429Smarkm buffer_put_char(&e->output, SSH_AGENT_SUCCESS); 44857429Smarkm} 44957429Smarkm 450181111Sdes/* removes expired keys and returns number of seconds until the next expiry */ 451255767Sdesstatic time_t 45298684Sdesreaper(void) 45398684Sdes{ 454255767Sdes time_t deadline = 0, now = monotime(); 45598684Sdes Identity *id, *nxt; 45698684Sdes int version; 45799063Sdes Idtab *tab; 45898684Sdes 45998684Sdes for (version = 1; version < 3; version++) { 46098684Sdes tab = idtab_lookup(version); 46198684Sdes for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { 46298684Sdes nxt = TAILQ_NEXT(id, next); 463181111Sdes if (id->death == 0) 464181111Sdes continue; 465181111Sdes if (now >= id->death) { 466181111Sdes debug("expiring key '%s'", id->comment); 46798684Sdes TAILQ_REMOVE(&tab->idlist, id, next); 46898684Sdes free_identity(id); 46998684Sdes tab->nentries--; 470181111Sdes } else 471181111Sdes deadline = (deadline == 0) ? id->death : 472181111Sdes MIN(deadline, id->death); 47398684Sdes } 47498684Sdes } 475181111Sdes if (deadline == 0 || deadline <= now) 476181111Sdes return 0; 477181111Sdes else 478181111Sdes return (deadline - now); 47998684Sdes} 48098684Sdes 48198684Sdesstatic void 48265674Skrisprocess_add_identity(SocketEntry *e, int version) 48357429Smarkm{ 48499063Sdes Idtab *tab = idtab_lookup(version); 485181111Sdes Identity *id; 486255767Sdes int type, success = 0, confirm = 0; 48799063Sdes char *type_name, *comment; 488255767Sdes time_t death = 0; 48965674Skris Key *k = NULL; 490221420Sdes#ifdef OPENSSL_HAS_ECC 491221420Sdes BIGNUM *exponent; 492221420Sdes EC_POINT *q; 493221420Sdes char *curve; 494221420Sdes#endif 495204917Sdes u_char *cert; 496204917Sdes u_int len; 49757429Smarkm 49865674Skris switch (version) { 49965674Skris case 1: 50076262Sgreen k = key_new_private(KEY_RSA1); 50199063Sdes (void) buffer_get_int(&e->request); /* ignored */ 50298684Sdes buffer_get_bignum(&e->request, k->rsa->n); 50398684Sdes buffer_get_bignum(&e->request, k->rsa->e); 50498684Sdes buffer_get_bignum(&e->request, k->rsa->d); 50598684Sdes buffer_get_bignum(&e->request, k->rsa->iqmp); 50657429Smarkm 50765674Skris /* SSH and SSL have p and q swapped */ 50898684Sdes buffer_get_bignum(&e->request, k->rsa->q); /* p */ 50998684Sdes buffer_get_bignum(&e->request, k->rsa->p); /* q */ 51057429Smarkm 51165674Skris /* Generate additional parameters */ 51292559Sdes rsa_generate_additional_parameters(k->rsa); 51365674Skris break; 51465674Skris case 2: 51598684Sdes type_name = buffer_get_string(&e->request, NULL); 51676262Sgreen type = key_type_from_name(type_name); 51792559Sdes switch (type) { 51876262Sgreen case KEY_DSA: 51976262Sgreen k = key_new_private(type); 52098684Sdes buffer_get_bignum2(&e->request, k->dsa->p); 52198684Sdes buffer_get_bignum2(&e->request, k->dsa->q); 52298684Sdes buffer_get_bignum2(&e->request, k->dsa->g); 52398684Sdes buffer_get_bignum2(&e->request, k->dsa->pub_key); 52498684Sdes buffer_get_bignum2(&e->request, k->dsa->priv_key); 52576262Sgreen break; 526215116Sdes case KEY_DSA_CERT_V00: 527204917Sdes case KEY_DSA_CERT: 528204917Sdes cert = buffer_get_string(&e->request, &len); 529204917Sdes if ((k = key_from_blob(cert, len)) == NULL) 530204917Sdes fatal("Certificate parse failed"); 531255767Sdes free(cert); 532204917Sdes key_add_private(k); 533204917Sdes buffer_get_bignum2(&e->request, k->dsa->priv_key); 534204917Sdes break; 535221420Sdes#ifdef OPENSSL_HAS_ECC 536221420Sdes case KEY_ECDSA: 537221420Sdes k = key_new_private(type); 538221420Sdes k->ecdsa_nid = key_ecdsa_nid_from_name(type_name); 539221420Sdes curve = buffer_get_string(&e->request, NULL); 540221420Sdes if (k->ecdsa_nid != key_curve_name_to_nid(curve)) 541221420Sdes fatal("%s: curve names mismatch", __func__); 542255767Sdes free(curve); 543221420Sdes k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); 544221420Sdes if (k->ecdsa == NULL) 545221420Sdes fatal("%s: EC_KEY_new_by_curve_name failed", 546221420Sdes __func__); 547221420Sdes q = EC_POINT_new(EC_KEY_get0_group(k->ecdsa)); 548221420Sdes if (q == NULL) 549221420Sdes fatal("%s: BN_new failed", __func__); 550221420Sdes if ((exponent = BN_new()) == NULL) 551221420Sdes fatal("%s: BN_new failed", __func__); 552221420Sdes buffer_get_ecpoint(&e->request, 553221420Sdes EC_KEY_get0_group(k->ecdsa), q); 554221420Sdes buffer_get_bignum2(&e->request, exponent); 555221420Sdes if (EC_KEY_set_public_key(k->ecdsa, q) != 1) 556221420Sdes fatal("%s: EC_KEY_set_public_key failed", 557221420Sdes __func__); 558221420Sdes if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) 559221420Sdes fatal("%s: EC_KEY_set_private_key failed", 560221420Sdes __func__); 561221420Sdes if (key_ec_validate_public(EC_KEY_get0_group(k->ecdsa), 562221420Sdes EC_KEY_get0_public_key(k->ecdsa)) != 0) 563221420Sdes fatal("%s: bad ECDSA public key", __func__); 564221420Sdes if (key_ec_validate_private(k->ecdsa) != 0) 565221420Sdes fatal("%s: bad ECDSA private key", __func__); 566221420Sdes BN_clear_free(exponent); 567221420Sdes EC_POINT_free(q); 568221420Sdes break; 569221420Sdes case KEY_ECDSA_CERT: 570221420Sdes cert = buffer_get_string(&e->request, &len); 571221420Sdes if ((k = key_from_blob(cert, len)) == NULL) 572221420Sdes fatal("Certificate parse failed"); 573255767Sdes free(cert); 574221420Sdes key_add_private(k); 575221420Sdes if ((exponent = BN_new()) == NULL) 576221420Sdes fatal("%s: BN_new failed", __func__); 577221420Sdes buffer_get_bignum2(&e->request, exponent); 578221420Sdes if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) 579221420Sdes fatal("%s: EC_KEY_set_private_key failed", 580221420Sdes __func__); 581221420Sdes if (key_ec_validate_public(EC_KEY_get0_group(k->ecdsa), 582221420Sdes EC_KEY_get0_public_key(k->ecdsa)) != 0 || 583221420Sdes key_ec_validate_private(k->ecdsa) != 0) 584221420Sdes fatal("%s: bad ECDSA key", __func__); 585221420Sdes BN_clear_free(exponent); 586221420Sdes break; 587221420Sdes#endif /* OPENSSL_HAS_ECC */ 58876262Sgreen case KEY_RSA: 58976262Sgreen k = key_new_private(type); 59098684Sdes buffer_get_bignum2(&e->request, k->rsa->n); 59198684Sdes buffer_get_bignum2(&e->request, k->rsa->e); 59298684Sdes buffer_get_bignum2(&e->request, k->rsa->d); 59398684Sdes buffer_get_bignum2(&e->request, k->rsa->iqmp); 59498684Sdes buffer_get_bignum2(&e->request, k->rsa->p); 59598684Sdes buffer_get_bignum2(&e->request, k->rsa->q); 59676262Sgreen 59776262Sgreen /* Generate additional parameters */ 59892559Sdes rsa_generate_additional_parameters(k->rsa); 59976262Sgreen break; 600215116Sdes case KEY_RSA_CERT_V00: 601204917Sdes case KEY_RSA_CERT: 602204917Sdes cert = buffer_get_string(&e->request, &len); 603204917Sdes if ((k = key_from_blob(cert, len)) == NULL) 604204917Sdes fatal("Certificate parse failed"); 605255767Sdes free(cert); 606204917Sdes key_add_private(k); 607204917Sdes buffer_get_bignum2(&e->request, k->rsa->d); 608204917Sdes buffer_get_bignum2(&e->request, k->rsa->iqmp); 609204917Sdes buffer_get_bignum2(&e->request, k->rsa->p); 610204917Sdes buffer_get_bignum2(&e->request, k->rsa->q); 611204917Sdes break; 61276262Sgreen default: 613255767Sdes free(type_name); 61498684Sdes buffer_clear(&e->request); 61565674Skris goto send; 61657429Smarkm } 617255767Sdes free(type_name); 61865674Skris break; 61965674Skris } 620113911Sdes /* enable blinding */ 621113911Sdes switch (k->type) { 622113911Sdes case KEY_RSA: 623215116Sdes case KEY_RSA_CERT_V00: 624204917Sdes case KEY_RSA_CERT: 625113911Sdes case KEY_RSA1: 626113911Sdes if (RSA_blinding_on(k->rsa, NULL) != 1) { 627113911Sdes error("process_add_identity: RSA_blinding_on failed"); 628113911Sdes key_free(k); 629113911Sdes goto send; 630113911Sdes } 631113911Sdes break; 632113911Sdes } 63398684Sdes comment = buffer_get_string(&e->request, NULL); 63465674Skris if (k == NULL) { 635255767Sdes free(comment); 63665674Skris goto send; 63765674Skris } 63898684Sdes while (buffer_len(&e->request)) { 639181111Sdes switch ((type = buffer_get_char(&e->request))) { 64098684Sdes case SSH_AGENT_CONSTRAIN_LIFETIME: 641255767Sdes death = monotime() + buffer_get_int(&e->request); 64298684Sdes break; 643113911Sdes case SSH_AGENT_CONSTRAIN_CONFIRM: 644113911Sdes confirm = 1; 645113911Sdes break; 64698684Sdes default: 647181111Sdes error("process_add_identity: " 648181111Sdes "Unknown constraint type %d", type); 649255767Sdes free(comment); 650181111Sdes key_free(k); 651181111Sdes goto send; 65298684Sdes } 65398684Sdes } 654181111Sdes success = 1; 655113911Sdes if (lifetime && !death) 656255767Sdes death = monotime() + lifetime; 657181111Sdes if ((id = lookup_identity(k, version)) == NULL) { 658204917Sdes id = xcalloc(1, sizeof(Identity)); 65992559Sdes id->key = k; 66092559Sdes TAILQ_INSERT_TAIL(&tab->idlist, id, next); 66165674Skris /* Increment the number of identities. */ 66265674Skris tab->nentries++; 66365674Skris } else { 66465674Skris key_free(k); 665255767Sdes free(id->comment); 66665674Skris } 667181111Sdes id->comment = comment; 668181111Sdes id->death = death; 669181111Sdes id->confirm = confirm; 67065674Skrissend: 67157429Smarkm buffer_put_int(&e->output, 1); 67265674Skris buffer_put_char(&e->output, 67365674Skris success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 67457429Smarkm} 67557429Smarkm 67698684Sdes/* XXX todo: encrypt sensitive data with passphrase */ 67798684Sdesstatic void 67898684Sdesprocess_lock_agent(SocketEntry *e, int lock) 67998684Sdes{ 68099063Sdes int success = 0; 68198684Sdes char *passwd; 68292559Sdes 68398684Sdes passwd = buffer_get_string(&e->request, NULL); 68498684Sdes if (locked && !lock && strcmp(passwd, lock_passwd) == 0) { 68598684Sdes locked = 0; 68698684Sdes memset(lock_passwd, 0, strlen(lock_passwd)); 687255767Sdes free(lock_passwd); 68898684Sdes lock_passwd = NULL; 68998684Sdes success = 1; 69098684Sdes } else if (!locked && lock) { 69198684Sdes locked = 1; 69298684Sdes lock_passwd = xstrdup(passwd); 69398684Sdes success = 1; 69498684Sdes } 69598684Sdes memset(passwd, 0, strlen(passwd)); 696255767Sdes free(passwd); 69798684Sdes 69898684Sdes buffer_put_int(&e->output, 1); 69998684Sdes buffer_put_char(&e->output, 70098684Sdes success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 70198684Sdes} 70298684Sdes 70398684Sdesstatic void 70498684Sdesno_identities(SocketEntry *e, u_int type) 70598684Sdes{ 70698684Sdes Buffer msg; 70798684Sdes 70898684Sdes buffer_init(&msg); 70998684Sdes buffer_put_char(&msg, 71098684Sdes (type == SSH_AGENTC_REQUEST_RSA_IDENTITIES) ? 71198684Sdes SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); 71298684Sdes buffer_put_int(&msg, 0); 71398684Sdes buffer_put_int(&e->output, buffer_len(&msg)); 71498684Sdes buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); 71598684Sdes buffer_free(&msg); 71698684Sdes} 71798684Sdes 718204917Sdes#ifdef ENABLE_PKCS11 71992559Sdesstatic void 720181111Sdesprocess_add_smartcard_key(SocketEntry *e) 72192559Sdes{ 722204917Sdes char *provider = NULL, *pin; 723255767Sdes int i, type, version, count = 0, success = 0, confirm = 0; 724255767Sdes time_t death = 0; 725204917Sdes Key **keys = NULL, *k; 72698684Sdes Identity *id; 72792559Sdes Idtab *tab; 72892559Sdes 729204917Sdes provider = buffer_get_string(&e->request, NULL); 73098684Sdes pin = buffer_get_string(&e->request, NULL); 731124211Sdes 732124211Sdes while (buffer_len(&e->request)) { 733181111Sdes switch ((type = buffer_get_char(&e->request))) { 734124211Sdes case SSH_AGENT_CONSTRAIN_LIFETIME: 735255767Sdes death = monotime() + buffer_get_int(&e->request); 736124211Sdes break; 737124211Sdes case SSH_AGENT_CONSTRAIN_CONFIRM: 738124211Sdes confirm = 1; 739124211Sdes break; 740124211Sdes default: 741181111Sdes error("process_add_smartcard_key: " 742181111Sdes "Unknown constraint type %d", type); 743181111Sdes goto send; 744124211Sdes } 745124211Sdes } 746124211Sdes if (lifetime && !death) 747255767Sdes death = monotime() + lifetime; 748124211Sdes 749204917Sdes count = pkcs11_add_provider(provider, pin, &keys); 750204917Sdes for (i = 0; i < count; i++) { 75198684Sdes k = keys[i]; 75298684Sdes version = k->type == KEY_RSA1 ? 1 : 2; 75398684Sdes tab = idtab_lookup(version); 75498684Sdes if (lookup_identity(k, version) == NULL) { 755204917Sdes id = xcalloc(1, sizeof(Identity)); 75698684Sdes id->key = k; 757204917Sdes id->provider = xstrdup(provider); 758204917Sdes id->comment = xstrdup(provider); /* XXX */ 759124211Sdes id->death = death; 760124211Sdes id->confirm = confirm; 76198684Sdes TAILQ_INSERT_TAIL(&tab->idlist, id, next); 76298684Sdes tab->nentries++; 76398684Sdes success = 1; 76498684Sdes } else { 76598684Sdes key_free(k); 76698684Sdes } 76798684Sdes keys[i] = NULL; 76892559Sdes } 76992559Sdessend: 770255767Sdes free(pin); 771255767Sdes free(provider); 772255767Sdes free(keys); 77392559Sdes buffer_put_int(&e->output, 1); 77492559Sdes buffer_put_char(&e->output, 77592559Sdes success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 77692559Sdes} 77792559Sdes 77892559Sdesstatic void 77992559Sdesprocess_remove_smartcard_key(SocketEntry *e) 78092559Sdes{ 781204917Sdes char *provider = NULL, *pin = NULL; 782204917Sdes int version, success = 0; 783204917Sdes Identity *id, *nxt; 78498684Sdes Idtab *tab; 78592559Sdes 786204917Sdes provider = buffer_get_string(&e->request, NULL); 78798684Sdes pin = buffer_get_string(&e->request, NULL); 788255767Sdes free(pin); 78992559Sdes 790204917Sdes for (version = 1; version < 3; version++) { 791204917Sdes tab = idtab_lookup(version); 792204917Sdes for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { 793204917Sdes nxt = TAILQ_NEXT(id, next); 794204917Sdes if (!strcmp(provider, id->provider)) { 795204917Sdes TAILQ_REMOVE(&tab->idlist, id, next); 796204917Sdes free_identity(id); 797204917Sdes tab->nentries--; 798204917Sdes } 79992559Sdes } 80092559Sdes } 801204917Sdes if (pkcs11_del_provider(provider) == 0) 802204917Sdes success = 1; 803204917Sdes else 804204917Sdes error("process_remove_smartcard_key:" 805204917Sdes " pkcs11_del_provider failed"); 806255767Sdes free(provider); 80792559Sdes buffer_put_int(&e->output, 1); 80892559Sdes buffer_put_char(&e->output, 80992559Sdes success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 81092559Sdes} 811204917Sdes#endif /* ENABLE_PKCS11 */ 81292559Sdes 81365674Skris/* dispatch incoming messages */ 81465674Skris 81592559Sdesstatic void 81657429Smarkmprocess_message(SocketEntry *e) 81757429Smarkm{ 81899063Sdes u_int msg_len, type; 81976262Sgreen u_char *cp; 82098684Sdes 82157429Smarkm if (buffer_len(&e->input) < 5) 82257429Smarkm return; /* Incomplete message. */ 82392559Sdes cp = buffer_ptr(&e->input); 824162856Sdes msg_len = get_u32(cp); 82557429Smarkm if (msg_len > 256 * 1024) { 826106130Sdes close_socket(e); 82757429Smarkm return; 82857429Smarkm } 82957429Smarkm if (buffer_len(&e->input) < msg_len + 4) 83057429Smarkm return; 83198684Sdes 83298684Sdes /* move the current input to e->request */ 83357429Smarkm buffer_consume(&e->input, 4); 83498684Sdes buffer_clear(&e->request); 83598684Sdes buffer_append(&e->request, buffer_ptr(&e->input), msg_len); 83698684Sdes buffer_consume(&e->input, msg_len); 83798684Sdes type = buffer_get_char(&e->request); 83857429Smarkm 83998684Sdes /* check wheter agent is locked */ 84098684Sdes if (locked && type != SSH_AGENTC_UNLOCK) { 84198684Sdes buffer_clear(&e->request); 84298684Sdes switch (type) { 84398684Sdes case SSH_AGENTC_REQUEST_RSA_IDENTITIES: 84498684Sdes case SSH2_AGENTC_REQUEST_IDENTITIES: 84598684Sdes /* send empty lists */ 84698684Sdes no_identities(e, type); 84798684Sdes break; 84898684Sdes default: 84998684Sdes /* send a fail message for all other request types */ 85098684Sdes buffer_put_int(&e->output, 1); 85198684Sdes buffer_put_char(&e->output, SSH_AGENT_FAILURE); 85298684Sdes } 85398684Sdes return; 85498684Sdes } 85598684Sdes 85692559Sdes debug("type %d", type); 85757429Smarkm switch (type) { 85898684Sdes case SSH_AGENTC_LOCK: 85998684Sdes case SSH_AGENTC_UNLOCK: 86098684Sdes process_lock_agent(e, type == SSH_AGENTC_LOCK); 86198684Sdes break; 86265674Skris /* ssh1 */ 86365674Skris case SSH_AGENTC_RSA_CHALLENGE: 86465674Skris process_authentication_challenge1(e); 86565674Skris break; 86657429Smarkm case SSH_AGENTC_REQUEST_RSA_IDENTITIES: 86765674Skris process_request_identities(e, 1); 86857429Smarkm break; 86957429Smarkm case SSH_AGENTC_ADD_RSA_IDENTITY: 87098684Sdes case SSH_AGENTC_ADD_RSA_ID_CONSTRAINED: 87165674Skris process_add_identity(e, 1); 87257429Smarkm break; 87357429Smarkm case SSH_AGENTC_REMOVE_RSA_IDENTITY: 87465674Skris process_remove_identity(e, 1); 87557429Smarkm break; 87657429Smarkm case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: 87765674Skris process_remove_all_identities(e, 1); 87857429Smarkm break; 87965674Skris /* ssh2 */ 88065674Skris case SSH2_AGENTC_SIGN_REQUEST: 88165674Skris process_sign_request2(e); 88265674Skris break; 88365674Skris case SSH2_AGENTC_REQUEST_IDENTITIES: 88465674Skris process_request_identities(e, 2); 88565674Skris break; 88665674Skris case SSH2_AGENTC_ADD_IDENTITY: 88798684Sdes case SSH2_AGENTC_ADD_ID_CONSTRAINED: 88865674Skris process_add_identity(e, 2); 88965674Skris break; 89065674Skris case SSH2_AGENTC_REMOVE_IDENTITY: 89165674Skris process_remove_identity(e, 2); 89265674Skris break; 89365674Skris case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: 89465674Skris process_remove_all_identities(e, 2); 89565674Skris break; 896204917Sdes#ifdef ENABLE_PKCS11 89792559Sdes case SSH_AGENTC_ADD_SMARTCARD_KEY: 898124211Sdes case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED: 89992559Sdes process_add_smartcard_key(e); 90092559Sdes break; 90192559Sdes case SSH_AGENTC_REMOVE_SMARTCARD_KEY: 90292559Sdes process_remove_smartcard_key(e); 90392559Sdes break; 904204917Sdes#endif /* ENABLE_PKCS11 */ 90557429Smarkm default: 90657429Smarkm /* Unknown message. Respond with failure. */ 90757429Smarkm error("Unknown message %d", type); 90898684Sdes buffer_clear(&e->request); 90957429Smarkm buffer_put_int(&e->output, 1); 91057429Smarkm buffer_put_char(&e->output, SSH_AGENT_FAILURE); 91157429Smarkm break; 91257429Smarkm } 91357429Smarkm} 91457429Smarkm 91592559Sdesstatic void 91692559Sdesnew_socket(sock_type type, int fd) 91757429Smarkm{ 918120161Snectar u_int i, old_alloc, new_alloc; 91999063Sdes 920226103Sdes if (type == AUTH_CONNECTION) { 921226103Sdes debug("xcount %d -> %d", xcount, xcount + 1); 922226103Sdes ++xcount; 923226103Sdes } 924137019Sdes set_nonblock(fd); 92557429Smarkm 92657429Smarkm if (fd > max_fd) 92757429Smarkm max_fd = fd; 92857429Smarkm 92957429Smarkm for (i = 0; i < sockets_alloc; i++) 93057429Smarkm if (sockets[i].type == AUTH_UNUSED) { 93157429Smarkm sockets[i].fd = fd; 93257429Smarkm buffer_init(&sockets[i].input); 93357429Smarkm buffer_init(&sockets[i].output); 93498684Sdes buffer_init(&sockets[i].request); 935120161Snectar sockets[i].type = type; 93657429Smarkm return; 93757429Smarkm } 93857429Smarkm old_alloc = sockets_alloc; 939120161Snectar new_alloc = sockets_alloc + 10; 940162856Sdes sockets = xrealloc(sockets, new_alloc, sizeof(sockets[0])); 941120161Snectar for (i = old_alloc; i < new_alloc; i++) 94257429Smarkm sockets[i].type = AUTH_UNUSED; 943120161Snectar sockets_alloc = new_alloc; 94457429Smarkm sockets[old_alloc].fd = fd; 94557429Smarkm buffer_init(&sockets[old_alloc].input); 94657429Smarkm buffer_init(&sockets[old_alloc].output); 94798684Sdes buffer_init(&sockets[old_alloc].request); 948120161Snectar sockets[old_alloc].type = type; 94957429Smarkm} 95057429Smarkm 95192559Sdesstatic int 952181111Sdesprepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp, 953181111Sdes struct timeval **tvpp) 95457429Smarkm{ 955255767Sdes u_int i, sz; 95676262Sgreen int n = 0; 957181111Sdes static struct timeval tv; 958255767Sdes time_t deadline; 95976262Sgreen 96076262Sgreen for (i = 0; i < sockets_alloc; i++) { 96157429Smarkm switch (sockets[i].type) { 96257429Smarkm case AUTH_SOCKET: 96357429Smarkm case AUTH_CONNECTION: 96476262Sgreen n = MAX(n, sockets[i].fd); 96557429Smarkm break; 96657429Smarkm case AUTH_UNUSED: 96757429Smarkm break; 96857429Smarkm default: 96957429Smarkm fatal("Unknown socket type %d", sockets[i].type); 97057429Smarkm break; 97157429Smarkm } 97276262Sgreen } 97376262Sgreen 97476262Sgreen sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); 97592559Sdes if (*fdrp == NULL || sz > *nallocp) { 976255767Sdes free(*fdrp); 977255767Sdes free(*fdwp); 97876262Sgreen *fdrp = xmalloc(sz); 97976262Sgreen *fdwp = xmalloc(sz); 98092559Sdes *nallocp = sz; 98176262Sgreen } 98292559Sdes if (n < *fdl) 98392559Sdes debug("XXX shrink: %d < %d", n, *fdl); 98492559Sdes *fdl = n; 98576262Sgreen memset(*fdrp, 0, sz); 98676262Sgreen memset(*fdwp, 0, sz); 98776262Sgreen 98876262Sgreen for (i = 0; i < sockets_alloc; i++) { 98976262Sgreen switch (sockets[i].type) { 99076262Sgreen case AUTH_SOCKET: 99176262Sgreen case AUTH_CONNECTION: 99276262Sgreen FD_SET(sockets[i].fd, *fdrp); 99376262Sgreen if (buffer_len(&sockets[i].output) > 0) 99476262Sgreen FD_SET(sockets[i].fd, *fdwp); 99576262Sgreen break; 99676262Sgreen default: 99776262Sgreen break; 99876262Sgreen } 99976262Sgreen } 1000181111Sdes deadline = reaper(); 1001181111Sdes if (parent_alive_interval != 0) 1002181111Sdes deadline = (deadline == 0) ? parent_alive_interval : 1003181111Sdes MIN(deadline, parent_alive_interval); 1004181111Sdes if (deadline == 0) { 1005181111Sdes *tvpp = NULL; 1006181111Sdes } else { 1007181111Sdes tv.tv_sec = deadline; 1008181111Sdes tv.tv_usec = 0; 1009181111Sdes *tvpp = &tv; 1010181111Sdes } 101176262Sgreen return (1); 101257429Smarkm} 101357429Smarkm 101492559Sdesstatic void 101557429Smarkmafter_select(fd_set *readset, fd_set *writeset) 101657429Smarkm{ 101799063Sdes struct sockaddr_un sunaddr; 101858585Skris socklen_t slen; 101957429Smarkm char buf[1024]; 102099063Sdes int len, sock; 1021204917Sdes u_int i, orig_alloc; 1022106130Sdes uid_t euid; 1023106130Sdes gid_t egid; 102457429Smarkm 1025204917Sdes for (i = 0, orig_alloc = sockets_alloc; i < orig_alloc; i++) 102657429Smarkm switch (sockets[i].type) { 102757429Smarkm case AUTH_UNUSED: 102857429Smarkm break; 102957429Smarkm case AUTH_SOCKET: 103057429Smarkm if (FD_ISSET(sockets[i].fd, readset)) { 103158585Skris slen = sizeof(sunaddr); 103276262Sgreen sock = accept(sockets[i].fd, 1033162856Sdes (struct sockaddr *)&sunaddr, &slen); 103457429Smarkm if (sock < 0) { 103592559Sdes error("accept from AUTH_SOCKET: %s", 103692559Sdes strerror(errno)); 103757429Smarkm break; 103857429Smarkm } 1039106130Sdes if (getpeereid(sock, &euid, &egid) < 0) { 1040106130Sdes error("getpeereid %d failed: %s", 1041106130Sdes sock, strerror(errno)); 1042106130Sdes close(sock); 1043106130Sdes break; 1044106130Sdes } 1045106130Sdes if ((euid != 0) && (getuid() != euid)) { 1046106130Sdes error("uid mismatch: " 1047106130Sdes "peer euid %u != uid %u", 1048106130Sdes (u_int) euid, (u_int) getuid()); 1049106130Sdes close(sock); 1050106130Sdes break; 1051106130Sdes } 105257429Smarkm new_socket(AUTH_CONNECTION, sock); 105357429Smarkm } 105457429Smarkm break; 105557429Smarkm case AUTH_CONNECTION: 105657429Smarkm if (buffer_len(&sockets[i].output) > 0 && 105757429Smarkm FD_ISSET(sockets[i].fd, writeset)) { 1058204917Sdes len = write(sockets[i].fd, 1059204917Sdes buffer_ptr(&sockets[i].output), 1060204917Sdes buffer_len(&sockets[i].output)); 1061204917Sdes if (len == -1 && (errno == EAGAIN || 1062204917Sdes errno == EWOULDBLOCK || 1063204917Sdes errno == EINTR)) 1064204917Sdes continue; 106557429Smarkm if (len <= 0) { 1066106130Sdes close_socket(&sockets[i]); 106757429Smarkm break; 106857429Smarkm } 106957429Smarkm buffer_consume(&sockets[i].output, len); 107057429Smarkm } 107157429Smarkm if (FD_ISSET(sockets[i].fd, readset)) { 1072204917Sdes len = read(sockets[i].fd, buf, sizeof(buf)); 1073204917Sdes if (len == -1 && (errno == EAGAIN || 1074204917Sdes errno == EWOULDBLOCK || 1075204917Sdes errno == EINTR)) 1076204917Sdes continue; 107757429Smarkm if (len <= 0) { 1078106130Sdes close_socket(&sockets[i]); 107957429Smarkm break; 108057429Smarkm } 108157429Smarkm buffer_append(&sockets[i].input, buf, len); 108257429Smarkm process_message(&sockets[i]); 108357429Smarkm } 108457429Smarkm break; 108557429Smarkm default: 108657429Smarkm fatal("Unknown type %d", sockets[i].type); 108757429Smarkm } 108857429Smarkm} 108957429Smarkm 109092559Sdesstatic void 1091126277Sdescleanup_socket(void) 109257429Smarkm{ 109376262Sgreen if (socket_name[0]) 109476262Sgreen unlink(socket_name); 109576262Sgreen if (socket_dir[0]) 109676262Sgreen rmdir(socket_dir); 109757429Smarkm} 109857429Smarkm 1099126277Sdesvoid 110057429Smarkmcleanup_exit(int i) 110157429Smarkm{ 1102126277Sdes cleanup_socket(); 1103126277Sdes _exit(i); 110457429Smarkm} 110557429Smarkm 1106162856Sdes/*ARGSUSED*/ 110792559Sdesstatic void 110876262Sgreencleanup_handler(int sig) 110957429Smarkm{ 1110126277Sdes cleanup_socket(); 1111204917Sdes#ifdef ENABLE_PKCS11 1112204917Sdes pkcs11_terminate(); 1113204917Sdes#endif 111476262Sgreen _exit(2); 111576262Sgreen} 111676262Sgreen 111792559Sdesstatic void 1118181111Sdescheck_parent_exists(void) 111992559Sdes{ 1120226046Sdes /* 1121226046Sdes * If our parent has exited then getppid() will return (pid_t)1, 1122226046Sdes * so testing for that should be safe. 1123226046Sdes */ 1124226046Sdes if (parent_pid != -1 && getppid() != parent_pid) { 112592559Sdes /* printf("Parent has died - Authentication agent exiting.\n"); */ 1126181111Sdes cleanup_socket(); 1127181111Sdes _exit(2); 112892559Sdes } 112992559Sdes} 113092559Sdes 113192559Sdesstatic void 113276262Sgreenusage(void) 113376262Sgreen{ 1134181111Sdes fprintf(stderr, "usage: %s [options] [command [arg ...]]\n", 113576262Sgreen __progname); 113692559Sdes fprintf(stderr, "Options:\n"); 113792559Sdes fprintf(stderr, " -c Generate C-shell commands on stdout.\n"); 113892559Sdes fprintf(stderr, " -s Generate Bourne shell commands on stdout.\n"); 113992559Sdes fprintf(stderr, " -k Kill the current agent.\n"); 114092559Sdes fprintf(stderr, " -d Debug mode.\n"); 114198684Sdes fprintf(stderr, " -a socket Bind agent socket to given name.\n"); 1142113911Sdes fprintf(stderr, " -t life Default identity lifetime (seconds).\n"); 1143226103Sdes fprintf(stderr, " -x Exit when the last client disconnects.\n"); 114457429Smarkm exit(1); 114557429Smarkm} 114657429Smarkm 114757429Smarkmint 114857429Smarkmmain(int ac, char **av) 114957429Smarkm{ 1150113911Sdes int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0; 1151181111Sdes int sock, fd, ch, result, saved_errno; 1152137019Sdes u_int nalloc; 115399063Sdes char *shell, *format, *pidstr, *agentsocket = NULL; 115499063Sdes fd_set *readsetp = NULL, *writesetp = NULL; 115557429Smarkm struct sockaddr_un sunaddr; 115698941Sdes#ifdef HAVE_SETRLIMIT 115776262Sgreen struct rlimit rlim; 115898941Sdes#endif 115998941Sdes int prev_mask; 116099063Sdes extern int optind; 116199063Sdes extern char *optarg; 116257429Smarkm pid_t pid; 116399063Sdes char pidstrbuf[1 + 3 * sizeof pid]; 1164181111Sdes struct timeval *tvp = NULL; 1165197679Sdes size_t len; 116657429Smarkm 1167157019Sdes /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1168157019Sdes sanitise_stdfd(); 1169157019Sdes 1170106130Sdes /* drop */ 1171106130Sdes setegid(getgid()); 1172106130Sdes setgid(getgid()); 1173110506Sdes setuid(geteuid()); 1174106130Sdes 1175128460Sdes#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) 1176128460Sdes /* Disable ptrace on Linux without sgid bit */ 1177128460Sdes prctl(PR_SET_DUMPABLE, 0); 1178128460Sdes#endif 1179128460Sdes 1180221420Sdes OpenSSL_add_all_algorithms(); 118176262Sgreen 1182124211Sdes __progname = ssh_get_progname(av[0]); 118398941Sdes seed_rng(); 118498941Sdes 1185226103Sdes while ((ch = getopt(ac, av, "cdksa:t:x")) != -1) { 118657429Smarkm switch (ch) { 118757429Smarkm case 'c': 118857429Smarkm if (s_flag) 118957429Smarkm usage(); 119057429Smarkm c_flag++; 119157429Smarkm break; 119257429Smarkm case 'k': 119357429Smarkm k_flag++; 119457429Smarkm break; 119557429Smarkm case 's': 119657429Smarkm if (c_flag) 119757429Smarkm usage(); 119857429Smarkm s_flag++; 119957429Smarkm break; 120092559Sdes case 'd': 120192559Sdes if (d_flag) 120292559Sdes usage(); 120392559Sdes d_flag++; 120492559Sdes break; 120598684Sdes case 'a': 120698684Sdes agentsocket = optarg; 120798684Sdes break; 1208113911Sdes case 't': 1209113911Sdes if ((lifetime = convtime(optarg)) == -1) { 1210113911Sdes fprintf(stderr, "Invalid lifetime\n"); 1211113911Sdes usage(); 1212113911Sdes } 1213113911Sdes break; 1214226103Sdes case 'x': 1215226103Sdes xcount = 0; 1216226103Sdes break; 121757429Smarkm default: 121857429Smarkm usage(); 121957429Smarkm } 122057429Smarkm } 122157429Smarkm ac -= optind; 122257429Smarkm av += optind; 122357429Smarkm 122492559Sdes if (ac > 0 && (c_flag || k_flag || s_flag || d_flag)) 122557429Smarkm usage(); 122657429Smarkm 122798684Sdes if (ac == 0 && !c_flag && !s_flag) { 122857429Smarkm shell = getenv("SHELL"); 1229197679Sdes if (shell != NULL && (len = strlen(shell)) > 2 && 1230197679Sdes strncmp(shell + len - 3, "csh", 3) == 0) 123157429Smarkm c_flag = 1; 123257429Smarkm } 123357429Smarkm if (k_flag) { 1234162856Sdes const char *errstr = NULL; 1235162856Sdes 123657429Smarkm pidstr = getenv(SSH_AGENTPID_ENV_NAME); 123757429Smarkm if (pidstr == NULL) { 123857429Smarkm fprintf(stderr, "%s not set, cannot kill agent\n", 123976262Sgreen SSH_AGENTPID_ENV_NAME); 124057429Smarkm exit(1); 124157429Smarkm } 1242162856Sdes pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr); 1243162856Sdes if (errstr) { 1244162856Sdes fprintf(stderr, 1245162856Sdes "%s=\"%s\", which is not a good PID: %s\n", 1246162856Sdes SSH_AGENTPID_ENV_NAME, pidstr, errstr); 124757429Smarkm exit(1); 124857429Smarkm } 124957429Smarkm if (kill(pid, SIGTERM) == -1) { 125057429Smarkm perror("kill"); 125157429Smarkm exit(1); 125257429Smarkm } 125357429Smarkm format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; 125457429Smarkm printf(format, SSH_AUTHSOCKET_ENV_NAME); 125557429Smarkm printf(format, SSH_AGENTPID_ENV_NAME); 125698684Sdes printf("echo Agent pid %ld killed;\n", (long)pid); 125757429Smarkm exit(0); 125857429Smarkm } 125957429Smarkm parent_pid = getpid(); 126057429Smarkm 126198684Sdes if (agentsocket == NULL) { 126298684Sdes /* Create private directory for agent socket */ 1263221420Sdes mktemp_proto(socket_dir, sizeof(socket_dir)); 126498684Sdes if (mkdtemp(socket_dir) == NULL) { 126598684Sdes perror("mkdtemp: private socket dir"); 126698684Sdes exit(1); 126798684Sdes } 126898684Sdes snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, 126998684Sdes (long)parent_pid); 127098684Sdes } else { 127198684Sdes /* Try to use specified agent socket */ 127298684Sdes socket_dir[0] = '\0'; 127398684Sdes strlcpy(socket_name, agentsocket, sizeof socket_name); 127457429Smarkm } 127557429Smarkm 127657429Smarkm /* 127757429Smarkm * Create socket early so it will exist before command gets run from 127857429Smarkm * the parent. 127957429Smarkm */ 128057429Smarkm sock = socket(AF_UNIX, SOCK_STREAM, 0); 128157429Smarkm if (sock < 0) { 128257429Smarkm perror("socket"); 1283147005Sdes *socket_name = '\0'; /* Don't unlink any existing file */ 128457429Smarkm cleanup_exit(1); 128557429Smarkm } 128657429Smarkm memset(&sunaddr, 0, sizeof(sunaddr)); 128757429Smarkm sunaddr.sun_family = AF_UNIX; 128857429Smarkm strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); 128998941Sdes prev_mask = umask(0177); 1290162856Sdes if (bind(sock, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) { 129157429Smarkm perror("bind"); 1292147005Sdes *socket_name = '\0'; /* Don't unlink any existing file */ 129398941Sdes umask(prev_mask); 129457429Smarkm cleanup_exit(1); 129557429Smarkm } 129698941Sdes umask(prev_mask); 1297126277Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 129857429Smarkm perror("listen"); 129957429Smarkm cleanup_exit(1); 130057429Smarkm } 130176262Sgreen 130257429Smarkm /* 130357429Smarkm * Fork, and have the parent execute the command, if any, or present 130457429Smarkm * the socket data. The child continues as the authentication agent. 130557429Smarkm */ 130692559Sdes if (d_flag) { 130792559Sdes log_init(__progname, SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 1); 130892559Sdes format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; 130992559Sdes printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, 131092559Sdes SSH_AUTHSOCKET_ENV_NAME); 131198684Sdes printf("echo Agent pid %ld;\n", (long)parent_pid); 131292559Sdes goto skip; 131392559Sdes } 131457429Smarkm pid = fork(); 131557429Smarkm if (pid == -1) { 131657429Smarkm perror("fork"); 131792559Sdes cleanup_exit(1); 131857429Smarkm } 131957429Smarkm if (pid != 0) { /* Parent - execute the given command. */ 132057429Smarkm close(sock); 132198684Sdes snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); 132257429Smarkm if (ac == 0) { 132357429Smarkm format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; 132457429Smarkm printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, 132576262Sgreen SSH_AUTHSOCKET_ENV_NAME); 132657429Smarkm printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, 132776262Sgreen SSH_AGENTPID_ENV_NAME); 132898684Sdes printf("echo Agent pid %ld;\n", (long)pid); 132957429Smarkm exit(0); 133057429Smarkm } 133169591Sgreen if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || 133269591Sgreen setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { 133369591Sgreen perror("setenv"); 133469591Sgreen exit(1); 133569591Sgreen } 133657429Smarkm execvp(av[0], av); 133757429Smarkm perror(av[0]); 133857429Smarkm exit(1); 133957429Smarkm } 134092559Sdes /* child */ 134192559Sdes log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); 134292559Sdes 134392559Sdes if (setsid() == -1) { 134492559Sdes error("setsid: %s", strerror(errno)); 134592559Sdes cleanup_exit(1); 134692559Sdes } 134792559Sdes 134892559Sdes (void)chdir("/"); 1349113911Sdes if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { 1350113911Sdes /* XXX might close listen socket */ 1351113911Sdes (void)dup2(fd, STDIN_FILENO); 1352113911Sdes (void)dup2(fd, STDOUT_FILENO); 1353113911Sdes (void)dup2(fd, STDERR_FILENO); 1354113911Sdes if (fd > 2) 1355113911Sdes close(fd); 1356113911Sdes } 135757429Smarkm 135898941Sdes#ifdef HAVE_SETRLIMIT 135976262Sgreen /* deny core dumps, since memory contains unencrypted private keys */ 136076262Sgreen rlim.rlim_cur = rlim.rlim_max = 0; 136176262Sgreen if (setrlimit(RLIMIT_CORE, &rlim) < 0) { 136292559Sdes error("setrlimit RLIMIT_CORE: %s", strerror(errno)); 136376262Sgreen cleanup_exit(1); 136476262Sgreen } 136598941Sdes#endif 136692559Sdes 136792559Sdesskip: 1368204917Sdes 1369204917Sdes#ifdef ENABLE_PKCS11 1370204917Sdes pkcs11_init(0); 1371204917Sdes#endif 137257429Smarkm new_socket(AUTH_SOCKET, sock); 1373181111Sdes if (ac > 0) 1374181111Sdes parent_alive_interval = 10; 137565674Skris idtab_init(); 1376255767Sdes signal(SIGPIPE, SIG_IGN); 1377226103Sdes signal(SIGINT, d_flag ? cleanup_handler : SIG_IGN); 137876262Sgreen signal(SIGHUP, cleanup_handler); 137976262Sgreen signal(SIGTERM, cleanup_handler); 138092559Sdes nalloc = 0; 138192559Sdes 138257429Smarkm while (1) { 1383181111Sdes prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp); 1384181111Sdes result = select(max_fd + 1, readsetp, writesetp, NULL, tvp); 1385181111Sdes saved_errno = errno; 1386181111Sdes if (parent_alive_interval != 0) 1387181111Sdes check_parent_exists(); 1388181111Sdes (void) reaper(); /* remove expired keys */ 1389181111Sdes if (result < 0) { 1390181111Sdes if (saved_errno == EINTR) 139157429Smarkm continue; 1392181111Sdes fatal("select: %s", strerror(saved_errno)); 1393181111Sdes } else if (result > 0) 1394181111Sdes after_select(readsetp, writesetp); 139557429Smarkm } 139657429Smarkm /* NOTREACHED */ 139757429Smarkm} 1398