Coverage Report

Created: 2026-10-17 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssh/sshconnect.c
Line
Count
Source
1
/* $OpenBSD: sshconnect.c,v 1.386 2026/09/16 00:29:44 djm Exp $ */
2
/*
3
 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4
 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5
 *                    All rights reserved
6
 * Code to connect to a remote host, and to perform the client side of the
7
 * login (authentication) dialog.
8
 *
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".
14
 */
15
16
#include "includes.h"
17
18
#include <sys/types.h>
19
#include <sys/wait.h>
20
#include <sys/socket.h>
21
22
#include <net/if.h>
23
#include <netinet/in.h>
24
#include <arpa/inet.h>
25
26
#include <errno.h>
27
#include <fcntl.h>
28
#include <limits.h>
29
#include <netdb.h>
30
#include <paths.h>
31
#include <pwd.h>
32
#include <poll.h>
33
#include <signal.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <stdarg.h>
37
#include <string.h>
38
#include <unistd.h>
39
#include <ifaddrs.h>
40
41
#include "xmalloc.h"
42
#include "hostfile.h"
43
#include "ssh.h"
44
#include "compat.h"
45
#include "packet.h"
46
#include "sshkey.h"
47
#include "sshconnect.h"
48
#include "log.h"
49
#include "match.h"
50
#include "misc.h"
51
#include "readconf.h"
52
#include "dns.h"
53
#include "monitor_fdpass.h"
54
#include "authfile.h"
55
#include "ssherr.h"
56
#include "authfd.h"
57
#include "kex.h"
58
59
struct sshkey *previous_host_key = NULL;
60
61
static int matching_host_key_dns = 0;
62
63
static pid_t proxy_command_pid = 0;
64
65
/* import */
66
extern int debug_flag;
67
extern Options options;
68
extern char *__progname;
69
70
static int show_other_keys(struct hostkeys *, struct sshkey *);
71
static void warn_changed_key(struct sshkey *);
72
73
void
74
ssh_conn_info_free(struct ssh_conn_info *cinfo)
75
0
{
76
0
  if (cinfo == NULL)
77
0
    return;
78
0
  free(cinfo->conn_hash_hex);
79
0
  free(cinfo->shorthost);
80
0
  free(cinfo->uidstr);
81
0
  free(cinfo->keyalias);
82
0
  free(cinfo->thishost);
83
0
  free(cinfo->host_arg);
84
0
  free(cinfo->portstr);
85
0
  free(cinfo->remhost);
86
0
  free(cinfo->remuser);
87
0
  free(cinfo->homedir);
88
0
  free(cinfo->locuser);
89
0
  free(cinfo->jmphost);
90
0
  freezero(cinfo, sizeof(*cinfo));
91
0
}
92
93
struct ssh_conn_info *
94
ssh_conn_info_dup(const struct ssh_conn_info *cinfo)
95
0
{
96
0
  struct ssh_conn_info *ret;
97
98
0
  if (cinfo == NULL)
99
0
    return NULL;
100
0
  ret = xcalloc(1, sizeof(*ret));
101
0
  ret->conn_hash_hex = xstrdup(cinfo->conn_hash_hex);
102
0
  ret->shorthost = xstrdup(cinfo->shorthost);
103
0
  ret->uidstr = xstrdup(cinfo->uidstr);
104
0
  ret->keyalias = xstrdup(cinfo->keyalias);
105
0
  ret->thishost = xstrdup(cinfo->thishost);
106
0
  ret->host_arg = xstrdup(cinfo->host_arg);
107
0
  ret->portstr = xstrdup(cinfo->portstr);
108
0
  ret->remhost = xstrdup(cinfo->remhost);
109
0
  ret->remuser = xstrdup(cinfo->remuser);
110
0
  ret->homedir = xstrdup(cinfo->homedir);
111
0
  ret->locuser = xstrdup(cinfo->locuser);
112
0
  ret->jmphost = xstrdup(cinfo->jmphost);
113
0
  return ret;
114
0
}
115
116
/* Expand a proxy command */
117
static char *
118
expand_proxy_command(const char *proxy_command, const char *user,
119
    const char *host, const char *host_arg, int port)
120
0
{
121
0
  char *tmp, *ret, strport[NI_MAXSERV];
122
0
  const char *keyalias = options.host_key_alias ?
123
0
      options.host_key_alias : host_arg;
124
125
0
  snprintf(strport, sizeof strport, "%d", port);
126
0
  xasprintf(&tmp, "exec %s", proxy_command);
127
0
  ret = percent_expand(tmp,
128
0
      "h", host,
129
0
      "k", keyalias,
130
0
      "n", host_arg,
131
0
      "p", strport,
132
0
      "r", options.user,
133
0
      (char *)NULL);
134
0
  free(tmp);
135
0
  return ret;
136
0
}
137
138
/*
139
 * Connect to the given ssh server using a proxy command that passes a
140
 * a connected fd back to us.
141
 */
142
static int
143
ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host,
144
    const char *host_arg, u_short port, const char *proxy_command)
145
0
{
146
0
  char *command_string;
147
0
  int sp[2], sock;
148
0
  pid_t pid;
149
0
  char *shell;
150
151
0
  if ((shell = getenv("SHELL")) == NULL)
152
0
    shell = _PATH_BSHELL;
153
154
0
  if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == -1)
155
0
    fatal("Could not create socketpair to communicate with "
156
0
        "proxy dialer: %.100s", strerror(errno));
157
158
0
  command_string = expand_proxy_command(proxy_command, options.user,
159
0
      host, host_arg, port);
160
0
  debug("Executing proxy dialer command: %.500s", command_string);
161
162
  /* Fork and execute the proxy command. */
163
0
  if ((pid = fork()) == 0) {
164
0
    char *argv[10];
165
166
0
    close(sp[1]);
167
    /* Redirect stdin and stdout. */
168
0
    if (sp[0] != 0) {
169
0
      if (dup2(sp[0], 0) == -1)
170
0
        perror("dup2 stdin");
171
0
    }
172
0
    if (sp[0] != 1) {
173
0
      if (dup2(sp[0], 1) == -1)
174
0
        perror("dup2 stdout");
175
0
    }
176
0
    if (sp[0] >= 2)
177
0
      close(sp[0]);
178
179
    /*
180
     * Stderr is left for non-ControlPersist connections is so
181
     * error messages may be printed on the user's terminal.
182
     */
183
0
    if (!debug_flag && options.control_path != NULL &&
184
0
        options.control_persist && stdfd_devnull(0, 0, 1) == -1)
185
0
      error_f("stdfd_devnull failed");
186
187
0
    argv[0] = shell;
188
0
    argv[1] = "-c";
189
0
    argv[2] = command_string;
190
0
    argv[3] = NULL;
191
192
    /*
193
     * Execute the proxy command.
194
     * Note that we gave up any extra privileges above.
195
     */
196
0
    execv(argv[0], argv);
197
0
    perror(argv[0]);
198
0
    exit(1);
199
0
  }
200
  /* Parent. */
201
0
  if (pid == -1)
202
0
    fatal("fork failed: %.100s", strerror(errno));
203
0
  close(sp[0]);
204
0
  free(command_string);
205
206
0
  if ((sock = mm_receive_fd(sp[1])) == -1)
207
0
    fatal("proxy dialer did not pass back a connection");
208
0
  close(sp[1]);
209
210
0
  while (waitpid(pid, NULL, 0) == -1)
211
0
    if (errno != EINTR)
212
0
      fatal("Couldn't wait for child: %s", strerror(errno));
213
214
  /* Set the connection file descriptors. */
215
0
  if (ssh_packet_set_connection(ssh, sock, sock) == NULL)
216
0
    return -1; /* ssh_packet_set_connection logs error */
217
218
0
  return 0;
219
0
}
220
221
/*
222
 * Connect to the given ssh server using a proxy command.
223
 */
224
static int
225
ssh_proxy_connect(struct ssh *ssh, const char *host, const char *host_arg,
226
    u_short port, const char *proxy_command)
227
0
{
228
0
  char *command_string;
229
0
  int pin[2], pout[2];
230
0
  pid_t pid;
231
0
  char *shell;
232
233
0
  if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
234
0
    shell = _PATH_BSHELL;
235
236
  /* Create pipes for communicating with the proxy. */
237
0
  if (pipe(pin) == -1 || pipe(pout) == -1)
238
0
    fatal("Could not create pipes to communicate with the proxy: %.100s",
239
0
        strerror(errno));
240
241
0
  command_string = expand_proxy_command(proxy_command, options.user,
242
0
      host, host_arg, port);
243
0
  debug("Executing proxy command: %.500s", command_string);
244
245
  /* Fork and execute the proxy command. */
246
0
  if ((pid = fork()) == 0) {
247
0
    char *argv[10];
248
249
    /* Redirect stdin and stdout. */
250
0
    close(pin[1]);
251
0
    if (pin[0] != 0) {
252
0
      if (dup2(pin[0], 0) == -1)
253
0
        perror("dup2 stdin");
254
0
      close(pin[0]);
255
0
    }
256
0
    close(pout[0]);
257
0
    if (dup2(pout[1], 1) == -1)
258
0
      perror("dup2 stdout");
259
    /* Cannot be 1 because pin allocated two descriptors. */
260
0
    close(pout[1]);
261
262
    /*
263
     * Stderr is left for non-ControlPersist connections is so
264
     * error messages may be printed on the user's terminal.
265
     */
266
0
    if (!debug_flag && options.control_path != NULL &&
267
0
        options.control_persist && stdfd_devnull(0, 0, 1) == -1)
268
0
      error_f("stdfd_devnull failed");
269
270
0
    argv[0] = shell;
271
0
    argv[1] = "-c";
272
0
    argv[2] = command_string;
273
0
    argv[3] = NULL;
274
275
    /*
276
     * Execute the proxy command.  Note that we gave up any
277
     * extra privileges above.
278
     */
279
0
    ssh_signal(SIGPIPE, SIG_DFL);
280
0
    execv(argv[0], argv);
281
0
    perror(argv[0]);
282
0
    exit(1);
283
0
  }
284
  /* Parent. */
285
0
  if (pid == -1)
286
0
    fatal("fork failed: %.100s", strerror(errno));
287
0
  else
288
0
    proxy_command_pid = pid; /* save pid to clean up later */
289
290
  /* Close child side of the descriptors. */
291
0
  close(pin[0]);
292
0
  close(pout[1]);
293
294
  /* Free the command name. */
295
0
  free(command_string);
296
297
  /* Set the connection file descriptors. */
298
0
  if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL)
