upstream commit
add RevokedHostKeys option for the client Allow textfile or KRL-based revocation of hostkeys.
This commit is contained in:
parent
74de254bb9
commit
5e39a49930
62
auth.c
62
auth.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: auth.c,v 1.106 2014/07/15 15:54:14 millert Exp $ */
|
||||
/* $OpenBSD: auth.c,v 1.107 2014/12/04 02:24:32 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
*
|
||||
|
@ -71,7 +71,8 @@
|
|||
#endif
|
||||
#include "authfile.h"
|
||||
#include "monitor_wrap.h"
|
||||
#include "krl.h"
|
||||
#include "authfile.h"
|
||||
#include "ssherr.h"
|
||||
#include "compat.h"
|
||||
|
||||
/* import */
|
||||
|
@ -673,43 +674,38 @@ getpwnamallow(const char *user)
|
|||
int
|
||||
auth_key_is_revoked(Key *key)
|
||||
{
|
||||
#ifdef WITH_OPENSSL
|
||||
char *key_fp;
|
||||
char *fp = NULL;
|
||||
int r;
|
||||
|
||||
if (options.revoked_keys_file == NULL)
|
||||
return 0;
|
||||
switch (ssh_krl_file_contains_key(options.revoked_keys_file, key)) {
|
||||
if ((fp = sshkey_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX)) == NULL) {
|
||||
r = SSH_ERR_ALLOC_FAIL;
|
||||
error("%s: fingerprint key: %s", __func__, ssh_err(r));
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = sshkey_check_revoked(key, options.revoked_keys_file);
|
||||
switch (r) {
|
||||
case 0:
|
||||
return 0; /* Not revoked */
|
||||
case -2:
|
||||
break; /* Not a KRL */
|
||||
break; /* not revoked */
|
||||
case SSH_ERR_KEY_REVOKED:
|
||||
error("Authentication key %s %s revoked by file %s",
|
||||
sshkey_type(key), fp, options.revoked_keys_file);
|
||||
goto out;
|
||||
default:
|
||||
goto revoked;
|
||||
error("Error checking authentication key %s %s in "
|
||||
"revoked keys file %s: %s", sshkey_type(key), fp,
|
||||
options.revoked_keys_file, ssh_err(r));
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
debug3("%s: treating %s as a key list", __func__,
|
||||
options.revoked_keys_file);
|
||||
switch (key_in_file(key, options.revoked_keys_file, 0)) {
|
||||
case 0:
|
||||
/* key not revoked */
|
||||
return 0;
|
||||
case -1:
|
||||
/* Error opening revoked_keys_file: refuse all keys */
|
||||
error("Revoked keys file is unreadable: refusing public key "
|
||||
"authentication");
|
||||
return 1;
|
||||
#ifdef WITH_OPENSSL
|
||||
case 1:
|
||||
revoked:
|
||||
/* Key revoked */
|
||||
key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
|
||||
error("WARNING: authentication attempt with a revoked "
|
||||
"%s key %s ", key_type(key), key_fp);
|
||||
free(key_fp);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
fatal("key_in_file returned junk");
|
||||
|
||||
/* Success */
|
||||
r = 0;
|
||||
|
||||
out:
|
||||
free(fp);
|
||||
return r == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: auth2-pubkey.c,v 1.41 2014/07/15 15:54:14 millert Exp $ */
|
||||
/* $OpenBSD: auth2-pubkey.c,v 1.42 2014/12/04 02:24:32 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
*
|
||||
|
@ -434,8 +434,8 @@ user_cert_trusted_ca(struct passwd *pw, Key *key)
|
|||
ca_fp = key_fingerprint(key->cert->signature_key,
|
||||
SSH_FP_MD5, SSH_FP_HEX);
|
||||
|
||||
if (key_in_file(key->cert->signature_key,
|
||||
options.trusted_user_ca_keys, 1) != 1) {
|
||||
if (sshkey_in_file(key->cert->signature_key,
|
||||
options.trusted_user_ca_keys, 1, 0) != 0) {
|
||||
debug2("%s: CA %s %s is not listed in %s", __func__,
|
||||
key_type(key->cert->signature_key), ca_fp,
|
||||
options.trusted_user_ca_keys);
|
||||
|
|
58
authfile.c
58
authfile.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: authfile.c,v 1.107 2014/06/24 01:13:21 djm Exp $ */
|
||||
/* $OpenBSD: authfile.c,v 1.108 2014/12/04 02:24:32 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000, 2013 Markus Friedl. All rights reserved.
|
||||
*
|
||||
|
@ -48,6 +48,7 @@
|
|||
#include "atomicio.h"
|
||||
#include "sshbuf.h"
|
||||
#include "ssherr.h"
|
||||
#include "krl.h"
|
||||
|
||||
#define MAX_KEY_FILE_SIZE (1024 * 1024)
|
||||
|
||||
|
@ -494,11 +495,14 @@ sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
|
|||
/*
|
||||
* Returns success if the specified "key" is listed in the file "filename",
|
||||
* SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
|
||||
* If strict_type is set then the key type must match exactly,
|
||||
* If "strict_type" is set then the key type must match exactly,
|
||||
* otherwise a comparison that ignores certficiate data is performed.
|
||||
* If "check_ca" is set and "key" is a certificate, then its CA key is
|
||||
* also checked and sshkey_in_file() will return success if either is found.
|
||||
*/
|
||||
int
|
||||
sshkey_in_file(struct sshkey *key, const char *filename, int strict_type)
|
||||
sshkey_in_file(struct sshkey *key, const char *filename, int strict_type,
|
||||
int check_ca)
|
||||
{
|
||||
FILE *f;
|
||||
char line[SSH_MAX_PUBKEY_BYTES];
|
||||
|
@ -509,12 +513,8 @@ sshkey_in_file(struct sshkey *key, const char *filename, int strict_type)
|
|||
int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
|
||||
strict_type ? sshkey_equal : sshkey_equal_public;
|
||||
|
||||
if ((f = fopen(filename, "r")) == NULL) {
|
||||
if (errno == ENOENT)
|
||||
return SSH_ERR_KEY_NOT_FOUND;
|
||||
else
|
||||
return SSH_ERR_SYSTEM_ERROR;
|
||||
}
|
||||
if ((f = fopen(filename, "r")) == NULL)
|
||||
return SSH_ERR_SYSTEM_ERROR;
|
||||
|
||||
while (read_keyfile_line(f, filename, line, sizeof(line),
|
||||
&linenum) != -1) {
|
||||
|
@ -538,7 +538,9 @@ sshkey_in_file(struct sshkey *key, const char *filename, int strict_type)
|
|||
}
|
||||
if ((r = sshkey_read(pub, &cp)) != 0)
|
||||
goto out;
|
||||
if (sshkey_compare(key, pub)) {
|
||||
if (sshkey_compare(key, pub) ||
|
||||
(check_ca && sshkey_is_cert(key) &&
|
||||
sshkey_compare(key->cert->signature_key, pub))) {
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
|
@ -553,3 +555,39 @@ sshkey_in_file(struct sshkey *key, const char *filename, int strict_type)
|
|||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks whether the specified key is revoked, returning 0 if not,
|
||||
* SSH_ERR_KEY_REVOKED if it is or another error code if something
|
||||
* unexpected happened.
|
||||
* This will check both the key and, if it is a certificate, its CA key too.
|
||||
* "revoked_keys_file" may be a KRL or a one-per-line list of public keys.
|
||||
*/
|
||||
int
|
||||
sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file)
|
||||
{
|
||||
int r;
|
||||
|
||||
#ifdef WITH_OPENSSL
|
||||
r = ssh_krl_file_contains_key(revoked_keys_file, key);
|
||||
/* If this was not a KRL to begin with then continue below */
|
||||
if (r != SSH_ERR_KRL_BAD_MAGIC)
|
||||
return r;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the file is not a KRL or we can't handle KRLs then attempt to
|
||||
* parse the file as a flat list of keys.
|
||||
*/
|
||||
switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) {
|
||||
case 0:
|
||||
/* Key found => revoked */
|
||||
return SSH_ERR_KEY_REVOKED;
|
||||
case SSH_ERR_KEY_NOT_FOUND:
|
||||
/* Key not found => not revoked */
|
||||
return 0;
|
||||
default:
|
||||
/* Some other error occurred */
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: authfile.h,v 1.19 2014/07/03 23:18:35 djm Exp $ */
|
||||
/* $OpenBSD: authfile.h,v 1.20 2014/12/04 02:24:32 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000, 2013 Markus Friedl. All rights reserved.
|
||||
|
@ -42,6 +42,7 @@ int sshkey_load_private_type(int, const char *, const char *,
|
|||
struct sshkey **, char **, int *);
|
||||
int sshkey_load_private_pem(int, int, const char *, struct sshkey **, char **);
|
||||
int sshkey_perm_ok(int, const char *);
|
||||
int sshkey_in_file(struct sshkey *, const char *, int);
|
||||
int sshkey_in_file(struct sshkey *, const char *, int, int);
|
||||
int sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file);
|
||||
|
||||
#endif
|
||||
|
|
12
readconf.c
12
readconf.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: readconf.c,v 1.222 2014/10/24 02:01:20 lteo Exp $ */
|
||||
/* $OpenBSD: readconf.c,v 1.223 2014/12/04 02:24:32 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -154,7 +154,7 @@ typedef enum {
|
|||
oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass,
|
||||
oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
|
||||
oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
|
||||
oStreamLocalBindMask, oStreamLocalBindUnlink,
|
||||
oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
|
||||
oIgnoredUnknownOption, oDeprecated, oUnsupported
|
||||
} OpCodes;
|
||||
|
||||
|
@ -269,6 +269,7 @@ static struct {
|
|||
{ "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs },
|
||||
{ "streamlocalbindmask", oStreamLocalBindMask },
|
||||
{ "streamlocalbindunlink", oStreamLocalBindUnlink },
|
||||
{ "revokedhostkeys", oRevokedHostKeys },
|
||||
{ "ignoreunknown", oIgnoreUnknown },
|
||||
|
||||
{ NULL, oBadOption }
|
||||
|
@ -1455,6 +1456,10 @@ parse_int:
|
|||
intptr = &options->fwd_opts.streamlocal_bind_unlink;
|
||||
goto parse_flag;
|
||||
|
||||
case oRevokedHostKeys:
|
||||
charptr = &options->revoked_host_keys;
|
||||
goto parse_string;
|
||||
|
||||
case oDeprecated:
|
||||
debug("%s line %d: Deprecated option \"%s\"",
|
||||
filename, linenum, keyword);
|
||||
|
@ -1631,6 +1636,7 @@ initialize_options(Options * options)
|
|||
options->canonicalize_max_dots = -1;
|
||||
options->canonicalize_fallback_local = -1;
|
||||
options->canonicalize_hostname = -1;
|
||||
options->revoked_host_keys = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1818,6 +1824,7 @@ fill_default_options(Options * options)
|
|||
CLEAR_ON_NONE(options->local_command);
|
||||
CLEAR_ON_NONE(options->proxy_command);
|
||||
CLEAR_ON_NONE(options->control_path);
|
||||
CLEAR_ON_NONE(options->revoked_host_keys);
|
||||
/* options->user will be set in the main program if appropriate */
|
||||
/* options->hostname will be set in the main program if appropriate */
|
||||
/* options->host_key_alias should not be set by default */
|
||||
|
@ -2251,6 +2258,7 @@ dump_client_config(Options *o, const char *host)
|
|||
dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
|
||||
dump_cfg_string(oProxyCommand, o->proxy_command);
|
||||
dump_cfg_string(oXAuthLocation, o->xauth_location);
|
||||
dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys);
|
||||
|
||||
dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards);
|
||||
dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: readconf.h,v 1.103 2014/10/08 22:20:25 djm Exp $ */
|
||||
/* $OpenBSD: readconf.h,v 1.104 2014/12/04 02:24:32 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
|
@ -144,6 +144,8 @@ typedef struct {
|
|||
int num_permitted_cnames;
|
||||
struct allowed_cname permitted_cnames[MAX_CANON_DOMAINS];
|
||||
|
||||
char *revoked_host_keys;
|
||||
|
||||
char *ignored_unknown; /* Pattern list of unknown tokens to ignore */
|
||||
} Options;
|
||||
|
||||
|
|
14
ssh_config.5
14
ssh_config.5
|
@ -33,8 +33,8 @@
|
|||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $OpenBSD: ssh_config.5,v 1.195 2014/11/10 22:25:49 djm Exp $
|
||||
.Dd $Mdocdate: November 10 2014 $
|
||||
.\" $OpenBSD: ssh_config.5,v 1.196 2014/12/04 02:24:32 djm Exp $
|
||||
.Dd $Mdocdate: December 4 2014 $
|
||||
.Dt SSH_CONFIG 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -1253,6 +1253,16 @@ and
|
|||
.Fl T
|
||||
flags for
|
||||
.Xr ssh 1 .
|
||||
.It Cm RevokedHostKeys
|
||||
Specifies revoked host public keys.
|
||||
Keys listed in this file will be refused for host authentication.
|
||||
Note that if this file does not exist or is not readable,
|
||||
then host authentication will be refused for all hosts.
|
||||
Keys may be specified as a text file, listing one public key per line, or as
|
||||
an OpenSSH Key Revocation List (KRL) as generated by
|
||||
.Xr ssh-keygen 1 .
|
||||
For more information on KRLs, see the KEY REVOCATION LISTS section in
|
||||
.Xr ssh-keygen 1 .
|
||||
.It Cm RhostsRSAAuthentication
|
||||
Specifies whether to try rhosts based authentication with RSA host
|
||||
authentication.
|
||||
|
|
64
sshconnect.c
64
sshconnect.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: sshconnect.c,v 1.251 2014/07/15 15:54:14 millert Exp $ */
|
||||
/* $OpenBSD: sshconnect.c,v 1.252 2014/12/04 02:24:32 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -62,6 +62,8 @@
|
|||
#include "monitor_fdpass.h"
|
||||
#include "ssh2.h"
|
||||
#include "version.h"
|
||||
#include "authfile.h"
|
||||
#include "ssherr.h"
|
||||
|
||||
char *client_version_string = NULL;
|
||||
char *server_version_string = NULL;
|
||||
|
@ -1219,16 +1221,44 @@ int
|
|||
verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
|
||||
{
|
||||
int r = -1, flags = 0;
|
||||
char *fp;
|
||||
Key *plain = NULL;
|
||||
char *fp = NULL;
|
||||
struct sshkey *plain = NULL;
|
||||
|
||||
fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
|
||||
debug("Server host key: %s %s", key_type(host_key), fp);
|
||||
free(fp);
|
||||
if ((fp = sshkey_fingerprint(host_key,
|
||||
SSH_FP_MD5, SSH_FP_HEX)) == NULL) {
|
||||
error("%s: fingerprint host key: %s", __func__, ssh_err(r));
|
||||
r = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (key_equal(previous_host_key, host_key)) {
|
||||
debug("%s: server host key matches cached key", __func__);
|
||||
return 0;
|
||||
debug("Server host key: %s %s", sshkey_type(host_key), fp);
|
||||
|
||||
if (sshkey_equal(previous_host_key, host_key)) {
|
||||
debug2("%s: server host key %s %s matches cached key",
|
||||
__func__, sshkey_type(host_key), fp);
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check in RevokedHostKeys file if specified */
|
||||
if (options.revoked_host_keys != NULL) {
|
||||
r = sshkey_check_revoked(host_key, options.revoked_host_keys);
|
||||
switch (r) {
|
||||
case 0:
|
||||
break; /* not revoked */
|
||||
case SSH_ERR_KEY_REVOKED:
|
||||
error("Host key %s %s revoked by file %s",
|
||||
sshkey_type(host_key), fp,
|
||||
options.revoked_host_keys);
|
||||
r = -1;
|
||||
goto out;
|
||||
default:
|
||||
error("Error checking host key %s %s in "
|
||||
"revoked keys file %s: %s", sshkey_type(host_key),
|
||||
fp, options.revoked_host_keys, ssh_err(r));
|
||||
r = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.verify_host_key_dns) {
|
||||
|
@ -1236,17 +1266,17 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
|
|||
* XXX certs are not yet supported for DNS, so downgrade
|
||||
* them and try the plain key.
|
||||
*/
|
||||
plain = key_from_private(host_key);
|
||||
if (key_is_cert(plain))
|
||||
key_drop_cert(plain);
|
||||
if ((r = sshkey_from_private(host_key, &plain)) != 0)
|
||||
goto out;
|
||||
if (sshkey_is_cert(plain))
|
||||
sshkey_drop_cert(plain);
|
||||
if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) {
|
||||
if (flags & DNS_VERIFY_FOUND) {
|
||||
if (options.verify_host_key_dns == 1 &&
|
||||
flags & DNS_VERIFY_MATCH &&
|
||||
flags & DNS_VERIFY_SECURE) {
|
||||
key_free(plain);
|
||||
r = 0;
|
||||
goto done;
|
||||
goto out;
|
||||
}
|
||||
if (flags & DNS_VERIFY_MATCH) {
|
||||
matching_host_key_dns = 1;
|
||||
|
@ -1258,14 +1288,14 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
|
|||
}
|
||||
}
|
||||
}
|
||||
key_free(plain);
|
||||
}
|
||||
|
||||
r = check_host_key(host, hostaddr, options.port, host_key, RDRW,
|
||||
options.user_hostfiles, options.num_user_hostfiles,
|
||||
options.system_hostfiles, options.num_system_hostfiles);
|
||||
|
||||
done:
|
||||
out:
|
||||
sshkey_free(plain);
|
||||
free(fp);
|
||||
if (r == 0 && host_key != NULL) {
|
||||
key_free(previous_host_key);
|
||||
previous_host_key = key_from_private(host_key);
|
||||
|
|
Loading…
Reference in New Issue