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