xref: /netbsd-src/crypto/external/bsd/openssh/dist/ssh-add.c (revision 514b5d45114b5d7b0e4a77c402fe3dc63c475ad5)
1 /*	$NetBSD: ssh-add.c,v 1.30 2023/12/20 17:15:21 christos Exp $	*/
2 /* $OpenBSD: ssh-add.c,v 1.169 2023/12/18 14:46:56 djm Exp $ */
3 
4 /*
5  * Author: Tatu Ylonen <ylo@cs.hut.fi>
6  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
7  *                    All rights reserved
8  * Adds an identity to the authentication server, or removes an identity.
9  *
10  * As far as I am concerned, the code I have written for this software
11  * can be used freely for any purpose.  Any derived versions of this
12  * software must be clearly marked as such, and if the derived work is
13  * incompatible with the protocol description in the RFC file, it must be
14  * called by a name other than "ssh" or "Secure Shell".
15  *
16  * SSH2 implementation,
17  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
18  *
19  * Redistribution and use in source and binary forms, with or without
20  * modification, are permitted provided that the following conditions
21  * are met:
22  * 1. Redistributions of source code must retain the above copyright
23  *    notice, this list of conditions and the following disclaimer.
24  * 2. Redistributions in binary form must reproduce the above copyright
25  *    notice, this list of conditions and the following disclaimer in the
26  *    documentation and/or other materials provided with the distribution.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
29  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
30  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
31  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
32  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
33  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
37  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 #include "includes.h"
41 __RCSID("$NetBSD: ssh-add.c,v 1.30 2023/12/20 17:15:21 christos Exp $");
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 
45 #ifdef WITH_OPENSSL
46 #include <openssl/evp.h>
47 #endif
48 
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <pwd.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <stdarg.h>
56 #include <unistd.h>
57 #include <limits.h>
58 
59 #include "xmalloc.h"
60 #include "ssh.h"
61 #include "log.h"
62 #include "sshkey.h"
63 #include "sshbuf.h"
64 #include "authfd.h"
65 #include "authfile.h"
66 #include "pathnames.h"
67 #include "misc.h"
68 #include "ssherr.h"
69 #include "digest.h"
70 #include "ssh-sk.h"
71 #include "sk-api.h"
72 #include "hostfile.h"
73 
74 /* argv0 */
75 extern char *__progname;
76 
77 /* Default files to add */
78 static const char *default_files[] = {
79 	_PATH_SSH_CLIENT_ID_RSA,
80 	_PATH_SSH_CLIENT_ID_ECDSA,
81 	_PATH_SSH_CLIENT_ID_ECDSA_SK,
82 	_PATH_SSH_CLIENT_ID_ED25519,
83 	_PATH_SSH_CLIENT_ID_ED25519_SK,
84 	_PATH_SSH_CLIENT_ID_XMSS,
85 	_PATH_SSH_CLIENT_ID_DSA,
86 	NULL
87 };
88 
89 static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
90 
91 /* Default lifetime (0 == forever) */
92 static int lifetime = 0;
93 
94 /* User has to confirm key use */
95 static int confirm = 0;
96 
97 /* Maximum number of signatures (XMSS) */
98 static u_int maxsign = 0;
99 static u_int minleft = 0;
100 
101 /* we keep a cache of one passphrase */
102 static char *pass = NULL;
103 static void
104 clear_pass(void)
105 {
106 	if (pass) {
107 		freezero(pass, strlen(pass));
108 		pass = NULL;
109 	}
110 }
111 
112 static int
113 delete_one(int agent_fd, const struct sshkey *key, const char *comment,
114     const char *path, int qflag)
115 {
116 	int r;
117 
118 	if ((r = ssh_remove_identity(agent_fd, key)) != 0) {
119 		fprintf(stderr, "Could not remove identity \"%s\": %s\n",
120 		    path, ssh_err(r));
121 		return r;
122 	}
123 	if (!qflag) {
124 		fprintf(stderr, "Identity removed: %s %s (%s)\n", path,
125 		    sshkey_type(key), comment ? comment : "no comment");
126 	}
127 	return 0;
128 }
129 
130 static int
131 delete_stdin(int agent_fd, int qflag, int key_only, int cert_only)
132 {
133 	char *line = NULL, *cp;
134 	size_t linesize = 0;
135 	struct sshkey *key = NULL;
136 	int lnum = 0, r, ret = -1;
137 
138 	while (getline(&line, &linesize, stdin) != -1) {
139 		lnum++;
140 		sshkey_free(key);
141 		key = NULL;
142 		line[strcspn(line, "\n")] = '\0';
143 		cp = line + strspn(line, " \t");
144 		if (*cp == '#' || *cp == '\0')
145 			continue;
146 		if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
147 			fatal_f("sshkey_new");
148 		if ((r = sshkey_read(key, &cp)) != 0) {
149 			error_r(r, "(stdin):%d: invalid key", lnum);
150 			continue;
151 		}
152 		if ((!key_only && !cert_only) ||
153 		    (key_only && !sshkey_is_cert(key)) ||
154 		    (cert_only && sshkey_is_cert(key))) {
155 			if (delete_one(agent_fd, key, cp,
156 			    "(stdin)", qflag) == 0)
157 				ret = 0;
158 		}
159 	}
160 	sshkey_free(key);
161 	free(line);
162 	return ret;
163 }
164 
165 static int
166 delete_file(int agent_fd, const char *filename, int key_only,
167     int cert_only, int qflag)
168 {
169 	struct sshkey *public, *cert = NULL;
170 	char *certpath = NULL, *comment = NULL;
171 	int r, ret = -1;
172 
173 	if (strcmp(filename, "-") == 0)
174 		return delete_stdin(agent_fd, qflag, key_only, cert_only);
175 
176 	if ((r = sshkey_load_public(filename, &public,  &comment)) != 0) {
177 		printf("Bad key file %s: %s\n", filename, ssh_err(r));
178 		return -1;
179 	}
180 	if ((!key_only && !cert_only) ||
181 	    (key_only && !sshkey_is_cert(public)) ||
182 	    (cert_only && sshkey_is_cert(public))) {
183 		if (delete_one(agent_fd, public, comment, filename, qflag) == 0)
184 			ret = 0;
185 	}
186 
187 	if (key_only)
188 		goto out;
189 
190 	/* Now try to delete the corresponding certificate too */
191 	free(comment);
192 	comment = NULL;
193 	xasprintf(&certpath, "%s-cert.pub", filename);
194 	if ((r = sshkey_load_public(certpath, &cert, &comment)) != 0) {
195 		if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
196 			error_r(r, "Failed to load certificate \"%s\"", certpath);
197 		goto out;
198 	}
199 
200 	if (!sshkey_equal_public(cert, public))
201 		fatal("Certificate %s does not match private key %s",
202 		    certpath, filename);
203 
204 	if (delete_one(agent_fd, cert, comment, certpath, qflag) == 0)
205 		ret = 0;
206 
207  out:
208 	sshkey_free(cert);
209 	sshkey_free(public);
210 	free(certpath);
211 	free(comment);
212 
213 	return ret;
214 }
215 
216 /* Send a request to remove all identities. */
217 static int
218 delete_all(int agent_fd, int qflag)
219 {
220 	int ret = -1;
221 
222 	/*
223 	 * Since the agent might be forwarded, old or non-OpenSSH, when asked
224 	 * to remove all keys, attempt to remove both protocol v.1 and v.2
225 	 * keys.
226 	 */
227 	if (ssh_remove_all_identities(agent_fd, 2) == 0)
228 		ret = 0;
229 	/* ignore error-code for ssh1 */
230 	ssh_remove_all_identities(agent_fd, 1);
231 
232 	if (ret != 0)
233 		fprintf(stderr, "Failed to remove all identities.\n");
234 	else if (!qflag)
235 		fprintf(stderr, "All identities removed.\n");
236 
237 	return ret;
238 }
239 
240 static int
241 add_file(int agent_fd, const char *filename, int key_only, int cert_only,
242     int qflag, const char *skprovider,
243     struct dest_constraint **dest_constraints,
244     size_t ndest_constraints)
245 {
246 	struct sshkey *private, *cert;
247 	char *comment = NULL;
248 	char msg[1024], *certpath = NULL;
249 	int r, fd, ret = -1;
250 	size_t i;
251 	u_int32_t left;
252 	struct sshbuf *keyblob;
253 	struct ssh_identitylist *idlist;
254 
255 	if (strcmp(filename, "-") == 0) {
256 		fd = STDIN_FILENO;
257 		filename = "(stdin)";
258 	} else if ((fd = open(filename, O_RDONLY)) == -1) {
259 		perror(filename);
260 		return -1;
261 	}
262 
263 	/*
264 	 * Since we'll try to load a keyfile multiple times, permission errors
265 	 * will occur multiple times, so check perms first and bail if wrong.
266 	 */
267 	if (fd != STDIN_FILENO) {
268 		if (sshkey_perm_ok(fd, filename) != 0) {
269 			close(fd);
270 			return -1;
271 		}
272 	}
273 	if ((r = sshbuf_load_fd(fd, &keyblob)) != 0) {
274 		fprintf(stderr, "Error loading key \"%s\": %s\n",
275 		    filename, ssh_err(r));
276 		sshbuf_free(keyblob);
277 		close(fd);
278 		return -1;
279 	}
280 	close(fd);
281 
282 	/* At first, try empty passphrase */
283 	if ((r = sshkey_parse_private_fileblob(keyblob, "", &private,
284 	    &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
285 		fprintf(stderr, "Error loading key \"%s\": %s\n",
286 		    filename, ssh_err(r));
287 		goto fail_load;
288 	}
289 	/* try last */
290 	if (private == NULL && pass != NULL) {
291 		if ((r = sshkey_parse_private_fileblob(keyblob, pass, &private,
292 		    &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
293 			fprintf(stderr, "Error loading key \"%s\": %s\n",
294 			    filename, ssh_err(r));
295 			goto fail_load;
296 		}
297 	}
298 	if (private == NULL) {
299 		/* clear passphrase since it did not work */
300 		clear_pass();
301 		snprintf(msg, sizeof msg, "Enter passphrase for %s%s: ",
302 		    filename, confirm ? " (will confirm each use)" : "");
303 		for (;;) {
304 			pass = read_passphrase(msg, RP_ALLOW_STDIN);
305 			if (strcmp(pass, "") == 0)
306 				goto fail_load;
307 			if ((r = sshkey_parse_private_fileblob(keyblob, pass,
308 			    &private, &comment)) == 0)
309 				break;
310 			else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
311 				fprintf(stderr,
312 				    "Error loading key \"%s\": %s\n",
313 				    filename, ssh_err(r));
314  fail_load:
315 				clear_pass();
316 				sshbuf_free(keyblob);
317 				return -1;
318 			}
319 			clear_pass();
320 			snprintf(msg, sizeof msg,
321 			    "Bad passphrase, try again for %s%s: ", filename,
322 			    confirm ? " (will confirm each use)" : "");
323 		}
324 	}
325 	if (comment == NULL || *comment == '\0')
326 		comment = xstrdup(filename);
327 	sshbuf_free(keyblob);
328 
329 	/* For XMSS */
330 	if ((r = sshkey_set_filename(private, filename)) != 0) {
331 		fprintf(stderr, "Could not add filename to private key: %s (%s)\n",
332 		    filename, comment);
333 		goto out;
334 	}
335 	if (maxsign && minleft &&
336 	    (r = ssh_fetch_identitylist(agent_fd, &idlist)) == 0) {
337 		for (i = 0; i < idlist->nkeys; i++) {
338 			if (!sshkey_equal_public(idlist->keys[i], private))
339 				continue;
340 			left = sshkey_signatures_left(idlist->keys[i]);
341 			if (left < minleft) {
342 				fprintf(stderr,
343 				    "Only %d signatures left.\n", left);
344 				break;
345 			}
346 			fprintf(stderr, "Skipping update: ");
347 			if (left == minleft) {
348 				fprintf(stderr,
349 				    "required signatures left (%d).\n", left);
350 			} else {
351 				fprintf(stderr,
352 				    "more signatures left (%d) than"
353 				    " required (%d).\n", left, minleft);
354 			}
355 			ssh_free_identitylist(idlist);
356 			goto out;
357 		}
358 		ssh_free_identitylist(idlist);
359 	}
360 
361 	if (sshkey_is_sk(private)) {
362 		if (skprovider == NULL) {
363 			fprintf(stderr, "Cannot load FIDO key %s "
364 			    "without provider\n", filename);
365 			goto out;
366 		}
367 	} else {
368 		/* Don't send provider constraint for other keys */
369 		skprovider = NULL;
370 	}
371 
372 	if (!cert_only &&
373 	    (r = ssh_add_identity_constrained(agent_fd, private, comment,
374 	    lifetime, confirm, maxsign, skprovider,
375 	    dest_constraints, ndest_constraints)) == 0) {
376 		ret = 0;
377 		if (!qflag) {
378 			fprintf(stderr, "Identity added: %s (%s)\n",
379 			    filename, comment);
380 			if (lifetime != 0) {
381 				fprintf(stderr,
382 				    "Lifetime set to %d seconds\n", lifetime);
383 			}
384 			if (confirm != 0) {
385 				fprintf(stderr, "The user must confirm "
386 				    "each use of the key\n");
387 			}
388 		}
389 	} else {
390 		fprintf(stderr, "Could not add identity \"%s\": %s\n",
391 		    filename, ssh_err(r));
392 	}
393 
394 	/* Skip trying to load the cert if requested */
395 	if (key_only)
396 		goto out;
397 
398 	/* Now try to add the certificate flavour too */
399 	xasprintf(&certpath, "%s-cert.pub", filename);
400 	if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) {
401 		if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
402 			error_r(r, "Failed to load certificate \"%s\"",
403 			    certpath);
404 		goto out;
405 	}
406 
407 	if (!sshkey_equal_public(cert, private)) {
408 		error("Certificate %s does not match private key %s",
409 		    certpath, filename);
410 		sshkey_free(cert);
411 		goto out;
412 	}
413 
414 	/* Graft with private bits */
415 	if ((r = sshkey_to_certified(private)) != 0) {
416 		error_fr(r, "sshkey_to_certified");
417 		sshkey_free(cert);
418 		goto out;
419 	}
420 	if ((r = sshkey_cert_copy(cert, private)) != 0) {
421 		error_fr(r, "sshkey_cert_copy");
422 		sshkey_free(cert);
423 		goto out;
424 	}
425 	sshkey_free(cert);
426 
427 	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
428 	    lifetime, confirm, maxsign, skprovider,
429 	    dest_constraints, ndest_constraints)) != 0) {
430 		error_r(r, "Certificate %s (%s) add failed", certpath,
431 		    private->cert->key_id);
432 		goto out;
433 	}
434 	/* success */
435 	if (!qflag) {
436 		fprintf(stderr, "Certificate added: %s (%s)\n", certpath,
437 		    private->cert->key_id);
438 		if (lifetime != 0) {
439 			fprintf(stderr, "Lifetime set to %d seconds\n",
440 			    lifetime);
441 		}
442 		if (confirm != 0) {
443 			fprintf(stderr, "The user must confirm each use "
444 			    "of the key\n");
445 		}
446 	}
447 
448  out:
449 	free(certpath);
450 	free(comment);
451 	sshkey_free(private);
452 
453 	return ret;
454 }
455 
456 static int
457 update_card(int agent_fd, int add, const char *id, int qflag,
458     int key_only, int cert_only,
459     struct dest_constraint **dest_constraints, size_t ndest_constraints,
460     struct sshkey **certs, size_t ncerts)
461 {
462 	char *pin = NULL;
463 	int r, ret = -1;
464 
465 	if (key_only)
466 		ncerts = 0;
467 
468 	if (add) {
469 		if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
470 		    RP_ALLOW_STDIN)) == NULL)
471 			return -1;
472 	}
473 
474 	if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin,
475 	    lifetime, confirm, dest_constraints, ndest_constraints,
476 	    cert_only, certs, ncerts)) == 0) {
477 		ret = 0;
478 		if (!qflag) {
479 			fprintf(stderr, "Card %s: %s\n",
480 			    add ? "added" : "removed", id);
481 		}
482 	} else {
483 		fprintf(stderr, "Could not %s card \"%s\": %s\n",
484 		    add ? "add" : "remove", id, ssh_err(r));
485 		ret = -1;
486 	}
487 	free(pin);
488 	return ret;
489 }
490 
491 static int
492 test_key(int agent_fd, const char *filename)
493 {
494 	struct sshkey *key = NULL;
495 	u_char *sig = NULL;
496 	const char *alg = NULL;
497 	size_t slen = 0;
498 	int r, ret = -1;
499 	u_char data[1024];
500 
501 	if ((r = sshkey_load_public(filename, &key, NULL)) != 0) {
502 		error_r(r, "Couldn't read public key %s", filename);
503 		return -1;
504 	}
505 	if (sshkey_type_plain(key->type) == KEY_RSA)
506 		alg = "rsa-sha2-256";
507 	arc4random_buf(data, sizeof(data));
508 	if ((r = ssh_agent_sign(agent_fd, key, &sig, &slen, data, sizeof(data),
509 	    alg, 0)) != 0) {
510 		error_r(r, "Agent signature failed for %s", filename);
511 		goto done;
512 	}
513 	if ((r = sshkey_verify(key, sig, slen, data, sizeof(data),
514 	    alg, 0, NULL)) != 0) {
515 		error_r(r, "Signature verification failed for %s", filename);
516 		goto done;
517 	}
518 	/* success */
519 	ret = 0;
520  done:
521 	free(sig);
522 	sshkey_free(key);
523 	return ret;
524 }
525 
526 static int
527 list_identities(int agent_fd, int do_fp)
528 {
529 	char *fp;
530 	int r;
531 	struct ssh_identitylist *idlist;
532 	u_int32_t left;
533 	size_t i;
534 
535 	if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) {
536 		if (r != SSH_ERR_AGENT_NO_IDENTITIES)
537 			fprintf(stderr, "error fetching identities: %s\n",
538 			    ssh_err(r));
539 		else
540 			printf("The agent has no identities.\n");
541 		return -1;
542 	}
543 	for (i = 0; i < idlist->nkeys; i++) {
544 		if (do_fp) {
545 			fp = sshkey_fingerprint(idlist->keys[i],
546 			    fingerprint_hash, SSH_FP_DEFAULT);
547 			printf("%u %s %s (%s)\n", sshkey_size(idlist->keys[i]),
548 			    fp == NULL ? "(null)" : fp, idlist->comments[i],
549 			    sshkey_type(idlist->keys[i]));
550 			free(fp);
551 		} else {
552 			if ((r = sshkey_write(idlist->keys[i], stdout)) != 0) {
553 				fprintf(stderr, "sshkey_write: %s\n",
554 				    ssh_err(r));
555 				continue;
556 			}
557 			fprintf(stdout, " %s", idlist->comments[i]);
558 			left = sshkey_signatures_left(idlist->keys[i]);
559 			if (left > 0)
560 				fprintf(stdout,
561 				    " [signatures left %d]", left);
562 			fprintf(stdout, "\n");
563 		}
564 	}
565 	ssh_free_identitylist(idlist);
566 	return 0;
567 }
568 
569 static int
570 lock_agent(int agent_fd, int lock)
571 {
572 	char prompt[100], *p1, *p2;
573 	int r, passok = 1, ret = -1;
574 
575 	strlcpy(prompt, "Enter lock password: ", sizeof(prompt));
576 	p1 = read_passphrase(prompt, RP_ALLOW_STDIN);
577 	if (lock) {
578 		strlcpy(prompt, "Again: ", sizeof prompt);
579 		p2 = read_passphrase(prompt, RP_ALLOW_STDIN);
580 		if (strcmp(p1, p2) != 0) {
581 			fprintf(stderr, "Passwords do not match.\n");
582 			passok = 0;
583 		}
584 		freezero(p2, strlen(p2));
585 	}
586 	if (passok) {
587 		if ((r = ssh_lock_agent(agent_fd, lock, p1)) == 0) {
588 			fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un");
589 			ret = 0;
590 		} else {
591 			fprintf(stderr, "Failed to %slock agent: %s\n",
592 			    lock ? "" : "un", ssh_err(r));
593 		}
594 	}
595 	freezero(p1, strlen(p1));
596 	return (ret);
597 }
598 
599 static int
600 load_resident_keys(int agent_fd, const char *skprovider, int qflag,
601     struct dest_constraint **dest_constraints, size_t ndest_constraints)
602 {
603 	struct sshsk_resident_key **srks;
604 	size_t nsrks, i;
605 	struct sshkey *key;
606 	int r, ok = 0;
607 	char *fp;
608 
609 	pass = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN);
610 	if ((r = sshsk_load_resident(skprovider, NULL, pass, 0,
611 	    &srks, &nsrks)) != 0) {
612 		error_r(r, "Unable to load resident keys");
613 		return r;
614 	}
615 	for (i = 0; i < nsrks; i++) {
616 		key = srks[i]->key;
617 		if ((fp = sshkey_fingerprint(key,
618 		    fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
619 			fatal_f("sshkey_fingerprint failed");
620 		if ((r = ssh_add_identity_constrained(agent_fd, key, "",
621 		    lifetime, confirm, maxsign, skprovider,
622 		    dest_constraints, ndest_constraints)) != 0) {
623 			error("Unable to add key %s %s",
624 			    sshkey_type(key), fp);
625 			free(fp);
626 			ok = r;
627 			continue;
628 		}
629 		if (ok == 0)
630 			ok = 1;
631 		if (!qflag) {
632 			fprintf(stderr, "Resident identity added: %s %s\n",
633 			    sshkey_type(key), fp);
634 			if (lifetime != 0) {
635 				fprintf(stderr,
636 				    "Lifetime set to %d seconds\n", lifetime);
637 			}
638 			if (confirm != 0) {
639 				fprintf(stderr, "The user must confirm "
640 				    "each use of the key\n");
641 			}
642 		}
643 		free(fp);
644 	}
645 	sshsk_free_resident_keys(srks, nsrks);
646 	if (nsrks == 0)
647 		return SSH_ERR_KEY_NOT_FOUND;
648 	return ok == 1 ? 0 : ok;
649 }
650 
651 static int
652 do_file(int agent_fd, int deleting, int key_only, int cert_only,
653     char *file, int qflag, const char *skprovider,
654     struct dest_constraint **dest_constraints, size_t ndest_constraints)
655 {
656 	if (deleting) {
657 		if (delete_file(agent_fd, file, key_only,
658 		    cert_only, qflag) == -1)
659 			return -1;
660 	} else {
661 		if (add_file(agent_fd, file, key_only, cert_only, qflag,
662 		    skprovider, dest_constraints, ndest_constraints) == -1)
663 			return -1;
664 	}
665 	return 0;
666 }
667 
668 /* Append string 's' to a NULL-terminated array of strings */
669 static void
670 stringlist_append(char ***listp, const char *s)
671 {
672 	size_t i = 0;
673 
674 	if (*listp == NULL)
675 		*listp = xcalloc(2, sizeof(**listp));
676 	else {
677 		for (i = 0; (*listp)[i] != NULL; i++)
678 			; /* count */
679 		*listp = xrecallocarray(*listp, i + 1, i + 2, sizeof(**listp));
680 	}
681 	(*listp)[i] = xstrdup(s);
682 }
683 
684 static void
685 parse_dest_constraint_hop(const char *s, struct dest_constraint_hop *dch,
686     char **hostkey_files)
687 {
688 	char *user = NULL, *host, *os, *path;
689 	size_t i;
690 	struct hostkeys *hostkeys;
691 	const struct hostkey_entry *hke;
692 	int r, want_ca;
693 
694 	memset(dch, '\0', sizeof(*dch));
695 	os = xstrdup(s);
696 	if ((host = strchr(os, '@')) == NULL)
697 		host = os;
698 	else {
699 		*host++ = '\0';
700 		user = os;
701 	}
702 	cleanhostname(host);
703 	/* Trivial case: username@ (all hosts) */
704 	if (*host == '\0') {
705 		if (user == NULL) {
706 			fatal("Invalid key destination constraint \"%s\": "
707 			    "does not specify user or host", s);
708 		}
709 		dch->user = xstrdup(user);
710 		/* other fields left blank */
711 		free(os);
712 		return;
713 	}
714 	if (hostkey_files == NULL)
715 		fatal_f("no hostkey files");
716 	/* Otherwise we need to look up the keys for this hostname */
717 	hostkeys = init_hostkeys();
718 	for (i = 0; hostkey_files[i]; i++) {
719 		path = tilde_expand_filename(hostkey_files[i], getuid());
720 		debug2_f("looking up host keys for \"%s\" in %s", host, path);
721                 load_hostkeys(hostkeys, host, path, 0);
722 		free(path);
723 	}
724 	dch->user = user == NULL ? NULL : xstrdup(user);
725 	dch->hostname = xstrdup(host);
726 	for (i = 0; i < hostkeys->num_entries; i++) {
727 		hke = hostkeys->entries + i;
728 		want_ca = hke->marker == MRK_CA;
729 		if (hke->marker != MRK_NONE && !want_ca)
730 			continue;
731 		debug3_f("%s%s%s: adding %s %skey from %s:%lu as key %u",
732 		    user == NULL ? "": user, user == NULL ? "" : "@",
733 		    host, sshkey_type(hke->key), want_ca ? "CA " : "",
734 		    hke->file, hke->line, dch->nkeys);
735 		dch->keys = xrecallocarray(dch->keys, dch->nkeys,
736 		    dch->nkeys + 1, sizeof(*dch->keys));
737 		dch->key_is_ca = xrecallocarray(dch->key_is_ca, dch->nkeys,
738 		    dch->nkeys + 1, sizeof(*dch->key_is_ca));
739 		if ((r = sshkey_from_private(hke->key,
740 		    &(dch->keys[dch->nkeys]))) != 0)
741 			fatal_fr(r, "sshkey_from_private");
742 		dch->key_is_ca[dch->nkeys] = want_ca;
743 		dch->nkeys++;
744 	}
745 	if (dch->nkeys == 0)
746 		fatal("No host keys found for destination \"%s\"", host);
747 	free_hostkeys(hostkeys);
748 	free(os);
749 	return;
750 }
751 
752 static void
753 parse_dest_constraint(const char *s, struct dest_constraint ***dcp,
754     size_t *ndcp, char **hostkey_files)
755 {
756 	struct dest_constraint *dc;
757 	char *os, *cp;
758 
759 	dc = xcalloc(1, sizeof(*dc));
760 	os = xstrdup(s);
761 	if ((cp = strchr(os, '>')) == NULL) {
762 		/* initial hop; no 'from' hop specified */
763 		parse_dest_constraint_hop(os, &dc->to, hostkey_files);
764 	} else {
765 		/* two hops specified */
766 		*(cp++) = '\0';
767 		parse_dest_constraint_hop(os, &dc->from, hostkey_files);
768 		parse_dest_constraint_hop(cp, &dc->to, hostkey_files);
769 		if (dc->from.user != NULL) {
770 			fatal("Invalid key constraint %s: cannot specify "
771 			    "user on 'from' host", os);
772 		}
773 	}
774 	/* XXX eliminate or error on duplicates */
775 	debug2_f("constraint %zu: %s%s%s (%u keys) > %s%s%s (%u keys)", *ndcp,
776 	    dc->from.user ? dc->from.user : "", dc->from.user ? "@" : "",
777 	    dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc->from.nkeys,
778 	    dc->to.user ? dc->to.user : "", dc->to.user ? "@" : "",
779 	    dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys);
780 	*dcp = xrecallocarray(*dcp, *ndcp, *ndcp + 1, sizeof(**dcp));
781 	(*dcp)[(*ndcp)++] = dc;
782 	free(os);
783 }
784 
785 
786 static void
787 usage(void)
788 {
789 	fprintf(stderr,
790 "usage: ssh-add [-cDdKkLlqvXx] [-E fingerprint_hash] [-H hostkey_file]\n"
791 "               [-h destination_constraint] [-S provider] [-t life]\n"
792 #ifdef WITH_XMSS
793 "               [-M maxsign] [-m minleft]\n"
794 #endif
795 "               [file ...]\n"
796 "       ssh-add -s pkcs11\n"
797 "       ssh-add -e pkcs11\n"
798 "       ssh-add -T pubkey ...\n"
799 	);
800 }
801 
802 int
803 main(int argc, char **argv)
804 {
805 	extern char *optarg;
806 	extern int optind;
807 	int agent_fd;
808 	char *pkcs11provider = NULL;
809 	const char *skprovider = NULL;
810 	char **dest_constraint_strings = NULL, **hostkey_files = NULL;
811 	int r, i, ch, deleting = 0, ret = 0, key_only = 0, cert_only = 0;
812 	int do_download = 0, xflag = 0, lflag = 0, Dflag = 0;
813 	int qflag = 0, Tflag = 0;
814 	SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
815 	LogLevel log_level = SYSLOG_LEVEL_INFO;
816 	struct sshkey *k, **certs = NULL;
817 	struct dest_constraint **dest_constraints = NULL;
818 	size_t ndest_constraints = 0i, ncerts = 0;
819 
820 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
821 	sanitise_stdfd();
822 
823 #ifdef WITH_OPENSSL
824 	OpenSSL_add_all_algorithms();
825 #endif
826 	log_init(__progname, log_level, log_facility, 1);
827 
828 	setvbuf(stdout, NULL, _IOLBF, 0);
829 
830 	/* First, get a connection to the authentication agent. */
831 	switch (r = ssh_get_authentication_socket(&agent_fd)) {
832 	case 0:
833 		break;
834 	case SSH_ERR_AGENT_NOT_PRESENT:
835 		fprintf(stderr, "Could not open a connection to your "
836 		    "authentication agent.\n");
837 		exit(2);
838 	default:
839 		fprintf(stderr, "Error connecting to agent: %s\n", ssh_err(r));
840 		exit(2);
841 	}
842 
843 	skprovider = getenv("SSH_SK_PROVIDER");
844 
845 	while ((ch = getopt(argc, argv, "vkKlLCcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) {
846 		switch (ch) {
847 		case 'v':
848 			if (log_level == SYSLOG_LEVEL_INFO)
849 				log_level = SYSLOG_LEVEL_DEBUG1;
850 			else if (log_level < SYSLOG_LEVEL_DEBUG3)
851 				log_level++;
852 			break;
853 		case 'E':
854 			fingerprint_hash = ssh_digest_alg_by_name(optarg);
855 			if (fingerprint_hash == -1)
856 				fatal("Invalid hash algorithm \"%s\"", optarg);
857 			break;
858 		case 'H':
859 			stringlist_append(&hostkey_files, optarg);
860 			break;
861 		case 'h':
862 			stringlist_append(&dest_constraint_strings, optarg);
863 			break;
864 		case 'k':
865 			key_only = 1;
866 			break;
867 		case 'C':
868 			cert_only = 1;
869 			break;
870 		case 'K':
871 			do_download = 1;
872 			break;
873 		case 'l':
874 		case 'L':
875 			if (lflag != 0)
876 				fatal("-%c flag already specified", lflag);
877 			lflag = ch;
878 			break;
879 		case 'x':
880 		case 'X':
881 			if (xflag != 0)
882 				fatal("-%c flag already specified", xflag);
883 			xflag = ch;
884 			break;
885 		case 'c':
886 			confirm = 1;
887 			break;
888 		case 'm':
889 			minleft = (u_int)strtonum(optarg, 1, UINT_MAX, NULL);
890 			if (minleft == 0) {
891 				usage();
892 				ret = 1;
893 				goto done;
894 			}
895 			break;
896 		case 'M':
897 			maxsign = (u_int)strtonum(optarg, 1, UINT_MAX, NULL);
898 			if (maxsign == 0) {
899 				usage();
900 				ret = 1;
901 				goto done;
902 			}
903 			break;
904 		case 'd':
905 			deleting = 1;
906 			break;
907 		case 'D':
908 			Dflag = 1;
909 			break;
910 		case 's':
911 			pkcs11provider = optarg;
912 			break;
913 		case 'S':
914 			skprovider = optarg;
915 			break;
916 		case 'e':
917 			deleting = 1;
918 			pkcs11provider = optarg;
919 			break;
920 		case 't':
921 			if ((lifetime = convtime(optarg)) == -1 ||
922 			    lifetime < 0 || (u_long)lifetime > UINT32_MAX) {
923 				fprintf(stderr, "Invalid lifetime\n");
924 				ret = 1;
925 				goto done;
926 			}
927 			break;
928 		case 'q':
929 			qflag = 1;
930 			break;
931 		case 'T':
932 			Tflag = 1;
933 			break;
934 		default:
935 			usage();
936 			ret = 1;
937 			goto done;
938 		}
939 	}
940 	log_init(__progname, log_level, log_facility, 1);
941 
942 	if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1)
943 		fatal("Invalid combination of actions");
944 	else if (xflag) {
945 		if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1)
946 			ret = 1;
947 		goto done;
948 	} else if (lflag) {
949 		if (list_identities(agent_fd, lflag == 'l' ? 1 : 0) == -1)
950 			ret = 1;
951 		goto done;
952 	} else if (Dflag) {
953 		if (delete_all(agent_fd, qflag) == -1)
954 			ret = 1;
955 		goto done;
956 	}
957 
958 	if (skprovider == NULL)
959 		skprovider = "internal";
960 	if (hostkey_files == NULL) {
961 		/* use defaults from readconf.c */
962 		stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE);
963 		stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE2);
964 		stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE);
965 		stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE2);
966 	}
967 	if (dest_constraint_strings != NULL) {
968 		for (i = 0; dest_constraint_strings[i] != NULL; i++) {
969 			parse_dest_constraint(dest_constraint_strings[i],
970 			  &dest_constraints, &ndest_constraints, hostkey_files);
971 		}
972 	}
973 
974 	argc -= optind;
975 	argv += optind;
976 	if (Tflag) {
977 		if (argc <= 0)
978 			fatal("no keys to test");
979 		for (r = i = 0; i < argc; i++)
980 			r |= test_key(agent_fd, argv[i]);
981 		ret = r == 0 ? 0 : 1;
982 		goto done;
983 	}
984 	if (pkcs11provider != NULL) {
985 		for (i = 0; i < argc; i++) {
986 			if ((r = sshkey_load_public(argv[i], &k, NULL)) != 0)
987 				fatal_fr(r, "load certificate %s", argv[i]);
988 			certs = xrecallocarray(certs, ncerts, ncerts + 1,
989 			    sizeof(*certs));
990 			debug2("%s: %s", argv[i], sshkey_ssh_name(k));
991 			certs[ncerts++] = k;
992 		}
993 		debug2_f("loaded %zu certificates", ncerts);
994 		if (update_card(agent_fd, !deleting, pkcs11provider,
995 		    qflag, key_only, cert_only,
996 		    dest_constraints, ndest_constraints,
997 		    certs, ncerts) == -1)
998 			ret = 1;
999 		goto done;
1000 	}
1001 	if (do_download) {
1002 		if (skprovider == NULL)
1003 			fatal("Cannot download keys without provider");
1004 		if (load_resident_keys(agent_fd, skprovider, qflag,
1005 		    dest_constraints, ndest_constraints) != 0)
1006 			ret = 1;
1007 		goto done;
1008 	}
1009 	if (argc == 0) {
1010 		char buf[PATH_MAX];
1011 		struct passwd *pw;
1012 		struct stat st;
1013 		int count = 0;
1014 
1015 		if ((pw = getpwuid(getuid())) == NULL) {
1016 			fprintf(stderr, "No user found with uid %u\n",
1017 			    (u_int)getuid());
1018 			ret = 1;
1019 			goto done;
1020 		}
1021 
1022 		for (i = 0; default_files[i]; i++) {
1023 			snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir,
1024 			    default_files[i]);
1025 			if (stat(buf, &st) == -1)
1026 				continue;
1027 			if (do_file(agent_fd, deleting, key_only, cert_only,
1028 			    buf, qflag, skprovider,
1029 			    dest_constraints, ndest_constraints) == -1)
1030 				ret = 1;
1031 			else
1032 				count++;
1033 		}
1034 		if (count == 0)
1035 			ret = 1;
1036 	} else {
1037 		for (i = 0; i < argc; i++) {
1038 			if (do_file(agent_fd, deleting, key_only, cert_only,
1039 			    argv[i], qflag, skprovider,
1040 			    dest_constraints, ndest_constraints) == -1)
1041 				ret = 1;
1042 		}
1043 	}
1044 done:
1045 	clear_pass();
1046 	ssh_close_authentication_socket(agent_fd);
1047 	return ret;
1048 }
1049