Annotation of src/usr.bin/ssh/auth-rsa.c, Revision 1.19
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.19 ! markus 19: RCSID("$Id: auth-rsa.c,v 1.4 2000/02/11 11:12:40 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:
30: #include <ssl/rsa.h>
1.6 deraadt 31: #include <ssl/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) {
188: log(buf);
189: packet_send_debug(buf);
190: restore_uid();
191: return 0;
192: }
1.1 provos 193: }
1.12 markus 194: /* Flag indicating whether authentication has succeeded. */
195: authenticated = 0;
1.1 provos 196:
1.19 ! markus 197: pk = RSA_new();
! 198: pk->e = BN_new();
! 199: pk->n = BN_new();
1.12 markus 200:
1.14 markus 201: /*
202: * Go though the accepted keys, looking for the current key. If
203: * found, perform a challenge-response dialog to verify that the
204: * user really has the corresponding private key.
205: */
1.12 markus 206: while (fgets(line, sizeof(line), f)) {
207: char *cp;
208: char *options;
209:
210: linenum++;
211:
1.14 markus 212: /* Skip leading whitespace, empty and comment lines. */
213: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
214: ;
1.12 markus 215: if (!*cp || *cp == '\n' || *cp == '#')
216: continue;
217:
1.14 markus 218: /*
219: * Check if there are options for this key, and if so,
220: * save their starting address and skip the option part
221: * for now. If there are no options, set the starting
222: * address to NULL.
223: */
1.12 markus 224: if (*cp < '0' || *cp > '9') {
225: int quoted = 0;
226: options = cp;
227: for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
228: if (*cp == '\\' && cp[1] == '"')
229: cp++; /* Skip both */
230: else if (*cp == '"')
231: quoted = !quoted;
232: }
233: } else
234: options = NULL;
1.1 provos 235:
1.12 markus 236: /* Parse the key from the line. */
1.19 ! markus 237: if (!auth_rsa_read_key(&cp, &bits, pk->e, pk->n)) {
1.12 markus 238: debug("%.100s, line %lu: bad key syntax",
239: SSH_USER_PERMITTED_KEYS, linenum);
240: packet_send_debug("%.100s, line %lu: bad key syntax",
241: SSH_USER_PERMITTED_KEYS, linenum);
242: continue;
1.1 provos 243: }
1.12 markus 244: /* cp now points to the comment part. */
245:
1.16 markus 246: /* Check if the we have found the desired key (identified by its modulus). */
1.19 ! markus 247: if (BN_cmp(pk->n, client_n) != 0)
1.16 markus 248: continue;
249:
1.12 markus 250: /* check the real bits */
1.19 ! markus 251: if (bits != BN_num_bits(pk->n))
1.15 markus 252: log("Warning: %s, line %ld: keysize mismatch: "
253: "actual %d vs. announced %d.",
1.19 ! markus 254: file, linenum, BN_num_bits(pk->n), bits);
1.12 markus 255:
256: /* We have found the desired key. */
257:
1.19 ! markus 258:
1.12 markus 259: /* Perform the challenge-response dialog for this key. */
1.19 ! markus 260: if (!auth_rsa_challenge_dialog(pk)) {
1.12 markus 261: /* Wrong response. */
262: verbose("Wrong response to RSA authentication challenge.");
263: packet_send_debug("Wrong response to RSA authentication challenge.");
264: continue;
1.1 provos 265: }
1.14 markus 266: /*
267: * Correct response. The client has been successfully
268: * authenticated. Note that we have not yet processed the
269: * options; this will be reset if the options cause the
270: * authentication to be rejected.
271: */
1.12 markus 272: authenticated = 1;
273:
274: /* RSA part of authentication was accepted. Now process the options. */
275: if (options) {
276: while (*options && *options != ' ' && *options != '\t') {
277: cp = "no-port-forwarding";
278: if (strncmp(options, cp, strlen(cp)) == 0) {
279: packet_send_debug("Port forwarding disabled.");
280: no_port_forwarding_flag = 1;
281: options += strlen(cp);
282: goto next_option;
283: }
284: cp = "no-agent-forwarding";
285: if (strncmp(options, cp, strlen(cp)) == 0) {
286: packet_send_debug("Agent forwarding disabled.");
287: no_agent_forwarding_flag = 1;
288: options += strlen(cp);
289: goto next_option;
290: }
291: cp = "no-X11-forwarding";
292: if (strncmp(options, cp, strlen(cp)) == 0) {
293: packet_send_debug("X11 forwarding disabled.");
294: no_x11_forwarding_flag = 1;
295: options += strlen(cp);
296: goto next_option;
297: }
298: cp = "no-pty";
299: if (strncmp(options, cp, strlen(cp)) == 0) {
300: packet_send_debug("Pty allocation disabled.");
301: no_pty_flag = 1;
302: options += strlen(cp);
303: goto next_option;
304: }
305: cp = "command=\"";
306: if (strncmp(options, cp, strlen(cp)) == 0) {
307: int i;
308: options += strlen(cp);
309: forced_command = xmalloc(strlen(options) + 1);
310: i = 0;
311: while (*options) {
312: if (*options == '"')
313: break;
314: if (*options == '\\' && options[1] == '"') {
315: options += 2;
316: forced_command[i++] = '"';
317: continue;
318: }
319: forced_command[i++] = *options++;
320: }
321: if (!*options) {
322: debug("%.100s, line %lu: missing end quote",
323: SSH_USER_PERMITTED_KEYS, linenum);
324: packet_send_debug("%.100s, line %lu: missing end quote",
325: SSH_USER_PERMITTED_KEYS, linenum);
326: continue;
327: }
328: forced_command[i] = 0;
329: packet_send_debug("Forced command: %.900s", forced_command);
330: options++;
331: goto next_option;
332: }
333: cp = "environment=\"";
334: if (strncmp(options, cp, strlen(cp)) == 0) {
335: int i;
336: char *s;
337: struct envstring *new_envstring;
338: options += strlen(cp);
339: s = xmalloc(strlen(options) + 1);
340: i = 0;
341: while (*options) {
342: if (*options == '"')
343: break;
344: if (*options == '\\' && options[1] == '"') {
345: options += 2;
346: s[i++] = '"';
347: continue;
348: }
349: s[i++] = *options++;
350: }
351: if (!*options) {
352: debug("%.100s, line %lu: missing end quote",
353: SSH_USER_PERMITTED_KEYS, linenum);
354: packet_send_debug("%.100s, line %lu: missing end quote",
355: SSH_USER_PERMITTED_KEYS, linenum);
356: continue;
357: }
358: s[i] = 0;
359: packet_send_debug("Adding to environment: %.900s", s);
360: debug("Adding to environment: %.900s", s);
361: options++;
362: new_envstring = xmalloc(sizeof(struct envstring));
363: new_envstring->s = s;
364: new_envstring->next = custom_environment;
365: custom_environment = new_envstring;
366: goto next_option;
367: }
368: cp = "from=\"";
369: if (strncmp(options, cp, strlen(cp)) == 0) {
370: char *patterns = xmalloc(strlen(options) + 1);
371: int i;
372: options += strlen(cp);
373: i = 0;
374: while (*options) {
375: if (*options == '"')
376: break;
377: if (*options == '\\' && options[1] == '"') {
378: options += 2;
379: patterns[i++] = '"';
380: continue;
381: }
382: patterns[i++] = *options++;
383: }
384: if (!*options) {
385: debug("%.100s, line %lu: missing end quote",
386: SSH_USER_PERMITTED_KEYS, linenum);
387: packet_send_debug("%.100s, line %lu: missing end quote",
388: SSH_USER_PERMITTED_KEYS, linenum);
389: continue;
390: }
391: patterns[i] = 0;
392: options++;
393: if (!match_hostname(get_canonical_hostname(), patterns,
394: strlen(patterns)) &&
395: !match_hostname(get_remote_ipaddr(), patterns,
396: strlen(patterns))) {
397: log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
398: pw->pw_name, get_canonical_hostname(),
399: get_remote_ipaddr());
400: packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
401: get_canonical_hostname());
402: xfree(patterns);
1.17 markus 403: /* key invalid for this host, reset flags */
1.12 markus 404: authenticated = 0;
1.17 markus 405: no_agent_forwarding_flag = 0;
406: no_port_forwarding_flag = 0;
407: no_pty_flag = 0;
408: no_x11_forwarding_flag = 0;
409: while (custom_environment) {
410: struct envstring *ce = custom_environment;
411: custom_environment = ce->next;
412: xfree(ce->s);
413: xfree(ce);
414: }
415: if (forced_command) {
416: xfree(forced_command);
417: forced_command = NULL;
418: }
1.12 markus 419: break;
420: }
421: xfree(patterns);
422: /* Host name matches. */
423: goto next_option;
424: }
425: bad_option:
426: log("Bad options in %.100s file, line %lu: %.50s",
427: SSH_USER_PERMITTED_KEYS, linenum, options);
428: packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
429: SSH_USER_PERMITTED_KEYS, linenum, options);
430: authenticated = 0;
431: break;
432:
433: next_option:
1.14 markus 434: /*
435: * Skip the comma, and move to the next option
436: * (or break out if there are no more).
437: */
1.12 markus 438: if (!*options)
439: fatal("Bugs in auth-rsa.c option processing.");
440: if (*options == ' ' || *options == '\t')
1.14 markus 441: break; /* End of options. */
1.12 markus 442: if (*options != ',')
443: goto bad_option;
444: options++;
445: /* Process the next option. */
446: continue;
1.1 provos 447: }
448: }
1.14 markus 449: /*
450: * Break out of the loop if authentication was successful;
451: * otherwise continue searching.
452: */
1.12 markus 453: if (authenticated)
1.1 provos 454: break;
455: }
456:
1.12 markus 457: /* Restore the privileged uid. */
458: restore_uid();
459:
460: /* Close the file. */
461: fclose(f);
462:
1.19 ! markus 463: RSA_free(pk);
1.1 provos 464:
1.12 markus 465: if (authenticated)
466: packet_send_debug("RSA authentication accepted.");
1.1 provos 467:
1.12 markus 468: /* Return authentication result. */
469: return authenticated;
1.1 provos 470: }