ssh-agent.c revision 113911
157429Smarkm/* 257429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 357429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 457429Smarkm * All rights reserved 557429Smarkm * The authentication agent program. 665674Skris * 765674Skris * As far as I am concerned, the code I have written for this software 865674Skris * can be used freely for any purpose. Any derived versions of this 965674Skris * software must be clearly marked as such, and if the derived work is 1065674Skris * incompatible with the protocol description in the RFC file, it must be 1165674Skris * called by a name other than "ssh" or "Secure Shell". 1265674Skris * 1392559Sdes * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 1465674Skris * 1565674Skris * Redistribution and use in source and binary forms, with or without 1665674Skris * modification, are permitted provided that the following conditions 1765674Skris * are met: 1865674Skris * 1. Redistributions of source code must retain the above copyright 1965674Skris * notice, this list of conditions and the following disclaimer. 2065674Skris * 2. Redistributions in binary form must reproduce the above copyright 2165674Skris * notice, this list of conditions and the following disclaimer in the 2265674Skris * documentation and/or other materials provided with the distribution. 2365674Skris * 2465674Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2565674Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2665674Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2765674Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2865674Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2965674Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3065674Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3165674Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3265674Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3365674Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3457429Smarkm */ 3557429Smarkm 3657429Smarkm#include "includes.h" 37106130Sdes#include "openbsd-compat/sys-queue.h" 38113911SdesRCSID("$OpenBSD: ssh-agent.c,v 1.108 2003/03/13 11:44:50 markus Exp $"); 3999063SdesRCSID("$FreeBSD: head/crypto/openssh/ssh-agent.c 113911 2003-04-23 17:13:13Z des $"); 4057429Smarkm 4176262Sgreen#include <openssl/evp.h> 4276262Sgreen#include <openssl/md5.h> 4376262Sgreen 4457429Smarkm#include "ssh.h" 4557429Smarkm#include "rsa.h" 4657429Smarkm#include "buffer.h" 4757429Smarkm#include "bufaux.h" 4857429Smarkm#include "xmalloc.h" 4957429Smarkm#include "getput.h" 5065674Skris#include "key.h" 5165674Skris#include "authfd.h" 5269591Sgreen#include "compat.h" 5376262Sgreen#include "log.h" 54113911Sdes#include "readpass.h" 55113911Sdes#include "misc.h" 5657429Smarkm 5792559Sdes#ifdef SMARTCARD 5892559Sdes#include "scard.h" 5992559Sdes#endif 6092559Sdes 6192559Sdestypedef enum { 6292559Sdes AUTH_UNUSED, 6392559Sdes AUTH_SOCKET, 6492559Sdes AUTH_CONNECTION 6592559Sdes} sock_type; 6692559Sdes 6757429Smarkmtypedef struct { 6857429Smarkm int fd; 6992559Sdes sock_type type; 7057429Smarkm Buffer input; 7157429Smarkm Buffer output; 7298684Sdes Buffer request; 7357429Smarkm} SocketEntry; 7457429Smarkm 7576262Sgreenu_int sockets_alloc = 0; 7657429SmarkmSocketEntry *sockets = NULL; 7757429Smarkm 7892559Sdestypedef struct identity { 7992559Sdes TAILQ_ENTRY(identity) next; 8065674Skris Key *key; 8157429Smarkm char *comment; 8298684Sdes u_int death; 83113911Sdes u_int confirm; 8457429Smarkm} Identity; 8557429Smarkm 8665674Skristypedef struct { 8765674Skris int nentries; 8892559Sdes TAILQ_HEAD(idqueue, identity) idlist; 8965674Skris} Idtab; 9057429Smarkm 9165674Skris/* private key table, one per protocol version */ 9265674SkrisIdtab idtable[3]; 9365674Skris 9457429Smarkmint max_fd = 0; 9557429Smarkm 9657429Smarkm/* pid of shell == parent of agent */ 9760576Skrispid_t parent_pid = -1; 9857429Smarkm 9957429Smarkm/* pathname and directory for AUTH_SOCKET */ 10057429Smarkmchar socket_name[1024]; 10157429Smarkmchar socket_dir[1024]; 10257429Smarkm 10398684Sdes/* locking */ 10498684Sdesint locked = 0; 10598684Sdeschar *lock_passwd = NULL; 10698684Sdes 10798941Sdes#ifdef HAVE___PROGNAME 10857429Smarkmextern char *__progname; 10998941Sdes#else 11098941Sdeschar *__progname; 11198941Sdes#endif 11257429Smarkm 113113911Sdes/* Default lifetime (0 == forever) */ 114113911Sdesstatic int lifetime = 0; 115113911Sdes 11692559Sdesstatic void 117106130Sdesclose_socket(SocketEntry *e) 118106130Sdes{ 119106130Sdes close(e->fd); 120106130Sdes e->fd = -1; 121106130Sdes e->type = AUTH_UNUSED; 122106130Sdes buffer_free(&e->input); 123106130Sdes buffer_free(&e->output); 124106130Sdes buffer_free(&e->request); 125106130Sdes} 126106130Sdes 127106130Sdesstatic void 12865674Skrisidtab_init(void) 12957429Smarkm{ 13065674Skris int i; 13199063Sdes 13292559Sdes for (i = 0; i <=2; i++) { 13392559Sdes TAILQ_INIT(&idtable[i].idlist); 13465674Skris idtable[i].nentries = 0; 13565674Skris } 13665674Skris} 13765674Skris 13865674Skris/* return private key table for requested protocol version */ 13992559Sdesstatic Idtab * 14065674Skrisidtab_lookup(int version) 14165674Skris{ 14265674Skris if (version < 1 || version > 2) 14365674Skris fatal("internal error, bad protocol version %d", version); 14465674Skris return &idtable[version]; 14565674Skris} 14665674Skris 14798684Sdesstatic void 14898684Sdesfree_identity(Identity *id) 14998684Sdes{ 15098684Sdes key_free(id->key); 15198684Sdes xfree(id->comment); 15298684Sdes xfree(id); 15398684Sdes} 15498684Sdes 15565674Skris/* return matching private key for given public key */ 15692559Sdesstatic Identity * 15792559Sdeslookup_identity(Key *key, int version) 15865674Skris{ 15992559Sdes Identity *id; 16092559Sdes 16165674Skris Idtab *tab = idtab_lookup(version); 16292559Sdes TAILQ_FOREACH(id, &tab->idlist, next) { 16392559Sdes if (key_equal(key, id->key)) 16492559Sdes return (id); 16565674Skris } 16692559Sdes return (NULL); 16765674Skris} 16865674Skris 169113911Sdes/* Check confirmation of keysign request */ 170113911Sdesstatic int 171113911Sdesconfirm_key(Identity *id) 172113911Sdes{ 173113911Sdes char *p, prompt[1024]; 174113911Sdes int ret = -1; 175113911Sdes 176113911Sdes p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); 177113911Sdes snprintf(prompt, sizeof(prompt), "Allow use of key %s?\n" 178113911Sdes "Key fingerprint %s.", id->comment, p); 179113911Sdes xfree(p); 180113911Sdes p = read_passphrase(prompt, RP_ALLOW_EOF); 181113911Sdes if (p != NULL) { 182113911Sdes /* 183113911Sdes * Accept empty responses and responses consisting 184113911Sdes * of the word "yes" as affirmative. 185113911Sdes */ 186113911Sdes if (*p == '\0' || *p == '\n' || strcasecmp(p, "yes") == 0) 187113911Sdes ret = 0; 188113911Sdes xfree(p); 189113911Sdes } 190113911Sdes return (ret); 191113911Sdes} 192113911Sdes 19365674Skris/* send list of supported public keys to 'client' */ 19492559Sdesstatic void 19565674Skrisprocess_request_identities(SocketEntry *e, int version) 19665674Skris{ 19765674Skris Idtab *tab = idtab_lookup(version); 19899063Sdes Identity *id; 19957429Smarkm Buffer msg; 20057429Smarkm 20157429Smarkm buffer_init(&msg); 20265674Skris buffer_put_char(&msg, (version == 1) ? 20365674Skris SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); 20465674Skris buffer_put_int(&msg, tab->nentries); 20592559Sdes TAILQ_FOREACH(id, &tab->idlist, next) { 20676262Sgreen if (id->key->type == KEY_RSA1) { 20765674Skris buffer_put_int(&msg, BN_num_bits(id->key->rsa->n)); 20865674Skris buffer_put_bignum(&msg, id->key->rsa->e); 20965674Skris buffer_put_bignum(&msg, id->key->rsa->n); 21065674Skris } else { 21176262Sgreen u_char *blob; 21276262Sgreen u_int blen; 21376262Sgreen key_to_blob(id->key, &blob, &blen); 21465674Skris buffer_put_string(&msg, blob, blen); 21565674Skris xfree(blob); 21665674Skris } 21765674Skris buffer_put_cstring(&msg, id->comment); 21857429Smarkm } 21957429Smarkm buffer_put_int(&e->output, buffer_len(&msg)); 22057429Smarkm buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); 22157429Smarkm buffer_free(&msg); 22257429Smarkm} 22357429Smarkm 22465674Skris/* ssh1 only */ 22592559Sdesstatic void 22665674Skrisprocess_authentication_challenge1(SocketEntry *e) 22757429Smarkm{ 22899063Sdes u_char buf[32], mdbuf[16], session_id[16]; 22999063Sdes u_int response_type; 23099063Sdes BIGNUM *challenge; 23192559Sdes Identity *id; 23265674Skris int i, len; 23357429Smarkm Buffer msg; 23457429Smarkm MD5_CTX md; 23599063Sdes Key *key; 23657429Smarkm 23757429Smarkm buffer_init(&msg); 23876262Sgreen key = key_new(KEY_RSA1); 23992559Sdes if ((challenge = BN_new()) == NULL) 24092559Sdes fatal("process_authentication_challenge1: BN_new failed"); 24165674Skris 24299063Sdes (void) buffer_get_int(&e->request); /* ignored */ 24398684Sdes buffer_get_bignum(&e->request, key->rsa->e); 24498684Sdes buffer_get_bignum(&e->request, key->rsa->n); 24598684Sdes buffer_get_bignum(&e->request, challenge); 24657429Smarkm 24765674Skris /* Only protocol 1.1 is supported */ 24898684Sdes if (buffer_len(&e->request) == 0) 24965674Skris goto failure; 25098684Sdes buffer_get(&e->request, session_id, 16); 25198684Sdes response_type = buffer_get_int(&e->request); 25265674Skris if (response_type != 1) 25365674Skris goto failure; 25457429Smarkm 25592559Sdes id = lookup_identity(key, 1); 256113911Sdes if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { 25792559Sdes Key *private = id->key; 25865674Skris /* Decrypt the challenge using the private key. */ 25972397Skris if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0) 26072397Skris goto failure; 26157429Smarkm 26265674Skris /* The response is MD5 of decrypted challenge plus session id. */ 26365674Skris len = BN_num_bytes(challenge); 26465674Skris if (len <= 0 || len > 32) { 26565674Skris log("process_authentication_challenge: bad challenge length %d", len); 26665674Skris goto failure; 26765674Skris } 26865674Skris memset(buf, 0, 32); 26965674Skris BN_bn2bin(challenge, buf + 32 - len); 27065674Skris MD5_Init(&md); 27165674Skris MD5_Update(&md, buf, 32); 27265674Skris MD5_Update(&md, session_id, 16); 27365674Skris MD5_Final(mdbuf, &md); 27457429Smarkm 27565674Skris /* Send the response. */ 27665674Skris buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); 27765674Skris for (i = 0; i < 16; i++) 27865674Skris buffer_put_char(&msg, mdbuf[i]); 27965674Skris goto send; 28065674Skris } 28157429Smarkm 28265674Skrisfailure: 28365674Skris /* Unknown identity or protocol error. Send failure. */ 28457429Smarkm buffer_put_char(&msg, SSH_AGENT_FAILURE); 28557429Smarkmsend: 28657429Smarkm buffer_put_int(&e->output, buffer_len(&msg)); 28765674Skris buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); 28865674Skris key_free(key); 28965674Skris BN_clear_free(challenge); 29065674Skris buffer_free(&msg); 29165674Skris} 29265674Skris 29365674Skris/* ssh2 only */ 29492559Sdesstatic void 29565674Skrisprocess_sign_request2(SocketEntry *e) 29665674Skris{ 29776262Sgreen u_char *blob, *data, *signature = NULL; 29876262Sgreen u_int blen, dlen, slen = 0; 29999063Sdes extern int datafellows; 30099063Sdes int ok = -1, flags; 30165674Skris Buffer msg; 30299063Sdes Key *key; 30365674Skris 30465674Skris datafellows = 0; 30576262Sgreen 30698684Sdes blob = buffer_get_string(&e->request, &blen); 30798684Sdes data = buffer_get_string(&e->request, &dlen); 30865674Skris 30998684Sdes flags = buffer_get_int(&e->request); 31069591Sgreen if (flags & SSH_AGENT_OLD_SIGNATURE) 31169591Sgreen datafellows = SSH_BUG_SIGBLOB; 31269591Sgreen 31376262Sgreen key = key_from_blob(blob, blen); 31465674Skris if (key != NULL) { 31592559Sdes Identity *id = lookup_identity(key, 2); 316113911Sdes if (id != NULL && (!id->confirm || confirm_key(id) == 0)) 31792559Sdes ok = key_sign(id->key, &signature, &slen, data, dlen); 31865674Skris } 31965674Skris key_free(key); 32065674Skris buffer_init(&msg); 32165674Skris if (ok == 0) { 32265674Skris buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE); 32365674Skris buffer_put_string(&msg, signature, slen); 32465674Skris } else { 32565674Skris buffer_put_char(&msg, SSH_AGENT_FAILURE); 32665674Skris } 32765674Skris buffer_put_int(&e->output, buffer_len(&msg)); 32857429Smarkm buffer_append(&e->output, buffer_ptr(&msg), 32965674Skris buffer_len(&msg)); 33057429Smarkm buffer_free(&msg); 33165674Skris xfree(data); 33265674Skris xfree(blob); 33365674Skris if (signature != NULL) 33465674Skris xfree(signature); 33557429Smarkm} 33657429Smarkm 33765674Skris/* shared */ 33892559Sdesstatic void 33965674Skrisprocess_remove_identity(SocketEntry *e, int version) 34057429Smarkm{ 34199063Sdes u_int blen, bits; 34299063Sdes int success = 0; 34392559Sdes Key *key = NULL; 34476262Sgreen u_char *blob; 34557429Smarkm 34692559Sdes switch (version) { 34765674Skris case 1: 34876262Sgreen key = key_new(KEY_RSA1); 34998684Sdes bits = buffer_get_int(&e->request); 35098684Sdes buffer_get_bignum(&e->request, key->rsa->e); 35198684Sdes buffer_get_bignum(&e->request, key->rsa->n); 35257429Smarkm 35365674Skris if (bits != key_size(key)) 35499063Sdes log("Warning: identity keysize mismatch: actual %u, announced %u", 35576262Sgreen key_size(key), bits); 35665674Skris break; 35765674Skris case 2: 35898684Sdes blob = buffer_get_string(&e->request, &blen); 35976262Sgreen key = key_from_blob(blob, blen); 36065674Skris xfree(blob); 36165674Skris break; 36265674Skris } 36365674Skris if (key != NULL) { 36492559Sdes Identity *id = lookup_identity(key, version); 36592559Sdes if (id != NULL) { 36657429Smarkm /* 36757429Smarkm * We have this key. Free the old key. Since we 36857429Smarkm * don\'t want to leave empty slots in the middle of 36976262Sgreen * the array, we actually free the key there and move 37076262Sgreen * all the entries between the empty slot and the end 37176262Sgreen * of the array. 37257429Smarkm */ 37365674Skris Idtab *tab = idtab_lookup(version); 37476262Sgreen if (tab->nentries < 1) 37576262Sgreen fatal("process_remove_identity: " 37676262Sgreen "internal error: tab->nentries %d", 37776262Sgreen tab->nentries); 37892559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 37992559Sdes free_identity(id); 38065674Skris tab->nentries--; 38165674Skris success = 1; 38257429Smarkm } 38365674Skris key_free(key); 38465674Skris } 38557429Smarkm buffer_put_int(&e->output, 1); 38665674Skris buffer_put_char(&e->output, 38765674Skris success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 38857429Smarkm} 38957429Smarkm 39092559Sdesstatic void 39165674Skrisprocess_remove_all_identities(SocketEntry *e, int version) 39257429Smarkm{ 39365674Skris Idtab *tab = idtab_lookup(version); 39492559Sdes Identity *id; 39557429Smarkm 39657429Smarkm /* Loop over all identities and clear the keys. */ 39792559Sdes for (id = TAILQ_FIRST(&tab->idlist); id; 39892559Sdes id = TAILQ_FIRST(&tab->idlist)) { 39992559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 40092559Sdes free_identity(id); 40157429Smarkm } 40257429Smarkm 40357429Smarkm /* Mark that there are no identities. */ 40465674Skris tab->nentries = 0; 40557429Smarkm 40657429Smarkm /* Send success. */ 40757429Smarkm buffer_put_int(&e->output, 1); 40857429Smarkm buffer_put_char(&e->output, SSH_AGENT_SUCCESS); 40957429Smarkm} 41057429Smarkm 41192559Sdesstatic void 41298684Sdesreaper(void) 41398684Sdes{ 41499063Sdes u_int now = time(NULL); 41598684Sdes Identity *id, *nxt; 41698684Sdes int version; 41799063Sdes Idtab *tab; 41898684Sdes 41998684Sdes for (version = 1; version < 3; version++) { 42098684Sdes tab = idtab_lookup(version); 42198684Sdes for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { 42298684Sdes nxt = TAILQ_NEXT(id, next); 42398684Sdes if (id->death != 0 && now >= id->death) { 42498684Sdes TAILQ_REMOVE(&tab->idlist, id, next); 42598684Sdes free_identity(id); 42698684Sdes tab->nentries--; 42798684Sdes } 42898684Sdes } 42998684Sdes } 43098684Sdes} 43198684Sdes 43298684Sdesstatic void 43365674Skrisprocess_add_identity(SocketEntry *e, int version) 43457429Smarkm{ 43599063Sdes Idtab *tab = idtab_lookup(version); 436113911Sdes int type, success = 0, death = 0, confirm = 0; 43799063Sdes char *type_name, *comment; 43865674Skris Key *k = NULL; 43957429Smarkm 44065674Skris switch (version) { 44165674Skris case 1: 44276262Sgreen k = key_new_private(KEY_RSA1); 44399063Sdes (void) buffer_get_int(&e->request); /* ignored */ 44498684Sdes buffer_get_bignum(&e->request, k->rsa->n); 44598684Sdes buffer_get_bignum(&e->request, k->rsa->e); 44698684Sdes buffer_get_bignum(&e->request, k->rsa->d); 44798684Sdes buffer_get_bignum(&e->request, k->rsa->iqmp); 44857429Smarkm 44965674Skris /* SSH and SSL have p and q swapped */ 45098684Sdes buffer_get_bignum(&e->request, k->rsa->q); /* p */ 45198684Sdes buffer_get_bignum(&e->request, k->rsa->p); /* q */ 45257429Smarkm 45365674Skris /* Generate additional parameters */ 45492559Sdes rsa_generate_additional_parameters(k->rsa); 45565674Skris break; 45665674Skris case 2: 45798684Sdes type_name = buffer_get_string(&e->request, NULL); 45876262Sgreen type = key_type_from_name(type_name); 45976262Sgreen xfree(type_name); 46092559Sdes switch (type) { 46176262Sgreen case KEY_DSA: 46276262Sgreen k = key_new_private(type); 46398684Sdes buffer_get_bignum2(&e->request, k->dsa->p); 46498684Sdes buffer_get_bignum2(&e->request, k->dsa->q); 46598684Sdes buffer_get_bignum2(&e->request, k->dsa->g); 46698684Sdes buffer_get_bignum2(&e->request, k->dsa->pub_key); 46798684Sdes buffer_get_bignum2(&e->request, k->dsa->priv_key); 46876262Sgreen break; 46976262Sgreen case KEY_RSA: 47076262Sgreen k = key_new_private(type); 47198684Sdes buffer_get_bignum2(&e->request, k->rsa->n); 47298684Sdes buffer_get_bignum2(&e->request, k->rsa->e); 47398684Sdes buffer_get_bignum2(&e->request, k->rsa->d); 47498684Sdes buffer_get_bignum2(&e->request, k->rsa->iqmp); 47598684Sdes buffer_get_bignum2(&e->request, k->rsa->p); 47698684Sdes buffer_get_bignum2(&e->request, k->rsa->q); 47776262Sgreen 47876262Sgreen /* Generate additional parameters */ 47992559Sdes rsa_generate_additional_parameters(k->rsa); 48076262Sgreen break; 48176262Sgreen default: 48298684Sdes buffer_clear(&e->request); 48365674Skris goto send; 48457429Smarkm } 48565674Skris break; 48665674Skris } 487113911Sdes /* enable blinding */ 488113911Sdes switch (k->type) { 489113911Sdes case KEY_RSA: 490113911Sdes case KEY_RSA1: 491113911Sdes if (RSA_blinding_on(k->rsa, NULL) != 1) { 492113911Sdes error("process_add_identity: RSA_blinding_on failed"); 493113911Sdes key_free(k); 494113911Sdes goto send; 495113911Sdes } 496113911Sdes break; 497113911Sdes } 49898684Sdes comment = buffer_get_string(&e->request, NULL); 49965674Skris if (k == NULL) { 50065674Skris xfree(comment); 50165674Skris goto send; 50265674Skris } 50365674Skris success = 1; 50498684Sdes while (buffer_len(&e->request)) { 50598684Sdes switch (buffer_get_char(&e->request)) { 50698684Sdes case SSH_AGENT_CONSTRAIN_LIFETIME: 50798684Sdes death = time(NULL) + buffer_get_int(&e->request); 50898684Sdes break; 509113911Sdes case SSH_AGENT_CONSTRAIN_CONFIRM: 510113911Sdes confirm = 1; 511113911Sdes break; 51298684Sdes default: 51398684Sdes break; 51498684Sdes } 51598684Sdes } 516113911Sdes if (lifetime && !death) 517113911Sdes death = time(NULL) + lifetime; 51892559Sdes if (lookup_identity(k, version) == NULL) { 51992559Sdes Identity *id = xmalloc(sizeof(Identity)); 52092559Sdes id->key = k; 52192559Sdes id->comment = comment; 52298684Sdes id->death = death; 523113911Sdes id->confirm = confirm; 52492559Sdes TAILQ_INSERT_TAIL(&tab->idlist, id, next); 52565674Skris /* Increment the number of identities. */ 52665674Skris tab->nentries++; 52765674Skris } else { 52865674Skris key_free(k); 52965674Skris xfree(comment); 53065674Skris } 53165674Skrissend: 53257429Smarkm buffer_put_int(&e->output, 1); 53365674Skris buffer_put_char(&e->output, 53465674Skris success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 53557429Smarkm} 53657429Smarkm 53798684Sdes/* XXX todo: encrypt sensitive data with passphrase */ 53898684Sdesstatic void 53998684Sdesprocess_lock_agent(SocketEntry *e, int lock) 54098684Sdes{ 54199063Sdes int success = 0; 54298684Sdes char *passwd; 54392559Sdes 54498684Sdes passwd = buffer_get_string(&e->request, NULL); 54598684Sdes if (locked && !lock && strcmp(passwd, lock_passwd) == 0) { 54698684Sdes locked = 0; 54798684Sdes memset(lock_passwd, 0, strlen(lock_passwd)); 54898684Sdes xfree(lock_passwd); 54998684Sdes lock_passwd = NULL; 55098684Sdes success = 1; 55198684Sdes } else if (!locked && lock) { 55298684Sdes locked = 1; 55398684Sdes lock_passwd = xstrdup(passwd); 55498684Sdes success = 1; 55598684Sdes } 55698684Sdes memset(passwd, 0, strlen(passwd)); 55798684Sdes xfree(passwd); 55898684Sdes 55998684Sdes buffer_put_int(&e->output, 1); 56098684Sdes buffer_put_char(&e->output, 56198684Sdes success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 56298684Sdes} 56398684Sdes 56498684Sdesstatic void 56598684Sdesno_identities(SocketEntry *e, u_int type) 56698684Sdes{ 56798684Sdes Buffer msg; 56898684Sdes 56998684Sdes buffer_init(&msg); 57098684Sdes buffer_put_char(&msg, 57198684Sdes (type == SSH_AGENTC_REQUEST_RSA_IDENTITIES) ? 57298684Sdes SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); 57398684Sdes buffer_put_int(&msg, 0); 57498684Sdes buffer_put_int(&e->output, buffer_len(&msg)); 57598684Sdes buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); 57698684Sdes buffer_free(&msg); 57798684Sdes} 57898684Sdes 57992559Sdes#ifdef SMARTCARD 58092559Sdesstatic void 58192559Sdesprocess_add_smartcard_key (SocketEntry *e) 58292559Sdes{ 58399063Sdes char *sc_reader_id = NULL, *pin; 58499063Sdes int i, version, success = 0; 58599063Sdes Key **keys, *k; 58698684Sdes Identity *id; 58792559Sdes Idtab *tab; 58892559Sdes 58998684Sdes sc_reader_id = buffer_get_string(&e->request, NULL); 59098684Sdes pin = buffer_get_string(&e->request, NULL); 59198684Sdes keys = sc_get_keys(sc_reader_id, pin); 59292559Sdes xfree(sc_reader_id); 59398684Sdes xfree(pin); 59492559Sdes 59598684Sdes if (keys == NULL || keys[0] == NULL) { 59698684Sdes error("sc_get_keys failed"); 59792559Sdes goto send; 59892559Sdes } 59998684Sdes for (i = 0; keys[i] != NULL; i++) { 60098684Sdes k = keys[i]; 60198684Sdes version = k->type == KEY_RSA1 ? 1 : 2; 60298684Sdes tab = idtab_lookup(version); 60398684Sdes if (lookup_identity(k, version) == NULL) { 60498684Sdes id = xmalloc(sizeof(Identity)); 60598684Sdes id->key = k; 60698684Sdes id->comment = xstrdup("smartcard key"); 60798684Sdes id->death = 0; 608113911Sdes id->confirm = 0; 60998684Sdes TAILQ_INSERT_TAIL(&tab->idlist, id, next); 61098684Sdes tab->nentries++; 61198684Sdes success = 1; 61298684Sdes } else { 61398684Sdes key_free(k); 61498684Sdes } 61598684Sdes keys[i] = NULL; 61692559Sdes } 61798684Sdes xfree(keys); 61892559Sdessend: 61992559Sdes buffer_put_int(&e->output, 1); 62092559Sdes buffer_put_char(&e->output, 62192559Sdes success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 62292559Sdes} 62392559Sdes 62492559Sdesstatic void 62592559Sdesprocess_remove_smartcard_key(SocketEntry *e) 62692559Sdes{ 62799063Sdes char *sc_reader_id = NULL, *pin; 62899063Sdes int i, version, success = 0; 62999063Sdes Key **keys, *k = NULL; 63098684Sdes Identity *id; 63198684Sdes Idtab *tab; 63292559Sdes 63398684Sdes sc_reader_id = buffer_get_string(&e->request, NULL); 63498684Sdes pin = buffer_get_string(&e->request, NULL); 63598684Sdes keys = sc_get_keys(sc_reader_id, pin); 63692559Sdes xfree(sc_reader_id); 63798684Sdes xfree(pin); 63892559Sdes 63998684Sdes if (keys == NULL || keys[0] == NULL) { 64098684Sdes error("sc_get_keys failed"); 64198684Sdes goto send; 64298684Sdes } 64398684Sdes for (i = 0; keys[i] != NULL; i++) { 64498684Sdes k = keys[i]; 64598684Sdes version = k->type == KEY_RSA1 ? 1 : 2; 64698684Sdes if ((id = lookup_identity(k, version)) != NULL) { 64798684Sdes tab = idtab_lookup(version); 64892559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 64992559Sdes tab->nentries--; 65092559Sdes free_identity(id); 65192559Sdes success = 1; 65292559Sdes } 65392559Sdes key_free(k); 65498684Sdes keys[i] = NULL; 65592559Sdes } 65698684Sdes xfree(keys); 65798684Sdessend: 65892559Sdes buffer_put_int(&e->output, 1); 65992559Sdes buffer_put_char(&e->output, 66092559Sdes success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); 66192559Sdes} 66292559Sdes#endif /* SMARTCARD */ 66392559Sdes 66465674Skris/* dispatch incoming messages */ 66565674Skris 66692559Sdesstatic void 66757429Smarkmprocess_message(SocketEntry *e) 66857429Smarkm{ 66999063Sdes u_int msg_len, type; 67076262Sgreen u_char *cp; 67198684Sdes 67298684Sdes /* kill dead keys */ 67398684Sdes reaper(); 67498684Sdes 67557429Smarkm if (buffer_len(&e->input) < 5) 67657429Smarkm return; /* Incomplete message. */ 67792559Sdes cp = buffer_ptr(&e->input); 67857429Smarkm msg_len = GET_32BIT(cp); 67957429Smarkm if (msg_len > 256 * 1024) { 680106130Sdes close_socket(e); 68157429Smarkm return; 68257429Smarkm } 68357429Smarkm if (buffer_len(&e->input) < msg_len + 4) 68457429Smarkm return; 68598684Sdes 68698684Sdes /* move the current input to e->request */ 68757429Smarkm buffer_consume(&e->input, 4); 68898684Sdes buffer_clear(&e->request); 68998684Sdes buffer_append(&e->request, buffer_ptr(&e->input), msg_len); 69098684Sdes buffer_consume(&e->input, msg_len); 69198684Sdes type = buffer_get_char(&e->request); 69257429Smarkm 69398684Sdes /* check wheter agent is locked */ 69498684Sdes if (locked && type != SSH_AGENTC_UNLOCK) { 69598684Sdes buffer_clear(&e->request); 69698684Sdes switch (type) { 69798684Sdes case SSH_AGENTC_REQUEST_RSA_IDENTITIES: 69898684Sdes case SSH2_AGENTC_REQUEST_IDENTITIES: 69998684Sdes /* send empty lists */ 70098684Sdes no_identities(e, type); 70198684Sdes break; 70298684Sdes default: 70398684Sdes /* send a fail message for all other request types */ 70498684Sdes buffer_put_int(&e->output, 1); 70598684Sdes buffer_put_char(&e->output, SSH_AGENT_FAILURE); 70698684Sdes } 70798684Sdes return; 70898684Sdes } 70998684Sdes 71092559Sdes debug("type %d", type); 71157429Smarkm switch (type) { 71298684Sdes case SSH_AGENTC_LOCK: 71398684Sdes case SSH_AGENTC_UNLOCK: 71498684Sdes process_lock_agent(e, type == SSH_AGENTC_LOCK); 71598684Sdes break; 71665674Skris /* ssh1 */ 71765674Skris case SSH_AGENTC_RSA_CHALLENGE: 71865674Skris process_authentication_challenge1(e); 71965674Skris break; 72057429Smarkm case SSH_AGENTC_REQUEST_RSA_IDENTITIES: 72165674Skris process_request_identities(e, 1); 72257429Smarkm break; 72357429Smarkm case SSH_AGENTC_ADD_RSA_IDENTITY: 72498684Sdes case SSH_AGENTC_ADD_RSA_ID_CONSTRAINED: 72565674Skris process_add_identity(e, 1); 72657429Smarkm break; 72757429Smarkm case SSH_AGENTC_REMOVE_RSA_IDENTITY: 72865674Skris process_remove_identity(e, 1); 72957429Smarkm break; 73057429Smarkm case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: 73165674Skris process_remove_all_identities(e, 1); 73257429Smarkm break; 73365674Skris /* ssh2 */ 73465674Skris case SSH2_AGENTC_SIGN_REQUEST: 73565674Skris process_sign_request2(e); 73665674Skris break; 73765674Skris case SSH2_AGENTC_REQUEST_IDENTITIES: 73865674Skris process_request_identities(e, 2); 73965674Skris break; 74065674Skris case SSH2_AGENTC_ADD_IDENTITY: 74198684Sdes case SSH2_AGENTC_ADD_ID_CONSTRAINED: 74265674Skris process_add_identity(e, 2); 74365674Skris break; 74465674Skris case SSH2_AGENTC_REMOVE_IDENTITY: 74565674Skris process_remove_identity(e, 2); 74665674Skris break; 74765674Skris case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: 74865674Skris process_remove_all_identities(e, 2); 74965674Skris break; 75092559Sdes#ifdef SMARTCARD 75192559Sdes case SSH_AGENTC_ADD_SMARTCARD_KEY: 75292559Sdes process_add_smartcard_key(e); 75392559Sdes break; 75492559Sdes case SSH_AGENTC_REMOVE_SMARTCARD_KEY: 75592559Sdes process_remove_smartcard_key(e); 75692559Sdes break; 75792559Sdes#endif /* SMARTCARD */ 75857429Smarkm default: 75957429Smarkm /* Unknown message. Respond with failure. */ 76057429Smarkm error("Unknown message %d", type); 76198684Sdes buffer_clear(&e->request); 76257429Smarkm buffer_put_int(&e->output, 1); 76357429Smarkm buffer_put_char(&e->output, SSH_AGENT_FAILURE); 76457429Smarkm break; 76557429Smarkm } 76657429Smarkm} 76757429Smarkm 76892559Sdesstatic void 76992559Sdesnew_socket(sock_type type, int fd) 77057429Smarkm{ 77176262Sgreen u_int i, old_alloc; 77299063Sdes 77357429Smarkm if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 77457429Smarkm error("fcntl O_NONBLOCK: %s", strerror(errno)); 77557429Smarkm 77657429Smarkm if (fd > max_fd) 77757429Smarkm max_fd = fd; 77857429Smarkm 77957429Smarkm for (i = 0; i < sockets_alloc; i++) 78057429Smarkm if (sockets[i].type == AUTH_UNUSED) { 78157429Smarkm sockets[i].fd = fd; 78257429Smarkm sockets[i].type = type; 78357429Smarkm buffer_init(&sockets[i].input); 78457429Smarkm buffer_init(&sockets[i].output); 78598684Sdes buffer_init(&sockets[i].request); 78657429Smarkm return; 78757429Smarkm } 78857429Smarkm old_alloc = sockets_alloc; 78957429Smarkm sockets_alloc += 10; 79057429Smarkm if (sockets) 79157429Smarkm sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0])); 79257429Smarkm else 79357429Smarkm sockets = xmalloc(sockets_alloc * sizeof(sockets[0])); 79457429Smarkm for (i = old_alloc; i < sockets_alloc; i++) 79557429Smarkm sockets[i].type = AUTH_UNUSED; 79657429Smarkm sockets[old_alloc].type = type; 79757429Smarkm sockets[old_alloc].fd = fd; 79857429Smarkm buffer_init(&sockets[old_alloc].input); 79957429Smarkm buffer_init(&sockets[old_alloc].output); 80098684Sdes buffer_init(&sockets[old_alloc].request); 80157429Smarkm} 80257429Smarkm 80392559Sdesstatic int 80492559Sdesprepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, int *nallocp) 80557429Smarkm{ 80676262Sgreen u_int i, sz; 80776262Sgreen int n = 0; 80876262Sgreen 80976262Sgreen for (i = 0; i < sockets_alloc; i++) { 81057429Smarkm switch (sockets[i].type) { 81157429Smarkm case AUTH_SOCKET: 81257429Smarkm case AUTH_CONNECTION: 81376262Sgreen n = MAX(n, sockets[i].fd); 81457429Smarkm break; 81557429Smarkm case AUTH_UNUSED: 81657429Smarkm break; 81757429Smarkm default: 81857429Smarkm fatal("Unknown socket type %d", sockets[i].type); 81957429Smarkm break; 82057429Smarkm } 82176262Sgreen } 82276262Sgreen 82376262Sgreen sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); 82492559Sdes if (*fdrp == NULL || sz > *nallocp) { 82576262Sgreen if (*fdrp) 82676262Sgreen xfree(*fdrp); 82776262Sgreen if (*fdwp) 82876262Sgreen xfree(*fdwp); 82976262Sgreen *fdrp = xmalloc(sz); 83076262Sgreen *fdwp = xmalloc(sz); 83192559Sdes *nallocp = sz; 83276262Sgreen } 83392559Sdes if (n < *fdl) 83492559Sdes debug("XXX shrink: %d < %d", n, *fdl); 83592559Sdes *fdl = n; 83676262Sgreen memset(*fdrp, 0, sz); 83776262Sgreen memset(*fdwp, 0, sz); 83876262Sgreen 83976262Sgreen for (i = 0; i < sockets_alloc; i++) { 84076262Sgreen switch (sockets[i].type) { 84176262Sgreen case AUTH_SOCKET: 84276262Sgreen case AUTH_CONNECTION: 84376262Sgreen FD_SET(sockets[i].fd, *fdrp); 84476262Sgreen if (buffer_len(&sockets[i].output) > 0) 84576262Sgreen FD_SET(sockets[i].fd, *fdwp); 84676262Sgreen break; 84776262Sgreen default: 84876262Sgreen break; 84976262Sgreen } 85076262Sgreen } 85176262Sgreen return (1); 85257429Smarkm} 85357429Smarkm 85492559Sdesstatic void 85557429Smarkmafter_select(fd_set *readset, fd_set *writeset) 85657429Smarkm{ 85799063Sdes struct sockaddr_un sunaddr; 85858585Skris socklen_t slen; 85957429Smarkm char buf[1024]; 86099063Sdes int len, sock; 86199063Sdes u_int i; 862106130Sdes uid_t euid; 863106130Sdes gid_t egid; 86457429Smarkm 86557429Smarkm for (i = 0; i < sockets_alloc; i++) 86657429Smarkm switch (sockets[i].type) { 86757429Smarkm case AUTH_UNUSED: 86857429Smarkm break; 86957429Smarkm case AUTH_SOCKET: 87057429Smarkm if (FD_ISSET(sockets[i].fd, readset)) { 87158585Skris slen = sizeof(sunaddr); 87276262Sgreen sock = accept(sockets[i].fd, 87376262Sgreen (struct sockaddr *) &sunaddr, &slen); 87457429Smarkm if (sock < 0) { 87592559Sdes error("accept from AUTH_SOCKET: %s", 87692559Sdes strerror(errno)); 87757429Smarkm break; 87857429Smarkm } 879106130Sdes if (getpeereid(sock, &euid, &egid) < 0) { 880106130Sdes error("getpeereid %d failed: %s", 881106130Sdes sock, strerror(errno)); 882106130Sdes close(sock); 883106130Sdes break; 884106130Sdes } 885106130Sdes if ((euid != 0) && (getuid() != euid)) { 886106130Sdes error("uid mismatch: " 887106130Sdes "peer euid %u != uid %u", 888106130Sdes (u_int) euid, (u_int) getuid()); 889106130Sdes close(sock); 890106130Sdes break; 891106130Sdes } 89257429Smarkm new_socket(AUTH_CONNECTION, sock); 89357429Smarkm } 89457429Smarkm break; 89557429Smarkm case AUTH_CONNECTION: 89657429Smarkm if (buffer_len(&sockets[i].output) > 0 && 89757429Smarkm FD_ISSET(sockets[i].fd, writeset)) { 89876262Sgreen do { 89976262Sgreen len = write(sockets[i].fd, 90076262Sgreen buffer_ptr(&sockets[i].output), 90176262Sgreen buffer_len(&sockets[i].output)); 90276262Sgreen if (len == -1 && (errno == EAGAIN || 90376262Sgreen errno == EINTR)) 90476262Sgreen continue; 90576262Sgreen break; 90676262Sgreen } while (1); 90757429Smarkm if (len <= 0) { 908106130Sdes close_socket(&sockets[i]); 90957429Smarkm break; 91057429Smarkm } 91157429Smarkm buffer_consume(&sockets[i].output, len); 91257429Smarkm } 91357429Smarkm if (FD_ISSET(sockets[i].fd, readset)) { 91476262Sgreen do { 91576262Sgreen len = read(sockets[i].fd, buf, sizeof(buf)); 91676262Sgreen if (len == -1 && (errno == EAGAIN || 91776262Sgreen errno == EINTR)) 91876262Sgreen continue; 91976262Sgreen break; 92076262Sgreen } while (1); 92157429Smarkm if (len <= 0) { 922106130Sdes close_socket(&sockets[i]); 92357429Smarkm break; 92457429Smarkm } 92557429Smarkm buffer_append(&sockets[i].input, buf, len); 92657429Smarkm process_message(&sockets[i]); 92757429Smarkm } 92857429Smarkm break; 92957429Smarkm default: 93057429Smarkm fatal("Unknown type %d", sockets[i].type); 93157429Smarkm } 93257429Smarkm} 93357429Smarkm 93492559Sdesstatic void 93592559Sdescleanup_socket(void *p) 93657429Smarkm{ 93776262Sgreen if (socket_name[0]) 93876262Sgreen unlink(socket_name); 93976262Sgreen if (socket_dir[0]) 94076262Sgreen rmdir(socket_dir); 94157429Smarkm} 94257429Smarkm 94392559Sdesstatic void 94457429Smarkmcleanup_exit(int i) 94557429Smarkm{ 94692559Sdes cleanup_socket(NULL); 94757429Smarkm exit(i); 94857429Smarkm} 94957429Smarkm 95092559Sdesstatic void 95176262Sgreencleanup_handler(int sig) 95257429Smarkm{ 95392559Sdes cleanup_socket(NULL); 95476262Sgreen _exit(2); 95576262Sgreen} 95676262Sgreen 95792559Sdesstatic void 95892559Sdescheck_parent_exists(int sig) 95992559Sdes{ 96092559Sdes int save_errno = errno; 96192559Sdes 96292559Sdes if (parent_pid != -1 && kill(parent_pid, 0) < 0) { 96392559Sdes /* printf("Parent has died - Authentication agent exiting.\n"); */ 96492559Sdes cleanup_handler(sig); /* safe */ 96592559Sdes } 96692559Sdes signal(SIGALRM, check_parent_exists); 96792559Sdes alarm(10); 96892559Sdes errno = save_errno; 96992559Sdes} 97092559Sdes 97192559Sdesstatic void 97276262Sgreenusage(void) 97376262Sgreen{ 97492559Sdes fprintf(stderr, "Usage: %s [options] [command [args ...]]\n", 97576262Sgreen __progname); 97692559Sdes fprintf(stderr, "Options:\n"); 97792559Sdes fprintf(stderr, " -c Generate C-shell commands on stdout.\n"); 97892559Sdes fprintf(stderr, " -s Generate Bourne shell commands on stdout.\n"); 97992559Sdes fprintf(stderr, " -k Kill the current agent.\n"); 98092559Sdes fprintf(stderr, " -d Debug mode.\n"); 98198684Sdes fprintf(stderr, " -a socket Bind agent socket to given name.\n"); 982113911Sdes fprintf(stderr, " -t life Default identity lifetime (seconds).\n"); 98357429Smarkm exit(1); 98457429Smarkm} 98557429Smarkm 98657429Smarkmint 98757429Smarkmmain(int ac, char **av) 98857429Smarkm{ 989113911Sdes int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0; 990113911Sdes int sock, fd, ch, nalloc; 99199063Sdes char *shell, *format, *pidstr, *agentsocket = NULL; 99299063Sdes fd_set *readsetp = NULL, *writesetp = NULL; 99357429Smarkm struct sockaddr_un sunaddr; 99498941Sdes#ifdef HAVE_SETRLIMIT 99576262Sgreen struct rlimit rlim; 99698941Sdes#endif 99798941Sdes#ifdef HAVE_CYGWIN 99898941Sdes int prev_mask; 99998941Sdes#endif 100099063Sdes extern int optind; 100199063Sdes extern char *optarg; 100257429Smarkm pid_t pid; 100399063Sdes char pidstrbuf[1 + 3 * sizeof pid]; 100457429Smarkm 1005106130Sdes /* drop */ 1006106130Sdes setegid(getgid()); 1007106130Sdes setgid(getgid()); 1008110506Sdes setuid(geteuid()); 1009106130Sdes 101076262Sgreen SSLeay_add_all_algorithms(); 101176262Sgreen 101298941Sdes __progname = get_progname(av[0]); 101398941Sdes init_rng(); 101498941Sdes seed_rng(); 101598941Sdes 1016113911Sdes while ((ch = getopt(ac, av, "cdksa:t:")) != -1) { 101757429Smarkm switch (ch) { 101857429Smarkm case 'c': 101957429Smarkm if (s_flag) 102057429Smarkm usage(); 102157429Smarkm c_flag++; 102257429Smarkm break; 102357429Smarkm case 'k': 102457429Smarkm k_flag++; 102557429Smarkm break; 102657429Smarkm case 's': 102757429Smarkm if (c_flag) 102857429Smarkm usage(); 102957429Smarkm s_flag++; 103057429Smarkm break; 103192559Sdes case 'd': 103292559Sdes if (d_flag) 103392559Sdes usage(); 103492559Sdes d_flag++; 103592559Sdes break; 103698684Sdes case 'a': 103798684Sdes agentsocket = optarg; 103898684Sdes break; 1039113911Sdes case 't': 1040113911Sdes if ((lifetime = convtime(optarg)) == -1) { 1041113911Sdes fprintf(stderr, "Invalid lifetime\n"); 1042113911Sdes usage(); 1043113911Sdes } 1044113911Sdes break; 104557429Smarkm default: 104657429Smarkm usage(); 104757429Smarkm } 104857429Smarkm } 104957429Smarkm ac -= optind; 105057429Smarkm av += optind; 105157429Smarkm 105292559Sdes if (ac > 0 && (c_flag || k_flag || s_flag || d_flag)) 105357429Smarkm usage(); 105457429Smarkm 105598684Sdes if (ac == 0 && !c_flag && !s_flag) { 105657429Smarkm shell = getenv("SHELL"); 105757429Smarkm if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0) 105857429Smarkm c_flag = 1; 105957429Smarkm } 106057429Smarkm if (k_flag) { 106157429Smarkm pidstr = getenv(SSH_AGENTPID_ENV_NAME); 106257429Smarkm if (pidstr == NULL) { 106357429Smarkm fprintf(stderr, "%s not set, cannot kill agent\n", 106476262Sgreen SSH_AGENTPID_ENV_NAME); 106557429Smarkm exit(1); 106657429Smarkm } 106757429Smarkm pid = atoi(pidstr); 106876262Sgreen if (pid < 1) { 106957429Smarkm fprintf(stderr, "%s=\"%s\", which is not a good PID\n", 107076262Sgreen SSH_AGENTPID_ENV_NAME, pidstr); 107157429Smarkm exit(1); 107257429Smarkm } 107357429Smarkm if (kill(pid, SIGTERM) == -1) { 107457429Smarkm perror("kill"); 107557429Smarkm exit(1); 107657429Smarkm } 107757429Smarkm format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; 107857429Smarkm printf(format, SSH_AUTHSOCKET_ENV_NAME); 107957429Smarkm printf(format, SSH_AGENTPID_ENV_NAME); 108098684Sdes printf("echo Agent pid %ld killed;\n", (long)pid); 108157429Smarkm exit(0); 108257429Smarkm } 108357429Smarkm parent_pid = getpid(); 108457429Smarkm 108598684Sdes if (agentsocket == NULL) { 108698684Sdes /* Create private directory for agent socket */ 108798684Sdes strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir); 108898684Sdes if (mkdtemp(socket_dir) == NULL) { 108998684Sdes perror("mkdtemp: private socket dir"); 109098684Sdes exit(1); 109198684Sdes } 109298684Sdes snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, 109398684Sdes (long)parent_pid); 109498684Sdes } else { 109598684Sdes /* Try to use specified agent socket */ 109698684Sdes socket_dir[0] = '\0'; 109798684Sdes strlcpy(socket_name, agentsocket, sizeof socket_name); 109857429Smarkm } 109957429Smarkm 110057429Smarkm /* 110157429Smarkm * Create socket early so it will exist before command gets run from 110257429Smarkm * the parent. 110357429Smarkm */ 110457429Smarkm sock = socket(AF_UNIX, SOCK_STREAM, 0); 110557429Smarkm if (sock < 0) { 110657429Smarkm perror("socket"); 110757429Smarkm cleanup_exit(1); 110857429Smarkm } 110957429Smarkm memset(&sunaddr, 0, sizeof(sunaddr)); 111057429Smarkm sunaddr.sun_family = AF_UNIX; 111157429Smarkm strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); 111298941Sdes#ifdef HAVE_CYGWIN 111398941Sdes prev_mask = umask(0177); 111498941Sdes#endif 111598941Sdes if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) { 111657429Smarkm perror("bind"); 111798941Sdes#ifdef HAVE_CYGWIN 111898941Sdes umask(prev_mask); 111998941Sdes#endif 112057429Smarkm cleanup_exit(1); 112157429Smarkm } 112298941Sdes#ifdef HAVE_CYGWIN 112398941Sdes umask(prev_mask); 112498941Sdes#endif 1125106130Sdes if (listen(sock, 128) < 0) { 112657429Smarkm perror("listen"); 112757429Smarkm cleanup_exit(1); 112857429Smarkm } 112976262Sgreen 113057429Smarkm /* 113157429Smarkm * Fork, and have the parent execute the command, if any, or present 113257429Smarkm * the socket data. The child continues as the authentication agent. 113357429Smarkm */ 113492559Sdes if (d_flag) { 113592559Sdes log_init(__progname, SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 1); 113692559Sdes format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; 113792559Sdes printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, 113892559Sdes SSH_AUTHSOCKET_ENV_NAME); 113998684Sdes printf("echo Agent pid %ld;\n", (long)parent_pid); 114092559Sdes goto skip; 114192559Sdes } 114257429Smarkm pid = fork(); 114357429Smarkm if (pid == -1) { 114457429Smarkm perror("fork"); 114592559Sdes cleanup_exit(1); 114657429Smarkm } 114757429Smarkm if (pid != 0) { /* Parent - execute the given command. */ 114857429Smarkm close(sock); 114998684Sdes snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); 115057429Smarkm if (ac == 0) { 115157429Smarkm format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; 115257429Smarkm printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, 115376262Sgreen SSH_AUTHSOCKET_ENV_NAME); 115457429Smarkm printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, 115576262Sgreen SSH_AGENTPID_ENV_NAME); 115698684Sdes printf("echo Agent pid %ld;\n", (long)pid); 115757429Smarkm exit(0); 115857429Smarkm } 115969591Sgreen if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || 116069591Sgreen setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { 116169591Sgreen perror("setenv"); 116269591Sgreen exit(1); 116369591Sgreen } 116457429Smarkm execvp(av[0], av); 116557429Smarkm perror(av[0]); 116657429Smarkm exit(1); 116757429Smarkm } 116892559Sdes /* child */ 116992559Sdes log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); 117092559Sdes 117192559Sdes if (setsid() == -1) { 117292559Sdes error("setsid: %s", strerror(errno)); 117392559Sdes cleanup_exit(1); 117492559Sdes } 117592559Sdes 117692559Sdes (void)chdir("/"); 1177113911Sdes if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { 1178113911Sdes /* XXX might close listen socket */ 1179113911Sdes (void)dup2(fd, STDIN_FILENO); 1180113911Sdes (void)dup2(fd, STDOUT_FILENO); 1181113911Sdes (void)dup2(fd, STDERR_FILENO); 1182113911Sdes if (fd > 2) 1183113911Sdes close(fd); 1184113911Sdes } 118557429Smarkm 118698941Sdes#ifdef HAVE_SETRLIMIT 118776262Sgreen /* deny core dumps, since memory contains unencrypted private keys */ 118876262Sgreen rlim.rlim_cur = rlim.rlim_max = 0; 118976262Sgreen if (setrlimit(RLIMIT_CORE, &rlim) < 0) { 119092559Sdes error("setrlimit RLIMIT_CORE: %s", strerror(errno)); 119176262Sgreen cleanup_exit(1); 119276262Sgreen } 119398941Sdes#endif 119492559Sdes 119592559Sdesskip: 119692559Sdes fatal_add_cleanup(cleanup_socket, NULL); 119757429Smarkm new_socket(AUTH_SOCKET, sock); 119857429Smarkm if (ac > 0) { 119957429Smarkm signal(SIGALRM, check_parent_exists); 120057429Smarkm alarm(10); 120157429Smarkm } 120265674Skris idtab_init(); 120392559Sdes if (!d_flag) 120492559Sdes signal(SIGINT, SIG_IGN); 120557429Smarkm signal(SIGPIPE, SIG_IGN); 120676262Sgreen signal(SIGHUP, cleanup_handler); 120776262Sgreen signal(SIGTERM, cleanup_handler); 120892559Sdes nalloc = 0; 120992559Sdes 121057429Smarkm while (1) { 121192559Sdes prepare_select(&readsetp, &writesetp, &max_fd, &nalloc); 121276262Sgreen if (select(max_fd + 1, readsetp, writesetp, NULL, NULL) < 0) { 121357429Smarkm if (errno == EINTR) 121457429Smarkm continue; 121592559Sdes fatal("select: %s", strerror(errno)); 121657429Smarkm } 121776262Sgreen after_select(readsetp, writesetp); 121857429Smarkm } 121957429Smarkm /* NOTREACHED */ 122057429Smarkm} 1221