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(unsigned int bits, 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);
105 BN_bn2bin(challenge, buf + 32 - len);
107 MD5_Update(&md, buf, 32);
108 MD5_Update(&md, session_id, 16);
109 MD5_Final(mdbuf, &md);
111 /* We will no longer need these. */
112 BN_clear_free(encrypted_challenge);
113 BN_clear_free(challenge);
117 /* Wait for a response. */
118 packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE);
119 packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE);
120 for (i = 0; i < 16; i++)
121 response[i] = packet_get_char();
123 /* Verify that the response is the original challenge. */
124 if (memcmp(response, mdbuf, 16) != 0)
130 /* Correct answer. */
134 /* Performs the RSA authentication dialog with the client. This returns
135 0 if the client could not be authenticated, and 1 if authentication was
136 successful. This may exit if there is a serious protocol violation. */
139 auth_rsa(struct passwd *pw, BIGNUM *client_n, int strict_modes)
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(line, sizeof line, "%.500s/%.100s", pw->pw_dir,
154 SSH_USER_PERMITTED_KEYS);
156 /* Fail quietly if file does not exist */
157 if (stat(line, &st) < 0)
159 /* Restore the privileged uid. */
164 /* Open the file containing the authorized keys. */
165 f = fopen(line, "r");
168 /* Restore the privileged uid. */
170 packet_send_debug("Could not open %.900s for reading.", line);
171 packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
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, line);
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 if the we have found the desired key (identified by its
268 if (BN_cmp(n, client_n) != 0)
269 continue; /* Wrong key. */
271 /* We have found the desired key. */
273 /* Perform the challenge-response dialog for this key. */
274 if (!auth_rsa_challenge_dialog(bits, e, n))
276 /* Wrong response. */
277 log("Wrong response to RSA authentication challenge.");
278 packet_send_debug("Wrong response to RSA authentication challenge.");
282 /* Correct response. The client has been successfully authenticated.
283 Note that we have not yet processed the options; this will be reset
284 if the options cause the authentication to be rejected. */
287 /* RSA part of authentication was accepted. Now process the options. */
290 while (*options && *options != ' ' && *options != '\t')
292 cp = "no-port-forwarding";
293 if (strncmp(options, cp, strlen(cp)) == 0)
295 packet_send_debug("Port forwarding disabled.");
296 no_port_forwarding_flag = 1;
297 options += strlen(cp);
300 cp = "no-agent-forwarding";
301 if (strncmp(options, cp, strlen(cp)) == 0)
303 packet_send_debug("Agent forwarding disabled.");
304 no_agent_forwarding_flag = 1;
305 options += strlen(cp);
308 cp = "no-X11-forwarding";
309 if (strncmp(options, cp, strlen(cp)) == 0)
311 packet_send_debug("X11 forwarding disabled.");
312 no_x11_forwarding_flag = 1;
313 options += strlen(cp);
317 if (strncmp(options, cp, strlen(cp)) == 0)
319 packet_send_debug("Pty allocation disabled.");
321 options += strlen(cp);
325 if (strncmp(options, cp, strlen(cp)) == 0)
328 options += strlen(cp);
329 forced_command = xmalloc(strlen(options) + 1);
335 if (*options == '\\' && options[1] == '"')
338 forced_command[i++] = '"';
341 forced_command[i++] = *options++;
345 debug("%.100s, line %lu: missing end quote",
346 SSH_USER_PERMITTED_KEYS, linenum);
347 packet_send_debug("%.100s, line %lu: missing end quote",
348 SSH_USER_PERMITTED_KEYS, linenum);
351 forced_command[i] = 0;
352 packet_send_debug("Forced command: %.900s", forced_command);
356 cp = "environment=\"";
357 if (strncmp(options, cp, strlen(cp)) == 0)
361 struct envstring *new_envstring;
362 options += strlen(cp);
363 s = xmalloc(strlen(options) + 1);
369 if (*options == '\\' && options[1] == '"')
379 debug("%.100s, line %lu: missing end quote",
380 SSH_USER_PERMITTED_KEYS, linenum);
381 packet_send_debug("%.100s, line %lu: missing end quote",
382 SSH_USER_PERMITTED_KEYS, linenum);
386 packet_send_debug("Adding to environment: %.900s", s);
387 debug("Adding to environment: %.900s", s);
389 new_envstring = xmalloc(sizeof(struct envstring));
390 new_envstring->s = s;
391 new_envstring->next = custom_environment;
392 custom_environment = new_envstring;
396 if (strncmp(options, cp, strlen(cp)) == 0)
398 char *patterns = xmalloc(strlen(options) + 1);
400 options += strlen(cp);
406 if (*options == '\\' && options[1] == '"')
412 patterns[i++] = *options++;
416 debug("%.100s, line %lu: missing end quote",
417 SSH_USER_PERMITTED_KEYS, linenum);
418 packet_send_debug("%.100s, line %lu: missing end quote",
419 SSH_USER_PERMITTED_KEYS, linenum);
424 if (!match_hostname(get_canonical_hostname(), patterns,
426 !match_hostname(get_remote_ipaddr(), patterns,
429 log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
430 pw->pw_name, get_canonical_hostname(),
431 get_remote_ipaddr());
432 packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
433 get_canonical_hostname());
439 /* Host name matches. */
443 /* Unknown option. */
444 log("Bad options in %.100s file, line %lu: %.50s",
445 SSH_USER_PERMITTED_KEYS, linenum, options);
446 packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
447 SSH_USER_PERMITTED_KEYS, linenum, options);
452 /* Skip the comma, and move to the next option (or break out
453 if there are no more). */
455 fatal("Bugs in auth-rsa.c option processing.");
456 if (*options == ' ' || *options == '\t')
457 break; /* End of options. */
461 /* Process the next option. */
466 /* Break out of the loop if authentication was successful; otherwise
467 continue searching. */
472 /* Restore the privileged uid. */
475 /* Close the file. */
478 /* Clear any mp-int variables. */
483 packet_send_debug("RSA authentication accepted.");
485 /* Return authentication result. */
486 return authenticated;