xref: /dflybsd-src/crypto/openssh/ssh-add.c (revision 36e94dc5bac047676e52d6d94a07db1b31c653e3)
1*36e94dc5SPeter Avalos /* $OpenBSD: ssh-add.c,v 1.113 2014/07/09 14:15:56 benno Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos  * Author: Tatu Ylonen <ylo@cs.hut.fi>
418de8d7fSPeter Avalos  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
518de8d7fSPeter Avalos  *                    All rights reserved
618de8d7fSPeter Avalos  * Adds an identity to the authentication server, or removes an identity.
718de8d7fSPeter Avalos  *
818de8d7fSPeter Avalos  * As far as I am concerned, the code I have written for this software
918de8d7fSPeter Avalos  * can be used freely for any purpose.  Any derived versions of this
1018de8d7fSPeter Avalos  * software must be clearly marked as such, and if the derived work is
1118de8d7fSPeter Avalos  * incompatible with the protocol description in the RFC file, it must be
1218de8d7fSPeter Avalos  * called by a name other than "ssh" or "Secure Shell".
1318de8d7fSPeter Avalos  *
1418de8d7fSPeter Avalos  * SSH2 implementation,
1518de8d7fSPeter Avalos  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
1618de8d7fSPeter Avalos  *
1718de8d7fSPeter Avalos  * Redistribution and use in source and binary forms, with or without
1818de8d7fSPeter Avalos  * modification, are permitted provided that the following conditions
1918de8d7fSPeter Avalos  * are met:
2018de8d7fSPeter Avalos  * 1. Redistributions of source code must retain the above copyright
2118de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer.
2218de8d7fSPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
2318de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
2418de8d7fSPeter Avalos  *    documentation and/or other materials provided with the distribution.
2518de8d7fSPeter Avalos  *
2618de8d7fSPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2718de8d7fSPeter Avalos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2818de8d7fSPeter Avalos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2918de8d7fSPeter Avalos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
3018de8d7fSPeter Avalos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3118de8d7fSPeter Avalos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3218de8d7fSPeter Avalos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3318de8d7fSPeter Avalos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3418de8d7fSPeter Avalos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3518de8d7fSPeter Avalos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3618de8d7fSPeter Avalos  */
3718de8d7fSPeter Avalos 
3818de8d7fSPeter Avalos #include "includes.h"
3918de8d7fSPeter Avalos 
4018de8d7fSPeter Avalos #include <sys/types.h>
4118de8d7fSPeter Avalos #include <sys/stat.h>
4218de8d7fSPeter Avalos #include <sys/param.h>
4318de8d7fSPeter Avalos 
4418de8d7fSPeter Avalos #include <openssl/evp.h>
4518de8d7fSPeter Avalos #include "openbsd-compat/openssl-compat.h"
4618de8d7fSPeter Avalos 
4718de8d7fSPeter Avalos #include <fcntl.h>
4818de8d7fSPeter Avalos #include <pwd.h>
4918de8d7fSPeter Avalos #include <stdarg.h>
5018de8d7fSPeter Avalos #include <stdio.h>
5118de8d7fSPeter Avalos #include <stdlib.h>
5218de8d7fSPeter Avalos #include <string.h>
5318de8d7fSPeter Avalos #include <unistd.h>
5418de8d7fSPeter Avalos 
5518de8d7fSPeter Avalos #include "xmalloc.h"
5618de8d7fSPeter Avalos #include "ssh.h"
5718de8d7fSPeter Avalos #include "rsa.h"
5818de8d7fSPeter Avalos #include "log.h"
5918de8d7fSPeter Avalos #include "key.h"
6018de8d7fSPeter Avalos #include "buffer.h"
6118de8d7fSPeter Avalos #include "authfd.h"
6218de8d7fSPeter Avalos #include "authfile.h"
6318de8d7fSPeter Avalos #include "pathnames.h"
6418de8d7fSPeter Avalos #include "misc.h"
65*36e94dc5SPeter Avalos #include "ssherr.h"
6618de8d7fSPeter Avalos 
6718de8d7fSPeter Avalos /* argv0 */
6818de8d7fSPeter Avalos extern char *__progname;
6918de8d7fSPeter Avalos 
7018de8d7fSPeter Avalos /* Default files to add */
7118de8d7fSPeter Avalos static char *default_files[] = {
7218de8d7fSPeter Avalos 	_PATH_SSH_CLIENT_ID_RSA,
7318de8d7fSPeter Avalos 	_PATH_SSH_CLIENT_ID_DSA,
749f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
759f304aafSPeter Avalos 	_PATH_SSH_CLIENT_ID_ECDSA,
769f304aafSPeter Avalos #endif
77*36e94dc5SPeter Avalos 	_PATH_SSH_CLIENT_ID_ED25519,
7818de8d7fSPeter Avalos 	_PATH_SSH_CLIENT_IDENTITY,
7918de8d7fSPeter Avalos 	NULL
8018de8d7fSPeter Avalos };
8118de8d7fSPeter Avalos 
8218de8d7fSPeter Avalos /* Default lifetime (0 == forever) */
8318de8d7fSPeter Avalos static int lifetime = 0;
8418de8d7fSPeter Avalos 
8518de8d7fSPeter Avalos /* User has to confirm key use */
8618de8d7fSPeter Avalos static int confirm = 0;
8718de8d7fSPeter Avalos 
8818de8d7fSPeter Avalos /* we keep a cache of one passphrases */
8918de8d7fSPeter Avalos static char *pass = NULL;
9018de8d7fSPeter Avalos static void
9118de8d7fSPeter Avalos clear_pass(void)
9218de8d7fSPeter Avalos {
9318de8d7fSPeter Avalos 	if (pass) {
94*36e94dc5SPeter Avalos 		explicit_bzero(pass, strlen(pass));
95*36e94dc5SPeter Avalos 		free(pass);
9618de8d7fSPeter Avalos 		pass = NULL;
9718de8d7fSPeter Avalos 	}
9818de8d7fSPeter Avalos }
9918de8d7fSPeter Avalos 
10018de8d7fSPeter Avalos static int
101*36e94dc5SPeter Avalos delete_file(AuthenticationConnection *ac, const char *filename, int key_only)
10218de8d7fSPeter Avalos {
103*36e94dc5SPeter Avalos 	Key *public = NULL, *cert = NULL;
104*36e94dc5SPeter Avalos 	char *certpath = NULL, *comment = NULL;
10518de8d7fSPeter Avalos 	int ret = -1;
10618de8d7fSPeter Avalos 
10718de8d7fSPeter Avalos 	public = key_load_public(filename, &comment);
10818de8d7fSPeter Avalos 	if (public == NULL) {
10918de8d7fSPeter Avalos 		printf("Bad key file %s\n", filename);
11018de8d7fSPeter Avalos 		return -1;
11118de8d7fSPeter Avalos 	}
11218de8d7fSPeter Avalos 	if (ssh_remove_identity(ac, public)) {
11318de8d7fSPeter Avalos 		fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment);
11418de8d7fSPeter Avalos 		ret = 0;
11518de8d7fSPeter Avalos 	} else
11618de8d7fSPeter Avalos 		fprintf(stderr, "Could not remove identity: %s\n", filename);
11718de8d7fSPeter Avalos 
118*36e94dc5SPeter Avalos 	if (key_only)
119*36e94dc5SPeter Avalos 		goto out;
120*36e94dc5SPeter Avalos 
121*36e94dc5SPeter Avalos 	/* Now try to delete the corresponding certificate too */
122*36e94dc5SPeter Avalos 	free(comment);
123*36e94dc5SPeter Avalos 	comment = NULL;
124*36e94dc5SPeter Avalos 	xasprintf(&certpath, "%s-cert.pub", filename);
125*36e94dc5SPeter Avalos 	if ((cert = key_load_public(certpath, &comment)) == NULL)
126*36e94dc5SPeter Avalos 		goto out;
127*36e94dc5SPeter Avalos 	if (!key_equal_public(cert, public))
128*36e94dc5SPeter Avalos 		fatal("Certificate %s does not match private key %s",
129*36e94dc5SPeter Avalos 		    certpath, filename);
130*36e94dc5SPeter Avalos 
131*36e94dc5SPeter Avalos 	if (ssh_remove_identity(ac, cert)) {
132*36e94dc5SPeter Avalos 		fprintf(stderr, "Identity removed: %s (%s)\n", certpath,
133*36e94dc5SPeter Avalos 		    comment);
134*36e94dc5SPeter Avalos 		ret = 0;
135*36e94dc5SPeter Avalos 	} else
136*36e94dc5SPeter Avalos 		fprintf(stderr, "Could not remove identity: %s\n", certpath);
137*36e94dc5SPeter Avalos 
138*36e94dc5SPeter Avalos  out:
139*36e94dc5SPeter Avalos 	if (cert != NULL)
140*36e94dc5SPeter Avalos 		key_free(cert);
141*36e94dc5SPeter Avalos 	if (public != NULL)
14218de8d7fSPeter Avalos 		key_free(public);
143*36e94dc5SPeter Avalos 	free(certpath);
144*36e94dc5SPeter Avalos 	free(comment);
14518de8d7fSPeter Avalos 
14618de8d7fSPeter Avalos 	return ret;
14718de8d7fSPeter Avalos }
14818de8d7fSPeter Avalos 
14918de8d7fSPeter Avalos /* Send a request to remove all identities. */
15018de8d7fSPeter Avalos static int
15118de8d7fSPeter Avalos delete_all(AuthenticationConnection *ac)
15218de8d7fSPeter Avalos {
15318de8d7fSPeter Avalos 	int ret = -1;
15418de8d7fSPeter Avalos 
15518de8d7fSPeter Avalos 	if (ssh_remove_all_identities(ac, 1))
15618de8d7fSPeter Avalos 		ret = 0;
15718de8d7fSPeter Avalos 	/* ignore error-code for ssh2 */
15818de8d7fSPeter Avalos 	ssh_remove_all_identities(ac, 2);
15918de8d7fSPeter Avalos 
16018de8d7fSPeter Avalos 	if (ret == 0)
16118de8d7fSPeter Avalos 		fprintf(stderr, "All identities removed.\n");
16218de8d7fSPeter Avalos 	else
16318de8d7fSPeter Avalos 		fprintf(stderr, "Failed to remove all identities.\n");
16418de8d7fSPeter Avalos 
16518de8d7fSPeter Avalos 	return ret;
16618de8d7fSPeter Avalos }
16718de8d7fSPeter Avalos 
16818de8d7fSPeter Avalos static int
16999e85e0dSPeter Avalos add_file(AuthenticationConnection *ac, const char *filename, int key_only)
17018de8d7fSPeter Avalos {
171856ea928SPeter Avalos 	Key *private, *cert;
17218de8d7fSPeter Avalos 	char *comment = NULL;
17399e85e0dSPeter Avalos 	char msg[1024], *certpath = NULL;
174*36e94dc5SPeter Avalos 	int r, fd, perms_ok, ret = -1;
1751c188a7fSPeter Avalos 	Buffer keyblob;
17618de8d7fSPeter Avalos 
1771c188a7fSPeter Avalos 	if (strcmp(filename, "-") == 0) {
1781c188a7fSPeter Avalos 		fd = STDIN_FILENO;
1791c188a7fSPeter Avalos 		filename = "(stdin)";
1801c188a7fSPeter Avalos 	} else if ((fd = open(filename, O_RDONLY)) < 0) {
18118de8d7fSPeter Avalos 		perror(filename);
18218de8d7fSPeter Avalos 		return -1;
18318de8d7fSPeter Avalos 	}
18418de8d7fSPeter Avalos 
18518de8d7fSPeter Avalos 	/*
18618de8d7fSPeter Avalos 	 * Since we'll try to load a keyfile multiple times, permission errors
18718de8d7fSPeter Avalos 	 * will occur multiple times, so check perms first and bail if wrong.
18818de8d7fSPeter Avalos 	 */
1891c188a7fSPeter Avalos 	if (fd != STDIN_FILENO) {
19018de8d7fSPeter Avalos 		perms_ok = key_perm_ok(fd, filename);
1911c188a7fSPeter Avalos 		if (!perms_ok) {
19218de8d7fSPeter Avalos 			close(fd);
19318de8d7fSPeter Avalos 			return -1;
1941c188a7fSPeter Avalos 		}
1951c188a7fSPeter Avalos 	}
1961c188a7fSPeter Avalos 	buffer_init(&keyblob);
1971c188a7fSPeter Avalos 	if (!key_load_file(fd, filename, &keyblob)) {
1981c188a7fSPeter Avalos 		buffer_free(&keyblob);
1991c188a7fSPeter Avalos 		close(fd);
2001c188a7fSPeter Avalos 		return -1;
2011c188a7fSPeter Avalos 	}
2021c188a7fSPeter Avalos 	close(fd);
20318de8d7fSPeter Avalos 
20418de8d7fSPeter Avalos 	/* At first, try empty passphrase */
205*36e94dc5SPeter Avalos 	if ((r = sshkey_parse_private_fileblob(&keyblob, "", filename,
206*36e94dc5SPeter Avalos 	    &private, &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE)
207*36e94dc5SPeter Avalos 		fatal("Cannot parse %s: %s", filename, ssh_err(r));
208*36e94dc5SPeter Avalos 	/* try last */
209*36e94dc5SPeter Avalos 	if (private == NULL && pass != NULL) {
210*36e94dc5SPeter Avalos 		if ((r = sshkey_parse_private_fileblob(&keyblob, pass, filename,
211*36e94dc5SPeter Avalos 		    &private, &comment)) != 0 &&
212*36e94dc5SPeter Avalos 		    r != SSH_ERR_KEY_WRONG_PASSPHRASE)
213*36e94dc5SPeter Avalos 			fatal("Cannot parse %s: %s", filename, ssh_err(r));
214*36e94dc5SPeter Avalos 	}
21518de8d7fSPeter Avalos 	if (comment == NULL)
21618de8d7fSPeter Avalos 		comment = xstrdup(filename);
21718de8d7fSPeter Avalos 	if (private == NULL) {
21818de8d7fSPeter Avalos 		/* clear passphrase since it did not work */
21918de8d7fSPeter Avalos 		clear_pass();
22018de8d7fSPeter Avalos 		snprintf(msg, sizeof msg, "Enter passphrase for %.200s: ",
22118de8d7fSPeter Avalos 		    comment);
22218de8d7fSPeter Avalos 		for (;;) {
22318de8d7fSPeter Avalos 			pass = read_passphrase(msg, RP_ALLOW_STDIN);
22418de8d7fSPeter Avalos 			if (strcmp(pass, "") == 0) {
22518de8d7fSPeter Avalos 				clear_pass();
226*36e94dc5SPeter Avalos 				free(comment);
2271c188a7fSPeter Avalos 				buffer_free(&keyblob);
22818de8d7fSPeter Avalos 				return -1;
22918de8d7fSPeter Avalos 			}
230*36e94dc5SPeter Avalos 			if ((r = sshkey_parse_private_fileblob(&keyblob,
231*36e94dc5SPeter Avalos 			     pass, filename, &private, NULL)) != 0 &&
232*36e94dc5SPeter Avalos 			    r != SSH_ERR_KEY_WRONG_PASSPHRASE)
233*36e94dc5SPeter Avalos 				fatal("Cannot parse %s: %s",
234*36e94dc5SPeter Avalos 					    filename, ssh_err(r));
23518de8d7fSPeter Avalos 			if (private != NULL)
23618de8d7fSPeter Avalos 				break;
23718de8d7fSPeter Avalos 			clear_pass();
23818de8d7fSPeter Avalos 			snprintf(msg, sizeof msg,
23918de8d7fSPeter Avalos 			    "Bad passphrase, try again for %.200s: ", comment);
24018de8d7fSPeter Avalos 		}
24118de8d7fSPeter Avalos 	}
2421c188a7fSPeter Avalos 	buffer_free(&keyblob);
24318de8d7fSPeter Avalos 
24418de8d7fSPeter Avalos 	if (ssh_add_identity_constrained(ac, private, comment, lifetime,
24518de8d7fSPeter Avalos 	    confirm)) {
24618de8d7fSPeter Avalos 		fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
24718de8d7fSPeter Avalos 		ret = 0;
24818de8d7fSPeter Avalos 		if (lifetime != 0)
24918de8d7fSPeter Avalos 			fprintf(stderr,
25018de8d7fSPeter Avalos 			    "Lifetime set to %d seconds\n", lifetime);
25118de8d7fSPeter Avalos 		if (confirm != 0)
25218de8d7fSPeter Avalos 			fprintf(stderr,
253856ea928SPeter Avalos 			    "The user must confirm each use of the key\n");
25418de8d7fSPeter Avalos 	} else {
25518de8d7fSPeter Avalos 		fprintf(stderr, "Could not add identity: %s\n", filename);
25618de8d7fSPeter Avalos 	}
25718de8d7fSPeter Avalos 
25899e85e0dSPeter Avalos 	/* Skip trying to load the cert if requested */
25999e85e0dSPeter Avalos 	if (key_only)
26099e85e0dSPeter Avalos 		goto out;
261856ea928SPeter Avalos 
262856ea928SPeter Avalos 	/* Now try to add the certificate flavour too */
263856ea928SPeter Avalos 	xasprintf(&certpath, "%s-cert.pub", filename);
264856ea928SPeter Avalos 	if ((cert = key_load_public(certpath, NULL)) == NULL)
265856ea928SPeter Avalos 		goto out;
266856ea928SPeter Avalos 
267856ea928SPeter Avalos 	if (!key_equal_public(cert, private)) {
268856ea928SPeter Avalos 		error("Certificate %s does not match private key %s",
269856ea928SPeter Avalos 		    certpath, filename);
270856ea928SPeter Avalos 		key_free(cert);
271856ea928SPeter Avalos 		goto out;
272856ea928SPeter Avalos 	}
273856ea928SPeter Avalos 
274856ea928SPeter Avalos 	/* Graft with private bits */
275856ea928SPeter Avalos 	if (key_to_certified(private, key_cert_is_legacy(cert)) != 0) {
276856ea928SPeter Avalos 		error("%s: key_to_certified failed", __func__);
277856ea928SPeter Avalos 		key_free(cert);
278856ea928SPeter Avalos 		goto out;
279856ea928SPeter Avalos 	}
280856ea928SPeter Avalos 	key_cert_copy(cert, private);
281856ea928SPeter Avalos 	key_free(cert);
282856ea928SPeter Avalos 
283856ea928SPeter Avalos 	if (!ssh_add_identity_constrained(ac, private, comment,
284856ea928SPeter Avalos 	    lifetime, confirm)) {
285856ea928SPeter Avalos 		error("Certificate %s (%s) add failed", certpath,
286856ea928SPeter Avalos 		    private->cert->key_id);
287856ea928SPeter Avalos 	}
288856ea928SPeter Avalos 	fprintf(stderr, "Certificate added: %s (%s)\n", certpath,
289856ea928SPeter Avalos 	    private->cert->key_id);
290856ea928SPeter Avalos 	if (lifetime != 0)
291856ea928SPeter Avalos 		fprintf(stderr, "Lifetime set to %d seconds\n", lifetime);
292856ea928SPeter Avalos 	if (confirm != 0)
293856ea928SPeter Avalos 		fprintf(stderr, "The user must confirm each use of the key\n");
294856ea928SPeter Avalos  out:
29599e85e0dSPeter Avalos 	if (certpath != NULL)
296*36e94dc5SPeter Avalos 		free(certpath);
297*36e94dc5SPeter Avalos 	free(comment);
29818de8d7fSPeter Avalos 	key_free(private);
29918de8d7fSPeter Avalos 
30018de8d7fSPeter Avalos 	return ret;
30118de8d7fSPeter Avalos }
30218de8d7fSPeter Avalos 
30318de8d7fSPeter Avalos static int
30418de8d7fSPeter Avalos update_card(AuthenticationConnection *ac, int add, const char *id)
30518de8d7fSPeter Avalos {
306*36e94dc5SPeter Avalos 	char *pin = NULL;
30718de8d7fSPeter Avalos 	int ret = -1;
30818de8d7fSPeter Avalos 
309*36e94dc5SPeter Avalos 	if (add) {
310*36e94dc5SPeter Avalos 		if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
311*36e94dc5SPeter Avalos 		    RP_ALLOW_STDIN)) == NULL)
31218de8d7fSPeter Avalos 			return -1;
313*36e94dc5SPeter Avalos 	}
31418de8d7fSPeter Avalos 
315*36e94dc5SPeter Avalos 	if (ssh_update_card(ac, add, id, pin == NULL ? "" : pin,
316*36e94dc5SPeter Avalos 	    lifetime, confirm)) {
31718de8d7fSPeter Avalos 		fprintf(stderr, "Card %s: %s\n",
31818de8d7fSPeter Avalos 		    add ? "added" : "removed", id);
31918de8d7fSPeter Avalos 		ret = 0;
32018de8d7fSPeter Avalos 	} else {
32118de8d7fSPeter Avalos 		fprintf(stderr, "Could not %s card: %s\n",
32218de8d7fSPeter Avalos 		    add ? "add" : "remove", id);
32318de8d7fSPeter Avalos 		ret = -1;
32418de8d7fSPeter Avalos 	}
325*36e94dc5SPeter Avalos 	free(pin);
32618de8d7fSPeter Avalos 	return ret;
32718de8d7fSPeter Avalos }
32818de8d7fSPeter Avalos 
32918de8d7fSPeter Avalos static int
33018de8d7fSPeter Avalos list_identities(AuthenticationConnection *ac, int do_fp)
33118de8d7fSPeter Avalos {
33218de8d7fSPeter Avalos 	Key *key;
33318de8d7fSPeter Avalos 	char *comment, *fp;
33418de8d7fSPeter Avalos 	int had_identities = 0;
33518de8d7fSPeter Avalos 	int version;
33618de8d7fSPeter Avalos 
33718de8d7fSPeter Avalos 	for (version = 1; version <= 2; version++) {
33818de8d7fSPeter Avalos 		for (key = ssh_get_first_identity(ac, &comment, version);
33918de8d7fSPeter Avalos 		    key != NULL;
34018de8d7fSPeter Avalos 		    key = ssh_get_next_identity(ac, &comment, version)) {
34118de8d7fSPeter Avalos 			had_identities = 1;
34218de8d7fSPeter Avalos 			if (do_fp) {
34318de8d7fSPeter Avalos 				fp = key_fingerprint(key, SSH_FP_MD5,
34418de8d7fSPeter Avalos 				    SSH_FP_HEX);
34518de8d7fSPeter Avalos 				printf("%d %s %s (%s)\n",
34618de8d7fSPeter Avalos 				    key_size(key), fp, comment, key_type(key));
347*36e94dc5SPeter Avalos 				free(fp);
34818de8d7fSPeter Avalos 			} else {
34918de8d7fSPeter Avalos 				if (!key_write(key, stdout))
35018de8d7fSPeter Avalos 					fprintf(stderr, "key_write failed");
35118de8d7fSPeter Avalos 				fprintf(stdout, " %s\n", comment);
35218de8d7fSPeter Avalos 			}
35318de8d7fSPeter Avalos 			key_free(key);
354*36e94dc5SPeter Avalos 			free(comment);
35518de8d7fSPeter Avalos 		}
35618de8d7fSPeter Avalos 	}
35718de8d7fSPeter Avalos 	if (!had_identities) {
35818de8d7fSPeter Avalos 		printf("The agent has no identities.\n");
35918de8d7fSPeter Avalos 		return -1;
36018de8d7fSPeter Avalos 	}
36118de8d7fSPeter Avalos 	return 0;
36218de8d7fSPeter Avalos }
36318de8d7fSPeter Avalos 
36418de8d7fSPeter Avalos static int
36518de8d7fSPeter Avalos lock_agent(AuthenticationConnection *ac, int lock)
36618de8d7fSPeter Avalos {
36718de8d7fSPeter Avalos 	char prompt[100], *p1, *p2;
36818de8d7fSPeter Avalos 	int passok = 1, ret = -1;
36918de8d7fSPeter Avalos 
37018de8d7fSPeter Avalos 	strlcpy(prompt, "Enter lock password: ", sizeof(prompt));
37118de8d7fSPeter Avalos 	p1 = read_passphrase(prompt, RP_ALLOW_STDIN);
37218de8d7fSPeter Avalos 	if (lock) {
37318de8d7fSPeter Avalos 		strlcpy(prompt, "Again: ", sizeof prompt);
37418de8d7fSPeter Avalos 		p2 = read_passphrase(prompt, RP_ALLOW_STDIN);
37518de8d7fSPeter Avalos 		if (strcmp(p1, p2) != 0) {
37618de8d7fSPeter Avalos 			fprintf(stderr, "Passwords do not match.\n");
37718de8d7fSPeter Avalos 			passok = 0;
37818de8d7fSPeter Avalos 		}
379*36e94dc5SPeter Avalos 		explicit_bzero(p2, strlen(p2));
380*36e94dc5SPeter Avalos 		free(p2);
38118de8d7fSPeter Avalos 	}
38218de8d7fSPeter Avalos 	if (passok && ssh_lock_agent(ac, lock, p1)) {
38318de8d7fSPeter Avalos 		fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un");
38418de8d7fSPeter Avalos 		ret = 0;
38518de8d7fSPeter Avalos 	} else
38618de8d7fSPeter Avalos 		fprintf(stderr, "Failed to %slock agent.\n", lock ? "" : "un");
387*36e94dc5SPeter Avalos 	explicit_bzero(p1, strlen(p1));
388*36e94dc5SPeter Avalos 	free(p1);
38918de8d7fSPeter Avalos 	return (ret);
39018de8d7fSPeter Avalos }
39118de8d7fSPeter Avalos 
39218de8d7fSPeter Avalos static int
39399e85e0dSPeter Avalos do_file(AuthenticationConnection *ac, int deleting, int key_only, char *file)
39418de8d7fSPeter Avalos {
39518de8d7fSPeter Avalos 	if (deleting) {
396*36e94dc5SPeter Avalos 		if (delete_file(ac, file, key_only) == -1)
39718de8d7fSPeter Avalos 			return -1;
39818de8d7fSPeter Avalos 	} else {
39999e85e0dSPeter Avalos 		if (add_file(ac, file, key_only) == -1)
40018de8d7fSPeter Avalos 			return -1;
40118de8d7fSPeter Avalos 	}
40218de8d7fSPeter Avalos 	return 0;
40318de8d7fSPeter Avalos }
40418de8d7fSPeter Avalos 
40518de8d7fSPeter Avalos static void
40618de8d7fSPeter Avalos usage(void)
40718de8d7fSPeter Avalos {
40818de8d7fSPeter Avalos 	fprintf(stderr, "usage: %s [options] [file ...]\n", __progname);
40918de8d7fSPeter Avalos 	fprintf(stderr, "Options:\n");
41018de8d7fSPeter Avalos 	fprintf(stderr, "  -l          List fingerprints of all identities.\n");
41118de8d7fSPeter Avalos 	fprintf(stderr, "  -L          List public key parameters of all identities.\n");
41299e85e0dSPeter Avalos 	fprintf(stderr, "  -k          Load only keys and not certificates.\n");
41399e85e0dSPeter Avalos 	fprintf(stderr, "  -c          Require confirmation to sign using identities\n");
41499e85e0dSPeter Avalos 	fprintf(stderr, "  -t life     Set lifetime (in seconds) when adding identities.\n");
41518de8d7fSPeter Avalos 	fprintf(stderr, "  -d          Delete identity.\n");
41618de8d7fSPeter Avalos 	fprintf(stderr, "  -D          Delete all identities.\n");
41718de8d7fSPeter Avalos 	fprintf(stderr, "  -x          Lock agent.\n");
41818de8d7fSPeter Avalos 	fprintf(stderr, "  -X          Unlock agent.\n");
419856ea928SPeter Avalos 	fprintf(stderr, "  -s pkcs11   Add keys from PKCS#11 provider.\n");
420856ea928SPeter Avalos 	fprintf(stderr, "  -e pkcs11   Remove keys provided by PKCS#11 provider.\n");
42118de8d7fSPeter Avalos }
42218de8d7fSPeter Avalos 
42318de8d7fSPeter Avalos int
42418de8d7fSPeter Avalos main(int argc, char **argv)
42518de8d7fSPeter Avalos {
42618de8d7fSPeter Avalos 	extern char *optarg;
42718de8d7fSPeter Avalos 	extern int optind;
42818de8d7fSPeter Avalos 	AuthenticationConnection *ac = NULL;
429856ea928SPeter Avalos 	char *pkcs11provider = NULL;
43099e85e0dSPeter Avalos 	int i, ch, deleting = 0, ret = 0, key_only = 0;
43118de8d7fSPeter Avalos 
43218de8d7fSPeter Avalos 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
43318de8d7fSPeter Avalos 	sanitise_stdfd();
43418de8d7fSPeter Avalos 
43518de8d7fSPeter Avalos 	__progname = ssh_get_progname(argv[0]);
43618de8d7fSPeter Avalos 	seed_rng();
43718de8d7fSPeter Avalos 
4389f304aafSPeter Avalos 	OpenSSL_add_all_algorithms();
43918de8d7fSPeter Avalos 
440*36e94dc5SPeter Avalos 	setlinebuf(stdout);
441*36e94dc5SPeter Avalos 
44218de8d7fSPeter Avalos 	/* At first, get a connection to the authentication agent. */
44318de8d7fSPeter Avalos 	ac = ssh_get_authentication_connection();
44418de8d7fSPeter Avalos 	if (ac == NULL) {
44518de8d7fSPeter Avalos 		fprintf(stderr,
44618de8d7fSPeter Avalos 		    "Could not open a connection to your authentication agent.\n");
44718de8d7fSPeter Avalos 		exit(2);
44818de8d7fSPeter Avalos 	}
44999e85e0dSPeter Avalos 	while ((ch = getopt(argc, argv, "klLcdDxXe:s:t:")) != -1) {
45018de8d7fSPeter Avalos 		switch (ch) {
45199e85e0dSPeter Avalos 		case 'k':
45299e85e0dSPeter Avalos 			key_only = 1;
45399e85e0dSPeter Avalos 			break;
45418de8d7fSPeter Avalos 		case 'l':
45518de8d7fSPeter Avalos 		case 'L':
45618de8d7fSPeter Avalos 			if (list_identities(ac, ch == 'l' ? 1 : 0) == -1)
45718de8d7fSPeter Avalos 				ret = 1;
45818de8d7fSPeter Avalos 			goto done;
45918de8d7fSPeter Avalos 		case 'x':
46018de8d7fSPeter Avalos 		case 'X':
46118de8d7fSPeter Avalos 			if (lock_agent(ac, ch == 'x' ? 1 : 0) == -1)
46218de8d7fSPeter Avalos 				ret = 1;
46318de8d7fSPeter Avalos 			goto done;
46418de8d7fSPeter Avalos 		case 'c':
46518de8d7fSPeter Avalos 			confirm = 1;
46618de8d7fSPeter Avalos 			break;
46718de8d7fSPeter Avalos 		case 'd':
46818de8d7fSPeter Avalos 			deleting = 1;
46918de8d7fSPeter Avalos 			break;
47018de8d7fSPeter Avalos 		case 'D':
47118de8d7fSPeter Avalos 			if (delete_all(ac) == -1)
47218de8d7fSPeter Avalos 				ret = 1;
47318de8d7fSPeter Avalos 			goto done;
47418de8d7fSPeter Avalos 		case 's':
475856ea928SPeter Avalos 			pkcs11provider = optarg;
47618de8d7fSPeter Avalos 			break;
47718de8d7fSPeter Avalos 		case 'e':
47818de8d7fSPeter Avalos 			deleting = 1;
479856ea928SPeter Avalos 			pkcs11provider = optarg;
48018de8d7fSPeter Avalos 			break;
48118de8d7fSPeter Avalos 		case 't':
48218de8d7fSPeter Avalos 			if ((lifetime = convtime(optarg)) == -1) {
48318de8d7fSPeter Avalos 				fprintf(stderr, "Invalid lifetime\n");
48418de8d7fSPeter Avalos 				ret = 1;
48518de8d7fSPeter Avalos 				goto done;
48618de8d7fSPeter Avalos 			}
48718de8d7fSPeter Avalos 			break;
48818de8d7fSPeter Avalos 		default:
48918de8d7fSPeter Avalos 			usage();
49018de8d7fSPeter Avalos 			ret = 1;
49118de8d7fSPeter Avalos 			goto done;
49218de8d7fSPeter Avalos 		}
49318de8d7fSPeter Avalos 	}
49418de8d7fSPeter Avalos 	argc -= optind;
49518de8d7fSPeter Avalos 	argv += optind;
496856ea928SPeter Avalos 	if (pkcs11provider != NULL) {
497856ea928SPeter Avalos 		if (update_card(ac, !deleting, pkcs11provider) == -1)
49818de8d7fSPeter Avalos 			ret = 1;
49918de8d7fSPeter Avalos 		goto done;
50018de8d7fSPeter Avalos 	}
50118de8d7fSPeter Avalos 	if (argc == 0) {
50218de8d7fSPeter Avalos 		char buf[MAXPATHLEN];
50318de8d7fSPeter Avalos 		struct passwd *pw;
50418de8d7fSPeter Avalos 		struct stat st;
50518de8d7fSPeter Avalos 		int count = 0;
50618de8d7fSPeter Avalos 
50718de8d7fSPeter Avalos 		if ((pw = getpwuid(getuid())) == NULL) {
50818de8d7fSPeter Avalos 			fprintf(stderr, "No user found with uid %u\n",
50918de8d7fSPeter Avalos 			    (u_int)getuid());
51018de8d7fSPeter Avalos 			ret = 1;
51118de8d7fSPeter Avalos 			goto done;
51218de8d7fSPeter Avalos 		}
51318de8d7fSPeter Avalos 
51418de8d7fSPeter Avalos 		for (i = 0; default_files[i]; i++) {
51518de8d7fSPeter Avalos 			snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir,
51618de8d7fSPeter Avalos 			    default_files[i]);
51718de8d7fSPeter Avalos 			if (stat(buf, &st) < 0)
51818de8d7fSPeter Avalos 				continue;
51999e85e0dSPeter Avalos 			if (do_file(ac, deleting, key_only, buf) == -1)
52018de8d7fSPeter Avalos 				ret = 1;
52118de8d7fSPeter Avalos 			else
52218de8d7fSPeter Avalos 				count++;
52318de8d7fSPeter Avalos 		}
52418de8d7fSPeter Avalos 		if (count == 0)
52518de8d7fSPeter Avalos 			ret = 1;
52618de8d7fSPeter Avalos 	} else {
52718de8d7fSPeter Avalos 		for (i = 0; i < argc; i++) {
52899e85e0dSPeter Avalos 			if (do_file(ac, deleting, key_only, argv[i]) == -1)
52918de8d7fSPeter Avalos 				ret = 1;
53018de8d7fSPeter Avalos 		}
53118de8d7fSPeter Avalos 	}
53218de8d7fSPeter Avalos 	clear_pass();
53318de8d7fSPeter Avalos 
53418de8d7fSPeter Avalos done:
53518de8d7fSPeter Avalos 	ssh_close_authentication_connection(ac);
53618de8d7fSPeter Avalos 	return ret;
53718de8d7fSPeter Avalos }
538