Annotation of src/usr.bin/ssh/auth-rsa.c, Revision 1.7
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.7 ! markus 19: RCSID("$Id: auth-rsa.c,v 1.6 1999/10/27 16:37:45 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"
27:
28: #include <ssl/rsa.h>
1.6 deraadt 29: #include <ssl/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.7 ! markus 94: if (len <= 0 || len > 32)
! 95: fatal("auth_rsa_challenge_dialog: bad challenge length %d", len);
1.1 provos 96: memset(buf, 0, 32);
97: BN_bn2bin(challenge, buf + 32 - len);
1.6 deraadt 98: MD5_Init(&md);
99: MD5_Update(&md, buf, 32);
100: MD5_Update(&md, session_id, 16);
101: MD5_Final(mdbuf, &md);
1.1 provos 102:
103: /* We will no longer need these. */
104: BN_clear_free(encrypted_challenge);
105: BN_clear_free(challenge);
106: BN_clear_free(aux);
107: BN_CTX_free(ctx);
108:
109: /* Wait for a response. */
110: packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE);
111: packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE);
112: for (i = 0; i < 16; i++)
113: response[i] = packet_get_char();
114:
115: /* Verify that the response is the original challenge. */
116: if (memcmp(response, mdbuf, 16) != 0)
117: {
118: /* Wrong answer. */
119: return 0;
120: }
121:
122: /* Correct answer. */
123: return 1;
124: }
125:
126: /* Performs the RSA authentication dialog with the client. This returns
127: 0 if the client could not be authenticated, and 1 if authentication was
128: successful. This may exit if there is a serious protocol violation. */
129:
130: int
1.4 markus 131: auth_rsa(struct passwd *pw, BIGNUM *client_n, int strict_modes)
1.1 provos 132: {
133: char line[8192];
134: int authenticated;
135: unsigned int bits;
136: FILE *f;
137: unsigned long linenum = 0;
138: struct stat st;
139: BIGNUM *e, *n;
140:
1.4 markus 141: /* Temporarily use the user's uid. */
142: temporarily_use_uid(pw->pw_uid);
143:
144: /* The authorized keys. */
1.2 deraadt 145: snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir,
146: SSH_USER_PERMITTED_KEYS);
1.1 provos 147:
1.4 markus 148: /* Fail quietly if file does not exist */
1.1 provos 149: if (stat(line, &st) < 0)
150: {
151: /* Restore the privileged uid. */
152: restore_uid();
153: return 0;
154: }
1.4 markus 155:
156: /* Open the file containing the authorized keys. */
1.1 provos 157: f = fopen(line, "r");
158: if (!f)
159: {
160: /* Restore the privileged uid. */
161: restore_uid();
162: packet_send_debug("Could not open %.900s for reading.", line);
163: packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
164: return 0;
165: }
1.4 markus 166:
167: if (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, line);
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: }
201: }
1.1 provos 202:
203: /* Flag indicating whether authentication has succeeded. */
204: authenticated = 0;
205:
206: /* Initialize mp-int variables. */
207: e = BN_new();
208: n = BN_new();
209:
210: /* Go though the accepted keys, looking for the current key. If found,
211: perform a challenge-response dialog to verify that the user really has
212: the corresponding private key. */
213: while (fgets(line, sizeof(line), f))
214: {
215: char *cp;
216: char *options;
217:
218: linenum++;
219:
220: /* Skip leading whitespace. */
221: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
222: ;
223:
224: /* Skip empty and comment lines. */
225: if (!*cp || *cp == '\n' || *cp == '#')
226: continue;
227:
228: /* Check if there are options for this key, and if so, save their
229: starting address and skip the option part for now. If there are no
230: options, set the starting address to NULL. */
231: if (*cp < '0' || *cp > '9')
232: {
233: int quoted = 0;
234: options = cp;
235: for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++)
236: {
237: if (*cp == '\\' && cp[1] == '"')
238: cp++; /* Skip both */
239: else
240: if (*cp == '"')
241: quoted = !quoted;
242: }
243: }
244: else
245: options = NULL;
246:
247: /* Parse the key from the line. */
248: if (!auth_rsa_read_key(&cp, &bits, e, n))
249: {
250: debug("%.100s, line %lu: bad key syntax",
251: SSH_USER_PERMITTED_KEYS, linenum);
252: packet_send_debug("%.100s, line %lu: bad key syntax",
253: SSH_USER_PERMITTED_KEYS, linenum);
254: continue;
255: }
256: /* cp now points to the comment part. */
257:
258: /* Check if the we have found the desired key (identified by its
259: modulus). */
260: if (BN_cmp(n, client_n) != 0)
261: continue; /* Wrong key. */
262:
263: /* We have found the desired key. */
264:
265: /* Perform the challenge-response dialog for this key. */
266: if (!auth_rsa_challenge_dialog(bits, e, n))
267: {
268: /* Wrong response. */
269: log("Wrong response to RSA authentication challenge.");
270: packet_send_debug("Wrong response to RSA authentication challenge.");
271: continue;
272: }
273:
274: /* Correct response. The client has been successfully authenticated.
275: Note that we have not yet processed the options; this will be reset
276: if the options cause the authentication to be rejected. */
277: authenticated = 1;
278:
279: /* RSA part of authentication was accepted. Now process the options. */
280: if (options)
281: {
282: while (*options && *options != ' ' && *options != '\t')
283: {
284: cp = "no-port-forwarding";
285: if (strncmp(options, cp, strlen(cp)) == 0)
286: {
287: packet_send_debug("Port forwarding disabled.");
288: no_port_forwarding_flag = 1;
289: options += strlen(cp);
290: goto next_option;
291: }
292: cp = "no-agent-forwarding";
293: if (strncmp(options, cp, strlen(cp)) == 0)
294: {
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: {
303: packet_send_debug("X11 forwarding disabled.");
304: no_x11_forwarding_flag = 1;
305: options += strlen(cp);
306: goto next_option;
307: }
308: cp = "no-pty";
309: if (strncmp(options, cp, strlen(cp)) == 0)
310: {
311: packet_send_debug("Pty allocation disabled.");
312: no_pty_flag = 1;
313: options += strlen(cp);
314: goto next_option;
315: }
316: cp = "command=\"";
317: if (strncmp(options, cp, strlen(cp)) == 0)
318: {
319: int i;
320: options += strlen(cp);
321: forced_command = xmalloc(strlen(options) + 1);
322: i = 0;
323: while (*options)
324: {
325: if (*options == '"')
326: break;
327: if (*options == '\\' && options[1] == '"')
328: {
329: options += 2;
330: forced_command[i++] = '"';
331: continue;
332: }
333: forced_command[i++] = *options++;
334: }
335: if (!*options)
336: {
337: debug("%.100s, line %lu: missing end quote",
338: SSH_USER_PERMITTED_KEYS, linenum);
339: packet_send_debug("%.100s, line %lu: missing end quote",
340: SSH_USER_PERMITTED_KEYS, linenum);
341: continue;
342: }
343: forced_command[i] = 0;
344: packet_send_debug("Forced command: %.900s", forced_command);
345: options++;
346: goto next_option;
347: }
348: cp = "environment=\"";
349: if (strncmp(options, cp, strlen(cp)) == 0)
350: {
351: int i;
352: char *s;
353: struct envstring *new_envstring;
354: options += strlen(cp);
355: s = xmalloc(strlen(options) + 1);
356: i = 0;
357: while (*options)
358: {
359: if (*options == '"')
360: break;
361: if (*options == '\\' && options[1] == '"')
362: {
363: options += 2;
364: s[i++] = '"';
365: continue;
366: }
367: s[i++] = *options++;
368: }
369: if (!*options)
370: {
371: debug("%.100s, line %lu: missing end quote",
372: SSH_USER_PERMITTED_KEYS, linenum);
373: packet_send_debug("%.100s, line %lu: missing end quote",
374: SSH_USER_PERMITTED_KEYS, linenum);
375: continue;
376: }
377: s[i] = 0;
378: packet_send_debug("Adding to environment: %.900s", s);
379: debug("Adding to environment: %.900s", s);
380: options++;
381: new_envstring = xmalloc(sizeof(struct envstring));
382: new_envstring->s = s;
383: new_envstring->next = custom_environment;
384: custom_environment = new_envstring;
385: goto next_option;
386: }
387: cp = "from=\"";
388: if (strncmp(options, cp, strlen(cp)) == 0)
389: {
390: char *patterns = xmalloc(strlen(options) + 1);
391: int i;
392: options += strlen(cp);
393: i = 0;
394: while (*options)
395: {
396: if (*options == '"')
397: break;
398: if (*options == '\\' && options[1] == '"')
399: {
400: options += 2;
401: patterns[i++] = '"';
402: continue;
403: }
404: patterns[i++] = *options++;
405: }
406: if (!*options)
407: {
408: debug("%.100s, line %lu: missing end quote",
409: SSH_USER_PERMITTED_KEYS, linenum);
410: packet_send_debug("%.100s, line %lu: missing end quote",
411: SSH_USER_PERMITTED_KEYS, linenum);
412: continue;
413: }
414: patterns[i] = 0;
415: options++;
416: if (!match_hostname(get_canonical_hostname(), patterns,
417: strlen(patterns)) &&
418: !match_hostname(get_remote_ipaddr(), patterns,
419: strlen(patterns)))
420: {
421: log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
422: pw->pw_name, get_canonical_hostname(),
423: get_remote_ipaddr());
424: packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
425: get_canonical_hostname());
426: xfree(patterns);
427: authenticated = 0;
428: break;
429: }
430: xfree(patterns);
431: /* Host name matches. */
432: goto next_option;
433: }
434: bad_option:
435: /* Unknown option. */
436: log("Bad options in %.100s file, line %lu: %.50s",
437: SSH_USER_PERMITTED_KEYS, linenum, options);
438: packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
439: SSH_USER_PERMITTED_KEYS, linenum, options);
440: authenticated = 0;
441: break;
442:
443: next_option:
444: /* Skip the comma, and move to the next option (or break out
445: if there are no more). */
446: if (!*options)
447: fatal("Bugs in auth-rsa.c option processing.");
448: if (*options == ' ' || *options == '\t')
449: break; /* End of options. */
450: if (*options != ',')
451: goto bad_option;
452: options++;
453: /* Process the next option. */
454: continue;
455: }
456: }
457:
458: /* Break out of the loop if authentication was successful; otherwise
459: continue searching. */
460: if (authenticated)
461: break;
462: }
463:
464: /* Restore the privileged uid. */
465: restore_uid();
466:
467: /* Close the file. */
468: fclose(f);
469:
470: /* Clear any mp-int variables. */
471: BN_clear_free(n);
472: BN_clear_free(e);
473:
474: if (authenticated)
475: packet_send_debug("RSA authentication accepted.");
476:
477: /* Return authentication result. */
478: return authenticated;
479: }