diff options
-rw-r--r-- | ChangeLog | 13 | ||||
-rw-r--r-- | auth-rh-rsa.c | 5 | ||||
-rw-r--r-- | auth-rsa.c | 5 | ||||
-rw-r--r-- | auth.c | 31 | ||||
-rw-r--r-- | auth.h | 3 | ||||
-rw-r--r-- | auth2-hostbased.c | 5 | ||||
-rw-r--r-- | auth2-pubkey.c | 53 | ||||
-rw-r--r-- | authfile.c | 64 | ||||
-rw-r--r-- | authfile.h | 3 | ||||
-rw-r--r-- | hostfile.c | 102 | ||||
-rw-r--r-- | hostfile.h | 5 | ||||
-rw-r--r-- | servconf.c | 19 | ||||
-rw-r--r-- | servconf.h | 4 | ||||
-rw-r--r-- | ssh-keygen.c | 2 | ||||
-rw-r--r-- | ssh.1 | 20 | ||||
-rw-r--r-- | sshconnect.c | 24 | ||||
-rw-r--r-- | sshd_config.5 | 25 |
17 files changed, 343 insertions, 40 deletions
@@ -22,6 +22,19 @@ | |||
22 | [key.c] | 22 | [key.c] |
23 | use buffer_get_string_ptr_ret() where we are checking the return | 23 | use buffer_get_string_ptr_ret() where we are checking the return |
24 | value explicitly instead of the fatal()-causing buffer_get_string_ptr() | 24 | value explicitly instead of the fatal()-causing buffer_get_string_ptr() |
25 | - djm@cvs.openbsd.org 2010/03/04 10:36:03 | ||
26 | [auth-rh-rsa.c auth-rsa.c auth.c auth.h auth2-hostbased.c auth2-pubkey.c] | ||
27 | [authfile.c authfile.h hostfile.c hostfile.h servconf.c servconf.h] | ||
28 | [ssh-keygen.c ssh.1 sshconnect.c sshd_config.5] | ||
29 | Add a TrustedUserCAKeys option to sshd_config to specify CA keys that | ||
30 | are trusted to authenticate users (in addition than doing it per-user | ||
31 | in authorized_keys). | ||
32 | |||
33 | Add a RevokedKeys option to sshd_config and a @revoked marker to | ||
34 | known_hosts to allow keys to me revoked and banned for user or host | ||
35 | authentication. | ||
36 | |||
37 | feedback and ok markus@ | ||
25 | 38 | ||
26 | 20100303 | 39 | 20100303 |
27 | - (djm) [PROTOCOL.certkeys] Add RCS Ident | 40 | - (djm) [PROTOCOL.certkeys] Add RCS Ident |
diff --git a/auth-rh-rsa.c b/auth-rh-rsa.c index eca750275..b21a0f4a2 100644 --- a/auth-rh-rsa.c +++ b/auth-rh-rsa.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth-rh-rsa.c,v 1.42 2006/08/03 03:34:41 deraadt Exp $ */ | 1 | /* $OpenBSD: auth-rh-rsa.c,v 1.43 2010/03/04 10:36:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -44,6 +44,9 @@ auth_rhosts_rsa_key_allowed(struct passwd *pw, char *cuser, char *chost, | |||
44 | { | 44 | { |
45 | HostStatus host_status; | 45 | HostStatus host_status; |
46 | 46 | ||
47 | if (auth_key_is_revoked(client_host_key)) | ||
48 | return 0; | ||
49 | |||
47 | /* Check if we would accept it using rhosts authentication. */ | 50 | /* Check if we would accept it using rhosts authentication. */ |
48 | if (!auth_rhosts(pw, cuser)) | 51 | if (!auth_rhosts(pw, cuser)) |
49 | return 0; | 52 | return 0; |
diff --git a/auth-rsa.c b/auth-rsa.c index bf5462076..65571a890 100644 --- a/auth-rsa.c +++ b/auth-rsa.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth-rsa.c,v 1.73 2008/07/02 12:03:51 dtucker Exp $ */ | 1 | /* $OpenBSD: auth-rsa.c,v 1.74 2010/03/04 10:36:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -94,6 +94,9 @@ auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) | |||
94 | MD5_CTX md; | 94 | MD5_CTX md; |
95 | int len; | 95 | int len; |
96 | 96 | ||
97 | if (auth_key_is_revoked(key)) | ||
98 | return 0; | ||
99 | |||
97 | /* don't allow short keys */ | 100 | /* don't allow short keys */ |
98 | if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { | 101 | if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { |
99 | error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits", | 102 | error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits", |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth.c,v 1.84 2010/02/09 06:18:46 djm Exp $ */ | 1 | /* $OpenBSD: auth.c,v 1.85 2010/03/04 10:36:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -69,6 +69,7 @@ | |||
69 | #ifdef GSSAPI | 69 | #ifdef GSSAPI |
70 | #include "ssh-gss.h" | 70 | #include "ssh-gss.h" |
71 | #endif | 71 | #endif |
72 | #include "authfile.h" | ||
72 | #include "monitor_wrap.h" | 73 | #include "monitor_wrap.h" |
73 | 74 | ||
74 | /* import */ | 75 | /* import */ |
@@ -582,6 +583,34 @@ getpwnamallow(const char *user) | |||
582 | return (NULL); | 583 | return (NULL); |
583 | } | 584 | } |
584 | 585 | ||
586 | /* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */ | ||
587 | int | ||
588 | auth_key_is_revoked(Key *key) | ||
589 | { | ||
590 | char *key_fp; | ||
591 | |||
592 | if (options.revoked_keys_file == NULL) | ||
593 | return 0; | ||
594 | |||
595 | switch (key_in_file(key, options.revoked_keys_file, 0)) { | ||
596 | case 0: | ||
597 | /* key not revoked */ | ||
598 | return 0; | ||
599 | case -1: | ||
600 | /* Error opening revoked_keys_file: refuse all keys */ | ||
601 | error("Revoked keys file is unreadable: refusing public key " | ||
602 | "authentication"); | ||
603 | return 1; | ||
604 | case 1: | ||
605 | /* Key revoked */ | ||
606 | key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | ||
607 | error("%s key %s is revoked", key_type(key), key_fp); | ||
608 | xfree(key_fp); | ||
609 | return 1; | ||
610 | } | ||
611 | fatal("key_in_file returned junk"); | ||
612 | } | ||
613 | |||
585 | void | 614 | void |
586 | auth_debug_add(const char *fmt,...) | 615 | auth_debug_add(const char *fmt,...) |
587 | { | 616 | { |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth.h,v 1.64 2010/02/26 20:29:54 djm Exp $ */ | 1 | /* $OpenBSD: auth.h,v 1.65 2010/03/04 10:36:03 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 4 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
@@ -171,6 +171,7 @@ char *authorized_keys_file(struct passwd *); | |||
171 | char *authorized_keys_file2(struct passwd *); | 171 | char *authorized_keys_file2(struct passwd *); |
172 | 172 | ||
173 | FILE *auth_openkeyfile(const char *, struct passwd *, int); | 173 | FILE *auth_openkeyfile(const char *, struct passwd *, int); |
174 | int auth_key_is_revoked(Key *); | ||
174 | 175 | ||
175 | HostStatus | 176 | HostStatus |
176 | check_key_in_hostfiles(struct passwd *, Key *, const char *, | 177 | check_key_in_hostfiles(struct passwd *, Key *, const char *, |
diff --git a/auth2-hostbased.c b/auth2-hostbased.c index 041051c53..721646520 100644 --- a/auth2-hostbased.c +++ b/auth2-hostbased.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth2-hostbased.c,v 1.12 2008/07/17 08:51:07 djm Exp $ */ | 1 | /* $OpenBSD: auth2-hostbased.c,v 1.13 2010/03/04 10:36:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -145,6 +145,9 @@ hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, | |||
145 | HostStatus host_status; | 145 | HostStatus host_status; |
146 | int len; | 146 | int len; |
147 | 147 | ||
148 | if (auth_key_is_revoked(key)) | ||
149 | return 0; | ||
150 | |||
148 | resolvedname = get_canonical_hostname(options.use_dns); | 151 | resolvedname = get_canonical_hostname(options.use_dns); |
149 | ipaddr = get_remote_ipaddr(); | 152 | ipaddr = get_remote_ipaddr(); |
150 | 153 | ||
diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 66ca5266b..51aa77487 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth2-pubkey.c,v 1.20 2010/02/26 20:29:54 djm Exp $ */ | 1 | /* $OpenBSD: auth2-pubkey.c,v 1.21 2010/03/04 10:36:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -56,6 +56,7 @@ | |||
56 | #endif | 56 | #endif |
57 | #include "monitor_wrap.h" | 57 | #include "monitor_wrap.h" |
58 | #include "misc.h" | 58 | #include "misc.h" |
59 | #include "authfile.h" | ||
59 | 60 | ||
60 | /* import */ | 61 | /* import */ |
61 | extern ServerOptions options; | 62 | extern ServerOptions options; |
@@ -276,6 +277,47 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) | |||
276 | return found_key; | 277 | return found_key; |
277 | } | 278 | } |
278 | 279 | ||
280 | /* Authenticate a certificate key against TrustedUserCAKeys */ | ||
281 | static int | ||
282 | user_cert_trusted_ca(struct passwd *pw, Key *key) | ||
283 | { | ||
284 | char *key_fp, *ca_fp; | ||
285 | const char *reason; | ||
286 | int ret = 0; | ||
287 | |||
288 | if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL) | ||
289 | return 0; | ||
290 | |||
291 | key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | ||
292 | ca_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | ||
293 | |||
294 | if (key_in_file(key->cert->signature_key, | ||
295 | options.trusted_user_ca_keys, 1) != 1) { | ||
296 | debug2("%s: CA %s %s is not listed in %s", __func__, | ||
297 | key_type(key->cert->signature_key), ca_fp, | ||
298 | options.trusted_user_ca_keys); | ||
299 | goto out; | ||
300 | } | ||
301 | if (key_cert_check_authority(key, 0, 1, pw->pw_name, &reason) != 0) { | ||
302 | error("%s", reason); | ||
303 | auth_debug_add("%s", reason); | ||
304 | goto out; | ||
305 | } | ||
306 | if (auth_cert_constraints(&key->cert->constraints, pw) != 0) | ||
307 | goto out; | ||
308 | |||
309 | verbose("%s certificate %s allowed by trusted %s key %s", | ||
310 | key_type(key), key_fp, key_type(key->cert->signature_key), ca_fp); | ||
311 | ret = 1; | ||
312 | |||
313 | out: | ||
314 | if (key_fp != NULL) | ||
315 | xfree(key_fp); | ||
316 | if (ca_fp != NULL) | ||
317 | xfree(ca_fp); | ||
318 | return ret; | ||
319 | } | ||
320 | |||
279 | /* check whether given key is in .ssh/authorized_keys* */ | 321 | /* check whether given key is in .ssh/authorized_keys* */ |
280 | int | 322 | int |
281 | user_key_allowed(struct passwd *pw, Key *key) | 323 | user_key_allowed(struct passwd *pw, Key *key) |
@@ -283,6 +325,15 @@ user_key_allowed(struct passwd *pw, Key *key) | |||
283 | int success; | 325 | int success; |
284 | char *file; | 326 | char *file; |
285 | 327 | ||
328 | if (auth_key_is_revoked(key)) | ||
329 | return 0; | ||
330 | if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key)) | ||
331 | return 0; | ||
332 | |||
333 | success = user_cert_trusted_ca(pw, key); | ||
334 | if (success) | ||
335 | return success; | ||
336 | |||
286 | file = authorized_keys_file(pw); | 337 | file = authorized_keys_file(pw); |
287 | success = user_key_allowed2(pw, key, file); | 338 | success = user_key_allowed2(pw, key, file); |
288 | xfree(file); | 339 | xfree(file); |
diff --git a/authfile.c b/authfile.c index 2c615709d..224c6aa80 100644 --- a/authfile.c +++ b/authfile.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: authfile.c,v 1.79 2010/01/12 00:16:47 dtucker Exp $ */ | 1 | /* $OpenBSD: authfile.c,v 1.80 2010/03/04 10:36:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -692,3 +692,65 @@ key_load_public(const char *filename, char **commentp) | |||
692 | key_free(pub); | 692 | key_free(pub); |
693 | return NULL; | 693 | return NULL; |
694 | } | 694 | } |
695 | |||
696 | /* | ||
697 | * Returns 1 if the specified "key" is listed in the file "filename", | ||
698 | * 0 if the key is not listed or -1 on error. | ||
699 | * If strict_type is set then the key type must match exactly, | ||
700 | * otherwise a comparison that ignores certficiate data is performed. | ||
701 | */ | ||
702 | int | ||
703 | key_in_file(Key *key, const char *filename, int strict_type) | ||
704 | { | ||
705 | FILE *f; | ||
706 | char line[SSH_MAX_PUBKEY_BYTES]; | ||
707 | char *cp; | ||
708 | u_long linenum = 0; | ||
709 | int ret = 0; | ||
710 | Key *pub; | ||
711 | int (*key_compare)(const Key *, const Key *) = strict_type ? | ||
712 | key_equal : key_equal_public; | ||
713 | |||
714 | if ((f = fopen(filename, "r")) == NULL) { | ||
715 | if (errno == ENOENT) { | ||
716 | debug("%s: keyfile \"%s\" missing", __func__, filename); | ||
717 | return 0; | ||
718 | } else { | ||
719 | error("%s: could not open keyfile \"%s\": %s", __func__, | ||
720 | filename, strerror(errno)); | ||
721 | return -1; | ||
722 | } | ||
723 | } | ||
724 | |||
725 | while (read_keyfile_line(f, filename, line, sizeof(line), | ||
726 | &linenum) != -1) { | ||
727 | cp = line; | ||
728 | |||
729 | /* Skip leading whitespace. */ | ||
730 | for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) | ||
731 | ; | ||
732 | |||
733 | /* Skip comments and empty lines */ | ||
734 | switch (*cp) { | ||
735 | case '#': | ||
736 | case '\n': | ||
737 | case '\0': | ||
738 | continue; | ||
739 | } | ||
740 | |||
741 | pub = key_new(KEY_UNSPEC); | ||
742 | if (key_read(pub, &cp) != 1) { | ||
743 | key_free(pub); | ||
744 | continue; | ||
745 | } | ||
746 | if (key_compare(key, pub)) { | ||
747 | ret = 1; | ||
748 | key_free(pub); | ||
749 | break; | ||
750 | } | ||
751 | key_free(pub); | ||
752 | } | ||
753 | fclose(f); | ||
754 | return ret; | ||
755 | } | ||
756 | |||
diff --git a/authfile.h b/authfile.h index a6c74934d..6dfa478e7 100644 --- a/authfile.h +++ b/authfile.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: authfile.h,v 1.13 2006/04/25 08:02:27 dtucker Exp $ */ | 1 | /* $OpenBSD: authfile.h,v 1.14 2010/03/04 10:36:03 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -22,5 +22,6 @@ Key *key_load_private(const char *, const char *, char **); | |||
22 | Key *key_load_private_type(int, const char *, const char *, char **, int *); | 22 | Key *key_load_private_type(int, const char *, const char *, char **, int *); |
23 | Key *key_load_private_pem(int, int, const char *, char **); | 23 | Key *key_load_private_pem(int, int, const char *, char **); |
24 | int key_perm_ok(int, const char *); | 24 | int key_perm_ok(int, const char *); |
25 | int key_in_file(Key *, const char *, int); | ||
25 | 26 | ||
26 | #endif | 27 | #endif |
diff --git a/hostfile.c b/hostfile.c index fc7f84c79..afab6dad1 100644 --- a/hostfile.c +++ b/hostfile.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: hostfile.c,v 1.47 2010/02/26 20:29:54 djm Exp $ */ | 1 | /* $OpenBSD: hostfile.c,v 1.48 2010/03/04 10:36:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -183,6 +183,41 @@ hostfile_check_key(int bits, const Key *key, const char *host, const char *filen | |||
183 | return 1; | 183 | return 1; |
184 | } | 184 | } |
185 | 185 | ||
186 | static enum { MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA } | ||
187 | check_markers(char **cpp) | ||
188 | { | ||
189 | char marker[32], *sp, *cp = *cpp; | ||
190 | int ret = MRK_NONE; | ||
191 | |||
192 | while (*cp == '@') { | ||
193 | /* Only one marker is allowed */ | ||
194 | if (ret != MRK_NONE) | ||
195 | return MRK_ERROR; | ||
196 | /* Markers are terminated by whitespace */ | ||
197 | if ((sp = strchr(cp, ' ')) == NULL && | ||
198 | (sp = strchr(cp, '\t')) == NULL) | ||
199 | return MRK_ERROR; | ||
200 | /* Extract marker for comparison */ | ||
201 | if (sp <= cp + 1 || sp >= cp + sizeof(marker)) | ||
202 | return MRK_ERROR; | ||
203 | memcpy(marker, cp, sp - cp); | ||
204 | marker[sp - cp] = '\0'; | ||
205 | if (strcmp(marker, CA_MARKER) == 0) | ||
206 | ret = MRK_CA; | ||
207 | else if (strcmp(marker, REVOKE_MARKER) == 0) | ||
208 | ret = MRK_REVOKE; | ||
209 | else | ||
210 | return MRK_ERROR; | ||
211 | |||
212 | /* Skip past marker and any whitespace that follows it */ | ||
213 | cp = sp; | ||
214 | for (; *cp == ' ' || *cp == '\t'; cp++) | ||
215 | ; | ||
216 | } | ||
217 | *cpp = cp; | ||
218 | return ret; | ||
219 | } | ||
220 | |||
186 | /* | 221 | /* |
187 | * Checks whether the given host (which must be in all lowercase) is already | 222 | * Checks whether the given host (which must be in all lowercase) is already |
188 | * in the list of our known hosts. Returns HOST_OK if the host is known and | 223 | * in the list of our known hosts. Returns HOST_OK if the host is known and |
@@ -195,17 +230,21 @@ hostfile_check_key(int bits, const Key *key, const char *host, const char *filen | |||
195 | 230 | ||
196 | static HostStatus | 231 | static HostStatus |
197 | check_host_in_hostfile_by_key_or_type(const char *filename, | 232 | check_host_in_hostfile_by_key_or_type(const char *filename, |
198 | const char *host, const Key *key, int keytype, Key *found, int *numret) | 233 | const char *host, const Key *key, int keytype, Key *found, |
234 | int want_revocation, int *numret) | ||
199 | { | 235 | { |
200 | FILE *f; | 236 | FILE *f; |
201 | char line[8192]; | 237 | char line[8192]; |
202 | int linenum = 0, want_cert = key_is_cert(key); | 238 | int want, have, linenum = 0, want_cert = key_is_cert(key); |
203 | u_int kbits; | 239 | u_int kbits; |
204 | char *cp, *cp2, *hashed_host; | 240 | char *cp, *cp2, *hashed_host; |
205 | HostStatus end_return; | 241 | HostStatus end_return; |
206 | 242 | ||
207 | debug3("check_host_in_hostfile: host %s filename %s", host, filename); | 243 | debug3("check_host_in_hostfile: host %s filename %s", host, filename); |
208 | 244 | ||
245 | if (want_revocation && (key == NULL || keytype != 0 || found != NULL)) | ||
246 | fatal("%s: invalid arguments", __func__); | ||
247 | |||
209 | /* Open the file containing the list of known hosts. */ | 248 | /* Open the file containing the list of known hosts. */ |
210 | f = fopen(filename, "r"); | 249 | f = fopen(filename, "r"); |
211 | if (!f) | 250 | if (!f) |
@@ -229,21 +268,18 @@ check_host_in_hostfile_by_key_or_type(const char *filename, | |||
229 | if (!*cp || *cp == '#' || *cp == '\n') | 268 | if (!*cp || *cp == '#' || *cp == '\n') |
230 | continue; | 269 | continue; |
231 | 270 | ||
232 | /* | 271 | if (want_revocation) |
233 | * Ignore CA keys when looking for raw keys. | 272 | want = MRK_REVOKE; |
234 | * Ignore raw keys when looking for CA keys. | 273 | else if (want_cert) |
235 | */ | 274 | want = MRK_CA; |
236 | if (strncasecmp(cp, CA_MARKER, sizeof(CA_MARKER) - 1) == 0 && | 275 | else |
237 | (cp[sizeof(CA_MARKER) - 1] == ' ' || | 276 | want = MRK_NONE; |
238 | cp[sizeof(CA_MARKER) - 1] == '\t')) { | 277 | |
239 | if (want_cert) { | 278 | if ((have = check_markers(&cp)) == MRK_ERROR) { |
240 | /* Skip the marker and following whitespace */ | 279 | verbose("%s: invalid marker at %s:%d", |
241 | cp += sizeof(CA_MARKER); | 280 | __func__, filename, linenum); |
242 | for (; *cp == ' ' || *cp == '\t'; cp++) | 281 | continue; |
243 | ; | 282 | } else if (want != have) |
244 | } else | ||
245 | continue; | ||
246 | } else if (want_cert) | ||
247 | continue; | 283 | continue; |
248 | 284 | ||
249 | /* Find the end of the host name portion. */ | 285 | /* Find the end of the host name portion. */ |
@@ -267,6 +303,9 @@ check_host_in_hostfile_by_key_or_type(const char *filename, | |||
267 | /* Got a match. Skip host name. */ | 303 | /* Got a match. Skip host name. */ |
268 | cp = cp2; | 304 | cp = cp2; |
269 | 305 | ||
306 | if (want_revocation) | ||
307 | found = key_new(KEY_UNSPEC); | ||
308 | |||
270 | /* | 309 | /* |
271 | * Extract the key from the line. This will skip any leading | 310 | * Extract the key from the line. This will skip any leading |
272 | * whitespace. Ignore badly formatted lines. | 311 | * whitespace. Ignore badly formatted lines. |
@@ -289,6 +328,24 @@ check_host_in_hostfile_by_key_or_type(const char *filename, | |||
289 | if (!hostfile_check_key(kbits, found, host, filename, linenum)) | 328 | if (!hostfile_check_key(kbits, found, host, filename, linenum)) |
290 | continue; | 329 | continue; |
291 | 330 | ||
331 | if (want_revocation) { | ||
332 | if (key_is_cert(key) && | ||
333 | key_equal_public(key->cert->signature_key, found)) { | ||
334 | verbose("check_host_in_hostfile: revoked CA " | ||
335 | "line %d", linenum); | ||
336 | key_free(found); | ||
337 | return HOST_REVOKED; | ||
338 | } | ||
339 | if (key_equal_public(key, found)) { | ||
340 | verbose("check_host_in_hostfile: revoked key " | ||
341 | "line %d", linenum); | ||
342 | key_free(found); | ||
343 | return HOST_REVOKED; | ||
344 | } | ||
345 | key_free(found); | ||
346 | continue; | ||
347 | } | ||
348 | |||
292 | /* Check if the current key is the same as the given key. */ | 349 | /* Check if the current key is the same as the given key. */ |
293 | if (want_cert && key_equal(key->cert->signature_key, found)) { | 350 | if (want_cert && key_equal(key->cert->signature_key, found)) { |
294 | /* Found CA cert for key */ | 351 | /* Found CA cert for key */ |
@@ -325,8 +382,11 @@ check_host_in_hostfile(const char *filename, const char *host, const Key *key, | |||
325 | { | 382 | { |
326 | if (key == NULL) | 383 | if (key == NULL) |
327 | fatal("no key to look up"); | 384 | fatal("no key to look up"); |
328 | return (check_host_in_hostfile_by_key_or_type(filename, host, key, 0, | 385 | if (check_host_in_hostfile_by_key_or_type(filename, host, |
329 | found, numret)); | 386 | key, 0, NULL, 1, NULL) == HOST_REVOKED) |
387 | return HOST_REVOKED; | ||
388 | return check_host_in_hostfile_by_key_or_type(filename, host, key, 0, | ||
389 | found, 0, numret); | ||
330 | } | 390 | } |
331 | 391 | ||
332 | int | 392 | int |
@@ -334,7 +394,7 @@ lookup_key_in_hostfile_by_type(const char *filename, const char *host, | |||
334 | int keytype, Key *found, int *numret) | 394 | int keytype, Key *found, int *numret) |
335 | { | 395 | { |
336 | return (check_host_in_hostfile_by_key_or_type(filename, host, NULL, | 396 | return (check_host_in_hostfile_by_key_or_type(filename, host, NULL, |
337 | keytype, found, numret) == HOST_FOUND); | 397 | keytype, found, 0, numret) == HOST_FOUND); |
338 | } | 398 | } |
339 | 399 | ||
340 | /* | 400 | /* |
diff --git a/hostfile.h b/hostfile.h index ebac1e4f1..1d460c1a9 100644 --- a/hostfile.h +++ b/hostfile.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: hostfile.h,v 1.17 2010/02/26 20:29:54 djm Exp $ */ | 1 | /* $OpenBSD: hostfile.h,v 1.18 2010/03/04 10:36:03 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -15,7 +15,7 @@ | |||
15 | #define HOSTFILE_H | 15 | #define HOSTFILE_H |
16 | 16 | ||
17 | typedef enum { | 17 | typedef enum { |
18 | HOST_OK, HOST_NEW, HOST_CHANGED, HOST_FOUND | 18 | HOST_OK, HOST_NEW, HOST_CHANGED, HOST_REVOKED, HOST_FOUND |
19 | } HostStatus; | 19 | } HostStatus; |
20 | 20 | ||
21 | int hostfile_read_key(char **, u_int *, Key *); | 21 | int hostfile_read_key(char **, u_int *, Key *); |
@@ -29,6 +29,7 @@ int lookup_key_in_hostfile_by_type(const char *, const char *, | |||
29 | #define HASH_DELIM '|' | 29 | #define HASH_DELIM '|' |
30 | 30 | ||
31 | #define CA_MARKER "@cert-authority" | 31 | #define CA_MARKER "@cert-authority" |
32 | #define REVOKE_MARKER "@revoked" | ||
32 | 33 | ||
33 | char *host_hash(const char *, const char *, u_int); | 34 | char *host_hash(const char *, const char *, u_int); |
34 | 35 | ||
diff --git a/servconf.c b/servconf.c index 0a6cdb655..f9e2f2dfd 100644 --- a/servconf.c +++ b/servconf.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: servconf.c,v 1.203 2010/02/26 20:29:54 djm Exp $ */ | 1 | /* $OpenBSD: servconf.c,v 1.204 2010/03/04 10:36:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | * All rights reserved | 4 | * All rights reserved |
@@ -129,6 +129,8 @@ initialize_server_options(ServerOptions *options) | |||
129 | options->adm_forced_command = NULL; | 129 | options->adm_forced_command = NULL; |
130 | options->chroot_directory = NULL; | 130 | options->chroot_directory = NULL; |
131 | options->zero_knowledge_password_authentication = -1; | 131 | options->zero_knowledge_password_authentication = -1; |
132 | options->revoked_keys_file = NULL; | ||
133 | options->trusted_user_ca_keys = NULL; | ||
132 | } | 134 | } |
133 | 135 | ||
134 | void | 136 | void |
@@ -308,6 +310,7 @@ typedef enum { | |||
308 | sMatch, sPermitOpen, sForceCommand, sChrootDirectory, | 310 | sMatch, sPermitOpen, sForceCommand, sChrootDirectory, |
309 | sUsePrivilegeSeparation, sAllowAgentForwarding, | 311 | sUsePrivilegeSeparation, sAllowAgentForwarding, |
310 | sZeroKnowledgePasswordAuthentication, sHostCertificate, | 312 | sZeroKnowledgePasswordAuthentication, sHostCertificate, |
313 | sRevokedKeys, sTrustedUserCAKeys, | ||
311 | sDeprecated, sUnsupported | 314 | sDeprecated, sUnsupported |
312 | } ServerOpCodes; | 315 | } ServerOpCodes; |
313 | 316 | ||
@@ -427,6 +430,8 @@ static struct { | |||
427 | { "forcecommand", sForceCommand, SSHCFG_ALL }, | 430 | { "forcecommand", sForceCommand, SSHCFG_ALL }, |
428 | { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, | 431 | { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, |
429 | { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, | 432 | { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, |
433 | { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, | ||
434 | { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, | ||
430 | { NULL, sBadOption, 0 } | 435 | { NULL, sBadOption, 0 } |
431 | }; | 436 | }; |
432 | 437 | ||
@@ -1323,6 +1328,14 @@ process_server_config_line(ServerOptions *options, char *line, | |||
1323 | *charptr = xstrdup(arg); | 1328 | *charptr = xstrdup(arg); |
1324 | break; | 1329 | break; |
1325 | 1330 | ||
1331 | case sTrustedUserCAKeys: | ||
1332 | charptr = &options->trusted_user_ca_keys; | ||
1333 | goto parse_filename; | ||
1334 | |||
1335 | case sRevokedKeys: | ||
1336 | charptr = &options->revoked_keys_file; | ||
1337 | goto parse_filename; | ||
1338 | |||
1326 | case sDeprecated: | 1339 | case sDeprecated: |
1327 | logit("%s line %d: Deprecated option %s", | 1340 | logit("%s line %d: Deprecated option %s", |
1328 | filename, linenum, arg); | 1341 | filename, linenum, arg); |
@@ -1437,6 +1450,8 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) | |||
1437 | return; | 1450 | return; |
1438 | M_CP_STROPT(adm_forced_command); | 1451 | M_CP_STROPT(adm_forced_command); |
1439 | M_CP_STROPT(chroot_directory); | 1452 | M_CP_STROPT(chroot_directory); |
1453 | M_CP_STROPT(trusted_user_ca_keys); | ||
1454 | M_CP_STROPT(revoked_keys_file); | ||
1440 | } | 1455 | } |
1441 | 1456 | ||
1442 | #undef M_CP_INTOPT | 1457 | #undef M_CP_INTOPT |
@@ -1656,6 +1671,8 @@ dump_config(ServerOptions *o) | |||
1656 | dump_cfg_string(sAuthorizedKeysFile2, o->authorized_keys_file2); | 1671 | dump_cfg_string(sAuthorizedKeysFile2, o->authorized_keys_file2); |
1657 | dump_cfg_string(sForceCommand, o->adm_forced_command); | 1672 | dump_cfg_string(sForceCommand, o->adm_forced_command); |
1658 | dump_cfg_string(sChrootDirectory, o->chroot_directory); | 1673 | dump_cfg_string(sChrootDirectory, o->chroot_directory); |
1674 | dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); | ||
1675 | dump_cfg_string(sRevokedKeys, o->revoked_keys_file); | ||
1659 | 1676 | ||
1660 | /* string arguments requiring a lookup */ | 1677 | /* string arguments requiring a lookup */ |
1661 | dump_cfg_string(sLogLevel, log_level_name(o->log_level)); | 1678 | dump_cfg_string(sLogLevel, log_level_name(o->log_level)); |
diff --git a/servconf.h b/servconf.h index c5c9c6ecd..860009f9c 100644 --- a/servconf.h +++ b/servconf.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: servconf.h,v 1.91 2010/02/26 20:29:54 djm Exp $ */ | 1 | /* $OpenBSD: servconf.h,v 1.92 2010/03/04 10:36:03 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -154,6 +154,8 @@ typedef struct { | |||
154 | int num_permitted_opens; | 154 | int num_permitted_opens; |
155 | 155 | ||
156 | char *chroot_directory; | 156 | char *chroot_directory; |
157 | char *revoked_keys_file; | ||
158 | char *trusted_user_ca_keys; | ||
157 | } ServerOptions; | 159 | } ServerOptions; |
158 | 160 | ||
159 | void initialize_server_options(ServerOptions *); | 161 | void initialize_server_options(ServerOptions *); |
diff --git a/ssh-keygen.c b/ssh-keygen.c index f910dce38..c2120bbc1 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.180 2010/03/02 23:20:57 djm Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.181 2010/03/04 10:36:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -34,8 +34,8 @@ | |||
34 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 34 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
35 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 35 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
36 | .\" | 36 | .\" |
37 | .\" $OpenBSD: ssh.1,v 1.296 2010/02/26 22:09:28 jmc Exp $ | 37 | .\" $OpenBSD: ssh.1,v 1.297 2010/03/04 10:36:03 djm Exp $ |
38 | .Dd $Mdocdate: February 26 2010 $ | 38 | .Dd $Mdocdate: March 4 2010 $ |
39 | .Dt SSH 1 | 39 | .Dt SSH 1 |
40 | .Os | 40 | .Os |
41 | .Sh NAME | 41 | .Sh NAME |
@@ -1121,6 +1121,22 @@ See the | |||
1121 | section of | 1121 | section of |
1122 | .Xr ssh-keygen 1 | 1122 | .Xr ssh-keygen 1 |
1123 | for more details. | 1123 | for more details. |
1124 | .Pp | ||
1125 | Keys may be also be marked as revoked using the | ||
1126 | .Dq @revoked | ||
1127 | marker. | ||
1128 | Revoked keys will always trigger a warning when encountered and the host | ||
1129 | that presented them will be treated as untrusted. | ||
1130 | For example: | ||
1131 | .Pp | ||
1132 | .Dl @revoked * ssh-rsa AAAAB5W... | ||
1133 | .Pp | ||
1134 | Revoking a key revokes it for direct use and as a certification authority. | ||
1135 | Do not use both the | ||
1136 | .Dq @cert-authority and | ||
1137 | .Dq @revoked | ||
1138 | markers on the same line. | ||
1139 | .Pp | ||
1124 | .Sh SSH-BASED VIRTUAL PRIVATE NETWORKS | 1140 | .Sh SSH-BASED VIRTUAL PRIVATE NETWORKS |
1125 | .Nm | 1141 | .Nm |
1126 | contains support for Virtual Private Network (VPN) tunnelling | 1142 | contains support for Virtual Private Network (VPN) tunnelling |
diff --git a/sshconnect.c b/sshconnect.c index 35c2f49be..9de52224d 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.219 2010/02/26 20:29:54 djm Exp $ */ | 1 | /* $OpenBSD: sshconnect.c,v 1.220 2010/03/04 10:36:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -859,6 +859,25 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
859 | logit("Warning: Permanently added '%.200s' (%s) to the " | 859 | logit("Warning: Permanently added '%.200s' (%s) to the " |
860 | "list of known hosts.", hostp, type); | 860 | "list of known hosts.", hostp, type); |
861 | break; | 861 | break; |
862 | case HOST_REVOKED: | ||
863 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
864 | error("@ WARNING: REVOKED HOST KEY DETECTED! @"); | ||
865 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
866 | error("The %s host key for %s is marked as revoked.", type, host); | ||
867 | error("This could mean that a stolen key is being used to"); | ||
868 | error("impersonate this host."); | ||
869 | |||
870 | /* | ||
871 | * If strict host key checking is in use, the user will have | ||
872 | * to edit the key manually and we can only abort. | ||
873 | */ | ||
874 | if (options.strict_host_key_checking) { | ||
875 | error("%s host key for %.200s was revoked and you have " | ||
876 | "requested strict checking.", type, host); | ||
877 | goto fail; | ||
878 | } | ||
879 | goto continue_unsafe; | ||
880 | |||
862 | case HOST_CHANGED: | 881 | case HOST_CHANGED: |
863 | if (want_cert) { | 882 | if (want_cert) { |
864 | /* | 883 | /* |
@@ -908,6 +927,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
908 | goto fail; | 927 | goto fail; |
909 | } | 928 | } |
910 | 929 | ||
930 | continue_unsafe: | ||
911 | /* | 931 | /* |
912 | * If strict host key checking has not been requested, allow | 932 | * If strict host key checking has not been requested, allow |
913 | * the connection but without MITM-able authentication or | 933 | * the connection but without MITM-able authentication or |
@@ -1007,7 +1027,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
1007 | return 0; | 1027 | return 0; |
1008 | 1028 | ||
1009 | fail: | 1029 | fail: |
1010 | if (want_cert) { | 1030 | if (want_cert && host_status != HOST_REVOKED) { |
1011 | /* | 1031 | /* |
1012 | * No matching certificate. Downgrade cert to raw key and | 1032 | * No matching certificate. Downgrade cert to raw key and |
1013 | * search normally. | 1033 | * search normally. |
diff --git a/sshd_config.5 b/sshd_config.5 index 001114655..07e74e2b7 100644 --- a/sshd_config.5 +++ b/sshd_config.5 | |||
@@ -34,8 +34,8 @@ | |||
34 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 34 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
35 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 35 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
36 | .\" | 36 | .\" |
37 | .\" $OpenBSD: sshd_config.5,v 1.117 2010/02/26 20:29:54 djm Exp $ | 37 | .\" $OpenBSD: sshd_config.5,v 1.118 2010/03/04 10:36:03 djm Exp $ |
38 | .Dd $Mdocdate: February 26 2010 $ | 38 | .Dd $Mdocdate: March 4 2010 $ |
39 | .Dt SSHD_CONFIG 5 | 39 | .Dt SSHD_CONFIG 5 |
40 | .Os | 40 | .Os |
41 | .Sh NAME | 41 | .Sh NAME |
@@ -814,6 +814,11 @@ Specifies whether public key authentication is allowed. | |||
814 | The default is | 814 | The default is |
815 | .Dq yes . | 815 | .Dq yes . |
816 | Note that this option applies to protocol version 2 only. | 816 | Note that this option applies to protocol version 2 only. |
817 | .It Cm RevokedKeys | ||
818 | Specifies a list of revoked public keys. | ||
819 | Keys listed in this file will be refused for public key authentication. | ||
820 | Note that if this file is not readable, then public key authentication will | ||
821 | be refused for all users. | ||
817 | .It Cm RhostsRSAAuthentication | 822 | .It Cm RhostsRSAAuthentication |
818 | Specifies whether rhosts or /etc/hosts.equiv authentication together | 823 | Specifies whether rhosts or /etc/hosts.equiv authentication together |
819 | with successful RSA host authentication is allowed. | 824 | with successful RSA host authentication is allowed. |
@@ -889,6 +894,22 @@ This avoids infinitely hanging sessions. | |||
889 | .Pp | 894 | .Pp |
890 | To disable TCP keepalive messages, the value should be set to | 895 | To disable TCP keepalive messages, the value should be set to |
891 | .Dq no . | 896 | .Dq no . |
897 | .It Cm TrustedUserCAKeys | ||
898 | Specifies a file containing public keys of certificate authorities that are | ||
899 | trusted sign user certificates for authentication. | ||
900 | Keys are listed one per line, empty lines and comments starting with | ||
901 | .Ql # | ||
902 | are allowed. | ||
903 | If a certificate is presented for authentication and has its signing CA key | ||
904 | listed in this file, then it may be used for authentication for any user | ||
905 | listed in the certificate's principals list. | ||
906 | Note that certificates that lack a list of principals will not be permitted | ||
907 | for authentication using | ||
908 | .Cm TrustedUserCAKeys . | ||
909 | For more details in certificates, please see the | ||
910 | .Sx CERTIFICATES | ||
911 | section in | ||
912 | .Xr ssh-keygen 1 . | ||
892 | .It Cm UseDNS | 913 | .It Cm UseDNS |
893 | Specifies whether | 914 | Specifies whether |
894 | .Xr sshd 8 | 915 | .Xr sshd 8 |