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