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