xref: /dflybsd-src/crypto/openssh/ssh-add.c (revision 664f47636b7e6e9e2c54a4799ca4884a9c628df5)
1*664f4763Szrj /* $OpenBSD: ssh-add.c,v 1.138 2019/01/21 12:53:35 djm 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 
4318de8d7fSPeter Avalos #include <openssl/evp.h>
4418de8d7fSPeter Avalos #include "openbsd-compat/openssl-compat.h"
4518de8d7fSPeter Avalos 
46e9778795SPeter Avalos #include <errno.h>
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>
54e9778795SPeter Avalos #include <limits.h>
5518de8d7fSPeter Avalos 
5618de8d7fSPeter Avalos #include "xmalloc.h"
5718de8d7fSPeter Avalos #include "ssh.h"
5818de8d7fSPeter Avalos #include "log.h"
59e9778795SPeter Avalos #include "sshkey.h"
60e9778795SPeter Avalos #include "sshbuf.h"
6118de8d7fSPeter Avalos #include "authfd.h"
6218de8d7fSPeter Avalos #include "authfile.h"
6318de8d7fSPeter Avalos #include "pathnames.h"
6418de8d7fSPeter Avalos #include "misc.h"
6536e94dc5SPeter Avalos #include "ssherr.h"
66e9778795SPeter Avalos #include "digest.h"
6718de8d7fSPeter Avalos 
6818de8d7fSPeter Avalos /* argv0 */
6918de8d7fSPeter Avalos extern char *__progname;
7018de8d7fSPeter Avalos 
7118de8d7fSPeter Avalos /* Default files to add */
7218de8d7fSPeter Avalos static char *default_files[] = {
73e9778795SPeter Avalos #ifdef WITH_OPENSSL
7418de8d7fSPeter Avalos 	_PATH_SSH_CLIENT_ID_RSA,
7518de8d7fSPeter Avalos 	_PATH_SSH_CLIENT_ID_DSA,
769f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
779f304aafSPeter Avalos 	_PATH_SSH_CLIENT_ID_ECDSA,
789f304aafSPeter Avalos #endif
79e9778795SPeter Avalos #endif /* WITH_OPENSSL */
8036e94dc5SPeter Avalos 	_PATH_SSH_CLIENT_ID_ED25519,
81*664f4763Szrj 	_PATH_SSH_CLIENT_ID_XMSS,
8218de8d7fSPeter Avalos 	NULL
8318de8d7fSPeter Avalos };
8418de8d7fSPeter Avalos 
85e9778795SPeter Avalos static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
86e9778795SPeter Avalos 
8718de8d7fSPeter Avalos /* Default lifetime (0 == forever) */
8818de8d7fSPeter Avalos static int lifetime = 0;
8918de8d7fSPeter Avalos 
9018de8d7fSPeter Avalos /* User has to confirm key use */
9118de8d7fSPeter Avalos static int confirm = 0;
9218de8d7fSPeter Avalos 
93*664f4763Szrj /* Maximum number of signatures (XMSS) */
94*664f4763Szrj static u_int maxsign = 0;
95*664f4763Szrj static u_int minleft = 0;
96*664f4763Szrj 
97e9778795SPeter Avalos /* we keep a cache of one passphrase */
9818de8d7fSPeter Avalos static char *pass = NULL;
9918de8d7fSPeter Avalos static void
10018de8d7fSPeter Avalos clear_pass(void)
10118de8d7fSPeter Avalos {
10218de8d7fSPeter Avalos 	if (pass) {
10336e94dc5SPeter Avalos 		explicit_bzero(pass, strlen(pass));
10436e94dc5SPeter Avalos 		free(pass);
10518de8d7fSPeter Avalos 		pass = NULL;
10618de8d7fSPeter Avalos 	}
10718de8d7fSPeter Avalos }
10818de8d7fSPeter Avalos 
10918de8d7fSPeter Avalos static int
110ce74bacaSMatthew Dillon delete_file(int agent_fd, const char *filename, int key_only, int qflag)
11118de8d7fSPeter Avalos {
112e9778795SPeter Avalos 	struct sshkey *public, *cert = NULL;
11336e94dc5SPeter Avalos 	char *certpath = NULL, *comment = NULL;
114e9778795SPeter Avalos 	int r, ret = -1;
11518de8d7fSPeter Avalos 
116e9778795SPeter Avalos 	if ((r = sshkey_load_public(filename, &public,  &comment)) != 0) {
117e9778795SPeter Avalos 		printf("Bad key file %s: %s\n", filename, ssh_err(r));
11818de8d7fSPeter Avalos 		return -1;
11918de8d7fSPeter Avalos 	}
120e9778795SPeter Avalos 	if ((r = ssh_remove_identity(agent_fd, public)) == 0) {
121ce74bacaSMatthew Dillon 		if (!qflag) {
122ce74bacaSMatthew Dillon 			fprintf(stderr, "Identity removed: %s (%s)\n",
123ce74bacaSMatthew Dillon 			    filename, comment);
124ce74bacaSMatthew Dillon 		}
12518de8d7fSPeter Avalos 		ret = 0;
12618de8d7fSPeter Avalos 	} else
127e9778795SPeter Avalos 		fprintf(stderr, "Could not remove identity \"%s\": %s\n",
128e9778795SPeter Avalos 		    filename, ssh_err(r));
12918de8d7fSPeter Avalos 
13036e94dc5SPeter Avalos 	if (key_only)
13136e94dc5SPeter Avalos 		goto out;
13236e94dc5SPeter Avalos 
13336e94dc5SPeter Avalos 	/* Now try to delete the corresponding certificate too */
13436e94dc5SPeter Avalos 	free(comment);
13536e94dc5SPeter Avalos 	comment = NULL;
13636e94dc5SPeter Avalos 	xasprintf(&certpath, "%s-cert.pub", filename);
137e9778795SPeter Avalos 	if ((r = sshkey_load_public(certpath, &cert, &comment)) != 0) {
138e9778795SPeter Avalos 		if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
139e9778795SPeter Avalos 			error("Failed to load certificate \"%s\": %s",
140e9778795SPeter Avalos 			    certpath, ssh_err(r));
14136e94dc5SPeter Avalos 		goto out;
142e9778795SPeter Avalos 	}
143e9778795SPeter Avalos 
144e9778795SPeter Avalos 	if (!sshkey_equal_public(cert, public))
14536e94dc5SPeter Avalos 		fatal("Certificate %s does not match private key %s",
14636e94dc5SPeter Avalos 		    certpath, filename);
14736e94dc5SPeter Avalos 
148e9778795SPeter Avalos 	if ((r = ssh_remove_identity(agent_fd, cert)) == 0) {
149ce74bacaSMatthew Dillon 		if (!qflag) {
150ce74bacaSMatthew Dillon 			fprintf(stderr, "Identity removed: %s (%s)\n",
151ce74bacaSMatthew Dillon 			    certpath, comment);
152ce74bacaSMatthew Dillon 		}
15336e94dc5SPeter Avalos 		ret = 0;
15436e94dc5SPeter Avalos 	} else
155e9778795SPeter Avalos 		fprintf(stderr, "Could not remove identity \"%s\": %s\n",
156e9778795SPeter Avalos 		    certpath, ssh_err(r));
15736e94dc5SPeter Avalos 
15836e94dc5SPeter Avalos  out:
159e9778795SPeter Avalos 	sshkey_free(cert);
160e9778795SPeter Avalos 	sshkey_free(public);
16136e94dc5SPeter Avalos 	free(certpath);
16236e94dc5SPeter Avalos 	free(comment);
16318de8d7fSPeter Avalos 
16418de8d7fSPeter Avalos 	return ret;
16518de8d7fSPeter Avalos }
16618de8d7fSPeter Avalos 
16718de8d7fSPeter Avalos /* Send a request to remove all identities. */
16818de8d7fSPeter Avalos static int
169*664f4763Szrj delete_all(int agent_fd, int qflag)
17018de8d7fSPeter Avalos {
17118de8d7fSPeter Avalos 	int ret = -1;
17218de8d7fSPeter Avalos 
173ce74bacaSMatthew Dillon 	/*
174ce74bacaSMatthew Dillon 	 * Since the agent might be forwarded, old or non-OpenSSH, when asked
175ce74bacaSMatthew Dillon 	 * to remove all keys, attempt to remove both protocol v.1 and v.2
176ce74bacaSMatthew Dillon 	 * keys.
177ce74bacaSMatthew Dillon 	 */
178e9778795SPeter Avalos 	if (ssh_remove_all_identities(agent_fd, 2) == 0)
17918de8d7fSPeter Avalos 		ret = 0;
180e9778795SPeter Avalos 	/* ignore error-code for ssh1 */
181e9778795SPeter Avalos 	ssh_remove_all_identities(agent_fd, 1);
18218de8d7fSPeter Avalos 
183*664f4763Szrj 	if (ret != 0)
18418de8d7fSPeter Avalos 		fprintf(stderr, "Failed to remove all identities.\n");
185*664f4763Szrj 	else if (!qflag)
186*664f4763Szrj 		fprintf(stderr, "All identities removed.\n");
18718de8d7fSPeter Avalos 
18818de8d7fSPeter Avalos 	return ret;
18918de8d7fSPeter Avalos }
19018de8d7fSPeter Avalos 
19118de8d7fSPeter Avalos static int
192ce74bacaSMatthew Dillon add_file(int agent_fd, const char *filename, int key_only, int qflag)
19318de8d7fSPeter Avalos {
194e9778795SPeter Avalos 	struct sshkey *private, *cert;
19518de8d7fSPeter Avalos 	char *comment = NULL;
19699e85e0dSPeter Avalos 	char msg[1024], *certpath = NULL;
197e9778795SPeter Avalos 	int r, fd, ret = -1;
198*664f4763Szrj 	size_t i;
199*664f4763Szrj 	u_int32_t left;
200e9778795SPeter Avalos 	struct sshbuf *keyblob;
201*664f4763Szrj 	struct ssh_identitylist *idlist;
20218de8d7fSPeter Avalos 
2031c188a7fSPeter Avalos 	if (strcmp(filename, "-") == 0) {
2041c188a7fSPeter Avalos 		fd = STDIN_FILENO;
2051c188a7fSPeter Avalos 		filename = "(stdin)";
2061c188a7fSPeter Avalos 	} else if ((fd = open(filename, O_RDONLY)) < 0) {
20718de8d7fSPeter Avalos 		perror(filename);
20818de8d7fSPeter Avalos 		return -1;
20918de8d7fSPeter Avalos 	}
21018de8d7fSPeter Avalos 
21118de8d7fSPeter Avalos 	/*
21218de8d7fSPeter Avalos 	 * Since we'll try to load a keyfile multiple times, permission errors
21318de8d7fSPeter Avalos 	 * will occur multiple times, so check perms first and bail if wrong.
21418de8d7fSPeter Avalos 	 */
2151c188a7fSPeter Avalos 	if (fd != STDIN_FILENO) {
216e9778795SPeter Avalos 		if (sshkey_perm_ok(fd, filename) != 0) {
21718de8d7fSPeter Avalos 			close(fd);
21818de8d7fSPeter Avalos 			return -1;
2191c188a7fSPeter Avalos 		}
2201c188a7fSPeter Avalos 	}
221e9778795SPeter Avalos 	if ((keyblob = sshbuf_new()) == NULL)
222e9778795SPeter Avalos 		fatal("%s: sshbuf_new failed", __func__);
223e9778795SPeter Avalos 	if ((r = sshkey_load_file(fd, keyblob)) != 0) {
224e9778795SPeter Avalos 		fprintf(stderr, "Error loading key \"%s\": %s\n",
225e9778795SPeter Avalos 		    filename, ssh_err(r));
226e9778795SPeter Avalos 		sshbuf_free(keyblob);
2271c188a7fSPeter Avalos 		close(fd);
2281c188a7fSPeter Avalos 		return -1;
2291c188a7fSPeter Avalos 	}
2301c188a7fSPeter Avalos 	close(fd);
23118de8d7fSPeter Avalos 
23218de8d7fSPeter Avalos 	/* At first, try empty passphrase */
233e9778795SPeter Avalos 	if ((r = sshkey_parse_private_fileblob(keyblob, "", &private,
234e9778795SPeter Avalos 	    &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
235e9778795SPeter Avalos 		fprintf(stderr, "Error loading key \"%s\": %s\n",
236e9778795SPeter Avalos 		    filename, ssh_err(r));
237e9778795SPeter Avalos 		goto fail_load;
238e9778795SPeter Avalos 	}
23936e94dc5SPeter Avalos 	/* try last */
24036e94dc5SPeter Avalos 	if (private == NULL && pass != NULL) {
241e9778795SPeter Avalos 		if ((r = sshkey_parse_private_fileblob(keyblob, pass, &private,
242e9778795SPeter Avalos 		    &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
243e9778795SPeter Avalos 			fprintf(stderr, "Error loading key \"%s\": %s\n",
244e9778795SPeter Avalos 			    filename, ssh_err(r));
245e9778795SPeter Avalos 			goto fail_load;
24636e94dc5SPeter Avalos 		}
247e9778795SPeter Avalos 	}
24818de8d7fSPeter Avalos 	if (private == NULL) {
24918de8d7fSPeter Avalos 		/* clear passphrase since it did not work */
25018de8d7fSPeter Avalos 		clear_pass();
251e9778795SPeter Avalos 		snprintf(msg, sizeof msg, "Enter passphrase for %s%s: ",
252e9778795SPeter Avalos 		    filename, confirm ? " (will confirm each use)" : "");
25318de8d7fSPeter Avalos 		for (;;) {
25418de8d7fSPeter Avalos 			pass = read_passphrase(msg, RP_ALLOW_STDIN);
255e9778795SPeter Avalos 			if (strcmp(pass, "") == 0)
256e9778795SPeter Avalos 				goto fail_load;
257e9778795SPeter Avalos 			if ((r = sshkey_parse_private_fileblob(keyblob, pass,
258e9778795SPeter Avalos 			    &private, &comment)) == 0)
259e9778795SPeter Avalos 				break;
260e9778795SPeter Avalos 			else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
261e9778795SPeter Avalos 				fprintf(stderr,
262e9778795SPeter Avalos 				    "Error loading key \"%s\": %s\n",
263e9778795SPeter Avalos 				    filename, ssh_err(r));
264e9778795SPeter Avalos  fail_load:
26518de8d7fSPeter Avalos 				clear_pass();
266e9778795SPeter Avalos 				sshbuf_free(keyblob);
26718de8d7fSPeter Avalos 				return -1;
26818de8d7fSPeter Avalos 			}
26918de8d7fSPeter Avalos 			clear_pass();
27018de8d7fSPeter Avalos 			snprintf(msg, sizeof msg,
271e9778795SPeter Avalos 			    "Bad passphrase, try again for %s%s: ", filename,
272e9778795SPeter Avalos 			    confirm ? " (will confirm each use)" : "");
27318de8d7fSPeter Avalos 		}
27418de8d7fSPeter Avalos 	}
275e9778795SPeter Avalos 	if (comment == NULL || *comment == '\0')
276e9778795SPeter Avalos 		comment = xstrdup(filename);
277e9778795SPeter Avalos 	sshbuf_free(keyblob);
27818de8d7fSPeter Avalos 
279*664f4763Szrj 	/* For XMSS */
280*664f4763Szrj 	if ((r = sshkey_set_filename(private, filename)) != 0) {
281*664f4763Szrj 		fprintf(stderr, "Could not add filename to private key: %s (%s)\n",
282*664f4763Szrj 		    filename, comment);
283*664f4763Szrj 		goto out;
284*664f4763Szrj 	}
285*664f4763Szrj 	if (maxsign && minleft &&
286*664f4763Szrj 	    (r = ssh_fetch_identitylist(agent_fd, &idlist)) == 0) {
287*664f4763Szrj 		for (i = 0; i < idlist->nkeys; i++) {
288*664f4763Szrj 			if (!sshkey_equal_public(idlist->keys[i], private))
289*664f4763Szrj 				continue;
290*664f4763Szrj 			left = sshkey_signatures_left(idlist->keys[i]);
291*664f4763Szrj 			if (left < minleft) {
292*664f4763Szrj 				fprintf(stderr,
293*664f4763Szrj 				    "Only %d signatures left.\n", left);
294*664f4763Szrj 				break;
295*664f4763Szrj 			}
296*664f4763Szrj 			fprintf(stderr, "Skipping update: ");
297*664f4763Szrj 			if (left == minleft) {
298*664f4763Szrj 				fprintf(stderr,
299*664f4763Szrj 				   "required signatures left (%d).\n", left);
300*664f4763Szrj 			} else {
301*664f4763Szrj 				fprintf(stderr,
302*664f4763Szrj 				   "more signatures left (%d) than"
303*664f4763Szrj 				    " required (%d).\n", left, minleft);
304*664f4763Szrj 			}
305*664f4763Szrj 			ssh_free_identitylist(idlist);
306*664f4763Szrj 			goto out;
307*664f4763Szrj 		}
308*664f4763Szrj 		ssh_free_identitylist(idlist);
309*664f4763Szrj 	}
310*664f4763Szrj 
311e9778795SPeter Avalos 	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
312*664f4763Szrj 	    lifetime, confirm, maxsign)) == 0) {
31318de8d7fSPeter Avalos 		ret = 0;
314*664f4763Szrj 		if (!qflag) {
315*664f4763Szrj 			fprintf(stderr, "Identity added: %s (%s)\n",
316*664f4763Szrj 			    filename, comment);
317*664f4763Szrj 			if (lifetime != 0) {
31818de8d7fSPeter Avalos 				fprintf(stderr,
31918de8d7fSPeter Avalos 				    "Lifetime set to %d seconds\n", lifetime);
320*664f4763Szrj 			}
321*664f4763Szrj 			if (confirm != 0) {
322*664f4763Szrj 				fprintf(stderr, "The user must confirm "
323*664f4763Szrj 				    "each use of the key\n");
324*664f4763Szrj 			}
325*664f4763Szrj 		}
32618de8d7fSPeter Avalos 	} else {
327e9778795SPeter Avalos 		fprintf(stderr, "Could not add identity \"%s\": %s\n",
328e9778795SPeter Avalos 		    filename, ssh_err(r));
32918de8d7fSPeter Avalos 	}
33018de8d7fSPeter Avalos 
33199e85e0dSPeter Avalos 	/* Skip trying to load the cert if requested */
33299e85e0dSPeter Avalos 	if (key_only)
33399e85e0dSPeter Avalos 		goto out;
334856ea928SPeter Avalos 
335856ea928SPeter Avalos 	/* Now try to add the certificate flavour too */
336856ea928SPeter Avalos 	xasprintf(&certpath, "%s-cert.pub", filename);
337e9778795SPeter Avalos 	if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) {
338e9778795SPeter Avalos 		if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
339e9778795SPeter Avalos 			error("Failed to load certificate \"%s\": %s",
340e9778795SPeter Avalos 			    certpath, ssh_err(r));
341856ea928SPeter Avalos 		goto out;
342e9778795SPeter Avalos 	}
343856ea928SPeter Avalos 
344e9778795SPeter Avalos 	if (!sshkey_equal_public(cert, private)) {
345856ea928SPeter Avalos 		error("Certificate %s does not match private key %s",
346856ea928SPeter Avalos 		    certpath, filename);
347e9778795SPeter Avalos 		sshkey_free(cert);
348856ea928SPeter Avalos 		goto out;
349856ea928SPeter Avalos 	}
350856ea928SPeter Avalos 
351856ea928SPeter Avalos 	/* Graft with private bits */
352e9778795SPeter Avalos 	if ((r = sshkey_to_certified(private)) != 0) {
353e9778795SPeter Avalos 		error("%s: sshkey_to_certified: %s", __func__, ssh_err(r));
354e9778795SPeter Avalos 		sshkey_free(cert);
355856ea928SPeter Avalos 		goto out;
356856ea928SPeter Avalos 	}
357e9778795SPeter Avalos 	if ((r = sshkey_cert_copy(cert, private)) != 0) {
358ce74bacaSMatthew Dillon 		error("%s: sshkey_cert_copy: %s", __func__, ssh_err(r));
359e9778795SPeter Avalos 		sshkey_free(cert);
360e9778795SPeter Avalos 		goto out;
361e9778795SPeter Avalos 	}
362e9778795SPeter Avalos 	sshkey_free(cert);
363856ea928SPeter Avalos 
364e9778795SPeter Avalos 	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
365*664f4763Szrj 	    lifetime, confirm, maxsign)) != 0) {
366e9778795SPeter Avalos 		error("Certificate %s (%s) add failed: %s", certpath,
367e9778795SPeter Avalos 		    private->cert->key_id, ssh_err(r));
368e9778795SPeter Avalos 		goto out;
369856ea928SPeter Avalos 	}
370*664f4763Szrj 	/* success */
371*664f4763Szrj 	if (!qflag) {
372856ea928SPeter Avalos 		fprintf(stderr, "Certificate added: %s (%s)\n", certpath,
373856ea928SPeter Avalos 		    private->cert->key_id);
374*664f4763Szrj 		if (lifetime != 0) {
375*664f4763Szrj 			fprintf(stderr, "Lifetime set to %d seconds\n",
376*664f4763Szrj 			    lifetime);
377*664f4763Szrj 		}
378*664f4763Szrj 		if (confirm != 0) {
379*664f4763Szrj 			fprintf(stderr, "The user must confirm each use "
380*664f4763Szrj 			    "of the key\n");
381*664f4763Szrj 		}
382*664f4763Szrj 	}
383*664f4763Szrj 
384856ea928SPeter Avalos  out:
38536e94dc5SPeter Avalos 	free(certpath);
38636e94dc5SPeter Avalos 	free(comment);
387e9778795SPeter Avalos 	sshkey_free(private);
38818de8d7fSPeter Avalos 
38918de8d7fSPeter Avalos 	return ret;
39018de8d7fSPeter Avalos }
39118de8d7fSPeter Avalos 
39218de8d7fSPeter Avalos static int
393*664f4763Szrj update_card(int agent_fd, int add, const char *id, int qflag)
39418de8d7fSPeter Avalos {
39536e94dc5SPeter Avalos 	char *pin = NULL;
396e9778795SPeter Avalos 	int r, ret = -1;
39718de8d7fSPeter Avalos 
39836e94dc5SPeter Avalos 	if (add) {
39936e94dc5SPeter Avalos 		if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
40036e94dc5SPeter Avalos 		    RP_ALLOW_STDIN)) == NULL)
40118de8d7fSPeter Avalos 			return -1;
40236e94dc5SPeter Avalos 	}
40318de8d7fSPeter Avalos 
404e9778795SPeter Avalos 	if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin,
405e9778795SPeter Avalos 	    lifetime, confirm)) == 0) {
406*664f4763Szrj 		ret = 0;
407*664f4763Szrj 		if (!qflag) {
40818de8d7fSPeter Avalos 			fprintf(stderr, "Card %s: %s\n",
40918de8d7fSPeter Avalos 			    add ? "added" : "removed", id);
410*664f4763Szrj 		}
41118de8d7fSPeter Avalos 	} else {
412e9778795SPeter Avalos 		fprintf(stderr, "Could not %s card \"%s\": %s\n",
413e9778795SPeter Avalos 		    add ? "add" : "remove", id, ssh_err(r));
41418de8d7fSPeter Avalos 		ret = -1;
41518de8d7fSPeter Avalos 	}
41636e94dc5SPeter Avalos 	free(pin);
41718de8d7fSPeter Avalos 	return ret;
41818de8d7fSPeter Avalos }
41918de8d7fSPeter Avalos 
42018de8d7fSPeter Avalos static int
421*664f4763Szrj test_key(int agent_fd, const char *filename)
422*664f4763Szrj {
423*664f4763Szrj 	struct sshkey *key = NULL;
424*664f4763Szrj 	u_char *sig = NULL;
425*664f4763Szrj 	size_t slen = 0;
426*664f4763Szrj 	int r, ret = -1;
427*664f4763Szrj 	char data[1024];
428*664f4763Szrj 
429*664f4763Szrj 	if ((r = sshkey_load_public(filename, &key, NULL)) != 0) {
430*664f4763Szrj 		error("Couldn't read public key %s: %s", filename, ssh_err(r));
431*664f4763Szrj 		return -1;
432*664f4763Szrj 	}
433*664f4763Szrj 	arc4random_buf(data, sizeof(data));
434*664f4763Szrj 	if ((r = ssh_agent_sign(agent_fd, key, &sig, &slen, data, sizeof(data),
435*664f4763Szrj 	    NULL, 0)) != 0) {
436*664f4763Szrj 		error("Agent signature failed for %s: %s",
437*664f4763Szrj 		    filename, ssh_err(r));
438*664f4763Szrj 		goto done;
439*664f4763Szrj 	}
440*664f4763Szrj 	if ((r = sshkey_verify(key, sig, slen, data, sizeof(data),
441*664f4763Szrj 	    NULL, 0)) != 0) {
442*664f4763Szrj 		error("Signature verification failed for %s: %s",
443*664f4763Szrj 		    filename, ssh_err(r));
444*664f4763Szrj 		goto done;
445*664f4763Szrj 	}
446*664f4763Szrj 	/* success */
447*664f4763Szrj 	ret = 0;
448*664f4763Szrj  done:
449*664f4763Szrj 	free(sig);
450*664f4763Szrj 	sshkey_free(key);
451*664f4763Szrj 	return ret;
452*664f4763Szrj }
453*664f4763Szrj 
454*664f4763Szrj static int
455e9778795SPeter Avalos list_identities(int agent_fd, int do_fp)
45618de8d7fSPeter Avalos {
457e9778795SPeter Avalos 	char *fp;
458ce74bacaSMatthew Dillon 	int r;
459e9778795SPeter Avalos 	struct ssh_identitylist *idlist;
460*664f4763Szrj 	u_int32_t left;
461e9778795SPeter Avalos 	size_t i;
46218de8d7fSPeter Avalos 
463ce74bacaSMatthew Dillon 	if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) {
464e9778795SPeter Avalos 		if (r != SSH_ERR_AGENT_NO_IDENTITIES)
465ce74bacaSMatthew Dillon 			fprintf(stderr, "error fetching identities: %s\n",
466ce74bacaSMatthew Dillon 			    ssh_err(r));
467ce74bacaSMatthew Dillon 		else
468ce74bacaSMatthew Dillon 			printf("The agent has no identities.\n");
469ce74bacaSMatthew Dillon 		return -1;
470e9778795SPeter Avalos 	}
471e9778795SPeter Avalos 	for (i = 0; i < idlist->nkeys; i++) {
47218de8d7fSPeter Avalos 		if (do_fp) {
473e9778795SPeter Avalos 			fp = sshkey_fingerprint(idlist->keys[i],
474e9778795SPeter Avalos 			    fingerprint_hash, SSH_FP_DEFAULT);
475ce74bacaSMatthew Dillon 			printf("%u %s %s (%s)\n", sshkey_size(idlist->keys[i]),
476ce74bacaSMatthew Dillon 			    fp == NULL ? "(null)" : fp, idlist->comments[i],
477e9778795SPeter Avalos 			    sshkey_type(idlist->keys[i]));
47836e94dc5SPeter Avalos 			free(fp);
47918de8d7fSPeter Avalos 		} else {
480ce74bacaSMatthew Dillon 			if ((r = sshkey_write(idlist->keys[i], stdout)) != 0) {
481e9778795SPeter Avalos 				fprintf(stderr, "sshkey_write: %s\n",
482e9778795SPeter Avalos 				    ssh_err(r));
483e9778795SPeter Avalos 				continue;
48418de8d7fSPeter Avalos 			}
485*664f4763Szrj 			fprintf(stdout, " %s", idlist->comments[i]);
486*664f4763Szrj 			left = sshkey_signatures_left(idlist->keys[i]);
487*664f4763Szrj 			if (left > 0)
488*664f4763Szrj 				fprintf(stdout,
489*664f4763Szrj 				    " [signatures left %d]", left);
490*664f4763Szrj 			fprintf(stdout, "\n");
49118de8d7fSPeter Avalos 		}
49218de8d7fSPeter Avalos 	}
493e9778795SPeter Avalos 	ssh_free_identitylist(idlist);
49418de8d7fSPeter Avalos 	return 0;
49518de8d7fSPeter Avalos }
49618de8d7fSPeter Avalos 
49718de8d7fSPeter Avalos static int
498e9778795SPeter Avalos lock_agent(int agent_fd, int lock)
49918de8d7fSPeter Avalos {
50018de8d7fSPeter Avalos 	char prompt[100], *p1, *p2;
501e9778795SPeter Avalos 	int r, passok = 1, ret = -1;
50218de8d7fSPeter Avalos 
50318de8d7fSPeter Avalos 	strlcpy(prompt, "Enter lock password: ", sizeof(prompt));
50418de8d7fSPeter Avalos 	p1 = read_passphrase(prompt, RP_ALLOW_STDIN);
50518de8d7fSPeter Avalos 	if (lock) {
50618de8d7fSPeter Avalos 		strlcpy(prompt, "Again: ", sizeof prompt);
50718de8d7fSPeter Avalos 		p2 = read_passphrase(prompt, RP_ALLOW_STDIN);
50818de8d7fSPeter Avalos 		if (strcmp(p1, p2) != 0) {
50918de8d7fSPeter Avalos 			fprintf(stderr, "Passwords do not match.\n");
51018de8d7fSPeter Avalos 			passok = 0;
51118de8d7fSPeter Avalos 		}
51236e94dc5SPeter Avalos 		explicit_bzero(p2, strlen(p2));
51336e94dc5SPeter Avalos 		free(p2);
51418de8d7fSPeter Avalos 	}
515e9778795SPeter Avalos 	if (passok) {
516e9778795SPeter Avalos 		if ((r = ssh_lock_agent(agent_fd, lock, p1)) == 0) {
51718de8d7fSPeter Avalos 			fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un");
51818de8d7fSPeter Avalos 			ret = 0;
519e9778795SPeter Avalos 		} else {
520e9778795SPeter Avalos 			fprintf(stderr, "Failed to %slock agent: %s\n",
521e9778795SPeter Avalos 			    lock ? "" : "un", ssh_err(r));
522e9778795SPeter Avalos 		}
523e9778795SPeter Avalos 	}
52436e94dc5SPeter Avalos 	explicit_bzero(p1, strlen(p1));
52536e94dc5SPeter Avalos 	free(p1);
52618de8d7fSPeter Avalos 	return (ret);
52718de8d7fSPeter Avalos }
52818de8d7fSPeter Avalos 
52918de8d7fSPeter Avalos static int
530ce74bacaSMatthew Dillon do_file(int agent_fd, int deleting, int key_only, char *file, int qflag)
53118de8d7fSPeter Avalos {
53218de8d7fSPeter Avalos 	if (deleting) {
533ce74bacaSMatthew Dillon 		if (delete_file(agent_fd, file, key_only, qflag) == -1)
53418de8d7fSPeter Avalos 			return -1;
53518de8d7fSPeter Avalos 	} else {
536ce74bacaSMatthew Dillon 		if (add_file(agent_fd, file, key_only, qflag) == -1)
53718de8d7fSPeter Avalos 			return -1;
53818de8d7fSPeter Avalos 	}
53918de8d7fSPeter Avalos 	return 0;
54018de8d7fSPeter Avalos }
54118de8d7fSPeter Avalos 
54218de8d7fSPeter Avalos static void
54318de8d7fSPeter Avalos usage(void)
54418de8d7fSPeter Avalos {
54518de8d7fSPeter Avalos 	fprintf(stderr, "usage: %s [options] [file ...]\n", __progname);
54618de8d7fSPeter Avalos 	fprintf(stderr, "Options:\n");
54718de8d7fSPeter Avalos 	fprintf(stderr, "  -l          List fingerprints of all identities.\n");
548e9778795SPeter Avalos 	fprintf(stderr, "  -E hash     Specify hash algorithm used for fingerprints.\n");
54918de8d7fSPeter Avalos 	fprintf(stderr, "  -L          List public key parameters of all identities.\n");
55099e85e0dSPeter Avalos 	fprintf(stderr, "  -k          Load only keys and not certificates.\n");
55199e85e0dSPeter Avalos 	fprintf(stderr, "  -c          Require confirmation to sign using identities\n");
552*664f4763Szrj 	fprintf(stderr, "  -m minleft  Maxsign is only changed if less than minleft are left (for XMSS)\n");
553*664f4763Szrj 	fprintf(stderr, "  -M maxsign  Maximum number of signatures allowed (for XMSS)\n");
55499e85e0dSPeter Avalos 	fprintf(stderr, "  -t life     Set lifetime (in seconds) when adding identities.\n");
55518de8d7fSPeter Avalos 	fprintf(stderr, "  -d          Delete identity.\n");
55618de8d7fSPeter Avalos 	fprintf(stderr, "  -D          Delete all identities.\n");
55718de8d7fSPeter Avalos 	fprintf(stderr, "  -x          Lock agent.\n");
55818de8d7fSPeter Avalos 	fprintf(stderr, "  -X          Unlock agent.\n");
559856ea928SPeter Avalos 	fprintf(stderr, "  -s pkcs11   Add keys from PKCS#11 provider.\n");
560856ea928SPeter Avalos 	fprintf(stderr, "  -e pkcs11   Remove keys provided by PKCS#11 provider.\n");
561*664f4763Szrj 	fprintf(stderr, "  -T pubkey   Test if ssh-agent can access matching private key.\n");
562ce74bacaSMatthew Dillon 	fprintf(stderr, "  -q          Be quiet after a successful operation.\n");
563*664f4763Szrj 	fprintf(stderr, "  -v          Be more verbose.\n");
56418de8d7fSPeter Avalos }
56518de8d7fSPeter Avalos 
56618de8d7fSPeter Avalos int
56718de8d7fSPeter Avalos main(int argc, char **argv)
56818de8d7fSPeter Avalos {
56918de8d7fSPeter Avalos 	extern char *optarg;
57018de8d7fSPeter Avalos 	extern int optind;
571e9778795SPeter Avalos 	int agent_fd;
572856ea928SPeter Avalos 	char *pkcs11provider = NULL;
573e9778795SPeter Avalos 	int r, i, ch, deleting = 0, ret = 0, key_only = 0;
574*664f4763Szrj 	int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0;
575*664f4763Szrj 	SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
576*664f4763Szrj 	LogLevel log_level = SYSLOG_LEVEL_INFO;
57718de8d7fSPeter Avalos 
578e9778795SPeter Avalos 	ssh_malloc_init();	/* must be called before any mallocs */
57918de8d7fSPeter Avalos 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
58018de8d7fSPeter Avalos 	sanitise_stdfd();
58118de8d7fSPeter Avalos 
58218de8d7fSPeter Avalos 	__progname = ssh_get_progname(argv[0]);
58318de8d7fSPeter Avalos 	seed_rng();
58418de8d7fSPeter Avalos 
585*664f4763Szrj 	log_init(__progname, log_level, log_facility, 1);
58618de8d7fSPeter Avalos 
587e9778795SPeter Avalos 	setvbuf(stdout, NULL, _IOLBF, 0);
58836e94dc5SPeter Avalos 
589e9778795SPeter Avalos 	/* First, get a connection to the authentication agent. */
590e9778795SPeter Avalos 	switch (r = ssh_get_authentication_socket(&agent_fd)) {
591e9778795SPeter Avalos 	case 0:
592e9778795SPeter Avalos 		break;
593e9778795SPeter Avalos 	case SSH_ERR_AGENT_NOT_PRESENT:
594e9778795SPeter Avalos 		fprintf(stderr, "Could not open a connection to your "
595e9778795SPeter Avalos 		    "authentication agent.\n");
596e9778795SPeter Avalos 		exit(2);
597e9778795SPeter Avalos 	default:
598e9778795SPeter Avalos 		fprintf(stderr, "Error connecting to agent: %s\n", ssh_err(r));
59918de8d7fSPeter Avalos 		exit(2);
60018de8d7fSPeter Avalos 	}
601e9778795SPeter Avalos 
602*664f4763Szrj 	while ((ch = getopt(argc, argv, "vklLcdDTxXE:e:M:m:qs:t:")) != -1) {
60318de8d7fSPeter Avalos 		switch (ch) {
604*664f4763Szrj 		case 'v':
605*664f4763Szrj 			if (log_level == SYSLOG_LEVEL_INFO)
606*664f4763Szrj 				log_level = SYSLOG_LEVEL_DEBUG1;
607*664f4763Szrj 			else if (log_level < SYSLOG_LEVEL_DEBUG3)
608*664f4763Szrj 				log_level++;
609*664f4763Szrj 			break;
610e9778795SPeter Avalos 		case 'E':
611e9778795SPeter Avalos 			fingerprint_hash = ssh_digest_alg_by_name(optarg);
612e9778795SPeter Avalos 			if (fingerprint_hash == -1)
613e9778795SPeter Avalos 				fatal("Invalid hash algorithm \"%s\"", optarg);
614e9778795SPeter Avalos 			break;
61599e85e0dSPeter Avalos 		case 'k':
61699e85e0dSPeter Avalos 			key_only = 1;
61799e85e0dSPeter Avalos 			break;
61818de8d7fSPeter Avalos 		case 'l':
61918de8d7fSPeter Avalos 		case 'L':
620e9778795SPeter Avalos 			if (lflag != 0)
621e9778795SPeter Avalos 				fatal("-%c flag already specified", lflag);
622e9778795SPeter Avalos 			lflag = ch;
623e9778795SPeter Avalos 			break;
62418de8d7fSPeter Avalos 		case 'x':
62518de8d7fSPeter Avalos 		case 'X':
626e9778795SPeter Avalos 			if (xflag != 0)
627e9778795SPeter Avalos 				fatal("-%c flag already specified", xflag);
628e9778795SPeter Avalos 			xflag = ch;
629e9778795SPeter Avalos 			break;
63018de8d7fSPeter Avalos 		case 'c':
63118de8d7fSPeter Avalos 			confirm = 1;
63218de8d7fSPeter Avalos 			break;
633*664f4763Szrj 		case 'm':
634*664f4763Szrj 			minleft = (int)strtonum(optarg, 1, UINT_MAX, NULL);
635*664f4763Szrj 			if (minleft == 0) {
636*664f4763Szrj 				usage();
637*664f4763Szrj 				ret = 1;
638*664f4763Szrj 				goto done;
639*664f4763Szrj 			}
640*664f4763Szrj 			break;
641*664f4763Szrj 		case 'M':
642*664f4763Szrj 			maxsign = (int)strtonum(optarg, 1, UINT_MAX, NULL);
643*664f4763Szrj 			if (maxsign == 0) {
644*664f4763Szrj 				usage();
645*664f4763Szrj 				ret = 1;
646*664f4763Szrj 				goto done;
647*664f4763Szrj 			}
648*664f4763Szrj 			break;
64918de8d7fSPeter Avalos 		case 'd':
65018de8d7fSPeter Avalos 			deleting = 1;
65118de8d7fSPeter Avalos 			break;
65218de8d7fSPeter Avalos 		case 'D':
653e9778795SPeter Avalos 			Dflag = 1;
654e9778795SPeter Avalos 			break;
65518de8d7fSPeter Avalos 		case 's':
656856ea928SPeter Avalos 			pkcs11provider = optarg;
65718de8d7fSPeter Avalos 			break;
65818de8d7fSPeter Avalos 		case 'e':
65918de8d7fSPeter Avalos 			deleting = 1;
660856ea928SPeter Avalos 			pkcs11provider = optarg;
66118de8d7fSPeter Avalos 			break;
66218de8d7fSPeter Avalos 		case 't':
66318de8d7fSPeter Avalos 			if ((lifetime = convtime(optarg)) == -1) {
66418de8d7fSPeter Avalos 				fprintf(stderr, "Invalid lifetime\n");
66518de8d7fSPeter Avalos 				ret = 1;
66618de8d7fSPeter Avalos 				goto done;
66718de8d7fSPeter Avalos 			}
66818de8d7fSPeter Avalos 			break;
669ce74bacaSMatthew Dillon 		case 'q':
670ce74bacaSMatthew Dillon 			qflag = 1;
671ce74bacaSMatthew Dillon 			break;
672*664f4763Szrj 		case 'T':
673*664f4763Szrj 			Tflag = 1;
674*664f4763Szrj 			break;
67518de8d7fSPeter Avalos 		default:
67618de8d7fSPeter Avalos 			usage();
67718de8d7fSPeter Avalos 			ret = 1;
67818de8d7fSPeter Avalos 			goto done;
67918de8d7fSPeter Avalos 		}
68018de8d7fSPeter Avalos 	}
681*664f4763Szrj 	log_init(__progname, log_level, log_facility, 1);
682e9778795SPeter Avalos 
683e9778795SPeter Avalos 	if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1)
684e9778795SPeter Avalos 		fatal("Invalid combination of actions");
685e9778795SPeter Avalos 	else if (xflag) {
686e9778795SPeter Avalos 		if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1)
687e9778795SPeter Avalos 			ret = 1;
688e9778795SPeter Avalos 		goto done;
689e9778795SPeter Avalos 	} else if (lflag) {
690e9778795SPeter Avalos 		if (list_identities(agent_fd, lflag == 'l' ? 1 : 0) == -1)
691e9778795SPeter Avalos 			ret = 1;
692e9778795SPeter Avalos 		goto done;
693e9778795SPeter Avalos 	} else if (Dflag) {
694*664f4763Szrj 		if (delete_all(agent_fd, qflag) == -1)
695e9778795SPeter Avalos 			ret = 1;
696e9778795SPeter Avalos 		goto done;
697e9778795SPeter Avalos 	}
698e9778795SPeter Avalos 
69918de8d7fSPeter Avalos 	argc -= optind;
70018de8d7fSPeter Avalos 	argv += optind;
701*664f4763Szrj 	if (Tflag) {
702*664f4763Szrj 		if (argc <= 0)
703*664f4763Szrj 			fatal("no keys to test");
704*664f4763Szrj 		for (r = i = 0; i < argc; i++)
705*664f4763Szrj 			r |= test_key(agent_fd, argv[i]);
706*664f4763Szrj 		ret = r == 0 ? 0 : 1;
707*664f4763Szrj 		goto done;
708*664f4763Szrj 	}
709856ea928SPeter Avalos 	if (pkcs11provider != NULL) {
710*664f4763Szrj 		if (update_card(agent_fd, !deleting, pkcs11provider,
711*664f4763Szrj 		    qflag) == -1)
71218de8d7fSPeter Avalos 			ret = 1;
71318de8d7fSPeter Avalos 		goto done;
71418de8d7fSPeter Avalos 	}
71518de8d7fSPeter Avalos 	if (argc == 0) {
716e9778795SPeter Avalos 		char buf[PATH_MAX];
71718de8d7fSPeter Avalos 		struct passwd *pw;
71818de8d7fSPeter Avalos 		struct stat st;
71918de8d7fSPeter Avalos 		int count = 0;
72018de8d7fSPeter Avalos 
72118de8d7fSPeter Avalos 		if ((pw = getpwuid(getuid())) == NULL) {
72218de8d7fSPeter Avalos 			fprintf(stderr, "No user found with uid %u\n",
72318de8d7fSPeter Avalos 			    (u_int)getuid());
72418de8d7fSPeter Avalos 			ret = 1;
72518de8d7fSPeter Avalos 			goto done;
72618de8d7fSPeter Avalos 		}
72718de8d7fSPeter Avalos 
72818de8d7fSPeter Avalos 		for (i = 0; default_files[i]; i++) {
72918de8d7fSPeter Avalos 			snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir,
73018de8d7fSPeter Avalos 			    default_files[i]);
73118de8d7fSPeter Avalos 			if (stat(buf, &st) < 0)
73218de8d7fSPeter Avalos 				continue;
733ce74bacaSMatthew Dillon 			if (do_file(agent_fd, deleting, key_only, buf,
734ce74bacaSMatthew Dillon 			    qflag) == -1)
73518de8d7fSPeter Avalos 				ret = 1;
73618de8d7fSPeter Avalos 			else
73718de8d7fSPeter Avalos 				count++;
73818de8d7fSPeter Avalos 		}
73918de8d7fSPeter Avalos 		if (count == 0)
74018de8d7fSPeter Avalos 			ret = 1;
74118de8d7fSPeter Avalos 	} else {
74218de8d7fSPeter Avalos 		for (i = 0; i < argc; i++) {
743e9778795SPeter Avalos 			if (do_file(agent_fd, deleting, key_only,
744ce74bacaSMatthew Dillon 			    argv[i], qflag) == -1)
74518de8d7fSPeter Avalos 				ret = 1;
74618de8d7fSPeter Avalos 		}
74718de8d7fSPeter Avalos 	}
74818de8d7fSPeter Avalos 	clear_pass();
74918de8d7fSPeter Avalos 
75018de8d7fSPeter Avalos done:
751e9778795SPeter Avalos 	ssh_close_authentication_socket(agent_fd);
75218de8d7fSPeter Avalos 	return ret;
75318de8d7fSPeter Avalos }
754