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