diff options
-rw-r--r-- | ChangeLog | 33 | ||||
-rw-r--r-- | PROTOCOL | 23 | ||||
-rw-r--r-- | PROTOCOL.agent | 24 | ||||
-rw-r--r-- | PROTOCOL.certkeys | 191 | ||||
-rw-r--r-- | addrmatch.c | 78 | ||||
-rw-r--r-- | auth-options.c | 150 | ||||
-rw-r--r-- | auth-options.h | 4 | ||||
-rw-r--r-- | auth.h | 5 | ||||
-rw-r--r-- | auth2-pubkey.c | 37 | ||||
-rw-r--r-- | authfd.c | 24 | ||||
-rw-r--r-- | dns.c | 8 | ||||
-rw-r--r-- | dns.h | 6 | ||||
-rw-r--r-- | hostfile.c | 31 | ||||
-rw-r--r-- | hostfile.h | 4 | ||||
-rw-r--r-- | kex.h | 5 | ||||
-rw-r--r-- | kexdhs.c | 19 | ||||
-rw-r--r-- | kexgexs.c | 20 | ||||
-rw-r--r-- | key.c | 595 | ||||
-rw-r--r-- | key.h | 32 | ||||
-rw-r--r-- | match.h | 4 | ||||
-rw-r--r-- | monitor.c | 5 | ||||
-rw-r--r-- | myproposal.h | 6 | ||||
-rw-r--r-- | servconf.c | 19 | ||||
-rw-r--r-- | servconf.h | 5 | ||||
-rw-r--r-- | ssh-add.c | 34 | ||||
-rw-r--r-- | ssh-agent.c | 24 | ||||
-rw-r--r-- | ssh-dss.c | 10 | ||||
-rw-r--r-- | ssh-keygen.1 | 178 | ||||
-rw-r--r-- | ssh-keygen.c | 433 | ||||
-rw-r--r-- | ssh-rsa.c | 10 | ||||
-rw-r--r-- | ssh.1 | 23 | ||||
-rw-r--r-- | ssh.c | 71 | ||||
-rw-r--r-- | ssh2.h | 5 | ||||
-rw-r--r-- | sshconnect.c | 78 | ||||
-rw-r--r-- | sshconnect2.c | 4 | ||||
-rw-r--r-- | sshd.8 | 25 | ||||
-rw-r--r-- | sshd.c | 117 | ||||
-rw-r--r-- | sshd_config.5 | 12 |
38 files changed, 2164 insertions, 188 deletions
@@ -1,3 +1,36 @@ | |||
1 | 20100226 | ||
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 | |||
1 | 20100224 | 34 | 20100224 |
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 |
@@ -31,7 +31,14 @@ The method is documented in: | |||
31 | 31 | ||
32 | http://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt | 32 | http://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt |
33 | 33 | ||
34 | 3. connection: Channel write close extension "eow@openssh.com" | 34 | 3. transport: New public key algorithms "ssh-rsa-cert-v00@openssh.com" and |
35 | "ssh-dsa-cert-v00@openssh.com" | ||
36 | |||
37 | OpenSSH introduces two new public key algorithms to support certificate | ||
38 | authentication for users and hostkeys. These methods are documented in | ||
39 | the file PROTOCOL.certkeys | ||
40 | |||
41 | 4. connection: Channel write close extension "eow@openssh.com" | ||
35 | 42 | ||
36 | The SSH connection protocol (rfc4254) provides the SSH_MSG_CHANNEL_EOF | 43 | The SSH connection protocol (rfc4254) provides the SSH_MSG_CHANNEL_EOF |
37 | message to allow an endpoint to signal its peer that it will send no | 44 | message 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). | |||
70 | Other SSH implementations may be whitelisted to receive this message | 77 | Other SSH implementations may be whitelisted to receive this message |
71 | upon request. | 78 | upon request. |
72 | 79 | ||
73 | 4. connection: disallow additional sessions extension | 80 | 5. connection: disallow additional sessions extension |
74 | "no-more-sessions@openssh.com" | 81 | "no-more-sessions@openssh.com" |
75 | 82 | ||
76 | Most SSH connections will only ever request a single session, but a | 83 | Most 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 | |||
98 | servers (identified by banner). Other SSH implementations may be | 105 | servers (identified by banner). Other SSH implementations may be |
99 | whitelisted to receive this message upon request. | 106 | whitelisted to receive this message upon request. |
100 | 107 | ||
101 | 5. connection: Tunnel forward extension "tun@openssh.com" | 108 | 6. connection: Tunnel forward extension "tun@openssh.com" |
102 | 109 | ||
103 | OpenSSH supports layer 2 and layer 3 tunnelling via the "tun@openssh.com" | 110 | OpenSSH supports layer 2 and layer 3 tunnelling via the "tun@openssh.com" |
104 | channel type. This channel type supports forwarding of network packets | 111 | channel type. This channel type supports forwarding of network packets |
@@ -159,7 +166,7 @@ The contents of the "data" field for layer 2 packets is: | |||
159 | The "frame" field contains an IEEE 802.3 Ethernet frame, including | 166 | The "frame" field contains an IEEE 802.3 Ethernet frame, including |
160 | header. | 167 | header. |
161 | 168 | ||
162 | 6. sftp: Reversal of arguments to SSH_FXP_SYMLINK | 169 | 7. sftp: Reversal of arguments to SSH_FXP_SYMLINK |
163 | 170 | ||
164 | When OpenSSH's sftp-server was implemented, the order of the arguments | 171 | When OpenSSH's sftp-server was implemented, the order of the arguments |
165 | to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately, | 172 | to 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 | ||
175 | 7. sftp: Server extension announcement in SSH_FXP_VERSION | 182 | 8. sftp: Server extension announcement in SSH_FXP_VERSION |
176 | 183 | ||
177 | OpenSSH's sftp-server lists the extensions it supports using the | 184 | OpenSSH's sftp-server lists the extensions it supports using the |
178 | standard extension announcement mechanism in the SSH_FXP_VERSION server | 185 | standard 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 | |||
193 | extension with multiple versions (though this is unlikely). Clients MUST | 200 | extension with multiple versions (though this is unlikely). Clients MUST |
194 | check the version number before attempting to use the extension. | 201 | check the version number before attempting to use the extension. |
195 | 202 | ||
196 | 8. sftp: Extension request "posix-rename@openssh.com" | 203 | 9. sftp: Extension request "posix-rename@openssh.com" |
197 | 204 | ||
198 | This operation provides a rename operation with POSIX semantics, which | 205 | This operation provides a rename operation with POSIX semantics, which |
199 | are different to those provided by the standard SSH_FXP_RENAME in | 206 | are 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. | |||
210 | This extension is advertised in the SSH_FXP_VERSION hello with version | 217 | This extension is advertised in the SSH_FXP_VERSION hello with version |
211 | "1". | 218 | "1". |
212 | 219 | ||
213 | 9. sftp: Extension requests "statvfs@openssh.com" and | 220 | 10. sftp: Extension requests "statvfs@openssh.com" and |
214 | "fstatvfs@openssh.com" | 221 | "fstatvfs@openssh.com" |
215 | 222 | ||
216 | These requests correspond to the statvfs and fstatvfs POSIX system | 223 | These requests correspond to the statvfs and fstatvfs POSIX system |
@@ -251,4 +258,4 @@ The values of the f_flag bitmask are as follows: | |||
251 | Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are | 258 | Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are |
252 | advertised in the SSH_FXP_VERSION hello with version "2". | 259 | advertised 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 | ||
176 | DSA 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 | |||
176 | RSA keys may be added with this request: | 185 | RSA 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 | ||
199 | RSA 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 | |||
190 | Note that the 'rsa_p' and 'rsa_q' parameters are sent in the reverse | 212 | Note that the 'rsa_p' and 'rsa_q' parameters are sent in the reverse |
191 | order to the protocol 1 add keys message. As with the corresponding | 213 | order to the protocol 1 add keys message. As with the corresponding |
192 | protocol 1 "add key" request, the private key is overspecified to avoid | 214 | protocol 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 @@ | |||
1 | This document describes a simple public-key certificate authentication | ||
2 | system for use by SSH. | ||
3 | |||
4 | Background | ||
5 | ---------- | ||
6 | |||
7 | The SSH protocol currently supports a simple public key authentication | ||
8 | mechanism. Unlike other public key implementations, SSH eschews the | ||
9 | use of X.509 certificates and uses raw keys. This approach has some | ||
10 | benefits relating to simplicity of configuration and minimisation | ||
11 | of attack surface, but it does not support the important use-cases | ||
12 | of centrally managed, passwordless authentication and centrally | ||
13 | certified host keys. | ||
14 | |||
15 | These protocol extensions build on the simple public key authentication | ||
16 | system already in SSH to allow certificate-based authentication. | ||
17 | The certificates used are not traditional X.509 certificates, with | ||
18 | numerous options and complex encoding rules, but something rather | ||
19 | more minimal: a key, some identity information and usage constraints | ||
20 | that have been signed with some other trusted key. | ||
21 | |||
22 | A sshd server may be configured to allow authentication via certified | ||
23 | keys, by extending the existing ~/.ssh/authorized_keys mechanism | ||
24 | to allow specification of certification authority keys in addition | ||
25 | to raw user keys. The ssh client will support automatic verification | ||
26 | of acceptance of certified host keys, by adding a similar ability | ||
27 | to specify CA keys in ~/.ssh/known_hosts. | ||
28 | |||
29 | Certified keys are represented using two new key types: | ||
30 | ssh-rsa-cert-v00@openssh.com and ssh-dss-cert-v00@openssh.com that | ||
31 | include certification information along with the public key that is used | ||
32 | to sign challenges. ssh-keygen performs the CA signing operation. | ||
33 | |||
34 | Protocol extensions | ||
35 | ------------------- | ||
36 | |||
37 | The SSH wire protocol includes several extensibility mechanisms. | ||
38 | These modifications shall take advantage of namespaced public key | ||
39 | algorithm names to add support for certificate authentication without | ||
40 | breaking the protocol - implementations that do not support the | ||
41 | extensions will simply ignore them. | ||
42 | |||
43 | Authentication using the new key formats described below proceeds | ||
44 | using the existing SSH "publickey" authentication method described | ||
45 | in RFC4252 section 7. | ||
46 | |||
47 | New public key formats | ||
48 | ---------------------- | ||
49 | |||
50 | The ssh-rsa-cert-v00@openssh.com and ssh-dss-cert-v00@openssh.com key | ||
51 | types take a similar same high-level format (note: data types and | ||
52 | encoding are as per RFC4251 section 5). The serialised wire encoding of | ||
53 | these 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 | |||
58 | RSA 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 | |||
74 | DSA 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 | |||
92 | e and n are the RSA exponent and public modulus respectively. | ||
93 | |||
94 | p, q, g, y are the DSA parameters as described in FIPS-186-2. | ||
95 | |||
96 | type specifies whether this certificate is for identification of a user | ||
97 | or a host using a SSH_CERT_TYPE_... value. | ||
98 | |||
99 | key id is a free-form text field that is filled in by the CA at the time | ||
100 | of signing; the intention is that the contents of this field are used to | ||
101 | identify the identity principal in log messages. | ||
102 | |||
103 | "valid principals" is a string containing zero or more principals as | ||
104 | strings packed inside it. These principals list the names for which this | ||
105 | certificate is valid; hostnames for SSH_CERT_TYPE_HOST certificates and | ||
106 | usernames for SSH_CERT_TYPE_USER certificates. As a special case, a | ||
107 | zero-length "valid principals" field means the certificate is valid for | ||
108 | any principal of the specified type. XXX DNS wildcards? | ||
109 | |||
110 | "valid after" and "valid before" specify a validity period for the | ||
111 | certificate. Each represents a time in seconds since 1970-01-01 | ||
112 | 00:00:00. A certificate is considered valid if: | ||
113 | valid after <= current time < valid before | ||
114 | |||
115 | constraints is a set of zero or more key constraints encoded as below. | ||
116 | |||
117 | The 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 | ||
119 | inducing collisions in the signature hash infeasible. | ||
120 | |||
121 | The reserved field is current unused and is ignored in this version of | ||
122 | the protocol. | ||
123 | |||
124 | signature key contains the CA key used to sign the certificate. | ||
125 | The valid key types for CA keys are ssh-rsa and ssh-dss. "Chained" | ||
126 | certificates, where the signature key type is a certificate type itself | ||
127 | are NOT supported. Note that it is possible for a RSA certificate key to | ||
128 | be signed by a DSS CA key and vice-versa. | ||
129 | |||
130 | signature is computed over all preceding fields from the initial string | ||
131 | up to, and including the signature key. Signatures are computed and | ||
132 | encoded according to the rules defined for the CA's public key algorithm | ||
133 | (RFC4253 section 6.6 for ssh-rsa and ssh-dss). | ||
134 | |||
135 | Constraints | ||
136 | ----------- | ||
137 | |||
138 | The constraints section of the certificate specifies zero or more | ||
139 | constraints on the certificates validity. The format of this field | ||
140 | is a sequence of zero or more tuples: | ||
141 | |||
142 | string name | ||
143 | string data | ||
144 | |||
145 | The name field identifies the constraint and the data field encodes | ||
146 | constraint-specific information (see below). All constraints are | ||
147 | "critical", if an implementation does not recognise a constraint | ||
148 | then the validating party should refuse to accept the certificate. | ||
149 | |||
150 | The supported constraints and the contents and structure of their | ||
151 | data fields are: | ||
152 | |||
153 | Name Format Description | ||
154 | ----------------------------------------------------------------------------- | ||
155 | force-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 | |||
160 | permit-X11-forwarding empty Flag indicating that X11 forwarding | ||
161 | should be permitted. X11 forwarding will | ||
162 | be refused if this constraint is absent. | ||
163 | |||
164 | permit-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 | |||
169 | permit-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 | |||
174 | permit-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 | |||
179 | permit-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 | |||
184 | source-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 | */ | ||
437 | int | ||
438 | addr_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; | |||
44 | int no_x11_forwarding_flag = 0; | 44 | int no_x11_forwarding_flag = 0; |
45 | int no_pty_flag = 0; | 45 | int no_pty_flag = 0; |
46 | int no_user_rc = 0; | 46 | int no_user_rc = 0; |
47 | int key_is_cert_authority = 0; | ||
47 | 48 | ||
48 | /* "command=" option. */ | 49 | /* "command=" option. */ |
49 | char *forced_command = NULL; | 50 | char *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 | */ | ||
390 | int | ||
391 | auth_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; | |||
30 | extern char *forced_command; | 30 | extern char *forced_command; |
31 | extern struct envstring *custom_environment; | 31 | extern struct envstring *custom_environment; |
32 | extern int forced_tun_device; | 32 | extern int forced_tun_device; |
33 | extern int key_is_cert_authority; | ||
33 | 34 | ||
34 | int auth_parse_options(struct passwd *, char *, char *, u_long); | 35 | int auth_parse_options(struct passwd *, char *, char *, u_long); |
35 | void auth_clear_options(void); | 36 | void auth_clear_options(void); |
37 | int auth_cert_constraints(Buffer *, struct passwd *); | ||
36 | 38 | ||
37 | #endif | 39 | #endif |
@@ -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 */ |
180 | Key *get_hostkey_by_index(int); | 180 | Key *get_hostkey_by_index(int); |
181 | Key *get_hostkey_by_type(int); | 181 | Key *get_hostkey_public_by_type(int); |
182 | Key *get_hostkey_private_by_type(int); | ||
182 | int get_hostkey_index(Key *); | 183 | int get_hostkey_index(Key *); |
183 | int ssh1_session_key(BIGNUM *); | 184 | int 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 | |||
178 | user_key_allowed2(struct passwd *pw, Key *key, char *file) | 180 | user_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); |
@@ -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); |
@@ -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 | */ |
76 | static int | 76 | static int |
77 | dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, | 77 | dns_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 | */ |
173 | int | 173 | int |
174 | verify_host_key_dns(const char *hostname, struct sockaddr *address, | 174 | verify_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 | */ |
273 | int | 273 | int |
274 | export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic) | 274 | export_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; |
@@ -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 | ||
49 | int verify_host_key_dns(const char *, struct sockaddr *, const Key *, int *); | 49 | int verify_host_key_dns(const char *, struct sockaddr *, Key *, int *); |
50 | int export_dns_rr(const char *, const Key *, FILE *, int); | 50 | int 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 | |||
31 | char *host_hash(const char *, const char *, u_int); | 33 | char *host_hash(const char *, const char *, u_int); |
32 | 34 | ||
33 | #endif | 35 | #endif |
@@ -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 | }; |
@@ -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 | ||
@@ -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 | |||
52 | kexgex_server(Kex *kex) | 52 | kexgex_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 | ||
@@ -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 | |||
57 | static struct KeyCert * | ||
58 | cert_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 | ||
56 | Key * | 71 | Key * |
57 | key_new(int type) | 72 | key_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 | ||
99 | Key * | 121 | void |
100 | key_new_private(int type) | 122 | key_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 | |||
153 | Key * | ||
154 | key_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 | ||
162 | static void | ||
163 | cert_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 | |||
131 | void | 179 | void |
132 | key_free(Key *k) | 180 | key_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 | ||
213 | static int | ||
214 | cert_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 | */ | ||
157 | int | 232 | int |
158 | key_equal(const Key *a, const Key *b) | 233 | key_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 | ||
259 | int | ||
260 | key_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 | |||
180 | u_char* | 271 | u_char* |
181 | key_fingerprint_raw(const Key *k, enum fp_type dgst_type, | 272 | key_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 | ||
410 | char * | 508 | char * |
411 | key_fingerprint(const Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep) | 509 | key_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 | ||
882 | void | ||
883 | key_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 | |||
733 | Key * | 924 | Key * |
734 | key_from_private(const Key *k) | 925 | key_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 | ||
999 | static int | ||
1000 | cert_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 | |||
800 | Key * | 1110 | Key * |
801 | key_from_blob(const u_char *blob, u_int blen) | 1111 | key_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 | |||
1321 | int | ||
1322 | key_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 */ | ||
1329 | int | ||
1330 | key_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 */ | ||
1343 | int | ||
1344 | key_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 */ | ||
1362 | int | ||
1363 | key_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 */ | ||
1381 | int | ||
1382 | key_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 | |||
1464 | int | ||
1465 | key_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 | } | ||
@@ -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 | }; |
39 | enum fp_type { | 42 | enum 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 | ||
56 | struct 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 | |||
52 | struct Key { | 67 | struct 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 | ||
59 | Key *key_new(int); | 75 | Key *key_new(int); |
76 | void key_add_private(Key *); | ||
60 | Key *key_new_private(int); | 77 | Key *key_new_private(int); |
61 | void key_free(Key *); | 78 | void key_free(Key *); |
62 | Key *key_demote(const Key *); | 79 | Key *key_demote(const Key *); |
80 | int key_equal_public(const Key *, const Key *); | ||
63 | int key_equal(const Key *, const Key *); | 81 | int key_equal(const Key *, const Key *); |
64 | char *key_fingerprint(const Key *, enum fp_type, enum fp_rep); | 82 | char *key_fingerprint(Key *, enum fp_type, enum fp_rep); |
65 | u_char *key_fingerprint_raw(const Key *, enum fp_type, u_int *); | 83 | u_char *key_fingerprint_raw(Key *, enum fp_type, u_int *); |
66 | const char *key_type(const Key *); | 84 | const char *key_type(const Key *); |
67 | int key_write(const Key *, FILE *); | 85 | int key_write(const Key *, FILE *); |
68 | int key_read(Key *, char **); | 86 | int key_read(Key *, char **); |
@@ -71,6 +89,14 @@ u_int key_size(const Key *); | |||
71 | Key *key_generate(int, u_int); | 89 | Key *key_generate(int, u_int); |
72 | Key *key_from_private(const Key *); | 90 | Key *key_from_private(const Key *); |
73 | int key_type_from_name(char *); | 91 | int key_type_from_name(char *); |
92 | int key_is_cert(const Key *); | ||
93 | int key_type_plain(int); | ||
94 | int key_to_certified(Key *); | ||
95 | int key_drop_cert(Key *); | ||
96 | int key_certify(Key *, Key *); | ||
97 | void key_cert_copy(const Key *, struct Key *); | ||
98 | int key_cert_check_authority(const Key *, int, int, const char *, | ||
99 | const char **); | ||
74 | 100 | ||
75 | Key *key_from_blob(const u_char *, u_int); | 101 | Key *key_from_blob(const u_char *, u_int); |
76 | int key_to_blob(const Key *, u_char **, u_int *); | 102 | int key_to_blob(const Key *, u_char **, u_int *); |
@@ -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 */ |
25 | int addr_match_list(const char *, const char *); | 25 | int addr_match_list(const char *, const char *); |
26 | 26 | int addr_match_cidr_list(const char *, const char *); | |
27 | #endif | 27 | #endif |
@@ -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 |
@@ -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) | |||
138 | static int | 138 | static int |
139 | add_file(AuthenticationConnection *ac, const char *filename) | 139 | add_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"); |
@@ -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 |
111 | generates, manages and converts authentication keys for | 119 | generates, manages and converts authentication keys for |
@@ -245,6 +253,17 @@ but they do not reveal identifying information should the file's contents | |||
245 | be disclosed. | 253 | be disclosed. |
246 | This option will not modify existing hashed hostnames and is therefore safe | 254 | This option will not modify existing hashed hostnames and is therefore safe |
247 | to use on files that mix hashed and non-hashed names. | 255 | to use on files that mix hashed and non-hashed names. |
256 | .It Fl h | ||
257 | When signing a key, create a host certificate instead of a user | ||
258 | certificate. | ||
259 | Please see the | ||
260 | .Sx CERTIFICATES | ||
261 | section for details. | ||
262 | .It Fl I | ||
263 | Specify the key identity when signing a public key. | ||
264 | Please see the | ||
265 | .Sx CERTIFICATES | ||
266 | section for details. | ||
248 | .It Fl i | 267 | .It Fl i |
249 | This option will read an unencrypted private (or public) key file | 268 | This option will read an unencrypted private (or public) key file |
250 | in SSH2-compatible format and print an OpenSSH compatible private | 269 | in SSH2-compatible format and print an OpenSSH compatible private |
@@ -268,6 +287,67 @@ Specify the amount of memory to use (in megabytes) when generating | |||
268 | candidate moduli for DH-GEX. | 287 | candidate moduli for DH-GEX. |
269 | .It Fl N Ar new_passphrase | 288 | .It Fl N Ar new_passphrase |
270 | Provides the new passphrase. | 289 | Provides the new passphrase. |
290 | .It Fl n Ar principals | ||
291 | Specify one or more principals (user or host names) to be included in | ||
292 | a certificate when signing a key. | ||
293 | Multiple principals may be specified, separated by commas. | ||
294 | Please see the | ||
295 | .Sx CERTIFICATES | ||
296 | section for details. | ||
297 | .It Fl O Ar constraint | ||
298 | Specify a certificate constraint when signing a key. | ||
299 | This option may be specified multiple times. | ||
300 | Please see the | ||
301 | .Sx CERTIFICATES | ||
302 | section for details. | ||
303 | The constraints that are valid for user certificates are: | ||
304 | .Bl -tag -width Ds | ||
305 | .It Ic no-x11-forwarding | ||
306 | Disable X11 forwarding. (permitted by default) | ||
307 | .It Ic no-agent-forwarding | ||
308 | Disable | ||
309 | .Xr ssh-agent 1 | ||
310 | forwarding. (permitted by default) | ||
311 | .It Ic no-port-forwarding | ||
312 | Disable port forwarding. (permitted by default) | ||
313 | .It Ic no-pty | ||
314 | Disable PTY allocation. (permitted by default) | ||
315 | .It Ic no-user-rc | ||
316 | Disable execution of | ||
317 | .Pa ~/.ssh/rc | ||
318 | by | ||
319 | .Xr sshd 8 . | ||
320 | (permitted by default) | ||
321 | .It Ic clear | ||
322 | Clear all enabled permissions. | ||
323 | This is useful for clearing the default set of permissions so permissions may | ||
324 | be added individually. | ||
325 | .It Ic permit-x11-forwarding | ||
326 | Allows X11 forwarding. | ||
327 | .It Ic permit-port-forwarding | ||
328 | Allows port forwarding. | ||
329 | .It Ic permit-pty | ||
330 | Allows PTY allocation. | ||
331 | .It Ic permit-user-rc | ||
332 | Allows execution of | ||
333 | .Pa ~/.ssh/rc | ||
334 | by | ||
335 | .Xr sshd 8 . | ||
336 | .It Ic force-command=command | ||
337 | Forces the execution of | ||
338 | .Ar command | ||
339 | instead of any shell or command specified by the user when | ||
340 | the certificate is used for authentication. | ||
341 | .It Ic source-address=address_list | ||
342 | Restrict the source addresses from which the certificate is considered valid | ||
343 | from. | ||
344 | The | ||
345 | .Ar address_list | ||
346 | is a comma-separated list of one or more address/netmask pairs in CIDR | ||
347 | format. | ||
348 | .El | ||
349 | .Pp | ||
350 | At present, no constraints are valid for host keys. | ||
271 | .It Fl P Ar passphrase | 351 | .It Fl P Ar passphrase |
272 | Provides the (old) passphrase. | 352 | Provides the (old) passphrase. |
273 | .It Fl p | 353 | .It Fl p |
@@ -297,6 +377,11 @@ Print the SSHFP fingerprint resource record named | |||
297 | for the specified public key file. | 377 | for the specified public key file. |
298 | .It Fl S Ar start | 378 | .It Fl S Ar start |
299 | Specify start point (in hex) when generating candidate moduli for DH-GEX. | 379 | Specify start point (in hex) when generating candidate moduli for DH-GEX. |
380 | .It Fl s Ar ca_key | ||
381 | Certify (sign) a public key using the specified CA key. | ||
382 | Please see the | ||
383 | .Sx CERTIFICATES | ||
384 | section for details. | ||
300 | .It Fl T Ar output_file | 385 | .It Fl T Ar output_file |
301 | Test DH group exchange candidate primes (generated using the | 386 | Test DH group exchange candidate primes (generated using the |
302 | .Fl G | 387 | .Fl G |
@@ -310,6 +395,29 @@ for protocol version 1 and | |||
310 | or | 395 | or |
311 | .Dq dsa | 396 | .Dq dsa |
312 | for protocol version 2. | 397 | for protocol version 2. |
398 | .It Fl V Ar validity_interval | ||
399 | Specify a validity interval when signing a certificate. | ||
400 | A validity interval may consist of a single time, indicating that the | ||
401 | certificate is valid beginning now and expiring at that time, or may consist | ||
402 | of two times separated by a colon to indicate an explicit time interval. | ||
403 | The start time may be specified as a date in YYYYMMDD format, a time | ||
404 | in YYYYMMDDHHMMSS format or a relative time (to the current time) consisting | ||
405 | of a minus sign followed by a relative time in the format described in the | ||
406 | .Sx TIME FORMATS | ||
407 | section of | ||
408 | .Xr ssh_config 5 . | ||
409 | The end time may be specified as a YYYYMMDD date, a YYYYMMDDHHMMSS time or | ||
410 | a relative time starting with a plus character. | ||
411 | .Pp | ||
412 | For 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 |
314 | Verbose mode. | 422 | Verbose mode. |
315 | Causes | 423 | Causes |
@@ -380,6 +488,72 @@ Screened DH groups may be installed in | |||
380 | .Pa /etc/moduli . | 488 | .Pa /etc/moduli . |
381 | It is important that this file contains moduli of a range of bit lengths and | 489 | It is important that this file contains moduli of a range of bit lengths and |
382 | that both ends of a connection share common moduli. | 490 | that both ends of a connection share common moduli. |
491 | .Sh CERTIFICATES | ||
492 | .Nm | ||
493 | supports signing of keys to produce certificates that may be used for | ||
494 | user or host authentication. | ||
495 | Certificates consist of a public key, some identity information, zero or | ||
496 | more principal (user or host) names and an optional set of constraints that | ||
497 | are signed by a Certification Authority (CA) key. | ||
498 | Clients or servers may then trust only the CA key and verify its signature | ||
499 | on a certificate rather than trusting many user/host keys. | ||
500 | Note that OpenSSH certificates are a different, and much simpler, format to | ||
501 | the X.509 certificates used in | ||
502 | .Xr ssl 8 . | ||
503 | .Pp | ||
504 | .Nm | ||
505 | supports two types of certificates: user and host. | ||
506 | User certificates authenticate users to servers, whereas host certificates | ||
507 | authenticate 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 | ||
511 | The resultant certificate will be placed in | ||
512 | .Pa /path/to/user_key_cert.pub . | ||
513 | A host certificate requires the | ||
514 | .Fl h | ||
515 | option: | ||
516 | .Pp | ||
517 | .Dl $ ssh-keygen -s /path/to/ca_key -I key_id -h /path/to/host_key.pub | ||
518 | .Pp | ||
519 | The host certificate will be output to | ||
520 | .Pa /path/to/host_key_cert.pub . | ||
521 | In both cases, | ||
522 | .Ar key_id | ||
523 | is a "key identifier" that is logged by the server when the certificate | ||
524 | is used for authentication. | ||
525 | .Pp | ||
526 | Certificates may be limited to be valid for a set of principal (user/host) | ||
527 | names. | ||
528 | By default, generated certificates are valid for all users or hosts. | ||
529 | To 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 | ||
534 | Additional limitations on the validity and use of user certificates may | ||
535 | be specified through certificate constraints. | ||
536 | A constrained certificate may disable features of the SSH session, may be | ||
537 | valid only when presented from particular source addresses or may | ||
538 | force the use of a specific command. | ||
539 | For a list of valid certificate constraints, see the documentation for the | ||
540 | .Fl O | ||
541 | option above. | ||
542 | .Pp | ||
543 | Finally, certificates may be defined with a validity lifetime. | ||
544 | The | ||
545 | .Fl V | ||
546 | option allows specification of certificate start and end times. | ||
547 | A certificate that is presented at a time outside this range will not be | ||
548 | considered valid. | ||
549 | By default, certificates have a maximum validity interval. | ||
550 | .Pp | ||
551 | For certificates to be used for user or host authentication, the CA | ||
552 | public key must be trusted by | ||
553 | .Xr sshd 8 | ||
554 | or | ||
555 | .Xr ssh 1 . | ||
556 | Please 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. */ |
99 | char *identity_comment = NULL; | 100 | char *identity_comment = NULL; |
100 | 101 | ||
102 | /* Path to CA key when certifying keys. */ | ||
103 | char *ca_key_path = NULL; | ||
104 | |||
105 | /* Key type when certifying */ | ||
106 | u_int cert_key_type = SSH2_CERT_TYPE_USER; | ||
107 | |||
108 | /* "key ID" of signed key */ | ||
109 | char *cert_key_id = NULL; | ||
110 | |||
111 | /* Comma-separated list of principal names for certifying keys */ | ||
112 | char *cert_principals = NULL; | ||
113 | |||
114 | /* Validity period for certificates */ | ||
115 | u_int64_t cert_valid_from = 0; | ||
116 | u_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) | ||
127 | u_int32_t constraint_flags = CONSTRAINT_DEFAULT; | ||
128 | char *constraint_command = NULL; | ||
129 | char *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 */ |
102 | int convert_to_ssh2 = 0; | 132 | int convert_to_ssh2 = 0; |
103 | int convert_from_ssh2 = 0; | 133 | int convert_from_ssh2 = 0; |
@@ -591,7 +621,7 @@ do_fingerprint(struct passwd *pw) | |||
591 | } | 621 | } |
592 | 622 | ||
593 | static void | 623 | static void |
594 | print_host(FILE *f, const char *name, Key *public, int hash) | 624 | printhost(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 | |||
622 | do_known_hosts(struct passwd *pw, const char *name) | 652 | do_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 | ||
1065 | static const char * | ||
1066 | fmt_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 | |||
1104 | static void | ||
1105 | add_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 | |||
1112 | static void | ||
1113 | add_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 | |||
1127 | static void | ||
1128 | prepare_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 | |||
1148 | static void | ||
1149 | do_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 | |||
1227 | static u_int64_t | ||
1228 | parse_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 | |||
1241 | static u_int64_t | ||
1242 | parse_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 | |||
1259 | static void | ||
1260 | parse_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 | |||
1305 | static void | ||
1306 | add_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 | |||
1015 | static void | 1352 | static void |
1016 | usage(void) | 1353 | usage(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) |
@@ -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 | } |
@@ -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 | |||
1103 | option in | 1103 | option in |
1104 | .Xr ssh_config 5 | 1104 | .Xr ssh_config 5 |
1105 | for more information. | 1105 | for more information. |
1106 | .Pp | ||
1107 | Host keys may also be presented as certificates signed by a trusted | ||
1108 | certification authority (CA). | ||
1109 | In this case, trust of the CA key alone is sufficient for the host key | ||
1110 | to be accepted. | ||
1111 | To specify a public key as a trusted CA key in a known hosts file, | ||
1112 | it should be added after a | ||
1113 | .Dq @cert-authority | ||
1114 | tag and a set of one or more domain-name wildcards separated by commas. | ||
1115 | For example: | ||
1116 | .Pp | ||
1117 | .Dl @cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W... | ||
1118 | .Pp | ||
1119 | See the | ||
1120 | .Sx CERTIFICATES | ||
1121 | section of | ||
1122 | .Xr ssh-keygen 1 | ||
1123 | for more details. | ||
1124 | .Pp | ||
1106 | .Sh SSH-BASED VIRTUAL PRIVATE NETWORKS | 1125 | .Sh SSH-BASED VIRTUAL PRIVATE NETWORKS |
1107 | .Nm | 1126 | .Nm |
1108 | contains support for Virtual Private Network (VPN) tunnelling | 1127 | contains support for Virtual Private Network (VPN) tunnelling |
@@ -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)); |
@@ -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 | ||
63 | char *client_version_string = NULL; | 64 | char *client_version_string = NULL; |
@@ -576,6 +577,23 @@ confirm(const char *prompt) | |||
576 | } | 577 | } |
577 | } | 578 | } |
578 | 579 | ||
580 | static int | ||
581 | check_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 | ||
972 | fail: | 1009 | fail: |
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; |
@@ -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 |
102 | Specifies the number of bits in the ephemeral protocol version 1 | 103 | Specifies the number of bits in the ephemeral protocol version 1 |
103 | server key (default 1024). | 104 | server key (default 1024). |
105 | .It Fl c Ar host_certificate_file | ||
106 | Specifies a path to a certificate file to identify | ||
107 | .Nm | ||
108 | during key exchange. | ||
109 | The certificate file must match a host key file specified using the | ||
110 | .Fl -h | ||
111 | option or the | ||
112 | .Cm HostKey | ||
113 | configuration directive. | ||
104 | .It Fl C Ar connection_spec | 114 | .It Fl C Ar connection_spec |
105 | Specify the connection parameters to use for the | 115 | Specify 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. | |||
498 | The following option specifications are supported (note | 508 | The following option specifications are supported (note |
499 | that option keywords are case-insensitive): | 509 | that option keywords are case-insensitive): |
500 | .Bl -tag -width Ds | 510 | .Bl -tag -width Ds |
511 | .It Cm from="cert-authority" | ||
512 | Specifies that the listed key is a certification authority (CA) that is | ||
513 | trusted to validate signed certificates for user authentication. | ||
514 | .Pp | ||
515 | Certificates may encode access restrictions similar to these key options. | ||
516 | If both certificate restrictions and key options are present, the most | ||
517 | restrictive union of the two is applied. | ||
501 | .It Cm command="command" | 518 | .It Cm command="command" |
502 | Specifies that the command is executed whenever this key is used for | 519 | Specifies that the command is executed whenever this key is used for |
503 | authentication. | 520 | authentication. |
@@ -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 |
518 | environment variable. | 535 | environment variable. |
519 | Note that this option applies to shell, command or subsystem execution. | 536 | Note that this option applies to shell, command or subsystem execution. |
537 | Also note that this command may be superseded by either a | ||
538 | .Xr sshd_config 5 | ||
539 | .Cm ForceCommand | ||
540 | directive or a command embedded in a certificate. | ||
520 | .It Cm environment="NAME=value" | 541 | .It Cm environment="NAME=value" |
521 | Specifies that the string is to be added to the environment when | 542 | Specifies that the string is to be added to the environment when |
522 | logging in using this key. | 543 | logging in using this key. |
@@ -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 | ||
743 | Key * | 763 | static Key * |
744 | get_hostkey_by_type(int type) | 764 | get_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 | ||
756 | Key * | 781 | Key * |
782 | get_hostkey_public_by_type(int type) | ||
783 | { | ||
784 | return get_hostkey_by_type(type, 0); | ||
785 | } | ||
786 | |||
787 | Key * | ||
788 | get_hostkey_private_by_type(int type) | ||
789 | { | ||
790 | return get_hostkey_by_type(type, 1); | ||
791 | } | ||
792 | |||
793 | Key * | ||
757 | get_hostkey_by_index(int ind) | 794 | get_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 | |||
411 | attempting to resolve the name from the TCP connection itself. | 411 | attempting to resolve the name from the TCP connection itself. |
412 | The default is | 412 | The default is |
413 | .Dq no . | 413 | .Dq no . |
414 | .It Cm HostCertificate | ||
415 | Specifies a file containing a public host certificate. | ||
416 | The certificate's public key must match a private host key already specified | ||
417 | by | ||
418 | .Cm HostKey . | ||
419 | The default behaviour of | ||
420 | .Xr sshd 8 | ||
421 | is not to load any certificates. | ||
414 | .It Cm HostKey | 422 | .It Cm HostKey |
415 | Specifies a file containing a private host key | 423 | Specifies a file containing a private host key |
416 | used by SSH. | 424 | used by SSH. |