auth-rsa.c revision 57430
1/* 2 * 3 * auth-rsa.c 4 * 5 * Author: Tatu Ylonen <ylo@cs.hut.fi> 6 * 7 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 8 * All rights reserved 9 * 10 * Created: Mon Mar 27 01:46:52 1995 ylo 11 * 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. 15 * 16 */ 17 18#include "includes.h" 19RCSID("$Id: auth-rsa.c,v 1.18 2000/02/11 10:59:11 markus Exp $"); 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" 27#include "servconf.h" 28 29#include <ssl/rsa.h> 30#include <ssl/md5.h> 31 32/* Flags that may be set in authorized_keys options. */ 33extern int no_port_forwarding_flag; 34extern int no_agent_forwarding_flag; 35extern int no_x11_forwarding_flag; 36extern int no_pty_flag; 37extern char *forced_command; 38extern struct envstring *custom_environment; 39 40/* 41 * Session identifier that is used to bind key exchange and authentication 42 * responses to a particular session. 43 */ 44extern unsigned char session_id[16]; 45 46/* 47 * The .ssh/authorized_keys file contains public keys, one per line, in the 48 * following format: 49 * options bits e n comment 50 * where bits, e and n are decimal numbers, 51 * and comment is any string of characters up to newline. The maximum 52 * length of a line is 8000 characters. See the documentation for a 53 * description of the options. 54 */ 55 56/* 57 * Performs the RSA authentication challenge-response dialog with the client, 58 * and returns true (non-zero) if the client gave the correct answer to 59 * our challenge; returns zero if the client gives a wrong answer. 60 */ 61 62int 63auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n) 64{ 65 BIGNUM *challenge, *encrypted_challenge; 66 RSA *pk; 67 BN_CTX *ctx; 68 unsigned char buf[32], mdbuf[16], response[16]; 69 MD5_CTX md; 70 unsigned int i; 71 int plen, len; 72 73 encrypted_challenge = BN_new(); 74 challenge = BN_new(); 75 76 /* Generate a random challenge. */ 77 BN_rand(challenge, 256, 0, 0); 78 ctx = BN_CTX_new(); 79 BN_mod(challenge, challenge, n, ctx); 80 BN_CTX_free(ctx); 81 82 /* Create the public key data structure. */ 83 pk = RSA_new(); 84 pk->e = BN_new(); 85 BN_copy(pk->e, e); 86 pk->n = BN_new(); 87 BN_copy(pk->n, n); 88 89 /* Encrypt the challenge with the public key. */ 90 rsa_public_encrypt(encrypted_challenge, challenge, pk); 91 RSA_free(pk); 92 93 /* Send the encrypted challenge to the client. */ 94 packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); 95 packet_put_bignum(encrypted_challenge); 96 packet_send(); 97 BN_clear_free(encrypted_challenge); 98 packet_write_wait(); 99 100 /* Wait for a response. */ 101 packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE); 102 packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE); 103 for (i = 0; i < 16; i++) 104 response[i] = packet_get_char(); 105 106 /* The response is MD5 of decrypted challenge plus session id. */ 107 len = BN_num_bytes(challenge); 108 if (len <= 0 || len > 32) 109 fatal("auth_rsa_challenge_dialog: bad challenge length %d", len); 110 memset(buf, 0, 32); 111 BN_bn2bin(challenge, buf + 32 - len); 112 MD5_Init(&md); 113 MD5_Update(&md, buf, 32); 114 MD5_Update(&md, session_id, 16); 115 MD5_Final(mdbuf, &md); 116 BN_clear_free(challenge); 117 118 /* Verify that the response is the original challenge. */ 119 if (memcmp(response, mdbuf, 16) != 0) { 120 /* Wrong answer. */ 121 return 0; 122 } 123 /* Correct answer. */ 124 return 1; 125} 126 127/* 128 * Performs the RSA authentication dialog with the client. This returns 129 * 0 if the client could not be authenticated, and 1 if authentication was 130 * successful. This may exit if there is a serious protocol violation. 131 */ 132 133int 134auth_rsa(struct passwd *pw, BIGNUM *client_n) 135{ 136 extern ServerOptions options; 137 char line[8192], file[1024]; 138 int authenticated; 139 unsigned int bits; 140 FILE *f; 141 unsigned long linenum = 0; 142 struct stat st; 143 BIGNUM *e, *n; 144 145 /* Temporarily use the user's uid. */ 146 temporarily_use_uid(pw->pw_uid); 147 148 /* The authorized keys. */ 149 snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, 150 SSH_USER_PERMITTED_KEYS); 151 152 /* Fail quietly if file does not exist */ 153 if (stat(file, &st) < 0) { 154 /* Restore the privileged uid. */ 155 restore_uid(); 156 return 0; 157 } 158 /* Open the file containing the authorized keys. */ 159 f = fopen(file, "r"); 160 if (!f) { 161 /* Restore the privileged uid. */ 162 restore_uid(); 163 packet_send_debug("Could not open %.900s for reading.", file); 164 packet_send_debug("If your home is on an NFS volume, it may need to be world-readable."); 165 return 0; 166 } 167 if (options.strict_modes) { 168 int fail = 0; 169 char buf[1024]; 170 /* Check open file in order to avoid open/stat races */ 171 if (fstat(fileno(f), &st) < 0 || 172 (st.st_uid != 0 && st.st_uid != pw->pw_uid) || 173 (st.st_mode & 022) != 0) { 174 snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " 175 "bad ownership or modes for '%s'.", pw->pw_name, file); 176 fail = 1; 177 } else { 178 /* Check path to SSH_USER_PERMITTED_KEYS */ 179 int i; 180 static const char *check[] = { 181 "", SSH_USER_DIR, NULL 182 }; 183 for (i = 0; check[i]; i++) { 184 snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); 185 if (stat(line, &st) < 0 || 186 (st.st_uid != 0 && st.st_uid != pw->pw_uid) || 187 (st.st_mode & 022) != 0) { 188 snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " 189 "bad ownership or modes for '%s'.", pw->pw_name, line); 190 fail = 1; 191 break; 192 } 193 } 194 } 195 if (fail) { 196 log(buf); 197 packet_send_debug(buf); 198 restore_uid(); 199 return 0; 200 } 201 } 202 /* Flag indicating whether authentication has succeeded. */ 203 authenticated = 0; 204 205 e = BN_new(); 206 n = BN_new(); 207 208 /* 209 * Go though the accepted keys, looking for the current key. If 210 * found, perform a challenge-response dialog to verify that the 211 * user really has the corresponding private key. 212 */ 213 while (fgets(line, sizeof(line), f)) { 214 char *cp; 215 char *options; 216 217 linenum++; 218 219 /* Skip leading whitespace, empty and comment lines. */ 220 for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 221 ; 222 if (!*cp || *cp == '\n' || *cp == '#') 223 continue; 224 225 /* 226 * Check if there are options for this key, and if so, 227 * save their starting address and skip the option part 228 * for now. If there are no options, set the starting 229 * address to NULL. 230 */ 231 if (*cp < '0' || *cp > '9') { 232 int quoted = 0; 233 options = cp; 234 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { 235 if (*cp == '\\' && cp[1] == '"') 236 cp++; /* Skip both */ 237 else if (*cp == '"') 238 quoted = !quoted; 239 } 240 } else 241 options = NULL; 242 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); 249 continue; 250 } 251 /* cp now points to the comment part. */ 252 253 /* Check if the we have found the desired key (identified by its modulus). */ 254 if (BN_cmp(n, client_n) != 0) 255 continue; 256 257 /* check the real bits */ 258 if (bits != BN_num_bits(n)) 259 log("Warning: %s, line %ld: keysize mismatch: " 260 "actual %d vs. announced %d.", 261 file, linenum, BN_num_bits(n), bits); 262 263 /* We have found the desired key. */ 264 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."); 270 continue; 271 } 272 /* 273 * Correct response. The client has been successfully 274 * authenticated. Note that we have not yet processed the 275 * options; this will be reset if the options cause the 276 * authentication to be rejected. 277 */ 278 authenticated = 1; 279 280 /* RSA part of authentication was accepted. Now process the options. */ 281 if (options) { 282 while (*options && *options != ' ' && *options != '\t') { 283 cp = "no-port-forwarding"; 284 if (strncmp(options, cp, strlen(cp)) == 0) { 285 packet_send_debug("Port forwarding disabled."); 286 no_port_forwarding_flag = 1; 287 options += strlen(cp); 288 goto next_option; 289 } 290 cp = "no-agent-forwarding"; 291 if (strncmp(options, cp, strlen(cp)) == 0) { 292 packet_send_debug("Agent forwarding disabled."); 293 no_agent_forwarding_flag = 1; 294 options += strlen(cp); 295 goto next_option; 296 } 297 cp = "no-X11-forwarding"; 298 if (strncmp(options, cp, strlen(cp)) == 0) { 299 packet_send_debug("X11 forwarding disabled."); 300 no_x11_forwarding_flag = 1; 301 options += strlen(cp); 302 goto next_option; 303 } 304 cp = "no-pty"; 305 if (strncmp(options, cp, strlen(cp)) == 0) { 306 packet_send_debug("Pty allocation disabled."); 307 no_pty_flag = 1; 308 options += strlen(cp); 309 goto next_option; 310 } 311 cp = "command=\""; 312 if (strncmp(options, cp, strlen(cp)) == 0) { 313 int i; 314 options += strlen(cp); 315 forced_command = xmalloc(strlen(options) + 1); 316 i = 0; 317 while (*options) { 318 if (*options == '"') 319 break; 320 if (*options == '\\' && options[1] == '"') { 321 options += 2; 322 forced_command[i++] = '"'; 323 continue; 324 } 325 forced_command[i++] = *options++; 326 } 327 if (!*options) { 328 debug("%.100s, line %lu: missing end quote", 329 SSH_USER_PERMITTED_KEYS, linenum); 330 packet_send_debug("%.100s, line %lu: missing end quote", 331 SSH_USER_PERMITTED_KEYS, linenum); 332 continue; 333 } 334 forced_command[i] = 0; 335 packet_send_debug("Forced command: %.900s", forced_command); 336 options++; 337 goto next_option; 338 } 339 cp = "environment=\""; 340 if (strncmp(options, cp, strlen(cp)) == 0) { 341 int i; 342 char *s; 343 struct envstring *new_envstring; 344 options += strlen(cp); 345 s = xmalloc(strlen(options) + 1); 346 i = 0; 347 while (*options) { 348 if (*options == '"') 349 break; 350 if (*options == '\\' && options[1] == '"') { 351 options += 2; 352 s[i++] = '"'; 353 continue; 354 } 355 s[i++] = *options++; 356 } 357 if (!*options) { 358 debug("%.100s, line %lu: missing end quote", 359 SSH_USER_PERMITTED_KEYS, linenum); 360 packet_send_debug("%.100s, line %lu: missing end quote", 361 SSH_USER_PERMITTED_KEYS, linenum); 362 continue; 363 } 364 s[i] = 0; 365 packet_send_debug("Adding to environment: %.900s", s); 366 debug("Adding to environment: %.900s", s); 367 options++; 368 new_envstring = xmalloc(sizeof(struct envstring)); 369 new_envstring->s = s; 370 new_envstring->next = custom_environment; 371 custom_environment = new_envstring; 372 goto next_option; 373 } 374 cp = "from=\""; 375 if (strncmp(options, cp, strlen(cp)) == 0) { 376 char *patterns = xmalloc(strlen(options) + 1); 377 int i; 378 options += strlen(cp); 379 i = 0; 380 while (*options) { 381 if (*options == '"') 382 break; 383 if (*options == '\\' && options[1] == '"') { 384 options += 2; 385 patterns[i++] = '"'; 386 continue; 387 } 388 patterns[i++] = *options++; 389 } 390 if (!*options) { 391 debug("%.100s, line %lu: missing end quote", 392 SSH_USER_PERMITTED_KEYS, linenum); 393 packet_send_debug("%.100s, line %lu: missing end quote", 394 SSH_USER_PERMITTED_KEYS, linenum); 395 continue; 396 } 397 patterns[i] = 0; 398 options++; 399 if (!match_hostname(get_canonical_hostname(), patterns, 400 strlen(patterns)) && 401 !match_hostname(get_remote_ipaddr(), patterns, 402 strlen(patterns))) { 403 log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).", 404 pw->pw_name, get_canonical_hostname(), 405 get_remote_ipaddr()); 406 packet_send_debug("Your host '%.200s' is not permitted to use this key for login.", 407 get_canonical_hostname()); 408 xfree(patterns); 409 /* key invalid for this host, reset flags */ 410 authenticated = 0; 411 no_agent_forwarding_flag = 0; 412 no_port_forwarding_flag = 0; 413 no_pty_flag = 0; 414 no_x11_forwarding_flag = 0; 415 while (custom_environment) { 416 struct envstring *ce = custom_environment; 417 custom_environment = ce->next; 418 xfree(ce->s); 419 xfree(ce); 420 } 421 if (forced_command) { 422 xfree(forced_command); 423 forced_command = NULL; 424 } 425 break; 426 } 427 xfree(patterns); 428 /* Host name matches. */ 429 goto next_option; 430 } 431 bad_option: 432 log("Bad options in %.100s file, line %lu: %.50s", 433 SSH_USER_PERMITTED_KEYS, linenum, options); 434 packet_send_debug("Bad options in %.100s file, line %lu: %.50s", 435 SSH_USER_PERMITTED_KEYS, linenum, options); 436 authenticated = 0; 437 break; 438 439 next_option: 440 /* 441 * Skip the comma, and move to the next option 442 * (or break out if there are no more). 443 */ 444 if (!*options) 445 fatal("Bugs in auth-rsa.c option processing."); 446 if (*options == ' ' || *options == '\t') 447 break; /* End of options. */ 448 if (*options != ',') 449 goto bad_option; 450 options++; 451 /* Process the next option. */ 452 continue; 453 } 454 } 455 /* 456 * Break out of the loop if authentication was successful; 457 * otherwise continue searching. 458 */ 459 if (authenticated) 460 break; 461 } 462 463 /* Restore the privileged uid. */ 464 restore_uid(); 465 466 /* Close the file. */ 467 fclose(f); 468 469 BN_clear_free(n); 470 BN_clear_free(e); 471 472 if (authenticated) 473 packet_send_debug("RSA authentication accepted."); 474 475 /* Return authentication result. */ 476 return authenticated; 477} 478