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.
28 #include <openssl/rsa.h>
29 #include <openssl/md5.h>
31 /* Flags that may be set in authorized_keys options. */
32 extern int no_port_forwarding_flag;
33 extern int no_agent_forwarding_flag;
34 extern int no_x11_forwarding_flag;
35 extern int no_pty_flag;
36 extern char *forced_command;
37 extern struct envstring *custom_environment;
39 /* Session identifier that is used to bind key exchange and authentication
40 responses to a particular session. */
41 extern unsigned char session_id[16];
43 /* The .ssh/authorized_keys file contains public keys, one per line, in the
45 options bits e n comment
46 where bits, e and n are decimal numbers,
47 and comment is any string of characters up to newline. The maximum
48 length of a line is 8000 characters. See the documentation for a
49 description of the options.
52 /* Performs the RSA authentication challenge-response dialog with the client,
53 and returns true (non-zero) if the client gave the correct answer to
54 our challenge; returns zero if the client gives a wrong answer. */
57 auth_rsa_challenge_dialog(unsigned int bits, BIGNUM *e, BIGNUM *n)
59 BIGNUM *challenge, *encrypted_challenge, *aux;
61 BN_CTX *ctx = BN_CTX_new();
62 unsigned char buf[32], mdbuf[16], response[16];
67 encrypted_challenge = BN_new();
71 /* Generate a random challenge. */
72 BN_rand(challenge, 256, 0, 0);
73 BN_mod(challenge, challenge, n, ctx);
75 /* Create the public key data structure. */
82 /* Encrypt the challenge with the public key. */
83 rsa_public_encrypt(encrypted_challenge, challenge, pk);
86 /* Send the encrypted challenge to the client. */
87 packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
88 packet_put_bignum(encrypted_challenge);
92 /* The response is MD5 of decrypted challenge plus session id. */
93 len = BN_num_bytes(challenge);
94 assert(len <= 32 && len);
96 BN_bn2bin(challenge, buf + 32 - len);
98 MD5_Update(&md, buf, 32);
99 MD5_Update(&md, session_id, 16);
100 MD5_Final(mdbuf, &md);
102 /* We will no longer need these. */
103 BN_clear_free(encrypted_challenge);
104 BN_clear_free(challenge);
108 /* Wait for a response. */
109 packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE);
110 packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE);
111 for (i = 0; i < 16; i++)
112 response[i] = packet_get_char();
114 /* Verify that the response is the original challenge. */
115 if (memcmp(response, mdbuf, 16) != 0)
121 /* Correct answer. */
125 /* Performs the RSA authentication dialog with the client. This returns
126 0 if the client could not be authenticated, and 1 if authentication was
127 successful. This may exit if there is a serious protocol violation. */
130 auth_rsa(struct passwd *pw, BIGNUM *client_n, int strict_modes)
136 unsigned long linenum = 0;
140 /* Temporarily use the user's uid. */
141 temporarily_use_uid(pw->pw_uid);
143 /* The authorized keys. */
144 snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir,
145 SSH_USER_PERMITTED_KEYS);
147 /* Fail quietly if file does not exist */
148 if (stat(line, &st) < 0)
150 /* Restore the privileged uid. */
155 /* Open the file containing the authorized keys. */
156 f = fopen(line, "r");
159 /* Restore the privileged uid. */
161 packet_send_debug("Could not open %.900s for reading.", line);
162 packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
169 /* Check open file in order to avoid open/stat races */
170 if (fstat(fileno(f), &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);
177 /* Check path to SSH_USER_PERMITTED_KEYS */
179 static const char *check[] = {
180 "", SSH_USER_DIR, NULL
182 for (i=0; check[i]; i++) {
183 snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]);
184 if (stat(line, &st) < 0 ||
185 (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
186 (st.st_mode & 022) != 0) {
187 snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: "
188 "bad ownership or modes for '%s'.", pw->pw_name, line);
196 packet_send_debug(buf);
202 /* Flag indicating whether authentication has succeeded. */
205 /* Initialize mp-int variables. */
209 /* Go though the accepted keys, looking for the current key. If found,
210 perform a challenge-response dialog to verify that the user really has
211 the corresponding private key. */
212 while (fgets(line, sizeof(line), f))
219 /* Skip leading whitespace. */
220 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, save their
228 starting address and skip the option part for now. If there are no
229 options, set the starting address to NULL. */
230 if (*cp < '0' || *cp > '9')
234 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++)
236 if (*cp == '\\' && cp[1] == '"')
237 cp++; /* Skip both */
246 /* Parse the key from the line. */
247 if (!auth_rsa_read_key(&cp, &bits, e, n))
249 debug("%.100s, line %lu: bad key syntax",
250 SSH_USER_PERMITTED_KEYS, linenum);
251 packet_send_debug("%.100s, line %lu: bad key syntax",
252 SSH_USER_PERMITTED_KEYS, linenum);
255 /* cp now points to the comment part. */
257 /* Check if the we have found the desired key (identified by its
259 if (BN_cmp(n, client_n) != 0)
260 continue; /* Wrong key. */
262 /* We have found the desired key. */
264 /* Perform the challenge-response dialog for this key. */
265 if (!auth_rsa_challenge_dialog(bits, e, n))
267 /* Wrong response. */
268 log("Wrong response to RSA authentication challenge.");
269 packet_send_debug("Wrong response to RSA authentication challenge.");
273 /* Correct response. The client has been successfully authenticated.
274 Note that we have not yet processed the options; this will be reset
275 if the options cause the authentication to be rejected. */
278 /* RSA part of authentication was accepted. Now process the options. */
281 while (*options && *options != ' ' && *options != '\t')
283 cp = "no-port-forwarding";
284 if (strncmp(options, cp, strlen(cp)) == 0)
286 packet_send_debug("Port forwarding disabled.");
287 no_port_forwarding_flag = 1;
288 options += strlen(cp);
291 cp = "no-agent-forwarding";
292 if (strncmp(options, cp, strlen(cp)) == 0)
294 packet_send_debug("Agent forwarding disabled.");
295 no_agent_forwarding_flag = 1;
296 options += strlen(cp);
299 cp = "no-X11-forwarding";
300 if (strncmp(options, cp, strlen(cp)) == 0)
302 packet_send_debug("X11 forwarding disabled.");
303 no_x11_forwarding_flag = 1;
304 options += strlen(cp);
308 if (strncmp(options, cp, strlen(cp)) == 0)
310 packet_send_debug("Pty allocation disabled.");
312 options += strlen(cp);
316 if (strncmp(options, cp, strlen(cp)) == 0)
319 options += strlen(cp);
320 forced_command = xmalloc(strlen(options) + 1);
326 if (*options == '\\' && options[1] == '"')
329 forced_command[i++] = '"';
332 forced_command[i++] = *options++;
336 debug("%.100s, line %lu: missing end quote",
337 SSH_USER_PERMITTED_KEYS, linenum);
338 packet_send_debug("%.100s, line %lu: missing end quote",
339 SSH_USER_PERMITTED_KEYS, linenum);
342 forced_command[i] = 0;
343 packet_send_debug("Forced command: %.900s", forced_command);
347 cp = "environment=\"";
348 if (strncmp(options, cp, strlen(cp)) == 0)
352 struct envstring *new_envstring;
353 options += strlen(cp);
354 s = xmalloc(strlen(options) + 1);
360 if (*options == '\\' && options[1] == '"')
370 debug("%.100s, line %lu: missing end quote",
371 SSH_USER_PERMITTED_KEYS, linenum);
372 packet_send_debug("%.100s, line %lu: missing end quote",
373 SSH_USER_PERMITTED_KEYS, linenum);
377 packet_send_debug("Adding to environment: %.900s", s);
378 debug("Adding to environment: %.900s", s);
380 new_envstring = xmalloc(sizeof(struct envstring));
381 new_envstring->s = s;
382 new_envstring->next = custom_environment;
383 custom_environment = new_envstring;
387 if (strncmp(options, cp, strlen(cp)) == 0)
389 char *patterns = xmalloc(strlen(options) + 1);
391 options += strlen(cp);
397 if (*options == '\\' && options[1] == '"')
403 patterns[i++] = *options++;
407 debug("%.100s, line %lu: missing end quote",
408 SSH_USER_PERMITTED_KEYS, linenum);
409 packet_send_debug("%.100s, line %lu: missing end quote",
410 SSH_USER_PERMITTED_KEYS, linenum);
415 if (!match_hostname(get_canonical_hostname(), patterns,
417 !match_hostname(get_remote_ipaddr(), patterns,
420 log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
421 pw->pw_name, get_canonical_hostname(),
422 get_remote_ipaddr());
423 packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
424 get_canonical_hostname());
430 /* Host name matches. */
434 /* Unknown option. */
435 log("Bad options in %.100s file, line %lu: %.50s",
436 SSH_USER_PERMITTED_KEYS, linenum, options);
437 packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
438 SSH_USER_PERMITTED_KEYS, linenum, options);
443 /* Skip the comma, and move to the next option (or break out
444 if there are no more). */
446 fatal("Bugs in auth-rsa.c option processing.");
447 if (*options == ' ' || *options == '\t')
448 break; /* End of options. */
452 /* Process the next option. */
457 /* Break out of the loop if authentication was successful; otherwise
458 continue searching. */
463 /* Restore the privileged uid. */
466 /* Close the file. */
469 /* Clear any mp-int variables. */
474 packet_send_debug("RSA authentication accepted.");
476 /* Return authentication result. */
477 return authenticated;