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)
129 /* Correct answer. */
133 /* Performs the RSA authentication dialog with the client. This returns
134 0 if the client could not be authenticated, and 1 if authentication was
135 successful. This may exit if there is a serious protocol violation. */
138 auth_rsa(struct passwd *pw, BIGNUM *client_n)
140 extern ServerOptions options;
141 char line[8192], file[1024];
145 unsigned long linenum = 0;
149 /* Temporarily use the user's uid. */
150 temporarily_use_uid(pw->pw_uid);
152 /* The authorized keys. */
153 snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
154 SSH_USER_PERMITTED_KEYS);
156 /* Fail quietly if file does not exist */
157 if (stat(file, &st) < 0)
159 /* Restore the privileged uid. */
164 /* Open the file containing the authorized keys. */
165 f = fopen(file, "r");
168 /* Restore the privileged uid. */
170 packet_send_debug("Could not open %.900s for reading.", file);
171 packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
175 if (options.strict_modes) {
178 /* Check open file in order to avoid open/stat races */
179 if (fstat(fileno(f), &st) < 0 ||
180 (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
181 (st.st_mode & 022) != 0) {
182 snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: "
183 "bad ownership or modes for '%s'.", pw->pw_name, file);
186 /* Check path to SSH_USER_PERMITTED_KEYS */
188 static const char *check[] = {
189 "", SSH_USER_DIR, NULL
191 for (i=0; check[i]; i++) {
192 snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]);
193 if (stat(line, &st) < 0 ||
194 (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
195 (st.st_mode & 022) != 0) {
196 snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: "
197 "bad ownership or modes for '%s'.", pw->pw_name, line);
205 packet_send_debug(buf);
211 /* Flag indicating whether authentication has succeeded. */
214 /* Initialize mp-int variables. */
218 /* Go though the accepted keys, looking for the current key. If found,
219 perform a challenge-response dialog to verify that the user really has
220 the corresponding private key. */
221 while (fgets(line, sizeof(line), f))
228 /* Skip leading whitespace. */
229 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
232 /* Skip empty and comment lines. */
233 if (!*cp || *cp == '\n' || *cp == '#')
236 /* Check if there are options for this key, and if so, save their
237 starting address and skip the option part for now. If there are no
238 options, set the starting address to NULL. */
239 if (*cp < '0' || *cp > '9')
243 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++)
245 if (*cp == '\\' && cp[1] == '"')
246 cp++; /* Skip both */
255 /* Parse the key from the line. */
256 if (!auth_rsa_read_key(&cp, &bits, e, n))
258 debug("%.100s, line %lu: bad key syntax",
259 SSH_USER_PERMITTED_KEYS, linenum);
260 packet_send_debug("%.100s, line %lu: bad key syntax",
261 SSH_USER_PERMITTED_KEYS, linenum);
264 /* cp now points to the comment part. */
266 /* check the real bits */
267 if (bits != BN_num_bits(n))
268 error("Warning: error in %s, line %d: keysize mismatch: "
269 "actual size %d vs. announced %d.",
270 file, linenum, BN_num_bits(n), bits);
272 /* Check if the we have found the desired key (identified by its
274 if (BN_cmp(n, client_n) != 0)
275 continue; /* Wrong key. */
277 /* We have found the desired key. */
279 /* Perform the challenge-response dialog for this key. */
280 if (!auth_rsa_challenge_dialog(e, n))
282 /* Wrong response. */
283 log("Wrong response to RSA authentication challenge.");
284 packet_send_debug("Wrong response to RSA authentication challenge.");
288 /* Correct response. The client has been successfully authenticated.
289 Note that we have not yet processed the options; this will be reset
290 if the options cause the authentication to be rejected. */
293 /* RSA part of authentication was accepted. Now process the options. */
296 while (*options && *options != ' ' && *options != '\t')
298 cp = "no-port-forwarding";
299 if (strncmp(options, cp, strlen(cp)) == 0)
301 packet_send_debug("Port forwarding disabled.");
302 no_port_forwarding_flag = 1;
303 options += strlen(cp);
306 cp = "no-agent-forwarding";
307 if (strncmp(options, cp, strlen(cp)) == 0)
309 packet_send_debug("Agent forwarding disabled.");
310 no_agent_forwarding_flag = 1;
311 options += strlen(cp);
314 cp = "no-X11-forwarding";
315 if (strncmp(options, cp, strlen(cp)) == 0)
317 packet_send_debug("X11 forwarding disabled.");
318 no_x11_forwarding_flag = 1;
319 options += strlen(cp);
323 if (strncmp(options, cp, strlen(cp)) == 0)
325 packet_send_debug("Pty allocation disabled.");
327 options += strlen(cp);
331 if (strncmp(options, cp, strlen(cp)) == 0)
334 options += strlen(cp);
335 forced_command = xmalloc(strlen(options) + 1);
341 if (*options == '\\' && options[1] == '"')
344 forced_command[i++] = '"';
347 forced_command[i++] = *options++;
351 debug("%.100s, line %lu: missing end quote",
352 SSH_USER_PERMITTED_KEYS, linenum);
353 packet_send_debug("%.100s, line %lu: missing end quote",
354 SSH_USER_PERMITTED_KEYS, linenum);
357 forced_command[i] = 0;
358 packet_send_debug("Forced command: %.900s", forced_command);
362 cp = "environment=\"";
363 if (strncmp(options, cp, strlen(cp)) == 0)
367 struct envstring *new_envstring;
368 options += strlen(cp);
369 s = xmalloc(strlen(options) + 1);
375 if (*options == '\\' && options[1] == '"')
385 debug("%.100s, line %lu: missing end quote",
386 SSH_USER_PERMITTED_KEYS, linenum);
387 packet_send_debug("%.100s, line %lu: missing end quote",
388 SSH_USER_PERMITTED_KEYS, linenum);
392 packet_send_debug("Adding to environment: %.900s", s);
393 debug("Adding to environment: %.900s", s);
395 new_envstring = xmalloc(sizeof(struct envstring));
396 new_envstring->s = s;
397 new_envstring->next = custom_environment;
398 custom_environment = new_envstring;
402 if (strncmp(options, cp, strlen(cp)) == 0)
404 char *patterns = xmalloc(strlen(options) + 1);
406 options += strlen(cp);
412 if (*options == '\\' && options[1] == '"')
418 patterns[i++] = *options++;
422 debug("%.100s, line %lu: missing end quote",
423 SSH_USER_PERMITTED_KEYS, linenum);
424 packet_send_debug("%.100s, line %lu: missing end quote",
425 SSH_USER_PERMITTED_KEYS, linenum);
430 if (!match_hostname(get_canonical_hostname(), patterns,
432 !match_hostname(get_remote_ipaddr(), patterns,
435 log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
436 pw->pw_name, get_canonical_hostname(),
437 get_remote_ipaddr());
438 packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
439 get_canonical_hostname());
445 /* Host name matches. */
449 /* Unknown option. */
450 log("Bad options in %.100s file, line %lu: %.50s",
451 SSH_USER_PERMITTED_KEYS, linenum, options);
452 packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
453 SSH_USER_PERMITTED_KEYS, linenum, options);
458 /* Skip the comma, and move to the next option (or break out
459 if there are no more). */
461 fatal("Bugs in auth-rsa.c option processing.");
462 if (*options == ' ' || *options == '\t')
463 break; /* End of options. */
467 /* Process the next option. */
472 /* Break out of the loop if authentication was successful; otherwise
473 continue searching. */
478 /* Restore the privileged uid. */
481 /* Close the file. */
484 /* Clear any mp-int variables. */
489 packet_send_debug("RSA authentication accepted.");
491 /* Return authentication result. */
492 return authenticated;