~ubuntu-branches/ubuntu/lucid/openssh/lucid

« back to all changes in this revision

Viewing changes to sshconnect.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2008-09-30 23:09:58 UTC
  • mfrom: (1.13.3 upstream) (29 hardy)
  • mto: This revision was merged to the branch mainline in revision 43.
  • Revision ID: james.westby@ubuntu.com-20080930230958-o6vsgn8c4mm959s0
Tags: 1:5.1p1-3
* Remove unnecessary ssh-vulnkey output in non-verbose mode when no
  compromised or unknown keys were found (closes: #496495).
* Configure with --disable-strip; dh_strip will deal with stripping
  binaries and will honour DEB_BUILD_OPTIONS (thanks, Bernhard R. Link;
  closes: #498681).
* Fix handling of zero-length server banners (thanks, Tomas Mraz; closes:
  #497026).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $OpenBSD: sshconnect.c,v 1.200 2006/10/10 10:12:45 markus Exp $ */
 
1
/* $OpenBSD: sshconnect.c,v 1.211 2008/07/01 07:24:22 dtucker Exp $ */
2
2
/*
3
3
 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4
4
 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
74
74
#define INET6_ADDRSTRLEN 46
75
75
#endif
76
76
 
77
 
static sig_atomic_t banner_timedout;
78
 
 
79
 
static void banner_alarm_catch (int signum)
80
 
{
81
 
        banner_timedout = 1;
82
 
}
83
 
 
84
77
static int show_other_keys(const char *, Key *);
85
78
static void warn_changed_key(Key *);
86
79
 
93
86
        char *command_string, *tmp;
94
87
        int pin[2], pout[2];
95
88
        pid_t pid;
96
 
        char strport[NI_MAXSERV];
 
89
        char *shell, strport[NI_MAXSERV];
 
90
 
 
91
        if ((shell = getenv("SHELL")) == NULL)
 
92
                shell = _PATH_BSHELL;
97
93
 
98
94
        /* Convert the port number into a string. */
99
95
        snprintf(strport, sizeof strport, "%hu", port);
139
135
 
140
136
                /* Stderr is left as it is so that error messages get
141
137
                   printed on the user's terminal. */
142
 
                argv[0] = _PATH_BSHELL;
 
138
                argv[0] = shell;
143
139
                argv[1] = "-c";
144
140
                argv[2] = command_string;
145
141
                argv[3] = NULL;
146
142
 
147
143
                /* Execute the proxy command.  Note that we gave up any
148
144
                   extra privileges above. */
149
 
                execv(argv[0], argv);
 
145
                execvp(argv[0], argv);
150
146
                perror(argv[0]);
151
147
                exit(1);
152
148
        }
164
160
        xfree(command_string);
165
161
 
166
162
        /* Set the connection file descriptors. */
167
 
        packet_set_connection(pout[0], pin[1], options.setuptimeout);
 
163
        packet_set_connection(pout[0], pin[1]);
 
164
        packet_set_timeout(options.server_alive_interval,
 
165
            options.server_alive_count_max);
168
166
 
169
167
        /* Indicate OK return */
170
168
        return 0;
208
206
        hints.ai_socktype = ai->ai_socktype;
209
207
        hints.ai_protocol = ai->ai_protocol;
210
208
        hints.ai_flags = AI_PASSIVE;
211
 
        gaierr = getaddrinfo(options.bind_address, "0", &hints, &res);
 
209
        gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
212
210
        if (gaierr) {
213
211
                error("getaddrinfo: %s: %s", options.bind_address,
214
 
                    gai_strerror(gaierr));
 
212
                    ssh_gai_strerror(gaierr));
215
213
                close(sock);
216
214
                return -1;
217
215
        }
227
225
 
228
226
static int
229
227
timeout_connect(int sockfd, const struct sockaddr *serv_addr,
230
 
    socklen_t addrlen, int timeout)
 
228
    socklen_t addrlen, int *timeoutp)
