1*50a69bb5SSascha Wildner /* $OpenBSD: ssh-add.c,v 1.160 2021/04/03 06:18:41 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" 70*50a69bb5SSascha Wildner #include "sk-api.h" 7118de8d7fSPeter Avalos 7218de8d7fSPeter Avalos /* argv0 */ 7318de8d7fSPeter Avalos extern char *__progname; 7418de8d7fSPeter Avalos 7518de8d7fSPeter Avalos /* Default files to add */ 7618de8d7fSPeter Avalos static char *default_files[] = { 77e9778795SPeter Avalos #ifdef WITH_OPENSSL 7818de8d7fSPeter Avalos _PATH_SSH_CLIENT_ID_RSA, 7918de8d7fSPeter Avalos _PATH_SSH_CLIENT_ID_DSA, 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, 8818de8d7fSPeter Avalos NULL 8918de8d7fSPeter Avalos }; 9018de8d7fSPeter Avalos 91e9778795SPeter Avalos static int fingerprint_hash = SSH_FP_HASH_DEFAULT; 92e9778795SPeter Avalos 9318de8d7fSPeter Avalos /* Default lifetime (0 == forever) */ 94*50a69bb5SSascha Wildner static int lifetime = 0; 9518de8d7fSPeter Avalos 9618de8d7fSPeter Avalos /* User has to confirm key use */ 9718de8d7fSPeter Avalos static int confirm = 0; 9818de8d7fSPeter Avalos 99664f4763Szrj /* Maximum number of signatures (XMSS) */ 100664f4763Szrj static u_int maxsign = 0; 101664f4763Szrj static u_int minleft = 0; 102664f4763Szrj 103e9778795SPeter Avalos /* we keep a cache of one passphrase */ 10418de8d7fSPeter Avalos static char *pass = NULL; 10518de8d7fSPeter Avalos static void 10618de8d7fSPeter Avalos clear_pass(void) 10718de8d7fSPeter Avalos { 10818de8d7fSPeter Avalos if (pass) { 1090cbfa66cSDaniel Fojt freezero(pass, strlen(pass)); 11018de8d7fSPeter Avalos pass = NULL; 11118de8d7fSPeter Avalos } 11218de8d7fSPeter Avalos } 11318de8d7fSPeter Avalos 11418de8d7fSPeter Avalos static int 115*50a69bb5SSascha Wildner delete_one(int agent_fd, const struct sshkey *key, const char *comment, 116*50a69bb5SSascha Wildner const char *path, int qflag) 117*50a69bb5SSascha Wildner { 118*50a69bb5SSascha Wildner int r; 119*50a69bb5SSascha Wildner 120*50a69bb5SSascha Wildner if ((r = ssh_remove_identity(agent_fd, key)) != 0) { 121*50a69bb5SSascha Wildner fprintf(stderr, "Could not remove identity \"%s\": %s\n", 122*50a69bb5SSascha Wildner path, ssh_err(r)); 123*50a69bb5SSascha Wildner return r; 124*50a69bb5SSascha Wildner } 125*50a69bb5SSascha Wildner if (!qflag) { 126*50a69bb5SSascha Wildner fprintf(stderr, "Identity removed: %s %s (%s)\n", path, 127*50a69bb5SSascha Wildner sshkey_type(key), comment); 128*50a69bb5SSascha Wildner } 129*50a69bb5SSascha Wildner return 0; 130*50a69bb5SSascha Wildner } 131*50a69bb5SSascha Wildner 132*50a69bb5SSascha Wildner static int 133*50a69bb5SSascha Wildner delete_stdin(int agent_fd, int qflag) 134*50a69bb5SSascha Wildner { 135*50a69bb5SSascha Wildner char *line = NULL, *cp; 136*50a69bb5SSascha Wildner size_t linesize = 0; 137*50a69bb5SSascha Wildner struct sshkey *key = NULL; 138*50a69bb5SSascha Wildner int lnum = 0, r, ret = -1; 139*50a69bb5SSascha Wildner 140*50a69bb5SSascha Wildner while (getline(&line, &linesize, stdin) != -1) { 141*50a69bb5SSascha Wildner lnum++; 142*50a69bb5SSascha Wildner sshkey_free(key); 143*50a69bb5SSascha Wildner key = NULL; 144*50a69bb5SSascha Wildner line[strcspn(line, "\n")] = '\0'; 145*50a69bb5SSascha Wildner cp = line + strspn(line, " \t"); 146*50a69bb5SSascha Wildner if (*cp == '#' || *cp == '\0') 147*50a69bb5SSascha Wildner continue; 148*50a69bb5SSascha Wildner if ((key = sshkey_new(KEY_UNSPEC)) == NULL) 149*50a69bb5SSascha Wildner fatal_f("sshkey_new"); 150*50a69bb5SSascha Wildner if ((r = sshkey_read(key, &cp)) != 0) { 151*50a69bb5SSascha Wildner error_r(r, "(stdin):%d: invalid key", lnum); 152*50a69bb5SSascha Wildner continue; 153*50a69bb5SSascha Wildner } 154*50a69bb5SSascha Wildner if (delete_one(agent_fd, key, cp, "(stdin)", qflag) == 0) 155*50a69bb5SSascha Wildner ret = 0; 156*50a69bb5SSascha Wildner } 157*50a69bb5SSascha Wildner sshkey_free(key); 158*50a69bb5SSascha Wildner free(line); 159*50a69bb5SSascha Wildner return ret; 160*50a69bb5SSascha Wildner } 161*50a69bb5SSascha Wildner 162*50a69bb5SSascha Wildner static int 163ce74bacaSMatthew Dillon delete_file(int agent_fd, const char *filename, int key_only, int qflag) 16418de8d7fSPeter Avalos { 165e9778795SPeter Avalos struct sshkey *public, *cert = NULL; 16636e94dc5SPeter Avalos char *certpath = NULL, *comment = NULL; 167e9778795SPeter Avalos int r, ret = -1; 16818de8d7fSPeter Avalos 169*50a69bb5SSascha Wildner if (strcmp(filename, "-") == 0) 170*50a69bb5SSascha Wildner return delete_stdin(agent_fd, qflag); 171*50a69bb5SSascha Wildner 172e9778795SPeter Avalos if ((r = sshkey_load_public(filename, &public, &comment)) != 0) { 173e9778795SPeter Avalos printf("Bad key file %s: %s\n", filename, ssh_err(r)); 17418de8d7fSPeter Avalos return -1; 17518de8d7fSPeter Avalos } 176*50a69bb5SSascha Wildner if (delete_one(agent_fd, public, comment, filename, qflag) == 0) 17718de8d7fSPeter Avalos ret = 0; 17818de8d7fSPeter Avalos 17936e94dc5SPeter Avalos if (key_only) 18036e94dc5SPeter Avalos goto out; 18136e94dc5SPeter Avalos 18236e94dc5SPeter Avalos /* Now try to delete the corresponding certificate too */ 18336e94dc5SPeter Avalos free(comment); 18436e94dc5SPeter Avalos comment = NULL; 18536e94dc5SPeter Avalos xasprintf(&certpath, "%s-cert.pub", filename); 186e9778795SPeter Avalos if ((r = sshkey_load_public(certpath, &cert, &comment)) != 0) { 187e9778795SPeter Avalos if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) 188*50a69bb5SSascha Wildner error_r(r, "Failed to load certificate \"%s\"", certpath); 18936e94dc5SPeter Avalos goto out; 190e9778795SPeter Avalos } 191e9778795SPeter Avalos 192e9778795SPeter Avalos if (!sshkey_equal_public(cert, public)) 19336e94dc5SPeter Avalos fatal("Certificate %s does not match private key %s", 19436e94dc5SPeter Avalos certpath, filename); 19536e94dc5SPeter Avalos 196*50a69bb5SSascha Wildner if (delete_one(agent_fd, cert, comment, certpath, qflag) == 0) 19736e94dc5SPeter Avalos ret = 0; 19836e94dc5SPeter Avalos 19936e94dc5SPeter Avalos out: 200e9778795SPeter Avalos sshkey_free(cert); 201e9778795SPeter Avalos sshkey_free(public); 20236e94dc5SPeter Avalos free(certpath); 20336e94dc5SPeter Avalos free(comment); 20418de8d7fSPeter Avalos 20518de8d7fSPeter Avalos return ret; 20618de8d7fSPeter Avalos } 20718de8d7fSPeter Avalos 20818de8d7fSPeter Avalos /* Send a request to remove all identities. */ 20918de8d7fSPeter Avalos static int 210664f4763Szrj delete_all(int agent_fd, int qflag) 21118de8d7fSPeter Avalos { 21218de8d7fSPeter Avalos int ret = -1; 21318de8d7fSPeter Avalos 214ce74bacaSMatthew Dillon /* 215ce74bacaSMatthew Dillon * Since the agent might be forwarded, old or non-OpenSSH, when asked 216ce74bacaSMatthew Dillon * to remove all keys, attempt to remove both protocol v.1 and v.2 217ce74bacaSMatthew Dillon * keys. 218ce74bacaSMatthew Dillon */ 219e9778795SPeter Avalos if (ssh_remove_all_identities(agent_fd, 2) == 0) 22018de8d7fSPeter Avalos ret = 0; 221e9778795SPeter Avalos /* ignore error-code for ssh1 */ 222e9778795SPeter Avalos ssh_remove_all_identities(agent_fd, 1); 22318de8d7fSPeter Avalos 224664f4763Szrj if (ret != 0) 22518de8d7fSPeter Avalos fprintf(stderr, "Failed to remove all identities.\n"); 226664f4763Szrj else if (!qflag) 227664f4763Szrj fprintf(stderr, "All identities removed.\n"); 22818de8d7fSPeter Avalos 22918de8d7fSPeter Avalos return ret; 23018de8d7fSPeter Avalos } 23118de8d7fSPeter Avalos 23218de8d7fSPeter Avalos static int 2330cbfa66cSDaniel Fojt add_file(int agent_fd, const char *filename, int key_only, int qflag, 2340cbfa66cSDaniel Fojt const char *skprovider) 23518de8d7fSPeter Avalos { 236e9778795SPeter Avalos struct sshkey *private, *cert; 23718de8d7fSPeter Avalos char *comment = NULL; 23899e85e0dSPeter Avalos char msg[1024], *certpath = NULL; 239e9778795SPeter Avalos int r, fd, ret = -1; 240664f4763Szrj size_t i; 241664f4763Szrj u_int32_t left; 242e9778795SPeter Avalos struct sshbuf *keyblob; 243664f4763Szrj struct ssh_identitylist *idlist; 24418de8d7fSPeter Avalos 2451c188a7fSPeter Avalos if (strcmp(filename, "-") == 0) { 2461c188a7fSPeter Avalos fd = STDIN_FILENO; 2471c188a7fSPeter Avalos filename = "(stdin)"; 2480cbfa66cSDaniel Fojt } else if ((fd = open(filename, O_RDONLY)) == -1) { 24918de8d7fSPeter Avalos perror(filename); 25018de8d7fSPeter Avalos return -1; 25118de8d7fSPeter Avalos } 25218de8d7fSPeter Avalos 25318de8d7fSPeter Avalos /* 25418de8d7fSPeter Avalos * Since we'll try to load a keyfile multiple times, permission errors 25518de8d7fSPeter Avalos * will occur multiple times, so check perms first and bail if wrong. 25618de8d7fSPeter Avalos */ 2571c188a7fSPeter Avalos if (fd != STDIN_FILENO) { 258e9778795SPeter Avalos if (sshkey_perm_ok(fd, filename) != 0) { 25918de8d7fSPeter Avalos close(fd); 26018de8d7fSPeter Avalos return -1; 2611c188a7fSPeter Avalos } 2621c188a7fSPeter Avalos } 2630cbfa66cSDaniel Fojt if ((r = sshbuf_load_fd(fd, &keyblob)) != 0) { 264e9778795SPeter Avalos fprintf(stderr, "Error loading key \"%s\": %s\n", 265e9778795SPeter Avalos filename, ssh_err(r)); 266e9778795SPeter Avalos sshbuf_free(keyblob); 2671c188a7fSPeter Avalos close(fd); 2681c188a7fSPeter Avalos return -1; 2691c188a7fSPeter Avalos } 2701c188a7fSPeter Avalos close(fd); 27118de8d7fSPeter Avalos 27218de8d7fSPeter Avalos /* At first, try empty passphrase */ 273e9778795SPeter Avalos if ((r = sshkey_parse_private_fileblob(keyblob, "", &private, 274e9778795SPeter Avalos &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) { 275e9778795SPeter Avalos fprintf(stderr, "Error loading key \"%s\": %s\n", 276e9778795SPeter Avalos filename, ssh_err(r)); 277e9778795SPeter Avalos goto fail_load; 278e9778795SPeter Avalos } 27936e94dc5SPeter Avalos /* try last */ 28036e94dc5SPeter Avalos if (private == NULL && pass != NULL) { 281e9778795SPeter Avalos if ((r = sshkey_parse_private_fileblob(keyblob, pass, &private, 282e9778795SPeter Avalos &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) { 283e9778795SPeter Avalos fprintf(stderr, "Error loading key \"%s\": %s\n", 284e9778795SPeter Avalos filename, ssh_err(r)); 285e9778795SPeter Avalos goto fail_load; 28636e94dc5SPeter Avalos } 287e9778795SPeter Avalos } 28818de8d7fSPeter Avalos if (private == NULL) { 28918de8d7fSPeter Avalos /* clear passphrase since it did not work */ 29018de8d7fSPeter Avalos clear_pass(); 291e9778795SPeter Avalos snprintf(msg, sizeof msg, "Enter passphrase for %s%s: ", 292e9778795SPeter Avalos filename, confirm ? " (will confirm each use)" : ""); 29318de8d7fSPeter Avalos for (;;) { 29418de8d7fSPeter Avalos pass = read_passphrase(msg, RP_ALLOW_STDIN); 295e9778795SPeter Avalos if (strcmp(pass, "") == 0) 296e9778795SPeter Avalos goto fail_load; 297e9778795SPeter Avalos if ((r = sshkey_parse_private_fileblob(keyblob, pass, 298e9778795SPeter Avalos &private, &comment)) == 0) 299e9778795SPeter Avalos break; 300e9778795SPeter Avalos else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) { 301e9778795SPeter Avalos fprintf(stderr, 302e9778795SPeter Avalos "Error loading key \"%s\": %s\n", 303e9778795SPeter Avalos filename, ssh_err(r)); 304e9778795SPeter Avalos fail_load: 30518de8d7fSPeter Avalos clear_pass(); 306e9778795SPeter Avalos sshbuf_free(keyblob); 30718de8d7fSPeter Avalos return -1; 30818de8d7fSPeter Avalos } 30918de8d7fSPeter Avalos clear_pass(); 31018de8d7fSPeter Avalos snprintf(msg, sizeof msg, 311e9778795SPeter Avalos "Bad passphrase, try again for %s%s: ", filename, 312e9778795SPeter Avalos confirm ? " (will confirm each use)" : ""); 31318de8d7fSPeter Avalos } 31418de8d7fSPeter Avalos } 315e9778795SPeter Avalos if (comment == NULL || *comment == '\0') 316e9778795SPeter Avalos comment = xstrdup(filename); 317e9778795SPeter Avalos sshbuf_free(keyblob); 31818de8d7fSPeter Avalos 319664f4763Szrj /* For XMSS */ 320664f4763Szrj if ((r = sshkey_set_filename(private, filename)) != 0) { 321664f4763Szrj fprintf(stderr, "Could not add filename to private key: %s (%s)\n", 322664f4763Szrj filename, comment); 323664f4763Szrj goto out; 324664f4763Szrj } 325664f4763Szrj if (maxsign && minleft && 326664f4763Szrj (r = ssh_fetch_identitylist(agent_fd, &idlist)) == 0) { 327664f4763Szrj for (i = 0; i < idlist->nkeys; i++) { 328664f4763Szrj if (!sshkey_equal_public(idlist->keys[i], private)) 329664f4763Szrj continue; 330664f4763Szrj left = sshkey_signatures_left(idlist->keys[i]); 331664f4763Szrj if (left < minleft) { 332664f4763Szrj fprintf(stderr, 333664f4763Szrj "Only %d signatures left.\n", left); 334664f4763Szrj break; 335664f4763Szrj } 336664f4763Szrj fprintf(stderr, "Skipping update: "); 337664f4763Szrj if (left == minleft) { 338664f4763Szrj fprintf(stderr, 339664f4763Szrj "required signatures left (%d).\n", left); 340664f4763Szrj } else { 341664f4763Szrj fprintf(stderr, 342664f4763Szrj "more signatures left (%d) than" 343664f4763Szrj " required (%d).\n", left, minleft); 344664f4763Szrj } 345664f4763Szrj ssh_free_identitylist(idlist); 346664f4763Szrj goto out; 347664f4763Szrj } 348664f4763Szrj ssh_free_identitylist(idlist); 349664f4763Szrj } 350664f4763Szrj 351*50a69bb5SSascha Wildner if (sshkey_is_sk(private)) { 352*50a69bb5SSascha Wildner if (skprovider == NULL) { 353*50a69bb5SSascha Wildner fprintf(stderr, "Cannot load FIDO key %s " 3540cbfa66cSDaniel Fojt "without provider\n", filename); 3550cbfa66cSDaniel Fojt goto out; 3560cbfa66cSDaniel Fojt } 357*50a69bb5SSascha Wildner if ((private->sk_flags & SSH_SK_USER_VERIFICATION_REQD) != 0) { 358*50a69bb5SSascha Wildner fprintf(stderr, "FIDO verify-required key %s is not " 359*50a69bb5SSascha Wildner "currently supported by ssh-agent\n", filename); 360*50a69bb5SSascha Wildner goto out; 361*50a69bb5SSascha Wildner } 362*50a69bb5SSascha Wildner } else { 363*50a69bb5SSascha Wildner /* Don't send provider constraint for other keys */ 364*50a69bb5SSascha Wildner skprovider = NULL; 365*50a69bb5SSascha Wildner } 3660cbfa66cSDaniel Fojt 367e9778795SPeter Avalos if ((r = ssh_add_identity_constrained(agent_fd, private, comment, 3680cbfa66cSDaniel Fojt lifetime, confirm, maxsign, skprovider)) == 0) { 36918de8d7fSPeter Avalos ret = 0; 370664f4763Szrj if (!qflag) { 371664f4763Szrj fprintf(stderr, "Identity added: %s (%s)\n", 372664f4763Szrj filename, comment); 373664f4763Szrj if (lifetime != 0) { 37418de8d7fSPeter Avalos fprintf(stderr, 375*50a69bb5SSascha Wildner "Lifetime set to %d seconds\n", lifetime); 376664f4763Szrj } 377664f4763Szrj if (confirm != 0) { 378664f4763Szrj fprintf(stderr, "The user must confirm " 379664f4763Szrj "each use of the key\n"); 380664f4763Szrj } 381664f4763Szrj } 38218de8d7fSPeter Avalos } else { 383e9778795SPeter Avalos fprintf(stderr, "Could not add identity \"%s\": %s\n", 384e9778795SPeter Avalos filename, ssh_err(r)); 38518de8d7fSPeter Avalos } 38618de8d7fSPeter Avalos 38799e85e0dSPeter Avalos /* Skip trying to load the cert if requested */ 38899e85e0dSPeter Avalos if (key_only) 38999e85e0dSPeter Avalos goto out; 390856ea928SPeter Avalos 391856ea928SPeter Avalos /* Now try to add the certificate flavour too */ 392856ea928SPeter Avalos xasprintf(&certpath, "%s-cert.pub", filename); 393e9778795SPeter Avalos if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) { 394e9778795SPeter Avalos if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) 395*50a69bb5SSascha Wildner error_r(r, "Failed to load certificate \"%s\"", certpath); 396856ea928SPeter Avalos goto out; 397e9778795SPeter Avalos } 398856ea928SPeter Avalos 399e9778795SPeter Avalos if (!sshkey_equal_public(cert, private)) { 400856ea928SPeter Avalos error("Certificate %s does not match private key %s", 401856ea928SPeter Avalos certpath, filename); 402e9778795SPeter Avalos sshkey_free(cert); 403856ea928SPeter Avalos goto out; 404856ea928SPeter Avalos } 405856ea928SPeter Avalos 406856ea928SPeter Avalos /* Graft with private bits */ 407e9778795SPeter Avalos if ((r = sshkey_to_certified(private)) != 0) { 408*50a69bb5SSascha Wildner error_fr(r, "sshkey_to_certified"); 409e9778795SPeter Avalos sshkey_free(cert); 410856ea928SPeter Avalos goto out; 411856ea928SPeter Avalos } 412e9778795SPeter Avalos if ((r = sshkey_cert_copy(cert, private)) != 0) { 413*50a69bb5SSascha Wildner error_fr(r, "sshkey_cert_copy"); 414e9778795SPeter Avalos sshkey_free(cert); 415e9778795SPeter Avalos goto out; 416e9778795SPeter Avalos } 417e9778795SPeter Avalos sshkey_free(cert); 418856ea928SPeter Avalos 419e9778795SPeter Avalos if ((r = ssh_add_identity_constrained(agent_fd, private, comment, 4200cbfa66cSDaniel Fojt lifetime, confirm, maxsign, skprovider)) != 0) { 421*50a69bb5SSascha Wildner error_r(r, "Certificate %s (%s) add failed", certpath, 422*50a69bb5SSascha Wildner private->cert->key_id); 423e9778795SPeter Avalos goto out; 424856ea928SPeter Avalos } 425664f4763Szrj /* success */ 426664f4763Szrj if (!qflag) { 427856ea928SPeter Avalos fprintf(stderr, "Certificate added: %s (%s)\n", certpath, 428856ea928SPeter Avalos private->cert->key_id); 429664f4763Szrj if (lifetime != 0) { 430*50a69bb5SSascha Wildner fprintf(stderr, "Lifetime set to %d seconds\n", 431664f4763Szrj lifetime); 432664f4763Szrj } 433664f4763Szrj if (confirm != 0) { 434664f4763Szrj fprintf(stderr, "The user must confirm each use " 435664f4763Szrj "of the key\n"); 436664f4763Szrj } 437664f4763Szrj } 438664f4763Szrj 439856ea928SPeter Avalos out: 44036e94dc5SPeter Avalos free(certpath); 44136e94dc5SPeter Avalos free(comment); 442e9778795SPeter Avalos sshkey_free(private); 44318de8d7fSPeter Avalos 44418de8d7fSPeter Avalos return ret; 44518de8d7fSPeter Avalos } 44618de8d7fSPeter Avalos 44718de8d7fSPeter Avalos static int 448664f4763Szrj update_card(int agent_fd, int add, const char *id, int qflag) 44918de8d7fSPeter Avalos { 45036e94dc5SPeter Avalos char *pin = NULL; 451e9778795SPeter Avalos int r, ret = -1; 45218de8d7fSPeter Avalos 45336e94dc5SPeter Avalos if (add) { 45436e94dc5SPeter Avalos if ((pin = read_passphrase("Enter passphrase for PKCS#11: ", 45536e94dc5SPeter Avalos RP_ALLOW_STDIN)) == NULL) 45618de8d7fSPeter Avalos return -1; 45736e94dc5SPeter Avalos } 45818de8d7fSPeter Avalos 459e9778795SPeter Avalos if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin, 460e9778795SPeter Avalos lifetime, confirm)) == 0) { 461664f4763Szrj ret = 0; 462664f4763Szrj if (!qflag) { 46318de8d7fSPeter Avalos fprintf(stderr, "Card %s: %s\n", 46418de8d7fSPeter Avalos add ? "added" : "removed", id); 465664f4763Szrj } 46618de8d7fSPeter Avalos } else { 467e9778795SPeter Avalos fprintf(stderr, "Could not %s card \"%s\": %s\n", 468e9778795SPeter Avalos add ? "add" : "remove", id, ssh_err(r)); 46918de8d7fSPeter Avalos ret = -1; 47018de8d7fSPeter Avalos } 47136e94dc5SPeter Avalos free(pin); 47218de8d7fSPeter Avalos return ret; 47318de8d7fSPeter Avalos } 47418de8d7fSPeter Avalos 47518de8d7fSPeter Avalos static int 476664f4763Szrj test_key(int agent_fd, const char *filename) 477664f4763Szrj { 478664f4763Szrj struct sshkey *key = NULL; 479664f4763Szrj u_char *sig = NULL; 480664f4763Szrj size_t slen = 0; 481664f4763Szrj int r, ret = -1; 482664f4763Szrj char data[1024]; 483664f4763Szrj 484664f4763Szrj if ((r = sshkey_load_public(filename, &key, NULL)) != 0) { 485*50a69bb5SSascha Wildner error_r(r, "Couldn't read public key %s", filename); 486664f4763Szrj return -1; 487664f4763Szrj } 488664f4763Szrj arc4random_buf(data, sizeof(data)); 489664f4763Szrj if ((r = ssh_agent_sign(agent_fd, key, &sig, &slen, data, sizeof(data), 490664f4763Szrj NULL, 0)) != 0) { 491*50a69bb5SSascha Wildner error_r(r, "Agent signature failed for %s", filename); 492664f4763Szrj goto done; 493664f4763Szrj } 494664f4763Szrj if ((r = sshkey_verify(key, sig, slen, data, sizeof(data), 4950cbfa66cSDaniel Fojt NULL, 0, NULL)) != 0) { 496*50a69bb5SSascha Wildner error_r(r, "Signature verification failed for %s", filename); 497664f4763Szrj goto done; 498664f4763Szrj } 499664f4763Szrj /* success */ 500664f4763Szrj ret = 0; 501664f4763Szrj done: 502664f4763Szrj free(sig); 503664f4763Szrj sshkey_free(key); 504664f4763Szrj return ret; 505664f4763Szrj } 506664f4763Szrj 507664f4763Szrj static int 508e9778795SPeter Avalos list_identities(int agent_fd, int do_fp) 50918de8d7fSPeter Avalos { 510e9778795SPeter Avalos char *fp; 511ce74bacaSMatthew Dillon int r; 512e9778795SPeter Avalos struct ssh_identitylist *idlist; 513664f4763Szrj u_int32_t left; 514e9778795SPeter Avalos size_t i; 51518de8d7fSPeter Avalos 516ce74bacaSMatthew Dillon if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) { 517e9778795SPeter Avalos if (r != SSH_ERR_AGENT_NO_IDENTITIES) 518ce74bacaSMatthew Dillon fprintf(stderr, "error fetching identities: %s\n", 519ce74bacaSMatthew Dillon ssh_err(r)); 520ce74bacaSMatthew Dillon else 521ce74bacaSMatthew Dillon printf("The agent has no identities.\n"); 522ce74bacaSMatthew Dillon return -1; 523e9778795SPeter Avalos } 524e9778795SPeter Avalos for (i = 0; i < idlist->nkeys; i++) { 52518de8d7fSPeter Avalos if (do_fp) { 526e9778795SPeter Avalos fp = sshkey_fingerprint(idlist->keys[i], 527e9778795SPeter Avalos fingerprint_hash, SSH_FP_DEFAULT); 528ce74bacaSMatthew Dillon printf("%u %s %s (%s)\n", sshkey_size(idlist->keys[i]), 529ce74bacaSMatthew Dillon fp == NULL ? "(null)" : fp, idlist->comments[i], 530e9778795SPeter Avalos sshkey_type(idlist->keys[i])); 53136e94dc5SPeter Avalos free(fp); 53218de8d7fSPeter Avalos } else { 533ce74bacaSMatthew Dillon if ((r = sshkey_write(idlist->keys[i], stdout)) != 0) { 534e9778795SPeter Avalos fprintf(stderr, "sshkey_write: %s\n", 535e9778795SPeter Avalos ssh_err(r)); 536e9778795SPeter Avalos continue; 53718de8d7fSPeter Avalos } 538664f4763Szrj fprintf(stdout, " %s", idlist->comments[i]); 539664f4763Szrj left = sshkey_signatures_left(idlist->keys[i]); 540664f4763Szrj if (left > 0) 541664f4763Szrj fprintf(stdout, 542664f4763Szrj " [signatures left %d]", left); 543664f4763Szrj fprintf(stdout, "\n"); 54418de8d7fSPeter Avalos } 54518de8d7fSPeter Avalos } 546e9778795SPeter Avalos ssh_free_identitylist(idlist); 54718de8d7fSPeter Avalos return 0; 54818de8d7fSPeter Avalos } 54918de8d7fSPeter Avalos 55018de8d7fSPeter Avalos static int 551e9778795SPeter Avalos lock_agent(int agent_fd, int lock) 55218de8d7fSPeter Avalos { 55318de8d7fSPeter Avalos char prompt[100], *p1, *p2; 554e9778795SPeter Avalos int r, passok = 1, ret = -1; 55518de8d7fSPeter Avalos 55618de8d7fSPeter Avalos strlcpy(prompt, "Enter lock password: ", sizeof(prompt)); 55718de8d7fSPeter Avalos p1 = read_passphrase(prompt, RP_ALLOW_STDIN); 55818de8d7fSPeter Avalos if (lock) { 55918de8d7fSPeter Avalos strlcpy(prompt, "Again: ", sizeof prompt); 56018de8d7fSPeter Avalos p2 = read_passphrase(prompt, RP_ALLOW_STDIN); 56118de8d7fSPeter Avalos if (strcmp(p1, p2) != 0) { 56218de8d7fSPeter Avalos fprintf(stderr, "Passwords do not match.\n"); 56318de8d7fSPeter Avalos passok = 0; 56418de8d7fSPeter Avalos } 5650cbfa66cSDaniel Fojt freezero(p2, strlen(p2)); 56618de8d7fSPeter Avalos } 567e9778795SPeter Avalos if (passok) { 568e9778795SPeter Avalos if ((r = ssh_lock_agent(agent_fd, lock, p1)) == 0) { 56918de8d7fSPeter Avalos fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un"); 57018de8d7fSPeter Avalos ret = 0; 571e9778795SPeter Avalos } else { 572e9778795SPeter Avalos fprintf(stderr, "Failed to %slock agent: %s\n", 573e9778795SPeter Avalos lock ? "" : "un", ssh_err(r)); 574e9778795SPeter Avalos } 575e9778795SPeter Avalos } 5760cbfa66cSDaniel Fojt freezero(p1, strlen(p1)); 57718de8d7fSPeter Avalos return (ret); 57818de8d7fSPeter Avalos } 57918de8d7fSPeter Avalos 58018de8d7fSPeter Avalos static int 5810cbfa66cSDaniel Fojt load_resident_keys(int agent_fd, const char *skprovider, int qflag) 5820cbfa66cSDaniel Fojt { 5830cbfa66cSDaniel Fojt struct sshkey **keys; 5840cbfa66cSDaniel Fojt size_t nkeys, i; 5850cbfa66cSDaniel Fojt int r, ok = 0; 5860cbfa66cSDaniel Fojt char *fp; 5870cbfa66cSDaniel Fojt 5880cbfa66cSDaniel Fojt pass = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN); 5890cbfa66cSDaniel Fojt if ((r = sshsk_load_resident(skprovider, NULL, pass, 5900cbfa66cSDaniel Fojt &keys, &nkeys)) != 0) { 591*50a69bb5SSascha Wildner error_r(r, "Unable to load resident keys"); 5920cbfa66cSDaniel Fojt return r; 5930cbfa66cSDaniel Fojt } 5940cbfa66cSDaniel Fojt for (i = 0; i < nkeys; i++) { 5950cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(keys[i], 5960cbfa66cSDaniel Fojt fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 597*50a69bb5SSascha Wildner fatal_f("sshkey_fingerprint failed"); 5980cbfa66cSDaniel Fojt if ((r = ssh_add_identity_constrained(agent_fd, keys[i], "", 5990cbfa66cSDaniel Fojt lifetime, confirm, maxsign, skprovider)) != 0) { 6000cbfa66cSDaniel Fojt error("Unable to add key %s %s", 6010cbfa66cSDaniel Fojt sshkey_type(keys[i]), fp); 6020cbfa66cSDaniel Fojt free(fp); 6030cbfa66cSDaniel Fojt ok = r; 6040cbfa66cSDaniel Fojt continue; 6050cbfa66cSDaniel Fojt } 6060cbfa66cSDaniel Fojt if (ok == 0) 6070cbfa66cSDaniel Fojt ok = 1; 6080cbfa66cSDaniel Fojt if (!qflag) { 6090cbfa66cSDaniel Fojt fprintf(stderr, "Resident identity added: %s %s\n", 6100cbfa66cSDaniel Fojt sshkey_type(keys[i]), fp); 6110cbfa66cSDaniel Fojt if (lifetime != 0) { 6120cbfa66cSDaniel Fojt fprintf(stderr, 613*50a69bb5SSascha Wildner "Lifetime set to %d seconds\n", lifetime); 6140cbfa66cSDaniel Fojt } 6150cbfa66cSDaniel Fojt if (confirm != 0) { 6160cbfa66cSDaniel Fojt fprintf(stderr, "The user must confirm " 6170cbfa66cSDaniel Fojt "each use of the key\n"); 6180cbfa66cSDaniel Fojt } 6190cbfa66cSDaniel Fojt } 6200cbfa66cSDaniel Fojt free(fp); 6210cbfa66cSDaniel Fojt sshkey_free(keys[i]); 6220cbfa66cSDaniel Fojt } 6230cbfa66cSDaniel Fojt free(keys); 6240cbfa66cSDaniel Fojt if (nkeys == 0) 6250cbfa66cSDaniel Fojt return SSH_ERR_KEY_NOT_FOUND; 6260cbfa66cSDaniel Fojt return ok == 1 ? 0 : ok; 6270cbfa66cSDaniel Fojt } 6280cbfa66cSDaniel Fojt 6290cbfa66cSDaniel Fojt static int 6300cbfa66cSDaniel Fojt do_file(int agent_fd, int deleting, int key_only, char *file, int qflag, 6310cbfa66cSDaniel Fojt const char *skprovider) 63218de8d7fSPeter Avalos { 63318de8d7fSPeter Avalos if (deleting) { 634ce74bacaSMatthew Dillon if (delete_file(agent_fd, file, key_only, qflag) == -1) 63518de8d7fSPeter Avalos return -1; 63618de8d7fSPeter Avalos } else { 6370cbfa66cSDaniel Fojt if (add_file(agent_fd, file, key_only, qflag, skprovider) == -1) 63818de8d7fSPeter Avalos return -1; 63918de8d7fSPeter Avalos } 64018de8d7fSPeter Avalos return 0; 64118de8d7fSPeter Avalos } 64218de8d7fSPeter Avalos 64318de8d7fSPeter Avalos static void 64418de8d7fSPeter Avalos usage(void) 64518de8d7fSPeter Avalos { 6460cbfa66cSDaniel Fojt fprintf(stderr, 6470cbfa66cSDaniel Fojt "usage: ssh-add [-cDdKkLlqvXx] [-E fingerprint_hash] [-S provider] [-t life]\n" 6480cbfa66cSDaniel Fojt #ifdef WITH_XMSS 6490cbfa66cSDaniel Fojt " [-M maxsign] [-m minleft]\n" 6500cbfa66cSDaniel Fojt #endif 6510cbfa66cSDaniel Fojt " [file ...]\n" 6520cbfa66cSDaniel Fojt " ssh-add -s pkcs11\n" 6530cbfa66cSDaniel Fojt " ssh-add -e pkcs11\n" 6540cbfa66cSDaniel Fojt " ssh-add -T pubkey ...\n" 6550cbfa66cSDaniel Fojt ); 65618de8d7fSPeter Avalos } 65718de8d7fSPeter Avalos 65818de8d7fSPeter Avalos int 65918de8d7fSPeter Avalos main(int argc, char **argv) 66018de8d7fSPeter Avalos { 66118de8d7fSPeter Avalos extern char *optarg; 66218de8d7fSPeter Avalos extern int optind; 663e9778795SPeter Avalos int agent_fd; 6640cbfa66cSDaniel Fojt char *pkcs11provider = NULL, *skprovider = NULL; 6650cbfa66cSDaniel Fojt int r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0; 666664f4763Szrj int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0; 667664f4763Szrj SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 668664f4763Szrj LogLevel log_level = SYSLOG_LEVEL_INFO; 66918de8d7fSPeter Avalos 67018de8d7fSPeter Avalos /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 67118de8d7fSPeter Avalos sanitise_stdfd(); 67218de8d7fSPeter Avalos 67318de8d7fSPeter Avalos __progname = ssh_get_progname(argv[0]); 67418de8d7fSPeter Avalos seed_rng(); 67518de8d7fSPeter Avalos 676664f4763Szrj log_init(__progname, log_level, log_facility, 1); 67718de8d7fSPeter Avalos 678e9778795SPeter Avalos setvbuf(stdout, NULL, _IOLBF, 0); 67936e94dc5SPeter Avalos 680e9778795SPeter Avalos /* First, get a connection to the authentication agent. */ 681e9778795SPeter Avalos switch (r = ssh_get_authentication_socket(&agent_fd)) { 682e9778795SPeter Avalos case 0: 683e9778795SPeter Avalos break; 684e9778795SPeter Avalos case SSH_ERR_AGENT_NOT_PRESENT: 685e9778795SPeter Avalos fprintf(stderr, "Could not open a connection to your " 686e9778795SPeter Avalos "authentication agent.\n"); 687e9778795SPeter Avalos exit(2); 688e9778795SPeter Avalos default: 689e9778795SPeter Avalos fprintf(stderr, "Error connecting to agent: %s\n", ssh_err(r)); 69018de8d7fSPeter Avalos exit(2); 69118de8d7fSPeter Avalos } 692e9778795SPeter Avalos 6930cbfa66cSDaniel Fojt skprovider = getenv("SSH_SK_PROVIDER"); 6940cbfa66cSDaniel Fojt 6950cbfa66cSDaniel Fojt while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:M:m:qs:S:t:")) != -1) { 69618de8d7fSPeter Avalos switch (ch) { 697664f4763Szrj case 'v': 698664f4763Szrj if (log_level == SYSLOG_LEVEL_INFO) 699664f4763Szrj log_level = SYSLOG_LEVEL_DEBUG1; 700664f4763Szrj else if (log_level < SYSLOG_LEVEL_DEBUG3) 701664f4763Szrj log_level++; 702664f4763Szrj break; 703e9778795SPeter Avalos case 'E': 704e9778795SPeter Avalos fingerprint_hash = ssh_digest_alg_by_name(optarg); 705e9778795SPeter Avalos if (fingerprint_hash == -1) 706e9778795SPeter Avalos fatal("Invalid hash algorithm \"%s\"", optarg); 707e9778795SPeter Avalos break; 70899e85e0dSPeter Avalos case 'k': 70999e85e0dSPeter Avalos key_only = 1; 71099e85e0dSPeter Avalos break; 7110cbfa66cSDaniel Fojt case 'K': 7120cbfa66cSDaniel Fojt do_download = 1; 7130cbfa66cSDaniel Fojt break; 71418de8d7fSPeter Avalos case 'l': 71518de8d7fSPeter Avalos case 'L': 716e9778795SPeter Avalos if (lflag != 0) 717e9778795SPeter Avalos fatal("-%c flag already specified", lflag); 718e9778795SPeter Avalos lflag = ch; 719e9778795SPeter Avalos break; 72018de8d7fSPeter Avalos case 'x': 72118de8d7fSPeter Avalos case 'X': 722e9778795SPeter Avalos if (xflag != 0) 723e9778795SPeter Avalos fatal("-%c flag already specified", xflag); 724e9778795SPeter Avalos xflag = ch; 725e9778795SPeter Avalos break; 72618de8d7fSPeter Avalos case 'c': 72718de8d7fSPeter Avalos confirm = 1; 72818de8d7fSPeter Avalos break; 729664f4763Szrj case 'm': 730664f4763Szrj minleft = (int)strtonum(optarg, 1, UINT_MAX, NULL); 731664f4763Szrj if (minleft == 0) { 732664f4763Szrj usage(); 733664f4763Szrj ret = 1; 734664f4763Szrj goto done; 735664f4763Szrj } 736664f4763Szrj break; 737664f4763Szrj case 'M': 738664f4763Szrj maxsign = (int)strtonum(optarg, 1, UINT_MAX, NULL); 739664f4763Szrj if (maxsign == 0) { 740664f4763Szrj usage(); 741664f4763Szrj ret = 1; 742664f4763Szrj goto done; 743664f4763Szrj } 744664f4763Szrj break; 74518de8d7fSPeter Avalos case 'd': 74618de8d7fSPeter Avalos deleting = 1; 74718de8d7fSPeter Avalos break; 74818de8d7fSPeter Avalos case 'D': 749e9778795SPeter Avalos Dflag = 1; 750e9778795SPeter Avalos break; 75118de8d7fSPeter Avalos case 's': 752856ea928SPeter Avalos pkcs11provider = optarg; 75318de8d7fSPeter Avalos break; 7540cbfa66cSDaniel Fojt case 'S': 7550cbfa66cSDaniel Fojt skprovider = optarg; 7560cbfa66cSDaniel Fojt break; 75718de8d7fSPeter Avalos case 'e': 75818de8d7fSPeter Avalos deleting = 1; 759856ea928SPeter Avalos pkcs11provider = optarg; 76018de8d7fSPeter Avalos break; 76118de8d7fSPeter Avalos case 't': 7620cbfa66cSDaniel Fojt if ((lifetime = convtime(optarg)) == -1 || 7630cbfa66cSDaniel Fojt lifetime < 0 || (u_long)lifetime > UINT32_MAX) { 76418de8d7fSPeter Avalos fprintf(stderr, "Invalid lifetime\n"); 76518de8d7fSPeter Avalos ret = 1; 76618de8d7fSPeter Avalos goto done; 76718de8d7fSPeter Avalos } 76818de8d7fSPeter Avalos break; 769ce74bacaSMatthew Dillon case 'q': 770ce74bacaSMatthew Dillon qflag = 1; 771ce74bacaSMatthew Dillon break; 772664f4763Szrj case 'T': 773664f4763Szrj Tflag = 1; 774664f4763Szrj break; 77518de8d7fSPeter Avalos default: 77618de8d7fSPeter Avalos usage(); 77718de8d7fSPeter Avalos ret = 1; 77818de8d7fSPeter Avalos goto done; 77918de8d7fSPeter Avalos } 78018de8d7fSPeter Avalos } 781664f4763Szrj log_init(__progname, log_level, log_facility, 1); 782e9778795SPeter Avalos 783e9778795SPeter Avalos if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1) 784e9778795SPeter Avalos fatal("Invalid combination of actions"); 785e9778795SPeter Avalos else if (xflag) { 786e9778795SPeter Avalos if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1) 787e9778795SPeter Avalos ret = 1; 788e9778795SPeter Avalos goto done; 789e9778795SPeter Avalos } else if (lflag) { 790e9778795SPeter Avalos if (list_identities(agent_fd, lflag == 'l' ? 1 : 0) == -1) 791e9778795SPeter Avalos ret = 1; 792e9778795SPeter Avalos goto done; 793e9778795SPeter Avalos } else if (Dflag) { 794664f4763Szrj if (delete_all(agent_fd, qflag) == -1) 795e9778795SPeter Avalos ret = 1; 796e9778795SPeter Avalos goto done; 797e9778795SPeter Avalos } 798e9778795SPeter Avalos 7990cbfa66cSDaniel Fojt #ifdef ENABLE_SK_INTERNAL 8000cbfa66cSDaniel Fojt if (skprovider == NULL) 8010cbfa66cSDaniel Fojt skprovider = "internal"; 8020cbfa66cSDaniel Fojt #endif 8030cbfa66cSDaniel Fojt 80418de8d7fSPeter Avalos argc -= optind; 80518de8d7fSPeter Avalos argv += optind; 806664f4763Szrj if (Tflag) { 807664f4763Szrj if (argc <= 0) 808664f4763Szrj fatal("no keys to test"); 809664f4763Szrj for (r = i = 0; i < argc; i++) 810664f4763Szrj r |= test_key(agent_fd, argv[i]); 811664f4763Szrj ret = r == 0 ? 0 : 1; 812664f4763Szrj goto done; 813664f4763Szrj } 814856ea928SPeter Avalos if (pkcs11provider != NULL) { 815664f4763Szrj if (update_card(agent_fd, !deleting, pkcs11provider, 816664f4763Szrj qflag) == -1) 81718de8d7fSPeter Avalos ret = 1; 81818de8d7fSPeter Avalos goto done; 81918de8d7fSPeter Avalos } 8200cbfa66cSDaniel Fojt if (do_download) { 8210cbfa66cSDaniel Fojt if (skprovider == NULL) 8220cbfa66cSDaniel Fojt fatal("Cannot download keys without provider"); 8230cbfa66cSDaniel Fojt if (load_resident_keys(agent_fd, skprovider, qflag) != 0) 8240cbfa66cSDaniel Fojt ret = 1; 8250cbfa66cSDaniel Fojt goto done; 8260cbfa66cSDaniel Fojt } 82718de8d7fSPeter Avalos if (argc == 0) { 828e9778795SPeter Avalos char buf[PATH_MAX]; 82918de8d7fSPeter Avalos struct passwd *pw; 83018de8d7fSPeter Avalos struct stat st; 83118de8d7fSPeter Avalos int count = 0; 83218de8d7fSPeter Avalos 83318de8d7fSPeter Avalos if ((pw = getpwuid(getuid())) == NULL) { 83418de8d7fSPeter Avalos fprintf(stderr, "No user found with uid %u\n", 83518de8d7fSPeter Avalos (u_int)getuid()); 83618de8d7fSPeter Avalos ret = 1; 83718de8d7fSPeter Avalos goto done; 83818de8d7fSPeter Avalos } 83918de8d7fSPeter Avalos 84018de8d7fSPeter Avalos for (i = 0; default_files[i]; i++) { 84118de8d7fSPeter Avalos snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, 84218de8d7fSPeter Avalos default_files[i]); 8430cbfa66cSDaniel Fojt if (stat(buf, &st) == -1) 84418de8d7fSPeter Avalos continue; 845ce74bacaSMatthew Dillon if (do_file(agent_fd, deleting, key_only, buf, 8460cbfa66cSDaniel Fojt qflag, skprovider) == -1) 84718de8d7fSPeter Avalos ret = 1; 84818de8d7fSPeter Avalos else 84918de8d7fSPeter Avalos count++; 85018de8d7fSPeter Avalos } 85118de8d7fSPeter Avalos if (count == 0) 85218de8d7fSPeter Avalos ret = 1; 85318de8d7fSPeter Avalos } else { 85418de8d7fSPeter Avalos for (i = 0; i < argc; i++) { 855e9778795SPeter Avalos if (do_file(agent_fd, deleting, key_only, 8560cbfa66cSDaniel Fojt argv[i], qflag, skprovider) == -1) 85718de8d7fSPeter Avalos ret = 1; 85818de8d7fSPeter Avalos } 85918de8d7fSPeter Avalos } 86018de8d7fSPeter Avalos done: 8610cbfa66cSDaniel Fojt clear_pass(); 862e9778795SPeter Avalos ssh_close_authentication_socket(agent_fd); 86318de8d7fSPeter Avalos return ret; 86418de8d7fSPeter Avalos } 865