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