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 * Created: Sat Mar 18 22:15:47 1995 ylo 6 * Code to connect to a remote host, and to perform the client side of the 7 * login (authentication) dialog. 8 * 9 * $FreeBSD$ 10 */ 11 12 #include "includes.h" 13 RCSID("$OpenBSD: sshconnect.c,v 1.74 2000/05/17 16:57:02 markus Exp $"); 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 31 char *client_version_string = NULL; 32 char *server_version_string = NULL; 33 34 extern Options options; 35 extern char *__progname; 36 37 /* 38 * Connect to the given ssh server using a proxy command. 39 */ 40 int 41 ssh_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 */ 140 int 141 ssh_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 */ 181 int 182 ssh_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. */ 256 memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 257 restore_uid(); 258 break; 259 } else { 260 debug("connect: %.100s", strerror(errno)); 261 restore_uid(); 262 /* 263 * Close the failed socket; there appear to 264 * be some problems when reusing a socket for 265 * which connect() has already returned an 266 * error. 267 */ 268 shutdown(sock, SHUT_RDWR); 269 close(sock); 270 } 271 } 272 if (ai) 273 break; /* Successful connection. */ 274 275 /* Sleep a moment before retrying. */ 276 sleep(1); 277 } 278 279 freeaddrinfo(aitop); 280 281 /* Return failure if we didn't get a successful connection. */ 282 if (attempt >= connection_attempts) 283 return 0; 284 285 debug("Connection established."); 286 287 /* 288 * Set socket options. We would like the socket to disappear as soon 289 * as it has been closed for whatever reason. 290 */ 291 /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ 292 linger.l_onoff = 1; 293 linger.l_linger = 5; 294 setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); 295 296 /* Set the connection. */ 297 packet_set_connection(sock, sock); 298 299 return 1; 300 } 301 302 /* 303 * Waits for the server identification string, and sends our own 304 * identification string. 305 */ 306 void 307 ssh_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 399 int 400 read_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 446 void 447 check_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 644 int 645 try_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 767 out: 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 785 void 786 send_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 870 out: 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 */ 891 void 892 ssh_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 } 928