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