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