ssh-agent.c revision 181111
1181111Sdes/* $OpenBSD: ssh-agent.c,v 1.159 2008/06/28 14:05:15 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 181111 2008-08-01 02:48:36Z 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 8092559Sdes#ifdef SMARTCARD 8192559Sdes#include "scard.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; 10998684Sdes u_int death; 110113911Sdes u_int confirm; 11157429Smarkm} Identity; 11257429Smarkm 11365674Skristypedef struct { 11465674Skris int nentries; 11592559Sdes TAILQ_HEAD(idqueue, identity) idlist; 11665674Skris} Idtab; 11757429Smarkm 11865674Skris/* private key table, one per protocol version */ 11965674SkrisIdtab idtable[3]; 12065674Skris 12157429Smarkmint max_fd = 0; 12257429Smarkm 12357429Smarkm/* pid of shell == parent of agent */ 12460576Skrispid_t parent_pid = -1; 125181111Sdesu_int parent_alive_interval = 0; 12657429Smarkm 12757429Smarkm/* pathname and directory for AUTH_SOCKET */ 128162856Sdeschar socket_name[MAXPATHLEN]; 129162856Sdeschar socket_dir[MAXPATHLEN]; 13057429Smarkm 13198684Sdes/* locking */ 13298684Sdesint locked = 0; 13398684Sdeschar *lock_passwd = NULL; 13498684Sdes 13557429Smarkmextern char *__progname; 13657429Smarkm 137113911Sdes/* Default lifetime (0 == forever) */ 138113911Sdesstatic int lifetime = 0; 139113911Sdes 14092559Sdesstatic void 141106130Sdesclose_socket(SocketEntry *e) 142106130Sdes{ 143106130Sdes close(e->fd); 144106130Sdes e->fd = -1; 145106130Sdes e->type = AUTH_UNUSED; 146106130Sdes buffer_free(&e->input); 147106130Sdes buffer_free(&e->output); 148106130Sdes buffer_free(&e->request); 149106130Sdes} 150106130Sdes 151106130Sdesstatic void 15265674Skrisidtab_init(void) 15357429Smarkm{ 15465674Skris int i; 15599063Sdes 15692559Sdes for (i = 0; i <=2; i++) { 15792559Sdes TAILQ_INIT(&idtable[i].idlist); 15865674Skris idtable[i].nentries = 0; 15965674Skris } 16065674Skris} 16165674Skris 16265674Skris/* return private key table for requested protocol version */ 16392559Sdesstatic Idtab * 16465674Skrisidtab_lookup(int version) 16565674Skris{ 16665674Skris if (version < 1 || version > 2) 16765674Skris fatal("internal error, bad protocol version %d", version); 16865674Skris return &idtable[version]; 16965674Skris} 17065674Skris 17198684Sdesstatic void 17298684Sdesfree_identity(Identity *id) 17398684Sdes{ 17498684Sdes key_free(id->key); 17598684Sdes xfree(id->comment); 17698684Sdes xfree(id); 17798684Sdes} 17898684Sdes 17965674Skris/* return matching private key for given public key */ 18092559Sdesstatic Identity * 18192559Sdeslookup_identity(Key *key, int version) 18265674Skris{ 18392559Sdes Identity *id; 18492559Sdes 18565674Skris Idtab *tab = idtab_lookup(version); 18692559Sdes TAILQ_FOREACH(id, &tab->idlist, next) { 18792559Sdes if (key_equal(key, id->key)) 18892559Sdes return (id); 18965674Skris } 19092559Sdes return (NULL); 19165674Skris} 19265674Skris 193113911Sdes/* Check confirmation of keysign request */ 194113911Sdesstatic int 195113911Sdesconfirm_key(Identity *id) 196113911Sdes{ 197147005Sdes char *p; 198113911Sdes int ret = -1; 199113911Sdes 200113911Sdes p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); 201147005Sdes if (ask_permission("Allow use of key %s?\nKey fingerprint %s.", 202147005Sdes id->comment, p)) 203147005Sdes ret = 0; 204113911Sdes xfree(p); 205147005Sdes 206113911Sdes return (ret); 207113911Sdes} 208113911Sdes 20965674Skris/* send list of supported public keys to 'client' */ 21092559Sdesstatic void 21165674Skrisprocess_request_identities(SocketEntry *e, int version) 21265674Skris{ 21365674Skris Idtab *tab = idtab_lookup(version); 21499063Sdes Identity *id; 21557429Smarkm Buffer msg; 21657429Smarkm 21757429Smarkm buffer_init(&msg); 21865674Skris buffer_put_char(&msg, (version == 1) ? 21965674Skris SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); 22065674Skris buffer_put_int(&msg, tab->nentries); 22192559Sdes TAILQ_FOREACH(id, &tab->idlist, next) { 22276262Sgreen if (id->key->type == KEY_RSA1) { 22365674Skris buffer_put_int(&msg, BN_num_bits(id->key->rsa->n)); 22465674Skris buffer_put_bignum(&msg, id->key->rsa->e); 22565674Skris buffer_put_bignum(&msg, id->key->rsa->n); 22665674Skris } else { 22776262Sgreen u_char *blob; 22876262Sgreen u_int blen; 22976262Sgreen key_to_blob(id->key, &blob, &blen); 23065674Skris buffer_put_string(&msg, blob, blen); 23165674Skris xfree(blob); 23265674Skris } 23365674Skris buffer_put_cstring(&msg, id->comment); 23457429Smarkm } 23557429Smarkm buffer_put_int(&e->output, buffer_len(&msg)); 23657429Smarkm buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); 23757429Smarkm buffer_free(&msg); 23857429Smarkm} 23957429Smarkm 24065674Skris/* ssh1 only */ 24192559Sdesstatic void 24265674Skrisprocess_authentication_challenge1(SocketEntry *e) 24357429Smarkm{ 24499063Sdes u_char buf[32], mdbuf[16], session_id[16]; 24599063Sdes u_int response_type; 24699063Sdes BIGNUM *challenge; 24792559Sdes Identity *id; 24865674Skris int i, len; 24957429Smarkm Buffer msg; 25057429Smarkm MD5_CTX md; 25199063Sdes Key *key; 25257429Smarkm 25357429Smarkm buffer_init(&msg); 25476262Sgreen key = key_new(KEY_RSA1); 25592559Sdes if ((challenge = BN_new()) == NULL) 25692559Sdes fatal("process_authentication_challenge1: BN_new failed"); 25765674Skris 25899063Sdes (void) buffer_get_int(&e->request); /* ignored */ 25998684Sdes buffer_get_bignum(&e->request, key->rsa->e); 26098684Sdes buffer_get_bignum(&e->request, key->rsa->n); 26198684Sdes buffer_get_bignum(&e->request, challenge); 26257429Smarkm 26365674Skris /* Only protocol 1.1 is supported */ 26498684Sdes if (buffer_len(&e->request) == 0) 26565674Skris goto failure; 26698684Sdes buffer_get(&e->request, session_id, 16); 26798684Sdes response_type = buffer_get_int(&e->request); 26865674Skris if (response_type != 1) 26965674Skris goto failure; 27057429Smarkm 27192559Sdes id = lookup_identity(key, 1); 272113911Sdes if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { 27392559Sdes Key *private = id->key; 27465674Skris /* Decrypt the challenge using the private key. */ 27572397Skris if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0) 27672397Skris goto failure; 27757429Smarkm 27865674Skris /* The response is MD5 of decrypted challenge plus session id. */ 27965674Skris len = BN_num_bytes(challenge); 28065674Skris if (len <= 0 || len > 32) { 281124211Sdes logit("process_authentication_challenge: bad challenge length %d", len); 28265674Skris goto failure; 28365674Skris } 28465674Skris memset(buf, 0, 32); 28565674Skris BN_bn2bin(challenge, buf + 32 - len); 28665674Skris MD5_Init(&md); 28765674Skris MD5_Update(&md, buf, 32); 28865674Skris MD5_Update(&md, session_id, 16); 28965674Skris MD5_Final(mdbuf, &md); 29057429Smarkm 29165674Skris /* Send the response. */ 29265674Skris buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); 29365674Skris for (i = 0; i < 16; i++) 29465674Skris buffer_put_char(&msg, mdbuf[i]); 29565674Skris goto send; 29665674Skris } 29757429Smarkm 29865674Skrisfailure: 29965674Skris /* Unknown identity or protocol error. Send failure. */ 30057429Smarkm buffer_put_char(&msg, SSH_AGENT_FAILURE); 30157429Smarkmsend: 30257429Smarkm buffer_put_int(&e->output, buffer_len(&msg)); 30365674Skris buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); 30465674Skris key_free(key); 30565674Skris BN_clear_free(challenge); 30665674Skris buffer_free(&msg); 30765674Skris} 30865674Skris 30965674Skris/* ssh2 only */ 31092559Sdesstatic void 31165674Skrisprocess_sign_request2(SocketEntry *e) 31265674Skris{ 31376262Sgreen u_char *blob, *data, *signature = NULL; 31476262Sgreen u_int blen, dlen, slen = 0; 31599063Sdes extern int datafellows; 316181111Sdes int odatafellows; 31799063Sdes int ok = -1, flags; 31865674Skris Buffer msg; 31999063Sdes Key *key; 32065674Skris 32165674Skris datafellows = 0; 32276262Sgreen 32398684Sdes blob = buffer_get_string(&e->request, &blen); 32498684Sdes data = buffer_get_string(&e->request, &dlen); 32565674Skris 32698684Sdes flags = buffer_get_int(&e->request); 327181111Sdes odatafellows = datafellows; 32869591Sgreen if (flags & SSH_AGENT_OLD_SIGNATURE) 32969591Sgreen datafellows = SSH_BUG_SIGBLOB; 33069591Sgreen 33176262Sgreen key = key_from_blob(blob, blen); 33265674Skris if (key != NULL) { 33392559Sdes Identity *id = lookup_identity(key, 2); 334113911Sdes if (id != NULL && (!id->confirm || confirm_key(id) == 0)) 33592559Sdes ok = key_sign(id->key, &signature, &slen, data, dlen); 336162856Sdes key_free(key); 33765674Skris } 33865674Skris buffer_init(&msg); 33965674Skris if (ok == 0) { 34065674Skris buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE); 34165674Skris buffer_put_string(&msg, signature, slen); 34265674Skris } else { 34365674Skris buffer_put_char(&msg, SSH_AGENT_FAILURE); 34465674Skris } 34565674Skris buffer_put_int(&e->output, buffer_len(&msg)); 34657429Smarkm buffer_append(&e->output, buffer_ptr(&msg), 34765674Skris buffer_len(&msg)); 34857429Smarkm buffer_free(&msg); 34965674Skris xfree(data); 35065674Skris xfree(blob); 35165674Skris if (signature != NULL) 35265674Skris xfree(signature); 353181111Sdes datafellows = odatafellows; 35457429Smarkm} 35557429Smarkm 35665674Skris/* shared */ 35792559Sdesstatic void 35865674Skrisprocess_remove_identity(SocketEntry *e, int version) 35957429Smarkm{ 36099063Sdes u_int blen, bits; 36199063Sdes int success = 0; 36292559Sdes Key *key = NULL; 36376262Sgreen u_char *blob; 36457429Smarkm 36592559Sdes switch (version) { 36665674Skris case 1: 36776262Sgreen key = key_new(KEY_RSA1); 36898684Sdes bits = buffer_get_int(&e->request); 36998684Sdes buffer_get_bignum(&e->request, key->rsa->e); 37098684Sdes buffer_get_bignum(&e->request, key->rsa->n); 37157429Smarkm 37265674Skris if (bits != key_size(key)) 373124211Sdes logit("Warning: identity keysize mismatch: actual %u, announced %u", 37476262Sgreen key_size(key), bits); 37565674Skris break; 37665674Skris case 2: 37798684Sdes blob = buffer_get_string(&e->request, &blen); 37876262Sgreen key = key_from_blob(blob, blen); 37965674Skris xfree(blob); 38065674Skris break; 38165674Skris } 38265674Skris if (key != NULL) { 38392559Sdes Identity *id = lookup_identity(key, version); 38492559Sdes if (id != NULL) { 38557429Smarkm /* 38657429Smarkm * We have this key. Free the old key. Since we 387157019Sdes * don't want to leave empty slots in the middle of 38876262Sgreen * the array, we actually free the key there and move 38976262Sgreen * all the entries between the empty slot and the end 39076262Sgreen * of the array. 39157429Smarkm */ 39265674Skris Idtab *tab = idtab_lookup(version); 39376262Sgreen if (tab->nentries < 1) 39476262Sgreen fatal("process_remove_identity: " 39576262Sgreen "internal error: tab->nentries %d", 39676262Sgreen tab->nentries); 39792559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 39892559Sdes free_identity(id); 39965674Skris tab->nentries--; 40065674Skris success = 1; 40157429Smarkm } 40265674Skris key_free(key); 40365674Skris } 40457429Smarkm buffer_put_int(&e->output, 1); 40565674Skris buffer_put_char(&e->output, 40665674Skris success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 40757429Smarkm} 40857429Smarkm 40992559Sdesstatic void 41065674Skrisprocess_remove_all_identities(SocketEntry *e, int version) 41157429Smarkm{ 41265674Skris Idtab *tab = idtab_lookup(version); 41392559Sdes Identity *id; 41457429Smarkm 41557429Smarkm /* Loop over all identities and clear the keys. */ 41692559Sdes for (id = TAILQ_FIRST(&tab->idlist); id; 41792559Sdes id = TAILQ_FIRST(&tab->idlist)) { 41892559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 41992559Sdes free_identity(id); 42057429Smarkm } 42157429Smarkm 42257429Smarkm /* Mark that there are no identities. */ 42365674Skris tab->nentries = 0; 42457429Smarkm 42557429Smarkm /* Send success. */ 42657429Smarkm buffer_put_int(&e->output, 1); 42757429Smarkm buffer_put_char(&e->output, SSH_AGENT_SUCCESS); 42857429Smarkm} 42957429Smarkm 430181111Sdes/* removes expired keys and returns number of seconds until the next expiry */ 431181111Sdesstatic u_int 43298684Sdesreaper(void) 43398684Sdes{ 434181111Sdes u_int deadline = 0, now = time(NULL); 43598684Sdes Identity *id, *nxt; 43698684Sdes int version; 43799063Sdes Idtab *tab; 43898684Sdes 43998684Sdes for (version = 1; version < 3; version++) { 44098684Sdes tab = idtab_lookup(version); 44198684Sdes for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { 44298684Sdes nxt = TAILQ_NEXT(id, next); 443181111Sdes if (id->death == 0) 444181111Sdes continue; 445181111Sdes if (now >= id->death) { 446181111Sdes debug("expiring key '%s'", id->comment); 44798684Sdes TAILQ_REMOVE(&tab->idlist, id, next); 44898684Sdes free_identity(id); 44998684Sdes tab->nentries--; 450181111Sdes } else 451181111Sdes deadline = (deadline == 0) ? id->death : 452181111Sdes MIN(deadline, id->death); 45398684Sdes } 45498684Sdes } 455181111Sdes if (deadline == 0 || deadline <= now) 456181111Sdes return 0; 457181111Sdes else 458181111Sdes return (deadline - now); 45998684Sdes} 46098684Sdes 46198684Sdesstatic void 46265674Skrisprocess_add_identity(SocketEntry *e, int version) 46357429Smarkm{ 46499063Sdes Idtab *tab = idtab_lookup(version); 465181111Sdes Identity *id; 466113911Sdes int type, success = 0, death = 0, confirm = 0; 46799063Sdes char *type_name, *comment; 46865674Skris Key *k = NULL; 46957429Smarkm 47065674Skris switch (version) { 47165674Skris case 1: 47276262Sgreen k = key_new_private(KEY_RSA1); 47399063Sdes (void) buffer_get_int(&e->request); /* ignored */ 47498684Sdes buffer_get_bignum(&e->request, k->rsa->n); 47598684Sdes buffer_get_bignum(&e->request, k->rsa->e); 47698684Sdes buffer_get_bignum(&e->request, k->rsa->d); 47798684Sdes buffer_get_bignum(&e->request, k->rsa->iqmp); 47857429Smarkm 47965674Skris /* SSH and SSL have p and q swapped */ 48098684Sdes buffer_get_bignum(&e->request, k->rsa->q); /* p */ 48198684Sdes buffer_get_bignum(&e->request, k->rsa->p); /* q */ 48257429Smarkm 48365674Skris /* Generate additional parameters */ 48492559Sdes rsa_generate_additional_parameters(k->rsa); 48565674Skris break; 48665674Skris case 2: 48798684Sdes type_name = buffer_get_string(&e->request, NULL); 48876262Sgreen type = key_type_from_name(type_name); 48976262Sgreen xfree(type_name); 49092559Sdes switch (type) { 49176262Sgreen case KEY_DSA: 49276262Sgreen k = key_new_private(type); 49398684Sdes buffer_get_bignum2(&e->request, k->dsa->p); 49498684Sdes buffer_get_bignum2(&e->request, k->dsa->q); 49598684Sdes buffer_get_bignum2(&e->request, k->dsa->g); 49698684Sdes buffer_get_bignum2(&e->request, k->dsa->pub_key); 49798684Sdes buffer_get_bignum2(&e->request, k->dsa->priv_key); 49876262Sgreen break; 49976262Sgreen case KEY_RSA: 50076262Sgreen k = key_new_private(type); 50198684Sdes buffer_get_bignum2(&e->request, k->rsa->n); 50298684Sdes buffer_get_bignum2(&e->request, k->rsa->e); 50398684Sdes buffer_get_bignum2(&e->request, k->rsa->d); 50498684Sdes buffer_get_bignum2(&e->request, k->rsa->iqmp); 50598684Sdes buffer_get_bignum2(&e->request, k->rsa->p); 50698684Sdes buffer_get_bignum2(&e->request, k->rsa->q); 50776262Sgreen 50876262Sgreen /* Generate additional parameters */ 50992559Sdes rsa_generate_additional_parameters(k->rsa); 51076262Sgreen break; 51176262Sgreen default: 51298684Sdes buffer_clear(&e->request); 51365674Skris goto send; 51457429Smarkm } 51565674Skris break; 51665674Skris } 517113911Sdes /* enable blinding */ 518113911Sdes switch (k->type) { 519113911Sdes case KEY_RSA: 520113911Sdes case KEY_RSA1: 521113911Sdes if (RSA_blinding_on(k->rsa, NULL) != 1) { 522113911Sdes error("process_add_identity: RSA_blinding_on failed"); 523113911Sdes key_free(k); 524113911Sdes goto send; 525113911Sdes } 526113911Sdes break; 527113911Sdes } 52898684Sdes comment = buffer_get_string(&e->request, NULL); 52965674Skris if (k == NULL) { 53065674Skris xfree(comment); 53165674Skris goto send; 53265674Skris } 53398684Sdes while (buffer_len(&e->request)) { 534181111Sdes switch ((type = buffer_get_char(&e->request))) { 53598684Sdes case SSH_AGENT_CONSTRAIN_LIFETIME: 53698684Sdes death = time(NULL) + buffer_get_int(&e->request); 53798684Sdes break; 538113911Sdes case SSH_AGENT_CONSTRAIN_CONFIRM: 539113911Sdes confirm = 1; 540113911Sdes break; 54198684Sdes default: 542181111Sdes error("process_add_identity: " 543181111Sdes "Unknown constraint type %d", type); 544181111Sdes xfree(comment); 545181111Sdes key_free(k); 546181111Sdes goto send; 54798684Sdes } 54898684Sdes } 549181111Sdes success = 1; 550113911Sdes if (lifetime && !death) 551113911Sdes death = time(NULL) + lifetime; 552181111Sdes if ((id = lookup_identity(k, version)) == NULL) { 553181111Sdes id = xmalloc(sizeof(Identity)); 55492559Sdes id->key = k; 55592559Sdes TAILQ_INSERT_TAIL(&tab->idlist, id, next); 55665674Skris /* Increment the number of identities. */ 55765674Skris tab->nentries++; 55865674Skris } else { 55965674Skris key_free(k); 560181111Sdes xfree(id->comment); 56165674Skris } 562181111Sdes id->comment = comment; 563181111Sdes id->death = death; 564181111Sdes id->confirm = confirm; 56565674Skrissend: 56657429Smarkm buffer_put_int(&e->output, 1); 56765674Skris buffer_put_char(&e->output, 56865674Skris success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 56957429Smarkm} 57057429Smarkm 57198684Sdes/* XXX todo: encrypt sensitive data with passphrase */ 57298684Sdesstatic void 57398684Sdesprocess_lock_agent(SocketEntry *e, int lock) 57498684Sdes{ 57599063Sdes int success = 0; 57698684Sdes char *passwd; 57792559Sdes 57898684Sdes passwd = buffer_get_string(&e->request, NULL); 57998684Sdes if (locked && !lock && strcmp(passwd, lock_passwd) == 0) { 58098684Sdes locked = 0; 58198684Sdes memset(lock_passwd, 0, strlen(lock_passwd)); 58298684Sdes xfree(lock_passwd); 58398684Sdes lock_passwd = NULL; 58498684Sdes success = 1; 58598684Sdes } else if (!locked && lock) { 58698684Sdes locked = 1; 58798684Sdes lock_passwd = xstrdup(passwd); 58898684Sdes success = 1; 58998684Sdes } 59098684Sdes memset(passwd, 0, strlen(passwd)); 59198684Sdes xfree(passwd); 59298684Sdes 59398684Sdes buffer_put_int(&e->output, 1); 59498684Sdes buffer_put_char(&e->output, 59598684Sdes success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 59698684Sdes} 59798684Sdes 59898684Sdesstatic void 59998684Sdesno_identities(SocketEntry *e, u_int type) 60098684Sdes{ 60198684Sdes Buffer msg; 60298684Sdes 60398684Sdes buffer_init(&msg); 60498684Sdes buffer_put_char(&msg, 60598684Sdes (type == SSH_AGENTC_REQUEST_RSA_IDENTITIES) ? 60698684Sdes SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); 60798684Sdes buffer_put_int(&msg, 0); 60898684Sdes buffer_put_int(&e->output, buffer_len(&msg)); 60998684Sdes buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); 61098684Sdes buffer_free(&msg); 61198684Sdes} 61298684Sdes 61392559Sdes#ifdef SMARTCARD 61492559Sdesstatic void 615181111Sdesprocess_add_smartcard_key(SocketEntry *e) 61692559Sdes{ 61799063Sdes char *sc_reader_id = NULL, *pin; 618181111Sdes int i, type, version, success = 0, death = 0, confirm = 0; 61999063Sdes Key **keys, *k; 62098684Sdes Identity *id; 62192559Sdes Idtab *tab; 62292559Sdes 62398684Sdes sc_reader_id = buffer_get_string(&e->request, NULL); 62498684Sdes pin = buffer_get_string(&e->request, NULL); 625124211Sdes 626124211Sdes while (buffer_len(&e->request)) { 627181111Sdes switch ((type = buffer_get_char(&e->request))) { 628124211Sdes case SSH_AGENT_CONSTRAIN_LIFETIME: 629124211Sdes death = time(NULL) + buffer_get_int(&e->request); 630124211Sdes break; 631124211Sdes case SSH_AGENT_CONSTRAIN_CONFIRM: 632124211Sdes confirm = 1; 633124211Sdes break; 634124211Sdes default: 635181111Sdes error("process_add_smartcard_key: " 636181111Sdes "Unknown constraint type %d", type); 637181111Sdes xfree(sc_reader_id); 638181111Sdes xfree(pin); 639181111Sdes goto send; 640124211Sdes } 641124211Sdes } 642124211Sdes if (lifetime && !death) 643124211Sdes death = time(NULL) + lifetime; 644124211Sdes 64598684Sdes keys = sc_get_keys(sc_reader_id, pin); 64692559Sdes xfree(sc_reader_id); 64798684Sdes xfree(pin); 64892559Sdes 64998684Sdes if (keys == NULL || keys[0] == NULL) { 65098684Sdes error("sc_get_keys failed"); 65192559Sdes goto send; 65292559Sdes } 65398684Sdes for (i = 0; keys[i] != NULL; i++) { 65498684Sdes k = keys[i]; 65598684Sdes version = k->type == KEY_RSA1 ? 1 : 2; 65698684Sdes tab = idtab_lookup(version); 65798684Sdes if (lookup_identity(k, version) == NULL) { 65898684Sdes id = xmalloc(sizeof(Identity)); 65998684Sdes id->key = k; 660124211Sdes id->comment = sc_get_key_label(k); 661124211Sdes id->death = death; 662124211Sdes id->confirm = confirm; 66398684Sdes TAILQ_INSERT_TAIL(&tab->idlist, id, next); 66498684Sdes tab->nentries++; 66598684Sdes success = 1; 66698684Sdes } else { 66798684Sdes key_free(k); 66898684Sdes } 66998684Sdes keys[i] = NULL; 67092559Sdes } 67198684Sdes xfree(keys); 67292559Sdessend: 67392559Sdes buffer_put_int(&e->output, 1); 67492559Sdes buffer_put_char(&e->output, 67592559Sdes success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 67692559Sdes} 67792559Sdes 67892559Sdesstatic void 67992559Sdesprocess_remove_smartcard_key(SocketEntry *e) 68092559Sdes{ 68199063Sdes char *sc_reader_id = NULL, *pin; 68299063Sdes int i, version, success = 0; 68399063Sdes Key **keys, *k = NULL; 68498684Sdes Identity *id; 68598684Sdes Idtab *tab; 68692559Sdes 68798684Sdes sc_reader_id = buffer_get_string(&e->request, NULL); 68898684Sdes pin = buffer_get_string(&e->request, NULL); 68998684Sdes keys = sc_get_keys(sc_reader_id, pin); 69092559Sdes xfree(sc_reader_id); 69198684Sdes xfree(pin); 69292559Sdes 69398684Sdes if (keys == NULL || keys[0] == NULL) { 69498684Sdes error("sc_get_keys failed"); 69598684Sdes goto send; 69698684Sdes } 69798684Sdes for (i = 0; keys[i] != NULL; i++) { 69898684Sdes k = keys[i]; 69998684Sdes version = k->type == KEY_RSA1 ? 1 : 2; 70098684Sdes if ((id = lookup_identity(k, version)) != NULL) { 70198684Sdes tab = idtab_lookup(version); 70292559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 70392559Sdes tab->nentries--; 70492559Sdes free_identity(id); 70592559Sdes success = 1; 70692559Sdes } 70792559Sdes key_free(k); 70898684Sdes keys[i] = NULL; 70992559Sdes } 71098684Sdes xfree(keys); 71198684Sdessend: 71292559Sdes buffer_put_int(&e->output, 1); 71392559Sdes buffer_put_char(&e->output, 71492559Sdes success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 71592559Sdes} 71692559Sdes#endif /* SMARTCARD */ 71792559Sdes 71865674Skris/* dispatch incoming messages */ 71965674Skris 72092559Sdesstatic void 72157429Smarkmprocess_message(SocketEntry *e) 72257429Smarkm{ 72399063Sdes u_int msg_len, type; 72476262Sgreen u_char *cp; 72598684Sdes 72657429Smarkm if (buffer_len(&e->input) < 5) 72757429Smarkm return; /* Incomplete message. */ 72892559Sdes cp = buffer_ptr(&e->input); 729162856Sdes msg_len = get_u32(cp); 73057429Smarkm if (msg_len > 256 * 1024) { 731106130Sdes close_socket(e); 73257429Smarkm return; 73357429Smarkm } 73457429Smarkm if (buffer_len(&e->input) < msg_len + 4) 73557429Smarkm return; 73698684Sdes 73798684Sdes /* move the current input to e->request */ 73857429Smarkm buffer_consume(&e->input, 4); 73998684Sdes buffer_clear(&e->request); 74098684Sdes buffer_append(&e->request, buffer_ptr(&e->input), msg_len); 74198684Sdes buffer_consume(&e->input, msg_len); 74298684Sdes type = buffer_get_char(&e->request); 74357429Smarkm 74498684Sdes /* check wheter agent is locked */ 74598684Sdes if (locked && type != SSH_AGENTC_UNLOCK) { 74698684Sdes buffer_clear(&e->request); 74798684Sdes switch (type) { 74898684Sdes case SSH_AGENTC_REQUEST_RSA_IDENTITIES: 74998684Sdes case SSH2_AGENTC_REQUEST_IDENTITIES: 75098684Sdes /* send empty lists */ 75198684Sdes no_identities(e, type); 75298684Sdes break; 75398684Sdes default: 75498684Sdes /* send a fail message for all other request types */ 75598684Sdes buffer_put_int(&e->output, 1); 75698684Sdes buffer_put_char(&e->output, SSH_AGENT_FAILURE); 75798684Sdes } 75898684Sdes return; 75998684Sdes } 76098684Sdes 76192559Sdes debug("type %d", type); 76257429Smarkm switch (type) { 76398684Sdes case SSH_AGENTC_LOCK: 76498684Sdes case SSH_AGENTC_UNLOCK: 76598684Sdes process_lock_agent(e, type == SSH_AGENTC_LOCK); 76698684Sdes break; 76765674Skris /* ssh1 */ 76865674Skris case SSH_AGENTC_RSA_CHALLENGE: 76965674Skris process_authentication_challenge1(e); 77065674Skris break; 77157429Smarkm case SSH_AGENTC_REQUEST_RSA_IDENTITIES: 77265674Skris process_request_identities(e, 1); 77357429Smarkm break; 77457429Smarkm case SSH_AGENTC_ADD_RSA_IDENTITY: 77598684Sdes case SSH_AGENTC_ADD_RSA_ID_CONSTRAINED: 77665674Skris process_add_identity(e, 1); 77757429Smarkm break; 77857429Smarkm case SSH_AGENTC_REMOVE_RSA_IDENTITY: 77965674Skris process_remove_identity(e, 1); 78057429Smarkm break; 78157429Smarkm case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: 78265674Skris process_remove_all_identities(e, 1); 78357429Smarkm break; 78465674Skris /* ssh2 */ 78565674Skris case SSH2_AGENTC_SIGN_REQUEST: 78665674Skris process_sign_request2(e); 78765674Skris break; 78865674Skris case SSH2_AGENTC_REQUEST_IDENTITIES: 78965674Skris process_request_identities(e, 2); 79065674Skris break; 79165674Skris case SSH2_AGENTC_ADD_IDENTITY: 79298684Sdes case SSH2_AGENTC_ADD_ID_CONSTRAINED: 79365674Skris process_add_identity(e, 2); 79465674Skris break; 79565674Skris case SSH2_AGENTC_REMOVE_IDENTITY: 79665674Skris process_remove_identity(e, 2); 79765674Skris break; 79865674Skris case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: 79965674Skris process_remove_all_identities(e, 2); 80065674Skris break; 80192559Sdes#ifdef SMARTCARD 80292559Sdes case SSH_AGENTC_ADD_SMARTCARD_KEY: 803124211Sdes case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED: 80492559Sdes process_add_smartcard_key(e); 80592559Sdes break; 80692559Sdes case SSH_AGENTC_REMOVE_SMARTCARD_KEY: 80792559Sdes process_remove_smartcard_key(e); 80892559Sdes break; 80992559Sdes#endif /* SMARTCARD */ 81057429Smarkm default: 81157429Smarkm /* Unknown message. Respond with failure. */ 81257429Smarkm error("Unknown message %d", type); 81398684Sdes buffer_clear(&e->request); 81457429Smarkm buffer_put_int(&e->output, 1); 81557429Smarkm buffer_put_char(&e->output, SSH_AGENT_FAILURE); 81657429Smarkm break; 81757429Smarkm } 81857429Smarkm} 81957429Smarkm 82092559Sdesstatic void 82192559Sdesnew_socket(sock_type type, int fd) 82257429Smarkm{ 823120161Snectar u_int i, old_alloc, new_alloc; 82499063Sdes 825137019Sdes set_nonblock(fd); 82657429Smarkm 82757429Smarkm if (fd > max_fd) 82857429Smarkm max_fd = fd; 82957429Smarkm 83057429Smarkm for (i = 0; i < sockets_alloc; i++) 83157429Smarkm if (sockets[i].type == AUTH_UNUSED) { 83257429Smarkm sockets[i].fd = fd; 83357429Smarkm buffer_init(&sockets[i].input); 83457429Smarkm buffer_init(&sockets[i].output); 83598684Sdes buffer_init(&sockets[i].request); 836120161Snectar sockets[i].type = type; 83757429Smarkm return; 83857429Smarkm } 83957429Smarkm old_alloc = sockets_alloc; 840120161Snectar new_alloc = sockets_alloc + 10; 841162856Sdes sockets = xrealloc(sockets, new_alloc, sizeof(sockets[0])); 842120161Snectar for (i = old_alloc; i < new_alloc; i++) 84357429Smarkm sockets[i].type = AUTH_UNUSED; 844120161Snectar sockets_alloc = new_alloc; 84557429Smarkm sockets[old_alloc].fd = fd; 84657429Smarkm buffer_init(&sockets[old_alloc].input); 84757429Smarkm buffer_init(&sockets[old_alloc].output); 84898684Sdes buffer_init(&sockets[old_alloc].request); 849120161Snectar sockets[old_alloc].type = type; 85057429Smarkm} 85157429Smarkm 85292559Sdesstatic int 853181111Sdesprepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp, 854181111Sdes struct timeval **tvpp) 85557429Smarkm{ 856181111Sdes u_int i, sz, deadline; 85776262Sgreen int n = 0; 858181111Sdes static struct timeval tv; 85976262Sgreen 86076262Sgreen for (i = 0; i < sockets_alloc; i++) { 86157429Smarkm switch (sockets[i].type) { 86257429Smarkm case AUTH_SOCKET: 86357429Smarkm case AUTH_CONNECTION: 86476262Sgreen n = MAX(n, sockets[i].fd); 86557429Smarkm break; 86657429Smarkm case AUTH_UNUSED: 86757429Smarkm break; 86857429Smarkm default: 86957429Smarkm fatal("Unknown socket type %d", sockets[i].type); 87057429Smarkm break; 87157429Smarkm } 87276262Sgreen } 87376262Sgreen 87476262Sgreen sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); 87592559Sdes if (*fdrp == NULL || sz > *nallocp) { 87676262Sgreen if (*fdrp) 87776262Sgreen xfree(*fdrp); 87876262Sgreen if (*fdwp) 87976262Sgreen xfree(*fdwp); 88076262Sgreen *fdrp = xmalloc(sz); 88176262Sgreen *fdwp = xmalloc(sz); 88292559Sdes *nallocp = sz; 88376262Sgreen } 88492559Sdes if (n < *fdl) 88592559Sdes debug("XXX shrink: %d < %d", n, *fdl); 88692559Sdes *fdl = n; 88776262Sgreen memset(*fdrp, 0, sz); 88876262Sgreen memset(*fdwp, 0, sz); 88976262Sgreen 89076262Sgreen for (i = 0; i < sockets_alloc; i++) { 89176262Sgreen switch (sockets[i].type) { 89276262Sgreen case AUTH_SOCKET: 89376262Sgreen case AUTH_CONNECTION: 89476262Sgreen FD_SET(sockets[i].fd, *fdrp); 89576262Sgreen if (buffer_len(&sockets[i].output) > 0) 89676262Sgreen FD_SET(sockets[i].fd, *fdwp); 89776262Sgreen break; 89876262Sgreen default: 89976262Sgreen break; 90076262Sgreen } 90176262Sgreen } 902181111Sdes deadline = reaper(); 903181111Sdes if (parent_alive_interval != 0) 904181111Sdes deadline = (deadline == 0) ? parent_alive_interval : 905181111Sdes MIN(deadline, parent_alive_interval); 906181111Sdes if (deadline == 0) { 907181111Sdes *tvpp = NULL; 908181111Sdes } else { 909181111Sdes tv.tv_sec = deadline; 910181111Sdes tv.tv_usec = 0; 911181111Sdes *tvpp = &tv; 912181111Sdes } 91376262Sgreen return (1); 91457429Smarkm} 91557429Smarkm 91692559Sdesstatic void 91757429Smarkmafter_select(fd_set *readset, fd_set *writeset) 91857429Smarkm{ 91999063Sdes struct sockaddr_un sunaddr; 92058585Skris socklen_t slen; 92157429Smarkm char buf[1024]; 92299063Sdes int len, sock; 92399063Sdes u_int i; 924106130Sdes uid_t euid; 925106130Sdes gid_t egid; 92657429Smarkm 92757429Smarkm for (i = 0; i < sockets_alloc; i++) 92857429Smarkm switch (sockets[i].type) { 92957429Smarkm case AUTH_UNUSED: 93057429Smarkm break; 93157429Smarkm case AUTH_SOCKET: 93257429Smarkm if (FD_ISSET(sockets[i].fd, readset)) { 93358585Skris slen = sizeof(sunaddr); 93476262Sgreen sock = accept(sockets[i].fd, 935162856Sdes (struct sockaddr *)&sunaddr, &slen); 93657429Smarkm if (sock < 0) { 93792559Sdes error("accept from AUTH_SOCKET: %s", 93892559Sdes strerror(errno)); 93957429Smarkm break; 94057429Smarkm } 941106130Sdes if (getpeereid(sock, &euid, &egid) < 0) { 942106130Sdes error("getpeereid %d failed: %s", 943106130Sdes sock, strerror(errno)); 944106130Sdes close(sock); 945106130Sdes break; 946106130Sdes } 947106130Sdes if ((euid != 0) && (getuid() != euid)) { 948106130Sdes error("uid mismatch: " 949106130Sdes "peer euid %u != uid %u", 950106130Sdes (u_int) euid, (u_int) getuid()); 951106130Sdes close(sock); 952106130Sdes break; 953106130Sdes } 95457429Smarkm new_socket(AUTH_CONNECTION, sock); 95557429Smarkm } 95657429Smarkm break; 95757429Smarkm case AUTH_CONNECTION: 95857429Smarkm if (buffer_len(&sockets[i].output) > 0 && 95957429Smarkm FD_ISSET(sockets[i].fd, writeset)) { 96076262Sgreen do { 96176262Sgreen len = write(sockets[i].fd, 96276262Sgreen buffer_ptr(&sockets[i].output), 96376262Sgreen buffer_len(&sockets[i].output)); 96476262Sgreen if (len == -1 && (errno == EAGAIN || 965181111Sdes errno == EINTR || 966181111Sdes errno == EWOULDBLOCK)) 96776262Sgreen continue; 96876262Sgreen break; 96976262Sgreen } while (1); 97057429Smarkm if (len <= 0) { 971106130Sdes close_socket(&sockets[i]); 97257429Smarkm break; 97357429Smarkm } 97457429Smarkm buffer_consume(&sockets[i].output, len); 97557429Smarkm } 97657429Smarkm if (FD_ISSET(sockets[i].fd, readset)) { 97776262Sgreen do { 97876262Sgreen len = read(sockets[i].fd, buf, sizeof(buf)); 97976262Sgreen if (len == -1 && (errno == EAGAIN || 980181111Sdes errno == EINTR || 981181111Sdes errno == EWOULDBLOCK)) 98276262Sgreen continue; 98376262Sgreen break; 98476262Sgreen } while (1); 98557429Smarkm if (len <= 0) { 986106130Sdes close_socket(&sockets[i]); 98757429Smarkm break; 98857429Smarkm } 98957429Smarkm buffer_append(&sockets[i].input, buf, len); 99057429Smarkm process_message(&sockets[i]); 99157429Smarkm } 99257429Smarkm break; 99357429Smarkm default: 99457429Smarkm fatal("Unknown type %d", sockets[i].type); 99557429Smarkm } 99657429Smarkm} 99757429Smarkm 99892559Sdesstatic void 999126277Sdescleanup_socket(void) 100057429Smarkm{ 100176262Sgreen if (socket_name[0]) 100276262Sgreen unlink(socket_name); 100376262Sgreen if (socket_dir[0]) 100476262Sgreen rmdir(socket_dir); 100557429Smarkm} 100657429Smarkm 1007126277Sdesvoid 100857429Smarkmcleanup_exit(int i) 100957429Smarkm{ 1010126277Sdes cleanup_socket(); 1011126277Sdes _exit(i); 101257429Smarkm} 101357429Smarkm 1014162856Sdes/*ARGSUSED*/ 101592559Sdesstatic void 101676262Sgreencleanup_handler(int sig) 101757429Smarkm{ 1018126277Sdes cleanup_socket(); 101976262Sgreen _exit(2); 102076262Sgreen} 102176262Sgreen 102292559Sdesstatic void 1023181111Sdescheck_parent_exists(void) 102492559Sdes{ 102592559Sdes if (parent_pid != -1 && kill(parent_pid, 0) < 0) { 102692559Sdes /* printf("Parent has died - Authentication agent exiting.\n"); */ 1027181111Sdes cleanup_socket(); 1028181111Sdes _exit(2); 102992559Sdes } 103092559Sdes} 103192559Sdes 103292559Sdesstatic void 103376262Sgreenusage(void) 103476262Sgreen{ 1035181111Sdes fprintf(stderr, "usage: %s [options] [command [arg ...]]\n", 103676262Sgreen __progname); 103792559Sdes fprintf(stderr, "Options:\n"); 103892559Sdes fprintf(stderr, " -c Generate C-shell commands on stdout.\n"); 103992559Sdes fprintf(stderr, " -s Generate Bourne shell commands on stdout.\n"); 104092559Sdes fprintf(stderr, " -k Kill the current agent.\n"); 104192559Sdes fprintf(stderr, " -d Debug mode.\n"); 104298684Sdes fprintf(stderr, " -a socket Bind agent socket to given name.\n"); 1043113911Sdes fprintf(stderr, " -t life Default identity lifetime (seconds).\n"); 104457429Smarkm exit(1); 104557429Smarkm} 104657429Smarkm 104757429Smarkmint 104857429Smarkmmain(int ac, char **av) 104957429Smarkm{ 1050113911Sdes int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0; 1051181111Sdes int sock, fd, ch, result, saved_errno; 1052137019Sdes u_int nalloc; 105399063Sdes char *shell, *format, *pidstr, *agentsocket = NULL; 105499063Sdes fd_set *readsetp = NULL, *writesetp = NULL; 105557429Smarkm struct sockaddr_un sunaddr; 105698941Sdes#ifdef HAVE_SETRLIMIT 105776262Sgreen struct rlimit rlim; 105898941Sdes#endif 105998941Sdes int prev_mask; 106099063Sdes extern int optind; 106199063Sdes extern char *optarg; 106257429Smarkm pid_t pid; 106399063Sdes char pidstrbuf[1 + 3 * sizeof pid]; 1064181111Sdes struct timeval *tvp = NULL; 106557429Smarkm 1066157019Sdes /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1067157019Sdes sanitise_stdfd(); 1068157019Sdes 1069106130Sdes /* drop */ 1070106130Sdes setegid(getgid()); 1071106130Sdes setgid(getgid()); 1072110506Sdes setuid(geteuid()); 1073106130Sdes 1074128460Sdes#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) 1075128460Sdes /* Disable ptrace on Linux without sgid bit */ 1076128460Sdes prctl(PR_SET_DUMPABLE, 0); 1077128460Sdes#endif 1078128460Sdes 107976262Sgreen SSLeay_add_all_algorithms(); 108076262Sgreen 1081124211Sdes __progname = ssh_get_progname(av[0]); 108298941Sdes init_rng(); 108398941Sdes seed_rng(); 108498941Sdes 1085113911Sdes while ((ch = getopt(ac, av, "cdksa:t:")) != -1) { 108657429Smarkm switch (ch) { 108757429Smarkm case 'c': 108857429Smarkm if (s_flag) 108957429Smarkm usage(); 109057429Smarkm c_flag++; 109157429Smarkm break; 109257429Smarkm case 'k': 109357429Smarkm k_flag++; 109457429Smarkm break; 109557429Smarkm case 's': 109657429Smarkm if (c_flag) 109757429Smarkm usage(); 109857429Smarkm s_flag++; 109957429Smarkm break; 110092559Sdes case 'd': 110192559Sdes if (d_flag) 110292559Sdes usage(); 110392559Sdes d_flag++; 110492559Sdes break; 110598684Sdes case 'a': 110698684Sdes agentsocket = optarg; 110798684Sdes break; 1108113911Sdes case 't': 1109113911Sdes if ((lifetime = convtime(optarg)) == -1) { 1110113911Sdes fprintf(stderr, "Invalid lifetime\n"); 1111113911Sdes usage(); 1112113911Sdes } 1113113911Sdes break; 111457429Smarkm default: 111557429Smarkm usage(); 111657429Smarkm } 111757429Smarkm } 111857429Smarkm ac -= optind; 111957429Smarkm av += optind; 112057429Smarkm 112192559Sdes if (ac > 0 && (c_flag || k_flag || s_flag || d_flag)) 112257429Smarkm usage(); 112357429Smarkm 112498684Sdes if (ac == 0 && !c_flag && !s_flag) { 112557429Smarkm shell = getenv("SHELL"); 1126162856Sdes if (shell != NULL && 1127162856Sdes strncmp(shell + strlen(shell) - 3, "csh", 3) == 0) 112857429Smarkm c_flag = 1; 112957429Smarkm } 113057429Smarkm if (k_flag) { 1131162856Sdes const char *errstr = NULL; 1132162856Sdes 113357429Smarkm pidstr = getenv(SSH_AGENTPID_ENV_NAME); 113457429Smarkm if (pidstr == NULL) { 113557429Smarkm fprintf(stderr, "%s not set, cannot kill agent\n", 113676262Sgreen SSH_AGENTPID_ENV_NAME); 113757429Smarkm exit(1); 113857429Smarkm } 1139162856Sdes pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr); 1140162856Sdes if (errstr) { 1141162856Sdes fprintf(stderr, 1142162856Sdes "%s=\"%s\", which is not a good PID: %s\n", 1143162856Sdes SSH_AGENTPID_ENV_NAME, pidstr, errstr); 114457429Smarkm exit(1); 114557429Smarkm } 114657429Smarkm if (kill(pid, SIGTERM) == -1) { 114757429Smarkm perror("kill"); 114857429Smarkm exit(1); 114957429Smarkm } 115057429Smarkm format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; 115157429Smarkm printf(format, SSH_AUTHSOCKET_ENV_NAME); 115257429Smarkm printf(format, SSH_AGENTPID_ENV_NAME); 115398684Sdes printf("echo Agent pid %ld killed;\n", (long)pid); 115457429Smarkm exit(0); 115557429Smarkm } 115657429Smarkm parent_pid = getpid(); 115757429Smarkm 115898684Sdes if (agentsocket == NULL) { 115998684Sdes /* Create private directory for agent socket */ 1160126277Sdes strlcpy(socket_dir, "/tmp/ssh-XXXXXXXXXX", sizeof socket_dir); 116198684Sdes if (mkdtemp(socket_dir) == NULL) { 116298684Sdes perror("mkdtemp: private socket dir"); 116398684Sdes exit(1); 116498684Sdes } 116598684Sdes snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, 116698684Sdes (long)parent_pid); 116798684Sdes } else { 116898684Sdes /* Try to use specified agent socket */ 116998684Sdes socket_dir[0] = '\0'; 117098684Sdes strlcpy(socket_name, agentsocket, sizeof socket_name); 117157429Smarkm } 117257429Smarkm 117357429Smarkm /* 117457429Smarkm * Create socket early so it will exist before command gets run from 117557429Smarkm * the parent. 117657429Smarkm */ 117757429Smarkm sock = socket(AF_UNIX, SOCK_STREAM, 0); 117857429Smarkm if (sock < 0) { 117957429Smarkm perror("socket"); 1180147005Sdes *socket_name = '\0'; /* Don't unlink any existing file */ 118157429Smarkm cleanup_exit(1); 118257429Smarkm } 118357429Smarkm memset(&sunaddr, 0, sizeof(sunaddr)); 118457429Smarkm sunaddr.sun_family = AF_UNIX; 118557429Smarkm strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); 118698941Sdes prev_mask = umask(0177); 1187162856Sdes if (bind(sock, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) { 118857429Smarkm perror("bind"); 1189147005Sdes *socket_name = '\0'; /* Don't unlink any existing file */ 119098941Sdes umask(prev_mask); 119157429Smarkm cleanup_exit(1); 119257429Smarkm } 119398941Sdes umask(prev_mask); 1194126277Sdes if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { 119557429Smarkm perror("listen"); 119657429Smarkm cleanup_exit(1); 119757429Smarkm } 119876262Sgreen 119957429Smarkm /* 120057429Smarkm * Fork, and have the parent execute the command, if any, or present 120157429Smarkm * the socket data. The child continues as the authentication agent. 120257429Smarkm */ 120392559Sdes if (d_flag) { 120492559Sdes log_init(__progname, SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 1); 120592559Sdes format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; 120692559Sdes printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, 120792559Sdes SSH_AUTHSOCKET_ENV_NAME); 120898684Sdes printf("echo Agent pid %ld;\n", (long)parent_pid); 120992559Sdes goto skip; 121092559Sdes } 121157429Smarkm pid = fork(); 121257429Smarkm if (pid == -1) { 121357429Smarkm perror("fork"); 121492559Sdes cleanup_exit(1); 121557429Smarkm } 121657429Smarkm if (pid != 0) { /* Parent - execute the given command. */ 121757429Smarkm close(sock); 121898684Sdes snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); 121957429Smarkm if (ac == 0) { 122057429Smarkm format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; 122157429Smarkm printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, 122276262Sgreen SSH_AUTHSOCKET_ENV_NAME); 122357429Smarkm printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, 122476262Sgreen SSH_AGENTPID_ENV_NAME); 122598684Sdes printf("echo Agent pid %ld;\n", (long)pid); 122657429Smarkm exit(0); 122757429Smarkm } 122869591Sgreen if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || 122969591Sgreen setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { 123069591Sgreen perror("setenv"); 123169591Sgreen exit(1); 123269591Sgreen } 123357429Smarkm execvp(av[0], av); 123457429Smarkm perror(av[0]); 123557429Smarkm exit(1); 123657429Smarkm } 123792559Sdes /* child */ 123892559Sdes log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); 123992559Sdes 124092559Sdes if (setsid() == -1) { 124192559Sdes error("setsid: %s", strerror(errno)); 124292559Sdes cleanup_exit(1); 124392559Sdes } 124492559Sdes 124592559Sdes (void)chdir("/"); 1246113911Sdes if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { 1247113911Sdes /* XXX might close listen socket */ 1248113911Sdes (void)dup2(fd, STDIN_FILENO); 1249113911Sdes (void)dup2(fd, STDOUT_FILENO); 1250113911Sdes (void)dup2(fd, STDERR_FILENO); 1251113911Sdes if (fd > 2) 1252113911Sdes close(fd); 1253113911Sdes } 125457429Smarkm 125598941Sdes#ifdef HAVE_SETRLIMIT 125676262Sgreen /* deny core dumps, since memory contains unencrypted private keys */ 125776262Sgreen rlim.rlim_cur = rlim.rlim_max = 0; 125876262Sgreen if (setrlimit(RLIMIT_CORE, &rlim) < 0) { 125992559Sdes error("setrlimit RLIMIT_CORE: %s", strerror(errno)); 126076262Sgreen cleanup_exit(1); 126176262Sgreen } 126298941Sdes#endif 126392559Sdes 126492559Sdesskip: 126557429Smarkm new_socket(AUTH_SOCKET, sock); 1266181111Sdes if (ac > 0) 1267181111Sdes parent_alive_interval = 10; 126865674Skris idtab_init(); 126992559Sdes if (!d_flag) 127092559Sdes signal(SIGINT, SIG_IGN); 127157429Smarkm signal(SIGPIPE, SIG_IGN); 127276262Sgreen signal(SIGHUP, cleanup_handler); 127376262Sgreen signal(SIGTERM, cleanup_handler); 127492559Sdes nalloc = 0; 127592559Sdes 127657429Smarkm while (1) { 1277181111Sdes prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp); 1278181111Sdes result = select(max_fd + 1, readsetp, writesetp, NULL, tvp); 1279181111Sdes saved_errno = errno; 1280181111Sdes if (parent_alive_interval != 0) 1281181111Sdes check_parent_exists(); 1282181111Sdes (void) reaper(); /* remove expired keys */ 1283181111Sdes if (result < 0) { 1284181111Sdes if (saved_errno == EINTR) 128557429Smarkm continue; 1286181111Sdes fatal("select: %s", strerror(saved_errno)); 1287181111Sdes } else if (result > 0) 1288181111Sdes after_select(readsetp, writesetp); 128957429Smarkm } 129057429Smarkm /* NOTREACHED */ 129157429Smarkm} 1292