1323136Sdes/* $OpenBSD: sshconnect.c,v 1.273 2017/03/10 03:22:40 dtucker Exp $ */ 257429Smarkm/* 357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 557429Smarkm * All rights reserved 657429Smarkm * Code to connect to a remote host, and to perform the client side of the 757429Smarkm * login (authentication) dialog. 865674Skris * 965674Skris * As far as I am concerned, the code I have written for this software 1065674Skris * can be used freely for any purpose. Any derived versions of this 1165674Skris * software must be clearly marked as such, and if the derived work is 1265674Skris * incompatible with the protocol description in the RFC file, it must be 1365674Skris * called by a name other than "ssh" or "Secure Shell". 1457429Smarkm */ 1557429Smarkm 1657429Smarkm#include "includes.h" 17263691Sdes__RCSID("$FreeBSD: stable/11/crypto/openssh/sshconnect.c 323136 2017-09-02 23:39:51Z des $"); 1857429Smarkm 19162856Sdes#include <sys/types.h> 20162856Sdes#include <sys/wait.h> 21162856Sdes#include <sys/stat.h> 22162856Sdes#include <sys/socket.h> 23162856Sdes#ifdef HAVE_SYS_TIME_H 24162856Sdes# include <sys/time.h> 25162856Sdes#endif 2660576Skris 27162856Sdes#include <netinet/in.h> 28162856Sdes#include <arpa/inet.h> 29261320Sdes#include <rpc/rpc.h> 30162856Sdes 31162856Sdes#include <ctype.h> 32162856Sdes#include <errno.h> 33204917Sdes#include <fcntl.h> 34162856Sdes#include <netdb.h> 35162856Sdes#ifdef HAVE_PATHS_H 36162856Sdes#include <paths.h> 37162856Sdes#endif 38162856Sdes#include <pwd.h> 39221420Sdes#include <signal.h> 40162856Sdes#include <stdarg.h> 41162856Sdes#include <stdio.h> 42162856Sdes#include <stdlib.h> 43162856Sdes#include <string.h> 44162856Sdes#include <unistd.h> 45162856Sdes 46162856Sdes#include "xmalloc.h" 47162856Sdes#include "key.h" 48162856Sdes#include "hostfile.h" 4976262Sgreen#include "ssh.h" 5057429Smarkm#include "rsa.h" 5160576Skris#include "buffer.h" 5257429Smarkm#include "packet.h" 5357429Smarkm#include "uidswap.h" 5457429Smarkm#include "compat.h" 5558585Skris#include "key.h" 5660576Skris#include "sshconnect.h" 5758585Skris#include "hostfile.h" 5876262Sgreen#include "log.h" 59294328Sdes#include "misc.h" 6076262Sgreen#include "readconf.h" 6176262Sgreen#include "atomicio.h" 62124211Sdes#include "dns.h" 63261320Sdes#include "monitor_fdpass.h" 64204917Sdes#include "ssh2.h" 65162856Sdes#include "version.h" 66294332Sdes#include "authfile.h" 67294332Sdes#include "ssherr.h" 68296633Sdes#include "authfd.h" 69124211Sdes 7060576Skrischar *client_version_string = NULL; 7160576Skrischar *server_version_string = NULL; 72294328SdesKey *previous_host_key = NULL; 7357429Smarkm 74157019Sdesstatic int matching_host_key_dns = 0; 75124211Sdes 76221420Sdesstatic pid_t proxy_command_pid = 0; 77221420Sdes 7898684Sdes/* import */ 7957429Smarkmextern Options options; 8057429Smarkmextern char *__progname; 8198684Sdesextern uid_t original_real_uid; 8298684Sdesextern uid_t original_effective_uid; 8357429Smarkm 84221420Sdesstatic int show_other_keys(struct hostkeys *, Key *); 85126277Sdesstatic void warn_changed_key(Key *); 8676262Sgreen 87261320Sdes/* Expand a proxy command */ 88261320Sdesstatic char * 89261320Sdesexpand_proxy_command(const char *proxy_command, const char *user, 90261320Sdes const char *host, int port) 91261320Sdes{ 92261320Sdes char *tmp, *ret, strport[NI_MAXSERV]; 93261320Sdes 94261320Sdes snprintf(strport, sizeof strport, "%d", port); 95261320Sdes xasprintf(&tmp, "exec %s", proxy_command); 96261320Sdes ret = percent_expand(tmp, "h", host, "p", strport, 97261320Sdes "r", options.user, (char *)NULL); 98261320Sdes free(tmp); 99261320Sdes return ret; 100261320Sdes} 101261320Sdes 10257429Smarkm/* 103261320Sdes * Connect to the given ssh server using a proxy command that passes a 104261320Sdes * a connected fd back to us. 105261320Sdes */ 106261320Sdesstatic int 107261320Sdesssh_proxy_fdpass_connect(const char *host, u_short port, 108261320Sdes const char *proxy_command) 109261320Sdes{ 110261320Sdes char *command_string; 111261320Sdes int sp[2], sock; 112261320Sdes pid_t pid; 113261320Sdes char *shell; 114261320Sdes 115261320Sdes if ((shell = getenv("SHELL")) == NULL) 116261320Sdes shell = _PATH_BSHELL; 117261320Sdes 118261320Sdes if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) 119261320Sdes fatal("Could not create socketpair to communicate with " 120261320Sdes "proxy dialer: %.100s", strerror(errno)); 121261320Sdes 122261320Sdes command_string = expand_proxy_command(proxy_command, options.user, 123261320Sdes host, port); 124261320Sdes debug("Executing proxy dialer command: %.500s", command_string); 125261320Sdes 126261320Sdes /* Fork and execute the proxy command. */ 127261320Sdes if ((pid = fork()) == 0) { 128261320Sdes char *argv[10]; 129261320Sdes 130261320Sdes /* Child. Permanently give up superuser privileges. */ 131261320Sdes permanently_drop_suid(original_real_uid); 132261320Sdes 133261320Sdes close(sp[1]); 134261320Sdes /* Redirect stdin and stdout. */ 135261320Sdes if (sp[0] != 0) { 136261320Sdes if (dup2(sp[0], 0) < 0) 137261320Sdes perror("dup2 stdin"); 138261320Sdes } 139261320Sdes if (sp[0] != 1) { 140261320Sdes if (dup2(sp[0], 1) < 0) 141261320Sdes perror("dup2 stdout"); 142261320Sdes } 143261320Sdes if (sp[0] >= 2) 144261320Sdes close(sp[0]); 145261320Sdes 146261320Sdes /* 147261320Sdes * Stderr is left as it is so that error messages get 148261320Sdes * printed on the user's terminal. 149261320Sdes */ 150261320Sdes argv[0] = shell; 151261320Sdes argv[1] = "-c"; 152261320Sdes argv[2] = command_string; 153261320Sdes argv[3] = NULL; 154261320Sdes 155261320Sdes /* 156261320Sdes * Execute the proxy command. 157261320Sdes * Note that we gave up any extra privileges above. 158261320Sdes */ 159261320Sdes execv(argv[0], argv); 160261320Sdes perror(argv[0]); 161261320Sdes exit(1); 162261320Sdes } 163261320Sdes /* Parent. */ 164261320Sdes if (pid < 0) 165261320Sdes fatal("fork failed: %.100s", strerror(errno)); 166261320Sdes close(sp[0]); 167261320Sdes free(command_string); 168261320Sdes 169261320Sdes if ((sock = mm_receive_fd(sp[1])) == -1) 170261320Sdes fatal("proxy dialer did not pass back a connection"); 171296633Sdes close(sp[1]); 172261320Sdes 173261320Sdes while (waitpid(pid, NULL, 0) == -1) 174261320Sdes if (errno != EINTR) 175261320Sdes fatal("Couldn't wait for child: %s", strerror(errno)); 176261320Sdes 177261320Sdes /* Set the connection file descriptors. */ 178261320Sdes packet_set_connection(sock, sock); 179261320Sdes 180261320Sdes return 0; 181261320Sdes} 182261320Sdes 183261320Sdes/* 18457429Smarkm * Connect to the given ssh server using a proxy command. 18557429Smarkm */ 18692559Sdesstatic int 18798684Sdesssh_proxy_connect(const char *host, u_short port, const char *proxy_command) 18857429Smarkm{ 189261320Sdes char *command_string; 19057429Smarkm int pin[2], pout[2]; 19160576Skris pid_t pid; 192261320Sdes char *shell; 19357429Smarkm 194221420Sdes if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 195181111Sdes shell = _PATH_BSHELL; 196181111Sdes 19757429Smarkm /* Create pipes for communicating with the proxy. */ 19857429Smarkm if (pipe(pin) < 0 || pipe(pout) < 0) 19957429Smarkm fatal("Could not create pipes to communicate with the proxy: %.100s", 20092559Sdes strerror(errno)); 20157429Smarkm 202261320Sdes command_string = expand_proxy_command(proxy_command, options.user, 203261320Sdes host, port); 20457429Smarkm debug("Executing proxy command: %.500s", command_string); 20557429Smarkm 20657429Smarkm /* Fork and execute the proxy command. */ 20757429Smarkm if ((pid = fork()) == 0) { 20857429Smarkm char *argv[10]; 20957429Smarkm 21057429Smarkm /* Child. Permanently give up superuser privileges. */ 211162856Sdes permanently_drop_suid(original_real_uid); 21257429Smarkm 21357429Smarkm /* Redirect stdin and stdout. */ 21457429Smarkm close(pin[1]); 21557429Smarkm if (pin[0] != 0) { 21657429Smarkm if (dup2(pin[0], 0) < 0) 21757429Smarkm perror("dup2 stdin"); 21857429Smarkm close(pin[0]); 21957429Smarkm } 22057429Smarkm close(pout[0]); 22157429Smarkm if (dup2(pout[1], 1) < 0) 22257429Smarkm perror("dup2 stdout"); 22357429Smarkm /* Cannot be 1 because pin allocated two descriptors. */ 22457429Smarkm close(pout[1]); 22557429Smarkm 22657429Smarkm /* Stderr is left as it is so that error messages get 22757429Smarkm printed on the user's terminal. */ 228181111Sdes argv[0] = shell; 22957429Smarkm argv[1] = "-c"; 23057429Smarkm argv[2] = command_string; 23157429Smarkm argv[3] = NULL; 23257429Smarkm 23357429Smarkm /* Execute the proxy command. Note that we gave up any 23457429Smarkm extra privileges above. */ 235221420Sdes signal(SIGPIPE, SIG_DFL); 23676262Sgreen execv(argv[0], argv); 23776262Sgreen perror(argv[0]); 23857429Smarkm exit(1); 23957429Smarkm } 24057429Smarkm /* Parent. */ 24157429Smarkm if (pid < 0) 24257429Smarkm fatal("fork failed: %.100s", strerror(errno)); 243106130Sdes else 244106130Sdes proxy_command_pid = pid; /* save pid to clean up later */ 24557429Smarkm 24657429Smarkm /* Close child side of the descriptors. */ 24757429Smarkm close(pin[0]); 24857429Smarkm close(pout[1]); 24957429Smarkm 25057429Smarkm /* Free the command name. */ 251255767Sdes free(command_string); 25257429Smarkm 25357429Smarkm /* Set the connection file descriptors. */ 25457429Smarkm packet_set_connection(pout[0], pin[1]); 25557429Smarkm 25692559Sdes /* Indicate OK return */ 25792559Sdes return 0; 25857429Smarkm} 25957429Smarkm 260221420Sdesvoid 261221420Sdesssh_kill_proxy_command(void) 262221420Sdes{ 263221420Sdes /* 264221420Sdes * Send SIGHUP to proxy command if used. We don't wait() in 265221420Sdes * case it hangs and instead rely on init to reap the child 266221420Sdes */ 267221420Sdes if (proxy_command_pid > 1) 268221420Sdes kill(proxy_command_pid, SIGHUP); 269221420Sdes} 270221420Sdes 27157429Smarkm/* 27257429Smarkm * Creates a (possibly privileged) socket for use as the ssh connection. 27357429Smarkm */ 27492559Sdesstatic int 275124211Sdesssh_create_socket(int privileged, struct addrinfo *ai) 27657429Smarkm{ 277261320Sdes int sock, r, gaierr; 278263712Sdes struct addrinfo hints, *res = NULL; 27957429Smarkm 280124211Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 281204917Sdes if (sock < 0) { 282261320Sdes error("socket: %s", strerror(errno)); 283204917Sdes return -1; 284204917Sdes } 285204917Sdes fcntl(sock, F_SETFD, FD_CLOEXEC); 28692559Sdes 28792559Sdes /* Bind the socket to an alternative local IP address */ 288261320Sdes if (options.bind_address == NULL && !privileged) 28992559Sdes return sock; 29092559Sdes 291263712Sdes if (options.bind_address) { 292263712Sdes memset(&hints, 0, sizeof(hints)); 293263712Sdes hints.ai_family = ai->ai_family; 294263712Sdes hints.ai_socktype = ai->ai_socktype; 295263712Sdes hints.ai_protocol = ai->ai_protocol; 296263712Sdes hints.ai_flags = AI_PASSIVE; 297263712Sdes gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); 298263712Sdes if (gaierr) { 299263712Sdes error("getaddrinfo: %s: %s", options.bind_address, 300263712Sdes ssh_gai_strerror(gaierr)); 301263712Sdes close(sock); 302263712Sdes return -1; 303263712Sdes } 30492559Sdes } 305261320Sdes /* 306261320Sdes * If we are running as root and want to connect to a privileged 307261320Sdes * port, bind our own socket to a privileged port. 308261320Sdes */ 309261320Sdes if (privileged) { 310261320Sdes PRIV_START; 311263712Sdes r = bindresvport_sa(sock, res ? res->ai_addr : NULL); 312261320Sdes PRIV_END; 313261320Sdes if (r < 0) { 314261320Sdes error("bindresvport_sa: af=%d %s", ai->ai_family, 315261320Sdes strerror(errno)); 316261320Sdes goto fail; 317261320Sdes } 318261320Sdes } else { 319261320Sdes if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 320261320Sdes error("bind: %s: %s", options.bind_address, 321261320Sdes strerror(errno)); 322261320Sdes fail: 323261320Sdes close(sock); 324261320Sdes freeaddrinfo(res); 325261320Sdes return -1; 326261320Sdes } 32792559Sdes } 328263712Sdes if (res != NULL) 329263712Sdes freeaddrinfo(res); 33057429Smarkm return sock; 33157429Smarkm} 33257429Smarkm 333124211Sdesstatic int 334124211Sdestimeout_connect(int sockfd, const struct sockaddr *serv_addr, 335181111Sdes socklen_t addrlen, int *timeoutp) 336124211Sdes{ 337124211Sdes fd_set *fdset; 338181111Sdes struct timeval tv, t_start; 339124211Sdes socklen_t optlen; 340162856Sdes int optval, rc, result = -1; 341124211Sdes 342181111Sdes gettimeofday(&t_start, NULL); 343124211Sdes 344181111Sdes if (*timeoutp <= 0) { 345181111Sdes result = connect(sockfd, serv_addr, addrlen); 346181111Sdes goto done; 347181111Sdes } 348181111Sdes 349126277Sdes set_nonblock(sockfd); 350124211Sdes rc = connect(sockfd, serv_addr, addrlen); 351126277Sdes if (rc == 0) { 352126277Sdes unset_nonblock(sockfd); 353181111Sdes result = 0; 354181111Sdes goto done; 355126277Sdes } 356181111Sdes if (errno != EINPROGRESS) { 357181111Sdes result = -1; 358181111Sdes goto done; 359181111Sdes } 360124211Sdes 361294496Sdes fdset = xcalloc(howmany(sockfd + 1, NFDBITS), 362162856Sdes sizeof(fd_mask)); 363124211Sdes FD_SET(sockfd, fdset); 364181111Sdes ms_to_timeval(&tv, *timeoutp); 365124211Sdes 366147005Sdes for (;;) { 367124211Sdes rc = select(sockfd + 1, NULL, fdset, NULL, &tv); 368124211Sdes if (rc != -1 || errno != EINTR) 369124211Sdes break; 370124211Sdes } 371124211Sdes 372147005Sdes switch (rc) { 373124211Sdes case 0: 374124211Sdes /* Timed out */ 375124211Sdes errno = ETIMEDOUT; 376124211Sdes break; 377124211Sdes case -1: 378124211Sdes /* Select error */ 379126277Sdes debug("select: %s", strerror(errno)); 380124211Sdes break; 381124211Sdes case 1: 382124211Sdes /* Completed or failed */ 383124211Sdes optval = 0; 384124211Sdes optlen = sizeof(optval); 385126277Sdes if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, 386124211Sdes &optlen) == -1) { 387126277Sdes debug("getsockopt: %s", strerror(errno)); 388124211Sdes break; 389124211Sdes } 390124211Sdes if (optval != 0) { 391124211Sdes errno = optval; 392124211Sdes break; 393124211Sdes } 394124211Sdes result = 0; 395126277Sdes unset_nonblock(sockfd); 396124211Sdes break; 397124211Sdes default: 398124211Sdes /* Should not occur */ 399124211Sdes fatal("Bogus return (%d) from select()", rc); 400124211Sdes } 401124211Sdes 402255767Sdes free(fdset); 403181111Sdes 404181111Sdes done: 405181111Sdes if (result == 0 && *timeoutp > 0) { 406181111Sdes ms_subtract_diff(&t_start, timeoutp); 407181111Sdes if (*timeoutp <= 0) { 408181111Sdes errno = ETIMEDOUT; 409181111Sdes result = -1; 410181111Sdes } 411181111Sdes } 412181111Sdes 413124211Sdes return (result); 414124211Sdes} 415124211Sdes 41657429Smarkm/* 41757429Smarkm * Opens a TCP/IP connection to the remote server on the given host. 41857429Smarkm * The address of the remote host will be returned in hostaddr. 41998684Sdes * If port is 0, the default port will be used. If needpriv is true, 42057429Smarkm * a privileged port will be allocated to make the connection. 42198684Sdes * This requires super-user privileges if needpriv is true. 42257429Smarkm * Connection_attempts specifies the maximum number of tries (one per 42357429Smarkm * second). If proxy_command is non-NULL, it specifies the command (with %h 42457429Smarkm * and %p substituted for host and port, respectively) to use to contact 42557429Smarkm * the daemon. 42657429Smarkm */ 427261320Sdesstatic int 428261320Sdesssh_connect_direct(const char *host, struct addrinfo *aitop, 429261320Sdes struct sockaddr_storage *hostaddr, u_short port, int family, 430261320Sdes int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 43157429Smarkm{ 43276262Sgreen int on = 1; 43357429Smarkm int sock = -1, attempt; 43476262Sgreen char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 435261320Sdes struct addrinfo *ai; 43657429Smarkm 437296633Sdes debug2("%s: needpriv %d", __func__, needpriv); 438296633Sdes memset(ntop, 0, sizeof(ntop)); 439296633Sdes memset(strport, 0, sizeof(strport)); 44057429Smarkm 441162856Sdes for (attempt = 0; attempt < connection_attempts; attempt++) { 442164149Sdes if (attempt > 0) { 443164149Sdes /* Sleep a moment before retrying. */ 444164149Sdes sleep(1); 44557429Smarkm debug("Trying again..."); 446164149Sdes } 447162856Sdes /* 448162856Sdes * Loop through addresses for this host, and try each one in 449162856Sdes * sequence until the connection succeeds. 450162856Sdes */ 45157429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 452261320Sdes if (ai->ai_family != AF_INET && 453261320Sdes ai->ai_family != AF_INET6) 45457429Smarkm continue; 45557429Smarkm if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 45657429Smarkm ntop, sizeof(ntop), strport, sizeof(strport), 45757429Smarkm NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 458296633Sdes error("%s: getnameinfo failed", __func__); 45957429Smarkm continue; 46057429Smarkm } 46157429Smarkm debug("Connecting to %.200s [%.100s] port %s.", 46276226Sgreen host, ntop, strport); 46357429Smarkm 46457429Smarkm /* Create a socket for connecting. */ 465124211Sdes sock = ssh_create_socket(needpriv, ai); 46657429Smarkm if (sock < 0) 46792559Sdes /* Any error is already output */ 46857429Smarkm continue; 46957429Smarkm 470124211Sdes if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 471181111Sdes timeout_ms) >= 0) { 47257429Smarkm /* Successful connection. */ 47376262Sgreen memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 47457429Smarkm break; 47557429Smarkm } else { 476106130Sdes debug("connect to address %s port %s: %s", 477106130Sdes ntop, strport, strerror(errno)); 47857429Smarkm close(sock); 479162856Sdes sock = -1; 48057429Smarkm } 48157429Smarkm } 482162856Sdes if (sock != -1) 48357429Smarkm break; /* Successful connection. */ 48457429Smarkm } 48557429Smarkm 48657429Smarkm /* Return failure if we didn't get a successful connection. */ 487162856Sdes if (sock == -1) { 488147005Sdes error("ssh: connect to host %s port %s: %s", 489106130Sdes host, strport, strerror(errno)); 490147005Sdes return (-1); 491106130Sdes } 49257429Smarkm 49357429Smarkm debug("Connection established."); 49457429Smarkm 495126277Sdes /* Set SO_KEEPALIVE if requested. */ 496181111Sdes if (want_keepalive && 49776262Sgreen setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 49876262Sgreen sizeof(on)) < 0) 49976262Sgreen error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 50076262Sgreen 50157429Smarkm /* Set the connection. */ 50257429Smarkm packet_set_connection(sock, sock); 50357429Smarkm 50492559Sdes return 0; 50557429Smarkm} 50657429Smarkm 507261320Sdesint 508261320Sdesssh_connect(const char *host, struct addrinfo *addrs, 509261320Sdes struct sockaddr_storage *hostaddr, u_short port, int family, 510261320Sdes int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 511261320Sdes{ 512261320Sdes if (options.proxy_command == NULL) { 513261320Sdes return ssh_connect_direct(host, addrs, hostaddr, port, family, 514261320Sdes connection_attempts, timeout_ms, want_keepalive, needpriv); 515261320Sdes } else if (strcmp(options.proxy_command, "-") == 0) { 516261320Sdes packet_set_connection(STDIN_FILENO, STDOUT_FILENO); 517261320Sdes return 0; /* Always succeeds */ 518261320Sdes } else if (options.proxy_use_fdpass) { 519261320Sdes return ssh_proxy_fdpass_connect(host, port, 520261320Sdes options.proxy_command); 521261320Sdes } 522261320Sdes return ssh_proxy_connect(host, port, options.proxy_command); 523261320Sdes} 524261320Sdes 525248619Sdesstatic void 526248619Sdessend_client_banner(int connection_out, int minor1) 527248619Sdes{ 528248619Sdes /* Send our own protocol version identification. */ 529294325Sdes xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s%s", 530248619Sdes compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 531248619Sdes compat20 ? PROTOCOL_MINOR_2 : minor1, 532294325Sdes SSH_VERSION, 533248619Sdes *options.version_addendum == '\0' ? "" : " ", 534248619Sdes options.version_addendum, compat20 ? "\r\n" : "\n"); 535296633Sdes if (atomicio(vwrite, connection_out, client_version_string, 536248619Sdes strlen(client_version_string)) != strlen(client_version_string)) 537248619Sdes fatal("write: %.100s", strerror(errno)); 538248619Sdes chop(client_version_string); 539248619Sdes debug("Local version string %.100s", client_version_string); 540248619Sdes} 541248619Sdes 54257429Smarkm/* 54360576Skris * Waits for the server identification string, and sends our own 54460576Skris * identification string. 54557429Smarkm */ 546197679Sdesvoid 547181111Sdesssh_exchange_identification(int timeout_ms) 54857429Smarkm{ 54960576Skris char buf[256], remote_version[256]; /* must be same size! */ 550149753Sdes int remote_major, remote_minor, mismatch; 55160576Skris int connection_in = packet_get_connection_in(); 55260576Skris int connection_out = packet_get_connection_out(); 553248619Sdes int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0; 554162856Sdes u_int i, n; 555181111Sdes size_t len; 556181111Sdes int fdsetsz, remaining, rc; 557181111Sdes struct timeval t_start, t_remaining; 558181111Sdes fd_set *fdset; 55957429Smarkm 560181111Sdes fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); 561181111Sdes fdset = xcalloc(1, fdsetsz); 562181111Sdes 563248619Sdes /* 564248619Sdes * If we are SSH2-only then we can send the banner immediately and 565248619Sdes * save a round-trip. 566248619Sdes */ 567248619Sdes if (options.protocol == SSH_PROTO_2) { 568248619Sdes enable_compat20(); 569248619Sdes send_client_banner(connection_out, 0); 570248619Sdes client_banner_sent = 1; 571248619Sdes } 572248619Sdes 573149753Sdes /* Read other side's version identification. */ 574181111Sdes remaining = timeout_ms; 575162856Sdes for (n = 0;;) { 57665674Skris for (i = 0; i < sizeof(buf) - 1; i++) { 577181111Sdes if (timeout_ms > 0) { 578181111Sdes gettimeofday(&t_start, NULL); 579181111Sdes ms_to_timeval(&t_remaining, remaining); 580181111Sdes FD_SET(connection_in, fdset); 581181111Sdes rc = select(connection_in + 1, fdset, NULL, 582181111Sdes fdset, &t_remaining); 583181111Sdes ms_subtract_diff(&t_start, &remaining); 584181111Sdes if (rc == 0 || remaining <= 0) 585181111Sdes fatal("Connection timed out during " 586181111Sdes "banner exchange"); 587181111Sdes if (rc == -1) { 588181111Sdes if (errno == EINTR) 589181111Sdes continue; 590181111Sdes fatal("ssh_exchange_identification: " 591181111Sdes "select: %s", strerror(errno)); 592181111Sdes } 593181111Sdes } 594149753Sdes 595296633Sdes len = atomicio(read, connection_in, &buf[i], 1); 596181111Sdes 597149753Sdes if (len != 1 && errno == EPIPE) 598181111Sdes fatal("ssh_exchange_identification: " 599181111Sdes "Connection closed by remote host"); 600149753Sdes else if (len != 1) 601181111Sdes fatal("ssh_exchange_identification: " 602181111Sdes "read: %.100s", strerror(errno)); 60365674Skris if (buf[i] == '\r') { 60465674Skris buf[i] = '\n'; 60565674Skris buf[i + 1] = 0; 60665674Skris continue; /**XXX wait for \n */ 60765674Skris } 60865674Skris if (buf[i] == '\n') { 60965674Skris buf[i + 1] = 0; 61065674Skris break; 61165674Skris } 612162856Sdes if (++n > 65536) 613181111Sdes fatal("ssh_exchange_identification: " 614181111Sdes "No banner received"); 61560576Skris } 61665674Skris buf[sizeof(buf) - 1] = 0; 61765674Skris if (strncmp(buf, "SSH-", 4) == 0) 61860576Skris break; 61965674Skris debug("ssh_exchange_identification: %s", buf); 62060576Skris } 62160576Skris server_version_string = xstrdup(buf); 622255767Sdes free(fdset); 62357429Smarkm 62460576Skris /* 62560576Skris * Check that the versions match. In future this might accept 62660576Skris * several versions and set appropriate flags to handle them. 62760576Skris */ 62860576Skris if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 62960576Skris &remote_major, &remote_minor, remote_version) != 3) 63060576Skris fatal("Bad remote protocol version identification: '%.100s'", buf); 63160576Skris debug("Remote protocol version %d.%d, remote software version %.100s", 63292559Sdes remote_major, remote_minor, remote_version); 63357429Smarkm 634294332Sdes active_state->compat = compat_datafellows(remote_version); 63560576Skris mismatch = 0; 63657429Smarkm 63792559Sdes switch (remote_major) { 63860576Skris case 1: 63960576Skris if (remote_minor == 99 && 64060576Skris (options.protocol & SSH_PROTO_2) && 64160576Skris !(options.protocol & SSH_PROTO_1_PREFERRED)) { 64260576Skris enable_compat20(); 64360576Skris break; 64460576Skris } 64560576Skris if (!(options.protocol & SSH_PROTO_1)) { 64660576Skris mismatch = 1; 64760576Skris break; 64860576Skris } 64960576Skris if (remote_minor < 3) { 65060576Skris fatal("Remote machine has too old SSH software version."); 65176262Sgreen } else if (remote_minor == 3 || remote_minor == 4) { 65260576Skris /* We speak 1.3, too. */ 65360576Skris enable_compat13(); 65476262Sgreen minor1 = 3; 65560576Skris if (options.forward_agent) { 656124211Sdes logit("Agent forwarding disabled for protocol 1.3"); 65760576Skris options.forward_agent = 0; 65860576Skris } 65960576Skris } 66060576Skris break; 66160576Skris case 2: 66260576Skris if (options.protocol & SSH_PROTO_2) { 66360576Skris enable_compat20(); 66460576Skris break; 66560576Skris } 66660576Skris /* FALLTHROUGH */ 66760576Skris default: 66860576Skris mismatch = 1; 66960576Skris break; 67060576Skris } 67160576Skris if (mismatch) 67260576Skris fatal("Protocol major versions differ: %d vs. %d", 67360576Skris (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 67460576Skris remote_major); 675261320Sdes if ((datafellows & SSH_BUG_DERIVEKEY) != 0) 676261320Sdes fatal("Server version \"%.100s\" uses unsafe key agreement; " 677261320Sdes "refusing connection", remote_version); 678261320Sdes if ((datafellows & SSH_BUG_RSASIGMD5) != 0) 679261320Sdes logit("Server version \"%.100s\" uses unsafe RSA signature " 680261320Sdes "scheme; disabling use of RSA keys", remote_version); 681248619Sdes if (!client_banner_sent) 682248619Sdes send_client_banner(connection_out, minor1); 68360576Skris chop(server_version_string); 68457429Smarkm} 68557429Smarkm 68676262Sgreen/* defaults to 'no' */ 68792559Sdesstatic int 68892559Sdesconfirm(const char *prompt) 68957429Smarkm{ 69092559Sdes const char *msg, *again = "Please type 'yes' or 'no': "; 69192559Sdes char *p; 69292559Sdes int ret = -1; 69357429Smarkm 69476262Sgreen if (options.batch_mode) 69576262Sgreen return 0; 69692559Sdes for (msg = prompt;;msg = again) { 69792559Sdes p = read_passphrase(msg, RP_ECHO); 69892559Sdes if (p == NULL || 69992559Sdes (p[0] == '\0') || (p[0] == '\n') || 70092559Sdes strncasecmp(p, "no", 2) == 0) 70192559Sdes ret = 0; 702106130Sdes if (p && strncasecmp(p, "yes", 3) == 0) 70392559Sdes ret = 1; 704255767Sdes free(p); 70592559Sdes if (ret != -1) 70692559Sdes return ret; 70757429Smarkm } 70857429Smarkm} 70957429Smarkm 710204917Sdesstatic int 711204917Sdescheck_host_cert(const char *host, const Key *host_key) 712204917Sdes{ 713204917Sdes const char *reason; 714204917Sdes 715204917Sdes if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { 716204917Sdes error("%s", reason); 717204917Sdes return 0; 718204917Sdes } 719294328Sdes if (buffer_len(host_key->cert->critical) != 0) { 720215116Sdes error("Certificate for %s contains unsupported " 721215116Sdes "critical options(s)", host); 722204917Sdes return 0; 723204917Sdes } 724204917Sdes return 1; 725204917Sdes} 726204917Sdes 727221420Sdesstatic int 728221420Sdessockaddr_is_local(struct sockaddr *hostaddr) 729221420Sdes{ 730221420Sdes switch (hostaddr->sa_family) { 731221420Sdes case AF_INET: 732221420Sdes return (ntohl(((struct sockaddr_in *)hostaddr)-> 733221420Sdes sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 734221420Sdes case AF_INET6: 735221420Sdes return IN6_IS_ADDR_LOOPBACK( 736221420Sdes &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 737221420Sdes default: 738221420Sdes return 0; 739221420Sdes } 740221420Sdes} 741221420Sdes 74257429Smarkm/* 743221420Sdes * Prepare the hostname and ip address strings that are used to lookup 744221420Sdes * host keys in known_hosts files. These may have a port number appended. 745221420Sdes */ 746221420Sdesvoid 747221420Sdesget_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, 748221420Sdes u_short port, char **hostfile_hostname, char **hostfile_ipaddr) 749221420Sdes{ 750221420Sdes char ntop[NI_MAXHOST]; 751221420Sdes socklen_t addrlen; 752221420Sdes 753221420Sdes switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { 754221420Sdes case -1: 755221420Sdes addrlen = 0; 756221420Sdes break; 757221420Sdes case AF_INET: 758221420Sdes addrlen = sizeof(struct sockaddr_in); 759221420Sdes break; 760221420Sdes case AF_INET6: 761221420Sdes addrlen = sizeof(struct sockaddr_in6); 762221420Sdes break; 763221420Sdes default: 764221420Sdes addrlen = sizeof(struct sockaddr); 765221420Sdes break; 766221420Sdes } 767221420Sdes 768221420Sdes /* 769221420Sdes * We don't have the remote ip-address for connections 770221420Sdes * using a proxy command 771221420Sdes */ 772221420Sdes if (hostfile_ipaddr != NULL) { 773221420Sdes if (options.proxy_command == NULL) { 774221420Sdes if (getnameinfo(hostaddr, addrlen, 775221420Sdes ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) 776294332Sdes fatal("%s: getnameinfo failed", __func__); 777221420Sdes *hostfile_ipaddr = put_host_port(ntop, port); 778221420Sdes } else { 779221420Sdes *hostfile_ipaddr = xstrdup("<no hostip for proxy " 780221420Sdes "command>"); 781221420Sdes } 782221420Sdes } 783221420Sdes 784221420Sdes /* 785221420Sdes * Allow the user to record the key under a different name or 786221420Sdes * differentiate a non-standard port. This is useful for ssh 787221420Sdes * tunneling over forwarded connections or if you run multiple 788221420Sdes * sshd's on different ports on the same machine. 789221420Sdes */ 790221420Sdes if (hostfile_hostname != NULL) { 791221420Sdes if (options.host_key_alias != NULL) { 792221420Sdes *hostfile_hostname = xstrdup(options.host_key_alias); 793221420Sdes debug("using hostkeyalias: %s", *hostfile_hostname); 794221420Sdes } else { 795221420Sdes *hostfile_hostname = put_host_port(hostname, port); 796221420Sdes } 797221420Sdes } 798221420Sdes} 799221420Sdes 800221420Sdes/* 80192559Sdes * check whether the supplied host key is valid, return -1 if the key 802226046Sdes * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. 80357429Smarkm */ 804162856Sdes#define RDRW 0 805162856Sdes#define RDONLY 1 806162856Sdes#define ROQUIET 2 80792559Sdesstatic int 808162856Sdescheck_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 809226046Sdes Key *host_key, int readonly, 810226046Sdes char **user_hostfiles, u_int num_user_hostfiles, 811226046Sdes char **system_hostfiles, u_int num_system_hostfiles) 81257429Smarkm{ 813226046Sdes HostStatus host_status; 814226046Sdes HostStatus ip_status; 815221420Sdes Key *raw_key = NULL; 816162856Sdes char *ip = NULL, *host = NULL; 817181111Sdes char hostline[1000], *hostp, *fp, *ra; 81892559Sdes char msg[1024]; 819226046Sdes const char *type; 820226046Sdes const struct hostkey_entry *host_found, *ip_found; 821221420Sdes int len, cancelled_forwarding = 0; 822226046Sdes int local = sockaddr_is_local(hostaddr); 823226046Sdes int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; 824294332Sdes int hostkey_trusted = 0; /* Known or explicitly accepted by user */ 825221420Sdes struct hostkeys *host_hostkeys, *ip_hostkeys; 826226046Sdes u_int i; 82757429Smarkm 82860576Skris /* 82960576Skris * Force accepting of the host key for loopback/localhost. The 83060576Skris * problem is that if the home directory is NFS-mounted to multiple 83160576Skris * machines, localhost will refer to a different machine in each of 83260576Skris * them, and the user will get bogus HOST_CHANGED warnings. This 83360576Skris * essentially disables host authentication for localhost; however, 83460576Skris * this is probably not a real problem. 83560576Skris */ 83692559Sdes if (options.no_host_authentication_for_localhost == 1 && local && 83792559Sdes options.host_key_alias == NULL) { 83876262Sgreen debug("Forcing accepting of host key for " 83976262Sgreen "loopback/localhost."); 84092559Sdes return 0; 84160576Skris } 84257429Smarkm 84360576Skris /* 844221420Sdes * Prepare the hostname and address strings used for hostkey lookup. 845221420Sdes * In some cases, these will have a port number appended. 84660576Skris */ 847221420Sdes get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); 848181111Sdes 84976262Sgreen /* 85076262Sgreen * Turn off check_host_ip if the connection is to localhost, via proxy 85176262Sgreen * command or if we don't have a hostname to compare with 85276262Sgreen */ 853162856Sdes if (options.check_host_ip && (local || 854162856Sdes strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 85576262Sgreen options.check_host_ip = 0; 85657429Smarkm 857221420Sdes host_hostkeys = init_hostkeys(); 858226046Sdes for (i = 0; i < num_user_hostfiles; i++) 859226046Sdes load_hostkeys(host_hostkeys, host, user_hostfiles[i]); 860226046Sdes for (i = 0; i < num_system_hostfiles; i++) 861226046Sdes load_hostkeys(host_hostkeys, host, system_hostfiles[i]); 862221420Sdes 863221420Sdes ip_hostkeys = NULL; 864221420Sdes if (!want_cert && options.check_host_ip) { 865221420Sdes ip_hostkeys = init_hostkeys(); 866226046Sdes for (i = 0; i < num_user_hostfiles; i++) 867226046Sdes load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); 868226046Sdes for (i = 0; i < num_system_hostfiles; i++) 869226046Sdes load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); 87076262Sgreen } 87176262Sgreen 872204917Sdes retry: 873221420Sdes /* Reload these as they may have changed on cert->key downgrade */ 874204917Sdes want_cert = key_is_cert(host_key); 875204917Sdes type = key_type(host_key); 876204917Sdes 87776262Sgreen /* 878157019Sdes * Check if the host key is present in the user's list of known 87960576Skris * hosts or in the systemwide list. 88060576Skris */ 881221420Sdes host_status = check_key_in_hostkeys(host_hostkeys, host_key, 882221420Sdes &host_found); 883221420Sdes 88460576Skris /* 88560576Skris * Also perform check for the ip address, skip the check if we are 886204917Sdes * localhost, looking for a certificate, or the hostname was an ip 887204917Sdes * address to begin with. 88860576Skris */ 889221420Sdes if (!want_cert && ip_hostkeys != NULL) { 890221420Sdes ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, 891221420Sdes &ip_found); 89260576Skris if (host_status == HOST_CHANGED && 893221420Sdes (ip_status != HOST_CHANGED || 894221420Sdes (ip_found != NULL && 895221420Sdes !key_equal(ip_found->key, host_found->key)))) 89660576Skris host_ip_differ = 1; 89760576Skris } else 89860576Skris ip_status = host_status; 89957429Smarkm 90060576Skris switch (host_status) { 90160576Skris case HOST_OK: 90260576Skris /* The host is known and the key matches. */ 903204917Sdes debug("Host '%.200s' is known and matches the %s host %s.", 904204917Sdes host, type, want_cert ? "certificate" : "key"); 905221420Sdes debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", 906221420Sdes host_found->file, host_found->line); 907204917Sdes if (want_cert && !check_host_cert(hostname, host_key)) 908204917Sdes goto fail; 90976262Sgreen if (options.check_host_ip && ip_status == HOST_NEW) { 910204917Sdes if (readonly || want_cert) 911124211Sdes logit("%s host key for IP address " 91292559Sdes "'%.128s' not in list of known hosts.", 91392559Sdes type, ip); 914226046Sdes else if (!add_host_to_hostfile(user_hostfiles[0], ip, 915147005Sdes host_key, options.hash_known_hosts)) 916124211Sdes logit("Failed to add the %s host key for IP " 91792559Sdes "address '%.128s' to the list of known " 918294336Sdes "hosts (%.500s).", type, ip, 919226046Sdes user_hostfiles[0]); 92076262Sgreen else 921124211Sdes logit("Warning: Permanently added the %s host " 92292559Sdes "key for IP address '%.128s' to the list " 92392559Sdes "of known hosts.", type, ip); 924181111Sdes } else if (options.visual_host_key) { 925294332Sdes fp = sshkey_fingerprint(host_key, 926294332Sdes options.fingerprint_hash, SSH_FP_DEFAULT); 927294332Sdes ra = sshkey_fingerprint(host_key, 928294332Sdes options.fingerprint_hash, SSH_FP_RANDOMART); 929294332Sdes if (fp == NULL || ra == NULL) 930294332Sdes fatal("%s: sshkey_fingerprint fail", __func__); 931296633Sdes logit("Host key fingerprint is %s\n%s", fp, ra); 932255767Sdes free(ra); 933255767Sdes free(fp); 93460576Skris } 935294332Sdes hostkey_trusted = 1; 93660576Skris break; 93760576Skris case HOST_NEW: 938162856Sdes if (options.host_key_alias == NULL && port != 0 && 939162856Sdes port != SSH_DEFAULT_PORT) { 940162856Sdes debug("checking without port identifier"); 941192595Sdes if (check_host_key(hostname, hostaddr, 0, host_key, 942226046Sdes ROQUIET, user_hostfiles, num_user_hostfiles, 943226046Sdes system_hostfiles, num_system_hostfiles) == 0) { 944162856Sdes debug("found matching key w/out port"); 945162856Sdes break; 946162856Sdes } 947162856Sdes } 948204917Sdes if (readonly || want_cert) 94992559Sdes goto fail; 95060576Skris /* The host is new. */ 95160576Skris if (options.strict_host_key_checking == 1) { 95292559Sdes /* 95392559Sdes * User has requested strict host key checking. We 95492559Sdes * will not add the host key automatically. The only 95592559Sdes * alternative left is to abort. 95692559Sdes */ 95792559Sdes error("No %s host key is known for %.200s and you " 95892559Sdes "have requested strict checking.", type, host); 95992559Sdes goto fail; 96060576Skris } else if (options.strict_host_key_checking == 2) { 961124211Sdes char msg1[1024], msg2[1024]; 962124211Sdes 963221420Sdes if (show_other_keys(host_hostkeys, host_key)) 964124211Sdes snprintf(msg1, sizeof(msg1), 965149753Sdes "\nbut keys of different type are already" 966149753Sdes " known for this host."); 967124211Sdes else 968124211Sdes snprintf(msg1, sizeof(msg1), "."); 96960576Skris /* The default */ 970294332Sdes fp = sshkey_fingerprint(host_key, 971294332Sdes options.fingerprint_hash, SSH_FP_DEFAULT); 972294332Sdes ra = sshkey_fingerprint(host_key, 973294332Sdes options.fingerprint_hash, SSH_FP_RANDOMART); 974294332Sdes if (fp == NULL || ra == NULL) 975294332Sdes fatal("%s: sshkey_fingerprint fail", __func__); 976124211Sdes msg2[0] = '\0'; 977124211Sdes if (options.verify_host_key_dns) { 978126277Sdes if (matching_host_key_dns) 979124211Sdes snprintf(msg2, sizeof(msg2), 980124211Sdes "Matching host key fingerprint" 981124211Sdes " found in DNS.\n"); 982124211Sdes else 983124211Sdes snprintf(msg2, sizeof(msg2), 984124211Sdes "No matching host key fingerprint" 985124211Sdes " found in DNS.\n"); 986124211Sdes } 98792559Sdes snprintf(msg, sizeof(msg), 98892559Sdes "The authenticity of host '%.200s (%s)' can't be " 989106130Sdes "established%s\n" 990181111Sdes "%s key fingerprint is %s.%s%s\n%s" 99192559Sdes "Are you sure you want to continue connecting " 992106130Sdes "(yes/no)? ", 993181111Sdes host, ip, msg1, type, fp, 994181111Sdes options.visual_host_key ? "\n" : "", 995181111Sdes options.visual_host_key ? ra : "", 996181111Sdes msg2); 997255767Sdes free(ra); 998255767Sdes free(fp); 99992559Sdes if (!confirm(msg)) 100092559Sdes goto fail; 1001294332Sdes hostkey_trusted = 1; /* user explicitly confirmed */ 100260576Skris } 1003147005Sdes /* 1004147005Sdes * If not in strict mode, add the key automatically to the 1005147005Sdes * local known_hosts file. 1006147005Sdes */ 100776262Sgreen if (options.check_host_ip && ip_status == HOST_NEW) { 1008221420Sdes snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 100960576Skris hostp = hostline; 1010147005Sdes if (options.hash_known_hosts) { 1011147005Sdes /* Add hash of host and IP separately */ 1012226046Sdes r = add_host_to_hostfile(user_hostfiles[0], 1013226046Sdes host, host_key, options.hash_known_hosts) && 1014226046Sdes add_host_to_hostfile(user_hostfiles[0], ip, 1015147005Sdes host_key, options.hash_known_hosts); 1016147005Sdes } else { 1017147005Sdes /* Add unhashed "host,ip" */ 1018226046Sdes r = add_host_to_hostfile(user_hostfiles[0], 1019147005Sdes hostline, host_key, 1020147005Sdes options.hash_known_hosts); 1021147005Sdes } 1022147005Sdes } else { 1023226046Sdes r = add_host_to_hostfile(user_hostfiles[0], host, 1024226046Sdes host_key, options.hash_known_hosts); 102560576Skris hostp = host; 1026147005Sdes } 102757429Smarkm 1028147005Sdes if (!r) 1029124211Sdes logit("Failed to add the host to the list of known " 1030226046Sdes "hosts (%.500s).", user_hostfiles[0]); 103160576Skris else 1032124211Sdes logit("Warning: Permanently added '%.200s' (%s) to the " 103392559Sdes "list of known hosts.", hostp, type); 103457429Smarkm break; 1035204917Sdes case HOST_REVOKED: 1036204917Sdes error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1037204917Sdes error("@ WARNING: REVOKED HOST KEY DETECTED! @"); 1038204917Sdes error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1039204917Sdes error("The %s host key for %s is marked as revoked.", type, host); 1040204917Sdes error("This could mean that a stolen key is being used to"); 1041204917Sdes error("impersonate this host."); 1042204917Sdes 1043204917Sdes /* 1044204917Sdes * If strict host key checking is in use, the user will have 1045204917Sdes * to edit the key manually and we can only abort. 1046204917Sdes */ 1047204917Sdes if (options.strict_host_key_checking) { 1048204917Sdes error("%s host key for %.200s was revoked and you have " 1049204917Sdes "requested strict checking.", type, host); 1050204917Sdes goto fail; 1051204917Sdes } 1052204917Sdes goto continue_unsafe; 1053204917Sdes 105460576Skris case HOST_CHANGED: 1055204917Sdes if (want_cert) { 1056204917Sdes /* 1057204917Sdes * This is only a debug() since it is valid to have 1058204917Sdes * CAs with wildcard DNS matches that don't match 1059204917Sdes * all hosts that one might visit. 1060204917Sdes */ 1061204917Sdes debug("Host certificate authority does not " 1062221420Sdes "match %s in %s:%lu", CA_MARKER, 1063221420Sdes host_found->file, host_found->line); 1064204917Sdes goto fail; 1065204917Sdes } 1066162856Sdes if (readonly == ROQUIET) 1067162856Sdes goto fail; 106860576Skris if (options.check_host_ip && host_ip_differ) { 1069137019Sdes char *key_msg; 107060576Skris if (ip_status == HOST_NEW) 1071137019Sdes key_msg = "is unknown"; 107260576Skris else if (ip_status == HOST_OK) 1073137019Sdes key_msg = "is unchanged"; 107460576Skris else 1075137019Sdes key_msg = "has a different value"; 107660576Skris error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 107760576Skris error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 107860576Skris error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 107960576Skris error("The %s host key for %s has changed,", type, host); 1080181111Sdes error("and the key for the corresponding IP address %s", ip); 1081137019Sdes error("%s. This could either mean that", key_msg); 108260576Skris error("DNS SPOOFING is happening or the IP address for the host"); 108376262Sgreen error("and its host key have changed at the same time."); 108476262Sgreen if (ip_status != HOST_NEW) 1085221420Sdes error("Offending key for IP in %s:%lu", 1086221420Sdes ip_found->file, ip_found->line); 108760576Skris } 108860576Skris /* The host key has changed. */ 1089126277Sdes warn_changed_key(host_key); 109060576Skris error("Add correct host key in %.100s to get rid of this message.", 1091226046Sdes user_hostfiles[0]); 1092221420Sdes error("Offending %s key in %s:%lu", key_type(host_found->key), 1093221420Sdes host_found->file, host_found->line); 109457429Smarkm 109560576Skris /* 109660576Skris * If strict host key checking is in use, the user will have 109760576Skris * to edit the key manually and we can only abort. 109860576Skris */ 109992559Sdes if (options.strict_host_key_checking) { 110092559Sdes error("%s host key for %.200s has changed and you have " 110192559Sdes "requested strict checking.", type, host); 110292559Sdes goto fail; 110392559Sdes } 110457429Smarkm 1105204917Sdes continue_unsafe: 110657429Smarkm /* 110760576Skris * If strict host key checking has not been requested, allow 1108124211Sdes * the connection but without MITM-able authentication or 1109162856Sdes * forwarding. 111057429Smarkm */ 111160576Skris if (options.password_authentication) { 111292559Sdes error("Password authentication is disabled to avoid " 111392559Sdes "man-in-the-middle attacks."); 111460576Skris options.password_authentication = 0; 1115181111Sdes cancelled_forwarding = 1; 111657429Smarkm } 1117124211Sdes if (options.kbd_interactive_authentication) { 1118124211Sdes error("Keyboard-interactive authentication is disabled" 1119124211Sdes " to avoid man-in-the-middle attacks."); 1120124211Sdes options.kbd_interactive_authentication = 0; 1121124211Sdes options.challenge_response_authentication = 0; 1122181111Sdes cancelled_forwarding = 1; 1123124211Sdes } 1124124211Sdes if (options.challenge_response_authentication) { 1125124211Sdes error("Challenge/response authentication is disabled" 1126124211Sdes " to avoid man-in-the-middle attacks."); 1127124211Sdes options.challenge_response_authentication = 0; 1128181111Sdes cancelled_forwarding = 1; 1129124211Sdes } 113060576Skris if (options.forward_agent) { 113192559Sdes error("Agent forwarding is disabled to avoid " 113292559Sdes "man-in-the-middle attacks."); 113360576Skris options.forward_agent = 0; 1134181111Sdes cancelled_forwarding = 1; 113560576Skris } 113676262Sgreen if (options.forward_x11) { 113792559Sdes error("X11 forwarding is disabled to avoid " 113892559Sdes "man-in-the-middle attacks."); 113976262Sgreen options.forward_x11 = 0; 1140181111Sdes cancelled_forwarding = 1; 114176262Sgreen } 114292559Sdes if (options.num_local_forwards > 0 || 114392559Sdes options.num_remote_forwards > 0) { 114492559Sdes error("Port forwarding is disabled to avoid " 114592559Sdes "man-in-the-middle attacks."); 114692559Sdes options.num_local_forwards = 114792559Sdes options.num_remote_forwards = 0; 1148181111Sdes cancelled_forwarding = 1; 114976262Sgreen } 1150162856Sdes if (options.tun_open != SSH_TUNMODE_NO) { 1151162856Sdes error("Tunnel forwarding is disabled to avoid " 1152162856Sdes "man-in-the-middle attacks."); 1153162856Sdes options.tun_open = SSH_TUNMODE_NO; 1154181111Sdes cancelled_forwarding = 1; 1155162856Sdes } 1156181111Sdes if (options.exit_on_forward_failure && cancelled_forwarding) 1157181111Sdes fatal("Error: forwarding disabled due to host key " 1158181111Sdes "check failure"); 1159181111Sdes 116060576Skris /* 116160576Skris * XXX Should permit the user to change to use the new id. 116260576Skris * This could be done by converting the host key to an 116360576Skris * identifying sentence, tell that the host identifies itself 1164204917Sdes * by that sentence, and ask the user if he/she wishes to 116560576Skris * accept the authentication. 116660576Skris */ 116757429Smarkm break; 1168106130Sdes case HOST_FOUND: 1169106130Sdes fatal("internal error"); 1170106130Sdes break; 117157429Smarkm } 117276262Sgreen 117376262Sgreen if (options.check_host_ip && host_status != HOST_CHANGED && 117476262Sgreen ip_status == HOST_CHANGED) { 117592559Sdes snprintf(msg, sizeof(msg), 117692559Sdes "Warning: the %s host key for '%.200s' " 117792559Sdes "differs from the key for the IP address '%.128s'" 1178221420Sdes "\nOffending key for IP in %s:%lu", 1179221420Sdes type, host, ip, ip_found->file, ip_found->line); 118092559Sdes if (host_status == HOST_OK) { 118192559Sdes len = strlen(msg); 118292559Sdes snprintf(msg + len, sizeof(msg) - len, 1183221420Sdes "\nMatching host key in %s:%lu", 1184221420Sdes host_found->file, host_found->line); 118592559Sdes } 118676262Sgreen if (options.strict_host_key_checking == 1) { 1187124211Sdes logit("%s", msg); 118892559Sdes error("Exiting, you have requested strict checking."); 118992559Sdes goto fail; 119076262Sgreen } else if (options.strict_host_key_checking == 2) { 119192559Sdes strlcat(msg, "\nAre you sure you want " 119292559Sdes "to continue connecting (yes/no)? ", sizeof(msg)); 119392559Sdes if (!confirm(msg)) 119492559Sdes goto fail; 119592559Sdes } else { 1196124211Sdes logit("%s", msg); 119776262Sgreen } 119876262Sgreen } 119976262Sgreen 1200294332Sdes if (!hostkey_trusted && options.update_hostkeys) { 1201294332Sdes debug("%s: hostkey not known or explicitly trusted: " 1202294332Sdes "disabling UpdateHostkeys", __func__); 1203294332Sdes options.update_hostkeys = 0; 1204294332Sdes } 1205294332Sdes 1206255767Sdes free(ip); 1207255767Sdes free(host); 1208221420Sdes if (host_hostkeys != NULL) 1209221420Sdes free_hostkeys(host_hostkeys); 1210221420Sdes if (ip_hostkeys != NULL) 1211221420Sdes free_hostkeys(ip_hostkeys); 121292559Sdes return 0; 121392559Sdes 121492559Sdesfail: 1215204917Sdes if (want_cert && host_status != HOST_REVOKED) { 1216204917Sdes /* 1217204917Sdes * No matching certificate. Downgrade cert to raw key and 1218204917Sdes * search normally. 1219204917Sdes */ 1220204917Sdes debug("No matching CA found. Retry with plain key"); 1221204917Sdes raw_key = key_from_private(host_key); 1222204917Sdes if (key_drop_cert(raw_key) != 0) 1223204917Sdes fatal("Couldn't drop certificate"); 1224204917Sdes host_key = raw_key; 1225204917Sdes goto retry; 1226204917Sdes } 1227204917Sdes if (raw_key != NULL) 1228204917Sdes key_free(raw_key); 1229255767Sdes free(ip); 1230255767Sdes free(host); 1231221420Sdes if (host_hostkeys != NULL) 1232221420Sdes free_hostkeys(host_hostkeys); 1233221420Sdes if (ip_hostkeys != NULL) 1234221420Sdes free_hostkeys(ip_hostkeys); 123592559Sdes return -1; 123657429Smarkm} 123757429Smarkm 1238124211Sdes/* returns 0 if key verifies or -1 if key does NOT verify */ 123957565Smarkmint 124092559Sdesverify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 124157565Smarkm{ 1242296633Sdes u_int i; 1243294328Sdes int r = -1, flags = 0; 1244296633Sdes char valid[64], *fp = NULL, *cafp = NULL; 1245294332Sdes struct sshkey *plain = NULL; 124657565Smarkm 1247294332Sdes if ((fp = sshkey_fingerprint(host_key, 1248294332Sdes options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1249294332Sdes error("%s: fingerprint host key: %s", __func__, ssh_err(r)); 1250294332Sdes r = -1; 1251294332Sdes goto out; 1252294332Sdes } 1253221420Sdes 1254296633Sdes if (sshkey_is_cert(host_key)) { 1255296633Sdes if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, 1256296633Sdes options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1257296633Sdes error("%s: fingerprint CA key: %s", 1258296633Sdes __func__, ssh_err(r)); 1259296633Sdes r = -1; 1260296633Sdes goto out; 1261296633Sdes } 1262296633Sdes sshkey_format_cert_validity(host_key->cert, 1263296633Sdes valid, sizeof(valid)); 1264296633Sdes debug("Server host certificate: %s %s, serial %llu " 1265296633Sdes "ID \"%s\" CA %s %s valid %s", 1266296633Sdes sshkey_ssh_name(host_key), fp, 1267296633Sdes (unsigned long long)host_key->cert->serial, 1268296633Sdes host_key->cert->key_id, 1269296633Sdes sshkey_ssh_name(host_key->cert->signature_key), cafp, 1270296633Sdes valid); 1271296633Sdes for (i = 0; i < host_key->cert->nprincipals; i++) { 1272296633Sdes debug2("Server host certificate hostname: %s", 1273296633Sdes host_key->cert->principals[i]); 1274296633Sdes } 1275296633Sdes } else { 1276296633Sdes debug("Server host key: %s %s", compat20 ? 1277296633Sdes sshkey_ssh_name(host_key) : sshkey_type(host_key), fp); 1278296633Sdes } 1279294332Sdes 1280294332Sdes if (sshkey_equal(previous_host_key, host_key)) { 1281294332Sdes debug2("%s: server host key %s %s matches cached key", 1282294332Sdes __func__, sshkey_type(host_key), fp); 1283294332Sdes r = 0; 1284294332Sdes goto out; 1285294328Sdes } 1286294328Sdes 1287294332Sdes /* Check in RevokedHostKeys file if specified */ 1288294332Sdes if (options.revoked_host_keys != NULL) { 1289294332Sdes r = sshkey_check_revoked(host_key, options.revoked_host_keys); 1290294332Sdes switch (r) { 1291294332Sdes case 0: 1292294332Sdes break; /* not revoked */ 1293294332Sdes case SSH_ERR_KEY_REVOKED: 1294294332Sdes error("Host key %s %s revoked by file %s", 1295294332Sdes sshkey_type(host_key), fp, 1296294332Sdes options.revoked_host_keys); 1297294332Sdes r = -1; 1298294332Sdes goto out; 1299294332Sdes default: 1300294332Sdes error("Error checking host key %s %s in " 1301294332Sdes "revoked keys file %s: %s", sshkey_type(host_key), 1302294332Sdes fp, options.revoked_host_keys, ssh_err(r)); 1303294332Sdes r = -1; 1304294332Sdes goto out; 1305294332Sdes } 1306294332Sdes } 1307294332Sdes 1308285975Sdelphij if (options.verify_host_key_dns) { 1309285975Sdelphij /* 1310285975Sdelphij * XXX certs are not yet supported for DNS, so downgrade 1311285975Sdelphij * them and try the plain key. 1312285975Sdelphij */ 1313294332Sdes if ((r = sshkey_from_private(host_key, &plain)) != 0) 1314294332Sdes goto out; 1315294332Sdes if (sshkey_is_cert(plain)) 1316294332Sdes sshkey_drop_cert(plain); 1317285975Sdelphij if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { 1318285975Sdelphij if (flags & DNS_VERIFY_FOUND) { 1319285975Sdelphij if (options.verify_host_key_dns == 1 && 1320285975Sdelphij flags & DNS_VERIFY_MATCH && 1321285975Sdelphij flags & DNS_VERIFY_SECURE) { 1322294328Sdes r = 0; 1323294332Sdes goto out; 1324285975Sdelphij } 1325285975Sdelphij if (flags & DNS_VERIFY_MATCH) { 1326285975Sdelphij matching_host_key_dns = 1; 1327285975Sdelphij } else { 1328285975Sdelphij warn_changed_key(plain); 1329285975Sdelphij error("Update the SSHFP RR in DNS " 1330285975Sdelphij "with the new host key to get rid " 1331285975Sdelphij "of this message."); 1332285975Sdelphij } 1333126277Sdes } 1334124211Sdes } 1335124211Sdes } 1336294328Sdes r = check_host_key(host, hostaddr, options.port, host_key, RDRW, 1337226046Sdes options.user_hostfiles, options.num_user_hostfiles, 1338226046Sdes options.system_hostfiles, options.num_system_hostfiles); 1339294328Sdes 1340294332Sdesout: 1341294332Sdes sshkey_free(plain); 1342294332Sdes free(fp); 1343296633Sdes free(cafp); 1344294328Sdes if (r == 0 && host_key != NULL) { 1345294328Sdes key_free(previous_host_key); 1346294328Sdes previous_host_key = key_from_private(host_key); 1347294328Sdes } 1348294328Sdes 1349294328Sdes return r; 135057565Smarkm} 135157565Smarkm 135257429Smarkm/* 135360576Skris * Starts a dialog with the server, and authenticates the current user on the 135460576Skris * server. This does not need any extra privileges. The basic connection 135560576Skris * to the server must already have been established before this is called. 135660576Skris * If login fails, this function prints an error and never returns. 135760576Skris * This function does not require super-user privileges. 135857429Smarkm */ 135957429Smarkmvoid 136098684Sdesssh_login(Sensitive *sensitive, const char *orighost, 1361221420Sdes struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 136257429Smarkm{ 1363261320Sdes char *host; 136460576Skris char *server_user, *local_user; 136557429Smarkm 136657429Smarkm local_user = xstrdup(pw->pw_name); 136757429Smarkm server_user = options.user ? options.user : local_user; 136857429Smarkm 136957429Smarkm /* Convert the user-supplied hostname into all lowercase. */ 137057429Smarkm host = xstrdup(orighost); 1371261320Sdes lowercase(host); 137257429Smarkm 137357429Smarkm /* Exchange protocol version identification strings with the server. */ 1374181111Sdes ssh_exchange_identification(timeout_ms); 137557429Smarkm 137657429Smarkm /* Put the connection into non-blocking mode. */ 137757429Smarkm packet_set_nonblocking(); 137857429Smarkm 137957429Smarkm /* key exchange */ 138057429Smarkm /* authenticate user */ 1381294336Sdes debug("Authenticating to %s:%d as '%s'", host, port, server_user); 138260576Skris if (compat20) { 1383221420Sdes ssh_kex2(host, hostaddr, port); 138498684Sdes ssh_userauth2(local_user, server_user, host, sensitive); 138560576Skris } else { 1386294328Sdes#ifdef WITH_SSH1 138760576Skris ssh_kex(host, hostaddr); 138898684Sdes ssh_userauth1(local_user, server_user, host, sensitive); 1389294328Sdes#else 1390294336Sdes fatal("ssh1 is not supported"); 1391294328Sdes#endif 139260576Skris } 1393255767Sdes free(local_user); 139457429Smarkm} 139574500Sgreen 139674500Sgreenvoid 139774500Sgreenssh_put_password(char *password) 139874500Sgreen{ 139974500Sgreen int size; 140074500Sgreen char *padded; 140174500Sgreen 140276262Sgreen if (datafellows & SSH_BUG_PASSWORDPAD) { 140392559Sdes packet_put_cstring(password); 140476262Sgreen return; 140576262Sgreen } 1406323134Sdes size = ROUNDUP(strlen(password) + 1, 32); 1407162856Sdes padded = xcalloc(1, size); 140874500Sgreen strlcpy(padded, password, size); 140974500Sgreen packet_put_string(padded, size); 1410263712Sdes explicit_bzero(padded, size); 1411255767Sdes free(padded); 141274500Sgreen} 1413106130Sdes 1414221420Sdes/* print all known host keys for a given host, but skip keys of given type */ 1415106130Sdesstatic int 1416221420Sdesshow_other_keys(struct hostkeys *hostkeys, Key *key) 1417106130Sdes{ 1418261320Sdes int type[] = { 1419261320Sdes KEY_RSA1, 1420261320Sdes KEY_RSA, 1421261320Sdes KEY_DSA, 1422261320Sdes KEY_ECDSA, 1423261320Sdes KEY_ED25519, 1424261320Sdes -1 1425261320Sdes }; 1426221420Sdes int i, ret = 0; 1427181111Sdes char *fp, *ra; 1428221420Sdes const struct hostkey_entry *found; 1429106130Sdes 1430106130Sdes for (i = 0; type[i] != -1; i++) { 1431106130Sdes if (type[i] == key->type) 1432106130Sdes continue; 1433221420Sdes if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) 1434106130Sdes continue; 1435294332Sdes fp = sshkey_fingerprint(found->key, 1436294332Sdes options.fingerprint_hash, SSH_FP_DEFAULT); 1437294332Sdes ra = sshkey_fingerprint(found->key, 1438294332Sdes options.fingerprint_hash, SSH_FP_RANDOMART); 1439294332Sdes if (fp == NULL || ra == NULL) 1440294332Sdes fatal("%s: sshkey_fingerprint fail", __func__); 1441221420Sdes logit("WARNING: %s key found for host %s\n" 1442221420Sdes "in %s:%lu\n" 1443221420Sdes "%s key fingerprint %s.", 1444221420Sdes key_type(found->key), 1445221420Sdes found->host, found->file, found->line, 1446221420Sdes key_type(found->key), fp); 1447221420Sdes if (options.visual_host_key) 1448221420Sdes logit("%s", ra); 1449255767Sdes free(ra); 1450255767Sdes free(fp); 1451221420Sdes ret = 1; 1452106130Sdes } 1453221420Sdes return ret; 1454106130Sdes} 1455126277Sdes 1456126277Sdesstatic void 1457126277Sdeswarn_changed_key(Key *host_key) 1458126277Sdes{ 1459126277Sdes char *fp; 1460126277Sdes 1461294332Sdes fp = sshkey_fingerprint(host_key, options.fingerprint_hash, 1462294332Sdes SSH_FP_DEFAULT); 1463294332Sdes if (fp == NULL) 1464294332Sdes fatal("%s: sshkey_fingerprint fail", __func__); 1465126277Sdes 1466126277Sdes error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1467126277Sdes error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 1468126277Sdes error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1469126277Sdes error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 1470126277Sdes error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 1471221420Sdes error("It is also possible that a host key has just been changed."); 1472126277Sdes error("The fingerprint for the %s key sent by the remote host is\n%s.", 1473221420Sdes key_type(host_key), fp); 1474126277Sdes error("Please contact your system administrator."); 1475126277Sdes 1476255767Sdes free(fp); 1477126277Sdes} 1478157019Sdes 1479157019Sdes/* 1480157019Sdes * Execute a local command 1481157019Sdes */ 1482157019Sdesint 1483157019Sdesssh_local_cmd(const char *args) 1484157019Sdes{ 1485157019Sdes char *shell; 1486157019Sdes pid_t pid; 1487157019Sdes int status; 1488221420Sdes void (*osighand)(int); 1489157019Sdes 1490157019Sdes if (!options.permit_local_command || 1491157019Sdes args == NULL || !*args) 1492157019Sdes return (1); 1493157019Sdes 1494221420Sdes if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 1495157019Sdes shell = _PATH_BSHELL; 1496157019Sdes 1497221420Sdes osighand = signal(SIGCHLD, SIG_DFL); 1498157019Sdes pid = fork(); 1499157019Sdes if (pid == 0) { 1500221420Sdes signal(SIGPIPE, SIG_DFL); 1501157019Sdes debug3("Executing %s -c \"%s\"", shell, args); 1502157019Sdes execl(shell, shell, "-c", args, (char *)NULL); 1503157019Sdes error("Couldn't execute %s -c \"%s\": %s", 1504157019Sdes shell, args, strerror(errno)); 1505157019Sdes _exit(1); 1506157019Sdes } else if (pid == -1) 1507157019Sdes fatal("fork failed: %.100s", strerror(errno)); 1508157019Sdes while (waitpid(pid, &status, 0) == -1) 1509157019Sdes if (errno != EINTR) 1510157019Sdes fatal("Couldn't wait for child: %s", strerror(errno)); 1511221420Sdes signal(SIGCHLD, osighand); 1512157019Sdes 1513157019Sdes if (!WIFEXITED(status)) 1514157019Sdes return (1); 1515157019Sdes 1516157019Sdes return (WEXITSTATUS(status)); 1517157019Sdes} 1518296633Sdes 1519296633Sdesvoid 1520296633Sdesmaybe_add_key_to_agent(char *authfile, Key *private, char *comment, 1521296633Sdes char *passphrase) 1522296633Sdes{ 1523296633Sdes int auth_sock = -1, r; 1524296633Sdes 1525296633Sdes if (options.add_keys_to_agent == 0) 1526296633Sdes return; 1527296633Sdes 1528296633Sdes if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { 1529296633Sdes debug3("no authentication agent, not adding key"); 1530296633Sdes return; 1531296633Sdes } 1532296633Sdes 1533296633Sdes if (options.add_keys_to_agent == 2 && 1534296633Sdes !ask_permission("Add key %s (%s) to agent?", authfile, comment)) { 1535296633Sdes debug3("user denied adding this key"); 1536323136Sdes close(auth_sock); 1537296633Sdes return; 1538296633Sdes } 1539296633Sdes 1540296633Sdes if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, 1541296633Sdes (options.add_keys_to_agent == 3))) == 0) 1542296633Sdes debug("identity added to agent: %s", authfile); 1543296633Sdes else 1544296633Sdes debug("could not add identity to agent: %s (%d)", authfile, r); 1545323136Sdes close(auth_sock); 1546296633Sdes} 1547