231
229
{
232
230
        fd_set *fdset;
233
 
        struct timeval tv;
 
231
        struct timeval tv, t_start;
234
232
        socklen_t optlen;
235
233
        int optval, rc, result = -1;
236
234
 
237
 
        if (timeout <= 0)
238
 
                return (connect(sockfd, serv_addr, addrlen));
 
235
        gettimeofday(&t_start, NULL);
 
236
 
 
237
        if (*timeoutp <= 0) {
 
238
                result = connect(sockfd, serv_addr, addrlen);
 
239
                goto done;
 
240
        }
239
241
 
240
242
        set_nonblock(sockfd);
241
243
        rc = connect(sockfd, serv_addr, addrlen);
242
244
        if (rc == 0) {
243
245
                unset_nonblock(sockfd);
244
 
                return (0);
245
 
        }
246
 
        if (errno != EINPROGRESS)
247
 
                return (-1);
 
246
                result = 0;
 
247
                goto done;
 
248
        }
 
249
        if (errno != EINPROGRESS) {
 
250
                result = -1;
 
251
                goto done;
 
252
        }
248
253
 
249
254
        fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS),
250
255
            sizeof(fd_mask));
251
256
        FD_SET(sockfd, fdset);
252
 
        tv.tv_sec = timeout;
253
 
        tv.tv_usec = 0;
 
257
        ms_to_timeval(&tv, *timeoutp);
254
258
 
255
259
        for (;;) {
256
260
                rc = select(sockfd + 1, NULL, fdset, NULL, &tv);
289
293
        }
290
294
 
291
295
        xfree(fdset);
 
296
 
 
297
 done:
 
298
        if (result == 0 && *timeoutp > 0) {
 
299
                ms_subtract_diff(&t_start, timeoutp);
 
300
                if (*timeoutp <= 0) {
 
301
                        errno = ETIMEDOUT;
 
302
                        result = -1;
 
303
                }
 
304
        }
 
305
 
292
306
        return (result);
293
307
}
294
308
 
305
319
 */
306
320
int
307
321
ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
308
 
    u_short port, int family, int connection_attempts,
309
 
    int needpriv, const char *proxy_command)
 
322
    u_short port, int family, int connection_attempts, int *timeout_ms,
 
323
    int want_keepalive, int needpriv, const char *proxy_command)
310
324
{
311
325
        int gaierr;
312
326
        int on = 1;
327
341
        hints.ai_socktype = SOCK_STREAM;
328
342
        snprintf(strport, sizeof strport, "%u", port);
329
343
        if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
330
 
                fatal("%s: %.100s: %s", __progname, host,
331
 
                    gai_strerror(gaierr));
 
344
                fatal("%s: Could not resolve hostname %.100s: %s", __progname,
 
345
                    host, ssh_gai_strerror(gaierr));
332
346
 
333
347
        for (attempt = 0; attempt < connection_attempts; attempt++) {
334
348
                if (attempt > 0) {
359
373
                                continue;
360
374
 
361
375
                        if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
362
 
                            options.connection_timeout) >= 0) {
 
376
                            timeout_ms) >= 0) {
363
377
                                /* Successful connection. */
364
378
                                memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
365
379
                                break;
386
400
        debug("Connection established.");
387
401
 
388
402
        /* Set SO_KEEPALIVE if requested. */
389
 
        if (options.tcp_keep_alive &&
 
403
        if (want_keepalive &&
390
404
            setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
391
405
            sizeof(on)) < 0)
392
406
                error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
393
407
 
394
408
        /* Set the connection. */
395
 
        packet_set_connection(sock, sock, options.setuptimeout);
 
409
        packet_set_connection(sock, sock);
 
410
        packet_set_timeout(options.server_alive_interval,
 
411
            options.server_alive_count_max);
396
412
 
397
413
        return 0;
398
414
}
402
418
 * identification string.
403
419
 */
404
420
static void
405
 
ssh_exchange_identification(void)
 
421
ssh_exchange_identification(int timeout_ms)
406
422
{
407
423
        char buf[256], remote_version[256];     /* must be same size! */
408
424
        int remote_major, remote_minor, mismatch;
410
426
        int connection_out = packet_get_connection_out();
411
427
        int minor1 = PROTOCOL_MINOR_1;
412
428
        u_int i, n;
413
 
        struct sigaction sa, osa;
414
 
 
415
 
        /* Read other side's version identification.
416
 
         * If SetupTimeOut has been set, give up after the specified amount
417
 
         * of time.
418
 
         */
419
 
        if (options.setuptimeout > 0) {
420
 
                memset(&sa, 0, sizeof(sa));
421
 
                sa.sa_handler = banner_alarm_catch;
422
 
                /* throw away any pending alarms, since we'd block otherwise */
423
 
                alarm(0);
424
 
                sigaction(SIGALRM, &sa, &osa);
425
 
                alarm(options.setuptimeout);
426
 
        }
 
429
        size_t len;
 
430
        int fdsetsz, remaining, rc;
 
431
        struct timeval t_start, t_remaining;
 
432
        fd_set *fdset;
 
433
 
 
434
        fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask);
 
