source: trunk/third/openssh/sshconnect.c @ 16801

Revision 16801, 25.9 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16800, which included commits to RCS files with non-trunk default branches.
Line 
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"
16RCSID("$OpenBSD: sshconnect.c,v 1.110 2001/07/25 14:35:18 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
35char *client_version_string = NULL;
36char *server_version_string = NULL;
37
38extern Options options;
39extern char *__progname;
40
41/* AF_UNSPEC or AF_INET or AF_INET6 */
42extern int IPv4or6;
43
44#ifndef INET6_ADDRSTRLEN                /* for non IPv6 machines */
45#define INET6_ADDRSTRLEN 46
46#endif
47
48static const char *
49sockaddr_ntop(struct sockaddr *sa)
50{
51        void *addr;
52        static char addrbuf[INET6_ADDRSTRLEN];
53
54        switch (sa->sa_family) {
55                case AF_INET:
56                        addr = &((struct sockaddr_in *)sa)->sin_addr;
57                        break;
58                case AF_INET6:
59                        addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
60                        break;
61                default:
62                        /* This case should be protected against elsewhere */
63                        abort();
64        }
65        inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf));
66        return addrbuf;
67}
68
69/*
70 * Connect to the given ssh server using a proxy command.
71 */
72static int
73ssh_proxy_connect(const char *host, u_short port, struct passwd *pw,
74                  const char *proxy_command)
75{
76        Buffer command;
77        const char *cp;
78        char *command_string;
79        int pin[2], pout[2];
80        pid_t pid;
81        char strport[NI_MAXSERV];
82
83        /* Convert the port number into a string. */
84        snprintf(strport, sizeof strport, "%hu", port);
85
86        /* Build the final command string in the buffer by making the
87           appropriate substitutions to the given proxy command. */
88        buffer_init(&command);
89        for (cp = proxy_command; *cp; cp++) {
90                if (cp[0] == '%' && cp[1] == '%') {
91                        buffer_append(&command, "%", 1);
92                        cp++;
93                        continue;
94                }
95                if (cp[0] == '%' && cp[1] == 'h') {
96                        buffer_append(&command, host, strlen(host));
97                        cp++;
98                        continue;
99                }
100                if (cp[0] == '%' && cp[1] == 'p') {
101                        buffer_append(&command, strport, strlen(strport));
102                        cp++;
103                        continue;
104                }
105                buffer_append(&command, cp, 1);
106        }
107        buffer_append(&command, "\0", 1);
108
109        /* Get the final command string. */
110        command_string = buffer_ptr(&command);
111
112        /* Create pipes for communicating with the proxy. */
113        if (pipe(pin) < 0 || pipe(pout) < 0)
114                fatal("Could not create pipes to communicate with the proxy: %.100s",
115                      strerror(errno));
116
117        debug("Executing proxy command: %.500s", command_string);
118
119        /* Fork and execute the proxy command. */
120        if ((pid = fork()) == 0) {
121                char *argv[10];
122
123                /* Child.  Permanently give up superuser privileges. */
124                permanently_set_uid(pw);
125
126                /* Redirect stdin and stdout. */
127                close(pin[1]);
128                if (pin[0] != 0) {
129                        if (dup2(pin[0], 0) < 0)
130                                perror("dup2 stdin");
131                        close(pin[0]);
132                }
133                close(pout[0]);
134                if (dup2(pout[1], 1) < 0)
135                        perror("dup2 stdout");
136                /* Cannot be 1 because pin allocated two descriptors. */
137                close(pout[1]);
138
139                /* Stderr is left as it is so that error messages get
140                   printed on the user's terminal. */
141                argv[0] = _PATH_BSHELL;
142                argv[1] = "-c";
143                argv[2] = command_string;
144                argv[3] = NULL;
145
146                /* Execute the proxy command.  Note that we gave up any
147                   extra privileges above. */
148                execv(argv[0], argv);
149                perror(argv[0]);
150                exit(1);
151        }
152        /* Parent. */
153        if (pid < 0)
154                fatal("fork failed: %.100s", strerror(errno));
155
156        /* Close child side of the descriptors. */
157        close(pin[0]);
158        close(pout[1]);
159
160        /* Free the command name. */
161        buffer_free(&command);
162
163        /* Set the connection file descriptors. */
164        packet_set_connection(pout[0], pin[1]);
165
166        /* Indicate OK return */
167        return 0;
168}
169
170/*
171 * Creates a (possibly privileged) socket for use as the ssh connection.
172 */
173static int
174ssh_create_socket(struct passwd *pw, int privileged, int family)
175{
176        int sock, gaierr;
177        struct addrinfo hints, *res;
178
179        /*
180         * If we are running as root and want to connect to a privileged
181         * port, bind our own socket to a privileged port.
182         */
183        if (privileged) {
184                int p = IPPORT_RESERVED - 1;
185                sock = rresvport_af(&p, family);
186                if (sock < 0)
187                        error("rresvport: af=%d %.100s", family, strerror(errno));
188                else
189                        debug("Allocated local port %d.", p);
190                return sock;
191        }
192        /*
193         * Just create an ordinary socket on arbitrary port.  We use
194         * the user's uid to create the socket.
195         */
196        temporarily_use_uid(pw);
197        sock = socket(family, SOCK_STREAM, 0);
198        if (sock < 0)
199                error("socket: %.100s", strerror(errno));
200        restore_uid();
201
202        /* Bind the socket to an alternative local IP address */
203        if (options.bind_address == NULL)
204                return sock;
205
206        memset(&hints, 0, sizeof(hints));
207        hints.ai_family = IPv4or6;
208        hints.ai_socktype = SOCK_STREAM;
209        hints.ai_flags = AI_PASSIVE;
210        gaierr = getaddrinfo(options.bind_address, "0", &hints, &res);
211        if (gaierr) {
212                error("getaddrinfo: %s: %s", options.bind_address,
213                    gai_strerror(gaierr));
214                close(sock);
215                return -1;
216        }
217        if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
218                error("bind: %s: %s", options.bind_address, strerror(errno));
219                close(sock);
220                freeaddrinfo(res);
221                return -1;
222        }
223        freeaddrinfo(res);
224        return sock;
225}
226
227/*
228 * Opens a TCP/IP connection to the remote server on the given host.
229 * The address of the remote host will be returned in hostaddr.
230 * If port is 0, the default port will be used.  If anonymous is zero,
231 * a privileged port will be allocated to make the connection.
232 * This requires super-user privileges if anonymous is false.
233 * Connection_attempts specifies the maximum number of tries (one per
234 * second).  If proxy_command is non-NULL, it specifies the command (with %h
235 * and %p substituted for host and port, respectively) to use to contact
236 * the daemon.
237 * Return values:
238 *    0 for OK
239 *    ECONNREFUSED if we got a "Connection Refused" by the peer on any address
240 *    ECONNABORTED if we failed without a "Connection refused"
241 * Suitable error messages for the connection failure will already have been
242 * printed.
243 */
244int
245ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
246            u_short port, int connection_attempts,
247            int anonymous, struct passwd *pw,
248            const char *proxy_command)
249{
250        int gaierr;
251        int on = 1;
252        int sock = -1, attempt;
253        char ntop[NI_MAXHOST], strport[NI_MAXSERV];
254        struct addrinfo hints, *ai, *aitop;
255        struct linger linger;
256        struct servent *sp;
257        /*
258         * Did we get only other errors than "Connection refused" (which
259         * should block fallback to rsh and similar), or did we get at least
260         * one "Connection refused"?
261         */
262        int full_failure = 1;
263
264        debug("ssh_connect: getuid %u geteuid %u anon %d",
265              (u_int) getuid(), (u_int) geteuid(), anonymous);
266
267        /* Get default port if port has not been set. */
268        if (port == 0) {
269                sp = getservbyname(SSH_SERVICE_NAME, "tcp");
270                if (sp)
271                        port = ntohs(sp->s_port);
272                else
273                        port = SSH_DEFAULT_PORT;
274        }
275        /* If a proxy command is given, connect using it. */
276        if (proxy_command != NULL)
277                return ssh_proxy_connect(host, port, pw, proxy_command);
278
279        /* No proxy command. */
280
281        memset(&hints, 0, sizeof(hints));
282        hints.ai_family = IPv4or6;
283        hints.ai_socktype = SOCK_STREAM;
284        snprintf(strport, sizeof strport, "%d", port);
285        if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
286                fatal("%s: %.100s: %s", __progname, host,
287                    gai_strerror(gaierr));
288
289        /*
290         * Try to connect several times.  On some machines, the first time
291         * will sometimes fail.  In general socket code appears to behave
292         * quite magically on many machines.
293                 */
294        for (attempt = 0; ;) {
295                if (attempt > 0)
296                        debug("Trying again...");
297
298                /* Loop through addresses for this host, and try each one in
299                   sequence until the connection succeeds. */
300                for (ai = aitop; ai; ai = ai->ai_next) {
301                        if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
302                                continue;
303                        if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
304                            ntop, sizeof(ntop), strport, sizeof(strport),
305                            NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
306                                error("ssh_connect: getnameinfo failed");
307                                continue;
308                        }
309                        debug("Connecting to %.200s [%.100s] port %s.",
310                                host, ntop, strport);
311
312                        /* Create a socket for connecting. */
313                        sock = ssh_create_socket(pw,
314#ifdef HAVE_CYGWIN
315                            !anonymous,
316#else
317                            !anonymous && geteuid() == 0,
318#endif
319                            ai->ai_family);
320                        if (sock < 0)
321                                /* Any error is already output */
322                                continue;
323
324                        /* Connect to the host.  We use the user's uid in the
325                         * hope that it will help with tcp_wrappers showing
326                         * the remote uid as root.
327                         */
328                        temporarily_use_uid(pw);
329                        if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) {
330                                /* Successful connection. */
331                                memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
332                                restore_uid();
333                                break;
334                        } else {
335                                if (errno == ECONNREFUSED)
336                                        full_failure = 0;
337                                log("ssh: connect to address %s port %s: %s",
338                                    sockaddr_ntop(ai->ai_addr), strport,
339                                    strerror(errno));
340                                restore_uid();
341                                /*
342                                 * Close the failed socket; there appear to
343                                 * be some problems when reusing a socket for
344                                 * which connect() has already returned an
345                                 * error.
346                                 */
347                                shutdown(sock, SHUT_RDWR);
348                                close(sock);
349                        }
350                }
351                if (ai)
352                        break;  /* Successful connection. */
353
354                attempt++;
355                if (attempt >= connection_attempts)
356                        break;
357                /* Sleep a moment before retrying. */
358                sleep(1);
359        }
360
361        freeaddrinfo(aitop);
362
363        /* Return failure if we didn't get a successful connection. */
364        if (attempt >= connection_attempts)
365                return full_failure ? ECONNABORTED : ECONNREFUSED;
366
367        debug("Connection established.");
368
369        /*
370         * Set socket options.  We would like the socket to disappear as soon
371         * as it has been closed for whatever reason.
372         */
373        /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */
374        linger.l_onoff = 1;
375        linger.l_linger = 5;
376        setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
377
378        /* Set keepalives if requested. */
379        if (options.keepalives &&
380            setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
381            sizeof(on)) < 0)
382                error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
383
384        /* Set the connection. */
385        packet_set_connection(sock, sock);
386
387        return 0;
388}
389
390/*
391 * Waits for the server identification string, and sends our own
392 * identification string.
393 */
394static void
395ssh_exchange_identification(void)
396{
397        char buf[256], remote_version[256];     /* must be same size! */
398        int remote_major, remote_minor, i, mismatch;
399        int connection_in = packet_get_connection_in();
400        int connection_out = packet_get_connection_out();
401        int minor1 = PROTOCOL_MINOR_1;
402
403        /* Read other side\'s version identification. */
404        for (;;) {
405                for (i = 0; i < sizeof(buf) - 1; i++) {
406                        int len = atomicio(read, connection_in, &buf[i], 1);
407                        if (len < 0)
408                                fatal("ssh_exchange_identification: read: %.100s", strerror(errno));
409                        if (len != 1)
410                                fatal("ssh_exchange_identification: Connection closed by remote host");
411                        if (buf[i] == '\r') {
412                                buf[i] = '\n';
413                                buf[i + 1] = 0;
414                                continue;               /**XXX wait for \n */
415                        }
416                        if (buf[i] == '\n') {
417                                buf[i + 1] = 0;
418                                break;
419                        }
420                }
421                buf[sizeof(buf) - 1] = 0;
422                if (strncmp(buf, "SSH-", 4) == 0)
423                        break;
424                debug("ssh_exchange_identification: %s", buf);
425        }
426        server_version_string = xstrdup(buf);
427
428        /*
429         * Check that the versions match.  In future this might accept
430         * several versions and set appropriate flags to handle them.
431         */
432        if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n",
433            &remote_major, &remote_minor, remote_version) != 3)
434                fatal("Bad remote protocol version identification: '%.100s'", buf);
435        debug("Remote protocol version %d.%d, remote software version %.100s",
436              remote_major, remote_minor, remote_version);
437
438        compat_datafellows(remote_version);
439        mismatch = 0;
440
441        switch(remote_major) {
442        case 1:
443                if (remote_minor == 99 &&
444                    (options.protocol & SSH_PROTO_2) &&
445                    !(options.protocol & SSH_PROTO_1_PREFERRED)) {
446                        enable_compat20();
447                        break;
448                }
449                if (!(options.protocol & SSH_PROTO_1)) {
450                        mismatch = 1;
451                        break;
452                }
453                if (remote_minor < 3) {
454                        fatal("Remote machine has too old SSH software version.");
455                } else if (remote_minor == 3 || remote_minor == 4) {
456                        /* We speak 1.3, too. */
457                        enable_compat13();
458                        minor1 = 3;
459                        if (options.forward_agent) {
460                                log("Agent forwarding disabled for protocol 1.3");
461                                options.forward_agent = 0;
462                        }
463                }
464                break;
465        case 2:
466                if (options.protocol & SSH_PROTO_2) {
467                        enable_compat20();
468                        break;
469                }
470                /* FALLTHROUGH */
471        default:
472                mismatch = 1;
473                break;
474        }
475        if (mismatch)
476                fatal("Protocol major versions differ: %d vs. %d",
477                    (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
478                    remote_major);
479        /* Send our own protocol version identification. */
480        snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
481            compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
482            compat20 ? PROTOCOL_MINOR_2 : minor1,
483            SSH_VERSION);
484        if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf))
485                fatal("write: %.100s", strerror(errno));
486        client_version_string = xstrdup(buf);
487        chop(client_version_string);
488        chop(server_version_string);
489        debug("Local version string %.100s", client_version_string);
490}
491
492/* defaults to 'no' */
493static int
494read_yes_or_no(const char *prompt, int defval)
495{
496        char buf[1024];
497        FILE *f;
498        int retval = -1;
499
500        if (options.batch_mode)
501                return 0;
502
503        if (isatty(STDIN_FILENO))
504                f = stdin;
505        else
506                f = fopen(_PATH_TTY, "rw");
507
508        if (f == NULL)
509                return 0;
510
511        fflush(stdout);
512
513        while (1) {
514                fprintf(stderr, "%s", prompt);
515                if (fgets(buf, sizeof(buf), f) == NULL) {
516                        /*
517                         * Print a newline (the prompt probably didn\'t have
518                         * one).
519                         */
520                        fprintf(stderr, "\n");
521                        strlcpy(buf, "no", sizeof buf);
522                }
523                /* Remove newline from response. */
524                if (strchr(buf, '\n'))
525                        *strchr(buf, '\n') = 0;
526
527                if (buf[0] == 0)
528                        retval = defval;
529                if (strcmp(buf, "yes") == 0)
530                        retval = 1;
531                else if (strcmp(buf, "no") == 0)
532                        retval = 0;
533                else
534                        fprintf(stderr, "Please type 'yes' or 'no'.\n");
535
536                if (retval != -1) {
537                        if (f != stdin)
538                                fclose(f);
539                        return retval;
540                }
541        }
542}
543
544/*
545 * check whether the supplied host key is valid, return -1 if the key
546 * is not valid. the user_hostfile will not be updated if 'readonly' is true.
547 */
548
549static int
550check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key,
551    int readonly, const char *user_hostfile, const char *system_hostfile)
552{
553        Key *file_key;
554        char *type = key_type(host_key);
555        char *ip = NULL;
556        char hostline[1000], *hostp, *fp;
557        HostStatus host_status;
558        HostStatus ip_status;
559        int local = 0, host_ip_differ = 0;
560        int salen;
561        char ntop[NI_MAXHOST];
562        int host_line, ip_line;
563        const char *host_file = NULL, *ip_file = NULL;
564
565        /*
566         * Force accepting of the host key for loopback/localhost. The
567         * problem is that if the home directory is NFS-mounted to multiple
568         * machines, localhost will refer to a different machine in each of
569         * them, and the user will get bogus HOST_CHANGED warnings.  This
570         * essentially disables host authentication for localhost; however,
571         * this is probably not a real problem.
572         */
573        /**  hostaddr == 0! */
574        switch (hostaddr->sa_family) {
575        case AF_INET:
576                local = (ntohl(((struct sockaddr_in *)hostaddr)->
577                    sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
578                salen = sizeof(struct sockaddr_in);
579                break;
580        case AF_INET6:
581                local = IN6_IS_ADDR_LOOPBACK(
582                    &(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
583                salen = sizeof(struct sockaddr_in6);
584                break;
585        default:
586                local = 0;
587                salen = sizeof(struct sockaddr_storage);
588                break;
589        }
590        if (local && options.host_key_alias == NULL) {
591                debug("Forcing accepting of host key for "
592                    "loopback/localhost.");
593                return 0;
594        }
595
596        /*
597         * We don't have the remote ip-address for connections
598         * using a proxy command
599         */
600        if (options.proxy_command == NULL) {
601                if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop),
602                    NULL, 0, NI_NUMERICHOST) != 0)
603                        fatal("check_host_key: getnameinfo failed");
604                ip = xstrdup(ntop);
605        } else {
606                ip = xstrdup("<no hostip for proxy command>");
607        }
608        /*
609         * Turn off check_host_ip if the connection is to localhost, via proxy
610         * command or if we don't have a hostname to compare with
611         */
612        if (options.check_host_ip &&
613            (local || strcmp(host, ip) == 0 || options.proxy_command != NULL))
614                options.check_host_ip = 0;
615
616        /*
617         * Allow the user to record the key under a different name. This is
618         * useful for ssh tunneling over forwarded connections or if you run
619         * multiple sshd's on different ports on the same machine.
620         */
621        if (options.host_key_alias != NULL) {
622                host = options.host_key_alias;
623                debug("using hostkeyalias: %s", host);
624        }
625
626        /*
627         * Store the host key from the known host file in here so that we can
628         * compare it with the key for the IP address.
629         */
630        file_key = key_new(host_key->type);
631
632        /*
633         * Check if the host key is present in the user\'s list of known
634         * hosts or in the systemwide list.
635         */
636        host_file = user_hostfile;
637        host_status = check_host_in_hostfile(host_file, host, host_key,
638             file_key, &host_line);
639        if (host_status == HOST_NEW) {
640                host_file = system_hostfile;
641                host_status = check_host_in_hostfile(host_file, host, host_key,
642                    file_key, &host_line);
643        }
644        /*
645         * Also perform check for the ip address, skip the check if we are
646         * localhost or the hostname was an ip address to begin with
647         */
648        if (options.check_host_ip) {
649                Key *ip_key = key_new(host_key->type);
650
651                ip_file = user_hostfile;
652                ip_status = check_host_in_hostfile(ip_file, ip, host_key,
653                    ip_key, &ip_line);
654                if (ip_status == HOST_NEW) {
655                        ip_file = system_hostfile;
656                        ip_status = check_host_in_hostfile(ip_file, ip,
657                            host_key, ip_key, &ip_line);
658                }
659                if (host_status == HOST_CHANGED &&
660                    (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key)))
661                        host_ip_differ = 1;
662
663                key_free(ip_key);
664        } else
665                ip_status = host_status;
666
667        key_free(file_key);
668
669        switch (host_status) {
670        case HOST_OK:
671                /* The host is known and the key matches. */
672                debug("Host '%.200s' is known and matches the %s host key.",
673                    host, type);
674                debug("Found key in %s:%d", host_file, host_line);
675                if (options.check_host_ip && ip_status == HOST_NEW) {
676                        if (readonly)
677                                log("%s host key for IP address "
678                                    "'%.128s' not in list of known hosts.",
679                                    type, ip);
680                        else if (!add_host_to_hostfile(user_hostfile, ip,
681                             host_key))
682                                log("Failed to add the %s host key for IP "
683                                    "address '%.128s' to the list of known "
684                                    "hosts (%.30s).", type, ip, user_hostfile);
685                        else
686                                log("Warning: Permanently added the %s host "
687                                    "key for IP address '%.128s' to the list "
688                                    "of known hosts.", type, ip);
689                }
690                break;
691        case HOST_NEW:
692                if (readonly)
693                        goto fail;
694                /* The host is new. */
695                if (options.strict_host_key_checking == 1) {
696                        /*
697                         * User has requested strict host key checking.  We
698                         * will not add the host key automatically.  The only
699                         * alternative left is to abort.
700                         */
701                        error("No %s host key is known for %.200s and you "
702                            "have requested strict checking.", type, host);
703                        goto fail;
704                } else if (options.strict_host_key_checking == 2) {
705                        /* The default */
706                        char prompt[1024];
707                        fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
708                        snprintf(prompt, sizeof(prompt),
709                            "The authenticity of host '%.200s (%s)' can't be "
710                            "established.\n"
711                            "%s key fingerprint is %s.\n"
712                            "Are you sure you want to continue connecting "
713                            "(yes/no)? ", host, ip, type, fp);
714                        xfree(fp);
715                        if (!read_yes_or_no(prompt, -1)) {
716                                log("Aborted by user!");
717                                goto fail;
718                        }
719                }
720                if (options.check_host_ip && ip_status == HOST_NEW) {
721                        snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
722                        hostp = hostline;
723                } else
724                        hostp = host;
725
726                /*
727                 * If not in strict mode, add the key automatically to the
728                 * local known_hosts file.
729                 */
730                if (!add_host_to_hostfile(user_hostfile, hostp, host_key))
731                        log("Failed to add the host to the list of known "
732                            "hosts (%.500s).", user_hostfile);
733                else
734                        log("Warning: Permanently added '%.200s' (%s) to the "
735                            "list of known hosts.", hostp, type);
736                break;
737        case HOST_CHANGED:
738                if (options.check_host_ip && host_ip_differ) {
739                        char *msg;
740                        if (ip_status == HOST_NEW)
741                                msg = "is unknown";
742                        else if (ip_status == HOST_OK)
743                                msg = "is unchanged";
744                        else
745                                msg = "has a different value";
746                        error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
747                        error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
748                        error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
749                        error("The %s host key for %s has changed,", type, host);
750                        error("and the key for the according IP address %s", ip);
751                        error("%s. This could either mean that", msg);
752                        error("DNS SPOOFING is happening or the IP address for the host");
753                        error("and its host key have changed at the same time.");
754                        if (ip_status != HOST_NEW)
755                                error("Offending key for IP in %s:%d", ip_file, ip_line);
756                }
757                /* The host key has changed. */
758                fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
759                error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
760                error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
761                error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
762                error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
763                error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
764                error("It is also possible that the %s host key has just been changed.", type);
765                error("The fingerprint for the %s key sent by the remote host is\n%s.",
766                    type, fp);
767                error("Please contact your system administrator.");
768                error("Add correct host key in %.100s to get rid of this message.",
769                    user_hostfile);
770                error("Offending key in %s:%d", host_file, host_line);
771                xfree(fp);
772
773                /*
774                 * If strict host key checking is in use, the user will have
775                 * to edit the key manually and we can only abort.
776                 */
777                if (options.strict_host_key_checking) {
778                        error("%s host key for %.200s has changed and you have "
779                            "requested strict checking.", type, host);
780                        goto fail;
781                }
782
783                /*
784                 * If strict host key checking has not been requested, allow
785                 * the connection but without password authentication or
786                 * agent forwarding.
787                 */
788                if (options.password_authentication) {
789                        error("Password authentication is disabled to avoid "
790                            "man-in-the-middle attacks.");
791                        options.password_authentication = 0;
792                }
793                if (options.forward_agent) {
794                        error("Agent forwarding is disabled to avoid "
795                            "man-in-the-middle attacks.");
796                        options.forward_agent = 0;
797                }
798                if (options.forward_x11) {
799                        error("X11 forwarding is disabled to avoid "
800                            "man-in-the-middle attacks.");
801                        options.forward_x11 = 0;
802                }
803                if (options.num_local_forwards > 0 ||
804                    options.num_remote_forwards > 0) {
805                        error("Port forwarding is disabled to avoid "
806                            "man-in-the-middle attacks.");
807                        options.num_local_forwards =
808                             options.num_remote_forwards = 0;
809                }
810                /*
811                 * XXX Should permit the user to change to use the new id.
812                 * This could be done by converting the host key to an
813                 * identifying sentence, tell that the host identifies itself
814                 * by that sentence, and ask the user if he/she whishes to
815                 * accept the authentication.
816                 */
817                break;
818        }
819
820        if (options.check_host_ip && host_status != HOST_CHANGED &&
821            ip_status == HOST_CHANGED) {
822                log("Warning: the %s host key for '%.200s' "
823                    "differs from the key for the IP address '%.128s'",
824                    type, host, ip);
825                if (host_status == HOST_OK)
826                        log("Matching host key in %s:%d", host_file, host_line);
827                log("Offending key for IP in %s:%d", ip_file, ip_line);
828                if (options.strict_host_key_checking == 1) {
829                        error("Exiting, you have requested strict checking.");
830                        goto fail;
831                } else if (options.strict_host_key_checking == 2) {
832                        if (!read_yes_or_no("Are you sure you want "
833                            "to continue connecting (yes/no)? ", -1)) {
834                                log("Aborted by user!");
835                                goto fail;
836                        }
837                }
838        }
839
840        xfree(ip);
841        return 0;
842
843fail:
844        xfree(ip);
845        return -1;
846}
847
848int
849verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
850{
851        struct stat st;
852
853        /* return ok if the key can be found in an old keyfile */
854        if (stat(options.system_hostfile2, &st) == 0 ||
855            stat(options.user_hostfile2, &st) == 0) {
856                if (check_host_key(host, hostaddr, host_key, /*readonly*/ 1,
857                    options.user_hostfile2, options.system_hostfile2) == 0)
858                        return 0;
859        }
860        return check_host_key(host, hostaddr, host_key, /*readonly*/ 0,
861            options.user_hostfile, options.system_hostfile);
862}
863
864/*
865 * Starts a dialog with the server, and authenticates the current user on the
866 * server.  This does not need any extra privileges.  The basic connection
867 * to the server must already have been established before this is called.
868 * If login fails, this function prints an error and never returns.
869 * This function does not require super-user privileges.
870 */
871void
872ssh_login(Key **keys, int nkeys, const char *orighost,
873    struct sockaddr *hostaddr, struct passwd *pw)
874{
875        char *host, *cp;
876        char *server_user, *local_user;
877
878        local_user = xstrdup(pw->pw_name);
879        server_user = options.user ? options.user : local_user;
880
881        /* Convert the user-supplied hostname into all lowercase. */
882        host = xstrdup(orighost);
883        for (cp = host; *cp; cp++)
884                if (isupper(*cp))
885                        *cp = tolower(*cp);
886
887        /* Exchange protocol version identification strings with the server. */
888        ssh_exchange_identification();
889
890        /* Put the connection into non-blocking mode. */
891        packet_set_nonblocking();
892
893        /* key exchange */
894        /* authenticate user */
895        if (compat20) {
896                ssh_kex2(host, hostaddr);
897                ssh_userauth2(local_user, server_user, host, keys, nkeys);
898        } else {
899                ssh_kex(host, hostaddr);
900                ssh_userauth1(local_user, server_user, host, keys, nkeys);
901        }
902}
903
904void
905ssh_put_password(char *password)
906{
907        int size;
908        char *padded;
909
910        if (datafellows & SSH_BUG_PASSWORDPAD) {
911                packet_put_cstring(password);
912                return;
913        }
914        size = roundup(strlen(password) + 1, 32);
915        padded = xmalloc(size);
916        memset(padded, 0, size);
917        strlcpy(padded, password, size);
918        packet_put_string(padded, size);
919        memset(padded, 0, size);
920        xfree(padded);
921}
Note: See TracBrowser for help on using the repository browser.