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