Annotation of src/usr.bin/ssh/auth-rsa.c, Revision 1.9
1.1 provos 1: /*
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: */
17:
18: #include "includes.h"
1.9 ! markus 19: RCSID("$Id: auth-rsa.c,v 1.8 1999/11/11 23:36:52 markus Exp $");
1.1 provos 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"
1.8 markus 27: #include "servconf.h"
1.1 provos 28:
29: #include <ssl/rsa.h>
1.6 deraadt 30: #include <ssl/md5.h>
1.1 provos 31:
32: /* Flags that may be set in authorized_keys options. */
33: extern int no_port_forwarding_flag;
34: extern int no_agent_forwarding_flag;
35: extern int no_x11_forwarding_flag;
36: extern int no_pty_flag;
37: extern char *forced_command;
38: extern struct envstring *custom_environment;
39:
40: /* Session identifier that is used to bind key exchange and authentication
41: responses to a particular session. */
42: extern unsigned char session_id[16];
43:
44: /* The .ssh/authorized_keys file contains public keys, one per line, in the
45: following format:
46: options bits e n comment
47: where bits, e and n are decimal numbers,
48: and comment is any string of characters up to newline. The maximum
49: length of a line is 8000 characters. See the documentation for a
50: description of the options.
51: */
52:
53: /* Performs the RSA authentication challenge-response dialog with the client,
54: and returns true (non-zero) if the client gave the correct answer to
55: our challenge; returns zero if the client gives a wrong answer. */
56:
57: int
1.9 ! markus 58: auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n)
1.1 provos 59: {
60: BIGNUM *challenge, *encrypted_challenge, *aux;
61: RSA *pk;
62: BN_CTX *ctx = BN_CTX_new();
63: unsigned char buf[32], mdbuf[16], response[16];
1.3 deraadt 64: MD5_CTX md;
1.1 provos 65: unsigned int i;
66: int plen, len;
67:
68: encrypted_challenge = BN_new();
69: challenge = BN_new();
70: aux = BN_new();
71:
72: /* Generate a random challenge. */
73: BN_rand(challenge, 256, 0, 0);
74: BN_mod(challenge, challenge, n, ctx);
75:
76: /* Create the public key data structure. */
77: pk = RSA_new();
78: pk->e = BN_new();
79: BN_copy(pk->e, e);
80: pk->n = BN_new();
81: BN_copy(pk->n, n);
82:
83: /* Encrypt the challenge with the public key. */
84: rsa_public_encrypt(encrypted_challenge, challenge, pk);
85: RSA_free(pk);
86:
87: /* Send the encrypted challenge to the client. */
88: packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
89: packet_put_bignum(encrypted_challenge);
90: packet_send();
91: packet_write_wait();
92:
93: /* The response is MD5 of decrypted challenge plus session id. */
1.5 provos 94: len = BN_num_bytes(challenge);
1.7 markus 95: if (len <= 0 || len > 32)
96: fatal("auth_rsa_challenge_dialog: bad challenge length %d", len);
1.1 provos 97: memset(buf, 0, 32);
98: BN_bn2bin(challenge, buf + 32 - len);
1.6 deraadt 99: MD5_Init(&md);
100: MD5_Update(&md, buf, 32);
101: MD5_Update(&md, session_id, 16);
102: MD5_Final(mdbuf, &md);
1.1 provos 103:
104: /* We will no longer need these. */
105: BN_clear_free(encrypted_challenge);
106: BN_clear_free(challenge);
107: BN_clear_free(aux);
108: BN_CTX_free(ctx);
109:
110: /* Wait for a response. */
111: packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE);
112: packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE);
113: for (i = 0; i < 16; i++)
114: response[i] = packet_get_char();
115:
116: /* Verify that the response is the original challenge. */
117: if (memcmp(response, mdbuf, 16) != 0)
118: {
119: /* Wrong answer. */
120: return 0;
121: }
122:
123: /* Correct answer. */
124: return 1;
125: }
126:
127: /* Performs the RSA authentication dialog with the client. This returns
128: 0 if the client could not be authenticated, and 1 if authentication was
129: successful. This may exit if there is a serious protocol violation. */
130:
131: int
1.8 markus 132: auth_rsa(struct passwd *pw, BIGNUM *client_n)
1.1 provos 133: {
1.8 markus 134: extern ServerOptions options;
1.9 ! markus 135: char line[8192], file[1024];
1.1 provos 136: int authenticated;
137: unsigned int bits;
138: FILE *f;
139: unsigned long linenum = 0;
140: struct stat st;
141: BIGNUM *e, *n;
142:
1.4 markus 143: /* Temporarily use the user's uid. */
144: temporarily_use_uid(pw->pw_uid);
145:
146: /* The authorized keys. */
1.9 ! markus 147: snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
1.2 deraadt 148: SSH_USER_PERMITTED_KEYS);
1.1 provos 149:
1.4 markus 150: /* Fail quietly if file does not exist */
1.9 ! markus 151: if (stat(file, &st) < 0)
1.1 provos 152: {
153: /* Restore the privileged uid. */
154: restore_uid();
155: return 0;
156: }
1.4 markus 157:
158: /* Open the file containing the authorized keys. */
1.9 ! markus 159: f = fopen(file, "r");
1.1 provos 160: if (!f)
161: {
162: /* Restore the privileged uid. */
163: restore_uid();
1.9 ! markus 164: packet_send_debug("Could not open %.900s for reading.", file);
1.1 provos 165: packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
166: return 0;
167: }
1.4 markus 168:
1.8 markus 169: if (options.strict_modes) {
1.4 markus 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: "
1.9 ! markus 177: "bad ownership or modes for '%s'.", pw->pw_name, file);
1.4 markus 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: }
196: }
197: if (fail) {
198: log(buf);
199: packet_send_debug(buf);
200: restore_uid();
201: return 0;
202: }
203: }
1.1 provos 204:
205: /* Flag indicating whether authentication has succeeded. */
206: authenticated = 0;
207:
208: /* Initialize mp-int variables. */
209: e = BN_new();
210: n = BN_new();
211:
212: /* Go though the accepted keys, looking for the current key. If found,
213: perform a challenge-response dialog to verify that the user really has
214: the corresponding private key. */
215: while (fgets(line, sizeof(line), f))
216: {
217: char *cp;
218: char *options;
219:
220: linenum++;
221:
222: /* Skip leading whitespace. */
223: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
224: ;
225:
226: /* Skip empty and comment lines. */
227: if (!*cp || *cp == '\n' || *cp == '#')
228: continue;
229:
230: /* Check if there are options for this key, and if so, save their
231: starting address and skip the option part for now. If there are no
232: options, set the starting address to NULL. */
233: if (*cp < '0' || *cp > '9')
234: {
235: int quoted = 0;
236: options = cp;
237: for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++)
238: {
239: if (*cp == '\\' && cp[1] == '"')
240: cp++; /* Skip both */
241: else
242: if (*cp == '"')
243: quoted = !quoted;
244: }
245: }
246: else
247: options = NULL;
248:
249: /* Parse the key from the line. */
250: if (!auth_rsa_read_key(&cp, &bits, e, n))
251: {
252: debug("%.100s, line %lu: bad key syntax",
253: SSH_USER_PERMITTED_KEYS, linenum);
254: packet_send_debug("%.100s, line %lu: bad key syntax",
255: SSH_USER_PERMITTED_KEYS, linenum);
256: continue;
257: }
258: /* cp now points to the comment part. */
259:
1.9 ! markus 260: /* check the real bits */
! 261: if (bits != BN_num_bits(n))
! 262: error("Warning: error in %s, line %d: keysize mismatch: "
! 263: "actual size %d vs. announced %d.",
! 264: file, linenum, BN_num_bits(n), bits);
! 265:
1.1 provos 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. */
1.9 ! markus 274: if (!auth_rsa_challenge_dialog(e, n))
1.1 provos 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: }