435
        fdset = xcalloc(1, fdsetsz);
 
436
 
427
437
        /* Read other side's version identification. */
 
438
        remaining = timeout_ms;
428
439
        for (n = 0;;) {
429
 
                for (i = 0; i < sizeof(buf) - 1; ) {
430
 
                        ssize_t len = read(connection_in, &buf[i], 1);
431
 
                        if (banner_timedout)
432
 
                                fatal("ssh_exchange_identification: Timeout waiting for version information.");
433
 
                        if (len == 0)
434
 
                                errno = EPIPE;
 
440
                for (i = 0; i < sizeof(buf) - 1; i++) {
 
441
                        if (timeout_ms > 0) {
 
442
                                gettimeofday(&t_start, NULL);
 
443
                                ms_to_timeval(&t_remaining, remaining);
 
444
                                FD_SET(connection_in, fdset);
 
445
                                rc = select(connection_in + 1, fdset, NULL,
 
446
                                    fdset, &t_remaining);
 
447
                                ms_subtract_diff(&t_start, &remaining);
 
448
                                if (rc == 0 || remaining <= 0)
 
449
                                        fatal("Connection timed out during "
 
450
                                            "banner exchange");
 
451
                                if (rc == -1) {
 
452
                                        if (errno == EINTR)
 
453
                                                continue;
 
454
                                        fatal("ssh_exchange_identification: "
 
455
                                            "select: %s", strerror(errno));
 
456
                                }
 
457
                        }
 
458
 
 
459
                        len = atomicio(read, connection_in, &buf[i], 1);
435
460
 
436
461
                        if (len != 1 && errno == EPIPE)
437
 
                                fatal("ssh_exchange_identification: Connection closed by remote host");
438
 
                        else if (len != 1) {
439
 
#ifdef EWOULDBLOCK
440
 
                                if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
441
 
#else
442
 
                                if (errno == EINTR || errno == EAGAIN)
443
 
#endif
444
 
                                        continue;
445
 
                                fatal("ssh_exchange_identification: read: %.100s", strerror(errno));
446
 
                        }
 
462
                                fatal("ssh_exchange_identification: "
 
463
                                    "Connection closed by remote host");
 
464
                        else if (len != 1)
 
465
                                fatal("ssh_exchange_identification: "
 
466
                                    "read: %.100s", strerror(errno));
447
467
                        if (buf[i] == '\r') {
448
468
                                buf[i] = '\n';
449
469
                                buf[i + 1] = 0;
453
473
                                buf[i + 1] = 0;
454
474
                                break;
455
475
                        }
456
 
                        if (buf[i] == '\r') {
457
 
                                buf[i] = '\n';
458
 
                                buf[i + 1] = 0;         /**XXX wait for \n */
459
 
                        }
460
476
                        if (++n > 65536)
461
 
                                fatal("ssh_exchange_identification: No banner received");
462
 
                        i++;
 
477
                                fatal("ssh_exchange_identification: "
 
478
                                    "No banner received");
463
479
                }
464
480
                buf[sizeof(buf) - 1] = 0;
465
481
                if (strncmp(buf, "SSH-", 4) == 0)
467
483
                debug("ssh_exchange_identification: %s", buf);
468
484
        }
469
485
        server_version_string = xstrdup(buf);
470
 
 
471
 
        /* If SetupTimeOut has been set, unset the alarm now, and
472
 
         * put the correct handler for SIGALRM back.
473
 
         */
474
 
        if (options.setuptimeout > 0) {
475
 
                alarm(0);
476
 
                sigaction(SIGALRM, &osa, NULL);
477
 
        }
 
486
        xfree(fdset);
478
487
 
479
488
        /*
480
489
         * Check that the versions match.  In future this might accept
528
537
                    (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
529
538
                    remote_major);
530
539
        /* Send our own protocol version identification. */
531
 
        snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
 
540
        snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s",
532
541
            compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
533
542
            compat20 ? PROTOCOL_MINOR_2 : minor1,
534
 
            SSH_RELEASE);
 
