Annotation of src/usr.bin/ssh/auth-rsa.c, Revision 1.43
1.1 provos 1: /*
1.13 deraadt 2: * Author: Tatu Ylonen <ylo@cs.hut.fi>
3: * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4: * All rights reserved
5: * RSA-based authentication. This code determines whether to admit a login
6: * based on RSA authentication. This file also contains functions to check
7: * validity of the host key.
1.21 markus 8: *
1.28 deraadt 9: * As far as I am concerned, the code I have written for this software
10: * can be used freely for any purpose. Any derived versions of this
11: * software must be clearly marked as such, and if the derived work is
12: * incompatible with the protocol description in the RFC file, it must be
13: * called by a name other than "ssh" or "Secure Shell".
1.13 deraadt 14: */
1.1 provos 15:
16: #include "includes.h"
1.43 ! provos 17: RCSID("$OpenBSD: auth-rsa.c,v 1.42 2001/06/22 21:55:48 markus Exp $");
1.38 markus 18:
19: #include <openssl/rsa.h>
20: #include <openssl/md5.h>
1.1 provos 21:
22: #include "rsa.h"
23: #include "packet.h"
24: #include "xmalloc.h"
1.35 markus 25: #include "ssh1.h"
1.1 provos 26: #include "mpaux.h"
27: #include "uidswap.h"
1.19 markus 28: #include "match.h"
1.25 markus 29: #include "auth-options.h"
1.35 markus 30: #include "pathnames.h"
1.38 markus 31: #include "log.h"
32: #include "servconf.h"
33: #include "auth.h"
1.30 markus 34:
35: /* import */
36: extern ServerOptions options;
37:
1.14 markus 38: /*
39: * Session identifier that is used to bind key exchange and authentication
40: * responses to a particular session.
41: */
1.34 markus 42: extern u_char session_id[16];
1.1 provos 43:
1.14 markus 44: /*
45: * The .ssh/authorized_keys file contains public keys, one per line, in the
46: * following format:
47: * options bits e n comment
48: * where bits, e and n are decimal numbers,
49: * and comment is any string of characters up to newline. The maximum
50: * length of a line is 8000 characters. See the documentation for a
51: * description of the options.
52: */
53:
54: /*
55: * Performs the RSA authentication challenge-response dialog with the client,
56: * and returns true (non-zero) if the client gave the correct answer to
57: * our challenge; returns zero if the client gives a wrong answer.
58: */
1.1 provos 59:
60: int
1.19 markus 61: auth_rsa_challenge_dialog(RSA *pk)
1.1 provos 62: {
1.18 markus 63: BIGNUM *challenge, *encrypted_challenge;
64: BN_CTX *ctx;
1.34 markus 65: u_char buf[32], mdbuf[16], response[16];
1.12 markus 66: MD5_CTX md;
1.34 markus 67: u_int i;
1.12 markus 68: int plen, len;
69:
70: encrypted_challenge = BN_new();
71: challenge = BN_new();
72:
73: /* Generate a random challenge. */
74: BN_rand(challenge, 256, 0, 0);
1.18 markus 75: ctx = BN_CTX_new();
1.19 markus 76: BN_mod(challenge, challenge, pk->n, ctx);
1.18 markus 77: BN_CTX_free(ctx);
1.12 markus 78:
79: /* Encrypt the challenge with the public key. */
80: rsa_public_encrypt(encrypted_challenge, challenge, pk);
81:
82: /* Send the encrypted challenge to the client. */
83: packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
84: packet_put_bignum(encrypted_challenge);
85: packet_send();
1.18 markus 86: BN_clear_free(encrypted_challenge);
1.12 markus 87: packet_write_wait();
88:
1.18 markus 89: /* Wait for a response. */
90: packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE);
91: packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE);
92: for (i = 0; i < 16; i++)
93: response[i] = packet_get_char();
94:
1.12 markus 95: /* The response is MD5 of decrypted challenge plus session id. */
96: len = BN_num_bytes(challenge);
97: if (len <= 0 || len > 32)
98: fatal("auth_rsa_challenge_dialog: bad challenge length %d", len);
99: memset(buf, 0, 32);
100: BN_bn2bin(challenge, buf + 32 - len);
101: MD5_Init(&md);
102: MD5_Update(&md, buf, 32);
103: MD5_Update(&md, session_id, 16);
104: MD5_Final(mdbuf, &md);
105: BN_clear_free(challenge);
106:
107: /* Verify that the response is the original challenge. */
108: if (memcmp(response, mdbuf, 16) != 0) {
109: /* Wrong answer. */
110: return 0;
111: }
112: /* Correct answer. */
113: return 1;
1.1 provos 114: }
115:
1.14 markus 116: /*
117: * Performs the RSA authentication dialog with the client. This returns
118: * 0 if the client could not be authenticated, and 1 if authentication was
119: * successful. This may exit if there is a serious protocol violation.
120: */
1.1 provos 121:
122: int
1.8 markus 123: auth_rsa(struct passwd *pw, BIGNUM *client_n)
1.1 provos 124: {
1.41 markus 125: char line[8192], *file;
1.12 markus 126: int authenticated;
1.34 markus 127: u_int bits;
1.12 markus 128: FILE *f;
1.34 markus 129: u_long linenum = 0;
1.12 markus 130: struct stat st;
1.19 markus 131: RSA *pk;
1.30 markus 132:
133: /* no user given */
134: if (pw == NULL)
135: return 0;
1.12 markus 136:
137: /* Temporarily use the user's uid. */
1.40 markus 138: temporarily_use_uid(pw);
1.12 markus 139:
140: /* The authorized keys. */
1.41 markus 141: file = authorized_keys_file(pw);
142: debug("trying public RSA key file %s", file);
1.12 markus 143:
144: /* Fail quietly if file does not exist */
145: if (stat(file, &st) < 0) {
146: /* Restore the privileged uid. */
147: restore_uid();
1.41 markus 148: xfree(file);
1.12 markus 149: return 0;
150: }
151: /* Open the file containing the authorized keys. */
152: f = fopen(file, "r");
153: if (!f) {
154: /* Restore the privileged uid. */
155: restore_uid();
156: packet_send_debug("Could not open %.900s for reading.", file);
157: packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
1.41 markus 158: xfree(file);
1.12 markus 159: return 0;
1.1 provos 160: }
1.41 markus 161: if (options.strict_modes &&
1.43 ! provos 162: secure_filename(f, file, pw, line, sizeof(line)) != 0) {
1.41 markus 163: xfree(file);
164: fclose(f);
165: log("Authentication refused: %s", line);
166: packet_send_debug("Authentication refused: %s", line);
167: restore_uid();
168: return 0;
1.1 provos 169: }
1.12 markus 170: /* Flag indicating whether authentication has succeeded. */
171: authenticated = 0;
1.1 provos 172:
1.19 markus 173: pk = RSA_new();
174: pk->e = BN_new();
175: pk->n = BN_new();
1.12 markus 176:
1.14 markus 177: /*
178: * Go though the accepted keys, looking for the current key. If
179: * found, perform a challenge-response dialog to verify that the
180: * user really has the corresponding private key.
181: */
1.12 markus 182: while (fgets(line, sizeof(line), f)) {
183: char *cp;
184: char *options;
185:
186: linenum++;
187:
1.14 markus 188: /* Skip leading whitespace, empty and comment lines. */
189: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
190: ;
1.12 markus 191: if (!*cp || *cp == '\n' || *cp == '#')
192: continue;
193:
1.14 markus 194: /*
195: * Check if there are options for this key, and if so,
196: * save their starting address and skip the option part
197: * for now. If there are no options, set the starting
198: * address to NULL.
199: */
1.12 markus 200: if (*cp < '0' || *cp > '9') {
201: int quoted = 0;
202: options = cp;
203: for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
204: if (*cp == '\\' && cp[1] == '"')
205: cp++; /* Skip both */
206: else if (*cp == '"')
207: quoted = !quoted;
208: }
209: } else
210: options = NULL;
1.1 provos 211:
1.12 markus 212: /* Parse the key from the line. */
1.19 markus 213: if (!auth_rsa_read_key(&cp, &bits, pk->e, pk->n)) {
1.42 markus 214: debug("%.100s, line %lu: non ssh1 key syntax",
1.36 markus 215: file, linenum);
1.12 markus 216: continue;
1.1 provos 217: }
1.12 markus 218: /* cp now points to the comment part. */
219:
1.16 markus 220: /* Check if the we have found the desired key (identified by its modulus). */
1.19 markus 221: if (BN_cmp(pk->n, client_n) != 0)
1.16 markus 222: continue;
223:
1.12 markus 224: /* check the real bits */
1.19 markus 225: if (bits != BN_num_bits(pk->n))
1.15 markus 226: log("Warning: %s, line %ld: keysize mismatch: "
227: "actual %d vs. announced %d.",
1.19 markus 228: file, linenum, BN_num_bits(pk->n), bits);
1.12 markus 229:
230: /* We have found the desired key. */
1.33 markus 231: /*
232: * If our options do not allow this key to be used,
233: * do not send challenge.
234: */
1.36 markus 235: if (!auth_parse_options(pw, options, file, linenum))
1.33 markus 236: continue;
1.19 markus 237:
1.12 markus 238: /* Perform the challenge-response dialog for this key. */
1.19 markus 239: if (!auth_rsa_challenge_dialog(pk)) {
1.12 markus 240: /* Wrong response. */
241: verbose("Wrong response to RSA authentication challenge.");
242: packet_send_debug("Wrong response to RSA authentication challenge.");
243: continue;
1.1 provos 244: }
1.14 markus 245: /*
246: * Correct response. The client has been successfully
247: * authenticated. Note that we have not yet processed the
248: * options; this will be reset if the options cause the
249: * authentication to be rejected.
250: * Break out of the loop if authentication was successful;
251: * otherwise continue searching.
252: */
1.32 markus 253: authenticated = 1;
254: break;
1.1 provos 255: }
256:
1.12 markus 257: /* Restore the privileged uid. */
258: restore_uid();
259:
260: /* Close the file. */
1.41 markus 261: xfree(file);
1.12 markus 262: fclose(f);
263:
1.19 markus 264: RSA_free(pk);
1.1 provos 265:
1.12 markus 266: if (authenticated)
267: packet_send_debug("RSA authentication accepted.");
1.31 markus 268: else
269: auth_clear_options();
1.1 provos 270:
1.12 markus 271: /* Return authentication result. */
272: return authenticated;
1.1 provos 273: }