summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog33
-rw-r--r--PROTOCOL23
-rw-r--r--PROTOCOL.agent24
-rw-r--r--PROTOCOL.certkeys191
-rw-r--r--addrmatch.c78
-rw-r--r--auth-options.c150
-rw-r--r--auth-options.h4
-rw-r--r--auth.h5
-rw-r--r--auth2-pubkey.c37
-rw-r--r--authfd.c24
-rw-r--r--dns.c8
-rw-r--r--dns.h6
-rw-r--r--hostfile.c31
-rw-r--r--hostfile.h4
-rw-r--r--kex.h5
-rw-r--r--kexdhs.c19
-rw-r--r--kexgexs.c20
-rw-r--r--key.c595
-rw-r--r--key.h32
-rw-r--r--match.h4
-rw-r--r--monitor.c5
-rw-r--r--myproposal.h6
-rw-r--r--servconf.c19
-rw-r--r--servconf.h5
-rw-r--r--ssh-add.c34
-rw-r--r--ssh-agent.c24
-rw-r--r--ssh-dss.c10
-rw-r--r--ssh-keygen.1178
-rw-r--r--ssh-keygen.c433
-rw-r--r--ssh-rsa.c10
-rw-r--r--ssh.123
-rw-r--r--ssh.c71
-rw-r--r--ssh2.h5
-rw-r--r--sshconnect.c78
-rw-r--r--sshconnect2.c4
-rw-r--r--sshd.825
-rw-r--r--sshd.c117
-rw-r--r--sshd_config.512
38 files changed, 2164 insertions, 188 deletions
diff --git a/ChangeLog b/ChangeLog
index 10c074c26..fec38e028 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,36 @@
120100226
2 - OpenBSD CVS Sync
3 - djm@cvs.openbsd.org 2010/02/26 20:29:54
4 [PROTOCOL PROTOCOL.agent PROTOCOL.certkeys addrmatch.c auth-options.c]
5 [auth-options.h auth.h auth2-pubkey.c authfd.c dns.c dns.h hostfile.c]
6 [hostfile.h kex.h kexdhs.c kexgexs.c key.c key.h match.h monitor.c]
7 [myproposal.h servconf.c servconf.h ssh-add.c ssh-agent.c ssh-dss.c]
8 [ssh-keygen.1 ssh-keygen.c ssh-rsa.c ssh.1 ssh.c ssh2.h sshconnect.c]
9 [sshconnect2.c sshd.8 sshd.c sshd_config.5]
10 Add support for certificate key types for users and hosts.
11
12 OpenSSH certificate key types are not X.509 certificates, but a much
13 simpler format that encodes a public key, identity information and
14 some validity constraints and signs it with a CA key. CA keys are
15 regular SSH keys. This certificate style avoids the attack surface
16 of X.509 certificates and is very easy to deploy.
17
18 Certified host keys allow automatic acceptance of new host keys
19 when a CA certificate is marked as trusted in ~/.ssh/known_hosts.
20 see VERIFYING HOST KEYS in ssh(1) for details.
21
22 Certified user keys allow authentication of users when the signing
23 CA key is marked as trusted in authorized_keys. See "AUTHORIZED_KEYS
24 FILE FORMAT" in sshd(8) for details.
25
26 Certificates are minted using ssh-keygen(1), documentation is in
27 the "CERTIFICATES" section of that manpage.
28
29 Documentation on the format of certificates is in the file
30 PROTOCOL.certkeys
31
32 feedback and ok markus@
33
120100224 3420100224
2 - (djm) [pkcs11.h ssh-pkcs11-client.c ssh-pkcs11-helper.c ssh-pkcs11.c] 35 - (djm) [pkcs11.h ssh-pkcs11-client.c ssh-pkcs11-helper.c ssh-pkcs11.c]
3 [ssh-pkcs11.h] Add $OpenBSD$ RCS idents so we can sync portable 36 [ssh-pkcs11.h] Add $OpenBSD$ RCS idents so we can sync portable
diff --git a/PROTOCOL b/PROTOCOL
index 9b74b9475..5fc31eade 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -31,7 +31,14 @@ The method is documented in:
31 31
32http://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt 32http://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt
33 33
343. connection: Channel write close extension "eow@openssh.com" 343. transport: New public key algorithms "ssh-rsa-cert-v00@openssh.com" and
35 "ssh-dsa-cert-v00@openssh.com"
36
37OpenSSH introduces two new public key algorithms to support certificate
38authentication for users and hostkeys. These methods are documented in
39the file PROTOCOL.certkeys
40
414. connection: Channel write close extension "eow@openssh.com"
35 42
36The SSH connection protocol (rfc4254) provides the SSH_MSG_CHANNEL_EOF 43The SSH connection protocol (rfc4254) provides the SSH_MSG_CHANNEL_EOF
37message to allow an endpoint to signal its peer that it will send no 44message to allow an endpoint to signal its peer that it will send no
@@ -70,7 +77,7 @@ message is only sent to OpenSSH peers (identified by banner).
70Other SSH implementations may be whitelisted to receive this message 77Other SSH implementations may be whitelisted to receive this message
71upon request. 78upon request.
72 79
734. connection: disallow additional sessions extension 805. connection: disallow additional sessions extension
74 "no-more-sessions@openssh.com" 81 "no-more-sessions@openssh.com"
75 82
76Most SSH connections will only ever request a single session, but a 83Most SSH connections will only ever request a single session, but a
@@ -98,7 +105,7 @@ of this message, the no-more-sessions request is only sent to OpenSSH
98servers (identified by banner). Other SSH implementations may be 105servers (identified by banner). Other SSH implementations may be
99whitelisted to receive this message upon request. 106whitelisted to receive this message upon request.
100 107
1015. connection: Tunnel forward extension "tun@openssh.com" 1086. connection: Tunnel forward extension "tun@openssh.com"
102 109
103OpenSSH supports layer 2 and layer 3 tunnelling via the "tun@openssh.com" 110OpenSSH supports layer 2 and layer 3 tunnelling via the "tun@openssh.com"
104channel type. This channel type supports forwarding of network packets 111channel type. This channel type supports forwarding of network packets
@@ -159,7 +166,7 @@ The contents of the "data" field for layer 2 packets is:
159The "frame" field contains an IEEE 802.3 Ethernet frame, including 166The "frame" field contains an IEEE 802.3 Ethernet frame, including
160header. 167header.
161 168
1626. sftp: Reversal of arguments to SSH_FXP_SYMLINK 1697. sftp: Reversal of arguments to SSH_FXP_SYMLINK
163 170
164When OpenSSH's sftp-server was implemented, the order of the arguments 171When OpenSSH's sftp-server was implemented, the order of the arguments
165to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately, 172to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately,
@@ -172,7 +179,7 @@ SSH_FXP_SYMLINK as follows:
172 string targetpath 179 string targetpath
173 string linkpath 180 string linkpath
174 181
1757. sftp: Server extension announcement in SSH_FXP_VERSION 1828. sftp: Server extension announcement in SSH_FXP_VERSION
176 183
177OpenSSH's sftp-server lists the extensions it supports using the 184OpenSSH's sftp-server lists the extensions it supports using the
178standard extension announcement mechanism in the SSH_FXP_VERSION server 185standard extension announcement mechanism in the SSH_FXP_VERSION server
@@ -193,7 +200,7 @@ ever changed in an incompatible way. The server MAY advertise the same
193extension with multiple versions (though this is unlikely). Clients MUST 200extension with multiple versions (though this is unlikely). Clients MUST
194check the version number before attempting to use the extension. 201check the version number before attempting to use the extension.
195 202
1968. sftp: Extension request "posix-rename@openssh.com" 2039. sftp: Extension request "posix-rename@openssh.com"
197 204
198This operation provides a rename operation with POSIX semantics, which 205This operation provides a rename operation with POSIX semantics, which
199are different to those provided by the standard SSH_FXP_RENAME in 206are different to those provided by the standard SSH_FXP_RENAME in
@@ -210,7 +217,7 @@ rename(oldpath, newpath) and will respond with a SSH_FXP_STATUS message.
210This extension is advertised in the SSH_FXP_VERSION hello with version 217This extension is advertised in the SSH_FXP_VERSION hello with version
211"1". 218"1".
212 219
2139. sftp: Extension requests "statvfs@openssh.com" and 22010. sftp: Extension requests "statvfs@openssh.com" and
214 "fstatvfs@openssh.com" 221 "fstatvfs@openssh.com"
215 222
216These requests correspond to the statvfs and fstatvfs POSIX system 223These requests correspond to the statvfs and fstatvfs POSIX system
@@ -251,4 +258,4 @@ The values of the f_flag bitmask are as follows:
251Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are 258Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are
252advertised in the SSH_FXP_VERSION hello with version "2". 259advertised in the SSH_FXP_VERSION hello with version "2".
253 260
254$OpenBSD: PROTOCOL,v 1.14 2010/01/09 00:57:10 djm Exp $ 261$OpenBSD: PROTOCOL,v 1.15 2010/02/26 20:29:54 djm Exp $
diff --git a/PROTOCOL.agent b/PROTOCOL.agent
index 49adbdd5c..b34fcd318 100644
--- a/PROTOCOL.agent
+++ b/PROTOCOL.agent
@@ -173,6 +173,15 @@ be added using the following request
173 string key_comment 173 string key_comment
174 constraint[] key_constraints 174 constraint[] key_constraints
175 175
176DSA certificates may be added with:
177 byte SSH2_AGENTC_ADD_IDENTITY or
178 SSH2_AGENTC_ADD_ID_CONSTRAINED
179 string "ssh-dss-cert-v00@openssh.com"
180 string certificate
181 mpint dsa_private_key
182 string key_comment
183 constraint[] key_constraints
184
176RSA keys may be added with this request: 185RSA keys may be added with this request:
177 186
178 byte SSH2_AGENTC_ADD_IDENTITY or 187 byte SSH2_AGENTC_ADD_IDENTITY or
@@ -187,6 +196,19 @@ RSA keys may be added with this request:
187 string key_comment 196 string key_comment
188 constraint[] key_constraints 197 constraint[] key_constraints
189 198
199RSA certificates may be added with this request:
200
201 byte SSH2_AGENTC_ADD_IDENTITY or
202 SSH2_AGENTC_ADD_ID_CONSTRAINED
203 string "ssh-rsa-cert-v00@openssh.com"
204 string certificate
205 mpint rsa_d
206 mpint rsa_iqmp
207 mpint rsa_p
208 mpint rsa_q
209 string key_comment
210 constraint[] key_constraints
211
190Note that the 'rsa_p' and 'rsa_q' parameters are sent in the reverse 212Note that the 'rsa_p' and 'rsa_q' parameters are sent in the reverse
191order to the protocol 1 add keys message. As with the corresponding 213order to the protocol 1 add keys message. As with the corresponding
192protocol 1 "add key" request, the private key is overspecified to avoid 214protocol 1 "add key" request, the private key is overspecified to avoid
@@ -513,4 +535,4 @@ Locking and unlocking affects both protocol 1 and protocol 2 keys.
513 SSH_AGENT_CONSTRAIN_LIFETIME 1 535 SSH_AGENT_CONSTRAIN_LIFETIME 1
514 SSH_AGENT_CONSTRAIN_CONFIRM 2 536 SSH_AGENT_CONSTRAIN_CONFIRM 2
515 537
516$OpenBSD: PROTOCOL.agent,v 1.4 2008/07/01 23:12:47 stevesk Exp $ 538$OpenBSD: PROTOCOL.agent,v 1.5 2010/02/26 20:29:54 djm Exp $
diff --git a/PROTOCOL.certkeys b/PROTOCOL.certkeys
new file mode 100644
index 000000000..0b887a240
--- /dev/null
+++ b/PROTOCOL.certkeys
@@ -0,0 +1,191 @@
1This document describes a simple public-key certificate authentication
2system for use by SSH.
3
4Background
5----------
6
7The SSH protocol currently supports a simple public key authentication
8mechanism. Unlike other public key implementations, SSH eschews the
9use of X.509 certificates and uses raw keys. This approach has some
10benefits relating to simplicity of configuration and minimisation
11of attack surface, but it does not support the important use-cases
12of centrally managed, passwordless authentication and centrally
13certified host keys.
14
15These protocol extensions build on the simple public key authentication
16system already in SSH to allow certificate-based authentication.
17The certificates used are not traditional X.509 certificates, with
18numerous options and complex encoding rules, but something rather
19more minimal: a key, some identity information and usage constraints
20that have been signed with some other trusted key.
21
22A sshd server may be configured to allow authentication via certified
23keys, by extending the existing ~/.ssh/authorized_keys mechanism
24to allow specification of certification authority keys in addition
25to raw user keys. The ssh client will support automatic verification
26of acceptance of certified host keys, by adding a similar ability
27to specify CA keys in ~/.ssh/known_hosts.
28
29Certified keys are represented using two new key types:
30ssh-rsa-cert-v00@openssh.com and ssh-dss-cert-v00@openssh.com that
31include certification information along with the public key that is used
32to sign challenges. ssh-keygen performs the CA signing operation.
33
34Protocol extensions
35-------------------
36
37The SSH wire protocol includes several extensibility mechanisms.
38These modifications shall take advantage of namespaced public key
39algorithm names to add support for certificate authentication without
40breaking the protocol - implementations that do not support the
41extensions will simply ignore them.
42
43Authentication using the new key formats described below proceeds
44using the existing SSH "publickey" authentication method described
45in RFC4252 section 7.
46
47New public key formats
48----------------------
49
50The ssh-rsa-cert-v00@openssh.com and ssh-dss-cert-v00@openssh.com key
51types take a similar same high-level format (note: data types and
52encoding are as per RFC4251 section 5). The serialised wire encoding of
53these certificates is also used for storing them on disk.
54
55#define SSH_CERT_TYPE_USER 1
56#define SSH_CERT_TYPE_HOST 2
57
58RSA certificate
59
60 string "ssh-rsa-cert-v00@openssh.com"
61 mpint e
62 mpint n
63 uint32 type
64 string key id
65 string valid principals
66 uint64 valid after
67 uint64 valid before
68 string constraints
69 string nonce
70 string reserved
71 string signature key
72 string signature
73
74DSA certificate
75
76 string "ssh-dss-cert-v00@openssh.com"
77 mpint p
78 mpint q
79 mpint g
80 mpint y
81 uint32 type
82 string key id
83 string valid principals
84 uint64 valid after
85 uint64 valid before
86 string constraints
87 string nonce
88 string reserved
89 string signature key
90 string signature
91
92e and n are the RSA exponent and public modulus respectively.
93
94p, q, g, y are the DSA parameters as described in FIPS-186-2.
95
96type specifies whether this certificate is for identification of a user
97or a host using a SSH_CERT_TYPE_... value.
98
99key id is a free-form text field that is filled in by the CA at the time
100of signing; the intention is that the contents of this field are used to
101identify the identity principal in log messages.
102
103"valid principals" is a string containing zero or more principals as
104strings packed inside it. These principals list the names for which this
105certificate is valid; hostnames for SSH_CERT_TYPE_HOST certificates and
106usernames for SSH_CERT_TYPE_USER certificates. As a special case, a
107zero-length "valid principals" field means the certificate is valid for
108any principal of the specified type. XXX DNS wildcards?
109
110"valid after" and "valid before" specify a validity period for the
111certificate. Each represents a time in seconds since 1970-01-01
11200:00:00. A certificate is considered valid if:
113 valid after <= current time < valid before
114
115constraints is a set of zero or more key constraints encoded as below.
116
117The nonce field is a CA-provided random bitstring of arbitrary length
118(but typically 16 or 32 bytes) included to make attacks that depend on
119inducing collisions in the signature hash infeasible.
120
121The reserved field is current unused and is ignored in this version of
122the protocol.
123
124signature key contains the CA key used to sign the certificate.
125The valid key types for CA keys are ssh-rsa and ssh-dss. "Chained"
126certificates, where the signature key type is a certificate type itself
127are NOT supported. Note that it is possible for a RSA certificate key to
128be signed by a DSS CA key and vice-versa.
129
130signature is computed over all preceding fields from the initial string
131up to, and including the signature key. Signatures are computed and
132encoded according to the rules defined for the CA's public key algorithm
133(RFC4253 section 6.6 for ssh-rsa and ssh-dss).
134
135Constraints
136-----------
137
138The constraints section of the certificate specifies zero or more
139constraints on the certificates validity. The format of this field
140is a sequence of zero or more tuples:
141
142 string name
143 string data
144
145The name field identifies the constraint and the data field encodes
146constraint-specific information (see below). All constraints are
147"critical", if an implementation does not recognise a constraint
148then the validating party should refuse to accept the certificate.
149
150The supported constraints and the contents and structure of their
151data fields are:
152
153Name Format Description
154-----------------------------------------------------------------------------
155force-command string Specifies a command that is executed
156 (replacing any the user specified on the
157 ssh command-line) whenever this key is
158 used for authentication.
159
160permit-X11-forwarding empty Flag indicating that X11 forwarding
161 should be permitted. X11 forwarding will
162 be refused if this constraint is absent.
163
164permit-agent-forwarding empty Flag indicating that agent forwarding
165 should be allowed. Agent forwarding
166 must not be permitted unless this
167 constraint is present.
168
169permit-port-forwarding empty Flag indicating that port-forwarding
170 should be allowed. If this constraint is
171 not present then no port forwarding will
172 be allowed.
173
174permit-pty empty Flag indicating that PTY allocation
175 should be permitted. In the absence of
176 this constraint PTY allocation will be
177 disabled.
178
179permit-user-rc empty Flag indicating that execution of
180 ~/.ssh/rc should be permitted. Execution
181 of this script will not be permitted if
182 this constraint is not present.
183
184source-address string Comma-separated list of source addresses
185 from which this certificate is accepted
186 for authentication. Addresses are
187 specified in CIDR format (nn.nn.nn.nn/nn
188 or hhhh::hhhh/nn).
189 If this constraint is not present then
190 certificates may be presented from any
191 source address.
diff --git a/addrmatch.c b/addrmatch.c
index d39885b7b..5b6773cce 100644
--- a/addrmatch.c
+++ b/addrmatch.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: addrmatch.c,v 1.4 2008/12/10 03:55:20 stevesk Exp $ */ 1/* $OpenBSD: addrmatch.c,v 1.5 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org> 4 * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org>
@@ -126,6 +126,8 @@ addr_netmask(int af, u_int l, struct xaddr *n)
126 switch (af) { 126 switch (af) {
127 case AF_INET: 127 case AF_INET:
128 n->af = AF_INET; 128 n->af = AF_INET;
129 if (l == 0)
130 return 0;
129 n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff); 131 n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff);
130 return 0; 132 return 0;
131 case AF_INET6: 133 case AF_INET6:
@@ -422,3 +424,77 @@ addr_match_list(const char *addr, const char *_list)
422 424
423 return ret; 425 return ret;
424} 426}
427
428/*
429 * Match "addr" against list CIDR list "_list". Lexical wildcards and
430 * negation are not supported. If "addr" == NULL, will verify structure
431 * of "_list".
432 *
433 * Returns 1 on match found (never returned when addr == NULL).
434 * Returns 0 on if no match found, or no errors found when addr == NULL.
435 * Returns -1 on error
436 */
437int
438addr_match_cidr_list(const char *addr, const char *_list)
439{
440 char *list, *cp, *o;
441 struct xaddr try_addr, match_addr;
442 u_int masklen;
443 int ret = 0, r;
444
445 if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
446 debug2("%s: couldn't parse address %.100s", __func__, addr);
447 return 0;
448 }
449 if ((o = list = strdup(_list)) == NULL)
450 return -1;
451 while ((cp = strsep(&list, ",")) != NULL) {
452 if (*cp == '\0') {
453 error("%s: empty entry in list \"%.100s\"",
454 __func__, o);
455 ret = -1;
456 break;
457 }
458
459 /*
460 * NB. This function is called in pre-auth with untrusted data,
461 * so be extra paranoid about junk reaching getaddrino (via
462 * addr_pton_cidr).
463 */
464
465 /* Stop junk from reaching getaddrinfo. +3 is for masklen */
466 if (strlen(cp) > INET6_ADDRSTRLEN + 3) {
467 error("%s: list entry \"%.100s\" too long",
468 __func__, cp);
469 ret = -1;
470 break;
471 }
472#define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/"
473 if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) {
474 error("%s: list entry \"%.100s\" contains invalid "
475 "characters", __func__, cp);
476 ret = -1;
477 }
478
479 /* Prefer CIDR address matching */
480 r = addr_pton_cidr(cp, &match_addr, &masklen);
481 if (r == -1) {
482 error("Invalid network entry \"%.100s\"", cp);
483 ret = -1;
484 break;
485 } else if (r == -2) {
486 error("Inconsistent mask length for "
487 "network \"%.100s\"", cp);
488 ret = -1;
489 break;
490 } else if (r == 0 && addr != NULL) {
491 if (addr_netmatch(&try_addr, &match_addr,
492 masklen) == 0)
493 ret = 1;
494 continue;
495 }
496 }
497 xfree(o);
498
499 return ret;
500}
diff --git a/auth-options.c b/auth-options.c
index ab085c233..396bda62f 100644
--- a/auth-options.c
+++ b/auth-options.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth-options.c,v 1.44 2009/01/22 10:09:16 djm Exp $ */ 1/* $OpenBSD: auth-options.c,v 1.45 2010/02/26 20:29:54 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,7 @@ int no_agent_forwarding_flag = 0;
44int no_x11_forwarding_flag = 0; 44int no_x11_forwarding_flag = 0;
45int no_pty_flag = 0; 45int no_pty_flag = 0;
46int no_user_rc = 0; 46int no_user_rc = 0;
47int key_is_cert_authority = 0;
47 48
48/* "command=" option. */ 49/* "command=" option. */
49char *forced_command = NULL; 50char *forced_command = NULL;
@@ -64,6 +65,7 @@ auth_clear_options(void)
64 no_pty_flag = 0; 65 no_pty_flag = 0;
65 no_x11_forwarding_flag = 0; 66 no_x11_forwarding_flag = 0;
66 no_user_rc = 0; 67 no_user_rc = 0;
68 key_is_cert_authority = 0;
67 while (custom_environment) { 69 while (custom_environment) {
68 struct envstring *ce = custom_environment; 70 struct envstring *ce = custom_environment;
69 custom_environment = ce->next; 71 custom_environment = ce->next;
@@ -96,6 +98,12 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
96 return 1; 98 return 1;
97 99
98 while (*opts && *opts != ' ' && *opts != '\t') { 100 while (*opts && *opts != ' ' && *opts != '\t') {
101 cp = "cert-authority";
102 if (strncasecmp(opts, cp, strlen(cp)) == 0) {
103 key_is_cert_authority = 1;
104 opts += strlen(cp);
105 goto next_option;
106 }
99 cp = "no-port-forwarding"; 107 cp = "no-port-forwarding";
100 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 108 if (strncasecmp(opts, cp, strlen(cp)) == 0) {
101 auth_debug_add("Port forwarding disabled."); 109 auth_debug_add("Port forwarding disabled.");
@@ -374,3 +382,143 @@ bad_option:
374 /* deny access */ 382 /* deny access */
375 return 0; 383 return 0;
376} 384}
385
386/*
387 * Set options from certificate constraints. These supersede user key options
388 * so this must be called after auth_parse_options().
389 */
390int
391auth_cert_constraints(Buffer *c_orig, struct passwd *pw)
392{
393 u_char *name = NULL, *data_blob = NULL;
394 u_int len;
395 Buffer c, data;
396 int ret = -1;
397
398 int cert_no_port_forwarding_flag = 1;
399 int cert_no_agent_forwarding_flag = 1;
400 int cert_no_x11_forwarding_flag = 1;
401 int cert_no_pty_flag = 1;
402 int cert_no_user_rc = 1;
403 char *cert_forced_command = NULL;
404 int cert_source_address_done = 0;
405
406 buffer_init(&data);
407
408 /* Make copy to avoid altering original */
409 buffer_init(&c);
410 buffer_append(&c, buffer_ptr(c_orig), buffer_len(c_orig));
411
412 while (buffer_len(&c) > 0) {
413 if ((name = buffer_get_string_ret(&c, NULL)) == NULL ||
414 (data_blob = buffer_get_string_ret(&c, &len)) == NULL) {
415 error("Certificate constraints corrupt");
416 goto out;
417 }
418 buffer_append(&data, data_blob, len);
419 debug3("found certificate constraint \"%.100s\" len %u",
420 name, len);
421 if (strcmp(name, "permit-X11-forwarding") == 0)
422 cert_no_x11_forwarding_flag = 0;
423 else if (strcmp(name, "permit-agent-forwarding") == 0)
424 cert_no_agent_forwarding_flag = 0;
425 else if (strcmp(name, "permit-port-forwarding") == 0)
426 cert_no_port_forwarding_flag = 0;
427 else if (strcmp(name, "permit-pty") == 0)
428 cert_no_pty_flag = 0;
429 else if (strcmp(name, "permit-user-rc") == 0)
430 cert_no_user_rc = 0;
431 else if (strcmp(name, "force-command") == 0) {
432 char *command = buffer_get_string_ret(&data, NULL);
433
434 if (command == NULL) {
435 error("Certificate constraint \"%s\" corrupt",
436 name);
437 goto out;
438 }
439 if (cert_forced_command != NULL) {
440 error("Certificate has multiple "
441 "forced-command constraints");
442 xfree(command);
443 goto out;
444 }
445 cert_forced_command = command;
446 } else if (strcmp(name, "source-address") == 0) {
447 char *allowed = buffer_get_string_ret(&data, NULL);
448 const char *remote_ip = get_remote_ipaddr();
449
450 if (allowed == NULL) {
451 error("Certificate constraint \"%s\" corrupt",
452 name);
453 goto out;
454 }
455 if (cert_source_address_done++) {
456 error("Certificate has multiple "
457 "source-address constraints");
458 xfree(allowed);
459 goto out;
460 }
461 switch (addr_match_cidr_list(remote_ip, allowed)) {
462 case 1:
463 /* accepted */
464 xfree(allowed);
465 break;
466 case 0:
467 /* no match */
468 logit("Authentication tried for %.100s with "
469 "valid certificate but not from a "
470 "permitted host (ip=%.200s).",
471 pw->pw_name, remote_ip);
472 auth_debug_add("Your address '%.200s' is not "
473 "permitted to use this certificate for "
474 "login.", remote_ip);
475 xfree(allowed);
476 goto out;
477 case -1:
478 error("Certificate source-address contents "
479 "invalid");
480 xfree(allowed);
481 goto out;
482 }
483 } else {
484 error("Certificate constraint \"%s\" is not supported",
485 name);
486 goto out;
487 }
488
489 if (buffer_len(&data) != 0) {
490 error("Certificate constraint \"%s\" corrupt "
491 "(extra data)", name);
492 goto out;
493 }
494 buffer_clear(&data);
495 xfree(name);
496 xfree(data_blob);
497 name = data_blob = NULL;
498 }
499
500 /* successfully parsed all constraints */
501 ret = 0;
502
503 no_port_forwarding_flag |= cert_no_port_forwarding_flag;
504 no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
505 no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
506 no_pty_flag |= cert_no_pty_flag;
507 no_user_rc |= cert_no_user_rc;
508 /* CA-specified forced command supersedes key option */
509 if (cert_forced_command != NULL) {
510 if (forced_command != NULL)
511 xfree(forced_command);
512 forced_command = cert_forced_command;
513 }
514
515 out:
516 if (name != NULL)
517 xfree(name);
518 if (data_blob != NULL)
519 xfree(data_blob);
520 buffer_free(&data);
521 buffer_free(&c);
522 return ret;
523}
524
diff --git a/auth-options.h b/auth-options.h
index 14488f72d..694edc842 100644
--- a/auth-options.h
+++ b/auth-options.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth-options.h,v 1.17 2008/03/26 21:28:14 djm Exp $ */ 1/* $OpenBSD: auth-options.h,v 1.18 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -30,8 +30,10 @@ extern int no_user_rc;
30extern char *forced_command; 30extern char *forced_command;
31extern struct envstring *custom_environment; 31extern struct envstring *custom_environment;
32extern int forced_tun_device; 32extern int forced_tun_device;
33extern int key_is_cert_authority;
33 34
34int auth_parse_options(struct passwd *, char *, char *, u_long); 35int auth_parse_options(struct passwd *, char *, char *, u_long);
35void auth_clear_options(void); 36void auth_clear_options(void);
37int auth_cert_constraints(Buffer *, struct passwd *);
36 38
37#endif 39#endif
diff --git a/auth.h b/auth.h
index bebfb672d..117485ca9 100644
--- a/auth.h
+++ b/auth.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth.h,v 1.63 2009/08/15 18:56:34 fgsch Exp $ */ 1/* $OpenBSD: auth.h,v 1.64 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -178,7 +178,8 @@ check_key_in_hostfiles(struct passwd *, Key *, const char *,
178 178
179/* hostkey handling */ 179/* hostkey handling */
180Key *get_hostkey_by_index(int); 180Key *get_hostkey_by_index(int);
181Key *get_hostkey_by_type(int); 181Key *get_hostkey_public_by_type(int);
182Key *get_hostkey_private_by_type(int);
182int get_hostkey_index(Key *); 183int get_hostkey_index(Key *);
183int ssh1_session_key(BIGNUM *); 184int ssh1_session_key(BIGNUM *);
184 185
diff --git a/auth2-pubkey.c b/auth2-pubkey.c
index 2886f1275..66ca5266b 100644
--- a/auth2-pubkey.c
+++ b/auth2-pubkey.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth2-pubkey.c,v 1.19 2008/07/03 21:46:58 otto Exp $ */ 1/* $OpenBSD: auth2-pubkey.c,v 1.20 2010/02/26 20:29:54 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * 4 *
@@ -32,6 +32,8 @@
32#include <pwd.h> 32#include <pwd.h>
33#include <stdio.h> 33#include <stdio.h>
34#include <stdarg.h> 34#include <stdarg.h>
35#include <string.h>
36#include <time.h>
35#include <unistd.h> 37#include <unistd.h>
36 38
37#include "xmalloc.h" 39#include "xmalloc.h"
@@ -178,6 +180,7 @@ static int
178user_key_allowed2(struct passwd *pw, Key *key, char *file) 180user_key_allowed2(struct passwd *pw, Key *key, char *file)
179{ 181{
180 char line[SSH_MAX_PUBKEY_BYTES]; 182 char line[SSH_MAX_PUBKEY_BYTES];
183 const char *reason;
181 int found_key = 0; 184 int found_key = 0;
182 FILE *f; 185 FILE *f;
183 u_long linenum = 0; 186 u_long linenum = 0;
@@ -196,11 +199,13 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file)
196 } 199 }
197 200
198 found_key = 0; 201 found_key = 0;
199 found = key_new(key->type); 202 found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
200 203
201 while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { 204 while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
202 char *cp, *key_options = NULL; 205 char *cp, *key_options = NULL;
203 206
207 auth_clear_options();
208
204 /* Skip leading whitespace, empty and comment lines. */ 209 /* Skip leading whitespace, empty and comment lines. */
205 for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 210 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
206 ; 211 ;
@@ -227,8 +232,32 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file)
227 continue; 232 continue;
228 } 233 }
229 } 234 }
230 if (key_equal(found, key) && 235 if (auth_parse_options(pw, key_options, file, linenum) != 1)
231 auth_parse_options(pw, key_options, file, linenum) == 1) { 236 continue;
237 if (key->type == KEY_RSA_CERT || key->type == KEY_DSA_CERT) {
238 if (!key_is_cert_authority)
239 continue;
240 if (!key_equal(found, key->cert->signature_key))
241 continue;
242 debug("matching CA found: file %s, line %lu",
243 file, linenum);
244 fp = key_fingerprint(found, SSH_FP_MD5,
245 SSH_FP_HEX);
246 verbose("Found matching %s CA: %s",
247 key_type(found), fp);
248 xfree(fp);
249 if (key_cert_check_authority(key, 0, 0, pw->pw_name,
250 &reason) != 0) {
251 error("%s", reason);
252 auth_debug_add("%s", reason);
253 continue;
254 }
255 if (auth_cert_constraints(&key->cert->constraints,
256 pw) != 0)
257 continue;
258 found_key = 1;
259 break;
260 } else if (!key_is_cert_authority && key_equal(found, key)) {
232 found_key = 1; 261 found_key = 1;
233 debug("matching key found: file %s, line %lu", 262 debug("matching key found: file %s, line %lu",
234 file, linenum); 263 file, linenum);
diff --git a/authfd.c b/authfd.c
index 78a53c7a6..28a8cf2d7 100644
--- a/authfd.c
+++ b/authfd.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: authfd.c,v 1.81 2009/08/27 17:44:52 djm Exp $ */ 1/* $OpenBSD: authfd.c,v 1.82 2010/02/26 20:29:54 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
@@ -483,6 +483,16 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment)
483 buffer_put_bignum2(b, key->rsa->p); 483 buffer_put_bignum2(b, key->rsa->p);
484 buffer_put_bignum2(b, key->rsa->q); 484 buffer_put_bignum2(b, key->rsa->q);
485 break; 485 break;
486 case KEY_RSA_CERT:
487 if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0)
488 fatal("%s: no cert/certblob", __func__);
489 buffer_put_string(b, buffer_ptr(&key->cert->certblob),
490 buffer_len(&key->cert->certblob));
491 buffer_put_bignum2(b, key->rsa->d);
492 buffer_put_bignum2(b, key->rsa->iqmp);
493 buffer_put_bignum2(b, key->rsa->p);
494 buffer_put_bignum2(b, key->rsa->q);
495 break;
486 case KEY_DSA: 496 case KEY_DSA:
487 buffer_put_bignum2(b, key->dsa->p); 497 buffer_put_bignum2(b, key->dsa->p);
488 buffer_put_bignum2(b, key->dsa->q); 498 buffer_put_bignum2(b, key->dsa->q);
@@ -490,6 +500,13 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment)
490 buffer_put_bignum2(b, key->dsa->pub_key); 500 buffer_put_bignum2(b, key->dsa->pub_key);
491 buffer_put_bignum2(b, key->dsa->priv_key); 501 buffer_put_bignum2(b, key->dsa->priv_key);
492 break; 502 break;
503 case KEY_DSA_CERT:
504 if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0)
505 fatal("%s: no cert/certblob", __func__);
506 buffer_put_string(b, buffer_ptr(&key->cert->certblob),
507 buffer_len(&key->cert->certblob));
508 buffer_put_bignum2(b, key->dsa->priv_key);
509 break;
493 } 510 }
494 buffer_put_cstring(b, comment); 511 buffer_put_cstring(b, comment);
495} 512}
@@ -517,7 +534,9 @@ ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key,
517 ssh_encode_identity_rsa1(&msg, key->rsa, comment); 534 ssh_encode_identity_rsa1(&msg, key->rsa, comment);
518 break; 535 break;
519 case KEY_RSA: 536 case KEY_RSA:
537 case KEY_RSA_CERT:
520 case KEY_DSA: 538 case KEY_DSA:
539 case KEY_DSA_CERT:
521 type = constrained ? 540 type = constrained ?
522 SSH2_AGENTC_ADD_ID_CONSTRAINED : 541 SSH2_AGENTC_ADD_ID_CONSTRAINED :
523 SSH2_AGENTC_ADD_IDENTITY; 542 SSH2_AGENTC_ADD_IDENTITY;
@@ -565,7 +584,8 @@ ssh_remove_identity(AuthenticationConnection *auth, Key *key)
565 buffer_put_int(&msg, BN_num_bits(key->rsa->n)); 584 buffer_put_int(&msg, BN_num_bits(key->rsa->n));
566 buffer_put_bignum(&msg, key->rsa->e); 585 buffer_put_bignum(&msg, key->rsa->e);
567 buffer_put_bignum(&msg, key->rsa->n); 586 buffer_put_bignum(&msg, key->rsa->n);
568 } else if (key->type == KEY_DSA || key->type == KEY_RSA) { 587 } else if (key_type_plain(key->type) == KEY_DSA ||
588 key_type_plain(key->type) == KEY_RSA) {
569 key_to_blob(key, &blob, &blen); 589 key_to_blob(key, &blob, &blen);
570 buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY); 590 buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY);
571 buffer_put_string(&msg, blob, blen); 591 buffer_put_string(&msg, blob, blen);
diff --git a/dns.c b/dns.c
index a7da03fa3..2e7bb5aae 100644
--- a/dns.c
+++ b/dns.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: dns.c,v 1.25 2008/06/12 00:03:49 dtucker Exp $ */ 1/* $OpenBSD: dns.c,v 1.26 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2003 Wesley Griffin. All rights reserved. 4 * Copyright (c) 2003 Wesley Griffin. All rights reserved.
@@ -75,7 +75,7 @@ dns_result_totext(unsigned int res)
75 */ 75 */
76static int 76static int
77dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, 77dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
78 u_char **digest, u_int *digest_len, const Key *key) 78 u_char **digest, u_int *digest_len, Key *key)
79{ 79{
80 int success = 0; 80 int success = 0;
81 81
@@ -172,7 +172,7 @@ is_numeric_hostname(const char *hostname)
172 */ 172 */
173int 173int
174verify_host_key_dns(const char *hostname, struct sockaddr *address, 174verify_host_key_dns(const char *hostname, struct sockaddr *address,
175 const Key *hostkey, int *flags) 175 Key *hostkey, int *flags)
176{ 176{
177 u_int counter; 177 u_int counter;
178 int result; 178 int result;
@@ -271,7 +271,7 @@ verify_host_key_dns(const char *hostname, struct sockaddr *address,
271 * Export the fingerprint of a key as a DNS resource record 271 * Export the fingerprint of a key as a DNS resource record
272 */ 272 */
273int 273int
274export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic) 274export_dns_rr(const char *hostname, Key *key, FILE *f, int generic)
275{ 275{
276 u_int8_t rdata_pubkey_algorithm = 0; 276 u_int8_t rdata_pubkey_algorithm = 0;
277 u_int8_t rdata_digest_type = SSHFP_HASH_SHA1; 277 u_int8_t rdata_digest_type = SSHFP_HASH_SHA1;
diff --git a/dns.h b/dns.h
index b2633a1fe..90cfd7b92 100644
--- a/dns.h
+++ b/dns.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: dns.h,v 1.10 2006/08/03 03:34:42 deraadt Exp $ */ 1/* $OpenBSD: dns.h,v 1.11 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2003 Wesley Griffin. All rights reserved. 4 * Copyright (c) 2003 Wesley Griffin. All rights reserved.
@@ -46,7 +46,7 @@ enum sshfp_hashes {
46#define DNS_VERIFY_MATCH 0x00000002 46#define DNS_VERIFY_MATCH 0x00000002
47#define DNS_VERIFY_SECURE 0x00000004 47#define DNS_VERIFY_SECURE 0x00000004
48 48
49int verify_host_key_dns(const char *, struct sockaddr *, const Key *, int *); 49int verify_host_key_dns(const char *, struct sockaddr *, Key *, int *);
50int export_dns_rr(const char *, const Key *, FILE *, int); 50int export_dns_rr(const char *, Key *, FILE *, int);
51 51
52#endif /* DNS_H */ 52#endif /* DNS_H */
diff --git a/hostfile.c b/hostfile.c
index cd28bf446..fc7f84c79 100644
--- a/hostfile.c
+++ b/hostfile.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: hostfile.c,v 1.46 2009/10/11 23:03:15 djm Exp $ */ 1/* $OpenBSD: hostfile.c,v 1.47 2010/02/26 20:29:54 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
@@ -199,7 +199,7 @@ check_host_in_hostfile_by_key_or_type(const char *filename,
199{ 199{
200 FILE *f; 200 FILE *f;
201 char line[8192]; 201 char line[8192];
202 int linenum = 0; 202 int linenum = 0, want_cert = key_is_cert(key);
203 u_int kbits; 203 u_int kbits;
204 char *cp, *cp2, *hashed_host; 204 char *cp, *cp2, *hashed_host;
205 HostStatus end_return; 205 HostStatus end_return;
@@ -229,6 +229,23 @@ check_host_in_hostfile_by_key_or_type(const char *filename,
229 if (!*cp || *cp == '#' || *cp == '\n') 229 if (!*cp || *cp == '#' || *cp == '\n')
230 continue; 230 continue;
231 231
232 /*
233 * Ignore CA keys when looking for raw keys.
234 * Ignore raw keys when looking for CA keys.
235 */
236 if (strncasecmp(cp, CA_MARKER, sizeof(CA_MARKER) - 1) == 0 &&
237 (cp[sizeof(CA_MARKER) - 1] == ' ' ||
238 cp[sizeof(CA_MARKER) - 1] == '\t')) {
239 if (want_cert) {
240 /* Skip the marker and following whitespace */
241 cp += sizeof(CA_MARKER);
242 for (; *cp == ' ' || *cp == '\t'; cp++)
243 ;
244 } else
245 continue;
246 } else if (want_cert)
247 continue;
248
232 /* Find the end of the host name portion. */ 249 /* Find the end of the host name portion. */
233 for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) 250 for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
234 ; 251 ;
@@ -273,8 +290,14 @@ check_host_in_hostfile_by_key_or_type(const char *filename,
273 continue; 290 continue;
274 291
275 /* Check if the current key is the same as the given key. */ 292 /* Check if the current key is the same as the given key. */
276 if (key_equal(key, found)) { 293 if (want_cert && key_equal(key->cert->signature_key, found)) {
277 /* Ok, they match. */ 294 /* Found CA cert for key */
295 debug3("check_host_in_hostfile: CA match line %d",
296 linenum);
297 fclose(f);
298 return HOST_OK;
299 } else if (!want_cert && key_equal(key, found)) {
300 /* Found identical key */
278 debug3("check_host_in_hostfile: match line %d", linenum); 301 debug3("check_host_in_hostfile: match line %d", linenum);
279 fclose(f); 302 fclose(f);
280 return HOST_OK; 303 return HOST_OK;
diff --git a/hostfile.h b/hostfile.h
index d1983b3e0..ebac1e4f1 100644
--- a/hostfile.h
+++ b/hostfile.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: hostfile.h,v 1.16 2006/03/25 22:22:43 djm Exp $ */ 1/* $OpenBSD: hostfile.h,v 1.17 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -28,6 +28,8 @@ int lookup_key_in_hostfile_by_type(const char *, const char *,
28#define HASH_MAGIC "|1|" 28#define HASH_MAGIC "|1|"
29#define HASH_DELIM '|' 29#define HASH_DELIM '|'
30 30
31#define CA_MARKER "@cert-authority"
32
31char *host_hash(const char *, const char *, u_int); 33char *host_hash(const char *, const char *, u_int);
32 34
33#endif 35#endif
diff --git a/kex.h b/kex.h
index 1fa13799d..62fa2ea50 100644
--- a/kex.h
+++ b/kex.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: kex.h,v 1.48 2009/10/24 11:13:54 andreas Exp $ */ 1/* $OpenBSD: kex.h,v 1.49 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -126,7 +126,8 @@ struct Kex {
126 char *client_version_string; 126 char *client_version_string;
127 char *server_version_string; 127 char *server_version_string;
128 int (*verify_host_key)(Key *); 128 int (*verify_host_key)(Key *);
129 Key *(*load_host_key)(int); 129 Key *(*load_host_public_key)(int);
130 Key *(*load_host_private_key)(int);
130 int (*host_key_index)(Key *); 131 int (*host_key_index)(Key *);
131 void (*kex[KEX_MAX])(Kex *); 132 void (*kex[KEX_MAX])(Kex *);
132}; 133};
diff --git a/kexdhs.c b/kexdhs.c
index a6719f672..e722877d5 100644
--- a/kexdhs.c
+++ b/kexdhs.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: kexdhs.c,v 1.10 2009/06/21 07:37:15 dtucker Exp $ */ 1/* $OpenBSD: kexdhs.c,v 1.11 2010/02/26 20:29:54 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001 Markus Friedl. All rights reserved. 3 * Copyright (c) 2001 Markus Friedl. All rights reserved.
4 * 4 *
@@ -50,7 +50,7 @@ kexdh_server(Kex *kex)
50{ 50{
51 BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; 51 BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
52 DH *dh; 52 DH *dh;
53 Key *server_host_key; 53 Key *server_host_public, *server_host_private;
54 u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; 54 u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL;
55 u_int sbloblen, klen, hashlen, slen; 55 u_int sbloblen, klen, hashlen, slen;
56 int kout; 56 int kout;
@@ -71,11 +71,16 @@ kexdh_server(Kex *kex)
71 debug("expecting SSH2_MSG_KEXDH_INIT"); 71 debug("expecting SSH2_MSG_KEXDH_INIT");
72 packet_read_expect(SSH2_MSG_KEXDH_INIT); 72 packet_read_expect(SSH2_MSG_KEXDH_INIT);
73 73
74 if (kex->load_host_key == NULL) 74 if (kex->load_host_public_key == NULL ||
75 kex->load_host_private_key == NULL)
75 fatal("Cannot load hostkey"); 76 fatal("Cannot load hostkey");
76 server_host_key = kex->load_host_key(kex->hostkey_type); 77 server_host_public = kex->load_host_public_key(kex->hostkey_type);
77 if (server_host_key == NULL) 78 if (server_host_public == NULL)
78 fatal("Unsupported hostkey type %d", kex->hostkey_type); 79 fatal("Unsupported hostkey type %d", kex->hostkey_type);
80 server_host_private = kex->load_host_private_key(kex->hostkey_type);
81 if (server_host_private == NULL)
82 fatal("Missing private key for hostkey type %d",
83 kex->hostkey_type);
79 84
80 /* key, cert */ 85 /* key, cert */
81 if ((dh_client_pub = BN_new()) == NULL) 86 if ((dh_client_pub = BN_new()) == NULL)
@@ -113,7 +118,7 @@ kexdh_server(Kex *kex)
113 memset(kbuf, 0, klen); 118 memset(kbuf, 0, klen);
114 xfree(kbuf); 119 xfree(kbuf);
115 120
116 key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); 121 key_to_blob(server_host_public, &server_host_key_blob, &sbloblen);
117 122
118 /* calc H */ 123 /* calc H */
119 kex_dh_hash( 124 kex_dh_hash(
@@ -137,7 +142,7 @@ kexdh_server(Kex *kex)
137 } 142 }
138 143
139 /* sign H */ 144 /* sign H */
140 if (PRIVSEP(key_sign(server_host_key, &signature, &slen, hash, 145 if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash,
141 hashlen)) < 0) 146 hashlen)) < 0)
142 fatal("kexdh_server: key_sign failed"); 147 fatal("kexdh_server: key_sign failed");
143 148
diff --git a/kexgexs.c b/kexgexs.c
index 8515568b3..f4156af96 100644
--- a/kexgexs.c
+++ b/kexgexs.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: kexgexs.c,v 1.12 2009/06/21 07:37:15 dtucker Exp $ */ 1/* $OpenBSD: kexgexs.c,v 1.13 2010/02/26 20:29:54 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Niels Provos. All rights reserved. 3 * Copyright (c) 2000 Niels Provos. All rights reserved.
4 * Copyright (c) 2001 Markus Friedl. All rights reserved. 4 * Copyright (c) 2001 Markus Friedl. All rights reserved.
@@ -52,18 +52,24 @@ void
52kexgex_server(Kex *kex) 52kexgex_server(Kex *kex)
53{ 53{
54 BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; 54 BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
55 Key *server_host_key; 55 Key *server_host_public, *server_host_private;
56 DH *dh; 56 DH *dh;
57 u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; 57 u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL;
58 u_int sbloblen, klen, slen, hashlen; 58 u_int sbloblen, klen, slen, hashlen;
59 int omin = -1, min = -1, omax = -1, max = -1, onbits = -1, nbits = -1; 59 int omin = -1, min = -1, omax = -1, max = -1, onbits = -1, nbits = -1;
60 int type, kout; 60 int type, kout;
61 61
62 if (kex->load_host_key == NULL) 62 if (kex->load_host_public_key == NULL ||
63 kex->load_host_private_key == NULL)
63 fatal("Cannot load hostkey"); 64 fatal("Cannot load hostkey");
64 server_host_key = kex->load_host_key(kex->hostkey_type); 65 server_host_public = kex->load_host_public_key(kex->hostkey_type);
65 if (server_host_key == NULL) 66 if (server_host_public == NULL)
66 fatal("Unsupported hostkey type %d", kex->hostkey_type); 67 fatal("Unsupported hostkey type %d", kex->hostkey_type);
68 server_host_private = kex->load_host_private_key(kex->hostkey_type);
69 if (server_host_private == NULL)
70 fatal("Missing private key for hostkey type %d",
71 kex->hostkey_type);
72
67 73
68 type = packet_read(); 74 type = packet_read();
69 switch (type) { 75 switch (type) {
@@ -149,7 +155,7 @@ kexgex_server(Kex *kex)
149 memset(kbuf, 0, klen); 155 memset(kbuf, 0, klen);
150 xfree(kbuf); 156 xfree(kbuf);
151 157
152 key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); 158 key_to_blob(server_host_public, &server_host_key_blob, &sbloblen);
153 159
154 if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) 160 if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD)
155 omin = min = omax = max = -1; 161 omin = min = omax = max = -1;
@@ -179,7 +185,7 @@ kexgex_server(Kex *kex)
179 } 185 }
180 186
181 /* sign H */ 187 /* sign H */
182 if (PRIVSEP(key_sign(server_host_key, &signature, &slen, hash, 188 if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash,
183 hashlen)) < 0) 189 hashlen)) < 0)
184 fatal("kexgex_server: key_sign failed"); 190 fatal("kexgex_server: key_sign failed");
185 191
diff --git a/key.c b/key.c
index 5aea416b3..387190b53 100644
--- a/key.c
+++ b/key.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: key.c,v 1.82 2010/01/13 01:10:56 dtucker Exp $ */ 1/* $OpenBSD: key.c,v 1.83 2010/02/26 20:29:54 djm Exp $ */
2/* 2/*
3 * read_bignum(): 3 * read_bignum():
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -52,6 +52,21 @@
52#include "uuencode.h" 52#include "uuencode.h"
53#include "buffer.h" 53#include "buffer.h"
54#include "log.h" 54#include "log.h"
55#include "ssh2.h"
56
57static struct KeyCert *
58cert_new(void)
59{
60 struct KeyCert *cert;
61
62 cert = xcalloc(1, sizeof(*cert));
63 buffer_init(&cert->certblob);
64 buffer_init(&cert->constraints);
65 cert->key_id = NULL;
66 cert->principals = NULL;
67 cert->signature_key = NULL;
68 return cert;
69}
55 70
56Key * 71Key *
57key_new(int type) 72key_new(int type)
@@ -63,9 +78,11 @@ key_new(int type)
63 k->type = type; 78 k->type = type;
64 k->dsa = NULL; 79 k->dsa = NULL;
65 k->rsa = NULL; 80 k->rsa = NULL;
81 k->cert = NULL;
66 switch (k->type) { 82 switch (k->type) {
67 case KEY_RSA1: 83 case KEY_RSA1:
68 case KEY_RSA: 84 case KEY_RSA:
85 case KEY_RSA_CERT:
69 if ((rsa = RSA_new()) == NULL) 86 if ((rsa = RSA_new()) == NULL)
70 fatal("key_new: RSA_new failed"); 87 fatal("key_new: RSA_new failed");
71 if ((rsa->n = BN_new()) == NULL) 88 if ((rsa->n = BN_new()) == NULL)
@@ -75,6 +92,7 @@ key_new(int type)
75 k->rsa = rsa; 92 k->rsa = rsa;
76 break; 93 break;
77 case KEY_DSA: 94 case KEY_DSA:
95 case KEY_DSA_CERT:
78 if ((dsa = DSA_new()) == NULL) 96 if ((dsa = DSA_new()) == NULL)
79 fatal("key_new: DSA_new failed"); 97 fatal("key_new: DSA_new failed");
80 if ((dsa->p = BN_new()) == NULL) 98 if ((dsa->p = BN_new()) == NULL)
@@ -93,16 +111,20 @@ key_new(int type)
93 fatal("key_new: bad key type %d", k->type); 111 fatal("key_new: bad key type %d", k->type);
94 break; 112 break;
95 } 113 }
114
115 if (key_is_cert(k))
116 k->cert = cert_new();
117
96 return k; 118 return k;
97} 119}
98 120
99Key * 121void
100key_new_private(int type) 122key_add_private(Key *k)
101{ 123{
102 Key *k = key_new(type);
103 switch (k->type) { 124 switch (k->type) {
104 case KEY_RSA1: 125 case KEY_RSA1:
105 case KEY_RSA: 126 case KEY_RSA:
127 case KEY_RSA_CERT:
106 if ((k->rsa->d = BN_new()) == NULL) 128 if ((k->rsa->d = BN_new()) == NULL)
107 fatal("key_new_private: BN_new failed"); 129 fatal("key_new_private: BN_new failed");
108 if ((k->rsa->iqmp = BN_new()) == NULL) 130 if ((k->rsa->iqmp = BN_new()) == NULL)
@@ -117,6 +139,7 @@ key_new_private(int type)
117 fatal("key_new_private: BN_new failed"); 139 fatal("key_new_private: BN_new failed");
118 break; 140 break;
119 case KEY_DSA: 141 case KEY_DSA:
142 case KEY_DSA_CERT:
120 if ((k->dsa->priv_key = BN_new()) == NULL) 143 if ((k->dsa->priv_key = BN_new()) == NULL)
121 fatal("key_new_private: BN_new failed"); 144 fatal("key_new_private: BN_new failed");
122 break; 145 break;
@@ -125,9 +148,34 @@ key_new_private(int type)
125 default: 148 default:
126 break; 149 break;
127 } 150 }
151}
152
153Key *
154key_new_private(int type)
155{
156 Key *k = key_new(type);
157
158 key_add_private(k);
128 return k; 159 return k;
129} 160}
130 161
162static void
163cert_free(struct KeyCert *cert)
164{
165 u_int i;
166
167 buffer_free(&cert->certblob);
168 buffer_free(&cert->constraints);
169 if (cert->key_id != NULL)
170 xfree(cert->key_id);
171 for (i = 0; i < cert->nprincipals; i++)
172 xfree(cert->principals[i]);
173 if (cert->principals != NULL)
174 xfree(cert->principals);
175 if (cert->signature_key != NULL)
176 key_free(cert->signature_key);
177}
178
131void 179void
132key_free(Key *k) 180key_free(Key *k)
133{ 181{
@@ -136,11 +184,13 @@ key_free(Key *k)
136 switch (k->type) { 184 switch (k->type) {
137 case KEY_RSA1: 185 case KEY_RSA1:
138 case KEY_RSA: 186 case KEY_RSA:
187 case KEY_RSA_CERT:
139 if (k->rsa != NULL) 188 if (k->rsa != NULL)
140 RSA_free(k->rsa); 189 RSA_free(k->rsa);
141 k->rsa = NULL; 190 k->rsa = NULL;
142 break; 191 break;
143 case KEY_DSA: 192 case KEY_DSA:
193 case KEY_DSA_CERT:
144 if (k->dsa != NULL) 194 if (k->dsa != NULL)
145 DSA_free(k->dsa); 195 DSA_free(k->dsa);
146 k->dsa = NULL; 196 k->dsa = NULL;
@@ -151,20 +201,49 @@ key_free(Key *k)
151 fatal("key_free: bad key type %d", k->type); 201 fatal("key_free: bad key type %d", k->type);
152 break; 202 break;
153 } 203 }
204 if (key_is_cert(k)) {
205 if (k->cert != NULL)
206 cert_free(k->cert);
207 k->cert = NULL;
208 }
209
154 xfree(k); 210 xfree(k);
155} 211}
156 212
213static int
214cert_compare(struct KeyCert *a, struct KeyCert *b)
215{
216 if (a == NULL && b == NULL)
217 return 1;
218 if (a == NULL || b == NULL)
219 return 0;
220 if (buffer_len(&a->certblob) != buffer_len(&b->certblob))
221 return 0;
222 if (memcmp(buffer_ptr(&a->certblob), buffer_ptr(&b->certblob),
223 buffer_len(&a->certblob)) != 0)
224 return 0;
225 return 1;
226}
227
228/*
229 * Compare public portions of key only, allowing comparisons between
230 * certificates and plain keys too.
231 */
157int 232int
158key_equal(const Key *a, const Key *b) 233key_equal_public(const Key *a, const Key *b)
159{ 234{
160 if (a == NULL || b == NULL || a->type != b->type) 235 if (a == NULL || b == NULL ||
236 key_type_plain(a->type) != key_type_plain(b->type))
161 return 0; 237 return 0;
238
162 switch (a->type) { 239 switch (a->type) {
163 case KEY_RSA1: 240 case KEY_RSA1:
241 case KEY_RSA_CERT:
164 case KEY_RSA: 242 case KEY_RSA:
165 return a->rsa != NULL && b->rsa != NULL && 243 return a->rsa != NULL && b->rsa != NULL &&
166 BN_cmp(a->rsa->e, b->rsa->e) == 0 && 244 BN_cmp(a->rsa->e, b->rsa->e) == 0 &&
167 BN_cmp(a->rsa->n, b->rsa->n) == 0; 245 BN_cmp(a->rsa->n, b->rsa->n) == 0;
246 case KEY_DSA_CERT:
168 case KEY_DSA: 247 case KEY_DSA:
169 return a->dsa != NULL && b->dsa != NULL && 248 return a->dsa != NULL && b->dsa != NULL &&
170 BN_cmp(a->dsa->p, b->dsa->p) == 0 && 249 BN_cmp(a->dsa->p, b->dsa->p) == 0 &&
@@ -177,16 +256,27 @@ key_equal(const Key *a, const Key *b)
177 /* NOTREACHED */ 256 /* NOTREACHED */
178} 257}
179 258
259int
260key_equal(const Key *a, const Key *b)
261{
262 if (a == NULL || b == NULL || a->type != b->type)
263 return 0;
264 if (key_is_cert(a)) {
265 if (!cert_compare(a->cert, b->cert))
266 return 0;
267 }
268 return key_equal_public(a, b);
269}
270
180u_char* 271u_char*
181key_fingerprint_raw(const Key *k, enum fp_type dgst_type, 272key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length)
182 u_int *dgst_raw_length)
183{ 273{
184 const EVP_MD *md = NULL; 274 const EVP_MD *md = NULL;
185 EVP_MD_CTX ctx; 275 EVP_MD_CTX ctx;
186 u_char *blob = NULL; 276 u_char *blob = NULL;
187 u_char *retval = NULL; 277 u_char *retval = NULL;
188 u_int len = 0; 278 u_int len = 0;
189 int nlen, elen; 279 int nlen, elen, otype;
190 280
191 *dgst_raw_length = 0; 281 *dgst_raw_length = 0;
192 282
@@ -214,6 +304,14 @@ key_fingerprint_raw(const Key *k, enum fp_type dgst_type,
214 case KEY_RSA: 304 case KEY_RSA:
215 key_to_blob(k, &blob, &len); 305 key_to_blob(k, &blob, &len);
216 break; 306 break;
307 case KEY_DSA_CERT:
308 case KEY_RSA_CERT:
309 /* We want a fingerprint of the _key_ not of the cert */
310 otype = k->type;
311 k->type = key_type_plain(k->type);
312 key_to_blob(k, &blob, &len);
313 k->type = otype;
314 break;
217 case KEY_UNSPEC: 315 case KEY_UNSPEC:
218 return retval; 316 return retval;
219 default: 317 default:
@@ -408,7 +506,7 @@ key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, const Key *k)
408} 506}
409 507
410char * 508char *
411key_fingerprint(const Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep) 509key_fingerprint(Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep)
412{ 510{
413 char *retval = NULL; 511 char *retval = NULL;
414 u_char *dgst_raw; 512 u_char *dgst_raw;
@@ -533,6 +631,8 @@ key_read(Key *ret, char **cpp)
533 case KEY_UNSPEC: 631 case KEY_UNSPEC:
534 case KEY_RSA: 632 case KEY_RSA:
535 case KEY_DSA: 633 case KEY_DSA:
634 case KEY_DSA_CERT:
635 case KEY_RSA_CERT:
536 space = strchr(cp, ' '); 636 space = strchr(cp, ' ');
537 if (space == NULL) { 637 if (space == NULL) {
538 debug3("key_read: missing whitespace"); 638 debug3("key_read: missing whitespace");
@@ -577,25 +677,36 @@ key_read(Key *ret, char **cpp)
577 return -1; 677 return -1;
578 } 678 }
579/*XXXX*/ 679/*XXXX*/
580 if (ret->type == KEY_RSA) { 680 if (key_is_cert(ret)) {
681 if (!key_is_cert(k)) {
682 error("key_read: loaded key is not a cert");
683 key_free(k);
684 return -1;
685 }
686 if (ret->cert != NULL)
687 cert_free(ret->cert);
688 ret->cert = k->cert;
689 k->cert = NULL;
690 }
691 if (key_type_plain(ret->type) == KEY_RSA) {
581 if (ret->rsa != NULL) 692 if (ret->rsa != NULL)
582 RSA_free(ret->rsa); 693 RSA_free(ret->rsa);
583 ret->rsa = k->rsa; 694 ret->rsa = k->rsa;
584 k->rsa = NULL; 695 k->rsa = NULL;
585 success = 1;
586#ifdef DEBUG_PK 696#ifdef DEBUG_PK
587 RSA_print_fp(stderr, ret->rsa, 8); 697 RSA_print_fp(stderr, ret->rsa, 8);
588#endif 698#endif
589 } else { 699 }
700 if (key_type_plain(ret->type) == KEY_DSA) {
590 if (ret->dsa != NULL) 701 if (ret->dsa != NULL)
591 DSA_free(ret->dsa); 702 DSA_free(ret->dsa);
592 ret->dsa = k->dsa; 703 ret->dsa = k->dsa;
593 k->dsa = NULL; 704 k->dsa = NULL;
594 success = 1;
595#ifdef DEBUG_PK 705#ifdef DEBUG_PK
596 DSA_print_fp(stderr, ret->dsa, 8); 706 DSA_print_fp(stderr, ret->dsa, 8);
597#endif 707#endif
598 } 708 }
709 success = 1;
599/*XXXX*/ 710/*XXXX*/
600 key_free(k); 711 key_free(k);
601 if (success != 1) 712 if (success != 1)
@@ -622,28 +733,53 @@ key_write(const Key *key, FILE *f)
622 u_char *blob; 733 u_char *blob;
623 char *uu; 734 char *uu;
624 735
625 if (key->type == KEY_RSA1 && key->rsa != NULL) { 736 if (key_is_cert(key)) {
737 if (key->cert == NULL) {
738 error("%s: no cert data", __func__);
739 return 0;
740 }
741 if (buffer_len(&key->cert->certblob) == 0) {
742 error("%s: no signed certificate blob", __func__);
743 return 0;
744 }
745 }
746
747 switch (key->type) {
748 case KEY_RSA1:
749 if (key->rsa == NULL)
750 return 0;
626 /* size of modulus 'n' */ 751 /* size of modulus 'n' */
627 bits = BN_num_bits(key->rsa->n); 752 bits = BN_num_bits(key->rsa->n);
628 fprintf(f, "%u", bits); 753 fprintf(f, "%u", bits);
629 if (write_bignum(f, key->rsa->e) && 754 if (write_bignum(f, key->rsa->e) &&
630 write_bignum(f, key->rsa->n)) { 755 write_bignum(f, key->rsa->n))
631 success = 1; 756 return 1;
632 } else { 757 error("key_write: failed for RSA key");
633 error("key_write: failed for RSA key"); 758 return 0;
634 } 759 case KEY_DSA:
635 } else if ((key->type == KEY_DSA && key->dsa != NULL) || 760 case KEY_DSA_CERT:
636 (key->type == KEY_RSA && key->rsa != NULL)) { 761 if (key->dsa == NULL)
637 key_to_blob(key, &blob, &len); 762 return 0;
638 uu = xmalloc(2*len); 763 break;
639 n = uuencode(blob, len, uu, 2*len); 764 case KEY_RSA:
640 if (n > 0) { 765 case KEY_RSA_CERT:
641 fprintf(f, "%s %s", key_ssh_name(key), uu); 766 if (key->rsa == NULL)
642 success = 1; 767 return 0;
643 } 768 break;
644 xfree(blob); 769 default:
645 xfree(uu); 770 return 0;
646 } 771 }
772
773 key_to_blob(key, &blob, &len);
774 uu = xmalloc(2*len);
775 n = uuencode(blob, len, uu, 2*len);
776 if (n > 0) {
777 fprintf(f, "%s %s", key_ssh_name(key), uu);
778 success = 1;
779 }
780 xfree(blob);
781 xfree(uu);
782
647 return success; 783 return success;
648} 784}
649 785
@@ -657,6 +793,10 @@ key_type(const Key *k)
657 return "RSA"; 793 return "RSA";
658 case KEY_DSA: 794 case KEY_DSA:
659 return "DSA"; 795 return "DSA";
796 case KEY_RSA_CERT:
797 return "RSA-CERT";
798 case KEY_DSA_CERT:
799 return "DSA-CERT";
660 } 800 }
661 return "unknown"; 801 return "unknown";
662} 802}
@@ -669,6 +809,10 @@ key_ssh_name(const Key *k)
669 return "ssh-rsa"; 809 return "ssh-rsa";
670 case KEY_DSA: 810 case KEY_DSA:
671 return "ssh-dss"; 811 return "ssh-dss";
812 case KEY_RSA_CERT:
813 return "ssh-rsa-cert-v00@openssh.com";
814 case KEY_DSA_CERT:
815 return "ssh-dss-cert-v00@openssh.com";
672 } 816 }
673 return "ssh-unknown"; 817 return "ssh-unknown";
674} 818}
@@ -679,8 +823,10 @@ key_size(const Key *k)
679 switch (k->type) { 823 switch (k->type) {
680 case KEY_RSA1: 824 case KEY_RSA1:
681 case KEY_RSA: 825 case KEY_RSA:
826 case KEY_RSA_CERT:
682 return BN_num_bits(k->rsa->n); 827 return BN_num_bits(k->rsa->n);
683 case KEY_DSA: 828 case KEY_DSA:
829 case KEY_DSA_CERT:
684 return BN_num_bits(k->dsa->p); 830 return BN_num_bits(k->dsa->p);
685 } 831 }
686 return 0; 832 return 0;
@@ -723,6 +869,9 @@ key_generate(int type, u_int bits)
723 case KEY_RSA1: 869 case KEY_RSA1:
724 k->rsa = rsa_generate_private_key(bits); 870 k->rsa = rsa_generate_private_key(bits);
725 break; 871 break;
872 case KEY_RSA_CERT:
873 case KEY_DSA_CERT:
874 fatal("key_generate: cert keys cannot be generated directly");
726 default: 875 default:
727 fatal("key_generate: unknown type %d", type); 876 fatal("key_generate: unknown type %d", type);
728 } 877 }
@@ -730,12 +879,55 @@ key_generate(int type, u_int bits)
730 return k; 879 return k;
731} 880}
732 881
882void
883key_cert_copy(const Key *from_key, struct Key *to_key)
884{
885 u_int i;
886 const struct KeyCert *from;
887 struct KeyCert *to;
888
889 if (to_key->cert != NULL) {
890 cert_free(to_key->cert);
891 to_key->cert = NULL;
892 }
893
894 if ((from = from_key->cert) == NULL)
895 return;
896
897 to = to_key->cert = cert_new();
898
899 buffer_append(&to->certblob, buffer_ptr(&from->certblob),
900 buffer_len(&from->certblob));
901
902 buffer_append(&to->constraints, buffer_ptr(&from->constraints),
903 buffer_len(&from->constraints));
904
905 to->type = from->type;
906 to->key_id = from->key_id == NULL ? NULL : xstrdup(from->key_id);
907 to->valid_after = from->valid_after;
908 to->valid_before = from->valid_before;
909 to->signature_key = from->signature_key == NULL ?
910 NULL : key_from_private(from->signature_key);
911
912 to->nprincipals = from->nprincipals;
913 if (to->nprincipals > CERT_MAX_PRINCIPALS)
914 fatal("%s: nprincipals (%u) > CERT_MAX_PRINCIPALS (%u)",
915 __func__, to->nprincipals, CERT_MAX_PRINCIPALS);
916 if (to->nprincipals > 0) {
917 to->principals = xcalloc(from->nprincipals,
918 sizeof(*to->principals));
919 for (i = 0; i < to->nprincipals; i++)
920 to->principals[i] = xstrdup(from->principals[i]);
921 }
922}
923
733Key * 924Key *
734key_from_private(const Key *k) 925key_from_private(const Key *k)
735{ 926{
736 Key *n = NULL; 927 Key *n = NULL;
737 switch (k->type) { 928 switch (k->type) {
738 case KEY_DSA: 929 case KEY_DSA:
930 case KEY_DSA_CERT:
739 n = key_new(k->type); 931 n = key_new(k->type);
740 if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || 932 if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) ||
741 (BN_copy(n->dsa->q, k->dsa->q) == NULL) || 933 (BN_copy(n->dsa->q, k->dsa->q) == NULL) ||
@@ -745,6 +937,7 @@ key_from_private(const Key *k)
745 break; 937 break;
746 case KEY_RSA: 938 case KEY_RSA:
747 case KEY_RSA1: 939 case KEY_RSA1:
940 case KEY_RSA_CERT:
748 n = key_new(k->type); 941 n = key_new(k->type);
749 if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || 942 if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) ||
750 (BN_copy(n->rsa->e, k->rsa->e) == NULL)) 943 (BN_copy(n->rsa->e, k->rsa->e) == NULL))
@@ -754,6 +947,8 @@ key_from_private(const Key *k)
754 fatal("key_from_private: unknown type %d", k->type); 947 fatal("key_from_private: unknown type %d", k->type);
755 break; 948 break;
756 } 949 }
950 if (key_is_cert(k))
951 key_cert_copy(k, n);
757 return n; 952 return n;
758} 953}
759 954
@@ -770,6 +965,10 @@ key_type_from_name(char *name)
770 return KEY_RSA; 965 return KEY_RSA;
771 } else if (strcmp(name, "ssh-dss") == 0) { 966 } else if (strcmp(name, "ssh-dss") == 0) {
772 return KEY_DSA; 967 return KEY_DSA;
968 } else if (strcmp(name, "ssh-rsa-cert-v00@openssh.com") == 0) {
969 return KEY_RSA_CERT;
970 } else if (strcmp(name, "ssh-dss-cert-v00@openssh.com") == 0) {
971 return KEY_DSA_CERT;
773 } 972 }
774 debug2("key_type_from_name: unknown key type '%s'", name); 973 debug2("key_type_from_name: unknown key type '%s'", name);
775 return KEY_UNSPEC; 974 return KEY_UNSPEC;
@@ -797,6 +996,117 @@ key_names_valid2(const char *names)
797 return 1; 996 return 1;
798} 997}
799 998
999static int
1000cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen)
1001{
1002 u_char *principals, *constraints, *sig_key, *sig;
1003 u_int signed_len, plen, clen, sklen, slen;
1004 Buffer tmp;
1005 char *principal;
1006 int ret = -1;
1007
1008 buffer_init(&tmp);
1009
1010 /* Copy the entire key blob for verification and later serialisation */
1011 buffer_append(&key->cert->certblob, blob, blen);
1012
1013 principals = constraints = sig_key = sig = NULL;
1014 if (buffer_get_int_ret(&key->cert->type, b) != 0 ||
1015 (key->cert->key_id = buffer_get_string_ret(b, NULL)) == NULL ||
1016 (principals = buffer_get_string_ret(b, &plen)) == NULL ||
1017 buffer_get_int64_ret(&key->cert->valid_after, b) != 0 ||
1018 buffer_get_int64_ret(&key->cert->valid_before, b) != 0 ||
1019 (constraints = buffer_get_string_ret(b, &clen)) == NULL ||
1020 /* skip nonce */ buffer_get_string_ptr_ret(b, NULL) == NULL ||
1021 /* skip reserved */ buffer_get_string_ptr_ret(b, NULL) == NULL ||
1022 (sig_key = buffer_get_string_ret(b, &sklen)) == NULL) {
1023 error("%s: parse error", __func__);
1024 goto out;
1025 }
1026
1027 /* Signature is left in the buffer so we can calculate this length */
1028 signed_len = buffer_len(&key->cert->certblob) - buffer_len(b);
1029
1030 if ((sig = buffer_get_string_ret(b, &slen)) == NULL) {
1031 error("%s: parse error", __func__);
1032 goto out;
1033 }
1034
1035 if (key->cert->type != SSH2_CERT_TYPE_USER &&
1036 key->cert->type != SSH2_CERT_TYPE_HOST) {
1037 error("Unknown certificate type %u", key->cert->type);
1038 goto out;
1039 }
1040
1041 buffer_append(&tmp, principals, plen);
1042 while (buffer_len(&tmp) > 0) {
1043 if (key->cert->nprincipals >= CERT_MAX_PRINCIPALS) {
1044 error("Too many principals");
1045 goto out;
1046 }
1047 if ((principal = buffer_get_string_ret(&tmp, NULL)) == NULL) {
1048 error("Principals data invalid");
1049 goto out;
1050 }
1051 key->cert->principals = xrealloc(key->cert->principals,
1052 key->cert->nprincipals + 1, sizeof(*key->cert->principals));
1053 key->cert->principals[key->cert->nprincipals++] = principal;
1054 }
1055
1056 buffer_clear(&tmp);
1057
1058 buffer_append(&key->cert->constraints, constraints, clen);
1059 buffer_append(&tmp, constraints, clen);
1060 /* validate structure */
1061 while (buffer_len(&tmp) != 0) {
1062 if (buffer_get_string_ptr(&tmp, NULL) == NULL ||
1063 buffer_get_string_ptr(&tmp, NULL) == NULL) {
1064 error("Constraints data invalid");
1065 goto out;
1066 }
1067 }
1068 buffer_clear(&tmp);
1069
1070 if ((key->cert->signature_key = key_from_blob(sig_key,
1071 sklen)) == NULL) {
1072 error("Signature key invalid");
1073 goto out;
1074 }
1075 if (key->cert->signature_key->type != KEY_RSA &&
1076 key->cert->signature_key->type != KEY_DSA) {
1077 error("Invalid signature key type %s (%d)",
1078 key_type(key->cert->signature_key),
1079 key->cert->signature_key->type);
1080 goto out;
1081 }
1082
1083 switch (key_verify(key->cert->signature_key, sig, slen,
1084 buffer_ptr(&key->cert->certblob), signed_len)) {
1085 case 1:
1086 break; /* Good signature */
1087 case 0:
1088 error("Invalid signature on certificate");
1089 goto out;
1090 case -1:
1091 error("Certificate signature verification failed");
1092 goto out;
1093 }
1094
1095 ret = 0;
1096
1097 out:
1098 buffer_free(&tmp);
1099 if (principals != NULL)
1100 xfree(principals);
1101 if (constraints != NULL)
1102 xfree(constraints);
1103 if (sig_key != NULL)
1104 xfree(sig_key);
1105 if (sig != NULL)
1106 xfree(sig);
1107 return ret;
1108}
1109
800Key * 1110Key *
801key_from_blob(const u_char *blob, u_int blen) 1111key_from_blob(const u_char *blob, u_int blen)
802{ 1112{
@@ -819,10 +1129,12 @@ key_from_blob(const u_char *blob, u_int blen)
819 1129
820 switch (type) { 1130 switch (type) {
821 case KEY_RSA: 1131 case KEY_RSA:
1132 case KEY_RSA_CERT:
822 key = key_new(type); 1133 key = key_new(type);
823 if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 || 1134 if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 ||
824 buffer_get_bignum2_ret(&b, key->rsa->n) == -1) { 1135 buffer_get_bignum2_ret(&b, key->rsa->n) == -1) {
825 error("key_from_blob: can't read rsa key"); 1136 error("key_from_blob: can't read rsa key");
1137 badkey:
826 key_free(key); 1138 key_free(key);
827 key = NULL; 1139 key = NULL;
828 goto out; 1140 goto out;
@@ -832,15 +1144,14 @@ key_from_blob(const u_char *blob, u_int blen)
832#endif 1144#endif
833 break; 1145 break;
834 case KEY_DSA: 1146 case KEY_DSA:
1147 case KEY_DSA_CERT:
835 key = key_new(type); 1148 key = key_new(type);
836 if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 || 1149 if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 ||
837 buffer_get_bignum2_ret(&b, key->dsa->q) == -1 || 1150 buffer_get_bignum2_ret(&b, key->dsa->q) == -1 ||
838 buffer_get_bignum2_ret(&b, key->dsa->g) == -1 || 1151 buffer_get_bignum2_ret(&b, key->dsa->g) == -1 ||
839 buffer_get_bignum2_ret(&b, key->dsa->pub_key) == -1) { 1152 buffer_get_bignum2_ret(&b, key->dsa->pub_key) == -1) {
840 error("key_from_blob: can't read dsa key"); 1153 error("key_from_blob: can't read dsa key");
841 key_free(key); 1154 goto badkey;
842 key = NULL;
843 goto out;
844 } 1155 }
845#ifdef DEBUG_PK 1156#ifdef DEBUG_PK
846 DSA_print_fp(stderr, key->dsa, 8); 1157 DSA_print_fp(stderr, key->dsa, 8);
@@ -853,6 +1164,10 @@ key_from_blob(const u_char *blob, u_int blen)
853 error("key_from_blob: cannot handle type %s", ktype); 1164 error("key_from_blob: cannot handle type %s", ktype);
854 goto out; 1165 goto out;
855 } 1166 }
1167 if (key_is_cert(key) && cert_parse(&b, key, blob, blen) == -1) {
1168 error("key_from_blob: can't parse cert data");
1169 goto badkey;
1170 }
856 rlen = buffer_len(&b); 1171 rlen = buffer_len(&b);
857 if (key != NULL && rlen != 0) 1172 if (key != NULL && rlen != 0)
858 error("key_from_blob: remaining bytes in key blob %d", rlen); 1173 error("key_from_blob: remaining bytes in key blob %d", rlen);
@@ -875,6 +1190,12 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp)
875 } 1190 }
876 buffer_init(&b); 1191 buffer_init(&b);
877 switch (key->type) { 1192 switch (key->type) {
1193 case KEY_DSA_CERT:
1194 case KEY_RSA_CERT:
1195 /* Use the existing blob */
1196 buffer_append(&b, buffer_ptr(&key->cert->certblob),
1197 buffer_len(&key->cert->certblob));
1198 break;
878 case KEY_DSA: 1199 case KEY_DSA:
879 buffer_put_cstring(&b, key_ssh_name(key)); 1200 buffer_put_cstring(&b, key_ssh_name(key));
880 buffer_put_bignum2(&b, key->dsa->p); 1201 buffer_put_bignum2(&b, key->dsa->p);
@@ -911,8 +1232,10 @@ key_sign(
911 const u_char *data, u_int datalen) 1232 const u_char *data, u_int datalen)
912{ 1233{
913 switch (key->type) { 1234 switch (key->type) {
1235 case KEY_DSA_CERT:
914 case KEY_DSA: 1236 case KEY_DSA:
915 return ssh_dss_sign(key, sigp, lenp, data, datalen); 1237 return ssh_dss_sign(key, sigp, lenp, data, datalen);
1238 case KEY_RSA_CERT:
916 case KEY_RSA: 1239 case KEY_RSA:
917 return ssh_rsa_sign(key, sigp, lenp, data, datalen); 1240 return ssh_rsa_sign(key, sigp, lenp, data, datalen);
918 default: 1241 default:
@@ -935,8 +1258,10 @@ key_verify(
935 return -1; 1258 return -1;
936 1259
937 switch (key->type) { 1260 switch (key->type) {
1261 case KEY_DSA_CERT:
938 case KEY_DSA: 1262 case KEY_DSA:
939 return ssh_dss_verify(key, signature, signaturelen, data, datalen); 1263 return ssh_dss_verify(key, signature, signaturelen, data, datalen);
1264 case KEY_RSA_CERT:
940 case KEY_RSA: 1265 case KEY_RSA:
941 return ssh_rsa_verify(key, signature, signaturelen, data, datalen); 1266 return ssh_rsa_verify(key, signature, signaturelen, data, datalen);
942 default: 1267 default:
@@ -958,6 +1283,9 @@ key_demote(const Key *k)
958 pk->rsa = NULL; 1283 pk->rsa = NULL;
959 1284
960 switch (k->type) { 1285 switch (k->type) {
1286 case KEY_RSA_CERT:
1287 key_cert_copy(k, pk);
1288 /* FALLTHROUGH */
961 case KEY_RSA1: 1289 case KEY_RSA1:
962 case KEY_RSA: 1290 case KEY_RSA:
963 if ((pk->rsa = RSA_new()) == NULL) 1291 if ((pk->rsa = RSA_new()) == NULL)
@@ -967,6 +1295,9 @@ key_demote(const Key *k)
967 if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL) 1295 if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL)
968 fatal("key_demote: BN_dup failed"); 1296 fatal("key_demote: BN_dup failed");
969 break; 1297 break;
1298 case KEY_DSA_CERT:
1299 key_cert_copy(k, pk);
1300 /* FALLTHROUGH */
970 case KEY_DSA: 1301 case KEY_DSA:
971 if ((pk->dsa = DSA_new()) == NULL) 1302 if ((pk->dsa = DSA_new()) == NULL)
972 fatal("key_demote: DSA_new failed"); 1303 fatal("key_demote: DSA_new failed");
@@ -986,3 +1317,199 @@ key_demote(const Key *k)
986 1317
987 return (pk); 1318 return (pk);
988} 1319}
1320
1321int
1322key_is_cert(const Key *k)
1323{
1324 return k != NULL &&
1325 (k->type == KEY_RSA_CERT || k->type == KEY_DSA_CERT);
1326}
1327
1328/* Return the cert-less equivalent to a certified key type */
1329int
1330key_type_plain(int type)
1331{
1332 switch (type) {
1333 case KEY_RSA_CERT:
1334 return KEY_RSA;
1335 case KEY_DSA_CERT:
1336 return KEY_DSA;
1337 default:
1338 return type;
1339 }
1340}
1341
1342/* Convert a KEY_RSA or KEY_DSA to their _CERT equivalent */
1343int
1344key_to_certified(Key *k)
1345{
1346 switch (k->type) {
1347 case KEY_RSA:
1348 k->cert = cert_new();
1349 k->type = KEY_RSA_CERT;
1350 return 0;
1351 case KEY_DSA:
1352 k->cert = cert_new();
1353 k->type = KEY_DSA_CERT;
1354 return 0;
1355 default:
1356 error("%s: key has incorrect type %s", __func__, key_type(k));
1357 return -1;
1358 }
1359}
1360
1361/* Convert a KEY_RSA_CERT or KEY_DSA_CERT to their raw key equivalent */
1362int
1363key_drop_cert(Key *k)
1364{
1365 switch (k->type) {
1366 case KEY_RSA_CERT:
1367 cert_free(k->cert);
1368 k->type = KEY_RSA;
1369 return 0;
1370 case KEY_DSA_CERT:
1371 cert_free(k->cert);
1372 k->type = KEY_DSA;
1373 return 0;
1374 default:
1375 error("%s: key has incorrect type %s", __func__, key_type(k));
1376 return -1;
1377 }
1378}
1379
1380/* Sign a KEY_RSA_CERT or KEY_DSA_CERT, (re-)generating the signed certblob */
1381int
1382key_certify(Key *k, Key *ca)
1383{
1384 Buffer principals;
1385 u_char *ca_blob, *sig_blob, nonce[32];
1386 u_int i, ca_len, sig_len;
1387
1388 if (k->cert == NULL) {
1389 error("%s: key lacks cert info", __func__);
1390 return -1;
1391 }
1392
1393 if (!key_is_cert(k)) {
1394 error("%s: certificate has unknown type %d", __func__,
1395 k->cert->type);
1396 return -1;
1397 }
1398
1399 if (ca->type != KEY_RSA && ca->type != KEY_DSA) {
1400 error("%s: CA key has unsupported type %s", __func__,
1401 key_type(ca));
1402 return -1;
1403 }
1404
1405 key_to_blob(ca, &ca_blob, &ca_len);
1406
1407 buffer_clear(&k->cert->certblob);
1408 buffer_put_cstring(&k->cert->certblob, key_ssh_name(k));
1409
1410 switch (k->type) {
1411 case KEY_DSA_CERT:
1412 buffer_put_bignum2(&k->cert->certblob, k->dsa->p);
1413 buffer_put_bignum2(&k->cert->certblob, k->dsa->q);
1414 buffer_put_bignum2(&k->cert->certblob, k->dsa->g);
1415 buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key);
1416 break;
1417 case KEY_RSA_CERT:
1418 buffer_put_bignum2(&k->cert->certblob, k->rsa->e);
1419 buffer_put_bignum2(&k->cert->certblob, k->rsa->n);
1420 break;
1421 default:
1422 error("%s: key has incorrect type %s", __func__, key_type(k));
1423 buffer_clear(&k->cert->certblob);
1424 xfree(ca_blob);
1425 return -1;
1426 }
1427
1428 buffer_put_int(&k->cert->certblob, k->cert->type);
1429 buffer_put_cstring(&k->cert->certblob, k->cert->key_id);
1430
1431 buffer_init(&principals);
1432 for (i = 0; i < k->cert->nprincipals; i++)
1433 buffer_put_cstring(&principals, k->cert->principals[i]);
1434 buffer_put_string(&k->cert->certblob, buffer_ptr(&principals),
1435 buffer_len(&principals));
1436 buffer_free(&principals);
1437
1438 buffer_put_int64(&k->cert->certblob, k->cert->valid_after);
1439 buffer_put_int64(&k->cert->certblob, k->cert->valid_before);
1440 buffer_put_string(&k->cert->certblob,
1441 buffer_ptr(&k->cert->constraints),
1442 buffer_len(&k->cert->constraints));
1443
1444 arc4random_buf(&nonce, sizeof(nonce));
1445 buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
1446 buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */
1447 buffer_put_string(&k->cert->certblob, ca_blob, ca_len);
1448 xfree(ca_blob);
1449
1450 /* Sign the whole mess */
1451 if (key_sign(ca, &sig_blob, &sig_len, buffer_ptr(&k->cert->certblob),
1452 buffer_len(&k->cert->certblob)) != 0) {
1453 error("%s: signature operation failed", __func__);
1454 buffer_clear(&k->cert->certblob);
1455 return -1;
1456 }
1457 /* Append signature and we are done */
1458 buffer_put_string(&k->cert->certblob, sig_blob, sig_len);
1459 xfree(sig_blob);
1460
1461 return 0;
1462}
1463
1464int
1465key_cert_check_authority(const Key *k, int want_host, int require_principal,
1466 const char *name, const char **reason)
1467{
1468 u_int i, principal_matches;
1469 time_t now = time(NULL);
1470
1471 if (want_host) {
1472 if (k->cert->type != SSH2_CERT_TYPE_HOST) {
1473 *reason = "Certificate invalid: not a host certificate";
1474 return -1;
1475 }
1476 } else {
1477 if (k->cert->type != SSH2_CERT_TYPE_USER) {
1478 *reason = "Certificate invalid: not a user certificate";
1479 return -1;
1480 }
1481 }
1482 if (now < 0) {
1483 error("%s: system clock lies before epoch", __func__);
1484 *reason = "Certificate invalid: not yet valid";
1485 return -1;
1486 }
1487 if ((u_int64_t)now < k->cert->valid_after) {
1488 *reason = "Certificate invalid: not yet valid";
1489 return -1;
1490 }
1491 if ((u_int64_t)now >= k->cert->valid_before) {
1492 *reason = "Certificate invalid: expired";
1493 return -1;
1494 }
1495 if (k->cert->nprincipals == 0) {
1496 if (require_principal) {
1497 *reason = "Certificate lacks principal list";
1498 return -1;
1499 }
1500 } else {
1501 principal_matches = 0;
1502 for (i = 0; i < k->cert->nprincipals; i++) {
1503 if (strcmp(name, k->cert->principals[i]) == 0) {
1504 principal_matches = 1;
1505 break;
1506 }
1507 }
1508 if (!principal_matches) {
1509 *reason = "Certificate invalid: name is not a listed "
1510 "principal";
1511 return -1;
1512 }
1513 }
1514 return 0;
1515}
diff --git a/key.h b/key.h
index 14aac79c2..6a2e049af 100644
--- a/key.h
+++ b/key.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: key.h,v 1.27 2008/06/11 21:01:35 grunk Exp $ */ 1/* $OpenBSD: key.h,v 1.28 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -26,6 +26,7 @@
26#ifndef KEY_H 26#ifndef KEY_H
27#define KEY_H 27#define KEY_H
28 28
29#include "buffer.h"
29#include <openssl/rsa.h> 30#include <openssl/rsa.h>
30#include <openssl/dsa.h> 31#include <openssl/dsa.h>
31 32
@@ -34,6 +35,8 @@ enum types {
34 KEY_RSA1, 35 KEY_RSA1,
35 KEY_RSA, 36 KEY_RSA,
36 KEY_DSA, 37 KEY_DSA,
38 KEY_RSA_CERT,
39 KEY_DSA_CERT,
37 KEY_UNSPEC 40 KEY_UNSPEC
38}; 41};
39enum fp_type { 42enum fp_type {
@@ -49,20 +52,35 @@ enum fp_rep {
49/* key is stored in external hardware */ 52/* key is stored in external hardware */
50#define KEY_FLAG_EXT 0x0001 53#define KEY_FLAG_EXT 0x0001
51 54
55#define CERT_MAX_PRINCIPALS 256
56struct KeyCert {
57 Buffer certblob; /* Kept around for use on wire */
58 u_int type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */
59 char *key_id;
60 u_int nprincipals;
61 char **principals;
62 u_int64_t valid_after, valid_before;
63 Buffer constraints;
64 Key *signature_key;
65};
66
52struct Key { 67struct Key {
53 int type; 68 int type;
54 int flags; 69 int flags;
55 RSA *rsa; 70 RSA *rsa;
56 DSA *dsa; 71 DSA *dsa;
72 struct KeyCert *cert;
57}; 73};
58 74
59Key *key_new(int); 75Key *key_new(int);
76void key_add_private(Key *);
60Key *key_new_private(int); 77Key *key_new_private(int);
61void key_free(Key *); 78void key_free(Key *);
62Key *key_demote(const Key *); 79Key *key_demote(const Key *);
80int key_equal_public(const Key *, const Key *);
63int key_equal(const Key *, const Key *); 81int key_equal(const Key *, const Key *);
64char *key_fingerprint(const Key *, enum fp_type, enum fp_rep); 82char *key_fingerprint(Key *, enum fp_type, enum fp_rep);
65u_char *key_fingerprint_raw(const Key *, enum fp_type, u_int *); 83u_char *key_fingerprint_raw(Key *, enum fp_type, u_int *);
66const char *key_type(const Key *); 84const char *key_type(const Key *);
67int key_write(const Key *, FILE *); 85int key_write(const Key *, FILE *);
68int key_read(Key *, char **); 86int key_read(Key *, char **);
@@ -71,6 +89,14 @@ u_int key_size(const Key *);
71Key *key_generate(int, u_int); 89Key *key_generate(int, u_int);
72Key *key_from_private(const Key *); 90Key *key_from_private(const Key *);
73int key_type_from_name(char *); 91int key_type_from_name(char *);
92int key_is_cert(const Key *);
93int key_type_plain(int);
94int key_to_certified(Key *);
95int key_drop_cert(Key *);
96int key_certify(Key *, Key *);
97void key_cert_copy(const Key *, struct Key *);
98int key_cert_check_authority(const Key *, int, int, const char *,
99 const char **);
74 100
75Key *key_from_blob(const u_char *, u_int); 101Key *key_from_blob(const u_char *, u_int);
76int key_to_blob(const Key *, u_char **, u_int *); 102int key_to_blob(const Key *, u_char **, u_int *);
diff --git a/match.h b/match.h
index 18f683070..3d7f70fc0 100644
--- a/match.h
+++ b/match.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: match.h,v 1.14 2008/06/10 03:57:27 djm Exp $ */ 1/* $OpenBSD: match.h,v 1.15 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -23,5 +23,5 @@ char *match_list(const char *, const char *, u_int *);
23 23
24/* addrmatch.c */ 24/* addrmatch.c */
25int addr_match_list(const char *, const char *); 25int addr_match_list(const char *, const char *);
26 26int addr_match_cidr_list(const char *, const char *);
27#endif 27#endif
diff --git a/monitor.c b/monitor.c
index ace25c404..f67cb7670 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: monitor.c,v 1.104 2009/06/12 20:43:22 andreas Exp $ */ 1/* $OpenBSD: monitor.c,v 1.105 2010/02/26 20:29:54 djm Exp $ */
2/* 2/*
3 * Copyright 2002 Niels Provos <provos@citi.umich.edu> 3 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
4 * Copyright 2002 Markus Friedl <markus@openbsd.org> 4 * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -1721,7 +1721,8 @@ mm_get_kex(Buffer *m)
1721 kex->flags = buffer_get_int(m); 1721 kex->flags = buffer_get_int(m);
1722 kex->client_version_string = buffer_get_string(m, NULL); 1722 kex->client_version_string = buffer_get_string(m, NULL);
1723 kex->server_version_string = buffer_get_string(m, NULL); 1723 kex->server_version_string = buffer_get_string(m, NULL);
1724 kex->load_host_key=&get_hostkey_by_type; 1724 kex->load_host_public_key=&get_hostkey_public_by_type;
1725 kex->load_host_private_key=&get_hostkey_private_by_type;
1725 kex->host_key_index=&get_hostkey_index; 1726 kex->host_key_index=&get_hostkey_index;
1726 1727
1727 return (kex); 1728 return (kex);
diff --git a/myproposal.h b/myproposal.h
index 7bca3bcae..98f27fd15 100644
--- a/myproposal.h
+++ b/myproposal.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: myproposal.h,v 1.23 2009/01/23 07:58:11 djm Exp $ */ 1/* $OpenBSD: myproposal.h,v 1.24 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -40,7 +40,9 @@
40 "diffie-hellman-group1-sha1" 40 "diffie-hellman-group1-sha1"
41#endif 41#endif
42 42
43#define KEX_DEFAULT_PK_ALG "ssh-rsa,ssh-dss" 43#define KEX_DEFAULT_PK_ALG "ssh-rsa-cert-v00@openssh.com," \
44 "ssh-dss-cert-v00@openssh.com," \
45 "ssh-rsa,ssh-dss"
44 46
45#define KEX_DEFAULT_ENCRYPT \ 47#define KEX_DEFAULT_ENCRYPT \
46 "aes128-ctr,aes192-ctr,aes256-ctr," \ 48 "aes128-ctr,aes192-ctr,aes256-ctr," \
diff --git a/servconf.c b/servconf.c
index 09296c9cf..0a6cdb655 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: servconf.c,v 1.202 2010/01/13 03:48:12 djm Exp $ */ 1/* $OpenBSD: servconf.c,v 1.203 2010/02/26 20:29:54 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
@@ -65,6 +65,7 @@ initialize_server_options(ServerOptions *options)
65 options->listen_addrs = NULL; 65 options->listen_addrs = NULL;
66 options->address_family = -1; 66 options->address_family = -1;
67 options->num_host_key_files = 0; 67 options->num_host_key_files = 0;
68 options->num_host_cert_files = 0;
68 options->pid_file = NULL; 69 options->pid_file = NULL;
69 options->server_key_bits = -1; 70 options->server_key_bits = -1;
70 options->login_grace_time = -1; 71 options->login_grace_time = -1;
@@ -152,6 +153,7 @@ fill_default_server_options(ServerOptions *options)
152 _PATH_HOST_DSA_KEY_FILE; 153 _PATH_HOST_DSA_KEY_FILE;
153 } 154 }
154 } 155 }
156 /* No certificates by default */
155 if (options->num_ports == 0) 157 if (options->num_ports == 0)
156 options->ports[options->num_ports++] = SSH_DEFAULT_PORT; 158 options->ports[options->num_ports++] = SSH_DEFAULT_PORT;
157 if (options->listen_addrs == NULL) 159 if (options->listen_addrs == NULL)
@@ -305,7 +307,7 @@ typedef enum {
305 sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, 307 sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
306 sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 308 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
307 sUsePrivilegeSeparation, sAllowAgentForwarding, 309 sUsePrivilegeSeparation, sAllowAgentForwarding,
308 sZeroKnowledgePasswordAuthentication, 310 sZeroKnowledgePasswordAuthentication, sHostCertificate,
309 sDeprecated, sUnsupported 311 sDeprecated, sUnsupported
310} ServerOpCodes; 312} ServerOpCodes;
311 313
@@ -424,6 +426,7 @@ static struct {
424 { "permitopen", sPermitOpen, SSHCFG_ALL }, 426 { "permitopen", sPermitOpen, SSHCFG_ALL },
425 { "forcecommand", sForceCommand, SSHCFG_ALL }, 427 { "forcecommand", sForceCommand, SSHCFG_ALL },
426 { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, 428 { "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
429 { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL },
427 { NULL, sBadOption, 0 } 430 { NULL, sBadOption, 0 }
428}; 431};
429 432
@@ -816,6 +819,16 @@ process_server_config_line(ServerOptions *options, char *line,
816 } 819 }
817 break; 820 break;
818 821
822 case sHostCertificate:
823 intptr = &options->num_host_cert_files;
824 if (*intptr >= MAX_HOSTKEYS)
825 fatal("%s line %d: too many host certificates "
826 "specified (max %d).", filename, linenum,
827 MAX_HOSTCERTS);
828 charptr = &options->host_cert_files[*intptr];
829 goto parse_filename;
830 break;
831
819 case sPidFile: 832 case sPidFile:
820 charptr = &options->pid_file; 833 charptr = &options->pid_file;
821 goto parse_filename; 834 goto parse_filename;
@@ -1651,6 +1664,8 @@ dump_config(ServerOptions *o)
1651 /* string array arguments */ 1664 /* string array arguments */
1652 dump_cfg_strarray(sHostKeyFile, o->num_host_key_files, 1665 dump_cfg_strarray(sHostKeyFile, o->num_host_key_files,
1653 o->host_key_files); 1666 o->host_key_files);
1667 dump_cfg_strarray(sHostKeyFile, o->num_host_cert_files,
1668 o->host_cert_files);
1654 dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users); 1669 dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users);
1655 dump_cfg_strarray(sDenyUsers, o->num_deny_users, o->deny_users); 1670 dump_cfg_strarray(sDenyUsers, o->num_deny_users, o->deny_users);
1656 dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups); 1671 dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups);
diff --git a/servconf.h b/servconf.h
index c9b8619cd..c5c9c6ecd 100644
--- a/servconf.h
+++ b/servconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: servconf.h,v 1.90 2010/01/13 03:48:13 djm Exp $ */ 1/* $OpenBSD: servconf.h,v 1.91 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -24,6 +24,7 @@
24#define MAX_DENY_GROUPS 256 /* Max # groups on deny list. */ 24#define MAX_DENY_GROUPS 256 /* Max # groups on deny list. */
25#define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ 25#define MAX_SUBSYSTEMS 256 /* Max # subsystems. */
26#define MAX_HOSTKEYS 256 /* Max # hostkeys. */ 26#define MAX_HOSTKEYS 256 /* Max # hostkeys. */
27#define MAX_HOSTCERTS 256 /* Max # host certificates. */
27#define MAX_ACCEPT_ENV 256 /* Max # of env vars. */ 28#define MAX_ACCEPT_ENV 256 /* Max # of env vars. */
28#define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */ 29#define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */
29 30
@@ -49,6 +50,8 @@ typedef struct {
49 int address_family; /* Address family used by the server. */ 50 int address_family; /* Address family used by the server. */
50 char *host_key_files[MAX_HOSTKEYS]; /* Files containing host keys. */ 51 char *host_key_files[MAX_HOSTKEYS]; /* Files containing host keys. */
51 int num_host_key_files; /* Number of files for host keys. */ 52 int num_host_key_files; /* Number of files for host keys. */
53 char *host_cert_files[MAX_HOSTCERTS]; /* Files containing host certs. */
54 int num_host_cert_files; /* Number of files for host certs. */
52 char *pid_file; /* Where to put our pid */ 55 char *pid_file; /* Where to put our pid */
53 int server_key_bits;/* Size of the server key. */ 56 int server_key_bits;/* Size of the server key. */
54 int login_grace_time; /* Disconnect if no auth in this time 57 int login_grace_time; /* Disconnect if no auth in this time
diff --git a/ssh-add.c b/ssh-add.c
index 90e5be20b..a7963223a 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-add.c,v 1.92 2010/02/08 10:50:20 markus Exp $ */ 1/* $OpenBSD: ssh-add.c,v 1.93 2010/02/26 20:29:54 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
@@ -138,9 +138,9 @@ delete_all(AuthenticationConnection *ac)
138static int 138static int
139add_file(AuthenticationConnection *ac, const char *filename) 139add_file(AuthenticationConnection *ac, const char *filename)
140{ 140{
141 Key *private; 141 Key *private, *cert;
142 char *comment = NULL; 142 char *comment = NULL;
143 char msg[1024]; 143 char msg[1024], *certpath;
144 int fd, perms_ok, ret = -1; 144 int fd, perms_ok, ret = -1;
145 145
146 if ((fd = open(filename, O_RDONLY)) < 0) { 146 if ((fd = open(filename, O_RDONLY)) < 0) {
@@ -199,6 +199,34 @@ add_file(AuthenticationConnection *ac, const char *filename)
199 fprintf(stderr, "Could not add identity: %s\n", filename); 199 fprintf(stderr, "Could not add identity: %s\n", filename);
200 } 200 }
201 201
202
203 /* Now try to add the certificate flavour too */
204 xasprintf(&certpath, "%s-cert.pub", filename);
205 if ((cert = key_load_public(certpath, NULL)) != NULL) {
206 /* Graft with private bits */
207 if (key_to_certified(private) != 0)
208 fatal("%s: key_to_certified failed", __func__);
209 key_cert_copy(cert, private);
210 key_free(cert);
211
212 if (ssh_add_identity_constrained(ac, private, comment,
213 lifetime, confirm)) {
214 fprintf(stderr, "Certificate added: %s (%s)\n",
215 certpath, private->cert->key_id);
216 if (lifetime != 0)
217 fprintf(stderr, "Lifetime set to %d seconds\n",
218 lifetime);
219 if (confirm != 0)
220 fprintf(stderr, "The user has to confirm each "
221 "use of the key\n");
222 } else {
223 error("Certificate %s (%s) add failed", certpath,
224 private->cert->key_id);
225 }
226 } else
227 fprintf(stderr, "Unable to load certificate %s", certpath);
228
229 xfree(certpath);
202 xfree(comment); 230 xfree(comment);
203 key_free(private); 231 key_free(private);
204 232
diff --git a/ssh-agent.c b/ssh-agent.c
index 46a744f4e..b5c565271 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-agent.c,v 1.164 2010/02/09 00:50:36 djm Exp $ */ 1/* $OpenBSD: ssh-agent.c,v 1.165 2010/02/26 20:29:54 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
@@ -468,6 +468,8 @@ process_add_identity(SocketEntry *e, int version)
468 int type, success = 0, death = 0, confirm = 0; 468 int type, success = 0, death = 0, confirm = 0;
469 char *type_name, *comment; 469 char *type_name, *comment;
470 Key *k = NULL; 470 Key *k = NULL;
471 u_char *cert;
472 u_int len;
471 473
472 switch (version) { 474 switch (version) {
473 case 1: 475 case 1:
@@ -498,6 +500,14 @@ process_add_identity(SocketEntry *e, int version)
498 buffer_get_bignum2(&e->request, k->dsa->pub_key); 500 buffer_get_bignum2(&e->request, k->dsa->pub_key);
499 buffer_get_bignum2(&e->request, k->dsa->priv_key); 501 buffer_get_bignum2(&e->request, k->dsa->priv_key);
500 break; 502 break;
503 case KEY_DSA_CERT:
504 cert = buffer_get_string(&e->request, &len);
505 if ((k = key_from_blob(cert, len)) == NULL)
506 fatal("Certificate parse failed");
507 xfree(cert);
508 key_add_private(k);
509 buffer_get_bignum2(&e->request, k->dsa->priv_key);
510 break;
501 case KEY_RSA: 511 case KEY_RSA:
502 k = key_new_private(type); 512 k = key_new_private(type);
503 buffer_get_bignum2(&e->request, k->rsa->n); 513 buffer_get_bignum2(&e->request, k->rsa->n);
@@ -510,6 +520,17 @@ process_add_identity(SocketEntry *e, int version)
510 /* Generate additional parameters */ 520 /* Generate additional parameters */
511 rsa_generate_additional_parameters(k->rsa); 521 rsa_generate_additional_parameters(k->rsa);
512 break; 522 break;
523 case KEY_RSA_CERT:
524 cert = buffer_get_string(&e->request, &len);
525 if ((k = key_from_blob(cert, len)) == NULL)
526 fatal("Certificate parse failed");
527 xfree(cert);
528 key_add_private(k);
529 buffer_get_bignum2(&e->request, k->rsa->d);
530 buffer_get_bignum2(&e->request, k->rsa->iqmp);
531 buffer_get_bignum2(&e->request, k->rsa->p);
532 buffer_get_bignum2(&e->request, k->rsa->q);
533 break;
513 default: 534 default:
514 buffer_clear(&e->request); 535 buffer_clear(&e->request);
515 goto send; 536 goto send;
@@ -519,6 +540,7 @@ process_add_identity(SocketEntry *e, int version)
519 /* enable blinding */ 540 /* enable blinding */
520 switch (k->type) { 541 switch (k->type) {
521 case KEY_RSA: 542 case KEY_RSA:
543 case KEY_RSA_CERT:
522 case KEY_RSA1: 544 case KEY_RSA1:
523 if (RSA_blinding_on(k->rsa, NULL) != 1) { 545 if (RSA_blinding_on(k->rsa, NULL) != 1) {
524 error("process_add_identity: RSA_blinding_on failed"); 546 error("process_add_identity: RSA_blinding_on failed");
diff --git a/ssh-dss.c b/ssh-dss.c
index 51a06e98f..449f493b4 100644
--- a/ssh-dss.c
+++ b/ssh-dss.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-dss.c,v 1.24 2006/11/06 21:25:28 markus Exp $ */ 1/* $OpenBSD: ssh-dss.c,v 1.25 2010/02/26 20:29:54 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * 4 *
@@ -53,7 +53,9 @@ ssh_dss_sign(const Key *key, u_char **sigp, u_int *lenp,
53 u_int rlen, slen, len, dlen; 53 u_int rlen, slen, len, dlen;
54 Buffer b; 54 Buffer b;
55 55
56 if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { 56 if (key == NULL ||
57 (key->type != KEY_DSA && key->type != KEY_DSA_CERT) ||
58 key->dsa == NULL) {
57 error("ssh_dss_sign: no DSA key"); 59 error("ssh_dss_sign: no DSA key");
58 return -1; 60 return -1;
59 } 61 }
@@ -116,7 +118,9 @@ ssh_dss_verify(const Key *key, const u_char *signature, u_int signaturelen,
116 int rlen, ret; 118 int rlen, ret;
117 Buffer b; 119 Buffer b;
118 120
119 if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { 121 if (key == NULL ||
122 (key->type != KEY_DSA && key->type != KEY_DSA_CERT) ||
123 key->dsa == NULL) {
120 error("ssh_dss_verify: no DSA key"); 124 error("ssh_dss_verify: no DSA key");
121 return -1; 125 return -1;
122 } 126 }
diff --git a/ssh-keygen.1 b/ssh-keygen.1
index f09e1a100..772caf7ad 100644
--- a/ssh-keygen.1
+++ b/ssh-keygen.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: ssh-keygen.1,v 1.83 2010/02/10 23:20:38 markus Exp $ 1.\" $OpenBSD: ssh-keygen.1,v 1.84 2010/02/26 20:29:54 djm Exp $
2.\" 2.\"
3.\" -*- nroff -*- 3.\" -*- nroff -*-
4.\" 4.\"
@@ -37,7 +37,7 @@
37.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 37.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
38.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39.\" 39.\"
40.Dd $Mdocdate: February 10 2010 $ 40.Dd $Mdocdate: February 26 2010 $
41.Dt SSH-KEYGEN 1 41.Dt SSH-KEYGEN 1
42.Os 42.Os
43.Sh NAME 43.Sh NAME
@@ -106,6 +106,14 @@
106.Op Fl v 106.Op Fl v
107.Op Fl a Ar num_trials 107.Op Fl a Ar num_trials
108.Op Fl W Ar generator 108.Op Fl W Ar generator
109.Nm ssh-keygen
110.Fl s Ar ca_key
111.Fl I Ar certificate_identity
112.Op Fl h
113.Op Fl n Ar principals
114.Op Fl O Ar constraint
115.Op Fl V Ar validity_interval
116.Ar
109.Sh DESCRIPTION 117.Sh DESCRIPTION
110.Nm 118.Nm
111generates, manages and converts authentication keys for 119generates, manages and converts authentication keys for
@@ -245,6 +253,17 @@ but they do not reveal identifying information should the file's contents
245be disclosed. 253be disclosed.
246This option will not modify existing hashed hostnames and is therefore safe 254This option will not modify existing hashed hostnames and is therefore safe
247to use on files that mix hashed and non-hashed names. 255to use on files that mix hashed and non-hashed names.
256.It Fl h
257When signing a key, create a host certificate instead of a user
258certificate.
259Please see the
260.Sx CERTIFICATES
261section for details.
262.It Fl I
263Specify the key identity when signing a public key.
264Please see the
265.Sx CERTIFICATES
266section for details.
248.It Fl i 267.It Fl i
249This option will read an unencrypted private (or public) key file 268This option will read an unencrypted private (or public) key file
250in SSH2-compatible format and print an OpenSSH compatible private 269in SSH2-compatible format and print an OpenSSH compatible private
@@ -268,6 +287,67 @@ Specify the amount of memory to use (in megabytes) when generating
268candidate moduli for DH-GEX. 287candidate moduli for DH-GEX.
269.It Fl N Ar new_passphrase 288.It Fl N Ar new_passphrase
270Provides the new passphrase. 289Provides the new passphrase.
290.It Fl n Ar principals
291Specify one or more principals (user or host names) to be included in
292a certificate when signing a key.
293Multiple principals may be specified, separated by commas.
294Please see the
295.Sx CERTIFICATES
296section for details.
297.It Fl O Ar constraint
298Specify a certificate constraint when signing a key.
299This option may be specified multiple times.
300Please see the
301.Sx CERTIFICATES
302section for details.
303The constraints that are valid for user certificates are:
304.Bl -tag -width Ds
305.It Ic no-x11-forwarding
306Disable X11 forwarding. (permitted by default)
307.It Ic no-agent-forwarding
308Disable
309.Xr ssh-agent 1
310forwarding. (permitted by default)
311.It Ic no-port-forwarding
312Disable port forwarding. (permitted by default)
313.It Ic no-pty
314Disable PTY allocation. (permitted by default)
315.It Ic no-user-rc
316Disable execution of
317.Pa ~/.ssh/rc
318by
319.Xr sshd 8 .
320(permitted by default)
321.It Ic clear
322Clear all enabled permissions.
323This is useful for clearing the default set of permissions so permissions may
324be added individually.
325.It Ic permit-x11-forwarding
326Allows X11 forwarding.
327.It Ic permit-port-forwarding
328Allows port forwarding.
329.It Ic permit-pty
330Allows PTY allocation.
331.It Ic permit-user-rc
332Allows execution of
333.Pa ~/.ssh/rc
334by
335.Xr sshd 8 .
336.It Ic force-command=command
337Forces the execution of
338.Ar command
339instead of any shell or command specified by the user when
340the certificate is used for authentication.
341.It Ic source-address=address_list
342Restrict the source addresses from which the certificate is considered valid
343from.
344The
345.Ar address_list
346is a comma-separated list of one or more address/netmask pairs in CIDR
347format.
348.El
349.Pp
350At present, no constraints are valid for host keys.
271.It Fl P Ar passphrase 351.It Fl P Ar passphrase
272Provides the (old) passphrase. 352Provides the (old) passphrase.
273.It Fl p 353.It Fl p
@@ -297,6 +377,11 @@ Print the SSHFP fingerprint resource record named
297for the specified public key file. 377for the specified public key file.
298.It Fl S Ar start 378.It Fl S Ar start
299Specify start point (in hex) when generating candidate moduli for DH-GEX. 379Specify start point (in hex) when generating candidate moduli for DH-GEX.
380.It Fl s Ar ca_key
381Certify (sign) a public key using the specified CA key.
382Please see the
383.Sx CERTIFICATES
384section for details.
300.It Fl T Ar output_file 385.It Fl T Ar output_file
301Test DH group exchange candidate primes (generated using the 386Test DH group exchange candidate primes (generated using the
302.Fl G 387.Fl G
@@ -310,6 +395,29 @@ for protocol version 1 and
310or 395or
311.Dq dsa 396.Dq dsa
312for protocol version 2. 397for protocol version 2.
398.It Fl V Ar validity_interval
399Specify a validity interval when signing a certificate.
400A validity interval may consist of a single time, indicating that the
401certificate is valid beginning now and expiring at that time, or may consist
402of two times separated by a colon to indicate an explicit time interval.
403The start time may be specified as a date in YYYYMMDD format, a time
404in YYYYMMDDHHMMSS format or a relative time (to the current time) consisting
405of a minus sign followed by a relative time in the format described in the
406.Sx TIME FORMATS
407section of
408.Xr ssh_config 5 .
409The end time may be specified as a YYYYMMDD date, a YYYYMMDDHHMMSS time or
410a relative time starting with a plus character.
411.Pp
412For example:
413.Dq +52w1d
414(valid from now to 52 weeks and one day from now),
415.Dq -4w:+4w
416(valid from four weeks ago to four weeks from now),
417.Dq 20100101123000:20110101123000
418(valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011),
419.Dq -1d:20110101
420(valid from yesterday to midnight, January 1st, 2011).
313.It Fl v 421.It Fl v
314Verbose mode. 422Verbose mode.
315Causes 423Causes
@@ -380,6 +488,72 @@ Screened DH groups may be installed in
380.Pa /etc/moduli . 488.Pa /etc/moduli .
381It is important that this file contains moduli of a range of bit lengths and 489It is important that this file contains moduli of a range of bit lengths and
382that both ends of a connection share common moduli. 490that both ends of a connection share common moduli.
491.Sh CERTIFICATES
492.Nm
493supports signing of keys to produce certificates that may be used for
494user or host authentication.
495Certificates consist of a public key, some identity information, zero or
496more principal (user or host) names and an optional set of constraints that
497are signed by a Certification Authority (CA) key.
498Clients or servers may then trust only the CA key and verify its signature
499on a certificate rather than trusting many user/host keys.
500Note that OpenSSH certificates are a different, and much simpler, format to
501the X.509 certificates used in
502.Xr ssl 8 .
503.Pp
504.Nm
505supports two types of certificates: user and host.
506User certificates authenticate users to servers, whereas host certificates
507authenticate server hosts to users. To generate a user certificate:
508.Pp
509.Dl $ ssh-keygen -s /path/to/ca_key -I key_id /path/to/user_key.pub
510.Pp
511The resultant certificate will be placed in
512.Pa /path/to/user_key_cert.pub .
513A host certificate requires the
514.Fl h
515option:
516.Pp
517.Dl $ ssh-keygen -s /path/to/ca_key -I key_id -h /path/to/host_key.pub
518.Pp
519The host certificate will be output to
520.Pa /path/to/host_key_cert.pub .
521In both cases,
522.Ar key_id
523is a "key identifier" that is logged by the server when the certificate
524is used for authentication.
525.Pp
526Certificates may be limited to be valid for a set of principal (user/host)
527names.
528By default, generated certificates are valid for all users or hosts.
529To generate a certificate for a specified set of principals:
530.Pp
531.Dl $ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub
532.Dl $ ssh-keygen -s ca_key -I key_id -h -n host.domain user_key.pub
533.Pp
534Additional limitations on the validity and use of user certificates may
535be specified through certificate constraints.
536A constrained certificate may disable features of the SSH session, may be
537valid only when presented from particular source addresses or may
538force the use of a specific command.
539For a list of valid certificate constraints, see the documentation for the
540.Fl O
541option above.
542.Pp
543Finally, certificates may be defined with a validity lifetime.
544The
545.Fl V
546option allows specification of certificate start and end times.
547A certificate that is presented at a time outside this range will not be
548considered valid.
549By default, certificates have a maximum validity interval.
550.Pp
551For certificates to be used for user or host authentication, the CA
552public key must be trusted by
553.Xr sshd 8
554or
555.Xr ssh 1 .
556Please refer to those manual pages for details.
383.Sh FILES 557.Sh FILES
384.Bl -tag -width Ds 558.Bl -tag -width Ds
385.It Pa ~/.ssh/identity 559.It Pa ~/.ssh/identity
diff --git a/ssh-keygen.c b/ssh-keygen.c
index b6b7a2d9f..60261c210 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-keygen.c,v 1.178 2010/02/09 00:50:59 djm Exp $ */ 1/* $OpenBSD: ssh-keygen.c,v 1.179 2010/02/26 20:29:54 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
@@ -48,6 +48,7 @@
48#include "match.h" 48#include "match.h"
49#include "hostfile.h" 49#include "hostfile.h"
50#include "dns.h" 50#include "dns.h"
51#include "ssh2.h"
51 52
52#ifdef ENABLE_PKCS11 53#ifdef ENABLE_PKCS11
53#include "ssh-pkcs11.h" 54#include "ssh-pkcs11.h"
@@ -98,6 +99,35 @@ char *identity_new_passphrase = NULL;
98/* This is set to the new comment if given on the command line. */ 99/* This is set to the new comment if given on the command line. */
99char *identity_comment = NULL; 100char *identity_comment = NULL;
100 101
102/* Path to CA key when certifying keys. */
103char *ca_key_path = NULL;
104
105/* Key type when certifying */
106u_int cert_key_type = SSH2_CERT_TYPE_USER;
107
108/* "key ID" of signed key */
109char *cert_key_id = NULL;
110
111/* Comma-separated list of principal names for certifying keys */
112char *cert_principals = NULL;
113
114/* Validity period for certificates */
115u_int64_t cert_valid_from = 0;
116u_int64_t cert_valid_to = ~0ULL;
117
118/* Certificate constraints */
119#define CONSTRAINT_X_FWD (1)
120#define CONSTRAINT_AGENT_FWD (1<<1)
121#define CONSTRAINT_PORT_FWD (1<<2)
122#define CONSTRAINT_PTY (1<<3)
123#define CONSTRAINT_USER_RC (1<<4)
124#define CONSTRAINT_DEFAULT (CONSTRAINT_X_FWD|CONSTRAINT_AGENT_FWD| \
125 CONSTRAINT_PORT_FWD|CONSTRAINT_PTY| \
126 CONSTRAINT_USER_RC)
127u_int32_t constraint_flags = CONSTRAINT_DEFAULT;
128char *constraint_command = NULL;
129char *constraint_src_addr = NULL;
130
101/* Dump public key file in format used by real and the original SSH 2 */ 131/* Dump public key file in format used by real and the original SSH 2 */
102int convert_to_ssh2 = 0; 132int convert_to_ssh2 = 0;
103int convert_from_ssh2 = 0; 133int convert_from_ssh2 = 0;
@@ -591,7 +621,7 @@ do_fingerprint(struct passwd *pw)
591} 621}
592 622
593static void 623static void
594print_host(FILE *f, const char *name, Key *public, int hash) 624printhost(FILE *f, const char *name, Key *public, int ca, int hash)
595{ 625{
596 if (print_fingerprint) { 626 if (print_fingerprint) {
597 enum fp_rep rep; 627 enum fp_rep rep;
@@ -611,7 +641,7 @@ print_host(FILE *f, const char *name, Key *public, int hash)
611 } else { 641 } else {
612 if (hash && (name = host_hash(name, NULL, 0)) == NULL) 642 if (hash && (name = host_hash(name, NULL, 0)) == NULL)
613 fatal("hash_host failed"); 643 fatal("hash_host failed");
614 fprintf(f, "%s ", name); 644 fprintf(f, "%s%s%s ", ca ? CA_MARKER : "", ca ? " " : "", name);
615 if (!key_write(public, f)) 645 if (!key_write(public, f))
616 fatal("key_write failed"); 646 fatal("key_write failed");
617 fprintf(f, "\n"); 647 fprintf(f, "\n");
@@ -622,10 +652,11 @@ static void
622do_known_hosts(struct passwd *pw, const char *name) 652do_known_hosts(struct passwd *pw, const char *name)
623{ 653{
624 FILE *in, *out = stdout; 654 FILE *in, *out = stdout;
625 Key *public; 655 Key *pub;
626 char *cp, *cp2, *kp, *kp2; 656 char *cp, *cp2, *kp, *kp2;
627 char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN]; 657 char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN];
628 int c, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; 658 int c, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0;
659 int ca;
629 660
630 if (!have_identity) { 661 if (!have_identity) {
631 cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); 662 cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
@@ -681,9 +712,19 @@ do_known_hosts(struct passwd *pw, const char *name)
681 fprintf(out, "%s\n", cp); 712 fprintf(out, "%s\n", cp);
682 continue; 713 continue;
683 } 714 }
715 /* Check whether this is a CA key */
716 if (strncasecmp(cp, CA_MARKER, sizeof(CA_MARKER) - 1) == 0 &&
717 (cp[sizeof(CA_MARKER) - 1] == ' ' ||
718 cp[sizeof(CA_MARKER) - 1] == '\t')) {
719 ca = 1;
720 cp += sizeof(CA_MARKER);
721 } else
722 ca = 0;
723
684 /* Find the end of the host name portion. */ 724 /* Find the end of the host name portion. */
685 for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++) 725 for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++)
686 ; 726 ;
727
687 if (*kp == '\0' || *(kp + 1) == '\0') { 728 if (*kp == '\0' || *(kp + 1) == '\0') {
688 error("line %d missing key: %.40s...", 729 error("line %d missing key: %.40s...",
689 num, line); 730 num, line);
@@ -693,15 +734,15 @@ do_known_hosts(struct passwd *pw, const char *name)
693 *kp++ = '\0'; 734 *kp++ = '\0';
694 kp2 = kp; 735 kp2 = kp;
695 736
696 public = key_new(KEY_RSA1); 737 pub = key_new(KEY_RSA1);
697 if (key_read(public, &kp) != 1) { 738 if (key_read(pub, &kp) != 1) {
698 kp = kp2; 739 kp = kp2;
699 key_free(public); 740 key_free(pub);
700 public = key_new(KEY_UNSPEC); 741 pub = key_new(KEY_UNSPEC);
701 if (key_read(public, &kp) != 1) { 742 if (key_read(pub, &kp) != 1) {
702 error("line %d invalid key: %.40s...", 743 error("line %d invalid key: %.40s...",
703 num, line); 744 num, line);
704 key_free(public); 745 key_free(pub);
705 invalid = 1; 746 invalid = 1;
706 continue; 747 continue;
707 } 748 }
@@ -719,43 +760,52 @@ do_known_hosts(struct passwd *pw, const char *name)
719 c = (strcmp(cp2, cp) == 0); 760 c = (strcmp(cp2, cp) == 0);
720 if (find_host && c) { 761 if (find_host && c) {
721 printf("# Host %s found: " 762 printf("# Host %s found: "
722 "line %d type %s\n", name, 763 "line %d type %s%s\n", name,
723 num, key_type(public)); 764 num, key_type(pub),
724 print_host(out, cp, public, 0); 765 ca ? " (CA key)" : "");
766 printhost(out, cp, pub, ca, 0);
725 } 767 }
726 if (delete_host && !c) 768 if (delete_host && !c && !ca)
727 print_host(out, cp, public, 0); 769 printhost(out, cp, pub, ca, 0);
728 } else if (hash_hosts) 770 } else if (hash_hosts)
729 print_host(out, cp, public, 0); 771 printhost(out, cp, pub, ca, 0);
730 } else { 772 } else {
731 if (find_host || delete_host) { 773 if (find_host || delete_host) {
732 c = (match_hostname(name, cp, 774 c = (match_hostname(name, cp,
733 strlen(cp)) == 1); 775 strlen(cp)) == 1);
734 if (find_host && c) { 776 if (find_host && c) {
735 printf("# Host %s found: " 777 printf("# Host %s found: "
736 "line %d type %s\n", name, 778 "line %d type %s%s\n", name,
737 num, key_type(public)); 779 num, key_type(pub),
738 print_host(out, name, public, 780 ca ? " (CA key)" : "");
739 hash_hosts); 781 printhost(out, name, pub,
782 ca, hash_hosts && !ca);
740 } 783 }
741 if (delete_host && !c) 784 if (delete_host && !c && !ca)
742 print_host(out, cp, public, 0); 785 printhost(out, cp, pub, ca, 0);
743 } else if (hash_hosts) { 786 } else if (hash_hosts) {
744 for (cp2 = strsep(&cp, ","); 787 for (cp2 = strsep(&cp, ",");
745 cp2 != NULL && *cp2 != '\0'; 788 cp2 != NULL && *cp2 != '\0';
746 cp2 = strsep(&cp, ",")) { 789 cp2 = strsep(&cp, ",")) {
747 if (strcspn(cp2, "*?!") != strlen(cp2)) 790 if (ca) {
791 fprintf(stderr, "Warning: "
792 "ignoring CA key for host: "
793 "%.64s\n", cp2);
794 printhost(out, cp2, pub, ca, 0);
795 } else if (strcspn(cp2, "*?!") !=
796 strlen(cp2)) {
748 fprintf(stderr, "Warning: " 797 fprintf(stderr, "Warning: "
749 "ignoring host name with " 798 "ignoring host name with "
750 "metacharacters: %.64s\n", 799 "metacharacters: %.64s\n",
751 cp2); 800 cp2);
752 else 801 printhost(out, cp2, pub, ca, 0);
753 print_host(out, cp2, public, 1); 802 } else
803 printhost(out, cp2, pub, ca, 1);
754 } 804 }
755 has_unhashed = 1; 805 has_unhashed = 1;
756 } 806 }
757 } 807 }
758 key_free(public); 808 key_free(pub);
759 } 809 }
760 fclose(in); 810 fclose(in);
761 811
@@ -1012,6 +1062,293 @@ do_change_comment(struct passwd *pw)
1012 exit(0); 1062 exit(0);
1013} 1063}
1014 1064
1065static const char *
1066fmt_validity(void)
1067{
1068 char from[32], to[32];
1069 static char ret[64];
1070 time_t tt;
1071 struct tm *tm;
1072
1073 *from = *to = '\0';
1074 if (cert_valid_from == 0 &&
1075 cert_valid_to == 0xffffffffffffffffULL)
1076 return "forever";
1077
1078 if (cert_valid_from != 0) {
1079 /* XXX revisit INT_MAX in 2038 :) */
1080 tt = cert_valid_from > INT_MAX ? INT_MAX : cert_valid_from;
1081 tm = localtime(&tt);
1082 strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm);
1083 }
1084 if (cert_valid_to != 0xffffffffffffffffULL) {
1085 /* XXX revisit INT_MAX in 2038 :) */
1086 tt = cert_valid_to > INT_MAX ? INT_MAX : cert_valid_to;
1087 tm = localtime(&tt);
1088 strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm);
1089 }
1090
1091 if (cert_valid_from == 0) {
1092 snprintf(ret, sizeof(ret), "before %s", to);
1093 return ret;
1094 }
1095 if (cert_valid_to == 0xffffffffffffffffULL) {
1096 snprintf(ret, sizeof(ret), "after %s", from);
1097 return ret;
1098 }
1099
1100 snprintf(ret, sizeof(ret), "from %s to %s", from, to);
1101 return ret;
1102}
1103
1104static void
1105add_flag_constraint(Buffer *c, const char *name)
1106{
1107 debug3("%s: %s", __func__, name);
1108 buffer_put_cstring(c, name);
1109 buffer_put_string(c, NULL, 0);
1110}
1111
1112static void
1113add_string_constraint(Buffer *c, const char *name, const char *value)
1114{
1115 Buffer b;
1116
1117 debug3("%s: %s=%s", __func__, name, value);
1118 buffer_init(&b);
1119 buffer_put_cstring(&b, value);
1120
1121 buffer_put_cstring(c, name);
1122 buffer_put_string(c, buffer_ptr(&b), buffer_len(&b));
1123
1124 buffer_free(&b);
1125}
1126
1127static void
1128prepare_constraint_buf(Buffer *c)
1129{
1130
1131 buffer_clear(c);
1132 if ((constraint_flags & CONSTRAINT_X_FWD) != 0)
1133 add_flag_constraint(c, "permit-X11-forwarding");
1134 if ((constraint_flags & CONSTRAINT_AGENT_FWD) != 0)
1135 add_flag_constraint(c, "permit-agent-forwarding");
1136 if ((constraint_flags & CONSTRAINT_PORT_FWD) != 0)
1137 add_flag_constraint(c, "permit-port-forwarding");
1138 if ((constraint_flags & CONSTRAINT_PTY) != 0)
1139 add_flag_constraint(c, "permit-pty");
1140 if ((constraint_flags & CONSTRAINT_USER_RC) != 0)
1141 add_flag_constraint(c, "permit-user-rc");
1142 if (constraint_command != NULL)
1143 add_string_constraint(c, "forced-command", constraint_command);
1144 if (constraint_src_addr != NULL)
1145 add_string_constraint(c, "source-address", constraint_src_addr);
1146}
1147
1148static void
1149do_ca_sign(struct passwd *pw, int argc, char **argv)
1150{
1151 int i, fd;
1152 u_int n;
1153 Key *ca, *public;
1154 char *otmp, *tmp, *cp, *out, *comment, **plist = NULL;
1155 FILE *f;
1156
1157 tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
1158 if ((ca = load_identity(tmp)) == NULL)
1159 fatal("Couldn't load CA key \"%s\"", tmp);
1160 xfree(tmp);
1161
1162 for (i = 0; i < argc; i++) {
1163 /* Split list of principals */
1164 n = 0;
1165 if (cert_principals != NULL) {
1166 otmp = tmp = xstrdup(cert_principals);
1167 plist = NULL;
1168 for (; (cp = strsep(&tmp, ",")) != NULL; n++) {
1169 plist = xrealloc(plist, n + 1, sizeof(*plist));
1170 if (*(plist[n] = xstrdup(cp)) == '\0')
1171 fatal("Empty principal name");
1172 }
1173 xfree(otmp);
1174 }
1175
1176 tmp = tilde_expand_filename(argv[i], pw->pw_uid);
1177 if ((public = key_load_public(tmp, &comment)) == NULL)
1178 fatal("%s: unable to open \"%s\"", __func__, tmp);
1179 if (public->type != KEY_RSA && public->type != KEY_DSA)
1180 fatal("%s: key \"%s\" type %s cannot be certified",
1181 __func__, tmp, key_type(public));
1182
1183 /* Prepare certificate to sign */
1184 if (key_to_certified(public) != 0)
1185 fatal("Could not upgrade key %s to certificate", tmp);
1186 public->cert->type = cert_key_type;
1187 public->cert->key_id = xstrdup(cert_key_id);
1188 public->cert->nprincipals = n;
1189 public->cert->principals = plist;
1190 public->cert->valid_after = cert_valid_from;
1191 public->cert->valid_before = cert_valid_to;
1192 prepare_constraint_buf(&public->cert->constraints);
1193 public->cert->signature_key = key_from_private(ca);
1194
1195 if (key_certify(public, ca) != 0)
1196 fatal("Couldn't not certify key %s", tmp);
1197
1198 if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0)
1199 *cp = '\0';
1200 xasprintf(&out, "%s-cert.pub", tmp);
1201 xfree(tmp);
1202
1203 if ((fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
1204 fatal("Could not open \"%s\" for writing: %s", out,
1205 strerror(errno));
1206 if ((f = fdopen(fd, "w")) == NULL)
1207 fatal("%s: fdopen: %s", __func__, strerror(errno));
1208 if (!key_write(public, f))
1209 fatal("Could not write certified key to %s", out);
1210 fprintf(f, " %s\n", comment);
1211 fclose(f);
1212
1213 if (!quiet)
1214 logit("Signed %s key %s: id \"%s\"%s%s valid %s",
1215 cert_key_type == SSH2_CERT_TYPE_USER?"user":"host",
1216 out, cert_key_id,
1217 cert_principals != NULL ? " for " : "",
1218 cert_principals != NULL ? cert_principals : "",
1219 fmt_validity());
1220
1221 key_free(public);
1222 xfree(out);
1223 }
1224 exit(0);
1225}
1226
1227static u_int64_t
1228parse_relative_time(const char *s, time_t now)
1229{
1230 int64_t mul, secs;
1231
1232 mul = *s == '-' ? -1 : 1;
1233
1234 if ((secs = convtime(s + 1)) == -1)
1235 fatal("Invalid relative certificate time %s", s);
1236 if (mul == -1 && secs > now)
1237 fatal("Certificate time %s cannot be represented", s);
1238 return now + (u_int64_t)(secs * mul);
1239}
1240
1241static u_int64_t
1242parse_absolute_time(const char *s)
1243{
1244 struct tm tm;
1245 time_t tt;
1246
1247 if (strlen(s) != 8 && strlen(s) != 14)
1248 fatal("Invalid certificate time format %s", s);
1249
1250 bzero(&tm, sizeof(tm));
1251 if (strptime(s,
1252 strlen(s) == 8 ? "%Y%m%d" : "%Y%m%d%H%M%S", &tm) == NULL)
1253 fatal("Invalid certificate time %s", s);
1254 if ((tt = mktime(&tm)) < 0)
1255 fatal("Certificate time %s cannot be represented", s);
1256 return (u_int64_t)tt;
1257}
1258
1259static void
1260parse_cert_times(char *timespec)
1261{
1262 char *from, *to;
1263 time_t now = time(NULL);
1264 int64_t secs;
1265
1266 /* +timespec relative to now */
1267 if (*timespec == '+' && strchr(timespec, ':') == NULL) {
1268 if ((secs = convtime(timespec + 1)) == -1)
1269 fatal("Invalid relative certificate life %s", timespec);
1270 cert_valid_to = now + secs;
1271 /*
1272 * Backdate certificate one minute to avoid problems on hosts
1273 * with poorly-synchronised clocks.
1274 */
1275 cert_valid_from = ((now - 59)/ 60) * 60;
1276 return;
1277 }
1278
1279 /*
1280 * from:to, where
1281 * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS
1282 * to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS
1283 */
1284 from = xstrdup(timespec);
1285 to = strchr(from, ':');
1286 if (to == NULL || from == to || *(to + 1) == '\0')
1287 fatal("Invalid certificate life specification %s", optarg);
1288 *to++ = '\0';
1289
1290 if (*from == '-' || *from == '+')
1291 cert_valid_from = parse_relative_time(from, now);
1292 else
1293 cert_valid_from = parse_absolute_time(from);
1294
1295 if (*to == '-' || *to == '+')
1296 cert_valid_to = parse_relative_time(to, cert_valid_from);
1297 else
1298 cert_valid_to = parse_absolute_time(to);
1299
1300 if (cert_valid_to <= cert_valid_from)
1301 fatal("Empty certificate validity interval");
1302 xfree(from);
1303}
1304
1305static void
1306add_cert_constraint(char *opt)
1307{
1308 char *val;
1309
1310 if (strcmp(opt, "clear") == 0)
1311 constraint_flags = 0;
1312 else if (strcasecmp(opt, "no-x11-forwarding") == 0)
1313 constraint_flags &= ~CONSTRAINT_X_FWD;
1314 else if (strcasecmp(opt, "permit-x11-forwarding") == 0)
1315 constraint_flags |= CONSTRAINT_X_FWD;
1316 else if (strcasecmp(opt, "no-agent-forwarding") == 0)
1317 constraint_flags &= ~CONSTRAINT_AGENT_FWD;
1318 else if (strcasecmp(opt, "permit-agent-forwarding") == 0)
1319 constraint_flags |= CONSTRAINT_AGENT_FWD;
1320 else if (strcasecmp(opt, "no-port-forwarding") == 0)
1321 constraint_flags &= ~CONSTRAINT_PORT_FWD;
1322 else if (strcasecmp(opt, "permit-port-forwarding") == 0)
1323 constraint_flags |= CONSTRAINT_PORT_FWD;
1324 else if (strcasecmp(opt, "no-pty") == 0)
1325 constraint_flags &= ~CONSTRAINT_PTY;
1326 else if (strcasecmp(opt, "permit-pty") == 0)
1327 constraint_flags |= CONSTRAINT_PTY;
1328 else if (strcasecmp(opt, "no-user-rc") == 0)
1329 constraint_flags &= ~CONSTRAINT_USER_RC;
1330 else if (strcasecmp(opt, "permit-user-rc") == 0)
1331 constraint_flags |= CONSTRAINT_USER_RC;
1332 else if (strncasecmp(opt, "force-command=", 14) == 0) {
1333 val = opt + 14;
1334 if (*val == '\0')
1335 fatal("Empty force-command constraint");
1336 if (constraint_command != NULL)
1337 fatal("force-command already specified");
1338 constraint_command = xstrdup(val);
1339 } else if (strncasecmp(opt, "source-address=", 15) == 0) {
1340 val = opt + 15;
1341 if (*val == '\0')
1342 fatal("Empty source-address constraint");
1343 if (constraint_src_addr != NULL)
1344 fatal("source-address already specified");
1345 if (addr_match_cidr_list(NULL, val) != 0)
1346 fatal("Invalid source-address list");
1347 constraint_src_addr = xstrdup(val);
1348 } else
1349 fatal("Unsupported certificate constraint \"%s\"", opt);
1350}
1351
1015static void 1352static void
1016usage(void) 1353usage(void)
1017{ 1354{
@@ -1031,18 +1368,24 @@ usage(void)
1031 fprintf(stderr, " -G file Generate candidates for DH-GEX moduli.\n"); 1368 fprintf(stderr, " -G file Generate candidates for DH-GEX moduli.\n");
1032 fprintf(stderr, " -g Use generic DNS resource record format.\n"); 1369 fprintf(stderr, " -g Use generic DNS resource record format.\n");
1033 fprintf(stderr, " -H Hash names in known_hosts file.\n"); 1370 fprintf(stderr, " -H Hash names in known_hosts file.\n");
1371 fprintf(stderr, " -h Generate host certificate instead of a user certificate.\n");
1372 fprintf(stderr, " -I key_id Key identifier to include in certificate.\n");
1034 fprintf(stderr, " -i Convert RFC 4716 to OpenSSH key file.\n"); 1373 fprintf(stderr, " -i Convert RFC 4716 to OpenSSH key file.\n");
1035 fprintf(stderr, " -l Show fingerprint of key file.\n"); 1374 fprintf(stderr, " -l Show fingerprint of key file.\n");
1036 fprintf(stderr, " -M memory Amount of memory (MB) to use for generating DH-GEX moduli.\n"); 1375 fprintf(stderr, " -M memory Amount of memory (MB) to use for generating DH-GEX moduli.\n");
1376 fprintf(stderr, " -n name,... User/host principal names to include in certificate\n");
1037 fprintf(stderr, " -N phrase Provide new passphrase.\n"); 1377 fprintf(stderr, " -N phrase Provide new passphrase.\n");
1378 fprintf(stderr, " -O cnstr Specify a certificate constraint.\n");
1038 fprintf(stderr, " -P phrase Provide old passphrase.\n"); 1379 fprintf(stderr, " -P phrase Provide old passphrase.\n");
1039 fprintf(stderr, " -p Change passphrase of private key file.\n"); 1380 fprintf(stderr, " -p Change passphrase of private key file.\n");
1040 fprintf(stderr, " -q Quiet.\n"); 1381 fprintf(stderr, " -q Quiet.\n");
1041 fprintf(stderr, " -R hostname Remove host from known_hosts file.\n"); 1382 fprintf(stderr, " -R hostname Remove host from known_hosts file.\n");
1042 fprintf(stderr, " -r hostname Print DNS resource record.\n"); 1383 fprintf(stderr, " -r hostname Print DNS resource record.\n");
1384 fprintf(stderr, " -s ca_key Certify keys with CA key.\n");
1043 fprintf(stderr, " -S start Start point (hex) for generating DH-GEX moduli.\n"); 1385 fprintf(stderr, " -S start Start point (hex) for generating DH-GEX moduli.\n");
1044 fprintf(stderr, " -T file Screen candidates for DH-GEX moduli.\n"); 1386 fprintf(stderr, " -T file Screen candidates for DH-GEX moduli.\n");
1045 fprintf(stderr, " -t type Specify type of key to create.\n"); 1387 fprintf(stderr, " -t type Specify type of key to create.\n");
1388 fprintf(stderr, " -V from:to Specify certificate validity interval.\n");
1046 fprintf(stderr, " -v Verbose.\n"); 1389 fprintf(stderr, " -v Verbose.\n");
1047 fprintf(stderr, " -W gen Generator to use for generating DH-GEX moduli.\n"); 1390 fprintf(stderr, " -W gen Generator to use for generating DH-GEX moduli.\n");
1048 fprintf(stderr, " -y Read private key file and print public key.\n"); 1391 fprintf(stderr, " -y Read private key file and print public key.\n");
@@ -1094,8 +1437,8 @@ main(int argc, char **argv)
1094 exit(1); 1437 exit(1);
1095 } 1438 }
1096 1439
1097 while ((opt = getopt(argc, argv, 1440 while ((opt = getopt(argc, argv, "degiqpclBHhvxXyF:b:f:t:D:I:P:N:n:"
1098 "degiqpclBHvxXyF:b:f:t:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) { 1441 "O:C:r:g:R:T:G:M:S:s:a:V:W:")) != -1) {
1099 switch (opt) { 1442 switch (opt) {
1100 case 'b': 1443 case 'b':
1101 bits = (u_int32_t)strtonum(optarg, 768, 32768, &errstr); 1444 bits = (u_int32_t)strtonum(optarg, 768, 32768, &errstr);
@@ -1110,6 +1453,9 @@ main(int argc, char **argv)
1110 case 'H': 1453 case 'H':
1111 hash_hosts = 1; 1454 hash_hosts = 1;
1112 break; 1455 break;
1456 case 'I':
1457 cert_key_id = optarg;
1458 break;
1113 case 'R': 1459 case 'R':
1114 delete_host = 1; 1460 delete_host = 1;
1115 rr_hostname = optarg; 1461 rr_hostname = optarg;
@@ -1120,6 +1466,9 @@ main(int argc, char **argv)
1120 case 'B': 1466 case 'B':
1121 print_bubblebabble = 1; 1467 print_bubblebabble = 1;
1122 break; 1468 break;
1469 case 'n':
1470 cert_principals = optarg;
1471 break;
1123 case 'p': 1472 case 'p':
1124 change_passphrase = 1; 1473 change_passphrase = 1;
1125 break; 1474 break;
@@ -1141,6 +1490,9 @@ main(int argc, char **argv)
1141 case 'N': 1490 case 'N':
1142 identity_new_passphrase = optarg; 1491 identity_new_passphrase = optarg;
1143 break; 1492 break;
1493 case 'O':
1494 add_cert_constraint(optarg);
1495 break;
1144 case 'C': 1496 case 'C':
1145 identity_comment = optarg; 1497 identity_comment = optarg;
1146 break; 1498 break;
@@ -1152,6 +1504,10 @@ main(int argc, char **argv)
1152 /* export key */ 1504 /* export key */
1153 convert_to_ssh2 = 1; 1505 convert_to_ssh2 = 1;
1154 break; 1506 break;
1507 case 'h':
1508 cert_key_type = SSH2_CERT_TYPE_HOST;
1509 constraint_flags = 0;
1510 break;
1155 case 'i': 1511 case 'i':
1156 case 'X': 1512 case 'X':
1157 /* import key */ 1513 /* import key */
@@ -1163,6 +1519,9 @@ main(int argc, char **argv)
1163 case 'd': 1519 case 'd':
1164 key_type_name = "dsa"; 1520 key_type_name = "dsa";
1165 break; 1521 break;
1522 case 's':
1523 ca_key_path = optarg;
1524 break;
1166 case 't': 1525 case 't':
1167 key_type_name = optarg; 1526 key_type_name = optarg;
1168 break; 1527 break;
@@ -1217,6 +1576,9 @@ main(int argc, char **argv)
1217 if (BN_hex2bn(&start, optarg) == 0) 1576 if (BN_hex2bn(&start, optarg) == 0)
1218 fatal("Invalid start point."); 1577 fatal("Invalid start point.");
1219 break; 1578 break;
1579 case 'V':
1580 parse_cert_times(optarg);
1581 break;
1220 case '?': 1582 case '?':
1221 default: 1583 default:
1222 usage(); 1584 usage();
@@ -1226,7 +1588,15 @@ main(int argc, char **argv)
1226 /* reinit */ 1588 /* reinit */
1227 log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1); 1589 log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1);
1228 1590
1229 if (optind < argc) { 1591 argv += optind;
1592 argc -= optind;
1593
1594 if (ca_key_path != NULL) {
1595 if (argc < 1) {
1596 printf("Too few arguments.\n");
1597 usage();
1598 }
1599 } else if (argc > 0) {
1230 printf("Too many arguments.\n"); 1600 printf("Too many arguments.\n");
1231 usage(); 1601 usage();
1232 } 1602 }
@@ -1238,6 +1608,11 @@ main(int argc, char **argv)
1238 printf("Cannot use -l with -D or -R.\n"); 1608 printf("Cannot use -l with -D or -R.\n");
1239 usage(); 1609 usage();
1240 } 1610 }
1611 if (ca_key_path != NULL) {
1612 if (cert_key_id == NULL)
1613 fatal("Must specify key id (-I) when certifying");
1614 do_ca_sign(pw, argc, argv);
1615 }
1241 if (delete_host || hash_hosts || find_host) 1616 if (delete_host || hash_hosts || find_host)
1242 do_known_hosts(pw, rr_hostname); 1617 do_known_hosts(pw, rr_hostname);
1243 if (print_fingerprint || print_bubblebabble) 1618 if (print_fingerprint || print_bubblebabble)
diff --git a/ssh-rsa.c b/ssh-rsa.c
index 0e16ff85f..842857fee 100644
--- a/ssh-rsa.c
+++ b/ssh-rsa.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-rsa.c,v 1.39 2006/08/03 03:34:42 deraadt Exp $ */ 1/* $OpenBSD: ssh-rsa.c,v 1.40 2010/02/26 20:29:54 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org> 3 * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org>
4 * 4 *
@@ -46,7 +46,9 @@ ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp,
46 int ok, nid; 46 int ok, nid;
47 Buffer b; 47 Buffer b;
48 48
49 if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { 49 if (key == NULL ||
50 (key->type != KEY_RSA && key->type != KEY_RSA_CERT) ||
51 key->rsa == NULL) {
50 error("ssh_rsa_sign: no RSA key"); 52 error("ssh_rsa_sign: no RSA key");
51 return -1; 53 return -1;
52 } 54 }
@@ -113,7 +115,9 @@ ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen,
113 u_int len, dlen, modlen; 115 u_int len, dlen, modlen;
114 int rlen, ret, nid; 116 int rlen, ret, nid;
115 117
116 if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { 118 if (key == NULL ||
119 (key->type != KEY_RSA && key->type != KEY_RSA_CERT) ||
120 key->rsa == NULL) {
117 error("ssh_rsa_verify: no RSA key"); 121 error("ssh_rsa_verify: no RSA key");
118 return -1; 122 return -1;
119 } 123 }
diff --git a/ssh.1 b/ssh.1
index 6964cd09c..7d8f92aba 100644
--- a/ssh.1
+++ b/ssh.1
@@ -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.294 2010/02/11 13:23:29 jmc Exp $ 37.\" $OpenBSD: ssh.1,v 1.295 2010/02/26 20:29:54 djm Exp $
38.Dd $Mdocdate: February 11 2010 $ 38.Dd $Mdocdate: February 26 2010 $
39.Dt SSH 1 39.Dt SSH 1
40.Os 40.Os
41.Sh NAME 41.Sh NAME
@@ -1103,6 +1103,25 @@ See the
1103option in 1103option in
1104.Xr ssh_config 5 1104.Xr ssh_config 5
1105for more information. 1105for more information.
1106.Pp
1107Host keys may also be presented as certificates signed by a trusted
1108certification authority (CA).
1109In this case, trust of the CA key alone is sufficient for the host key
1110to be accepted.
1111To specify a public key as a trusted CA key in a known hosts file,
1112it should be added after a
1113.Dq @cert-authority
1114tag and a set of one or more domain-name wildcards separated by commas.
1115For example:
1116.Pp
1117.Dl @cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W...
1118.Pp
1119See the
1120.Sx CERTIFICATES
1121section of
1122.Xr ssh-keygen 1
1123for more details.
1124.Pp
1106.Sh SSH-BASED VIRTUAL PRIVATE NETWORKS 1125.Sh SSH-BASED VIRTUAL PRIVATE NETWORKS
1107.Nm 1126.Nm
1108contains support for Virtual Private Network (VPN) tunnelling 1127contains support for Virtual Private Network (VPN) tunnelling
diff --git a/ssh.c b/ssh.c
index 25ccdcaa5..b9553d3e1 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh.c,v 1.334 2010/02/08 22:03:05 jmc Exp $ */ 1/* $OpenBSD: ssh.c,v 1.335 2010/02/26 20:29:54 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
@@ -1306,34 +1306,35 @@ load_public_identity_files(void)
1306 int i = 0; 1306 int i = 0;
1307 Key *public; 1307 Key *public;
1308 struct passwd *pw; 1308 struct passwd *pw;
1309 u_int n_ids;
1310 char *identity_files[SSH_MAX_IDENTITY_FILES];
1311 Key *identity_keys[SSH_MAX_IDENTITY_FILES];
1309#ifdef ENABLE_PKCS11 1312#ifdef ENABLE_PKCS11
1310 Key **keys; 1313 Key **keys;
1311 int nkeys; 1314 int nkeys;
1315#endif /* PKCS11 */
1312 1316
1317 n_ids = 0;
1318 bzero(identity_files, sizeof(identity_files));
1319 bzero(identity_keys, sizeof(identity_keys));
1320
1321#ifdef ENABLE_PKCS11
1313 if (options.pkcs11_provider != NULL && 1322 if (options.pkcs11_provider != NULL &&
1314 options.num_identity_files < SSH_MAX_IDENTITY_FILES && 1323 options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
1315 (pkcs11_init(!options.batch_mode) == 0) && 1324 (pkcs11_init(!options.batch_mode) == 0) &&
1316 (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, 1325 (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL,
1317 &keys)) > 0) { 1326 &keys)) > 0) {
1318 int count = 0;
1319 for (i = 0; i < nkeys; i++) { 1327 for (i = 0; i < nkeys; i++) {
1320 count++; 1328 if (n_ids >= SSH_MAX_IDENTITY_FILES) {
1321 memmove(&options.identity_files[1], 1329 key_free(keys[i]);
1322 &options.identity_files[0], 1330 continue;
1323 sizeof(char *) * (SSH_MAX_IDENTITY_FILES - 1)); 1331 }
1324 memmove(&options.identity_keys[1], 1332 identity_keys[n_ids] = keys[i];
1325 &options.identity_keys[0], 1333 identity_files[n_ids] =
1326 sizeof(Key *) * (SSH_MAX_IDENTITY_FILES - 1));
1327 options.num_identity_files++;
1328 options.identity_keys[0] = keys[i];
1329 options.identity_files[0] =
1330 xstrdup(options.pkcs11_provider); /* XXX */ 1334 xstrdup(options.pkcs11_provider); /* XXX */
1335 n_ids++;
1331 } 1336 }
1332 if (options.num_identity_files > SSH_MAX_IDENTITY_FILES)
1333 options.num_identity_files = SSH_MAX_IDENTITY_FILES;
1334 i = count;
1335 xfree(keys); 1337 xfree(keys);
1336 /* XXX leaks some keys */
1337 } 1338 }
1338#endif /* ENABLE_PKCS11 */ 1339#endif /* ENABLE_PKCS11 */
1339 if ((pw = getpwuid(original_real_uid)) == NULL) 1340 if ((pw = getpwuid(original_real_uid)) == NULL)
@@ -1343,7 +1344,11 @@ load_public_identity_files(void)
1343 if (gethostname(thishost, sizeof(thishost)) == -1) 1344 if (gethostname(thishost, sizeof(thishost)) == -1)
1344 fatal("load_public_identity_files: gethostname: %s", 1345 fatal("load_public_identity_files: gethostname: %s",
1345 strerror(errno)); 1346 strerror(errno));
1346 for (; i < options.num_identity_files; i++) { 1347 for (i = 0; i < options.num_identity_files; i++) {
1348 if (n_ids >= SSH_MAX_IDENTITY_FILES) {
1349 xfree(options.identity_files[i]);
1350 continue;
1351 }
1347 cp = tilde_expand_filename(options.identity_files[i], 1352 cp = tilde_expand_filename(options.identity_files[i],
1348 original_real_uid); 1353 original_real_uid);
1349 filename = percent_expand(cp, "d", pwdir, 1354 filename = percent_expand(cp, "d", pwdir,
@@ -1354,9 +1359,37 @@ load_public_identity_files(void)
1354 debug("identity file %s type %d", filename, 1359 debug("identity file %s type %d", filename,
1355 public ? public->type : -1); 1360 public ? public->type : -1);
1356 xfree(options.identity_files[i]); 1361 xfree(options.identity_files[i]);
1357 options.identity_files[i] = filename; 1362 identity_files[n_ids] = filename;
1358 options.identity_keys[i] = public; 1363 identity_keys[n_ids] = public;
1364
1365 if (++n_ids >= SSH_MAX_IDENTITY_FILES)
1366 continue;
1367
1368 /* Try to add the certificate variant too */
1369 xasprintf(&cp, "%s-cert", filename);
1370 public = key_load_public(cp, NULL);
1371 debug("identity file %s type %d", cp,
1372 public ? public->type : -1);
1373 if (public == NULL) {
1374 xfree(cp);
1375 continue;
1376 }
1377 if (!key_is_cert(public)) {
1378 debug("%s: key %s type %s is not a certificate",
1379 __func__, cp, key_type(public));
1380 key_free(public);
1381 xfree(cp);
1382 continue;
1383 }
1384 identity_keys[n_ids] = public;
1385 /* point to the original path, most likely the private key */
1386 identity_files[n_ids] = xstrdup(filename);
1387 n_ids++;
1359 } 1388 }
1389 options.num_identity_files = n_ids;
1390 memcpy(options.identity_files, identity_files, sizeof(identity_files));
1391 memcpy(options.identity_keys, identity_keys, sizeof(identity_keys));
1392
1360 bzero(pwname, strlen(pwname)); 1393 bzero(pwname, strlen(pwname));
1361 xfree(pwname); 1394 xfree(pwname);
1362 bzero(pwdir, strlen(pwdir)); 1395 bzero(pwdir, strlen(pwdir));
diff --git a/ssh2.h b/ssh2.h
index b01af7b1a..3ffaf686b 100644
--- a/ssh2.h
+++ b/ssh2.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh2.h,v 1.12 2009/10/24 11:19:17 andreas Exp $ */ 1/* $OpenBSD: ssh2.h,v 1.13 2010/02/26 20:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -173,3 +173,6 @@
173#define SSH2_MSG_KEX_ROAMING_AUTH_OK 33 173#define SSH2_MSG_KEX_ROAMING_AUTH_OK 33
174#define SSH2_MSG_KEX_ROAMING_AUTH_FAIL 34 174#define SSH2_MSG_KEX_ROAMING_AUTH_FAIL 34
175 175
176/* Certificate types for OpenSSH certificate keys extension */
177#define SSH2_CERT_TYPE_USER 1
178#define SSH2_CERT_TYPE_HOST 2
diff --git a/sshconnect.c b/sshconnect.c
index 63c4650f7..35c2f49be 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshconnect.c,v 1.218 2010/01/13 00:19:04 dtucker Exp $ */ 1/* $OpenBSD: sshconnect.c,v 1.219 2010/02/26 20:29:54 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
@@ -58,6 +58,7 @@
58#include "misc.h" 58#include "misc.h"
59#include "dns.h" 59#include "dns.h"
60#include "roaming.h" 60#include "roaming.h"
61#include "ssh2.h"
61#include "version.h" 62#include "version.h"
62 63
63char *client_version_string = NULL; 64char *client_version_string = NULL;
@@ -576,6 +577,23 @@ confirm(const char *prompt)
576 } 577 }
577} 578}
578 579
580static int
581check_host_cert(const char *host, const Key *host_key)
582{
583 const char *reason;
584
585 if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) {
586 error("%s", reason);
587 return 0;
588 }
589 if (buffer_len(&host_key->cert->constraints) != 0) {
590 error("Certificate for %s contains unsupported constraint(s)",
591 host);
592 return 0;
593 }
594 return 1;
595}
596
579/* 597/*
580 * check whether the supplied host key is valid, return -1 if the key 598 * check whether the supplied host key is valid, return -1 if the key
581 * is not valid. the user_hostfile will not be updated if 'readonly' is true. 599 * is not valid. the user_hostfile will not be updated if 'readonly' is true.
@@ -588,13 +606,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
588 Key *host_key, int readonly, const char *user_hostfile, 606 Key *host_key, int readonly, const char *user_hostfile,
589 const char *system_hostfile) 607 const char *system_hostfile)
590{ 608{
591 Key *file_key; 609 Key *file_key, *raw_key = NULL;
592 const char *type = key_type(host_key); 610 const char *type;
593 char *ip = NULL, *host = NULL; 611 char *ip = NULL, *host = NULL;
594 char hostline[1000], *hostp, *fp, *ra; 612 char hostline[1000], *hostp, *fp, *ra;
595 HostStatus host_status; 613 HostStatus host_status;
596 HostStatus ip_status; 614 HostStatus ip_status;
597 int r, local = 0, host_ip_differ = 0; 615 int r, want_cert, local = 0, host_ip_differ = 0;
598 int salen; 616 int salen;
599 char ntop[NI_MAXHOST]; 617 char ntop[NI_MAXHOST];
600 char msg[1024]; 618 char msg[1024];
@@ -667,11 +685,15 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
667 host = put_host_port(hostname, port); 685 host = put_host_port(hostname, port);
668 } 686 }
669 687
688 retry:
689 want_cert = key_is_cert(host_key);
690 type = key_type(host_key);
691
670 /* 692 /*
671 * Store the host key from the known host file in here so that we can 693 * Store the host key from the known host file in here so that we can
672 * compare it with the key for the IP address. 694 * compare it with the key for the IP address.
673 */ 695 */
674 file_key = key_new(host_key->type); 696 file_key = key_new(key_is_cert(host_key) ? KEY_UNSPEC : host_key->type);
675 697
676 /* 698 /*
677 * Check if the host key is present in the user's list of known 699 * Check if the host key is present in the user's list of known
@@ -687,9 +709,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
687 } 709 }
688 /* 710 /*
689 * Also perform check for the ip address, skip the check if we are 711 * Also perform check for the ip address, skip the check if we are
690 * localhost or the hostname was an ip address to begin with 712 * localhost, looking for a certificate, or the hostname was an ip
713 * address to begin with.
691 */ 714 */
692 if (options.check_host_ip) { 715 if (!want_cert && options.check_host_ip) {
693 Key *ip_key = key_new(host_key->type); 716 Key *ip_key = key_new(host_key->type);
694 717
695 ip_file = user_hostfile; 718 ip_file = user_hostfile;
@@ -713,11 +736,14 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
713 switch (host_status) { 736 switch (host_status) {
714 case HOST_OK: 737 case HOST_OK:
715 /* The host is known and the key matches. */ 738 /* The host is known and the key matches. */
716 debug("Host '%.200s' is known and matches the %s host key.", 739 debug("Host '%.200s' is known and matches the %s host %s.",
717 host, type); 740 host, type, want_cert ? "certificate" : "key");
718 debug("Found key in %s:%d", host_file, host_line); 741 debug("Found %s in %s:%d",
742 want_cert ? "certificate" : "key", host_file, host_line);
743 if (want_cert && !check_host_cert(hostname, host_key))
744 goto fail;
719 if (options.check_host_ip && ip_status == HOST_NEW) { 745 if (options.check_host_ip && ip_status == HOST_NEW) {
720 if (readonly) 746 if (readonly || want_cert)
721 logit("%s host key for IP address " 747 logit("%s host key for IP address "
722 "'%.128s' not in list of known hosts.", 748 "'%.128s' not in list of known hosts.",
723 type, ip); 749 type, ip);
@@ -749,7 +775,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
749 break; 775 break;
750 } 776 }
751 } 777 }
752 if (readonly) 778 if (readonly || want_cert)
753 goto fail; 779 goto fail;
754 /* The host is new. */ 780 /* The host is new. */
755 if (options.strict_host_key_checking == 1) { 781 if (options.strict_host_key_checking == 1) {
@@ -834,6 +860,17 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
834 "list of known hosts.", hostp, type); 860 "list of known hosts.", hostp, type);
835 break; 861 break;
836 case HOST_CHANGED: 862 case HOST_CHANGED:
863 if (want_cert) {
864 /*
865 * This is only a debug() since it is valid to have
866 * CAs with wildcard DNS matches that don't match
867 * all hosts that one might visit.
868 */
869 debug("Host certificate authority does not "
870 "match %s in %s:%d", CA_MARKER,
871 host_file, host_line);
872 goto fail;
873 }
837 if (readonly == ROQUIET) 874 if (readonly == ROQUIET)
838 goto fail; 875 goto fail;
839 if (options.check_host_ip && host_ip_differ) { 876 if (options.check_host_ip && host_ip_differ) {
@@ -970,6 +1007,20 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
970 return 0; 1007 return 0;
971 1008
972fail: 1009fail:
1010 if (want_cert) {
1011 /*
1012 * No matching certificate. Downgrade cert to raw key and
1013 * search normally.
1014 */
1015 debug("No matching CA found. Retry with plain key");
1016 raw_key = key_from_private(host_key);
1017 if (key_drop_cert(raw_key) != 0)
1018 fatal("Couldn't drop certificate");
1019 host_key = raw_key;
1020 goto retry;
1021 }
1022 if (raw_key != NULL)
1023 key_free(raw_key);
973 xfree(ip); 1024 xfree(ip);
974 xfree(host); 1025 xfree(host);
975 return -1; 1026 return -1;
@@ -982,7 +1033,8 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
982 struct stat st; 1033 struct stat st;
983 int flags = 0; 1034 int flags = 0;
984 1035
985 if (options.verify_host_key_dns && 1036 /* XXX certs are not yet supported for DNS */
1037 if (!key_is_cert(host_key) && options.verify_host_key_dns &&
986 verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { 1038 verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) {
987 1039
988 if (flags & DNS_VERIFY_FOUND) { 1040 if (flags & DNS_VERIFY_FOUND) {
diff --git a/sshconnect2.c b/sshconnect2.c
index e81064dae..2a5943e7e 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshconnect2.c,v 1.179 2010/01/13 01:20:20 dtucker Exp $ */ 1/* $OpenBSD: sshconnect2.c,v 1.180 2010/02/26 20:29:54 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2008 Damien Miller. All rights reserved. 4 * Copyright (c) 2008 Damien Miller. All rights reserved.
@@ -1311,6 +1311,8 @@ pubkey_prepare(Authctxt *authctxt)
1311 key = options.identity_keys[i]; 1311 key = options.identity_keys[i];
1312 if (key && key->type == KEY_RSA1) 1312 if (key && key->type == KEY_RSA1)
1313 continue; 1313 continue;
1314 if (key && key->cert && key->cert->type != SSH2_CERT_TYPE_USER)
1315 continue;
1314 options.identity_keys[i] = NULL; 1316 options.identity_keys[i] = NULL;
1315 id = xcalloc(1, sizeof(*id)); 1317 id = xcalloc(1, sizeof(*id));
1316 id->key = key; 1318 id->key = key;
diff --git a/sshd.8 b/sshd.8
index 76b7e2987..fcd5195db 100644
--- a/sshd.8
+++ b/sshd.8
@@ -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.8,v 1.250 2010/01/30 21:08:33 djm Exp $ 37.\" $OpenBSD: sshd.8,v 1.251 2010/02/26 20:29:54 djm Exp $
38.Dd $Mdocdate: January 30 2010 $ 38.Dd $Mdocdate: February 26 2010 $
39.Dt SSHD 8 39.Dt SSHD 8
40.Os 40.Os
41.Sh NAME 41.Sh NAME
@@ -47,6 +47,7 @@
47.Op Fl 46DdeiqTt 47.Op Fl 46DdeiqTt
48.Op Fl b Ar bits 48.Op Fl b Ar bits
49.Op Fl C Ar connection_spec 49.Op Fl C Ar connection_spec
50.Op Fl c Ar host_certificate_file
50.Op Fl f Ar config_file 51.Op Fl f Ar config_file
51.Op Fl g Ar login_grace_time 52.Op Fl g Ar login_grace_time
52.Op Fl h Ar host_key_file 53.Op Fl h Ar host_key_file
@@ -101,6 +102,15 @@ to use IPv6 addresses only.
101.It Fl b Ar bits 102.It Fl b Ar bits
102Specifies the number of bits in the ephemeral protocol version 1 103Specifies the number of bits in the ephemeral protocol version 1
103server key (default 1024). 104server key (default 1024).
105.It Fl c Ar host_certificate_file
106Specifies a path to a certificate file to identify
107.Nm
108during key exchange.
109The certificate file must match a host key file specified using the
110.Fl -h
111option or the
112.Cm HostKey
113configuration directive.
104.It Fl C Ar connection_spec 114.It Fl C Ar connection_spec
105Specify the connection parameters to use for the 115Specify the connection parameters to use for the
106.Fl T 116.Fl T
@@ -498,6 +508,13 @@ No spaces are permitted, except within double quotes.
498The following option specifications are supported (note 508The following option specifications are supported (note
499that option keywords are case-insensitive): 509that option keywords are case-insensitive):
500.Bl -tag -width Ds 510.Bl -tag -width Ds
511.It Cm from="cert-authority"
512Specifies that the listed key is a certification authority (CA) that is
513trusted to validate signed certificates for user authentication.
514.Pp
515Certificates may encode access restrictions similar to these key options.
516If both certificate restrictions and key options are present, the most
517restrictive union of the two is applied.
501.It Cm command="command" 518.It Cm command="command"
502Specifies that the command is executed whenever this key is used for 519Specifies that the command is executed whenever this key is used for
503authentication. 520authentication.
@@ -517,6 +534,10 @@ The command originally supplied by the client is available in the
517.Ev SSH_ORIGINAL_COMMAND 534.Ev SSH_ORIGINAL_COMMAND
518environment variable. 535environment variable.
519Note that this option applies to shell, command or subsystem execution. 536Note that this option applies to shell, command or subsystem execution.
537Also note that this command may be superseded by either a
538.Xr sshd_config 5
539.Cm ForceCommand
540directive or a command embedded in a certificate.
520.It Cm environment="NAME=value" 541.It Cm environment="NAME=value"
521Specifies that the string is to be added to the environment when 542Specifies that the string is to be added to the environment when
522logging in using this key. 543logging in using this key.
diff --git a/sshd.c b/sshd.c
index bf2e76cc8..0c3c04e4e 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshd.c,v 1.372 2010/01/29 00:20:41 djm Exp $ */ 1/* $OpenBSD: sshd.c,v 1.373 2010/02/26 20:29:54 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
@@ -205,6 +205,7 @@ struct {
205 Key *server_key; /* ephemeral server key */ 205 Key *server_key; /* ephemeral server key */
206 Key *ssh1_host_key; /* ssh1 host key */ 206 Key *ssh1_host_key; /* ssh1 host key */
207 Key **host_keys; /* all private host keys */ 207 Key **host_keys; /* all private host keys */
208 Key **host_certificates; /* all public host certificates */
208 int have_ssh1_key; 209 int have_ssh1_key;
209 int have_ssh2_key; 210 int have_ssh2_key;
210 u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH]; 211 u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH];
@@ -545,6 +546,10 @@ destroy_sensitive_data(void)
545 key_free(sensitive_data.host_keys[i]); 546 key_free(sensitive_data.host_keys[i]);
546 sensitive_data.host_keys[i] = NULL; 547 sensitive_data.host_keys[i] = NULL;
547 } 548 }
549 if (sensitive_data.host_certificates[i]) {
550 key_free(sensitive_data.host_certificates[i]);
551 sensitive_data.host_certificates[i] = NULL;
552 }
548 } 553 }
549 sensitive_data.ssh1_host_key = NULL; 554 sensitive_data.ssh1_host_key = NULL;
550 memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); 555 memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH);
@@ -571,6 +576,7 @@ demote_sensitive_data(void)
571 if (tmp->type == KEY_RSA1) 576 if (tmp->type == KEY_RSA1)
572 sensitive_data.ssh1_host_key = tmp; 577 sensitive_data.ssh1_host_key = tmp;
573 } 578 }
579 /* Certs do not need demotion */
574 } 580 }
575 581
576 /* We do not clear ssh1_host key and cookie. XXX - Okay Niels? */ 582 /* We do not clear ssh1_host key and cookie. XXX - Okay Niels? */
@@ -717,10 +723,11 @@ list_hostkey_types(void)
717 const char *p; 723 const char *p;
718 char *ret; 724 char *ret;
719 int i; 725 int i;
726 Key *key;
720 727
721 buffer_init(&b); 728 buffer_init(&b);
722 for (i = 0; i < options.num_host_key_files; i++) { 729 for (i = 0; i < options.num_host_key_files; i++) {
723 Key *key = sensitive_data.host_keys[i]; 730 key = sensitive_data.host_keys[i];
724 if (key == NULL) 731 if (key == NULL)
725 continue; 732 continue;
726 switch (key->type) { 733 switch (key->type) {
@@ -732,6 +739,19 @@ list_hostkey_types(void)
732 buffer_append(&b, p, strlen(p)); 739 buffer_append(&b, p, strlen(p));
733 break; 740 break;
734 } 741 }
742 /* If the private key has a cert peer, then list that too */
743 key = sensitive_data.host_certificates[i];
744 if (key == NULL)
745 continue;
746 switch (key->type) {
747 case KEY_RSA_CERT:
748 case KEY_DSA_CERT:
749 if (buffer_len(&b) > 0)
750 buffer_append(&b, ",", 1);
751 p = key_ssh_name(key);
752 buffer_append(&b, p, strlen(p));
753 break;
754 }
735 } 755 }
736 buffer_append(&b, "\0", 1); 756 buffer_append(&b, "\0", 1);
737 ret = xstrdup(buffer_ptr(&b)); 757 ret = xstrdup(buffer_ptr(&b));
@@ -740,20 +760,37 @@ list_hostkey_types(void)
740 return ret; 760 return ret;
741} 761}
742 762
743Key * 763static Key *
744get_hostkey_by_type(int type) 764get_hostkey_by_type(int type, int need_private)
745{ 765{
746 int i; 766 int i;
767 Key *key;
747 768
748 for (i = 0; i < options.num_host_key_files; i++) { 769 for (i = 0; i < options.num_host_key_files; i++) {
749 Key *key = sensitive_data.host_keys[i]; 770 if (type == KEY_RSA_CERT || type == KEY_DSA_CERT)
771 key = sensitive_data.host_certificates[i];
772 else
773 key = sensitive_data.host_keys[i];
750 if (key != NULL && key->type == type) 774 if (key != NULL && key->type == type)
751 return key; 775 return need_private ?
776 sensitive_data.host_keys[i] : key;
752 } 777 }
753 return NULL; 778 return NULL;
754} 779}
755 780
756Key * 781Key *
782get_hostkey_public_by_type(int type)
783{
784 return get_hostkey_by_type(type, 0);
785}
786
787Key *
788get_hostkey_private_by_type(int type)
789{
790 return get_hostkey_by_type(type, 1);
791}
792
793Key *
757get_hostkey_by_index(int ind) 794get_hostkey_by_index(int ind)
758{ 795{
759 if (ind < 0 || ind >= options.num_host_key_files) 796 if (ind < 0 || ind >= options.num_host_key_files)
@@ -767,8 +804,13 @@ get_hostkey_index(Key *key)
767 int i; 804 int i;
768 805
769 for (i = 0; i < options.num_host_key_files; i++) { 806 for (i = 0; i < options.num_host_key_files; i++) {
770 if (key == sensitive_data.host_keys[i]) 807 if (key_is_cert(key)) {
771 return (i); 808 if (key == sensitive_data.host_certificates[i])
809 return (i);
810 } else {
811 if (key == sensitive_data.host_keys[i])
812 return (i);
813 }
772 } 814 }
773 return (-1); 815 return (-1);
774} 816}
@@ -807,9 +849,9 @@ usage(void)
807 fprintf(stderr, "%s, %s\n", 849 fprintf(stderr, "%s, %s\n",
808 SSH_RELEASE, SSLeay_version(SSLEAY_VERSION)); 850 SSH_RELEASE, SSLeay_version(SSLEAY_VERSION));
809 fprintf(stderr, 851 fprintf(stderr,
810"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-f config_file]\n" 852"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n"
811" [-g login_grace_time] [-h host_key_file] [-k key_gen_time]\n" 853" [-f config_file] [-g login_grace_time] [-h host_key_file]\n"
812" [-o option] [-p port] [-u len]\n" 854" [-k key_gen_time] [-o option] [-p port] [-u len]\n"
813 ); 855 );
814 exit(1); 856 exit(1);
815} 857}
@@ -1236,7 +1278,7 @@ main(int ac, char **av)
1236{ 1278{
1237 extern char *optarg; 1279 extern char *optarg;
1238 extern int optind; 1280 extern int optind;
1239 int opt, i, on = 1; 1281 int opt, i, j, on = 1;
1240 int sock_in = -1, sock_out = -1, newsock = -1; 1282 int sock_in = -1, sock_out = -1, newsock = -1;
1241 const char *remote_ip; 1283 const char *remote_ip;
1242 char *test_user = NULL, *test_host = NULL, *test_addr = NULL; 1284 char *test_user = NULL, *test_host = NULL, *test_addr = NULL;
@@ -1289,6 +1331,14 @@ main(int ac, char **av)
1289 case 'f': 1331 case 'f':
1290 config_file_name = optarg; 1332 config_file_name = optarg;
1291 break; 1333 break;
1334 case 'c':
1335 if (options.num_host_cert_files >= MAX_HOSTCERTS) {
1336 fprintf(stderr, "too many host certificates.\n");
1337 exit(1);
1338 }
1339 options.host_cert_files[options.num_host_cert_files++] =
1340 derelativise_path(optarg);
1341 break;
1292 case 'd': 1342 case 'd':
1293 if (debug_flag == 0) { 1343 if (debug_flag == 0) {
1294 debug_flag = 1; 1344 debug_flag = 1;
@@ -1536,6 +1586,46 @@ main(int ac, char **av)
1536 exit(1); 1586 exit(1);
1537 } 1587 }
1538 1588
1589 /*
1590 * Load certificates. They are stored in an array at identical
1591 * indices to the public keys that they relate to.
1592 */
1593 sensitive_data.host_certificates = xcalloc(options.num_host_key_files,
1594 sizeof(Key *));
1595 for (i = 0; i < options.num_host_key_files; i++)
1596 sensitive_data.host_certificates[i] = NULL;
1597
1598 for (i = 0; i < options.num_host_cert_files; i++) {
1599 key = key_load_public(options.host_cert_files[i], NULL);
1600 if (key == NULL) {
1601 error("Could not load host certificate: %s",
1602 options.host_cert_files[i]);
1603 continue;
1604 }
1605 if (!key_is_cert(key)) {
1606 error("Certificate file is not a certificate: %s",
1607 options.host_cert_files[i]);
1608 key_free(key);
1609 continue;
1610 }
1611 /* Find matching private key */
1612 for (j = 0; j < options.num_host_key_files; j++) {
1613 if (key_equal_public(key,
1614 sensitive_data.host_keys[j])) {
1615 sensitive_data.host_certificates[j] = key;
1616 break;
1617 }
1618 }
1619 if (j >= options.num_host_key_files) {
1620 error("No matching private key for certificate: %s",
1621 options.host_cert_files[i]);
1622 key_free(key);
1623 continue;
1624 }
1625 sensitive_data.host_certificates[j] = key;
1626 debug("host certificate: #%d type %d %s", j, key->type,
1627 key_type(key));
1628 }
1539 /* Check certain values for sanity. */ 1629 /* Check certain values for sanity. */
1540 if (options.protocol & SSH_PROTO_1) { 1630 if (options.protocol & SSH_PROTO_1) {
1541 if (options.server_key_bits < 512 || 1631 if (options.server_key_bits < 512 ||
@@ -2205,7 +2295,8 @@ do_ssh2_kex(void)
2205 kex->server = 1; 2295 kex->server = 1;
2206 kex->client_version_string=client_version_string; 2296 kex->client_version_string=client_version_string;
2207 kex->server_version_string=server_version_string; 2297 kex->server_version_string=server_version_string;
2208 kex->load_host_key=&get_hostkey_by_type; 2298 kex->load_host_public_key=&get_hostkey_public_by_type;
2299 kex->load_host_private_key=&get_hostkey_private_by_type;
2209 kex->host_key_index=&get_hostkey_index; 2300 kex->host_key_index=&get_hostkey_index;
2210 2301
2211 xxx_kex = kex; 2302 xxx_kex = kex;
diff --git a/sshd_config.5 b/sshd_config.5
index bf3319c4d..001114655 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.116 2010/01/09 23:04:13 dtucker Exp $ 37.\" $OpenBSD: sshd_config.5,v 1.117 2010/02/26 20:29:54 djm Exp $
38.Dd $Mdocdate: January 9 2010 $ 38.Dd $Mdocdate: February 26 2010 $
39.Dt SSHD_CONFIG 5 39.Dt SSHD_CONFIG 5
40.Os 40.Os
41.Sh NAME 41.Sh NAME
@@ -411,6 +411,14 @@ uses the name supplied by the client rather than
411attempting to resolve the name from the TCP connection itself. 411attempting to resolve the name from the TCP connection itself.
412The default is 412The default is
413.Dq no . 413.Dq no .
414.It Cm HostCertificate
415Specifies a file containing a public host certificate.
416The certificate's public key must match a private host key already specified
417by
418.Cm HostKey .
419The default behaviour of
420.Xr sshd 8
421is not to load any certificates.
414.It Cm HostKey 422.It Cm HostKey
415Specifies a file containing a private host key 423Specifies a file containing a private host key
416used by SSH. 424used by SSH.