Annotation of src/usr.bin/ssh/auth-rsa.c, Revision 1.3
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.3 ! deraadt 19: RCSID("$Id: auth-rsa.c,v 1.2 1999/09/29 21:14:15 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.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. */
93: len = (BN_num_bits(challenge) + 7) / 8;
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
130: auth_rsa(struct passwd *pw, BIGNUM *client_n)
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:
140: /* Open the file containing the authorized keys. */
1.2 deraadt 141: snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir,
142: SSH_USER_PERMITTED_KEYS);
1.1 provos 143:
144: /* Temporarily use the user's uid. */
145: temporarily_use_uid(pw->pw_uid);
146: if (stat(line, &st) < 0)
147: {
148: /* Restore the privileged uid. */
149: restore_uid();
150: return 0;
151: }
152: f = fopen(line, "r");
153: if (!f)
154: {
155: /* Restore the privileged uid. */
156: restore_uid();
157: packet_send_debug("Could not open %.900s for reading.", line);
158: packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
159: return 0;
160: }
161:
162: /* Flag indicating whether authentication has succeeded. */
163: authenticated = 0;
164:
165: /* Initialize mp-int variables. */
166: e = BN_new();
167: n = BN_new();
168:
169: /* Go though the accepted keys, looking for the current key. If found,
170: perform a challenge-response dialog to verify that the user really has
171: the corresponding private key. */
172: while (fgets(line, sizeof(line), f))
173: {
174: char *cp;
175: char *options;
176:
177: linenum++;
178:
179: /* Skip leading whitespace. */
180: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
181: ;
182:
183: /* Skip empty and comment lines. */
184: if (!*cp || *cp == '\n' || *cp == '#')
185: continue;
186:
187: /* Check if there are options for this key, and if so, save their
188: starting address and skip the option part for now. If there are no
189: options, set the starting address to NULL. */
190: if (*cp < '0' || *cp > '9')
191: {
192: int quoted = 0;
193: options = cp;
194: for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++)
195: {
196: if (*cp == '\\' && cp[1] == '"')
197: cp++; /* Skip both */
198: else
199: if (*cp == '"')
200: quoted = !quoted;
201: }
202: }
203: else
204: options = NULL;
205:
206: /* Parse the key from the line. */
207: if (!auth_rsa_read_key(&cp, &bits, e, n))
208: {
209: debug("%.100s, line %lu: bad key syntax",
210: SSH_USER_PERMITTED_KEYS, linenum);
211: packet_send_debug("%.100s, line %lu: bad key syntax",
212: SSH_USER_PERMITTED_KEYS, linenum);
213: continue;
214: }
215: /* cp now points to the comment part. */
216:
217: /* Check if the we have found the desired key (identified by its
218: modulus). */
219: if (BN_cmp(n, client_n) != 0)
220: continue; /* Wrong key. */
221:
222: /* We have found the desired key. */
223:
224: /* Perform the challenge-response dialog for this key. */
225: if (!auth_rsa_challenge_dialog(bits, e, n))
226: {
227: /* Wrong response. */
228: log("Wrong response to RSA authentication challenge.");
229: packet_send_debug("Wrong response to RSA authentication challenge.");
230: continue;
231: }
232:
233: /* Correct response. The client has been successfully authenticated.
234: Note that we have not yet processed the options; this will be reset
235: if the options cause the authentication to be rejected. */
236: authenticated = 1;
237:
238: /* RSA part of authentication was accepted. Now process the options. */
239: if (options)
240: {
241: while (*options && *options != ' ' && *options != '\t')
242: {
243: cp = "no-port-forwarding";
244: if (strncmp(options, cp, strlen(cp)) == 0)
245: {
246: packet_send_debug("Port forwarding disabled.");
247: no_port_forwarding_flag = 1;
248: options += strlen(cp);
249: goto next_option;
250: }
251: cp = "no-agent-forwarding";
252: if (strncmp(options, cp, strlen(cp)) == 0)
253: {
254: packet_send_debug("Agent forwarding disabled.");
255: no_agent_forwarding_flag = 1;
256: options += strlen(cp);
257: goto next_option;
258: }
259: cp = "no-X11-forwarding";
260: if (strncmp(options, cp, strlen(cp)) == 0)
261: {
262: packet_send_debug("X11 forwarding disabled.");
263: no_x11_forwarding_flag = 1;
264: options += strlen(cp);
265: goto next_option;
266: }
267: cp = "no-pty";
268: if (strncmp(options, cp, strlen(cp)) == 0)
269: {
270: packet_send_debug("Pty allocation disabled.");
271: no_pty_flag = 1;
272: options += strlen(cp);
273: goto next_option;
274: }
275: cp = "command=\"";
276: if (strncmp(options, cp, strlen(cp)) == 0)
277: {
278: int i;
279: options += strlen(cp);
280: forced_command = xmalloc(strlen(options) + 1);
281: i = 0;
282: while (*options)
283: {
284: if (*options == '"')
285: break;
286: if (*options == '\\' && options[1] == '"')
287: {
288: options += 2;
289: forced_command[i++] = '"';
290: continue;
291: }
292: forced_command[i++] = *options++;
293: }
294: if (!*options)
295: {
296: debug("%.100s, line %lu: missing end quote",
297: SSH_USER_PERMITTED_KEYS, linenum);
298: packet_send_debug("%.100s, line %lu: missing end quote",
299: SSH_USER_PERMITTED_KEYS, linenum);
300: continue;
301: }
302: forced_command[i] = 0;
303: packet_send_debug("Forced command: %.900s", forced_command);
304: options++;
305: goto next_option;
306: }
307: cp = "environment=\"";
308: if (strncmp(options, cp, strlen(cp)) == 0)
309: {
310: int i;
311: char *s;
312: struct envstring *new_envstring;
313: options += strlen(cp);
314: s = xmalloc(strlen(options) + 1);
315: i = 0;
316: while (*options)
317: {
318: if (*options == '"')
319: break;
320: if (*options == '\\' && options[1] == '"')
321: {
322: options += 2;
323: s[i++] = '"';
324: continue;
325: }
326: s[i++] = *options++;
327: }
328: if (!*options)
329: {
330: debug("%.100s, line %lu: missing end quote",
331: SSH_USER_PERMITTED_KEYS, linenum);
332: packet_send_debug("%.100s, line %lu: missing end quote",
333: SSH_USER_PERMITTED_KEYS, linenum);
334: continue;
335: }
336: s[i] = 0;
337: packet_send_debug("Adding to environment: %.900s", s);
338: debug("Adding to environment: %.900s", s);
339: options++;
340: new_envstring = xmalloc(sizeof(struct envstring));
341: new_envstring->s = s;
342: new_envstring->next = custom_environment;
343: custom_environment = new_envstring;
344: goto next_option;
345: }
346: cp = "from=\"";
347: if (strncmp(options, cp, strlen(cp)) == 0)
348: {
349: char *patterns = xmalloc(strlen(options) + 1);
350: int i;
351: options += strlen(cp);
352: i = 0;
353: while (*options)
354: {
355: if (*options == '"')
356: break;
357: if (*options == '\\' && options[1] == '"')
358: {
359: options += 2;
360: patterns[i++] = '"';
361: continue;
362: }
363: patterns[i++] = *options++;
364: }
365: if (!*options)
366: {
367: debug("%.100s, line %lu: missing end quote",
368: SSH_USER_PERMITTED_KEYS, linenum);
369: packet_send_debug("%.100s, line %lu: missing end quote",
370: SSH_USER_PERMITTED_KEYS, linenum);
371: continue;
372: }
373: patterns[i] = 0;
374: options++;
375: if (!match_hostname(get_canonical_hostname(), patterns,
376: strlen(patterns)) &&
377: !match_hostname(get_remote_ipaddr(), patterns,
378: strlen(patterns)))
379: {
380: log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
381: pw->pw_name, get_canonical_hostname(),
382: get_remote_ipaddr());
383: packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
384: get_canonical_hostname());
385: xfree(patterns);
386: authenticated = 0;
387: break;
388: }
389: xfree(patterns);
390: /* Host name matches. */
391: goto next_option;
392: }
393: bad_option:
394: /* Unknown option. */
395: log("Bad options in %.100s file, line %lu: %.50s",
396: SSH_USER_PERMITTED_KEYS, linenum, options);
397: packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
398: SSH_USER_PERMITTED_KEYS, linenum, options);
399: authenticated = 0;
400: break;
401:
402: next_option:
403: /* Skip the comma, and move to the next option (or break out
404: if there are no more). */
405: if (!*options)
406: fatal("Bugs in auth-rsa.c option processing.");
407: if (*options == ' ' || *options == '\t')
408: break; /* End of options. */
409: if (*options != ',')
410: goto bad_option;
411: options++;
412: /* Process the next option. */
413: continue;
414: }
415: }
416:
417: /* Break out of the loop if authentication was successful; otherwise
418: continue searching. */
419: if (authenticated)
420: break;
421: }
422:
423: /* Restore the privileged uid. */
424: restore_uid();
425:
426: /* Close the file. */
427: fclose(f);
428:
429: /* Clear any mp-int variables. */
430: BN_clear_free(n);
431: BN_clear_free(e);
432:
433: if (authenticated)
434: packet_send_debug("RSA authentication accepted.");
435:
436: /* Return authentication result. */
437: return authenticated;
438: }