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