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