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