299
0
    return -1; /* ssh_packet_set_connection logs error */
300
301
0
  return 0;
302
0
}
303
304
void
305
ssh_kill_proxy_command(void)
306
0
{
307
  /*
308
   * Send SIGHUP to proxy command if used. We don't wait() in
309
   * case it hangs and instead rely on init to reap the child
310
   */
311
0
  if (proxy_command_pid > 1)
312
0
    kill(proxy_command_pid, SIGHUP);
313
0
}
314
315
#ifdef HAVE_IFADDRS_H
316
/*
317
 * Search a interface address list (returned from getifaddrs(3)) for an
318
 * address that matches the desired address family on the specified interface.
319
 * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure.
320
 */
321
static int
322
check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs,
323
    struct sockaddr_storage *resultp, socklen_t *rlenp)
324
0
{
325
0
  struct sockaddr_in6 *sa6;
326
0
  struct sockaddr_in *sa;
327
0
  struct in6_addr *v6addr;
328
0
  const struct ifaddrs *ifa;
329
0
  int allow_local;
330
331
  /*
332
   * Prefer addresses that are not loopback or linklocal, but use them
333
   * if nothing else matches.
334
   */
335
0
  for (allow_local = 0; allow_local < 2; allow_local++) {
336
0
    for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
337
0
      if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL ||
338
0
          (ifa->ifa_flags & IFF_UP) == 0 ||
339
0
          ifa->ifa_addr->sa_family != af ||
340
0
          strcmp(ifa->ifa_name, options.bind_interface) != 0)
341
0
        continue;
342
0
      switch (ifa->ifa_addr->sa_family) {
343
0
      case AF_INET:
344
0
        sa = (struct sockaddr_in *)ifa->ifa_addr;
345
0
        if (!allow_local && sa->sin_addr.s_addr ==
346
0
            htonl(INADDR_LOOPBACK))
347
0
          continue;
348
0
        if (*rlenp < sizeof(struct sockaddr_in)) {
349
0
          error_f("v4 addr doesn't fit");
350
0
          return -1;
351
0
        }
352
0
        *rlenp = sizeof(struct sockaddr_in);
353
0
        memcpy(resultp, sa, *rlenp);
354
0
        return 0;
355
0
      case AF_INET6:
356
0
        sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
357
0
        v6addr = &sa6->sin6_addr;
358
0
        if (!allow_local &&
359
0
            (IN6_IS_ADDR_LINKLOCAL(v6addr) ||
360
0
            IN6_IS_ADDR_LOOPBACK(v6addr)))
361
0
          continue;
362
0
        if (*rlenp < sizeof(struct sockaddr_in6)) {
363
0
          error_f("v6 addr doesn't fit");
364
0
          return -1;
365
0
        }
366
0
        *rlenp = sizeof(struct sockaddr_in6);
367
0
        memcpy(resultp, sa6, *rlenp);
368
0
        return 0;
369
0
      }
370
0
    }
371
0
  }
372
0
  return -1;
373
0
}
374
#endif
375
376
/*
377
 * Creates a socket for use as the ssh connection.
378
 */
379
static int
380
ssh_create_socket(struct addrinfo *ai)
381
0
{
382
0
  int sock, r;
383
0
  struct sockaddr_storage bindaddr;
384
0
  socklen_t bindaddrlen = 0;
385
0
  struct addrinfo hints, *res = NULL;
386
0
#ifdef HAVE_IFADDRS_H
387
0
  struct ifaddrs *ifaddrs = NULL;
388
0
#endif
389
0
  char ntop[NI_MAXHOST];
390
391
0
  sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
392
0
  if (sock == -1) {
393
0
    error("socket: %s", strerror(errno));
394
0
    return -1;
395
0
  }
396
0
  (void)fcntl(sock, F_SETFD, FD_CLOEXEC);
397
398
  /* Use interactive QOS (if specified) until authentication completed */
399
0
  if (options.ip_qos_interactive != INT_MAX)
400
0
    set_sock_tos(sock, options.ip_qos_interactive);
401
402
  /* Bind the socket to an alternative local IP address */
403
0
  if (options.bind_address == NULL && options.bind_interface == NULL)
404
0
    return sock;
405
406
0
  if (options.bind_address != NULL) {
407
0
    memset(&hints, 0, sizeof(hints));
408
0
    hints.ai_family = ai->ai_family;
409
0
    hints.ai_socktype = ai->ai_socktype;
410
0
    hints.ai_protocol = ai->ai_protocol;
411
0
    hints.ai_flags = AI_PASSIVE;
412
0
    if ((r = getaddrinfo(options.bind_address, NULL,
413
0
        &hints, &res)) != 0) {
414
0
      error("getaddrinfo: %s: %s", options.bind_address,
415
0
          ssh_gai_strerror(r));
416
0
      goto fail;
417
0
    }
418
0
    if (res == NULL) {
419
0
      error("getaddrinfo: no addrs");
420
0
      goto fail;
421
0
    }
422
0
    memcpy(&bindaddr, res->ai_addr, res->ai_addrlen);
423
0
    bindaddrlen = res->ai_addrlen;
424
0
  } else if (options.bind_interface != NULL) {
425
0
#ifdef HAVE_IFADDRS_H
426
0
    if ((r = getifaddrs(&ifaddrs)) != 0) {
427
0
      error("getifaddrs: %s: %s", options.bind_interface,
428
0
          strerror(errno));
429
0
      goto fail;
430
0
    }
431
0
    bindaddrlen = sizeof(bindaddr);
432
0
    if (check_ifaddrs(options.bind_interface, ai->ai_family,
433
0
        ifaddrs, &bindaddr, &bindaddrlen) != 0) {
434
0
      logit("getifaddrs: %s: no suitable addresses",
435
0
          options.bind_interface);
436
0
      goto fail;
437
0
    }
438
#else
439
    error("BindInterface not supported on this platform.");
440
#endif
441
0
  }
442
0
  if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen,
443
0
      ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) {
444
0
    error_f("getnameinfo failed: %s", ssh_gai_strerror(r));
445
0
    goto fail;
446
0
  }
447
0
  if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) {
448
0
    error("bind %s: %s", ntop, strerror(errno));
449
0
    goto fail;
450
0
  }
451
0
  debug_f("bound to %s", ntop);
452
  /* success */
453
0
  goto out;
454
0
fail:
455
0
  close(sock);
456
0
  sock = -1;
457
0
 out:
458
0
  if (res != NULL)
459
0
    freeaddrinfo(res);
460
0
#ifdef HAVE_IFADDRS_H
461
0
  if (ifaddrs != NULL)
462
0
    freeifaddrs(ifaddrs);
463
0
#endif
464
0
  return sock;
465
0
}
466
467
/*
468
 * Opens a TCP/IP connection to the remote server on the given host.
469
 * The address of the remote host will be returned in hostaddr.
470
 * If port is 0, the default port will be used.
471
 * Connection_attempts specifies the maximum number of tries (one per
472
 * second).  If proxy_command is non-NULL, it specifies the command (with %h
473
 * and %p substituted for host and port, respectively) to use to contact
474
 * the daemon.
475
 */
476
static int
477
ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop,
478
    struct sockaddr_storage *hostaddr, u_short port, int connection_attempts,
479
    int *timeout_ms, int want_keepalive)
