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