xref: /dflybsd-src/crypto/openssh/ssh-add.c (revision ba1276acd1c8c22d225b1bcf370a14c878644f44)
1*ba1276acSMatthew Dillon /* $OpenBSD: ssh-add.c,v 1.172 2024/01/11 01:45:36 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 
430cbfa66cSDaniel Fojt #ifdef WITH_OPENSSL
4418de8d7fSPeter Avalos # include <openssl/evp.h>
4518de8d7fSPeter Avalos # include "openbsd-compat/openssl-compat.h"
460cbfa66cSDaniel Fojt #endif
4718de8d7fSPeter Avalos 
48e9778795SPeter Avalos #include <errno.h>
4918de8d7fSPeter Avalos #include <fcntl.h>
5018de8d7fSPeter Avalos #include <pwd.h>
5118de8d7fSPeter Avalos #include <stdarg.h>
5218de8d7fSPeter Avalos #include <stdio.h>
5318de8d7fSPeter Avalos #include <stdlib.h>
5418de8d7fSPeter Avalos #include <string.h>
5518de8d7fSPeter Avalos #include <unistd.h>
56e9778795SPeter Avalos #include <limits.h>
5718de8d7fSPeter Avalos 
5818de8d7fSPeter Avalos #include "xmalloc.h"
5918de8d7fSPeter Avalos #include "ssh.h"
6018de8d7fSPeter Avalos #include "log.h"
61e9778795SPeter Avalos #include "sshkey.h"
62e9778795SPeter Avalos #include "sshbuf.h"
6318de8d7fSPeter Avalos #include "authfd.h"
6418de8d7fSPeter Avalos #include "authfile.h"
6518de8d7fSPeter Avalos #include "pathnames.h"
6618de8d7fSPeter Avalos #include "misc.h"
6736e94dc5SPeter Avalos #include "ssherr.h"
68e9778795SPeter Avalos #include "digest.h"
690cbfa66cSDaniel Fojt #include "ssh-sk.h"
7050a69bb5SSascha Wildner #include "sk-api.h"
71ee116499SAntonio Huete Jimenez #include "hostfile.h"
7218de8d7fSPeter Avalos 
7318de8d7fSPeter Avalos /* argv0 */
7418de8d7fSPeter Avalos extern char *__progname;
7518de8d7fSPeter Avalos 
7618de8d7fSPeter Avalos /* Default files to add */
7718de8d7fSPeter Avalos static char *default_files[] = {
78e9778795SPeter Avalos #ifdef WITH_OPENSSL
7918de8d7fSPeter Avalos 	_PATH_SSH_CLIENT_ID_RSA,
809f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
819f304aafSPeter Avalos 	_PATH_SSH_CLIENT_ID_ECDSA,
820cbfa66cSDaniel Fojt 	_PATH_SSH_CLIENT_ID_ECDSA_SK,
839f304aafSPeter Avalos #endif
84e9778795SPeter Avalos #endif /* WITH_OPENSSL */
8536e94dc5SPeter Avalos 	_PATH_SSH_CLIENT_ID_ED25519,
860cbfa66cSDaniel Fojt 	_PATH_SSH_CLIENT_ID_ED25519_SK,
87664f4763Szrj 	_PATH_SSH_CLIENT_ID_XMSS,
88*ba1276acSMatthew Dillon #ifdef WITH_DSA
89ee116499SAntonio Huete Jimenez 	_PATH_SSH_CLIENT_ID_DSA,
90*ba1276acSMatthew Dillon #endif
9118de8d7fSPeter Avalos 	NULL
9218de8d7fSPeter Avalos };
9318de8d7fSPeter Avalos 
94e9778795SPeter Avalos static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
95e9778795SPeter Avalos 
9618de8d7fSPeter Avalos /* Default lifetime (0 == forever) */
9750a69bb5SSascha Wildner static int lifetime = 0;
9818de8d7fSPeter Avalos 
9918de8d7fSPeter Avalos /* User has to confirm key use */
10018de8d7fSPeter Avalos static int confirm = 0;
10118de8d7fSPeter Avalos 
102664f4763Szrj /* Maximum number of signatures (XMSS) */
103664f4763Szrj static u_int maxsign = 0;
104664f4763Szrj static u_int minleft = 0;
105664f4763Szrj 
106e9778795SPeter Avalos /* we keep a cache of one passphrase */
10718de8d7fSPeter Avalos static char *pass = NULL;
10818de8d7fSPeter Avalos static void
clear_pass(void)10918de8d7fSPeter Avalos clear_pass(void)
11018de8d7fSPeter Avalos {
11118de8d7fSPeter Avalos 	if (pass) {
1120cbfa66cSDaniel Fojt 		freezero(pass, strlen(pass));
11318de8d7fSPeter Avalos 		pass = NULL;
11418de8d7fSPeter Avalos 	}
11518de8d7fSPeter Avalos }
11618de8d7fSPeter Avalos 
11718de8d7fSPeter Avalos static int
delete_one(int agent_fd,const struct sshkey * key,const char * comment,const char * path,int qflag)11850a69bb5SSascha Wildner delete_one(int agent_fd, const struct sshkey *key, const char *comment,
11950a69bb5SSascha Wildner     const char *path, int qflag)
12050a69bb5SSascha Wildner {
12150a69bb5SSascha Wildner 	int r;
12250a69bb5SSascha Wildner 
12350a69bb5SSascha Wildner 	if ((r = ssh_remove_identity(agent_fd, key)) != 0) {
12450a69bb5SSascha Wildner 		fprintf(stderr, "Could not remove identity \"%s\": %s\n",
12550a69bb5SSascha Wildner 		    path, ssh_err(r));
12650a69bb5SSascha Wildner 		return r;
12750a69bb5SSascha Wildner 	}
12850a69bb5SSascha Wildner 	if (!qflag) {
12950a69bb5SSascha Wildner 		fprintf(stderr, "Identity removed: %s %s (%s)\n", path,
130ee116499SAntonio Huete Jimenez 		    sshkey_type(key), comment ? comment : "no comment");
13150a69bb5SSascha Wildner 	}
13250a69bb5SSascha Wildner 	return 0;
13350a69bb5SSascha Wildner }
13450a69bb5SSascha Wildner 
13550a69bb5SSascha Wildner static int
delete_stdin(int agent_fd,int qflag,int key_only,int cert_only)136*ba1276acSMatthew Dillon delete_stdin(int agent_fd, int qflag, int key_only, int cert_only)
13750a69bb5SSascha Wildner {
13850a69bb5SSascha Wildner 	char *line = NULL, *cp;
13950a69bb5SSascha Wildner 	size_t linesize = 0;
14050a69bb5SSascha Wildner 	struct sshkey *key = NULL;
14150a69bb5SSascha Wildner 	int lnum = 0, r, ret = -1;
14250a69bb5SSascha Wildner 
14350a69bb5SSascha Wildner 	while (getline(&line, &linesize, stdin) != -1) {
14450a69bb5SSascha Wildner 		lnum++;
14550a69bb5SSascha Wildner 		sshkey_free(key);
14650a69bb5SSascha Wildner 		key = NULL;
14750a69bb5SSascha Wildner 		line[strcspn(line, "\n")] = '\0';
14850a69bb5SSascha Wildner 		cp = line + strspn(line, " \t");
14950a69bb5SSascha Wildner 		if (*cp == '#' || *cp == '\0')
15050a69bb5SSascha Wildner 			continue;
15150a69bb5SSascha Wildner 		if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
15250a69bb5SSascha Wildner 			fatal_f("sshkey_new");
15350a69bb5SSascha Wildner 		if ((r = sshkey_read(key, &cp)) != 0) {
15450a69bb5SSascha Wildner 			error_r(r, "(stdin):%d: invalid key", lnum);
15550a69bb5SSascha Wildner 			continue;
15650a69bb5SSascha Wildner 		}
157*ba1276acSMatthew Dillon 		if ((!key_only && !cert_only) ||
158*ba1276acSMatthew Dillon 		    (key_only && !sshkey_is_cert(key)) ||
159*ba1276acSMatthew Dillon 		    (cert_only && sshkey_is_cert(key))) {
160*ba1276acSMatthew Dillon 			if (delete_one(agent_fd, key, cp,
161*ba1276acSMatthew Dillon 			    "(stdin)", qflag) == 0)
16250a69bb5SSascha Wildner 				ret = 0;
16350a69bb5SSascha Wildner 		}
164*ba1276acSMatthew Dillon 	}
16550a69bb5SSascha Wildner 	sshkey_free(key);
16650a69bb5SSascha Wildner 	free(line);
16750a69bb5SSascha Wildner 	return ret;
16850a69bb5SSascha Wildner }
16950a69bb5SSascha Wildner 
17050a69bb5SSascha Wildner static int
delete_file(int agent_fd,const char * filename,int key_only,int cert_only,int qflag)171*ba1276acSMatthew Dillon delete_file(int agent_fd, const char *filename, int key_only,
172*ba1276acSMatthew Dillon     int cert_only, int qflag)
17318de8d7fSPeter Avalos {
174e9778795SPeter Avalos 	struct sshkey *public, *cert = NULL;
17536e94dc5SPeter Avalos 	char *certpath = NULL, *comment = NULL;
176e9778795SPeter Avalos 	int r, ret = -1;
17718de8d7fSPeter Avalos 
17850a69bb5SSascha Wildner 	if (strcmp(filename, "-") == 0)
179*ba1276acSMatthew Dillon 		return delete_stdin(agent_fd, qflag, key_only, cert_only);
18050a69bb5SSascha Wildner 
181e9778795SPeter Avalos 	if ((r = sshkey_load_public(filename, &public,  &comment)) != 0) {
182e9778795SPeter Avalos 		printf("Bad key file %s: %s\n", filename, ssh_err(r));
18318de8d7fSPeter Avalos 		return -1;
18418de8d7fSPeter Avalos 	}
185*ba1276acSMatthew Dillon 	if ((!key_only && !cert_only) ||
186*ba1276acSMatthew Dillon 	    (key_only && !sshkey_is_cert(public)) ||
187*ba1276acSMatthew Dillon 	    (cert_only && sshkey_is_cert(public))) {
18850a69bb5SSascha Wildner 		if (delete_one(agent_fd, public, comment, filename, qflag) == 0)
18918de8d7fSPeter Avalos 			ret = 0;
190*ba1276acSMatthew Dillon 	}
19118de8d7fSPeter Avalos 
19236e94dc5SPeter Avalos 	if (key_only)
19336e94dc5SPeter Avalos 		goto out;
19436e94dc5SPeter Avalos 
19536e94dc5SPeter Avalos 	/* Now try to delete the corresponding certificate too */
19636e94dc5SPeter Avalos 	free(comment);
19736e94dc5SPeter Avalos 	comment = NULL;
19836e94dc5SPeter Avalos 	xasprintf(&certpath, "%s-cert.pub", filename);
199e9778795SPeter Avalos 	if ((r = sshkey_load_public(certpath, &cert, &comment)) != 0) {
200e9778795SPeter Avalos 		if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
20150a69bb5SSascha Wildner 			error_r(r, "Failed to load certificate \"%s\"", certpath);
20236e94dc5SPeter Avalos 		goto out;
203e9778795SPeter Avalos 	}
204e9778795SPeter Avalos 
205e9778795SPeter Avalos 	if (!sshkey_equal_public(cert, public))
20636e94dc5SPeter Avalos 		fatal("Certificate %s does not match private key %s",
20736e94dc5SPeter Avalos 		    certpath, filename);
20836e94dc5SPeter Avalos 
20950a69bb5SSascha Wildner 	if (delete_one(agent_fd, cert, comment, certpath, qflag) == 0)
21036e94dc5SPeter Avalos 		ret = 0;
21136e94dc5SPeter Avalos 
21236e94dc5SPeter Avalos  out:
213e9778795SPeter Avalos 	sshkey_free(cert);
214e9778795SPeter Avalos 	sshkey_free(public);
21536e94dc5SPeter Avalos 	free(certpath);
21636e94dc5SPeter Avalos 	free(comment);
21718de8d7fSPeter Avalos 
21818de8d7fSPeter Avalos 	return ret;
21918de8d7fSPeter Avalos }
22018de8d7fSPeter Avalos 
22118de8d7fSPeter Avalos /* Send a request to remove all identities. */
22218de8d7fSPeter Avalos static int
delete_all(int agent_fd,int qflag)223664f4763Szrj delete_all(int agent_fd, int qflag)
22418de8d7fSPeter Avalos {
22518de8d7fSPeter Avalos 	int ret = -1;
22618de8d7fSPeter Avalos 
227ce74bacaSMatthew Dillon 	/*
228ce74bacaSMatthew Dillon 	 * Since the agent might be forwarded, old or non-OpenSSH, when asked
229ce74bacaSMatthew Dillon 	 * to remove all keys, attempt to remove both protocol v.1 and v.2
230ce74bacaSMatthew Dillon 	 * keys.
231ce74bacaSMatthew Dillon 	 */
232e9778795SPeter Avalos 	if (ssh_remove_all_identities(agent_fd, 2) == 0)
23318de8d7fSPeter Avalos 		ret = 0;
234e9778795SPeter Avalos 	/* ignore error-code for ssh1 */
235e9778795SPeter Avalos 	ssh_remove_all_identities(agent_fd, 1);
23618de8d7fSPeter Avalos 
237664f4763Szrj 	if (ret != 0)
23818de8d7fSPeter Avalos 		fprintf(stderr, "Failed to remove all identities.\n");
239664f4763Szrj 	else if (!qflag)
240664f4763Szrj 		fprintf(stderr, "All identities removed.\n");
24118de8d7fSPeter Avalos 
24218de8d7fSPeter Avalos 	return ret;
24318de8d7fSPeter Avalos }
24418de8d7fSPeter Avalos 
24518de8d7fSPeter Avalos static int
add_file(int agent_fd,const char * filename,int key_only,int cert_only,int qflag,const char * skprovider,struct dest_constraint ** dest_constraints,size_t ndest_constraints)246*ba1276acSMatthew Dillon add_file(int agent_fd, const char *filename, int key_only, int cert_only,
247*ba1276acSMatthew Dillon     int qflag, const char *skprovider,
248*ba1276acSMatthew Dillon     struct dest_constraint **dest_constraints,
249ee116499SAntonio Huete Jimenez     size_t ndest_constraints)
25018de8d7fSPeter Avalos {
251e9778795SPeter Avalos 	struct sshkey *private, *cert;
25218de8d7fSPeter Avalos 	char *comment = NULL;
25399e85e0dSPeter Avalos 	char msg[1024], *certpath = NULL;
254e9778795SPeter Avalos 	int r, fd, ret = -1;
255664f4763Szrj 	size_t i;
256664f4763Szrj 	u_int32_t left;
257e9778795SPeter Avalos 	struct sshbuf *keyblob;
258664f4763Szrj 	struct ssh_identitylist *idlist;
25918de8d7fSPeter Avalos 
2601c188a7fSPeter Avalos 	if (strcmp(filename, "-") == 0) {
2611c188a7fSPeter Avalos 		fd = STDIN_FILENO;
2621c188a7fSPeter Avalos 		filename = "(stdin)";
2630cbfa66cSDaniel Fojt 	} else if ((fd = open(filename, O_RDONLY)) == -1) {
26418de8d7fSPeter Avalos 		perror(filename);
26518de8d7fSPeter Avalos 		return -1;
26618de8d7fSPeter Avalos 	}
26718de8d7fSPeter Avalos 
26818de8d7fSPeter Avalos 	/*
26918de8d7fSPeter Avalos 	 * Since we'll try to load a keyfile multiple times, permission errors
27018de8d7fSPeter Avalos 	 * will occur multiple times, so check perms first and bail if wrong.
27118de8d7fSPeter Avalos 	 */
2721c188a7fSPeter Avalos 	if (fd != STDIN_FILENO) {
273e9778795SPeter Avalos 		if (sshkey_perm_ok(fd, filename) != 0) {
27418de8d7fSPeter Avalos 			close(fd);
27518de8d7fSPeter Avalos 			return -1;
2761c188a7fSPeter Avalos 		}
2771c188a7fSPeter Avalos 	}
2780cbfa66cSDaniel Fojt 	if ((r = sshbuf_load_fd(fd, &keyblob)) != 0) {
279e9778795SPeter Avalos 		fprintf(stderr, "Error loading key \"%s\": %s\n",
280e9778795SPeter Avalos 		    filename, ssh_err(r));
281e9778795SPeter Avalos 		sshbuf_free(keyblob);
2821c188a7fSPeter Avalos 		close(fd);
2831c188a7fSPeter Avalos 		return -1;
2841c188a7fSPeter Avalos 	}
2851c188a7fSPeter Avalos 	close(fd);
28618de8d7fSPeter Avalos 
28718de8d7fSPeter Avalos 	/* At first, try empty passphrase */
288e9778795SPeter Avalos 	if ((r = sshkey_parse_private_fileblob(keyblob, "", &private,
289e9778795SPeter Avalos 	    &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
290e9778795SPeter Avalos 		fprintf(stderr, "Error loading key \"%s\": %s\n",
291e9778795SPeter Avalos 		    filename, ssh_err(r));
292e9778795SPeter Avalos 		goto fail_load;
293e9778795SPeter Avalos 	}
29436e94dc5SPeter Avalos 	/* try last */
29536e94dc5SPeter Avalos 	if (private == NULL && pass != NULL) {
296e9778795SPeter Avalos 		if ((r = sshkey_parse_private_fileblob(keyblob, pass, &private,
297e9778795SPeter Avalos 		    &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
298e9778795SPeter Avalos 			fprintf(stderr, "Error loading key \"%s\": %s\n",
299e9778795SPeter Avalos 			    filename, ssh_err(r));
300e9778795SPeter Avalos 			goto fail_load;
30136e94dc5SPeter Avalos 		}
302e9778795SPeter Avalos 	}
30318de8d7fSPeter Avalos 	if (private == NULL) {
30418de8d7fSPeter Avalos 		/* clear passphrase since it did not work */
30518de8d7fSPeter Avalos 		clear_pass();
306e9778795SPeter Avalos 		snprintf(msg, sizeof msg, "Enter passphrase for %s%s: ",
307e9778795SPeter Avalos 		    filename, confirm ? " (will confirm each use)" : "");
30818de8d7fSPeter Avalos 		for (;;) {
30918de8d7fSPeter Avalos 			pass = read_passphrase(msg, RP_ALLOW_STDIN);
310e9778795SPeter Avalos 			if (strcmp(pass, "") == 0)
311e9778795SPeter Avalos 				goto fail_load;
312e9778795SPeter Avalos 			if ((r = sshkey_parse_private_fileblob(keyblob, pass,
313e9778795SPeter Avalos 			    &private, &comment)) == 0)
314e9778795SPeter Avalos 				break;
315e9778795SPeter Avalos 			else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
316e9778795SPeter Avalos 				fprintf(stderr,
317e9778795SPeter Avalos 				    "Error loading key \"%s\": %s\n",
318e9778795SPeter Avalos 				    filename, ssh_err(r));
319e9778795SPeter Avalos  fail_load:
32018de8d7fSPeter Avalos 				clear_pass();
321e9778795SPeter Avalos 				sshbuf_free(keyblob);
32218de8d7fSPeter Avalos 				return -1;
32318de8d7fSPeter Avalos 			}
32418de8d7fSPeter Avalos 			clear_pass();
32518de8d7fSPeter Avalos 			snprintf(msg, sizeof msg,
326e9778795SPeter Avalos 			    "Bad passphrase, try again for %s%s: ", filename,
327e9778795SPeter Avalos 			    confirm ? " (will confirm each use)" : "");
32818de8d7fSPeter Avalos 		}
32918de8d7fSPeter Avalos 	}
330e9778795SPeter Avalos 	if (comment == NULL || *comment == '\0')
331e9778795SPeter Avalos 		comment = xstrdup(filename);
332e9778795SPeter Avalos 	sshbuf_free(keyblob);
33318de8d7fSPeter Avalos 
334664f4763Szrj 	/* For XMSS */
335664f4763Szrj 	if ((r = sshkey_set_filename(private, filename)) != 0) {
336664f4763Szrj 		fprintf(stderr, "Could not add filename to private key: %s (%s)\n",
337664f4763Szrj 		    filename, comment);
338664f4763Szrj 		goto out;
339664f4763Szrj 	}
340664f4763Szrj 	if (maxsign && minleft &&
341664f4763Szrj 	    (r = ssh_fetch_identitylist(agent_fd, &idlist)) == 0) {
342664f4763Szrj 		for (i = 0; i < idlist->nkeys; i++) {
343664f4763Szrj 			if (!sshkey_equal_public(idlist->keys[i], private))
344664f4763Szrj 				continue;
345664f4763Szrj 			left = sshkey_signatures_left(idlist->keys[i]);
346664f4763Szrj 			if (left < minleft) {
347664f4763Szrj 				fprintf(stderr,
348664f4763Szrj 				    "Only %d signatures left.\n", left);
349664f4763Szrj 				break;
350664f4763Szrj 			}
351664f4763Szrj 			fprintf(stderr, "Skipping update: ");
352664f4763Szrj 			if (left == minleft) {
353664f4763Szrj 				fprintf(stderr,
354664f4763Szrj 				    "required signatures left (%d).\n", left);
355664f4763Szrj 			} else {
356664f4763Szrj 				fprintf(stderr,
357664f4763Szrj 				    "more signatures left (%d) than"
358664f4763Szrj 				    " required (%d).\n", left, minleft);
359664f4763Szrj 			}
360664f4763Szrj 			ssh_free_identitylist(idlist);
361664f4763Szrj 			goto out;
362664f4763Szrj 		}
363664f4763Szrj 		ssh_free_identitylist(idlist);
364664f4763Szrj 	}
365664f4763Szrj 
36650a69bb5SSascha Wildner 	if (sshkey_is_sk(private)) {
36750a69bb5SSascha Wildner 		if (skprovider == NULL) {
36850a69bb5SSascha Wildner 			fprintf(stderr, "Cannot load FIDO key %s "
3690cbfa66cSDaniel Fojt 			    "without provider\n", filename);
3700cbfa66cSDaniel Fojt 			goto out;
3710cbfa66cSDaniel Fojt 		}
37250a69bb5SSascha Wildner 	} else {
37350a69bb5SSascha Wildner 		/* Don't send provider constraint for other keys */
37450a69bb5SSascha Wildner 		skprovider = NULL;
37550a69bb5SSascha Wildner 	}
3760cbfa66cSDaniel Fojt 
377*ba1276acSMatthew Dillon 	if (!cert_only &&
378*ba1276acSMatthew Dillon 	    (r = ssh_add_identity_constrained(agent_fd, private, comment,
379ee116499SAntonio Huete Jimenez 	    lifetime, confirm, maxsign, skprovider,
380ee116499SAntonio Huete Jimenez 	    dest_constraints, ndest_constraints)) == 0) {
38118de8d7fSPeter Avalos 		ret = 0;
382664f4763Szrj 		if (!qflag) {
383664f4763Szrj 			fprintf(stderr, "Identity added: %s (%s)\n",
384664f4763Szrj 			    filename, comment);
385664f4763Szrj 			if (lifetime != 0) {
38618de8d7fSPeter Avalos 				fprintf(stderr,
38750a69bb5SSascha Wildner 				    "Lifetime set to %d seconds\n", lifetime);
388664f4763Szrj 			}
389664f4763Szrj 			if (confirm != 0) {
390664f4763Szrj 				fprintf(stderr, "The user must confirm "
391664f4763Szrj 				    "each use of the key\n");
392664f4763Szrj 			}
393664f4763Szrj 		}
39418de8d7fSPeter Avalos 	} else {
395e9778795SPeter Avalos 		fprintf(stderr, "Could not add identity \"%s\": %s\n",
396e9778795SPeter Avalos 		    filename, ssh_err(r));
39718de8d7fSPeter Avalos 	}
39818de8d7fSPeter Avalos 
39999e85e0dSPeter Avalos 	/* Skip trying to load the cert if requested */
40099e85e0dSPeter Avalos 	if (key_only)
40199e85e0dSPeter Avalos 		goto out;
402856ea928SPeter Avalos 
403856ea928SPeter Avalos 	/* Now try to add the certificate flavour too */
404856ea928SPeter Avalos 	xasprintf(&certpath, "%s-cert.pub", filename);
405e9778795SPeter Avalos 	if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) {
406e9778795SPeter Avalos 		if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
407*ba1276acSMatthew Dillon 			error_r(r, "Failed to load certificate \"%s\"",
408*ba1276acSMatthew Dillon 			    certpath);
409856ea928SPeter Avalos 		goto out;
410e9778795SPeter Avalos 	}
411856ea928SPeter Avalos 
412e9778795SPeter Avalos 	if (!sshkey_equal_public(cert, private)) {
413856ea928SPeter Avalos 		error("Certificate %s does not match private key %s",
414856ea928SPeter Avalos 		    certpath, filename);
415e9778795SPeter Avalos 		sshkey_free(cert);
416856ea928SPeter Avalos 		goto out;
417856ea928SPeter Avalos 	}
418856ea928SPeter Avalos 
419856ea928SPeter Avalos 	/* Graft with private bits */
420e9778795SPeter Avalos 	if ((r = sshkey_to_certified(private)) != 0) {
42150a69bb5SSascha Wildner 		error_fr(r, "sshkey_to_certified");
422e9778795SPeter Avalos 		sshkey_free(cert);
423856ea928SPeter Avalos 		goto out;
424856ea928SPeter Avalos 	}
425e9778795SPeter Avalos 	if ((r = sshkey_cert_copy(cert, private)) != 0) {
42650a69bb5SSascha Wildner 		error_fr(r, "sshkey_cert_copy");
427e9778795SPeter Avalos 		sshkey_free(cert);
428e9778795SPeter Avalos 		goto out;
429e9778795SPeter Avalos 	}
430e9778795SPeter Avalos 	sshkey_free(cert);
431856ea928SPeter Avalos 
432e9778795SPeter Avalos 	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
433ee116499SAntonio Huete Jimenez 	    lifetime, confirm, maxsign, skprovider,
434ee116499SAntonio Huete Jimenez 	    dest_constraints, ndest_constraints)) != 0) {
43550a69bb5SSascha Wildner 		error_r(r, "Certificate %s (%s) add failed", certpath,
43650a69bb5SSascha Wildner 		    private->cert->key_id);
437e9778795SPeter Avalos 		goto out;
438856ea928SPeter Avalos 	}
439664f4763Szrj 	/* success */
440664f4763Szrj 	if (!qflag) {
441856ea928SPeter Avalos 		fprintf(stderr, "Certificate added: %s (%s)\n", certpath,
442856ea928SPeter Avalos 		    private->cert->key_id);
443664f4763Szrj 		if (lifetime != 0) {
44450a69bb5SSascha Wildner 			fprintf(stderr, "Lifetime set to %d seconds\n",
445664f4763Szrj 			    lifetime);
446664f4763Szrj 		}
447664f4763Szrj 		if (confirm != 0) {
448664f4763Szrj 			fprintf(stderr, "The user must confirm each use "
449664f4763Szrj 			    "of the key\n");
450664f4763Szrj 		}
451664f4763Szrj 	}
452664f4763Szrj 
453856ea928SPeter Avalos  out:
45436e94dc5SPeter Avalos 	free(certpath);
45536e94dc5SPeter Avalos 	free(comment);
456e9778795SPeter Avalos 	sshkey_free(private);
45718de8d7fSPeter Avalos 
45818de8d7fSPeter Avalos 	return ret;
45918de8d7fSPeter Avalos }
46018de8d7fSPeter Avalos 
46118de8d7fSPeter Avalos static int
update_card(int agent_fd,int add,const char * id,int qflag,int key_only,int cert_only,struct dest_constraint ** dest_constraints,size_t ndest_constraints,struct sshkey ** certs,size_t ncerts)462ee116499SAntonio Huete Jimenez update_card(int agent_fd, int add, const char *id, int qflag,
463*ba1276acSMatthew Dillon     int key_only, int cert_only,
464*ba1276acSMatthew Dillon     struct dest_constraint **dest_constraints, size_t ndest_constraints,
465*ba1276acSMatthew Dillon     struct sshkey **certs, size_t ncerts)
46618de8d7fSPeter Avalos {
46736e94dc5SPeter Avalos 	char *pin = NULL;
468e9778795SPeter Avalos 	int r, ret = -1;
46918de8d7fSPeter Avalos 
470*ba1276acSMatthew Dillon 	if (key_only)
471*ba1276acSMatthew Dillon 		ncerts = 0;
472*ba1276acSMatthew Dillon 
47336e94dc5SPeter Avalos 	if (add) {
47436e94dc5SPeter Avalos 		if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
47536e94dc5SPeter Avalos 		    RP_ALLOW_STDIN)) == NULL)
47618de8d7fSPeter Avalos 			return -1;
47736e94dc5SPeter Avalos 	}
47818de8d7fSPeter Avalos 
479e9778795SPeter Avalos 	if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin,
480*ba1276acSMatthew Dillon 	    lifetime, confirm, dest_constraints, ndest_constraints,
481*ba1276acSMatthew Dillon 	    cert_only, certs, ncerts)) == 0) {
482664f4763Szrj 		ret = 0;
483664f4763Szrj 		if (!qflag) {
48418de8d7fSPeter Avalos 			fprintf(stderr, "Card %s: %s\n",
48518de8d7fSPeter Avalos 			    add ? "added" : "removed", id);
486664f4763Szrj 		}
48718de8d7fSPeter Avalos 	} else {
488e9778795SPeter Avalos 		fprintf(stderr, "Could not %s card \"%s\": %s\n",
489e9778795SPeter Avalos 		    add ? "add" : "remove", id, ssh_err(r));
49018de8d7fSPeter Avalos 		ret = -1;
49118de8d7fSPeter Avalos 	}
49236e94dc5SPeter Avalos 	free(pin);
49318de8d7fSPeter Avalos 	return ret;
49418de8d7fSPeter Avalos }
49518de8d7fSPeter Avalos 
49618de8d7fSPeter Avalos static int
test_key(int agent_fd,const char * filename)497664f4763Szrj test_key(int agent_fd, const char *filename)
498664f4763Szrj {
499664f4763Szrj 	struct sshkey *key = NULL;
500664f4763Szrj 	u_char *sig = NULL;
501*ba1276acSMatthew Dillon 	const char *alg = NULL;
502664f4763Szrj 	size_t slen = 0;
503664f4763Szrj 	int r, ret = -1;
504664f4763Szrj 	char data[1024];
505664f4763Szrj 
506664f4763Szrj 	if ((r = sshkey_load_public(filename, &key, NULL)) != 0) {
50750a69bb5SSascha Wildner 		error_r(r, "Couldn't read public key %s", filename);
508664f4763Szrj 		return -1;
509664f4763Szrj 	}
510*ba1276acSMatthew Dillon 	if (sshkey_type_plain(key->type) == KEY_RSA)
511*ba1276acSMatthew Dillon 		alg = "rsa-sha2-256";
512664f4763Szrj 	arc4random_buf(data, sizeof(data));
513664f4763Szrj 	if ((r = ssh_agent_sign(agent_fd, key, &sig, &slen, data, sizeof(data),
514*ba1276acSMatthew Dillon 	    alg, 0)) != 0) {
51550a69bb5SSascha Wildner 		error_r(r, "Agent signature failed for %s", filename);
516664f4763Szrj 		goto done;
517664f4763Szrj 	}
518664f4763Szrj 	if ((r = sshkey_verify(key, sig, slen, data, sizeof(data),
519*ba1276acSMatthew Dillon 	    alg, 0, NULL)) != 0) {
52050a69bb5SSascha Wildner 		error_r(r, "Signature verification failed for %s", filename);
521664f4763Szrj 		goto done;
522664f4763Szrj 	}
523664f4763Szrj 	/* success */
524664f4763Szrj 	ret = 0;
525664f4763Szrj  done:
526664f4763Szrj 	free(sig);
527664f4763Szrj 	sshkey_free(key);
528664f4763Szrj 	return ret;
529664f4763Szrj }
530664f4763Szrj 
531664f4763Szrj static int
list_identities(int agent_fd,int do_fp)532e9778795SPeter Avalos list_identities(int agent_fd, int do_fp)
53318de8d7fSPeter Avalos {
534e9778795SPeter Avalos 	char *fp;
535ce74bacaSMatthew Dillon 	int r;
536e9778795SPeter Avalos 	struct ssh_identitylist *idlist;
537664f4763Szrj 	u_int32_t left;
538e9778795SPeter Avalos 	size_t i;
53918de8d7fSPeter Avalos 
540ce74bacaSMatthew Dillon 	if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) {
541e9778795SPeter Avalos 		if (r != SSH_ERR_AGENT_NO_IDENTITIES)
542ce74bacaSMatthew Dillon 			fprintf(stderr, "error fetching identities: %s\n",
543ce74bacaSMatthew Dillon 			    ssh_err(r));
544ce74bacaSMatthew Dillon 		else
545ce74bacaSMatthew Dillon 			printf("The agent has no identities.\n");
546ce74bacaSMatthew Dillon 		return -1;
547e9778795SPeter Avalos 	}
548e9778795SPeter Avalos 	for (i = 0; i < idlist->nkeys; i++) {
54918de8d7fSPeter Avalos 		if (do_fp) {
550e9778795SPeter Avalos 			fp = sshkey_fingerprint(idlist->keys[i],
551e9778795SPeter Avalos 			    fingerprint_hash, SSH_FP_DEFAULT);
552ce74bacaSMatthew Dillon 			printf("%u %s %s (%s)\n", sshkey_size(idlist->keys[i]),
553ce74bacaSMatthew Dillon 			    fp == NULL ? "(null)" : fp, idlist->comments[i],
554e9778795SPeter Avalos 			    sshkey_type(idlist->keys[i]));
55536e94dc5SPeter Avalos 			free(fp);
55618de8d7fSPeter Avalos 		} else {
557ce74bacaSMatthew Dillon 			if ((r = sshkey_write(idlist->keys[i], stdout)) != 0) {
558e9778795SPeter Avalos 				fprintf(stderr, "sshkey_write: %s\n",
559e9778795SPeter Avalos 				    ssh_err(r));
560e9778795SPeter Avalos 				continue;
56118de8d7fSPeter Avalos 			}
562664f4763Szrj 			fprintf(stdout, " %s", idlist->comments[i]);
563664f4763Szrj 			left = sshkey_signatures_left(idlist->keys[i]);
564664f4763Szrj 			if (left > 0)
565664f4763Szrj 				fprintf(stdout,
566664f4763Szrj 				    " [signatures left %d]", left);
567664f4763Szrj 			fprintf(stdout, "\n");
56818de8d7fSPeter Avalos 		}
56918de8d7fSPeter Avalos 	}
570e9778795SPeter Avalos 	ssh_free_identitylist(idlist);
57118de8d7fSPeter Avalos 	return 0;
57218de8d7fSPeter Avalos }
57318de8d7fSPeter Avalos 
57418de8d7fSPeter Avalos static int
lock_agent(int agent_fd,int lock)575e9778795SPeter Avalos lock_agent(int agent_fd, int lock)
57618de8d7fSPeter Avalos {
57718de8d7fSPeter Avalos 	char prompt[100], *p1, *p2;
578e9778795SPeter Avalos 	int r, passok = 1, ret = -1;
57918de8d7fSPeter Avalos 
58018de8d7fSPeter Avalos 	strlcpy(prompt, "Enter lock password: ", sizeof(prompt));
58118de8d7fSPeter Avalos 	p1 = read_passphrase(prompt, RP_ALLOW_STDIN);
58218de8d7fSPeter Avalos 	if (lock) {
58318de8d7fSPeter Avalos 		strlcpy(prompt, "Again: ", sizeof prompt);
58418de8d7fSPeter Avalos 		p2 = read_passphrase(prompt, RP_ALLOW_STDIN);
58518de8d7fSPeter Avalos 		if (strcmp(p1, p2) != 0) {
58618de8d7fSPeter Avalos 			fprintf(stderr, "Passwords do not match.\n");
58718de8d7fSPeter Avalos 			passok = 0;
58818de8d7fSPeter Avalos 		}
5890cbfa66cSDaniel Fojt 		freezero(p2, strlen(p2));
59018de8d7fSPeter Avalos 	}
591e9778795SPeter Avalos 	if (passok) {
592e9778795SPeter Avalos 		if ((r = ssh_lock_agent(agent_fd, lock, p1)) == 0) {
59318de8d7fSPeter Avalos 			fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un");
59418de8d7fSPeter Avalos 			ret = 0;
595e9778795SPeter Avalos 		} else {
596e9778795SPeter Avalos 			fprintf(stderr, "Failed to %slock agent: %s\n",
597e9778795SPeter Avalos 			    lock ? "" : "un", ssh_err(r));
598e9778795SPeter Avalos 		}
599e9778795SPeter Avalos 	}
6000cbfa66cSDaniel Fojt 	freezero(p1, strlen(p1));
60118de8d7fSPeter Avalos 	return (ret);
60218de8d7fSPeter Avalos }
60318de8d7fSPeter Avalos 
60418de8d7fSPeter Avalos static int
load_resident_keys(int agent_fd,const char * skprovider,int qflag,struct dest_constraint ** dest_constraints,size_t ndest_constraints)605ee116499SAntonio Huete Jimenez load_resident_keys(int agent_fd, const char *skprovider, int qflag,
606ee116499SAntonio Huete Jimenez     struct dest_constraint **dest_constraints, size_t ndest_constraints)
6070cbfa66cSDaniel Fojt {
608ee116499SAntonio Huete Jimenez 	struct sshsk_resident_key **srks;
609ee116499SAntonio Huete Jimenez 	size_t nsrks, i;
610ee116499SAntonio Huete Jimenez 	struct sshkey *key;
6110cbfa66cSDaniel Fojt 	int r, ok = 0;
6120cbfa66cSDaniel Fojt 	char *fp;
6130cbfa66cSDaniel Fojt 
6140cbfa66cSDaniel Fojt 	pass = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN);
615ee116499SAntonio Huete Jimenez 	if ((r = sshsk_load_resident(skprovider, NULL, pass, 0,
616ee116499SAntonio Huete Jimenez 	    &srks, &nsrks)) != 0) {
61750a69bb5SSascha Wildner 		error_r(r, "Unable to load resident keys");
6180cbfa66cSDaniel Fojt 		return r;
6190cbfa66cSDaniel Fojt 	}
620ee116499SAntonio Huete Jimenez 	for (i = 0; i < nsrks; i++) {
621ee116499SAntonio Huete Jimenez 		key = srks[i]->key;
622ee116499SAntonio Huete Jimenez 		if ((fp = sshkey_fingerprint(key,
6230cbfa66cSDaniel Fojt 		    fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
62450a69bb5SSascha Wildner 			fatal_f("sshkey_fingerprint failed");
625ee116499SAntonio Huete Jimenez 		if ((r = ssh_add_identity_constrained(agent_fd, key, "",
626ee116499SAntonio Huete Jimenez 		    lifetime, confirm, maxsign, skprovider,
627ee116499SAntonio Huete Jimenez 		    dest_constraints, ndest_constraints)) != 0) {
6280cbfa66cSDaniel Fojt 			error("Unable to add key %s %s",
629ee116499SAntonio Huete Jimenez 			    sshkey_type(key), fp);
6300cbfa66cSDaniel Fojt 			free(fp);
6310cbfa66cSDaniel Fojt 			ok = r;
6320cbfa66cSDaniel Fojt 			continue;
6330cbfa66cSDaniel Fojt 		}
6340cbfa66cSDaniel Fojt 		if (ok == 0)
6350cbfa66cSDaniel Fojt 			ok = 1;
6360cbfa66cSDaniel Fojt 		if (!qflag) {
6370cbfa66cSDaniel Fojt 			fprintf(stderr, "Resident identity added: %s %s\n",
638ee116499SAntonio Huete Jimenez 			    sshkey_type(key), fp);
6390cbfa66cSDaniel Fojt 			if (lifetime != 0) {
6400cbfa66cSDaniel Fojt 				fprintf(stderr,
64150a69bb5SSascha Wildner 				    "Lifetime set to %d seconds\n", lifetime);
6420cbfa66cSDaniel Fojt 			}
6430cbfa66cSDaniel Fojt 			if (confirm != 0) {
6440cbfa66cSDaniel Fojt 				fprintf(stderr, "The user must confirm "
6450cbfa66cSDaniel Fojt 				    "each use of the key\n");
6460cbfa66cSDaniel Fojt 			}
6470cbfa66cSDaniel Fojt 		}
6480cbfa66cSDaniel Fojt 		free(fp);
6490cbfa66cSDaniel Fojt 	}
650ee116499SAntonio Huete Jimenez 	sshsk_free_resident_keys(srks, nsrks);
651ee116499SAntonio Huete Jimenez 	if (nsrks == 0)
6520cbfa66cSDaniel Fojt 		return SSH_ERR_KEY_NOT_FOUND;
6530cbfa66cSDaniel Fojt 	return ok == 1 ? 0 : ok;
6540cbfa66cSDaniel Fojt }
6550cbfa66cSDaniel Fojt 
6560cbfa66cSDaniel Fojt static int
do_file(int agent_fd,int deleting,int key_only,int cert_only,char * file,int qflag,const char * skprovider,struct dest_constraint ** dest_constraints,size_t ndest_constraints)657*ba1276acSMatthew Dillon do_file(int agent_fd, int deleting, int key_only, int cert_only,
658*ba1276acSMatthew Dillon     char *file, int qflag, const char *skprovider,
659*ba1276acSMatthew Dillon     struct dest_constraint **dest_constraints, size_t ndest_constraints)
66018de8d7fSPeter Avalos {
66118de8d7fSPeter Avalos 	if (deleting) {
662*ba1276acSMatthew Dillon 		if (delete_file(agent_fd, file, key_only,
663*ba1276acSMatthew Dillon 		    cert_only, qflag) == -1)
66418de8d7fSPeter Avalos 			return -1;
66518de8d7fSPeter Avalos 	} else {
666*ba1276acSMatthew Dillon 		if (add_file(agent_fd, file, key_only, cert_only, qflag,
667*ba1276acSMatthew Dillon 		    skprovider, dest_constraints, ndest_constraints) == -1)
66818de8d7fSPeter Avalos 			return -1;
66918de8d7fSPeter Avalos 	}
67018de8d7fSPeter Avalos 	return 0;
67118de8d7fSPeter Avalos }
67218de8d7fSPeter Avalos 
673ee116499SAntonio Huete Jimenez /* Append string 's' to a NULL-terminated array of strings */
674ee116499SAntonio Huete Jimenez static void
stringlist_append(char *** listp,const char * s)675ee116499SAntonio Huete Jimenez stringlist_append(char ***listp, const char *s)
676ee116499SAntonio Huete Jimenez {
677ee116499SAntonio Huete Jimenez 	size_t i = 0;
678ee116499SAntonio Huete Jimenez 
679ee116499SAntonio Huete Jimenez 	if (*listp == NULL)
680ee116499SAntonio Huete Jimenez 		*listp = xcalloc(2, sizeof(**listp));
681ee116499SAntonio Huete Jimenez 	else {
682ee116499SAntonio Huete Jimenez 		for (i = 0; (*listp)[i] != NULL; i++)
683ee116499SAntonio Huete Jimenez 			; /* count */
684ee116499SAntonio Huete Jimenez 		*listp = xrecallocarray(*listp, i + 1, i + 2, sizeof(**listp));
685ee116499SAntonio Huete Jimenez 	}
686ee116499SAntonio Huete Jimenez 	(*listp)[i] = xstrdup(s);
687ee116499SAntonio Huete Jimenez }
688ee116499SAntonio Huete Jimenez 
689ee116499SAntonio Huete Jimenez static void
parse_dest_constraint_hop(const char * s,struct dest_constraint_hop * dch,char ** hostkey_files)690ee116499SAntonio Huete Jimenez parse_dest_constraint_hop(const char *s, struct dest_constraint_hop *dch,
691ee116499SAntonio Huete Jimenez     char **hostkey_files)
692ee116499SAntonio Huete Jimenez {
693ee116499SAntonio Huete Jimenez 	char *user = NULL, *host, *os, *path;
694ee116499SAntonio Huete Jimenez 	size_t i;
695ee116499SAntonio Huete Jimenez 	struct hostkeys *hostkeys;
696ee116499SAntonio Huete Jimenez 	const struct hostkey_entry *hke;
697ee116499SAntonio Huete Jimenez 	int r, want_ca;
698ee116499SAntonio Huete Jimenez 
699ee116499SAntonio Huete Jimenez 	memset(dch, '\0', sizeof(*dch));
700ee116499SAntonio Huete Jimenez 	os = xstrdup(s);
701ee116499SAntonio Huete Jimenez 	if ((host = strchr(os, '@')) == NULL)
702ee116499SAntonio Huete Jimenez 		host = os;
703ee116499SAntonio Huete Jimenez 	else {
704ee116499SAntonio Huete Jimenez 		*host++ = '\0';
705ee116499SAntonio Huete Jimenez 		user = os;
706ee116499SAntonio Huete Jimenez 	}
707ee116499SAntonio Huete Jimenez 	cleanhostname(host);
708ee116499SAntonio Huete Jimenez 	/* Trivial case: username@ (all hosts) */
709ee116499SAntonio Huete Jimenez 	if (*host == '\0') {
710ee116499SAntonio Huete Jimenez 		if (user == NULL) {
711ee116499SAntonio Huete Jimenez 			fatal("Invalid key destination constraint \"%s\": "
712ee116499SAntonio Huete Jimenez 			    "does not specify user or host", s);
713ee116499SAntonio Huete Jimenez 		}
714ee116499SAntonio Huete Jimenez 		dch->user = xstrdup(user);
715ee116499SAntonio Huete Jimenez 		/* other fields left blank */
716ee116499SAntonio Huete Jimenez 		free(os);
717ee116499SAntonio Huete Jimenez 		return;
718ee116499SAntonio Huete Jimenez 	}
719ee116499SAntonio Huete Jimenez 	if (hostkey_files == NULL)
720ee116499SAntonio Huete Jimenez 		fatal_f("no hostkey files");
721ee116499SAntonio Huete Jimenez 	/* Otherwise we need to look up the keys for this hostname */
722ee116499SAntonio Huete Jimenez 	hostkeys = init_hostkeys();
723ee116499SAntonio Huete Jimenez 	for (i = 0; hostkey_files[i]; i++) {
724ee116499SAntonio Huete Jimenez 		path = tilde_expand_filename(hostkey_files[i], getuid());
725ee116499SAntonio Huete Jimenez 		debug2_f("looking up host keys for \"%s\" in %s", host, path);
726ee116499SAntonio Huete Jimenez                 load_hostkeys(hostkeys, host, path, 0);
727ee116499SAntonio Huete Jimenez 		free(path);
728ee116499SAntonio Huete Jimenez 	}
729ee116499SAntonio Huete Jimenez 	dch->user = user == NULL ? NULL : xstrdup(user);
730ee116499SAntonio Huete Jimenez 	dch->hostname = xstrdup(host);
731ee116499SAntonio Huete Jimenez 	for (i = 0; i < hostkeys->num_entries; i++) {
732ee116499SAntonio Huete Jimenez 		hke = hostkeys->entries + i;
733ee116499SAntonio Huete Jimenez 		want_ca = hke->marker == MRK_CA;
734ee116499SAntonio Huete Jimenez 		if (hke->marker != MRK_NONE && !want_ca)
735ee116499SAntonio Huete Jimenez 			continue;
736ee116499SAntonio Huete Jimenez 		debug3_f("%s%s%s: adding %s %skey from %s:%lu as key %u",
737ee116499SAntonio Huete Jimenez 		    user == NULL ? "": user, user == NULL ? "" : "@",
738ee116499SAntonio Huete Jimenez 		    host, sshkey_type(hke->key), want_ca ? "CA " : "",
739ee116499SAntonio Huete Jimenez 		    hke->file, hke->line, dch->nkeys);
740ee116499SAntonio Huete Jimenez 		dch->keys = xrecallocarray(dch->keys, dch->nkeys,
741ee116499SAntonio Huete Jimenez 		    dch->nkeys + 1, sizeof(*dch->keys));
742ee116499SAntonio Huete Jimenez 		dch->key_is_ca = xrecallocarray(dch->key_is_ca, dch->nkeys,
743ee116499SAntonio Huete Jimenez 		    dch->nkeys + 1, sizeof(*dch->key_is_ca));
744ee116499SAntonio Huete Jimenez 		if ((r = sshkey_from_private(hke->key,
745ee116499SAntonio Huete Jimenez 		    &(dch->keys[dch->nkeys]))) != 0)
746ee116499SAntonio Huete Jimenez 			fatal_fr(r, "sshkey_from_private");
747ee116499SAntonio Huete Jimenez 		dch->key_is_ca[dch->nkeys] = want_ca;
748ee116499SAntonio Huete Jimenez 		dch->nkeys++;
749ee116499SAntonio Huete Jimenez 	}
750ee116499SAntonio Huete Jimenez 	if (dch->nkeys == 0)
751ee116499SAntonio Huete Jimenez 		fatal("No host keys found for destination \"%s\"", host);
752ee116499SAntonio Huete Jimenez 	free_hostkeys(hostkeys);
753ee116499SAntonio Huete Jimenez 	free(os);
754ee116499SAntonio Huete Jimenez 	return;
755ee116499SAntonio Huete Jimenez }
756ee116499SAntonio Huete Jimenez 
757ee116499SAntonio Huete Jimenez static void
parse_dest_constraint(const char * s,struct dest_constraint *** dcp,size_t * ndcp,char ** hostkey_files)758ee116499SAntonio Huete Jimenez parse_dest_constraint(const char *s, struct dest_constraint ***dcp,
759ee116499SAntonio Huete Jimenez     size_t *ndcp, char **hostkey_files)
760ee116499SAntonio Huete Jimenez {
761ee116499SAntonio Huete Jimenez 	struct dest_constraint *dc;
762ee116499SAntonio Huete Jimenez 	char *os, *cp;
763ee116499SAntonio Huete Jimenez 
764ee116499SAntonio Huete Jimenez 	dc = xcalloc(1, sizeof(*dc));
765ee116499SAntonio Huete Jimenez 	os = xstrdup(s);
766ee116499SAntonio Huete Jimenez 	if ((cp = strchr(os, '>')) == NULL) {
767ee116499SAntonio Huete Jimenez 		/* initial hop; no 'from' hop specified */
768ee116499SAntonio Huete Jimenez 		parse_dest_constraint_hop(os, &dc->to, hostkey_files);
769ee116499SAntonio Huete Jimenez 	} else {
770ee116499SAntonio Huete Jimenez 		/* two hops specified */
771ee116499SAntonio Huete Jimenez 		*(cp++) = '\0';
772ee116499SAntonio Huete Jimenez 		parse_dest_constraint_hop(os, &dc->from, hostkey_files);
773ee116499SAntonio Huete Jimenez 		parse_dest_constraint_hop(cp, &dc->to, hostkey_files);
774ee116499SAntonio Huete Jimenez 		if (dc->from.user != NULL) {
775ee116499SAntonio Huete Jimenez 			fatal("Invalid key constraint %s: cannot specify "
776ee116499SAntonio Huete Jimenez 			    "user on 'from' host", os);
777ee116499SAntonio Huete Jimenez 		}
778ee116499SAntonio Huete Jimenez 	}
779ee116499SAntonio Huete Jimenez 	/* XXX eliminate or error on duplicates */
780ee116499SAntonio Huete Jimenez 	debug2_f("constraint %zu: %s%s%s (%u keys) > %s%s%s (%u keys)", *ndcp,
781ee116499SAntonio Huete Jimenez 	    dc->from.user ? dc->from.user : "", dc->from.user ? "@" : "",
782ee116499SAntonio Huete Jimenez 	    dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc->from.nkeys,
783ee116499SAntonio Huete Jimenez 	    dc->to.user ? dc->to.user : "", dc->to.user ? "@" : "",
784ee116499SAntonio Huete Jimenez 	    dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys);
785ee116499SAntonio Huete Jimenez 	*dcp = xrecallocarray(*dcp, *ndcp, *ndcp + 1, sizeof(**dcp));
786ee116499SAntonio Huete Jimenez 	(*dcp)[(*ndcp)++] = dc;
787ee116499SAntonio Huete Jimenez 	free(os);
788ee116499SAntonio Huete Jimenez }
789ee116499SAntonio Huete Jimenez 
790ee116499SAntonio Huete Jimenez 
79118de8d7fSPeter Avalos static void
usage(void)79218de8d7fSPeter Avalos usage(void)
79318de8d7fSPeter Avalos {
7940cbfa66cSDaniel Fojt 	fprintf(stderr,
795*ba1276acSMatthew Dillon "usage: ssh-add [-CcDdKkLlqvXx] [-E fingerprint_hash] [-H hostkey_file]\n"
796ee116499SAntonio Huete Jimenez "               [-h destination_constraint] [-S provider] [-t life]\n"
7970cbfa66cSDaniel Fojt #ifdef WITH_XMSS
7980cbfa66cSDaniel Fojt "               [-M maxsign] [-m minleft]\n"
7990cbfa66cSDaniel Fojt #endif
8000cbfa66cSDaniel Fojt "               [file ...]\n"
801*ba1276acSMatthew Dillon "       ssh-add -s pkcs11 [-Cv] [certificate ...]\n"
8020cbfa66cSDaniel Fojt "       ssh-add -e pkcs11\n"
8030cbfa66cSDaniel Fojt "       ssh-add -T pubkey ...\n"
8040cbfa66cSDaniel Fojt 	);
80518de8d7fSPeter Avalos }
80618de8d7fSPeter Avalos 
80718de8d7fSPeter Avalos int
main(int argc,char ** argv)80818de8d7fSPeter Avalos main(int argc, char **argv)
80918de8d7fSPeter Avalos {
81018de8d7fSPeter Avalos 	extern char *optarg;
81118de8d7fSPeter Avalos 	extern int optind;
812e9778795SPeter Avalos 	int agent_fd;
8130cbfa66cSDaniel Fojt 	char *pkcs11provider = NULL, *skprovider = NULL;
814ee116499SAntonio Huete Jimenez 	char **dest_constraint_strings = NULL, **hostkey_files = NULL;
815*ba1276acSMatthew Dillon 	int r, i, ch, deleting = 0, ret = 0, key_only = 0, cert_only = 0;
816*ba1276acSMatthew Dillon 	int do_download = 0, xflag = 0, lflag = 0, Dflag = 0;
817*ba1276acSMatthew Dillon 	int qflag = 0, Tflag = 0;
818664f4763Szrj 	SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
819664f4763Szrj 	LogLevel log_level = SYSLOG_LEVEL_INFO;
820*ba1276acSMatthew Dillon 	struct sshkey *k, **certs = NULL;
821ee116499SAntonio Huete Jimenez 	struct dest_constraint **dest_constraints = NULL;
822*ba1276acSMatthew Dillon 	size_t ndest_constraints = 0, ncerts = 0;
82318de8d7fSPeter Avalos 
82418de8d7fSPeter Avalos 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
82518de8d7fSPeter Avalos 	sanitise_stdfd();
82618de8d7fSPeter Avalos 
82718de8d7fSPeter Avalos 	__progname = ssh_get_progname(argv[0]);
82818de8d7fSPeter Avalos 	seed_rng();
82918de8d7fSPeter Avalos 
830664f4763Szrj 	log_init(__progname, log_level, log_facility, 1);
83118de8d7fSPeter Avalos 
832e9778795SPeter Avalos 	setvbuf(stdout, NULL, _IOLBF, 0);
83336e94dc5SPeter Avalos 
834e9778795SPeter Avalos 	/* First, get a connection to the authentication agent. */
835e9778795SPeter Avalos 	switch (r = ssh_get_authentication_socket(&agent_fd)) {
836e9778795SPeter Avalos 	case 0:
837e9778795SPeter Avalos 		break;
838e9778795SPeter Avalos 	case SSH_ERR_AGENT_NOT_PRESENT:
839e9778795SPeter Avalos 		fprintf(stderr, "Could not open a connection to your "
840e9778795SPeter Avalos 		    "authentication agent.\n");
841e9778795SPeter Avalos 		exit(2);
842e9778795SPeter Avalos 	default:
843e9778795SPeter Avalos 		fprintf(stderr, "Error connecting to agent: %s\n", ssh_err(r));
84418de8d7fSPeter Avalos 		exit(2);
84518de8d7fSPeter Avalos 	}
846e9778795SPeter Avalos 
8470cbfa66cSDaniel Fojt 	skprovider = getenv("SSH_SK_PROVIDER");
8480cbfa66cSDaniel Fojt 
849*ba1276acSMatthew Dillon 	while ((ch = getopt(argc, argv, "vkKlLCcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) {
85018de8d7fSPeter Avalos 		switch (ch) {
851664f4763Szrj 		case 'v':
852664f4763Szrj 			if (log_level == SYSLOG_LEVEL_INFO)
853664f4763Szrj 				log_level = SYSLOG_LEVEL_DEBUG1;
854664f4763Szrj 			else if (log_level < SYSLOG_LEVEL_DEBUG3)
855664f4763Szrj 				log_level++;
856664f4763Szrj 			break;
857e9778795SPeter Avalos 		case 'E':
858e9778795SPeter Avalos 			fingerprint_hash = ssh_digest_alg_by_name(optarg);
859e9778795SPeter Avalos 			if (fingerprint_hash == -1)
860e9778795SPeter Avalos 				fatal("Invalid hash algorithm \"%s\"", optarg);
861e9778795SPeter Avalos 			break;
862ee116499SAntonio Huete Jimenez 		case 'H':
863ee116499SAntonio Huete Jimenez 			stringlist_append(&hostkey_files, optarg);
864ee116499SAntonio Huete Jimenez 			break;
865ee116499SAntonio Huete Jimenez 		case 'h':
866ee116499SAntonio Huete Jimenez 			stringlist_append(&dest_constraint_strings, optarg);
867ee116499SAntonio Huete Jimenez 			break;
86899e85e0dSPeter Avalos 		case 'k':
86999e85e0dSPeter Avalos 			key_only = 1;
87099e85e0dSPeter Avalos 			break;
871*ba1276acSMatthew Dillon 		case 'C':
872*ba1276acSMatthew Dillon 			cert_only = 1;
873*ba1276acSMatthew Dillon 			break;
8740cbfa66cSDaniel Fojt 		case 'K':
8750cbfa66cSDaniel Fojt 			do_download = 1;
8760cbfa66cSDaniel Fojt 			break;
87718de8d7fSPeter Avalos 		case 'l':
87818de8d7fSPeter Avalos 		case 'L':
879e9778795SPeter Avalos 			if (lflag != 0)
880e9778795SPeter Avalos 				fatal("-%c flag already specified", lflag);
881e9778795SPeter Avalos 			lflag = ch;
882e9778795SPeter Avalos 			break;
88318de8d7fSPeter Avalos 		case 'x':
88418de8d7fSPeter Avalos 		case 'X':
885e9778795SPeter Avalos 			if (xflag != 0)
886e9778795SPeter Avalos 				fatal("-%c flag already specified", xflag);
887e9778795SPeter Avalos 			xflag = ch;
888e9778795SPeter Avalos 			break;
88918de8d7fSPeter Avalos 		case 'c':
89018de8d7fSPeter Avalos 			confirm = 1;
89118de8d7fSPeter Avalos 			break;
892664f4763Szrj 		case 'm':
893*ba1276acSMatthew Dillon 			minleft = (u_int)strtonum(optarg, 1, UINT_MAX, NULL);
894664f4763Szrj 			if (minleft == 0) {
895664f4763Szrj 				usage();
896664f4763Szrj 				ret = 1;
897664f4763Szrj 				goto done;
898664f4763Szrj 			}
899664f4763Szrj 			break;
900664f4763Szrj 		case 'M':
901*ba1276acSMatthew Dillon 			maxsign = (u_int)strtonum(optarg, 1, UINT_MAX, NULL);
902664f4763Szrj 			if (maxsign == 0) {
903664f4763Szrj 				usage();
904664f4763Szrj 				ret = 1;
905664f4763Szrj 				goto done;
906664f4763Szrj 			}
907664f4763Szrj 			break;
90818de8d7fSPeter Avalos 		case 'd':
90918de8d7fSPeter Avalos 			deleting = 1;
91018de8d7fSPeter Avalos 			break;
91118de8d7fSPeter Avalos 		case 'D':
912e9778795SPeter Avalos 			Dflag = 1;
913e9778795SPeter Avalos 			break;
91418de8d7fSPeter Avalos 		case 's':
915856ea928SPeter Avalos 			pkcs11provider = optarg;
91618de8d7fSPeter Avalos 			break;
9170cbfa66cSDaniel Fojt 		case 'S':
9180cbfa66cSDaniel Fojt 			skprovider = optarg;
9190cbfa66cSDaniel Fojt 			break;
92018de8d7fSPeter Avalos 		case 'e':
92118de8d7fSPeter Avalos 			deleting = 1;
922856ea928SPeter Avalos 			pkcs11provider = optarg;
92318de8d7fSPeter Avalos 			break;
92418de8d7fSPeter Avalos 		case 't':
9250cbfa66cSDaniel Fojt 			if ((lifetime = convtime(optarg)) == -1 ||
9260cbfa66cSDaniel Fojt 			    lifetime < 0 || (u_long)lifetime > UINT32_MAX) {
92718de8d7fSPeter Avalos 				fprintf(stderr, "Invalid lifetime\n");
92818de8d7fSPeter Avalos 				ret = 1;
92918de8d7fSPeter Avalos 				goto done;
93018de8d7fSPeter Avalos 			}
93118de8d7fSPeter Avalos 			break;
932ce74bacaSMatthew Dillon 		case 'q':
933ce74bacaSMatthew Dillon 			qflag = 1;
934ce74bacaSMatthew Dillon 			break;
935664f4763Szrj 		case 'T':
936664f4763Szrj 			Tflag = 1;
937664f4763Szrj 			break;
93818de8d7fSPeter Avalos 		default:
93918de8d7fSPeter Avalos 			usage();
94018de8d7fSPeter Avalos 			ret = 1;
94118de8d7fSPeter Avalos 			goto done;
94218de8d7fSPeter Avalos 		}
94318de8d7fSPeter Avalos 	}
944664f4763Szrj 	log_init(__progname, log_level, log_facility, 1);
945e9778795SPeter Avalos 
946e9778795SPeter Avalos 	if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1)
947e9778795SPeter Avalos 		fatal("Invalid combination of actions");
948e9778795SPeter Avalos 	else if (xflag) {
949e9778795SPeter Avalos 		if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1)
950e9778795SPeter Avalos 			ret = 1;
951e9778795SPeter Avalos 		goto done;
952e9778795SPeter Avalos 	} else if (lflag) {
953e9778795SPeter Avalos 		if (list_identities(agent_fd, lflag == 'l' ? 1 : 0) == -1)
954e9778795SPeter Avalos 			ret = 1;
955e9778795SPeter Avalos 		goto done;
956e9778795SPeter Avalos 	} else if (Dflag) {
957664f4763Szrj 		if (delete_all(agent_fd, qflag) == -1)
958e9778795SPeter Avalos 			ret = 1;
959e9778795SPeter Avalos 		goto done;
960e9778795SPeter Avalos 	}
961e9778795SPeter Avalos 
9620cbfa66cSDaniel Fojt #ifdef ENABLE_SK_INTERNAL
9630cbfa66cSDaniel Fojt 	if (skprovider == NULL)
9640cbfa66cSDaniel Fojt 		skprovider = "internal";
9650cbfa66cSDaniel Fojt #endif
9660cbfa66cSDaniel Fojt 
967ee116499SAntonio Huete Jimenez 	if (hostkey_files == NULL) {
968ee116499SAntonio Huete Jimenez 		/* use defaults from readconf.c */
969ee116499SAntonio Huete Jimenez 		stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE);
970ee116499SAntonio Huete Jimenez 		stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE2);
971ee116499SAntonio Huete Jimenez 		stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE);
972ee116499SAntonio Huete Jimenez 		stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE2);
973ee116499SAntonio Huete Jimenez 	}
974ee116499SAntonio Huete Jimenez 	if (dest_constraint_strings != NULL) {
975ee116499SAntonio Huete Jimenez 		for (i = 0; dest_constraint_strings[i] != NULL; i++) {
976ee116499SAntonio Huete Jimenez 			parse_dest_constraint(dest_constraint_strings[i],
977ee116499SAntonio Huete Jimenez 			  &dest_constraints, &ndest_constraints, hostkey_files);
978ee116499SAntonio Huete Jimenez 		}
979ee116499SAntonio Huete Jimenez 	}
980ee116499SAntonio Huete Jimenez 
98118de8d7fSPeter Avalos 	argc -= optind;
98218de8d7fSPeter Avalos 	argv += optind;
983664f4763Szrj 	if (Tflag) {
984664f4763Szrj 		if (argc <= 0)
985664f4763Szrj 			fatal("no keys to test");
986664f4763Szrj 		for (r = i = 0; i < argc; i++)
987664f4763Szrj 			r |= test_key(agent_fd, argv[i]);
988664f4763Szrj 		ret = r == 0 ? 0 : 1;
989664f4763Szrj 		goto done;
990664f4763Szrj 	}
991856ea928SPeter Avalos 	if (pkcs11provider != NULL) {
992*ba1276acSMatthew Dillon 		for (i = 0; i < argc; i++) {
993*ba1276acSMatthew Dillon 			if ((r = sshkey_load_public(argv[i], &k, NULL)) != 0)
994*ba1276acSMatthew Dillon 				fatal_fr(r, "load certificate %s", argv[i]);
995*ba1276acSMatthew Dillon 			certs = xrecallocarray(certs, ncerts, ncerts + 1,
996*ba1276acSMatthew Dillon 			    sizeof(*certs));
997*ba1276acSMatthew Dillon 			debug2("%s: %s", argv[i], sshkey_ssh_name(k));
998*ba1276acSMatthew Dillon 			certs[ncerts++] = k;
999*ba1276acSMatthew Dillon 		}
1000*ba1276acSMatthew Dillon 		debug2_f("loaded %zu certificates", ncerts);
1001664f4763Szrj 		if (update_card(agent_fd, !deleting, pkcs11provider,
1002*ba1276acSMatthew Dillon 		    qflag, key_only, cert_only,
1003*ba1276acSMatthew Dillon 		    dest_constraints, ndest_constraints,
1004*ba1276acSMatthew Dillon 		    certs, ncerts) == -1)
100518de8d7fSPeter Avalos 			ret = 1;
100618de8d7fSPeter Avalos 		goto done;
100718de8d7fSPeter Avalos 	}
10080cbfa66cSDaniel Fojt 	if (do_download) {
10090cbfa66cSDaniel Fojt 		if (skprovider == NULL)
10100cbfa66cSDaniel Fojt 			fatal("Cannot download keys without provider");
1011ee116499SAntonio Huete Jimenez 		if (load_resident_keys(agent_fd, skprovider, qflag,
1012ee116499SAntonio Huete Jimenez 		    dest_constraints, ndest_constraints) != 0)
10130cbfa66cSDaniel Fojt 			ret = 1;
10140cbfa66cSDaniel Fojt 		goto done;
10150cbfa66cSDaniel Fojt 	}
101618de8d7fSPeter Avalos 	if (argc == 0) {
1017e9778795SPeter Avalos 		char buf[PATH_MAX];
101818de8d7fSPeter Avalos 		struct passwd *pw;
101918de8d7fSPeter Avalos 		struct stat st;
102018de8d7fSPeter Avalos 		int count = 0;
102118de8d7fSPeter Avalos 
102218de8d7fSPeter Avalos 		if ((pw = getpwuid(getuid())) == NULL) {
102318de8d7fSPeter Avalos 			fprintf(stderr, "No user found with uid %u\n",
102418de8d7fSPeter Avalos 			    (u_int)getuid());
102518de8d7fSPeter Avalos 			ret = 1;
102618de8d7fSPeter Avalos 			goto done;
102718de8d7fSPeter Avalos 		}
102818de8d7fSPeter Avalos 
102918de8d7fSPeter Avalos 		for (i = 0; default_files[i]; i++) {
103018de8d7fSPeter Avalos 			snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir,
103118de8d7fSPeter Avalos 			    default_files[i]);
10320cbfa66cSDaniel Fojt 			if (stat(buf, &st) == -1)
103318de8d7fSPeter Avalos 				continue;
1034*ba1276acSMatthew Dillon 			if (do_file(agent_fd, deleting, key_only, cert_only,
1035*ba1276acSMatthew Dillon 			    buf, qflag, skprovider,
1036ee116499SAntonio Huete Jimenez 			    dest_constraints, ndest_constraints) == -1)
103718de8d7fSPeter Avalos 				ret = 1;
103818de8d7fSPeter Avalos 			else
103918de8d7fSPeter Avalos 				count++;
104018de8d7fSPeter Avalos 		}
104118de8d7fSPeter Avalos 		if (count == 0)
104218de8d7fSPeter Avalos 			ret = 1;
104318de8d7fSPeter Avalos 	} else {
104418de8d7fSPeter Avalos 		for (i = 0; i < argc; i++) {
1045*ba1276acSMatthew Dillon 			if (do_file(agent_fd, deleting, key_only, cert_only,
1046ee116499SAntonio Huete Jimenez 			    argv[i], qflag, skprovider,
1047ee116499SAntonio Huete Jimenez 			    dest_constraints, ndest_constraints) == -1)
104818de8d7fSPeter Avalos 				ret = 1;
104918de8d7fSPeter Avalos 		}
105018de8d7fSPeter Avalos 	}
105118de8d7fSPeter Avalos done:
10520cbfa66cSDaniel Fojt 	clear_pass();
1053e9778795SPeter Avalos 	ssh_close_authentication_socket(agent_fd);
105418de8d7fSPeter Avalos 	return ret;
105518de8d7fSPeter Avalos }
1056