1296781Sdes/* $OpenBSD: sshconnect.c,v 1.271 2016/01/14 22:56:56 markus 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" 17294666Sdes__RCSID("$FreeBSD$"); 1857429Smarkm 19295367Sdes#include <sys/param.h> /* roundup */ 20162856Sdes#include <sys/types.h> 21162856Sdes#include <sys/wait.h> 22162856Sdes#include <sys/stat.h> 23162856Sdes#include <sys/socket.h> 24162856Sdes#ifdef HAVE_SYS_TIME_H 25162856Sdes# include <sys/time.h> 26162856Sdes#endif 2760576Skris 28162856Sdes#include <netinet/in.h> 29162856Sdes#include <arpa/inet.h> 30262566Sdes#include <rpc/rpc.h> 31162856Sdes 32162856Sdes#include <ctype.h> 33162856Sdes#include <errno.h> 34204917Sdes#include <fcntl.h> 35162856Sdes#include <netdb.h> 36162856Sdes#ifdef HAVE_PATHS_H 37162856Sdes#include <paths.h> 38162856Sdes#endif 39162856Sdes#include <pwd.h> 40221420Sdes#include <signal.h> 41162856Sdes#include <stdarg.h> 42162856Sdes#include <stdio.h> 43162856Sdes#include <stdlib.h> 44162856Sdes#include <string.h> 45162856Sdes#include <unistd.h> 46162856Sdes 47162856Sdes#include "xmalloc.h" 48162856Sdes#include "key.h" 49162856Sdes#include "hostfile.h" 5076262Sgreen#include "ssh.h" 5157429Smarkm#include "rsa.h" 5260576Skris#include "buffer.h" 5357429Smarkm#include "packet.h" 5457429Smarkm#include "uidswap.h" 5557429Smarkm#include "compat.h" 5658585Skris#include "key.h" 5760576Skris#include "sshconnect.h" 5858585Skris#include "hostfile.h" 5976262Sgreen#include "log.h" 60295367Sdes#include "misc.h" 6176262Sgreen#include "readconf.h" 6276262Sgreen#include "atomicio.h" 63124211Sdes#include "dns.h" 64262566Sdes#include "monitor_fdpass.h" 65204917Sdes#include "ssh2.h" 66162856Sdes#include "version.h" 67295367Sdes#include "authfile.h" 68295367Sdes#include "ssherr.h" 69296781Sdes#include "authfd.h" 70124211Sdes 7160576Skrischar *client_version_string = NULL; 7260576Skrischar *server_version_string = NULL; 73295367SdesKey *previous_host_key = NULL; 7457429Smarkm 75157019Sdesstatic int matching_host_key_dns = 0; 76124211Sdes 77221420Sdesstatic pid_t proxy_command_pid = 0; 78221420Sdes 7998684Sdes/* import */ 8057429Smarkmextern Options options; 8157429Smarkmextern char *__progname; 8298684Sdesextern uid_t original_real_uid; 8398684Sdesextern uid_t original_effective_uid; 8457429Smarkm 85221420Sdesstatic int show_other_keys(struct hostkeys *, Key *); 86126277Sdesstatic void warn_changed_key(Key *); 8776262Sgreen 88262566Sdes/* Expand a proxy command */ 89262566Sdesstatic char * 90262566Sdesexpand_proxy_command(const char *proxy_command, const char *user, 91262566Sdes const char *host, int port) 92262566Sdes{ 93262566Sdes char *tmp, *ret, strport[NI_MAXSERV]; 94262566Sdes 95262566Sdes snprintf(strport, sizeof strport, "%d", port); 96262566Sdes xasprintf(&tmp, "exec %s", proxy_command); 97262566Sdes ret = percent_expand(tmp, "h", host, "p", strport, 98262566Sdes "r", options.user, (char *)NULL); 99262566Sdes free(tmp); 100262566Sdes return ret; 101262566Sdes} 102262566Sdes 10357429Smarkm/* 104262566Sdes * Connect to the given ssh server using a proxy command that passes a 105262566Sdes * a connected fd back to us. 106262566Sdes */ 107262566Sdesstatic int 108262566Sdesssh_proxy_fdpass_connect(const char *host, u_short port, 109262566Sdes const char *proxy_command) 110262566Sdes{ 111262566Sdes char *command_string; 112262566Sdes int sp[2], sock; 113262566Sdes pid_t pid; 114262566Sdes char *shell; 115262566Sdes 116262566Sdes if ((shell = getenv("SHELL")) == NULL) 117262566Sdes shell = _PATH_BSHELL; 118262566Sdes 119262566Sdes if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) 120262566Sdes fatal("Could not create socketpair to communicate with " 121262566Sdes "proxy dialer: %.100s", strerror(errno)); 122262566Sdes 123262566Sdes command_string = expand_proxy_command(proxy_command, options.user, 124262566Sdes host, port); 125262566Sdes debug("Executing proxy dialer command: %.500s", command_string); 126262566Sdes 127262566Sdes /* Fork and execute the proxy command. */ 128262566Sdes if ((pid = fork()) == 0) { 129262566Sdes char *argv[10]; 130262566Sdes 131262566Sdes /* Child. Permanently give up superuser privileges. */ 132262566Sdes permanently_drop_suid(original_real_uid); 133262566Sdes 134262566Sdes close(sp[1]); 135262566Sdes /* Redirect stdin and stdout. */ 136262566Sdes if (sp[0] != 0) { 137262566Sdes if (dup2(sp[0], 0) < 0) 138262566Sdes perror("dup2 stdin"); 139262566Sdes } 140262566Sdes if (sp[0] != 1) { 141262566Sdes if (dup2(sp[0], 1) < 0) 142262566Sdes perror("dup2 stdout"); 143262566Sdes } 144262566Sdes if (sp[0] >= 2) 145262566Sdes close(sp[0]); 146262566Sdes 147262566Sdes /* 148262566Sdes * Stderr is left as it is so that error messages get 149262566Sdes * printed on the user's terminal. 150262566Sdes */ 151262566Sdes argv[0] = shell; 152262566Sdes argv[1] = "-c"; 153262566Sdes argv[2] = command_string; 154262566Sdes argv[3] = NULL; 155262566Sdes 156262566Sdes /* 157262566Sdes * Execute the proxy command. 158262566Sdes * Note that we gave up any extra privileges above. 159262566Sdes */ 160262566Sdes execv(argv[0], argv); 161262566Sdes perror(argv[0]); 162262566Sdes exit(1); 163262566Sdes } 164262566Sdes /* Parent. */ 165262566Sdes if (pid < 0) 166262566Sdes fatal("fork failed: %.100s", strerror(errno)); 167262566Sdes close(sp[0]); 168262566Sdes free(command_string); 169262566Sdes 170262566Sdes if ((sock = mm_receive_fd(sp[1])) == -1) 171262566Sdes fatal("proxy dialer did not pass back a connection"); 172296781Sdes close(sp[1]); 173262566Sdes 174262566Sdes while (waitpid(pid, NULL, 0) == -1) 175262566Sdes if (errno != EINTR) 176262566Sdes fatal("Couldn't wait for child: %s", strerror(errno)); 177262566Sdes 178262566Sdes /* Set the connection file descriptors. */ 179262566Sdes packet_set_connection(sock, sock); 180262566Sdes 181262566Sdes return 0; 182262566Sdes} 183262566Sdes 184262566Sdes/* 18557429Smarkm * Connect to the given ssh server using a proxy command. 18657429Smarkm */ 18792559Sdesstatic int 18898684Sdesssh_proxy_connect(const char *host, u_short port, const char *proxy_command) 18957429Smarkm{ 190262566Sdes char *command_string; 19157429Smarkm int pin[2], pout[2]; 19260576Skris pid_t pid; 193262566Sdes char *shell; 19457429Smarkm 195221420Sdes if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 196181111Sdes shell = _PATH_BSHELL; 197181111Sdes 19857429Smarkm /* Create pipes for communicating with the proxy. */ 19957429Smarkm if (pipe(pin) < 0 || pipe(pout) < 0) 20057429Smarkm fatal("Could not create pipes to communicate with the proxy: %.100s", 20192559Sdes strerror(errno)); 20257429Smarkm 203262566Sdes command_string = expand_proxy_command(proxy_command, options.user, 204262566Sdes host, port); 20557429Smarkm debug("Executing proxy command: %.500s", command_string); 20657429Smarkm 20757429Smarkm /* Fork and execute the proxy command. */ 20857429Smarkm if ((pid = fork()) == 0) { 20957429Smarkm char *argv[10]; 21057429Smarkm 21157429Smarkm /* Child. Permanently give up superuser privileges. */ 212162856Sdes permanently_drop_suid(original_real_uid); 21357429Smarkm 21457429Smarkm /* Redirect stdin and stdout. */ 21557429Smarkm close(pin[1]); 21657429Smarkm if (pin[0] != 0) { 21757429Smarkm if (dup2(pin[0], 0) < 0) 21857429Smarkm perror("dup2 stdin"); 21957429Smarkm close(pin[0]); 22057429Smarkm } 22157429Smarkm close(pout[0]); 22257429Smarkm if (dup2(pout[1], 1) < 0) 22357429Smarkm perror("dup2 stdout"); 22457429Smarkm /* Cannot be 1 because pin allocated two descriptors. */ 22557429Smarkm close(pout[1]); 22657429Smarkm 22757429Smarkm /* Stderr is left as it is so that error messages get 22857429Smarkm printed on the user's terminal. */ 229181111Sdes argv[0] = shell; 23057429Smarkm argv[1] = "-c"; 23157429Smarkm argv[2] = command_string; 23257429Smarkm argv[3] = NULL; 23357429Smarkm 23457429Smarkm /* Execute the proxy command. Note that we gave up any 23557429Smarkm extra privileges above. */ 236221420Sdes signal(SIGPIPE, SIG_DFL); 23776262Sgreen execv(argv[0], argv); 23876262Sgreen perror(argv[0]); 23957429Smarkm exit(1); 24057429Smarkm } 24157429Smarkm /* Parent. */ 24257429Smarkm if (pid < 0) 24357429Smarkm fatal("fork failed: %.100s", strerror(errno)); 244106130Sdes else 245106130Sdes proxy_command_pid = pid; /* save pid to clean up later */ 24657429Smarkm 24757429Smarkm /* Close child side of the descriptors. */ 24857429Smarkm close(pin[0]); 24957429Smarkm close(pout[1]); 25057429Smarkm 25157429Smarkm /* Free the command name. */ 252255767Sdes free(command_string); 25357429Smarkm 25457429Smarkm /* Set the connection file descriptors. */ 25557429Smarkm packet_set_connection(pout[0], pin[1]); 25657429Smarkm 25792559Sdes /* Indicate OK return */ 25892559Sdes return 0; 25957429Smarkm} 26057429Smarkm 261221420Sdesvoid 262221420Sdesssh_kill_proxy_command(void) 263221420Sdes{ 264221420Sdes /* 265221420Sdes * Send SIGHUP to proxy command if used. We don't wait() in 266221420Sdes * case it hangs and instead rely on init to reap the child 267221420Sdes */ 268221420Sdes if (proxy_command_pid > 1) 269221420Sdes kill(proxy_command_pid, SIGHUP); 270221420Sdes} 271221420Sdes 27257429Smarkm/* 27357429Smarkm * Creates a (possibly privileged) socket for use as the ssh connection. 27457429Smarkm */ 27592559Sdesstatic int 276124211Sdesssh_create_socket(int privileged, struct addrinfo *ai) 27757429Smarkm{ 278262566Sdes int sock, r, gaierr; 279264377Sdes struct addrinfo hints, *res = NULL; 28057429Smarkm 281124211Sdes sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 282204917Sdes if (sock < 0) { 283262566Sdes error("socket: %s", strerror(errno)); 284204917Sdes return -1; 285204917Sdes } 286204917Sdes fcntl(sock, F_SETFD, FD_CLOEXEC); 28792559Sdes 28892559Sdes /* Bind the socket to an alternative local IP address */ 289262566Sdes if (options.bind_address == NULL && !privileged) 29092559Sdes return sock; 29192559Sdes 292264377Sdes if (options.bind_address) { 293264377Sdes memset(&hints, 0, sizeof(hints)); 294264377Sdes hints.ai_family = ai->ai_family; 295264377Sdes hints.ai_socktype = ai->ai_socktype; 296264377Sdes hints.ai_protocol = ai->ai_protocol; 297264377Sdes hints.ai_flags = AI_PASSIVE; 298264377Sdes gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); 299264377Sdes if (gaierr) { 300264377Sdes error("getaddrinfo: %s: %s", options.bind_address, 301264377Sdes ssh_gai_strerror(gaierr)); 302264377Sdes close(sock); 303264377Sdes return -1; 304264377Sdes } 30592559Sdes } 306262566Sdes /* 307262566Sdes * If we are running as root and want to connect to a privileged 308262566Sdes * port, bind our own socket to a privileged port. 309262566Sdes */ 310262566Sdes if (privileged) { 311262566Sdes PRIV_START; 312264377Sdes r = bindresvport_sa(sock, res ? res->ai_addr : NULL); 313262566Sdes PRIV_END; 314262566Sdes if (r < 0) { 315262566Sdes error("bindresvport_sa: af=%d %s", ai->ai_family, 316262566Sdes strerror(errno)); 317262566Sdes goto fail; 318262566Sdes } 319262566Sdes } else { 320262566Sdes if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 321262566Sdes error("bind: %s: %s", options.bind_address, 322262566Sdes strerror(errno)); 323262566Sdes fail: 324262566Sdes close(sock); 325262566Sdes freeaddrinfo(res); 326262566Sdes return -1; 327262566Sdes } 32892559Sdes } 329264377Sdes if (res != NULL) 330264377Sdes freeaddrinfo(res); 33157429Smarkm return sock; 33257429Smarkm} 33357429Smarkm 334124211Sdesstatic int 335124211Sdestimeout_connect(int sockfd, const struct sockaddr *serv_addr, 336181111Sdes socklen_t addrlen, int *timeoutp) 337124211Sdes{ 338124211Sdes fd_set *fdset; 339181111Sdes struct timeval tv, t_start; 340124211Sdes socklen_t optlen; 341162856Sdes int optval, rc, result = -1; 342124211Sdes 343181111Sdes gettimeofday(&t_start, NULL); 344124211Sdes 345181111Sdes if (*timeoutp <= 0) { 346181111Sdes result = connect(sockfd, serv_addr, addrlen); 347181111Sdes goto done; 348181111Sdes } 349181111Sdes 350126277Sdes set_nonblock(sockfd); 351124211Sdes rc = connect(sockfd, serv_addr, addrlen); 352126277Sdes if (rc == 0) { 353126277Sdes unset_nonblock(sockfd); 354181111Sdes result = 0; 355181111Sdes goto done; 356126277Sdes } 357181111Sdes if (errno != EINPROGRESS) { 358181111Sdes result = -1; 359181111Sdes goto done; 360181111Sdes } 361124211Sdes 362295367Sdes fdset = xcalloc(howmany(sockfd + 1, NFDBITS), 363162856Sdes sizeof(fd_mask)); 364124211Sdes FD_SET(sockfd, fdset); 365181111Sdes ms_to_timeval(&tv, *timeoutp); 366124211Sdes 367147005Sdes for (;;) { 368124211Sdes rc = select(sockfd + 1, NULL, fdset, NULL, &tv); 369124211Sdes if (rc != -1 || errno != EINTR) 370124211Sdes break; 371124211Sdes } 372124211Sdes 373147005Sdes switch (rc) { 374124211Sdes case 0: 375124211Sdes /* Timed out */ 376124211Sdes errno = ETIMEDOUT; 377124211Sdes break; 378124211Sdes case -1: 379124211Sdes /* Select error */ 380126277Sdes debug("select: %s", strerror(errno)); 381124211Sdes break; 382124211Sdes case 1: 383124211Sdes /* Completed or failed */ 384124211Sdes optval = 0; 385124211Sdes optlen = sizeof(optval); 386126277Sdes if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, 387124211Sdes &optlen) == -1) { 388126277Sdes debug("getsockopt: %s", strerror(errno)); 389124211Sdes break; 390124211Sdes } 391124211Sdes if (optval != 0) { 392124211Sdes errno = optval; 393124211Sdes break; 394124211Sdes } 395124211Sdes result = 0; 396126277Sdes unset_nonblock(sockfd); 397124211Sdes break; 398124211Sdes default: 399124211Sdes /* Should not occur */ 400124211Sdes fatal("Bogus return (%d) from select()", rc); 401124211Sdes } 402124211Sdes 403255767Sdes free(fdset); 404181111Sdes 405181111Sdes done: 406181111Sdes if (result == 0 && *timeoutp > 0) { 407181111Sdes ms_subtract_diff(&t_start, timeoutp); 408181111Sdes if (*timeoutp <= 0) { 409181111Sdes errno = ETIMEDOUT; 410181111Sdes result = -1; 411181111Sdes } 412181111Sdes } 413181111Sdes 414124211Sdes return (result); 415124211Sdes} 416124211Sdes 41757429Smarkm/* 41857429Smarkm * Opens a TCP/IP connection to the remote server on the given host. 41957429Smarkm * The address of the remote host will be returned in hostaddr. 42098684Sdes * If port is 0, the default port will be used. If needpriv is true, 42157429Smarkm * a privileged port will be allocated to make the connection. 42298684Sdes * This requires super-user privileges if needpriv is true. 42357429Smarkm * Connection_attempts specifies the maximum number of tries (one per 42457429Smarkm * second). If proxy_command is non-NULL, it specifies the command (with %h 42557429Smarkm * and %p substituted for host and port, respectively) to use to contact 42657429Smarkm * the daemon. 42757429Smarkm */ 428262566Sdesstatic int 429262566Sdesssh_connect_direct(const char *host, struct addrinfo *aitop, 430262566Sdes struct sockaddr_storage *hostaddr, u_short port, int family, 431262566Sdes int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 43257429Smarkm{ 43376262Sgreen int on = 1; 43457429Smarkm int sock = -1, attempt; 43576262Sgreen char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 436262566Sdes struct addrinfo *ai; 43757429Smarkm 438296781Sdes debug2("%s: needpriv %d", __func__, needpriv); 439296781Sdes memset(ntop, 0, sizeof(ntop)); 440296781Sdes memset(strport, 0, sizeof(strport)); 44157429Smarkm 442162856Sdes for (attempt = 0; attempt < connection_attempts; attempt++) { 443164149Sdes if (attempt > 0) { 444164149Sdes /* Sleep a moment before retrying. */ 445164149Sdes sleep(1); 44657429Smarkm debug("Trying again..."); 447164149Sdes } 448162856Sdes /* 449162856Sdes * Loop through addresses for this host, and try each one in 450162856Sdes * sequence until the connection succeeds. 451162856Sdes */ 45257429Smarkm for (ai = aitop; ai; ai = ai->ai_next) { 453262566Sdes if (ai->ai_family != AF_INET && 454262566Sdes ai->ai_family != AF_INET6) 45557429Smarkm continue; 45657429Smarkm if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 45757429Smarkm ntop, sizeof(ntop), strport, sizeof(strport), 45857429Smarkm NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 459296781Sdes error("%s: getnameinfo failed", __func__); 46057429Smarkm continue; 46157429Smarkm } 46257429Smarkm debug("Connecting to %.200s [%.100s] port %s.", 46376226Sgreen host, ntop, strport); 46457429Smarkm 46557429Smarkm /* Create a socket for connecting. */ 466124211Sdes sock = ssh_create_socket(needpriv, ai); 46757429Smarkm if (sock < 0) 46892559Sdes /* Any error is already output */ 46957429Smarkm continue; 47057429Smarkm 471124211Sdes if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 472181111Sdes timeout_ms) >= 0) { 47357429Smarkm /* Successful connection. */ 47476262Sgreen memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 47557429Smarkm break; 47657429Smarkm } else { 477106130Sdes debug("connect to address %s port %s: %s", 478106130Sdes ntop, strport, strerror(errno)); 47957429Smarkm close(sock); 480162856Sdes sock = -1; 48157429Smarkm } 48257429Smarkm } 483162856Sdes if (sock != -1) 48457429Smarkm break; /* Successful connection. */ 48557429Smarkm } 48657429Smarkm 48757429Smarkm /* Return failure if we didn't get a successful connection. */ 488162856Sdes if (sock == -1) { 489147005Sdes error("ssh: connect to host %s port %s: %s", 490106130Sdes host, strport, strerror(errno)); 491147005Sdes return (-1); 492106130Sdes } 49357429Smarkm 49457429Smarkm debug("Connection established."); 49557429Smarkm 496126277Sdes /* Set SO_KEEPALIVE if requested. */ 497181111Sdes if (want_keepalive && 49876262Sgreen setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 49976262Sgreen sizeof(on)) < 0) 50076262Sgreen error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 50176262Sgreen 50257429Smarkm /* Set the connection. */ 50357429Smarkm packet_set_connection(sock, sock); 50457429Smarkm 50592559Sdes return 0; 50657429Smarkm} 50757429Smarkm 508262566Sdesint 509262566Sdesssh_connect(const char *host, struct addrinfo *addrs, 510262566Sdes struct sockaddr_storage *hostaddr, u_short port, int family, 511262566Sdes int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 512262566Sdes{ 513262566Sdes if (options.proxy_command == NULL) { 514262566Sdes return ssh_connect_direct(host, addrs, hostaddr, port, family, 515262566Sdes connection_attempts, timeout_ms, want_keepalive, needpriv); 516262566Sdes } else if (strcmp(options.proxy_command, "-") == 0) { 517262566Sdes packet_set_connection(STDIN_FILENO, STDOUT_FILENO); 518262566Sdes return 0; /* Always succeeds */ 519262566Sdes } else if (options.proxy_use_fdpass) { 520262566Sdes return ssh_proxy_fdpass_connect(host, port, 521262566Sdes options.proxy_command); 522262566Sdes } 523262566Sdes return ssh_proxy_connect(host, port, options.proxy_command); 524262566Sdes} 525262566Sdes 526248619Sdesstatic void 527248619Sdessend_client_banner(int connection_out, int minor1) 528248619Sdes{ 529248619Sdes /* Send our own protocol version identification. */ 530294693Sdes xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s%s", 531248619Sdes compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 532248619Sdes compat20 ? PROTOCOL_MINOR_2 : minor1, 533294693Sdes SSH_VERSION, 534248619Sdes *options.version_addendum == '\0' ? "" : " ", 535248619Sdes options.version_addendum, compat20 ? "\r\n" : "\n"); 536296781Sdes if (atomicio(vwrite, connection_out, client_version_string, 537248619Sdes strlen(client_version_string)) != strlen(client_version_string)) 538248619Sdes fatal("write: %.100s", strerror(errno)); 539248619Sdes chop(client_version_string); 540248619Sdes debug("Local version string %.100s", client_version_string); 541248619Sdes} 542248619Sdes 54357429Smarkm/* 54460576Skris * Waits for the server identification string, and sends our own 54560576Skris * identification string. 54657429Smarkm */ 547197679Sdesvoid 548181111Sdesssh_exchange_identification(int timeout_ms) 54957429Smarkm{ 55060576Skris char buf[256], remote_version[256]; /* must be same size! */ 551149753Sdes int remote_major, remote_minor, mismatch; 55260576Skris int connection_in = packet_get_connection_in(); 55360576Skris int connection_out = packet_get_connection_out(); 554248619Sdes int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0; 555162856Sdes u_int i, n; 556181111Sdes size_t len; 557181111Sdes int fdsetsz, remaining, rc; 558181111Sdes struct timeval t_start, t_remaining; 559181111Sdes fd_set *fdset; 56057429Smarkm 561181111Sdes fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); 562181111Sdes fdset = xcalloc(1, fdsetsz); 563181111Sdes 564248619Sdes /* 565248619Sdes * If we are SSH2-only then we can send the banner immediately and 566248619Sdes * save a round-trip. 567248619Sdes */ 568248619Sdes if (options.protocol == SSH_PROTO_2) { 569248619Sdes enable_compat20(); 570248619Sdes send_client_banner(connection_out, 0); 571248619Sdes client_banner_sent = 1; 572248619Sdes } 573248619Sdes 574149753Sdes /* Read other side's version identification. */ 575181111Sdes remaining = timeout_ms; 576162856Sdes for (n = 0;;) { 57765674Skris for (i = 0; i < sizeof(buf) - 1; i++) { 578181111Sdes if (timeout_ms > 0) { 579181111Sdes gettimeofday(&t_start, NULL); 580181111Sdes ms_to_timeval(&t_remaining, remaining); 581181111Sdes FD_SET(connection_in, fdset); 582181111Sdes rc = select(connection_in + 1, fdset, NULL, 583181111Sdes fdset, &t_remaining); 584181111Sdes ms_subtract_diff(&t_start, &remaining); 585181111Sdes if (rc == 0 || remaining <= 0) 586181111Sdes fatal("Connection timed out during " 587181111Sdes "banner exchange"); 588181111Sdes if (rc == -1) { 589181111Sdes if (errno == EINTR) 590181111Sdes continue; 591181111Sdes fatal("ssh_exchange_identification: " 592181111Sdes "select: %s", strerror(errno)); 593181111Sdes } 594181111Sdes } 595149753Sdes 596296781Sdes len = atomicio(read, connection_in, &buf[i], 1); 597181111Sdes 598149753Sdes if (len != 1 && errno == EPIPE) 599181111Sdes fatal("ssh_exchange_identification: " 600181111Sdes "Connection closed by remote host"); 601149753Sdes else if (len != 1) 602181111Sdes fatal("ssh_exchange_identification: " 603181111Sdes "read: %.100s", strerror(errno)); 60465674Skris if (buf[i] == '\r') { 60565674Skris buf[i] = '\n'; 60665674Skris buf[i + 1] = 0; 60765674Skris continue; /**XXX wait for \n */ 60865674Skris } 60965674Skris if (buf[i] == '\n') { 61065674Skris buf[i + 1] = 0; 61165674Skris break; 61265674Skris } 613162856Sdes if (++n > 65536) 614181111Sdes fatal("ssh_exchange_identification: " 615181111Sdes "No banner received"); 61660576Skris } 61765674Skris buf[sizeof(buf) - 1] = 0; 61865674Skris if (strncmp(buf, "SSH-", 4) == 0) 61960576Skris break; 62065674Skris debug("ssh_exchange_identification: %s", buf); 62160576Skris } 62260576Skris server_version_string = xstrdup(buf); 623255767Sdes free(fdset); 62457429Smarkm 62560576Skris /* 62660576Skris * Check that the versions match. In future this might accept 62760576Skris * several versions and set appropriate flags to handle them. 62860576Skris */ 62960576Skris if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 63060576Skris &remote_major, &remote_minor, remote_version) != 3) 63160576Skris fatal("Bad remote protocol version identification: '%.100s'", buf); 63260576Skris debug("Remote protocol version %d.%d, remote software version %.100s", 63392559Sdes remote_major, remote_minor, remote_version); 63457429Smarkm 635295367Sdes active_state->compat = compat_datafellows(remote_version); 63660576Skris mismatch = 0; 63757429Smarkm 63892559Sdes switch (remote_major) { 63960576Skris case 1: 64060576Skris if (remote_minor == 99 && 64160576Skris (options.protocol & SSH_PROTO_2) && 64260576Skris !(options.protocol & SSH_PROTO_1_PREFERRED)) { 64360576Skris enable_compat20(); 64460576Skris break; 64560576Skris } 64660576Skris if (!(options.protocol & SSH_PROTO_1)) { 64760576Skris mismatch = 1; 64860576Skris break; 64960576Skris } 65060576Skris if (remote_minor < 3) { 65160576Skris fatal("Remote machine has too old SSH software version."); 65276262Sgreen } else if (remote_minor == 3 || remote_minor == 4) { 65360576Skris /* We speak 1.3, too. */ 65460576Skris enable_compat13(); 65576262Sgreen minor1 = 3; 65660576Skris if (options.forward_agent) { 657124211Sdes logit("Agent forwarding disabled for protocol 1.3"); 65860576Skris options.forward_agent = 0; 65960576Skris } 66060576Skris } 66160576Skris break; 66260576Skris case 2: 66360576Skris if (options.protocol & SSH_PROTO_2) { 66460576Skris enable_compat20(); 66560576Skris break; 66660576Skris } 66760576Skris /* FALLTHROUGH */ 66860576Skris default: 66960576Skris mismatch = 1; 67060576Skris break; 67160576Skris } 67260576Skris if (mismatch) 67360576Skris fatal("Protocol major versions differ: %d vs. %d", 67460576Skris (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 67560576Skris remote_major); 676262566Sdes if ((datafellows & SSH_BUG_DERIVEKEY) != 0) 677262566Sdes fatal("Server version \"%.100s\" uses unsafe key agreement; " 678262566Sdes "refusing connection", remote_version); 679262566Sdes if ((datafellows & SSH_BUG_RSASIGMD5) != 0) 680262566Sdes logit("Server version \"%.100s\" uses unsafe RSA signature " 681262566Sdes "scheme; disabling use of RSA keys", remote_version); 682248619Sdes if (!client_banner_sent) 683248619Sdes send_client_banner(connection_out, minor1); 68460576Skris chop(server_version_string); 68557429Smarkm} 68657429Smarkm 68776262Sgreen/* defaults to 'no' */ 68892559Sdesstatic int 68992559Sdesconfirm(const char *prompt) 69057429Smarkm{ 69192559Sdes const char *msg, *again = "Please type 'yes' or 'no': "; 69292559Sdes char *p; 69392559Sdes int ret = -1; 69457429Smarkm 69576262Sgreen if (options.batch_mode) 69676262Sgreen return 0; 69792559Sdes for (msg = prompt;;msg = again) { 69892559Sdes p = read_passphrase(msg, RP_ECHO); 69992559Sdes if (p == NULL || 70092559Sdes (p[0] == '\0') || (p[0] == '\n') || 70192559Sdes strncasecmp(p, "no", 2) == 0) 70292559Sdes ret = 0; 703106130Sdes if (p && strncasecmp(p, "yes", 3) == 0) 70492559Sdes ret = 1; 705255767Sdes free(p); 70692559Sdes if (ret != -1) 70792559Sdes return ret; 70857429Smarkm } 70957429Smarkm} 71057429Smarkm 711204917Sdesstatic int 712204917Sdescheck_host_cert(const char *host, const Key *host_key) 713204917Sdes{ 714204917Sdes const char *reason; 715204917Sdes 716204917Sdes if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { 717204917Sdes error("%s", reason); 718204917Sdes return 0; 719204917Sdes } 720295367Sdes if (buffer_len(host_key->cert->critical) != 0) { 721215116Sdes error("Certificate for %s contains unsupported " 722215116Sdes "critical options(s)", host); 723204917Sdes return 0; 724204917Sdes } 725204917Sdes return 1; 726204917Sdes} 727204917Sdes 728221420Sdesstatic int 729221420Sdessockaddr_is_local(struct sockaddr *hostaddr) 730221420Sdes{ 731221420Sdes switch (hostaddr->sa_family) { 732221420Sdes case AF_INET: 733221420Sdes return (ntohl(((struct sockaddr_in *)hostaddr)-> 734221420Sdes sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 735221420Sdes case AF_INET6: 736221420Sdes return IN6_IS_ADDR_LOOPBACK( 737221420Sdes &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 738221420Sdes default: 739221420Sdes return 0; 740221420Sdes } 741221420Sdes} 742221420Sdes 74357429Smarkm/* 744221420Sdes * Prepare the hostname and ip address strings that are used to lookup 745221420Sdes * host keys in known_hosts files. These may have a port number appended. 746221420Sdes */ 747221420Sdesvoid 748221420Sdesget_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, 749221420Sdes u_short port, char **hostfile_hostname, char **hostfile_ipaddr) 750221420Sdes{ 751221420Sdes char ntop[NI_MAXHOST]; 752221420Sdes socklen_t addrlen; 753221420Sdes 754221420Sdes switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { 755221420Sdes case -1: 756221420Sdes addrlen = 0; 757221420Sdes break; 758221420Sdes case AF_INET: 759221420Sdes addrlen = sizeof(struct sockaddr_in); 760221420Sdes break; 761221420Sdes case AF_INET6: 762221420Sdes addrlen = sizeof(struct sockaddr_in6); 763221420Sdes break; 764221420Sdes default: 765221420Sdes addrlen = sizeof(struct sockaddr); 766221420Sdes break; 767221420Sdes } 768221420Sdes 769221420Sdes /* 770221420Sdes * We don't have the remote ip-address for connections 771221420Sdes * using a proxy command 772221420Sdes */ 773221420Sdes if (hostfile_ipaddr != NULL) { 774221420Sdes if (options.proxy_command == NULL) { 775221420Sdes if (getnameinfo(hostaddr, addrlen, 776221420Sdes ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) 777295367Sdes fatal("%s: getnameinfo failed", __func__); 778221420Sdes *hostfile_ipaddr = put_host_port(ntop, port); 779221420Sdes } else { 780221420Sdes *hostfile_ipaddr = xstrdup("<no hostip for proxy " 781221420Sdes "command>"); 782221420Sdes } 783221420Sdes } 784221420Sdes 785221420Sdes /* 786221420Sdes * Allow the user to record the key under a different name or 787221420Sdes * differentiate a non-standard port. This is useful for ssh 788221420Sdes * tunneling over forwarded connections or if you run multiple 789221420Sdes * sshd's on different ports on the same machine. 790221420Sdes */ 791221420Sdes if (hostfile_hostname != NULL) { 792221420Sdes if (options.host_key_alias != NULL) { 793221420Sdes *hostfile_hostname = xstrdup(options.host_key_alias); 794221420Sdes debug("using hostkeyalias: %s", *hostfile_hostname); 795221420Sdes } else { 796221420Sdes *hostfile_hostname = put_host_port(hostname, port); 797221420Sdes } 798221420Sdes } 799221420Sdes} 800221420Sdes 801221420Sdes/* 80292559Sdes * check whether the supplied host key is valid, return -1 if the key 803226046Sdes * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. 80457429Smarkm */ 805162856Sdes#define RDRW 0 806162856Sdes#define RDONLY 1 807162856Sdes#define ROQUIET 2 80892559Sdesstatic int 809162856Sdescheck_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 810226046Sdes Key *host_key, int readonly, 811226046Sdes char **user_hostfiles, u_int num_user_hostfiles, 812226046Sdes char **system_hostfiles, u_int num_system_hostfiles) 81357429Smarkm{ 814226046Sdes HostStatus host_status; 815226046Sdes HostStatus ip_status; 816221420Sdes Key *raw_key = NULL; 817162856Sdes char *ip = NULL, *host = NULL; 818181111Sdes char hostline[1000], *hostp, *fp, *ra; 81992559Sdes char msg[1024]; 820226046Sdes const char *type; 821226046Sdes const struct hostkey_entry *host_found, *ip_found; 822221420Sdes int len, cancelled_forwarding = 0; 823226046Sdes int local = sockaddr_is_local(hostaddr); 824226046Sdes int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; 825295367Sdes int hostkey_trusted = 0; /* Known or explicitly accepted by user */ 826221420Sdes struct hostkeys *host_hostkeys, *ip_hostkeys; 827226046Sdes u_int i; 82857429Smarkm 82960576Skris /* 83060576Skris * Force accepting of the host key for loopback/localhost. The 83160576Skris * problem is that if the home directory is NFS-mounted to multiple 83260576Skris * machines, localhost will refer to a different machine in each of 83360576Skris * them, and the user will get bogus HOST_CHANGED warnings. This 83460576Skris * essentially disables host authentication for localhost; however, 83560576Skris * this is probably not a real problem. 83660576Skris */ 83792559Sdes if (options.no_host_authentication_for_localhost == 1 && local && 83892559Sdes options.host_key_alias == NULL) { 83976262Sgreen debug("Forcing accepting of host key for " 84076262Sgreen "loopback/localhost."); 84192559Sdes return 0; 84260576Skris } 84357429Smarkm 84460576Skris /* 845221420Sdes * Prepare the hostname and address strings used for hostkey lookup. 846221420Sdes * In some cases, these will have a port number appended. 84760576Skris */ 848221420Sdes get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); 849181111Sdes 85076262Sgreen /* 85176262Sgreen * Turn off check_host_ip if the connection is to localhost, via proxy 85276262Sgreen * command or if we don't have a hostname to compare with 85376262Sgreen */ 854162856Sdes if (options.check_host_ip && (local || 855162856Sdes strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 85676262Sgreen options.check_host_ip = 0; 85757429Smarkm 858221420Sdes host_hostkeys = init_hostkeys(); 859226046Sdes for (i = 0; i < num_user_hostfiles; i++) 860226046Sdes load_hostkeys(host_hostkeys, host, user_hostfiles[i]); 861226046Sdes for (i = 0; i < num_system_hostfiles; i++) 862226046Sdes load_hostkeys(host_hostkeys, host, system_hostfiles[i]); 863221420Sdes 864221420Sdes ip_hostkeys = NULL; 865221420Sdes if (!want_cert && options.check_host_ip) { 866221420Sdes ip_hostkeys = init_hostkeys(); 867226046Sdes for (i = 0; i < num_user_hostfiles; i++) 868226046Sdes load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); 869226046Sdes for (i = 0; i < num_system_hostfiles; i++) 870226046Sdes load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); 87176262Sgreen } 87276262Sgreen 873204917Sdes retry: 874221420Sdes /* Reload these as they may have changed on cert->key downgrade */ 875204917Sdes want_cert = key_is_cert(host_key); 876204917Sdes type = key_type(host_key); 877204917Sdes 87876262Sgreen /* 879157019Sdes * Check if the host key is present in the user's list of known 88060576Skris * hosts or in the systemwide list. 88160576Skris */ 882221420Sdes host_status = check_key_in_hostkeys(host_hostkeys, host_key, 883221420Sdes &host_found); 884221420Sdes 88560576Skris /* 88660576Skris * Also perform check for the ip address, skip the check if we are 887204917Sdes * localhost, looking for a certificate, or the hostname was an ip 888204917Sdes * address to begin with. 88960576Skris */ 890221420Sdes if (!want_cert && ip_hostkeys != NULL) { 891221420Sdes ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, 892221420Sdes &ip_found); 89360576Skris if (host_status == HOST_CHANGED && 894221420Sdes (ip_status != HOST_CHANGED || 895221420Sdes (ip_found != NULL && 896221420Sdes !key_equal(ip_found->key, host_found->key)))) 89760576Skris host_ip_differ = 1; 89860576Skris } else 89960576Skris ip_status = host_status; 90057429Smarkm 90160576Skris switch (host_status) { 90260576Skris case HOST_OK: 90360576Skris /* The host is known and the key matches. */ 904204917Sdes debug("Host '%.200s' is known and matches the %s host %s.", 905204917Sdes host, type, want_cert ? "certificate" : "key"); 906221420Sdes debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", 907221420Sdes host_found->file, host_found->line); 908204917Sdes if (want_cert && !check_host_cert(hostname, host_key)) 909204917Sdes goto fail; 91076262Sgreen if (options.check_host_ip && ip_status == HOST_NEW) { 911204917Sdes if (readonly || want_cert) 912124211Sdes logit("%s host key for IP address " 91392559Sdes "'%.128s' not in list of known hosts.", 91492559Sdes type, ip); 915226046Sdes else if (!add_host_to_hostfile(user_hostfiles[0], ip, 916147005Sdes host_key, options.hash_known_hosts)) 917124211Sdes logit("Failed to add the %s host key for IP " 91892559Sdes "address '%.128s' to the list of known " 919295367Sdes "hosts (%.500s).", type, ip, 920226046Sdes user_hostfiles[0]); 92176262Sgreen else 922124211Sdes logit("Warning: Permanently added the %s host " 92392559Sdes "key for IP address '%.128s' to the list " 92492559Sdes "of known hosts.", type, ip); 925181111Sdes } else if (options.visual_host_key) { 926295367Sdes fp = sshkey_fingerprint(host_key, 927295367Sdes options.fingerprint_hash, SSH_FP_DEFAULT); 928295367Sdes ra = sshkey_fingerprint(host_key, 929295367Sdes options.fingerprint_hash, SSH_FP_RANDOMART); 930295367Sdes if (fp == NULL || ra == NULL) 931295367Sdes fatal("%s: sshkey_fingerprint fail", __func__); 932296781Sdes logit("Host key fingerprint is %s\n%s", fp, ra); 933255767Sdes free(ra); 934255767Sdes free(fp); 93560576Skris } 936295367Sdes hostkey_trusted = 1; 93760576Skris break; 93860576Skris case HOST_NEW: 939162856Sdes if (options.host_key_alias == NULL && port != 0 && 940162856Sdes port != SSH_DEFAULT_PORT) { 941162856Sdes debug("checking without port identifier"); 942192595Sdes if (check_host_key(hostname, hostaddr, 0, host_key, 943226046Sdes ROQUIET, user_hostfiles, num_user_hostfiles, 944226046Sdes system_hostfiles, num_system_hostfiles) == 0) { 945162856Sdes debug("found matching key w/out port"); 946162856Sdes break; 947162856Sdes } 948162856Sdes } 949204917Sdes if (readonly || want_cert) 95092559Sdes goto fail; 95160576Skris /* The host is new. */ 95260576Skris if (options.strict_host_key_checking == 1) { 95392559Sdes /* 95492559Sdes * User has requested strict host key checking. We 95592559Sdes * will not add the host key automatically. The only 95692559Sdes * alternative left is to abort. 95792559Sdes */ 95892559Sdes error("No %s host key is known for %.200s and you " 95992559Sdes "have requested strict checking.", type, host); 96092559Sdes goto fail; 96160576Skris } else if (options.strict_host_key_checking == 2) { 962124211Sdes char msg1[1024], msg2[1024]; 963124211Sdes 964221420Sdes if (show_other_keys(host_hostkeys, host_key)) 965124211Sdes snprintf(msg1, sizeof(msg1), 966149753Sdes "\nbut keys of different type are already" 967149753Sdes " known for this host."); 968124211Sdes else 969124211Sdes snprintf(msg1, sizeof(msg1), "."); 97060576Skris /* The default */ 971295367Sdes fp = sshkey_fingerprint(host_key, 972295367Sdes options.fingerprint_hash, SSH_FP_DEFAULT); 973295367Sdes ra = sshkey_fingerprint(host_key, 974295367Sdes options.fingerprint_hash, SSH_FP_RANDOMART); 975295367Sdes if (fp == NULL || ra == NULL) 976295367Sdes fatal("%s: sshkey_fingerprint fail", __func__); 977124211Sdes msg2[0] = '\0'; 978124211Sdes if (options.verify_host_key_dns) { 979126277Sdes if (matching_host_key_dns) 980124211Sdes snprintf(msg2, sizeof(msg2), 981124211Sdes "Matching host key fingerprint" 982124211Sdes " found in DNS.\n"); 983124211Sdes else 984124211Sdes snprintf(msg2, sizeof(msg2), 985124211Sdes "No matching host key fingerprint" 986124211Sdes " found in DNS.\n"); 987124211Sdes } 98892559Sdes snprintf(msg, sizeof(msg), 98992559Sdes "The authenticity of host '%.200s (%s)' can't be " 990106130Sdes "established%s\n" 991181111Sdes "%s key fingerprint is %s.%s%s\n%s" 99292559Sdes "Are you sure you want to continue connecting " 993106130Sdes "(yes/no)? ", 994181111Sdes host, ip, msg1, type, fp, 995181111Sdes options.visual_host_key ? "\n" : "", 996181111Sdes options.visual_host_key ? ra : "", 997181111Sdes msg2); 998255767Sdes free(ra); 999255767Sdes free(fp); 100092559Sdes if (!confirm(msg)) 100192559Sdes goto fail; 1002295367Sdes hostkey_trusted = 1; /* user explicitly confirmed */ 100360576Skris } 1004147005Sdes /* 1005147005Sdes * If not in strict mode, add the key automatically to the 1006147005Sdes * local known_hosts file. 1007147005Sdes */ 100876262Sgreen if (options.check_host_ip && ip_status == HOST_NEW) { 1009221420Sdes snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 101060576Skris hostp = hostline; 1011147005Sdes if (options.hash_known_hosts) { 1012147005Sdes /* Add hash of host and IP separately */ 1013226046Sdes r = add_host_to_hostfile(user_hostfiles[0], 1014226046Sdes host, host_key, options.hash_known_hosts) && 1015226046Sdes add_host_to_hostfile(user_hostfiles[0], ip, 1016147005Sdes host_key, options.hash_known_hosts); 1017147005Sdes } else { 1018147005Sdes /* Add unhashed "host,ip" */ 1019226046Sdes r = add_host_to_hostfile(user_hostfiles[0], 1020147005Sdes hostline, host_key, 1021147005Sdes options.hash_known_hosts); 1022147005Sdes } 1023147005Sdes } else { 1024226046Sdes r = add_host_to_hostfile(user_hostfiles[0], host, 1025226046Sdes host_key, options.hash_known_hosts); 102660576Skris hostp = host; 1027147005Sdes } 102857429Smarkm 1029147005Sdes if (!r) 1030124211Sdes logit("Failed to add the host to the list of known " 1031226046Sdes "hosts (%.500s).", user_hostfiles[0]); 103260576Skris else 1033124211Sdes logit("Warning: Permanently added '%.200s' (%s) to the " 103492559Sdes "list of known hosts.", hostp, type); 103557429Smarkm break; 1036204917Sdes case HOST_REVOKED: 1037204917Sdes error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1038204917Sdes error("@ WARNING: REVOKED HOST KEY DETECTED! @"); 1039204917Sdes error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1040204917Sdes error("The %s host key for %s is marked as revoked.", type, host); 1041204917Sdes error("This could mean that a stolen key is being used to"); 1042204917Sdes error("impersonate this host."); 1043204917Sdes 1044204917Sdes /* 1045204917Sdes * If strict host key checking is in use, the user will have 1046204917Sdes * to edit the key manually and we can only abort. 1047204917Sdes */ 1048204917Sdes if (options.strict_host_key_checking) { 1049204917Sdes error("%s host key for %.200s was revoked and you have " 1050204917Sdes "requested strict checking.", type, host); 1051204917Sdes goto fail; 1052204917Sdes } 1053204917Sdes goto continue_unsafe; 1054204917Sdes 105560576Skris case HOST_CHANGED: 1056204917Sdes if (want_cert) { 1057204917Sdes /* 1058204917Sdes * This is only a debug() since it is valid to have 1059204917Sdes * CAs with wildcard DNS matches that don't match 1060204917Sdes * all hosts that one might visit. 1061204917Sdes */ 1062204917Sdes debug("Host certificate authority does not " 1063221420Sdes "match %s in %s:%lu", CA_MARKER, 1064221420Sdes host_found->file, host_found->line); 1065204917Sdes goto fail; 1066204917Sdes } 1067162856Sdes if (readonly == ROQUIET) 1068162856Sdes goto fail; 106960576Skris if (options.check_host_ip && host_ip_differ) { 1070137019Sdes char *key_msg; 107160576Skris if (ip_status == HOST_NEW) 1072137019Sdes key_msg = "is unknown"; 107360576Skris else if (ip_status == HOST_OK) 1074137019Sdes key_msg = "is unchanged"; 107560576Skris else 1076137019Sdes key_msg = "has a different value"; 107760576Skris error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 107860576Skris error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 107960576Skris error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 108060576Skris error("The %s host key for %s has changed,", type, host); 1081181111Sdes error("and the key for the corresponding IP address %s", ip); 1082137019Sdes error("%s. This could either mean that", key_msg); 108360576Skris error("DNS SPOOFING is happening or the IP address for the host"); 108476262Sgreen error("and its host key have changed at the same time."); 108576262Sgreen if (ip_status != HOST_NEW) 1086221420Sdes error("Offending key for IP in %s:%lu", 1087221420Sdes ip_found->file, ip_found->line); 108860576Skris } 108960576Skris /* The host key has changed. */ 1090126277Sdes warn_changed_key(host_key); 109160576Skris error("Add correct host key in %.100s to get rid of this message.", 1092226046Sdes user_hostfiles[0]); 1093221420Sdes error("Offending %s key in %s:%lu", key_type(host_found->key), 1094221420Sdes host_found->file, host_found->line); 109557429Smarkm 109660576Skris /* 109760576Skris * If strict host key checking is in use, the user will have 109860576Skris * to edit the key manually and we can only abort. 109960576Skris */ 110092559Sdes if (options.strict_host_key_checking) { 110192559Sdes error("%s host key for %.200s has changed and you have " 110292559Sdes "requested strict checking.", type, host); 110392559Sdes goto fail; 110492559Sdes } 110557429Smarkm 1106204917Sdes continue_unsafe: 110757429Smarkm /* 110860576Skris * If strict host key checking has not been requested, allow 1109124211Sdes * the connection but without MITM-able authentication or 1110162856Sdes * forwarding. 111157429Smarkm */ 111260576Skris if (options.password_authentication) { 111392559Sdes error("Password authentication is disabled to avoid " 111492559Sdes "man-in-the-middle attacks."); 111560576Skris options.password_authentication = 0; 1116181111Sdes cancelled_forwarding = 1; 111757429Smarkm } 1118124211Sdes if (options.kbd_interactive_authentication) { 1119124211Sdes error("Keyboard-interactive authentication is disabled" 1120124211Sdes " to avoid man-in-the-middle attacks."); 1121124211Sdes options.kbd_interactive_authentication = 0; 1122124211Sdes options.challenge_response_authentication = 0; 1123181111Sdes cancelled_forwarding = 1; 1124124211Sdes } 1125124211Sdes if (options.challenge_response_authentication) { 1126124211Sdes error("Challenge/response authentication is disabled" 1127124211Sdes " to avoid man-in-the-middle attacks."); 1128124211Sdes options.challenge_response_authentication = 0; 1129181111Sdes cancelled_forwarding = 1; 1130124211Sdes } 113160576Skris if (options.forward_agent) { 113292559Sdes error("Agent forwarding is disabled to avoid " 113392559Sdes "man-in-the-middle attacks."); 113460576Skris options.forward_agent = 0; 1135181111Sdes cancelled_forwarding = 1; 113660576Skris } 113776262Sgreen if (options.forward_x11) { 113892559Sdes error("X11 forwarding is disabled to avoid " 113992559Sdes "man-in-the-middle attacks."); 114076262Sgreen options.forward_x11 = 0; 1141181111Sdes cancelled_forwarding = 1; 114276262Sgreen } 114392559Sdes if (options.num_local_forwards > 0 || 114492559Sdes options.num_remote_forwards > 0) { 114592559Sdes error("Port forwarding is disabled to avoid " 114692559Sdes "man-in-the-middle attacks."); 114792559Sdes options.num_local_forwards = 114892559Sdes options.num_remote_forwards = 0; 1149181111Sdes cancelled_forwarding = 1; 115076262Sgreen } 1151162856Sdes if (options.tun_open != SSH_TUNMODE_NO) { 1152162856Sdes error("Tunnel forwarding is disabled to avoid " 1153162856Sdes "man-in-the-middle attacks."); 1154162856Sdes options.tun_open = SSH_TUNMODE_NO; 1155181111Sdes cancelled_forwarding = 1; 1156162856Sdes } 1157181111Sdes if (options.exit_on_forward_failure && cancelled_forwarding) 1158181111Sdes fatal("Error: forwarding disabled due to host key " 1159181111Sdes "check failure"); 1160181111Sdes 116160576Skris /* 116260576Skris * XXX Should permit the user to change to use the new id. 116360576Skris * This could be done by converting the host key to an 116460576Skris * identifying sentence, tell that the host identifies itself 1165204917Sdes * by that sentence, and ask the user if he/she wishes to 116660576Skris * accept the authentication. 116760576Skris */ 116857429Smarkm break; 1169106130Sdes case HOST_FOUND: 1170106130Sdes fatal("internal error"); 1171106130Sdes break; 117257429Smarkm } 117376262Sgreen 117476262Sgreen if (options.check_host_ip && host_status != HOST_CHANGED && 117576262Sgreen ip_status == HOST_CHANGED) { 117692559Sdes snprintf(msg, sizeof(msg), 117792559Sdes "Warning: the %s host key for '%.200s' " 117892559Sdes "differs from the key for the IP address '%.128s'" 1179221420Sdes "\nOffending key for IP in %s:%lu", 1180221420Sdes type, host, ip, ip_found->file, ip_found->line); 118192559Sdes if (host_status == HOST_OK) { 118292559Sdes len = strlen(msg); 118392559Sdes snprintf(msg + len, sizeof(msg) - len, 1184221420Sdes "\nMatching host key in %s:%lu", 1185221420Sdes host_found->file, host_found->line); 118692559Sdes } 118776262Sgreen if (options.strict_host_key_checking == 1) { 1188124211Sdes logit("%s", msg); 118992559Sdes error("Exiting, you have requested strict checking."); 119092559Sdes goto fail; 119176262Sgreen } else if (options.strict_host_key_checking == 2) { 119292559Sdes strlcat(msg, "\nAre you sure you want " 119392559Sdes "to continue connecting (yes/no)? ", sizeof(msg)); 119492559Sdes if (!confirm(msg)) 119592559Sdes goto fail; 119692559Sdes } else { 1197124211Sdes logit("%s", msg); 119876262Sgreen } 119976262Sgreen } 120076262Sgreen 1201295367Sdes if (!hostkey_trusted && options.update_hostkeys) { 1202295367Sdes debug("%s: hostkey not known or explicitly trusted: " 1203295367Sdes "disabling UpdateHostkeys", __func__); 1204295367Sdes options.update_hostkeys = 0; 1205295367Sdes } 1206295367Sdes 1207255767Sdes free(ip); 1208255767Sdes free(host); 1209221420Sdes if (host_hostkeys != NULL) 1210221420Sdes free_hostkeys(host_hostkeys); 1211221420Sdes if (ip_hostkeys != NULL) 1212221420Sdes free_hostkeys(ip_hostkeys); 121392559Sdes return 0; 121492559Sdes 121592559Sdesfail: 1216204917Sdes if (want_cert && host_status != HOST_REVOKED) { 1217204917Sdes /* 1218204917Sdes * No matching certificate. Downgrade cert to raw key and 1219204917Sdes * search normally. 1220204917Sdes */ 1221204917Sdes debug("No matching CA found. Retry with plain key"); 1222204917Sdes raw_key = key_from_private(host_key); 1223204917Sdes if (key_drop_cert(raw_key) != 0) 1224204917Sdes fatal("Couldn't drop certificate"); 1225204917Sdes host_key = raw_key; 1226204917Sdes goto retry; 1227204917Sdes } 1228204917Sdes if (raw_key != NULL) 1229204917Sdes key_free(raw_key); 1230255767Sdes free(ip); 1231255767Sdes free(host); 1232221420Sdes if (host_hostkeys != NULL) 1233221420Sdes free_hostkeys(host_hostkeys); 1234221420Sdes if (ip_hostkeys != NULL) 1235221420Sdes free_hostkeys(ip_hostkeys); 123692559Sdes return -1; 123757429Smarkm} 123857429Smarkm 1239124211Sdes/* returns 0 if key verifies or -1 if key does NOT verify */ 124057565Smarkmint 124192559Sdesverify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 124257565Smarkm{ 1243296781Sdes u_int i; 1244295367Sdes int r = -1, flags = 0; 1245296781Sdes char valid[64], *fp = NULL, *cafp = NULL; 1246295367Sdes struct sshkey *plain = NULL; 124757565Smarkm 1248295367Sdes if ((fp = sshkey_fingerprint(host_key, 1249295367Sdes options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1250295367Sdes error("%s: fingerprint host key: %s", __func__, ssh_err(r)); 1251295367Sdes r = -1; 1252295367Sdes goto out; 1253295367Sdes } 1254221420Sdes 1255296781Sdes if (sshkey_is_cert(host_key)) { 1256296781Sdes if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, 1257296781Sdes options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1258296781Sdes error("%s: fingerprint CA key: %s", 1259296781Sdes __func__, ssh_err(r)); 1260296781Sdes r = -1; 1261296781Sdes goto out; 1262296781Sdes } 1263296781Sdes sshkey_format_cert_validity(host_key->cert, 1264296781Sdes valid, sizeof(valid)); 1265296781Sdes debug("Server host certificate: %s %s, serial %llu " 1266296781Sdes "ID \"%s\" CA %s %s valid %s", 1267296781Sdes sshkey_ssh_name(host_key), fp, 1268296781Sdes (unsigned long long)host_key->cert->serial, 1269296781Sdes host_key->cert->key_id, 1270296781Sdes sshkey_ssh_name(host_key->cert->signature_key), cafp, 1271296781Sdes valid); 1272296781Sdes for (i = 0; i < host_key->cert->nprincipals; i++) { 1273296781Sdes debug2("Server host certificate hostname: %s", 1274296781Sdes host_key->cert->principals[i]); 1275296781Sdes } 1276296781Sdes } else { 1277296781Sdes debug("Server host key: %s %s", compat20 ? 1278296781Sdes sshkey_ssh_name(host_key) : sshkey_type(host_key), fp); 1279296781Sdes } 1280295367Sdes 1281295367Sdes if (sshkey_equal(previous_host_key, host_key)) { 1282295367Sdes debug2("%s: server host key %s %s matches cached key", 1283295367Sdes __func__, sshkey_type(host_key), fp); 1284295367Sdes r = 0; 1285295367Sdes goto out; 1286295367Sdes } 1287295367Sdes 1288295367Sdes /* Check in RevokedHostKeys file if specified */ 1289295367Sdes if (options.revoked_host_keys != NULL) { 1290295367Sdes r = sshkey_check_revoked(host_key, options.revoked_host_keys); 1291295367Sdes switch (r) { 1292295367Sdes case 0: 1293295367Sdes break; /* not revoked */ 1294295367Sdes case SSH_ERR_KEY_REVOKED: 1295295367Sdes error("Host key %s %s revoked by file %s", 1296295367Sdes sshkey_type(host_key), fp, 1297295367Sdes options.revoked_host_keys); 1298295367Sdes r = -1; 1299295367Sdes goto out; 1300295367Sdes default: 1301295367Sdes error("Error checking host key %s %s in " 1302295367Sdes "revoked keys file %s: %s", sshkey_type(host_key), 1303295367Sdes fp, options.revoked_host_keys, ssh_err(r)); 1304295367Sdes r = -1; 1305295367Sdes goto out; 1306295367Sdes } 1307295367Sdes } 1308295367Sdes 1309285976Sdelphij if (options.verify_host_key_dns) { 1310285976Sdelphij /* 1311285976Sdelphij * XXX certs are not yet supported for DNS, so downgrade 1312285976Sdelphij * them and try the plain key. 1313285976Sdelphij */ 1314295367Sdes if ((r = sshkey_from_private(host_key, &plain)) != 0) 1315295367Sdes goto out; 1316295367Sdes if (sshkey_is_cert(plain)) 1317295367Sdes sshkey_drop_cert(plain); 1318285976Sdelphij if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { 1319285976Sdelphij if (flags & DNS_VERIFY_FOUND) { 1320285976Sdelphij if (options.verify_host_key_dns == 1 && 1321285976Sdelphij flags & DNS_VERIFY_MATCH && 1322285976Sdelphij flags & DNS_VERIFY_SECURE) { 1323295367Sdes r = 0; 1324295367Sdes goto out; 1325285976Sdelphij } 1326285976Sdelphij if (flags & DNS_VERIFY_MATCH) { 1327285976Sdelphij matching_host_key_dns = 1; 1328285976Sdelphij } else { 1329285976Sdelphij warn_changed_key(plain); 1330285976Sdelphij error("Update the SSHFP RR in DNS " 1331285976Sdelphij "with the new host key to get rid " 1332285976Sdelphij "of this message."); 1333285976Sdelphij } 1334126277Sdes } 1335124211Sdes } 1336124211Sdes } 1337295367Sdes r = check_host_key(host, hostaddr, options.port, host_key, RDRW, 1338226046Sdes options.user_hostfiles, options.num_user_hostfiles, 1339226046Sdes options.system_hostfiles, options.num_system_hostfiles); 1340295367Sdes 1341295367Sdesout: 1342295367Sdes sshkey_free(plain); 1343295367Sdes free(fp); 1344296781Sdes free(cafp); 1345295367Sdes if (r == 0 && host_key != NULL) { 1346295367Sdes key_free(previous_host_key); 1347295367Sdes previous_host_key = key_from_private(host_key); 1348295367Sdes } 1349295367Sdes 1350295367Sdes return r; 135157565Smarkm} 135257565Smarkm 135357429Smarkm/* 135460576Skris * Starts a dialog with the server, and authenticates the current user on the 135560576Skris * server. This does not need any extra privileges. The basic connection 135660576Skris * to the server must already have been established before this is called. 135760576Skris * If login fails, this function prints an error and never returns. 135860576Skris * This function does not require super-user privileges. 135957429Smarkm */ 136057429Smarkmvoid 136198684Sdesssh_login(Sensitive *sensitive, const char *orighost, 1362221420Sdes struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 136357429Smarkm{ 1364262566Sdes char *host; 136560576Skris char *server_user, *local_user; 136657429Smarkm 136757429Smarkm local_user = xstrdup(pw->pw_name); 136857429Smarkm server_user = options.user ? options.user : local_user; 136957429Smarkm 137057429Smarkm /* Convert the user-supplied hostname into all lowercase. */ 137157429Smarkm host = xstrdup(orighost); 1372262566Sdes lowercase(host); 137357429Smarkm 137457429Smarkm /* Exchange protocol version identification strings with the server. */ 1375181111Sdes ssh_exchange_identification(timeout_ms); 137657429Smarkm 137757429Smarkm /* Put the connection into non-blocking mode. */ 137857429Smarkm packet_set_nonblocking(); 137957429Smarkm 138057429Smarkm /* key exchange */ 138157429Smarkm /* authenticate user */ 1382295367Sdes debug("Authenticating to %s:%d as '%s'", host, port, server_user); 138360576Skris if (compat20) { 1384221420Sdes ssh_kex2(host, hostaddr, port); 138598684Sdes ssh_userauth2(local_user, server_user, host, sensitive); 138660576Skris } else { 1387295367Sdes#ifdef WITH_SSH1 138860576Skris ssh_kex(host, hostaddr); 138998684Sdes ssh_userauth1(local_user, server_user, host, sensitive); 1390295367Sdes#else 1391295367Sdes fatal("ssh1 is not supported"); 1392295367Sdes#endif 139360576Skris } 1394255767Sdes free(local_user); 139557429Smarkm} 139674500Sgreen 139774500Sgreenvoid 139874500Sgreenssh_put_password(char *password) 139974500Sgreen{ 140074500Sgreen int size; 140174500Sgreen char *padded; 140274500Sgreen 140376262Sgreen if (datafellows & SSH_BUG_PASSWORDPAD) { 140492559Sdes packet_put_cstring(password); 140576262Sgreen return; 140676262Sgreen } 140774500Sgreen size = roundup(strlen(password) + 1, 32); 1408162856Sdes padded = xcalloc(1, size); 140974500Sgreen strlcpy(padded, password, size); 141074500Sgreen packet_put_string(padded, size); 1411264377Sdes explicit_bzero(padded, size); 1412255767Sdes free(padded); 141374500Sgreen} 1414106130Sdes 1415221420Sdes/* print all known host keys for a given host, but skip keys of given type */ 1416106130Sdesstatic int 1417221420Sdesshow_other_keys(struct hostkeys *hostkeys, Key *key) 1418106130Sdes{ 1419262566Sdes int type[] = { 1420262566Sdes KEY_RSA1, 1421262566Sdes KEY_RSA, 1422262566Sdes KEY_DSA, 1423262566Sdes KEY_ECDSA, 1424262566Sdes KEY_ED25519, 1425262566Sdes -1 1426262566Sdes }; 1427221420Sdes int i, ret = 0; 1428181111Sdes char *fp, *ra; 1429221420Sdes const struct hostkey_entry *found; 1430106130Sdes 1431106130Sdes for (i = 0; type[i] != -1; i++) { 1432106130Sdes if (type[i] == key->type) 1433106130Sdes continue; 1434221420Sdes if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) 1435106130Sdes continue; 1436295367Sdes fp = sshkey_fingerprint(found->key, 1437295367Sdes options.fingerprint_hash, SSH_FP_DEFAULT); 1438295367Sdes ra = sshkey_fingerprint(found->key, 1439295367Sdes options.fingerprint_hash, SSH_FP_RANDOMART); 1440295367Sdes if (fp == NULL || ra == NULL) 1441295367Sdes fatal("%s: sshkey_fingerprint fail", __func__); 1442221420Sdes logit("WARNING: %s key found for host %s\n" 1443221420Sdes "in %s:%lu\n" 1444221420Sdes "%s key fingerprint %s.", 1445221420Sdes key_type(found->key), 1446221420Sdes found->host, found->file, found->line, 1447221420Sdes key_type(found->key), fp); 1448221420Sdes if (options.visual_host_key) 1449221420Sdes logit("%s", ra); 1450255767Sdes free(ra); 1451255767Sdes free(fp); 1452221420Sdes ret = 1; 1453106130Sdes } 1454221420Sdes return ret; 1455106130Sdes} 1456126277Sdes 1457126277Sdesstatic void 1458126277Sdeswarn_changed_key(Key *host_key) 1459126277Sdes{ 1460126277Sdes char *fp; 1461126277Sdes 1462295367Sdes fp = sshkey_fingerprint(host_key, options.fingerprint_hash, 1463295367Sdes SSH_FP_DEFAULT); 1464295367Sdes if (fp == NULL) 1465295367Sdes fatal("%s: sshkey_fingerprint fail", __func__); 1466126277Sdes 1467126277Sdes error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1468126277Sdes error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 1469126277Sdes error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1470126277Sdes error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 1471126277Sdes error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 1472221420Sdes error("It is also possible that a host key has just been changed."); 1473126277Sdes error("The fingerprint for the %s key sent by the remote host is\n%s.", 1474221420Sdes key_type(host_key), fp); 1475126277Sdes error("Please contact your system administrator."); 1476126277Sdes 1477255767Sdes free(fp); 1478126277Sdes} 1479157019Sdes 1480157019Sdes/* 1481157019Sdes * Execute a local command 1482157019Sdes */ 1483157019Sdesint 1484157019Sdesssh_local_cmd(const char *args) 1485157019Sdes{ 1486157019Sdes char *shell; 1487157019Sdes pid_t pid; 1488157019Sdes int status; 1489221420Sdes void (*osighand)(int); 1490157019Sdes 1491157019Sdes if (!options.permit_local_command || 1492157019Sdes args == NULL || !*args) 1493157019Sdes return (1); 1494157019Sdes 1495221420Sdes if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 1496157019Sdes shell = _PATH_BSHELL; 1497157019Sdes 1498221420Sdes osighand = signal(SIGCHLD, SIG_DFL); 1499157019Sdes pid = fork(); 1500157019Sdes if (pid == 0) { 1501221420Sdes signal(SIGPIPE, SIG_DFL); 1502157019Sdes debug3("Executing %s -c \"%s\"", shell, args); 1503157019Sdes execl(shell, shell, "-c", args, (char *)NULL); 1504157019Sdes error("Couldn't execute %s -c \"%s\": %s", 1505157019Sdes shell, args, strerror(errno)); 1506157019Sdes _exit(1); 1507157019Sdes } else if (pid == -1) 1508157019Sdes fatal("fork failed: %.100s", strerror(errno)); 1509157019Sdes while (waitpid(pid, &status, 0) == -1) 1510157019Sdes if (errno != EINTR) 1511157019Sdes fatal("Couldn't wait for child: %s", strerror(errno)); 1512221420Sdes signal(SIGCHLD, osighand); 1513157019Sdes 1514157019Sdes if (!WIFEXITED(status)) 1515157019Sdes return (1); 1516157019Sdes 1517157019Sdes return (WEXITSTATUS(status)); 1518157019Sdes} 1519296781Sdes 1520296781Sdesvoid 1521296781Sdesmaybe_add_key_to_agent(char *authfile, Key *private, char *comment, 1522296781Sdes char *passphrase) 1523296781Sdes{ 1524296781Sdes int auth_sock = -1, r; 1525296781Sdes 1526296781Sdes if (options.add_keys_to_agent == 0) 1527296781Sdes return; 1528296781Sdes 1529296781Sdes if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { 1530296781Sdes debug3("no authentication agent, not adding key"); 1531296781Sdes return; 1532296781Sdes } 1533296781Sdes 1534296781Sdes if (options.add_keys_to_agent == 2 && 1535296781Sdes !ask_permission("Add key %s (%s) to agent?", authfile, comment)) { 1536296781Sdes debug3("user denied adding this key"); 1537296781Sdes return; 1538296781Sdes } 1539296781Sdes 1540296781Sdes if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, 1541296781Sdes (options.add_keys_to_agent == 3))) == 0) 1542296781Sdes debug("identity added to agent: %s", authfile); 1543296781Sdes else 1544296781Sdes debug("could not add identity to agent: %s (%d)", authfile, r); 1545296781Sdes} 1546