blob: 7f775ff165c4df9d1bc397f3dbacdfaf4b3cd3a0 [file] [log] [blame]
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001/* $OpenBSD: ssh-keygen.c,v 1.251 2014/12/21 22:27:56 djm Exp $ */
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002/*
Damien Miller95def091999-11-25 00:26:21 +11003 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5 * All rights reserved
Damien Miller95def091999-11-25 00:26:21 +11006 * Identity and host key generation and maintenance.
Damien Millere4340be2000-09-16 13:29:08 +11007 *
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".
Damien Miller95def091999-11-25 00:26:21 +110013 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100014
15#include "includes.h"
Damien Millerf17883e2006-03-15 11:45:54 +110016
17#include <sys/types.h>
Damien Millere3b60b52006-07-10 21:08:03 +100018#include <sys/socket.h>
Damien Millerf17883e2006-03-15 11:45:54 +110019#include <sys/stat.h>
Damien Miller8dbffe72006-08-05 11:02:17 +100020#include <sys/param.h>
Damien Millerd4a8b7e1999-10-27 13:42:43 +100021
Damien Miller72ef7c12015-01-15 02:21:31 +110022#ifdef WITH_OPENSSL
Damien Millereba71ba2000-04-29 23:57:08 +100023#include <openssl/evp.h>
24#include <openssl/pem.h>
Darren Tuckerbfaaf962008-02-28 19:13:52 +110025#include "openbsd-compat/openssl-compat.h"
Damien Miller72ef7c12015-01-15 02:21:31 +110026#endif
Damien Millereba71ba2000-04-29 23:57:08 +100027
Darren Tucker39972492006-07-12 22:22:46 +100028#include <errno.h>
Damien Miller57cf6382006-07-10 21:13:46 +100029#include <fcntl.h>
Damien Millerb8fe89c2006-07-24 14:51:00 +100030#include <netdb.h>
Darren Tucker2ee50c52006-07-11 18:55:05 +100031#ifdef HAVE_PATHS_H
32# include <paths.h>
33#endif
Damien Miller9f2abc42006-07-10 20:53:08 +100034#include <pwd.h>
Damien Millerded319c2006-09-01 15:38:36 +100035#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100036#include <stdio.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100037#include <stdlib.h>
Damien Millere3476ed2006-07-24 14:13:33 +100038#include <string.h>
Damien Millere6b3b612006-07-24 14:01:23 +100039#include <unistd.h>
Damien Miller9f2abc42006-07-10 20:53:08 +100040
Damien Millerd4a8b7e1999-10-27 13:42:43 +100041#include "xmalloc.h"
Damien Millereba71ba2000-04-29 23:57:08 +100042#include "key.h"
Ben Lindstromd09fcf52001-03-29 00:29:54 +000043#include "rsa.h"
Damien Millereba71ba2000-04-29 23:57:08 +100044#include "authfile.h"
45#include "uuencode.h"
Damien Miller874d77b2000-10-14 16:23:11 +110046#include "buffer.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000047#include "pathnames.h"
48#include "log.h"
Darren Tuckere608ca22004-05-13 16:15:47 +100049#include "misc.h"
Damien Miller4b42d7f2005-03-01 21:48:35 +110050#include "match.h"
51#include "hostfile.h"
Damien Miller69996102006-07-10 20:53:31 +100052#include "dns.h"
Damien Millerf3747bf2013-01-18 11:44:04 +110053#include "ssh.h"
Damien Miller0a80ca12010-02-27 07:55:05 +110054#include "ssh2.h"
Damien Miller7ea845e2010-02-12 09:21:02 +110055#include "ssh-pkcs11.h"
Damien Millerf3747bf2013-01-18 11:44:04 +110056#include "atomicio.h"
57#include "krl.h"
djm@openbsd.org56d1c832014-12-21 22:27:55 +000058#include "digest.h"
Ben Lindstromcd392282001-07-04 03:44:03 +000059
Damien Miller3f54a9f2005-11-05 14:52:18 +110060/* Number of bits in the RSA/DSA key. This value can be set on the command line. */
61#define DEFAULT_BITS 2048
62#define DEFAULT_BITS_DSA 1024
Damien Miller6e9f6802010-09-10 11:17:38 +100063#define DEFAULT_BITS_ECDSA 256
Damien Miller3f54a9f2005-11-05 14:52:18 +110064u_int32_t bits = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100065
Damien Miller5428f641999-11-25 11:54:57 +110066/*
67 * Flag indicating that we just want to change the passphrase. This can be
68 * set on the command line.
69 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100070int change_passphrase = 0;
71
Damien Miller5428f641999-11-25 11:54:57 +110072/*
73 * Flag indicating that we just want to change the comment. This can be set
74 * on the command line.
75 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100076int change_comment = 0;
77
78int quiet = 0;
79
Darren Tucker35c45532008-06-13 04:43:15 +100080int log_level = SYSLOG_LEVEL_INFO;
81
Damien Miller4b42d7f2005-03-01 21:48:35 +110082/* Flag indicating that we want to hash a known_hosts file */
83int hash_hosts = 0;
84/* Flag indicating that we want lookup a host in known_hosts file */
85int find_host = 0;
86/* Flag indicating that we want to delete a host from a known_hosts file */
87int delete_host = 0;
88
Damien Millerf2b70ca2010-03-05 07:39:35 +110089/* Flag indicating that we want to show the contents of a certificate */
90int show_cert = 0;
91
Damien Miller10f6f6b1999-11-17 17:29:08 +110092/* Flag indicating that we just want to see the key fingerprint */
93int print_fingerprint = 0;
Ben Lindstrom8fd372b2001-03-12 03:02:17 +000094int print_bubblebabble = 0;
Damien Miller10f6f6b1999-11-17 17:29:08 +110095
djm@openbsd.org56d1c832014-12-21 22:27:55 +000096/* Hash algorithm to use for fingerprints. */
97int fingerprint_hash = SSH_FP_HASH_DEFAULT;
98
Damien Miller431f66b1999-11-21 18:31:57 +110099/* The identity file name, given on the command line or entered by the user. */
100char identity_file[1024];
101int have_identity = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000102
103/* This is set to the passphrase if given on the command line. */
104char *identity_passphrase = NULL;
105
106/* This is set to the new passphrase if given on the command line. */
107char *identity_new_passphrase = NULL;
108
109/* This is set to the new comment if given on the command line. */
110char *identity_comment = NULL;
111
Damien Miller0a80ca12010-02-27 07:55:05 +1100112/* Path to CA key when certifying keys. */
113char *ca_key_path = NULL;
114
Damien Miller4e270b02010-04-16 15:56:21 +1000115/* Certificate serial number */
Damien Miller55aca022012-12-03 11:25:30 +1100116unsigned long long cert_serial = 0;
Damien Miller4e270b02010-04-16 15:56:21 +1000117
Damien Miller0a80ca12010-02-27 07:55:05 +1100118/* Key type when certifying */
119u_int cert_key_type = SSH2_CERT_TYPE_USER;
120
121/* "key ID" of signed key */
122char *cert_key_id = NULL;
123
124/* Comma-separated list of principal names for certifying keys */
125char *cert_principals = NULL;
126
127/* Validity period for certificates */
128u_int64_t cert_valid_from = 0;
129u_int64_t cert_valid_to = ~0ULL;
130
Damien Miller4e270b02010-04-16 15:56:21 +1000131/* Certificate options */
Damien Millerd0e4a8e2010-05-21 14:58:32 +1000132#define CERTOPT_X_FWD (1)
133#define CERTOPT_AGENT_FWD (1<<1)
134#define CERTOPT_PORT_FWD (1<<2)
135#define CERTOPT_PTY (1<<3)
136#define CERTOPT_USER_RC (1<<4)
137#define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \
138 CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC)
139u_int32_t certflags_flags = CERTOPT_DEFAULT;
140char *certflags_command = NULL;
141char *certflags_src_addr = NULL;
Damien Miller0a80ca12010-02-27 07:55:05 +1100142
Damien Miller44b25042010-07-02 13:35:01 +1000143/* Conversion to/from various formats */
144int convert_to = 0;
145int convert_from = 0;
146enum {
147 FMT_RFC4716,
148 FMT_PKCS8,
149 FMT_PEM
150} convert_format = FMT_RFC4716;
Damien Millereba71ba2000-04-29 23:57:08 +1000151int print_public = 0;
Damien Miller37876e92003-05-15 10:19:46 +1000152int print_generic = 0;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100153
Damien Millera41c8b12002-01-22 23:05:08 +1100154char *key_type_name = NULL;
Damien Millereba71ba2000-04-29 23:57:08 +1000155
Damien Miller757f34e2010-08-05 13:05:31 +1000156/* Load key from this PKCS#11 provider */
157char *pkcs11provider = NULL;
Damien Miller44b25042010-07-02 13:35:01 +1000158
Damien Millerbcd00ab2013-12-07 10:41:55 +1100159/* Use new OpenSSH private key format when writing SSH2 keys instead of PEM */
160int use_new_format = 0;
161
162/* Cipher for new-format private keys */
163char *new_format_cipher = NULL;
164
165/*
166 * Number of KDF rounds to derive new format keys /
167 * number of primality trials when screening moduli.
168 */
169int rounds = 0;
170
Damien Miller431f66b1999-11-21 18:31:57 +1100171/* argv0 */
172extern char *__progname;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000173
Damien Millere5c0d522014-07-03 21:24:19 +1000174char hostname[NI_MAXHOST];
Damien Millereba71ba2000-04-29 23:57:08 +1000175
Darren Tucker770fc012004-05-13 16:24:32 +1000176/* moduli.c */
Damien Millerb089fb52005-05-26 12:16:18 +1000177int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *);
Damien Millerdfceafe2012-07-06 13:44:19 +1000178int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long,
179 unsigned long);
Darren Tucker770fc012004-05-13 16:24:32 +1000180
Ben Lindstrombba81212001-06-25 05:01:22 +0000181static void
Damien Miller884b63a2011-05-05 14:14:52 +1000182type_bits_valid(int type, u_int32_t *bitsp)
Damien Miller58f1baf2011-05-05 14:06:15 +1000183{
Damien Miller72ef7c12015-01-15 02:21:31 +1100184#ifdef WITH_OPENSSL
Damien Miller58f1baf2011-05-05 14:06:15 +1000185 u_int maxbits;
Damien Miller72ef7c12015-01-15 02:21:31 +1100186#endif
Damien Miller58f1baf2011-05-05 14:06:15 +1000187
188 if (type == KEY_UNSPEC) {
189 fprintf(stderr, "unknown key type %s\n", key_type_name);
190 exit(1);
191 }
Damien Miller884b63a2011-05-05 14:14:52 +1000192 if (*bitsp == 0) {
Damien Miller58f1baf2011-05-05 14:06:15 +1000193 if (type == KEY_DSA)
Damien Miller884b63a2011-05-05 14:14:52 +1000194 *bitsp = DEFAULT_BITS_DSA;
Damien Miller58f1baf2011-05-05 14:06:15 +1000195 else if (type == KEY_ECDSA)
Damien Miller884b63a2011-05-05 14:14:52 +1000196 *bitsp = DEFAULT_BITS_ECDSA;
Damien Miller58f1baf2011-05-05 14:06:15 +1000197 else
Damien Miller884b63a2011-05-05 14:14:52 +1000198 *bitsp = DEFAULT_BITS;
Damien Miller58f1baf2011-05-05 14:06:15 +1000199 }
Damien Miller72ef7c12015-01-15 02:21:31 +1100200#ifdef WITH_OPENSSL
Damien Miller58f1baf2011-05-05 14:06:15 +1000201 maxbits = (type == KEY_DSA) ?
202 OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS;
Damien Miller884b63a2011-05-05 14:14:52 +1000203 if (*bitsp > maxbits) {
Damien Miller58f1baf2011-05-05 14:06:15 +1000204 fprintf(stderr, "key bits exceeds maximum %d\n", maxbits);
205 exit(1);
206 }
Damien Miller884b63a2011-05-05 14:14:52 +1000207 if (type == KEY_DSA && *bitsp != 1024)
Damien Miller58f1baf2011-05-05 14:06:15 +1000208 fatal("DSA keys must be 1024 bits");
Damien Miller5be9d9e2013-12-07 11:24:01 +1100209 else if (type != KEY_ECDSA && type != KEY_ED25519 && *bitsp < 768)
Damien Miller58f1baf2011-05-05 14:06:15 +1000210 fatal("Key must at least be 768 bits");
Damien Miller884b63a2011-05-05 14:14:52 +1000211 else if (type == KEY_ECDSA && key_ecdsa_bits_to_nid(*bitsp) == -1)
Damien Miller58f1baf2011-05-05 14:06:15 +1000212 fatal("Invalid ECDSA key length - valid lengths are "
213 "256, 384 or 521 bits");
Damien Miller1f0311c2014-05-15 14:24:09 +1000214#endif
Damien Miller58f1baf2011-05-05 14:06:15 +1000215}
216
217static void
Damien Miller431f66b1999-11-21 18:31:57 +1100218ask_filename(struct passwd *pw, const char *prompt)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000219{
Damien Miller95def091999-11-25 00:26:21 +1100220 char buf[1024];
Damien Millere39cacc2000-11-29 12:18:44 +1100221 char *name = NULL;
222
Damien Miller993dd552002-02-19 15:22:47 +1100223 if (key_type_name == NULL)
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000224 name = _PATH_SSH_CLIENT_ID_RSA;
Damien Miller90967402006-03-26 14:07:26 +1100225 else {
Damien Miller993dd552002-02-19 15:22:47 +1100226 switch (key_type_from_name(key_type_name)) {
227 case KEY_RSA1:
228 name = _PATH_SSH_CLIENT_IDENTITY;
229 break;
Damien Miller4e270b02010-04-16 15:56:21 +1000230 case KEY_DSA_CERT:
231 case KEY_DSA_CERT_V00:
Damien Miller993dd552002-02-19 15:22:47 +1100232 case KEY_DSA:
233 name = _PATH_SSH_CLIENT_ID_DSA;
234 break;
Damien Millerdd190dd2010-11-11 14:17:02 +1100235#ifdef OPENSSL_HAS_ECC
Damien Millereb8b60e2010-08-31 22:41:14 +1000236 case KEY_ECDSA_CERT:
237 case KEY_ECDSA:
238 name = _PATH_SSH_CLIENT_ID_ECDSA;
239 break;
Damien Millerdd190dd2010-11-11 14:17:02 +1100240#endif
Damien Miller4e270b02010-04-16 15:56:21 +1000241 case KEY_RSA_CERT:
242 case KEY_RSA_CERT_V00:
Damien Miller993dd552002-02-19 15:22:47 +1100243 case KEY_RSA:
244 name = _PATH_SSH_CLIENT_ID_RSA;
245 break;
Damien Miller5be9d9e2013-12-07 11:24:01 +1100246 case KEY_ED25519:
247 case KEY_ED25519_CERT:
248 name = _PATH_SSH_CLIENT_ID_ED25519;
249 break;
Damien Miller993dd552002-02-19 15:22:47 +1100250 default:
Damien Miller9eab9562009-02-22 08:47:02 +1100251 fprintf(stderr, "bad key type\n");
Damien Miller993dd552002-02-19 15:22:47 +1100252 exit(1);
253 break;
254 }
Damien Miller90967402006-03-26 14:07:26 +1100255 }
Damien Millere39cacc2000-11-29 12:18:44 +1100256 snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name);
Ben Lindstrom3deda8b2000-12-22 20:27:43 +0000257 fprintf(stderr, "%s (%s): ", prompt, identity_file);
Damien Miller95def091999-11-25 00:26:21 +1100258 if (fgets(buf, sizeof(buf), stdin) == NULL)
259 exit(1);
Damien Miller14b017d2007-09-17 16:09:15 +1000260 buf[strcspn(buf, "\n")] = '\0';
Damien Miller95def091999-11-25 00:26:21 +1100261 if (strcmp(buf, "") != 0)
262 strlcpy(identity_file, buf, sizeof(identity_file));
263 have_identity = 1;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100264}
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000265
Ben Lindstrombba81212001-06-25 05:01:22 +0000266static Key *
Ben Lindstromd78ae762001-06-05 20:35:09 +0000267load_identity(char *filename)
Damien Millereba71ba2000-04-29 23:57:08 +1000268{
Ben Lindstromd0fca422001-03-26 13:44:06 +0000269 char *pass;
270 Key *prv;
271
Ben Lindstroma3700052001-04-05 23:26:32 +0000272 prv = key_load_private(filename, "", NULL);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000273 if (prv == NULL) {
Ben Lindstromd78ae762001-06-05 20:35:09 +0000274 if (identity_passphrase)
275 pass = xstrdup(identity_passphrase);
276 else
Ben Lindstrom949974b2001-06-25 05:20:31 +0000277 pass = read_passphrase("Enter passphrase: ",
278 RP_ALLOW_STDIN);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000279 prv = key_load_private(filename, pass, NULL);
Damien Millera5103f42014-02-04 11:20:14 +1100280 explicit_bzero(pass, strlen(pass));
Darren Tuckera627d422013-06-02 07:31:17 +1000281 free(pass);
Damien Millereba71ba2000-04-29 23:57:08 +1000282 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000283 return prv;
Damien Millereba71ba2000-04-29 23:57:08 +1000284}
285
Damien Miller874d77b2000-10-14 16:23:11 +1100286#define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
Ben Lindstromcb72e4f2002-06-21 00:41:51 +0000287#define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----"
Damien Miller874d77b2000-10-14 16:23:11 +1100288#define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
Kevin Stevesef4eea92001-02-05 12:42:17 +0000289#define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb
Damien Millereba71ba2000-04-29 23:57:08 +1000290
Damien Miller1f0311c2014-05-15 14:24:09 +1000291#ifdef WITH_OPENSSL
Ben Lindstrombba81212001-06-25 05:01:22 +0000292static void
Damien Miller44b25042010-07-02 13:35:01 +1000293do_convert_to_ssh2(struct passwd *pw, Key *k)
Damien Millereba71ba2000-04-29 23:57:08 +1000294{
Ben Lindstromc58ab022002-02-26 18:15:09 +0000295 u_int len;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000296 u_char *blob;
Darren Tuckerd04758d2010-01-12 19:41:57 +1100297 char comment[61];
Damien Millereba71ba2000-04-29 23:57:08 +1000298
Damien Millera563cce2012-04-22 11:07:28 +1000299 if (k->type == KEY_RSA1) {
300 fprintf(stderr, "version 1 keys are not supported\n");
301 exit(1);
302 }
Ben Lindstrom99a30f12001-09-18 05:49:14 +0000303 if (key_to_blob(k, &blob, &len) <= 0) {
304 fprintf(stderr, "key_to_blob failed\n");
305 exit(1);
306 }
Darren Tuckerd04758d2010-01-12 19:41:57 +1100307 /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */
308 snprintf(comment, sizeof(comment),
309 "%u-bit %s, converted by %s@%s from OpenSSH",
Ben Lindstrom46c264f2001-04-24 16:56:58 +0000310 key_size(k), key_type(k),
Damien Millereba71ba2000-04-29 23:57:08 +1000311 pw->pw_name, hostname);
Darren Tuckerd04758d2010-01-12 19:41:57 +1100312
313 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
314 fprintf(stdout, "Comment: \"%s\"\n", comment);
Damien Millereba71ba2000-04-29 23:57:08 +1000315 dump_base64(stdout, blob, len);
Damien Miller874d77b2000-10-14 16:23:11 +1100316 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
Ben Lindstrom46c264f2001-04-24 16:56:58 +0000317 key_free(k);
Darren Tuckera627d422013-06-02 07:31:17 +1000318 free(blob);
Damien Millereba71ba2000-04-29 23:57:08 +1000319 exit(0);
320}
321
Ben Lindstrombba81212001-06-25 05:01:22 +0000322static void
Damien Miller44b25042010-07-02 13:35:01 +1000323do_convert_to_pkcs8(Key *k)
324{
325 switch (key_type_plain(k->type)) {
Damien Millera563cce2012-04-22 11:07:28 +1000326 case KEY_RSA1:
Damien Miller44b25042010-07-02 13:35:01 +1000327 case KEY_RSA:
328 if (!PEM_write_RSA_PUBKEY(stdout, k->rsa))
329 fatal("PEM_write_RSA_PUBKEY failed");
330 break;
331 case KEY_DSA:
332 if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
333 fatal("PEM_write_DSA_PUBKEY failed");
334 break;
Damien Miller6af914a2010-09-10 11:39:26 +1000335#ifdef OPENSSL_HAS_ECC
Damien Millereb8b60e2010-08-31 22:41:14 +1000336 case KEY_ECDSA:
337 if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
338 fatal("PEM_write_EC_PUBKEY failed");
339 break;
Damien Miller6af914a2010-09-10 11:39:26 +1000340#endif
Damien Miller44b25042010-07-02 13:35:01 +1000341 default:
342 fatal("%s: unsupported key type %s", __func__, key_type(k));
343 }
344 exit(0);
345}
346
347static void
348do_convert_to_pem(Key *k)
349{
350 switch (key_type_plain(k->type)) {
Damien Millera563cce2012-04-22 11:07:28 +1000351 case KEY_RSA1:
Damien Miller44b25042010-07-02 13:35:01 +1000352 case KEY_RSA:
353 if (!PEM_write_RSAPublicKey(stdout, k->rsa))
354 fatal("PEM_write_RSAPublicKey failed");
355 break;
356#if notyet /* OpenSSH 0.9.8 lacks this function */
357 case KEY_DSA:
358 if (!PEM_write_DSAPublicKey(stdout, k->dsa))
359 fatal("PEM_write_DSAPublicKey failed");
360 break;
361#endif
Damien Millereb8b60e2010-08-31 22:41:14 +1000362 /* XXX ECDSA? */
Damien Miller44b25042010-07-02 13:35:01 +1000363 default:
364 fatal("%s: unsupported key type %s", __func__, key_type(k));
365 }
366 exit(0);
367}
368
369static void
370do_convert_to(struct passwd *pw)
371{
372 Key *k;
373 struct stat st;
374
375 if (!have_identity)
376 ask_filename(pw, "Enter file in which the key is");
377 if (stat(identity_file, &st) < 0)
378 fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
379 if ((k = key_load_public(identity_file, NULL)) == NULL) {
380 if ((k = load_identity(identity_file)) == NULL) {
381 fprintf(stderr, "load failed\n");
382 exit(1);
383 }
384 }
Damien Miller44b25042010-07-02 13:35:01 +1000385
386 switch (convert_format) {
387 case FMT_RFC4716:
388 do_convert_to_ssh2(pw, k);
389 break;
390 case FMT_PKCS8:
391 do_convert_to_pkcs8(k);
392 break;
393 case FMT_PEM:
394 do_convert_to_pem(k);
395 break;
396 default:
397 fatal("%s: unknown key format %d", __func__, convert_format);
398 }
399 exit(0);
400}
401
402static void
Damien Miller874d77b2000-10-14 16:23:11 +1100403buffer_get_bignum_bits(Buffer *b, BIGNUM *value)
404{
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000405 u_int bignum_bits = buffer_get_int(b);
406 u_int bytes = (bignum_bits + 7) / 8;
Ben Lindstromd09fcf52001-03-29 00:29:54 +0000407
Damien Miller874d77b2000-10-14 16:23:11 +1100408 if (buffer_len(b) < bytes)
Ben Lindstromd09fcf52001-03-29 00:29:54 +0000409 fatal("buffer_get_bignum_bits: input buffer too small: "
410 "need %d have %d", bytes, buffer_len(b));
Darren Tucker0bc85572006-11-07 23:14:41 +1100411 if (BN_bin2bn(buffer_ptr(b), bytes, value) == NULL)
412 fatal("buffer_get_bignum_bits: BN_bin2bn failed");
Damien Miller874d77b2000-10-14 16:23:11 +1100413 buffer_consume(b, bytes);
414}
415
Ben Lindstrombba81212001-06-25 05:01:22 +0000416static Key *
Ben Lindstrom90fd8142002-02-26 18:09:42 +0000417do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
Damien Miller874d77b2000-10-14 16:23:11 +1100418{
419 Buffer b;
Damien Miller874d77b2000-10-14 16:23:11 +1100420 Key *key = NULL;
Damien Miller874d77b2000-10-14 16:23:11 +1100421 char *type, *cipher;
Damien Miller8f9cd702014-04-20 13:00:11 +1000422 u_char *sig = NULL, data[] = "abcde12345";
Ben Lindstrome586c4c2001-06-25 05:04:58 +0000423 int magic, rlen, ktype, i1, i2, i3, i4;
424 u_int slen;
425 u_long e;
Damien Miller874d77b2000-10-14 16:23:11 +1100426
427 buffer_init(&b);
428 buffer_append(&b, blob, blen);
429
Darren Tucker82a3d2b2007-02-19 22:10:25 +1100430 magic = buffer_get_int(&b);
Damien Miller874d77b2000-10-14 16:23:11 +1100431 if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
432 error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC);
433 buffer_free(&b);
434 return NULL;
435 }
Ben Lindstrom34f91882001-06-25 04:47:54 +0000436 i1 = buffer_get_int(&b);
Damien Miller874d77b2000-10-14 16:23:11 +1100437 type = buffer_get_string(&b, NULL);
438 cipher = buffer_get_string(&b, NULL);
Ben Lindstrom34f91882001-06-25 04:47:54 +0000439 i2 = buffer_get_int(&b);
440 i3 = buffer_get_int(&b);
441 i4 = buffer_get_int(&b);
Damien Miller80163902007-01-05 16:30:16 +1100442 debug("ignore (%d %d %d %d)", i1, i2, i3, i4);
Damien Miller874d77b2000-10-14 16:23:11 +1100443 if (strcmp(cipher, "none") != 0) {
444 error("unsupported cipher %s", cipher);
Darren Tuckera627d422013-06-02 07:31:17 +1000445 free(cipher);
Damien Miller874d77b2000-10-14 16:23:11 +1100446 buffer_free(&b);
Darren Tuckera627d422013-06-02 07:31:17 +1000447 free(type);
Damien Miller874d77b2000-10-14 16:23:11 +1100448 return NULL;
449 }
Darren Tuckera627d422013-06-02 07:31:17 +1000450 free(cipher);
Damien Miller874d77b2000-10-14 16:23:11 +1100451
Ben Lindstromd09fcf52001-03-29 00:29:54 +0000452 if (strstr(type, "dsa")) {
453 ktype = KEY_DSA;
454 } else if (strstr(type, "rsa")) {
455 ktype = KEY_RSA;
456 } else {
Darren Tucker7cfeecf2005-01-20 10:56:31 +1100457 buffer_free(&b);
Darren Tuckera627d422013-06-02 07:31:17 +1000458 free(type);
Damien Miller874d77b2000-10-14 16:23:11 +1100459 return NULL;
460 }
Ben Lindstromd09fcf52001-03-29 00:29:54 +0000461 key = key_new_private(ktype);
Darren Tuckera627d422013-06-02 07:31:17 +1000462 free(type);
Ben Lindstromd09fcf52001-03-29 00:29:54 +0000463
464 switch (key->type) {
465 case KEY_DSA:
466 buffer_get_bignum_bits(&b, key->dsa->p);
467 buffer_get_bignum_bits(&b, key->dsa->g);
468 buffer_get_bignum_bits(&b, key->dsa->q);
469 buffer_get_bignum_bits(&b, key->dsa->pub_key);
470 buffer_get_bignum_bits(&b, key->dsa->priv_key);
471 break;
472 case KEY_RSA:
Darren Tucker82a3d2b2007-02-19 22:10:25 +1100473 e = buffer_get_char(&b);
Ben Lindstrom34f91882001-06-25 04:47:54 +0000474 debug("e %lx", e);
475 if (e < 30) {
476 e <<= 8;
477 e += buffer_get_char(&b);
478 debug("e %lx", e);
479 e <<= 8;
480 e += buffer_get_char(&b);
481 debug("e %lx", e);
482 }
483 if (!BN_set_word(key->rsa->e, e)) {
Ben Lindstromd09fcf52001-03-29 00:29:54 +0000484 buffer_free(&b);
485 key_free(key);
486 return NULL;
487 }
488 buffer_get_bignum_bits(&b, key->rsa->d);
489 buffer_get_bignum_bits(&b, key->rsa->n);
490 buffer_get_bignum_bits(&b, key->rsa->iqmp);
491 buffer_get_bignum_bits(&b, key->rsa->q);
492 buffer_get_bignum_bits(&b, key->rsa->p);
Damien Miller86687062014-07-02 15:28:02 +1000493 if (rsa_generate_additional_parameters(key->rsa) != 0)
494 fatal("%s: rsa_generate_additional_parameters "
495 "error", __func__);
Ben Lindstromd09fcf52001-03-29 00:29:54 +0000496 break;
497 }
Damien Miller874d77b2000-10-14 16:23:11 +1100498 rlen = buffer_len(&b);
Ben Lindstrom1c37c6a2001-12-06 18:00:18 +0000499 if (rlen != 0)
Ben Lindstromd09fcf52001-03-29 00:29:54 +0000500 error("do_convert_private_ssh2_from_blob: "
501 "remaining bytes in key blob %d", rlen);
Damien Miller874d77b2000-10-14 16:23:11 +1100502 buffer_free(&b);
Ben Lindstromd09fcf52001-03-29 00:29:54 +0000503
Ben Lindstrome586c4c2001-06-25 05:04:58 +0000504 /* try the key */
505 key_sign(key, &sig, &slen, data, sizeof(data));
506 key_verify(key, sig, slen, data, sizeof(data));
Darren Tuckera627d422013-06-02 07:31:17 +1000507 free(sig);
Damien Miller874d77b2000-10-14 16:23:11 +1100508 return key;
509}
510
Damien Miller8056a9d2006-03-15 12:05:40 +1100511static int
512get_line(FILE *fp, char *line, size_t len)
513{
514 int c;
515 size_t pos = 0;
516
517 line[0] = '\0';
518 while ((c = fgetc(fp)) != EOF) {
519 if (pos >= len - 1) {
520 fprintf(stderr, "input line too long.\n");
521 exit(1);
522 }
Damien Miller90967402006-03-26 14:07:26 +1100523 switch (c) {
Damien Miller8056a9d2006-03-15 12:05:40 +1100524 case '\r':
525 c = fgetc(fp);
526 if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) {
527 fprintf(stderr, "unget: %s\n", strerror(errno));
528 exit(1);
529 }
530 return pos;
531 case '\n':
532 return pos;
533 }
534 line[pos++] = c;
535 line[pos] = '\0';
536 }
Damien Miller6c7439f2007-01-05 16:29:55 +1100537 /* We reached EOF */
538 return -1;
Damien Miller8056a9d2006-03-15 12:05:40 +1100539}
540
Ben Lindstrombba81212001-06-25 05:01:22 +0000541static void
Damien Miller44b25042010-07-02 13:35:01 +1000542do_convert_from_ssh2(struct passwd *pw, Key **k, int *private)
Damien Millereba71ba2000-04-29 23:57:08 +1000543{
Damien Millereba71ba2000-04-29 23:57:08 +1000544 int blen;
Ben Lindstrom155b9812002-04-02 20:26:26 +0000545 u_int len;
Damien Miller8056a9d2006-03-15 12:05:40 +1100546 char line[1024];
Ben Lindstrom9e0ddd42001-09-18 05:41:19 +0000547 u_char blob[8096];
Damien Millereba71ba2000-04-29 23:57:08 +1000548 char encoded[8096];
Damien Miller44b25042010-07-02 13:35:01 +1000549 int escaped = 0;
Damien Millereba71ba2000-04-29 23:57:08 +1000550 FILE *fp;
551
Damien Millerba3420a2010-06-26 09:39:07 +1000552 if ((fp = fopen(identity_file, "r")) == NULL)
553 fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
Damien Millereba71ba2000-04-29 23:57:08 +1000554 encoded[0] = '\0';
Damien Miller8056a9d2006-03-15 12:05:40 +1100555 while ((blen = get_line(fp, line, sizeof(line))) != -1) {
Damien Miller746d1a62013-07-18 16:13:02 +1000556 if (blen > 0 && line[blen - 1] == '\\')
Damien Miller30c3d422000-05-09 11:02:59 +1000557 escaped++;
Damien Millereba71ba2000-04-29 23:57:08 +1000558 if (strncmp(line, "----", 4) == 0 ||
559 strstr(line, ": ") != NULL) {
Damien Miller874d77b2000-10-14 16:23:11 +1100560 if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
Damien Miller44b25042010-07-02 13:35:01 +1000561 *private = 1;
Ben Lindstrome586c4c2001-06-25 05:04:58 +0000562 if (strstr(line, " END ") != NULL) {
563 break;
564 }
Ben Lindstrom30358602001-04-24 16:59:28 +0000565 /* fprintf(stderr, "ignore: %s", line); */
Damien Millereba71ba2000-04-29 23:57:08 +1000566 continue;
567 }
Damien Miller30c3d422000-05-09 11:02:59 +1000568 if (escaped) {
569 escaped--;
Ben Lindstrom30358602001-04-24 16:59:28 +0000570 /* fprintf(stderr, "escaped: %s", line); */
Damien Miller30c3d422000-05-09 11:02:59 +1000571 continue;
Damien Millereba71ba2000-04-29 23:57:08 +1000572 }
Damien Millereba71ba2000-04-29 23:57:08 +1000573 strlcat(encoded, line, sizeof(encoded));
574 }
Ben Lindstrom155b9812002-04-02 20:26:26 +0000575 len = strlen(encoded);
576 if (((len % 4) == 3) &&
577 (encoded[len-1] == '=') &&
578 (encoded[len-2] == '=') &&
579 (encoded[len-3] == '='))
580 encoded[len-3] = '\0';
Damien Miller4a8ed542002-01-22 23:33:31 +1100581 blen = uudecode(encoded, blob, sizeof(blob));
Damien Millereba71ba2000-04-29 23:57:08 +1000582 if (blen < 0) {
583 fprintf(stderr, "uudecode failed.\n");
584 exit(1);
585 }
Damien Miller44b25042010-07-02 13:35:01 +1000586 *k = *private ?
Damien Miller874d77b2000-10-14 16:23:11 +1100587 do_convert_private_ssh2_from_blob(blob, blen) :
Damien Miller0bc1bd82000-11-13 22:57:25 +1100588 key_from_blob(blob, blen);
Damien Miller44b25042010-07-02 13:35:01 +1000589 if (*k == NULL) {
Damien Miller874d77b2000-10-14 16:23:11 +1100590 fprintf(stderr, "decode blob failed.\n");
591 exit(1);
592 }
Damien Miller44b25042010-07-02 13:35:01 +1000593 fclose(fp);
594}
595
596static void
597do_convert_from_pkcs8(Key **k, int *private)
598{
599 EVP_PKEY *pubkey;
600 FILE *fp;
601
602 if ((fp = fopen(identity_file, "r")) == NULL)
603 fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
604 if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
605 fatal("%s: %s is not a recognised public key format", __func__,
606 identity_file);
607 }
608 fclose(fp);
609 switch (EVP_PKEY_type(pubkey->type)) {
610 case EVP_PKEY_RSA:
611 *k = key_new(KEY_UNSPEC);
612 (*k)->type = KEY_RSA;
613 (*k)->rsa = EVP_PKEY_get1_RSA(pubkey);
614 break;
615 case EVP_PKEY_DSA:
616 *k = key_new(KEY_UNSPEC);
617 (*k)->type = KEY_DSA;
618 (*k)->dsa = EVP_PKEY_get1_DSA(pubkey);
619 break;
Damien Miller6af914a2010-09-10 11:39:26 +1000620#ifdef OPENSSL_HAS_ECC
Damien Millereb8b60e2010-08-31 22:41:14 +1000621 case EVP_PKEY_EC:
622 *k = key_new(KEY_UNSPEC);
623 (*k)->type = KEY_ECDSA;
624 (*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey);
Damien Millerb472a902010-11-05 10:19:49 +1100625 (*k)->ecdsa_nid = key_ecdsa_key_to_nid((*k)->ecdsa);
Damien Millereb8b60e2010-08-31 22:41:14 +1000626 break;
Damien Miller6af914a2010-09-10 11:39:26 +1000627#endif
Damien Miller44b25042010-07-02 13:35:01 +1000628 default:
629 fatal("%s: unsupported pubkey type %d", __func__,
630 EVP_PKEY_type(pubkey->type));
631 }
632 EVP_PKEY_free(pubkey);
633 return;
634}
635
636static void
637do_convert_from_pem(Key **k, int *private)
638{
639 FILE *fp;
640 RSA *rsa;
641#ifdef notyet
642 DSA *dsa;
643#endif
644
645 if ((fp = fopen(identity_file, "r")) == NULL)
646 fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
647 if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
648 *k = key_new(KEY_UNSPEC);
649 (*k)->type = KEY_RSA;
650 (*k)->rsa = rsa;
651 fclose(fp);
652 return;
653 }
654#if notyet /* OpenSSH 0.9.8 lacks this function */
655 rewind(fp);
656 if ((dsa = PEM_read_DSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
657 *k = key_new(KEY_UNSPEC);
658 (*k)->type = KEY_DSA;
659 (*k)->dsa = dsa;
660 fclose(fp);
661 return;
662 }
Damien Millereb8b60e2010-08-31 22:41:14 +1000663 /* XXX ECDSA */
Damien Miller44b25042010-07-02 13:35:01 +1000664#endif
665 fatal("%s: unrecognised raw private key format", __func__);
666}
667
668static void
669do_convert_from(struct passwd *pw)
670{
671 Key *k = NULL;
Damien Miller844cccf2010-08-03 16:03:29 +1000672 int private = 0, ok = 0;
Damien Miller44b25042010-07-02 13:35:01 +1000673 struct stat st;
674
675 if (!have_identity)
676 ask_filename(pw, "Enter file in which the key is");
677 if (stat(identity_file, &st) < 0)
678 fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
679
680 switch (convert_format) {
681 case FMT_RFC4716:
682 do_convert_from_ssh2(pw, &k, &private);
683 break;
684 case FMT_PKCS8:
685 do_convert_from_pkcs8(&k, &private);
686 break;
687 case FMT_PEM:
688 do_convert_from_pem(&k, &private);
689 break;
690 default:
691 fatal("%s: unknown key format %d", __func__, convert_format);
692 }
693
694 if (!private)
695 ok = key_write(k, stdout);
696 if (ok)
697 fprintf(stdout, "\n");
698 else {
699 switch (k->type) {
700 case KEY_DSA:
701 ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL,
702 NULL, 0, NULL, NULL);
703 break;
Damien Miller6af914a2010-09-10 11:39:26 +1000704#ifdef OPENSSL_HAS_ECC
Damien Millereb8b60e2010-08-31 22:41:14 +1000705 case KEY_ECDSA:
706 ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL,
707 NULL, 0, NULL, NULL);
708 break;
Damien Miller6af914a2010-09-10 11:39:26 +1000709#endif
Damien Miller44b25042010-07-02 13:35:01 +1000710 case KEY_RSA:
711 ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL,
712 NULL, 0, NULL, NULL);
713 break;
714 default:
715 fatal("%s: unsupported key type %s", __func__,
716 key_type(k));
717 }
718 }
719
Damien Miller874d77b2000-10-14 16:23:11 +1100720 if (!ok) {
Damien Miller9eab9562009-02-22 08:47:02 +1100721 fprintf(stderr, "key write failed\n");
Damien Miller874d77b2000-10-14 16:23:11 +1100722 exit(1);
723 }
Damien Millereba71ba2000-04-29 23:57:08 +1000724 key_free(k);
Damien Millereba71ba2000-04-29 23:57:08 +1000725 exit(0);
726}
Damien Miller1f0311c2014-05-15 14:24:09 +1000727#endif
Damien Millereba71ba2000-04-29 23:57:08 +1000728
Ben Lindstrombba81212001-06-25 05:01:22 +0000729static void
Damien Millereba71ba2000-04-29 23:57:08 +1000730do_print_public(struct passwd *pw)
731{
Ben Lindstromd0fca422001-03-26 13:44:06 +0000732 Key *prv;
Damien Millereba71ba2000-04-29 23:57:08 +1000733 struct stat st;
734
735 if (!have_identity)
736 ask_filename(pw, "Enter file in which the key is");
737 if (stat(identity_file, &st) < 0) {
738 perror(identity_file);
739 exit(1);
740 }
Ben Lindstromd78ae762001-06-05 20:35:09 +0000741 prv = load_identity(identity_file);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000742 if (prv == NULL) {
Damien Millereba71ba2000-04-29 23:57:08 +1000743 fprintf(stderr, "load failed\n");
744 exit(1);
745 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000746 if (!key_write(prv, stdout))
Damien Millereba71ba2000-04-29 23:57:08 +1000747 fprintf(stderr, "key_write failed");
Ben Lindstromd0fca422001-03-26 13:44:06 +0000748 key_free(prv);
Damien Millereba71ba2000-04-29 23:57:08 +1000749 fprintf(stdout, "\n");
750 exit(0);
751}
752
Ben Lindstromcd392282001-07-04 03:44:03 +0000753static void
Damien Miller757f34e2010-08-05 13:05:31 +1000754do_download(struct passwd *pw)
Ben Lindstromcd392282001-07-04 03:44:03 +0000755{
Damien Miller7ea845e2010-02-12 09:21:02 +1100756#ifdef ENABLE_PKCS11
Ben Lindstrom0936a5b2002-03-26 03:17:42 +0000757 Key **keys = NULL;
Damien Miller7ea845e2010-02-12 09:21:02 +1100758 int i, nkeys;
Damien Millerec77c952013-01-09 15:58:00 +1100759 enum fp_rep rep;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000760 int fptype;
Damien Millerec77c952013-01-09 15:58:00 +1100761 char *fp, *ra;
Ben Lindstrom8282d6a2001-08-06 21:44:05 +0000762
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000763 fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
764 rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
Damien Miller1422c082013-01-09 16:44:54 +1100765
Damien Miller7ea845e2010-02-12 09:21:02 +1100766 pkcs11_init(0);
767 nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys);
768 if (nkeys <= 0)
769 fatal("cannot read public key from pkcs11");
770 for (i = 0; i < nkeys; i++) {
Damien Millerec77c952013-01-09 15:58:00 +1100771 if (print_fingerprint) {
772 fp = key_fingerprint(keys[i], fptype, rep);
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000773 ra = key_fingerprint(keys[i], fingerprint_hash,
Damien Millerec77c952013-01-09 15:58:00 +1100774 SSH_FP_RANDOMART);
775 printf("%u %s %s (PKCS11 key)\n", key_size(keys[i]),
776 fp, key_type(keys[i]));
777 if (log_level >= SYSLOG_LEVEL_VERBOSE)
778 printf("%s\n", ra);
Darren Tuckera627d422013-06-02 07:31:17 +1000779 free(ra);
780 free(fp);
Damien Millerec77c952013-01-09 15:58:00 +1100781 } else {
782 key_write(keys[i], stdout);
783 fprintf(stdout, "\n");
784 }
Ben Lindstrom0936a5b2002-03-26 03:17:42 +0000785 key_free(keys[i]);
Ben Lindstrom0936a5b2002-03-26 03:17:42 +0000786 }
Darren Tuckera627d422013-06-02 07:31:17 +1000787 free(keys);
Damien Miller7ea845e2010-02-12 09:21:02 +1100788 pkcs11_terminate();
Ben Lindstrom8282d6a2001-08-06 21:44:05 +0000789 exit(0);
Damien Miller7ea845e2010-02-12 09:21:02 +1100790#else
791 fatal("no pkcs11 support");
792#endif /* ENABLE_PKCS11 */
Ben Lindstrom8282d6a2001-08-06 21:44:05 +0000793}
Ben Lindstromcd392282001-07-04 03:44:03 +0000794
Ben Lindstrombba81212001-06-25 05:01:22 +0000795static void
Damien Miller10f6f6b1999-11-17 17:29:08 +1100796do_fingerprint(struct passwd *pw)
797{
Damien Miller98c7ad62000-03-09 21:27:49 +1100798 FILE *f;
Damien Millereba71ba2000-04-29 23:57:08 +1000799 Key *public;
Darren Tucker9c16ac92008-06-13 04:40:35 +1000800 char *comment = NULL, *cp, *ep, line[16*1024], *fp, *ra;
Damien Millercb2fbb22008-02-10 22:24:55 +1100801 int i, skip = 0, num = 0, invalid = 1;
Ben Lindstrom65366a82001-12-06 16:32:47 +0000802 enum fp_rep rep;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000803 int fptype;
Damien Miller95def091999-11-25 00:26:21 +1100804 struct stat st;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100805
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000806 fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
807 rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
Damien Miller95def091999-11-25 00:26:21 +1100808 if (!have_identity)
809 ask_filename(pw, "Enter file in which the key is");
810 if (stat(identity_file, &st) < 0) {
811 perror(identity_file);
812 exit(1);
813 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000814 public = key_load_public(identity_file, &comment);
815 if (public != NULL) {
816 fp = key_fingerprint(public, fptype, rep);
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000817 ra = key_fingerprint(public, fingerprint_hash,
818 SSH_FP_RANDOMART);
Darren Tuckerb68fb4a2008-06-13 08:57:27 +1000819 printf("%u %s %s (%s)\n", key_size(public), fp, comment,
820 key_type(public));
Darren Tucker35c45532008-06-13 04:43:15 +1000821 if (log_level >= SYSLOG_LEVEL_VERBOSE)
822 printf("%s\n", ra);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100823 key_free(public);
Darren Tuckera627d422013-06-02 07:31:17 +1000824 free(comment);
825 free(ra);
826 free(fp);
Damien Miller98c7ad62000-03-09 21:27:49 +1100827 exit(0);
828 }
Damien Miller40b59852006-06-13 13:00:25 +1000829 if (comment) {
Darren Tuckera627d422013-06-02 07:31:17 +1000830 free(comment);
Damien Miller40b59852006-06-13 13:00:25 +1000831 comment = NULL;
832 }
Damien Miller98c7ad62000-03-09 21:27:49 +1100833
Damien Millerba3420a2010-06-26 09:39:07 +1000834 if ((f = fopen(identity_file, "r")) == NULL)
835 fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
Damien Miller98c7ad62000-03-09 21:27:49 +1100836
Damien Millerba3420a2010-06-26 09:39:07 +1000837 while (fgets(line, sizeof(line), f)) {
838 if ((cp = strchr(line, '\n')) == NULL) {
839 error("line %d too long: %.40s...",
840 num + 1, line);
841 skip = 1;
842 continue;
Damien Miller95def091999-11-25 00:26:21 +1100843 }
Damien Millerba3420a2010-06-26 09:39:07 +1000844 num++;
845 if (skip) {
846 skip = 0;
847 continue;
848 }
849 *cp = '\0';
850
851 /* Skip leading whitespace, empty and comment lines. */
852 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
853 ;
854 if (!*cp || *cp == '\n' || *cp == '#')
855 continue;
856 i = strtol(cp, &ep, 10);
857 if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) {
858 int quoted = 0;
859 comment = cp;
860 for (; *cp && (quoted || (*cp != ' ' &&
861 *cp != '\t')); cp++) {
862 if (*cp == '\\' && cp[1] == '"')
863 cp++; /* Skip both */
864 else if (*cp == '"')
865 quoted = !quoted;
866 }
867 if (!*cp)
868 continue;
869 *cp++ = '\0';
870 }
871 ep = cp;
872 public = key_new(KEY_RSA1);
873 if (key_read(public, &cp) != 1) {
874 cp = ep;
875 key_free(public);
876 public = key_new(KEY_UNSPEC);
877 if (key_read(public, &cp) != 1) {
878 key_free(public);
879 continue;
880 }
881 }
882 comment = *cp ? cp : comment;
883 fp = key_fingerprint(public, fptype, rep);
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000884 ra = key_fingerprint(public, fingerprint_hash,
885 SSH_FP_RANDOMART);
Damien Millerba3420a2010-06-26 09:39:07 +1000886 printf("%u %s %s (%s)\n", key_size(public), fp,
887 comment ? comment : "no comment", key_type(public));
888 if (log_level >= SYSLOG_LEVEL_VERBOSE)
889 printf("%s\n", ra);
Darren Tuckera627d422013-06-02 07:31:17 +1000890 free(ra);
891 free(fp);
Damien Millerba3420a2010-06-26 09:39:07 +1000892 key_free(public);
893 invalid = 0;
Damien Miller95def091999-11-25 00:26:21 +1100894 }
Damien Millerba3420a2010-06-26 09:39:07 +1000895 fclose(f);
896
Damien Miller98c7ad62000-03-09 21:27:49 +1100897 if (invalid) {
Damien Millereb5fec62001-11-12 10:52:44 +1100898 printf("%s is not a public key file.\n", identity_file);
Damien Miller98c7ad62000-03-09 21:27:49 +1100899 exit(1);
900 }
Damien Miller95def091999-11-25 00:26:21 +1100901 exit(0);
Damien Miller10f6f6b1999-11-17 17:29:08 +1100902}
903
Damien Miller4b42d7f2005-03-01 21:48:35 +1100904static void
Damien Miller58f1baf2011-05-05 14:06:15 +1000905do_gen_all_hostkeys(struct passwd *pw)
906{
907 struct {
908 char *key_type;
909 char *key_type_display;
910 char *path;
911 } key_types[] = {
912 { "rsa1", "RSA1", _PATH_HOST_KEY_FILE },
913 { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
914 { "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE },
Damien Millerb56e4932012-02-06 07:41:27 +1100915#ifdef OPENSSL_HAS_ECC
Damien Miller58f1baf2011-05-05 14:06:15 +1000916 { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
Damien Millerb56e4932012-02-06 07:41:27 +1100917#endif
Damien Miller5be9d9e2013-12-07 11:24:01 +1100918 { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
Damien Miller58f1baf2011-05-05 14:06:15 +1000919 { NULL, NULL, NULL }
920 };
921
922 int first = 0;
923 struct stat st;
924 Key *private, *public;
925 char comment[1024];
926 int i, type, fd;
927 FILE *f;
928
929 for (i = 0; key_types[i].key_type; i++) {
930 if (stat(key_types[i].path, &st) == 0)
931 continue;
932 if (errno != ENOENT) {
933 printf("Could not stat %s: %s", key_types[i].path,
934 strerror(errno));
935 first = 0;
936 continue;
937 }
938
939 if (first == 0) {
940 first = 1;
941 printf("%s: generating new host keys: ", __progname);
942 }
943 printf("%s ", key_types[i].key_type_display);
944 fflush(stdout);
Damien Miller58f1baf2011-05-05 14:06:15 +1000945 type = key_type_from_name(key_types[i].key_type);
946 strlcpy(identity_file, key_types[i].path, sizeof(identity_file));
947 bits = 0;
948 type_bits_valid(type, &bits);
949 private = key_generate(type, bits);
950 if (private == NULL) {
951 fprintf(stderr, "key_generate failed\n");
952 first = 0;
953 continue;
954 }
955 public = key_from_private(private);
956 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name,
957 hostname);
Damien Millerbcd00ab2013-12-07 10:41:55 +1100958 if (!key_save_private(private, identity_file, "", comment,
959 use_new_format, new_format_cipher, rounds)) {
Damien Miller58f1baf2011-05-05 14:06:15 +1000960 printf("Saving the key failed: %s.\n", identity_file);
961 key_free(private);
962 key_free(public);
963 first = 0;
964 continue;
965 }
966 key_free(private);
Damien Miller58f1baf2011-05-05 14:06:15 +1000967 strlcat(identity_file, ".pub", sizeof(identity_file));
968 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
969 if (fd == -1) {
970 printf("Could not save your public key in %s\n",
971 identity_file);
972 key_free(public);
973 first = 0;
974 continue;
975 }
976 f = fdopen(fd, "w");
977 if (f == NULL) {
978 printf("fdopen %s failed\n", identity_file);
doug@openbsd.org7df88182014-08-21 01:08:52 +0000979 close(fd);
Damien Miller58f1baf2011-05-05 14:06:15 +1000980 key_free(public);
981 first = 0;
982 continue;
983 }
984 if (!key_write(public, f)) {
985 fprintf(stderr, "write key failed\n");
doug@openbsd.org7df88182014-08-21 01:08:52 +0000986 fclose(f);
Damien Miller58f1baf2011-05-05 14:06:15 +1000987 key_free(public);
988 first = 0;
989 continue;
990 }
991 fprintf(f, " %s\n", comment);
992 fclose(f);
993 key_free(public);
994
995 }
996 if (first != 0)
997 printf("\n");
998}
999
1000static void
Damien Miller4a1d3d52014-07-03 21:24:40 +10001001printhost(FILE *f, const char *name, Key *public, int ca, int revoked, int hash)
Damien Miller4b42d7f2005-03-01 21:48:35 +11001002{
Darren Tucker0f7e9102008-06-08 12:54:29 +10001003 if (print_fingerprint) {
1004 enum fp_rep rep;
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001005 int fptype;
Darren Tucker9c16ac92008-06-13 04:40:35 +10001006 char *fp, *ra;
Darren Tucker0f7e9102008-06-08 12:54:29 +10001007
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001008 fptype = print_bubblebabble ?
1009 SSH_DIGEST_SHA1 : fingerprint_hash;
1010 rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
Darren Tucker0f7e9102008-06-08 12:54:29 +10001011 fp = key_fingerprint(public, fptype, rep);
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001012 ra = key_fingerprint(public, fingerprint_hash,
1013 SSH_FP_RANDOMART);
Damien Miller81dec052008-07-14 11:28:29 +10001014 printf("%u %s %s (%s)\n", key_size(public), fp, name,
1015 key_type(public));
1016 if (log_level >= SYSLOG_LEVEL_VERBOSE)
1017 printf("%s\n", ra);
Darren Tuckera627d422013-06-02 07:31:17 +10001018 free(ra);
1019 free(fp);
Darren Tucker0f7e9102008-06-08 12:54:29 +10001020 } else {
1021 if (hash && (name = host_hash(name, NULL, 0)) == NULL)
1022 fatal("hash_host failed");
Damien Miller4a1d3d52014-07-03 21:24:40 +10001023 fprintf(f, "%s%s%s ", ca ? CA_MARKER " " : "",
1024 revoked ? REVOKE_MARKER " " : "" , name);
Darren Tucker0f7e9102008-06-08 12:54:29 +10001025 if (!key_write(public, f))
1026 fatal("key_write failed");
1027 fprintf(f, "\n");
1028 }
Damien Miller4b42d7f2005-03-01 21:48:35 +11001029}
1030
1031static void
1032do_known_hosts(struct passwd *pw, const char *name)
1033{
1034 FILE *in, *out = stdout;
Damien Miller0a80ca12010-02-27 07:55:05 +11001035 Key *pub;
Damien Miller4b42d7f2005-03-01 21:48:35 +11001036 char *cp, *cp2, *kp, *kp2;
1037 char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN];
Damien Millercb2fbb22008-02-10 22:24:55 +11001038 int c, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0;
Damien Miller4a1d3d52014-07-03 21:24:40 +10001039 int ca, revoked;
Damien Miller66085482013-09-14 09:45:03 +10001040 int found_key = 0;
Damien Miller4b42d7f2005-03-01 21:48:35 +11001041
1042 if (!have_identity) {
1043 cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
1044 if (strlcpy(identity_file, cp, sizeof(identity_file)) >=
1045 sizeof(identity_file))
1046 fatal("Specified known hosts path too long");
Darren Tuckera627d422013-06-02 07:31:17 +10001047 free(cp);
Damien Miller4b42d7f2005-03-01 21:48:35 +11001048 have_identity = 1;
1049 }
1050 if ((in = fopen(identity_file, "r")) == NULL)
Damien Millerba3420a2010-06-26 09:39:07 +10001051 fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
Damien Miller4b42d7f2005-03-01 21:48:35 +11001052
Damien Miller4a1d3d52014-07-03 21:24:40 +10001053 /* XXX this code is a mess; refactor -djm */
Damien Miller4b42d7f2005-03-01 21:48:35 +11001054 /*
1055 * Find hosts goes to stdout, hash and deletions happen in-place
1056 * A corner case is ssh-keygen -HF foo, which should go to stdout
1057 */
1058 if (!find_host && (hash_hosts || delete_host)) {
1059 if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) ||
1060 strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) ||
1061 strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) ||
1062 strlcat(old, ".old", sizeof(old)) >= sizeof(old))
1063 fatal("known_hosts path too long");
1064 umask(077);
1065 if ((c = mkstemp(tmp)) == -1)
1066 fatal("mkstemp: %s", strerror(errno));
1067 if ((out = fdopen(c, "w")) == NULL) {
1068 c = errno;
1069 unlink(tmp);
1070 fatal("fdopen: %s", strerror(c));
1071 }
1072 inplace = 1;
1073 }
1074
1075 while (fgets(line, sizeof(line), in)) {
Damien Miller0f4ed692007-10-26 14:26:32 +10001076 if ((cp = strchr(line, '\n')) == NULL) {
Damien Millercb2fbb22008-02-10 22:24:55 +11001077 error("line %d too long: %.40s...", num + 1, line);
Damien Miller4b42d7f2005-03-01 21:48:35 +11001078 skip = 1;
1079 invalid = 1;
1080 continue;
1081 }
Damien Miller0f4ed692007-10-26 14:26:32 +10001082 num++;
Damien Miller4b42d7f2005-03-01 21:48:35 +11001083 if (skip) {
1084 skip = 0;
1085 continue;
1086 }
Damien Miller0f4ed692007-10-26 14:26:32 +10001087 *cp = '\0';
Damien Miller4b42d7f2005-03-01 21:48:35 +11001088
1089 /* Skip leading whitespace, empty and comment lines. */
1090 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
1091 ;
1092 if (!*cp || *cp == '\n' || *cp == '#') {
1093 if (inplace)
1094 fprintf(out, "%s\n", cp);
1095 continue;
1096 }
Damien Miller4a1d3d52014-07-03 21:24:40 +10001097 /* Check whether this is a CA key or revocation marker */
Damien Miller0a80ca12010-02-27 07:55:05 +11001098 if (strncasecmp(cp, CA_MARKER, sizeof(CA_MARKER) - 1) == 0 &&
1099 (cp[sizeof(CA_MARKER) - 1] == ' ' ||
1100 cp[sizeof(CA_MARKER) - 1] == '\t')) {
1101 ca = 1;
1102 cp += sizeof(CA_MARKER);
1103 } else
1104 ca = 0;
Damien Miller4a1d3d52014-07-03 21:24:40 +10001105 if (strncasecmp(cp, REVOKE_MARKER,
1106 sizeof(REVOKE_MARKER) - 1) == 0 &&
1107 (cp[sizeof(REVOKE_MARKER) - 1] == ' ' ||
1108 cp[sizeof(REVOKE_MARKER) - 1] == '\t')) {
1109 revoked = 1;
1110 cp += sizeof(REVOKE_MARKER);
1111 } else
1112 revoked = 0;
Damien Miller0a80ca12010-02-27 07:55:05 +11001113
Damien Miller4b42d7f2005-03-01 21:48:35 +11001114 /* Find the end of the host name portion. */
1115 for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++)
1116 ;
Damien Miller0a80ca12010-02-27 07:55:05 +11001117
Damien Miller4b42d7f2005-03-01 21:48:35 +11001118 if (*kp == '\0' || *(kp + 1) == '\0') {
1119 error("line %d missing key: %.40s...",
1120 num, line);
1121 invalid = 1;
1122 continue;
1123 }
1124 *kp++ = '\0';
1125 kp2 = kp;
1126
Damien Miller0a80ca12010-02-27 07:55:05 +11001127 pub = key_new(KEY_RSA1);
1128 if (key_read(pub, &kp) != 1) {
Damien Miller4b42d7f2005-03-01 21:48:35 +11001129 kp = kp2;
Damien Miller0a80ca12010-02-27 07:55:05 +11001130 key_free(pub);
1131 pub = key_new(KEY_UNSPEC);
1132 if (key_read(pub, &kp) != 1) {
Damien Miller4b42d7f2005-03-01 21:48:35 +11001133 error("line %d invalid key: %.40s...",
1134 num, line);
Damien Miller0a80ca12010-02-27 07:55:05 +11001135 key_free(pub);
Damien Miller4b42d7f2005-03-01 21:48:35 +11001136 invalid = 1;
1137 continue;
1138 }
1139 }
1140
1141 if (*cp == HASH_DELIM) {
1142 if (find_host || delete_host) {
1143 cp2 = host_hash(name, cp, strlen(cp));
1144 if (cp2 == NULL) {
1145 error("line %d: invalid hashed "
1146 "name: %.64s...", num, line);
1147 invalid = 1;
1148 continue;
1149 }
1150 c = (strcmp(cp2, cp) == 0);
1151 if (find_host && c) {
Damien Miller66085482013-09-14 09:45:03 +10001152 if (!quiet)
1153 printf("# Host %s found: "
1154 "line %d type %s%s\n", name,
1155 num, key_type(pub),
Damien Miller4a1d3d52014-07-03 21:24:40 +10001156 ca ? " (CA key)" :
1157 revoked? " (revoked)" : "");
1158 printhost(out, cp, pub, ca, revoked, 0);
Damien Miller66085482013-09-14 09:45:03 +10001159 found_key = 1;
Damien Miller4b42d7f2005-03-01 21:48:35 +11001160 }
Darren Tuckerf09a8a62012-09-06 21:20:39 +10001161 if (delete_host) {
Damien Miller4a1d3d52014-07-03 21:24:40 +10001162 if (!c || ca || revoked) {
1163 printhost(out, cp, pub,
1164 ca, revoked, 0);
1165 } else {
Darren Tuckerf09a8a62012-09-06 21:20:39 +10001166 printf("# Host %s found: "
1167 "line %d type %s\n", name,
1168 num, key_type(pub));
Damien Miller4a1d3d52014-07-03 21:24:40 +10001169 }
Darren Tuckerf09a8a62012-09-06 21:20:39 +10001170 }
Damien Miller4b42d7f2005-03-01 21:48:35 +11001171 } else if (hash_hosts)
Damien Miller4a1d3d52014-07-03 21:24:40 +10001172 printhost(out, cp, pub, ca, revoked, 0);
Damien Miller4b42d7f2005-03-01 21:48:35 +11001173 } else {
1174 if (find_host || delete_host) {
1175 c = (match_hostname(name, cp,
1176 strlen(cp)) == 1);
1177 if (find_host && c) {
Damien Miller66085482013-09-14 09:45:03 +10001178 if (!quiet)
1179 printf("# Host %s found: "
1180 "line %d type %s%s\n", name,
1181 num, key_type(pub),
1182 ca ? " (CA key)" : "");
Damien Miller4a1d3d52014-07-03 21:24:40 +10001183 printhost(out, name, pub, ca, revoked,
1184 hash_hosts && !(ca || revoked));
Damien Miller66085482013-09-14 09:45:03 +10001185 found_key = 1;
Damien Miller4b42d7f2005-03-01 21:48:35 +11001186 }
Darren Tuckerf09a8a62012-09-06 21:20:39 +10001187 if (delete_host) {
Damien Miller4a1d3d52014-07-03 21:24:40 +10001188 if (!c || ca || revoked) {
1189 printhost(out, cp, pub,
1190 ca, revoked, 0);
1191 } else {
Darren Tuckerf09a8a62012-09-06 21:20:39 +10001192 printf("# Host %s found: "
1193 "line %d type %s\n", name,
1194 num, key_type(pub));
Damien Miller4a1d3d52014-07-03 21:24:40 +10001195 }
Darren Tuckerf09a8a62012-09-06 21:20:39 +10001196 }
Damien Miller4a1d3d52014-07-03 21:24:40 +10001197 } else if (hash_hosts && (ca || revoked)) {
1198 /* Don't hash CA and revoked keys' hostnames */
1199 printhost(out, cp, pub, ca, revoked, 0);
1200 has_unhashed = 1;
Damien Miller4b42d7f2005-03-01 21:48:35 +11001201 } else if (hash_hosts) {
Damien Miller4a1d3d52014-07-03 21:24:40 +10001202 /* Hash each hostname separately */
Darren Tucker47eede72005-03-14 23:08:12 +11001203 for (cp2 = strsep(&cp, ",");
Damien Miller4b42d7f2005-03-01 21:48:35 +11001204 cp2 != NULL && *cp2 != '\0';
Damien Miller89eac802005-03-02 12:33:04 +11001205 cp2 = strsep(&cp, ",")) {
Damien Miller4a1d3d52014-07-03 21:24:40 +10001206 if (strcspn(cp2, "*?!") !=
Damien Miller0a80ca12010-02-27 07:55:05 +11001207 strlen(cp2)) {
Damien Miller89eac802005-03-02 12:33:04 +11001208 fprintf(stderr, "Warning: "
1209 "ignoring host name with "
1210 "metacharacters: %.64s\n",
1211 cp2);
Damien Miller4a1d3d52014-07-03 21:24:40 +10001212 printhost(out, cp2, pub, ca,
1213 revoked, 0);
1214 has_unhashed = 1;
1215 } else {
1216 printhost(out, cp2, pub, ca,
1217 revoked, 1);
1218 }
Damien Miller89eac802005-03-02 12:33:04 +11001219 }
Damien Miller4b42d7f2005-03-01 21:48:35 +11001220 }
1221 }
Damien Miller0a80ca12010-02-27 07:55:05 +11001222 key_free(pub);
Damien Miller4b42d7f2005-03-01 21:48:35 +11001223 }
1224 fclose(in);
1225
1226 if (invalid) {
Damien Millercb2fbb22008-02-10 22:24:55 +11001227 fprintf(stderr, "%s is not a valid known_hosts file.\n",
Damien Miller4b42d7f2005-03-01 21:48:35 +11001228 identity_file);
1229 if (inplace) {
1230 fprintf(stderr, "Not replacing existing known_hosts "
Darren Tucker9f438a92005-03-14 23:09:18 +11001231 "file because of errors\n");
Damien Miller4b42d7f2005-03-01 21:48:35 +11001232 fclose(out);
1233 unlink(tmp);
1234 }
1235 exit(1);
1236 }
1237
1238 if (inplace) {
1239 fclose(out);
1240
1241 /* Backup existing file */
1242 if (unlink(old) == -1 && errno != ENOENT)
1243 fatal("unlink %.100s: %s", old, strerror(errno));
1244 if (link(identity_file, old) == -1)
1245 fatal("link %.100s to %.100s: %s", identity_file, old,
1246 strerror(errno));
1247 /* Move new one into place */
1248 if (rename(tmp, identity_file) == -1) {
1249 error("rename\"%s\" to \"%s\": %s", tmp, identity_file,
1250 strerror(errno));
1251 unlink(tmp);
1252 unlink(old);
1253 exit(1);
1254 }
1255
1256 fprintf(stderr, "%s updated.\n", identity_file);
1257 fprintf(stderr, "Original contents retained as %s\n", old);
1258 if (has_unhashed) {
1259 fprintf(stderr, "WARNING: %s contains unhashed "
1260 "entries\n", old);
1261 fprintf(stderr, "Delete this file to ensure privacy "
Damien Miller0dc1bef2005-07-17 17:22:45 +10001262 "of hostnames\n");
Damien Miller4b42d7f2005-03-01 21:48:35 +11001263 }
1264 }
1265
Damien Miller66085482013-09-14 09:45:03 +10001266 exit (find_host && !found_key);
Damien Miller4b42d7f2005-03-01 21:48:35 +11001267}
1268
Damien Miller95def091999-11-25 00:26:21 +11001269/*
1270 * Perform changing a passphrase. The argument is the passwd structure
1271 * for the current user.
1272 */
Ben Lindstrombba81212001-06-25 05:01:22 +00001273static void
Damien Miller10f6f6b1999-11-17 17:29:08 +11001274do_change_passphrase(struct passwd *pw)
1275{
Damien Miller95def091999-11-25 00:26:21 +11001276 char *comment;
1277 char *old_passphrase, *passphrase1, *passphrase2;
1278 struct stat st;
Damien Millereba71ba2000-04-29 23:57:08 +10001279 Key *private;
Damien Miller10f6f6b1999-11-17 17:29:08 +11001280
Damien Miller95def091999-11-25 00:26:21 +11001281 if (!have_identity)
1282 ask_filename(pw, "Enter file in which the key is");
Damien Miller95def091999-11-25 00:26:21 +11001283 if (stat(identity_file, &st) < 0) {
1284 perror(identity_file);
1285 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001286 }
Damien Miller95def091999-11-25 00:26:21 +11001287 /* Try to load the file with empty passphrase. */
Ben Lindstromd0fca422001-03-26 13:44:06 +00001288 private = key_load_private(identity_file, "", &comment);
1289 if (private == NULL) {
Damien Miller95def091999-11-25 00:26:21 +11001290 if (identity_passphrase)
1291 old_passphrase = xstrdup(identity_passphrase);
1292 else
Ben Lindstrom949974b2001-06-25 05:20:31 +00001293 old_passphrase =
1294 read_passphrase("Enter old passphrase: ",
1295 RP_ALLOW_STDIN);
1296 private = key_load_private(identity_file, old_passphrase,
1297 &comment);
Damien Millera5103f42014-02-04 11:20:14 +11001298 explicit_bzero(old_passphrase, strlen(old_passphrase));
Darren Tuckera627d422013-06-02 07:31:17 +10001299 free(old_passphrase);
Ben Lindstromd0fca422001-03-26 13:44:06 +00001300 if (private == NULL) {
Damien Miller95def091999-11-25 00:26:21 +11001301 printf("Bad passphrase.\n");
1302 exit(1);
1303 }
Damien Miller95def091999-11-25 00:26:21 +11001304 }
1305 printf("Key has comment '%s'\n", comment);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001306
Damien Miller95def091999-11-25 00:26:21 +11001307 /* Ask the new passphrase (twice). */
1308 if (identity_new_passphrase) {
1309 passphrase1 = xstrdup(identity_new_passphrase);
1310 passphrase2 = NULL;
1311 } else {
1312 passphrase1 =
Ben Lindstrom949974b2001-06-25 05:20:31 +00001313 read_passphrase("Enter new passphrase (empty for no "
1314 "passphrase): ", RP_ALLOW_STDIN);
1315 passphrase2 = read_passphrase("Enter same passphrase again: ",
Damien Miller9f0f5c62001-12-21 14:45:46 +11001316 RP_ALLOW_STDIN);
Damien Miller95def091999-11-25 00:26:21 +11001317
1318 /* Verify that they are the same. */
1319 if (strcmp(passphrase1, passphrase2) != 0) {
Damien Millera5103f42014-02-04 11:20:14 +11001320 explicit_bzero(passphrase1, strlen(passphrase1));
1321 explicit_bzero(passphrase2, strlen(passphrase2));
Darren Tuckera627d422013-06-02 07:31:17 +10001322 free(passphrase1);
1323 free(passphrase2);
Damien Miller95def091999-11-25 00:26:21 +11001324 printf("Pass phrases do not match. Try again.\n");
1325 exit(1);
1326 }
1327 /* Destroy the other copy. */
Damien Millera5103f42014-02-04 11:20:14 +11001328 explicit_bzero(passphrase2, strlen(passphrase2));
Darren Tuckera627d422013-06-02 07:31:17 +10001329 free(passphrase2);
Damien Miller95def091999-11-25 00:26:21 +11001330 }
1331
1332 /* Save the file using the new passphrase. */
Damien Millerbcd00ab2013-12-07 10:41:55 +11001333 if (!key_save_private(private, identity_file, passphrase1, comment,
1334 use_new_format, new_format_cipher, rounds)) {
Ben Lindstrom15f33862001-04-16 02:00:02 +00001335 printf("Saving the key failed: %s.\n", identity_file);
Damien Millera5103f42014-02-04 11:20:14 +11001336 explicit_bzero(passphrase1, strlen(passphrase1));
Darren Tuckera627d422013-06-02 07:31:17 +10001337 free(passphrase1);
Damien Millereba71ba2000-04-29 23:57:08 +10001338 key_free(private);
Darren Tuckera627d422013-06-02 07:31:17 +10001339 free(comment);
Damien Miller95def091999-11-25 00:26:21 +11001340 exit(1);
1341 }
1342 /* Destroy the passphrase and the copy of the key in memory. */
Damien Millera5103f42014-02-04 11:20:14 +11001343 explicit_bzero(passphrase1, strlen(passphrase1));
Darren Tuckera627d422013-06-02 07:31:17 +10001344 free(passphrase1);
Damien Millereba71ba2000-04-29 23:57:08 +10001345 key_free(private); /* Destroys contents */
Darren Tuckera627d422013-06-02 07:31:17 +10001346 free(comment);
Damien Miller95def091999-11-25 00:26:21 +11001347
1348 printf("Your identification has been saved with the new passphrase.\n");
1349 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001350}
1351
Damien Miller37876e92003-05-15 10:19:46 +10001352/*
1353 * Print the SSHFP RR.
1354 */
Damien Millercb314822006-03-26 13:48:01 +11001355static int
1356do_print_resource_record(struct passwd *pw, char *fname, char *hname)
Damien Miller37876e92003-05-15 10:19:46 +10001357{
1358 Key *public;
1359 char *comment = NULL;
1360 struct stat st;
1361
Damien Millercb314822006-03-26 13:48:01 +11001362 if (fname == NULL)
Damien Miller5bb88332013-07-18 16:13:37 +10001363 fatal("%s: no filename", __func__);
Damien Millercb314822006-03-26 13:48:01 +11001364 if (stat(fname, &st) < 0) {
1365 if (errno == ENOENT)
1366 return 0;
1367 perror(fname);
Damien Miller37876e92003-05-15 10:19:46 +10001368 exit(1);
1369 }
Damien Millercb314822006-03-26 13:48:01 +11001370 public = key_load_public(fname, &comment);
Damien Miller37876e92003-05-15 10:19:46 +10001371 if (public != NULL) {
Darren Tucker3f9fdc72004-06-22 12:56:01 +10001372 export_dns_rr(hname, public, stdout, print_generic);
Damien Miller37876e92003-05-15 10:19:46 +10001373 key_free(public);
Darren Tuckera627d422013-06-02 07:31:17 +10001374 free(comment);
Damien Millercb314822006-03-26 13:48:01 +11001375 return 1;
Damien Miller37876e92003-05-15 10:19:46 +10001376 }
1377 if (comment)
Darren Tuckera627d422013-06-02 07:31:17 +10001378 free(comment);
Damien Miller37876e92003-05-15 10:19:46 +10001379
Damien Millercb314822006-03-26 13:48:01 +11001380 printf("failed to read v2 public key from %s.\n", fname);
Damien Miller37876e92003-05-15 10:19:46 +10001381 exit(1);
1382}
Damien Miller37876e92003-05-15 10:19:46 +10001383
Damien Miller95def091999-11-25 00:26:21 +11001384/*
1385 * Change the comment of a private key file.
1386 */
Ben Lindstrombba81212001-06-25 05:01:22 +00001387static void
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001388do_change_comment(struct passwd *pw)
1389{
Ben Lindstrom5fc62702001-03-09 18:19:24 +00001390 char new_comment[1024], *comment, *passphrase;
Ben Lindstromd0fca422001-03-26 13:44:06 +00001391 Key *private;
1392 Key *public;
Damien Miller95def091999-11-25 00:26:21 +11001393 struct stat st;
1394 FILE *f;
Ben Lindstrom5fc62702001-03-09 18:19:24 +00001395 int fd;
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001396
Damien Miller95def091999-11-25 00:26:21 +11001397 if (!have_identity)
1398 ask_filename(pw, "Enter file in which the key is");
Damien Miller95def091999-11-25 00:26:21 +11001399 if (stat(identity_file, &st) < 0) {
1400 perror(identity_file);
1401 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001402 }
Ben Lindstromd0fca422001-03-26 13:44:06 +00001403 private = key_load_private(identity_file, "", &comment);
1404 if (private == NULL) {
Damien Miller95def091999-11-25 00:26:21 +11001405 if (identity_passphrase)
1406 passphrase = xstrdup(identity_passphrase);
1407 else if (identity_new_passphrase)
1408 passphrase = xstrdup(identity_new_passphrase);
1409 else
Ben Lindstrom949974b2001-06-25 05:20:31 +00001410 passphrase = read_passphrase("Enter passphrase: ",
1411 RP_ALLOW_STDIN);
Damien Miller95def091999-11-25 00:26:21 +11001412 /* Try to load using the passphrase. */
Ben Lindstromd0fca422001-03-26 13:44:06 +00001413 private = key_load_private(identity_file, passphrase, &comment);
1414 if (private == NULL) {
Damien Millera5103f42014-02-04 11:20:14 +11001415 explicit_bzero(passphrase, strlen(passphrase));
Darren Tuckera627d422013-06-02 07:31:17 +10001416 free(passphrase);
Damien Miller95def091999-11-25 00:26:21 +11001417 printf("Bad passphrase.\n");
1418 exit(1);
1419 }
Ben Lindstromd0fca422001-03-26 13:44:06 +00001420 } else {
1421 passphrase = xstrdup("");
Damien Miller95def091999-11-25 00:26:21 +11001422 }
Ben Lindstromd0fca422001-03-26 13:44:06 +00001423 if (private->type != KEY_RSA1) {
1424 fprintf(stderr, "Comments are only supported for RSA1 keys.\n");
1425 key_free(private);
1426 exit(1);
Damien Miller9f0f5c62001-12-21 14:45:46 +11001427 }
Damien Miller95def091999-11-25 00:26:21 +11001428 printf("Key now has comment '%s'\n", comment);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001429
Damien Miller95def091999-11-25 00:26:21 +11001430 if (identity_comment) {
1431 strlcpy(new_comment, identity_comment, sizeof(new_comment));
1432 } else {
1433 printf("Enter new comment: ");
1434 fflush(stdout);
1435 if (!fgets(new_comment, sizeof(new_comment), stdin)) {
Damien Millera5103f42014-02-04 11:20:14 +11001436 explicit_bzero(passphrase, strlen(passphrase));
Damien Millereba71ba2000-04-29 23:57:08 +10001437 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +11001438 exit(1);
1439 }
Damien Miller14b017d2007-09-17 16:09:15 +10001440 new_comment[strcspn(new_comment, "\n")] = '\0';
Damien Miller95def091999-11-25 00:26:21 +11001441 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001442
Damien Miller95def091999-11-25 00:26:21 +11001443 /* Save the file using the new passphrase. */
Damien Millerbcd00ab2013-12-07 10:41:55 +11001444 if (!key_save_private(private, identity_file, passphrase, new_comment,
1445 use_new_format, new_format_cipher, rounds)) {
Ben Lindstrom15f33862001-04-16 02:00:02 +00001446 printf("Saving the key failed: %s.\n", identity_file);
Damien Millera5103f42014-02-04 11:20:14 +11001447 explicit_bzero(passphrase, strlen(passphrase));
Darren Tuckera627d422013-06-02 07:31:17 +10001448 free(passphrase);
Damien Millereba71ba2000-04-29 23:57:08 +10001449 key_free(private);
Darren Tuckera627d422013-06-02 07:31:17 +10001450 free(comment);
Damien Miller95def091999-11-25 00:26:21 +11001451 exit(1);
1452 }
Damien Millera5103f42014-02-04 11:20:14 +11001453 explicit_bzero(passphrase, strlen(passphrase));
Darren Tuckera627d422013-06-02 07:31:17 +10001454 free(passphrase);
Ben Lindstromd0fca422001-03-26 13:44:06 +00001455 public = key_from_private(private);
Damien Millereba71ba2000-04-29 23:57:08 +10001456 key_free(private);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001457
Damien Miller95def091999-11-25 00:26:21 +11001458 strlcat(identity_file, ".pub", sizeof(identity_file));
Ben Lindstrom5fc62702001-03-09 18:19:24 +00001459 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1460 if (fd == -1) {
Damien Miller95def091999-11-25 00:26:21 +11001461 printf("Could not save your public key in %s\n", identity_file);
1462 exit(1);
1463 }
Ben Lindstrom5fc62702001-03-09 18:19:24 +00001464 f = fdopen(fd, "w");
1465 if (f == NULL) {
Damien Miller9eab9562009-02-22 08:47:02 +11001466 printf("fdopen %s failed\n", identity_file);
Ben Lindstrom5fc62702001-03-09 18:19:24 +00001467 exit(1);
1468 }
Damien Millereba71ba2000-04-29 23:57:08 +10001469 if (!key_write(public, f))
Damien Miller9eab9562009-02-22 08:47:02 +11001470 fprintf(stderr, "write key failed\n");
Damien Millereba71ba2000-04-29 23:57:08 +10001471 key_free(public);
1472 fprintf(f, " %s\n", new_comment);
Damien Miller95def091999-11-25 00:26:21 +11001473 fclose(f);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001474
Darren Tuckera627d422013-06-02 07:31:17 +10001475 free(comment);
Damien Miller95def091999-11-25 00:26:21 +11001476
1477 printf("The comment in your key file has been changed.\n");
1478 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001479}
1480
Damien Miller0a80ca12010-02-27 07:55:05 +11001481static const char *
Damien Millerf2b70ca2010-03-05 07:39:35 +11001482fmt_validity(u_int64_t valid_from, u_int64_t valid_to)
Damien Miller0a80ca12010-02-27 07:55:05 +11001483{
1484 char from[32], to[32];
1485 static char ret[64];
1486 time_t tt;
1487 struct tm *tm;
1488
1489 *from = *to = '\0';
Damien Millerf2b70ca2010-03-05 07:39:35 +11001490 if (valid_from == 0 && valid_to == 0xffffffffffffffffULL)
Damien Miller0a80ca12010-02-27 07:55:05 +11001491 return "forever";
1492
Damien Millerf2b70ca2010-03-05 07:39:35 +11001493 if (valid_from != 0) {
Damien Miller0a80ca12010-02-27 07:55:05 +11001494 /* XXX revisit INT_MAX in 2038 :) */
Damien Millerf2b70ca2010-03-05 07:39:35 +11001495 tt = valid_from > INT_MAX ? INT_MAX : valid_from;
Damien Miller0a80ca12010-02-27 07:55:05 +11001496 tm = localtime(&tt);
1497 strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm);
1498 }
Damien Millerf2b70ca2010-03-05 07:39:35 +11001499 if (valid_to != 0xffffffffffffffffULL) {
Damien Miller0a80ca12010-02-27 07:55:05 +11001500 /* XXX revisit INT_MAX in 2038 :) */
Damien Millerf2b70ca2010-03-05 07:39:35 +11001501 tt = valid_to > INT_MAX ? INT_MAX : valid_to;
Damien Miller0a80ca12010-02-27 07:55:05 +11001502 tm = localtime(&tt);
1503 strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm);
1504 }
1505
Damien Millerf2b70ca2010-03-05 07:39:35 +11001506 if (valid_from == 0) {
Damien Miller0a80ca12010-02-27 07:55:05 +11001507 snprintf(ret, sizeof(ret), "before %s", to);
1508 return ret;
1509 }
Damien Millerf2b70ca2010-03-05 07:39:35 +11001510 if (valid_to == 0xffffffffffffffffULL) {
Damien Miller0a80ca12010-02-27 07:55:05 +11001511 snprintf(ret, sizeof(ret), "after %s", from);
1512 return ret;
1513 }
1514
1515 snprintf(ret, sizeof(ret), "from %s to %s", from, to);
1516 return ret;
1517}
1518
1519static void
Damien Miller4e270b02010-04-16 15:56:21 +10001520add_flag_option(Buffer *c, const char *name)
Damien Miller0a80ca12010-02-27 07:55:05 +11001521{
1522 debug3("%s: %s", __func__, name);
1523 buffer_put_cstring(c, name);
1524 buffer_put_string(c, NULL, 0);
1525}
1526
1527static void
Damien Miller4e270b02010-04-16 15:56:21 +10001528add_string_option(Buffer *c, const char *name, const char *value)
Damien Miller0a80ca12010-02-27 07:55:05 +11001529{
1530 Buffer b;
1531
1532 debug3("%s: %s=%s", __func__, name, value);
1533 buffer_init(&b);
1534 buffer_put_cstring(&b, value);
1535
1536 buffer_put_cstring(c, name);
1537 buffer_put_string(c, buffer_ptr(&b), buffer_len(&b));
1538
1539 buffer_free(&b);
1540}
1541
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001542#define OPTIONS_CRITICAL 1
1543#define OPTIONS_EXTENSIONS 2
Damien Miller0a80ca12010-02-27 07:55:05 +11001544static void
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001545prepare_options_buf(Buffer *c, int which)
Damien Miller0a80ca12010-02-27 07:55:05 +11001546{
Damien Miller0a80ca12010-02-27 07:55:05 +11001547 buffer_clear(c);
Damien Miller1da63882010-08-05 13:03:51 +10001548 if ((which & OPTIONS_CRITICAL) != 0 &&
1549 certflags_command != NULL)
1550 add_string_option(c, "force-command", certflags_command);
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001551 if ((which & OPTIONS_EXTENSIONS) != 0 &&
Damien Miller2ce12ef2011-05-05 14:17:18 +10001552 (certflags_flags & CERTOPT_X_FWD) != 0)
1553 add_flag_option(c, "permit-X11-forwarding");
1554 if ((which & OPTIONS_EXTENSIONS) != 0 &&
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001555 (certflags_flags & CERTOPT_AGENT_FWD) != 0)
Damien Miller4e270b02010-04-16 15:56:21 +10001556 add_flag_option(c, "permit-agent-forwarding");
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001557 if ((which & OPTIONS_EXTENSIONS) != 0 &&
1558 (certflags_flags & CERTOPT_PORT_FWD) != 0)
Damien Miller4e270b02010-04-16 15:56:21 +10001559 add_flag_option(c, "permit-port-forwarding");
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001560 if ((which & OPTIONS_EXTENSIONS) != 0 &&
1561 (certflags_flags & CERTOPT_PTY) != 0)
Damien Miller4e270b02010-04-16 15:56:21 +10001562 add_flag_option(c, "permit-pty");
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001563 if ((which & OPTIONS_EXTENSIONS) != 0 &&
1564 (certflags_flags & CERTOPT_USER_RC) != 0)
Damien Miller4e270b02010-04-16 15:56:21 +10001565 add_flag_option(c, "permit-user-rc");
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001566 if ((which & OPTIONS_CRITICAL) != 0 &&
1567 certflags_src_addr != NULL)
1568 add_string_option(c, "source-address", certflags_src_addr);
Damien Miller0a80ca12010-02-27 07:55:05 +11001569}
1570
Damien Miller757f34e2010-08-05 13:05:31 +10001571static Key *
1572load_pkcs11_key(char *path)
1573{
1574#ifdef ENABLE_PKCS11
1575 Key **keys = NULL, *public, *private = NULL;
1576 int i, nkeys;
1577
1578 if ((public = key_load_public(path, NULL)) == NULL)
1579 fatal("Couldn't load CA public key \"%s\"", path);
1580
1581 nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, &keys);
1582 debug3("%s: %d keys", __func__, nkeys);
1583 if (nkeys <= 0)
1584 fatal("cannot read public key from pkcs11");
1585 for (i = 0; i < nkeys; i++) {
1586 if (key_equal_public(public, keys[i])) {
1587 private = keys[i];
1588 continue;
1589 }
1590 key_free(keys[i]);
1591 }
Darren Tuckera627d422013-06-02 07:31:17 +10001592 free(keys);
Damien Miller757f34e2010-08-05 13:05:31 +10001593 key_free(public);
1594 return private;
1595#else
1596 fatal("no pkcs11 support");
1597#endif /* ENABLE_PKCS11 */
1598}
1599
Damien Miller0a80ca12010-02-27 07:55:05 +11001600static void
1601do_ca_sign(struct passwd *pw, int argc, char **argv)
1602{
1603 int i, fd;
1604 u_int n;
1605 Key *ca, *public;
1606 char *otmp, *tmp, *cp, *out, *comment, **plist = NULL;
1607 FILE *f;
Damien Miller4e270b02010-04-16 15:56:21 +10001608 int v00 = 0; /* legacy keys */
Damien Miller0a80ca12010-02-27 07:55:05 +11001609
Damien Miller4e270b02010-04-16 15:56:21 +10001610 if (key_type_name != NULL) {
1611 switch (key_type_from_name(key_type_name)) {
1612 case KEY_RSA_CERT_V00:
1613 case KEY_DSA_CERT_V00:
1614 v00 = 1;
1615 break;
1616 case KEY_UNSPEC:
1617 if (strcasecmp(key_type_name, "v00") == 0) {
1618 v00 = 1;
1619 break;
1620 } else if (strcasecmp(key_type_name, "v01") == 0)
1621 break;
1622 /* FALLTHROUGH */
1623 default:
1624 fprintf(stderr, "unknown key type %s\n", key_type_name);
1625 exit(1);
1626 }
1627 }
1628
Damien Miller1f0311c2014-05-15 14:24:09 +10001629#ifdef ENABLE_PKCS11
Damien Miller757f34e2010-08-05 13:05:31 +10001630 pkcs11_init(1);
Damien Miller1f0311c2014-05-15 14:24:09 +10001631#endif
Damien Miller757f34e2010-08-05 13:05:31 +10001632 tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
1633 if (pkcs11provider != NULL) {
1634 if ((ca = load_pkcs11_key(tmp)) == NULL)
1635 fatal("No PKCS#11 key matching %s found", ca_key_path);
1636 } else if ((ca = load_identity(tmp)) == NULL)
1637 fatal("Couldn't load CA key \"%s\"", tmp);
Darren Tuckera627d422013-06-02 07:31:17 +10001638 free(tmp);
Damien Miller757f34e2010-08-05 13:05:31 +10001639
Damien Miller0a80ca12010-02-27 07:55:05 +11001640 for (i = 0; i < argc; i++) {
1641 /* Split list of principals */
1642 n = 0;
1643 if (cert_principals != NULL) {
1644 otmp = tmp = xstrdup(cert_principals);
1645 plist = NULL;
1646 for (; (cp = strsep(&tmp, ",")) != NULL; n++) {
1647 plist = xrealloc(plist, n + 1, sizeof(*plist));
1648 if (*(plist[n] = xstrdup(cp)) == '\0')
1649 fatal("Empty principal name");
1650 }
Darren Tuckera627d422013-06-02 07:31:17 +10001651 free(otmp);
Damien Miller0a80ca12010-02-27 07:55:05 +11001652 }
1653
1654 tmp = tilde_expand_filename(argv[i], pw->pw_uid);
1655 if ((public = key_load_public(tmp, &comment)) == NULL)
1656 fatal("%s: unable to open \"%s\"", __func__, tmp);
Damien Millereb8b60e2010-08-31 22:41:14 +10001657 if (public->type != KEY_RSA && public->type != KEY_DSA &&
Damien Miller5be9d9e2013-12-07 11:24:01 +11001658 public->type != KEY_ECDSA && public->type != KEY_ED25519)
Damien Miller0a80ca12010-02-27 07:55:05 +11001659 fatal("%s: key \"%s\" type %s cannot be certified",
1660 __func__, tmp, key_type(public));
1661
1662 /* Prepare certificate to sign */
Damien Miller4e270b02010-04-16 15:56:21 +10001663 if (key_to_certified(public, v00) != 0)
Damien Miller0a80ca12010-02-27 07:55:05 +11001664 fatal("Could not upgrade key %s to certificate", tmp);
1665 public->cert->type = cert_key_type;
Damien Miller4e270b02010-04-16 15:56:21 +10001666 public->cert->serial = (u_int64_t)cert_serial;
Damien Miller0a80ca12010-02-27 07:55:05 +11001667 public->cert->key_id = xstrdup(cert_key_id);
1668 public->cert->nprincipals = n;
1669 public->cert->principals = plist;
1670 public->cert->valid_after = cert_valid_from;
1671 public->cert->valid_before = cert_valid_to;
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001672 if (v00) {
Damien Miller86687062014-07-02 15:28:02 +10001673 prepare_options_buf(public->cert->critical,
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001674 OPTIONS_CRITICAL|OPTIONS_EXTENSIONS);
1675 } else {
Damien Miller86687062014-07-02 15:28:02 +10001676 prepare_options_buf(public->cert->critical,
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001677 OPTIONS_CRITICAL);
Damien Miller86687062014-07-02 15:28:02 +10001678 prepare_options_buf(public->cert->extensions,
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001679 OPTIONS_EXTENSIONS);
1680 }
Damien Miller0a80ca12010-02-27 07:55:05 +11001681 public->cert->signature_key = key_from_private(ca);
1682
1683 if (key_certify(public, ca) != 0)
1684 fatal("Couldn't not certify key %s", tmp);
1685
1686 if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0)
1687 *cp = '\0';
1688 xasprintf(&out, "%s-cert.pub", tmp);
Darren Tuckera627d422013-06-02 07:31:17 +10001689 free(tmp);
Damien Miller0a80ca12010-02-27 07:55:05 +11001690
1691 if ((fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
1692 fatal("Could not open \"%s\" for writing: %s", out,
1693 strerror(errno));
1694 if ((f = fdopen(fd, "w")) == NULL)
1695 fatal("%s: fdopen: %s", __func__, strerror(errno));
1696 if (!key_write(public, f))
1697 fatal("Could not write certified key to %s", out);
1698 fprintf(f, " %s\n", comment);
1699 fclose(f);
1700
Damien Miller4e270b02010-04-16 15:56:21 +10001701 if (!quiet) {
1702 logit("Signed %s key %s: id \"%s\" serial %llu%s%s "
1703 "valid %s", key_cert_type(public),
Damien Miller821de0a2011-01-11 17:20:29 +11001704 out, public->cert->key_id,
1705 (unsigned long long)public->cert->serial,
Damien Miller0a80ca12010-02-27 07:55:05 +11001706 cert_principals != NULL ? " for " : "",
1707 cert_principals != NULL ? cert_principals : "",
Damien Millerf2b70ca2010-03-05 07:39:35 +11001708 fmt_validity(cert_valid_from, cert_valid_to));
Damien Miller4e270b02010-04-16 15:56:21 +10001709 }
Damien Miller0a80ca12010-02-27 07:55:05 +11001710
1711 key_free(public);
Darren Tuckera627d422013-06-02 07:31:17 +10001712 free(out);
Damien Miller0a80ca12010-02-27 07:55:05 +11001713 }
Damien Miller1f0311c2014-05-15 14:24:09 +10001714#ifdef ENABLE_PKCS11
Damien Miller757f34e2010-08-05 13:05:31 +10001715 pkcs11_terminate();
Damien Miller1f0311c2014-05-15 14:24:09 +10001716#endif
Damien Miller0a80ca12010-02-27 07:55:05 +11001717 exit(0);
1718}
1719
1720static u_int64_t
1721parse_relative_time(const char *s, time_t now)
1722{
1723 int64_t mul, secs;
1724
1725 mul = *s == '-' ? -1 : 1;
1726
1727 if ((secs = convtime(s + 1)) == -1)
1728 fatal("Invalid relative certificate time %s", s);
1729 if (mul == -1 && secs > now)
1730 fatal("Certificate time %s cannot be represented", s);
1731 return now + (u_int64_t)(secs * mul);
1732}
1733
1734static u_int64_t
1735parse_absolute_time(const char *s)
1736{
1737 struct tm tm;
1738 time_t tt;
Damien Miller2ca342b2010-03-03 12:14:15 +11001739 char buf[32], *fmt;
Damien Miller0a80ca12010-02-27 07:55:05 +11001740
Damien Miller2ca342b2010-03-03 12:14:15 +11001741 /*
1742 * POSIX strptime says "The application shall ensure that there
1743 * is white-space or other non-alphanumeric characters between
1744 * any two conversion specifications" so arrange things this way.
1745 */
1746 switch (strlen(s)) {
1747 case 8:
Damien Miller3e1ee492010-03-08 09:24:11 +11001748 fmt = "%Y-%m-%d";
1749 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6);
Damien Miller2ca342b2010-03-03 12:14:15 +11001750 break;
1751 case 14:
Damien Miller3e1ee492010-03-08 09:24:11 +11001752 fmt = "%Y-%m-%dT%H:%M:%S";
1753 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s",
Damien Miller2ca342b2010-03-03 12:14:15 +11001754 s, s + 4, s + 6, s + 8, s + 10, s + 12);
1755 break;
1756 default:
Damien Miller0a80ca12010-02-27 07:55:05 +11001757 fatal("Invalid certificate time format %s", s);
Damien Miller2ca342b2010-03-03 12:14:15 +11001758 }
Damien Miller0a80ca12010-02-27 07:55:05 +11001759
Damien Miller1d2c4562014-02-04 11:18:20 +11001760 memset(&tm, 0, sizeof(tm));
Damien Miller2ca342b2010-03-03 12:14:15 +11001761 if (strptime(buf, fmt, &tm) == NULL)
Damien Miller0a80ca12010-02-27 07:55:05 +11001762 fatal("Invalid certificate time %s", s);
1763 if ((tt = mktime(&tm)) < 0)
1764 fatal("Certificate time %s cannot be represented", s);
1765 return (u_int64_t)tt;
1766}
1767
1768static void
1769parse_cert_times(char *timespec)
1770{
1771 char *from, *to;
1772 time_t now = time(NULL);
1773 int64_t secs;
1774
1775 /* +timespec relative to now */
1776 if (*timespec == '+' && strchr(timespec, ':') == NULL) {
1777 if ((secs = convtime(timespec + 1)) == -1)
1778 fatal("Invalid relative certificate life %s", timespec);
1779 cert_valid_to = now + secs;
1780 /*
1781 * Backdate certificate one minute to avoid problems on hosts
1782 * with poorly-synchronised clocks.
1783 */
1784 cert_valid_from = ((now - 59)/ 60) * 60;
1785 return;
1786 }
1787
1788 /*
1789 * from:to, where
1790 * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS
1791 * to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS
1792 */
1793 from = xstrdup(timespec);
1794 to = strchr(from, ':');
1795 if (to == NULL || from == to || *(to + 1) == '\0')
Damien Miller910f2092010-03-04 14:17:22 +11001796 fatal("Invalid certificate life specification %s", timespec);
Damien Miller0a80ca12010-02-27 07:55:05 +11001797 *to++ = '\0';
1798
1799 if (*from == '-' || *from == '+')
1800 cert_valid_from = parse_relative_time(from, now);
1801 else
1802 cert_valid_from = parse_absolute_time(from);
1803
1804 if (*to == '-' || *to == '+')
Damien Miller5b01b0d2013-10-23 16:31:31 +11001805 cert_valid_to = parse_relative_time(to, now);
Damien Miller0a80ca12010-02-27 07:55:05 +11001806 else
1807 cert_valid_to = parse_absolute_time(to);
1808
1809 if (cert_valid_to <= cert_valid_from)
1810 fatal("Empty certificate validity interval");
Darren Tuckera627d422013-06-02 07:31:17 +10001811 free(from);
Damien Miller0a80ca12010-02-27 07:55:05 +11001812}
1813
1814static void
Damien Miller4e270b02010-04-16 15:56:21 +10001815add_cert_option(char *opt)
Damien Miller0a80ca12010-02-27 07:55:05 +11001816{
1817 char *val;
1818
Damien Miller044f4a62011-05-05 14:14:08 +10001819 if (strcasecmp(opt, "clear") == 0)
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001820 certflags_flags = 0;
Damien Miller0a80ca12010-02-27 07:55:05 +11001821 else if (strcasecmp(opt, "no-x11-forwarding") == 0)
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001822 certflags_flags &= ~CERTOPT_X_FWD;
Damien Miller0a80ca12010-02-27 07:55:05 +11001823 else if (strcasecmp(opt, "permit-x11-forwarding") == 0)
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001824 certflags_flags |= CERTOPT_X_FWD;
Damien Miller0a80ca12010-02-27 07:55:05 +11001825 else if (strcasecmp(opt, "no-agent-forwarding") == 0)
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001826 certflags_flags &= ~CERTOPT_AGENT_FWD;
Damien Miller0a80ca12010-02-27 07:55:05 +11001827 else if (strcasecmp(opt, "permit-agent-forwarding") == 0)
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001828 certflags_flags |= CERTOPT_AGENT_FWD;
Damien Miller0a80ca12010-02-27 07:55:05 +11001829 else if (strcasecmp(opt, "no-port-forwarding") == 0)
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001830 certflags_flags &= ~CERTOPT_PORT_FWD;
Damien Miller0a80ca12010-02-27 07:55:05 +11001831 else if (strcasecmp(opt, "permit-port-forwarding") == 0)
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001832 certflags_flags |= CERTOPT_PORT_FWD;
Damien Miller0a80ca12010-02-27 07:55:05 +11001833 else if (strcasecmp(opt, "no-pty") == 0)
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001834 certflags_flags &= ~CERTOPT_PTY;
Damien Miller0a80ca12010-02-27 07:55:05 +11001835 else if (strcasecmp(opt, "permit-pty") == 0)
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001836 certflags_flags |= CERTOPT_PTY;
Damien Miller0a80ca12010-02-27 07:55:05 +11001837 else if (strcasecmp(opt, "no-user-rc") == 0)
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001838 certflags_flags &= ~CERTOPT_USER_RC;
Damien Miller0a80ca12010-02-27 07:55:05 +11001839 else if (strcasecmp(opt, "permit-user-rc") == 0)
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001840 certflags_flags |= CERTOPT_USER_RC;
Damien Miller0a80ca12010-02-27 07:55:05 +11001841 else if (strncasecmp(opt, "force-command=", 14) == 0) {
1842 val = opt + 14;
1843 if (*val == '\0')
Damien Miller4e270b02010-04-16 15:56:21 +10001844 fatal("Empty force-command option");
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001845 if (certflags_command != NULL)
Damien Miller0a80ca12010-02-27 07:55:05 +11001846 fatal("force-command already specified");
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001847 certflags_command = xstrdup(val);
Damien Miller0a80ca12010-02-27 07:55:05 +11001848 } else if (strncasecmp(opt, "source-address=", 15) == 0) {
1849 val = opt + 15;
1850 if (*val == '\0')
Damien Miller4e270b02010-04-16 15:56:21 +10001851 fatal("Empty source-address option");
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001852 if (certflags_src_addr != NULL)
Damien Miller0a80ca12010-02-27 07:55:05 +11001853 fatal("source-address already specified");
1854 if (addr_match_cidr_list(NULL, val) != 0)
1855 fatal("Invalid source-address list");
Damien Millerd0e4a8e2010-05-21 14:58:32 +10001856 certflags_src_addr = xstrdup(val);
Damien Miller0a80ca12010-02-27 07:55:05 +11001857 } else
Damien Miller4e270b02010-04-16 15:56:21 +10001858 fatal("Unsupported certificate option \"%s\"", opt);
Damien Miller0a80ca12010-02-27 07:55:05 +11001859}
1860
Ben Lindstrombba81212001-06-25 05:01:22 +00001861static void
Damien Millerd834d352010-06-26 09:48:02 +10001862show_options(const Buffer *optbuf, int v00, int in_critical)
1863{
Damien Miller633de332014-05-15 13:48:26 +10001864 char *name, *arg;
1865 const u_char *data;
Damien Millerd834d352010-06-26 09:48:02 +10001866 u_int dlen;
1867 Buffer options, option;
1868
1869 buffer_init(&options);
1870 buffer_append(&options, buffer_ptr(optbuf), buffer_len(optbuf));
1871
1872 buffer_init(&option);
1873 while (buffer_len(&options) != 0) {
1874 name = buffer_get_string(&options, NULL);
1875 data = buffer_get_string_ptr(&options, &dlen);
1876 buffer_append(&option, data, dlen);
1877 printf(" %s", name);
1878 if ((v00 || !in_critical) &&
1879 (strcmp(name, "permit-X11-forwarding") == 0 ||
1880 strcmp(name, "permit-agent-forwarding") == 0 ||
1881 strcmp(name, "permit-port-forwarding") == 0 ||
1882 strcmp(name, "permit-pty") == 0 ||
1883 strcmp(name, "permit-user-rc") == 0))
1884 printf("\n");
1885 else if ((v00 || in_critical) &&
1886 (strcmp(name, "force-command") == 0 ||
1887 strcmp(name, "source-address") == 0)) {
Damien Miller633de332014-05-15 13:48:26 +10001888 arg = buffer_get_cstring(&option, NULL);
1889 printf(" %s\n", arg);
1890 free(arg);
Damien Millerd834d352010-06-26 09:48:02 +10001891 } else {
1892 printf(" UNKNOWN OPTION (len %u)\n",
1893 buffer_len(&option));
1894 buffer_clear(&option);
1895 }
Darren Tuckera627d422013-06-02 07:31:17 +10001896 free(name);
Damien Millerd834d352010-06-26 09:48:02 +10001897 if (buffer_len(&option) != 0)
1898 fatal("Option corrupt: extra data at end");
1899 }
1900 buffer_free(&option);
1901 buffer_free(&options);
1902}
1903
1904static void
Damien Millerf2b70ca2010-03-05 07:39:35 +11001905do_show_cert(struct passwd *pw)
1906{
1907 Key *key;
1908 struct stat st;
1909 char *key_fp, *ca_fp;
Damien Millerd834d352010-06-26 09:48:02 +10001910 u_int i, v00;
Damien Millerf2b70ca2010-03-05 07:39:35 +11001911
1912 if (!have_identity)
1913 ask_filename(pw, "Enter file in which the key is");
Damien Millerba3420a2010-06-26 09:39:07 +10001914 if (stat(identity_file, &st) < 0)
1915 fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
Damien Millerf2b70ca2010-03-05 07:39:35 +11001916 if ((key = key_load_public(identity_file, NULL)) == NULL)
1917 fatal("%s is not a public key", identity_file);
1918 if (!key_is_cert(key))
1919 fatal("%s is not a certificate", identity_file);
Damien Miller4e270b02010-04-16 15:56:21 +10001920 v00 = key->type == KEY_RSA_CERT_V00 || key->type == KEY_DSA_CERT_V00;
1921
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001922 key_fp = key_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT);
Damien Millerf2b70ca2010-03-05 07:39:35 +11001923 ca_fp = key_fingerprint(key->cert->signature_key,
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001924 fingerprint_hash, SSH_FP_DEFAULT);
Damien Millerf2b70ca2010-03-05 07:39:35 +11001925
1926 printf("%s:\n", identity_file);
Damien Miller4e270b02010-04-16 15:56:21 +10001927 printf(" Type: %s %s certificate\n", key_ssh_name(key),
1928 key_cert_type(key));
1929 printf(" Public key: %s %s\n", key_type(key), key_fp);
1930 printf(" Signing CA: %s %s\n",
Damien Millerf2b70ca2010-03-05 07:39:35 +11001931 key_type(key->cert->signature_key), ca_fp);
Damien Miller4e270b02010-04-16 15:56:21 +10001932 printf(" Key ID: \"%s\"\n", key->cert->key_id);
Damien Miller821de0a2011-01-11 17:20:29 +11001933 if (!v00) {
1934 printf(" Serial: %llu\n",
1935 (unsigned long long)key->cert->serial);
1936 }
Damien Millerf2b70ca2010-03-05 07:39:35 +11001937 printf(" Valid: %s\n",
1938 fmt_validity(key->cert->valid_after, key->cert->valid_before));
1939 printf(" Principals: ");
1940 if (key->cert->nprincipals == 0)
1941 printf("(none)\n");
1942 else {
1943 for (i = 0; i < key->cert->nprincipals; i++)
1944 printf("\n %s",
1945 key->cert->principals[i]);
1946 printf("\n");
1947 }
Damien Miller4e270b02010-04-16 15:56:21 +10001948 printf(" Critical Options: ");
Damien Miller86687062014-07-02 15:28:02 +10001949 if (buffer_len(key->cert->critical) == 0)
Damien Millerf2b70ca2010-03-05 07:39:35 +11001950 printf("(none)\n");
1951 else {
1952 printf("\n");
Damien Miller86687062014-07-02 15:28:02 +10001953 show_options(key->cert->critical, v00, 1);
Damien Millerf2b70ca2010-03-05 07:39:35 +11001954 }
Damien Miller4e270b02010-04-16 15:56:21 +10001955 if (!v00) {
1956 printf(" Extensions: ");
Damien Miller86687062014-07-02 15:28:02 +10001957 if (buffer_len(key->cert->extensions) == 0)
Damien Miller4e270b02010-04-16 15:56:21 +10001958 printf("(none)\n");
1959 else {
1960 printf("\n");
Damien Miller86687062014-07-02 15:28:02 +10001961 show_options(key->cert->extensions, v00, 0);
Damien Miller4e270b02010-04-16 15:56:21 +10001962 }
1963 }
Damien Millerf2b70ca2010-03-05 07:39:35 +11001964 exit(0);
1965}
1966
1967static void
Damien Millerf3747bf2013-01-18 11:44:04 +11001968load_krl(const char *path, struct ssh_krl **krlp)
1969{
1970 Buffer krlbuf;
1971 int fd;
1972
1973 buffer_init(&krlbuf);
1974 if ((fd = open(path, O_RDONLY)) == -1)
1975 fatal("open %s: %s", path, strerror(errno));
1976 if (!key_load_file(fd, path, &krlbuf))
1977 fatal("Unable to load KRL");
1978 close(fd);
1979 /* XXX check sigs */
1980 if (ssh_krl_from_blob(&krlbuf, krlp, NULL, 0) != 0 ||
1981 *krlp == NULL)
1982 fatal("Invalid KRL file");
1983 buffer_free(&krlbuf);
1984}
1985
1986static void
1987update_krl_from_file(struct passwd *pw, const char *file, const Key *ca,
1988 struct ssh_krl *krl)
1989{
1990 Key *key = NULL;
1991 u_long lnum = 0;
1992 char *path, *cp, *ep, line[SSH_MAX_PUBKEY_BYTES];
1993 unsigned long long serial, serial2;
1994 int i, was_explicit_key, was_sha1, r;
1995 FILE *krl_spec;
1996
1997 path = tilde_expand_filename(file, pw->pw_uid);
1998 if (strcmp(path, "-") == 0) {
1999 krl_spec = stdin;
2000 free(path);
2001 path = xstrdup("(standard input)");
2002 } else if ((krl_spec = fopen(path, "r")) == NULL)
2003 fatal("fopen %s: %s", path, strerror(errno));
2004
2005 if (!quiet)
2006 printf("Revoking from %s\n", path);
2007 while (read_keyfile_line(krl_spec, path, line, sizeof(line),
2008 &lnum) == 0) {
2009 was_explicit_key = was_sha1 = 0;
2010 cp = line + strspn(line, " \t");
2011 /* Trim trailing space, comments and strip \n */
2012 for (i = 0, r = -1; cp[i] != '\0'; i++) {
2013 if (cp[i] == '#' || cp[i] == '\n') {
2014 cp[i] = '\0';
2015 break;
2016 }
2017 if (cp[i] == ' ' || cp[i] == '\t') {
2018 /* Remember the start of a span of whitespace */
2019 if (r == -1)
2020 r = i;
2021 } else
2022 r = -1;
2023 }
2024 if (r != -1)
2025 cp[r] = '\0';
2026 if (*cp == '\0')
2027 continue;
2028 if (strncasecmp(cp, "serial:", 7) == 0) {
2029 if (ca == NULL) {
Damien Millerd234afb2013-08-21 02:42:58 +10002030 fatal("revoking certificates by serial number "
Damien Millerf3747bf2013-01-18 11:44:04 +11002031 "requires specification of a CA key");
2032 }
2033 cp += 7;
2034 cp = cp + strspn(cp, " \t");
2035 errno = 0;
2036 serial = strtoull(cp, &ep, 0);
2037 if (*cp == '\0' || (*ep != '\0' && *ep != '-'))
2038 fatal("%s:%lu: invalid serial \"%s\"",
2039 path, lnum, cp);
2040 if (errno == ERANGE && serial == ULLONG_MAX)
2041 fatal("%s:%lu: serial out of range",
2042 path, lnum);
2043 serial2 = serial;
2044 if (*ep == '-') {
2045 cp = ep + 1;
2046 errno = 0;
2047 serial2 = strtoull(cp, &ep, 0);
2048 if (*cp == '\0' || *ep != '\0')
2049 fatal("%s:%lu: invalid serial \"%s\"",
2050 path, lnum, cp);
2051 if (errno == ERANGE && serial2 == ULLONG_MAX)
2052 fatal("%s:%lu: serial out of range",
2053 path, lnum);
2054 if (serial2 <= serial)
2055 fatal("%s:%lu: invalid serial range "
2056 "%llu:%llu", path, lnum,
2057 (unsigned long long)serial,
2058 (unsigned long long)serial2);
2059 }
2060 if (ssh_krl_revoke_cert_by_serial_range(krl,
2061 ca, serial, serial2) != 0) {
2062 fatal("%s: revoke serial failed",
2063 __func__);
2064 }
2065 } else if (strncasecmp(cp, "id:", 3) == 0) {
2066 if (ca == NULL) {
Damien Millerd5d9d7b2013-08-21 02:43:27 +10002067 fatal("revoking certificates by key ID "
Damien Millerf3747bf2013-01-18 11:44:04 +11002068 "requires specification of a CA key");
2069 }
2070 cp += 3;
2071 cp = cp + strspn(cp, " \t");
2072 if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0)
2073 fatal("%s: revoke key ID failed", __func__);
2074 } else {
2075 if (strncasecmp(cp, "key:", 4) == 0) {
2076 cp += 4;
2077 cp = cp + strspn(cp, " \t");
2078 was_explicit_key = 1;
2079 } else if (strncasecmp(cp, "sha1:", 5) == 0) {
2080 cp += 5;
2081 cp = cp + strspn(cp, " \t");
2082 was_sha1 = 1;
2083 } else {
2084 /*
2085 * Just try to process the line as a key.
2086 * Parsing will fail if it isn't.
2087 */
2088 }
2089 if ((key = key_new(KEY_UNSPEC)) == NULL)
2090 fatal("key_new");
2091 if (key_read(key, &cp) != 1)
2092 fatal("%s:%lu: invalid key", path, lnum);
2093 if (was_explicit_key)
2094 r = ssh_krl_revoke_key_explicit(krl, key);
2095 else if (was_sha1)
2096 r = ssh_krl_revoke_key_sha1(krl, key);
2097 else
2098 r = ssh_krl_revoke_key(krl, key);
2099 if (r != 0)
2100 fatal("%s: revoke key failed", __func__);
2101 key_free(key);
2102 }
2103 }
2104 if (strcmp(path, "-") != 0)
2105 fclose(krl_spec);
Damien Miller0d6771b2013-04-23 15:23:24 +10002106 free(path);
Damien Millerf3747bf2013-01-18 11:44:04 +11002107}
2108
2109static void
2110do_gen_krl(struct passwd *pw, int updating, int argc, char **argv)
2111{
2112 struct ssh_krl *krl;
2113 struct stat sb;
2114 Key *ca = NULL;
2115 int fd, i;
2116 char *tmp;
2117 Buffer kbuf;
2118
2119 if (*identity_file == '\0')
2120 fatal("KRL generation requires an output file");
2121 if (stat(identity_file, &sb) == -1) {
2122 if (errno != ENOENT)
2123 fatal("Cannot access KRL \"%s\": %s",
2124 identity_file, strerror(errno));
2125 if (updating)
2126 fatal("KRL \"%s\" does not exist", identity_file);
2127 }
2128 if (ca_key_path != NULL) {
2129 tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
2130 if ((ca = key_load_public(tmp, NULL)) == NULL)
2131 fatal("Cannot load CA public key %s", tmp);
Darren Tuckera627d422013-06-02 07:31:17 +10002132 free(tmp);
Damien Millerf3747bf2013-01-18 11:44:04 +11002133 }
2134
2135 if (updating)
2136 load_krl(identity_file, &krl);
2137 else if ((krl = ssh_krl_init()) == NULL)
2138 fatal("couldn't create KRL");
2139
2140 if (cert_serial != 0)
2141 ssh_krl_set_version(krl, cert_serial);
2142 if (identity_comment != NULL)
2143 ssh_krl_set_comment(krl, identity_comment);
2144
2145 for (i = 0; i < argc; i++)
2146 update_krl_from_file(pw, argv[i], ca, krl);
2147
2148 buffer_init(&kbuf);
2149 if (ssh_krl_to_blob(krl, &kbuf, NULL, 0) != 0)
2150 fatal("Couldn't generate KRL");
2151 if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
2152 fatal("open %s: %s", identity_file, strerror(errno));
2153 if (atomicio(vwrite, fd, buffer_ptr(&kbuf), buffer_len(&kbuf)) !=
2154 buffer_len(&kbuf))
2155 fatal("write %s: %s", identity_file, strerror(errno));
2156 close(fd);
2157 buffer_free(&kbuf);
2158 ssh_krl_free(krl);
Damien Miller0d6771b2013-04-23 15:23:24 +10002159 if (ca != NULL)
2160 key_free(ca);
Damien Millerf3747bf2013-01-18 11:44:04 +11002161}
2162
2163static void
2164do_check_krl(struct passwd *pw, int argc, char **argv)
2165{
2166 int i, r, ret = 0;
2167 char *comment;
2168 struct ssh_krl *krl;
2169 Key *k;
2170
2171 if (*identity_file == '\0')
2172 fatal("KRL checking requires an input file");
2173 load_krl(identity_file, &krl);
2174 for (i = 0; i < argc; i++) {
2175 if ((k = key_load_public(argv[i], &comment)) == NULL)
2176 fatal("Cannot load public key %s", argv[i]);
2177 r = ssh_krl_check_key(krl, k);
2178 printf("%s%s%s%s: %s\n", argv[i],
2179 *comment ? " (" : "", comment, *comment ? ")" : "",
2180 r == 0 ? "ok" : "REVOKED");
2181 if (r != 0)
2182 ret = 1;
2183 key_free(k);
2184 free(comment);
2185 }
2186 ssh_krl_free(krl);
2187 exit(ret);
2188}
2189
2190static void
Damien Miller431f66b1999-11-21 18:31:57 +11002191usage(void)
2192{
Damien Millerf0858de2014-04-20 13:01:30 +10002193 fprintf(stderr,
2194 "usage: ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa | rsa1]\n"
2195 " [-N new_passphrase] [-C comment] [-f output_keyfile]\n"
2196 " ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-f keyfile]\n"
2197 " ssh-keygen -i [-m key_format] [-f input_keyfile]\n"
2198 " ssh-keygen -e [-m key_format] [-f input_keyfile]\n"
2199 " ssh-keygen -y [-f input_keyfile]\n"
2200 " ssh-keygen -c [-P passphrase] [-C comment] [-f keyfile]\n"
djm@openbsd.org56d1c832014-12-21 22:27:55 +00002201 " ssh-keygen -l [-E fingerprint_hash] [-f input_keyfile]\n"
Damien Millerf0858de2014-04-20 13:01:30 +10002202 " ssh-keygen -B [-f input_keyfile]\n");
Damien Miller7ea845e2010-02-12 09:21:02 +11002203#ifdef ENABLE_PKCS11
Damien Millerf0858de2014-04-20 13:01:30 +10002204 fprintf(stderr,
2205 " ssh-keygen -D pkcs11\n");
Damien Miller7ea845e2010-02-12 09:21:02 +11002206#endif
Damien Millerf0858de2014-04-20 13:01:30 +10002207 fprintf(stderr,
2208 " ssh-keygen -F hostname [-f known_hosts_file] [-l]\n"
2209 " ssh-keygen -H [-f known_hosts_file]\n"
2210 " ssh-keygen -R hostname [-f known_hosts_file]\n"
2211 " ssh-keygen -r hostname [-f input_keyfile] [-g]\n"
2212 " ssh-keygen -G output_file [-v] [-b bits] [-M memory] [-S start_point]\n"
2213 " ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n"
2214 " [-j start_line] [-K checkpt] [-W generator]\n"
2215 " ssh-keygen -s ca_key -I certificate_identity [-h] [-n principals]\n"
2216 " [-O option] [-V validity_interval] [-z serial_number] file ...\n"
2217 " ssh-keygen -L [-f input_keyfile]\n"
2218 " ssh-keygen -A\n"
2219 " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
2220 " file ...\n"
2221 " ssh-keygen -Q -f krl_file file ...\n");
Damien Miller95def091999-11-25 00:26:21 +11002222 exit(1);
Damien Miller431f66b1999-11-21 18:31:57 +11002223}
2224
Damien Miller95def091999-11-25 00:26:21 +11002225/*
2226 * Main program for key management.
2227 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002228int
Damien Millerdf8b7db2007-01-05 16:22:57 +11002229main(int argc, char **argv)
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002230{
Damien Millera41c8b12002-01-22 23:05:08 +11002231 char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2;
Damien Miller390d0562011-10-18 16:05:19 +11002232 char *checkpoint = NULL;
Damien Millerf3747bf2013-01-18 11:44:04 +11002233 char out_file[MAXPATHLEN], *ep, *rr_hostname = NULL;
Ben Lindstrom5fc62702001-03-09 18:19:24 +00002234 Key *private, *public;
Damien Miller95def091999-11-25 00:26:21 +11002235 struct passwd *pw;
Damien Miller95def091999-11-25 00:26:21 +11002236 struct stat st;
Damien Miller7ea845e2010-02-12 09:21:02 +11002237 int opt, type, fd;
Damien Millerbcd00ab2013-12-07 10:41:55 +11002238 u_int32_t memory = 0, generator_wanted = 0;
Darren Tucker019cefe2003-08-02 22:40:07 +10002239 int do_gen_candidates = 0, do_screen_candidates = 0;
Damien Millerf3747bf2013-01-18 11:44:04 +11002240 int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
Damien Millerdfceafe2012-07-06 13:44:19 +10002241 unsigned long start_lineno = 0, lines_to_process = 0;
Darren Tucker019cefe2003-08-02 22:40:07 +10002242 BIGNUM *start = NULL;
Damien Miller95def091999-11-25 00:26:21 +11002243 FILE *f;
Damien Miller02e754f2005-05-26 12:19:39 +10002244 const char *errstr;
Damien Miller0bc1bd82000-11-13 22:57:25 +11002245
Damien Miller95def091999-11-25 00:26:21 +11002246 extern int optind;
2247 extern char *optarg;
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002248
Darren Tuckerce321d82005-10-03 18:11:24 +10002249 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2250 sanitise_stdfd();
2251
Darren Tucker9ac56e92007-01-14 10:19:59 +11002252 __progname = ssh_get_progname(argv[0]);
Damien Millerf9b625c2000-07-09 22:42:32 +10002253
Damien Miller72ef7c12015-01-15 02:21:31 +11002254#ifdef WITH_OPENSSL
Damien Miller4314c2b2010-09-10 11:12:09 +10002255 OpenSSL_add_all_algorithms();
Damien Miller72ef7c12015-01-15 02:21:31 +11002256#endif
Damien Millerdf8b7db2007-01-05 16:22:57 +11002257 log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
Darren Tucker019cefe2003-08-02 22:40:07 +10002258
Kevin Steves3a881912002-07-20 19:05:40 +00002259 seed_rng();
Damien Millereba71ba2000-04-29 23:57:08 +10002260
Damien Miller5428f641999-11-25 11:54:57 +11002261 /* we need this for the home * directory. */
Damien Miller95def091999-11-25 00:26:21 +11002262 pw = getpwuid(getuid());
2263 if (!pw) {
Damien Miller3009d3c2013-07-20 13:22:31 +10002264 printf("No user exists for uid %lu\n", (u_long)getuid());
Damien Miller95def091999-11-25 00:26:21 +11002265 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002266 }
Damien Millereba71ba2000-04-29 23:57:08 +10002267 if (gethostname(hostname, sizeof(hostname)) < 0) {
2268 perror("gethostname");
2269 exit(1);
2270 }
Damien Miller5428f641999-11-25 11:54:57 +11002271
djm@openbsd.org56d1c832014-12-21 22:27:55 +00002272 /* Remaining characters: UYdw */
Damien Millerbcd00ab2013-12-07 10:41:55 +11002273 while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy"
djm@openbsd.org56d1c832014-12-21 22:27:55 +00002274 "C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:"
2275 "a:b:f:g:j:m:n:r:s:t:z:")) != -1) {
Damien Miller95def091999-11-25 00:26:21 +11002276 switch (opt) {
Damien Miller58f1baf2011-05-05 14:06:15 +10002277 case 'A':
2278 gen_all_hostkeys = 1;
2279 break;
Damien Miller95def091999-11-25 00:26:21 +11002280 case 'b':
Damien Miller57737942010-09-10 11:16:37 +10002281 bits = (u_int32_t)strtonum(optarg, 256, 32768, &errstr);
Damien Miller02e754f2005-05-26 12:19:39 +10002282 if (errstr)
2283 fatal("Bits has bad value %s (%s)",
2284 optarg, errstr);
Damien Miller95def091999-11-25 00:26:21 +11002285 break;
djm@openbsd.org56d1c832014-12-21 22:27:55 +00002286 case 'E':
2287 fingerprint_hash = ssh_digest_alg_by_name(optarg);
2288 if (fingerprint_hash == -1)
2289 fatal("Invalid hash algorithm \"%s\"", optarg);
2290 break;
Damien Miller4b42d7f2005-03-01 21:48:35 +11002291 case 'F':
2292 find_host = 1;
2293 rr_hostname = optarg;
2294 break;
2295 case 'H':
2296 hash_hosts = 1;
2297 break;
Damien Miller0a80ca12010-02-27 07:55:05 +11002298 case 'I':
2299 cert_key_id = optarg;
2300 break;
Damien Millerdfceafe2012-07-06 13:44:19 +10002301 case 'J':
2302 lines_to_process = strtoul(optarg, NULL, 10);
2303 break;
2304 case 'j':
2305 start_lineno = strtoul(optarg, NULL, 10);
2306 break;
Damien Miller4b42d7f2005-03-01 21:48:35 +11002307 case 'R':
2308 delete_host = 1;
2309 rr_hostname = optarg;
2310 break;
Damien Millerf2b70ca2010-03-05 07:39:35 +11002311 case 'L':
2312 show_cert = 1;
2313 break;
Damien Miller95def091999-11-25 00:26:21 +11002314 case 'l':
2315 print_fingerprint = 1;
2316 break;
Ben Lindstrom8fd372b2001-03-12 03:02:17 +00002317 case 'B':
2318 print_bubblebabble = 1;
2319 break;
Damien Miller44b25042010-07-02 13:35:01 +10002320 case 'm':
2321 if (strcasecmp(optarg, "RFC4716") == 0 ||
2322 strcasecmp(optarg, "ssh2") == 0) {
2323 convert_format = FMT_RFC4716;
2324 break;
2325 }
2326 if (strcasecmp(optarg, "PKCS8") == 0) {
2327 convert_format = FMT_PKCS8;
2328 break;
2329 }
2330 if (strcasecmp(optarg, "PEM") == 0) {
2331 convert_format = FMT_PEM;
2332 break;
2333 }
2334 fatal("Unsupported conversion format \"%s\"", optarg);
Damien Miller0a80ca12010-02-27 07:55:05 +11002335 case 'n':
2336 cert_principals = optarg;
2337 break;
Damien Millerbcd00ab2013-12-07 10:41:55 +11002338 case 'o':
2339 use_new_format = 1;
2340 break;
Damien Miller95def091999-11-25 00:26:21 +11002341 case 'p':
2342 change_passphrase = 1;
2343 break;
Damien Miller95def091999-11-25 00:26:21 +11002344 case 'c':
2345 change_comment = 1;
2346 break;
Damien Miller95def091999-11-25 00:26:21 +11002347 case 'f':
Damien Millerb089fb52005-05-26 12:16:18 +10002348 if (strlcpy(identity_file, optarg, sizeof(identity_file)) >=
2349 sizeof(identity_file))
2350 fatal("Identity filename too long");
Damien Miller95def091999-11-25 00:26:21 +11002351 have_identity = 1;
2352 break;
Damien Miller37876e92003-05-15 10:19:46 +10002353 case 'g':
2354 print_generic = 1;
2355 break;
Damien Miller95def091999-11-25 00:26:21 +11002356 case 'P':
2357 identity_passphrase = optarg;
2358 break;
Damien Miller95def091999-11-25 00:26:21 +11002359 case 'N':
2360 identity_new_passphrase = optarg;
2361 break;
Damien Millerf3747bf2013-01-18 11:44:04 +11002362 case 'Q':
2363 check_krl = 1;
2364 break;
Damien Miller0a80ca12010-02-27 07:55:05 +11002365 case 'O':
Damien Miller4e270b02010-04-16 15:56:21 +10002366 add_cert_option(optarg);
Damien Miller0a80ca12010-02-27 07:55:05 +11002367 break;
Damien Millerbcd00ab2013-12-07 10:41:55 +11002368 case 'Z':
2369 new_format_cipher = optarg;
2370 break;
Damien Miller95def091999-11-25 00:26:21 +11002371 case 'C':
2372 identity_comment = optarg;
2373 break;
Damien Miller95def091999-11-25 00:26:21 +11002374 case 'q':
2375 quiet = 1;
2376 break;
Ben Lindstrom5a707822001-04-22 17:15:46 +00002377 case 'e':
Damien Millereba71ba2000-04-29 23:57:08 +10002378 case 'x':
Ben Lindstrom5a707822001-04-22 17:15:46 +00002379 /* export key */
Damien Miller44b25042010-07-02 13:35:01 +10002380 convert_to = 1;
Damien Millereba71ba2000-04-29 23:57:08 +10002381 break;
Damien Miller0a80ca12010-02-27 07:55:05 +11002382 case 'h':
2383 cert_key_type = SSH2_CERT_TYPE_HOST;
Damien Millerd0e4a8e2010-05-21 14:58:32 +10002384 certflags_flags = 0;
Damien Miller0a80ca12010-02-27 07:55:05 +11002385 break;
Damien Millerf3747bf2013-01-18 11:44:04 +11002386 case 'k':
2387 gen_krl = 1;
2388 break;
Ben Lindstrom5a707822001-04-22 17:15:46 +00002389 case 'i':
Damien Millereba71ba2000-04-29 23:57:08 +10002390 case 'X':
Ben Lindstrom5a707822001-04-22 17:15:46 +00002391 /* import key */
Damien Miller44b25042010-07-02 13:35:01 +10002392 convert_from = 1;
Damien Millereba71ba2000-04-29 23:57:08 +10002393 break;
Damien Millereba71ba2000-04-29 23:57:08 +10002394 case 'y':
2395 print_public = 1;
2396 break;
Damien Miller0a80ca12010-02-27 07:55:05 +11002397 case 's':
2398 ca_key_path = optarg;
2399 break;
Damien Miller0bc1bd82000-11-13 22:57:25 +11002400 case 't':
2401 key_type_name = optarg;
Damien Miller0bc1bd82000-11-13 22:57:25 +11002402 break;
Ben Lindstrom8282d6a2001-08-06 21:44:05 +00002403 case 'D':
Damien Miller7ea845e2010-02-12 09:21:02 +11002404 pkcs11provider = optarg;
Ben Lindstromcd392282001-07-04 03:44:03 +00002405 break;
Damien Millerf3747bf2013-01-18 11:44:04 +11002406 case 'u':
2407 update_krl = 1;
2408 break;
Darren Tucker06930c72003-12-31 11:34:51 +11002409 case 'v':
2410 if (log_level == SYSLOG_LEVEL_INFO)
2411 log_level = SYSLOG_LEVEL_DEBUG1;
2412 else {
Darren Tuckerfc959702004-07-17 16:12:08 +10002413 if (log_level >= SYSLOG_LEVEL_DEBUG1 &&
Darren Tucker06930c72003-12-31 11:34:51 +11002414 log_level < SYSLOG_LEVEL_DEBUG3)
2415 log_level++;
2416 }
2417 break;
Damien Miller37876e92003-05-15 10:19:46 +10002418 case 'r':
Damien Miller4b42d7f2005-03-01 21:48:35 +11002419 rr_hostname = optarg;
Damien Miller37876e92003-05-15 10:19:46 +10002420 break;
Darren Tucker019cefe2003-08-02 22:40:07 +10002421 case 'W':
Damien Miller5f340062006-03-26 14:27:57 +11002422 generator_wanted = (u_int32_t)strtonum(optarg, 1,
2423 UINT_MAX, &errstr);
Damien Millerb089fb52005-05-26 12:16:18 +10002424 if (errstr)
2425 fatal("Desired generator has bad value: %s (%s)",
2426 optarg, errstr);
Darren Tucker019cefe2003-08-02 22:40:07 +10002427 break;
2428 case 'a':
Damien Millerbcd00ab2013-12-07 10:41:55 +11002429 rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr);
Damien Millerb089fb52005-05-26 12:16:18 +10002430 if (errstr)
Damien Millerbcd00ab2013-12-07 10:41:55 +11002431 fatal("Invalid number: %s (%s)",
Damien Millerb089fb52005-05-26 12:16:18 +10002432 optarg, errstr);
Darren Tucker019cefe2003-08-02 22:40:07 +10002433 break;
Damien Miller72ef7c12015-01-15 02:21:31 +11002434#ifdef WITH_OPENSSL
Darren Tucker019cefe2003-08-02 22:40:07 +10002435 case 'M':
Damien Miller5f340062006-03-26 14:27:57 +11002436 memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr);
Damien Miller4e270b02010-04-16 15:56:21 +10002437 if (errstr)
Damien Millerb089fb52005-05-26 12:16:18 +10002438 fatal("Memory limit is %s: %s", errstr, optarg);
Darren Tucker019cefe2003-08-02 22:40:07 +10002439 break;
2440 case 'G':
2441 do_gen_candidates = 1;
Damien Millerb089fb52005-05-26 12:16:18 +10002442 if (strlcpy(out_file, optarg, sizeof(out_file)) >=
2443 sizeof(out_file))
2444 fatal("Output filename too long");
Darren Tucker019cefe2003-08-02 22:40:07 +10002445 break;
2446 case 'T':
2447 do_screen_candidates = 1;
Damien Millerb089fb52005-05-26 12:16:18 +10002448 if (strlcpy(out_file, optarg, sizeof(out_file)) >=
2449 sizeof(out_file))
2450 fatal("Output filename too long");
Darren Tucker019cefe2003-08-02 22:40:07 +10002451 break;
Damien Miller390d0562011-10-18 16:05:19 +11002452 case 'K':
2453 if (strlen(optarg) >= MAXPATHLEN)
2454 fatal("Checkpoint filename too long");
2455 checkpoint = xstrdup(optarg);
2456 break;
Darren Tucker019cefe2003-08-02 22:40:07 +10002457 case 'S':
2458 /* XXX - also compare length against bits */
2459 if (BN_hex2bn(&start, optarg) == 0)
2460 fatal("Invalid start point.");
2461 break;
Damien Miller72ef7c12015-01-15 02:21:31 +11002462#endif /* WITH_OPENSSL */
Damien Miller0a80ca12010-02-27 07:55:05 +11002463 case 'V':
2464 parse_cert_times(optarg);
2465 break;
Damien Miller4e270b02010-04-16 15:56:21 +10002466 case 'z':
Damien Miller6f3b3622012-11-14 19:04:33 +11002467 errno = 0;
2468 cert_serial = strtoull(optarg, &ep, 10);
2469 if (*optarg < '0' || *optarg > '9' || *ep != '\0' ||
2470 (errno == ERANGE && cert_serial == ULLONG_MAX))
2471 fatal("Invalid serial number \"%s\"", optarg);
Damien Miller4e270b02010-04-16 15:56:21 +10002472 break;
Damien Miller95def091999-11-25 00:26:21 +11002473 case '?':
2474 default:
2475 usage();
2476 }
2477 }
Darren Tucker06930c72003-12-31 11:34:51 +11002478
2479 /* reinit */
Damien Millerdf8b7db2007-01-05 16:22:57 +11002480 log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1);
Darren Tucker06930c72003-12-31 11:34:51 +11002481
Damien Miller0a80ca12010-02-27 07:55:05 +11002482 argv += optind;
2483 argc -= optind;
2484
2485 if (ca_key_path != NULL) {
Damien Millerf3747bf2013-01-18 11:44:04 +11002486 if (argc < 1 && !gen_krl) {
Damien Miller0a80ca12010-02-27 07:55:05 +11002487 printf("Too few arguments.\n");
2488 usage();
2489 }
Damien Millerf3747bf2013-01-18 11:44:04 +11002490 } else if (argc > 0 && !gen_krl && !check_krl) {
Damien Miller95def091999-11-25 00:26:21 +11002491 printf("Too many arguments.\n");
2492 usage();
2493 }
2494 if (change_passphrase && change_comment) {
2495 printf("Can only have one of -p and -c.\n");
2496 usage();
2497 }
Darren Tucker0f7e9102008-06-08 12:54:29 +10002498 if (print_fingerprint && (delete_host || hash_hosts)) {
Damien Millerec77c952013-01-09 15:58:00 +11002499 printf("Cannot use -l with -H or -R.\n");
Darren Tucker0f7e9102008-06-08 12:54:29 +10002500 usage();
2501 }
Damien Millerf3747bf2013-01-18 11:44:04 +11002502 if (gen_krl) {
2503 do_gen_krl(pw, update_krl, argc, argv);
2504 return (0);
2505 }
2506 if (check_krl) {
2507 do_check_krl(pw, argc, argv);
2508 return (0);
2509 }
Damien Miller0a80ca12010-02-27 07:55:05 +11002510 if (ca_key_path != NULL) {
2511 if (cert_key_id == NULL)
2512 fatal("Must specify key id (-I) when certifying");
2513 do_ca_sign(pw, argc, argv);
2514 }
Damien Millerf2b70ca2010-03-05 07:39:35 +11002515 if (show_cert)
2516 do_show_cert(pw);
Damien Miller4b42d7f2005-03-01 21:48:35 +11002517 if (delete_host || hash_hosts || find_host)
2518 do_known_hosts(pw, rr_hostname);
Damien Millerec77c952013-01-09 15:58:00 +11002519 if (pkcs11provider != NULL)
2520 do_download(pw);
Ben Lindstrom8fd372b2001-03-12 03:02:17 +00002521 if (print_fingerprint || print_bubblebabble)
Damien Miller95def091999-11-25 00:26:21 +11002522 do_fingerprint(pw);
Damien Miller95def091999-11-25 00:26:21 +11002523 if (change_passphrase)
2524 do_change_passphrase(pw);
Damien Miller4a10d2e2002-03-11 22:53:29 +11002525 if (change_comment)
2526 do_change_comment(pw);
Damien Miller1f0311c2014-05-15 14:24:09 +10002527#ifdef WITH_OPENSSL
Damien Miller44b25042010-07-02 13:35:01 +10002528 if (convert_to)
2529 do_convert_to(pw);
2530 if (convert_from)
2531 do_convert_from(pw);
Damien Miller1f0311c2014-05-15 14:24:09 +10002532#endif
Damien Millereba71ba2000-04-29 23:57:08 +10002533 if (print_public)
2534 do_print_public(pw);
Damien Miller4b42d7f2005-03-01 21:48:35 +11002535 if (rr_hostname != NULL) {
Damien Millercb314822006-03-26 13:48:01 +11002536 unsigned int n = 0;
2537
2538 if (have_identity) {
2539 n = do_print_resource_record(pw,
2540 identity_file, rr_hostname);
2541 if (n == 0) {
2542 perror(identity_file);
2543 exit(1);
2544 }
2545 exit(0);
2546 } else {
2547
2548 n += do_print_resource_record(pw,
2549 _PATH_HOST_RSA_KEY_FILE, rr_hostname);
2550 n += do_print_resource_record(pw,
2551 _PATH_HOST_DSA_KEY_FILE, rr_hostname);
Damien Miller3bde12a2012-06-20 21:51:11 +10002552 n += do_print_resource_record(pw,
2553 _PATH_HOST_ECDSA_KEY_FILE, rr_hostname);
Damien Miller16cd3922014-05-15 13:45:58 +10002554 n += do_print_resource_record(pw,
2555 _PATH_HOST_ED25519_KEY_FILE, rr_hostname);
Damien Millercb314822006-03-26 13:48:01 +11002556 if (n == 0)
2557 fatal("no keys found.");
2558 exit(0);
2559 }
Damien Miller37876e92003-05-15 10:19:46 +10002560 }
Damien Miller95def091999-11-25 00:26:21 +11002561
Darren Tucker019cefe2003-08-02 22:40:07 +10002562 if (do_gen_candidates) {
2563 FILE *out = fopen(out_file, "w");
Damien Miller787b2ec2003-11-21 23:56:47 +11002564
Darren Tucker019cefe2003-08-02 22:40:07 +10002565 if (out == NULL) {
2566 error("Couldn't open modulus candidate file \"%s\": %s",
2567 out_file, strerror(errno));
2568 return (1);
2569 }
Damien Miller3f54a9f2005-11-05 14:52:18 +11002570 if (bits == 0)
2571 bits = DEFAULT_BITS;
Darren Tucker019cefe2003-08-02 22:40:07 +10002572 if (gen_candidates(out, memory, bits, start) != 0)
Damien Miller15d72a02005-11-05 15:07:33 +11002573 fatal("modulus candidate generation failed");
Darren Tucker019cefe2003-08-02 22:40:07 +10002574
2575 return (0);
2576 }
2577
2578 if (do_screen_candidates) {
2579 FILE *in;
Damien Miller78d22712013-02-12 11:03:36 +11002580 FILE *out = fopen(out_file, "a");
Darren Tucker019cefe2003-08-02 22:40:07 +10002581
2582 if (have_identity && strcmp(identity_file, "-") != 0) {
2583 if ((in = fopen(identity_file, "r")) == NULL) {
2584 fatal("Couldn't open modulus candidate "
Damien Millera8e06ce2003-11-21 23:48:55 +11002585 "file \"%s\": %s", identity_file,
Darren Tucker019cefe2003-08-02 22:40:07 +10002586 strerror(errno));
2587 }
2588 } else
2589 in = stdin;
2590
2591 if (out == NULL) {
2592 fatal("Couldn't open moduli file \"%s\": %s",
2593 out_file, strerror(errno));
2594 }
Damien Millerbcd00ab2013-12-07 10:41:55 +11002595 if (prime_test(in, out, rounds == 0 ? 100 : rounds,
2596 generator_wanted, checkpoint,
Damien Millerdfceafe2012-07-06 13:44:19 +10002597 start_lineno, lines_to_process) != 0)
Damien Miller15d72a02005-11-05 15:07:33 +11002598 fatal("modulus screening failed");
Darren Tuckerf4220e62003-08-21 16:44:07 +10002599 return (0);
Darren Tucker019cefe2003-08-02 22:40:07 +10002600 }
2601
Damien Miller58f1baf2011-05-05 14:06:15 +10002602 if (gen_all_hostkeys) {
2603 do_gen_all_hostkeys(pw);
2604 return (0);
2605 }
2606
Damien Millerf14be5c2005-11-05 15:15:49 +11002607 if (key_type_name == NULL)
2608 key_type_name = "rsa";
2609
Damien Millere39cacc2000-11-29 12:18:44 +11002610 type = key_type_from_name(key_type_name);
Damien Miller58f1baf2011-05-05 14:06:15 +10002611 type_bits_valid(type, &bits);
2612
Darren Tucker3af2ac52005-11-29 13:10:24 +11002613 if (!quiet)
2614 printf("Generating public/private %s key pair.\n", key_type_name);
Damien Miller0bc1bd82000-11-13 22:57:25 +11002615 private = key_generate(type, bits);
2616 if (private == NULL) {
Damien Miller9eab9562009-02-22 08:47:02 +11002617 fprintf(stderr, "key_generate failed\n");
Damien Miller0bc1bd82000-11-13 22:57:25 +11002618 exit(1);
2619 }
2620 public = key_from_private(private);
Damien Miller95def091999-11-25 00:26:21 +11002621
2622 if (!have_identity)
2623 ask_filename(pw, "Enter file in which to save the key");
2624
Damien Miller788f2122005-11-05 15:14:59 +11002625 /* Create ~/.ssh directory if it doesn't already exist. */
Damien Miller50af79b2010-05-10 11:52:00 +10002626 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s",
2627 pw->pw_dir, _PATH_SSH_USER_DIR);
2628 if (strstr(identity_file, dotsshdir) != NULL) {
2629 if (stat(dotsshdir, &st) < 0) {
2630 if (errno != ENOENT) {
2631 error("Could not stat %s: %s", dotsshdir,
2632 strerror(errno));
2633 } else if (mkdir(dotsshdir, 0700) < 0) {
2634 error("Could not create directory '%s': %s",
2635 dotsshdir, strerror(errno));
2636 } else if (!quiet)
2637 printf("Created directory '%s'.\n", dotsshdir);
2638 }
Damien Miller95def091999-11-25 00:26:21 +11002639 }
2640 /* If the file already exists, ask the user to confirm. */
2641 if (stat(identity_file, &st) >= 0) {
2642 char yesno[3];
2643 printf("%s already exists.\n", identity_file);
2644 printf("Overwrite (y/n)? ");
2645 fflush(stdout);
2646 if (fgets(yesno, sizeof(yesno), stdin) == NULL)
2647 exit(1);
2648 if (yesno[0] != 'y' && yesno[0] != 'Y')
2649 exit(1);
2650 }
2651 /* Ask for a passphrase (twice). */
2652 if (identity_passphrase)
2653 passphrase1 = xstrdup(identity_passphrase);
2654 else if (identity_new_passphrase)
2655 passphrase1 = xstrdup(identity_new_passphrase);
2656 else {
2657passphrase_again:
2658 passphrase1 =
Ben Lindstrom949974b2001-06-25 05:20:31 +00002659 read_passphrase("Enter passphrase (empty for no "
2660 "passphrase): ", RP_ALLOW_STDIN);
2661 passphrase2 = read_passphrase("Enter same passphrase again: ",
2662 RP_ALLOW_STDIN);
Damien Miller95def091999-11-25 00:26:21 +11002663 if (strcmp(passphrase1, passphrase2) != 0) {
Ben Lindstrom949974b2001-06-25 05:20:31 +00002664 /*
2665 * The passphrases do not match. Clear them and
2666 * retry.
2667 */
Damien Millera5103f42014-02-04 11:20:14 +11002668 explicit_bzero(passphrase1, strlen(passphrase1));
2669 explicit_bzero(passphrase2, strlen(passphrase2));
Darren Tuckera627d422013-06-02 07:31:17 +10002670 free(passphrase1);
2671 free(passphrase2);
Damien Miller95def091999-11-25 00:26:21 +11002672 printf("Passphrases do not match. Try again.\n");
2673 goto passphrase_again;
2674 }
2675 /* Clear the other copy of the passphrase. */
Damien Millera5103f42014-02-04 11:20:14 +11002676 explicit_bzero(passphrase2, strlen(passphrase2));
Darren Tuckera627d422013-06-02 07:31:17 +10002677 free(passphrase2);
Damien Miller95def091999-11-25 00:26:21 +11002678 }
2679
Damien Miller95def091999-11-25 00:26:21 +11002680 if (identity_comment) {
2681 strlcpy(comment, identity_comment, sizeof(comment));
2682 } else {
Darren Tuckere15fb092008-11-11 16:31:43 +11002683 /* Create default comment field for the passphrase. */
Damien Miller95def091999-11-25 00:26:21 +11002684 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
2685 }
2686
2687 /* Save the key with the given passphrase and comment. */
Damien Millerbcd00ab2013-12-07 10:41:55 +11002688 if (!key_save_private(private, identity_file, passphrase1, comment,
2689 use_new_format, new_format_cipher, rounds)) {
Ben Lindstrom15f33862001-04-16 02:00:02 +00002690 printf("Saving the key failed: %s.\n", identity_file);
Damien Millera5103f42014-02-04 11:20:14 +11002691 explicit_bzero(passphrase1, strlen(passphrase1));
Darren Tuckera627d422013-06-02 07:31:17 +10002692 free(passphrase1);
Damien Miller95def091999-11-25 00:26:21 +11002693 exit(1);
2694 }
2695 /* Clear the passphrase. */
Damien Millera5103f42014-02-04 11:20:14 +11002696 explicit_bzero(passphrase1, strlen(passphrase1));
Darren Tuckera627d422013-06-02 07:31:17 +10002697 free(passphrase1);
Damien Miller95def091999-11-25 00:26:21 +11002698
2699 /* Clear the private key and the random number generator. */
Damien Miller0bc1bd82000-11-13 22:57:25 +11002700 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +11002701
2702 if (!quiet)
2703 printf("Your identification has been saved in %s.\n", identity_file);
2704
Damien Miller95def091999-11-25 00:26:21 +11002705 strlcat(identity_file, ".pub", sizeof(identity_file));
Ben Lindstrom5fc62702001-03-09 18:19:24 +00002706 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
2707 if (fd == -1) {
Damien Miller95def091999-11-25 00:26:21 +11002708 printf("Could not save your public key in %s\n", identity_file);
2709 exit(1);
2710 }
Ben Lindstrom5fc62702001-03-09 18:19:24 +00002711 f = fdopen(fd, "w");
2712 if (f == NULL) {
Damien Miller9eab9562009-02-22 08:47:02 +11002713 printf("fdopen %s failed\n", identity_file);
Ben Lindstrom5fc62702001-03-09 18:19:24 +00002714 exit(1);
2715 }
Damien Millereba71ba2000-04-29 23:57:08 +10002716 if (!key_write(public, f))
Damien Miller9eab9562009-02-22 08:47:02 +11002717 fprintf(stderr, "write key failed\n");
Damien Millereba71ba2000-04-29 23:57:08 +10002718 fprintf(f, " %s\n", comment);
Damien Miller95def091999-11-25 00:26:21 +11002719 fclose(f);
2720
2721 if (!quiet) {
djm@openbsd.org56d1c832014-12-21 22:27:55 +00002722 char *fp = key_fingerprint(public, fingerprint_hash,
2723 SSH_FP_DEFAULT);
2724 char *ra = key_fingerprint(public, fingerprint_hash,
Darren Tucker9c16ac92008-06-13 04:40:35 +10002725 SSH_FP_RANDOMART);
Damien Millereba71ba2000-04-29 23:57:08 +10002726 printf("Your public key has been saved in %s.\n",
2727 identity_file);
Damien Miller95def091999-11-25 00:26:21 +11002728 printf("The key fingerprint is:\n");
Ben Lindstromcfccef92001-03-13 04:57:58 +00002729 printf("%s %s\n", fp, comment);
Darren Tucker9c16ac92008-06-13 04:40:35 +10002730 printf("The key's randomart image is:\n");
2731 printf("%s\n", ra);
Darren Tuckera627d422013-06-02 07:31:17 +10002732 free(ra);
2733 free(fp);
Damien Miller95def091999-11-25 00:26:21 +11002734 }
Damien Millereba71ba2000-04-29 23:57:08 +10002735
2736 key_free(public);
Damien Miller95def091999-11-25 00:26:21 +11002737 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002738}