diff options
Diffstat (limited to 'other/ssharp/sshconnect.c')
| -rw-r--r-- | other/ssharp/sshconnect.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/other/ssharp/sshconnect.c b/other/ssharp/sshconnect.c new file mode 100644 index 0000000..d964c5f --- /dev/null +++ b/other/ssharp/sshconnect.c | |||
| @@ -0,0 +1,497 @@ | |||
| 1 | /* | ||
| 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
| 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
| 4 | * All rights reserved | ||
| 5 | * Code to connect to a remote host, and to perform the client side of the | ||
| 6 | * login (authentication) dialog. | ||
| 7 | * | ||
| 8 | * As far as I am concerned, the code I have written for this software | ||
| 9 | * can be used freely for any purpose. Any derived versions of this | ||
| 10 | * software must be clearly marked as such, and if the derived work is | ||
| 11 | * incompatible with the protocol description in the RFC file, it must be | ||
| 12 | * called by a name other than "ssh" or "Secure Shell". | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include "includes.h" | ||
| 16 | RCSID("$OpenBSD: sshconnect.c,v 1.104 2001/04/12 19:15:25 markus Exp $"); | ||
| 17 | |||
| 18 | #include <openssl/bn.h> | ||
| 19 | |||
| 20 | #include "ssh.h" | ||
| 21 | #include "xmalloc.h" | ||
| 22 | #include "rsa.h" | ||
| 23 | #include "buffer.h" | ||
| 24 | #include "packet.h" | ||
| 25 | #include "uidswap.h" | ||
| 26 | #include "compat.h" | ||
| 27 | #include "key.h" | ||
| 28 | #include "sshconnect.h" | ||
| 29 | #include "hostfile.h" | ||
| 30 | #include "log.h" | ||
| 31 | #include "readconf.h" | ||
| 32 | #include "atomicio.h" | ||
| 33 | #include "misc.h" | ||
| 34 | #include "ssharp.h" | ||
| 35 | |||
| 36 | char *client_version_string = NULL; | ||
| 37 | char *server_version_string = NULL; | ||
| 38 | |||
| 39 | extern Options options; | ||
| 40 | extern char *__progname; | ||
| 41 | |||
| 42 | /* AF_UNSPEC or AF_INET or AF_INET6 */ | ||
| 43 | extern int IPv4or6; | ||
| 44 | |||
| 45 | /* | ||
| 46 | * Connect to the given ssh server using a proxy command. | ||
| 47 | */ | ||
| 48 | int | ||
| 49 | ssh_proxy_connect(const char *host, u_short port, | ||
| 50 | const char *proxy_command) | ||
| 51 | { | ||
| 52 | Buffer command; | ||
| 53 | const char *cp; | ||
| 54 | char *command_string; | ||
| 55 | int pin[2], pout[2]; | ||
| 56 | pid_t pid; | ||
| 57 | char strport[NI_MAXSERV]; | ||
| 58 | |||
| 59 | /* Convert the port number into a string. */ | ||
| 60 | snprintf(strport, sizeof strport, "%hu", port); | ||
| 61 | |||
| 62 | /* Build the final command string in the buffer by making the | ||
| 63 | appropriate substitutions to the given proxy command. */ | ||
| 64 | buffer_init(&command); | ||
| 65 | for (cp = proxy_command; *cp; cp++) { | ||
| 66 | if (cp[0] == '%' && cp[1] == '%') { | ||
| 67 | buffer_append(&command, "%", 1); | ||
| 68 | cp++; | ||
| 69 | continue; | ||
| 70 | } | ||
| 71 | if (cp[0] == '%' && cp[1] == 'h') { | ||
| 72 | buffer_append(&command, host, strlen(host)); | ||
| 73 | cp++; | ||
| 74 | continue; | ||
| 75 | } | ||
| 76 | if (cp[0] == '%' && cp[1] == 'p') { | ||
| 77 | buffer_append(&command, strport, strlen(strport)); | ||
| 78 | cp++; | ||
| 79 | continue; | ||
| 80 | } | ||
| 81 | buffer_append(&command, cp, 1); | ||
| 82 | } | ||
| 83 | buffer_append(&command, "\0", 1); | ||
| 84 | |||
| 85 | /* Get the final command string. */ | ||
| 86 | command_string = buffer_ptr(&command); | ||
| 87 | |||
| 88 | /* Create pipes for communicating with the proxy. */ | ||
| 89 | if (pipe(pin) < 0 || pipe(pout) < 0) | ||
| 90 | fatal("Could not create pipes to communicate with the proxy: %.100s", | ||
| 91 | strerror(errno)); | ||
| 92 | |||
| 93 | debug("Executing proxy command: %.500s", command_string); | ||
| 94 | |||
| 95 | /* Fork and execute the proxy command. */ | ||
| 96 | if ((pid = fork()) == 0) { | ||
| 97 | char *argv[10]; | ||
| 98 | |||
| 99 | |||
| 100 | /* Redirect stdin and stdout. */ | ||
| 101 | close(pin[1]); | ||
| 102 | if (pin[0] != 0) { | ||
| 103 | if (dup2(pin[0], 0) < 0) | ||
| 104 | perror("dup2 stdin"); | ||
| 105 | close(pin[0]); | ||
| 106 | } | ||
| 107 | close(pout[0]); | ||
| 108 | if (dup2(pout[1], 1) < 0) | ||
| 109 | perror("dup2 stdout"); | ||
| 110 | /* Cannot be 1 because pin allocated two descriptors. */ | ||
| 111 | close(pout[1]); | ||
| 112 | |||
| 113 | /* Stderr is left as it is so that error messages get | ||
| 114 | printed on the user's terminal. */ | ||
| 115 | argv[0] = _PATH_BSHELL; | ||
| 116 | argv[1] = "-c"; | ||
| 117 | argv[2] = command_string; | ||
| 118 | argv[3] = NULL; | ||
| 119 | |||
| 120 | /* Execute the proxy command. Note that we gave up any | ||
| 121 | extra privileges above. */ | ||
| 122 | execv(argv[0], argv); | ||
| 123 | perror(argv[0]); | ||
| 124 | exit(1); | ||
| 125 | } | ||
| 126 | /* Parent. */ | ||
| 127 | if (pid < 0) | ||
| 128 | fatal("fork failed: %.100s", strerror(errno)); | ||
| 129 | |||
| 130 | /* Close child side of the descriptors. */ | ||
| 131 | close(pin[0]); | ||
| 132 | close(pout[1]); | ||
| 133 | |||
| 134 | /* Free the command name. */ | ||
| 135 | buffer_free(&command); | ||
| 136 | |||
| 137 | /* Set the connection file descriptors. */ | ||
| 138 | packet_set_connection(pout[0], pin[1]); | ||
| 139 | |||
| 140 | return 1; | ||
| 141 | } | ||
| 142 | |||
| 143 | |||
| 144 | /* | ||
| 145 | * Creates a (possibly privileged) socket for use as the ssh connection. | ||
| 146 | */ | ||
| 147 | int | ||
| 148 | ssh_create_socket(int family) | ||
| 149 | { | ||
| 150 | int sock = socket(family, SOCK_STREAM, 0); | ||
| 151 | return sock; | ||
| 152 | } | ||
| 153 | |||
| 154 | /* | ||
| 155 | * Opens a TCP/IP connection to the remote server on the given host. | ||
| 156 | * The address of the remote host will be returned in hostaddr. | ||
| 157 | * If port is 0, the default port will be used. If anonymous is zero, | ||
| 158 | * a privileged port will be allocated to make the connection. | ||
| 159 | * This requires super-user privileges if anonymous is false. | ||
| 160 | * Connection_attempts specifies the maximum number of tries (one per | ||
| 161 | * second). If proxy_command is non-NULL, it specifies the command (with %h | ||
| 162 | * and %p substituted for host and port, respectively) to use to contact | ||
| 163 | * the daemon. | ||
| 164 | */ | ||
| 165 | int | ||
| 166 | ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | ||
| 167 | u_short port, int connection_attempts, | ||
| 168 | int anonymous, | ||
| 169 | const char *proxy_command) | ||
| 170 | { | ||
| 171 | int gaierr; | ||
| 172 | int on = 1; | ||
| 173 | int sock = -1, attempt; | ||
| 174 | char ntop[NI_MAXHOST], strport[NI_MAXSERV]; | ||
| 175 | struct addrinfo hints, *ai, *aitop; | ||
| 176 | struct linger linger; | ||
| 177 | struct servent *sp; | ||
| 178 | |||
| 179 | debug("ssh_connect: getuid %u geteuid %u anon %d", | ||
| 180 | (u_int) getuid(), (u_int) geteuid(), anonymous); | ||
| 181 | |||
| 182 | /* Get default port if port has not been set. */ | ||
| 183 | if (port == 0) { | ||
| 184 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | ||
| 185 | if (sp) | ||
| 186 | port = ntohs(sp->s_port); | ||
| 187 | else | ||
| 188 | port = SSH_DEFAULT_PORT; | ||
| 189 | } | ||
| 190 | /* If a proxy command is given, connect using it. */ | ||
| 191 | if (proxy_command != NULL) | ||
| 192 | return ssh_proxy_connect(host, port, proxy_command); | ||
| 193 | |||
| 194 | /* No proxy command. */ | ||
| 195 | |||
| 196 | memset(&hints, 0, sizeof(hints)); | ||
| 197 | hints.ai_family = IPv4or6; | ||
| 198 | hints.ai_socktype = SOCK_STREAM; | ||
| 199 | snprintf(strport, sizeof strport, "%d", port); | ||
| 200 | if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) | ||
| 201 | fatal("%s: %.100s: %s", __progname, host, | ||
| 202 | gai_strerror(gaierr)); | ||
| 203 | |||
| 204 | /* | ||
| 205 | * Try to connect several times. On some machines, the first time | ||
| 206 | * will sometimes fail. In general socket code appears to behave | ||
| 207 | * quite magically on many machines. | ||
| 208 | */ | ||
| 209 | for (attempt = 0; attempt < connection_attempts; attempt++) { | ||
| 210 | if (attempt > 0) | ||
| 211 | debug("Trying again..."); | ||
| 212 | |||
| 213 | /* Loop through addresses for this host, and try each one in | ||
| 214 | sequence until the connection succeeds. */ | ||
| 215 | for (ai = aitop; ai; ai = ai->ai_next) { | ||
| 216 | if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) | ||
| 217 | continue; | ||
| 218 | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, | ||
| 219 | ntop, sizeof(ntop), strport, sizeof(strport), | ||
| 220 | NI_NUMERICHOST|NI_NUMERICSERV) != 0) { | ||
| 221 | error("ssh_connect: getnameinfo failed"); | ||
| 222 | continue; | ||
| 223 | } | ||
| 224 | debug("Connecting to %.200s [%.100s] port %s.", | ||
| 225 | host, ntop, strport); | ||
| 226 | |||
| 227 | |||
| 228 | if ((sock = socket_connect_b(ai->ai_addr, ai->ai_addrlen, SSHARP_MINPORT)) >= 0) { | ||
| 229 | /* Successful connection. */ | ||
| 230 | memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); | ||
| 231 | break; | ||
| 232 | } else { | ||
| 233 | debug("connect: %.100s", strerror(errno)); | ||
| 234 | /* | ||
| 235 | * Close the failed socket; there appear to | ||
| 236 | * be some problems when reusing a socket for | ||
| 237 | * which connect() has already returned an | ||
| 238 | * error. | ||
| 239 | */ | ||
| 240 | shutdown(sock, SHUT_RDWR); | ||
| 241 | close(sock); | ||
| 242 | } | ||
| 243 | } | ||
| 244 | if (ai) | ||
| 245 | break; /* Successful connection. */ | ||
| 246 | |||
| 247 | /* Sleep a moment before retrying. */ | ||
| 248 | sleep(1); | ||
| 249 | } | ||
| 250 | |||
| 251 | freeaddrinfo(aitop); | ||
| 252 | |||
| 253 | /* Return failure if we didn't get a successful connection. */ | ||
| 254 | if (attempt >= connection_attempts) | ||
| 255 | return 0; | ||
| 256 | |||
| 257 | debug("Connection established."); | ||
| 258 | |||
| 259 | /* | ||
| 260 | * Set socket options. We would like the socket to disappear as soon | ||
| 261 | * as it has been closed for whatever reason. | ||
| 262 | */ | ||
| 263 | /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ | ||
| 264 | linger.l_onoff = 1; | ||
| 265 | linger.l_linger = 5; | ||
| 266 | setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); | ||
| 267 | |||
| 268 | /* Set keepalives if requested. */ | ||
| 269 | if (options.keepalives && | ||
| 270 | setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, | ||
| 271 | sizeof(on)) < 0) | ||
| 272 | error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); | ||
| 273 | |||
| 274 | /* Set the connection. */ | ||
| 275 | packet_set_connection(sock, sock); | ||
| 276 | |||
| 277 | return 1; | ||
| 278 | } | ||
| 279 | |||
| 280 | /* | ||
| 281 | * Waits for the server identification string, and sends our own | ||
| 282 | * identification string. | ||
| 283 | */ | ||
| 284 | void | ||
| 285 | ssh_exchange_identification(void) | ||
| 286 | { | ||
| 287 | char buf[256], remote_version[256]; /* must be same size! */ | ||
| 288 | int remote_major, remote_minor, i, mismatch; | ||
| 289 | int connection_in = packet_get_connection_in(); | ||
| 290 | int connection_out = packet_get_connection_out(); | ||
| 291 | int minor1 = PROTOCOL_MINOR_1; | ||
| 292 | |||
| 293 | /* Read other side\'s version identification. */ | ||
| 294 | for (;;) { | ||
| 295 | for (i = 0; i < sizeof(buf) - 1; i++) { | ||
| 296 | int len = atomicio(read, connection_in, &buf[i], 1); | ||
| 297 | if (len < 0) | ||
| 298 | fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); | ||
| 299 | if (len != 1) | ||
| 300 | fatal("ssh_exchange_identification: Connection closed by remote host"); | ||
| 301 | if (buf[i] == '\r') { | ||
| 302 | buf[i] = '\n'; | ||
| 303 | buf[i + 1] = 0; | ||
| 304 | continue; /**XXX wait for \n */ | ||
| 305 | } | ||
| 306 | if (buf[i] == '\n') { | ||
| 307 | buf[i + 1] = 0; | ||
| 308 | break; | ||
| 309 | } | ||
| 310 | } | ||
| 311 | buf[sizeof(buf) - 1] = 0; | ||
| 312 | if (strncmp(buf, "SSH-", 4) == 0) | ||
| 313 | break; | ||
| 314 | debug("ssh_exchange_identification: %s", buf); | ||
| 315 | } | ||
| 316 | server_version_string = xstrdup(buf); | ||
| 317 | |||
| 318 | /* | ||
| 319 | * Check that the versions match. In future this might accept | ||
| 320 | * several versions and set appropriate flags to handle them. | ||
| 321 | */ | ||
| 322 | if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", | ||
| 323 | &remote_major, &remote_minor, remote_version) != 3) | ||
| 324 | fatal("Bad remote protocol version identification: '%.100s'", buf); | ||
| 325 | debug("Remote protocol version %d.%d, remote software version %.100s", | ||
| 326 | remote_major, remote_minor, remote_version); | ||
| 327 | |||
| 328 | compat_datafellows(remote_version); | ||
| 329 | mismatch = 0; | ||
| 330 | |||
| 331 | switch(remote_major) { | ||
| 332 | case 1: | ||
| 333 | if (remote_minor == 99 && | ||
| 334 | (options.protocol & SSH_PROTO_2) && | ||
| 335 | !(options.protocol & SSH_PROTO_1_PREFERRED)) { | ||
| 336 | enable_compat20(); | ||
| 337 | break; | ||
| 338 | } | ||
| 339 | if (!(options.protocol & SSH_PROTO_1)) { | ||
| 340 | mismatch = 1; | ||
| 341 | break; | ||
| 342 | } | ||
| 343 | if (remote_minor < 3) { | ||
| 344 | fatal("Remote machine has too old SSH software version."); | ||
| 345 | } else if (remote_minor == 3 || remote_minor == 4) { | ||
| 346 | /* We speak 1.3, too. */ | ||
| 347 | enable_compat13(); | ||
| 348 | minor1 = 3; | ||
| 349 | if (options.forward_agent) { | ||
| 350 | log("Agent forwarding disabled for protocol 1.3"); | ||
| 351 | options.forward_agent = 0; | ||
| 352 | } | ||
| 353 | } | ||
| 354 | break; | ||
| 355 | case 2: | ||
| 356 | if (options.protocol & SSH_PROTO_2) { | ||
| 357 | enable_compat20(); | ||
| 358 | break; | ||
| 359 | } | ||
| 360 | /* FALLTHROUGH */ | ||
| 361 | default: | ||
| 362 | mismatch = 1; | ||
| 363 | break; | ||
| 364 | } | ||
| 365 | if (mismatch) | ||
| 366 | fatal("Protocol major versions differ: %d vs. %d", | ||
| 367 | (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, | ||
| 368 | remote_major); | ||
| 369 | if (compat20) | ||
| 370 | packet_set_ssh2_format(); | ||
| 371 | /* Send our own protocol version identification. */ | ||
| 372 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", | ||
| 373 | compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, | ||
| 374 | compat20 ? PROTOCOL_MINOR_2 : minor1, | ||
| 375 | SSH_VERSION); | ||
| 376 | if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) | ||
| 377 | fatal("write: %.100s", strerror(errno)); | ||
| 378 | client_version_string = xstrdup(buf); | ||
| 379 | chop(client_version_string); | ||
| 380 | chop(server_version_string); | ||
| 381 | debug("Local version string %.100s", client_version_string); | ||
| 382 | } | ||
| 383 | |||
| 384 | /* defaults to 'no' */ | ||
| 385 | int | ||
| 386 | read_yes_or_no(const char *prompt, int defval) | ||
| 387 | { | ||
| 388 | char buf[1024]; | ||
| 389 | FILE *f; | ||
| 390 | int retval = -1; | ||
| 391 | |||
| 392 | if (options.batch_mode) | ||
| 393 | return 0; | ||
| 394 | |||
| 395 | if (isatty(STDIN_FILENO)) | ||
| 396 | f = stdin; | ||
| 397 | else | ||
| 398 | f = fopen(_PATH_TTY, "rw"); | ||
| 399 | |||
| 400 | if (f == NULL) | ||
| 401 | return 0; | ||
| 402 | |||
| 403 | fflush(stdout); | ||
| 404 | |||
| 405 | while (1) { | ||
| 406 | fprintf(stderr, "%s", prompt); | ||
| 407 | if (fgets(buf, sizeof(buf), f) == NULL) { | ||
| 408 | /* Print a newline (the prompt probably didn\'t have one). */ | ||
| 409 | fprintf(stderr, "\n"); | ||
| 410 | strlcpy(buf, "no", sizeof buf); | ||
| 411 | } | ||
| 412 | /* Remove newline from response. */ | ||
| 413 | if (strchr(buf, '\n')) | ||
| 414 | *strchr(buf, '\n') = 0; | ||
| 415 | |||
| 416 | if (buf[0] == 0) | ||
| 417 | retval = defval; | ||
| 418 | if (strcmp(buf, "yes") == 0) | ||
| 419 | retval = 1; | ||
| 420 | else if (strcmp(buf, "no") == 0) | ||
| 421 | retval = 0; | ||
| 422 | else | ||
| 423 | fprintf(stderr, "Please type 'yes' or 'no'.\n"); | ||
| 424 | |||
| 425 | if (retval != -1) { | ||
| 426 | if (f != stdin) | ||
| 427 | fclose(f); | ||
| 428 | return retval; | ||
| 429 | } | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 433 | /* | ||
| 434 | * check whether the supplied host key is valid, return only if ok. | ||
| 435 | */ | ||
| 436 | |||
| 437 | void | ||
| 438 | check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | ||
| 439 | const char *user_hostfile, const char *system_hostfile) | ||
| 440 | { | ||
| 441 | /* SSHARP: dummy */ | ||
| 442 | } | ||
| 443 | |||
| 444 | /* | ||
| 445 | * Starts a dialog with the server, and authenticates the current user on the | ||
| 446 | * server. This does not need any extra privileges. The basic connection | ||
| 447 | * to the server must already have been established before this is called. | ||
| 448 | * If login fails, this function prints an error and never returns. | ||
| 449 | * This function does not require super-user privileges. | ||
| 450 | */ | ||
| 451 | void | ||
| 452 | ssh_login(Key **keys, int nkeys, const char *orighost, | ||
| 453 | struct sockaddr *hostaddr, char *login, char *pass) | ||
| 454 | { | ||
| 455 | char *host, *cp; | ||
| 456 | |||
| 457 | /* Convert the user-supplied hostname into all lowercase. */ | ||
| 458 | host = xstrdup(orighost); | ||
| 459 | for (cp = host; *cp; cp++) | ||
| 460 | if (isupper(*cp)) | ||
| 461 | *cp = tolower(*cp); | ||
| 462 | |||
| 463 | /* Exchange protocol version identification strings with the server. */ | ||
| 464 | ssh_exchange_identification(); | ||
| 465 | |||
| 466 | /* Put the connection into non-blocking mode. */ | ||
| 467 | packet_set_nonblocking(); | ||
| 468 | |||
| 469 | /* key exchange */ | ||
| 470 | /* authenticate user */ | ||
| 471 | if (compat20) { | ||
| 472 | ssh_kex2(host, hostaddr); | ||
| 473 | ssh_userauth2(login, pass, host, keys, nkeys); | ||
| 474 | } else { | ||
| 475 | ssh_kex(host, hostaddr); | ||
| 476 | ssh_userauth1(login, pass, host, keys, nkeys); | ||
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 | void | ||
| 481 | ssh_put_password(char *password) | ||
| 482 | { | ||
| 483 | int size; | ||
| 484 | char *padded; | ||
| 485 | |||
| 486 | if (datafellows & SSH_BUG_PASSWORDPAD) { | ||
| 487 | packet_put_string(password, strlen(password)); | ||
| 488 | return; | ||
| 489 | } | ||
| 490 | size = roundup(strlen(password) + 1, 32); | ||
| 491 | padded = xmalloc(size); | ||
| 492 | memset(padded, 0, size); | ||
| 493 | strlcpy(padded, password, size); | ||
| 494 | packet_put_string(padded, size); | ||
| 495 | memset(padded, 0, size); | ||
| 496 | xfree(padded); | ||
| 497 | } | ||
