1 /* $OpenBSD: sshconnect.c,v 1.366 2024/01/11 01:45:36 djm Exp $ */
3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
6 * Code to connect to a remote host, and to perform the client side of the
7 * login (authentication) dialog.
9 * As far as I am concerned, the code I have written for this software
10 * can be used freely for any purpose. Any derived versions of this
11 * software must be clearly marked as such, and if the derived work is
12 * incompatible with the protocol description in the RFC file, it must be
13 * called by a name other than "ssh" or "Secure Shell".
18 #include <sys/types.h>
21 #include <sys/socket.h>
22 #ifdef HAVE_SYS_TIME_H
23 # include <sys/time.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
58 #include "sshconnect.h"
64 #include "monitor_fdpass.h"
72 struct sshkey
*previous_host_key
= NULL
;
74 static int matching_host_key_dns
= 0;
76 static pid_t proxy_command_pid
= 0;
79 extern int debug_flag
;
80 extern Options options
;
81 extern char *__progname
;
83 static int show_other_keys(struct hostkeys
*, struct sshkey
*);
84 static void warn_changed_key(struct sshkey
*);
86 /* Expand a proxy command */
88 expand_proxy_command(const char *proxy_command
, const char *user
,
89 const char *host
, const char *host_arg
, int port
)
91 char *tmp
, *ret
, strport
[NI_MAXSERV
];
92 const char *keyalias
= options
.host_key_alias
?
93 options
.host_key_alias
: host_arg
;
95 snprintf(strport
, sizeof strport
, "%d", port
);
96 xasprintf(&tmp
, "exec %s", proxy_command
);
97 ret
= percent_expand(tmp
,
109 * Connect to the given ssh server using a proxy command that passes a
110 * a connected fd back to us.
113 ssh_proxy_fdpass_connect(struct ssh
*ssh
, const char *host
,
114 const char *host_arg
, u_short port
, const char *proxy_command
)
116 char *command_string
;
121 if ((shell
= getenv("SHELL")) == NULL
)
122 shell
= _PATH_BSHELL
;
124 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, sp
) == -1)
125 fatal("Could not create socketpair to communicate with "
126 "proxy dialer: %.100s", strerror(errno
));
128 command_string
= expand_proxy_command(proxy_command
, options
.user
,
129 host
, host_arg
, port
);
130 debug("Executing proxy dialer command: %.500s", command_string
);
132 /* Fork and execute the proxy command. */
133 if ((pid
= fork()) == 0) {
137 /* Redirect stdin and stdout. */
139 if (dup2(sp
[0], 0) == -1)
140 perror("dup2 stdin");
143 if (dup2(sp
[0], 1) == -1)
144 perror("dup2 stdout");
150 * Stderr is left for non-ControlPersist connections is so
151 * error messages may be printed on the user's terminal.
153 if (!debug_flag
&& options
.control_path
!= NULL
&&
154 options
.control_persist
&& stdfd_devnull(0, 0, 1) == -1)
155 error_f("stdfd_devnull failed");
159 argv
[2] = command_string
;
163 * Execute the proxy command.
164 * Note that we gave up any extra privileges above.
166 execv(argv
[0], argv
);
172 fatal("fork failed: %.100s", strerror(errno
));
174 free(command_string
);
176 if ((sock
= mm_receive_fd(sp
[1])) == -1)
177 fatal("proxy dialer did not pass back a connection");
180 while (waitpid(pid
, NULL
, 0) == -1)
182 fatal("Couldn't wait for child: %s", strerror(errno
));
184 /* Set the connection file descriptors. */
185 if (ssh_packet_set_connection(ssh
, sock
, sock
) == NULL
)
186 return -1; /* ssh_packet_set_connection logs error */
192 * Connect to the given ssh server using a proxy command.
195 ssh_proxy_connect(struct ssh
*ssh
, const char *host
, const char *host_arg
,
196 u_short port
, const char *proxy_command
)
198 char *command_string
;
203 if ((shell
= getenv("SHELL")) == NULL
|| *shell
== '\0')
204 shell
= _PATH_BSHELL
;
206 /* Create pipes for communicating with the proxy. */
207 if (pipe(pin
) == -1 || pipe(pout
) == -1)
208 fatal("Could not create pipes to communicate with the proxy: %.100s",
211 command_string
= expand_proxy_command(proxy_command
, options
.user
,
212 host
, host_arg
, port
);
213 debug("Executing proxy command: %.500s", command_string
);
215 /* Fork and execute the proxy command. */
216 if ((pid
= fork()) == 0) {
219 /* Redirect stdin and stdout. */
222 if (dup2(pin
[0], 0) == -1)
223 perror("dup2 stdin");
227 if (dup2(pout
[1], 1) == -1)
228 perror("dup2 stdout");
229 /* Cannot be 1 because pin allocated two descriptors. */
233 * Stderr is left for non-ControlPersist connections is so
234 * error messages may be printed on the user's terminal.
236 if (!debug_flag
&& options
.control_path
!= NULL
&&
237 options
.control_persist
&& stdfd_devnull(0, 0, 1) == -1)
238 error_f("stdfd_devnull failed");
242 argv
[2] = command_string
;
246 * Execute the proxy command. Note that we gave up any
247 * extra privileges above.
249 ssh_signal(SIGPIPE
, SIG_DFL
);
250 execv(argv
[0], argv
);
256 fatal("fork failed: %.100s", strerror(errno
));
258 proxy_command_pid
= pid
; /* save pid to clean up later */
260 /* Close child side of the descriptors. */
264 /* Free the command name. */
265 free(command_string
);
267 /* Set the connection file descriptors. */
268 if (ssh_packet_set_connection(ssh
, pout
[0], pin
[1]) == NULL
)
269 return -1; /* ssh_packet_set_connection logs error */
275 ssh_kill_proxy_command(void)
278 * Send SIGHUP to proxy command if used. We don't wait() in
279 * case it hangs and instead rely on init to reap the child
281 if (proxy_command_pid
> 1)
282 kill(proxy_command_pid
, SIGHUP
);
285 #ifdef HAVE_IFADDRS_H
287 * Search a interface address list (returned from getifaddrs(3)) for an
288 * address that matches the desired address family on the specified interface.
289 * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure.
292 check_ifaddrs(const char *ifname
, int af
, const struct ifaddrs
*ifaddrs
,
293 struct sockaddr_storage
*resultp
, socklen_t
*rlenp
)
295 struct sockaddr_in6
*sa6
;
296 struct sockaddr_in
*sa
;
297 struct in6_addr
*v6addr
;
298 const struct ifaddrs
*ifa
;
302 * Prefer addresses that are not loopback or linklocal, but use them
303 * if nothing else matches.
305 for (allow_local
= 0; allow_local
< 2; allow_local
++) {
306 for (ifa
= ifaddrs
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
307 if (ifa
->ifa_addr
== NULL
|| ifa
->ifa_name
== NULL
||
308 (ifa
->ifa_flags
& IFF_UP
) == 0 ||
309 ifa
->ifa_addr
->sa_family
!= af
||
310 strcmp(ifa
->ifa_name
, options
.bind_interface
) != 0)
312 switch (ifa
->ifa_addr
->sa_family
) {
314 sa
= (struct sockaddr_in
*)ifa
->ifa_addr
;
315 if (!allow_local
&& sa
->sin_addr
.s_addr
==
316 htonl(INADDR_LOOPBACK
))
318 if (*rlenp
< sizeof(struct sockaddr_in
)) {
319 error_f("v4 addr doesn't fit");
322 *rlenp
= sizeof(struct sockaddr_in
);
323 memcpy(resultp
, sa
, *rlenp
);
326 sa6
= (struct sockaddr_in6
*)ifa
->ifa_addr
;
327 v6addr
= &sa6
->sin6_addr
;
329 (IN6_IS_ADDR_LINKLOCAL(v6addr
) ||
330 IN6_IS_ADDR_LOOPBACK(v6addr
)))
332 if (*rlenp
< sizeof(struct sockaddr_in6
)) {
333 error_f("v6 addr doesn't fit");
336 *rlenp
= sizeof(struct sockaddr_in6
);
337 memcpy(resultp
, sa6
, *rlenp
);
347 * Creates a socket for use as the ssh connection.
350 ssh_create_socket(struct addrinfo
*ai
)
353 struct sockaddr_storage bindaddr
;
354 socklen_t bindaddrlen
= 0;
355 struct addrinfo hints
, *res
= NULL
;
356 #ifdef HAVE_IFADDRS_H
357 struct ifaddrs
*ifaddrs
= NULL
;
359 char ntop
[NI_MAXHOST
];
361 sock
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
363 error("socket: %s", strerror(errno
));
366 (void)fcntl(sock
, F_SETFD
, FD_CLOEXEC
);
368 /* Use interactive QOS (if specified) until authentication completed */
369 if (options
.ip_qos_interactive
!= INT_MAX
)
370 set_sock_tos(sock
, options
.ip_qos_interactive
);
372 /* Bind the socket to an alternative local IP address */
373 if (options
.bind_address
== NULL
&& options
.bind_interface
== NULL
)
376 if (options
.bind_address
!= NULL
) {
377 memset(&hints
, 0, sizeof(hints
));
378 hints
.ai_family
= ai
->ai_family
;
379 hints
.ai_socktype
= ai
->ai_socktype
;
380 hints
.ai_protocol
= ai
->ai_protocol
;
381 hints
.ai_flags
= AI_PASSIVE
;
382 if ((r
= getaddrinfo(options
.bind_address
, NULL
,
383 &hints
, &res
)) != 0) {
384 error("getaddrinfo: %s: %s", options
.bind_address
,
385 ssh_gai_strerror(r
));
389 error("getaddrinfo: no addrs");
392 memcpy(&bindaddr
, res
->ai_addr
, res
->ai_addrlen
);
393 bindaddrlen
= res
->ai_addrlen
;
394 } else if (options
.bind_interface
!= NULL
) {
395 #ifdef HAVE_IFADDRS_H
396 if ((r
= getifaddrs(&ifaddrs
)) != 0) {
397 error("getifaddrs: %s: %s", options
.bind_interface
,
401 bindaddrlen
= sizeof(bindaddr
);
402 if (check_ifaddrs(options
.bind_interface
, ai
->ai_family
,
403 ifaddrs
, &bindaddr
, &bindaddrlen
) != 0) {
404 logit("getifaddrs: %s: no suitable addresses",
405 options
.bind_interface
);
409 error("BindInterface not supported on this platform.");
412 if ((r
= getnameinfo((struct sockaddr
*)&bindaddr
, bindaddrlen
,
413 ntop
, sizeof(ntop
), NULL
, 0, NI_NUMERICHOST
)) != 0) {
414 error_f("getnameinfo failed: %s", ssh_gai_strerror(r
));
417 if (bind(sock
, (struct sockaddr
*)&bindaddr
, bindaddrlen
) != 0) {
418 error("bind %s: %s", ntop
, strerror(errno
));
421 debug_f("bound to %s", ntop
);
430 #ifdef HAVE_IFADDRS_H
432 freeifaddrs(ifaddrs
);
438 * Opens a TCP/IP connection to the remote server on the given host.
439 * The address of the remote host will be returned in hostaddr.
440 * If port is 0, the default port will be used.
441 * Connection_attempts specifies the maximum number of tries (one per
442 * second). If proxy_command is non-NULL, it specifies the command (with %h
443 * and %p substituted for host and port, respectively) to use to contact
447 ssh_connect_direct(struct ssh
*ssh
, const char *host
, struct addrinfo
*aitop
,
448 struct sockaddr_storage
*hostaddr
, u_short port
, int connection_attempts
,
449 int *timeout_ms
, int want_keepalive
)
451 int on
= 1, saved_timeout_ms
= *timeout_ms
;
452 int oerrno
, sock
= -1, attempt
;
453 char ntop
[NI_MAXHOST
], strport
[NI_MAXSERV
];
456 debug3_f("entering");
457 memset(ntop
, 0, sizeof(ntop
));
458 memset(strport
, 0, sizeof(strport
));
460 for (attempt
= 0; attempt
< connection_attempts
; attempt
++) {
462 /* Sleep a moment before retrying. */
464 debug("Trying again...");
467 * Loop through addresses for this host, and try each one in
468 * sequence until the connection succeeds.
470 for (ai
= aitop
; ai
; ai
= ai
->ai_next
) {
471 if (ai
->ai_family
!= AF_INET
&&
472 ai
->ai_family
!= AF_INET6
) {
473 errno
= EAFNOSUPPORT
;
476 if (getnameinfo(ai
->ai_addr
, ai
->ai_addrlen
,
477 ntop
, sizeof(ntop
), strport
, sizeof(strport
),
478 NI_NUMERICHOST
|NI_NUMERICSERV
) != 0) {
480 error_f("getnameinfo failed");
484 if (options
.address_family
!= AF_UNSPEC
&&
485 ai
->ai_family
!= options
.address_family
) {
486 debug2_f("skipping address [%s]:%s: "
487 "wrong address family", ntop
, strport
);
488 errno
= EAFNOSUPPORT
;
492 debug("Connecting to %.200s [%.100s] port %s.",
493 host
, ntop
, strport
);
495 /* Create a socket for connecting. */
496 sock
= ssh_create_socket(ai
);
498 /* Any error is already output */
503 *timeout_ms
= saved_timeout_ms
;
504 if (timeout_connect(sock
, ai
->ai_addr
, ai
->ai_addrlen
,
506 /* Successful connection. */
507 memcpy(hostaddr
, ai
->ai_addr
, ai
->ai_addrlen
);
511 debug("connect to address %s port %s: %s",
512 ntop
, strport
, strerror(errno
));
519 break; /* Successful connection. */
522 /* Return failure if we didn't get a successful connection. */
524 error("ssh: connect to host %s port %s: %s",
525 host
, strport
, errno
== 0 ? "failure" : strerror(errno
));
529 debug("Connection established.");
531 /* Set SO_KEEPALIVE if requested. */
532 if (want_keepalive
&&
533 setsockopt(sock
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&on
,
535 error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno
));
537 /* Set the connection. */
538 if (ssh_packet_set_connection(ssh
, sock
, sock
) == NULL
)
539 return -1; /* ssh_packet_set_connection logs error */
545 ssh_connect(struct ssh
*ssh
, const char *host
, const char *host_arg
,
546 struct addrinfo
*addrs
, struct sockaddr_storage
*hostaddr
, u_short port
,
547 int connection_attempts
, int *timeout_ms
, int want_keepalive
)
551 if (options
.proxy_command
== NULL
) {
552 return ssh_connect_direct(ssh
, host
, addrs
, hostaddr
, port
,
553 connection_attempts
, timeout_ms
, want_keepalive
);
554 } else if (strcmp(options
.proxy_command
, "-") == 0) {
555 if ((in
= dup(STDIN_FILENO
)) == -1 ||
556 (out
= dup(STDOUT_FILENO
)) == -1) {
559 error_f("dup() in/out failed");
560 return -1; /* ssh_packet_set_connection logs error */
562 if ((ssh_packet_set_connection(ssh
, in
, out
)) == NULL
)
563 return -1; /* ssh_packet_set_connection logs error */
565 } else if (options
.proxy_use_fdpass
) {
566 return ssh_proxy_fdpass_connect(ssh
, host
, host_arg
, port
,
567 options
.proxy_command
);
569 return ssh_proxy_connect(ssh
, host
, host_arg
, port
,
570 options
.proxy_command
);
573 /* defaults to 'no' */
575 confirm(const char *prompt
, const char *fingerprint
)
577 const char *msg
, *again
= "Please type 'yes' or 'no': ";
578 const char *again_fp
= "Please type 'yes', 'no' or the fingerprint: ";
582 if (options
.batch_mode
)
584 for (msg
= prompt
;;msg
= fingerprint
? again_fp
: again
) {
585 cp
= p
= read_passphrase(msg
, RP_ECHO
);
588 p
+= strspn(p
, " \t"); /* skip leading whitespace */
589 p
[strcspn(p
, " \t\n")] = '\0'; /* remove trailing whitespace */
590 if (p
[0] == '\0' || strcasecmp(p
, "no") == 0)
592 else if (strcasecmp(p
, "yes") == 0 || (fingerprint
!= NULL
&&
593 strcmp(p
, fingerprint
) == 0))
602 sockaddr_is_local(struct sockaddr
*hostaddr
)
604 switch (hostaddr
->sa_family
) {
606 return (ntohl(((struct sockaddr_in
*)hostaddr
)->
607 sin_addr
.s_addr
) >> 24) == IN_LOOPBACKNET
;
609 return IN6_IS_ADDR_LOOPBACK(
610 &(((struct sockaddr_in6
*)hostaddr
)->sin6_addr
));
617 * Prepare the hostname and ip address strings that are used to lookup
618 * host keys in known_hosts files. These may have a port number appended.
621 get_hostfile_hostname_ipaddr(char *hostname
, struct sockaddr
*hostaddr
,
622 u_short port
, char **hostfile_hostname
, char **hostfile_ipaddr
)
624 char ntop
[NI_MAXHOST
];
627 switch (hostaddr
== NULL
? -1 : hostaddr
->sa_family
) {
632 addrlen
= sizeof(struct sockaddr_in
);
635 addrlen
= sizeof(struct sockaddr_in6
);
638 addrlen
= sizeof(struct sockaddr
);
643 * We don't have the remote ip-address for connections
644 * using a proxy command
646 if (hostfile_ipaddr
!= NULL
) {
647 if (options
.proxy_command
== NULL
) {
648 if (getnameinfo(hostaddr
, addrlen
,
649 ntop
, sizeof(ntop
), NULL
, 0, NI_NUMERICHOST
) != 0)
650 fatal_f("getnameinfo failed");
651 *hostfile_ipaddr
= put_host_port(ntop
, port
);
653 *hostfile_ipaddr
= xstrdup("<no hostip for proxy "
659 * Allow the user to record the key under a different name or
660 * differentiate a non-standard port. This is useful for ssh
661 * tunneling over forwarded connections or if you run multiple
662 * sshd's on different ports on the same machine.
664 if (hostfile_hostname
!= NULL
) {
665 if (options
.host_key_alias
!= NULL
) {
666 *hostfile_hostname
= xstrdup(options
.host_key_alias
);
667 debug("using hostkeyalias: %s", *hostfile_hostname
);
669 *hostfile_hostname
= put_host_port(hostname
, port
);
674 /* returns non-zero if path appears in hostfiles, or 0 if not. */
676 path_in_hostfiles(const char *path
, char **hostfiles
, u_int num_hostfiles
)
680 for (i
= 0; i
< num_hostfiles
; i
++) {
681 if (strcmp(path
, hostfiles
[i
]) == 0)
687 struct find_by_key_ctx
{
688 const char *host
, *ip
;
689 const struct sshkey
*key
;
694 /* Try to replace home directory prefix (per $HOME) with a ~/ sequence */
696 try_tilde_unexpand(const char *path
)
698 char *home
, *ret
= NULL
;
702 return xstrdup(path
);
703 if ((home
= getenv("HOME")) == NULL
|| (l
= strlen(home
)) == 0)
704 return xstrdup(path
);
705 if (strncmp(path
, home
, l
) != 0)
706 return xstrdup(path
);
708 * ensure we have matched on a path boundary: either the $HOME that
709 * we just compared ends with a '/' or the next character of the path
712 if (home
[l
- 1] != '/' && path
[l
] != '/')
713 return xstrdup(path
);
716 xasprintf(&ret
, "~/%s", path
+ l
);
721 hostkeys_find_by_key_cb(struct hostkey_foreach_line
*l
, void *_ctx
)
723 struct find_by_key_ctx
*ctx
= (struct find_by_key_ctx
*)_ctx
;
726 /* we are looking for keys with names that *do not* match */
727 if ((l
->match
& HKF_MATCH_HOST
) != 0)
729 /* not interested in marker lines */
730 if (l
->marker
!= MRK_NONE
)
732 /* we are only interested in exact key matches */
733 if (l
->key
== NULL
|| !sshkey_equal(ctx
->key
, l
->key
))
735 path
= try_tilde_unexpand(l
->path
);
736 debug_f("found matching key in %s:%lu", path
, l
->linenum
);
737 ctx
->names
= xrecallocarray(ctx
->names
,
738 ctx
->nnames
, ctx
->nnames
+ 1, sizeof(*ctx
->names
));
739 xasprintf(&ctx
->names
[ctx
->nnames
], "%s:%lu: %s", path
, l
->linenum
,
740 strncmp(l
->hosts
, HASH_MAGIC
, strlen(HASH_MAGIC
)) == 0 ?
741 "[hashed name]" : l
->hosts
);
748 hostkeys_find_by_key_hostfile(const char *file
, const char *which
,
749 struct find_by_key_ctx
*ctx
)
753 debug3_f("trying %s hostfile \"%s\"", which
, file
);
754 if ((r
= hostkeys_foreach(file
, hostkeys_find_by_key_cb
, ctx
,
755 ctx
->host
, ctx
->ip
, HKF_WANT_PARSE_KEY
, 0)) != 0) {
756 if (r
== SSH_ERR_SYSTEM_ERROR
&& errno
== ENOENT
) {
757 debug_f("hostkeys file %s does not exist", file
);
760 error_fr(r
, "hostkeys_foreach failed for %s", file
);
767 * Find 'key' in known hosts file(s) that do not match host/ip.
768 * Used to display also-known-as information for previously-unseen hostkeys.
771 hostkeys_find_by_key(const char *host
, const char *ip
, const struct sshkey
*key
,
772 char **user_hostfiles
, u_int num_user_hostfiles
,
773 char **system_hostfiles
, u_int num_system_hostfiles
,
774 char ***names
, u_int
*nnames
)
776 struct find_by_key_ctx ctx
= {0, 0, 0, 0, 0};
782 if (key
== NULL
|| sshkey_is_cert(key
))
789 for (i
= 0; i
< num_user_hostfiles
; i
++) {
790 if (hostkeys_find_by_key_hostfile(user_hostfiles
[i
],
794 for (i
= 0; i
< num_system_hostfiles
; i
++) {
795 if (hostkeys_find_by_key_hostfile(system_hostfiles
[i
],
796 "system", &ctx
) != 0)
801 *nnames
= ctx
.nnames
;
806 for (i
= 0; i
< ctx
.nnames
; i
++)
811 #define MAX_OTHER_NAMES 8 /* Maximum number of names to list */
813 other_hostkeys_message(const char *host
, const char *ip
,
814 const struct sshkey
*key
,
815 char **user_hostfiles
, u_int num_user_hostfiles
,
816 char **system_hostfiles
, u_int num_system_hostfiles
)
818 char *ret
= NULL
, **othernames
= NULL
;
819 u_int i
, n
, num_othernames
= 0;
821 hostkeys_find_by_key(host
, ip
, key
,
822 user_hostfiles
, num_user_hostfiles
,
823 system_hostfiles
, num_system_hostfiles
,
824 &othernames
, &num_othernames
);
825 if (num_othernames
== 0)
826 return xstrdup("This key is not known by any other names.");
828 xasprintf(&ret
, "This host key is known by the following other "
832 if (n
> MAX_OTHER_NAMES
)
834 for (i
= 0; i
< n
; i
++) {
835 xextendf(&ret
, "\n", " %s", othernames
[i
]);
837 if (n
< num_othernames
) {
838 xextendf(&ret
, "\n", " (%d additional names omitted)",
841 for (i
= 0; i
< num_othernames
; i
++)
848 load_hostkeys_command(struct hostkeys
*hostkeys
, const char *command_template
,
849 const char *invocation
, const struct ssh_conn_info
*cinfo
,
850 const struct sshkey
*host_key
, const char *hostfile_hostname
)
853 char *key_fp
= NULL
, *keytext
= NULL
, *tmp
;
854 char *command
= NULL
, *tag
= NULL
, **av
= NULL
;
857 void (*osigchld
)(int);
859 xasprintf(&tag
, "KnownHostsCommand-%s", invocation
);
861 if (host_key
!= NULL
) {
862 if ((key_fp
= sshkey_fingerprint(host_key
,
863 options
.fingerprint_hash
, SSH_FP_DEFAULT
)) == NULL
)
864 fatal_f("sshkey_fingerprint failed");
865 if ((r
= sshkey_to_base64(host_key
, &keytext
)) != 0)
866 fatal_fr(r
, "sshkey_to_base64 failed");
869 * NB. all returns later this function should go via "out" to
870 * ensure the original SIGCHLD handler is restored properly.
872 osigchld
= ssh_signal(SIGCHLD
, SIG_DFL
);
874 /* Turn the command into an argument vector */
875 if (argv_split(command_template
, &ac
, &av
, 0) != 0) {
876 error("%s \"%s\" contains invalid quotes", tag
,
881 error("%s \"%s\" yielded no arguments", tag
,
885 for (i
= 1; i
< ac
; i
++) {
886 tmp
= percent_dollar_expand(av
[i
],
887 DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo
),
888 "H", hostfile_hostname
,
890 "t", host_key
== NULL
? "NONE" : sshkey_ssh_name(host_key
),
891 "f", key_fp
== NULL
? "NONE" : key_fp
,
892 "K", keytext
== NULL
? "NONE" : keytext
,
895 fatal_f("percent_expand failed");
899 /* Prepare a printable command for logs, etc. */
900 command
= argv_assemble(ac
, av
);
902 if ((pid
= subprocess(tag
, command
, ac
, av
, &f
,
903 SSH_SUBPROCESS_STDOUT_CAPTURE
|SSH_SUBPROCESS_UNSAFE_PATH
|
904 SSH_SUBPROCESS_PRESERVE_ENV
, NULL
, NULL
, NULL
)) == 0)
907 load_hostkeys_file(hostkeys
, hostfile_hostname
, tag
, f
, 1);
909 if (exited_cleanly(pid
, tag
, command
, 0) != 0)
910 fatal("KnownHostsCommand failed");
915 ssh_signal(SIGCHLD
, osigchld
);
916 for (i
= 0; i
< ac
; i
++)
926 * check whether the supplied host key is valid, return -1 if the key
927 * is not valid. user_hostfile[0] will not be updated if 'readonly' is true.
933 check_host_key(char *hostname
, const struct ssh_conn_info
*cinfo
,
934 struct sockaddr
*hostaddr
, u_short port
,
935 struct sshkey
*host_key
, int readonly
, int clobber_port
,
936 char **user_hostfiles
, u_int num_user_hostfiles
,
937 char **system_hostfiles
, u_int num_system_hostfiles
,
938 const char *hostfile_command
)
940 HostStatus host_status
= -1, ip_status
= -1;
941 struct sshkey
*raw_key
= NULL
;
942 char *ip
= NULL
, *host
= NULL
;
943 char hostline
[1000], *hostp
, *fp
, *ra
;
945 const char *type
, *fail_reason
= NULL
;
946 const struct hostkey_entry
*host_found
= NULL
, *ip_found
= NULL
;
947 int len
, cancelled_forwarding
= 0, confirmed
;
948 int local
= sockaddr_is_local(hostaddr
);
949 int r
, want_cert
= sshkey_is_cert(host_key
), host_ip_differ
= 0;
950 int hostkey_trusted
= 0; /* Known or explicitly accepted by user */
951 struct hostkeys
*host_hostkeys
, *ip_hostkeys
;
955 * Force accepting of the host key for loopback/localhost. The
956 * problem is that if the home directory is NFS-mounted to multiple
957 * machines, localhost will refer to a different machine in each of
958 * them, and the user will get bogus HOST_CHANGED warnings. This
959 * essentially disables host authentication for localhost; however,
960 * this is probably not a real problem.
962 if (options
.no_host_authentication_for_localhost
== 1 && local
&&
963 options
.host_key_alias
== NULL
) {
964 debug("Forcing accepting of host key for "
965 "loopback/localhost.");
966 options
.update_hostkeys
= 0;
971 * Don't ever try to write an invalid name to a known hosts file.
972 * Note: do this before get_hostfile_hostname_ipaddr() to catch
973 * '[' or ']' in the name before they are added.
975 if (strcspn(hostname
, "@?*#[]|'\'\"\\") != strlen(hostname
)) {
976 debug_f("invalid hostname \"%s\"; will not record: %s",
977 hostname
, fail_reason
);
982 * Prepare the hostname and address strings used for hostkey lookup.
983 * In some cases, these will have a port number appended.
985 get_hostfile_hostname_ipaddr(hostname
, hostaddr
,
986 clobber_port
? 0 : port
, &host
, &ip
);
989 * Turn off check_host_ip if the connection is to localhost, via proxy
990 * command or if we don't have a hostname to compare with
992 if (options
.check_host_ip
&& (local
||
993 strcmp(hostname
, ip
) == 0 || options
.proxy_command
!= NULL
))
994 options
.check_host_ip
= 0;
996 host_hostkeys
= init_hostkeys();
997 for (i
= 0; i
< num_user_hostfiles
; i
++)
998 load_hostkeys(host_hostkeys
, host
, user_hostfiles
[i
], 0);
999 for (i
= 0; i
< num_system_hostfiles
; i
++)
1000 load_hostkeys(host_hostkeys
, host
, system_hostfiles
[i
], 0);
1001 if (hostfile_command
!= NULL
&& !clobber_port
) {
1002 load_hostkeys_command(host_hostkeys
, hostfile_command
,
1003 "HOSTNAME", cinfo
, host_key
, host
);
1007 if (!want_cert
&& options
.check_host_ip
) {
1008 ip_hostkeys
= init_hostkeys();
1009 for (i
= 0; i
< num_user_hostfiles
; i
++)
1010 load_hostkeys(ip_hostkeys
, ip
, user_hostfiles
[i
], 0);
1011 for (i
= 0; i
< num_system_hostfiles
; i
++)
1012 load_hostkeys(ip_hostkeys
, ip
, system_hostfiles
[i
], 0);
1013 if (hostfile_command
!= NULL
&& !clobber_port
) {
1014 load_hostkeys_command(ip_hostkeys
, hostfile_command
,
1015 "ADDRESS", cinfo
, host_key
, ip
);
1020 /* Reload these as they may have changed on cert->key downgrade */
1021 want_cert
= sshkey_is_cert(host_key
);
1022 type
= sshkey_type(host_key
);
1025 * Check if the host key is present in the user's list of known
1026 * hosts or in the systemwide list.
1028 host_status
= check_key_in_hostkeys(host_hostkeys
, host_key
,
1032 * If there are no hostfiles, or if the hostkey was found via
1033 * KnownHostsCommand, then don't try to touch the disk.
1035 if (!readonly
&& (num_user_hostfiles
== 0 ||
1036 (host_found
!= NULL
&& host_found
->note
!= 0)))
1040 * Also perform check for the ip address, skip the check if we are
1041 * localhost, looking for a certificate, or the hostname was an ip
1042 * address to begin with.
1044 if (!want_cert
&& ip_hostkeys
!= NULL
) {
1045 ip_status
= check_key_in_hostkeys(ip_hostkeys
, host_key
,
1047 if (host_status
== HOST_CHANGED
&&
1048 (ip_status
!= HOST_CHANGED
||
1049 (ip_found
!= NULL
&&
1050 !sshkey_equal(ip_found
->key
, host_found
->key
))))
1053 ip_status
= host_status
;
1055 switch (host_status
) {
1057 /* The host is known and the key matches. */
1058 debug("Host '%.200s' is known and matches the %s host %s.",
1059 host
, type
, want_cert
? "certificate" : "key");
1060 debug("Found %s in %s:%lu", want_cert
? "CA key" : "key",
1061 host_found
->file
, host_found
->line
);
1063 if (sshkey_cert_check_host(host_key
,
1064 options
.host_key_alias
== NULL
?
1065 hostname
: options
.host_key_alias
, 0,
1066 options
.ca_sign_algorithms
, &fail_reason
) != 0) {
1067 error("%s", fail_reason
);
1071 * Do not attempt hostkey update if a certificate was
1072 * successfully matched.
1074 if (options
.update_hostkeys
!= 0) {
1075 options
.update_hostkeys
= 0;
1076 debug3_f("certificate host key in use; "
1077 "disabling UpdateHostkeys");
1080 /* Turn off UpdateHostkeys if key was in system known_hosts */
1081 if (options
.update_hostkeys
!= 0 &&
1082 (path_in_hostfiles(host_found
->file
,
1083 system_hostfiles
, num_system_hostfiles
) ||
1084 (ip_status
== HOST_OK
&& ip_found
!= NULL
&&
1085 path_in_hostfiles(ip_found
->file
,
1086 system_hostfiles
, num_system_hostfiles
)))) {
1087 options
.update_hostkeys
= 0;
1088 debug3_f("host key found in GlobalKnownHostsFile; "
1089 "disabling UpdateHostkeys");
1091 if (options
.update_hostkeys
!= 0 && host_found
->note
) {
1092 options
.update_hostkeys
= 0;
1093 debug3_f("host key found via KnownHostsCommand; "
1094 "disabling UpdateHostkeys");
1096 if (options
.check_host_ip
&& ip_status
== HOST_NEW
) {
1097 if (readonly
|| want_cert
)
1098 logit("%s host key for IP address "
1099 "'%.128s' not in list of known hosts.",
1101 else if (!add_host_to_hostfile(user_hostfiles
[0], ip
,
1102 host_key
, options
.hash_known_hosts
))
1103 logit("Failed to add the %s host key for IP "
1104 "address '%.128s' to the list of known "
1105 "hosts (%.500s).", type
, ip
,
1108 logit("Warning: Permanently added the %s host "
1109 "key for IP address '%.128s' to the list "
1110 "of known hosts.", type
, ip
);
1111 } else if (options
.visual_host_key
) {
1112 fp
= sshkey_fingerprint(host_key
,
1113 options
.fingerprint_hash
, SSH_FP_DEFAULT
);
1114 ra
= sshkey_fingerprint(host_key
,
1115 options
.fingerprint_hash
, SSH_FP_RANDOMART
);
1116 if (fp
== NULL
|| ra
== NULL
)
1117 fatal_f("sshkey_fingerprint failed");
1118 logit("Host key fingerprint is %s\n%s", fp
, ra
);
1122 hostkey_trusted
= 1;
1125 if (options
.host_key_alias
== NULL
&& port
!= 0 &&
1126 port
!= SSH_DEFAULT_PORT
&& !clobber_port
) {
1127 debug("checking without port identifier");
1128 if (check_host_key(hostname
, cinfo
, hostaddr
, 0,
1129 host_key
, ROQUIET
, 1,
1130 user_hostfiles
, num_user_hostfiles
,
1131 system_hostfiles
, num_system_hostfiles
,
1132 hostfile_command
) == 0) {
1133 debug("found matching key w/out port");
1137 if (readonly
|| want_cert
)
1139 /* The host is new. */
1140 if (options
.strict_host_key_checking
==
1141 SSH_STRICT_HOSTKEY_YES
) {
1143 * User has requested strict host key checking. We
1144 * will not add the host key automatically. The only
1145 * alternative left is to abort.
1147 error("No %s host key is known for %.200s and you "
1148 "have requested strict checking.", type
, host
);
1150 } else if (options
.strict_host_key_checking
==
1151 SSH_STRICT_HOSTKEY_ASK
) {
1152 char *msg1
= NULL
, *msg2
= NULL
;
1154 xasprintf(&msg1
, "The authenticity of host "
1155 "'%.200s (%s)' can't be established", host
, ip
);
1157 if (show_other_keys(host_hostkeys
, host_key
)) {
1158 xextendf(&msg1
, "\n", "but keys of different "
1159 "type are already known for this host.");
1161 xextendf(&msg1
, "", ".");
1163 fp
= sshkey_fingerprint(host_key
,
1164 options
.fingerprint_hash
, SSH_FP_DEFAULT
);
1165 ra
= sshkey_fingerprint(host_key
,
1166 options
.fingerprint_hash
, SSH_FP_RANDOMART
);
1167 if (fp
== NULL
|| ra
== NULL
)
1168 fatal_f("sshkey_fingerprint failed");
1169 xextendf(&msg1
, "\n", "%s key fingerprint is %s.",
1171 if (options
.visual_host_key
)
1172 xextendf(&msg1
, "\n", "%s", ra
);
1173 if (options
.verify_host_key_dns
) {
1174 xextendf(&msg1
, "\n",
1175 "%s host key fingerprint found in DNS.",
1176 matching_host_key_dns
?
1177 "Matching" : "No matching");
1179 /* msg2 informs for other names matching this key */
1180 if ((msg2
= other_hostkeys_message(host
, ip
, host_key
,
1181 user_hostfiles
, num_user_hostfiles
,
1182 system_hostfiles
, num_system_hostfiles
)) != NULL
)
1183 xextendf(&msg1
, "\n", "%s", msg2
);
1185 xextendf(&msg1
, "\n",
1186 "Are you sure you want to continue connecting "
1187 "(yes/no/[fingerprint])? ");
1189 confirmed
= confirm(msg1
, fp
);
1196 hostkey_trusted
= 1; /* user explicitly confirmed */
1199 * If in "new" or "off" strict mode, add the key automatically
1200 * to the local known_hosts file.
1202 if (options
.check_host_ip
&& ip_status
== HOST_NEW
) {
1203 snprintf(hostline
, sizeof(hostline
), "%s,%s", host
, ip
);
1205 if (options
.hash_known_hosts
) {
1206 /* Add hash of host and IP separately */
1207 r
= add_host_to_hostfile(user_hostfiles
[0],
1208 host
, host_key
, options
.hash_known_hosts
) &&
1209 add_host_to_hostfile(user_hostfiles
[0], ip
,
1210 host_key
, options
.hash_known_hosts
);
1212 /* Add unhashed "host,ip" */
1213 r
= add_host_to_hostfile(user_hostfiles
[0],
1215 options
.hash_known_hosts
);
1218 r
= add_host_to_hostfile(user_hostfiles
[0], host
,
1219 host_key
, options
.hash_known_hosts
);
1224 logit("Failed to add the host to the list of known "
1225 "hosts (%.500s).", user_hostfiles
[0]);
1227 logit("Warning: Permanently added '%.200s' (%s) to the "
1228 "list of known hosts.", hostp
, type
);
1231 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1232 error("@ WARNING: REVOKED HOST KEY DETECTED! @");
1233 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1234 error("The %s host key for %s is marked as revoked.", type
, host
);
1235 error("This could mean that a stolen key is being used to");
1236 error("impersonate this host.");
1239 * If strict host key checking is in use, the user will have
1240 * to edit the key manually and we can only abort.
1242 if (options
.strict_host_key_checking
!=
1243 SSH_STRICT_HOSTKEY_OFF
) {
1244 error("%s host key for %.200s was revoked and you have "
1245 "requested strict checking.", type
, host
);
1248 goto continue_unsafe
;
1253 * This is only a debug() since it is valid to have
1254 * CAs with wildcard DNS matches that don't match
1255 * all hosts that one might visit.
1257 debug("Host certificate authority does not "
1258 "match %s in %s:%lu", CA_MARKER
,
1259 host_found
->file
, host_found
->line
);
1262 if (readonly
== ROQUIET
)
1264 if (options
.check_host_ip
&& host_ip_differ
) {
1266 if (ip_status
== HOST_NEW
)
1267 key_msg
= "is unknown";
1268 else if (ip_status
== HOST_OK
)
1269 key_msg
= "is unchanged";
1271 key_msg
= "has a different value";
1272 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1273 error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @");
1274 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1275 error("The %s host key for %s has changed,", type
, host
);
1276 error("and the key for the corresponding IP address %s", ip
);
1277 error("%s. This could either mean that", key_msg
);
1278 error("DNS SPOOFING is happening or the IP address for the host");
1279 error("and its host key have changed at the same time.");
1280 if (ip_status
!= HOST_NEW
)
1281 error("Offending key for IP in %s:%lu",
1282 ip_found
->file
, ip_found
->line
);
1284 /* The host key has changed. */
1285 warn_changed_key(host_key
);
1286 if (num_user_hostfiles
> 0 || num_system_hostfiles
> 0) {
1287 error("Add correct host key in %.100s to get rid "
1288 "of this message.", num_user_hostfiles
> 0 ?
1289 user_hostfiles
[0] : system_hostfiles
[0]);
1291 error("Offending %s key in %s:%lu",
1292 sshkey_type(host_found
->key
),
1293 host_found
->file
, host_found
->line
);
1296 * If strict host key checking is in use, the user will have
1297 * to edit the key manually and we can only abort.
1299 if (options
.strict_host_key_checking
!=
1300 SSH_STRICT_HOSTKEY_OFF
) {
1301 error("Host key for %.200s has changed and you have "
1302 "requested strict checking.", host
);
1308 * If strict host key checking has not been requested, allow
1309 * the connection but without MITM-able authentication or
1312 if (options
.password_authentication
) {
1313 error("Password authentication is disabled to avoid "
1314 "man-in-the-middle attacks.");
1315 options
.password_authentication
= 0;
1316 cancelled_forwarding
= 1;
1318 if (options
.kbd_interactive_authentication
) {
1319 error("Keyboard-interactive authentication is disabled"
1320 " to avoid man-in-the-middle attacks.");
1321 options
.kbd_interactive_authentication
= 0;
1322 cancelled_forwarding
= 1;
1324 if (options
.forward_agent
) {
1325 error("Agent forwarding is disabled to avoid "
1326 "man-in-the-middle attacks.");
1327 options
.forward_agent
= 0;
1328 cancelled_forwarding
= 1;
1330 if (options
.forward_x11
) {
1331 error("X11 forwarding is disabled to avoid "
1332 "man-in-the-middle attacks.");
1333 options
.forward_x11
= 0;
1334 cancelled_forwarding
= 1;
1336 if (options
.num_local_forwards
> 0 ||
1337 options
.num_remote_forwards
> 0) {
1338 error("Port forwarding is disabled to avoid "
1339 "man-in-the-middle attacks.");
1340 options
.num_local_forwards
=
1341 options
.num_remote_forwards
= 0;
1342 cancelled_forwarding
= 1;
1344 if (options
.tun_open
!= SSH_TUNMODE_NO
) {
1345 error("Tunnel forwarding is disabled to avoid "
1346 "man-in-the-middle attacks.");
1347 options
.tun_open
= SSH_TUNMODE_NO
;
1348 cancelled_forwarding
= 1;
1350 if (options
.update_hostkeys
!= 0) {
1351 error("UpdateHostkeys is disabled because the host "
1352 "key is not trusted.");
1353 options
.update_hostkeys
= 0;
1355 if (options
.exit_on_forward_failure
&& cancelled_forwarding
)
1356 fatal("Error: forwarding disabled due to host key "
1360 * XXX Should permit the user to change to use the new id.
1361 * This could be done by converting the host key to an
1362 * identifying sentence, tell that the host identifies itself
1363 * by that sentence, and ask the user if they wish to
1364 * accept the authentication.
1368 fatal("internal error");
1372 if (options
.check_host_ip
&& host_status
!= HOST_CHANGED
&&
1373 ip_status
== HOST_CHANGED
) {
1374 snprintf(msg
, sizeof(msg
),
1375 "Warning: the %s host key for '%.200s' "
1376 "differs from the key for the IP address '%.128s'"
1377 "\nOffending key for IP in %s:%lu",
1378 type
, host
, ip
, ip_found
->file
, ip_found
->line
);
1379 if (host_status
== HOST_OK
) {
1381 snprintf(msg
+ len
, sizeof(msg
) - len
,
1382 "\nMatching host key in %s:%lu",
1383 host_found
->file
, host_found
->line
);
1385 if (options
.strict_host_key_checking
==
1386 SSH_STRICT_HOSTKEY_ASK
) {
1387 strlcat(msg
, "\nAre you sure you want "
1388 "to continue connecting (yes/no)? ", sizeof(msg
));
1389 if (!confirm(msg
, NULL
))
1391 } else if (options
.strict_host_key_checking
!=
1392 SSH_STRICT_HOSTKEY_OFF
) {
1394 error("Exiting, you have requested strict checking.");
1401 if (!hostkey_trusted
&& options
.update_hostkeys
) {
1402 debug_f("hostkey not known or explicitly trusted: "
1403 "disabling UpdateHostkeys");
1404 options
.update_hostkeys
= 0;
1409 if (host_hostkeys
!= NULL
)
1410 free_hostkeys(host_hostkeys
);
1411 if (ip_hostkeys
!= NULL
)
1412 free_hostkeys(ip_hostkeys
);
1416 if (want_cert
&& host_status
!= HOST_REVOKED
) {
1418 * No matching certificate. Downgrade cert to raw key and
1421 debug("No matching CA found. Retry with plain key");
1422 if ((r
= sshkey_from_private(host_key
, &raw_key
)) != 0)
1423 fatal_fr(r
, "decode key");
1424 if ((r
= sshkey_drop_cert(raw_key
)) != 0)
1425 fatal_r(r
, "Couldn't drop certificate");
1429 sshkey_free(raw_key
);
1432 if (host_hostkeys
!= NULL
)
1433 free_hostkeys(host_hostkeys
);
1434 if (ip_hostkeys
!= NULL
)
1435 free_hostkeys(ip_hostkeys
);
1439 /* returns 0 if key verifies or -1 if key does NOT verify */
1441 verify_host_key(char *host
, struct sockaddr
*hostaddr
, struct sshkey
*host_key
,
1442 const struct ssh_conn_info
*cinfo
)
1445 int r
= -1, flags
= 0;
1446 char valid
[64], *fp
= NULL
, *cafp
= NULL
;
1447 struct sshkey
*plain
= NULL
;
1449 if ((fp
= sshkey_fingerprint(host_key
,
1450 options
.fingerprint_hash
, SSH_FP_DEFAULT
)) == NULL
) {
1451 error_fr(r
, "fingerprint host key");
1456 if (sshkey_is_cert(host_key
)) {
1457 if ((cafp
= sshkey_fingerprint(host_key
->cert
->signature_key
,
1458 options
.fingerprint_hash
, SSH_FP_DEFAULT
)) == NULL
) {
1459 error_fr(r
, "fingerprint CA key");
1463 sshkey_format_cert_validity(host_key
->cert
,
1464 valid
, sizeof(valid
));
1465 debug("Server host certificate: %s %s, serial %llu "
1466 "ID \"%s\" CA %s %s valid %s",
1467 sshkey_ssh_name(host_key
), fp
,
1468 (unsigned long long)host_key
->cert
->serial
,
1469 host_key
->cert
->key_id
,
1470 sshkey_ssh_name(host_key
->cert
->signature_key
), cafp
,
1472 for (i
= 0; i
< host_key
->cert
->nprincipals
; i
++) {
1473 debug2("Server host certificate hostname: %s",
1474 host_key
->cert
->principals
[i
]);
1477 debug("Server host key: %s %s", sshkey_ssh_name(host_key
), fp
);
1480 if (sshkey_equal(previous_host_key
, host_key
)) {
1481 debug2_f("server host key %s %s matches cached key",
1482 sshkey_type(host_key
), fp
);
1487 /* Check in RevokedHostKeys file if specified */
1488 if (options
.revoked_host_keys
!= NULL
) {
1489 r
= sshkey_check_revoked(host_key
, options
.revoked_host_keys
);
1492 break; /* not revoked */
1493 case SSH_ERR_KEY_REVOKED
:
1494 error("Host key %s %s revoked by file %s",
1495 sshkey_type(host_key
), fp
,
1496 options
.revoked_host_keys
);
1500 error_r(r
, "Error checking host key %s %s in "
1501 "revoked keys file %s", sshkey_type(host_key
),
1502 fp
, options
.revoked_host_keys
);
1508 if (options
.verify_host_key_dns
) {
1510 * XXX certs are not yet supported for DNS, so downgrade
1511 * them and try the plain key.
1513 if ((r
= sshkey_from_private(host_key
, &plain
)) != 0)
1515 if (sshkey_is_cert(plain
))
1516 sshkey_drop_cert(plain
);
1517 if (verify_host_key_dns(host
, hostaddr
, plain
, &flags
) == 0) {
1518 if (flags
& DNS_VERIFY_FOUND
) {
1519 if (options
.verify_host_key_dns
== 1 &&
1520 flags
& DNS_VERIFY_MATCH
&&
1521 flags
& DNS_VERIFY_SECURE
) {
1525 if (flags
& DNS_VERIFY_MATCH
) {
1526 matching_host_key_dns
= 1;
1528 warn_changed_key(plain
);
1529 error("Update the SSHFP RR in DNS "
1530 "with the new host key to get rid "
1531 "of this message.");
1536 r
= check_host_key(host
, cinfo
, hostaddr
, options
.port
, host_key
,
1537 RDRW
, 0, options
.user_hostfiles
, options
.num_user_hostfiles
,
1538 options
.system_hostfiles
, options
.num_system_hostfiles
,
1539 options
.known_hosts_command
);
1545 if (r
== 0 && host_key
!= NULL
) {
1546 sshkey_free(previous_host_key
);
1547 r
= sshkey_from_private(host_key
, &previous_host_key
);
1554 * Starts a dialog with the server, and authenticates the current user on the
1555 * server. This does not need any extra privileges. The basic connection
1556 * to the server must already have been established before this is called.
1557 * If login fails, this function prints an error and never returns.
1558 * This function does not require super-user privileges.
1561 ssh_login(struct ssh
*ssh
, Sensitive
*sensitive
, const char *orighost
,
1562 struct sockaddr
*hostaddr
, u_short port
, struct passwd
*pw
, int timeout_ms
,
1563 const struct ssh_conn_info
*cinfo
)
1566 char *server_user
, *local_user
;
1569 local_user
= xstrdup(pw
->pw_name
);
1570 server_user
= options
.user
? options
.user
: local_user
;
1572 /* Convert the user-supplied hostname into all lowercase. */
1573 host
= xstrdup(orighost
);
1576 /* Exchange protocol version identification strings with the server. */
1577 if ((r
= kex_exchange_identification(ssh
, timeout_ms
, NULL
)) != 0)
1578 sshpkt_fatal(ssh
, r
, "banner exchange");
1580 /* Put the connection into non-blocking mode. */
1581 ssh_packet_set_nonblocking(ssh
);
1584 /* authenticate user */
1585 debug("Authenticating to %s:%d as '%s'", host
, port
, server_user
);
1586 ssh_kex2(ssh
, host
, hostaddr
, port
, cinfo
);
1587 ssh_userauth2(ssh
, local_user
, server_user
, host
, sensitive
);
1592 /* print all known host keys for a given host, but skip keys of given type */
1594 show_other_keys(struct hostkeys
*hostkeys
, struct sshkey
*key
)
1608 const struct hostkey_entry
*found
;
1610 for (i
= 0; type
[i
] != -1; i
++) {
1611 if (type
[i
] == key
->type
)
1613 if (!lookup_key_in_hostkeys_by_type(hostkeys
, type
[i
],
1616 fp
= sshkey_fingerprint(found
->key
,
1617 options
.fingerprint_hash
, SSH_FP_DEFAULT
);
1618 ra
= sshkey_fingerprint(found
->key
,
1619 options
.fingerprint_hash
, SSH_FP_RANDOMART
);
1620 if (fp
== NULL
|| ra
== NULL
)
1621 fatal_f("sshkey_fingerprint fail");
1622 logit("WARNING: %s key found for host %s\n"
1624 "%s key fingerprint %s.",
1625 sshkey_type(found
->key
),
1626 found
->host
, found
->file
, found
->line
,
1627 sshkey_type(found
->key
), fp
);
1628 if (options
.visual_host_key
)
1638 warn_changed_key(struct sshkey
*host_key
)
1642 fp
= sshkey_fingerprint(host_key
, options
.fingerprint_hash
,
1645 fatal_f("sshkey_fingerprint fail");
1647 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1648 error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @");
1649 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1650 error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
1651 error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
1652 error("It is also possible that a host key has just been changed.");
1653 error("The fingerprint for the %s key sent by the remote host is\n%s.",
1654 sshkey_type(host_key
), fp
);
1655 error("Please contact your system administrator.");
1661 * Execute a local command
1664 ssh_local_cmd(const char *args
)
1669 void (*osighand
)(int);
1671 if (!options
.permit_local_command
||
1672 args
== NULL
|| !*args
)
1675 if ((shell
= getenv("SHELL")) == NULL
|| *shell
== '\0')
1676 shell
= _PATH_BSHELL
;
1678 osighand
= ssh_signal(SIGCHLD
, SIG_DFL
);
1681 ssh_signal(SIGPIPE
, SIG_DFL
);
1682 debug3("Executing %s -c \"%s\"", shell
, args
);
1683 execl(shell
, shell
, "-c", args
, (char *)NULL
);
1684 error("Couldn't execute %s -c \"%s\": %s",
1685 shell
, args
, strerror(errno
));
1687 } else if (pid
== -1)
1688 fatal("fork failed: %.100s", strerror(errno
));
1689 while (waitpid(pid
, &status
, 0) == -1)
1691 fatal("Couldn't wait for child: %s", strerror(errno
));
1692 ssh_signal(SIGCHLD
, osighand
);
1694 if (!WIFEXITED(status
))
1697 return (WEXITSTATUS(status
));
1701 maybe_add_key_to_agent(const char *authfile
, struct sshkey
*private,
1702 const char *comment
, const char *passphrase
)
1704 int auth_sock
= -1, r
;
1705 const char *skprovider
= NULL
;
1707 if (options
.add_keys_to_agent
== 0)
1710 if ((r
= ssh_get_authentication_socket(&auth_sock
)) != 0) {
1711 debug3("no authentication agent, not adding key");
1715 if (options
.add_keys_to_agent
== 2 &&
1716 !ask_permission("Add key %s (%s) to agent?", authfile
, comment
)) {
1717 debug3("user denied adding this key");
1721 if (sshkey_is_sk(private))
1722 skprovider
= options
.sk_provider
;
1723 if ((r
= ssh_add_identity_constrained(auth_sock
, private,
1724 comment
== NULL
? authfile
: comment
,
1725 options
.add_keys_to_agent_lifespan
,
1726 (options
.add_keys_to_agent
== 3), 0, skprovider
, NULL
, 0)) == 0)
1727 debug("identity added to agent: %s", authfile
);
1729 debug("could not add identity to agent: %s (%d)", authfile
, r
);