480
0
{
481
0
  int saved_timeout_ms = *timeout_ms;
482
0
  int oerrno, sock = -1, attempt;
483
0
  char ntop[NI_MAXHOST], strport[NI_MAXSERV];
484
0
  struct addrinfo *ai;
485
486
0
  debug3_f("entering");
487
0
  memset(ntop, 0, sizeof(ntop));
488
0
  memset(strport, 0, sizeof(strport));
489
490
0
  for (attempt = 0; attempt < connection_attempts; attempt++) {
491
0
    if (attempt > 0) {
492
      /* Sleep a moment before retrying. */
493
0
      sleep(1);
494
0
      debug("Trying again...");
495
0
    }
496
    /*
497
     * Loop through addresses for this host, and try each one in
498
     * sequence until the connection succeeds.
499
     */
500
0
    for (ai = aitop; ai; ai = ai->ai_next) {
501
0
      if (ai->ai_family != AF_INET &&
502
0
          ai->ai_family != AF_INET6) {
503
0
        errno = EAFNOSUPPORT;
504
0
        continue;
505
0
      }
506
0
      if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
507
0
          ntop, sizeof(ntop), strport, sizeof(strport),
508
0
          NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
509
0
        oerrno = errno;
510
0
        error_f("getnameinfo failed");
511
0
        errno = oerrno;
512
0
        continue;
513
0
      }
514
0
      if (options.address_family != AF_UNSPEC &&
515
0
          ai->ai_family != options.address_family) {
516
0
        debug2_f("skipping address [%s]:%s: "
517
0
            "wrong address family", ntop, strport);
518
0
        errno = EAFNOSUPPORT;
519
0
        continue;
520
0
      }
521
522
0
      debug("Connecting to %.200s [%.100s] port %s.",
523
0
        host, ntop, strport);
524
525
      /* Create a socket for connecting. */
526
0
      sock = ssh_create_socket(ai);
527
0
      if (sock < 0) {
528
        /* Any error is already output */
529
0
        errno = 0;
530
0
        continue;
531
0
      }
532
533
0
      *timeout_ms = saved_timeout_ms;
534
0
      if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
535
0
          timeout_ms) >= 0) {
536
        /* Successful connection. */
537
0
        memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
538
0
        break;
539
0
      } else {
540
0
        oerrno = errno;
541
0
        debug("connect to address %s port %s: %s",
542
0
            ntop, strport, strerror(errno));
543
0
        close(sock);
544
0
        sock = -1;
545
0
        errno = oerrno;
546
0
      }
547
0
    }
548
0
    if (sock != -1)
549
0
      break; /* Successful connection. */
550
0
  }
551
552
  /* Return failure if we didn't get a successful connection. */
553
0
  if (sock == -1) {
554
0
    error("ssh: connect to host %s port %s: %s",
555
0
        host, strport, errno == 0 ? "failure" : strerror(errno));
556
0
    return -1;
557
0
  }
558
559
0
  debug("Connection established.");
560
561
  /* Set SO_KEEPALIVE if requested. */
562
0
  if (want_keepalive)
563
0
    set_keepalive(sock); /* logs errors */
564
565
  /* Set the connection. */
566
0
  if (ssh_packet_set_connection(ssh, sock, sock) == NULL)
567
0
    return -1; /* ssh_packet_set_connection logs error */
568
569
0
  return 0;
570
0
}
571
572
int
573
ssh_connect(struct ssh *ssh, const char *host, const char *host_arg,
574
    struct addrinfo *addrs, struct sockaddr_storage *hostaddr, u_short port,
575
    int connection_attempts, int *timeout_ms, int want_keepalive)
