2014-05-15 06:24:09 +02:00
|
|
|
/* $OpenBSD: ssh-keygen.c,v 1.246 2014/04/29 18:01:49 markus Exp $ */
|
1999-10-27 05:42:43 +02:00
|
|
|
/*
|
1999-11-24 14:26:21 +01:00
|
|
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
|
|
|
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
|
|
|
* All rights reserved
|
|
|
|
* Identity and host key generation and maintenance.
|
2000-09-16 04:29:08 +02:00
|
|
|
*
|
|
|
|
* As far as I am concerned, the code I have written for this software
|
|
|
|
* can be used freely for any purpose. Any derived versions of this
|
|
|
|
* software must be clearly marked as such, and if the derived work is
|
|
|
|
* incompatible with the protocol description in the RFC file, it must be
|
|
|
|
* called by a name other than "ssh" or "Secure Shell".
|
1999-11-24 14:26:21 +01:00
|
|
|
*/
|
1999-10-27 05:42:43 +02:00
|
|
|
|
|
|
|
#include "includes.h"
|
2006-03-15 01:45:54 +01:00
|
|
|
|
|
|
|
#include <sys/types.h>
|
2006-07-10 13:08:03 +02:00
|
|
|
#include <sys/socket.h>
|
2006-03-15 01:45:54 +01:00
|
|
|
#include <sys/stat.h>
|
2006-08-05 03:02:17 +02:00
|
|
|
#include <sys/param.h>
|
2000-04-29 15:57:08 +02:00
|
|
|
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include <openssl/pem.h>
|
2008-02-28 09:13:52 +01:00
|
|
|
#include "openbsd-compat/openssl-compat.h"
|
1999-10-27 05:42:43 +02:00
|
|
|
|
2006-07-12 14:22:46 +02:00
|
|
|
#include <errno.h>
|
2006-07-10 13:13:46 +02:00
|
|
|
#include <fcntl.h>
|
2006-07-24 06:51:00 +02:00
|
|
|
#include <netdb.h>
|
2006-07-11 10:55:05 +02:00
|
|
|
#ifdef HAVE_PATHS_H
|
|
|
|
# include <paths.h>
|
|
|
|
#endif
|
2006-07-10 12:53:08 +02:00
|
|
|
#include <pwd.h>
|
2006-09-01 07:38:36 +02:00
|
|
|
#include <stdarg.h>
|
2006-08-05 03:37:59 +02:00
|
|
|
#include <stdio.h>
|
2006-08-05 03:34:19 +02:00
|
|
|
#include <stdlib.h>
|
2006-07-24 06:13:33 +02:00
|
|
|
#include <string.h>
|
2006-07-24 06:01:23 +02:00
|
|
|
#include <unistd.h>
|
2006-07-10 12:53:08 +02:00
|
|
|
|
1999-10-27 05:42:43 +02:00
|
|
|
#include "xmalloc.h"
|
2000-04-29 15:57:08 +02:00
|
|
|
#include "key.h"
|
2001-03-29 02:29:54 +02:00
|
|
|
#include "rsa.h"
|
2000-04-29 15:57:08 +02:00
|
|
|
#include "authfile.h"
|
|
|
|
#include "uuencode.h"
|
2000-10-14 07:23:11 +02:00
|
|
|
#include "buffer.h"
|
2001-01-22 06:34:40 +01:00
|
|
|
#include "pathnames.h"
|
|
|
|
#include "log.h"
|
2004-05-13 08:15:47 +02:00
|
|
|
#include "misc.h"
|
2005-03-01 11:48:35 +01:00
|
|
|
#include "match.h"
|
|
|
|
#include "hostfile.h"
|
2006-07-10 12:53:31 +02:00
|
|
|
#include "dns.h"
|
2013-01-18 01:44:04 +01:00
|
|
|
#include "ssh.h"
|
2010-02-26 21:55:05 +01:00
|
|
|
#include "ssh2.h"
|
2010-02-11 23:21:02 +01:00
|
|
|
#include "ssh-pkcs11.h"
|
2013-01-18 01:44:04 +01:00
|
|
|
#include "atomicio.h"
|
|
|
|
#include "krl.h"
|
2001-07-04 05:44:03 +02:00
|
|
|
|
2005-11-05 04:52:18 +01:00
|
|
|
/* Number of bits in the RSA/DSA key. This value can be set on the command line. */
|
|
|
|
#define DEFAULT_BITS 2048
|
|
|
|
#define DEFAULT_BITS_DSA 1024
|
2010-09-10 03:17:38 +02:00
|
|
|
#define DEFAULT_BITS_ECDSA 256
|
2005-11-05 04:52:18 +01:00
|
|
|
u_int32_t bits = 0;
|
1999-10-27 05:42:43 +02:00
|
|
|
|
1999-11-25 01:54:57 +01:00
|
|
|
/*
|
|
|
|
* Flag indicating that we just want to change the passphrase. This can be
|
|
|
|
* set on the command line.
|
|
|
|
*/
|
1999-10-27 05:42:43 +02:00
|
|
|
int change_passphrase = 0;
|
|
|
|
|
1999-11-25 01:54:57 +01:00
|
|
|
/*
|
|
|
|
* Flag indicating that we just want to change the comment. This can be set
|
|
|
|
* on the command line.
|
|
|
|
*/
|
1999-10-27 05:42:43 +02:00
|
|
|
int change_comment = 0;
|
|
|
|
|
|
|
|
int quiet = 0;
|
|
|
|
|
2008-06-12 20:43:15 +02:00
|
|
|
int log_level = SYSLOG_LEVEL_INFO;
|
|
|
|
|
2005-03-01 11:48:35 +01:00
|
|
|
/* Flag indicating that we want to hash a known_hosts file */
|
|
|
|
int hash_hosts = 0;
|
|
|
|
/* Flag indicating that we want lookup a host in known_hosts file */
|
|
|
|
int find_host = 0;
|
|
|
|
/* Flag indicating that we want to delete a host from a known_hosts file */
|
|
|
|
int delete_host = 0;
|
|
|
|
|
2010-03-04 21:39:35 +01:00
|
|
|
/* Flag indicating that we want to show the contents of a certificate */
|
|
|
|
int show_cert = 0;
|
|
|
|
|
1999-11-17 07:29:08 +01:00
|
|
|
/* Flag indicating that we just want to see the key fingerprint */
|
|
|
|
int print_fingerprint = 0;
|
2001-03-12 04:02:17 +01:00
|
|
|
int print_bubblebabble = 0;
|
1999-11-17 07:29:08 +01:00
|
|
|
|
1999-11-21 08:31:57 +01:00
|
|
|
/* The identity file name, given on the command line or entered by the user. */
|
|
|
|
char identity_file[1024];
|
|
|
|
int have_identity = 0;
|
1999-10-27 05:42:43 +02:00
|
|
|
|
|
|
|
/* This is set to the passphrase if given on the command line. */
|
|
|
|
char *identity_passphrase = NULL;
|
|
|
|
|
|
|
|
/* This is set to the new passphrase if given on the command line. */
|
|
|
|
char *identity_new_passphrase = NULL;
|
|
|
|
|
|
|
|
/* This is set to the new comment if given on the command line. */
|
|
|
|
char *identity_comment = NULL;
|
|
|
|
|
2010-02-26 21:55:05 +01:00
|
|
|
/* Path to CA key when certifying keys. */
|
|
|
|
char *ca_key_path = NULL;
|
|
|
|
|
2010-04-16 07:56:21 +02:00
|
|
|
/* Certificate serial number */
|
2012-12-03 01:25:30 +01:00
|
|
|
unsigned long long cert_serial = 0;
|
2010-04-16 07:56:21 +02:00
|
|
|
|
2010-02-26 21:55:05 +01:00
|
|
|
/* Key type when certifying */
|
|
|
|
u_int cert_key_type = SSH2_CERT_TYPE_USER;
|
|
|
|
|
|
|
|
/* "key ID" of signed key */
|
|
|
|
char *cert_key_id = NULL;
|
|
|
|
|
|
|
|
/* Comma-separated list of principal names for certifying keys */
|
|
|
|
char *cert_principals = NULL;
|
|
|
|
|
|
|
|
/* Validity period for certificates */
|
|
|
|
u_int64_t cert_valid_from = 0;
|
|
|
|
u_int64_t cert_valid_to = ~0ULL;
|
|
|
|
|
2010-04-16 07:56:21 +02:00
|
|
|
/* Certificate options */
|
2010-05-21 06:58:32 +02:00
|
|
|
#define CERTOPT_X_FWD (1)
|
|
|
|
#define CERTOPT_AGENT_FWD (1<<1)
|
|
|
|
#define CERTOPT_PORT_FWD (1<<2)
|
|
|
|
#define CERTOPT_PTY (1<<3)
|
|
|
|
#define CERTOPT_USER_RC (1<<4)
|
|
|
|
#define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \
|
|
|
|
CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC)
|
|
|
|
u_int32_t certflags_flags = CERTOPT_DEFAULT;
|
|
|
|
char *certflags_command = NULL;
|
|
|
|
char *certflags_src_addr = NULL;
|
2010-02-26 21:55:05 +01:00
|
|
|
|
2010-07-02 05:35:01 +02:00
|
|
|
/* Conversion to/from various formats */
|
|
|
|
int convert_to = 0;
|
|
|
|
int convert_from = 0;
|
|
|
|
enum {
|
|
|
|
FMT_RFC4716,
|
|
|
|
FMT_PKCS8,
|
|
|
|
FMT_PEM
|
|
|
|
} convert_format = FMT_RFC4716;
|
2000-04-29 15:57:08 +02:00
|
|
|
int print_public = 0;
|
2003-05-15 02:19:46 +02:00
|
|
|
int print_generic = 0;
|
2000-11-13 12:57:25 +01:00
|
|
|
|
2002-01-22 13:05:08 +01:00
|
|
|
char *key_type_name = NULL;
|
2000-04-29 15:57:08 +02:00
|
|
|
|
2010-08-05 05:05:31 +02:00
|
|
|
/* Load key from this PKCS#11 provider */
|
|
|
|
char *pkcs11provider = NULL;
|
2010-07-02 05:35:01 +02:00
|
|
|
|
2013-12-07 00:41:55 +01:00
|
|
|
/* Use new OpenSSH private key format when writing SSH2 keys instead of PEM */
|
|
|
|
int use_new_format = 0;
|
|
|
|
|
|
|
|
/* Cipher for new-format private keys */
|
|
|
|
char *new_format_cipher = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Number of KDF rounds to derive new format keys /
|
|
|
|
* number of primality trials when screening moduli.
|
|
|
|
*/
|
|
|
|
int rounds = 0;
|
|
|
|
|
1999-11-21 08:31:57 +01:00
|
|
|
/* argv0 */
|
|
|
|
extern char *__progname;
|
1999-10-27 05:42:43 +02:00
|
|
|
|
2000-04-29 15:57:08 +02:00
|
|
|
char hostname[MAXHOSTNAMELEN];
|
|
|
|
|
2004-05-13 08:24:32 +02:00
|
|
|
/* moduli.c */
|
2005-05-26 04:16:18 +02:00
|
|
|
int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *);
|
2012-07-06 05:44:19 +02:00
|
|
|
int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long,
|
|
|
|
unsigned long);
|
2004-05-13 08:24:32 +02:00
|
|
|
|
2011-05-05 06:06:15 +02:00
|
|
|
static void
|
2011-05-05 06:14:52 +02:00
|
|
|
type_bits_valid(int type, u_int32_t *bitsp)
|
2011-05-05 06:06:15 +02:00
|
|
|
{
|
|
|
|
u_int maxbits;
|
|
|
|
|
|
|
|
if (type == KEY_UNSPEC) {
|
|
|
|
fprintf(stderr, "unknown key type %s\n", key_type_name);
|
|
|
|
exit(1);
|
|
|
|
}
|
2011-05-05 06:14:52 +02:00
|
|
|
if (*bitsp == 0) {
|
2011-05-05 06:06:15 +02:00
|
|
|
if (type == KEY_DSA)
|
2011-05-05 06:14:52 +02:00
|
|
|
*bitsp = DEFAULT_BITS_DSA;
|
2011-05-05 06:06:15 +02:00
|
|
|
else if (type == KEY_ECDSA)
|
2011-05-05 06:14:52 +02:00
|
|
|
*bitsp = DEFAULT_BITS_ECDSA;
|
2011-05-05 06:06:15 +02:00
|
|
|
else
|
2011-05-05 06:14:52 +02:00
|
|
|
*bitsp = DEFAULT_BITS;
|
2011-05-05 06:06:15 +02:00
|
|
|
}
|
|
|
|
maxbits = (type == KEY_DSA) ?
|
|
|
|
OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS;
|
2011-05-05 06:14:52 +02:00
|
|
|
if (*bitsp > maxbits) {
|
2011-05-05 06:06:15 +02:00
|
|
|
fprintf(stderr, "key bits exceeds maximum %d\n", maxbits);
|
|
|
|
exit(1);
|
|
|
|
}
|
2014-05-15 06:24:09 +02:00
|
|
|
#ifdef WITH_OPENSSL
|
2011-05-05 06:14:52 +02:00
|
|
|
if (type == KEY_DSA && *bitsp != 1024)
|
2011-05-05 06:06:15 +02:00
|
|
|
fatal("DSA keys must be 1024 bits");
|
2013-12-07 01:24:01 +01:00
|
|
|
else if (type != KEY_ECDSA && type != KEY_ED25519 && *bitsp < 768)
|
2011-05-05 06:06:15 +02:00
|
|
|
fatal("Key must at least be 768 bits");
|
2011-05-05 06:14:52 +02:00
|
|
|
else if (type == KEY_ECDSA && key_ecdsa_bits_to_nid(*bitsp) == -1)
|
2011-05-05 06:06:15 +02:00
|
|
|
fatal("Invalid ECDSA key length - valid lengths are "
|
|
|
|
"256, 384 or 521 bits");
|
2014-05-15 06:24:09 +02:00
|
|
|
#endif
|
2011-05-05 06:06:15 +02:00
|
|
|
}
|
|
|
|
|
2001-06-25 07:01:22 +02:00
|
|
|
static void
|
1999-11-21 08:31:57 +01:00
|
|
|
ask_filename(struct passwd *pw, const char *prompt)
|
1999-10-27 05:42:43 +02:00
|
|
|
{
|
1999-11-24 14:26:21 +01:00
|
|
|
char buf[1024];
|
2000-11-29 02:18:44 +01:00
|
|
|
char *name = NULL;
|
|
|
|
|
2002-02-19 05:22:47 +01:00
|
|
|
if (key_type_name == NULL)
|
2001-01-22 06:34:40 +01:00
|
|
|
name = _PATH_SSH_CLIENT_ID_RSA;
|
2006-03-26 05:07:26 +02:00
|
|
|
else {
|
2002-02-19 05:22:47 +01:00
|
|
|
switch (key_type_from_name(key_type_name)) {
|
|
|
|
case KEY_RSA1:
|
|
|
|
name = _PATH_SSH_CLIENT_IDENTITY;
|
|
|
|
break;
|
2010-04-16 07:56:21 +02:00
|
|
|
case KEY_DSA_CERT:
|
|
|
|
case KEY_DSA_CERT_V00:
|
2002-02-19 05:22:47 +01:00
|
|
|
case KEY_DSA:
|
|
|
|
name = _PATH_SSH_CLIENT_ID_DSA;
|
|
|
|
break;
|
2010-11-11 04:17:02 +01:00
|
|
|
#ifdef OPENSSL_HAS_ECC
|
2010-08-31 14:41:14 +02:00
|
|
|
case KEY_ECDSA_CERT:
|
|
|
|
case KEY_ECDSA:
|
|
|
|
name = _PATH_SSH_CLIENT_ID_ECDSA;
|
|
|
|
break;
|
2010-11-11 04:17:02 +01:00
|
|
|
#endif
|
2010-04-16 07:56:21 +02:00
|
|
|
case KEY_RSA_CERT:
|
|
|
|
case KEY_RSA_CERT_V00:
|
2002-02-19 05:22:47 +01:00
|
|
|
case KEY_RSA:
|
|
|
|
name = _PATH_SSH_CLIENT_ID_RSA;
|
|
|
|
break;
|
2013-12-07 01:24:01 +01:00
|
|
|
case KEY_ED25519:
|
|
|
|
case KEY_ED25519_CERT:
|
|
|
|
name = _PATH_SSH_CLIENT_ID_ED25519;
|
|
|
|
break;
|
2002-02-19 05:22:47 +01:00
|
|
|
default:
|
2009-02-21 22:47:02 +01:00
|
|
|
fprintf(stderr, "bad key type\n");
|
2002-02-19 05:22:47 +01:00
|
|
|
exit(1);
|
|
|
|
break;
|
|
|
|
}
|
2006-03-26 05:07:26 +02:00
|
|
|
}
|
2000-11-29 02:18:44 +01:00
|
|
|
snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name);
|
2000-12-22 21:27:43 +01:00
|
|
|
fprintf(stderr, "%s (%s): ", prompt, identity_file);
|
1999-11-24 14:26:21 +01:00
|
|
|
if (fgets(buf, sizeof(buf), stdin) == NULL)
|
|
|
|
exit(1);
|
2007-09-17 08:09:15 +02:00
|
|
|
buf[strcspn(buf, "\n")] = '\0';
|
1999-11-24 14:26:21 +01:00
|
|
|
if (strcmp(buf, "") != 0)
|
|
|
|
strlcpy(identity_file, buf, sizeof(identity_file));
|
|
|
|
have_identity = 1;
|
1999-11-17 07:29:08 +01:00
|
|
|
}
|
1999-10-27 05:42:43 +02:00
|
|
|
|
2001-06-25 07:01:22 +02:00
|
|
|
static Key *
|
2001-06-05 22:35:09 +02:00
|
|
|
load_identity(char *filename)
|
2000-04-29 15:57:08 +02:00
|
|
|
{
|
2001-03-26 15:44:06 +02:00
|
|
|
char *pass;
|
|
|
|
Key *prv;
|
|
|
|
|
2001-04-06 01:26:32 +02:00
|
|
|
prv = key_load_private(filename, "", NULL);
|
2001-03-26 15:44:06 +02:00
|
|
|
if (prv == NULL) {
|
2001-06-05 22:35:09 +02:00
|
|
|
if (identity_passphrase)
|
|
|
|
pass = xstrdup(identity_passphrase);
|
|
|
|
else
|
2001-06-25 07:20:31 +02:00
|
|
|
pass = read_passphrase("Enter passphrase: ",
|
|
|
|
RP_ALLOW_STDIN);
|
2001-03-26 15:44:06 +02:00
|
|
|
prv = key_load_private(filename, pass, NULL);
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(pass, strlen(pass));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(pass);
|
2000-04-29 15:57:08 +02:00
|
|
|
}
|
2001-03-26 15:44:06 +02:00
|
|
|
return prv;
|
2000-04-29 15:57:08 +02:00
|
|
|
}
|
|
|
|
|
2000-10-14 07:23:11 +02:00
|
|
|
#define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
|
2002-06-21 02:41:51 +02:00
|
|
|
#define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----"
|
2000-10-14 07:23:11 +02:00
|
|
|
#define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
|
2001-02-05 13:42:17 +01:00
|
|
|
#define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb
|
2000-04-29 15:57:08 +02:00
|
|
|
|
2014-05-15 06:24:09 +02:00
|
|
|
#ifdef WITH_OPENSSL
|
2001-06-25 07:01:22 +02:00
|
|
|
static void
|
2010-07-02 05:35:01 +02:00
|
|
|
do_convert_to_ssh2(struct passwd *pw, Key *k)
|
2000-04-29 15:57:08 +02:00
|
|
|
{
|
2002-02-26 19:15:09 +01:00
|
|
|
u_int len;
|
2000-12-22 02:43:59 +01:00
|
|
|
u_char *blob;
|
2010-01-12 09:41:57 +01:00
|
|
|
char comment[61];
|
2000-04-29 15:57:08 +02:00
|
|
|
|
2012-04-22 03:07:28 +02:00
|
|
|
if (k->type == KEY_RSA1) {
|
|
|
|
fprintf(stderr, "version 1 keys are not supported\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2001-09-18 07:49:14 +02:00
|
|
|
if (key_to_blob(k, &blob, &len) <= 0) {
|
|
|
|
fprintf(stderr, "key_to_blob failed\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2010-01-12 09:41:57 +01:00
|
|
|
/* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */
|
|
|
|
snprintf(comment, sizeof(comment),
|
|
|
|
"%u-bit %s, converted by %s@%s from OpenSSH",
|
2001-04-24 18:56:58 +02:00
|
|
|
key_size(k), key_type(k),
|
2000-04-29 15:57:08 +02:00
|
|
|
pw->pw_name, hostname);
|
2010-01-12 09:41:57 +01:00
|
|
|
|
|
|
|
fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
|
|
|
|
fprintf(stdout, "Comment: \"%s\"\n", comment);
|
2000-04-29 15:57:08 +02:00
|
|
|
dump_base64(stdout, blob, len);
|
2000-10-14 07:23:11 +02:00
|
|
|
fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
|
2001-04-24 18:56:58 +02:00
|
|
|
key_free(k);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(blob);
|
2000-04-29 15:57:08 +02:00
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2010-07-02 05:35:01 +02:00
|
|
|
static void
|
|
|
|
do_convert_to_pkcs8(Key *k)
|
|
|
|
{
|
|
|
|
switch (key_type_plain(k->type)) {
|
2012-04-22 03:07:28 +02:00
|
|
|
case KEY_RSA1:
|
2010-07-02 05:35:01 +02:00
|
|
|
case KEY_RSA:
|
|
|
|
if (!PEM_write_RSA_PUBKEY(stdout, k->rsa))
|
|
|
|
fatal("PEM_write_RSA_PUBKEY failed");
|
|
|
|
break;
|
|
|
|
case KEY_DSA:
|
|
|
|
if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
|
|
|
|
fatal("PEM_write_DSA_PUBKEY failed");
|
|
|
|
break;
|
2010-09-10 03:39:26 +02:00
|
|
|
#ifdef OPENSSL_HAS_ECC
|
2010-08-31 14:41:14 +02:00
|
|
|
case KEY_ECDSA:
|
|
|
|
if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
|
|
|
|
fatal("PEM_write_EC_PUBKEY failed");
|
|
|
|
break;
|
2010-09-10 03:39:26 +02:00
|
|
|
#endif
|
2010-07-02 05:35:01 +02:00
|
|
|
default:
|
|
|
|
fatal("%s: unsupported key type %s", __func__, key_type(k));
|
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
do_convert_to_pem(Key *k)
|
|
|
|
{
|
|
|
|
switch (key_type_plain(k->type)) {
|
2012-04-22 03:07:28 +02:00
|
|
|
case KEY_RSA1:
|
2010-07-02 05:35:01 +02:00
|
|
|
case KEY_RSA:
|
|
|
|
if (!PEM_write_RSAPublicKey(stdout, k->rsa))
|
|
|
|
fatal("PEM_write_RSAPublicKey failed");
|
|
|
|
break;
|
|
|
|
#if notyet /* OpenSSH 0.9.8 lacks this function */
|
|
|
|
case KEY_DSA:
|
|
|
|
if (!PEM_write_DSAPublicKey(stdout, k->dsa))
|
|
|
|
fatal("PEM_write_DSAPublicKey failed");
|
|
|
|
break;
|
|
|
|
#endif
|
2010-08-31 14:41:14 +02:00
|
|
|
/* XXX ECDSA? */
|
2010-07-02 05:35:01 +02:00
|
|
|
default:
|
|
|
|
fatal("%s: unsupported key type %s", __func__, key_type(k));
|
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
do_convert_to(struct passwd *pw)
|
|
|
|
{
|
|
|
|
Key *k;
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (!have_identity)
|
|
|
|
ask_filename(pw, "Enter file in which the key is");
|
|
|
|
if (stat(identity_file, &st) < 0)
|
|
|
|
fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
|
|
|
|
if ((k = key_load_public(identity_file, NULL)) == NULL) {
|
|
|
|
if ((k = load_identity(identity_file)) == NULL) {
|
|
|
|
fprintf(stderr, "load failed\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (convert_format) {
|
|
|
|
case FMT_RFC4716:
|
|
|
|
do_convert_to_ssh2(pw, k);
|
|
|
|
break;
|
|
|
|
case FMT_PKCS8:
|
|
|
|
do_convert_to_pkcs8(k);
|
|
|
|
break;
|
|
|
|
case FMT_PEM:
|
|
|
|
do_convert_to_pem(k);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fatal("%s: unknown key format %d", __func__, convert_format);
|
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2001-06-25 07:01:22 +02:00
|
|
|
static void
|
2000-10-14 07:23:11 +02:00
|
|
|
buffer_get_bignum_bits(Buffer *b, BIGNUM *value)
|
|
|
|
{
|
2004-06-22 04:56:01 +02:00
|
|
|
u_int bignum_bits = buffer_get_int(b);
|
|
|
|
u_int bytes = (bignum_bits + 7) / 8;
|
2001-03-29 02:29:54 +02:00
|
|
|
|
2000-10-14 07:23:11 +02:00
|
|
|
if (buffer_len(b) < bytes)
|
2001-03-29 02:29:54 +02:00
|
|
|
fatal("buffer_get_bignum_bits: input buffer too small: "
|
|
|
|
"need %d have %d", bytes, buffer_len(b));
|
2006-11-07 13:14:41 +01:00
|
|
|
if (BN_bin2bn(buffer_ptr(b), bytes, value) == NULL)
|
|
|
|
fatal("buffer_get_bignum_bits: BN_bin2bn failed");
|
2000-10-14 07:23:11 +02:00
|
|
|
buffer_consume(b, bytes);
|
|
|
|
}
|
|
|
|
|
2001-06-25 07:01:22 +02:00
|
|
|
static Key *
|
2002-02-26 19:09:42 +01:00
|
|
|
do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
|
2000-10-14 07:23:11 +02:00
|
|
|
{
|
|
|
|
Buffer b;
|
|
|
|
Key *key = NULL;
|
2001-06-25 07:04:58 +02:00
|
|
|
char *type, *cipher;
|
2014-04-20 05:00:11 +02:00
|
|
|
u_char *sig = NULL, data[] = "abcde12345";
|
2001-06-25 06:47:54 +02:00
|
|
|
int magic, rlen, ktype, i1, i2, i3, i4;
|
2001-06-25 07:04:58 +02:00
|
|
|
u_int slen;
|
2001-06-25 06:47:54 +02:00
|
|
|
u_long e;
|
2000-10-14 07:23:11 +02:00
|
|
|
|
|
|
|
buffer_init(&b);
|
|
|
|
buffer_append(&b, blob, blen);
|
|
|
|
|
2007-02-19 12:10:25 +01:00
|
|
|
magic = buffer_get_int(&b);
|
2000-10-14 07:23:11 +02:00
|
|
|
if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
|
|
|
|
error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC);
|
|
|
|
buffer_free(&b);
|
|
|
|
return NULL;
|
|
|
|
}
|
2001-06-25 06:47:54 +02:00
|
|
|
i1 = buffer_get_int(&b);
|
2000-10-14 07:23:11 +02:00
|
|
|
type = buffer_get_string(&b, NULL);
|
|
|
|
cipher = buffer_get_string(&b, NULL);
|
2001-06-25 06:47:54 +02:00
|
|
|
i2 = buffer_get_int(&b);
|
|
|
|
i3 = buffer_get_int(&b);
|
|
|
|
i4 = buffer_get_int(&b);
|
2007-01-05 06:30:16 +01:00
|
|
|
debug("ignore (%d %d %d %d)", i1, i2, i3, i4);
|
2000-10-14 07:23:11 +02:00
|
|
|
if (strcmp(cipher, "none") != 0) {
|
|
|
|
error("unsupported cipher %s", cipher);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(cipher);
|
2000-10-14 07:23:11 +02:00
|
|
|
buffer_free(&b);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(type);
|
2000-10-14 07:23:11 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2013-06-01 23:31:17 +02:00
|
|
|
free(cipher);
|
2000-10-14 07:23:11 +02:00
|
|
|
|
2001-03-29 02:29:54 +02:00
|
|
|
if (strstr(type, "dsa")) {
|
|
|
|
ktype = KEY_DSA;
|
|
|
|
} else if (strstr(type, "rsa")) {
|
|
|
|
ktype = KEY_RSA;
|
|
|
|
} else {
|
2005-01-20 00:56:31 +01:00
|
|
|
buffer_free(&b);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(type);
|
2000-10-14 07:23:11 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2001-03-29 02:29:54 +02:00
|
|
|
key = key_new_private(ktype);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(type);
|
2001-03-29 02:29:54 +02:00
|
|
|
|
|
|
|
switch (key->type) {
|
|
|
|
case KEY_DSA:
|
|
|
|
buffer_get_bignum_bits(&b, key->dsa->p);
|
|
|
|
buffer_get_bignum_bits(&b, key->dsa->g);
|
|
|
|
buffer_get_bignum_bits(&b, key->dsa->q);
|
|
|
|
buffer_get_bignum_bits(&b, key->dsa->pub_key);
|
|
|
|
buffer_get_bignum_bits(&b, key->dsa->priv_key);
|
|
|
|
break;
|
|
|
|
case KEY_RSA:
|
2007-02-19 12:10:25 +01:00
|
|
|
e = buffer_get_char(&b);
|
2001-06-25 06:47:54 +02:00
|
|
|
debug("e %lx", e);
|
|
|
|
if (e < 30) {
|
|
|
|
e <<= 8;
|
|
|
|
e += buffer_get_char(&b);
|
|
|
|
debug("e %lx", e);
|
|
|
|
e <<= 8;
|
|
|
|
e += buffer_get_char(&b);
|
|
|
|
debug("e %lx", e);
|
|
|
|
}
|
|
|
|
if (!BN_set_word(key->rsa->e, e)) {
|
2001-03-29 02:29:54 +02:00
|
|
|
buffer_free(&b);
|
|
|
|
key_free(key);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
buffer_get_bignum_bits(&b, key->rsa->d);
|
|
|
|
buffer_get_bignum_bits(&b, key->rsa->n);
|
|
|
|
buffer_get_bignum_bits(&b, key->rsa->iqmp);
|
|
|
|
buffer_get_bignum_bits(&b, key->rsa->q);
|
|
|
|
buffer_get_bignum_bits(&b, key->rsa->p);
|
2001-07-04 07:02:23 +02:00
|
|
|
rsa_generate_additional_parameters(key->rsa);
|
2001-03-29 02:29:54 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-10-14 07:23:11 +02:00
|
|
|
rlen = buffer_len(&b);
|
2001-12-06 19:00:18 +01:00
|
|
|
if (rlen != 0)
|
2001-03-29 02:29:54 +02:00
|
|
|
error("do_convert_private_ssh2_from_blob: "
|
|
|
|
"remaining bytes in key blob %d", rlen);
|
2000-10-14 07:23:11 +02:00
|
|
|
buffer_free(&b);
|
2001-06-25 07:04:58 +02:00
|
|
|
|
|
|
|
/* try the key */
|
|
|
|
key_sign(key, &sig, &slen, data, sizeof(data));
|
|
|
|
key_verify(key, sig, slen, data, sizeof(data));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(sig);
|
2000-10-14 07:23:11 +02:00
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2006-03-15 02:05:40 +01:00
|
|
|
static int
|
|
|
|
get_line(FILE *fp, char *line, size_t len)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
size_t pos = 0;
|
|
|
|
|
|
|
|
line[0] = '\0';
|
|
|
|
while ((c = fgetc(fp)) != EOF) {
|
|
|
|
if (pos >= len - 1) {
|
|
|
|
fprintf(stderr, "input line too long.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2006-03-26 05:07:26 +02:00
|
|
|
switch (c) {
|
2006-03-15 02:05:40 +01:00
|
|
|
case '\r':
|
|
|
|
c = fgetc(fp);
|
|
|
|
if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) {
|
|
|
|
fprintf(stderr, "unget: %s\n", strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
return pos;
|
|
|
|
case '\n':
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
line[pos++] = c;
|
|
|
|
line[pos] = '\0';
|
|
|
|
}
|
2007-01-05 06:29:55 +01:00
|
|
|
/* We reached EOF */
|
|
|
|
return -1;
|
2006-03-15 02:05:40 +01:00
|
|
|
}
|
|
|
|
|
2001-06-25 07:01:22 +02:00
|
|
|
static void
|
2010-07-02 05:35:01 +02:00
|
|
|
do_convert_from_ssh2(struct passwd *pw, Key **k, int *private)
|
2000-04-29 15:57:08 +02:00
|
|
|
{
|
|
|
|
int blen;
|
2002-04-02 22:26:26 +02:00
|
|
|
u_int len;
|
2006-03-15 02:05:40 +01:00
|
|
|
char line[1024];
|
2001-09-18 07:41:19 +02:00
|
|
|
u_char blob[8096];
|
2000-04-29 15:57:08 +02:00
|
|
|
char encoded[8096];
|
2010-07-02 05:35:01 +02:00
|
|
|
int escaped = 0;
|
2000-04-29 15:57:08 +02:00
|
|
|
FILE *fp;
|
|
|
|
|
2010-06-26 01:39:07 +02:00
|
|
|
if ((fp = fopen(identity_file, "r")) == NULL)
|
|
|
|
fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
|
2000-04-29 15:57:08 +02:00
|
|
|
encoded[0] = '\0';
|
2006-03-15 02:05:40 +01:00
|
|
|
while ((blen = get_line(fp, line, sizeof(line))) != -1) {
|
2013-07-18 08:13:02 +02:00
|
|
|
if (blen > 0 && line[blen - 1] == '\\')
|
2000-05-09 03:02:59 +02:00
|
|
|
escaped++;
|
2000-04-29 15:57:08 +02:00
|
|
|
if (strncmp(line, "----", 4) == 0 ||
|
|
|
|
strstr(line, ": ") != NULL) {
|
2000-10-14 07:23:11 +02:00
|
|
|
if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
|
2010-07-02 05:35:01 +02:00
|
|
|
*private = 1;
|
2001-06-25 07:04:58 +02:00
|
|
|
if (strstr(line, " END ") != NULL) {
|
|
|
|
break;
|
|
|
|
}
|
2001-04-24 18:59:28 +02:00
|
|
|
/* fprintf(stderr, "ignore: %s", line); */
|
2000-04-29 15:57:08 +02:00
|
|
|
continue;
|
|
|
|
}
|
2000-05-09 03:02:59 +02:00
|
|
|
if (escaped) {
|
|
|
|
escaped--;
|
2001-04-24 18:59:28 +02:00
|
|
|
/* fprintf(stderr, "escaped: %s", line); */
|
2000-05-09 03:02:59 +02:00
|
|
|
continue;
|
2000-04-29 15:57:08 +02:00
|
|
|
}
|
|
|
|
strlcat(encoded, line, sizeof(encoded));
|
|
|
|
}
|
2002-04-02 22:26:26 +02:00
|
|
|
len = strlen(encoded);
|
|
|
|
if (((len % 4) == 3) &&
|
|
|
|
(encoded[len-1] == '=') &&
|
|
|
|
(encoded[len-2] == '=') &&
|
|
|
|
(encoded[len-3] == '='))
|
|
|
|
encoded[len-3] = '\0';
|
2002-01-22 13:33:31 +01:00
|
|
|
blen = uudecode(encoded, blob, sizeof(blob));
|
2000-04-29 15:57:08 +02:00
|
|
|
if (blen < 0) {
|
|
|
|
fprintf(stderr, "uudecode failed.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2010-07-02 05:35:01 +02:00
|
|
|
*k = *private ?
|
2000-10-14 07:23:11 +02:00
|
|
|
do_convert_private_ssh2_from_blob(blob, blen) :
|
2000-11-13 12:57:25 +01:00
|
|
|
key_from_blob(blob, blen);
|
2010-07-02 05:35:01 +02:00
|
|
|
if (*k == NULL) {
|
2000-10-14 07:23:11 +02:00
|
|
|
fprintf(stderr, "decode blob failed.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2010-07-02 05:35:01 +02:00
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
do_convert_from_pkcs8(Key **k, int *private)
|
|
|
|
{
|
|
|
|
EVP_PKEY *pubkey;
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
if ((fp = fopen(identity_file, "r")) == NULL)
|
|
|
|
fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
|
|
|
|
if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
|
|
|
|
fatal("%s: %s is not a recognised public key format", __func__,
|
|
|
|
identity_file);
|
|
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
switch (EVP_PKEY_type(pubkey->type)) {
|
|
|
|
case EVP_PKEY_RSA:
|
|
|
|
*k = key_new(KEY_UNSPEC);
|
|
|
|
(*k)->type = KEY_RSA;
|
|
|
|
(*k)->rsa = EVP_PKEY_get1_RSA(pubkey);
|
|
|
|
break;
|
|
|
|
case EVP_PKEY_DSA:
|
|
|
|
*k = key_new(KEY_UNSPEC);
|
|
|
|
(*k)->type = KEY_DSA;
|
|
|
|
(*k)->dsa = EVP_PKEY_get1_DSA(pubkey);
|
|
|
|
break;
|
2010-09-10 03:39:26 +02:00
|
|
|
#ifdef OPENSSL_HAS_ECC
|
2010-08-31 14:41:14 +02:00
|
|
|
case EVP_PKEY_EC:
|
|
|
|
*k = key_new(KEY_UNSPEC);
|
|
|
|
(*k)->type = KEY_ECDSA;
|
|
|
|
(*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey);
|
2010-11-05 00:19:49 +01:00
|
|
|
(*k)->ecdsa_nid = key_ecdsa_key_to_nid((*k)->ecdsa);
|
2010-08-31 14:41:14 +02:00
|
|
|
break;
|
2010-09-10 03:39:26 +02:00
|
|
|
#endif
|
2010-07-02 05:35:01 +02:00
|
|
|
default:
|
|
|
|
fatal("%s: unsupported pubkey type %d", __func__,
|
|
|
|
EVP_PKEY_type(pubkey->type));
|
|
|
|
}
|
|
|
|
EVP_PKEY_free(pubkey);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
do_convert_from_pem(Key **k, int *private)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
RSA *rsa;
|
|
|
|
#ifdef notyet
|
|
|
|
DSA *dsa;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ((fp = fopen(identity_file, "r")) == NULL)
|
|
|
|
fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
|
|
|
|
if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
|
|
|
|
*k = key_new(KEY_UNSPEC);
|
|
|
|
(*k)->type = KEY_RSA;
|
|
|
|
(*k)->rsa = rsa;
|
|
|
|
fclose(fp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#if notyet /* OpenSSH 0.9.8 lacks this function */
|
|
|
|
rewind(fp);
|
|
|
|
if ((dsa = PEM_read_DSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
|
|
|
|
*k = key_new(KEY_UNSPEC);
|
|
|
|
(*k)->type = KEY_DSA;
|
|
|
|
(*k)->dsa = dsa;
|
|
|
|
fclose(fp);
|
|
|
|
return;
|
|
|
|
}
|
2010-08-31 14:41:14 +02:00
|
|
|
/* XXX ECDSA */
|
2010-07-02 05:35:01 +02:00
|
|
|
#endif
|
|
|
|
fatal("%s: unrecognised raw private key format", __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
do_convert_from(struct passwd *pw)
|
|
|
|
{
|
|
|
|
Key *k = NULL;
|
2010-08-03 08:03:29 +02:00
|
|
|
int private = 0, ok = 0;
|
2010-07-02 05:35:01 +02:00
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (!have_identity)
|
|
|
|
ask_filename(pw, "Enter file in which the key is");
|
|
|
|
if (stat(identity_file, &st) < 0)
|
|
|
|
fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
|
|
|
|
|
|
|
|
switch (convert_format) {
|
|
|
|
case FMT_RFC4716:
|
|
|
|
do_convert_from_ssh2(pw, &k, &private);
|
|
|
|
break;
|
|
|
|
case FMT_PKCS8:
|
|
|
|
do_convert_from_pkcs8(&k, &private);
|
|
|
|
break;
|
|
|
|
case FMT_PEM:
|
|
|
|
do_convert_from_pem(&k, &private);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fatal("%s: unknown key format %d", __func__, convert_format);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!private)
|
|
|
|
ok = key_write(k, stdout);
|
|
|
|
if (ok)
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
else {
|
|
|
|
switch (k->type) {
|
|
|
|
case KEY_DSA:
|
|
|
|
ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL,
|
|
|
|
NULL, 0, NULL, NULL);
|
|
|
|
break;
|
2010-09-10 03:39:26 +02:00
|
|
|
#ifdef OPENSSL_HAS_ECC
|
2010-08-31 14:41:14 +02:00
|
|
|
case KEY_ECDSA:
|
|
|
|
ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL,
|
|
|
|
NULL, 0, NULL, NULL);
|
|
|
|
break;
|
2010-09-10 03:39:26 +02:00
|
|
|
#endif
|
2010-07-02 05:35:01 +02:00
|
|
|
case KEY_RSA:
|
|
|
|
ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL,
|
|
|
|
NULL, 0, NULL, NULL);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fatal("%s: unsupported key type %s", __func__,
|
|
|
|
key_type(k));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-10-14 07:23:11 +02:00
|
|
|
if (!ok) {
|
2009-02-21 22:47:02 +01:00
|
|
|
fprintf(stderr, "key write failed\n");
|
2000-10-14 07:23:11 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
2000-04-29 15:57:08 +02:00
|
|
|
key_free(k);
|
|
|
|
exit(0);
|
|
|
|
}
|
2014-05-15 06:24:09 +02:00
|
|
|
#endif
|
2000-04-29 15:57:08 +02:00
|
|
|
|
2001-06-25 07:01:22 +02:00
|
|
|
static void
|
2000-04-29 15:57:08 +02:00
|
|
|
do_print_public(struct passwd *pw)
|
|
|
|
{
|
2001-03-26 15:44:06 +02:00
|
|
|
Key *prv;
|
2000-04-29 15:57:08 +02:00
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (!have_identity)
|
|
|
|
ask_filename(pw, "Enter file in which the key is");
|
|
|
|
if (stat(identity_file, &st) < 0) {
|
|
|
|
perror(identity_file);
|
|
|
|
exit(1);
|
|
|
|
}
|
2001-06-05 22:35:09 +02:00
|
|
|
prv = load_identity(identity_file);
|
2001-03-26 15:44:06 +02:00
|
|
|
if (prv == NULL) {
|
2000-04-29 15:57:08 +02:00
|
|
|
fprintf(stderr, "load failed\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2001-03-26 15:44:06 +02:00
|
|
|
if (!key_write(prv, stdout))
|
2000-04-29 15:57:08 +02:00
|
|
|
fprintf(stderr, "key_write failed");
|
2001-03-26 15:44:06 +02:00
|
|
|
key_free(prv);
|
2000-04-29 15:57:08 +02:00
|
|
|
fprintf(stdout, "\n");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2001-07-04 05:44:03 +02:00
|
|
|
static void
|
2010-08-05 05:05:31 +02:00
|
|
|
do_download(struct passwd *pw)
|
2001-08-06 23:44:05 +02:00
|
|
|
{
|
2010-02-11 23:21:02 +01:00
|
|
|
#ifdef ENABLE_PKCS11
|
2002-03-26 04:17:42 +01:00
|
|
|
Key **keys = NULL;
|
2010-02-11 23:21:02 +01:00
|
|
|
int i, nkeys;
|
2013-01-09 05:58:00 +01:00
|
|
|
enum fp_rep rep;
|
|
|
|
enum fp_type fptype;
|
|
|
|
char *fp, *ra;
|
2001-08-06 23:44:05 +02:00
|
|
|
|
2013-01-09 06:44:54 +01:00
|
|
|
fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5;
|
|
|
|
rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX;
|
|
|
|
|
2010-02-11 23:21:02 +01:00
|
|
|
pkcs11_init(0);
|
|
|
|
nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys);
|
|
|
|
if (nkeys <= 0)
|
|
|
|
fatal("cannot read public key from pkcs11");
|
|
|
|
for (i = 0; i < nkeys; i++) {
|
2013-01-09 05:58:00 +01:00
|
|
|
if (print_fingerprint) {
|
|
|
|
fp = key_fingerprint(keys[i], fptype, rep);
|
|
|
|
ra = key_fingerprint(keys[i], SSH_FP_MD5,
|
|
|
|
SSH_FP_RANDOMART);
|
|
|
|
printf("%u %s %s (PKCS11 key)\n", key_size(keys[i]),
|
|
|
|
fp, key_type(keys[i]));
|
|
|
|
if (log_level >= SYSLOG_LEVEL_VERBOSE)
|
|
|
|
printf("%s\n", ra);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(ra);
|
|
|
|
free(fp);
|
2013-01-09 05:58:00 +01:00
|
|
|
} else {
|
|
|
|
key_write(keys[i], stdout);
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
}
|
2002-03-26 04:17:42 +01:00
|
|
|
key_free(keys[i]);
|
|
|
|
}
|
2013-06-01 23:31:17 +02:00
|
|
|
free(keys);
|
2010-02-11 23:21:02 +01:00
|
|
|
pkcs11_terminate();
|
2001-08-06 23:44:05 +02:00
|
|
|
exit(0);
|
2010-02-11 23:21:02 +01:00
|
|
|
#else
|
|
|
|
fatal("no pkcs11 support");
|
|
|
|
#endif /* ENABLE_PKCS11 */
|
2001-08-06 23:44:05 +02:00
|
|
|
}
|
2001-07-04 05:44:03 +02:00
|
|
|
|
2001-06-25 07:01:22 +02:00
|
|
|
static void
|
1999-11-17 07:29:08 +01:00
|
|
|
do_fingerprint(struct passwd *pw)
|
|
|
|
{
|
- OpenBSD CVS updates to v1.2.3
[ssh.h atomicio.c]
- int atomicio -> ssize_t (for alpha). ok deraadt@
[auth-rsa.c]
- delay MD5 computation until client sends response, free() early, cleanup.
[cipher.c]
- void* -> unsigned char*, ok niels@
[hostfile.c]
- remove unused variable 'len'. fix comments.
- remove unused variable
[log-client.c log-server.c]
- rename a cpp symbol, to avoid param.h collision
[packet.c]
- missing xfree()
- getsockname() requires initialized tolen; andy@guildsoftware.com
- use getpeername() in packet_connection_is_on_socket(), fixes sshd -i;
from Holger.Trapp@Informatik.TU-Chemnitz.DE
[pty.c pty.h]
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
[readconf.c]
- turn off x11-fwd for the client, too.
[rsa.c]
- PKCS#1 padding
[scp.c]
- allow '.' in usernames; from jedgar@fxp.org
[servconf.c]
- typo: ignore_user_known_hosts int->flag; naddy@mips.rhein-neckar.de
- sync with sshd_config
[ssh-keygen.c]
- enable ssh-keygen -l -f ~/.ssh/known_hosts, ok deraadt@
[ssh.1]
- Change invalid 'CHAT' loglevel to 'VERBOSE'
[ssh.c]
- suppress AAAA query host when '-4' is used; from shin@nd.net.fujitsu.co.jp
- turn off x11-fwd for the client, too.
[sshconnect.c]
- missing xfree()
- retry rresvport_af(), too. from sumikawa@ebina.hitachi.co.jp.
- read error vs. "Connection closed by remote host"
[sshd.8]
- ie. -> i.e.,
- do not link to a commercial page..
- sync with sshd_config
[sshd.c]
- no need for poll.h; from bright@wintelcom.net
- log with level log() not fatal() if peer behaves badly.
- don't panic if client behaves strange. ok deraadt@
- make no-port-forwarding for RSA keys deny both -L and -R style fwding
- delay close() of pty until the pty has been chowned back to root
- oops, fix comment, too.
- missing xfree()
- move XAUTHORITY to subdir. ok dugsong@. fixes debian bug #57907, too.
(http://cgi.debian.org/cgi-bin/bugreport.cgi?archive=no&bug=57907)
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
- create x11 cookie file
- fix pr 1113, fclose() -> pclose(), todo: remote popen()
- version 1.2.3
- Cleaned up
2000-03-09 11:27:49 +01:00
|
|
|
FILE *f;
|
2000-04-29 15:57:08 +02:00
|
|
|
Key *public;
|
- grunk@cvs.openbsd.org 2008/06/11 21:01:35
[ssh_config.5 key.h readconf.c readconf.h ssh-keygen.1 ssh-keygen.c key.c
sshconnect.c]
Introduce SSH Fingerprint ASCII Visualization, a technique inspired by the
graphical hash visualization schemes known as "random art", and by
Dan Kaminsky's musings on the subject during a BlackOp talk at the
23C3 in Berlin.
Scientific publication (original paper):
"Hash Visualization: a New Technique to improve Real-World Security",
Perrig A. and Song D., 1999, International Workshop on Cryptographic
Techniques and E-Commerce (CrypTEC '99)
http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
The algorithm used here is a worm crawling over a discrete plane,
leaving a trace (augmenting the field) everywhere it goes.
Movement is taken from dgst_raw 2bit-wise. Bumping into walls
makes the respective movement vector be ignored for this turn,
thus switching to the other color of the chessboard.
Graphs are not unambiguous for now, because circles in graphs can be
walked in either direction.
discussions with several people,
help, corrections and ok markus@ djm@
2008-06-12 20:40:35 +02:00
|
|
|
char *comment = NULL, *cp, *ep, line[16*1024], *fp, *ra;
|
2008-02-10 12:24:55 +01:00
|
|
|
int i, skip = 0, num = 0, invalid = 1;
|
2001-12-06 17:32:47 +01:00
|
|
|
enum fp_rep rep;
|
|
|
|
enum fp_type fptype;
|
1999-11-24 14:26:21 +01:00
|
|
|
struct stat st;
|
|
|
|
|
2001-03-26 15:44:06 +02:00
|
|
|
fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5;
|
|
|
|
rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX;
|
2001-03-12 04:02:17 +01:00
|
|
|
|
1999-11-24 14:26:21 +01:00
|
|
|
if (!have_identity)
|
|
|
|
ask_filename(pw, "Enter file in which the key is");
|
|
|
|
if (stat(identity_file, &st) < 0) {
|
|
|
|
perror(identity_file);
|
|
|
|
exit(1);
|
|
|
|
}
|
2001-03-26 15:44:06 +02:00
|
|
|
public = key_load_public(identity_file, &comment);
|
|
|
|
if (public != NULL) {
|
|
|
|
fp = key_fingerprint(public, fptype, rep);
|
2009-10-06 23:45:48 +02:00
|
|
|
ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART);
|
2008-06-13 00:57:27 +02:00
|
|
|
printf("%u %s %s (%s)\n", key_size(public), fp, comment,
|
|
|
|
key_type(public));
|
2008-06-12 20:43:15 +02:00
|
|
|
if (log_level >= SYSLOG_LEVEL_VERBOSE)
|
|
|
|
printf("%s\n", ra);
|
2000-04-29 15:57:08 +02:00
|
|
|
key_free(public);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(comment);
|
|
|
|
free(ra);
|
|
|
|
free(fp);
|
- OpenBSD CVS updates to v1.2.3
[ssh.h atomicio.c]
- int atomicio -> ssize_t (for alpha). ok deraadt@
[auth-rsa.c]
- delay MD5 computation until client sends response, free() early, cleanup.
[cipher.c]
- void* -> unsigned char*, ok niels@
[hostfile.c]
- remove unused variable 'len'. fix comments.
- remove unused variable
[log-client.c log-server.c]
- rename a cpp symbol, to avoid param.h collision
[packet.c]
- missing xfree()
- getsockname() requires initialized tolen; andy@guildsoftware.com
- use getpeername() in packet_connection_is_on_socket(), fixes sshd -i;
from Holger.Trapp@Informatik.TU-Chemnitz.DE
[pty.c pty.h]
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
[readconf.c]
- turn off x11-fwd for the client, too.
[rsa.c]
- PKCS#1 padding
[scp.c]
- allow '.' in usernames; from jedgar@fxp.org
[servconf.c]
- typo: ignore_user_known_hosts int->flag; naddy@mips.rhein-neckar.de
- sync with sshd_config
[ssh-keygen.c]
- enable ssh-keygen -l -f ~/.ssh/known_hosts, ok deraadt@
[ssh.1]
- Change invalid 'CHAT' loglevel to 'VERBOSE'
[ssh.c]
- suppress AAAA query host when '-4' is used; from shin@nd.net.fujitsu.co.jp
- turn off x11-fwd for the client, too.
[sshconnect.c]
- missing xfree()
- retry rresvport_af(), too. from sumikawa@ebina.hitachi.co.jp.
- read error vs. "Connection closed by remote host"
[sshd.8]
- ie. -> i.e.,
- do not link to a commercial page..
- sync with sshd_config
[sshd.c]
- no need for poll.h; from bright@wintelcom.net
- log with level log() not fatal() if peer behaves badly.
- don't panic if client behaves strange. ok deraadt@
- make no-port-forwarding for RSA keys deny both -L and -R style fwding
- delay close() of pty until the pty has been chowned back to root
- oops, fix comment, too.
- missing xfree()
- move XAUTHORITY to subdir. ok dugsong@. fixes debian bug #57907, too.
(http://cgi.debian.org/cgi-bin/bugreport.cgi?archive=no&bug=57907)
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
- create x11 cookie file
- fix pr 1113, fclose() -> pclose(), todo: remote popen()
- version 1.2.3
- Cleaned up
2000-03-09 11:27:49 +01:00
|
|
|
exit(0);
|
|
|
|
}
|
2006-06-13 05:00:25 +02:00
|
|
|
if (comment) {
|
2013-06-01 23:31:17 +02:00
|
|
|
free(comment);
|
2006-06-13 05:00:25 +02:00
|
|
|
comment = NULL;
|
|
|
|
}
|
- OpenBSD CVS updates to v1.2.3
[ssh.h atomicio.c]
- int atomicio -> ssize_t (for alpha). ok deraadt@
[auth-rsa.c]
- delay MD5 computation until client sends response, free() early, cleanup.
[cipher.c]
- void* -> unsigned char*, ok niels@
[hostfile.c]
- remove unused variable 'len'. fix comments.
- remove unused variable
[log-client.c log-server.c]
- rename a cpp symbol, to avoid param.h collision
[packet.c]
- missing xfree()
- getsockname() requires initialized tolen; andy@guildsoftware.com
- use getpeername() in packet_connection_is_on_socket(), fixes sshd -i;
from Holger.Trapp@Informatik.TU-Chemnitz.DE
[pty.c pty.h]
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
[readconf.c]
- turn off x11-fwd for the client, too.
[rsa.c]
- PKCS#1 padding
[scp.c]
- allow '.' in usernames; from jedgar@fxp.org
[servconf.c]
- typo: ignore_user_known_hosts int->flag; naddy@mips.rhein-neckar.de
- sync with sshd_config
[ssh-keygen.c]
- enable ssh-keygen -l -f ~/.ssh/known_hosts, ok deraadt@
[ssh.1]
- Change invalid 'CHAT' loglevel to 'VERBOSE'
[ssh.c]
- suppress AAAA query host when '-4' is used; from shin@nd.net.fujitsu.co.jp
- turn off x11-fwd for the client, too.
[sshconnect.c]
- missing xfree()
- retry rresvport_af(), too. from sumikawa@ebina.hitachi.co.jp.
- read error vs. "Connection closed by remote host"
[sshd.8]
- ie. -> i.e.,
- do not link to a commercial page..
- sync with sshd_config
[sshd.c]
- no need for poll.h; from bright@wintelcom.net
- log with level log() not fatal() if peer behaves badly.
- don't panic if client behaves strange. ok deraadt@
- make no-port-forwarding for RSA keys deny both -L and -R style fwding
- delay close() of pty until the pty has been chowned back to root
- oops, fix comment, too.
- missing xfree()
- move XAUTHORITY to subdir. ok dugsong@. fixes debian bug #57907, too.
(http://cgi.debian.org/cgi-bin/bugreport.cgi?archive=no&bug=57907)
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
- create x11 cookie file
- fix pr 1113, fclose() -> pclose(), todo: remote popen()
- version 1.2.3
- Cleaned up
2000-03-09 11:27:49 +01:00
|
|
|
|
2010-06-26 01:39:07 +02:00
|
|
|
if ((f = fopen(identity_file, "r")) == NULL)
|
|
|
|
fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
|
- OpenBSD CVS updates to v1.2.3
[ssh.h atomicio.c]
- int atomicio -> ssize_t (for alpha). ok deraadt@
[auth-rsa.c]
- delay MD5 computation until client sends response, free() early, cleanup.
[cipher.c]
- void* -> unsigned char*, ok niels@
[hostfile.c]
- remove unused variable 'len'. fix comments.
- remove unused variable
[log-client.c log-server.c]
- rename a cpp symbol, to avoid param.h collision
[packet.c]
- missing xfree()
- getsockname() requires initialized tolen; andy@guildsoftware.com
- use getpeername() in packet_connection_is_on_socket(), fixes sshd -i;
from Holger.Trapp@Informatik.TU-Chemnitz.DE
[pty.c pty.h]
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
[readconf.c]
- turn off x11-fwd for the client, too.
[rsa.c]
- PKCS#1 padding
[scp.c]
- allow '.' in usernames; from jedgar@fxp.org
[servconf.c]
- typo: ignore_user_known_hosts int->flag; naddy@mips.rhein-neckar.de
- sync with sshd_config
[ssh-keygen.c]
- enable ssh-keygen -l -f ~/.ssh/known_hosts, ok deraadt@
[ssh.1]
- Change invalid 'CHAT' loglevel to 'VERBOSE'
[ssh.c]
- suppress AAAA query host when '-4' is used; from shin@nd.net.fujitsu.co.jp
- turn off x11-fwd for the client, too.
[sshconnect.c]
- missing xfree()
- retry rresvport_af(), too. from sumikawa@ebina.hitachi.co.jp.
- read error vs. "Connection closed by remote host"
[sshd.8]
- ie. -> i.e.,
- do not link to a commercial page..
- sync with sshd_config
[sshd.c]
- no need for poll.h; from bright@wintelcom.net
- log with level log() not fatal() if peer behaves badly.
- don't panic if client behaves strange. ok deraadt@
- make no-port-forwarding for RSA keys deny both -L and -R style fwding
- delay close() of pty until the pty has been chowned back to root
- oops, fix comment, too.
- missing xfree()
- move XAUTHORITY to subdir. ok dugsong@. fixes debian bug #57907, too.
(http://cgi.debian.org/cgi-bin/bugreport.cgi?archive=no&bug=57907)
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
- create x11 cookie file
- fix pr 1113, fclose() -> pclose(), todo: remote popen()
- version 1.2.3
- Cleaned up
2000-03-09 11:27:49 +01:00
|
|
|
|
2010-06-26 01:39:07 +02:00
|
|
|
while (fgets(line, sizeof(line), f)) {
|
|
|
|
if ((cp = strchr(line, '\n')) == NULL) {
|
|
|
|
error("line %d too long: %.40s...",
|
|
|
|
num + 1, line);
|
|
|
|
skip = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
num++;
|
|
|
|
if (skip) {
|
|
|
|
skip = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*cp = '\0';
|
|
|
|
|
|
|
|
/* Skip leading whitespace, empty and comment lines. */
|
|
|
|
for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
|
|
|
|
;
|
|
|
|
if (!*cp || *cp == '\n' || *cp == '#')
|
|
|
|
continue;
|
|
|
|
i = strtol(cp, &ep, 10);
|
|
|
|
if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) {
|
|
|
|
int quoted = 0;
|
|
|
|
comment = cp;
|
|
|
|
for (; *cp && (quoted || (*cp != ' ' &&
|
|
|
|
*cp != '\t')); cp++) {
|
|
|
|
if (*cp == '\\' && cp[1] == '"')
|
|
|
|
cp++; /* Skip both */
|
|
|
|
else if (*cp == '"')
|
|
|
|
quoted = !quoted;
|
- OpenBSD CVS updates to v1.2.3
[ssh.h atomicio.c]
- int atomicio -> ssize_t (for alpha). ok deraadt@
[auth-rsa.c]
- delay MD5 computation until client sends response, free() early, cleanup.
[cipher.c]
- void* -> unsigned char*, ok niels@
[hostfile.c]
- remove unused variable 'len'. fix comments.
- remove unused variable
[log-client.c log-server.c]
- rename a cpp symbol, to avoid param.h collision
[packet.c]
- missing xfree()
- getsockname() requires initialized tolen; andy@guildsoftware.com
- use getpeername() in packet_connection_is_on_socket(), fixes sshd -i;
from Holger.Trapp@Informatik.TU-Chemnitz.DE
[pty.c pty.h]
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
[readconf.c]
- turn off x11-fwd for the client, too.
[rsa.c]
- PKCS#1 padding
[scp.c]
- allow '.' in usernames; from jedgar@fxp.org
[servconf.c]
- typo: ignore_user_known_hosts int->flag; naddy@mips.rhein-neckar.de
- sync with sshd_config
[ssh-keygen.c]
- enable ssh-keygen -l -f ~/.ssh/known_hosts, ok deraadt@
[ssh.1]
- Change invalid 'CHAT' loglevel to 'VERBOSE'
[ssh.c]
- suppress AAAA query host when '-4' is used; from shin@nd.net.fujitsu.co.jp
- turn off x11-fwd for the client, too.
[sshconnect.c]
- missing xfree()
- retry rresvport_af(), too. from sumikawa@ebina.hitachi.co.jp.
- read error vs. "Connection closed by remote host"
[sshd.8]
- ie. -> i.e.,
- do not link to a commercial page..
- sync with sshd_config
[sshd.c]
- no need for poll.h; from bright@wintelcom.net
- log with level log() not fatal() if peer behaves badly.
- don't panic if client behaves strange. ok deraadt@
- make no-port-forwarding for RSA keys deny both -L and -R style fwding
- delay close() of pty until the pty has been chowned back to root
- oops, fix comment, too.
- missing xfree()
- move XAUTHORITY to subdir. ok dugsong@. fixes debian bug #57907, too.
(http://cgi.debian.org/cgi-bin/bugreport.cgi?archive=no&bug=57907)
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
- create x11 cookie file
- fix pr 1113, fclose() -> pclose(), todo: remote popen()
- version 1.2.3
- Cleaned up
2000-03-09 11:27:49 +01:00
|
|
|
}
|
2010-06-26 01:39:07 +02:00
|
|
|
if (!*cp)
|
|
|
|
continue;
|
|
|
|
*cp++ = '\0';
|
|
|
|
}
|
|
|
|
ep = cp;
|
|
|
|
public = key_new(KEY_RSA1);
|
|
|
|
if (key_read(public, &cp) != 1) {
|
|
|
|
cp = ep;
|
|
|
|
key_free(public);
|
|
|
|
public = key_new(KEY_UNSPEC);
|
2000-12-29 17:50:13 +01:00
|
|
|
if (key_read(public, &cp) != 1) {
|
|
|
|
key_free(public);
|
2010-06-26 01:39:07 +02:00
|
|
|
continue;
|
1999-11-24 14:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
2010-06-26 01:39:07 +02:00
|
|
|
comment = *cp ? cp : comment;
|
|
|
|
fp = key_fingerprint(public, fptype, rep);
|
|
|
|
ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART);
|
|
|
|
printf("%u %s %s (%s)\n", key_size(public), fp,
|
|
|
|
comment ? comment : "no comment", key_type(public));
|
|
|
|
if (log_level >= SYSLOG_LEVEL_VERBOSE)
|
|
|
|
printf("%s\n", ra);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(ra);
|
|
|
|
free(fp);
|
2010-06-26 01:39:07 +02:00
|
|
|
key_free(public);
|
|
|
|
invalid = 0;
|
- OpenBSD CVS updates to v1.2.3
[ssh.h atomicio.c]
- int atomicio -> ssize_t (for alpha). ok deraadt@
[auth-rsa.c]
- delay MD5 computation until client sends response, free() early, cleanup.
[cipher.c]
- void* -> unsigned char*, ok niels@
[hostfile.c]
- remove unused variable 'len'. fix comments.
- remove unused variable
[log-client.c log-server.c]
- rename a cpp symbol, to avoid param.h collision
[packet.c]
- missing xfree()
- getsockname() requires initialized tolen; andy@guildsoftware.com
- use getpeername() in packet_connection_is_on_socket(), fixes sshd -i;
from Holger.Trapp@Informatik.TU-Chemnitz.DE
[pty.c pty.h]
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
[readconf.c]
- turn off x11-fwd for the client, too.
[rsa.c]
- PKCS#1 padding
[scp.c]
- allow '.' in usernames; from jedgar@fxp.org
[servconf.c]
- typo: ignore_user_known_hosts int->flag; naddy@mips.rhein-neckar.de
- sync with sshd_config
[ssh-keygen.c]
- enable ssh-keygen -l -f ~/.ssh/known_hosts, ok deraadt@
[ssh.1]
- Change invalid 'CHAT' loglevel to 'VERBOSE'
[ssh.c]
- suppress AAAA query host when '-4' is used; from shin@nd.net.fujitsu.co.jp
- turn off x11-fwd for the client, too.
[sshconnect.c]
- missing xfree()
- retry rresvport_af(), too. from sumikawa@ebina.hitachi.co.jp.
- read error vs. "Connection closed by remote host"
[sshd.8]
- ie. -> i.e.,
- do not link to a commercial page..
- sync with sshd_config
[sshd.c]
- no need for poll.h; from bright@wintelcom.net
- log with level log() not fatal() if peer behaves badly.
- don't panic if client behaves strange. ok deraadt@
- make no-port-forwarding for RSA keys deny both -L and -R style fwding
- delay close() of pty until the pty has been chowned back to root
- oops, fix comment, too.
- missing xfree()
- move XAUTHORITY to subdir. ok dugsong@. fixes debian bug #57907, too.
(http://cgi.debian.org/cgi-bin/bugreport.cgi?archive=no&bug=57907)
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
- create x11 cookie file
- fix pr 1113, fclose() -> pclose(), todo: remote popen()
- version 1.2.3
- Cleaned up
2000-03-09 11:27:49 +01:00
|
|
|
}
|
2010-06-26 01:39:07 +02:00
|
|
|
fclose(f);
|
|
|
|
|
- OpenBSD CVS updates to v1.2.3
[ssh.h atomicio.c]
- int atomicio -> ssize_t (for alpha). ok deraadt@
[auth-rsa.c]
- delay MD5 computation until client sends response, free() early, cleanup.
[cipher.c]
- void* -> unsigned char*, ok niels@
[hostfile.c]
- remove unused variable 'len'. fix comments.
- remove unused variable
[log-client.c log-server.c]
- rename a cpp symbol, to avoid param.h collision
[packet.c]
- missing xfree()
- getsockname() requires initialized tolen; andy@guildsoftware.com
- use getpeername() in packet_connection_is_on_socket(), fixes sshd -i;
from Holger.Trapp@Informatik.TU-Chemnitz.DE
[pty.c pty.h]
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
[readconf.c]
- turn off x11-fwd for the client, too.
[rsa.c]
- PKCS#1 padding
[scp.c]
- allow '.' in usernames; from jedgar@fxp.org
[servconf.c]
- typo: ignore_user_known_hosts int->flag; naddy@mips.rhein-neckar.de
- sync with sshd_config
[ssh-keygen.c]
- enable ssh-keygen -l -f ~/.ssh/known_hosts, ok deraadt@
[ssh.1]
- Change invalid 'CHAT' loglevel to 'VERBOSE'
[ssh.c]
- suppress AAAA query host when '-4' is used; from shin@nd.net.fujitsu.co.jp
- turn off x11-fwd for the client, too.
[sshconnect.c]
- missing xfree()
- retry rresvport_af(), too. from sumikawa@ebina.hitachi.co.jp.
- read error vs. "Connection closed by remote host"
[sshd.8]
- ie. -> i.e.,
- do not link to a commercial page..
- sync with sshd_config
[sshd.c]
- no need for poll.h; from bright@wintelcom.net
- log with level log() not fatal() if peer behaves badly.
- don't panic if client behaves strange. ok deraadt@
- make no-port-forwarding for RSA keys deny both -L and -R style fwding
- delay close() of pty until the pty has been chowned back to root
- oops, fix comment, too.
- missing xfree()
- move XAUTHORITY to subdir. ok dugsong@. fixes debian bug #57907, too.
(http://cgi.debian.org/cgi-bin/bugreport.cgi?archive=no&bug=57907)
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
- create x11 cookie file
- fix pr 1113, fclose() -> pclose(), todo: remote popen()
- version 1.2.3
- Cleaned up
2000-03-09 11:27:49 +01:00
|
|
|
if (invalid) {
|
2001-11-12 00:52:44 +01:00
|
|
|
printf("%s is not a public key file.\n", identity_file);
|
- OpenBSD CVS updates to v1.2.3
[ssh.h atomicio.c]
- int atomicio -> ssize_t (for alpha). ok deraadt@
[auth-rsa.c]
- delay MD5 computation until client sends response, free() early, cleanup.
[cipher.c]
- void* -> unsigned char*, ok niels@
[hostfile.c]
- remove unused variable 'len'. fix comments.
- remove unused variable
[log-client.c log-server.c]
- rename a cpp symbol, to avoid param.h collision
[packet.c]
- missing xfree()
- getsockname() requires initialized tolen; andy@guildsoftware.com
- use getpeername() in packet_connection_is_on_socket(), fixes sshd -i;
from Holger.Trapp@Informatik.TU-Chemnitz.DE
[pty.c pty.h]
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
[readconf.c]
- turn off x11-fwd for the client, too.
[rsa.c]
- PKCS#1 padding
[scp.c]
- allow '.' in usernames; from jedgar@fxp.org
[servconf.c]
- typo: ignore_user_known_hosts int->flag; naddy@mips.rhein-neckar.de
- sync with sshd_config
[ssh-keygen.c]
- enable ssh-keygen -l -f ~/.ssh/known_hosts, ok deraadt@
[ssh.1]
- Change invalid 'CHAT' loglevel to 'VERBOSE'
[ssh.c]
- suppress AAAA query host when '-4' is used; from shin@nd.net.fujitsu.co.jp
- turn off x11-fwd for the client, too.
[sshconnect.c]
- missing xfree()
- retry rresvport_af(), too. from sumikawa@ebina.hitachi.co.jp.
- read error vs. "Connection closed by remote host"
[sshd.8]
- ie. -> i.e.,
- do not link to a commercial page..
- sync with sshd_config
[sshd.c]
- no need for poll.h; from bright@wintelcom.net
- log with level log() not fatal() if peer behaves badly.
- don't panic if client behaves strange. ok deraadt@
- make no-port-forwarding for RSA keys deny both -L and -R style fwding
- delay close() of pty until the pty has been chowned back to root
- oops, fix comment, too.
- missing xfree()
- move XAUTHORITY to subdir. ok dugsong@. fixes debian bug #57907, too.
(http://cgi.debian.org/cgi-bin/bugreport.cgi?archive=no&bug=57907)
- register cleanup for pty earlier. move code for pty-owner handling to
pty.c ok provos@, dugsong@
- create x11 cookie file
- fix pr 1113, fclose() -> pclose(), todo: remote popen()
- version 1.2.3
- Cleaned up
2000-03-09 11:27:49 +01:00
|
|
|
exit(1);
|
1999-11-24 14:26:21 +01:00
|
|
|
}
|
|
|
|
exit(0);
|
1999-11-17 07:29:08 +01:00
|
|
|
}
|
|
|
|
|
2011-05-05 06:06:15 +02:00
|
|
|
static void
|
|
|
|
do_gen_all_hostkeys(struct passwd *pw)
|
|
|
|
{
|
|
|
|
struct {
|
|
|
|
char *key_type;
|
|
|
|
char *key_type_display;
|
|
|
|
char *path;
|
|
|
|
} key_types[] = {
|
|
|
|
{ "rsa1", "RSA1", _PATH_HOST_KEY_FILE },
|
|
|
|
{ "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
|
|
|
|
{ "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE },
|
2012-02-05 21:41:27 +01:00
|
|
|
#ifdef OPENSSL_HAS_ECC
|
2011-05-05 06:06:15 +02:00
|
|
|
{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
|
2012-02-05 21:41:27 +01:00
|
|
|
#endif
|
2013-12-07 01:24:01 +01:00
|
|
|
{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
|
2011-05-05 06:06:15 +02:00
|
|
|
{ NULL, NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
int first = 0;
|
|
|
|
struct stat st;
|
|
|
|
Key *private, *public;
|
|
|
|
char comment[1024];
|
|
|
|
int i, type, fd;
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
for (i = 0; key_types[i].key_type; i++) {
|
|
|
|
if (stat(key_types[i].path, &st) == 0)
|
|
|
|
continue;
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
printf("Could not stat %s: %s", key_types[i].path,
|
|
|
|
strerror(errno));
|
|
|
|
first = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (first == 0) {
|
|
|
|
first = 1;
|
|
|
|
printf("%s: generating new host keys: ", __progname);
|
|
|
|
}
|
|
|
|
printf("%s ", key_types[i].key_type_display);
|
|
|
|
fflush(stdout);
|
|
|
|
type = key_type_from_name(key_types[i].key_type);
|
|
|
|
strlcpy(identity_file, key_types[i].path, sizeof(identity_file));
|
|
|
|
bits = 0;
|
|
|
|
type_bits_valid(type, &bits);
|
|
|
|
private = key_generate(type, bits);
|
|
|
|
if (private == NULL) {
|
|
|
|
fprintf(stderr, "key_generate failed\n");
|
|
|
|
first = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
public = key_from_private(private);
|
|
|
|
snprintf(comment, sizeof comment, "%s@%s", pw->pw_name,
|
|
|
|
hostname);
|
2013-12-07 00:41:55 +01:00
|
|
|
if (!key_save_private(private, identity_file, "", comment,
|
|
|
|
use_new_format, new_format_cipher, rounds)) {
|
2011-05-05 06:06:15 +02:00
|
|
|
printf("Saving the key failed: %s.\n", identity_file);
|
|
|
|
key_free(private);
|
|
|
|
key_free(public);
|
|
|
|
first = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
key_free(private);
|
|
|
|
strlcat(identity_file, ".pub", sizeof(identity_file));
|
|
|
|
fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
|
|
if (fd == -1) {
|
|
|
|
printf("Could not save your public key in %s\n",
|
|
|
|
identity_file);
|
|
|
|
key_free(public);
|
|
|
|
first = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
f = fdopen(fd, "w");
|
|
|
|
if (f == NULL) {
|
|
|
|
printf("fdopen %s failed\n", identity_file);
|
|
|
|
key_free(public);
|
|
|
|
first = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!key_write(public, f)) {
|
|
|
|
fprintf(stderr, "write key failed\n");
|
|
|
|
key_free(public);
|
|
|
|
first = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
fprintf(f, " %s\n", comment);
|
|
|
|
fclose(f);
|
|
|
|
key_free(public);
|
|
|
|
|
|
|
|
}
|
|
|
|
if (first != 0)
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2005-03-01 11:48:35 +01:00
|
|
|
static void
|
2010-02-26 21:55:05 +01:00
|
|
|
printhost(FILE *f, const char *name, Key *public, int ca, int hash)
|
2005-03-01 11:48:35 +01:00
|
|
|
{
|
2008-06-08 04:54:29 +02:00
|
|
|
if (print_fingerprint) {
|
|
|
|
enum fp_rep rep;
|
|
|
|
enum fp_type fptype;
|
- grunk@cvs.openbsd.org 2008/06/11 21:01:35
[ssh_config.5 key.h readconf.c readconf.h ssh-keygen.1 ssh-keygen.c key.c
sshconnect.c]
Introduce SSH Fingerprint ASCII Visualization, a technique inspired by the
graphical hash visualization schemes known as "random art", and by
Dan Kaminsky's musings on the subject during a BlackOp talk at the
23C3 in Berlin.
Scientific publication (original paper):
"Hash Visualization: a New Technique to improve Real-World Security",
Perrig A. and Song D., 1999, International Workshop on Cryptographic
Techniques and E-Commerce (CrypTEC '99)
http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
The algorithm used here is a worm crawling over a discrete plane,
leaving a trace (augmenting the field) everywhere it goes.
Movement is taken from dgst_raw 2bit-wise. Bumping into walls
makes the respective movement vector be ignored for this turn,
thus switching to the other color of the chessboard.
Graphs are not unambiguous for now, because circles in graphs can be
walked in either direction.
discussions with several people,
help, corrections and ok markus@ djm@
2008-06-12 20:40:35 +02:00
|
|
|
char *fp, *ra;
|
2008-06-08 04:54:29 +02:00
|
|
|
|
|
|
|
fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5;
|
|
|
|
rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX;
|
|
|
|
fp = key_fingerprint(public, fptype, rep);
|
2009-10-06 23:45:48 +02:00
|
|
|
ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART);
|
2008-07-14 03:28:29 +02:00
|
|
|
printf("%u %s %s (%s)\n", key_size(public), fp, name,
|
|
|
|
key_type(public));
|
|
|
|
if (log_level >= SYSLOG_LEVEL_VERBOSE)
|
|
|
|
printf("%s\n", ra);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(ra);
|
|
|
|
free(fp);
|
2008-06-08 04:54:29 +02:00
|
|
|
} else {
|
|
|
|
if (hash && (name = host_hash(name, NULL, 0)) == NULL)
|
|
|
|
fatal("hash_host failed");
|
2010-02-26 21:55:05 +01:00
|
|
|
fprintf(f, "%s%s%s ", ca ? CA_MARKER : "", ca ? " " : "", name);
|
2008-06-08 04:54:29 +02:00
|
|
|
if (!key_write(public, f))
|
|
|
|
fatal("key_write failed");
|
|
|
|
fprintf(f, "\n");
|
|
|
|
}
|
2005-03-01 11:48:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
do_known_hosts(struct passwd *pw, const char *name)
|
|
|
|
{
|
|
|
|
FILE *in, *out = stdout;
|
2010-02-26 21:55:05 +01:00
|
|
|
Key *pub;
|
2005-03-01 11:48:35 +01:00
|
|
|
char *cp, *cp2, *kp, *kp2;
|
|
|
|
char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN];
|
2008-02-10 12:24:55 +01:00
|
|
|
int c, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0;
|
2010-02-26 21:55:05 +01:00
|
|
|
int ca;
|
2013-09-14 01:45:03 +02:00
|
|
|
int found_key = 0;
|
2005-03-01 11:48:35 +01:00
|
|
|
|
|
|
|
if (!have_identity) {
|
|
|
|
cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
|
|
|
|
if (strlcpy(identity_file, cp, sizeof(identity_file)) >=
|
|
|
|
sizeof(identity_file))
|
|
|
|
fatal("Specified known hosts path too long");
|
2013-06-01 23:31:17 +02:00
|
|
|
free(cp);
|
2005-03-01 11:48:35 +01:00
|
|
|
have_identity = 1;
|
|
|
|
}
|
|
|
|
if ((in = fopen(identity_file, "r")) == NULL)
|
2010-06-26 01:39:07 +02:00
|
|
|
fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
|
2005-03-01 11:48:35 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Find hosts goes to stdout, hash and deletions happen in-place
|
|
|
|
* A corner case is ssh-keygen -HF foo, which should go to stdout
|
|
|
|
*/
|
|
|
|
if (!find_host && (hash_hosts || delete_host)) {
|
|
|
|
if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) ||
|
|
|
|
strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) ||
|
|
|
|
strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) ||
|
|
|
|
strlcat(old, ".old", sizeof(old)) >= sizeof(old))
|
|
|
|
fatal("known_hosts path too long");
|
|
|
|
umask(077);
|
|
|
|
if ((c = mkstemp(tmp)) == -1)
|
|
|
|
fatal("mkstemp: %s", strerror(errno));
|
|
|
|
if ((out = fdopen(c, "w")) == NULL) {
|
|
|
|
c = errno;
|
|
|
|
unlink(tmp);
|
|
|
|
fatal("fdopen: %s", strerror(c));
|
|
|
|
}
|
|
|
|
inplace = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fgets(line, sizeof(line), in)) {
|
2007-10-26 06:26:32 +02:00
|
|
|
if ((cp = strchr(line, '\n')) == NULL) {
|
2008-02-10 12:24:55 +01:00
|
|
|
error("line %d too long: %.40s...", num + 1, line);
|
2005-03-01 11:48:35 +01:00
|
|
|
skip = 1;
|
|
|
|
invalid = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2007-10-26 06:26:32 +02:00
|
|
|
num++;
|
2005-03-01 11:48:35 +01:00
|
|
|
if (skip) {
|
|
|
|
skip = 0;
|
|
|
|
continue;
|
|
|
|
}
|
2007-10-26 06:26:32 +02:00
|
|
|
*cp = '\0';
|
2005-03-01 11:48:35 +01:00
|
|
|
|
|
|
|
/* Skip leading whitespace, empty and comment lines. */
|
|
|
|
for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
|
|
|
|
;
|
|
|
|
if (!*cp || *cp == '\n' || *cp == '#') {
|
|
|
|
if (inplace)
|
|
|
|
fprintf(out, "%s\n", cp);
|
|
|
|
continue;
|
|
|
|
}
|
2010-02-26 21:55:05 +01:00
|
|
|
/* Check whether this is a CA key */
|
|
|
|
if (strncasecmp(cp, CA_MARKER, sizeof(CA_MARKER) - 1) == 0 &&
|
|
|
|
(cp[sizeof(CA_MARKER) - 1] == ' ' ||
|
|
|
|
cp[sizeof(CA_MARKER) - 1] == '\t')) {
|
|
|
|
ca = 1;
|
|
|
|
cp += sizeof(CA_MARKER);
|
|
|
|
} else
|
|
|
|
ca = 0;
|
|
|
|
|
2005-03-01 11:48:35 +01:00
|
|
|
/* Find the end of the host name portion. */
|
|
|
|
for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++)
|
|
|
|
;
|
2010-02-26 21:55:05 +01:00
|
|
|
|
2005-03-01 11:48:35 +01:00
|
|
|
if (*kp == '\0' || *(kp + 1) == '\0') {
|
|
|
|
error("line %d missing key: %.40s...",
|
|
|
|
num, line);
|
|
|
|
invalid = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*kp++ = '\0';
|
|
|
|
kp2 = kp;
|
|
|
|
|
2010-02-26 21:55:05 +01:00
|
|
|
pub = key_new(KEY_RSA1);
|
|
|
|
if (key_read(pub, &kp) != 1) {
|
2005-03-01 11:48:35 +01:00
|
|
|
kp = kp2;
|
2010-02-26 21:55:05 +01:00
|
|
|
key_free(pub);
|
|
|
|
pub = key_new(KEY_UNSPEC);
|
|
|
|
if (key_read(pub, &kp) != 1) {
|
2005-03-01 11:48:35 +01:00
|
|
|
error("line %d invalid key: %.40s...",
|
|
|
|
num, line);
|
2010-02-26 21:55:05 +01:00
|
|
|
key_free(pub);
|
2005-03-01 11:48:35 +01:00
|
|
|
invalid = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*cp == HASH_DELIM) {
|
|
|
|
if (find_host || delete_host) {
|
|
|
|
cp2 = host_hash(name, cp, strlen(cp));
|
|
|
|
if (cp2 == NULL) {
|
|
|
|
error("line %d: invalid hashed "
|
|
|
|
"name: %.64s...", num, line);
|
|
|
|
invalid = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
c = (strcmp(cp2, cp) == 0);
|
|
|
|
if (find_host && c) {
|
2013-09-14 01:45:03 +02:00
|
|
|
if (!quiet)
|
|
|
|
printf("# Host %s found: "
|
|
|
|
"line %d type %s%s\n", name,
|
|
|
|
num, key_type(pub),
|
|
|
|
ca ? " (CA key)" : "");
|
2010-02-26 21:55:05 +01:00
|
|
|
printhost(out, cp, pub, ca, 0);
|
2013-09-14 01:45:03 +02:00
|
|
|
found_key = 1;
|
2005-03-01 11:48:35 +01:00
|
|
|
}
|
2012-09-06 13:20:39 +02:00
|
|
|
if (delete_host) {
|
|
|
|
if (!c && !ca)
|
|
|
|
printhost(out, cp, pub, ca, 0);
|
|
|
|
else
|
|
|
|
printf("# Host %s found: "
|
|
|
|
"line %d type %s\n", name,
|
|
|
|
num, key_type(pub));
|
|
|
|
}
|
2005-03-01 11:48:35 +01:00
|
|
|
} else if (hash_hosts)
|
2010-02-26 21:55:05 +01:00
|
|
|
printhost(out, cp, pub, ca, 0);
|
2005-03-01 11:48:35 +01:00
|
|
|
} else {
|
|
|
|
if (find_host || delete_host) {
|
|
|
|
c = (match_hostname(name, cp,
|
|
|
|
strlen(cp)) == 1);
|
|
|
|
if (find_host && c) {
|
2013-09-14 01:45:03 +02:00
|
|
|
if (!quiet)
|
|
|
|
printf("# Host %s found: "
|
|
|
|
"line %d type %s%s\n", name,
|
|
|
|
num, key_type(pub),
|
|
|
|
ca ? " (CA key)" : "");
|
2010-02-26 21:55:05 +01:00
|
|
|
printhost(out, name, pub,
|
|
|
|
ca, hash_hosts && !ca);
|
2013-09-14 01:45:03 +02:00
|
|
|
found_key = 1;
|
2005-03-01 11:48:35 +01:00
|
|
|
}
|
2012-09-06 13:20:39 +02:00
|
|
|
if (delete_host) {
|
|
|
|
if (!c && !ca)
|
|
|
|
printhost(out, cp, pub, ca, 0);
|
|
|
|
else
|
|
|
|
printf("# Host %s found: "
|
|
|
|
"line %d type %s\n", name,
|
|
|
|
num, key_type(pub));
|
|
|
|
}
|
2005-03-01 11:48:35 +01:00
|
|
|
} else if (hash_hosts) {
|
2005-03-14 13:08:12 +01:00
|
|
|
for (cp2 = strsep(&cp, ",");
|
2005-03-01 11:48:35 +01:00
|
|
|
cp2 != NULL && *cp2 != '\0';
|
2005-03-02 02:33:04 +01:00
|
|
|
cp2 = strsep(&cp, ",")) {
|
2010-02-26 21:55:05 +01:00
|
|
|
if (ca) {
|
|
|
|
fprintf(stderr, "Warning: "
|
|
|
|
"ignoring CA key for host: "
|
|
|
|
"%.64s\n", cp2);
|
|
|
|
printhost(out, cp2, pub, ca, 0);
|
|
|
|
} else if (strcspn(cp2, "*?!") !=
|
|
|
|
strlen(cp2)) {
|
2005-03-02 02:33:04 +01:00
|
|
|
fprintf(stderr, "Warning: "
|
|
|
|
"ignoring host name with "
|
|
|
|
"metacharacters: %.64s\n",
|
|
|
|
cp2);
|
2010-02-26 21:55:05 +01:00
|
|
|
printhost(out, cp2, pub, ca, 0);
|
|
|
|
} else
|
|
|
|
printhost(out, cp2, pub, ca, 1);
|
2005-03-02 02:33:04 +01:00
|
|
|
}
|
2005-03-01 11:48:35 +01:00
|
|
|
has_unhashed = 1;
|
|
|
|
}
|
|
|
|
}
|
2010-02-26 21:55:05 +01:00
|
|
|
key_free(pub);
|
2005-03-01 11:48:35 +01:00
|
|
|
}
|
|
|
|
fclose(in);
|
|
|
|
|
|
|
|
if (invalid) {
|
2008-02-10 12:24:55 +01:00
|
|
|
fprintf(stderr, "%s is not a valid known_hosts file.\n",
|
2005-03-01 11:48:35 +01:00
|
|
|
identity_file);
|
|
|
|
if (inplace) {
|
|
|
|
fprintf(stderr, "Not replacing existing known_hosts "
|
2005-03-14 13:09:18 +01:00
|
|
|
"file because of errors\n");
|
2005-03-01 11:48:35 +01:00
|
|
|
fclose(out);
|
|
|
|
unlink(tmp);
|
|
|
|
}
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inplace) {
|
|
|
|
fclose(out);
|
|
|
|
|
|
|
|
/* Backup existing file */
|
|
|
|
if (unlink(old) == -1 && errno != ENOENT)
|
|
|
|
fatal("unlink %.100s: %s", old, strerror(errno));
|
|
|
|
if (link(identity_file, old) == -1)
|
|
|
|
fatal("link %.100s to %.100s: %s", identity_file, old,
|
|
|
|
strerror(errno));
|
|
|
|
/* Move new one into place */
|
|
|
|
if (rename(tmp, identity_file) == -1) {
|
|
|
|
error("rename\"%s\" to \"%s\": %s", tmp, identity_file,
|
|
|
|
strerror(errno));
|
|
|
|
unlink(tmp);
|
|
|
|
unlink(old);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "%s updated.\n", identity_file);
|
|
|
|
fprintf(stderr, "Original contents retained as %s\n", old);
|
|
|
|
if (has_unhashed) {
|
|
|
|
fprintf(stderr, "WARNING: %s contains unhashed "
|
|
|
|
"entries\n", old);
|
|
|
|
fprintf(stderr, "Delete this file to ensure privacy "
|
2005-07-17 09:22:45 +02:00
|
|
|
"of hostnames\n");
|
2005-03-01 11:48:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-14 01:45:03 +02:00
|
|
|
exit (find_host && !found_key);
|
2005-03-01 11:48:35 +01:00
|
|
|
}
|
|
|
|
|
1999-11-24 14:26:21 +01:00
|
|
|
/*
|
|
|
|
* Perform changing a passphrase. The argument is the passwd structure
|
|
|
|
* for the current user.
|
|
|
|
*/
|
2001-06-25 07:01:22 +02:00
|
|
|
static void
|
1999-11-17 07:29:08 +01:00
|
|
|
do_change_passphrase(struct passwd *pw)
|
|
|
|
{
|
1999-11-24 14:26:21 +01:00
|
|
|
char *comment;
|
|
|
|
char *old_passphrase, *passphrase1, *passphrase2;
|
|
|
|
struct stat st;
|
2000-04-29 15:57:08 +02:00
|
|
|
Key *private;
|
1999-11-24 14:26:21 +01:00
|
|
|
|
|
|
|
if (!have_identity)
|
|
|
|
ask_filename(pw, "Enter file in which the key is");
|
|
|
|
if (stat(identity_file, &st) < 0) {
|
|
|
|
perror(identity_file);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
/* Try to load the file with empty passphrase. */
|
2001-03-26 15:44:06 +02:00
|
|
|
private = key_load_private(identity_file, "", &comment);
|
|
|
|
if (private == NULL) {
|
1999-11-24 14:26:21 +01:00
|
|
|
if (identity_passphrase)
|
|
|
|
old_passphrase = xstrdup(identity_passphrase);
|
|
|
|
else
|
2001-06-25 07:20:31 +02:00
|
|
|
old_passphrase =
|
|
|
|
read_passphrase("Enter old passphrase: ",
|
|
|
|
RP_ALLOW_STDIN);
|
|
|
|
private = key_load_private(identity_file, old_passphrase,
|
|
|
|
&comment);
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(old_passphrase, strlen(old_passphrase));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(old_passphrase);
|
2001-03-26 15:44:06 +02:00
|
|
|
if (private == NULL) {
|
1999-11-24 14:26:21 +01:00
|
|
|
printf("Bad passphrase.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf("Key has comment '%s'\n", comment);
|
|
|
|
|
|
|
|
/* Ask the new passphrase (twice). */
|
|
|
|
if (identity_new_passphrase) {
|
|
|
|
passphrase1 = xstrdup(identity_new_passphrase);
|
|
|
|
passphrase2 = NULL;
|
|
|
|
} else {
|
|
|
|
passphrase1 =
|
2001-06-25 07:20:31 +02:00
|
|
|
read_passphrase("Enter new passphrase (empty for no "
|
|
|
|
"passphrase): ", RP_ALLOW_STDIN);
|
|
|
|
passphrase2 = read_passphrase("Enter same passphrase again: ",
|
2001-12-21 04:45:46 +01:00
|
|
|
RP_ALLOW_STDIN);
|
1999-11-24 14:26:21 +01:00
|
|
|
|
|
|
|
/* Verify that they are the same. */
|
|
|
|
if (strcmp(passphrase1, passphrase2) != 0) {
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase1, strlen(passphrase1));
|
|
|
|
explicit_bzero(passphrase2, strlen(passphrase2));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(passphrase1);
|
|
|
|
free(passphrase2);
|
1999-11-24 14:26:21 +01:00
|
|
|
printf("Pass phrases do not match. Try again.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
/* Destroy the other copy. */
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase2, strlen(passphrase2));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(passphrase2);
|
1999-10-27 05:42:43 +02:00
|
|
|
}
|
|
|
|
|
1999-11-24 14:26:21 +01:00
|
|
|
/* Save the file using the new passphrase. */
|
2013-12-07 00:41:55 +01:00
|
|
|
if (!key_save_private(private, identity_file, passphrase1, comment,
|
|
|
|
use_new_format, new_format_cipher, rounds)) {
|
2001-04-16 04:00:02 +02:00
|
|
|
printf("Saving the key failed: %s.\n", identity_file);
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase1, strlen(passphrase1));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(passphrase1);
|
2000-04-29 15:57:08 +02:00
|
|
|
key_free(private);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(comment);
|
1999-11-24 14:26:21 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
/* Destroy the passphrase and the copy of the key in memory. */
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase1, strlen(passphrase1));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(passphrase1);
|
2000-04-29 15:57:08 +02:00
|
|
|
key_free(private); /* Destroys contents */
|
2013-06-01 23:31:17 +02:00
|
|
|
free(comment);
|
1999-11-24 14:26:21 +01:00
|
|
|
|
|
|
|
printf("Your identification has been saved with the new passphrase.\n");
|
|
|
|
exit(0);
|
|
|
|
}
|
1999-10-27 05:42:43 +02:00
|
|
|
|
2003-05-15 02:19:46 +02:00
|
|
|
/*
|
|
|
|
* Print the SSHFP RR.
|
|
|
|
*/
|
2006-03-26 04:48:01 +02:00
|
|
|
static int
|
|
|
|
do_print_resource_record(struct passwd *pw, char *fname, char *hname)
|
2003-05-15 02:19:46 +02:00
|
|
|
{
|
|
|
|
Key *public;
|
|
|
|
char *comment = NULL;
|
|
|
|
struct stat st;
|
|
|
|
|
2006-03-26 04:48:01 +02:00
|
|
|
if (fname == NULL)
|
2013-07-18 08:13:37 +02:00
|
|
|
fatal("%s: no filename", __func__);
|
2006-03-26 04:48:01 +02:00
|
|
|
if (stat(fname, &st) < 0) {
|
|
|
|
if (errno == ENOENT)
|
|
|
|
return 0;
|
|
|
|
perror(fname);
|
2003-05-15 02:19:46 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
2006-03-26 04:48:01 +02:00
|
|
|
public = key_load_public(fname, &comment);
|
2003-05-15 02:19:46 +02:00
|
|
|
if (public != NULL) {
|
2004-06-22 04:56:01 +02:00
|
|
|
export_dns_rr(hname, public, stdout, print_generic);
|
2003-05-15 02:19:46 +02:00
|
|
|
key_free(public);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(comment);
|
2006-03-26 04:48:01 +02:00
|
|
|
return 1;
|
2003-05-15 02:19:46 +02:00
|
|
|
}
|
|
|
|
if (comment)
|
2013-06-01 23:31:17 +02:00
|
|
|
free(comment);
|
2003-05-15 02:19:46 +02:00
|
|
|
|
2006-03-26 04:48:01 +02:00
|
|
|
printf("failed to read v2 public key from %s.\n", fname);
|
2003-05-15 02:19:46 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
1999-11-24 14:26:21 +01:00
|
|
|
/*
|
|
|
|
* Change the comment of a private key file.
|
|
|
|
*/
|
2001-06-25 07:01:22 +02:00
|
|
|
static void
|
1999-10-27 05:42:43 +02:00
|
|
|
do_change_comment(struct passwd *pw)
|
|
|
|
{
|
2001-03-09 19:19:24 +01:00
|
|
|
char new_comment[1024], *comment, *passphrase;
|
2001-03-26 15:44:06 +02:00
|
|
|
Key *private;
|
|
|
|
Key *public;
|
1999-11-24 14:26:21 +01:00
|
|
|
struct stat st;
|
|
|
|
FILE *f;
|
2001-03-09 19:19:24 +01:00
|
|
|
int fd;
|
1999-11-24 14:26:21 +01:00
|
|
|
|
|
|
|
if (!have_identity)
|
|
|
|
ask_filename(pw, "Enter file in which the key is");
|
|
|
|
if (stat(identity_file, &st) < 0) {
|
|
|
|
perror(identity_file);
|
|
|
|
exit(1);
|
|
|
|
}
|
2001-03-26 15:44:06 +02:00
|
|
|
private = key_load_private(identity_file, "", &comment);
|
|
|
|
if (private == NULL) {
|
1999-11-24 14:26:21 +01:00
|
|
|
if (identity_passphrase)
|
|
|
|
passphrase = xstrdup(identity_passphrase);
|
|
|
|
else if (identity_new_passphrase)
|
|
|
|
passphrase = xstrdup(identity_new_passphrase);
|
|
|
|
else
|
2001-06-25 07:20:31 +02:00
|
|
|
passphrase = read_passphrase("Enter passphrase: ",
|
|
|
|
RP_ALLOW_STDIN);
|
1999-11-24 14:26:21 +01:00
|
|
|
/* Try to load using the passphrase. */
|
2001-03-26 15:44:06 +02:00
|
|
|
private = key_load_private(identity_file, passphrase, &comment);
|
|
|
|
if (private == NULL) {
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase, strlen(passphrase));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(passphrase);
|
1999-11-24 14:26:21 +01:00
|
|
|
printf("Bad passphrase.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2001-03-26 15:44:06 +02:00
|
|
|
} else {
|
|
|
|
passphrase = xstrdup("");
|
1999-11-24 14:26:21 +01:00
|
|
|
}
|
2001-03-26 15:44:06 +02:00
|
|
|
if (private->type != KEY_RSA1) {
|
|
|
|
fprintf(stderr, "Comments are only supported for RSA1 keys.\n");
|
|
|
|
key_free(private);
|
|
|
|
exit(1);
|
2001-12-21 04:45:46 +01:00
|
|
|
}
|
1999-11-24 14:26:21 +01:00
|
|
|
printf("Key now has comment '%s'\n", comment);
|
|
|
|
|
|
|
|
if (identity_comment) {
|
|
|
|
strlcpy(new_comment, identity_comment, sizeof(new_comment));
|
|
|
|
} else {
|
|
|
|
printf("Enter new comment: ");
|
|
|
|
fflush(stdout);
|
|
|
|
if (!fgets(new_comment, sizeof(new_comment), stdin)) {
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase, strlen(passphrase));
|
2000-04-29 15:57:08 +02:00
|
|
|
key_free(private);
|
1999-11-24 14:26:21 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
2007-09-17 08:09:15 +02:00
|
|
|
new_comment[strcspn(new_comment, "\n")] = '\0';
|
1999-11-24 14:26:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Save the file using the new passphrase. */
|
2013-12-07 00:41:55 +01:00
|
|
|
if (!key_save_private(private, identity_file, passphrase, new_comment,
|
|
|
|
use_new_format, new_format_cipher, rounds)) {
|
2001-04-16 04:00:02 +02:00
|
|
|
printf("Saving the key failed: %s.\n", identity_file);
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase, strlen(passphrase));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(passphrase);
|
2000-04-29 15:57:08 +02:00
|
|
|
key_free(private);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(comment);
|
1999-11-24 14:26:21 +01:00
|
|
|
exit(1);
|
1999-10-27 05:42:43 +02:00
|
|
|
}
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase, strlen(passphrase));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(passphrase);
|
2001-03-26 15:44:06 +02:00
|
|
|
public = key_from_private(private);
|
2000-04-29 15:57:08 +02:00
|
|
|
key_free(private);
|
1999-11-24 14:26:21 +01:00
|
|
|
|
|
|
|
strlcat(identity_file, ".pub", sizeof(identity_file));
|
2001-03-09 19:19:24 +01:00
|
|
|
fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
|
|
if (fd == -1) {
|
1999-11-24 14:26:21 +01:00
|
|
|
printf("Could not save your public key in %s\n", identity_file);
|
|
|
|
exit(1);
|
|
|
|
}
|
2001-03-09 19:19:24 +01:00
|
|
|
f = fdopen(fd, "w");
|
|
|
|
if (f == NULL) {
|
2009-02-21 22:47:02 +01:00
|
|
|
printf("fdopen %s failed\n", identity_file);
|
2001-03-09 19:19:24 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
2000-04-29 15:57:08 +02:00
|
|
|
if (!key_write(public, f))
|
2009-02-21 22:47:02 +01:00
|
|
|
fprintf(stderr, "write key failed\n");
|
2000-04-29 15:57:08 +02:00
|
|
|
key_free(public);
|
|
|
|
fprintf(f, " %s\n", new_comment);
|
1999-11-24 14:26:21 +01:00
|
|
|
fclose(f);
|
|
|
|
|
2013-06-01 23:31:17 +02:00
|
|
|
free(comment);
|
1999-11-24 14:26:21 +01:00
|
|
|
|
|
|
|
printf("The comment in your key file has been changed.\n");
|
|
|
|
exit(0);
|
1999-10-27 05:42:43 +02:00
|
|
|
}
|
|
|
|
|
2010-02-26 21:55:05 +01:00
|
|
|
static const char *
|
2010-03-04 21:39:35 +01:00
|
|
|
fmt_validity(u_int64_t valid_from, u_int64_t valid_to)
|
2010-02-26 21:55:05 +01:00
|
|
|
{
|
|
|
|
char from[32], to[32];
|
|
|
|
static char ret[64];
|
|
|
|
time_t tt;
|
|
|
|
struct tm *tm;
|
|
|
|
|
|
|
|
*from = *to = '\0';
|
2010-03-04 21:39:35 +01:00
|
|
|
if (valid_from == 0 && valid_to == 0xffffffffffffffffULL)
|
2010-02-26 21:55:05 +01:00
|
|
|
return "forever";
|
|
|
|
|
2010-03-04 21:39:35 +01:00
|
|
|
if (valid_from != 0) {
|
2010-02-26 21:55:05 +01:00
|
|
|
/* XXX revisit INT_MAX in 2038 :) */
|
2010-03-04 21:39:35 +01:00
|
|
|
tt = valid_from > INT_MAX ? INT_MAX : valid_from;
|
2010-02-26 21:55:05 +01:00
|
|
|
tm = localtime(&tt);
|
|
|
|
strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm);
|
|
|
|
}
|
2010-03-04 21:39:35 +01:00
|
|
|
if (valid_to != 0xffffffffffffffffULL) {
|
2010-02-26 21:55:05 +01:00
|
|
|
/* XXX revisit INT_MAX in 2038 :) */
|
2010-03-04 21:39:35 +01:00
|
|
|
tt = valid_to > INT_MAX ? INT_MAX : valid_to;
|
2010-02-26 21:55:05 +01:00
|
|
|
tm = localtime(&tt);
|
|
|
|
strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm);
|
|
|
|
}
|
|
|
|
|
2010-03-04 21:39:35 +01:00
|
|
|
if (valid_from == 0) {
|
2010-02-26 21:55:05 +01:00
|
|
|
snprintf(ret, sizeof(ret), "before %s", to);
|
|
|
|
return ret;
|
|
|
|
}
|
2010-03-04 21:39:35 +01:00
|
|
|
if (valid_to == 0xffffffffffffffffULL) {
|
2010-02-26 21:55:05 +01:00
|
|
|
snprintf(ret, sizeof(ret), "after %s", from);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(ret, sizeof(ret), "from %s to %s", from, to);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2010-04-16 07:56:21 +02:00
|
|
|
add_flag_option(Buffer *c, const char *name)
|
2010-02-26 21:55:05 +01:00
|
|
|
{
|
|
|
|
debug3("%s: %s", __func__, name);
|
|
|
|
buffer_put_cstring(c, name);
|
|
|
|
buffer_put_string(c, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2010-04-16 07:56:21 +02:00
|
|
|
add_string_option(Buffer *c, const char *name, const char *value)
|
2010-02-26 21:55:05 +01:00
|
|
|
{
|
|
|
|
Buffer b;
|
|
|
|
|
|
|
|
debug3("%s: %s=%s", __func__, name, value);
|
|
|
|
buffer_init(&b);
|
|
|
|
buffer_put_cstring(&b, value);
|
|
|
|
|
|
|
|
buffer_put_cstring(c, name);
|
|
|
|
buffer_put_string(c, buffer_ptr(&b), buffer_len(&b));
|
|
|
|
|
|
|
|
buffer_free(&b);
|
|
|
|
}
|
|
|
|
|
2010-05-21 06:58:32 +02:00
|
|
|
#define OPTIONS_CRITICAL 1
|
|
|
|
#define OPTIONS_EXTENSIONS 2
|
2010-02-26 21:55:05 +01:00
|
|
|
static void
|
2010-05-21 06:58:32 +02:00
|
|
|
prepare_options_buf(Buffer *c, int which)
|
2010-02-26 21:55:05 +01:00
|
|
|
{
|
|
|
|
buffer_clear(c);
|
2010-08-05 05:03:51 +02:00
|
|
|
if ((which & OPTIONS_CRITICAL) != 0 &&
|
|
|
|
certflags_command != NULL)
|
|
|
|
add_string_option(c, "force-command", certflags_command);
|
2011-05-05 06:17:18 +02:00
|
|
|
if ((which & OPTIONS_EXTENSIONS) != 0 &&
|
|
|
|
(certflags_flags & CERTOPT_X_FWD) != 0)
|
|
|
|
add_flag_option(c, "permit-X11-forwarding");
|
2010-05-21 06:58:32 +02:00
|
|
|
if ((which & OPTIONS_EXTENSIONS) != 0 &&
|
|
|
|
(certflags_flags & CERTOPT_AGENT_FWD) != 0)
|
2010-04-16 07:56:21 +02:00
|
|
|
add_flag_option(c, "permit-agent-forwarding");
|
2010-05-21 06:58:32 +02:00
|
|
|
if ((which & OPTIONS_EXTENSIONS) != 0 &&
|
|
|
|
(certflags_flags & CERTOPT_PORT_FWD) != 0)
|
2010-04-16 07:56:21 +02:00
|
|
|
add_flag_option(c, "permit-port-forwarding");
|
2010-05-21 06:58:32 +02:00
|
|
|
if ((which & OPTIONS_EXTENSIONS) != 0 &&
|
|
|
|
(certflags_flags & CERTOPT_PTY) != 0)
|
2010-04-16 07:56:21 +02:00
|
|
|
add_flag_option(c, "permit-pty");
|
2010-05-21 06:58:32 +02:00
|
|
|
if ((which & OPTIONS_EXTENSIONS) != 0 &&
|
|
|
|
(certflags_flags & CERTOPT_USER_RC) != 0)
|
2010-04-16 07:56:21 +02:00
|
|
|
add_flag_option(c, "permit-user-rc");
|
2010-05-21 06:58:32 +02:00
|
|
|
if ((which & OPTIONS_CRITICAL) != 0 &&
|
|
|
|
certflags_src_addr != NULL)
|
|
|
|
add_string_option(c, "source-address", certflags_src_addr);
|
2010-02-26 21:55:05 +01:00
|
|
|
}
|
|
|
|
|
2010-08-05 05:05:31 +02:00
|
|
|
static Key *
|
|
|
|
load_pkcs11_key(char *path)
|
|
|
|
{
|
|
|
|
#ifdef ENABLE_PKCS11
|
|
|
|
Key **keys = NULL, *public, *private = NULL;
|
|
|
|
int i, nkeys;
|
|
|
|
|
|
|
|
if ((public = key_load_public(path, NULL)) == NULL)
|
|
|
|
fatal("Couldn't load CA public key \"%s\"", path);
|
|
|
|
|
|
|
|
nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, &keys);
|
|
|
|
debug3("%s: %d keys", __func__, nkeys);
|
|
|
|
if (nkeys <= 0)
|
|
|
|
fatal("cannot read public key from pkcs11");
|
|
|
|
for (i = 0; i < nkeys; i++) {
|
|
|
|
if (key_equal_public(public, keys[i])) {
|
|
|
|
private = keys[i];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
key_free(keys[i]);
|
|
|
|
}
|
2013-06-01 23:31:17 +02:00
|
|
|
free(keys);
|
2010-08-05 05:05:31 +02:00
|
|
|
key_free(public);
|
|
|
|
return private;
|
|
|
|
#else
|
|
|
|
fatal("no pkcs11 support");
|
|
|
|
#endif /* ENABLE_PKCS11 */
|
|
|
|
}
|
|
|
|
|
2010-02-26 21:55:05 +01:00
|
|
|
static void
|
|
|
|
do_ca_sign(struct passwd *pw, int argc, char **argv)
|
|
|
|
{
|
|
|
|
int i, fd;
|
|
|
|
u_int n;
|
|
|
|
Key *ca, *public;
|
|
|
|
char *otmp, *tmp, *cp, *out, *comment, **plist = NULL;
|
|
|
|
FILE *f;
|
2010-04-16 07:56:21 +02:00
|
|
|
int v00 = 0; /* legacy keys */
|
2010-02-26 21:55:05 +01:00
|
|
|
|
2010-04-16 07:56:21 +02:00
|
|
|
if (key_type_name != NULL) {
|
|
|
|
switch (key_type_from_name(key_type_name)) {
|
|
|
|
case KEY_RSA_CERT_V00:
|
|
|
|
case KEY_DSA_CERT_V00:
|
|
|
|
v00 = 1;
|
|
|
|
break;
|
|
|
|
case KEY_UNSPEC:
|
|
|
|
if (strcasecmp(key_type_name, "v00") == 0) {
|
|
|
|
v00 = 1;
|
|
|
|
break;
|
|
|
|
} else if (strcasecmp(key_type_name, "v01") == 0)
|
|
|
|
break;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "unknown key type %s\n", key_type_name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-15 06:24:09 +02:00
|
|
|
#ifdef ENABLE_PKCS11
|
2010-08-05 05:05:31 +02:00
|
|
|
pkcs11_init(1);
|
2014-05-15 06:24:09 +02:00
|
|
|
#endif
|
2010-08-05 05:05:31 +02:00
|
|
|
tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
|
|
|
|
if (pkcs11provider != NULL) {
|
|
|
|
if ((ca = load_pkcs11_key(tmp)) == NULL)
|
|
|
|
fatal("No PKCS#11 key matching %s found", ca_key_path);
|
|
|
|
} else if ((ca = load_identity(tmp)) == NULL)
|
|
|
|
fatal("Couldn't load CA key \"%s\"", tmp);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(tmp);
|
2010-08-05 05:05:31 +02:00
|
|
|
|
2010-02-26 21:55:05 +01:00
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
/* Split list of principals */
|
|
|
|
n = 0;
|
|
|
|
if (cert_principals != NULL) {
|
|
|
|
otmp = tmp = xstrdup(cert_principals);
|
|
|
|
plist = NULL;
|
|
|
|
for (; (cp = strsep(&tmp, ",")) != NULL; n++) {
|
|
|
|
plist = xrealloc(plist, n + 1, sizeof(*plist));
|
|
|
|
if (*(plist[n] = xstrdup(cp)) == '\0')
|
|
|
|
fatal("Empty principal name");
|
|
|
|
}
|
2013-06-01 23:31:17 +02:00
|
|
|
free(otmp);
|
2010-02-26 21:55:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
tmp = tilde_expand_filename(argv[i], pw->pw_uid);
|
|
|
|
if ((public = key_load_public(tmp, &comment)) == NULL)
|
|
|
|
fatal("%s: unable to open \"%s\"", __func__, tmp);
|
2010-08-31 14:41:14 +02:00
|
|
|
if (public->type != KEY_RSA && public->type != KEY_DSA &&
|
2013-12-07 01:24:01 +01:00
|
|
|
public->type != KEY_ECDSA && public->type != KEY_ED25519)
|
2010-02-26 21:55:05 +01:00
|
|
|
fatal("%s: key \"%s\" type %s cannot be certified",
|
|
|
|
__func__, tmp, key_type(public));
|
|
|
|
|
|
|
|
/* Prepare certificate to sign */
|
2010-04-16 07:56:21 +02:00
|
|
|
if (key_to_certified(public, v00) != 0)
|
2010-02-26 21:55:05 +01:00
|
|
|
fatal("Could not upgrade key %s to certificate", tmp);
|
|
|
|
public->cert->type = cert_key_type;
|
2010-04-16 07:56:21 +02:00
|
|
|
public->cert->serial = (u_int64_t)cert_serial;
|
2010-02-26 21:55:05 +01:00
|
|
|
public->cert->key_id = xstrdup(cert_key_id);
|
|
|
|
public->cert->nprincipals = n;
|
|
|
|
public->cert->principals = plist;
|
|
|
|
public->cert->valid_after = cert_valid_from;
|
|
|
|
public->cert->valid_before = cert_valid_to;
|
2010-05-21 06:58:32 +02:00
|
|
|
if (v00) {
|
|
|
|
prepare_options_buf(&public->cert->critical,
|
|
|
|
OPTIONS_CRITICAL|OPTIONS_EXTENSIONS);
|
|
|
|
} else {
|
|
|
|
prepare_options_buf(&public->cert->critical,
|
|
|
|
OPTIONS_CRITICAL);
|
|
|
|
prepare_options_buf(&public->cert->extensions,
|
|
|
|
OPTIONS_EXTENSIONS);
|
|
|
|
}
|
2010-02-26 21:55:05 +01:00
|
|
|
public->cert->signature_key = key_from_private(ca);
|
|
|
|
|
|
|
|
if (key_certify(public, ca) != 0)
|
|
|
|
fatal("Couldn't not certify key %s", tmp);
|
|
|
|
|
|
|
|
if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0)
|
|
|
|
*cp = '\0';
|
|
|
|
xasprintf(&out, "%s-cert.pub", tmp);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(tmp);
|
2010-02-26 21:55:05 +01:00
|
|
|
|
|
|
|
if ((fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
|
|
|
|
fatal("Could not open \"%s\" for writing: %s", out,
|
|
|
|
strerror(errno));
|
|
|
|
if ((f = fdopen(fd, "w")) == NULL)
|
|
|
|
fatal("%s: fdopen: %s", __func__, strerror(errno));
|
|
|
|
if (!key_write(public, f))
|
|
|
|
fatal("Could not write certified key to %s", out);
|
|
|
|
fprintf(f, " %s\n", comment);
|
|
|
|
fclose(f);
|
|
|
|
|
2010-04-16 07:56:21 +02:00
|
|
|
if (!quiet) {
|
|
|
|
logit("Signed %s key %s: id \"%s\" serial %llu%s%s "
|
|
|
|
"valid %s", key_cert_type(public),
|
2011-01-11 07:20:29 +01:00
|
|
|
out, public->cert->key_id,
|
|
|
|
(unsigned long long)public->cert->serial,
|
2010-02-26 21:55:05 +01:00
|
|
|
cert_principals != NULL ? " for " : "",
|
|
|
|
cert_principals != NULL ? cert_principals : "",
|
2010-03-04 21:39:35 +01:00
|
|
|
fmt_validity(cert_valid_from, cert_valid_to));
|
2010-04-16 07:56:21 +02:00
|
|
|
}
|
2010-02-26 21:55:05 +01:00
|
|
|
|
|
|
|
key_free(public);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(out);
|
2010-02-26 21:55:05 +01:00
|
|
|
}
|
2014-05-15 06:24:09 +02:00
|
|
|
#ifdef ENABLE_PKCS11
|
2010-08-05 05:05:31 +02:00
|
|
|
pkcs11_terminate();
|
2014-05-15 06:24:09 +02:00
|
|
|
#endif
|
2010-02-26 21:55:05 +01:00
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u_int64_t
|
|
|
|
parse_relative_time(const char *s, time_t now)
|
|
|
|
{
|
|
|
|
int64_t mul, secs;
|
|
|
|
|
|
|
|
mul = *s == '-' ? -1 : 1;
|
|
|
|
|
|
|
|
if ((secs = convtime(s + 1)) == -1)
|
|
|
|
fatal("Invalid relative certificate time %s", s);
|
|
|
|
if (mul == -1 && secs > now)
|
|
|
|
fatal("Certificate time %s cannot be represented", s);
|
|
|
|
return now + (u_int64_t)(secs * mul);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u_int64_t
|
|
|
|
parse_absolute_time(const char *s)
|
|
|
|
{
|
|
|
|
struct tm tm;
|
|
|
|
time_t tt;
|
2010-03-03 02:14:15 +01:00
|
|
|
char buf[32], *fmt;
|
2010-02-26 21:55:05 +01:00
|
|
|
|
2010-03-03 02:14:15 +01:00
|
|
|
/*
|
|
|
|
* POSIX strptime says "The application shall ensure that there
|
|
|
|
* is white-space or other non-alphanumeric characters between
|
|
|
|
* any two conversion specifications" so arrange things this way.
|
|
|
|
*/
|
|
|
|
switch (strlen(s)) {
|
|
|
|
case 8:
|
2010-03-07 23:24:11 +01:00
|
|
|
fmt = "%Y-%m-%d";
|
|
|
|
snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6);
|
2010-03-03 02:14:15 +01:00
|
|
|
break;
|
|
|
|
case 14:
|
2010-03-07 23:24:11 +01:00
|
|
|
fmt = "%Y-%m-%dT%H:%M:%S";
|
|
|
|
snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s",
|
2010-03-03 02:14:15 +01:00
|
|
|
s, s + 4, s + 6, s + 8, s + 10, s + 12);
|
|
|
|
break;
|
|
|
|
default:
|
2010-02-26 21:55:05 +01:00
|
|
|
fatal("Invalid certificate time format %s", s);
|
2010-03-03 02:14:15 +01:00
|
|
|
}
|
2010-02-26 21:55:05 +01:00
|
|
|
|
2014-02-04 01:18:20 +01:00
|
|
|
memset(&tm, 0, sizeof(tm));
|
2010-03-03 02:14:15 +01:00
|
|
|
if (strptime(buf, fmt, &tm) == NULL)
|
2010-02-26 21:55:05 +01:00
|
|
|
fatal("Invalid certificate time %s", s);
|
|
|
|
if ((tt = mktime(&tm)) < 0)
|
|
|
|
fatal("Certificate time %s cannot be represented", s);
|
|
|
|
return (u_int64_t)tt;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_cert_times(char *timespec)
|
|
|
|
{
|
|
|
|
char *from, *to;
|
|
|
|
time_t now = time(NULL);
|
|
|
|
int64_t secs;
|
|
|
|
|
|
|
|
/* +timespec relative to now */
|
|
|
|
if (*timespec == '+' && strchr(timespec, ':') == NULL) {
|
|
|
|
if ((secs = convtime(timespec + 1)) == -1)
|
|
|
|
fatal("Invalid relative certificate life %s", timespec);
|
|
|
|
cert_valid_to = now + secs;
|
|
|
|
/*
|
|
|
|
* Backdate certificate one minute to avoid problems on hosts
|
|
|
|
* with poorly-synchronised clocks.
|
|
|
|
*/
|
|
|
|
cert_valid_from = ((now - 59)/ 60) * 60;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* from:to, where
|
|
|
|
* from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS
|
|
|
|
* to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS
|
|
|
|
*/
|
|
|
|
from = xstrdup(timespec);
|
|
|
|
to = strchr(from, ':');
|
|
|
|
if (to == NULL || from == to || *(to + 1) == '\0')
|
2010-03-04 04:17:22 +01:00
|
|
|
fatal("Invalid certificate life specification %s", timespec);
|
2010-02-26 21:55:05 +01:00
|
|
|
*to++ = '\0';
|
|
|
|
|
|
|
|
if (*from == '-' || *from == '+')
|
|
|
|
cert_valid_from = parse_relative_time(from, now);
|
|
|
|
else
|
|
|
|
cert_valid_from = parse_absolute_time(from);
|
|
|
|
|
|
|
|
if (*to == '-' || *to == '+')
|
2013-10-23 07:31:31 +02:00
|
|
|
cert_valid_to = parse_relative_time(to, now);
|
2010-02-26 21:55:05 +01:00
|
|
|
else
|
|
|
|
cert_valid_to = parse_absolute_time(to);
|
|
|
|
|
|
|
|
if (cert_valid_to <= cert_valid_from)
|
|
|
|
fatal("Empty certificate validity interval");
|
2013-06-01 23:31:17 +02:00
|
|
|
free(from);
|
2010-02-26 21:55:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2010-04-16 07:56:21 +02:00
|
|
|
add_cert_option(char *opt)
|
2010-02-26 21:55:05 +01:00
|
|
|
{
|
|
|
|
char *val;
|
|
|
|
|
2011-05-05 06:14:08 +02:00
|
|
|
if (strcasecmp(opt, "clear") == 0)
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags = 0;
|
2010-02-26 21:55:05 +01:00
|
|
|
else if (strcasecmp(opt, "no-x11-forwarding") == 0)
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags &= ~CERTOPT_X_FWD;
|
2010-02-26 21:55:05 +01:00
|
|
|
else if (strcasecmp(opt, "permit-x11-forwarding") == 0)
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags |= CERTOPT_X_FWD;
|
2010-02-26 21:55:05 +01:00
|
|
|
else if (strcasecmp(opt, "no-agent-forwarding") == 0)
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags &= ~CERTOPT_AGENT_FWD;
|
2010-02-26 21:55:05 +01:00
|
|
|
else if (strcasecmp(opt, "permit-agent-forwarding") == 0)
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags |= CERTOPT_AGENT_FWD;
|
2010-02-26 21:55:05 +01:00
|
|
|
else if (strcasecmp(opt, "no-port-forwarding") == 0)
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags &= ~CERTOPT_PORT_FWD;
|
2010-02-26 21:55:05 +01:00
|
|
|
else if (strcasecmp(opt, "permit-port-forwarding") == 0)
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags |= CERTOPT_PORT_FWD;
|
2010-02-26 21:55:05 +01:00
|
|
|
else if (strcasecmp(opt, "no-pty") == 0)
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags &= ~CERTOPT_PTY;
|
2010-02-26 21:55:05 +01:00
|
|
|
else if (strcasecmp(opt, "permit-pty") == 0)
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags |= CERTOPT_PTY;
|
2010-02-26 21:55:05 +01:00
|
|
|
else if (strcasecmp(opt, "no-user-rc") == 0)
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags &= ~CERTOPT_USER_RC;
|
2010-02-26 21:55:05 +01:00
|
|
|
else if (strcasecmp(opt, "permit-user-rc") == 0)
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags |= CERTOPT_USER_RC;
|
2010-02-26 21:55:05 +01:00
|
|
|
else if (strncasecmp(opt, "force-command=", 14) == 0) {
|
|
|
|
val = opt + 14;
|
|
|
|
if (*val == '\0')
|
2010-04-16 07:56:21 +02:00
|
|
|
fatal("Empty force-command option");
|
2010-05-21 06:58:32 +02:00
|
|
|
if (certflags_command != NULL)
|
2010-02-26 21:55:05 +01:00
|
|
|
fatal("force-command already specified");
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_command = xstrdup(val);
|
2010-02-26 21:55:05 +01:00
|
|
|
} else if (strncasecmp(opt, "source-address=", 15) == 0) {
|
|
|
|
val = opt + 15;
|
|
|
|
if (*val == '\0')
|
2010-04-16 07:56:21 +02:00
|
|
|
fatal("Empty source-address option");
|
2010-05-21 06:58:32 +02:00
|
|
|
if (certflags_src_addr != NULL)
|
2010-02-26 21:55:05 +01:00
|
|
|
fatal("source-address already specified");
|
|
|
|
if (addr_match_cidr_list(NULL, val) != 0)
|
|
|
|
fatal("Invalid source-address list");
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_src_addr = xstrdup(val);
|
2010-02-26 21:55:05 +01:00
|
|
|
} else
|
2010-04-16 07:56:21 +02:00
|
|
|
fatal("Unsupported certificate option \"%s\"", opt);
|
2010-02-26 21:55:05 +01:00
|
|
|
}
|
|
|
|
|
2010-06-26 01:48:02 +02:00
|
|
|
static void
|
|
|
|
show_options(const Buffer *optbuf, int v00, int in_critical)
|
|
|
|
{
|
2014-05-15 05:48:26 +02:00
|
|
|
char *name, *arg;
|
|
|
|
const u_char *data;
|
2010-06-26 01:48:02 +02:00
|
|
|
u_int dlen;
|
|
|
|
Buffer options, option;
|
|
|
|
|
|
|
|
buffer_init(&options);
|
|
|
|
buffer_append(&options, buffer_ptr(optbuf), buffer_len(optbuf));
|
|
|
|
|
|
|
|
buffer_init(&option);
|
|
|
|
while (buffer_len(&options) != 0) {
|
|
|
|
name = buffer_get_string(&options, NULL);
|
|
|
|
data = buffer_get_string_ptr(&options, &dlen);
|
|
|
|
buffer_append(&option, data, dlen);
|
|
|
|
printf(" %s", name);
|
|
|
|
if ((v00 || !in_critical) &&
|
|
|
|
(strcmp(name, "permit-X11-forwarding") == 0 ||
|
|
|
|
strcmp(name, "permit-agent-forwarding") == 0 ||
|
|
|
|
strcmp(name, "permit-port-forwarding") == 0 ||
|
|
|
|
strcmp(name, "permit-pty") == 0 ||
|
|
|
|
strcmp(name, "permit-user-rc") == 0))
|
|
|
|
printf("\n");
|
|
|
|
else if ((v00 || in_critical) &&
|
|
|
|
(strcmp(name, "force-command") == 0 ||
|
|
|
|
strcmp(name, "source-address") == 0)) {
|
2014-05-15 05:48:26 +02:00
|
|
|
arg = buffer_get_cstring(&option, NULL);
|
|
|
|
printf(" %s\n", arg);
|
|
|
|
free(arg);
|
2010-06-26 01:48:02 +02:00
|
|
|
} else {
|
|
|
|
printf(" UNKNOWN OPTION (len %u)\n",
|
|
|
|
buffer_len(&option));
|
|
|
|
buffer_clear(&option);
|
|
|
|
}
|
2013-06-01 23:31:17 +02:00
|
|
|
free(name);
|
2010-06-26 01:48:02 +02:00
|
|
|
if (buffer_len(&option) != 0)
|
|
|
|
fatal("Option corrupt: extra data at end");
|
|
|
|
}
|
|
|
|
buffer_free(&option);
|
|
|
|
buffer_free(&options);
|
|
|
|
}
|
|
|
|
|
2010-03-04 21:39:35 +01:00
|
|
|
static void
|
|
|
|
do_show_cert(struct passwd *pw)
|
|
|
|
{
|
|
|
|
Key *key;
|
|
|
|
struct stat st;
|
|
|
|
char *key_fp, *ca_fp;
|
2010-06-26 01:48:02 +02:00
|
|
|
u_int i, v00;
|
2010-03-04 21:39:35 +01:00
|
|
|
|
|
|
|
if (!have_identity)
|
|
|
|
ask_filename(pw, "Enter file in which the key is");
|
2010-06-26 01:39:07 +02:00
|
|
|
if (stat(identity_file, &st) < 0)
|
|
|
|
fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
|
2010-03-04 21:39:35 +01:00
|
|
|
if ((key = key_load_public(identity_file, NULL)) == NULL)
|
|
|
|
fatal("%s is not a public key", identity_file);
|
|
|
|
if (!key_is_cert(key))
|
|
|
|
fatal("%s is not a certificate", identity_file);
|
2010-04-16 07:56:21 +02:00
|
|
|
v00 = key->type == KEY_RSA_CERT_V00 || key->type == KEY_DSA_CERT_V00;
|
|
|
|
|
2010-03-04 21:39:35 +01:00
|
|
|
key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
|
|
|
|
ca_fp = key_fingerprint(key->cert->signature_key,
|
|
|
|
SSH_FP_MD5, SSH_FP_HEX);
|
|
|
|
|
|
|
|
printf("%s:\n", identity_file);
|
2010-04-16 07:56:21 +02:00
|
|
|
printf(" Type: %s %s certificate\n", key_ssh_name(key),
|
|
|
|
key_cert_type(key));
|
|
|
|
printf(" Public key: %s %s\n", key_type(key), key_fp);
|
|
|
|
printf(" Signing CA: %s %s\n",
|
2010-03-04 21:39:35 +01:00
|
|
|
key_type(key->cert->signature_key), ca_fp);
|
2010-04-16 07:56:21 +02:00
|
|
|
printf(" Key ID: \"%s\"\n", key->cert->key_id);
|
2011-01-11 07:20:29 +01:00
|
|
|
if (!v00) {
|
|
|
|
printf(" Serial: %llu\n",
|
|
|
|
(unsigned long long)key->cert->serial);
|
|
|
|
}
|
2010-03-04 21:39:35 +01:00
|
|
|
printf(" Valid: %s\n",
|
|
|
|
fmt_validity(key->cert->valid_after, key->cert->valid_before));
|
|
|
|
printf(" Principals: ");
|
|
|
|
if (key->cert->nprincipals == 0)
|
|
|
|
printf("(none)\n");
|
|
|
|
else {
|
|
|
|
for (i = 0; i < key->cert->nprincipals; i++)
|
|
|
|
printf("\n %s",
|
|
|
|
key->cert->principals[i]);
|
|
|
|
printf("\n");
|
|
|
|
}
|
2010-04-16 07:56:21 +02:00
|
|
|
printf(" Critical Options: ");
|
|
|
|
if (buffer_len(&key->cert->critical) == 0)
|
2010-03-04 21:39:35 +01:00
|
|
|
printf("(none)\n");
|
|
|
|
else {
|
|
|
|
printf("\n");
|
2010-06-26 01:48:02 +02:00
|
|
|
show_options(&key->cert->critical, v00, 1);
|
2010-04-16 07:56:21 +02:00
|
|
|
}
|
|
|
|
if (!v00) {
|
|
|
|
printf(" Extensions: ");
|
|
|
|
if (buffer_len(&key->cert->extensions) == 0)
|
|
|
|
printf("(none)\n");
|
|
|
|
else {
|
|
|
|
printf("\n");
|
2010-06-26 01:48:02 +02:00
|
|
|
show_options(&key->cert->extensions, v00, 0);
|
2010-03-04 21:39:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2014-05-15 06:24:09 +02:00
|
|
|
#ifdef WITH_OPENSSL
|
2013-01-18 01:44:04 +01:00
|
|
|
static void
|
|
|
|
load_krl(const char *path, struct ssh_krl **krlp)
|
|
|
|
{
|
|
|
|
Buffer krlbuf;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
buffer_init(&krlbuf);
|
|
|
|
if ((fd = open(path, O_RDONLY)) == -1)
|
|
|
|
fatal("open %s: %s", path, strerror(errno));
|
|
|
|
if (!key_load_file(fd, path, &krlbuf))
|
|
|
|
fatal("Unable to load KRL");
|
|
|
|
close(fd);
|
|
|
|
/* XXX check sigs */
|
|
|
|
if (ssh_krl_from_blob(&krlbuf, krlp, NULL, 0) != 0 ||
|
|
|
|
*krlp == NULL)
|
|
|
|
fatal("Invalid KRL file");
|
|
|
|
buffer_free(&krlbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
update_krl_from_file(struct passwd *pw, const char *file, const Key *ca,
|
|
|
|
struct ssh_krl *krl)
|
|
|
|
{
|
|
|
|
Key *key = NULL;
|
|
|
|
u_long lnum = 0;
|
|
|
|
char *path, *cp, *ep, line[SSH_MAX_PUBKEY_BYTES];
|
|
|
|
unsigned long long serial, serial2;
|
|
|
|
int i, was_explicit_key, was_sha1, r;
|
|
|
|
FILE *krl_spec;
|
|
|
|
|
|
|
|
path = tilde_expand_filename(file, pw->pw_uid);
|
|
|
|
if (strcmp(path, "-") == 0) {
|
|
|
|
krl_spec = stdin;
|
|
|
|
free(path);
|
|
|
|
path = xstrdup("(standard input)");
|
|
|
|
} else if ((krl_spec = fopen(path, "r")) == NULL)
|
|
|
|
fatal("fopen %s: %s", path, strerror(errno));
|
|
|
|
|
|
|
|
if (!quiet)
|
|
|
|
printf("Revoking from %s\n", path);
|
|
|
|
while (read_keyfile_line(krl_spec, path, line, sizeof(line),
|
|
|
|
&lnum) == 0) {
|
|
|
|
was_explicit_key = was_sha1 = 0;
|
|
|
|
cp = line + strspn(line, " \t");
|
|
|
|
/* Trim trailing space, comments and strip \n */
|
|
|
|
for (i = 0, r = -1; cp[i] != '\0'; i++) {
|
|
|
|
if (cp[i] == '#' || cp[i] == '\n') {
|
|
|
|
cp[i] = '\0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (cp[i] == ' ' || cp[i] == '\t') {
|
|
|
|
/* Remember the start of a span of whitespace */
|
|
|
|
if (r == -1)
|
|
|
|
r = i;
|
|
|
|
} else
|
|
|
|
r = -1;
|
|
|
|
}
|
|
|
|
if (r != -1)
|
|
|
|
cp[r] = '\0';
|
|
|
|
if (*cp == '\0')
|
|
|
|
continue;
|
|
|
|
if (strncasecmp(cp, "serial:", 7) == 0) {
|
|
|
|
if (ca == NULL) {
|
2013-08-20 18:42:58 +02:00
|
|
|
fatal("revoking certificates by serial number "
|
2013-01-18 01:44:04 +01:00
|
|
|
"requires specification of a CA key");
|
|
|
|
}
|
|
|
|
cp += 7;
|
|
|
|
cp = cp + strspn(cp, " \t");
|
|
|
|
errno = 0;
|
|
|
|
serial = strtoull(cp, &ep, 0);
|
|
|
|
if (*cp == '\0' || (*ep != '\0' && *ep != '-'))
|
|
|
|
fatal("%s:%lu: invalid serial \"%s\"",
|
|
|
|
path, lnum, cp);
|
|
|
|
if (errno == ERANGE && serial == ULLONG_MAX)
|
|
|
|
fatal("%s:%lu: serial out of range",
|
|
|
|
path, lnum);
|
|
|
|
serial2 = serial;
|
|
|
|
if (*ep == '-') {
|
|
|
|
cp = ep + 1;
|
|
|
|
errno = 0;
|
|
|
|
serial2 = strtoull(cp, &ep, 0);
|
|
|
|
if (*cp == '\0' || *ep != '\0')
|
|
|
|
fatal("%s:%lu: invalid serial \"%s\"",
|
|
|
|
path, lnum, cp);
|
|
|
|
if (errno == ERANGE && serial2 == ULLONG_MAX)
|
|
|
|
fatal("%s:%lu: serial out of range",
|
|
|
|
path, lnum);
|
|
|
|
if (serial2 <= serial)
|
|
|
|
fatal("%s:%lu: invalid serial range "
|
|
|
|
"%llu:%llu", path, lnum,
|
|
|
|
(unsigned long long)serial,
|
|
|
|
(unsigned long long)serial2);
|
|
|
|
}
|
|
|
|
if (ssh_krl_revoke_cert_by_serial_range(krl,
|
|
|
|
ca, serial, serial2) != 0) {
|
|
|
|
fatal("%s: revoke serial failed",
|
|
|
|
__func__);
|
|
|
|
}
|
|
|
|
} else if (strncasecmp(cp, "id:", 3) == 0) {
|
|
|
|
if (ca == NULL) {
|
2013-08-20 18:43:27 +02:00
|
|
|
fatal("revoking certificates by key ID "
|
2013-01-18 01:44:04 +01:00
|
|
|
"requires specification of a CA key");
|
|
|
|
}
|
|
|
|
cp += 3;
|
|
|
|
cp = cp + strspn(cp, " \t");
|
|
|
|
if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0)
|
|
|
|
fatal("%s: revoke key ID failed", __func__);
|
|
|
|
} else {
|
|
|
|
if (strncasecmp(cp, "key:", 4) == 0) {
|
|
|
|
cp += 4;
|
|
|
|
cp = cp + strspn(cp, " \t");
|
|
|
|
was_explicit_key = 1;
|
|
|
|
} else if (strncasecmp(cp, "sha1:", 5) == 0) {
|
|
|
|
cp += 5;
|
|
|
|
cp = cp + strspn(cp, " \t");
|
|
|
|
was_sha1 = 1;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Just try to process the line as a key.
|
|
|
|
* Parsing will fail if it isn't.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
if ((key = key_new(KEY_UNSPEC)) == NULL)
|
|
|
|
fatal("key_new");
|
|
|
|
if (key_read(key, &cp) != 1)
|
|
|
|
fatal("%s:%lu: invalid key", path, lnum);
|
|
|
|
if (was_explicit_key)
|
|
|
|
r = ssh_krl_revoke_key_explicit(krl, key);
|
|
|
|
else if (was_sha1)
|
|
|
|
r = ssh_krl_revoke_key_sha1(krl, key);
|
|
|
|
else
|
|
|
|
r = ssh_krl_revoke_key(krl, key);
|
|
|
|
if (r != 0)
|
|
|
|
fatal("%s: revoke key failed", __func__);
|
|
|
|
key_free(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (strcmp(path, "-") != 0)
|
|
|
|
fclose(krl_spec);
|
2013-04-23 07:23:24 +02:00
|
|
|
free(path);
|
2013-01-18 01:44:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
do_gen_krl(struct passwd *pw, int updating, int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct ssh_krl *krl;
|
|
|
|
struct stat sb;
|
|
|
|
Key *ca = NULL;
|
|
|
|
int fd, i;
|
|
|
|
char *tmp;
|
|
|
|
Buffer kbuf;
|
|
|
|
|
|
|
|
if (*identity_file == '\0')
|
|
|
|
fatal("KRL generation requires an output file");
|
|
|
|
if (stat(identity_file, &sb) == -1) {
|
|
|
|
if (errno != ENOENT)
|
|
|
|
fatal("Cannot access KRL \"%s\": %s",
|
|
|
|
identity_file, strerror(errno));
|
|
|
|
if (updating)
|
|
|
|
fatal("KRL \"%s\" does not exist", identity_file);
|
|
|
|
}
|
|
|
|
if (ca_key_path != NULL) {
|
|
|
|
tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
|
|
|
|
if ((ca = key_load_public(tmp, NULL)) == NULL)
|
|
|
|
fatal("Cannot load CA public key %s", tmp);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(tmp);
|
2013-01-18 01:44:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (updating)
|
|
|
|
load_krl(identity_file, &krl);
|
|
|
|
else if ((krl = ssh_krl_init()) == NULL)
|
|
|
|
fatal("couldn't create KRL");
|
|
|
|
|
|
|
|
if (cert_serial != 0)
|
|
|
|
ssh_krl_set_version(krl, cert_serial);
|
|
|
|
if (identity_comment != NULL)
|
|
|
|
ssh_krl_set_comment(krl, identity_comment);
|
|
|
|
|
|
|
|
for (i = 0; i < argc; i++)
|
|
|
|
update_krl_from_file(pw, argv[i], ca, krl);
|
|
|
|
|
|
|
|
buffer_init(&kbuf);
|
|
|
|
if (ssh_krl_to_blob(krl, &kbuf, NULL, 0) != 0)
|
|
|
|
fatal("Couldn't generate KRL");
|
|
|
|
if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
|
|
|
|
fatal("open %s: %s", identity_file, strerror(errno));
|
|
|
|
if (atomicio(vwrite, fd, buffer_ptr(&kbuf), buffer_len(&kbuf)) !=
|
|
|
|
buffer_len(&kbuf))
|
|
|
|
fatal("write %s: %s", identity_file, strerror(errno));
|
|
|
|
close(fd);
|
|
|
|
buffer_free(&kbuf);
|
|
|
|
ssh_krl_free(krl);
|
2013-04-23 07:23:24 +02:00
|
|
|
if (ca != NULL)
|
|
|
|
key_free(ca);
|
2013-01-18 01:44:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
do_check_krl(struct passwd *pw, int argc, char **argv)
|
|
|
|
{
|
|
|
|
int i, r, ret = 0;
|
|
|
|
char *comment;
|
|
|
|
struct ssh_krl *krl;
|
|
|
|
Key *k;
|
|
|
|
|
|
|
|
if (*identity_file == '\0')
|
|
|
|
fatal("KRL checking requires an input file");
|
|
|
|
load_krl(identity_file, &krl);
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
if ((k = key_load_public(argv[i], &comment)) == NULL)
|
|
|
|
fatal("Cannot load public key %s", argv[i]);
|
|
|
|
r = ssh_krl_check_key(krl, k);
|
|
|
|
printf("%s%s%s%s: %s\n", argv[i],
|
|
|
|
*comment ? " (" : "", comment, *comment ? ")" : "",
|
|
|
|
r == 0 ? "ok" : "REVOKED");
|
|
|
|
if (r != 0)
|
|
|
|
ret = 1;
|
|
|
|
key_free(k);
|
|
|
|
free(comment);
|
|
|
|
}
|
|
|
|
ssh_krl_free(krl);
|
|
|
|
exit(ret);
|
|
|
|
}
|
2014-05-15 06:24:09 +02:00
|
|
|
#endif
|
2013-01-18 01:44:04 +01:00
|
|
|
|
2001-06-25 07:01:22 +02:00
|
|
|
static void
|
1999-11-21 08:31:57 +01:00
|
|
|
usage(void)
|
|
|
|
{
|
2014-04-20 05:01:30 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
"usage: ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa | rsa1]\n"
|
|
|
|
" [-N new_passphrase] [-C comment] [-f output_keyfile]\n"
|
|
|
|
" ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-f keyfile]\n"
|
|
|
|
" ssh-keygen -i [-m key_format] [-f input_keyfile]\n"
|
|
|
|
" ssh-keygen -e [-m key_format] [-f input_keyfile]\n"
|
|
|
|
" ssh-keygen -y [-f input_keyfile]\n"
|
|
|
|
" ssh-keygen -c [-P passphrase] [-C comment] [-f keyfile]\n"
|
|
|
|
" ssh-keygen -l [-f input_keyfile]\n"
|
|
|
|
" ssh-keygen -B [-f input_keyfile]\n");
|
2010-02-11 23:21:02 +01:00
|
|
|
#ifdef ENABLE_PKCS11
|
2014-04-20 05:01:30 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
" ssh-keygen -D pkcs11\n");
|
2010-02-11 23:21:02 +01:00
|
|
|
#endif
|
2014-04-20 05:01:30 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
" ssh-keygen -F hostname [-f known_hosts_file] [-l]\n"
|
|
|
|
" ssh-keygen -H [-f known_hosts_file]\n"
|
|
|
|
" ssh-keygen -R hostname [-f known_hosts_file]\n"
|
|
|
|
" ssh-keygen -r hostname [-f input_keyfile] [-g]\n"
|
|
|
|
" ssh-keygen -G output_file [-v] [-b bits] [-M memory] [-S start_point]\n"
|
|
|
|
" ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n"
|
|
|
|
" [-j start_line] [-K checkpt] [-W generator]\n"
|
|
|
|
" ssh-keygen -s ca_key -I certificate_identity [-h] [-n principals]\n"
|
|
|
|
" [-O option] [-V validity_interval] [-z serial_number] file ...\n"
|
|
|
|
" ssh-keygen -L [-f input_keyfile]\n"
|
|
|
|
" ssh-keygen -A\n"
|
|
|
|
" ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
|
|
|
|
" file ...\n"
|
|
|
|
" ssh-keygen -Q -f krl_file file ...\n");
|
1999-11-24 14:26:21 +01:00
|
|
|
exit(1);
|
1999-11-21 08:31:57 +01:00
|
|
|
}
|
|
|
|
|
1999-11-24 14:26:21 +01:00
|
|
|
/*
|
|
|
|
* Main program for key management.
|
|
|
|
*/
|
1999-10-27 05:42:43 +02:00
|
|
|
int
|
2007-01-05 06:22:57 +01:00
|
|
|
main(int argc, char **argv)
|
1999-10-27 05:42:43 +02:00
|
|
|
{
|
2002-01-22 13:05:08 +01:00
|
|
|
char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2;
|
2011-10-18 07:05:19 +02:00
|
|
|
char *checkpoint = NULL;
|
2013-01-18 01:44:04 +01:00
|
|
|
char out_file[MAXPATHLEN], *ep, *rr_hostname = NULL;
|
2001-03-09 19:19:24 +01:00
|
|
|
Key *private, *public;
|
1999-11-24 14:26:21 +01:00
|
|
|
struct passwd *pw;
|
|
|
|
struct stat st;
|
2010-02-11 23:21:02 +01:00
|
|
|
int opt, type, fd;
|
2013-12-07 00:41:55 +01:00
|
|
|
u_int32_t memory = 0, generator_wanted = 0;
|
2003-08-02 14:40:07 +02:00
|
|
|
int do_gen_candidates = 0, do_screen_candidates = 0;
|
2013-01-18 01:44:04 +01:00
|
|
|
int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
|
2012-07-06 05:44:19 +02:00
|
|
|
unsigned long start_lineno = 0, lines_to_process = 0;
|
2003-08-02 14:40:07 +02:00
|
|
|
BIGNUM *start = NULL;
|
1999-11-24 14:26:21 +01:00
|
|
|
FILE *f;
|
2005-05-26 04:19:39 +02:00
|
|
|
const char *errstr;
|
2000-11-13 12:57:25 +01:00
|
|
|
|
1999-11-24 14:26:21 +01:00
|
|
|
extern int optind;
|
|
|
|
extern char *optarg;
|
|
|
|
|
2005-10-03 10:11:24 +02:00
|
|
|
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
|
|
|
|
sanitise_stdfd();
|
|
|
|
|
2007-01-14 00:19:59 +01:00
|
|
|
__progname = ssh_get_progname(argv[0]);
|
2000-07-09 14:42:32 +02:00
|
|
|
|
2010-09-10 03:12:09 +02:00
|
|
|
OpenSSL_add_all_algorithms();
|
2007-01-05 06:22:57 +01:00
|
|
|
log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
|
2003-08-02 14:40:07 +02:00
|
|
|
|
2002-07-20 21:05:40 +02:00
|
|
|
seed_rng();
|
2000-04-29 15:57:08 +02:00
|
|
|
|
1999-11-25 01:54:57 +01:00
|
|
|
/* we need this for the home * directory. */
|
1999-11-24 14:26:21 +01:00
|
|
|
pw = getpwuid(getuid());
|
|
|
|
if (!pw) {
|
2013-07-20 05:22:31 +02:00
|
|
|
printf("No user exists for uid %lu\n", (u_long)getuid());
|
1999-11-24 14:26:21 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
2000-04-29 15:57:08 +02:00
|
|
|
if (gethostname(hostname, sizeof(hostname)) < 0) {
|
|
|
|
perror("gethostname");
|
|
|
|
exit(1);
|
|
|
|
}
|
1999-11-25 01:54:57 +01:00
|
|
|
|
2013-12-07 00:41:55 +01:00
|
|
|
/* Remaining characters: EUYdw */
|
|
|
|
while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy"
|
|
|
|
"C:D:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:a:b:f:g:j:m:n:r:s:t:z:")) != -1) {
|
1999-11-24 14:26:21 +01:00
|
|
|
switch (opt) {
|
2011-05-05 06:06:15 +02:00
|
|
|
case 'A':
|
|
|
|
gen_all_hostkeys = 1;
|
|
|
|
break;
|
1999-11-24 14:26:21 +01:00
|
|
|
case 'b':
|
2010-09-10 03:16:37 +02:00
|
|
|
bits = (u_int32_t)strtonum(optarg, 256, 32768, &errstr);
|
2005-05-26 04:19:39 +02:00
|
|
|
if (errstr)
|
|
|
|
fatal("Bits has bad value %s (%s)",
|
|
|
|
optarg, errstr);
|
1999-11-24 14:26:21 +01:00
|
|
|
break;
|
2005-03-01 11:48:35 +01:00
|
|
|
case 'F':
|
|
|
|
find_host = 1;
|
|
|
|
rr_hostname = optarg;
|
|
|
|
break;
|
|
|
|
case 'H':
|
|
|
|
hash_hosts = 1;
|
|
|
|
break;
|
2010-02-26 21:55:05 +01:00
|
|
|
case 'I':
|
|
|
|
cert_key_id = optarg;
|
|
|
|
break;
|
2012-07-06 05:44:19 +02:00
|
|
|
case 'J':
|
|
|
|
lines_to_process = strtoul(optarg, NULL, 10);
|
|
|
|
break;
|
|
|
|
case 'j':
|
|
|
|
start_lineno = strtoul(optarg, NULL, 10);
|
|
|
|
break;
|
2005-03-01 11:48:35 +01:00
|
|
|
case 'R':
|
|
|
|
delete_host = 1;
|
|
|
|
rr_hostname = optarg;
|
|
|
|
break;
|
2010-03-04 21:39:35 +01:00
|
|
|
case 'L':
|
|
|
|
show_cert = 1;
|
|
|
|
break;
|
1999-11-24 14:26:21 +01:00
|
|
|
case 'l':
|
|
|
|
print_fingerprint = 1;
|
|
|
|
break;
|
2001-03-12 04:02:17 +01:00
|
|
|
case 'B':
|
|
|
|
print_bubblebabble = 1;
|
|
|
|
break;
|
2010-07-02 05:35:01 +02:00
|
|
|
case 'm':
|
|
|
|
if (strcasecmp(optarg, "RFC4716") == 0 ||
|
|
|
|
strcasecmp(optarg, "ssh2") == 0) {
|
|
|
|
convert_format = FMT_RFC4716;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (strcasecmp(optarg, "PKCS8") == 0) {
|
|
|
|
convert_format = FMT_PKCS8;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (strcasecmp(optarg, "PEM") == 0) {
|
|
|
|
convert_format = FMT_PEM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
fatal("Unsupported conversion format \"%s\"", optarg);
|
2010-02-26 21:55:05 +01:00
|
|
|
case 'n':
|
|
|
|
cert_principals = optarg;
|
|
|
|
break;
|
2013-12-07 00:41:55 +01:00
|
|
|
case 'o':
|
|
|
|
use_new_format = 1;
|
|
|
|
break;
|
1999-11-24 14:26:21 +01:00
|
|
|
case 'p':
|
|
|
|
change_passphrase = 1;
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
change_comment = 1;
|
|
|
|
break;
|
|
|
|
case 'f':
|
2005-05-26 04:16:18 +02:00
|
|
|
if (strlcpy(identity_file, optarg, sizeof(identity_file)) >=
|
|
|
|
sizeof(identity_file))
|
|
|
|
fatal("Identity filename too long");
|
1999-11-24 14:26:21 +01:00
|
|
|
have_identity = 1;
|
|
|
|
break;
|
2003-05-15 02:19:46 +02:00
|
|
|
case 'g':
|
|
|
|
print_generic = 1;
|
|
|
|
break;
|
1999-11-24 14:26:21 +01:00
|
|
|
case 'P':
|
|
|
|
identity_passphrase = optarg;
|
|
|
|
break;
|
|
|
|
case 'N':
|
|
|
|
identity_new_passphrase = optarg;
|
|
|
|
break;
|
2013-01-18 01:44:04 +01:00
|
|
|
case 'Q':
|
|
|
|
check_krl = 1;
|
|
|
|
break;
|
2010-02-26 21:55:05 +01:00
|
|
|
case 'O':
|
2010-04-16 07:56:21 +02:00
|
|
|
add_cert_option(optarg);
|
2010-02-26 21:55:05 +01:00
|
|
|
break;
|
2013-12-07 00:41:55 +01:00
|
|
|
case 'Z':
|
|
|
|
new_format_cipher = optarg;
|
|
|
|
break;
|
1999-11-24 14:26:21 +01:00
|
|
|
case 'C':
|
|
|
|
identity_comment = optarg;
|
|
|
|
break;
|
|
|
|
case 'q':
|
|
|
|
quiet = 1;
|
|
|
|
break;
|
2001-04-22 19:15:46 +02:00
|
|
|
case 'e':
|
2000-04-29 15:57:08 +02:00
|
|
|
case 'x':
|
2001-04-22 19:15:46 +02:00
|
|
|
/* export key */
|
2010-07-02 05:35:01 +02:00
|
|
|
convert_to = 1;
|
2000-04-29 15:57:08 +02:00
|
|
|
break;
|
2010-02-26 21:55:05 +01:00
|
|
|
case 'h':
|
|
|
|
cert_key_type = SSH2_CERT_TYPE_HOST;
|
2010-05-21 06:58:32 +02:00
|
|
|
certflags_flags = 0;
|
2010-02-26 21:55:05 +01:00
|
|
|
break;
|
2013-01-18 01:44:04 +01:00
|
|
|
case 'k':
|
|
|
|
gen_krl = 1;
|
|
|
|
break;
|
2001-04-22 19:15:46 +02:00
|
|
|
case 'i':
|
2000-04-29 15:57:08 +02:00
|
|
|
case 'X':
|
2001-04-22 19:15:46 +02:00
|
|
|
/* import key */
|
2010-07-02 05:35:01 +02:00
|
|
|
convert_from = 1;
|
2000-04-29 15:57:08 +02:00
|
|
|
break;
|
|
|
|
case 'y':
|
|
|
|
print_public = 1;
|
|
|
|
break;
|
2010-02-26 21:55:05 +01:00
|
|
|
case 's':
|
|
|
|
ca_key_path = optarg;
|
|
|
|
break;
|
2000-11-13 12:57:25 +01:00
|
|
|
case 't':
|
|
|
|
key_type_name = optarg;
|
|
|
|
break;
|
2001-08-06 23:44:05 +02:00
|
|
|
case 'D':
|
2010-02-11 23:21:02 +01:00
|
|
|
pkcs11provider = optarg;
|
2001-07-04 05:44:03 +02:00
|
|
|
break;
|
2013-01-18 01:44:04 +01:00
|
|
|
case 'u':
|
|
|
|
update_krl = 1;
|
|
|
|
break;
|
2003-12-31 01:34:51 +01:00
|
|
|
case 'v':
|
|
|
|
if (log_level == SYSLOG_LEVEL_INFO)
|
|
|
|
log_level = SYSLOG_LEVEL_DEBUG1;
|
|
|
|
else {
|
2004-07-17 08:12:08 +02:00
|
|
|
if (log_level >= SYSLOG_LEVEL_DEBUG1 &&
|
2003-12-31 01:34:51 +01:00
|
|
|
log_level < SYSLOG_LEVEL_DEBUG3)
|
|
|
|
log_level++;
|
|
|
|
}
|
|
|
|
break;
|
2003-05-15 02:19:46 +02:00
|
|
|
case 'r':
|
2005-03-01 11:48:35 +01:00
|
|
|
rr_hostname = optarg;
|
2003-05-15 02:19:46 +02:00
|
|
|
break;
|
2003-08-02 14:40:07 +02:00
|
|
|
case 'W':
|
2006-03-26 05:27:57 +02:00
|
|
|
generator_wanted = (u_int32_t)strtonum(optarg, 1,
|
|
|
|
UINT_MAX, &errstr);
|
2005-05-26 04:16:18 +02:00
|
|
|
if (errstr)
|
|
|
|
fatal("Desired generator has bad value: %s (%s)",
|
|
|
|
optarg, errstr);
|
2003-08-02 14:40:07 +02:00
|
|
|
break;
|
|
|
|
case 'a':
|
2013-12-07 00:41:55 +01:00
|
|
|
rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr);
|
2005-05-26 04:16:18 +02:00
|
|
|
if (errstr)
|
2013-12-07 00:41:55 +01:00
|
|
|
fatal("Invalid number: %s (%s)",
|
2005-05-26 04:16:18 +02:00
|
|
|
optarg, errstr);
|
2003-08-02 14:40:07 +02:00
|
|
|
break;
|
|
|
|
case 'M':
|
2006-03-26 05:27:57 +02:00
|
|
|
memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr);
|
2010-04-16 07:56:21 +02:00
|
|
|
if (errstr)
|
2005-05-26 04:16:18 +02:00
|
|
|
fatal("Memory limit is %s: %s", errstr, optarg);
|
2003-08-02 14:40:07 +02:00
|
|
|
break;
|
|
|
|
case 'G':
|
|
|
|
do_gen_candidates = 1;
|
2005-05-26 04:16:18 +02:00
|
|
|
if (strlcpy(out_file, optarg, sizeof(out_file)) >=
|
|
|
|
sizeof(out_file))
|
|
|
|
fatal("Output filename too long");
|
2003-08-02 14:40:07 +02:00
|
|
|
break;
|
|
|
|
case 'T':
|
|
|
|
do_screen_candidates = 1;
|
2005-05-26 04:16:18 +02:00
|
|
|
if (strlcpy(out_file, optarg, sizeof(out_file)) >=
|
|
|
|
sizeof(out_file))
|
|
|
|
fatal("Output filename too long");
|
2003-08-02 14:40:07 +02:00
|
|
|
break;
|
2011-10-18 07:05:19 +02:00
|
|
|
case 'K':
|
|
|
|
if (strlen(optarg) >= MAXPATHLEN)
|
|
|
|
fatal("Checkpoint filename too long");
|
|
|
|
checkpoint = xstrdup(optarg);
|
|
|
|
break;
|
2003-08-02 14:40:07 +02:00
|
|
|
case 'S':
|
|
|
|
/* XXX - also compare length against bits */
|
|
|
|
if (BN_hex2bn(&start, optarg) == 0)
|
|
|
|
fatal("Invalid start point.");
|
|
|
|
break;
|
2010-02-26 21:55:05 +01:00
|
|
|
case 'V':
|
|
|
|
parse_cert_times(optarg);
|
|
|
|
break;
|
2010-04-16 07:56:21 +02:00
|
|
|
case 'z':
|
2012-11-14 09:04:33 +01:00
|
|
|
errno = 0;
|
|
|
|
cert_serial = strtoull(optarg, &ep, 10);
|
|
|
|
if (*optarg < '0' || *optarg > '9' || *ep != '\0' ||
|
|
|
|
(errno == ERANGE && cert_serial == ULLONG_MAX))
|
|
|
|
fatal("Invalid serial number \"%s\"", optarg);
|
2010-04-16 07:56:21 +02:00
|
|
|
break;
|
1999-11-24 14:26:21 +01:00
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
}
|
2003-12-31 01:34:51 +01:00
|
|
|
|
|
|
|
/* reinit */
|
2007-01-05 06:22:57 +01:00
|
|
|
log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1);
|
2003-12-31 01:34:51 +01:00
|
|
|
|
2010-02-26 21:55:05 +01:00
|
|
|
argv += optind;
|
|
|
|
argc -= optind;
|
|
|
|
|
|
|
|
if (ca_key_path != NULL) {
|
2013-01-18 01:44:04 +01:00
|
|
|
if (argc < 1 && !gen_krl) {
|
2010-02-26 21:55:05 +01:00
|
|
|
printf("Too few arguments.\n");
|
|
|
|
usage();
|
|
|
|
}
|
2013-01-18 01:44:04 +01:00
|
|
|
} else if (argc > 0 && !gen_krl && !check_krl) {
|
1999-11-24 14:26:21 +01:00
|
|
|
printf("Too many arguments.\n");
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
if (change_passphrase && change_comment) {
|
|
|
|
printf("Can only have one of -p and -c.\n");
|
|
|
|
usage();
|
|
|
|
}
|
2008-06-08 04:54:29 +02:00
|
|
|
if (print_fingerprint && (delete_host || hash_hosts)) {
|
2013-01-09 05:58:00 +01:00
|
|
|
printf("Cannot use -l with -H or -R.\n");
|
2008-06-08 04:54:29 +02:00
|
|
|
usage();
|
|
|
|
}
|
2014-05-15 06:24:09 +02:00
|
|
|
#ifdef WITH_OPENSSL
|
2013-01-18 01:44:04 +01:00
|
|
|
if (gen_krl) {
|
|
|
|
do_gen_krl(pw, update_krl, argc, argv);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
if (check_krl) {
|
|
|
|
do_check_krl(pw, argc, argv);
|
|
|
|
return (0);
|
|
|
|
}
|
2014-05-15 06:24:09 +02:00
|
|
|
#endif
|
2010-02-26 21:55:05 +01:00
|
|
|
if (ca_key_path != NULL) {
|
|
|
|
if (cert_key_id == NULL)
|
|
|
|
fatal("Must specify key id (-I) when certifying");
|
|
|
|
do_ca_sign(pw, argc, argv);
|
|
|
|
}
|
2010-03-04 21:39:35 +01:00
|
|
|
if (show_cert)
|
|
|
|
do_show_cert(pw);
|
2005-03-01 11:48:35 +01:00
|
|
|
if (delete_host || hash_hosts || find_host)
|
|
|
|
do_known_hosts(pw, rr_hostname);
|
2013-01-09 05:58:00 +01:00
|
|
|
if (pkcs11provider != NULL)
|
|
|
|
do_download(pw);
|
2001-03-12 04:02:17 +01:00
|
|
|
if (print_fingerprint || print_bubblebabble)
|
1999-11-24 14:26:21 +01:00
|
|
|
do_fingerprint(pw);
|
|
|
|
if (change_passphrase)
|
|
|
|
do_change_passphrase(pw);
|
2002-03-11 12:53:29 +01:00
|
|
|
if (change_comment)
|
|
|
|
do_change_comment(pw);
|
2014-05-15 06:24:09 +02:00
|
|
|
#ifdef WITH_OPENSSL
|
2010-07-02 05:35:01 +02:00
|
|
|
if (convert_to)
|
|
|
|
do_convert_to(pw);
|
|
|
|
if (convert_from)
|
|
|
|
do_convert_from(pw);
|
2014-05-15 06:24:09 +02:00
|
|
|
#endif
|
2000-04-29 15:57:08 +02:00
|
|
|
if (print_public)
|
|
|
|
do_print_public(pw);
|
2005-03-01 11:48:35 +01:00
|
|
|
if (rr_hostname != NULL) {
|
2006-03-26 04:48:01 +02:00
|
|
|
unsigned int n = 0;
|
|
|
|
|
|
|
|
if (have_identity) {
|
|
|
|
n = do_print_resource_record(pw,
|
|
|
|
identity_file, rr_hostname);
|
|
|
|
if (n == 0) {
|
|
|
|
perror(identity_file);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
} else {
|
|
|
|
|
|
|
|
n += do_print_resource_record(pw,
|
|
|
|
_PATH_HOST_RSA_KEY_FILE, rr_hostname);
|
|
|
|
n += do_print_resource_record(pw,
|
|
|
|
_PATH_HOST_DSA_KEY_FILE, rr_hostname);
|
2012-06-20 13:51:11 +02:00
|
|
|
n += do_print_resource_record(pw,
|
|
|
|
_PATH_HOST_ECDSA_KEY_FILE, rr_hostname);
|
2014-05-15 05:45:58 +02:00
|
|
|
n += do_print_resource_record(pw,
|
|
|
|
_PATH_HOST_ED25519_KEY_FILE, rr_hostname);
|
2006-03-26 04:48:01 +02:00
|
|
|
if (n == 0)
|
|
|
|
fatal("no keys found.");
|
|
|
|
exit(0);
|
|
|
|
}
|
2003-05-15 02:19:46 +02:00
|
|
|
}
|
1999-11-24 14:26:21 +01:00
|
|
|
|
2003-08-02 14:40:07 +02:00
|
|
|
if (do_gen_candidates) {
|
|
|
|
FILE *out = fopen(out_file, "w");
|
2003-11-21 13:56:47 +01:00
|
|
|
|
2003-08-02 14:40:07 +02:00
|
|
|
if (out == NULL) {
|
|
|
|
error("Couldn't open modulus candidate file \"%s\": %s",
|
|
|
|
out_file, strerror(errno));
|
|
|
|
return (1);
|
|
|
|
}
|
2005-11-05 04:52:18 +01:00
|
|
|
if (bits == 0)
|
|
|
|
bits = DEFAULT_BITS;
|
2003-08-02 14:40:07 +02:00
|
|
|
if (gen_candidates(out, memory, bits, start) != 0)
|
2005-11-05 05:07:33 +01:00
|
|
|
fatal("modulus candidate generation failed");
|
2003-08-02 14:40:07 +02:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_screen_candidates) {
|
|
|
|
FILE *in;
|
2013-02-12 01:03:36 +01:00
|
|
|
FILE *out = fopen(out_file, "a");
|
2003-08-02 14:40:07 +02:00
|
|
|
|
|
|
|
if (have_identity && strcmp(identity_file, "-") != 0) {
|
|
|
|
if ((in = fopen(identity_file, "r")) == NULL) {
|
|
|
|
fatal("Couldn't open modulus candidate "
|
2003-11-21 13:48:55 +01:00
|
|
|
"file \"%s\": %s", identity_file,
|
2003-08-02 14:40:07 +02:00
|
|
|
strerror(errno));
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
in = stdin;
|
|
|
|
|
|
|
|
if (out == NULL) {
|
|
|
|
fatal("Couldn't open moduli file \"%s\": %s",
|
|
|
|
out_file, strerror(errno));
|
|
|
|
}
|
2013-12-07 00:41:55 +01:00
|
|
|
if (prime_test(in, out, rounds == 0 ? 100 : rounds,
|
|
|
|
generator_wanted, checkpoint,
|
2012-07-06 05:44:19 +02:00
|
|
|
start_lineno, lines_to_process) != 0)
|
2005-11-05 05:07:33 +01:00
|
|
|
fatal("modulus screening failed");
|
2003-08-21 08:44:07 +02:00
|
|
|
return (0);
|
2003-08-02 14:40:07 +02:00
|
|
|
}
|
|
|
|
|
2011-05-05 06:06:15 +02:00
|
|
|
if (gen_all_hostkeys) {
|
|
|
|
do_gen_all_hostkeys(pw);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2005-11-05 05:15:49 +01:00
|
|
|
if (key_type_name == NULL)
|
|
|
|
key_type_name = "rsa";
|
|
|
|
|
2000-11-29 02:18:44 +01:00
|
|
|
type = key_type_from_name(key_type_name);
|
2011-05-05 06:06:15 +02:00
|
|
|
type_bits_valid(type, &bits);
|
|
|
|
|
2005-11-29 03:10:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
printf("Generating public/private %s key pair.\n", key_type_name);
|
2000-11-13 12:57:25 +01:00
|
|
|
private = key_generate(type, bits);
|
|
|
|
if (private == NULL) {
|
2009-02-21 22:47:02 +01:00
|
|
|
fprintf(stderr, "key_generate failed\n");
|
2000-11-13 12:57:25 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
public = key_from_private(private);
|
1999-11-24 14:26:21 +01:00
|
|
|
|
|
|
|
if (!have_identity)
|
|
|
|
ask_filename(pw, "Enter file in which to save the key");
|
|
|
|
|
2005-11-05 05:14:59 +01:00
|
|
|
/* Create ~/.ssh directory if it doesn't already exist. */
|
2010-05-10 03:52:00 +02:00
|
|
|
snprintf(dotsshdir, sizeof dotsshdir, "%s/%s",
|
|
|
|
pw->pw_dir, _PATH_SSH_USER_DIR);
|
|
|
|
if (strstr(identity_file, dotsshdir) != NULL) {
|
|
|
|
if (stat(dotsshdir, &st) < 0) {
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
error("Could not stat %s: %s", dotsshdir,
|
|
|
|
strerror(errno));
|
|
|
|
} else if (mkdir(dotsshdir, 0700) < 0) {
|
|
|
|
error("Could not create directory '%s': %s",
|
|
|
|
dotsshdir, strerror(errno));
|
|
|
|
} else if (!quiet)
|
|
|
|
printf("Created directory '%s'.\n", dotsshdir);
|
|
|
|
}
|
1999-11-24 14:26:21 +01:00
|
|
|
}
|
|
|
|
/* If the file already exists, ask the user to confirm. */
|
|
|
|
if (stat(identity_file, &st) >= 0) {
|
|
|
|
char yesno[3];
|
|
|
|
printf("%s already exists.\n", identity_file);
|
|
|
|
printf("Overwrite (y/n)? ");
|
|
|
|
fflush(stdout);
|
|
|
|
if (fgets(yesno, sizeof(yesno), stdin) == NULL)
|
|
|
|
exit(1);
|
|
|
|
if (yesno[0] != 'y' && yesno[0] != 'Y')
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
/* Ask for a passphrase (twice). */
|
|
|
|
if (identity_passphrase)
|
|
|
|
passphrase1 = xstrdup(identity_passphrase);
|
|
|
|
else if (identity_new_passphrase)
|
|
|
|
passphrase1 = xstrdup(identity_new_passphrase);
|
|
|
|
else {
|
|
|
|
passphrase_again:
|
|
|
|
passphrase1 =
|
2001-06-25 07:20:31 +02:00
|
|
|
read_passphrase("Enter passphrase (empty for no "
|
|
|
|
"passphrase): ", RP_ALLOW_STDIN);
|
|
|
|
passphrase2 = read_passphrase("Enter same passphrase again: ",
|
|
|
|
RP_ALLOW_STDIN);
|
1999-11-24 14:26:21 +01:00
|
|
|
if (strcmp(passphrase1, passphrase2) != 0) {
|
2001-06-25 07:20:31 +02:00
|
|
|
/*
|
|
|
|
* The passphrases do not match. Clear them and
|
|
|
|
* retry.
|
|
|
|
*/
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase1, strlen(passphrase1));
|
|
|
|
explicit_bzero(passphrase2, strlen(passphrase2));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(passphrase1);
|
|
|
|
free(passphrase2);
|
1999-11-24 14:26:21 +01:00
|
|
|
printf("Passphrases do not match. Try again.\n");
|
|
|
|
goto passphrase_again;
|
|
|
|
}
|
|
|
|
/* Clear the other copy of the passphrase. */
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase2, strlen(passphrase2));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(passphrase2);
|
1999-11-24 14:26:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (identity_comment) {
|
|
|
|
strlcpy(comment, identity_comment, sizeof(comment));
|
|
|
|
} else {
|
2008-11-11 06:31:43 +01:00
|
|
|
/* Create default comment field for the passphrase. */
|
1999-11-24 14:26:21 +01:00
|
|
|
snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save the key with the given passphrase and comment. */
|
2013-12-07 00:41:55 +01:00
|
|
|
if (!key_save_private(private, identity_file, passphrase1, comment,
|
|
|
|
use_new_format, new_format_cipher, rounds)) {
|
2001-04-16 04:00:02 +02:00
|
|
|
printf("Saving the key failed: %s.\n", identity_file);
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase1, strlen(passphrase1));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(passphrase1);
|
1999-11-24 14:26:21 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
/* Clear the passphrase. */
|
2014-02-04 01:20:14 +01:00
|
|
|
explicit_bzero(passphrase1, strlen(passphrase1));
|
2013-06-01 23:31:17 +02:00
|
|
|
free(passphrase1);
|
1999-11-24 14:26:21 +01:00
|
|
|
|
|
|
|
/* Clear the private key and the random number generator. */
|
2000-11-13 12:57:25 +01:00
|
|
|
key_free(private);
|
1999-11-24 14:26:21 +01:00
|
|
|
|
|
|
|
if (!quiet)
|
|
|
|
printf("Your identification has been saved in %s.\n", identity_file);
|
|
|
|
|
|
|
|
strlcat(identity_file, ".pub", sizeof(identity_file));
|
2001-03-09 19:19:24 +01:00
|
|
|
fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
|
|
if (fd == -1) {
|
1999-11-24 14:26:21 +01:00
|
|
|
printf("Could not save your public key in %s\n", identity_file);
|
|
|
|
exit(1);
|
|
|
|
}
|
2001-03-09 19:19:24 +01:00
|
|
|
f = fdopen(fd, "w");
|
|
|
|
if (f == NULL) {
|
2009-02-21 22:47:02 +01:00
|
|
|
printf("fdopen %s failed\n", identity_file);
|
2001-03-09 19:19:24 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
2000-04-29 15:57:08 +02:00
|
|
|
if (!key_write(public, f))
|
2009-02-21 22:47:02 +01:00
|
|
|
fprintf(stderr, "write key failed\n");
|
2000-04-29 15:57:08 +02:00
|
|
|
fprintf(f, " %s\n", comment);
|
1999-11-24 14:26:21 +01:00
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
if (!quiet) {
|
2001-03-13 05:57:58 +01:00
|
|
|
char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX);
|
- grunk@cvs.openbsd.org 2008/06/11 21:01:35
[ssh_config.5 key.h readconf.c readconf.h ssh-keygen.1 ssh-keygen.c key.c
sshconnect.c]
Introduce SSH Fingerprint ASCII Visualization, a technique inspired by the
graphical hash visualization schemes known as "random art", and by
Dan Kaminsky's musings on the subject during a BlackOp talk at the
23C3 in Berlin.
Scientific publication (original paper):
"Hash Visualization: a New Technique to improve Real-World Security",
Perrig A. and Song D., 1999, International Workshop on Cryptographic
Techniques and E-Commerce (CrypTEC '99)
http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
The algorithm used here is a worm crawling over a discrete plane,
leaving a trace (augmenting the field) everywhere it goes.
Movement is taken from dgst_raw 2bit-wise. Bumping into walls
makes the respective movement vector be ignored for this turn,
thus switching to the other color of the chessboard.
Graphs are not unambiguous for now, because circles in graphs can be
walked in either direction.
discussions with several people,
help, corrections and ok markus@ djm@
2008-06-12 20:40:35 +02:00
|
|
|
char *ra = key_fingerprint(public, SSH_FP_MD5,
|
|
|
|
SSH_FP_RANDOMART);
|
2000-04-29 15:57:08 +02:00
|
|
|
printf("Your public key has been saved in %s.\n",
|
|
|
|
identity_file);
|
1999-11-24 14:26:21 +01:00
|
|
|
printf("The key fingerprint is:\n");
|
2001-03-13 05:57:58 +01:00
|
|
|
printf("%s %s\n", fp, comment);
|
- grunk@cvs.openbsd.org 2008/06/11 21:01:35
[ssh_config.5 key.h readconf.c readconf.h ssh-keygen.1 ssh-keygen.c key.c
sshconnect.c]
Introduce SSH Fingerprint ASCII Visualization, a technique inspired by the
graphical hash visualization schemes known as "random art", and by
Dan Kaminsky's musings on the subject during a BlackOp talk at the
23C3 in Berlin.
Scientific publication (original paper):
"Hash Visualization: a New Technique to improve Real-World Security",
Perrig A. and Song D., 1999, International Workshop on Cryptographic
Techniques and E-Commerce (CrypTEC '99)
http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
The algorithm used here is a worm crawling over a discrete plane,
leaving a trace (augmenting the field) everywhere it goes.
Movement is taken from dgst_raw 2bit-wise. Bumping into walls
makes the respective movement vector be ignored for this turn,
thus switching to the other color of the chessboard.
Graphs are not unambiguous for now, because circles in graphs can be
walked in either direction.
discussions with several people,
help, corrections and ok markus@ djm@
2008-06-12 20:40:35 +02:00
|
|
|
printf("The key's randomart image is:\n");
|
|
|
|
printf("%s\n", ra);
|
2013-06-01 23:31:17 +02:00
|
|
|
free(ra);
|
|
|
|
free(fp);
|
1999-10-27 05:42:43 +02:00
|
|
|
}
|
2000-04-29 15:57:08 +02:00
|
|
|
|
|
|
|
key_free(public);
|
1999-11-24 14:26:21 +01:00
|
|
|
exit(0);
|
1999-10-27 05:42:43 +02:00
|
|
|
}
|