]> andersk Git - openssh.git/blame - auth-rsa.c
*** empty log message ***
[openssh.git] / auth-rsa.c
CommitLineData
8efc0c15 1/*
5260325f 2 *
3 * auth-rsa.c
4 *
5 * Author: Tatu Ylonen <ylo@cs.hut.fi>
6 *
7 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
8 * All rights reserved
9 *
10 * Created: Mon Mar 27 01:46:52 1995 ylo
11 *
12 * RSA-based authentication. This code determines whether to admit a login
13 * based on RSA authentication. This file also contains functions to check
14 * validity of the host key.
15 *
16 */
8efc0c15 17
18#include "includes.h"
19RCSID("$Id$");
20
21#include "rsa.h"
22#include "packet.h"
23#include "xmalloc.h"
24#include "ssh.h"
25#include "mpaux.h"
26#include "uidswap.h"
6fa724bc 27#include "servconf.h"
8efc0c15 28
5881cd60 29#ifdef HAVE_OPENSSL
8efc0c15 30#include <openssl/rsa.h>
31#include <openssl/md5.h>
5881cd60 32#endif
33#ifdef HAVE_SSL
34#include <ssl/rsa.h>
35#include <ssl/md5.h>
36#endif
8efc0c15 37
38/* Flags that may be set in authorized_keys options. */
39extern int no_port_forwarding_flag;
40extern int no_agent_forwarding_flag;
41extern int no_x11_forwarding_flag;
42extern int no_pty_flag;
43extern char *forced_command;
44extern struct envstring *custom_environment;
45
46/* Session identifier that is used to bind key exchange and authentication
47 responses to a particular session. */
48extern unsigned char session_id[16];
49
50/* The .ssh/authorized_keys file contains public keys, one per line, in the
51 following format:
52 options bits e n comment
5260325f 53 where bits, e and n are decimal numbers,
8efc0c15 54 and comment is any string of characters up to newline. The maximum
55 length of a line is 8000 characters. See the documentation for a
56 description of the options.
57*/
58
59/* Performs the RSA authentication challenge-response dialog with the client,
60 and returns true (non-zero) if the client gave the correct answer to
61 our challenge; returns zero if the client gives a wrong answer. */
62
63int
4d195447 64auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n)
8efc0c15 65{
5260325f 66 BIGNUM *challenge, *encrypted_challenge, *aux;
67 RSA *pk;
68 BN_CTX *ctx = BN_CTX_new();
69 unsigned char buf[32], mdbuf[16], response[16];
70 MD5_CTX md;
71 unsigned int i;
72 int plen, len;
73
74 encrypted_challenge = BN_new();
75 challenge = BN_new();
76 aux = BN_new();
77
78 /* Generate a random challenge. */
79 BN_rand(challenge, 256, 0, 0);
80 BN_mod(challenge, challenge, n, ctx);
81
82 /* Create the public key data structure. */
83 pk = RSA_new();
84 pk->e = BN_new();
85 BN_copy(pk->e, e);
86 pk->n = BN_new();
87 BN_copy(pk->n, n);
88
89 /* Encrypt the challenge with the public key. */
90 rsa_public_encrypt(encrypted_challenge, challenge, pk);
91 RSA_free(pk);
92
93 /* Send the encrypted challenge to the client. */
94 packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
95 packet_put_bignum(encrypted_challenge);
96 packet_send();
97 packet_write_wait();
98
99 /* The response is MD5 of decrypted challenge plus session id. */
100 len = BN_num_bytes(challenge);
101 if (len <= 0 || len > 32)
102 fatal("auth_rsa_challenge_dialog: bad challenge length %d", len);
103 memset(buf, 0, 32);
104 BN_bn2bin(challenge, buf + 32 - len);
105 MD5_Init(&md);
106 MD5_Update(&md, buf, 32);
107 MD5_Update(&md, session_id, 16);
108 MD5_Final(mdbuf, &md);
109
110 /* We will no longer need these. */
111 BN_clear_free(encrypted_challenge);
112 BN_clear_free(challenge);
113 BN_clear_free(aux);
114 BN_CTX_free(ctx);
115
116 /* Wait for a response. */
117 packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE);
118 packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE);
119 for (i = 0; i < 16; i++)
120 response[i] = packet_get_char();
121
122 /* Verify that the response is the original challenge. */
123 if (memcmp(response, mdbuf, 16) != 0) {
124 /* Wrong answer. */
125 return 0;
126 }
127 /* Correct answer. */
128 return 1;
8efc0c15 129}
130
131/* Performs the RSA authentication dialog with the client. This returns
132 0 if the client could not be authenticated, and 1 if authentication was
133 successful. This may exit if there is a serious protocol violation. */
134
135int
6fa724bc 136auth_rsa(struct passwd *pw, BIGNUM *client_n)
8efc0c15 137{
5260325f 138 extern ServerOptions options;
139 char line[8192], file[1024];
140 int authenticated;
141 unsigned int bits;
142 FILE *f;
143 unsigned long linenum = 0;
144 struct stat st;
145 BIGNUM *e, *n;
146
147 /* Temporarily use the user's uid. */
148 temporarily_use_uid(pw->pw_uid);
149
150 /* The authorized keys. */
151 snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
152 SSH_USER_PERMITTED_KEYS);
153
154 /* Fail quietly if file does not exist */
155 if (stat(file, &st) < 0) {
156 /* Restore the privileged uid. */
157 restore_uid();
158 return 0;
8efc0c15 159 }
5260325f 160 /* Open the file containing the authorized keys. */
161 f = fopen(file, "r");
162 if (!f) {
163 /* Restore the privileged uid. */
164 restore_uid();
165 packet_send_debug("Could not open %.900s for reading.", file);
166 packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
167 return 0;
8efc0c15 168 }
5260325f 169 if (options.strict_modes) {
170 int fail = 0;
171 char buf[1024];
172 /* Check open file in order to avoid open/stat races */
173 if (fstat(fileno(f), &st) < 0 ||
174 (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
175 (st.st_mode & 022) != 0) {
176 snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: "
177 "bad ownership or modes for '%s'.", pw->pw_name, file);
178 fail = 1;
179 } else {
180 /* Check path to SSH_USER_PERMITTED_KEYS */
181 int i;
182 static const char *check[] = {
183 "", SSH_USER_DIR, NULL
184 };
185 for (i = 0; check[i]; i++) {
186 snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]);
187 if (stat(line, &st) < 0 ||
188 (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
189 (st.st_mode & 022) != 0) {
190 snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: "
191 "bad ownership or modes for '%s'.", pw->pw_name, line);
192 fail = 1;
193 break;
194 }
195 }
8efc0c15 196 }
5260325f 197 if (fail) {
198 log(buf);
199 packet_send_debug(buf);
200 restore_uid();
201 return 0;
8efc0c15 202 }
5260325f 203 }
204 /* Flag indicating whether authentication has succeeded. */
205 authenticated = 0;
206
207 /* Initialize mp-int variables. */
208 e = BN_new();
209 n = BN_new();
210
211 /* Go though the accepted keys, looking for the current key. If
212 found, perform a challenge-response dialog to verify that the
213 user really has the corresponding private key. */
214 while (fgets(line, sizeof(line), f)) {
215 char *cp;
216 char *options;
217
218 linenum++;
219
220 /* Skip leading whitespace. */
221 for (cp = line; *cp == ' ' || *cp == '\t'; cp++);
222
223 /* Skip empty and comment lines. */
224 if (!*cp || *cp == '\n' || *cp == '#')
225 continue;
226
227 /* Check if there are options for this key, and if so,
228 save their starting address and skip the option part
229 for now. If there are no options, set the starting
230 address to NULL. */
231 if (*cp < '0' || *cp > '9') {
232 int quoted = 0;
233 options = cp;
234 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
235 if (*cp == '\\' && cp[1] == '"')
236 cp++; /* Skip both */
237 else if (*cp == '"')
238 quoted = !quoted;
8efc0c15 239 }
5260325f 240 } else
241 options = NULL;
242
243 /* Parse the key from the line. */
244 if (!auth_rsa_read_key(&cp, &bits, e, n)) {
245 debug("%.100s, line %lu: bad key syntax",
246 SSH_USER_PERMITTED_KEYS, linenum);
247 packet_send_debug("%.100s, line %lu: bad key syntax",
248 SSH_USER_PERMITTED_KEYS, linenum);
249 continue;
8efc0c15 250 }
5260325f 251 /* cp now points to the comment part. */
252
253 /* check the real bits */
254 if (bits != BN_num_bits(n))
255 error("Warning: error in %s, line %ld: keysize mismatch: "
256 "actual size %d vs. announced %d.",
257 file, linenum, BN_num_bits(n), bits);
258
259 /* Check if the we have found the desired key (identified by its modulus). */
260 if (BN_cmp(n, client_n) != 0)
261 continue; /* Wrong key. */
262
263 /* We have found the desired key. */
264
265 /* Perform the challenge-response dialog for this key. */
266 if (!auth_rsa_challenge_dialog(e, n)) {
267 /* Wrong response. */
268 verbose("Wrong response to RSA authentication challenge.");
269 packet_send_debug("Wrong response to RSA authentication challenge.");
270 continue;
8efc0c15 271 }
5260325f 272 /* Correct response. The client has been successfully
273 authenticated. Note that we have not yet processed the
274 options; this will be reset if the options cause the
275 authentication to be rejected. */
276 authenticated = 1;
277
278 /* RSA part of authentication was accepted. Now process the options. */
279 if (options) {
280 while (*options && *options != ' ' && *options != '\t') {
281 cp = "no-port-forwarding";
282 if (strncmp(options, cp, strlen(cp)) == 0) {
283 packet_send_debug("Port forwarding disabled.");
284 no_port_forwarding_flag = 1;
285 options += strlen(cp);
286 goto next_option;
287 }
288 cp = "no-agent-forwarding";
289 if (strncmp(options, cp, strlen(cp)) == 0) {
290 packet_send_debug("Agent forwarding disabled.");
291 no_agent_forwarding_flag = 1;
292 options += strlen(cp);
293 goto next_option;
294 }
295 cp = "no-X11-forwarding";
296 if (strncmp(options, cp, strlen(cp)) == 0) {
297 packet_send_debug("X11 forwarding disabled.");
298 no_x11_forwarding_flag = 1;
299 options += strlen(cp);
300 goto next_option;
301 }
302 cp = "no-pty";
303 if (strncmp(options, cp, strlen(cp)) == 0) {
304 packet_send_debug("Pty allocation disabled.");
305 no_pty_flag = 1;
306 options += strlen(cp);
307 goto next_option;
308 }
309 cp = "command=\"";
310 if (strncmp(options, cp, strlen(cp)) == 0) {
311 int i;
312 options += strlen(cp);
313 forced_command = xmalloc(strlen(options) + 1);
314 i = 0;
315 while (*options) {
316 if (*options == '"')
317 break;
318 if (*options == '\\' && options[1] == '"') {
319 options += 2;
320 forced_command[i++] = '"';
321 continue;
322 }
323 forced_command[i++] = *options++;
324 }
325 if (!*options) {
326 debug("%.100s, line %lu: missing end quote",
327 SSH_USER_PERMITTED_KEYS, linenum);
328 packet_send_debug("%.100s, line %lu: missing end quote",
329 SSH_USER_PERMITTED_KEYS, linenum);
330 continue;
331 }
332 forced_command[i] = 0;
333 packet_send_debug("Forced command: %.900s", forced_command);
334 options++;
335 goto next_option;
336 }
337 cp = "environment=\"";
338 if (strncmp(options, cp, strlen(cp)) == 0) {
339 int i;
340 char *s;
341 struct envstring *new_envstring;
342 options += strlen(cp);
343 s = xmalloc(strlen(options) + 1);
344 i = 0;
345 while (*options) {
346 if (*options == '"')
347 break;
348 if (*options == '\\' && options[1] == '"') {
349 options += 2;
350 s[i++] = '"';
351 continue;
352 }
353 s[i++] = *options++;
354 }
355 if (!*options) {
356 debug("%.100s, line %lu: missing end quote",
357 SSH_USER_PERMITTED_KEYS, linenum);
358 packet_send_debug("%.100s, line %lu: missing end quote",
359 SSH_USER_PERMITTED_KEYS, linenum);
360 continue;
361 }
362 s[i] = 0;
363 packet_send_debug("Adding to environment: %.900s", s);
364 debug("Adding to environment: %.900s", s);
365 options++;
366 new_envstring = xmalloc(sizeof(struct envstring));
367 new_envstring->s = s;
368 new_envstring->next = custom_environment;
369 custom_environment = new_envstring;
370 goto next_option;
371 }
372 cp = "from=\"";
373 if (strncmp(options, cp, strlen(cp)) == 0) {
374 char *patterns = xmalloc(strlen(options) + 1);
375 int i;
376 options += strlen(cp);
377 i = 0;
378 while (*options) {
379 if (*options == '"')
380 break;
381 if (*options == '\\' && options[1] == '"') {
382 options += 2;
383 patterns[i++] = '"';
384 continue;
385 }
386 patterns[i++] = *options++;
387 }
388 if (!*options) {
389 debug("%.100s, line %lu: missing end quote",
390 SSH_USER_PERMITTED_KEYS, linenum);
391 packet_send_debug("%.100s, line %lu: missing end quote",
392 SSH_USER_PERMITTED_KEYS, linenum);
393 continue;
394 }
395 patterns[i] = 0;
396 options++;
397 if (!match_hostname(get_canonical_hostname(), patterns,
398 strlen(patterns)) &&
399 !match_hostname(get_remote_ipaddr(), patterns,
400 strlen(patterns))) {
401 log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
402 pw->pw_name, get_canonical_hostname(),
403 get_remote_ipaddr());
404 packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
405 get_canonical_hostname());
406 xfree(patterns);
407 authenticated = 0;
408 break;
409 }
410 xfree(patterns);
411 /* Host name matches. */
412 goto next_option;
413 }
414 bad_option:
415 /* Unknown option. */
416 log("Bad options in %.100s file, line %lu: %.50s",
417 SSH_USER_PERMITTED_KEYS, linenum, options);
418 packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
419 SSH_USER_PERMITTED_KEYS, linenum, options);
420 authenticated = 0;
421 break;
422
423 next_option:
424 /* Skip the comma, and move to the next option
425 (or break out if there are no more). */
426 if (!*options)
427 fatal("Bugs in auth-rsa.c option processing.");
428 if (*options == ' ' || *options == '\t')
429 break; /* End of options. */
430 if (*options != ',')
431 goto bad_option;
432 options++;
433 /* Process the next option. */
434 continue;
8efc0c15 435 }
8efc0c15 436 }
5260325f 437 /* Break out of the loop if authentication was successful;
438 otherwise continue searching. */
439 if (authenticated)
440 break;
8efc0c15 441 }
442
5260325f 443 /* Restore the privileged uid. */
444 restore_uid();
8efc0c15 445
5260325f 446 /* Close the file. */
447 fclose(f);
8efc0c15 448
5260325f 449 /* Clear any mp-int variables. */
450 BN_clear_free(n);
451 BN_clear_free(e);
8efc0c15 452
5260325f 453 if (authenticated)
454 packet_send_debug("RSA authentication accepted.");
8efc0c15 455
5260325f 456 /* Return authentication result. */
457 return authenticated;
8efc0c15 458}
This page took 1.093246 seconds and 5 git commands to generate.