576
0
{
577
0
  int in, out;
578
579
0
  if (options.proxy_command == NULL) {
580
0
    return ssh_connect_direct(ssh, host, addrs, hostaddr, port,
581
0
        connection_attempts, timeout_ms, want_keepalive);
582
0
  } else if (strcmp(options.proxy_command, "-") == 0) {
583
0
    if ((in = dup(STDIN_FILENO)) == -1 ||
584
0
        (out = dup(STDOUT_FILENO)) == -1) {
585
0
      if (in >= 0)
586
0
        close(in);
587
0
      error_f("dup() in/out failed");
588
0
      return -1; /* ssh_packet_set_connection logs error */
589
0
    }
590
0
    if ((ssh_packet_set_connection(ssh, in, out)) == NULL)
591
0
      return -1; /* ssh_packet_set_connection logs error */
592
0
    return 0;
593
0
  } else if (options.proxy_use_fdpass) {
594
0
    return ssh_proxy_fdpass_connect(ssh, host, host_arg, port,
595
0
        options.proxy_command);
596
0
  }
597
0
  return ssh_proxy_connect(ssh, host, host_arg, port,
598
0
      options.proxy_command);
599
0
}
600
601
/* defaults to 'no' */
602
static int
603
confirm(const char *prompt, const char *fingerprint)
604
0
{
605
0
  const char *msg, *again = "Please type 'yes' or 'no': ";
606
0
  const char *again_fp = "Please type 'yes', 'no' or the fingerprint: ";
607
0
  char *p, *cp;
608
0
  int ret = -1;
609
610
0
  if (options.batch_mode)
611
0
    return 0;
612
0
  for (msg = prompt;;msg = fingerprint ? again_fp : again) {
613
0
    cp = p = read_passphrase(msg, RP_ECHO);
614
0
    if (p == NULL)
615
0
      return 0;
616
0
    p += strspn(p, " \t"); /* skip leading whitespace */
617
0
    p[strcspn(p, " \t\n")] = '\0'; /* remove trailing whitespace */
618
0
    if (p[0] == '\0' || strcasecmp(p, "no") == 0)
619
0
      ret = 0;
620
0
    else if (strcasecmp(p, "yes") == 0 || (fingerprint != NULL &&
621
0
        strcmp(p, fingerprint) == 0))
622
0
      ret = 1;
623
0
    free(cp);
624
0
    if (ret != -1)
625
0
      return ret;
626
0
  }
627
0
}
628
629
static int
630
sockaddr_is_local(struct sockaddr *hostaddr)
631
0
{
632
0
  switch (hostaddr->sa_family) {
633
0
  case AF_INET:
634
0
    return (ntohl(((struct sockaddr_in *)hostaddr)->
635
0
        sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
636
0
  case AF_INET6:
637
0
    return IN6_IS_ADDR_LOOPBACK(
638
0
        &(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
639
0
  default:
640
0
    return 0;
641
0
  }
642
0
}
643
644
/*
645
 * Prepare the hostname and ip address strings that are used to lookup
646
 * host keys in known_hosts files. These may have a port number appended.
647
 */
648
void
649
get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr,
650
    u_short port, char **hostfile_hostname, char **hostfile_ipaddr)
651
0
{
652
0
  char ntop[NI_MAXHOST];
653
0
  socklen_t addrlen;
654
655
0
  switch (hostaddr == NULL ? -1 : hostaddr->sa_family) {
656
0
  case -1:
657
0
    addrlen = 0;
658
0
    break;
659
0
  case AF_INET:
660
0
    addrlen = sizeof(struct sockaddr_in);
661
0
    break;
662
0
  case AF_INET6:
663
0
    addrlen = sizeof(struct sockaddr_in6);
664
0
    break;
665
0
  default:
666
0
    addrlen = sizeof(struct sockaddr);
667
0
    break;
668
0
  }
669
670
  /*
671
   * We don't have the remote ip-address for connections
672
   * using a proxy command
673
   */
674
0
  if (hostfile_ipaddr != NULL) {
675
0
    if (options.proxy_command == NULL) {
676
0
      if (getnameinfo(hostaddr, addrlen,
677
0
          ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0)
678
0
        fatal_f("getnameinfo failed");
679
0
      *hostfile_ipaddr = put_host_port(ntop, port);
680
0
    } else {
681
0
      *hostfile_ipaddr = xstrdup("<no hostip for proxy "
682
0
          "command>");
683
0
    }
684
0
  }
685
686
  /*
687
   * Allow the user to record the key under a different name or
688
   * differentiate a non-standard port.  This is useful for ssh
689
   * tunneling over forwarded connections or if you run multiple
690
   * sshd's on different ports on the same machine.
691
   */
692
0
  if (hostfile_hostname != NULL) {
693
0
    if (options.host_key_alias != NULL) {
694
0
      *hostfile_hostname = xstrdup(options.host_key_alias);
695
0
      debug("using hostkeyalias: %s", *hostfile_hostname);
696
0
    } else {
697
0
      *hostfile_hostname = put_host_port(hostname, port);
698
0
    }
699
0
  }
700
0
}
701
702
/* returns non-zero if path appears in hostfiles, or 0 if not. */
703
static int
704
path_in_hostfiles(const char *path, char **hostfiles, u_int num_hostfiles)
705
0
{
706
0
  u_int i;
707
708
0
  for (i = 0; i < num_hostfiles; i++) {
709
0
    if (strcmp(path, hostfiles[i]) == 0)
710
0
      return 1;
711
0
  }
712
0
  return 0;
713
0
}
714
715
struct find_by_key_ctx {
716
  const char *host, *ip;
717
  const struct sshkey *key;
718
  char **names;
719
  u_int nnames;
720
};
721
722
/* Try to replace home directory prefix (per $HOME) with a ~/ sequence */
723
static char *
724
try_tilde_unexpand(const char *path)
725
0
{
726
0
  char *home, *ret = NULL;
727
0
  size_t l;
728
729
0
  if (*path != '/')
730
0
    return xstrdup(path);
731
0
  if ((home = getenv("HOME")) == NULL || (l = strlen(home)) == 0)
732
0
    return xstrdup(path);
733
0
  if (strncmp(path, home, l) != 0)
734
0
    return xstrdup(path);
735
  /*
736
   * ensure we have matched on a path boundary: either the $HOME that
737
   * we just compared ends with a '/' or the next character of the path
738
   * must be a '/'.
739
   */
740
0
  if (home[l - 1] != '/' && path[l] != '/')
741
0
    return xstrdup(path);
742
0
  if (path[l] == '/')
743
0
    l++;
744
0
  xasprintf(&ret, "~/%s", path + l);
745
0
  return ret;
746
0
}
747
748
/*
749
 * Returns non-zero if the key is accepted by HostkeyAlgorithms.
750
 * Made slightly less trivial by the multiple RSA signature algorithm names.
751
 */
752
int
753
hostkey_accepted_by_hostkeyalgs(const struct sshkey *key)
754
0
{
755
0
  const char *ktype = sshkey_ssh_name(key);
756
0
  const char *hostkeyalgs = options.hostkeyalgorithms;
757
758
0
  if (key->type == KEY_UNSPEC)
759
0
    return 0;
760
0
  if (key->type == KEY_RSA &&
761
0
      (match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 ||
762
0
      match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1))
763
0
    return 1;
764
0
  if (key->type == KEY_RSA_CERT &&
765
0
      (match_pattern_list("rsa-sha2-512-cert-v01@openssh.com", hostkeyalgs, 0) == 1 ||
766
0
      match_pattern_list("rsa-sha2-256-cert-v01@openssh.com", hostkeyalgs, 0) == 1))
767
0
    return 1;
768
0
  return match_pattern_list(ktype, hostkeyalgs, 0) == 1;
769
0
}
770
771
static int
772
hostkeys_find_by_key_cb(struct hostkey_foreach_line *l, void *_ctx)
773
0
{
774
0
  struct find_by_key_ctx *ctx = (struct find_by_key_ctx *)_ctx;
775
0
  char *path;
776
777
  /* we are looking for keys with names that *do not* match */
778
0
  if ((l->match & HKF_MATCH_HOST) != 0)
779
0
    return 0;
780
  /* not interested in marker lines */
781
0
  if (l->marker != MRK_NONE)
782
0
    return 0;
783
  /* we are only interested in exact key matches */
784
0
  if (l->key == NULL || !sshkey_equal(ctx->key, l->key))
785
0
    return 0;
786
0
  path = try_tilde_unexpand(l->path);
787
0
  debug_f("found matching key in %s:%lu", path, l->linenum);
788
0
  ctx->names = xrecallocarray(ctx->names,
789
0
      ctx->nnames, ctx->nnames + 1, sizeof(*ctx->names));
790
0
  xasprintf(&ctx->names[ctx->nnames], "%s:%lu: %s", path, l->linenum,
791
0
      strncmp(l->hosts, HASH_MAGIC, strlen(HASH_MAGIC)) == 0 ?
792
0
      "[hashed name]" : l->hosts);
793
0
  ctx->nnames++;
794
0
  free(path);
795
0
  return 0;
796
0
}
797
798
static int
799
hostkeys_find_by_key_hostfile(const char *file, const char *which,
800
    struct find_by_key_ctx *ctx)
801
0
{
802
0
  int r;
803
804
0
  debug3_f("trying %s hostfile \"%s\"", which, file);
805
0
  if ((r = hostkeys_foreach(file, hostkeys_find_by_key_cb, ctx,
806
0
      ctx->host, ctx->ip, HKF_WANT_PARSE_KEY, 0)) != 0) {
807
0
    if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) {
808
0
      debug_f("hostkeys file %s does not exist", file);
809
0
      return 0;
810
0
    }
811
0
    error_fr(r, "hostkeys_foreach failed for %s", file);
812
0
    return r;
813
0
  }
814
0
  return 0;
815
0
}
816
817
/*
818
 * Find 'key' in known hosts file(s) that do not match host/ip.
819
 * Used to display also-known-as information for previously-unseen hostkeys.
820
 */
821
static void
822
hostkeys_find_by_key(const char *host, const char *ip, const struct sshkey *key,
823
    char **user_hostfiles, u_int num_user_hostfiles,
824
    char **system_hostfiles, u_int num_system_hostfiles,
825
    char ***names, u_int *nnames)
826
0
{
827
0
  struct find_by_key_ctx ctx = {NULL, NULL, NULL, NULL, 0};
828
0
  u_int i;
829
830
0
  *names = NULL;
831
0
  *nnames = 0;
832
833
0
  if (key == NULL || sshkey_is_cert(key))
834
0
    return;
835
836
0
  ctx.host = host;
837
0
  ctx.ip = ip;
838
0
  ctx.key = key;
839
840
0
  for (i = 0; i < num_user_hostfiles; i++) {
841
0
    if (hostkeys_find_by_key_hostfile(user_hostfiles[i],
842
0
        "user", &ctx) != 0)
843
0
      goto fail;
844
0
  }
845
0
  for (i = 0; i < num_system_hostfiles; i++) {
846
0
    if (hostkeys_find_by_key_hostfile(system_hostfiles[i],
847
0
        "system", &ctx) != 0)
848
0
      goto fail;
849
0
  }
850
  /* success */
851
0
  *names = ctx.names;
852
0
  *nnames = ctx.nnames;
853
0
  ctx.names = NULL;
854
0
  ctx.nnames = 0;
855
0
  return;
856
0
 fail:
857
0
  for (i = 0; i < ctx.nnames; i++)
858
0
    free(ctx.names[i]);
859
0
  free(ctx.names);
860
0
}
861
862
0
#define MAX_OTHER_NAMES 8 /* Maximum number of names to list */
863
static char *
864
other_hostkeys_message(const char *host, const char *ip,
865
    const struct sshkey *key,
866
    char **user_hostfiles, u_int num_user_hostfiles,
867
    char **system_hostfiles, u_int num_system_hostfiles)
868
0
{
869
0
  char *ret = NULL, **othernames = NULL;
870
0
  u_int i, n, num_othernames = 0;
871
872
0
  hostkeys_find_by_key(host, ip, key,
873
0
      user_hostfiles, num_user_hostfiles,
874
0
      system_hostfiles, num_system_hostfiles,
875
0
      &othernames, &num_othernames);
876
0
  if (num_othernames == 0)
877
0
    return xstrdup("This key is not known by any other names.");
878
879
0
  xasprintf(&ret, "This host key is known by the following other "
880
0
      "names/addresses:");
881
882
0
  n = num_othernames;
883
0
  if (n > MAX_OTHER_NAMES)
884
0
    n = MAX_OTHER_NAMES;
885
0
  for (i = 0; i < n; i++) {
886
0
    xextendf(&ret, "\n", "    %s", othernames[i]);
887
0
  }
888
0
  if (n < num_othernames) {
889
0
    xextendf(&ret, "\n", "    (%d additional names omitted)",
890
0
        num_othernames - n);
891
0
  }
892
0
  for (i = 0; i < num_othernames; i++)
893
0
    free(othernames[i]);
894
0
  free(othernames);
895
0
  return ret;
896
0
}
897
898
void
899
load_hostkeys_command(struct hostkeys *hostkeys, const char *command_template,
900
    const char *invocation, const struct ssh_conn_info *cinfo,
901
    const struct sshkey *host_key, const char *hostfile_hostname)
902
0
{
903
0
  int r, i, ac = 0;
904
0
  char *key_fp = NULL, *keytext = NULL, *tmp;
905
0
  char *command = NULL, *tag = NULL, **av = NULL;
906
0
  FILE *f = NULL;
907
0
  pid_t pid;
908
0
  void (*osigchld)(int);
909
910
0
  xasprintf(&tag, "KnownHostsCommand-%s", invocation);
911
912
0
  if (host_key != NULL) {
913
0
    if ((key_fp = sshkey_fingerprint(host_key,
914
0
        options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
915
0
      fatal_f("sshkey_fingerprint failed");
916
0
    if ((r = sshkey_to_base64(host_key, &keytext)) != 0)
917
0
      fatal_fr(r, "sshkey_to_base64 failed");
918
0
  }
919
  /*
920
   * NB. all returns later this function should go via "out" to
921
   * ensure the original SIGCHLD handler is restored properly.
922
   */
923
0
  osigchld = ssh_signal(SIGCHLD, SIG_DFL);
924
925
  /* Turn the command into an argument vector */
926
0
  if (argv_split(command_template, &ac, &av, 0) != 0) {
927
0
    error("%s \"%s\" contains invalid quotes", tag,
928
0
        command_template);
929
0
    goto out;
930
0
  }
931
0
  if (ac == 0) {
932
0
    error("%s \"%s\" yielded no arguments", tag,
933
0
        command_template);
934
0
    goto out;
935
0
  }
936
0
  for (i = 1; i < ac; i++) {
937
0
    tmp = percent_dollar_expand(av[i],
938
0
        DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo),
939
0
        "H", hostfile_hostname,
940
0
        "I", invocation,
941
0
        "t", host_key == NULL ? "NONE" : sshkey_ssh_name(host_key),
942
0
        "f", key_fp == NULL ? "NONE" : key_fp,
943
0
        "K", keytext == NULL ? "NONE" : keytext,
944
0
        (char *)NULL);
945
0
    if (tmp == NULL)
946
0
      fatal_f("percent_expand failed");
947
0
    free(av[i]);
948
0
    av[i] = tmp;
949
0
  }
950
  /* Prepare a printable command for logs, etc. */
951
0
  command = argv_assemble(ac, av);
952
953
0
  if ((pid = subprocess(tag, command, ac, av, &f,
954
0
      SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_UNSAFE_PATH|
955
0
      SSH_SUBPROCESS_PRESERVE_ENV, NULL, NULL, NULL)) == 0)
956
0
    goto out;
957
958
0
  load_hostkeys_file(hostkeys, hostfile_hostname, tag, f, 1);
959
960
0
  if (exited_cleanly(pid, tag, command, 0) != 0)
961
0
    fatal("KnownHostsCommand failed");
962
963
0
 out:
964
0
  if (f != NULL)
965
0
    fclose(f);
966
0
  ssh_signal(SIGCHLD, osigchld);
967
0
  for (i = 0; i < ac; i++)
968
0
    free(av[i]);
969
0
  free(av);
970
0
  free(tag);
971
0
  free(command);
972
0
  free(key_fp);
973
0
  free(keytext);
974
0
}
975
976
/*
977
 * check whether the supplied host key is valid, return -1 if the key
978
 * is not valid. user_hostfile[0] will not be updated if 'readonly' is true.
979
 */
980
0
#define RDRW  0
981
0
#define RDONLY  1
982
0
#define ROQUIET 2
983
static int
984
check_host_key(char *hostname, const struct ssh_conn_info *cinfo,
985
    struct sockaddr *hostaddr, u_short port,
986
    struct sshkey *host_key, int readonly, int clobber_port,
987
    char **user_hostfiles, u_int num_user_hostfiles,
988
    char **system_hostfiles, u_int num_system_hostfiles,
989
    const char *hostfile_command)
990
0
{
991
0
  HostStatus host_status = -1, ip_status = -1;
992
0
  struct sshkey *raw_key = NULL;
993
0
  char *ip = NULL, *host = NULL;
994
0
  char hostline[1000], *hostp, *fp, *ra;
995
0
  char msg[1024];
996
0
  const char *type, *fail_reason = NULL;
997
0
  const struct hostkey_entry *host_found = NULL, *ip_found = NULL;
998
0
  int len, cancelled_forwarding = 0, confirmed;
999
0
  int local = sockaddr_is_local(hostaddr);
1000
0
  int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0;
1001
0
  int hostkey_trusted = 0; /* Known or explicitly accepted by user */
1002
0
  struct hostkeys *host_hostkeys, *ip_hostkeys;
1003
0
  u_int i;
1004
1005
  /*
1006
   * Force accepting of the host key for loopback/localhost. The
1007
   * problem is that if the home directory is NFS-mounted to multiple
1008
   * machines, localhost will refer to a different machine in each of
1009
   * them, and the user will get bogus HOST_CHANGED warnings.  This
1010
   * essentially disables host authentication for localhost; however,
1011
   * this is probably not a real problem.
1012
   */
1013
0
  if (options.no_host_authentication_for_localhost == 1 && local &&
1014
0
      options.host_key_alias == NULL) {
1015
0
    debug("Forcing accepting of host key for "
1016
0
        "loopback/localhost.");
1017
0
    options.update_hostkeys = 0;
1018
0
    return 0;
1019
0
  }
1020
1021
  /*
1022
   * Don't ever try to write an invalid name to a known hosts file.
1023
   * Note: do this before get_hostfile_hostname_ipaddr() to catch
1024
   * '[' or ']' in the name before they are added.
1025
   */
1026
0
  if (strcspn(hostname, "@?*#[]|'\'\"\\") != strlen(hostname)) {
1027
0
    debug_f("invalid hostname \"%s\"; will not record: %s",
1028
0
        hostname, fail_reason);
1029
0
    readonly = RDONLY;
1030
0
  }
1031
1032
  /*
1033
   * Prepare the hostname and address strings used for hostkey lookup.
1034
   * In some cases, these will have a port number appended.
1035
   */
1036
0
  get_hostfile_hostname_ipaddr(hostname, hostaddr,
1037
0
      clobber_port ? 0 : port, &host, &ip);
1038
1039
  /*
1040
   * Turn off check_host_ip if the connection is to localhost, via proxy
1041
   * command or if we don't have a hostname to compare with
1042
   */
1043
0
  if (options.check_host_ip && (local ||
1044
0
      strcmp(hostname, ip) == 0 || options.proxy_command != NULL))
1045
0
    options.check_host_ip = 0;
1046
1047
0
  host_hostkeys = init_hostkeys();
1048
0
  for (i = 0; i < num_user_hostfiles; i++)
1049
0
    load_hostkeys(host_hostkeys, host, user_hostfiles[i], 0);
1050
0
  for (i = 0; i < num_system_hostfiles; i++)
1051
0
    load_hostkeys(host_hostkeys, host, system_hostfiles[i], 0);
1052
0
  if (hostfile_command != NULL && !clobber_port) {
1053
0
    load_hostkeys_command(host_hostkeys, hostfile_command,
1054
0
        "HOSTNAME", cinfo, host_key, host);
1055
0
  }
1056
1057
0
  ip_hostkeys = NULL;
1058
0
  if (!want_cert && options.check_host_ip) {
1059
0
    ip_hostkeys = init_hostkeys();
1060
0
    for (i = 0; i < num_user_hostfiles; i++)
1061
0
      load_hostkeys(ip_hostkeys, ip, user_hostfiles[i], 0);
1062
0
    for (i = 0; i < num_system_hostfiles; i++)
1063
0
      load_hostkeys(ip_hostkeys, ip, system_hostfiles[i], 0);
1064
0
    if (hostfile_command != NULL && !clobber_port) {
1065
0
      load_hostkeys_command(ip_hostkeys, hostfile_command,
1066
0
          "ADDRESS", cinfo, host_key, ip);
1067
0
    }
1068
0
  }
1069
1070
0
 retry:
1071
0
  if (!hostkey_accepted_by_hostkeyalgs(host_key)) {
1072
0
    error("host key %s not permitted by HostkeyAlgorithms",
1073
0
        sshkey_ssh_name(host_key));
1074
0
    goto fail;
1075
0
  }
1076
1077
  /* Reload these as they may have changed on cert->key downgrade */
1078
0
  want_cert = sshkey_is_cert(host_key);
1079
0
  type = sshkey_type(host_key);
1080
1081
  /*
1082
   * Check if the host key is present in the user's list of known
1083
   * hosts or in the systemwide list.
1084
   */
1085
0
  host_status = check_key_in_hostkeys(host_hostkeys, host_key,
1086
0
      &host_found);
1087
1088
  /*
1089
   * If there are no hostfiles, or if the hostkey was found via
1090
   * KnownHostsCommand, then don't try to touch the disk.
1091
   */
1092
0
  if (!readonly && (num_user_hostfiles == 0 ||
1093
0
      (host_found != NULL && host_found->note != 0)))
1094
0
    readonly = RDONLY;
1095
1096
  /*
1097
   * Also perform check for the ip address, skip the check if we are
1098
   * localhost, looking for a certificate, or the hostname was an ip
1099
   * address to begin with.
1100
   */
1101
0
  if (!want_cert && ip_hostkeys != NULL) {
1102
0
    ip_status = check_key_in_hostkeys(ip_hostkeys, host_key,
1103
0
        &ip_found);
1104
0
    if (host_status == HOST_CHANGED &&
1105
0
        (ip_status != HOST_CHANGED ||
1106
0
        (ip_found != NULL &&
1107
0
        !sshkey_equal(ip_found->key, host_found->key))))
1108
0
      host_ip_differ = 1;
1109
0
  } else
1110
0
    ip_status = host_status;
1111
1112
0
  switch (host_status) {
1113
0
  case HOST_OK:
1114
    /* The host is known and the key matches. */
1115
0
    debug("Host '%.200s' is known and matches the %s host %s.",
1116
0
        host, type, want_cert ? "certificate" : "key");
1117
0
    debug("Found %s in %s:%lu", want_cert ? "CA key" : "key",
1118
0
        host_found->file, host_found->line);
1119
0
    if (want_cert) {
1120
0
      if (sshkey_cert_check_host(host_key,
1121
0
          options.host_key_alias == NULL ?
1122
0
          hostname : options.host_key_alias,
1123
0
          options.ca_sign_algorithms, &fail_reason) != 0) {
1124
0
        error("%s", fail_reason);
1125
0
        goto fail;
1126
0
      }
1127
      /*
1128
       * Do not attempt hostkey update if a certificate was
1129
       * successfully matched.
1130
       */
1131
0
      if (options.update_hostkeys != 0) {
1132
0
        options.update_hostkeys = 0;
1133
0
        debug3_f("certificate host key in use; "
1134
0
            "disabling UpdateHostkeys");
1135
0
      }
1136
0
    }
1137
    /* Turn off UpdateHostkeys if key was in system known_hosts */
1138
0
    if (options.update_hostkeys != 0 &&
1139
0
        (path_in_hostfiles(host_found->file,
1140
0
        system_hostfiles, num_system_hostfiles) ||
1141
0
        (ip_status == HOST_OK && ip_found != NULL &&
1142
0
        path_in_hostfiles(ip_found->file,
1143
0
        system_hostfiles, num_system_hostfiles)))) {
1144
0
      options.update_hostkeys = 0;
1145
0
      debug3_f("host key found in GlobalKnownHostsFile; "
1146
0
          "disabling UpdateHostkeys");
1147
0
    }
1148
0
    if (options.update_hostkeys != 0 && host_found->note) {
1149
0
      options.update_hostkeys = 0;
1150
0
      debug3_f("host key found via KnownHostsCommand; "
1151
0
          "disabling UpdateHostkeys");
1152
0
    }
1153
0
    if (options.check_host_ip && ip_status == HOST_NEW) {
1154
0
      if (readonly || want_cert)
1155
0
        logit("%s host key for IP address "
1156
0
            "'%.128s' not in list of known hosts.",
1157
0
            type, ip);
1158
0
      else if (!add_host_to_hostfile(user_hostfiles[0], ip,
1159
0
          host_key, options.hash_known_hosts))
1160
0
        logit("Failed to add the %s host key for IP "
1161
0
            "address '%.128s' to the list of known "
1162
0
            "hosts (%.500s).", type, ip,
1163
0
            user_hostfiles[0]);
1164
0
      else
1165
0
        logit("Warning: Permanently added the %s host "
1166
0
            "key for IP address '%.128s' to the list "
1167
0
            "of known hosts.", type, ip);
1168
0
    } else if (options.visual_host_key) {
1169
0
      fp = sshkey_fingerprint(host_key,
1170
0
          options.fingerprint_hash, SSH_FP_DEFAULT);
1171
0
      ra = sshkey_fingerprint(host_key,
1172
0
          options.fingerprint_hash, SSH_FP_RANDOMART);
1173
0
      if (fp == NULL || ra == NULL)
1174
0
        fatal_f("sshkey_fingerprint failed");
1175
0
      logit("Host key fingerprint is: %s\n%s", fp, ra);
1176
0
      free(ra);
1177
0
      free(fp);
1178
0
    }
1179
0
    hostkey_trusted = 1;
1180
0
    break;
1181
0
  case HOST_NEW:
1182
0
    if (options.host_key_alias == NULL && port != 0 &&
1183
0
        port != SSH_DEFAULT_PORT && !clobber_port) {
1184
0
      debug("checking without port identifier");
1185
0
      if (check_host_key(hostname, cinfo, hostaddr, 0,
1186
0
          host_key, ROQUIET, 1,
1187
0
          user_hostfiles, num_user_hostfiles,
1188
0
          system_hostfiles, num_system_hostfiles,
1189
0
          hostfile_command) == 0) {
1190
0
        debug("found matching key w/out port");
1191
0
        break;
1192
0
      }
1193
0
    }
1194
0
    if (readonly || want_cert)
1195
0
      goto fail;
1196
    /* The host is new. */
1197
0
    if (options.strict_host_key_checking ==
1198
0
        SSH_STRICT_HOSTKEY_YES) {
1199
      /*
1200
       * User has requested strict host key checking.  We
1201
       * will not add the host key automatically.  The only
1202
       * alternative left is to abort.
1203
       */
1204
0
      error("No %s host key is known for %.200s and you "
1205
0
          "have requested strict checking.", type, host);
1206
0
      goto fail;
1207
0
    } else if (options.strict_host_key_checking ==
1208
0
        SSH_STRICT_HOSTKEY_ASK) {
1209
0
      char *msg1 = NULL, *msg2 = NULL;
1210
1211
0
      xasprintf(&msg1, "The authenticity of host "
1212
0
          "'%.200s (%s)' can't be established", host, ip);
1213
1214
0
      if (show_other_keys(host_hostkeys, host_key)) {
1215
0
        xextendf(&msg1, "\n", "but keys of different "
1216
0
            "type are already known for this host.");
1217
0
      } else
1218
0
        xextendf(&msg1, "", ".");
1219
1220
0
      fp = sshkey_fingerprint(host_key,
1221
0
          options.fingerprint_hash, SSH_FP_DEFAULT);
1222
0
      ra = sshkey_fingerprint(host_key,
1223
0
          options.fingerprint_hash, SSH_FP_RANDOMART);
1224
0
      if (fp == NULL || ra == NULL)
1225
0
        fatal_f("sshkey_fingerprint failed");
1226
0
      xextendf(&msg1, "\n", "%s key fingerprint is: %s",
1227
0
          type, fp);
1228
0
      if (options.visual_host_key)
1229
0
        xextendf(&msg1, "\n", "%s", ra);
1230
0
      if (options.verify_host_key_dns) {
1231
0
        xextendf(&msg1, "\n",
1232
0
            "%s host key fingerprint found in DNS.",
1233
0
            matching_host_key_dns ?
1234
0
            "Matching" : "No matching");
1235
0
      }
1236
      /* msg2 informs for other names matching this key */
1237
0
      if ((msg2 = other_hostkeys_message(host, ip, host_key,
1238
0
          user_hostfiles, num_user_hostfiles,
1239
0
          system_hostfiles, num_system_hostfiles)) != NULL)
1240
0
        xextendf(&msg1, "\n", "%s", msg2);
1241
1242
0
      xextendf(&msg1, "\n",
1243
0
          "Are you sure you want to continue connecting "
1244
0
          "(yes/no/[fingerprint])? ");
1245
1246
0
      confirmed = confirm(msg1, fp);
1247
0
      free(ra);
1248
0
      free(fp);
1249
0
      free(msg1);
1250
0
      free(msg2);
1251
0
      if (!confirmed)
1252
0
        goto fail;
1253
0
      hostkey_trusted = 1; /* user explicitly confirmed */
1254
0
    }
1255
    /*
1256
     * If in "new" or "off" strict mode, add the key automatically
1257
     * to the local known_hosts file.
1258
     */
1259
0
    if (options.check_host_ip && ip_status == HOST_NEW) {
1260
0
      snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
1261
0
      hostp = hostline;
1262
0
      if (options.hash_known_hosts) {
1263
        /* Add hash of host and IP separately */
1264
0
        r = add_host_to_hostfile(user_hostfiles[0],
1265
0
            host, host_key, options.hash_known_hosts) &&
1266
0
            add_host_to_hostfile(user_hostfiles[0], ip,
1267
0
            host_key, options.hash_known_hosts);
1268
0
      } else {
1269
        /* Add unhashed "host,ip" */
1270
0
        r = add_host_to_hostfile(user_hostfiles[0],
1271
0
            hostline, host_key,
1272
0
            options.hash_known_hosts);
1273
0
      }
1274
0
    } else {
1275
0
      r = add_host_to_hostfile(user_hostfiles[0], host,
1276
0
          host_key, options.hash_known_hosts);
1277
0
      hostp = host;
1278
0
    }
1279
1280
0
    if (!r)
1281
0
      logit("Failed to add the host to the list of known "
1282
0
          "hosts (%.500s).", user_hostfiles[0]);
1283
0
    else
1284
0
      logit("Warning: Permanently added '%.200s' (%s) to the "
1285
0
          "list of known hosts.", hostp, type);
1286
0
    break;
1287
0
  case HOST_REVOKED:
1288
0
    error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1289
0
    error("@       WARNING: REVOKED HOST KEY DETECTED!               @");
1290
0
    error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1291
0
    error("The %s host key for %s is marked as revoked.", type, host);
1292
0
    error("This could mean that a stolen key is being used to");
1293
0
    error("impersonate this host.");
1294
1295
    /*
1296
     * If strict host key checking is in use, the user will have
1297
     * to edit the key manually and we can only abort.
1298
     */
1299
0
    if (options.strict_host_key_checking !=
1300
0
        SSH_STRICT_HOSTKEY_OFF) {
1301
0
      error("%s host key for %.200s was revoked and you have "
1302
0
          "requested strict checking.", type, host);
1303
0
      goto fail;
1304
0
    }
1305
0
    goto continue_unsafe;
1306
1307
0
  case HOST_CHANGED:
1308
0
    if (want_cert) {
1309
      /*
1310
       * This is only a debug() since it is valid to have
1311
       * CAs with wildcard DNS matches that don't match
1312
       * all hosts that one might visit.
1313
       */
1314
0
      debug("Host certificate authority does not "
1315
0
          "match %s in %s:%lu", CA_MARKER,
1316
0
          host_found->file, host_found->line);
1317
0
      goto fail;
1318
0
    }
1319
0
    if (readonly == ROQUIET)
1320
0
      goto fail;
1321
0
    if (options.check_host_ip && host_ip_differ) {
1322
0
      char *key_msg;
1323
0
      if (ip_status == HOST_NEW)
1324
0
        key_msg = "is unknown";
1325
0
      else if (ip_status == HOST_OK)
1326
0
        key_msg = "is unchanged";
1327
0
      else
1328
0
        key_msg = "has a different value";
1329
0
      error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1330
0
      error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
1331
0
      error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1332
0
      error("The %s host key for %s has changed,", type, host);
1333
0
      error("and the key for the corresponding IP address %s", ip);
1334
0
      error("%s. This could either mean that", key_msg);
1335
0
      error("DNS SPOOFING is happening or the IP address for the host");
1336
0
      error("and its host key have changed at the same time.");
1337
0
      if (ip_status != HOST_NEW)
1338
0
        error("Offending key for IP in %s:%lu",
1339
0
            ip_found->file, ip_found->line);
1340
0
    }
1341
    /* The host key has changed. */
1342
0
    warn_changed_key(host_key);
1343
0
    if (num_user_hostfiles > 0 || num_system_hostfiles > 0) {
1344
0
      error("Add correct host key in %.100s to get rid "
1345
0
          "of this message.", num_user_hostfiles > 0 ?
1346
0
          user_hostfiles[0] : system_hostfiles[0]);
1347
0
    }
1348
0
    error("Offending %s key in %s:%lu",
1349
0
        sshkey_type(host_found->key),
1350
0
        host_found->file, host_found->line);
1351
1352
    /*
1353
     * If strict host key checking is in use, the user will have
1354
     * to edit the key manually and we can only abort.
1355
     */
1356
0
    if (options.strict_host_key_checking !=
1357
0
        SSH_STRICT_HOSTKEY_OFF) {
1358
0
      error("Host key for %.200s has changed and you have "
1359
0
          "requested strict checking.", host);
1360
0
      goto fail;
1361
0
    }
1362
1363
0
 continue_unsafe:
1364
    /*
1365
     * If strict host key checking has not been requested, allow
1366
     * the connection but without MITM-able authentication or
1367
     * forwarding.
1368
     */
1369
0
    if (options.password_authentication) {
1370
0
      error("Password authentication is disabled to avoid "
1371
0
          "man-in-the-middle attacks.");
1372
0
      options.password_authentication = 0;
1373
0
      cancelled_forwarding = 1;
1374
0
    }
1375
0
    if (options.kbd_interactive_authentication) {
1376
0
      error("Keyboard-interactive authentication is disabled"
1377
0
          " to avoid man-in-the-middle attacks.");
1378
0
      options.kbd_interactive_authentication = 0;
1379
0
      cancelled_forwarding = 1;
1380
0
    }
1381
0
    if (options.forward_agent) {
1382
0
      error("Agent forwarding is disabled to avoid "
1383
0
          "man-in-the-middle attacks.");
1384
0
      options.forward_agent = 0;
1385
0
      cancelled_forwarding = 1;
1386
0
    }
1387
0
    if (options.forward_x11) {
1388
0
      error("X11 forwarding is disabled to avoid "
1389
0
          "man-in-the-middle attacks.");
1390
0
      options.forward_x11 = 0;
1391
0
      cancelled_forwarding = 1;
1392
0
    }
1393
0
    if (options.num_local_forwards > 0 ||
1394
0
        options.num_remote_forwards > 0) {
1395
0
      error("Port forwarding is disabled to avoid "
1396
0
          "man-in-the-middle attacks.");
1397
0
      options.num_local_forwards =
1398
0
          options.num_remote_forwards = 0;
1399
0
      cancelled_forwarding = 1;
1400
0
    }
1401
0
    if (options.tun_open != SSH_TUNMODE_NO) {
1402
0
      error("Tunnel forwarding is disabled to avoid "
1403
0
          "man-in-the-middle attacks.");
1404
0
      options.tun_open = SSH_TUNMODE_NO;
1405
0
      cancelled_forwarding = 1;
1406
0
    }
1407
0
    if (options.update_hostkeys != 0) {
1408
0
      error("UpdateHostkeys is disabled because the host "
1409
0
          "key is not trusted.");
1410
0
      options.update_hostkeys = 0;
1411
0
    }
1412
0
    if (options.exit_on_forward_failure && cancelled_forwarding)
1413
0
      fatal("Error: forwarding disabled due to host key "
1414
0
          "check failure");
1415
1416
    /*
1417
     * XXX Should permit the user to change to use the new id.
1418
     * This could be done by converting the host key to an
1419
     * identifying sentence, tell that the host identifies itself
1420
     * by that sentence, and ask the user if they wish to
1421
     * accept the authentication.
1422
     */
1423
0
    break;
1424
0
  case HOST_FOUND:
1425
0
    fatal("internal error");
1426
0
    break;
1427
0
  }
1428
1429
0
  if (options.check_host_ip && host_status != HOST_CHANGED &&
1430
0
      ip_status == HOST_CHANGED) {
1431
0
    snprintf(msg, sizeof(msg),
1432
0
        "Warning: the %s host key for '%.200s' "
1433
0
        "differs from the key for the IP address '%.128s'"
1434
0
        "\nOffending key for IP in %s:%lu",
1435
0
        type, host, ip, ip_found->file, ip_found->line);
1436
0
    if (host_status == HOST_OK) {
1437
0
      len = strlen(msg);
1438
0
      snprintf(msg + len, sizeof(msg) - len,
1439
0
          "\nMatching host key in %s:%lu",
1440
0
          host_found->file, host_found->line);
1441
0
    }
1442
0
    if (options.strict_host_key_checking ==
1443
0
        SSH_STRICT_HOSTKEY_ASK) {
1444
0
      strlcat(msg, "\nAre you sure you want "
1445
0
          "to continue connecting (yes/no)? ", sizeof(msg));
1446
0
      if (!confirm(msg, NULL))
1447
0
        goto fail;
1448
0
    } else if (options.strict_host_key_checking !=
1449
0
        SSH_STRICT_HOSTKEY_OFF) {
1450
0
      logit("%s", msg);
1451
0
      error("Exiting, you have requested strict checking.");
1452
0
      goto fail;
1453
0
    } else {
1454
0
      logit("%s", msg);
1455
0
    }
1456
0
  }
1457
1458
0
  if (!hostkey_trusted && options.update_hostkeys) {
1459
0
    debug_f("hostkey not known or explicitly trusted: "
1460
0
        "disabling UpdateHostkeys");
1461
0
    options.update_hostkeys = 0;
1462
0
  }
1463
1464
0
  sshkey_free(raw_key);
1465
0
  free(ip);
1466
0
  free(host);
1467
0
  if (host_hostkeys != NULL)
1468
0
    free_hostkeys(host_hostkeys);
1469
0
  if (ip_hostkeys != NULL)
1470
0
    free_hostkeys(ip_hostkeys);
1471
0
  return 0;
1472
1473
0
fail:
1474
0
  if (want_cert && host_status != HOST_REVOKED) {
1475
    /*
1476
     * No matching certificate. Downgrade cert to raw key and
1477
     * search normally.
1478
     */
1479
0
    debug("No matching CA found. Retry with plain key");
1480
0
    if ((r = sshkey_from_private(host_key, &raw_key)) != 0)
1481
0
      fatal_fr(r, "decode key");
1482
0
    if ((r = sshkey_drop_cert(raw_key)) != 0)
1483
0
      fatal_r(r, "Couldn't drop certificate");
1484
0
    host_key = raw_key;
1485
0
    goto retry;
1486
0
  }
1487
0
  sshkey_free(raw_key);
1488
0
  free(ip);
1489
0
  free(host);
1490
0
  if (host_hostkeys != NULL)
1491
0
    free_hostkeys(host_hostkeys);
1492
0
  if (ip_hostkeys != NULL)
1493
0
    free_hostkeys(ip_hostkeys);
1494
0
  return -1;
1495
0
}
1496
1497
/* returns 0 if key verifies or -1 if key does NOT verify */
1498
int
1499
verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key,
1500
    const struct ssh_conn_info *cinfo)
1501
0
{
1502
0
  u_int i;
1503
0
  int r = -1, flags = 0;
1504
0
  char valid[64], *fp = NULL, *cafp = NULL;
1505
0
  struct sshkey *plain = NULL;
1506
1507
0
  if ((fp = sshkey_fingerprint(host_key,
1508
0
      options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
1509
0
    error_fr(r, "fingerprint host key");
1510
0
    r = -1;
1511
0
    goto out;
1512
0
  }
1513
1514
0
  if (sshkey_is_cert(host_key)) {
1515
0
    if ((cafp = sshkey_fingerprint(host_key->cert->signature_key,
1516
0
        options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
1517
0
      error_fr(r, "fingerprint CA key");
1518
0
      r = -1;
1519
0
      goto out;
1520
0
    }
1521
0
    sshkey_format_cert_validity(host_key->cert,
1522
0
        valid, sizeof(valid));
1523
0
    debug("Server host certificate: %s %s, serial %llu "
1524
0
        "ID \"%s\" CA %s %s valid %s",
1525
0
        sshkey_ssh_name(host_key), fp,
1526
0
        (unsigned long long)host_key->cert->serial,
1527
0
        host_key->cert->key_id,
1528
0
        sshkey_ssh_name(host_key->cert->signature_key), cafp,
1529
0
        valid);
1530
0
    for (i = 0; i < host_key->cert->nprincipals; i++) {
1531
0
      debug2("Server host certificate hostname: %s",
1532
0
          host_key->cert->principals[i]);
1533
0
    }
1534
0
  } else {
1535
0
    debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp);
1536
0
  }
1537
1538
0
  if (sshkey_equal(previous_host_key, host_key)) {
1539
0
    debug2_f("server host key %s %s matches cached key",
1540
0
        sshkey_type(host_key), fp);
1541
0
    r = 0;
1542
0
    goto out;
1543
0
  }
1544
1545
  /* Check in RevokedHostKeys files if specified */
1546
0
  for (i = 0; i < options.num_revoked_host_keys; i++) {
1547
0
    r = sshkey_check_revoked(host_key,
1548
0
        options.revoked_host_keys[i]);
1549
0
    switch (r) {
1550
0
    case 0:
1551
0
      break; /* not revoked */
1552
0
    case SSH_ERR_KEY_REVOKED:
1553
0
      error("Host key %s %s revoked by file %s",
1554
0
          sshkey_type(host_key), fp,
1555
0
          options.revoked_host_keys[i]);
1556
0
      r = -1;
1557
0
      goto out;
1558
0
    default:
1559
0
      error_r(r, "Error checking host key %s %s in "
1560
0
          "revoked keys file %s", sshkey_type(host_key),
1561
0
          fp, options.revoked_host_keys[i]);
1562
0
      r = -1;
1563
0
      goto out;
1564
0
    }
1565
0
  }
1566
1567
0
  if (options.verify_host_key_dns) {
1568
    /*
1569
     * XXX certs are not yet supported for DNS, so downgrade
1570
     * them and try the plain key.
1571
     */
1572
0
    if ((r = sshkey_from_private(host_key, &plain)) != 0)
1573
0
      goto out;
1574
0
    if (sshkey_is_cert(plain))
1575
0
      sshkey_drop_cert(plain);
1576
0
    if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) {
1577
0
      if (flags & DNS_VERIFY_FOUND) {
1578
0
        if (options.verify_host_key_dns == 1 &&
1579
0
            flags & DNS_VERIFY_MATCH &&
1580
0
            flags & DNS_VERIFY_SECURE) {
1581
0
          r = 0;
1582
0
          goto out;
1583
0
        }
1584
0
        if (flags & DNS_VERIFY_MATCH) {
1585
0
          matching_host_key_dns = 1;
1586
0
        } else {
1587
0
          warn_changed_key(plain);
1588
0
          error("Update the SSHFP RR in DNS "
1589
0
              "with the new host key to get rid "
1590
0
              "of this message.");
1591
0
        }
1592
0
      }
1593
0
    }
1594
0
  }
1595
0
  r = check_host_key(host, cinfo, hostaddr, options.port, host_key,
1596
0
      RDRW, 0, options.user_hostfiles, options.num_user_hostfiles,
1597
0
      options.system_hostfiles, options.num_system_hostfiles,
1598
0
      options.known_hosts_command);
1599
1600
0
out:
1601
0
  sshkey_free(plain);
1602
0
  free(fp);
1603
0
  free(cafp);
1604
0
  if (r == 0 && host_key != NULL) {
1605
0
    sshkey_free(previous_host_key);
1606
0
    r = sshkey_from_private(host_key, &previous_host_key);
1607
0
  }
1608
1609
0
  return r;
1610
0
}
1611
1612
static void
1613
warn_nonpq_kex(void)
1614
0
{
1615
0
  logit("** WARNING: connection is not using a post-quantum key exchange algorithm.");
1616
0
  logit("** This session may be vulnerable to \"store now, decrypt later\" attacks.");
1617
0
  logit("** The server may need to be upgraded. See https://openssh.com/pq.html");
1618
0
}
1619
1620
/*
1621
 * Starts a dialog with the server, and authenticates the current user on the
1622
 * server.  This does not need any extra privileges.  The basic connection
1623
 * to the server must already have been established before this is called.
1624
 * If login fails, this function prints an error and never returns.
1625
 * This function does not require super-user privileges.
1626
 */
1627
void
1628
ssh_login(struct ssh *ssh, Sensitive *sensitive, const char *orighost,
1629
    struct sockaddr_storage *hostaddr, u_short port, struct passwd *pw,
1630
    int timeout_ms, const struct ssh_conn_info *cinfo)
1631
0
{
1632
0
  char *host;
1633
0
  char *server_user, *local_user;
1634
0
  int r;
1635
1636
0
  local_user = xstrdup(pw->pw_name);
1637
0
  server_user = options.user ? options.user : local_user;
1638
1639
  /* Convert the user-supplied hostname into all lowercase. */
1640
0
  host = xstrdup(orighost);
1641
0
  lowercase(host);
1642
1643
  /* Exchange protocol version identification strings with the server. */
1644
0
  if ((r = kex_exchange_identification(ssh, timeout_ms,
1645
0
      options.version_addendum)) != 0)
1646
0
    sshpkt_fatal(ssh, r, "banner exchange");
1647
1648
0
  if ((ssh->compat & SSH_BUG_NOREKEY)) {
1649
0
    logit("Warning: this server does not support rekeying.");
1650
0
    logit("This session will eventually fail");
1651
0
  }
1652
1653
  /* Put the connection into non-blocking mode. */
1654
0
  ssh_packet_set_nonblocking(ssh);
1655
1656
  /* key exchange */
1657
  /* authenticate user */
1658
0
  debug("Authenticating to %s:%d as '%s'", host, port, server_user);
1659
0
  ssh_kex2(ssh, host, hostaddr, port, cinfo);
1660
0
  kex_set_warn_weak_crypto(ssh, options.warn_weak_crypto &&
1661
0
      !options.kex_algorithms_set);
1662
0
  if (!options.kex_algorithms_set && ssh->kex != NULL &&
1663
0
      ssh->kex->name != NULL && options.warn_weak_crypto &&
1664
0
      !kex_is_pq_from_name(ssh->kex->name)) {
1665
0
    warn_nonpq_kex();
1666
0
    ssh->kex->non_pq_kex_warned = 1;
1667
0
  }
1668
0
  ssh_userauth2(ssh, local_user, server_user, host, sensitive);
1669
0
  free(local_user);
1670
0
  free(host);
1671
0
}
1672
1673
/* print all known host keys for a given host, but skip keys of given type */
1674
static int
1675
show_other_keys(struct hostkeys *hostkeys, struct sshkey *key)
1676
0
{
1677
0
  int type[] = {
1678
0
    KEY_RSA,
1679
0
    KEY_ECDSA,
1680
0
    KEY_ED25519,
1681
0
    KEY_MLDSA44_ED25519,
1682
0
    -1
1683
0
  };
1684
0
  int i, ret = 0;
1685
0
  char *fp, *ra;
1686
0
  const struct hostkey_entry *found;
1687
1688
0
  for (i = 0; type[i] != -1; i++) {
1689
0
    if (type[i] == key->type)
1690
0
      continue;
1691
0
    if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i],
1692
0
        -1, &found))
1693
0
      continue;
1694
0
    fp = sshkey_fingerprint(found->key,
1695
0
        options.fingerprint_hash, SSH_FP_DEFAULT);
1696
0
    ra = sshkey_fingerprint(found->key,
1697
0
        options.fingerprint_hash, SSH_FP_RANDOMART);
1698
0
    if (fp == NULL || ra == NULL)
1699
0
      fatal_f("sshkey_fingerprint fail");
1700
0
    logit("WARNING: %s key found for host %s\n"
1701
0
        "in %s:%lu\n"
1702
0
        "%s key fingerprint %s.",
1703
0
        sshkey_type(found->key),
1704
0
        found->host, found->file, found->line,
1705
0
        sshkey_type(found->key), fp);
1706
0
    if (options.visual_host_key)
1707
0
      logit("%s", ra);
1708
0
    free(ra);
1709
0
    free(fp);
1710
0
    ret = 1;
1711
0
  }
1712
0
  return ret;
1713
0
}
1714
1715
static void
1716
warn_changed_key(struct sshkey *host_key)
1717
0
{
1718
0
  char *fp;
1719
1720
0
  fp = sshkey_fingerprint(host_key, options.fingerprint_hash,
1721
0
      SSH_FP_DEFAULT);
1722
0
  if (fp == NULL)
1723
0
    fatal_f("sshkey_fingerprint fail");
1724
1725
0
  error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1726
0
  error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
1727
0
  error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1728
0
  error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
1729
0
  error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
1730
0
  error("It is also possible that a host key has just been changed.");
1731
0
  error("The fingerprint for the %s key sent by the remote host is\n%s.",
1732
0
      sshkey_type(host_key), fp);
1733
0
  error("Please contact your system administrator.");
1734
1735
0
  free(fp);
1736
0
}
1737
1738
/*
1739
 * Execute a local command
1740
 */
1741
int
1742
ssh_local_cmd(const char *args)
1743
0
{
1744
0
  char *shell;
1745
0
  pid_t pid;
1746
0
  int status;
1747
0
  void (*osighand)(int);
1748
1749
0
  if (!options.permit_local_command ||
1750
0
      args == NULL || !*args)
1751
0
    return (1);
1752
1753
0
  if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
1754
0
    shell = _PATH_BSHELL;
1755
1756
0
  osighand = ssh_signal(SIGCHLD, SIG_DFL);
1757
0
  pid = fork();
1758
0
  if (pid == 0) {
1759
0
    ssh_signal(SIGPIPE, SIG_DFL);
1760
0
    debug3("Executing %s -c \"%s\"", shell, args);
1761
0
    execl(shell, shell, "-c", args, (char *)NULL);
1762
0
    error("Couldn't execute %s -c \"%s\": %s",
1763
0
        shell, args, strerror(errno));
1764
0
    _exit(1);
1765
0
  } else if (pid == -1)
1766
0
    fatal("fork failed: %.100s", strerror(errno));
1767
0
  while (waitpid(pid, &status, 0) == -1)
1768
0
    if (errno != EINTR)
1769
0
      fatal("Couldn't wait for child: %s", strerror(errno));
1770
0
  ssh_signal(SIGCHLD, osighand);
1771
1772
0
  if (!WIFEXITED(status))
1773
0
    return (1);
1774
1775
0
  return (WEXITSTATUS(status));
1776
0
}
1777
1778
void
1779
maybe_add_key_to_agent(const char *authfile, struct sshkey *private,
1780
    const char *comment, const char *passphrase)
1781
0
{
1782
0
  int auth_sock = -1, r;
1783
0
  const char *skprovider = NULL;
1784
1785
0
  if (options.add_keys_to_agent == 0)
1786
0
    return;
1787
1788
0
  if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
1789
0
    debug3("no authentication agent, not adding key");
1790
0
    return;
1791
0
  }
1792
1793
0
  if (options.add_keys_to_agent == 2 &&
1794
0
      !ask_permission("Add key %s (%s) to agent?", authfile, comment)) {
1795
0
    debug3("user denied adding this key");
1796
0
    close(auth_sock);
1797
0
    return;
1798
0
  }
1799
0
  if (sshkey_is_sk(private))
1800
0
    skprovider = options.sk_provider;
1801
0
  if ((r = ssh_add_identity_constrained(auth_sock, private,
1802
0
      comment == NULL ? authfile : comment,
1803
0
      options.add_keys_to_agent_lifespan,
1804
0
      (options.add_keys_to_agent == 3), skprovider, NULL, 0)) == 0)
1805
0
    debug("identity added to agent: %s", authfile);
1806
0
  else
1807
    debug("could not add identity to agent: %s (%d)", authfile, r);
1808
0
  close(auth_sock);
1809
0
}