543
            SSH_RELEASE, compat20 ? "\r\n" : "\n");
535
544
        if (atomicio(vwrite, connection_out, buf, strlen(buf)) != strlen(buf))
536
545
                fatal("write: %.100s", strerror(errno));
537
546
        client_version_string = xstrdup(buf);
580
589
        Key *file_key;
581
590
        const char *type = key_type(host_key);
582
591
        char *ip = NULL, *host = NULL;
583
 
        char hostline[1000], *hostp, *fp;
 
592
        char hostline[1000], *hostp, *fp, *ra;
584
593
        HostStatus host_status;
585
594
        HostStatus ip_status;
586
595
        int r, local = 0, host_ip_differ = 0;
587
596
        int salen;
588
597
        char ntop[NI_MAXHOST];
589
598
        char msg[1024];
590
 
        int len, host_line, ip_line;
 
599
        int len, host_line, ip_line, cancelled_forwarding = 0;
591
600
        const char *host_file = NULL, *ip_file = NULL;
592
601
 
593
602
        /*
634
643
        } else {
635
644
                ip = xstrdup("<no hostip for proxy command>");
636
645
        }
 
646
 
637
647
        /*
638
648
         * Turn off check_host_ip if the connection is to localhost, via proxy
639
649
         * command or if we don't have a hostname to compare with
718
728
                                logit("Warning: Permanently added the %s host "
719
729
                                    "key for IP address '%.128s' to the list "
720
730
                                    "of known hosts.", type, ip);
 
731
                } else if (options.visual_host_key) {
 
732
                        fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
 
733
                        ra = key_fingerprint(host_key, SSH_FP_MD5,
 
734
                            SSH_FP_RANDOMART);
 
735
                        logit("Host key fingerprint is %s\n%s\n", fp, ra);
 
736
                        xfree(ra);
 
737
                        xfree(fp);
721
738
                }
722
739
                break;
723
740
        case HOST_NEW:
753
770
                                snprintf(msg1, sizeof(msg1), ".");
754
771
                        /* The default */
755
772
                        fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
 
773
                        ra = key_fingerprint(host_key, SSH_FP_MD5,
 
774
                            SSH_FP_RANDOMART);
756
775
                        msg2[0] = '\0';
757
776
                        if (options.verify_host_key_dns) {
758
777
                                if (matching_host_key_dns)
767
786
                        snprintf(msg, sizeof(msg),
768
787
                            "The authenticity of host '%.200s (%s)' can't be "
769
788
                            "established%s\n"
770
 
                            "%s key fingerprint is %s.\n%s"
 
789
                            "%s key fingerprint is %s.%s%s\n%s"
771
790
                            "Are you sure you want to continue connecting "
772
791
                            "(yes/no)? ",
773
 
                            host, ip, msg1, type, fp, msg2);
 
792
                            host, ip, msg1, type, fp,
 
793
                            options.visual_host_key ? "\n" : "",
 
794
                            options.visual_host_key ? ra : "",
 
795
                            msg2);
 
796
                        xfree(ra);
774
797
                        xfree(fp);
775
798
                        if (!confirm(msg))
776
799
                                goto fail;
823
846
                        error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
824
847
                        error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
825
848
                        error("The %s host key for %s has changed,", type, host);
826
 
                        error("and the key for the according IP address %s", ip);
 
849
                        error("and the key for the corresponding IP address %s", ip);
827
850
                        error("%s. This could either mean that", key_msg);
828
851
                        error("DNS SPOOFING is happening or the IP address for the host");
829
852
                        error("and its host key have changed at the same time.");
855
878
                        error("Password authentication is disabled to avoid "
856
879
                            "man-in-the-middle attacks.");
857
880
                        options.password_authentication = 0;
 
881
                        cancelled_forwarding = 1;
858
882
                }
859
883
                if (options.kbd_interactive_authentication) {
860
884
                        error("Keyboard-interactive authentication is disabled"
861
885
                            " to avoid man-in-the-middle attacks.");
862
886
                        options.kbd_interactive_authentication = 0;
863
887
                        options.challenge_response_authentication = 0;
 
888
                        cancelled_forwarding = 1;
864
889
                }
