1 | /* |
---|
2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
---|
3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
---|
4 | * All rights reserved |
---|
5 | * RSA-based authentication. This code determines whether to admit a login |
---|
6 | * based on RSA authentication. This file also contains functions to check |
---|
7 | * validity of the host key. |
---|
8 | * |
---|
9 | * As far as I am concerned, the code I have written for this software |
---|
10 | * can be used freely for any purpose. Any derived versions of this |
---|
11 | * software must be clearly marked as such, and if the derived work is |
---|
12 | * incompatible with the protocol description in the RFC file, it must be |
---|
13 | * called by a name other than "ssh" or "Secure Shell". |
---|
14 | */ |
---|
15 | |
---|
16 | #include "includes.h" |
---|
17 | RCSID("$OpenBSD: auth-rsa.c,v 1.56 2002/06/10 16:53:06 stevesk Exp $"); |
---|
18 | |
---|
19 | #include <openssl/rsa.h> |
---|
20 | #include <openssl/md5.h> |
---|
21 | |
---|
22 | #include "rsa.h" |
---|
23 | #include "packet.h" |
---|
24 | #include "xmalloc.h" |
---|
25 | #include "ssh1.h" |
---|
26 | #include "mpaux.h" |
---|
27 | #include "uidswap.h" |
---|
28 | #include "match.h" |
---|
29 | #include "auth-options.h" |
---|
30 | #include "pathnames.h" |
---|
31 | #include "log.h" |
---|
32 | #include "servconf.h" |
---|
33 | #include "auth.h" |
---|
34 | #include "hostfile.h" |
---|
35 | #include "monitor_wrap.h" |
---|
36 | #include "ssh.h" |
---|
37 | |
---|
38 | /* import */ |
---|
39 | extern ServerOptions options; |
---|
40 | |
---|
41 | /* |
---|
42 | * Session identifier that is used to bind key exchange and authentication |
---|
43 | * responses to a particular session. |
---|
44 | */ |
---|
45 | extern u_char session_id[16]; |
---|
46 | |
---|
47 | /* |
---|
48 | * The .ssh/authorized_keys file contains public keys, one per line, in the |
---|
49 | * following format: |
---|
50 | * options bits e n comment |
---|
51 | * where bits, e and n are decimal numbers, |
---|
52 | * and comment is any string of characters up to newline. The maximum |
---|
53 | * length of a line is 8000 characters. See the documentation for a |
---|
54 | * description of the options. |
---|
55 | */ |
---|
56 | |
---|
57 | BIGNUM * |
---|
58 | auth_rsa_generate_challenge(Key *key) |
---|
59 | { |
---|
60 | BIGNUM *challenge; |
---|
61 | BN_CTX *ctx; |
---|
62 | |
---|
63 | if ((challenge = BN_new()) == NULL) |
---|
64 | fatal("auth_rsa_generate_challenge: BN_new() failed"); |
---|
65 | /* Generate a random challenge. */ |
---|
66 | BN_rand(challenge, 256, 0, 0); |
---|
67 | if ((ctx = BN_CTX_new()) == NULL) |
---|
68 | fatal("auth_rsa_generate_challenge: BN_CTX_new() failed"); |
---|
69 | BN_mod(challenge, challenge, key->rsa->n, ctx); |
---|
70 | BN_CTX_free(ctx); |
---|
71 | |
---|
72 | return challenge; |
---|
73 | } |
---|
74 | |
---|
75 | int |
---|
76 | auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) |
---|
77 | { |
---|
78 | u_char buf[32], mdbuf[16]; |
---|
79 | MD5_CTX md; |
---|
80 | int len; |
---|
81 | |
---|
82 | /* don't allow short keys */ |
---|
83 | if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { |
---|
84 | error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits", |
---|
85 | BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); |
---|
86 | return (0); |
---|
87 | } |
---|
88 | |
---|
89 | /* The response is MD5 of decrypted challenge plus session id. */ |
---|
90 | len = BN_num_bytes(challenge); |
---|
91 | if (len <= 0 || len > 32) |
---|
92 | fatal("auth_rsa_verify_response: bad challenge length %d", len); |
---|
93 | memset(buf, 0, 32); |
---|
94 | BN_bn2bin(challenge, buf + 32 - len); |
---|
95 | MD5_Init(&md); |
---|
96 | MD5_Update(&md, buf, 32); |
---|
97 | MD5_Update(&md, session_id, 16); |
---|
98 | MD5_Final(mdbuf, &md); |
---|
99 | |
---|
100 | /* Verify that the response is the original challenge. */ |
---|
101 | if (memcmp(response, mdbuf, 16) != 0) { |
---|
102 | /* Wrong answer. */ |
---|
103 | return (0); |
---|
104 | } |
---|
105 | /* Correct answer. */ |
---|
106 | return (1); |
---|
107 | } |
---|
108 | |
---|
109 | /* |
---|
110 | * Performs the RSA authentication challenge-response dialog with the client, |
---|
111 | * and returns true (non-zero) if the client gave the correct answer to |
---|
112 | * our challenge; returns zero if the client gives a wrong answer. |
---|
113 | */ |
---|
114 | |
---|
115 | int |
---|
116 | auth_rsa_challenge_dialog(Key *key) |
---|
117 | { |
---|
118 | BIGNUM *challenge, *encrypted_challenge; |
---|
119 | u_char response[16]; |
---|
120 | int i, success; |
---|
121 | |
---|
122 | if ((encrypted_challenge = BN_new()) == NULL) |
---|
123 | fatal("auth_rsa_challenge_dialog: BN_new() failed"); |
---|
124 | |
---|
125 | challenge = PRIVSEP(auth_rsa_generate_challenge(key)); |
---|
126 | |
---|
127 | /* Encrypt the challenge with the public key. */ |
---|
128 | rsa_public_encrypt(encrypted_challenge, challenge, key->rsa); |
---|
129 | |
---|
130 | /* Send the encrypted challenge to the client. */ |
---|
131 | packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); |
---|
132 | packet_put_bignum(encrypted_challenge); |
---|
133 | packet_send(); |
---|
134 | BN_clear_free(encrypted_challenge); |
---|
135 | packet_write_wait(); |
---|
136 | |
---|
137 | /* Wait for a response. */ |
---|
138 | packet_read_expect(SSH_CMSG_AUTH_RSA_RESPONSE); |
---|
139 | for (i = 0; i < 16; i++) |
---|
140 | response[i] = packet_get_char(); |
---|
141 | packet_check_eom(); |
---|
142 | |
---|
143 | success = PRIVSEP(auth_rsa_verify_response(key, challenge, response)); |
---|
144 | BN_clear_free(challenge); |
---|
145 | return (success); |
---|
146 | } |
---|
147 | |
---|
148 | /* |
---|
149 | * check if there's user key matching client_n, |
---|
150 | * return key if login is allowed, NULL otherwise |
---|
151 | */ |
---|
152 | |
---|
153 | int |
---|
154 | auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) |
---|
155 | { |
---|
156 | char line[8192], *file; |
---|
157 | int allowed = 0; |
---|
158 | u_int bits; |
---|
159 | FILE *f; |
---|
160 | u_long linenum = 0; |
---|
161 | struct stat st; |
---|
162 | Key *key; |
---|
163 | |
---|
164 | /* Temporarily use the user's uid. */ |
---|
165 | temporarily_use_uid(pw); |
---|
166 | |
---|
167 | /* The authorized keys. */ |
---|
168 | file = authorized_keys_file(pw); |
---|
169 | debug("trying public RSA key file %s", file); |
---|
170 | |
---|
171 | /* Fail quietly if file does not exist */ |
---|
172 | if (stat(file, &st) < 0) { |
---|
173 | /* Restore the privileged uid. */ |
---|
174 | restore_uid(); |
---|
175 | xfree(file); |
---|
176 | return (0); |
---|
177 | } |
---|
178 | /* Open the file containing the authorized keys. */ |
---|
179 | f = fopen(file, "r"); |
---|
180 | if (!f) { |
---|
181 | /* Restore the privileged uid. */ |
---|
182 | restore_uid(); |
---|
183 | xfree(file); |
---|
184 | return (0); |
---|
185 | } |
---|
186 | if (options.strict_modes && |
---|
187 | secure_filename(f, file, pw, line, sizeof(line)) != 0) { |
---|
188 | xfree(file); |
---|
189 | fclose(f); |
---|
190 | log("Authentication refused: %s", line); |
---|
191 | restore_uid(); |
---|
192 | return (0); |
---|
193 | } |
---|
194 | |
---|
195 | /* Flag indicating whether the key is allowed. */ |
---|
196 | allowed = 0; |
---|
197 | |
---|
198 | key = key_new(KEY_RSA1); |
---|
199 | |
---|
200 | /* |
---|
201 | * Go though the accepted keys, looking for the current key. If |
---|
202 | * found, perform a challenge-response dialog to verify that the |
---|
203 | * user really has the corresponding private key. |
---|
204 | */ |
---|
205 | while (fgets(line, sizeof(line), f)) { |
---|
206 | char *cp; |
---|
207 | char *options; |
---|
208 | |
---|
209 | linenum++; |
---|
210 | |
---|
211 | /* Skip leading whitespace, empty and comment lines. */ |
---|
212 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) |
---|
213 | ; |
---|
214 | if (!*cp || *cp == '\n' || *cp == '#') |
---|
215 | continue; |
---|
216 | |
---|
217 | /* |
---|
218 | * Check if there are options for this key, and if so, |
---|
219 | * save their starting address and skip the option part |
---|
220 | * for now. If there are no options, set the starting |
---|
221 | * address to NULL. |
---|
222 | */ |
---|
223 | if (*cp < '0' || *cp > '9') { |
---|
224 | int quoted = 0; |
---|
225 | options = cp; |
---|
226 | for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { |
---|
227 | if (*cp == '\\' && cp[1] == '"') |
---|
228 | cp++; /* Skip both */ |
---|
229 | else if (*cp == '"') |
---|
230 | quoted = !quoted; |
---|
231 | } |
---|
232 | } else |
---|
233 | options = NULL; |
---|
234 | |
---|
235 | /* Parse the key from the line. */ |
---|
236 | if (hostfile_read_key(&cp, &bits, key) == 0) { |
---|
237 | debug("%.100s, line %lu: non ssh1 key syntax", |
---|
238 | file, linenum); |
---|
239 | continue; |
---|
240 | } |
---|
241 | /* cp now points to the comment part. */ |
---|
242 | |
---|
243 | /* Check if the we have found the desired key (identified by its modulus). */ |
---|
244 | if (BN_cmp(key->rsa->n, client_n) != 0) |
---|
245 | continue; |
---|
246 | |
---|
247 | /* check the real bits */ |
---|
248 | if (bits != BN_num_bits(key->rsa->n)) |
---|
249 | log("Warning: %s, line %lu: keysize mismatch: " |
---|
250 | "actual %d vs. announced %d.", |
---|
251 | file, linenum, BN_num_bits(key->rsa->n), bits); |
---|
252 | |
---|
253 | /* We have found the desired key. */ |
---|
254 | /* |
---|
255 | * If our options do not allow this key to be used, |
---|
256 | * do not send challenge. |
---|
257 | */ |
---|
258 | if (!auth_parse_options(pw, options, file, linenum)) |
---|
259 | continue; |
---|
260 | |
---|
261 | /* break out, this key is allowed */ |
---|
262 | allowed = 1; |
---|
263 | break; |
---|
264 | } |
---|
265 | |
---|
266 | /* Restore the privileged uid. */ |
---|
267 | restore_uid(); |
---|
268 | |
---|
269 | /* Close the file. */ |
---|
270 | xfree(file); |
---|
271 | fclose(f); |
---|
272 | |
---|
273 | /* return key if allowed */ |
---|
274 | if (allowed && rkey != NULL) |
---|
275 | *rkey = key; |
---|
276 | else |
---|
277 | key_free(key); |
---|
278 | return (allowed); |
---|
279 | } |
---|
280 | |
---|
281 | /* |
---|
282 | * Performs the RSA authentication dialog with the client. This returns |
---|
283 | * 0 if the client could not be authenticated, and 1 if authentication was |
---|
284 | * successful. This may exit if there is a serious protocol violation. |
---|
285 | */ |
---|
286 | int |
---|
287 | auth_rsa(struct passwd *pw, BIGNUM *client_n) |
---|
288 | { |
---|
289 | Key *key; |
---|
290 | char *fp; |
---|
291 | |
---|
292 | /* no user given */ |
---|
293 | if (pw == NULL) |
---|
294 | return 0; |
---|
295 | |
---|
296 | if (!PRIVSEP(auth_rsa_key_allowed(pw, client_n, &key))) { |
---|
297 | auth_clear_options(); |
---|
298 | return (0); |
---|
299 | } |
---|
300 | |
---|
301 | /* Perform the challenge-response dialog for this key. */ |
---|
302 | if (!auth_rsa_challenge_dialog(key)) { |
---|
303 | /* Wrong response. */ |
---|
304 | verbose("Wrong response to RSA authentication challenge."); |
---|
305 | packet_send_debug("Wrong response to RSA authentication challenge."); |
---|
306 | /* |
---|
307 | * Break out of the loop. Otherwise we might send |
---|
308 | * another challenge and break the protocol. |
---|
309 | */ |
---|
310 | key_free(key); |
---|
311 | return (0); |
---|
312 | } |
---|
313 | /* |
---|
314 | * Correct response. The client has been successfully |
---|
315 | * authenticated. Note that we have not yet processed the |
---|
316 | * options; this will be reset if the options cause the |
---|
317 | * authentication to be rejected. |
---|
318 | */ |
---|
319 | fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); |
---|
320 | verbose("Found matching %s key: %s", |
---|
321 | key_type(key), fp); |
---|
322 | xfree(fp); |
---|
323 | key_free(key); |
---|
324 | |
---|
325 | packet_send_debug("RSA authentication accepted."); |
---|
326 | return (1); |
---|
327 | } |
---|