14 15#include <openssl/bn.h> 16#include <openssl/dsa.h> 17#include <openssl/rsa.h> 18 19#include "xmalloc.h" 20#include "rsa.h" 21#include "ssh.h" 22#include "buffer.h" 23#include "packet.h" 24#include "uidswap.h" 25#include "compat.h" 26#include "readconf.h" 27#include "key.h" 28#include "sshconnect.h" 29#include "hostfile.h" 30 31char *client_version_string = NULL; 32char *server_version_string = NULL; 33 34extern Options options; 35extern char *__progname; 36 37/* 38 * Connect to the given ssh server using a proxy command. 39 */ 40int 41ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, 42 const char *proxy_command) 43{ 44 Buffer command; 45 const char *cp; 46 char *command_string; 47 int pin[2], pout[2]; 48 pid_t pid; 49 char strport[NI_MAXSERV]; 50 51 /* Convert the port number into a string. */ 52 snprintf(strport, sizeof strport, "%hu", port); 53 54 /* Build the final command string in the buffer by making the 55 appropriate substitutions to the given proxy command. */ 56 buffer_init(&command); 57 for (cp = proxy_command; *cp; cp++) { 58 if (cp[0] == '%' && cp[1] == '%') { 59 buffer_append(&command, "%", 1); 60 cp++; 61 continue; 62 } 63 if (cp[0] == '%' && cp[1] == 'h') { 64 buffer_append(&command, host, strlen(host)); 65 cp++; 66 continue; 67 } 68 if (cp[0] == '%' && cp[1] == 'p') { 69 buffer_append(&command, strport, strlen(strport)); 70 cp++; 71 continue; 72 } 73 buffer_append(&command, cp, 1); 74 } 75 buffer_append(&command, "\0", 1); 76 77 /* Get the final command string. */ 78 command_string = buffer_ptr(&command); 79 80 /* Create pipes for communicating with the proxy. */ 81 if (pipe(pin) < 0 || pipe(pout) < 0) 82 fatal("Could not create pipes to communicate with the proxy: %.100s", 83 strerror(errno)); 84 85 debug("Executing proxy command: %.500s", command_string); 86 87 /* Fork and execute the proxy command. */ 88 if ((pid = fork()) == 0) { 89 char *argv[10]; 90 91 /* Child. Permanently give up superuser privileges. */ 92 permanently_set_uid(original_real_uid); 93 94 /* Redirect stdin and stdout. */ 95 close(pin[1]); 96 if (pin[0] != 0) { 97 if (dup2(pin[0], 0) < 0) 98 perror("dup2 stdin"); 99 close(pin[0]); 100 } 101 close(pout[0]); 102 if (dup2(pout[1], 1) < 0) 103 perror("dup2 stdout"); 104 /* Cannot be 1 because pin allocated two descriptors. */ 105 close(pout[1]); 106 107 /* Stderr is left as it is so that error messages get 108 printed on the user's terminal. */ 109 argv[0] = "/bin/sh"; 110 argv[1] = "-c"; 111 argv[2] = command_string; 112 argv[3] = NULL; 113 114 /* Execute the proxy command. Note that we gave up any 115 extra privileges above. */ 116 execv("/bin/sh", argv); 117 perror("/bin/sh"); 118 exit(1); 119 } 120 /* Parent. */ 121 if (pid < 0) 122 fatal("fork failed: %.100s", strerror(errno)); 123 124 /* Close child side of the descriptors. */ 125 close(pin[0]); 126 close(pout[1]); 127 128 /* Free the command name. */ 129 buffer_free(&command); 130 131 /* Set the connection file descriptors. */ 132 packet_set_connection(pout[0], pin[1]); 133 134 return 1; 135} 136 137/* 138 * Creates a (possibly privileged) socket for use as the ssh connection. 139 */ 140int 141ssh_create_socket(uid_t original_real_uid, int privileged, int family) 142{ 143 int sock; 144 145 /* 146 * If we are running as root and want to connect to a privileged 147 * port, bind our own socket to a privileged port. 148 */ 149 if (privileged) { 150 int p = IPPORT_RESERVED - 1; 151 sock = rresvport_af(&p, family); 152 if (sock < 0) 153 error("rresvport: af=%d %.100s", family, strerror(errno)); 154 else 155 debug("Allocated local port %d.", p); 156 } else { 157 /* 158 * Just create an ordinary socket on arbitrary port. We use 159 * the user's uid to create the socket. 160 */ 161 temporarily_use_uid(original_real_uid); 162 sock = socket(family, SOCK_STREAM, 0); 163 if (sock < 0) 164 error("socket: %.100s", strerror(errno)); 165 restore_uid(); 166 } 167 return sock; 168} 169 170/* 171 * Opens a TCP/IP connection to the remote server on the given host. 172 * The address of the remote host will be returned in hostaddr. 173 * If port is 0, the default port will be used. If anonymous is zero, 174 * a privileged port will be allocated to make the connection. 175 * This requires super-user privileges if anonymous is false. 176 * Connection_attempts specifies the maximum number of tries (one per 177 * second). If proxy_command is non-NULL, it specifies the command (with %h 178 * and %p substituted for host and port, respectively) to use to contact 179 * the daemon. 180 */ 181int 182ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 183 u_short port, int connection_attempts, 184 int anonymous, uid_t original_real_uid, 185 const char *proxy_command) 186{ 187 int sock = -1, attempt; 188 struct servent *sp; 189 struct addrinfo hints, *ai, *aitop; 190 char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 191 int gaierr; 192 struct linger linger; 193 194 debug("ssh_connect: getuid %d geteuid %d anon %d", 195 (int) getuid(), (int) geteuid(), anonymous); 196 197 /* Get default port if port has not been set. */ 198 if (port == 0) { 199 sp = getservbyname(SSH_SERVICE_NAME, "tcp"); 200 if (sp) 201 port = ntohs(sp->s_port); 202 else 203 port = SSH_DEFAULT_PORT; 204 } 205 /* If a proxy command is given, connect using it. */ 206 if (proxy_command != NULL) 207 return ssh_proxy_connect(host, port, original_real_uid, proxy_command); 208 209 /* No proxy command. */ 210 211 memset(&hints, 0, sizeof(hints)); 212 hints.ai_family = IPv4or6; 213 hints.ai_socktype = SOCK_STREAM; 214 snprintf(strport, sizeof strport, "%d", port); 215 if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 216 fatal("%s: %.100s: %s", __progname, host, 217 gai_strerror(gaierr)); 218 219 /* 220 * Try to connect several times. On some machines, the first time 221 * will sometimes fail. In general socket code appears to behave 222 * quite magically on many machines. 223 */ 224 for (attempt = 0; attempt < connection_attempts; attempt++) { 225 if (attempt > 0) 226 debug("Trying again..."); 227 228 /* Loop through addresses for this host, and try each one in 229 sequence until the connection succeeds. */ 230 for (ai = aitop; ai; ai = ai->ai_next) { 231 if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 232 continue; 233 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 234 ntop, sizeof(ntop), strport, sizeof(strport), 235 NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 236 error("ssh_connect: getnameinfo failed"); 237 continue; 238 } 239 debug("Connecting to %.200s [%.100s] port %s.", 240 host, ntop, strport); 241 242 /* Create a socket for connecting. */ 243 sock = ssh_create_socket(original_real_uid, 244 !anonymous && geteuid() == 0 && port < IPPORT_RESERVED, 245 ai->ai_family); 246 if (sock < 0) 247 continue; 248 249 /* Connect to the host. We use the user's uid in the 250 * hope that it will help with tcp_wrappers showing 251 * the remote uid as root. 252 */ 253 temporarily_use_uid(original_real_uid); 254 if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { 255 /* Successful connection. */
| 14 15#include <openssl/bn.h> 16#include <openssl/dsa.h> 17#include <openssl/rsa.h> 18 19#include "xmalloc.h" 20#include "rsa.h" 21#include "ssh.h" 22#include "buffer.h" 23#include "packet.h" 24#include "uidswap.h" 25#include "compat.h" 26#include "readconf.h" 27#include "key.h" 28#include "sshconnect.h" 29#include "hostfile.h" 30 31char *client_version_string = NULL; 32char *server_version_string = NULL; 33 34extern Options options; 35extern char *__progname; 36 37/* 38 * Connect to the given ssh server using a proxy command. 39 */ 40int 41ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, 42 const char *proxy_command) 43{ 44 Buffer command; 45 const char *cp; 46 char *command_string; 47 int pin[2], pout[2]; 48 pid_t pid; 49 char strport[NI_MAXSERV]; 50 51 /* Convert the port number into a string. */ 52 snprintf(strport, sizeof strport, "%hu", port); 53 54 /* Build the final command string in the buffer by making the 55 appropriate substitutions to the given proxy command. */ 56 buffer_init(&command); 57 for (cp = proxy_command; *cp; cp++) { 58 if (cp[0] == '%' && cp[1] == '%') { 59 buffer_append(&command, "%", 1); 60 cp++; 61 continue; 62 } 63 if (cp[0] == '%' && cp[1] == 'h') { 64 buffer_append(&command, host, strlen(host)); 65 cp++; 66 continue; 67 } 68 if (cp[0] == '%' && cp[1] == 'p') { 69 buffer_append(&command, strport, strlen(strport)); 70 cp++; 71 continue; 72 } 73 buffer_append(&command, cp, 1); 74 } 75 buffer_append(&command, "\0", 1); 76 77 /* Get the final command string. */ 78 command_string = buffer_ptr(&command); 79 80 /* Create pipes for communicating with the proxy. */ 81 if (pipe(pin) < 0 || pipe(pout) < 0) 82 fatal("Could not create pipes to communicate with the proxy: %.100s", 83 strerror(errno)); 84 85 debug("Executing proxy command: %.500s", command_string); 86 87 /* Fork and execute the proxy command. */ 88 if ((pid = fork()) == 0) { 89 char *argv[10]; 90 91 /* Child. Permanently give up superuser privileges. */ 92 permanently_set_uid(original_real_uid); 93 94 /* Redirect stdin and stdout. */ 95 close(pin[1]); 96 if (pin[0] != 0) { 97 if (dup2(pin[0], 0) < 0) 98 perror("dup2 stdin"); 99 close(pin[0]); 100 } 101 close(pout[0]); 102 if (dup2(pout[1], 1) < 0) 103 perror("dup2 stdout"); 104 /* Cannot be 1 because pin allocated two descriptors. */ 105 close(pout[1]); 106 107 /* Stderr is left as it is so that error messages get 108 printed on the user's terminal. */ 109 argv[0] = "/bin/sh"; 110 argv[1] = "-c"; 111 argv[2] = command_string; 112 argv[3] = NULL; 113 114 /* Execute the proxy command. Note that we gave up any 115 extra privileges above. */ 116 execv("/bin/sh", argv); 117 perror("/bin/sh"); 118 exit(1); 119 } 120 /* Parent. */ 121 if (pid < 0) 122 fatal("fork failed: %.100s", strerror(errno)); 123 124 /* Close child side of the descriptors. */ 125 close(pin[0]); 126 close(pout[1]); 127 128 /* Free the command name. */ 129 buffer_free(&command); 130 131 /* Set the connection file descriptors. */ 132 packet_set_connection(pout[0], pin[1]); 133 134 return 1; 135} 136 137/* 138 * Creates a (possibly privileged) socket for use as the ssh connection. 139 */ 140int 141ssh_create_socket(uid_t original_real_uid, int privileged, int family) 142{ 143 int sock; 144 145 /* 146 * If we are running as root and want to connect to a privileged 147 * port, bind our own socket to a privileged port. 148 */ 149 if (privileged) { 150 int p = IPPORT_RESERVED - 1; 151 sock = rresvport_af(&p, family); 152 if (sock < 0) 153 error("rresvport: af=%d %.100s", family, strerror(errno)); 154 else 155 debug("Allocated local port %d.", p); 156 } else { 157 /* 158 * Just create an ordinary socket on arbitrary port. We use 159 * the user's uid to create the socket. 160 */ 161 temporarily_use_uid(original_real_uid); 162 sock = socket(family, SOCK_STREAM, 0); 163 if (sock < 0) 164 error("socket: %.100s", strerror(errno)); 165 restore_uid(); 166 } 167 return sock; 168} 169 170/* 171 * Opens a TCP/IP connection to the remote server on the given host. 172 * The address of the remote host will be returned in hostaddr. 173 * If port is 0, the default port will be used. If anonymous is zero, 174 * a privileged port will be allocated to make the connection. 175 * This requires super-user privileges if anonymous is false. 176 * Connection_attempts specifies the maximum number of tries (one per 177 * second). If proxy_command is non-NULL, it specifies the command (with %h 178 * and %p substituted for host and port, respectively) to use to contact 179 * the daemon. 180 */ 181int 182ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 183 u_short port, int connection_attempts, 184 int anonymous, uid_t original_real_uid, 185 const char *proxy_command) 186{ 187 int sock = -1, attempt; 188 struct servent *sp; 189 struct addrinfo hints, *ai, *aitop; 190 char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 191 int gaierr; 192 struct linger linger; 193 194 debug("ssh_connect: getuid %d geteuid %d anon %d", 195 (int) getuid(), (int) geteuid(), anonymous); 196 197 /* Get default port if port has not been set. */ 198 if (port == 0) { 199 sp = getservbyname(SSH_SERVICE_NAME, "tcp"); 200 if (sp) 201 port = ntohs(sp->s_port); 202 else 203 port = SSH_DEFAULT_PORT; 204 } 205 /* If a proxy command is given, connect using it. */ 206 if (proxy_command != NULL) 207 return ssh_proxy_connect(host, port, original_real_uid, proxy_command); 208 209 /* No proxy command. */ 210 211 memset(&hints, 0, sizeof(hints)); 212 hints.ai_family = IPv4or6; 213 hints.ai_socktype = SOCK_STREAM; 214 snprintf(strport, sizeof strport, "%d", port); 215 if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 216 fatal("%s: %.100s: %s", __progname, host, 217 gai_strerror(gaierr)); 218 219 /* 220 * Try to connect several times. On some machines, the first time 221 * will sometimes fail. In general socket code appears to behave 222 * quite magically on many machines. 223 */ 224 for (attempt = 0; attempt < connection_attempts; attempt++) { 225 if (attempt > 0) 226 debug("Trying again..."); 227 228 /* Loop through addresses for this host, and try each one in 229 sequence until the connection succeeds. */ 230 for (ai = aitop; ai; ai = ai->ai_next) { 231 if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 232 continue; 233 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 234 ntop, sizeof(ntop), strport, sizeof(strport), 235 NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 236 error("ssh_connect: getnameinfo failed"); 237 continue; 238 } 239 debug("Connecting to %.200s [%.100s] port %s.", 240 host, ntop, strport); 241 242 /* Create a socket for connecting. */ 243 sock = ssh_create_socket(original_real_uid, 244 !anonymous && geteuid() == 0 && port < IPPORT_RESERVED, 245 ai->ai_family); 246 if (sock < 0) 247 continue; 248 249 /* Connect to the host. We use the user's uid in the 250 * hope that it will help with tcp_wrappers showing 251 * the remote uid as root. 252 */ 253 temporarily_use_uid(original_real_uid); 254 if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { 255 /* Successful connection. */
|
317/* 318 * Waits for the server identification string, and sends our own 319 * identification string. 320 */ 321void 322ssh_exchange_identification() 323{ 324 char buf[256], remote_version[256]; /* must be same size! */ 325 int remote_major, remote_minor, i, mismatch; 326 int connection_in = packet_get_connection_in(); 327 int connection_out = packet_get_connection_out(); 328 329 /* Read other side\'s version identification. */ 330 for (i = 0; i < sizeof(buf) - 1; i++) { 331 int len = read(connection_in, &buf[i], 1); 332 if (len < 0) 333 fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 334 if (len != 1) 335 fatal("ssh_exchange_identification: Connection closed by remote host"); 336 if (buf[i] == '\r') { 337 buf[i] = '\n'; 338 buf[i + 1] = 0; 339 continue; /**XXX wait for \n */ 340 } 341 if (buf[i] == '\n') { 342 buf[i + 1] = 0; 343 break; 344 } 345 } 346 buf[sizeof(buf) - 1] = 0; 347 server_version_string = xstrdup(buf); 348 349 /* 350 * Check that the versions match. In future this might accept 351 * several versions and set appropriate flags to handle them. 352 */ 353 if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 354 &remote_major, &remote_minor, remote_version) != 3) 355 fatal("Bad remote protocol version identification: '%.100s'", buf); 356 debug("Remote protocol version %d.%d, remote software version %.100s", 357 remote_major, remote_minor, remote_version); 358 359 compat_datafellows(remote_version); 360 mismatch = 0; 361 362 switch(remote_major) { 363 case 1: 364 if (remote_minor == 99 && 365 (options.protocol & SSH_PROTO_2) && 366 !(options.protocol & SSH_PROTO_1_PREFERRED)) { 367 enable_compat20(); 368 break; 369 } 370 if (!(options.protocol & SSH_PROTO_1)) { 371 mismatch = 1; 372 break; 373 } 374 if (remote_minor < 3) { 375 fatal("Remote machine has too old SSH software version."); 376 } else if (remote_minor == 3) { 377 /* We speak 1.3, too. */ 378 enable_compat13(); 379 if (options.forward_agent) { 380 log("Agent forwarding disabled for protocol 1.3"); 381 options.forward_agent = 0; 382 } 383 } 384 break; 385 case 2: 386 if (options.protocol & SSH_PROTO_2) { 387 enable_compat20(); 388 break; 389 } 390 /* FALLTHROUGH */ 391 default: 392 mismatch = 1; 393 break; 394 } 395 if (mismatch) 396 fatal("Protocol major versions differ: %d vs. %d", 397 (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 398 remote_major); 399 if (compat20) 400 packet_set_ssh2_format(); 401 /* Send our own protocol version identification. */ 402 snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 403 compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 404 compat20 ? PROTOCOL_MINOR_2 : PROTOCOL_MINOR_1, 405 SSH_VERSION); 406 if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) 407 fatal("write: %.100s", strerror(errno)); 408 client_version_string = xstrdup(buf); 409 chop(client_version_string); 410 chop(server_version_string); 411 debug("Local version string %.100s", client_version_string); 412} 413 414int 415read_yes_or_no(const char *prompt, int defval) 416{ 417 char buf[1024]; 418 FILE *f; 419 int retval = -1; 420 421 if (isatty(0)) 422 f = stdin; 423 else 424 f = fopen("/dev/tty", "rw"); 425 426 if (f == NULL) 427 return 0; 428 429 fflush(stdout); 430 431 while (1) { 432 fprintf(stderr, "%s", prompt); 433 if (fgets(buf, sizeof(buf), f) == NULL) { 434 /* Print a newline (the prompt probably didn\'t have one). */ 435 fprintf(stderr, "\n"); 436 strlcpy(buf, "no", sizeof buf); 437 } 438 /* Remove newline from response. */ 439 if (strchr(buf, '\n')) 440 *strchr(buf, '\n') = 0; 441 442 if (buf[0] == 0) 443 retval = defval; 444 if (strcmp(buf, "yes") == 0) 445 retval = 1; 446 if (strcmp(buf, "no") == 0) 447 retval = 0; 448 449 if (retval != -1) { 450 if (f != stdin) 451 fclose(f); 452 return retval; 453 } 454 } 455} 456 457/* 458 * check whether the supplied host key is valid, return only if ok. 459 */ 460 461void 462check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, 463 const char *user_hostfile, const char *system_hostfile) 464{ 465 Key *file_key; 466 char *type = key_type(host_key); 467 char *ip = NULL; 468 char hostline[1000], *hostp; 469 HostStatus host_status; 470 HostStatus ip_status; 471 int local = 0, host_ip_differ = 0; 472 char ntop[NI_MAXHOST]; 473 474 /* 475 * Force accepting of the host key for loopback/localhost. The 476 * problem is that if the home directory is NFS-mounted to multiple 477 * machines, localhost will refer to a different machine in each of 478 * them, and the user will get bogus HOST_CHANGED warnings. This 479 * essentially disables host authentication for localhost; however, 480 * this is probably not a real problem. 481 */ 482 /** hostaddr == 0! */ 483 switch (hostaddr->sa_family) { 484 case AF_INET: 485 local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 486 break; 487 case AF_INET6: 488 local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 489 break; 490 default: 491 local = 0; 492 break; 493 } 494 if (local) { 495 debug("Forcing accepting of host key for loopback/localhost."); 496 return; 497 } 498 499 /* 500 * Turn off check_host_ip for proxy connects, since 501 * we don't have the remote ip-address 502 */ 503 if (options.proxy_command != NULL && options.check_host_ip) 504 options.check_host_ip = 0; 505 506 if (options.check_host_ip) { 507 if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), 508 NULL, 0, NI_NUMERICHOST) != 0) 509 fatal("check_host_key: getnameinfo failed"); 510 ip = xstrdup(ntop); 511 } 512 513 /* 514 * Store the host key from the known host file in here so that we can 515 * compare it with the key for the IP address. 516 */ 517 file_key = key_new(host_key->type); 518 519 /* 520 * Check if the host key is present in the user\'s list of known 521 * hosts or in the systemwide list. 522 */ 523 host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key); 524 if (host_status == HOST_NEW) 525 host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key); 526 /* 527 * Also perform check for the ip address, skip the check if we are 528 * localhost or the hostname was an ip address to begin with 529 */ 530 if (options.check_host_ip && !local && strcmp(host, ip)) { 531 Key *ip_key = key_new(host_key->type); 532 ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key); 533 534 if (ip_status == HOST_NEW) 535 ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key); 536 if (host_status == HOST_CHANGED && 537 (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 538 host_ip_differ = 1; 539 540 key_free(ip_key); 541 } else 542 ip_status = host_status; 543 544 key_free(file_key); 545 546 switch (host_status) { 547 case HOST_OK: 548 /* The host is known and the key matches. */ 549 debug("Host '%.200s' is known and matches the %s host key.", 550 host, type); 551 if (options.check_host_ip) { 552 if (ip_status == HOST_NEW) { 553 if (!add_host_to_hostfile(user_hostfile, ip, host_key)) 554 log("Failed to add the %s host key for IP address '%.30s' to the list of known hosts (%.30s).", 555 type, ip, user_hostfile); 556 else 557 log("Warning: Permanently added the %s host key for IP address '%.30s' to the list of known hosts.", 558 type, ip); 559 } else if (ip_status != HOST_OK) 560 log("Warning: the %s host key for '%.200s' differs from the key for the IP address '%.30s'", 561 type, host, ip); 562 } 563 break; 564 case HOST_NEW: 565 /* The host is new. */ 566 if (options.strict_host_key_checking == 1) { 567 /* User has requested strict host key checking. We will not add the host key 568 automatically. The only alternative left is to abort. */ 569 fatal("No %s host key is known for %.200s and you have requested strict checking.", type, host); 570 } else if (options.strict_host_key_checking == 2) { 571 /* The default */ 572 char prompt[1024]; 573 char *fp = key_fingerprint(host_key); 574 snprintf(prompt, sizeof(prompt), 575 "The authenticity of host '%.200s' can't be established.\n" 576 "%s key fingerprint is %s.\n" 577 "Are you sure you want to continue connecting (yes/no)? ", 578 host, type, fp); 579 if (!read_yes_or_no(prompt, -1)) 580 fatal("Aborted by user!\n"); 581 } 582 if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) { 583 snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 584 hostp = hostline; 585 } else 586 hostp = host; 587 588 /* If not in strict mode, add the key automatically to the local known_hosts file. */ 589 if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) 590 log("Failed to add the host to the list of known hosts (%.500s).", 591 user_hostfile); 592 else 593 log("Warning: Permanently added '%.200s' (%s) to the list of known hosts.", 594 hostp, type); 595 break; 596 case HOST_CHANGED: 597 if (options.check_host_ip && host_ip_differ) { 598 char *msg; 599 if (ip_status == HOST_NEW) 600 msg = "is unknown"; 601 else if (ip_status == HOST_OK) 602 msg = "is unchanged"; 603 else 604 msg = "has a different value"; 605 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 606 error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 607 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 608 error("The %s host key for %s has changed,", type, host); 609 error("and the key for the according IP address %s", ip); 610 error("%s. This could either mean that", msg); 611 error("DNS SPOOFING is happening or the IP address for the host"); 612 error("and its host key have changed at the same time"); 613 } 614 /* The host key has changed. */ 615 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 616 error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 617 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 618 error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 619 error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 620 error("It is also possible that the %s host key has just been changed.", type); 621 error("Please contact your system administrator."); 622 error("Add correct host key in %.100s to get rid of this message.", 623 user_hostfile); 624 625 /* 626 * If strict host key checking is in use, the user will have 627 * to edit the key manually and we can only abort. 628 */ 629 if (options.strict_host_key_checking) 630 fatal("%s host key for %.200s has changed and you have requested strict checking.", type, host); 631 632 /* 633 * If strict host key checking has not been requested, allow 634 * the connection but without password authentication or 635 * agent forwarding. 636 */ 637 if (options.password_authentication) { 638 error("Password authentication is disabled to avoid trojan horses."); 639 options.password_authentication = 0; 640 } 641 if (options.forward_agent) { 642 error("Agent forwarding is disabled to avoid trojan horses."); 643 options.forward_agent = 0; 644 } 645 /* 646 * XXX Should permit the user to change to use the new id. 647 * This could be done by converting the host key to an 648 * identifying sentence, tell that the host identifies itself 649 * by that sentence, and ask the user if he/she whishes to 650 * accept the authentication. 651 */ 652 break; 653 } 654 if (options.check_host_ip) 655 xfree(ip); 656} 657 658#ifdef KRB5 659int 660try_krb5_authentication(krb5_context *context, krb5_auth_context *auth_context) 661{ 662 krb5_error_code problem; 663 const char *tkfile; 664 struct stat buf; 665 krb5_ccache ccache = NULL; 666 krb5_creds req_creds; 667 krb5_creds *new_creds = NULL; 668 const char *remotehost; 669 krb5_data ap; 670 int type, payload_len; 671 krb5_ap_rep_enc_part *reply = NULL; 672 int ret; 673 674 memset(&ap, 0, sizeof(ap)); 675 676 problem = krb5_init_context(context); 677 if (problem) { 678 ret = 0; 679 goto out; 680 } 681 682 tkfile = krb5_cc_default_name(*context); 683 if (strncmp(tkfile, "FILE:", 5) == 0) 684 tkfile += 5; 685 686 if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 687 debug("Kerberos V5: could not get default ccache (permission denied)."); 688 ret = 0; 689 goto out; 690 } 691 692 problem = krb5_cc_default(*context, &ccache); 693 if (problem) { 694 ret = 0; 695 goto out; 696 } 697 698 memset(&req_creds, 0, sizeof(req_creds)); 699 700 remotehost = get_canonical_hostname(); 701 702 problem = krb5_sname_to_principal(*context, remotehost, 703 "host", KRB5_NT_SRV_HST, 704 &req_creds.server); 705 if (problem) { 706 ret = 0; 707 goto out; 708 709 } 710 711 problem = krb5_cc_get_principal(*context, ccache, &req_creds.client); 712 if (problem) { 713 ret = 0; 714 goto out; 715 } 716 717 /* creds.session.keytype=ETYPE_DES_CBC_CRC; */ 718 719 problem = krb5_get_credentials(*context, 0, ccache, &req_creds, &new_creds); 720 if (problem) { 721 ret = 0; 722 goto out; 723 } 724 725 problem = krb5_auth_con_init(*context, auth_context); 726 if (problem) { 727 ret = 0; 728 goto out; 729 } 730 731 /* krb5_auth_con_setflags(ssh_context, auth_context, 732 KRB5_AUTH_CONTEXT_RET_TIME); 733 */ 734 problem = krb5_mk_req_extended(*context, auth_context, 735 AP_OPTS_MUTUAL_REQUIRED /*| AP_OPTS_USE_SUBKEY*/ , 736 NULL, new_creds, &ap); 737 if (problem) { 738 ret = 0; 739 goto out; 740 } 741 742 packet_start(SSH_CMSG_AUTH_KRB5); 743 packet_put_string((char *) ap.data, ap.length); 744 packet_send(); 745 packet_write_wait(); 746 747 xfree(ap.data); 748 ap.length = 0; 749 750 type = packet_read(&payload_len); 751 switch (type) { 752 case SSH_SMSG_FAILURE: 753 /* Should really be SSH_SMSG_AUTH_KRB5_FAILURE */ 754 debug("Kerberos V5 authentication failed."); 755 ret = 0; 756 break; 757 758 case SSH_SMSG_AUTH_KRB5_RESPONSE: 759 /* SSH_SMSG_AUTH_KRB5_SUCCESS */ 760 debug("Kerberos V5 authentication accepted."); 761 762 /* Get server's response. */ 763 ap.data = packet_get_string((unsigned int *) &ap.length); 764 765 packet_integrity_check(payload_len, 4 + ap.length, type); 766 /* XXX je to dobre? */ 767 768 problem = krb5_rd_rep(*context, *auth_context, &ap, &reply); 769 if (problem) { 770 ret = 0; 771 } 772 ret = 1; 773 break; 774 775 default: 776 packet_disconnect("Protocol error on Kerberos V5 response: %d", type); 777 ret = 0; 778 break; 779 780 } 781 782out: 783 if (req_creds.server != NULL) 784 krb5_free_principal(*context, req_creds.server); 785 if (req_creds.client != NULL) 786 krb5_free_principal(*context, req_creds.client); 787 if (new_creds != NULL) 788 krb5_free_creds(*context, new_creds); 789 if (ccache != NULL) 790 krb5_cc_close(*context, ccache); 791 if (reply != NULL) 792 krb5_free_ap_rep_enc_part(*context, reply); 793 if (ap.length > 0) 794 krb5_data_free(&ap); 795 796 return ret; 797 798} 799 800void 801send_krb5_tgt(krb5_context context, krb5_auth_context auth_context) 802{ 803 int fd; 804 int type, payload_len; 805 krb5_error_code problem; 806 krb5_data outbuf; 807 krb5_ccache ccache = NULL; 808 krb5_creds creds; 809 krb5_kdc_flags flags; 810 const char* remotehost = get_canonical_hostname(); 811 812 memset(&creds, 0, sizeof(creds)); 813 memset(&outbuf, 0, sizeof(outbuf)); 814 815 fd = packet_get_connection_in(); 816 problem = krb5_auth_con_setaddrs_from_fd(context, auth_context, &fd); 817 if (problem) { 818 goto out; 819 } 820 821#if 0 822 tkfile = krb5_cc_default_name(context); 823 if (strncmp(tkfile, "FILE:", 5) == 0) 824 tkfile += 5; 825 826 if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 827 debug("Kerberos V5: could not get default ccache (permission denied)."); 828 goto out; 829 } 830#endif 831 832 problem = krb5_cc_default(context, &ccache); 833 if (problem) { 834 goto out; 835 } 836 837 problem = krb5_cc_get_principal(context, ccache, &creds.client); 838 if (problem) { 839 goto out; 840 } 841 842 problem = krb5_build_principal(context, &creds.server, 843 strlen(creds.client->realm), 844 creds.client->realm, 845 "krbtgt", 846 creds.client->realm, 847 NULL); 848 if (problem) { 849 goto out; 850 } 851 852 creds.times.endtime = 0; 853 854 flags.i = 0; 855 flags.b.forwarded = 1; 856 flags.b.forwardable = krb5_config_get_bool(context, NULL, 857 "libdefaults", "forwardable", NULL); 858 859 problem = krb5_get_forwarded_creds (context, 860 auth_context, 861 ccache, 862 flags.i, 863 remotehost, 864 &creds, 865 &outbuf); 866 if (problem) { 867 goto out; 868 } 869 870 packet_start(SSH_CMSG_HAVE_KRB5_TGT); 871 packet_put_string((char *)outbuf.data, outbuf.length); 872 packet_send(); 873 packet_write_wait(); 874 875 type = packet_read(&payload_len); 876 switch (type) { 877 case SSH_SMSG_SUCCESS: 878 break; 879 case SSH_SMSG_FAILURE: 880 break; 881 default: 882 break; 883 } 884 885out: 886 if (creds.client) 887 krb5_free_principal(context, creds.client); 888 if (creds.server) 889 krb5_free_principal(context, creds.server); 890 if (ccache) 891 krb5_cc_close(context, ccache); 892 if (outbuf.data) 893 xfree(outbuf.data); 894 895 return; 896} 897#endif /* KRB5 */ 898 899/* 900 * Starts a dialog with the server, and authenticates the current user on the 901 * server. This does not need any extra privileges. The basic connection 902 * to the server must already have been established before this is called. 903 * If login fails, this function prints an error and never returns. 904 * This function does not require super-user privileges. 905 */ 906void 907ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, 908 struct sockaddr *hostaddr, uid_t original_real_uid) 909{ 910 struct passwd *pw; 911 char *host, *cp; 912 char *server_user, *local_user; 913 914 /* Get local user name. Use it as server user if no user name was given. */ 915 pw = getpwuid(original_real_uid); 916 if (!pw) 917 fatal("User id %d not found from user database.", original_real_uid); 918 local_user = xstrdup(pw->pw_name); 919 server_user = options.user ? options.user : local_user; 920 921 /* Convert the user-supplied hostname into all lowercase. */ 922 host = xstrdup(orighost); 923 for (cp = host; *cp; cp++) 924 if (isupper(*cp)) 925 *cp = tolower(*cp); 926 927 /* Exchange protocol version identification strings with the server. */ 928 ssh_exchange_identification(); 929 930 /* Put the connection into non-blocking mode. */ 931 packet_set_nonblocking(); 932 933 /* key exchange */ 934 /* authenticate user */ 935 if (compat20) { 936 ssh_kex2(host, hostaddr); 937 ssh_userauth2(server_user, host); 938 } else { 939 ssh_kex(host, hostaddr); 940 ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key); 941 } 942}
| 302/* 303 * Waits for the server identification string, and sends our own 304 * identification string. 305 */ 306void 307ssh_exchange_identification() 308{ 309 char buf[256], remote_version[256]; /* must be same size! */ 310 int remote_major, remote_minor, i, mismatch; 311 int connection_in = packet_get_connection_in(); 312 int connection_out = packet_get_connection_out(); 313 314 /* Read other side\'s version identification. */ 315 for (i = 0; i < sizeof(buf) - 1; i++) { 316 int len = read(connection_in, &buf[i], 1); 317 if (len < 0) 318 fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 319 if (len != 1) 320 fatal("ssh_exchange_identification: Connection closed by remote host"); 321 if (buf[i] == '\r') { 322 buf[i] = '\n'; 323 buf[i + 1] = 0; 324 continue; /**XXX wait for \n */ 325 } 326 if (buf[i] == '\n') { 327 buf[i + 1] = 0; 328 break; 329 } 330 } 331 buf[sizeof(buf) - 1] = 0; 332 server_version_string = xstrdup(buf); 333 334 /* 335 * Check that the versions match. In future this might accept 336 * several versions and set appropriate flags to handle them. 337 */ 338 if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 339 &remote_major, &remote_minor, remote_version) != 3) 340 fatal("Bad remote protocol version identification: '%.100s'", buf); 341 debug("Remote protocol version %d.%d, remote software version %.100s", 342 remote_major, remote_minor, remote_version); 343 344 compat_datafellows(remote_version); 345 mismatch = 0; 346 347 switch(remote_major) { 348 case 1: 349 if (remote_minor == 99 && 350 (options.protocol & SSH_PROTO_2) && 351 !(options.protocol & SSH_PROTO_1_PREFERRED)) { 352 enable_compat20(); 353 break; 354 } 355 if (!(options.protocol & SSH_PROTO_1)) { 356 mismatch = 1; 357 break; 358 } 359 if (remote_minor < 3) { 360 fatal("Remote machine has too old SSH software version."); 361 } else if (remote_minor == 3) { 362 /* We speak 1.3, too. */ 363 enable_compat13(); 364 if (options.forward_agent) { 365 log("Agent forwarding disabled for protocol 1.3"); 366 options.forward_agent = 0; 367 } 368 } 369 break; 370 case 2: 371 if (options.protocol & SSH_PROTO_2) { 372 enable_compat20(); 373 break; 374 } 375 /* FALLTHROUGH */ 376 default: 377 mismatch = 1; 378 break; 379 } 380 if (mismatch) 381 fatal("Protocol major versions differ: %d vs. %d", 382 (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 383 remote_major); 384 if (compat20) 385 packet_set_ssh2_format(); 386 /* Send our own protocol version identification. */ 387 snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 388 compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 389 compat20 ? PROTOCOL_MINOR_2 : PROTOCOL_MINOR_1, 390 SSH_VERSION); 391 if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) 392 fatal("write: %.100s", strerror(errno)); 393 client_version_string = xstrdup(buf); 394 chop(client_version_string); 395 chop(server_version_string); 396 debug("Local version string %.100s", client_version_string); 397} 398 399int 400read_yes_or_no(const char *prompt, int defval) 401{ 402 char buf[1024]; 403 FILE *f; 404 int retval = -1; 405 406 if (isatty(0)) 407 f = stdin; 408 else 409 f = fopen("/dev/tty", "rw"); 410 411 if (f == NULL) 412 return 0; 413 414 fflush(stdout); 415 416 while (1) { 417 fprintf(stderr, "%s", prompt); 418 if (fgets(buf, sizeof(buf), f) == NULL) { 419 /* Print a newline (the prompt probably didn\'t have one). */ 420 fprintf(stderr, "\n"); 421 strlcpy(buf, "no", sizeof buf); 422 } 423 /* Remove newline from response. */ 424 if (strchr(buf, '\n')) 425 *strchr(buf, '\n') = 0; 426 427 if (buf[0] == 0) 428 retval = defval; 429 if (strcmp(buf, "yes") == 0) 430 retval = 1; 431 if (strcmp(buf, "no") == 0) 432 retval = 0; 433 434 if (retval != -1) { 435 if (f != stdin) 436 fclose(f); 437 return retval; 438 } 439 } 440} 441 442/* 443 * check whether the supplied host key is valid, return only if ok. 444 */ 445 446void 447check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, 448 const char *user_hostfile, const char *system_hostfile) 449{ 450 Key *file_key; 451 char *type = key_type(host_key); 452 char *ip = NULL; 453 char hostline[1000], *hostp; 454 HostStatus host_status; 455 HostStatus ip_status; 456 int local = 0, host_ip_differ = 0; 457 char ntop[NI_MAXHOST]; 458 459 /* 460 * Force accepting of the host key for loopback/localhost. The 461 * problem is that if the home directory is NFS-mounted to multiple 462 * machines, localhost will refer to a different machine in each of 463 * them, and the user will get bogus HOST_CHANGED warnings. This 464 * essentially disables host authentication for localhost; however, 465 * this is probably not a real problem. 466 */ 467 /** hostaddr == 0! */ 468 switch (hostaddr->sa_family) { 469 case AF_INET: 470 local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 471 break; 472 case AF_INET6: 473 local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 474 break; 475 default: 476 local = 0; 477 break; 478 } 479 if (local) { 480 debug("Forcing accepting of host key for loopback/localhost."); 481 return; 482 } 483 484 /* 485 * Turn off check_host_ip for proxy connects, since 486 * we don't have the remote ip-address 487 */ 488 if (options.proxy_command != NULL && options.check_host_ip) 489 options.check_host_ip = 0; 490 491 if (options.check_host_ip) { 492 if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), 493 NULL, 0, NI_NUMERICHOST) != 0) 494 fatal("check_host_key: getnameinfo failed"); 495 ip = xstrdup(ntop); 496 } 497 498 /* 499 * Store the host key from the known host file in here so that we can 500 * compare it with the key for the IP address. 501 */ 502 file_key = key_new(host_key->type); 503 504 /* 505 * Check if the host key is present in the user\'s list of known 506 * hosts or in the systemwide list. 507 */ 508 host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key); 509 if (host_status == HOST_NEW) 510 host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key); 511 /* 512 * Also perform check for the ip address, skip the check if we are 513 * localhost or the hostname was an ip address to begin with 514 */ 515 if (options.check_host_ip && !local && strcmp(host, ip)) { 516 Key *ip_key = key_new(host_key->type); 517 ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key); 518 519 if (ip_status == HOST_NEW) 520 ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key); 521 if (host_status == HOST_CHANGED && 522 (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 523 host_ip_differ = 1; 524 525 key_free(ip_key); 526 } else 527 ip_status = host_status; 528 529 key_free(file_key); 530 531 switch (host_status) { 532 case HOST_OK: 533 /* The host is known and the key matches. */ 534 debug("Host '%.200s' is known and matches the %s host key.", 535 host, type); 536 if (options.check_host_ip) { 537 if (ip_status == HOST_NEW) { 538 if (!add_host_to_hostfile(user_hostfile, ip, host_key)) 539 log("Failed to add the %s host key for IP address '%.30s' to the list of known hosts (%.30s).", 540 type, ip, user_hostfile); 541 else 542 log("Warning: Permanently added the %s host key for IP address '%.30s' to the list of known hosts.", 543 type, ip); 544 } else if (ip_status != HOST_OK) 545 log("Warning: the %s host key for '%.200s' differs from the key for the IP address '%.30s'", 546 type, host, ip); 547 } 548 break; 549 case HOST_NEW: 550 /* The host is new. */ 551 if (options.strict_host_key_checking == 1) { 552 /* User has requested strict host key checking. We will not add the host key 553 automatically. The only alternative left is to abort. */ 554 fatal("No %s host key is known for %.200s and you have requested strict checking.", type, host); 555 } else if (options.strict_host_key_checking == 2) { 556 /* The default */ 557 char prompt[1024]; 558 char *fp = key_fingerprint(host_key); 559 snprintf(prompt, sizeof(prompt), 560 "The authenticity of host '%.200s' can't be established.\n" 561 "%s key fingerprint is %s.\n" 562 "Are you sure you want to continue connecting (yes/no)? ", 563 host, type, fp); 564 if (!read_yes_or_no(prompt, -1)) 565 fatal("Aborted by user!\n"); 566 } 567 if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) { 568 snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 569 hostp = hostline; 570 } else 571 hostp = host; 572 573 /* If not in strict mode, add the key automatically to the local known_hosts file. */ 574 if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) 575 log("Failed to add the host to the list of known hosts (%.500s).", 576 user_hostfile); 577 else 578 log("Warning: Permanently added '%.200s' (%s) to the list of known hosts.", 579 hostp, type); 580 break; 581 case HOST_CHANGED: 582 if (options.check_host_ip && host_ip_differ) { 583 char *msg; 584 if (ip_status == HOST_NEW) 585 msg = "is unknown"; 586 else if (ip_status == HOST_OK) 587 msg = "is unchanged"; 588 else 589 msg = "has a different value"; 590 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 591 error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 592 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 593 error("The %s host key for %s has changed,", type, host); 594 error("and the key for the according IP address %s", ip); 595 error("%s. This could either mean that", msg); 596 error("DNS SPOOFING is happening or the IP address for the host"); 597 error("and its host key have changed at the same time"); 598 } 599 /* The host key has changed. */ 600 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 601 error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 602 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 603 error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 604 error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 605 error("It is also possible that the %s host key has just been changed.", type); 606 error("Please contact your system administrator."); 607 error("Add correct host key in %.100s to get rid of this message.", 608 user_hostfile); 609 610 /* 611 * If strict host key checking is in use, the user will have 612 * to edit the key manually and we can only abort. 613 */ 614 if (options.strict_host_key_checking) 615 fatal("%s host key for %.200s has changed and you have requested strict checking.", type, host); 616 617 /* 618 * If strict host key checking has not been requested, allow 619 * the connection but without password authentication or 620 * agent forwarding. 621 */ 622 if (options.password_authentication) { 623 error("Password authentication is disabled to avoid trojan horses."); 624 options.password_authentication = 0; 625 } 626 if (options.forward_agent) { 627 error("Agent forwarding is disabled to avoid trojan horses."); 628 options.forward_agent = 0; 629 } 630 /* 631 * XXX Should permit the user to change to use the new id. 632 * This could be done by converting the host key to an 633 * identifying sentence, tell that the host identifies itself 634 * by that sentence, and ask the user if he/she whishes to 635 * accept the authentication. 636 */ 637 break; 638 } 639 if (options.check_host_ip) 640 xfree(ip); 641} 642 643#ifdef KRB5 644int 645try_krb5_authentication(krb5_context *context, krb5_auth_context *auth_context) 646{ 647 krb5_error_code problem; 648 const char *tkfile; 649 struct stat buf; 650 krb5_ccache ccache = NULL; 651 krb5_creds req_creds; 652 krb5_creds *new_creds = NULL; 653 const char *remotehost; 654 krb5_data ap; 655 int type, payload_len; 656 krb5_ap_rep_enc_part *reply = NULL; 657 int ret; 658 659 memset(&ap, 0, sizeof(ap)); 660 661 problem = krb5_init_context(context); 662 if (problem) { 663 ret = 0; 664 goto out; 665 } 666 667 tkfile = krb5_cc_default_name(*context); 668 if (strncmp(tkfile, "FILE:", 5) == 0) 669 tkfile += 5; 670 671 if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 672 debug("Kerberos V5: could not get default ccache (permission denied)."); 673 ret = 0; 674 goto out; 675 } 676 677 problem = krb5_cc_default(*context, &ccache); 678 if (problem) { 679 ret = 0; 680 goto out; 681 } 682 683 memset(&req_creds, 0, sizeof(req_creds)); 684 685 remotehost = get_canonical_hostname(); 686 687 problem = krb5_sname_to_principal(*context, remotehost, 688 "host", KRB5_NT_SRV_HST, 689 &req_creds.server); 690 if (problem) { 691 ret = 0; 692 goto out; 693 694 } 695 696 problem = krb5_cc_get_principal(*context, ccache, &req_creds.client); 697 if (problem) { 698 ret = 0; 699 goto out; 700 } 701 702 /* creds.session.keytype=ETYPE_DES_CBC_CRC; */ 703 704 problem = krb5_get_credentials(*context, 0, ccache, &req_creds, &new_creds); 705 if (problem) { 706 ret = 0; 707 goto out; 708 } 709 710 problem = krb5_auth_con_init(*context, auth_context); 711 if (problem) { 712 ret = 0; 713 goto out; 714 } 715 716 /* krb5_auth_con_setflags(ssh_context, auth_context, 717 KRB5_AUTH_CONTEXT_RET_TIME); 718 */ 719 problem = krb5_mk_req_extended(*context, auth_context, 720 AP_OPTS_MUTUAL_REQUIRED /*| AP_OPTS_USE_SUBKEY*/ , 721 NULL, new_creds, &ap); 722 if (problem) { 723 ret = 0; 724 goto out; 725 } 726 727 packet_start(SSH_CMSG_AUTH_KRB5); 728 packet_put_string((char *) ap.data, ap.length); 729 packet_send(); 730 packet_write_wait(); 731 732 xfree(ap.data); 733 ap.length = 0; 734 735 type = packet_read(&payload_len); 736 switch (type) { 737 case SSH_SMSG_FAILURE: 738 /* Should really be SSH_SMSG_AUTH_KRB5_FAILURE */ 739 debug("Kerberos V5 authentication failed."); 740 ret = 0; 741 break; 742 743 case SSH_SMSG_AUTH_KRB5_RESPONSE: 744 /* SSH_SMSG_AUTH_KRB5_SUCCESS */ 745 debug("Kerberos V5 authentication accepted."); 746 747 /* Get server's response. */ 748 ap.data = packet_get_string((unsigned int *) &ap.length); 749 750 packet_integrity_check(payload_len, 4 + ap.length, type); 751 /* XXX je to dobre? */ 752 753 problem = krb5_rd_rep(*context, *auth_context, &ap, &reply); 754 if (problem) { 755 ret = 0; 756 } 757 ret = 1; 758 break; 759 760 default: 761 packet_disconnect("Protocol error on Kerberos V5 response: %d", type); 762 ret = 0; 763 break; 764 765 } 766 767out: 768 if (req_creds.server != NULL) 769 krb5_free_principal(*context, req_creds.server); 770 if (req_creds.client != NULL) 771 krb5_free_principal(*context, req_creds.client); 772 if (new_creds != NULL) 773 krb5_free_creds(*context, new_creds); 774 if (ccache != NULL) 775 krb5_cc_close(*context, ccache); 776 if (reply != NULL) 777 krb5_free_ap_rep_enc_part(*context, reply); 778 if (ap.length > 0) 779 krb5_data_free(&ap); 780 781 return ret; 782 783} 784 785void 786send_krb5_tgt(krb5_context context, krb5_auth_context auth_context) 787{ 788 int fd; 789 int type, payload_len; 790 krb5_error_code problem; 791 krb5_data outbuf; 792 krb5_ccache ccache = NULL; 793 krb5_creds creds; 794 krb5_kdc_flags flags; 795 const char* remotehost = get_canonical_hostname(); 796 797 memset(&creds, 0, sizeof(creds)); 798 memset(&outbuf, 0, sizeof(outbuf)); 799 800 fd = packet_get_connection_in(); 801 problem = krb5_auth_con_setaddrs_from_fd(context, auth_context, &fd); 802 if (problem) { 803 goto out; 804 } 805 806#if 0 807 tkfile = krb5_cc_default_name(context); 808 if (strncmp(tkfile, "FILE:", 5) == 0) 809 tkfile += 5; 810 811 if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 812 debug("Kerberos V5: could not get default ccache (permission denied)."); 813 goto out; 814 } 815#endif 816 817 problem = krb5_cc_default(context, &ccache); 818 if (problem) { 819 goto out; 820 } 821 822 problem = krb5_cc_get_principal(context, ccache, &creds.client); 823 if (problem) { 824 goto out; 825 } 826 827 problem = krb5_build_principal(context, &creds.server, 828 strlen(creds.client->realm), 829 creds.client->realm, 830 "krbtgt", 831 creds.client->realm, 832 NULL); 833 if (problem) { 834 goto out; 835 } 836 837 creds.times.endtime = 0; 838 839 flags.i = 0; 840 flags.b.forwarded = 1; 841 flags.b.forwardable = krb5_config_get_bool(context, NULL, 842 "libdefaults", "forwardable", NULL); 843 844 problem = krb5_get_forwarded_creds (context, 845 auth_context, 846 ccache, 847 flags.i, 848 remotehost, 849 &creds, 850 &outbuf); 851 if (problem) { 852 goto out; 853 } 854 855 packet_start(SSH_CMSG_HAVE_KRB5_TGT); 856 packet_put_string((char *)outbuf.data, outbuf.length); 857 packet_send(); 858 packet_write_wait(); 859 860 type = packet_read(&payload_len); 861 switch (type) { 862 case SSH_SMSG_SUCCESS: 863 break; 864 case SSH_SMSG_FAILURE: 865 break; 866 default: 867 break; 868 } 869 870out: 871 if (creds.client) 872 krb5_free_principal(context, creds.client); 873 if (creds.server) 874 krb5_free_principal(context, creds.server); 875 if (ccache) 876 krb5_cc_close(context, ccache); 877 if (outbuf.data) 878 xfree(outbuf.data); 879 880 return; 881} 882#endif /* KRB5 */ 883 884/* 885 * Starts a dialog with the server, and authenticates the current user on the 886 * server. This does not need any extra privileges. The basic connection 887 * to the server must already have been established before this is called. 888 * If login fails, this function prints an error and never returns. 889 * This function does not require super-user privileges. 890 */ 891void 892ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, 893 struct sockaddr *hostaddr, uid_t original_real_uid) 894{ 895 struct passwd *pw; 896 char *host, *cp; 897 char *server_user, *local_user; 898 899 /* Get local user name. Use it as server user if no user name was given. */ 900 pw = getpwuid(original_real_uid); 901 if (!pw) 902 fatal("User id %d not found from user database.", original_real_uid); 903 local_user = xstrdup(pw->pw_name); 904 server_user = options.user ? options.user : local_user; 905 906 /* Convert the user-supplied hostname into all lowercase. */ 907 host = xstrdup(orighost); 908 for (cp = host; *cp; cp++) 909 if (isupper(*cp)) 910 *cp = tolower(*cp); 911 912 /* Exchange protocol version identification strings with the server. */ 913 ssh_exchange_identification(); 914 915 /* Put the connection into non-blocking mode. */ 916 packet_set_nonblocking(); 917 918 /* key exchange */ 919 /* authenticate user */ 920 if (compat20) { 921 ssh_kex2(host, hostaddr); 922 ssh_userauth2(server_user, host); 923 } else { 924 ssh_kex(host, hostaddr); 925 ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key); 926 } 927}
|