blob: 75f8e2e09ffda1157e172c2e68bf4b33a2d677b2 [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
Damien Miller1f0311c2014-05-15 14:24:09 +10001967#ifdef WITH_OPENSSL
Damien Millerf2b70ca2010-03-05 07:39:35 +11001968static void
Damien Millerf3747bf2013-01-18 11:44:04 +11001969load_krl(const char *path, struct ssh_krl **krlp)
1970{
1971 Buffer krlbuf;
1972 int fd;
1973
1974 buffer_init(&krlbuf);
1975 if ((fd = open(path, O_RDONLY)) == -1)
1976 fatal("open %s: %s", path, strerror(errno));
1977 if (!key_load_file(fd, path, &krlbuf))
1978 fatal("Unable to load KRL");
1979 close(fd);
1980 /* XXX check sigs */
1981 if (ssh_krl_from_blob(&krlbuf, krlp, NULL, 0) != 0 ||
1982 *krlp == NULL)
1983 fatal("Invalid KRL file");
1984 buffer_free(&krlbuf);
1985}
1986
1987static void
1988update_krl_from_file(struct passwd *pw, const char *file, const Key *ca,
1989 struct ssh_krl *krl)
1990{
1991 Key *key = NULL;
1992 u_long lnum = 0;
1993 char *path, *cp, *ep, line[SSH_MAX_PUBKEY_BYTES];
1994 unsigned long long serial, serial2;
1995 int i, was_explicit_key, was_sha1, r;
1996 FILE *krl_spec;
1997
1998 path = tilde_expand_filename(file, pw->pw_uid);
1999 if (strcmp(path, "-") == 0) {
2000 krl_spec = stdin;
2001 free(path);
2002 path = xstrdup("(standard input)");
2003 } else if ((krl_spec = fopen(path, "r")) == NULL)
2004 fatal("fopen %s: %s", path, strerror(errno));
2005
2006 if (!quiet)
2007 printf("Revoking from %s\n", path);
2008 while (read_keyfile_line(krl_spec, path, line, sizeof(line),
2009 &lnum) == 0) {
2010 was_explicit_key = was_sha1 = 0;
2011 cp = line + strspn(line, " \t");
2012 /* Trim trailing space, comments and strip \n */
2013 for (i = 0, r = -1; cp[i] != '\0'; i++) {
2014 if (cp[i] == '#' || cp[i] == '\n') {
2015 cp[i] = '\0';
2016 break;
2017 }
2018 if (cp[i] == ' ' || cp[i] == '\t') {
2019 /* Remember the start of a span of whitespace */
2020 if (r == -1)
2021 r = i;
2022 } else
2023 r = -1;
2024 }
2025 if (r != -1)
2026 cp[r] = '\0';
2027 if (*cp == '\0')
2028 continue;
2029 if (strncasecmp(cp, "serial:", 7) == 0) {
2030 if (ca == NULL) {
Damien Millerd234afb2013-08-21 02:42:58 +10002031 fatal("revoking certificates by serial number "
Damien Millerf3747bf2013-01-18 11:44:04 +11002032 "requires specification of a CA key");
2033 }
2034 cp += 7;
2035 cp = cp + strspn(cp, " \t");
2036 errno = 0;
2037 serial = strtoull(cp, &ep, 0);
2038 if (*cp == '\0' || (*ep != '\0' && *ep != '-'))
2039 fatal("%s:%lu: invalid serial \"%s\"",
2040 path, lnum, cp);
2041 if (errno == ERANGE && serial == ULLONG_MAX)
2042 fatal("%s:%lu: serial out of range",
2043 path, lnum);
2044 serial2 = serial;
2045 if (*ep == '-') {
2046 cp = ep + 1;
2047 errno = 0;
2048 serial2 = strtoull(cp, &ep, 0);
2049 if (*cp == '\0' || *ep != '\0')
2050 fatal("%s:%lu: invalid serial \"%s\"",
2051 path, lnum, cp);
2052 if (errno == ERANGE && serial2 == ULLONG_MAX)
2053 fatal("%s:%lu: serial out of range",
2054 path, lnum);
2055 if (serial2 <= serial)
2056 fatal("%s:%lu: invalid serial range "
2057 "%llu:%llu", path, lnum,
2058 (unsigned long long)serial,
2059 (unsigned long long)serial2);
2060 }
2061 if (ssh_krl_revoke_cert_by_serial_range(krl,
2062 ca, serial, serial2) != 0) {
2063 fatal("%s: revoke serial failed",
2064 __func__);
2065 }
2066 } else if (strncasecmp(cp, "id:", 3) == 0) {
2067 if (ca == NULL) {
Damien Millerd5d9d7b2013-08-21 02:43:27 +10002068 fatal("revoking certificates by key ID "
Damien Millerf3747bf2013-01-18 11:44:04 +11002069 "requires specification of a CA key");
2070 }
2071 cp += 3;
2072 cp = cp + strspn(cp, " \t");
2073 if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0)
2074 fatal("%s: revoke key ID failed", __func__);
2075 } else {
2076 if (strncasecmp(cp, "key:", 4) == 0) {
2077 cp += 4;
2078 cp = cp + strspn(cp, " \t");
2079 was_explicit_key = 1;
2080 } else if (strncasecmp(cp, "sha1:", 5) == 0) {
2081 cp += 5;
2082 cp = cp + strspn(cp, " \t");
2083 was_sha1 = 1;
2084 } else {
2085 /*
2086 * Just try to process the line as a key.
2087 * Parsing will fail if it isn't.
2088 */
2089 }
2090 if ((key = key_new(KEY_UNSPEC)) == NULL)
2091 fatal("key_new");
2092 if (key_read(key, &cp) != 1)
2093 fatal("%s:%lu: invalid key", path, lnum);
2094 if (was_explicit_key)
2095 r = ssh_krl_revoke_key_explicit(krl, key);
2096 else if (was_sha1)
2097 r = ssh_krl_revoke_key_sha1(krl, key);
2098 else
2099 r = ssh_krl_revoke_key(krl, key);
2100 if (r != 0)
2101 fatal("%s: revoke key failed", __func__);
2102 key_free(key);
2103 }
2104 }
2105 if (strcmp(path, "-") != 0)
2106 fclose(krl_spec);
Damien Miller0d6771b2013-04-23 15:23:24 +10002107 free(path);
Damien Millerf3747bf2013-01-18 11:44:04 +11002108}
Damien Miller72ef7c12015-01-15 02:21:31 +11002109#endif /* WITH_OPENSSL */
Damien Millerf3747bf2013-01-18 11:44:04 +11002110
2111static void
2112do_gen_krl(struct passwd *pw, int updating, int argc, char **argv)
2113{
Damien Miller72ef7c12015-01-15 02:21:31 +11002114#ifdef WITH_OPENSSL
Damien Millerf3747bf2013-01-18 11:44:04 +11002115 struct ssh_krl *krl;
2116 struct stat sb;
2117 Key *ca = NULL;
2118 int fd, i;
2119 char *tmp;
2120 Buffer kbuf;
2121
2122 if (*identity_file == '\0')
2123 fatal("KRL generation requires an output file");
2124 if (stat(identity_file, &sb) == -1) {
2125 if (errno != ENOENT)
2126 fatal("Cannot access KRL \"%s\": %s",
2127 identity_file, strerror(errno));
2128 if (updating)
2129 fatal("KRL \"%s\" does not exist", identity_file);
2130 }
2131 if (ca_key_path != NULL) {
2132 tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
2133 if ((ca = key_load_public(tmp, NULL)) == NULL)
2134 fatal("Cannot load CA public key %s", tmp);
Darren Tuckera627d422013-06-02 07:31:17 +10002135 free(tmp);
Damien Millerf3747bf2013-01-18 11:44:04 +11002136 }
2137
2138 if (updating)
2139 load_krl(identity_file, &krl);
2140 else if ((krl = ssh_krl_init()) == NULL)
2141 fatal("couldn't create KRL");
2142
2143 if (cert_serial != 0)
2144 ssh_krl_set_version(krl, cert_serial);
2145 if (identity_comment != NULL)
2146 ssh_krl_set_comment(krl, identity_comment);
2147
2148 for (i = 0; i < argc; i++)
2149 update_krl_from_file(pw, argv[i], ca, krl);
2150
2151 buffer_init(&kbuf);
2152 if (ssh_krl_to_blob(krl, &kbuf, NULL, 0) != 0)
2153 fatal("Couldn't generate KRL");
2154 if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
2155 fatal("open %s: %s", identity_file, strerror(errno));
2156 if (atomicio(vwrite, fd, buffer_ptr(&kbuf), buffer_len(&kbuf)) !=
2157 buffer_len(&kbuf))
2158 fatal("write %s: %s", identity_file, strerror(errno));
2159 close(fd);
2160 buffer_free(&kbuf);
2161 ssh_krl_free(krl);
Damien Miller0d6771b2013-04-23 15:23:24 +10002162 if (ca != NULL)
2163 key_free(ca);
Damien Miller72ef7c12015-01-15 02:21:31 +11002164#else /* WITH_OPENSSL */
2165 fatal("KRLs not supported without OpenSSL");
2166#endif /* WITH_OPENSSL */
Damien Millerf3747bf2013-01-18 11:44:04 +11002167}
2168
2169static void
2170do_check_krl(struct passwd *pw, int argc, char **argv)
2171{
Damien Miller72ef7c12015-01-15 02:21:31 +11002172#ifdef WITH_OPENSSL
Damien Millerf3747bf2013-01-18 11:44:04 +11002173 int i, r, ret = 0;
2174 char *comment;
2175 struct ssh_krl *krl;
2176 Key *k;
2177
2178 if (*identity_file == '\0')
2179 fatal("KRL checking requires an input file");
2180 load_krl(identity_file, &krl);
2181 for (i = 0; i < argc; i++) {
2182 if ((k = key_load_public(argv[i], &comment)) == NULL)
2183 fatal("Cannot load public key %s", argv[i]);
2184 r = ssh_krl_check_key(krl, k);
2185 printf("%s%s%s%s: %s\n", argv[i],
2186 *comment ? " (" : "", comment, *comment ? ")" : "",
2187 r == 0 ? "ok" : "REVOKED");
2188 if (r != 0)
2189 ret = 1;
2190 key_free(k);
2191 free(comment);
2192 }
2193 ssh_krl_free(krl);
2194 exit(ret);
Damien Miller72ef7c12015-01-15 02:21:31 +11002195#else /* WITH_OPENSSL */
2196 fatal("KRLs not supported without OpenSSL");
2197#endif /* WITH_OPENSSL */
Damien Millerf3747bf2013-01-18 11:44:04 +11002198}
2199
2200static void
Damien Miller431f66b1999-11-21 18:31:57 +11002201usage(void)
2202{
Damien Millerf0858de2014-04-20 13:01:30 +10002203 fprintf(stderr,
2204 "usage: ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa | rsa1]\n"
2205 " [-N new_passphrase] [-C comment] [-f output_keyfile]\n"
2206 " ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-f keyfile]\n"
2207 " ssh-keygen -i [-m key_format] [-f input_keyfile]\n"
2208 " ssh-keygen -e [-m key_format] [-f input_keyfile]\n"
2209 " ssh-keygen -y [-f input_keyfile]\n"
2210 " ssh-keygen -c [-P passphrase] [-C comment] [-f keyfile]\n"
djm@openbsd.org56d1c832014-12-21 22:27:55 +00002211 " ssh-keygen -l [-E fingerprint_hash] [-f input_keyfile]\n"
Damien Millerf0858de2014-04-20 13:01:30 +10002212 " ssh-keygen -B [-f input_keyfile]\n");
Damien Miller7ea845e2010-02-12 09:21:02 +11002213#ifdef ENABLE_PKCS11
Damien Millerf0858de2014-04-20 13:01:30 +10002214 fprintf(stderr,
2215 " ssh-keygen -D pkcs11\n");
Damien Miller7ea845e2010-02-12 09:21:02 +11002216#endif
Damien Millerf0858de2014-04-20 13:01:30 +10002217 fprintf(stderr,
2218 " ssh-keygen -F hostname [-f known_hosts_file] [-l]\n"
2219 " ssh-keygen -H [-f known_hosts_file]\n"
2220 " ssh-keygen -R hostname [-f known_hosts_file]\n"
2221 " ssh-keygen -r hostname [-f input_keyfile] [-g]\n"
2222 " ssh-keygen -G output_file [-v] [-b bits] [-M memory] [-S start_point]\n"
2223 " ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n"
2224 " [-j start_line] [-K checkpt] [-W generator]\n"
2225 " ssh-keygen -s ca_key -I certificate_identity [-h] [-n principals]\n"
2226 " [-O option] [-V validity_interval] [-z serial_number] file ...\n"
2227 " ssh-keygen -L [-f input_keyfile]\n"
2228 " ssh-keygen -A\n"
2229 " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
2230 " file ...\n"
2231 " ssh-keygen -Q -f krl_file file ...\n");
Damien Miller95def091999-11-25 00:26:21 +11002232 exit(1);
Damien Miller431f66b1999-11-21 18:31:57 +11002233}
2234
Damien Miller95def091999-11-25 00:26:21 +11002235/*
2236 * Main program for key management.
2237 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002238int
Damien Millerdf8b7db2007-01-05 16:22:57 +11002239main(int argc, char **argv)
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002240{
Damien Millera41c8b12002-01-22 23:05:08 +11002241 char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2;
Damien Miller390d0562011-10-18 16:05:19 +11002242 char *checkpoint = NULL;
Damien Millerf3747bf2013-01-18 11:44:04 +11002243 char out_file[MAXPATHLEN], *ep, *rr_hostname = NULL;
Ben Lindstrom5fc62702001-03-09 18:19:24 +00002244 Key *private, *public;
Damien Miller95def091999-11-25 00:26:21 +11002245 struct passwd *pw;
Damien Miller95def091999-11-25 00:26:21 +11002246 struct stat st;
Damien Miller7ea845e2010-02-12 09:21:02 +11002247 int opt, type, fd;
Damien Millerbcd00ab2013-12-07 10:41:55 +11002248 u_int32_t memory = 0, generator_wanted = 0;
Darren Tucker019cefe2003-08-02 22:40:07 +10002249 int do_gen_candidates = 0, do_screen_candidates = 0;
Damien Millerf3747bf2013-01-18 11:44:04 +11002250 int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
Damien Millerdfceafe2012-07-06 13:44:19 +10002251 unsigned long start_lineno = 0, lines_to_process = 0;
Darren Tucker019cefe2003-08-02 22:40:07 +10002252 BIGNUM *start = NULL;
Damien Miller95def091999-11-25 00:26:21 +11002253 FILE *f;
Damien Miller02e754f2005-05-26 12:19:39 +10002254 const char *errstr;
Damien Miller0bc1bd82000-11-13 22:57:25 +11002255
Damien Miller95def091999-11-25 00:26:21 +11002256 extern int optind;
2257 extern char *optarg;
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002258
Darren Tuckerce321d82005-10-03 18:11:24 +10002259 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2260 sanitise_stdfd();
2261
Darren Tucker9ac56e92007-01-14 10:19:59 +11002262 __progname = ssh_get_progname(argv[0]);
Damien Millerf9b625c2000-07-09 22:42:32 +10002263
Damien Miller72ef7c12015-01-15 02:21:31 +11002264#ifdef WITH_OPENSSL
Damien Miller4314c2b2010-09-10 11:12:09 +10002265 OpenSSL_add_all_algorithms();
Damien Miller72ef7c12015-01-15 02:21:31 +11002266#endif
Damien Millerdf8b7db2007-01-05 16:22:57 +11002267 log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
Darren Tucker019cefe2003-08-02 22:40:07 +10002268
Kevin Steves3a881912002-07-20 19:05:40 +00002269 seed_rng();
Damien Millereba71ba2000-04-29 23:57:08 +10002270
Damien Miller5428f641999-11-25 11:54:57 +11002271 /* we need this for the home * directory. */
Damien Miller95def091999-11-25 00:26:21 +11002272 pw = getpwuid(getuid());
2273 if (!pw) {
Damien Miller3009d3c2013-07-20 13:22:31 +10002274 printf("No user exists for uid %lu\n", (u_long)getuid());
Damien Miller95def091999-11-25 00:26:21 +11002275 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002276 }
Damien Millereba71ba2000-04-29 23:57:08 +10002277 if (gethostname(hostname, sizeof(hostname)) < 0) {
2278 perror("gethostname");
2279 exit(1);
2280 }
Damien Miller5428f641999-11-25 11:54:57 +11002281
djm@openbsd.org56d1c832014-12-21 22:27:55 +00002282 /* Remaining characters: UYdw */
Damien Millerbcd00ab2013-12-07 10:41:55 +11002283 while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy"
djm@openbsd.org56d1c832014-12-21 22:27:55 +00002284 "C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:"
2285 "a:b:f:g:j:m:n:r:s:t:z:")) != -1) {
Damien Miller95def091999-11-25 00:26:21 +11002286 switch (opt) {
Damien Miller58f1baf2011-05-05 14:06:15 +10002287 case 'A':
2288 gen_all_hostkeys = 1;
2289 break;
Damien Miller95def091999-11-25 00:26:21 +11002290 case 'b':
Damien Miller57737942010-09-10 11:16:37 +10002291 bits = (u_int32_t)strtonum(optarg, 256, 32768, &errstr);
Damien Miller02e754f2005-05-26 12:19:39 +10002292 if (errstr)
2293 fatal("Bits has bad value %s (%s)",
2294 optarg, errstr);
Damien Miller95def091999-11-25 00:26:21 +11002295 break;
djm@openbsd.org56d1c832014-12-21 22:27:55 +00002296 case 'E':
2297 fingerprint_hash = ssh_digest_alg_by_name(optarg);
2298 if (fingerprint_hash == -1)
2299 fatal("Invalid hash algorithm \"%s\"", optarg);
2300 break;
Damien Miller4b42d7f2005-03-01 21:48:35 +11002301 case 'F':
2302 find_host = 1;
2303 rr_hostname = optarg;
2304 break;
2305 case 'H':
2306 hash_hosts = 1;
2307 break;
Damien Miller0a80ca12010-02-27 07:55:05 +11002308 case 'I':
2309 cert_key_id = optarg;
2310 break;
Damien Millerdfceafe2012-07-06 13:44:19 +10002311 case 'J':
2312 lines_to_process = strtoul(optarg, NULL, 10);
2313 break;
2314 case 'j':
2315 start_lineno = strtoul(optarg, NULL, 10);
2316 break;
Damien Miller4b42d7f2005-03-01 21:48:35 +11002317 case 'R':
2318 delete_host = 1;
2319 rr_hostname = optarg;
2320 break;
Damien Millerf2b70ca2010-03-05 07:39:35 +11002321 case 'L':
2322 show_cert = 1;
2323 break;
Damien Miller95def091999-11-25 00:26:21 +11002324 case 'l':
2325 print_fingerprint = 1;
2326 break;
Ben Lindstrom8fd372b2001-03-12 03:02:17 +00002327 case 'B':
2328 print_bubblebabble = 1;
2329 break;
Damien Miller44b25042010-07-02 13:35:01 +10002330 case 'm':
2331 if (strcasecmp(optarg, "RFC4716") == 0 ||
2332 strcasecmp(optarg, "ssh2") == 0) {
2333 convert_format = FMT_RFC4716;
2334 break;
2335 }
2336 if (strcasecmp(optarg, "PKCS8") == 0) {
2337 convert_format = FMT_PKCS8;
2338 break;
2339 }
2340 if (strcasecmp(optarg, "PEM") == 0) {
2341 convert_format = FMT_PEM;
2342 break;
2343 }
2344 fatal("Unsupported conversion format \"%s\"", optarg);
Damien Miller0a80ca12010-02-27 07:55:05 +11002345 case 'n':
2346 cert_principals = optarg;
2347 break;
Damien Millerbcd00ab2013-12-07 10:41:55 +11002348 case 'o':
2349 use_new_format = 1;
2350 break;
Damien Miller95def091999-11-25 00:26:21 +11002351 case 'p':
2352 change_passphrase = 1;
2353 break;
Damien Miller95def091999-11-25 00:26:21 +11002354 case 'c':
2355 change_comment = 1;
2356 break;
Damien Miller95def091999-11-25 00:26:21 +11002357 case 'f':
Damien Millerb089fb52005-05-26 12:16:18 +10002358 if (strlcpy(identity_file, optarg, sizeof(identity_file)) >=
2359 sizeof(identity_file))
2360 fatal("Identity filename too long");
Damien Miller95def091999-11-25 00:26:21 +11002361 have_identity = 1;
2362 break;
Damien Miller37876e92003-05-15 10:19:46 +10002363 case 'g':
2364 print_generic = 1;
2365 break;
Damien Miller95def091999-11-25 00:26:21 +11002366 case 'P':
2367 identity_passphrase = optarg;
2368 break;
Damien Miller95def091999-11-25 00:26:21 +11002369 case 'N':
2370 identity_new_passphrase = optarg;
2371 break;
Damien Millerf3747bf2013-01-18 11:44:04 +11002372 case 'Q':
2373 check_krl = 1;
2374 break;
Damien Miller0a80ca12010-02-27 07:55:05 +11002375 case 'O':
Damien Miller4e270b02010-04-16 15:56:21 +10002376 add_cert_option(optarg);
Damien Miller0a80ca12010-02-27 07:55:05 +11002377 break;
Damien Millerbcd00ab2013-12-07 10:41:55 +11002378 case 'Z':
2379 new_format_cipher = optarg;
2380 break;
Damien Miller95def091999-11-25 00:26:21 +11002381 case 'C':
2382 identity_comment = optarg;
2383 break;
Damien Miller95def091999-11-25 00:26:21 +11002384 case 'q':
2385 quiet = 1;
2386 break;
Ben Lindstrom5a707822001-04-22 17:15:46 +00002387 case 'e':
Damien Millereba71ba2000-04-29 23:57:08 +10002388 case 'x':
Ben Lindstrom5a707822001-04-22 17:15:46 +00002389 /* export key */
Damien Miller44b25042010-07-02 13:35:01 +10002390 convert_to = 1;
Damien Millereba71ba2000-04-29 23:57:08 +10002391 break;
Damien Miller0a80ca12010-02-27 07:55:05 +11002392 case 'h':
2393 cert_key_type = SSH2_CERT_TYPE_HOST;
Damien Millerd0e4a8e2010-05-21 14:58:32 +10002394 certflags_flags = 0;
Damien Miller0a80ca12010-02-27 07:55:05 +11002395 break;
Damien Millerf3747bf2013-01-18 11:44:04 +11002396 case 'k':
2397 gen_krl = 1;
2398 break;
Ben Lindstrom5a707822001-04-22 17:15:46 +00002399 case 'i':
Damien Millereba71ba2000-04-29 23:57:08 +10002400 case 'X':
Ben Lindstrom5a707822001-04-22 17:15:46 +00002401 /* import key */
Damien Miller44b25042010-07-02 13:35:01 +10002402 convert_from = 1;
Damien Millereba71ba2000-04-29 23:57:08 +10002403 break;
Damien Millereba71ba2000-04-29 23:57:08 +10002404 case 'y':
2405 print_public = 1;
2406 break;
Damien Miller0a80ca12010-02-27 07:55:05 +11002407 case 's':
2408 ca_key_path = optarg;
2409 break;
Damien Miller0bc1bd82000-11-13 22:57:25 +11002410 case 't':
2411 key_type_name = optarg;
Damien Miller0bc1bd82000-11-13 22:57:25 +11002412 break;
Ben Lindstrom8282d6a2001-08-06 21:44:05 +00002413 case 'D':
Damien Miller7ea845e2010-02-12 09:21:02 +11002414 pkcs11provider = optarg;
Ben Lindstromcd392282001-07-04 03:44:03 +00002415 break;
Damien Millerf3747bf2013-01-18 11:44:04 +11002416 case 'u':
2417 update_krl = 1;
2418 break;
Darren Tucker06930c72003-12-31 11:34:51 +11002419 case 'v':
2420 if (log_level == SYSLOG_LEVEL_INFO)
2421 log_level = SYSLOG_LEVEL_DEBUG1;
2422 else {
Darren Tuckerfc959702004-07-17 16:12:08 +10002423 if (log_level >= SYSLOG_LEVEL_DEBUG1 &&
Darren Tucker06930c72003-12-31 11:34:51 +11002424 log_level < SYSLOG_LEVEL_DEBUG3)
2425 log_level++;
2426 }
2427 break;
Damien Miller37876e92003-05-15 10:19:46 +10002428 case 'r':
Damien Miller4b42d7f2005-03-01 21:48:35 +11002429 rr_hostname = optarg;
Damien Miller37876e92003-05-15 10:19:46 +10002430 break;
Darren Tucker019cefe2003-08-02 22:40:07 +10002431 case 'W':
Damien Miller5f340062006-03-26 14:27:57 +11002432 generator_wanted = (u_int32_t)strtonum(optarg, 1,
2433 UINT_MAX, &errstr);
Damien Millerb089fb52005-05-26 12:16:18 +10002434 if (errstr)
2435 fatal("Desired generator has bad value: %s (%s)",
2436 optarg, errstr);
Darren Tucker019cefe2003-08-02 22:40:07 +10002437 break;
2438 case 'a':
Damien Millerbcd00ab2013-12-07 10:41:55 +11002439 rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr);
Damien Millerb089fb52005-05-26 12:16:18 +10002440 if (errstr)
Damien Millerbcd00ab2013-12-07 10:41:55 +11002441 fatal("Invalid number: %s (%s)",
Damien Millerb089fb52005-05-26 12:16:18 +10002442 optarg, errstr);
Darren Tucker019cefe2003-08-02 22:40:07 +10002443 break;
Damien Miller72ef7c12015-01-15 02:21:31 +11002444#ifdef WITH_OPENSSL
Darren Tucker019cefe2003-08-02 22:40:07 +10002445 case 'M':
Damien Miller5f340062006-03-26 14:27:57 +11002446 memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr);
Damien Miller4e270b02010-04-16 15:56:21 +10002447 if (errstr)
Damien Millerb089fb52005-05-26 12:16:18 +10002448 fatal("Memory limit is %s: %s", errstr, optarg);
Darren Tucker019cefe2003-08-02 22:40:07 +10002449 break;
2450 case 'G':
2451 do_gen_candidates = 1;
Damien Millerb089fb52005-05-26 12:16:18 +10002452 if (strlcpy(out_file, optarg, sizeof(out_file)) >=
2453 sizeof(out_file))
2454 fatal("Output filename too long");
Darren Tucker019cefe2003-08-02 22:40:07 +10002455 break;
2456 case 'T':
2457 do_screen_candidates = 1;
Damien Millerb089fb52005-05-26 12:16:18 +10002458 if (strlcpy(out_file, optarg, sizeof(out_file)) >=
2459 sizeof(out_file))
2460 fatal("Output filename too long");
Darren Tucker019cefe2003-08-02 22:40:07 +10002461 break;
Damien Miller390d0562011-10-18 16:05:19 +11002462 case 'K':
2463 if (strlen(optarg) >= MAXPATHLEN)
2464 fatal("Checkpoint filename too long");
2465 checkpoint = xstrdup(optarg);
2466 break;
Darren Tucker019cefe2003-08-02 22:40:07 +10002467 case 'S':
2468 /* XXX - also compare length against bits */
2469 if (BN_hex2bn(&start, optarg) == 0)
2470 fatal("Invalid start point.");
2471 break;
Damien Miller72ef7c12015-01-15 02:21:31 +11002472#endif /* WITH_OPENSSL */
Damien Miller0a80ca12010-02-27 07:55:05 +11002473 case 'V':
2474 parse_cert_times(optarg);
2475 break;
Damien Miller4e270b02010-04-16 15:56:21 +10002476 case 'z':
Damien Miller6f3b3622012-11-14 19:04:33 +11002477 errno = 0;
2478 cert_serial = strtoull(optarg, &ep, 10);
2479 if (*optarg < '0' || *optarg > '9' || *ep != '\0' ||
2480 (errno == ERANGE && cert_serial == ULLONG_MAX))
2481 fatal("Invalid serial number \"%s\"", optarg);
Damien Miller4e270b02010-04-16 15:56:21 +10002482 break;
Damien Miller95def091999-11-25 00:26:21 +11002483 case '?':
2484 default:
2485 usage();
2486 }
2487 }
Darren Tucker06930c72003-12-31 11:34:51 +11002488
2489 /* reinit */
Damien Millerdf8b7db2007-01-05 16:22:57 +11002490 log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1);
Darren Tucker06930c72003-12-31 11:34:51 +11002491
Damien Miller0a80ca12010-02-27 07:55:05 +11002492 argv += optind;
2493 argc -= optind;
2494
2495 if (ca_key_path != NULL) {
Damien Millerf3747bf2013-01-18 11:44:04 +11002496 if (argc < 1 && !gen_krl) {
Damien Miller0a80ca12010-02-27 07:55:05 +11002497 printf("Too few arguments.\n");
2498 usage();
2499 }
Damien Millerf3747bf2013-01-18 11:44:04 +11002500 } else if (argc > 0 && !gen_krl && !check_krl) {
Damien Miller95def091999-11-25 00:26:21 +11002501 printf("Too many arguments.\n");
2502 usage();
2503 }
2504 if (change_passphrase && change_comment) {
2505 printf("Can only have one of -p and -c.\n");
2506 usage();
2507 }
Darren Tucker0f7e9102008-06-08 12:54:29 +10002508 if (print_fingerprint && (delete_host || hash_hosts)) {
Damien Millerec77c952013-01-09 15:58:00 +11002509 printf("Cannot use -l with -H or -R.\n");
Darren Tucker0f7e9102008-06-08 12:54:29 +10002510 usage();
2511 }
Damien Millerf3747bf2013-01-18 11:44:04 +11002512 if (gen_krl) {
2513 do_gen_krl(pw, update_krl, argc, argv);
2514 return (0);
2515 }
2516 if (check_krl) {
2517 do_check_krl(pw, argc, argv);
2518 return (0);
2519 }
Damien Miller0a80ca12010-02-27 07:55:05 +11002520 if (ca_key_path != NULL) {
2521 if (cert_key_id == NULL)
2522 fatal("Must specify key id (-I) when certifying");
2523 do_ca_sign(pw, argc, argv);
2524 }
Damien Millerf2b70ca2010-03-05 07:39:35 +11002525 if (show_cert)
2526 do_show_cert(pw);
Damien Miller4b42d7f2005-03-01 21:48:35 +11002527 if (delete_host || hash_hosts || find_host)
2528 do_known_hosts(pw, rr_hostname);
Damien Millerec77c952013-01-09 15:58:00 +11002529 if (pkcs11provider != NULL)
2530 do_download(pw);
Ben Lindstrom8fd372b2001-03-12 03:02:17 +00002531 if (print_fingerprint || print_bubblebabble)
Damien Miller95def091999-11-25 00:26:21 +11002532 do_fingerprint(pw);
Damien Miller95def091999-11-25 00:26:21 +11002533 if (change_passphrase)
2534 do_change_passphrase(pw);
Damien Miller4a10d2e2002-03-11 22:53:29 +11002535 if (change_comment)
2536 do_change_comment(pw);
Damien Miller1f0311c2014-05-15 14:24:09 +10002537#ifdef WITH_OPENSSL
Damien Miller44b25042010-07-02 13:35:01 +10002538 if (convert_to)
2539 do_convert_to(pw);
2540 if (convert_from)
2541 do_convert_from(pw);
Damien Miller1f0311c2014-05-15 14:24:09 +10002542#endif
Damien Millereba71ba2000-04-29 23:57:08 +10002543 if (print_public)
2544 do_print_public(pw);
Damien Miller4b42d7f2005-03-01 21:48:35 +11002545 if (rr_hostname != NULL) {
Damien Millercb314822006-03-26 13:48:01 +11002546 unsigned int n = 0;
2547
2548 if (have_identity) {
2549 n = do_print_resource_record(pw,
2550 identity_file, rr_hostname);
2551 if (n == 0) {
2552 perror(identity_file);
2553 exit(1);
2554 }
2555 exit(0);
2556 } else {
2557
2558 n += do_print_resource_record(pw,
2559 _PATH_HOST_RSA_KEY_FILE, rr_hostname);
2560 n += do_print_resource_record(pw,
2561 _PATH_HOST_DSA_KEY_FILE, rr_hostname);
Damien Miller3bde12a2012-06-20 21:51:11 +10002562 n += do_print_resource_record(pw,
2563 _PATH_HOST_ECDSA_KEY_FILE, rr_hostname);
Damien Miller16cd3922014-05-15 13:45:58 +10002564 n += do_print_resource_record(pw,
2565 _PATH_HOST_ED25519_KEY_FILE, rr_hostname);
Damien Millercb314822006-03-26 13:48:01 +11002566 if (n == 0)
2567 fatal("no keys found.");
2568 exit(0);
2569 }
Damien Miller37876e92003-05-15 10:19:46 +10002570 }
Damien Miller95def091999-11-25 00:26:21 +11002571
Darren Tucker019cefe2003-08-02 22:40:07 +10002572 if (do_gen_candidates) {
2573 FILE *out = fopen(out_file, "w");
Damien Miller787b2ec2003-11-21 23:56:47 +11002574
Darren Tucker019cefe2003-08-02 22:40:07 +10002575 if (out == NULL) {
2576 error("Couldn't open modulus candidate file \"%s\": %s",
2577 out_file, strerror(errno));
2578 return (1);
2579 }
Damien Miller3f54a9f2005-11-05 14:52:18 +11002580 if (bits == 0)
2581 bits = DEFAULT_BITS;
Darren Tucker019cefe2003-08-02 22:40:07 +10002582 if (gen_candidates(out, memory, bits, start) != 0)
Damien Miller15d72a02005-11-05 15:07:33 +11002583 fatal("modulus candidate generation failed");
Darren Tucker019cefe2003-08-02 22:40:07 +10002584
2585 return (0);
2586 }
2587
2588 if (do_screen_candidates) {
2589 FILE *in;
Damien Miller78d22712013-02-12 11:03:36 +11002590 FILE *out = fopen(out_file, "a");
Darren Tucker019cefe2003-08-02 22:40:07 +10002591
2592 if (have_identity && strcmp(identity_file, "-") != 0) {
2593 if ((in = fopen(identity_file, "r")) == NULL) {
2594 fatal("Couldn't open modulus candidate "
Damien Millera8e06ce2003-11-21 23:48:55 +11002595 "file \"%s\": %s", identity_file,
Darren Tucker019cefe2003-08-02 22:40:07 +10002596 strerror(errno));
2597 }
2598 } else
2599 in = stdin;
2600
2601 if (out == NULL) {
2602 fatal("Couldn't open moduli file \"%s\": %s",
2603 out_file, strerror(errno));
2604 }
Damien Millerbcd00ab2013-12-07 10:41:55 +11002605 if (prime_test(in, out, rounds == 0 ? 100 : rounds,
2606 generator_wanted, checkpoint,
Damien Millerdfceafe2012-07-06 13:44:19 +10002607 start_lineno, lines_to_process) != 0)
Damien Miller15d72a02005-11-05 15:07:33 +11002608 fatal("modulus screening failed");
Darren Tuckerf4220e62003-08-21 16:44:07 +10002609 return (0);
Darren Tucker019cefe2003-08-02 22:40:07 +10002610 }
2611
Damien Miller58f1baf2011-05-05 14:06:15 +10002612 if (gen_all_hostkeys) {
2613 do_gen_all_hostkeys(pw);
2614 return (0);
2615 }
2616
Damien Millerf14be5c2005-11-05 15:15:49 +11002617 if (key_type_name == NULL)
2618 key_type_name = "rsa";
2619
Damien Millere39cacc2000-11-29 12:18:44 +11002620 type = key_type_from_name(key_type_name);
Damien Miller58f1baf2011-05-05 14:06:15 +10002621 type_bits_valid(type, &bits);
2622
Darren Tucker3af2ac52005-11-29 13:10:24 +11002623 if (!quiet)
2624 printf("Generating public/private %s key pair.\n", key_type_name);
Damien Miller0bc1bd82000-11-13 22:57:25 +11002625 private = key_generate(type, bits);
2626 if (private == NULL) {
Damien Miller9eab9562009-02-22 08:47:02 +11002627 fprintf(stderr, "key_generate failed\n");
Damien Miller0bc1bd82000-11-13 22:57:25 +11002628 exit(1);
2629 }
2630 public = key_from_private(private);
Damien Miller95def091999-11-25 00:26:21 +11002631
2632 if (!have_identity)
2633 ask_filename(pw, "Enter file in which to save the key");
2634
Damien Miller788f2122005-11-05 15:14:59 +11002635 /* Create ~/.ssh directory if it doesn't already exist. */
Damien Miller50af79b2010-05-10 11:52:00 +10002636 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s",
2637 pw->pw_dir, _PATH_SSH_USER_DIR);
2638 if (strstr(identity_file, dotsshdir) != NULL) {
2639 if (stat(dotsshdir, &st) < 0) {
2640 if (errno != ENOENT) {
2641 error("Could not stat %s: %s", dotsshdir,
2642 strerror(errno));
2643 } else if (mkdir(dotsshdir, 0700) < 0) {
2644 error("Could not create directory '%s': %s",
2645 dotsshdir, strerror(errno));
2646 } else if (!quiet)
2647 printf("Created directory '%s'.\n", dotsshdir);
2648 }
Damien Miller95def091999-11-25 00:26:21 +11002649 }
2650 /* If the file already exists, ask the user to confirm. */
2651 if (stat(identity_file, &st) >= 0) {
2652 char yesno[3];
2653 printf("%s already exists.\n", identity_file);
2654 printf("Overwrite (y/n)? ");
2655 fflush(stdout);
2656 if (fgets(yesno, sizeof(yesno), stdin) == NULL)
2657 exit(1);
2658 if (yesno[0] != 'y' && yesno[0] != 'Y')
2659 exit(1);
2660 }
2661 /* Ask for a passphrase (twice). */
2662 if (identity_passphrase)
2663 passphrase1 = xstrdup(identity_passphrase);
2664 else if (identity_new_passphrase)
2665 passphrase1 = xstrdup(identity_new_passphrase);
2666 else {
2667passphrase_again:
2668 passphrase1 =
Ben Lindstrom949974b2001-06-25 05:20:31 +00002669 read_passphrase("Enter passphrase (empty for no "
2670 "passphrase): ", RP_ALLOW_STDIN);
2671 passphrase2 = read_passphrase("Enter same passphrase again: ",
2672 RP_ALLOW_STDIN);
Damien Miller95def091999-11-25 00:26:21 +11002673 if (strcmp(passphrase1, passphrase2) != 0) {
Ben Lindstrom949974b2001-06-25 05:20:31 +00002674 /*
2675 * The passphrases do not match. Clear them and
2676 * retry.
2677 */
Damien Millera5103f42014-02-04 11:20:14 +11002678 explicit_bzero(passphrase1, strlen(passphrase1));
2679 explicit_bzero(passphrase2, strlen(passphrase2));
Darren Tuckera627d422013-06-02 07:31:17 +10002680 free(passphrase1);
2681 free(passphrase2);
Damien Miller95def091999-11-25 00:26:21 +11002682 printf("Passphrases do not match. Try again.\n");
2683 goto passphrase_again;
2684 }
2685 /* Clear the other copy of the passphrase. */
Damien Millera5103f42014-02-04 11:20:14 +11002686 explicit_bzero(passphrase2, strlen(passphrase2));
Darren Tuckera627d422013-06-02 07:31:17 +10002687 free(passphrase2);
Damien Miller95def091999-11-25 00:26:21 +11002688 }
2689
Damien Miller95def091999-11-25 00:26:21 +11002690 if (identity_comment) {
2691 strlcpy(comment, identity_comment, sizeof(comment));
2692 } else {
Darren Tuckere15fb092008-11-11 16:31:43 +11002693 /* Create default comment field for the passphrase. */
Damien Miller95def091999-11-25 00:26:21 +11002694 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
2695 }
2696
2697 /* Save the key with the given passphrase and comment. */
Damien Millerbcd00ab2013-12-07 10:41:55 +11002698 if (!key_save_private(private, identity_file, passphrase1, comment,
2699 use_new_format, new_format_cipher, rounds)) {
Ben Lindstrom15f33862001-04-16 02:00:02 +00002700 printf("Saving the key failed: %s.\n", identity_file);
Damien Millera5103f42014-02-04 11:20:14 +11002701 explicit_bzero(passphrase1, strlen(passphrase1));
Darren Tuckera627d422013-06-02 07:31:17 +10002702 free(passphrase1);
Damien Miller95def091999-11-25 00:26:21 +11002703 exit(1);
2704 }
2705 /* Clear the passphrase. */
Damien Millera5103f42014-02-04 11:20:14 +11002706 explicit_bzero(passphrase1, strlen(passphrase1));
Darren Tuckera627d422013-06-02 07:31:17 +10002707 free(passphrase1);
Damien Miller95def091999-11-25 00:26:21 +11002708
2709 /* Clear the private key and the random number generator. */
Damien Miller0bc1bd82000-11-13 22:57:25 +11002710 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +11002711
2712 if (!quiet)
2713 printf("Your identification has been saved in %s.\n", identity_file);
2714
Damien Miller95def091999-11-25 00:26:21 +11002715 strlcat(identity_file, ".pub", sizeof(identity_file));
Ben Lindstrom5fc62702001-03-09 18:19:24 +00002716 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
2717 if (fd == -1) {
Damien Miller95def091999-11-25 00:26:21 +11002718 printf("Could not save your public key in %s\n", identity_file);
2719 exit(1);
2720 }
Ben Lindstrom5fc62702001-03-09 18:19:24 +00002721 f = fdopen(fd, "w");
2722 if (f == NULL) {
Damien Miller9eab9562009-02-22 08:47:02 +11002723 printf("fdopen %s failed\n", identity_file);
Ben Lindstrom5fc62702001-03-09 18:19:24 +00002724 exit(1);
2725 }
Damien Millereba71ba2000-04-29 23:57:08 +10002726 if (!key_write(public, f))
Damien Miller9eab9562009-02-22 08:47:02 +11002727 fprintf(stderr, "write key failed\n");
Damien Millereba71ba2000-04-29 23:57:08 +10002728 fprintf(f, " %s\n", comment);
Damien Miller95def091999-11-25 00:26:21 +11002729 fclose(f);
2730
2731 if (!quiet) {
djm@openbsd.org56d1c832014-12-21 22:27:55 +00002732 char *fp = key_fingerprint(public, fingerprint_hash,
2733 SSH_FP_DEFAULT);
2734 char *ra = key_fingerprint(public, fingerprint_hash,
Darren Tucker9c16ac92008-06-13 04:40:35 +10002735 SSH_FP_RANDOMART);
Damien Millereba71ba2000-04-29 23:57:08 +10002736 printf("Your public key has been saved in %s.\n",
2737 identity_file);
Damien Miller95def091999-11-25 00:26:21 +11002738 printf("The key fingerprint is:\n");
Ben Lindstromcfccef92001-03-13 04:57:58 +00002739 printf("%s %s\n", fp, comment);
Darren Tucker9c16ac92008-06-13 04:40:35 +10002740 printf("The key's randomart image is:\n");
2741 printf("%s\n", ra);
Darren Tuckera627d422013-06-02 07:31:17 +10002742 free(ra);
2743 free(fp);
Damien Miller95def091999-11-25 00:26:21 +11002744 }
Damien Millereba71ba2000-04-29 23:57:08 +10002745
2746 key_free(public);
Damien Miller95def091999-11-25 00:26:21 +11002747 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002748}