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