5 * Author: Tatu Ylonen <ylo@cs.hut.fi>
7 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
10 * Created: Mon Mar 27 01:46:52 1995 ylo
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.
30 #include <openssl/rsa.h>
31 #include <openssl/md5.h>
38 /* Flags that may be set in authorized_keys options. */
39 extern int no_port_forwarding_flag;
40 extern int no_agent_forwarding_flag;
41 extern int no_x11_forwarding_flag;
42 extern int no_pty_flag;
43 extern char *forced_command;
44 extern struct envstring *custom_environment;
46 /* Session identifier that is used to bind key exchange and authentication
47 responses to a particular session. */
48 extern unsigned char session_id[16];
50 /* The .ssh/authorized_keys file contains public keys, one per line, in the
52 options bits e n comment
53 where bits, e and n are decimal numbers,
54 and comment is any string of characters up to newline. The maximum
55 length of a line is 8000 characters. See the documentation for a
56 description of the options.
59 /* Performs the RSA authentication challenge-response dialog with the client,
60 and returns true (non-zero) if the client gave the correct answer to
61 our challenge; returns zero if the client gives a wrong answer. */
64 auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n)
66 BIGNUM *challenge, *encrypted_challenge, *aux;
68 BN_CTX *ctx = BN_CTX_new();
69 unsigned char buf[32], mdbuf[16], response[16];
74 encrypted_challenge = BN_new();
78 /* Generate a random challenge. */
79 BN_rand(challenge, 256, 0, 0);
80 BN_mod(challenge, challenge, n, ctx);
82 /* Create the public key data structure. */
89 /* Encrypt the challenge with the public key. */
90 rsa_public_encrypt(encrypted_challenge, challenge, pk);
93 /* Send the encrypted challenge to the client. */
94 packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
95 packet_put_bignum(encrypted_challenge);
99 /* The response is MD5 of decrypted challenge plus session id. */
100 len = BN_num_bytes(challenge);
101 if (len <= 0 || len > 32)
102 fatal("auth_rsa_challenge_dialog: bad challenge length %d", len);
104 BN_bn2bin(challenge, buf + 32 - len);
106 MD5_Update(&md, buf, 32);
107 MD5_Update(&md, session_id, 16);
108 MD5_Final(mdbuf, &md);
110 /* We will no longer need these. */
111 BN_clear_free(encrypted_challenge);
112 BN_clear_free(challenge);
116 /* Wait for a response. */
117 packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE);
118 packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE);
119 for (i = 0; i < 16; i++)
120 response[i] = packet_get_char();
122 /* Verify that the response is the original challenge. */
123 if (memcmp(response, mdbuf, 16) != 0) {
127 /* Correct answer. */
131 /* Performs the RSA authentication dialog with the client. This returns
132 0 if the client could not be authenticated, and 1 if authentication was
133 successful. This may exit if there is a serious protocol violation. */
136 auth_rsa(struct passwd *pw, BIGNUM *client_n)
138 extern ServerOptions options;
139 char line[8192], file[1024];
143 unsigned long linenum = 0;
147 /* Temporarily use the user's uid. */
148 temporarily_use_uid(pw->pw_uid);
150 /* The authorized keys. */
151 snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
152 SSH_USER_PERMITTED_KEYS);
154 /* Fail quietly if file does not exist */
155 if (stat(file, &st) < 0) {
156 /* Restore the privileged uid. */
160 /* Open the file containing the authorized keys. */
161 f = fopen(file, "r");
163 /* Restore the privileged uid. */
165 packet_send_debug("Could not open %.900s for reading.", file);
166 packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
169 if (options.strict_modes) {
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, file);
180 /* Check path to SSH_USER_PERMITTED_KEYS */
182 static const char *check[] = {
183 "", SSH_USER_DIR, NULL
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);
199 packet_send_debug(buf);
204 /* Flag indicating whether authentication has succeeded. */
207 /* Initialize mp-int variables. */
211 /* Go though the accepted keys, looking for the current key. If
212 found, perform a challenge-response dialog to verify that the
213 user really has the corresponding private key. */
214 while (fgets(line, sizeof(line), f)) {
220 /* Skip leading whitespace. */
221 for (cp = line; *cp == ' ' || *cp == '\t'; cp++);
223 /* Skip empty and comment lines. */
224 if (!*cp || *cp == '\n' || *cp == '#')
227 /* Check if there are options for this key, and if so,
228 save their starting address and skip the option part
229 for now. If there are no options, set the starting
231 if (*cp < '0' || *cp > '9') {
234 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
235 if (*cp == '\\' && cp[1] == '"')
236 cp++; /* Skip both */
243 /* Parse the key from the line. */
244 if (!auth_rsa_read_key(&cp, &bits, e, n)) {
245 debug("%.100s, line %lu: bad key syntax",
246 SSH_USER_PERMITTED_KEYS, linenum);
247 packet_send_debug("%.100s, line %lu: bad key syntax",
248 SSH_USER_PERMITTED_KEYS, linenum);
251 /* cp now points to the comment part. */
253 /* check the real bits */
254 if (bits != BN_num_bits(n))
255 error("Warning: error in %s, line %ld: keysize mismatch: "
256 "actual size %d vs. announced %d.",
257 file, linenum, BN_num_bits(n), bits);
259 /* Check if the we have found the desired key (identified by its modulus). */
260 if (BN_cmp(n, client_n) != 0)
261 continue; /* Wrong key. */
263 /* We have found the desired key. */
265 /* Perform the challenge-response dialog for this key. */
266 if (!auth_rsa_challenge_dialog(e, n)) {
267 /* Wrong response. */
268 verbose("Wrong response to RSA authentication challenge.");
269 packet_send_debug("Wrong response to RSA authentication challenge.");
272 /* Correct response. The client has been successfully
273 authenticated. Note that we have not yet processed the
274 options; this will be reset if the options cause the
275 authentication to be rejected. */
278 /* RSA part of authentication was accepted. Now process the options. */
280 while (*options && *options != ' ' && *options != '\t') {
281 cp = "no-port-forwarding";
282 if (strncmp(options, cp, strlen(cp)) == 0) {
283 packet_send_debug("Port forwarding disabled.");
284 no_port_forwarding_flag = 1;
285 options += strlen(cp);
288 cp = "no-agent-forwarding";
289 if (strncmp(options, cp, strlen(cp)) == 0) {
290 packet_send_debug("Agent forwarding disabled.");
291 no_agent_forwarding_flag = 1;
292 options += strlen(cp);
295 cp = "no-X11-forwarding";
296 if (strncmp(options, cp, strlen(cp)) == 0) {
297 packet_send_debug("X11 forwarding disabled.");
298 no_x11_forwarding_flag = 1;
299 options += strlen(cp);
303 if (strncmp(options, cp, strlen(cp)) == 0) {
304 packet_send_debug("Pty allocation disabled.");
306 options += strlen(cp);
310 if (strncmp(options, cp, strlen(cp)) == 0) {
312 options += strlen(cp);
313 forced_command = xmalloc(strlen(options) + 1);
318 if (*options == '\\' && options[1] == '"') {
320 forced_command[i++] = '"';
323 forced_command[i++] = *options++;
326 debug("%.100s, line %lu: missing end quote",
327 SSH_USER_PERMITTED_KEYS, linenum);
328 packet_send_debug("%.100s, line %lu: missing end quote",
329 SSH_USER_PERMITTED_KEYS, linenum);
332 forced_command[i] = 0;
333 packet_send_debug("Forced command: %.900s", forced_command);
337 cp = "environment=\"";
338 if (strncmp(options, cp, strlen(cp)) == 0) {
341 struct envstring *new_envstring;
342 options += strlen(cp);
343 s = xmalloc(strlen(options) + 1);
348 if (*options == '\\' && options[1] == '"') {
356 debug("%.100s, line %lu: missing end quote",
357 SSH_USER_PERMITTED_KEYS, linenum);
358 packet_send_debug("%.100s, line %lu: missing end quote",
359 SSH_USER_PERMITTED_KEYS, linenum);
363 packet_send_debug("Adding to environment: %.900s", s);
364 debug("Adding to environment: %.900s", s);
366 new_envstring = xmalloc(sizeof(struct envstring));
367 new_envstring->s = s;
368 new_envstring->next = custom_environment;
369 custom_environment = new_envstring;
373 if (strncmp(options, cp, strlen(cp)) == 0) {
374 char *patterns = xmalloc(strlen(options) + 1);
376 options += strlen(cp);
381 if (*options == '\\' && options[1] == '"') {
386 patterns[i++] = *options++;
389 debug("%.100s, line %lu: missing end quote",
390 SSH_USER_PERMITTED_KEYS, linenum);
391 packet_send_debug("%.100s, line %lu: missing end quote",
392 SSH_USER_PERMITTED_KEYS, linenum);
397 if (!match_hostname(get_canonical_hostname(), patterns,
399 !match_hostname(get_remote_ipaddr(), patterns,
401 log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
402 pw->pw_name, get_canonical_hostname(),
403 get_remote_ipaddr());
404 packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
405 get_canonical_hostname());
411 /* Host name matches. */
415 /* Unknown option. */
416 log("Bad options in %.100s file, line %lu: %.50s",
417 SSH_USER_PERMITTED_KEYS, linenum, options);
418 packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
419 SSH_USER_PERMITTED_KEYS, linenum, options);
424 /* Skip the comma, and move to the next option
425 (or break out if there are no more). */
427 fatal("Bugs in auth-rsa.c option processing.");
428 if (*options == ' ' || *options == '\t')
429 break; /* End of options. */
433 /* Process the next option. */
437 /* Break out of the loop if authentication was successful;
438 otherwise continue searching. */
443 /* Restore the privileged uid. */
446 /* Close the file. */
449 /* Clear any mp-int variables. */
454 packet_send_debug("RSA authentication accepted.");
456 /* Return authentication result. */
457 return authenticated;