865
890
                if (options.challenge_response_authentication) {
866
891
                        error("Challenge/response authentication is disabled"
867
892
                            " to avoid man-in-the-middle attacks.");
868
893
                        options.challenge_response_authentication = 0;
 
894
                        cancelled_forwarding = 1;
869
895
                }
870
896
                if (options.forward_agent) {
871
897
                        error("Agent forwarding is disabled to avoid "
872
898
                            "man-in-the-middle attacks.");
873
899
                        options.forward_agent = 0;
 
900
                        cancelled_forwarding = 1;
874
901
                }
875
902
                if (options.forward_x11) {
876
903
                        error("X11 forwarding is disabled to avoid "
877
904
                            "man-in-the-middle attacks.");
878
905
                        options.forward_x11 = 0;
 
906
                        cancelled_forwarding = 1;
879
907
                }
880
908
                if (options.num_local_forwards > 0 ||
881
909
                    options.num_remote_forwards > 0) {
883
911
                            "man-in-the-middle attacks.");
884
912
                        options.num_local_forwards =
885
913
                            options.num_remote_forwards = 0;
 
914
                        cancelled_forwarding = 1;
886
915
                }
887
916
                if (options.tun_open != SSH_TUNMODE_NO) {
888
917
                        error("Tunnel forwarding is disabled to avoid "
889
918
                            "man-in-the-middle attacks.");
890
919
                        options.tun_open = SSH_TUNMODE_NO;
 
920
                        cancelled_forwarding = 1;
891
921
                }
 
922
                if (options.exit_on_forward_failure && cancelled_forwarding)
 
923
                        fatal("Error: forwarding disabled due to host key "
 
924
                            "check failure");
 
925
                
892
926
                /*
893
927
                 * XXX Should permit the user to change to use the new id.
894
928
                 * This could be done by converting the host key to an
987
1021
 */
988
1022
void
989
1023
ssh_login(Sensitive *sensitive, const char *orighost,
990
 
    struct sockaddr *hostaddr, struct passwd *pw)
 
1024
    struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms)
991
1025
{
992
1026
        char *host, *cp;
993
1027
        char *server_user, *local_user;
1002
1036
                        *cp = (char)tolower(*cp);
1003
1037
 
1004
1038
        /* Exchange protocol version identification strings with the server. */
1005
 
        ssh_exchange_identification();
 
1039
        ssh_exchange_identification(timeout_ms);
1006
1040
 
1007
1041
        /* Put the connection into non-blocking mode. */
1008
1042
        packet_set_nonblocking();
1041
1075
show_key_from_file(const char *file, const char *host, int keytype)
1042
1076
{
1043
1077
        Key *found;
1044
 
        char *fp;
 
1078
        char *fp, *ra;
1045
1079
        int line, ret;
1046
1080
 
1047
1081
        found = key_new(keytype);
1048
1082
        if ((ret = lookup_key_in_hostfile_by_type(file, host,
1049
1083
            keytype, found, &line))) {
1050
1084
                fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
 
1085
                ra = key_fingerprint(found, SSH_FP_MD5, SSH_FP_RANDOMART);
1051
1086
                logit("WARNING: %s key found for host %s\n"
1052
1087
                    "in %s:%d\n"
1053
 
                    "%s key fingerprint %s.",
 
1088
                    "%s key fingerprint %s.\n%s\n",
1054
1089
                    key_type(found), host, file, line,
1055
 
                    key_type(found), fp);
 
1090
                    key_type(found), fp, ra);
 
1091
                xfree(ra);
1056
1092
                xfree(fp);
1057
1093
        }
1058
1094
        key_free(found);
1133
1169
        pid = fork();
1134
1170
        if (pid == 0) {
1135
1171
                debug3("Executing %s -c \"%s\"", shell, args);
1136
 
                execl(shell, shell, "-c", args, (char *)NULL);
 
1172
                execlp(shell, shell, "-c", args, (char *)NULL);
1137
1173
                error("Couldn't execute %s -c \"%s\": %s",
1138
1174
                    shell, args, strerror(errno));
1139
1175
                _exit(1);