Coverage Report

Created: 2026-10-13 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssh/readconf.c
Line
Count
Source
1
/* $OpenBSD: readconf.c,v 1.419 2026/10/05 06:03:40 dtucker 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
 * Functions for reading the configuration files.
7
 *
8
 * As far as I am concerned, the code I have written for this software
9
 * can be used freely for any purpose.  Any derived versions of this
10
 * software must be clearly marked as such, and if the derived work is
11
 * incompatible with the protocol description in the RFC file, it must be
12
 * called by a name other than "ssh" or "Secure Shell".
13
 */
14
15
#include "includes.h"
16
17
#include <sys/types.h>
18
#include <sys/stat.h>
19
#include <sys/socket.h>
20
#include <sys/wait.h>
21
#include <sys/un.h>
22
23
#include <net/if.h>
24
#include <netinet/in.h>
25
#include <netinet/ip.h>
26
#include <arpa/inet.h>
27
28
#include <ctype.h>
29
#include <errno.h>
30
#include <glob.h>
31
#include <ifaddrs.h>
32
#include <limits.h>
33
#include <netdb.h>
34
#include <paths.h>
35
#include <pwd.h>
36
#include <signal.h>
37
#include <stdio.h>
38
#include <string.h>
39
#include <stdarg.h>
40
#include <unistd.h>
41
#include <util.h>
42
#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
43
# include <vis.h>
44
#endif
45
46
#include "xmalloc.h"
47
#include "ssh.h"
48
#include "cipher.h"
49
#include "pathnames.h"
50
#include "log.h"
51
#include "sshkey.h"
52
#include "misc.h"
53
#include "readconf.h"
54
#include "match.h"
55
#include "kex.h"
56
#include "mac.h"
57
#include "myproposal.h"
58
#include "digest.h"
59
#include "version.h"
60
61
/* Format of the configuration file:
62
63
   # Configuration data is parsed as follows:
64
   #  1. command line options
65
   #  2. user-specific file
66
   #  3. system-wide file
67
   # Any configuration value is only changed the first time it is set.
68
   # Thus, host-specific definitions should be at the beginning of the
69
   # configuration file, and defaults at the end.
70
71
   # Host-specific declarations.  These may override anything above.  A single
72
   # host may match multiple declarations; these are processed in the order
73
   # that they are given in.
74
75
   Host *.ngs.fi ngs.fi
76
     User foo
77
78
   Host fake.com
79
     Hostname another.host.name.real.org
80
     User blaah
81
     Port 34289
82
     ForwardX11 no
83
     ForwardAgent no
84
85
   Host books.com
86
     RemoteForward 9999 shadows.cs.hut.fi:9999
87
     Ciphers 3des-cbc
88
89
   Host fascist.blob.com
90
     Port 23123
91
     User tylonen
92
     PasswordAuthentication no
93
94
   Host puukko.hut.fi
95
     User t35124p
96
     ProxyCommand ssh-proxy %h %p
97
98
   Host *.fr
99
     PublicKeyAuthentication no
100
101
   Host *.su
102
     Ciphers aes128-ctr
103
     PasswordAuthentication no
104
105
   Host vpn.fake.com
106
     Tunnel yes
107
     TunnelDevice 3
108
109
   # Defaults for various options
110
   Host *
111
     ForwardAgent no
112
     ForwardX11 no
113
     PasswordAuthentication yes
114
     StrictHostKeyChecking yes
115
     TcpKeepAlive no
116
     IdentityFile ~/.ssh/identity
117
     Port 22
118
     EscapeChar ~
119
120
*/
121
122
static int read_config_file_depth(const char *filename, struct passwd *pw,
123
    const char *host, const char *original_host, const char *remote_command,
124
    Options *options, int flags, int *activep, int *want_final_pass, int depth);
125
static int process_config_line_depth(Options *options, struct passwd *pw,
126
    const char *host, const char *original_host, const char *remote_command,
127
    char *line, const char *filename, int linenum, int *activep, int flags,
128
    int *want_final_pass, int depth);
129
130
/* Keyword tokens. */
131
132
typedef enum {
133
  oBadOption,
134
  oHost, oMatch, oInclude, oTag,
135
  oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout,
136
  oGatewayPorts, oExitOnForwardFailure,
137
  oPasswordAuthentication,
138
  oXAuthLocation,
139
  oIdentityFile, oHostname, oPort, oRemoteForward, oLocalForward,
140
  oPermitRemoteOpen,
141
  oCertificateFile, oAddKeysToAgent, oIdentityAgent,
142
  oUser, oEscapeChar, oProxyCommand,
143
  oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
144
  oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
145
  oTCPKeepAlive, oNumberOfPasswordPrompts,
146
  oLogFacility, oLogLevel, oLogVerbose, oCiphers, oMacs,
147
  oPubkeyAuthentication,
148
  oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
149
  oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
150
  oHostKeyAlgorithms, oBindAddress, oBindInterface, oPKCS11Provider,
151
  oClearAllForwardings, oNoHostAuthenticationForLocalhost,
152
  oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
153
  oAddressFamily, oGssAuthentication, oGssDelegateCreds,
154
  oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
155
  oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
156
  oHashKnownHosts,
157
  oTunnel, oTunnelDevice,
158
  oLocalCommand, oPermitLocalCommand, oRemoteCommand,
159
  oVisualHostKey,
160
  oKexAlgorithms, oIPQoS, oRequestTTY, oSessionType, oStdinNull,
161
  oForkAfterAuthentication, oIgnoreUnknown, oProxyUseFdpass,
162
  oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
163
  oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
164
  oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
165
  oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms,
166
  oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump,
167
  oSecurityKeyProvider, oKnownHostsCommand, oRequiredRSASize,
168
  oEnableEscapeCommandline, oObscureKeystrokeTiming, oChannelTimeout,
169
  oVersionAddendum, oRefuseConnection, oWarnWeakCrypto,
170
  oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
171
} OpCodes;
172
173
/* Textual representations of the tokens. */
174
175
static struct {
176
  const char *name;
177
  OpCodes opcode;
178
} keywords[] = {
179
  /* Deprecated options */
180
  { "protocol", oIgnore }, /* NB. silently ignored */
181
  { "cipher", oDeprecated },
182
  { "fallbacktorsh", oDeprecated },
183
  { "globalknownhostsfile2", oDeprecated },
184
  { "rhostsauthentication", oDeprecated },
185
  { "userknownhostsfile2", oDeprecated },
186
  { "useroaming", oDeprecated },
187
  { "usersh", oDeprecated },
188
  { "useprivilegedport", oDeprecated },
189
190
  /* Unsupported options */
191
  { "afstokenpassing", oUnsupported },
192
  { "kerberosauthentication", oUnsupported },
193
  { "kerberostgtpassing", oUnsupported },
194
  { "rsaauthentication", oUnsupported },
195
  { "rhostsrsaauthentication", oUnsupported },
196
  { "compressionlevel", oUnsupported },
197
198
  /* Sometimes-unsupported options */
199
#if defined(GSSAPI)
200
  { "gssapiauthentication", oGssAuthentication },
201
  { "gssapidelegatecredentials", oGssDelegateCreds },
202
# else
203
  { "gssapiauthentication", oUnsupported },
204
  { "gssapidelegatecredentials", oUnsupported },
205
#endif
206
#ifdef ENABLE_PKCS11
207
  { "pkcs11provider", oPKCS11Provider },
208
  { "smartcarddevice", oPKCS11Provider },
209
# else
210
  { "smartcarddevice", oUnsupported },
211
  { "pkcs11provider", oUnsupported },
212
#endif
213
214
  { "forwardagent", oForwardAgent },
215
  { "forwardx11", oForwardX11 },
216
  { "forwardx11trusted", oForwardX11Trusted },
217
  { "forwardx11timeout", oForwardX11Timeout },
218
  { "exitonforwardfailure", oExitOnForwardFailure },
219
  { "xauthlocation", oXAuthLocation },
220
  { "gatewayports", oGatewayPorts },
221
  { "passwordauthentication", oPasswordAuthentication },
222
  { "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
223
  { "kbdinteractivedevices", oKbdInteractiveDevices },
224
  { "challengeresponseauthentication", oKbdInteractiveAuthentication }, /* alias */
225
  { "skeyauthentication", oKbdInteractiveAuthentication }, /* alias */
226
  { "tisauthentication", oKbdInteractiveAuthentication },  /* alias */
227
  { "pubkeyauthentication", oPubkeyAuthentication },
228
  { "dsaauthentication", oPubkeyAuthentication },       /* alias */
229
  { "hostbasedauthentication", oHostbasedAuthentication },
230
  { "identityfile", oIdentityFile },
231
  { "identityfile2", oIdentityFile },     /* obsolete */
232
  { "identitiesonly", oIdentitiesOnly },
233
  { "certificatefile", oCertificateFile },
234
  { "addkeystoagent", oAddKeysToAgent },
235
  { "identityagent", oIdentityAgent },
236
  { "hostname", oHostname },
237
  { "hostkeyalias", oHostKeyAlias },
238
  { "proxycommand", oProxyCommand },
239
  { "port", oPort },
240
  { "ciphers", oCiphers },
241
  { "macs", oMacs },
242
  { "remoteforward", oRemoteForward },
243
  { "localforward", oLocalForward },
244
  { "permitremoteopen", oPermitRemoteOpen },
245
  { "user", oUser },
246
  { "host", oHost },
247
  { "match", oMatch },
248
  { "tag", oTag },
249
  { "escapechar", oEscapeChar },
250
  { "globalknownhostsfile", oGlobalKnownHostsFile },
251
  { "userknownhostsfile", oUserKnownHostsFile },
252
  { "connectionattempts", oConnectionAttempts },
253
  { "batchmode", oBatchMode },
254
  { "checkhostip", oCheckHostIP },
255
  { "stricthostkeychecking", oStrictHostKeyChecking },
256
  { "compression", oCompression },
257
  { "tcpkeepalive", oTCPKeepAlive },
258
  { "keepalive", oTCPKeepAlive },       /* obsolete */
259
  { "numberofpasswordprompts", oNumberOfPasswordPrompts },
260
  { "syslogfacility", oLogFacility },
261
  { "loglevel", oLogLevel },
262
  { "logverbose", oLogVerbose },
263
  { "dynamicforward", oDynamicForward },
264
  { "preferredauthentications", oPreferredAuthentications },
265
  { "hostkeyalgorithms", oHostKeyAlgorithms },
266
  { "casignaturealgorithms", oCASignatureAlgorithms },
267
  { "bindaddress", oBindAddress },
268
  { "bindinterface", oBindInterface },
269
  { "clearallforwardings", oClearAllForwardings },
270
  { "enablesshkeysign", oEnableSSHKeysign },
271
  { "verifyhostkeydns", oVerifyHostKeyDNS },
272
  { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost },
273
  { "rekeylimit", oRekeyLimit },
274
  { "connecttimeout", oConnectTimeout },
275
  { "addressfamily", oAddressFamily },
276
  { "serveraliveinterval", oServerAliveInterval },
277
  { "serveralivecountmax", oServerAliveCountMax },
278
  { "sendenv", oSendEnv },
279
  { "setenv", oSetEnv },
280
  { "controlpath", oControlPath },
281
  { "controlmaster", oControlMaster },
282
  { "controlpersist", oControlPersist },
283
  { "hashknownhosts", oHashKnownHosts },
284
  { "include", oInclude },
285
  { "tunnel", oTunnel },
286
  { "tunneldevice", oTunnelDevice },
287
  { "localcommand", oLocalCommand },
288
  { "permitlocalcommand", oPermitLocalCommand },
289
  { "remotecommand", oRemoteCommand },
290
  { "visualhostkey", oVisualHostKey },
291
  { "kexalgorithms", oKexAlgorithms },
292
  { "ipqos", oIPQoS },
293
  { "requesttty", oRequestTTY },
294
  { "sessiontype", oSessionType },
295
  { "stdinnull", oStdinNull },
296
  { "forkafterauthentication", oForkAfterAuthentication },
297
  { "proxyusefdpass", oProxyUseFdpass },
298
  { "canonicaldomains", oCanonicalDomains },
299
  { "canonicalizefallbacklocal", oCanonicalizeFallbackLocal },
300
  { "canonicalizehostname", oCanonicalizeHostname },
301
  { "canonicalizemaxdots", oCanonicalizeMaxDots },
302
  { "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs },
303
  { "streamlocalbindmask", oStreamLocalBindMask },
304
  { "streamlocalbindunlink", oStreamLocalBindUnlink },
305
  { "revokedhostkeys", oRevokedHostKeys },
306
  { "fingerprinthash", oFingerprintHash },
307
  { "updatehostkeys", oUpdateHostkeys },
308
  { "hostbasedacceptedalgorithms", oHostbasedAcceptedAlgorithms },
309
  { "hostbasedkeytypes", oHostbasedAcceptedAlgorithms }, /* obsolete */
310
  { "pubkeyacceptedalgorithms", oPubkeyAcceptedAlgorithms },
311
  { "pubkeyacceptedkeytypes", oPubkeyAcceptedAlgorithms }, /* obsolete */
312
  { "ignoreunknown", oIgnoreUnknown },
313
  { "proxyjump", oProxyJump },
314
  { "securitykeyprovider", oSecurityKeyProvider },
315
  { "knownhostscommand", oKnownHostsCommand },
316
  { "requiredrsasize", oRequiredRSASize },
317
  { "enableescapecommandline", oEnableEscapeCommandline },
318
  { "obscurekeystroketiming", oObscureKeystrokeTiming },
319
  { "channeltimeout", oChannelTimeout },
320
  { "versionaddendum", oVersionAddendum },
321
  { "refuseconnection", oRefuseConnection },
322
  { "warnweakcrypto", oWarnWeakCrypto },
323
324
  { NULL, oBadOption }
325
};
326
327
static const char *lookup_opcode_name(OpCodes code);
328
329
const char *
330
kex_default_pk_alg(void)
331
0
{
332
0
  static char *pkalgs;
333
334
0
  if (pkalgs == NULL) {
335
0
    char *all_key;
336
337
0
    all_key = sshkey_alg_list(0, 0, 1, ',');
338
0
    pkalgs = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key);
339
0
    free(all_key);
340
0
  }
341
0
  return pkalgs;
342
0
}
343
344
char *
345
ssh_connection_hash(const char *thishost, const char *host, const char *portstr,
346
    const char *user, const char *jumphost)
347
798
{
348
798
  struct ssh_digest_ctx *md;
349
798
  u_char conn_hash[SSH_DIGEST_MAX_LENGTH];
350
351
798
  if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL ||
352
798
      ssh_digest_update(md, thishost, strlen(thishost)) < 0 ||
353
798
      ssh_digest_update(md, host, strlen(host)) < 0 ||
354
798
      ssh_digest_update(md, portstr, strlen(portstr)) < 0 ||
355
798
      ssh_digest_update(md, user, strlen(user)) < 0 ||
356
798
      ssh_digest_update(md, jumphost, strlen(jumphost)) < 0 ||
357
798
      ssh_digest_final(md, conn_hash, sizeof(conn_hash)) < 0)
358
0
    fatal_f("mux digest failed");
359
798
  ssh_digest_free(md);
360
798
  return tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
361
798
}
362
363
/*
364
 * Adds a local TCP/IP port forward to options.  Never returns if there is an
365
 * error.
366
 */
367
368
void
369
add_local_forward(Options *options, const struct Forward *newfwd)
370
0
{
371
0
  struct Forward *fwd;
372
0
  int i;
373
374
  /* Don't add duplicates */
375
0
  for (i = 0; i < options->num_local_forwards; i++) {
376
0
    if (forward_equals(newfwd, options->local_forwards + i))
377
0
      return;
378
0
  }
379
0
  options->local_forwards = xreallocarray(options->local_forwards,
380
0
      options->num_local_forwards + 1,
381
0
      sizeof(*options->local_forwards));
382
0
  fwd = &options->local_forwards[options->num_local_forwards++];
383
384
0
  fwd->listen_host = newfwd->listen_host;
385
0
  fwd->listen_port = newfwd->listen_port;
386
0
  fwd->listen_path = newfwd->listen_path;
387
0
  fwd->connect_host = newfwd->connect_host;
388
0
  fwd->connect_port = newfwd->connect_port;
389
0
  fwd->connect_path = newfwd->connect_path;
390
0
}
391
392
/*
393
 * Adds a remote TCP/IP port forward to options.  Never returns if there is
394
 * an error.
395
 */
396
397
void
398
add_remote_forward(Options *options, const struct Forward *newfwd)
399
4
{
400
4
  struct Forward *fwd;
401
4
  int i;
402
403
  /* Don't add duplicates */
404
4
  for (i = 0; i < options->num_remote_forwards; i++) {
405
0
    if (forward_equals(newfwd, options->remote_forwards + i))
406
0
      return;
407
0
  }
408
4
  options->remote_forwards = xreallocarray(options->remote_forwards,
409
4
      options->num_remote_forwards + 1,
410
4
      sizeof(*options->remote_forwards));
411
4
  fwd = &options->remote_forwards[options->num_remote_forwards++];
412
413
4
  fwd->listen_host = newfwd->listen_host;
414
4
  fwd->listen_port = newfwd->listen_port;
415
4
  fwd->listen_path = newfwd->listen_path;
416
4
  fwd->connect_host = newfwd->connect_host;
417
4
  fwd->connect_port = newfwd->connect_port;
418
4
  fwd->connect_path = newfwd->connect_path;
419
4
  fwd->handle = newfwd->handle;
420
4
  fwd->allocated_port = 0;
421
4
}
422
423
static void
424
clear_forwardings(Options *options)
425
0
{
426
0
  int i;
427
428
0
  for (i = 0; i < options->num_local_forwards; i++) {
429
0
    free(options->local_forwards[i].listen_host);
430
0
    free(options->local_forwards[i].listen_path);
431
0
    free(options->local_forwards[i].connect_host);
432
0
    free(options->local_forwards[i].connect_path);
433
0
  }
434
0
  if (options->num_local_forwards > 0) {
435
0
    free(options->local_forwards);
436
0
    options->local_forwards = NULL;
437
0
  }
438
0
  options->num_local_forwards = 0;
439
0
  for (i = 0; i < options->num_remote_forwards; i++) {
440
0
    free(options->remote_forwards[i].listen_host);
441
0
    free(options->remote_forwards[i].listen_path);
442
0
    free(options->remote_forwards[i].connect_host);
443
0
    free(options->remote_forwards[i].connect_path);
444
0
  }
445
0
  if (options->num_remote_forwards > 0) {
446
0
    free(options->remote_forwards);
447
0
    options->remote_forwards = NULL;
448
0
  }
449
0
  options->num_remote_forwards = 0;
450
0
  options->tun_open = SSH_TUNMODE_NO;
451
0
}
452
453
void
454
add_certificate_file(Options *options, const char *path, int userprovided)
455
0
{
456
0
  int i;
457
458
0
  if (options->num_certificate_files >= SSH_MAX_CERTIFICATE_FILES)
459
0
    fatal("Too many certificate files specified (max %d)",
460
0
        SSH_MAX_CERTIFICATE_FILES);
461
462
  /* Avoid registering duplicates */
463
0
  for (i = 0; i < options->num_certificate_files; i++) {
464
0
    if (options->certificate_file_userprovided[i] == userprovided &&
465
0
        strcmp(options->certificate_files[i], path) == 0) {
466
0
      debug2_f("ignoring duplicate key %s", path);
467
0
      return;
468
0
    }
469
0
  }
470
471
0
  options->certificate_file_userprovided[options->num_certificate_files] =
472
0
      userprovided;
473
0
  options->certificate_files[options->num_certificate_files++] =
474
0
      xstrdup(path);
475
0
}
476
477
void
478
add_identity_file(Options *options, const char *dir, const char *filename,
479
    int userprovided)
480
0
{
481
0
  char *path;
482
0
  int i;
483
484
0
  if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES)
485
0
    fatal("Too many identity files specified (max %d)",
486
0
        SSH_MAX_IDENTITY_FILES);
487
488
0
  if (dir == NULL) /* no dir, filename is absolute */
489
0
    path = xstrdup(filename);
490
0
  else if (xasprintf(&path, "%s%s", dir, filename) >= PATH_MAX)
491
0
    fatal("Identity file path %s too long", path);
492
493
  /* Avoid registering duplicates */
494
0
  for (i = 0; i < options->num_identity_files; i++) {
495
0
    if (options->identity_file_userprovided[i] == userprovided &&
496
0
        strcmp(options->identity_files[i], path) == 0) {
497
0
      debug2_f("ignoring duplicate key %s", path);
498
0
      free(path);
499
0
      return;
500
0
    }
501
0
  }
502
503
0
  options->identity_file_userprovided[options->num_identity_files] =
504
0
      userprovided;
505
0
  options->identity_files[options->num_identity_files++] = path;
506
0
}
507
508
int
509
default_ssh_port(void)
510
789
{
511
789
  static int port;
512
789
  struct servent *sp;
513
514
789
  if (port == 0) {
515
1
    sp = getservbyname(SSH_SERVICE_NAME, "tcp");
516
1
    port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
517
1
  }
518
789
  return port;
519
789
}
520
521
/*
522
 * Execute a command in a shell.
523
 * Return its exit status or -1 on abnormal exit.
524
 */
525
static int
526
execute_in_shell(const char *cmd)
527
0
{
528
0
  char *shell;
529
0
  pid_t pid;
530
0
  int status;
531
532
0
  if ((shell = getenv("SHELL")) == NULL)
533
0
    shell = _PATH_BSHELL;
534
535
0
  if (access(shell, X_OK) == -1) {
536
0
    fatal("Shell \"%s\" is not executable: %s",
537
0
        shell, strerror(errno));
538
0
  }
539
540
0
  debug("Executing command: '%.500s'", cmd);
541
542
  /* Fork and execute the command. */
543
0
  if ((pid = fork()) == 0) {
544
0
    char *argv[4];
545
546
0
    if (stdfd_devnull(1, 1, 0) == -1)
547
0
      fatal_f("stdfd_devnull failed");
548
0
    closefrom(STDERR_FILENO + 1);
549
550
0
    argv[0] = shell;
551
0
    argv[1] = "-c";
552
0
    argv[2] = xstrdup(cmd);
553
0
    argv[3] = NULL;
554
555
0
    execv(argv[0], argv);
556
0
    error("Unable to execute '%.100s': %s", cmd, strerror(errno));
557
    /* Die with signal to make this error apparent to parent. */
558
0
    ssh_signal(SIGTERM, SIG_DFL);
559
0
    kill(getpid(), SIGTERM);
560
0
    _exit(1);
561
0
  }
562
  /* Parent. */
563
0
  if (pid == -1)
564
0
    fatal_f("fork: %.100s", strerror(errno));
565
566
0
  while (waitpid(pid, &status, 0) == -1) {
567
0
    if (errno != EINTR && errno != EAGAIN)
568
0
      fatal_f("waitpid: %s", strerror(errno));
569
0
  }
570
0
  if (!WIFEXITED(status)) {
571
0
    error("command '%.100s' exited abnormally", cmd);
572
0
    return -1;
573
0
  }
574
0
  debug3("command returned status %d", WEXITSTATUS(status));
575
0
  return WEXITSTATUS(status);
576
0
}
577
578
/*
579
 * Check whether a local network interface address appears in CIDR pattern-
580
 * list 'addrlist'. Returns 1 if matched or 0 otherwise.
581
 */
582
static int
583
check_match_ifaddrs(const char *addrlist)
584
0
{
585
0
#ifdef HAVE_IFADDRS_H
586
0
  struct ifaddrs *ifa, *ifaddrs = NULL;
587
0
  int r, found = 0;
588
0
  char addr[NI_MAXHOST];
589
0
  socklen_t salen;
590
591
0
  if (getifaddrs(&ifaddrs) != 0) {
592
0
    error("match localnetwork: getifaddrs failed: %s",
593
0
        strerror(errno));
594
0
    return 0;
595
0
  }
596
0
  for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
597
0
    if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL ||
598
0
        (ifa->ifa_flags & IFF_UP) == 0)
599
0
      continue;
600
0
    switch (ifa->ifa_addr->sa_family) {
601
0
    case AF_INET:
602
0
      salen = sizeof(struct sockaddr_in);
603
0
      break;
604
0
    case AF_INET6:
605
0
      salen = sizeof(struct sockaddr_in6);
606
0
      break;
607
#ifdef AF_LINK
608
    case AF_LINK:
609
      /* ignore */
610
      continue;
611
#endif /* AF_LINK */
612
0
    default:
613
0
      debug2_f("interface %s: unsupported address family %d",
614
0
          ifa->ifa_name, ifa->ifa_addr->sa_family);
615
0
      continue;
616
0
    }
617
0
    if ((r = getnameinfo(ifa->ifa_addr, salen, addr, sizeof(addr),
618
0
        NULL, 0, NI_NUMERICHOST)) != 0) {
619
0
      debug2_f("interface %s getnameinfo failed: %s",
620
0
          ifa->ifa_name, gai_strerror(r));
621
0
      continue;
622
0
    }
623
0
    debug3_f("interface %s addr %s", ifa->ifa_name, addr);
624
0
    if (addr_match_cidr_list(addr, addrlist) == 1) {
625
0
      debug3_f("matched interface %s: address %s in %s",
626
0
          ifa->ifa_name, addr, addrlist);
627
0
      found = 1;
628
0
      break;
629
0
    }
630
0
  }
631
0
  freeifaddrs(ifaddrs);
632
0
  return found;
633
#else /* HAVE_IFADDRS_H */
634
  error("match localnetwork: not supported on this platform");
635
  return 0;
636
#endif /* HAVE_IFADDRS_H */
637
0
}
638
639
/*
640
 * Expand a "match exec" command or an Include path, caller must free returned
641
 * value.
642
 */
643
static char *
644
expand_match_exec_or_include_path(const char *path, Options *options,
645
    struct passwd *pw, const char *host_arg, const char *original_host,
646
    int final_pass, int is_include_path)
647
798
{
648
798
  char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
649
798
  char uidstr[32], *conn_hash_hex, *keyalias, *jmphost, *ruser;
650
798
  char *host, *ret;
651
798
  int port;
652
653
798
  port = options->port <= 0 ? default_ssh_port() : options->port;
654
798
  ruser = options->user == NULL ? pw->pw_name : options->user;
655
798
  if (final_pass) {
656
0
    host = xstrdup(options->hostname);
657
798
  } else if (options->hostname != NULL) {
658
    /* NB. Please keep in sync with ssh.c:main() */
659
0
    host = percent_expand(options->hostname,
660
0
        "h", host_arg, (char *)NULL);
661
798
  } else {
662
798
    host = xstrdup(host_arg);
663
798
  }
664
798
  if (gethostname(thishost, sizeof(thishost)) == -1)
665
0
    fatal("gethostname: %s", strerror(errno));
666
798
  jmphost = option_clear_or_none(options->jump_host) ?
667
798
      "" : options->jump_host;
668
798
  strlcpy(shorthost, thishost, sizeof(shorthost));
669
798
  shorthost[strcspn(thishost, ".")] = '\0';
670
798
  snprintf(portstr, sizeof(portstr), "%d", port);
671
798
  snprintf(uidstr, sizeof(uidstr), "%llu",
672
798
      (unsigned long long)pw->pw_uid);
673
798
  conn_hash_hex = ssh_connection_hash(thishost, host,
674
798
      portstr, ruser, jmphost);
675
798
  keyalias = options->host_key_alias ?  options->host_key_alias : host;
676
677
798
  ret = (is_include_path ? percent_dollar_expand : percent_expand)(path,
678
798
      "C", conn_hash_hex,
679
798
      "L", shorthost,
680
798
      "d", pw->pw_dir,
681
798
      "h", host,
682
798
      "k", keyalias,
683
798
      "l", thishost,
684
798
      "n", original_host,
685
798
      "p", portstr,
686
798
      "r", ruser,
687
798
      "u", pw->pw_name,
688
798
      "i", uidstr,
689
798
      "j", jmphost,
690
798
      (char *)NULL);
691
798
  free(host);
692
798
  free(conn_hash_hex);
693
798
  return ret;
694
798
}
695
696
/*
697
 * Parse and execute a Match directive.
698
 */
699
static int
700
match_cfg_line(Options *options, const char *full_line, int *acp, char ***avp,
701
    struct passwd *pw, const char *host_arg, const char *original_host,
702
    const char *remote_command, int final_pass, int *want_final_pass,
703
    const char *filename, int linenum)
704
0
{
705
0
  char *arg, *oattrib = NULL, *attrib = NULL, *cmd, *host, *criteria;
706
0
  const char *ruser;
707
0
  int r, this_result, result = 1, attributes = 0, negate;
708
709
  /*
710
   * Configuration is likely to be incomplete at this point so we
711
   * must be prepared to use default values.
712
   */
713
0
  ruser = options->user == NULL ? pw->pw_name : options->user;
714
0
  if (final_pass) {
715
0
    host = xstrdup(options->hostname);
716
0
  } else if (options->hostname != NULL) {
717
    /* NB. Please keep in sync with ssh.c:main() */
718
0
    host = percent_expand(options->hostname,
719
0
        "h", host_arg, (char *)NULL);
720
0
  } else {
721
0
    host = xstrdup(host_arg);
722
0
  }
723
724
0
  debug2("checking match for '%s' host %s originally %s",
725
0
      full_line, host, original_host);
726
0
  while ((attrib = argv_next(acp, avp)) != NULL) {
727
    /* Terminate on comment */
728
0
    if (*attrib == '#') {
729
0
      argv_consume(acp);
730
0
      break;
731
0
    }
732
0
    attrib = oattrib = xstrdup(attrib);
733
0
    arg = criteria = NULL;
734
0
    this_result = 1;
735
0
    if ((negate = (attrib[0] == '!')))
736
0
      attrib++;
737
    /* Criterion "all" has no argument and must appear alone */
738
0
    if (strcasecmp(attrib, "all") == 0) {
739
0
      if (attributes > 1 ||
740
0
          ((arg = argv_next(acp, avp)) != NULL &&
741
0
          *arg != '\0' && *arg != '#')) {
742
0
        error("%.200s line %d: '%s' cannot be combined "
743
0
            "with other Match attributes",
744
0
            filename, linenum, oattrib);
745
0
        result = -1;
746
0
        goto out;
747
0
      }
748
0
      if (arg != NULL && *arg == '#')
749
0
        argv_consume(acp); /* consume remaining args */
750
0
      if (result)
751
0
        result = negate ? 0 : 1;
752
0
      goto out;
753
0
    }
754
0
    attributes++;
755
    /* criteria "final" and "canonical" have no argument */
756
0
    if (strcasecmp(attrib, "canonical") == 0 ||
757
0
        strcasecmp(attrib, "final") == 0) {
758
      /*
759
       * If the config requests "Match final" without
760
       * negation then remember this so we can perform a
761
       * second pass later.
762
       */
763
0
      if (strcasecmp(attrib, "final") == 0 &&
764
0
          want_final_pass != NULL)
765
0
        *want_final_pass |= !negate;
766
0
      r = !!final_pass;  /* force bitmask member to boolean */
767
0
      if (r == (negate ? 1 : 0))
768
0
        this_result = result = 0;
769
0
      debug3("%.200s line %d: %smatched '%s'",
770
0
          filename, linenum,
771
0
          this_result ? "" : "not ", oattrib);
772
0
      goto next;
773
0
    }
774
775
    /* Keep this list in sync with below */
776
0
    if (strprefix(attrib, "host=", 1)  != NULL ||
777
0
        strprefix(attrib, "originalhost=", 1) != NULL ||
778
0
        strprefix(attrib, "user=", 1) != NULL ||
779
0
        strprefix(attrib, "localuser=", 1) != NULL ||
780
0
        strprefix(attrib, "localnetwork=", 1) != NULL ||
781
0
        strprefix(attrib, "version=", 1) != NULL ||
782
0
        strprefix(attrib, "tagged=", 1) != NULL ||
783
0
        strprefix(attrib, "command=", 1) != NULL ||
784
0
        strprefix(attrib, "exec=", 1) != NULL) {
785
0
      arg = strchr(attrib, '=');
786
0
      *(arg++) = '\0';
787
0
    } else if ((arg = argv_next(acp, avp)) == NULL) {
788
0
      error("%.200s line %d: missing argument for Match '%s'",
789
0
          filename, linenum, oattrib);
790
0
      result = -1;
791
0
      goto out;
792
0
    }
793
794
    /*
795
     * All other criteria require an argument, though it may
796
     * be the empty string for the "tagged" and "command"
797
     * options.
798
     */
799
0
    if (*arg == '\0' &&
800
0
        strcasecmp(attrib, "tagged") != 0 &&
801
0
        strcasecmp(attrib, "command") != 0)
802
0
      arg = NULL;
803
0
    if (arg == NULL || *arg == '#') {
804
0
      error("Missing Match criteria for %s", attrib);
805
0
      result = -1;
806
0
      goto out;
807
0
    }
808
0
    if (strcasecmp(attrib, "host") == 0) {
809
0
      criteria = xstrdup(host);
810
0
      r = match_hostname(host, arg) == 1;
811
0
      if (r == (negate ? 1 : 0))
812
0
        this_result = result = 0;
813
0
    } else if (strcasecmp(attrib, "originalhost") == 0) {
814
0
      criteria = xstrdup(original_host);
815
0
      r = match_hostname(original_host, arg) == 1;
816
0
      if (r == (negate ? 1 : 0))
817
0
        this_result = result = 0;
818
0
    } else if (strcasecmp(attrib, "user") == 0) {
819
0
      criteria = xstrdup(ruser);
820
0
      r = match_pattern_list(ruser, arg, 0) == 1;
821
0
      if (r == (negate ? 1 : 0))
822
0
        this_result = result = 0;
823
0
    } else if (strcasecmp(attrib, "localuser") == 0) {
824
0
      criteria = xstrdup(pw->pw_name);
825
0
      r = match_pattern_list(pw->pw_name, arg, 0) == 1;
826
0
      if (r == (negate ? 1 : 0))
827
0
        this_result = result = 0;
828
0
    } else if (strcasecmp(attrib, "localnetwork") == 0) {
829
0
      if (addr_match_cidr_list(NULL, arg) == -1) {
830
        /* Error already printed */
831
0
        result = -1;
832
0
        goto out;
833
0
      }
834
0
      r = check_match_ifaddrs(arg) == 1;
835
0
      if (r == (negate ? 1 : 0))
836
0
        this_result = result = 0;
837
0
    } else if (strcasecmp(attrib, "version") == 0) {
838
0
      criteria = xstrdup(SSH_RELEASE);
839
0
      r = match_pattern_list(SSH_RELEASE, arg, 0) == 1;
840
0
      if (r == (negate ? 1 : 0))
841
0
        this_result = result = 0;
842
0
    } else if (strcasecmp(attrib, "tagged") == 0) {
843
0
      criteria = xstrdup(options->tag == NULL ? "" :
844
0
          options->tag);
845
      /* Special case: empty criteria matches empty arg */
846
0
      r = (*criteria == '\0') ? *arg == '\0' :
847
0
          match_pattern_list(criteria, arg, 0) == 1;
848
0
      if (r == (negate ? 1 : 0))
849
0
        this_result = result = 0;
850
0
    } else if (strcasecmp(attrib, "command") == 0) {
851
0
      criteria = xstrdup(remote_command == NULL ?
852
0
          "" : remote_command);
853
      /* Special case: empty criteria matches empty arg */
854
0
      r = (*criteria == '\0') ? *arg == '\0' :
855
0
          match_pattern_list(criteria, arg, 0) == 1;
856
0
      if (r == (negate ? 1 : 0))
857
0
        this_result = result = 0;
858
0
    } else if (strcasecmp(attrib, "sessiontype") == 0) {
859
0
      if (options->session_type == SESSION_TYPE_SUBSYSTEM)
860
0
        criteria = xstrdup("subsystem");
861
0
      else if (options->session_type == SESSION_TYPE_NONE)
862
0
        criteria = xstrdup("none");
863
0
      else if (remote_command != NULL &&
864
0
          *remote_command != '\0')
865
0
        criteria = xstrdup("exec");
866
0
      else
867
0
        criteria = xstrdup("shell");
868
0
      r = match_pattern_list(criteria, arg, 0) == 1;
869
0
      if (r == (negate ? 1 : 0))
870
0
        this_result = result = 0;
871
0
    } else if (strcasecmp(attrib, "exec") == 0) {
872
0
      if ((cmd = expand_match_exec_or_include_path(arg,
873
0
          options, pw, host_arg, original_host,
874
0
          final_pass, 0)) == NULL) {
875
0
        fatal("%.200s line %d: failed to expand match "
876
0
            "exec '%.100s'", filename, linenum, arg);
877
0
      }
878
0
      if (result != 1) {
879
        /* skip execution if prior predicate failed */
880
0
        debug3("%.200s line %d: skipped exec "
881
0
            "\"%.100s\"", filename, linenum, cmd);
882
0
        free(cmd);
883
0
        goto next;
884
0
      }
885
0
      r = execute_in_shell(cmd);
886
0
      if (r == -1) {
887
0
        fatal("%.200s line %d: match exec "
888
0
            "'%.100s' error", filename,
889
0
            linenum, cmd);
890
0
      }
891
0
      criteria = xstrdup(cmd);
892
0
      free(cmd);
893
      /* Force exit status to boolean */
894
0
      r = r == 0;
895
0
      if (r == (negate ? 1 : 0))
896
0
        this_result = result = 0;
897
0
    } else {
898
0
      error("Unsupported Match attribute %s", attrib);
899
0
      result = -1;
900
0
      goto out;
901
0
    }
902
0
    debug3("%.200s line %d: %smatched '%s%s%.100s%s' ",
903
0
        filename, linenum, this_result ? "": "not ", oattrib,
904
0
        criteria == NULL ? "" : " \"",
905
0
        criteria == NULL ? "" : criteria,
906
0
        criteria == NULL ? "" : "\"");
907
0
 next:
908
0
    free(criteria);
909
0
    free(oattrib);
910
0
    oattrib = attrib = NULL;
911
0
  }
912
0
  if (attributes == 0) {
913
0
    error("One or more attributes required for Match");
914
0
    result = -1;
915
0
    goto out;
916
0
  }
917
0
 out:
918
0
  if (result != -1)
919
0
    debug2("match %sfound", result ? "" : "not ");
920
0
  free(oattrib);
921
0
  free(host);
922
0
  return result;
923
0
}
924
925
/* Remove environment variable by pattern */
926
static void
927
rm_env(Options *options, const char *arg, const char *filename, int linenum)
928
2.36k
{
929
2.36k
  u_int i, j, onum_send_env = options->num_send_env;
930
931
  /* Remove an environment variable */
932
10.8k
  for (i = 0; i < options->num_send_env; ) {
933
8.51k
    if (!match_pattern(options->send_env[i], arg + 1)) {
934
8.12k
      i++;
935
8.12k
      continue;
936
8.12k
    }
937
395
    debug3("%s line %d: removing environment %s",
938
395
        filename, linenum, options->send_env[i]);
939
395
    free(options->send_env[i]);
940
395
    options->send_env[i] = NULL;
941
516
    for (j = i; j < options->num_send_env - 1; j++) {
942
121
      options->send_env[j] = options->send_env[j + 1];
943
121
      options->send_env[j + 1] = NULL;
944
121
    }
945
395
    options->num_send_env--;
946
    /* NB. don't increment i */
947
395
  }
948
2.36k
  if (onum_send_env != options->num_send_env) {
949
349
    options->send_env = xrecallocarray(options->send_env,
950
349
        onum_send_env, options->num_send_env,
951
349
        sizeof(*options->send_env));
952
349
  }
953
2.36k
}
954
955
/*
956
 * Returns the number of the token pointed to by cp or oBadOption.
957
 */
958
static OpCodes
959
parse_token(const char *cp, const char *filename, int linenum,
960
    const char *ignored_unknown)
961
424
{
962
424
  int i;
963
964
31.0k
  for (i = 0; keywords[i].name; i++)
965
31.0k
    if (strcmp(cp, keywords[i].name) == 0)
966
424
      return keywords[i].opcode;
967
0
  if (ignored_unknown != NULL &&
968
0
      match_pattern_list(cp, ignored_unknown, 1) == 1)
969
0
    return oIgnoredUnknownOption;
970
0
  error("%s: line %d: Bad configuration option: %s",
971
0
      filename, linenum, cp);
972
0
  return oBadOption;
973
0
}
974
975
static void
976
free_canon_cnames(struct allowed_cname *cnames, u_int n)
977
424
{
978
424
  u_int i;
979
980
424
  if (cnames == NULL || n == 0)
981
424
    return;
982
0
  for (i = 0; i < n; i++) {
983
0
    free(cnames[i].source_list);
984
0
    free(cnames[i].target_list);
985
0
  }
986
0
  free(cnames);
987
0
}
988
989
/* Multistate option parsing */
990
struct multistate {
991
  char *key;
992
  int value;
993
};
994
static const struct multistate multistate_flag[] = {
995
  { "true",     1 },
996
  { "false",      0 },
997
  { "yes",      1 },
998
  { "no",       0 },
999
  { NULL, -1 }
1000
};
1001
static const struct multistate multistate_yesnoask[] = {
1002
  { "true",     1 },
1003
  { "false",      0 },
1004
  { "yes",      1 },
1005
  { "no",       0 },
1006
  { "ask",      2 },
1007
  { NULL, -1 }
1008
};
1009
static const struct multistate multistate_strict_hostkey[] = {
1010
  { "true",     SSH_STRICT_HOSTKEY_YES },
1011
  { "false",      SSH_STRICT_HOSTKEY_OFF },
1012
  { "yes",      SSH_STRICT_HOSTKEY_YES },
1013
  { "no",       SSH_STRICT_HOSTKEY_OFF },
1014
  { "ask",      SSH_STRICT_HOSTKEY_ASK },
1015
  { "off",      SSH_STRICT_HOSTKEY_OFF },
1016
  { "accept-new",     SSH_STRICT_HOSTKEY_NEW },
1017
  { NULL, -1 }
1018
};
1019
static const struct multistate multistate_yesnoaskconfirm[] = {
1020
  { "true",     1 },
1021
  { "false",      0 },
1022
  { "yes",      1 },
1023
  { "no",       0 },
1024
  { "ask",      2 },
1025
  { "confirm",      3 },
1026
  { NULL, -1 }
1027
};
1028
static const struct multistate multistate_addressfamily[] = {
1029
  { "inet",     AF_INET },
1030
  { "inet6",      AF_INET6 },
1031
  { "any",      AF_UNSPEC },
1032
  { NULL, -1 }
1033
};
1034
static const struct multistate multistate_controlmaster[] = {
1035
  { "true",     SSHCTL_MASTER_YES },
1036
  { "yes",      SSHCTL_MASTER_YES },
1037
  { "false",      SSHCTL_MASTER_NO },
1038
  { "no",       SSHCTL_MASTER_NO },
1039
  { "auto",     SSHCTL_MASTER_AUTO },
1040
  { "ask",      SSHCTL_MASTER_ASK },
1041
  { "autoask",      SSHCTL_MASTER_AUTO_ASK },
1042
  { NULL, -1 }
1043
};
1044
static const struct multistate multistate_tunnel[] = {
1045
  { "ethernet",     SSH_TUNMODE_ETHERNET },
1046
  { "point-to-point",   SSH_TUNMODE_POINTOPOINT },
1047
  { "true",     SSH_TUNMODE_DEFAULT },
1048
  { "yes",      SSH_TUNMODE_DEFAULT },
1049
  { "false",      SSH_TUNMODE_NO },
1050
  { "no",       SSH_TUNMODE_NO },
1051
  { NULL, -1 }
1052
};
1053
static const struct multistate multistate_requesttty[] = {
1054
  { "true",     REQUEST_TTY_YES },
1055
  { "yes",      REQUEST_TTY_YES },
1056
  { "false",      REQUEST_TTY_NO },
1057
  { "no",       REQUEST_TTY_NO },
1058
  { "force",      REQUEST_TTY_FORCE },
1059
  { "auto",     REQUEST_TTY_AUTO },
1060
  { NULL, -1 }
1061
};
1062
static const struct multistate multistate_sessiontype[] = {
1063
  { "none",     SESSION_TYPE_NONE },
1064
  { "subsystem",      SESSION_TYPE_SUBSYSTEM },
1065
  { "default",      SESSION_TYPE_DEFAULT },
1066
  { NULL, -1 }
1067
};
1068
static const struct multistate multistate_canonicalizehostname[] = {
1069
  { "true",     SSH_CANONICALISE_YES },
1070
  { "false",      SSH_CANONICALISE_NO },
1071
  { "yes",      SSH_CANONICALISE_YES },
1072
  { "no",       SSH_CANONICALISE_NO },
1073
  { "always",     SSH_CANONICALISE_ALWAYS },
1074
  { NULL, -1 }
1075
};
1076
static const struct multistate multistate_pubkey_auth[] = {
1077
  { "true",     SSH_PUBKEY_AUTH_ALL },
1078
  { "false",      SSH_PUBKEY_AUTH_NO },
1079
  { "yes",      SSH_PUBKEY_AUTH_ALL },
1080
  { "no",       SSH_PUBKEY_AUTH_NO },
1081
  { "unbound",      SSH_PUBKEY_AUTH_UNBOUND },
1082
  { "host-bound",     SSH_PUBKEY_AUTH_HBOUND },
1083
  { NULL, -1 }
1084
};
1085
static const struct multistate multistate_compression[] = {
1086
#ifdef WITH_ZLIB
1087
  { "yes",      COMP_DELAYED },
1088
#endif
1089
  { "no",       COMP_NONE },
1090
  { NULL, -1 }
1091
};
1092
static const struct multistate multistate_keepalives[] = {
1093
  { "true",     SSH_KEEPALIVES_TRANSPORT },
1094
  { "false",      SSH_KEEPALIVES_OFF },
1095
  { "yes",      SSH_KEEPALIVES_TRANSPORT },
1096
  { "no",       SSH_KEEPALIVES_OFF },
1097
  { "transport",      SSH_KEEPALIVES_TRANSPORT },
1098
  { "all",      SSH_KEEPALIVES_ALL },
1099
  { NULL, -1 }
1100
};
1101
/* XXX this will need to be replaced with a bitmask if we add more flags */
1102
static const struct multistate multistate_warnweakcrypto[] = {
1103
  { "true",     1 },
1104
  { "false",      0 },
1105
  { "yes",      1 },
1106
  { "no",       0 },
1107
  { "no-pq-kex",      0 },
1108
  { NULL, -1 }
1109
};
1110
1111
static int
1112
parse_multistate_value(const char *arg, const char *filename, int linenum,
1113
    const struct multistate *multistate_ptr)
1114
1
{
1115
1
  int i;
1116
1117
1
  if (!arg || *arg == '\0') {
1118
0
    error("%s line %d: missing argument.", filename, linenum);
1119
0
    return -1;
1120
0
  }
1121
7
  for (i = 0; multistate_ptr[i].key != NULL; i++) {
1122
6
    if (strcasecmp(arg, multistate_ptr[i].key) == 0)
1123
0
      return multistate_ptr[i].value;
1124
6
  }
1125
1
  return -1;
1126
1
}
1127
1128
/*
1129
 * Processes a single option line as used in the configuration files. This
1130
 * only sets those values that have not already been set.
1131
 */
1132
int
1133
process_config_line(Options *options, struct passwd *pw, const char *host,
1134
    const char *original_host, const char *remote_command, char *line,
1135
    const char *filename, int linenum, int *activep, int flags)
1136
0
{
1137
0
  return process_config_line_depth(options, pw, host, original_host,
1138
0
      remote_command, line, filename, linenum, activep, flags, NULL, 0);
1139
0
}
1140
1141
2.33k
#define WHITESPACE " \t\r\n"
1142
static int
1143
process_config_line_depth(Options *options, struct passwd *pw, const char *host,
1144
    const char *original_host, const char *remote_command, char *line,
1145
    const char *filename, int linenum, int *activep, int flags,
1146
    int *want_final_pass, int depth)
1147
1.49k
{
1148
1.49k
  char *str, **charptr, *endofnumber, *keyword, *arg, *arg2, *p;
1149
1.49k
  char **cpptr, ***cppptr, fwdarg[256];
1150
1.49k
  u_int i, *uintptr, max_entries = 0;
1151
1.49k
  int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0;
1152
1.49k
  int remotefwd, dynamicfwd, ca_only = 0, found = 0;
1153
1.49k
  LogLevel *log_level_ptr;
1154
1.49k
  SyslogFacility *log_facility_ptr;
1155
1.49k
  long long val64;
1156
1.49k
  size_t len;
1157
1.49k
  struct Forward fwd;
1158
1.49k
  const struct multistate *multistate_ptr;
1159
1.49k
  glob_t gl;
1160
1.49k
  const char *errstr;
1161
1.49k
  char **oav = NULL, **av;
1162
1.49k
  int oac = 0, ac;
1163
1.49k
  int ret = -1;
1164
1.49k
  struct allowed_cname *cnames = NULL;
1165
1.49k
  u_int ncnames = 0;
1166
1.49k
  char **strs = NULL; /* string array arguments; freed implicitly */
1167
1.49k
  u_int nstrs = 0;
1168
1169
1.49k
  if (activep == NULL) { /* We are processing a command line directive */
1170
0
    cmdline = 1;
1171
0
    activep = &cmdline;
1172
0
  }
1173
1174
  /* Strip trailing whitespace. Allow \f (form feed) at EOL only */
1175
1.49k
  if ((len = strlen(line)) == 0)
1176
213
    return 0;
1177
2.64k
  for (len--; len > 0; len--) {
1178
1.89k
    if (strchr(WHITESPACE "\f", line[len]) == NULL)
1179
531
      break;
1180
1.36k
    line[len] = '\0';
1181
1.36k
  }
1182
1183
1.27k
  str = line;
1184
  /* Get the keyword. (Each line is supposed to begin with a keyword). */
1185
1.27k
  if ((keyword = strdelim(&str)) == NULL)
1186
84
    return 0;
1187
  /* Ignore leading whitespace. */
1188
1.19k
  if (*keyword == '\0')
1189
978
    keyword = strdelim(&str);
1190
1.19k
  if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#')
1191
770
    return 0;
1192
  /* Match lowercase keyword */
1193
424
  lowercase(keyword);
1194
1195
  /* Prepare to parse remainder of line */
1196
424
  if (str != NULL)
1197
424
    str += strspn(str, WHITESPACE);
1198
424
  if (str == NULL || *str == '\0') {
1199
0
    error("%s line %d: no argument after keyword \"%s\"",
1200
0
        filename, linenum, keyword);
1201
0
    return -1;
1202
0
  }
1203
424
  opcode = parse_token(keyword, filename, linenum,
1204
424
      options->ignored_unknown);
1205
424
  if (argv_split(str, &oac, &oav, 1) != 0) {
1206
0
    error("%s line %d: invalid quotes", filename, linenum);
1207
0
    return -1;
1208
0
  }
1209
424
  ac = oac;
1210
424
  av = oav;
1211
1212
424
  switch (opcode) {
1213
0
  case oBadOption:
1214
    /* don't panic, but count bad options */
1215
0
    goto out;
1216
16
  case oIgnore:
1217
16
    argv_consume(&ac);
1218
16
    break;
1219
0
  case oIgnoredUnknownOption:
1220
0
    debug("%s line %d: Ignored unknown option \"%s\"",
1221
0
        filename, linenum, keyword);
1222
0
    argv_consume(&ac);
1223
0
    break;
1224
0
  case oConnectTimeout:
1225
0
    intptr = &options->connection_timeout;
1226
23
parse_time:
1227
23
    arg = argv_next(&ac, &av);
1228
23
    if (!arg || *arg == '\0') {
1229
0
      error("%s line %d: missing time value.",
1230
0
          filename, linenum);
1231
0
      goto out;
1232
0
    }
1233
23
    if (strcmp(arg, "none") == 0)
1234
0
      value = -1;
1235
23
    else if ((value = convtime(arg)) == -1) {
1236
0
      error("%s line %d: invalid time value.",
1237
0
          filename, linenum);
1238
0
      goto out;
1239
0
    }
1240
23
    if (*activep && *intptr == -1)
1241
12
      *intptr = value;
1242
23
    break;
1243
1244
0
  case oForwardAgent:
1245
0
    intptr = &options->forward_agent;
1246
1247
0
    arg = argv_next(&ac, &av);
1248
0
    if (!arg || *arg == '\0') {
1249
0
      error("%s line %d: missing argument.",
1250
0
          filename, linenum);
1251
0
      goto out;
1252
0
    }
1253
1254
0
    value = -1;
1255
0
    multistate_ptr = multistate_flag;
1256
0
    for (i = 0; multistate_ptr[i].key != NULL; i++) {
1257
0
      if (strcasecmp(arg, multistate_ptr[i].key) == 0) {
1258
0
        value = multistate_ptr[i].value;
1259
0
        break;
1260
0
      }
1261
0
    }
1262
0
    if (value != -1) {
1263
0
      if (*activep && *intptr == -1)
1264
0
        *intptr = value;
1265
0
      break;
1266
0
    }
1267
    /* ForwardAgent wasn't 'yes' or 'no', assume a path */
1268
0
    if (*activep && *intptr == -1)
1269
0
      *intptr = 1;
1270
1271
0
    charptr = &options->forward_agent_sock_path;
1272
0
    goto parse_agent_path;
1273
1274
0
  case oForwardX11:
1275
0
    intptr = &options->forward_x11;
1276
0
 parse_flag:
1277
0
    multistate_ptr = multistate_flag;
1278
0
 parse_multistate:
1279
0
    arg = argv_next(&ac, &av);
1280
0
    if ((value = parse_multistate_value(arg, filename, linenum,
1281
0
        multistate_ptr)) == -1) {
1282
0
      error("%s line %d: unsupported option \"%s\".",
1283
0
          filename, linenum, arg);
1284
0
      goto out;
1285
0
    }
1286
0
    if (*activep && *intptr == -1)
1287
0
      *intptr = value;
1288
0
    break;
1289
1290
0
  case oForwardX11Trusted:
1291
0
    intptr = &options->forward_x11_trusted;
1292
0
    goto parse_flag;
1293
1294
0
  case oForwardX11Timeout:
1295
0
    intptr = &options->forward_x11_timeout;
1296
0
    goto parse_time;
1297
1298
0
  case oGatewayPorts:
1299
0
    intptr = &options->fwd_opts.gateway_ports;
1300
0
    goto parse_flag;
1301
1302
0
  case oExitOnForwardFailure:
1303
0
    intptr = &options->exit_on_forward_failure;
1304
0
    goto parse_flag;
1305
1306
0
  case oPasswordAuthentication:
1307
0
    intptr = &options->password_authentication;
1308
0
    goto parse_flag;
1309
1310
0
  case oKbdInteractiveAuthentication:
1311
0
    intptr = &options->kbd_interactive_authentication;
1312
0
    goto parse_flag;
1313
1314
0
  case oKbdInteractiveDevices:
1315
0
    charptr = &options->kbd_interactive_devices;
1316
0
    goto parse_string;
1317
1318
0
  case oPubkeyAuthentication:
1319
0
    multistate_ptr = multistate_pubkey_auth;
1320
0
    intptr = &options->pubkey_authentication;
1321
0
    goto parse_multistate;
1322
1323
0
  case oHostbasedAuthentication:
1324
0
    intptr = &options->hostbased_authentication;
1325
0
    goto parse_flag;
1326
1327
0
  case oGssAuthentication:
1328
0
    intptr = &options->gss_authentication;
1329
0
    goto parse_flag;
1330
1331
0
  case oGssDelegateCreds:
1332
0
    intptr = &options->gss_deleg_creds;
1333
0
    goto parse_flag;
1334
1335
0
  case oBatchMode:
1336
0
    intptr = &options->batch_mode;
1337
0
    goto parse_flag;
1338
1339
0
  case oCheckHostIP:
1340
0
    intptr = &options->check_host_ip;
1341
0
    goto parse_flag;
1342
1343
0
  case oVerifyHostKeyDNS:
1344
0
    intptr = &options->verify_host_key_dns;
1345
0
    multistate_ptr = multistate_yesnoask;
1346
0
    goto parse_multistate;
1347
1348
0
  case oStrictHostKeyChecking:
1349
0
    intptr = &options->strict_host_key_checking;
1350
0
    multistate_ptr = multistate_strict_hostkey;
1351
0
    goto parse_multistate;
1352
1353
0
  case oCompression:
1354
0
    intptr = &options->compression;
1355
0
    multistate_ptr = multistate_compression;
1356
0
    goto parse_multistate;
1357
1358
0
  case oTCPKeepAlive:
1359
0
    intptr = &options->tcp_keep_alive;
1360
0
    multistate_ptr = multistate_keepalives;
1361
0
    goto parse_multistate;
1362
1363
0
  case oNoHostAuthenticationForLocalhost:
1364
0
    intptr = &options->no_host_authentication_for_localhost;
1365
0
    goto parse_flag;
1366
1367
0
  case oNumberOfPasswordPrompts:
1368
0
    intptr = &options->number_of_password_prompts;
1369
0
    goto parse_int;
1370
1371
35
  case oRekeyLimit:
1372
35
    arg = argv_next(&ac, &av);
1373
35
    if (!arg || *arg == '\0') {
1374
0
      error("%.200s line %d: Missing argument.", filename,
1375
0
          linenum);
1376
0
      goto out;
1377
0
    }
1378
35
    if (strcmp(arg, "default") == 0) {
1379
0
      val64 = 0;
1380
35
    } else {
1381
35
      if (scan_scaled(arg, &val64) == -1) {
1382
0
        error("%.200s line %d: Bad number '%s': %s",
1383
0
            filename, linenum, arg, strerror(errno));
1384
0
        goto out;
1385
0
      }
1386
35
      if (val64 != 0 && val64 < 16) {
1387
0
        error("%.200s line %d: RekeyLimit too small",
1388
0
            filename, linenum);
1389
0
        goto out;
1390
0
      }
1391
35
    }
1392
35
    if (*activep && options->rekey_limit == -1)
1393
19
      options->rekey_limit = val64;
1394
35
    if (ac != 0) { /* optional rekey interval present */
1395
23
      if (strcmp(av[0], "none") == 0) {
1396
0
        (void)argv_next(&ac, &av);  /* discard */
1397
0
        break;
1398
0
      }
1399
23
      intptr = &options->rekey_interval;
1400
23
      goto parse_time;
1401
23
    }
1402
12
    break;
1403
1404
12
  case oIdentityFile:
1405
0
    arg = argv_next(&ac, &av);
1406
0
    if (!arg || *arg == '\0') {
1407
0
      error("%.200s line %d: Missing argument.",
1408
0
          filename, linenum);
1409
0
      goto out;
1410
0
    }
1411
0
    if (*activep) {
1412
0
      intptr = &options->num_identity_files;
1413
0
      if (*intptr >= SSH_MAX_IDENTITY_FILES) {
1414
0
        error("%.200s line %d: Too many identity files "
1415
0
            "specified (max %d).", filename, linenum,
1416
0
            SSH_MAX_IDENTITY_FILES);
1417
0
        goto out;
1418
0
      }
1419
0
      add_identity_file(options, NULL,
1420
0
          arg, flags & SSHCONF_USERCONF);
1421
0
    }
1422
0
    break;
1423
1424
0
  case oCertificateFile:
1425
0
    arg = argv_next(&ac, &av);
1426
0
    if (!arg || *arg == '\0') {
1427
0
      error("%.200s line %d: Missing argument.",
1428
0
          filename, linenum);
1429
0
      goto out;
1430
0
    }
1431
0
    if (*activep) {
1432
0
      intptr = &options->num_certificate_files;
1433
0
      if (*intptr >= SSH_MAX_CERTIFICATE_FILES) {
1434
0
        error("%.200s line %d: Too many certificate "
1435
0
            "files specified (max %d).",
1436
0
            filename, linenum,
1437
0
            SSH_MAX_CERTIFICATE_FILES);
1438
0
        goto out;
1439
0
      }
1440
0
      add_certificate_file(options, arg,
1441
0
          flags & SSHCONF_USERCONF);
1442
0
    }
1443
0
    break;
1444
1445
0
  case oXAuthLocation:
1446
0
    charptr=&options->xauth_location;
1447
0
    goto parse_string;
1448
1449
9
  case oUser:
1450
9
    charptr = &options->user;
1451
34
parse_string:
1452
34
    arg = argv_next(&ac, &av);
1453
34
    if (!arg || *arg == '\0') {
1454
0
      error("%.200s line %d: Missing argument.",
1455
0
          filename, linenum);
1456
0
      goto out;
1457
0
    }
1458
34
    if (*activep && *charptr == NULL)
1459
20
      *charptr = xstrdup(arg);
1460
34
    break;
1461
1462
0
  case oGlobalKnownHostsFile:
1463
0
    cpptr = (char **)&options->system_hostfiles;
1464
0
    uintptr = &options->num_system_hostfiles;
1465
0
    max_entries = SSH_MAX_HOSTS_FILES;
1466
6
parse_char_array:
1467
6
    i = 0;
1468
6
    value = *uintptr == 0; /* was array empty when we started? */
1469
40
    while ((arg = argv_next(&ac, &av)) != NULL) {
1470
34
      if (*arg == '\0') {
1471
0
        error("%s line %d: keyword %s empty argument",
1472
0
            filename, linenum, keyword);
1473
0
        goto out;
1474
0
      }
1475
      /* Allow "none" only in first position */
1476
34
      if (strcasecmp(arg, "none") == 0) {
1477
0
        if (i > 0 || ac > 0) {
1478
0
          error("%s line %d: keyword %s \"none\" "
1479
0
              "argument must appear alone.",
1480
0
              filename, linenum, keyword);
1481
0
          goto out;
1482
0
        }
1483
0
      }
1484
34
      i++;
1485
34
      if (*activep && value) {
1486
25
        if ((*uintptr) >= max_entries) {
1487
0
          error("%s line %d: too many %s "
1488
0
              "entries.", filename, linenum,
1489
0
              keyword);
1490
0
          goto out;
1491
0
        }
1492
25
        cpptr[(*uintptr)++] = xstrdup(arg);
1493
25
      }
1494
34
    }
1495
6
    break;
1496
1497
6
  case oUserKnownHostsFile:
1498
6
    cpptr = (char **)&options->user_hostfiles;
1499
6
    uintptr = &options->num_user_hostfiles;
1500
6
    max_entries = SSH_MAX_HOSTS_FILES;
1501
6
    goto parse_char_array;
1502
1503
0
  case oHostname:
1504
0
    charptr = &options->hostname;
1505
0
    goto parse_string;
1506
1507
14
  case oTag:
1508
14
    charptr = &options->tag;
1509
14
    goto parse_string;
1510
1511
4
  case oHostKeyAlias:
1512
4
    charptr = &options->host_key_alias;
1513
4
    goto parse_string;
1514
1515
0
  case oPreferredAuthentications:
1516
0
    charptr = &options->preferred_authentications;
1517
0
    goto parse_string;
1518
1519
0
  case oBindAddress:
1520
0
    charptr = &options->bind_address;
1521
0
    goto parse_string;
1522
1523
0
  case oBindInterface:
1524
0
    charptr = &options->bind_interface;
1525
0
    goto parse_string;
1526
1527
0
  case oPKCS11Provider:
1528
0
    charptr = &options->pkcs11_provider;
1529
0
    goto parse_string;
1530
1531
6
  case oSecurityKeyProvider:
1532
6
    charptr = &options->sk_provider;
1533
6
    goto parse_string;
1534
1535
0
  case oKnownHostsCommand:
1536
0
    charptr = &options->known_hosts_command;
1537
0
    goto parse_command;
1538
1539
9
  case oProxyCommand:
1540
9
    charptr = &options->proxy_command;
1541
9
parse_command:
1542
9
    if (str == NULL) {
1543
0
      error("%.200s line %d: Missing argument.",
1544
0
          filename, linenum);
1545
0
      goto out;
1546
0
    }
1547
9
    len = strspn(str, WHITESPACE "=");
1548
9
    if (*activep && *charptr == NULL)
1549
6
      *charptr = xstrdup(str + len);
1550
9
    argv_consume(&ac);
1551
9
    break;
1552
1553
0
  case oProxyJump:
1554
0
    if (str == NULL) {
1555
0
      error("%.200s line %d: Missing argument.",
1556
0
          filename, linenum);
1557
0
      goto out;
1558
0
    }
1559
0
    len = strspn(str, WHITESPACE "=");
1560
    /* XXX use argv? */
1561
0
    if (parse_jump(str + len, options, cmdline, *activep) == -1) {
1562
0
      error("%.200s line %d: Invalid ProxyJump \"%s\"",
1563
0
          filename, linenum, str + len);
1564
0
      goto out;
1565
0
    }
1566
0
    argv_consume(&ac);
1567
0
    break;
1568
1569
35
  case oPort:
1570
35
    arg = argv_next(&ac, &av);
1571
35
    if (!arg || *arg == '\0') {
1572
0
      error("%.200s line %d: Missing argument.",
1573
0
          filename, linenum);
1574
0
      goto out;
1575
0
    }
1576
35
    value = a2port(arg);
1577
35
    if (value <= 0) {
1578
0
      error("%.200s line %d: Bad port '%s'.",
1579
0
          filename, linenum, arg);
1580
0
      goto out;
1581
0
    }
1582
35
    if (*activep && options->port == -1)
1583
9
      options->port = value;
1584
35
    break;
1585
1586
0
  case oConnectionAttempts:
1587
0
    intptr = &options->connection_attempts;
1588
0
parse_int:
1589
0
    arg = argv_next(&ac, &av);
1590
0
    if ((errstr = atoi_err(arg, &value)) != NULL) {
1591
0
      error("%s line %d: integer value %s.",
1592
0
          filename, linenum, errstr);
1593
0
      goto out;
1594
0
    }
1595
0
    if (*activep && *intptr == -1)
1596
0
      *intptr = value;
1597
0
    break;
1598
1599
0
  case oCiphers:
1600
0
    arg = argv_next(&ac, &av);
1601
0
    if (!arg || *arg == '\0') {
1602
0
      error("%.200s line %d: Missing argument.",
1603
0
          filename, linenum);
1604
0
      goto out;
1605
0
    }
1606
0
    if (*arg != '-' &&
1607
0
        !ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)){
1608
0
      error("%.200s line %d: Bad SSH2 cipher spec '%s'.",
1609
0
          filename, linenum, arg ? arg : "<NONE>");
1610
0
      goto out;
1611
0
    }
1612
0
    if (*activep && options->ciphers == NULL)
1613
0
      options->ciphers = xstrdup(arg);
1614
0
    break;
1615
1616
0
  case oMacs:
1617
0
    arg = argv_next(&ac, &av);
1618
0
    if (!arg || *arg == '\0') {
1619
0
      error("%.200s line %d: Missing argument.",
1620
0
          filename, linenum);
1621
0
      goto out;
1622
0
    }
1623
0
    if (*arg != '-' &&
1624
0
        !mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) {
1625
0
      error("%.200s line %d: Bad SSH2 MAC spec '%s'.",
1626
0
          filename, linenum, arg ? arg : "<NONE>");
1627
0
      goto out;
1628
0
    }
1629
0
    if (*activep && options->macs == NULL)
1630
0
      options->macs = xstrdup(arg);
1631
0
    break;
1632
1633
1
  case oKexAlgorithms:
1634
1
    arg = argv_next(&ac, &av);
1635
1
    if (!arg || *arg == '\0') {
1636
0
      error("%.200s line %d: Missing argument.",
1637
0
          filename, linenum);
1638
0
      goto out;
1639
0
    }
1640
1
    if (*arg != '-' &&
1641
1
        !kex_names_valid(*arg == '+' || *arg == '^' ?
1642
1
        arg + 1 : arg)) {
1643
0
      error("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.",
1644
0
          filename, linenum, arg ? arg : "<NONE>");
1645
0
      goto out;
1646
0
    }
1647
1
    if (*activep && options->kex_algorithms == NULL)
1648
1
      options->kex_algorithms = xstrdup(arg);
1649
1
    break;
1650
1651
0
  case oHostKeyAlgorithms:
1652
0
    charptr = &options->hostkeyalgorithms;
1653
0
    ca_only = 0;
1654
20
parse_pubkey_algos:
1655
20
    arg = argv_next(&ac, &av);
1656
20
    if (!arg || *arg == '\0') {
1657
0
      error("%.200s line %d: Missing argument.",
1658
0
          filename, linenum);
1659
0
      goto out;
1660
0
    }
1661
20
    if (*arg != '-' &&
1662
20
        !sshkey_names_valid2(*arg == '+' || *arg == '^' ?
1663
20
        arg + 1 : arg, 1, ca_only)) {
1664
0
      error("%s line %d: Bad key types '%s'.",
1665
0
          filename, linenum, arg ? arg : "<NONE>");
1666
0
      goto out;
1667
0
    }
1668
20
    if (*activep && *charptr == NULL)
1669
20
      *charptr = xstrdup(arg);
1670
20
    break;
1671
1672
20
  case oCASignatureAlgorithms:
1673
20
    charptr = &options->ca_sign_algorithms;
1674
20
    ca_only = 1;
1675
20
    goto parse_pubkey_algos;
1676
1677
0
  case oLogLevel:
1678
0
    log_level_ptr = &options->log_level;
1679
0
    arg = argv_next(&ac, &av);
1680
0
    value = log_level_number(arg);
1681
0
    if (value == SYSLOG_LEVEL_NOT_SET) {
1682
0
      error("%.200s line %d: unsupported log level '%s'",
1683
0
          filename, linenum, arg ? arg : "<NONE>");
1684
0
      goto out;
1685
0
    }
1686
0
    if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET)
1687
0
      *log_level_ptr = (LogLevel) value;
1688
0
    break;
1689
1690
0
  case oLogFacility:
1691
0
    log_facility_ptr = &options->log_facility;
1692
0
    arg = argv_next(&ac, &av);
1693
0
    value = log_facility_number(arg);
1694
0
    if (value == SYSLOG_FACILITY_NOT_SET) {
1695
0
      error("%.200s line %d: unsupported log facility '%s'",
1696
0
          filename, linenum, arg ? arg : "<NONE>");
1697
0
      goto out;
1698
0
    }
1699
0
    if (*log_facility_ptr == -1)
1700
0
      *log_facility_ptr = (SyslogFacility) value;
1701
0
    break;
1702
1703
22
  case oLogVerbose:
1704
22
    cppptr = &options->log_verbose;
1705
22
    uintptr = &options->num_log_verbose;
1706
22
    i = 0;
1707
306
    while ((arg = argv_next(&ac, &av)) != NULL) {
1708
284
      if (*arg == '\0') {
1709
0
        error("%s line %d: keyword %s empty argument",
1710
0
            filename, linenum, keyword);
1711
0
        goto out;
1712
0
      }
1713
      /* Allow "none" only in first position */
1714
284
      if (strcasecmp(arg, "none") == 0) {
1715
0
        if (i > 0 || ac > 0) {
1716
0
          error("%s line %d: keyword %s \"none\" "
1717
0
              "argument must appear alone.",
1718
0
              filename, linenum, keyword);
1719
0
          goto out;
1720
0
        }
1721
0
      }
1722
284
      i++;
1723
284
      if (*activep && *uintptr == 0) {
1724
7
        *cppptr = xrecallocarray(*cppptr, *uintptr,
1725
7
            *uintptr + 1, sizeof(**cppptr));
1726
7
        (*cppptr)[(*uintptr)++] = xstrdup(arg);
1727
7
      }
1728
284
    }
1729
22
    break;
1730
1731
22
  case oLocalForward:
1732
4
  case oRemoteForward:
1733
4
  case oDynamicForward:
1734
4
    arg = argv_next(&ac, &av);
1735
4
    if (!arg || *arg == '\0') {
1736
0
      error("%.200s line %d: Missing argument.",
1737
0
          filename, linenum);
1738
0
      goto out;
1739
0
    }
1740
1741
4
    remotefwd = (opcode == oRemoteForward);
1742
4
    dynamicfwd = (opcode == oDynamicForward);
1743
1744
4
    if (!dynamicfwd) {
1745
4
      arg2 = argv_next(&ac, &av);
1746
4
      if (arg2 == NULL || *arg2 == '\0') {
1747
2
        if (remotefwd)
1748
2
          dynamicfwd = 1;
1749
0
        else {
1750
0
          error("%.200s line %d: Missing target "
1751
0
              "argument.", filename, linenum);
1752
0
          goto out;
1753
0
        }
1754
2
      } else {
1755
        /* construct a string for parse_forward */
1756
2
        snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg,
1757
2
            arg2);
1758
2
      }
1759
4
    }
1760
4
    if (dynamicfwd)
1761
2
      strlcpy(fwdarg, arg, sizeof(fwdarg));
1762
1763
4
    if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0) {
1764
0
      error("%.200s line %d: Bad forwarding specification.",
1765
0
          filename, linenum);
1766
0
      goto out;
1767
0
    }
1768
1769
4
    if (*activep) {
1770
4
      if (remotefwd) {
1771
4
        add_remote_forward(options, &fwd);
1772
4
      } else {
1773
0
        add_local_forward(options, &fwd);
1774
0
      }
1775
4
    }
1776
4
    break;
1777
1778
3
  case oPermitRemoteOpen:
1779
3
    uintptr = &options->num_permitted_remote_opens;
1780
3
    cppptr = &options->permitted_remote_opens;
1781
3
    found = *uintptr == 0;
1782
10
    while ((arg = argv_next(&ac, &av)) != NULL) {
1783
7
      arg2 = xstrdup(arg);
1784
      /* Allow any/none only in first position */
1785
7
      if (strcasecmp(arg, "none") == 0 ||
1786
7
          strcasecmp(arg, "any") == 0) {
1787
0
        if (nstrs > 0 || ac > 0) {
1788
0
          error("%s line %d: keyword %s \"%s\" "
1789
0
              "argument must appear alone.",
1790
0
              filename, linenum, keyword, arg);
1791
0
          free(arg2);
1792
0
          goto out;
1793
0
        }
1794
7
      } else {
1795
7
        p = hpdelim(&arg);
1796
7
        if (p == NULL) {
1797
0
          fatal("%s line %d: missing host in %s",
1798
0
              filename, linenum,
1799
0
              lookup_opcode_name(opcode));
1800
0
        }
1801
7
        p = cleanhostname(p);
1802
        /*
1803
         * don't want to use permitopen_port to avoid
1804
         * dependency on channels.[ch] here.
1805
         */
1806
7
        if (arg == NULL || (strcmp(arg, "*") != 0 &&
1807
7
            a2port(arg) <= 0)) {
1808
0
          fatal("%s line %d: bad port number "
1809
0
              "in %s", filename, linenum,
1810
0
              lookup_opcode_name(opcode));
1811
0
        }
1812
7
      }
1813
7
      opt_array_append(filename, linenum,
1814
7
          lookup_opcode_name(opcode),
1815
7
          &strs, &nstrs, arg2);
1816
7
      free(arg2);
1817
7
    }
1818
3
    if (nstrs == 0)
1819
0
      fatal("%s line %d: missing %s specification",
1820
3
          filename, linenum, lookup_opcode_name(opcode));
1821
3
    if (found && *activep) {
1822
3
      *cppptr = strs;
1823
3
      *uintptr = nstrs;
1824
3
      strs = NULL; /* transferred */
1825
3
      nstrs = 0;
1826
3
    }
1827
3
    break;
1828
1829
0
  case oClearAllForwardings:
1830
0
    intptr = &options->clear_forwardings;
1831
0
    goto parse_flag;
1832
1833
23
  case oHost:
1834
23
    if (cmdline) {
1835
0
      error("Host directive not supported as a command-line "
1836
0
          "option");
1837
0
      goto out;
1838
0
    }
1839
23
    *activep = 0;
1840
23
    arg2 = NULL;
1841
174
    while ((arg = argv_next(&ac, &av)) != NULL) {
1842
152
      if (*arg == '\0') {
1843
0
        error("%s line %d: keyword %s empty argument",
1844
0
            filename, linenum, keyword);
1845
0
        goto out;
1846
0
      }
1847
152
      if ((flags & SSHCONF_NEVERMATCH) != 0) {
1848
0
        argv_consume(&ac);
1849
0
        break;
1850
0
      }
1851
152
      negated = *arg == '!';
1852
152
      if (negated)
1853
24
        arg++;
1854
152
      if (match_pattern(host, arg)) {
1855
5
        if (negated) {
1856
1
          debug("%.200s line %d: Skipping Host "
1857
1
              "block because of negated match "
1858
1
              "for %.100s", filename, linenum,
1859
1
              arg);
1860
1
          *activep = 0;
1861
1
          argv_consume(&ac);
1862
1
          break;
1863
1
        }
1864
4
        if (!*activep)
1865
1
          arg2 = arg; /* logged below */
1866
4
        *activep = 1;
1867
4
      }
1868
152
    }
1869
23
    if (*activep)
1870
1
      debug("%.200s line %d: Applying options for %.100s",
1871
23
          filename, linenum, arg2);
1872
23
    break;
1873
1874
0
  case oMatch:
1875
0
    if (cmdline) {
1876
0
      error("Match directive not supported as a command-line "
1877
0
          "option");
1878
0
      goto out;
1879
0
    }
1880
0
    value = match_cfg_line(options, str, &ac, &av, pw, host,
1881
0
        original_host, remote_command, flags & SSHCONF_FINAL,
1882
0
        want_final_pass, filename, linenum);
1883
0
    if (value < 0) {
1884
0
      error("%.200s line %d: Bad Match condition", filename,
1885
0
          linenum);
1886
0
      goto out;
1887
0
    }
1888
0
    *activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value;
1889
0
    break;
1890
1891
0
  case oEscapeChar:
1892
0
    intptr = &options->escape_char;
1893
0
    arg = argv_next(&ac, &av);
1894
0
    if (!arg || *arg == '\0') {
1895
0
      error("%.200s line %d: Missing argument.",
1896
0
          filename, linenum);
1897
0
      goto out;
1898
0
    }
1899
0
    if (strcmp(arg, "none") == 0)
1900
0
      value = SSH_ESCAPECHAR_NONE;
1901
0
    else if (arg[1] == '\0')
1902
0
      value = (u_char) arg[0];
1903
0
    else if (arg[0] == '^' && arg[2] == 0 &&
1904
0
        (u_char) arg[1] >= 64 && (u_char) arg[1] < 128)
1905
0
      value = (u_char) arg[1] & 31;
1906
0
    else {
1907
0
      error("%.200s line %d: Bad escape character.",
1908
0
          filename, linenum);
1909
0
      goto out;
1910
0
    }
1911
0
    if (*activep && *intptr == -1)
1912
0
      *intptr = value;
1913
0
    break;
1914
1915
0
  case oAddressFamily:
1916
0
    intptr = &options->address_family;
1917
0
    multistate_ptr = multistate_addressfamily;
1918
0
    goto parse_multistate;
1919
1920
0
  case oEnableSSHKeysign:
1921
0
    intptr = &options->enable_ssh_keysign;
1922
0
    goto parse_flag;
1923
1924
0
  case oIdentitiesOnly:
1925
0
    intptr = &options->identities_only;
1926
0
    goto parse_flag;
1927
1928
0
  case oServerAliveInterval:
1929
0
    intptr = &options->server_alive_interval;
1930
0
    goto parse_time;
1931
1932
0
  case oServerAliveCountMax:
1933
0
    intptr = &options->server_alive_count_max;
1934
0
    goto parse_int;
1935
1936
43
  case oSendEnv:
1937
    /* XXX appends to list; doesn't respect first-match-wins */
1938
3.10k
    while ((arg = argv_next(&ac, &av)) != NULL) {
1939
3.05k
      if (*arg == '\0' || strchr(arg, '=') != NULL) {
1940
0
        error("%s line %d: Invalid environment name.",
1941
0
            filename, linenum);
1942
0
        goto out;
1943
0
      }
1944
3.05k
      found = 1;
1945
3.05k
      if (!*activep)
1946
0
        continue;
1947
3.05k
      if (*arg == '-') {
1948
        /* Removing an env var */
1949
2.36k
        rm_env(options, arg, filename, linenum);
1950
2.36k
        continue;
1951
2.36k
      }
1952
691
      opt_array_append(filename, linenum,
1953
691
          lookup_opcode_name(opcode),
1954
691
          &options->send_env, &options->num_send_env, arg);
1955
691
    }
1956
43
    if (!found) {
1957
0
      fatal("%s line %d: no %s specified",
1958
0
          filename, linenum, keyword);
1959
0
    }
1960
43
    break;
1961
1962
43
  case oSetEnv:
1963
31
    found = options->num_setenv == 0;
1964
551
    while ((arg = argv_next(&ac, &av)) != NULL) {
1965
520
      if (strchr(arg, '=') == NULL) {
1966
0
        error("%s line %d: Invalid SetEnv.",
1967
0
            filename, linenum);
1968
0
        goto out;
1969
0
      }
1970
520
      if (lookup_setenv_in_list(arg, strs, nstrs) != NULL) {
1971
336
        debug2("%s line %d: ignoring duplicate env "
1972
336
            "name \"%.64s\"", filename, linenum, arg);
1973
336
        continue;
1974
336
      }
1975
184
      opt_array_append(filename, linenum,
1976
184
          lookup_opcode_name(opcode),
1977
184
          &strs, &nstrs, arg);
1978
184
    }
1979
31
    if (nstrs == 0) {
1980
0
      fatal("%s line %d: no %s specified",
1981
0
          filename, linenum, keyword);
1982
0
    }
1983
31
    if (found && *activep) {
1984
26
      options->setenv = strs;
1985
26
      options->num_setenv = nstrs;
1986
26
      strs = NULL; /* transferred */
1987
26
      nstrs = 0;
1988
26
    }
1989
31
    break;
1990
1991
0
  case oControlPath:
1992
0
    charptr = &options->control_path;
1993
0
    goto parse_string;
1994
1995
0
  case oControlMaster:
1996
0
    intptr = &options->control_master;
1997
0
    multistate_ptr = multistate_controlmaster;
1998
0
    goto parse_multistate;
1999
2000
0
  case oControlPersist:
2001
    /* no/false/yes/true, or a time spec */
2002
0
    intptr = &options->control_persist;
2003
0
    arg = argv_next(&ac, &av);
2004
0
    if (!arg || *arg == '\0') {
2005
0
      error("%.200s line %d: Missing ControlPersist"
2006
0
          " argument.", filename, linenum);
2007
0
      goto out;
2008
0
    }
2009
0
    value = 0;
2010
0
    value2 = 0; /* timeout */
2011
0
    if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
2012
0
      value = 0;
2013
0
    else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
2014
0
      value = 1;
2015
0
    else if ((value2 = convtime(arg)) >= 0)
2016
0
      value = 1;
2017
0
    else {
2018
0
      error("%.200s line %d: Bad ControlPersist argument.",
2019
0
          filename, linenum);
2020
0
      goto out;
2021
0
    }
2022
0
    if (*activep && *intptr == -1) {
2023
0
      *intptr = value;
2024
0
      options->control_persist_timeout = value2;
2025
0
    }
2026
0
    break;
2027
2028
0
  case oHashKnownHosts:
2029
0
    intptr = &options->hash_known_hosts;
2030
0
    goto parse_flag;
2031
2032
0
  case oTunnel:
2033
0
    intptr = &options->tun_open;
2034
0
    multistate_ptr = multistate_tunnel;
2035
0
    goto parse_multistate;
2036
2037
0
  case oTunnelDevice:
2038
0
    arg = argv_next(&ac, &av);
2039
0
    if (!arg || *arg == '\0') {
2040
0
      error("%.200s line %d: Missing argument.",
2041
0
          filename, linenum);
2042
0
      goto out;
2043
0
    }
2044
0
    value = a2tun(arg, &value2);
2045
0
    if (value == SSH_TUNID_ERR) {
2046
0
      error("%.200s line %d: Bad tun device.",
2047
0
          filename, linenum);
2048
0
      goto out;
2049
0
    }
2050
0
    if (*activep && options->tun_local == -1) {
2051
0
      options->tun_local = value;
2052
0
      options->tun_remote = value2;
2053
0
    }
2054
0
    break;
2055
2056
0
  case oLocalCommand:
2057
0
    charptr = &options->local_command;
2058
0
    goto parse_command;
2059
2060
0
  case oPermitLocalCommand:
2061
0
    intptr = &options->permit_local_command;
2062
0
    goto parse_flag;
2063
2064
0
  case oRemoteCommand:
2065
0
    charptr = &options->remote_command;
2066
0
    goto parse_command;
2067
2068
0
  case oVisualHostKey:
2069
0
    intptr = &options->visual_host_key;
2070
0
    goto parse_flag;
2071
2072
132
  case oInclude:
2073
132
    if (cmdline) {
2074
0
      error("Include directive not supported as a "
2075
0
          "command-line option");
2076
0
      goto out;
2077
0
    }
2078
132
    value = 0;
2079
930
    while ((arg = argv_next(&ac, &av)) != NULL) {
2080
798
      if (*arg == '\0') {
2081
0
        error("%s line %d: keyword %s empty argument",
2082
0
            filename, linenum, keyword);
2083
0
        goto out;
2084
0
      }
2085
      /* Expand %tokens and environment variables */
2086
798
      if ((p = expand_match_exec_or_include_path(arg,
2087
798
          options, pw, host, original_host,
2088
798
          flags & SSHCONF_FINAL, 1)) == NULL) {
2089
6
        error("%.200s line %d: Unable to expand user "
2090
6
            "config file '%.100s'",
2091
6
            filename, linenum, arg);
2092
6
        continue;
2093
6
      }
2094
      /*
2095
       * Ensure all paths are anchored. User configuration
2096
       * files may begin with '~/' but system configurations
2097
       * must not. If the path is relative, then treat it
2098
       * as living in ~/.ssh for user configurations or
2099
       * /etc/ssh for system ones.
2100
       */
2101
792
      if (*p == '~' && (flags & SSHCONF_USERCONF) == 0) {
2102
0
        error("%.200s line %d: bad include path %s.",
2103
0
            filename, linenum, p);
2104
0
        goto out;
2105
0
      }
2106
792
      if (!path_absolute(p) && *p != '~') {
2107
738
        xasprintf(&arg2, "%s/%s",
2108
738
            (flags & SSHCONF_USERCONF) ?
2109
738
            "~/" _PATH_SSH_USER_DIR : SSHDIR, p);
2110
738
      } else {
2111
54
        arg2 = xstrdup(p);
2112
54
      }
2113
792
      free(p);
2114
792
      memset(&gl, 0, sizeof(gl));
2115
792
      r = glob(arg2, GLOB_TILDE, NULL, &gl);
2116
792
      if (r == GLOB_NOMATCH) {
2117
792
        debug("%.200s line %d: include %s matched no "
2118
792
            "files",filename, linenum, arg2);
2119
792
        free(arg2);
2120
792
        continue;
2121
792
      } else if (r != 0) {
2122
0
        error("%.200s line %d: glob failed for %s.",
2123
0
            filename, linenum, arg2);
2124
0
        goto out;
2125
0
      }
2126
0
      free(arg2);
2127
0
      oactive = *activep;
2128
0
      for (i = 0; i < gl.gl_pathc; i++) {
2129
0
        debug3("%.200s line %d: Including file %s "
2130
0
            "depth %d%s", filename, linenum,
2131
0
            gl.gl_pathv[i], depth,
2132
0
            oactive ? "" : " (parse only)");
2133
0
        r = read_config_file_depth(gl.gl_pathv[i],
2134
0
            pw, host, original_host, remote_command,
2135
0
            options, flags | SSHCONF_CHECKPERM |
2136
0
            (oactive ? 0 : SSHCONF_NEVERMATCH),
2137
0
            activep, want_final_pass, depth + 1);
2138
0
        if (r != 1 && errno != ENOENT) {
2139
0
          error("%.200s line %d: Can't open user "
2140
0
              "config file %.100s: %.100s",
2141
0
              filename, linenum, gl.gl_pathv[i],
2142
0
              strerror(errno));
2143
0
          globfree(&gl);
2144
0
          goto out;
2145
0
        }
2146
        /*
2147
         * don't let Match in includes clobber the
2148
         * containing file's Match state.
2149
         */
2150
0
        *activep = oactive;
2151
0
        if (r != 1)
2152
0
          value = -1;
2153
0
      }
2154
0
      globfree(&gl);
2155
0
    }
2156
132
    if (value != 0)
2157
0
      ret = value;
2158
132
    break;
2159
2160
0
  case oIPQoS:
2161
0
    arg = argv_next(&ac, &av);
2162
0
    if ((value = parse_ipqos(arg)) == -1) {
2163
0
      error("%s line %d: Bad IPQoS value: %s",
2164
0
          filename, linenum, arg);
2165
0
      goto out;
2166
0
    }
2167
0
    if (value == INT_MIN) {
2168
0
      debug("%s line %d: Deprecated IPQoS value \"%s\" "
2169
0
          "ignored - using system default instead. Consider"
2170
0
          " using DSCP values.", filename, linenum, arg);
2171
0
      value = INT_MAX;
2172
0
    }
2173
0
    arg = argv_next(&ac, &av);
2174
0
    if (arg == NULL)
2175
0
      value2 = value;
2176
0
    else if ((value2 = parse_ipqos(arg)) == -1) {
2177
0
      error("%s line %d: Bad IPQoS value: %s",
2178
0
          filename, linenum, arg);
2179
0
      goto out;
2180
0
    }
2181
0
    if (value2 == INT_MIN) {
2182
0
      debug("%s line %d: Deprecated IPQoS value \"%s\" "
2183
0
          "ignored - using system default instead. Consider"
2184
0
          " using DSCP values.", filename, linenum, arg);
2185
0
      value2 = INT_MAX;
2186
0
    }
2187
0
    if (*activep && options->ip_qos_interactive == -1) {
2188
0
      options->ip_qos_interactive = value;
2189
0
      options->ip_qos_bulk = value2;
2190
0
    }
2191
0
    break;
2192
2193
0
  case oRequestTTY:
2194
0
    intptr = &options->request_tty;
2195
0
    multistate_ptr = multistate_requesttty;
2196
0
    goto parse_multistate;
2197
2198
0
  case oSessionType:
2199
0
    intptr = &options->session_type;
2200
0
    multistate_ptr = multistate_sessiontype;
2201
0
    goto parse_multistate;
2202
2203
0
  case oStdinNull:
2204
0
    intptr = &options->stdin_null;
2205
0
    goto parse_flag;
2206
2207
0
  case oForkAfterAuthentication:
2208
0
    intptr = &options->fork_after_authentication;
2209
0
    goto parse_flag;
2210
2211
1
  case oIgnoreUnknown:
2212
1
    charptr = &options->ignored_unknown;
2213
1
    goto parse_string;
2214
2215
0
  case oProxyUseFdpass:
2216
0
    intptr = &options->proxy_use_fdpass;
2217
0
    goto parse_flag;
2218
2219
0
  case oCanonicalDomains:
2220
0
    found = options->num_canonical_domains == 0;
2221
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
2222
      /* Allow "none" only in first position */
2223
0
      if (strcasecmp(arg, "none") == 0) {
2224
0
        if (nstrs > 0 || ac > 0) {
2225
0
          error("%s line %d: keyword %s \"none\" "
2226
0
              "argument must appear alone.",
2227
0
              filename, linenum, keyword);
2228
0
          goto out;
2229
0
        }
2230
0
      }
2231
0
      if (!valid_domain(arg, 1, &errstr)) {
2232
0
        error("%s line %d: %s", filename, linenum,
2233
0
            errstr);
2234
0
        goto out;
2235
0
      }
2236
0
      opt_array_append(filename, linenum, keyword,
2237
0
          &strs, &nstrs, arg);
2238
0
    }
2239
0
    if (nstrs == 0) {
2240
0
      fatal("%s line %d: no %s specified",
2241
0
          filename, linenum, keyword);
2242
0
    }
2243
0
    if (found && *activep) {
2244
0
      options->canonical_domains = strs;
2245
0
      options->num_canonical_domains = nstrs;
2246
0
      strs = NULL; /* transferred */
2247
0
      nstrs = 0;
2248
0
    }
2249
0
    break;
2250
2251
3
  case oCanonicalizePermittedCNAMEs:
2252
3
    found = options->num_permitted_cnames == 0;
2253
32
    while ((arg = argv_next(&ac, &av)) != NULL) {
2254
      /*
2255
       * Either 'none' (only in first position), '*' for
2256
       * everything or 'list:list'
2257
       */
2258
29
      if (strcasecmp(arg, "none") == 0) {
2259
0
        if (ncnames > 0 || ac > 0) {
2260
0
          error("%s line %d: keyword %s \"none\" "
2261
0
              "argument must appear alone.",
2262
0
              filename, linenum, keyword);
2263
0
          goto out;
2264
0
        }
2265
0
        arg2 = "";
2266
29
      } else if (strcmp(arg, "*") == 0) {
2267
9
        arg2 = arg;
2268
20
      } else {
2269
20
        lowercase(arg);
2270
20
        if ((arg2 = strchr(arg, ':')) == NULL ||
2271
20
            arg2[1] == '\0') {
2272
0
          error("%s line %d: "
2273
0
              "Invalid permitted CNAME \"%s\"",
2274
0
              filename, linenum, arg);
2275
0
          goto out;
2276
0
        }
2277
20
        *arg2 = '\0';
2278
20
        arg2++;
2279
20
      }
2280
29
      cnames = xrecallocarray(cnames, ncnames, ncnames + 1,
2281
29
          sizeof(*cnames));
2282
29
      cnames[ncnames].source_list = xstrdup(arg);
2283
29
      cnames[ncnames].target_list = xstrdup(arg2);
2284
29
      ncnames++;
2285
29
    }
2286
3
    if (ncnames == 0) {
2287
0
      fatal("%s line %d: no %s specified",
2288
0
          filename, linenum, keyword);
2289
0
    }
2290
3
    if (found && *activep) {
2291
3
      options->permitted_cnames = cnames;
2292
3
      options->num_permitted_cnames = ncnames;
2293
3
      cnames = NULL; /* transferred */
2294
3
      ncnames = 0;
2295
3
    }
2296
    /* un-transferred cnames is cleaned up before exit */
2297
3
    break;
2298
2299
0
  case oCanonicalizeHostname:
2300
0
    intptr = &options->canonicalize_hostname;
2301
0
    multistate_ptr = multistate_canonicalizehostname;
2302
0
    goto parse_multistate;
2303
2304
0
  case oCanonicalizeMaxDots:
2305
0
    intptr = &options->canonicalize_max_dots;
2306
0
    goto parse_int;
2307
2308
0
  case oCanonicalizeFallbackLocal:
2309
0
    intptr = &options->canonicalize_fallback_local;
2310
0
    goto parse_flag;
2311
2312
0
  case oStreamLocalBindMask:
2313
0
    arg = argv_next(&ac, &av);
2314
0
    if (!arg || *arg == '\0') {
2315
0
      error("%.200s line %d: Missing StreamLocalBindMask "
2316
0
          "argument.", filename, linenum);
2317
0
      goto out;
2318
0
    }
2319
    /* Parse mode in octal format */
2320
0
    value = strtol(arg, &endofnumber, 8);
2321
0
    if (arg == endofnumber || value < 0 || value > 0777) {
2322
0
      error("%.200s line %d: Bad mask.", filename, linenum);
2323
0
      goto out;
2324
0
    }
2325
0
    if (*activep &&
2326
0
        options->fwd_opts.streamlocal_bind_mask == (mode_t)-1)
2327
0
      options->fwd_opts.streamlocal_bind_mask = (mode_t)value;
2328
0
    break;
2329
2330
0
  case oStreamLocalBindUnlink:
2331
0
    intptr = &options->fwd_opts.streamlocal_bind_unlink;
2332
0
    goto parse_flag;
2333
2334
5
  case oRevokedHostKeys:
2335
5
    uintptr = &options->num_revoked_host_keys;
2336
5
    cppptr = &options->revoked_host_keys;
2337
5
    found = *uintptr == 0;
2338
203
    while ((arg = argv_next(&ac, &av)) != NULL) {
2339
198
      if (*arg == '\0') {
2340
0
        error("%s line %d: keyword %s empty argument",
2341
0
            filename, linenum, keyword);
2342
0
        goto out;
2343
0
      }
2344
      /* Allow "none" only in first position */
2345
198
      if (strcasecmp(arg, "none") == 0) {
2346
0
        if (nstrs > 0 || ac > 0) {
2347
0
          error("%s line %d: keyword %s \"none\" "
2348
0
              "argument must appear alone.",
2349
0
              filename, linenum, keyword);
2350
0
          goto out;
2351
0
        }
2352
0
      }
2353
198
      opt_array_append(filename, linenum, keyword,
2354
198
          &strs, &nstrs, arg);
2355
198
    }
2356
5
    if (nstrs == 0) {
2357
0
      fatal("%s line %d: no %s specified",
2358
0
          filename, linenum, keyword);
2359
0
    }
2360
5
    if (found && *activep) {
2361
5
      *cppptr = strs;
2362
5
      *uintptr = nstrs;
2363
5
      strs = NULL; /* transferred */
2364
5
      nstrs = 0;
2365
5
    }
2366
5
    break;
2367
2368
0
  case oFingerprintHash:
2369
0
    intptr = &options->fingerprint_hash;
2370
0
    arg = argv_next(&ac, &av);
2371
0
    if (!arg || *arg == '\0') {
2372
0
      error("%.200s line %d: Missing argument.",
2373
0
          filename, linenum);
2374
0
      goto out;
2375
0
    }
2376
0
    if ((value = ssh_digest_alg_by_name(arg)) == -1) {
2377
0
      error("%.200s line %d: Invalid hash algorithm \"%s\".",
2378
0
          filename, linenum, arg);
2379
0
      goto out;
2380
0
    }
2381
0
    if (*activep && *intptr == -1)
2382
0
      *intptr = value;
2383
0
    break;
2384
2385
0
  case oUpdateHostkeys:
2386
0
    intptr = &options->update_hostkeys;
2387
0
    multistate_ptr = multistate_yesnoask;
2388
0
    goto parse_multistate;
2389
2390
0
  case oHostbasedAcceptedAlgorithms:
2391
0
    charptr = &options->hostbased_accepted_algos;
2392
0
    ca_only = 0;
2393
0
    goto parse_pubkey_algos;
2394
2395
0
  case oPubkeyAcceptedAlgorithms:
2396
0
    charptr = &options->pubkey_accepted_algos;
2397
0
    ca_only = 0;
2398
0
    goto parse_pubkey_algos;
2399
2400
1
  case oAddKeysToAgent:
2401
1
    arg = argv_next(&ac, &av);
2402
1
    arg2 = argv_next(&ac, &av);
2403
1
    value = parse_multistate_value(arg, filename, linenum,
2404
1
        multistate_yesnoaskconfirm);
2405
1
    value2 = 0; /* unlimited lifespan by default */
2406
1
    if (value == 3 && arg2 != NULL) {
2407
      /* allow "AddKeysToAgent confirm 5m" */
2408
0
      if ((value2 = convtime(arg2)) == -1) {
2409
0
        error("%s line %d: invalid time value.",
2410
0
            filename, linenum);
2411
0
        goto out;
2412
0
      }
2413
1
    } else if (value == -1 && arg2 == NULL) {
2414
1
      if ((value2 = convtime(arg)) == -1) {
2415
0
        error("%s line %d: unsupported option",
2416
0
            filename, linenum);
2417
0
        goto out;
2418
0
      }
2419
1
      value = 1; /* yes */
2420
1
    } else if (value == -1 || arg2 != NULL) {
2421
0
      error("%s line %d: unsupported option",
2422
0
          filename, linenum);
2423
0
      goto out;
2424
0
    }
2425
1
    if (*activep && options->add_keys_to_agent == -1) {
2426
1
      options->add_keys_to_agent = value;
2427
1
      options->add_keys_to_agent_lifespan = value2;
2428
1
    }
2429
1
    break;
2430
2431
0
  case oIdentityAgent:
2432
0
    charptr = &options->identity_agent;
2433
0
    arg = argv_next(&ac, &av);
2434
0
    if (!arg || *arg == '\0') {
2435
0
      error("%.200s line %d: Missing argument.",
2436
0
          filename, linenum);
2437
0
      goto out;
2438
0
    }
2439
0
  parse_agent_path:
2440
    /* Extra validation if the string represents an env var. */
2441
0
    if ((arg2 = dollar_expand(&r, arg)) == NULL || r) {
2442
0
      error("%.200s line %d: Invalid environment expansion "
2443
0
          "%s.", filename, linenum, arg);
2444
0
      goto out;
2445
0
    }
2446
0
    free(arg2);
2447
    /* check for legacy environment format */
2448
0
    if (arg[0] == '$' && arg[1] != '{' &&
2449
0
        !valid_env_name(arg + 1)) {
2450
0
      error("%.200s line %d: Invalid environment name %s.",
2451
0
          filename, linenum, arg);
2452
0
      goto out;
2453
0
    }
2454
0
    if (*activep && *charptr == NULL)
2455
0
      *charptr = xstrdup(arg);
2456
0
    break;
2457
2458
0
  case oEnableEscapeCommandline:
2459
0
    intptr = &options->enable_escape_commandline;
2460
0
    goto parse_flag;
2461
2462
0
  case oRequiredRSASize:
2463
0
    intptr = &options->required_rsa_size;
2464
0
    goto parse_int;
2465
2466
0
  case oWarnWeakCrypto:
2467
0
    intptr = &options->warn_weak_crypto;
2468
0
    multistate_ptr = multistate_warnweakcrypto;
2469
0
    goto parse_multistate;
2470
2471
0
  case oObscureKeystrokeTiming:
2472
0
    value = -1;
2473
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
2474
0
      if (value != -1) {
2475
0
        error("%s line %d: invalid arguments",
2476
0
            filename, linenum);
2477
0
        goto out;
2478
0
      }
2479
0
      if (strcmp(arg, "yes") == 0 ||
2480
0
          strcmp(arg, "true") == 0)
2481
0
        value = SSH_KEYSTROKE_DEFAULT_INTERVAL_MS;
2482
0
      else if (strcmp(arg, "no") == 0 ||
2483
0
          strcmp(arg, "false") == 0)
2484
0
        value = 0;
2485
0
      else if (strncmp(arg, "interval:", 9) == 0) {
2486
0
        if ((errstr = atoi_err(arg + 9,
2487
0
            &value)) != NULL) {
2488
0
          error("%s line %d: integer value %s.",
2489
0
              filename, linenum, errstr);
2490
0
          goto out;
2491
0
        }
2492
0
        if (value <= 0 || value > 1000) {
2493
0
          error("%s line %d: value out of range.",
2494
0
              filename, linenum);
2495
0
          goto out;
2496
0
        }
2497
0
      } else {
2498
0
        error("%s line %d: unsupported argument \"%s\"",
2499
0
            filename, linenum, arg);
2500
0
        goto out;
2501
0
      }
2502
0
    }
2503
0
    if (value == -1) {
2504
0
      error("%s line %d: missing argument",
2505
0
          filename, linenum);
2506
0
      goto out;
2507
0
    }
2508
0
    intptr = &options->obscure_keystroke_timing_interval;
2509
0
    if (*activep && *intptr == -1)
2510
0
      *intptr = value;
2511
0
    break;
2512
2513
1
  case oChannelTimeout:
2514
1
    found = options->num_channel_timeouts == 0;
2515
2
    while ((arg = argv_next(&ac, &av)) != NULL) {
2516
      /* Allow "none" only in first position */
2517
1
      if (strcasecmp(arg, "none") == 0) {
2518
0
        if (nstrs > 0 || ac > 0) {
2519
0
          error("%s line %d: keyword %s \"none\" "
2520
0
              "argument must appear alone.",
2521
0
              filename, linenum, keyword);
2522
0
          goto out;
2523
0
        }
2524
1
      } else if (parse_pattern_interval(arg,
2525
1
          NULL, NULL) != 0) {
2526
0
        fatal("%s line %d: invalid channel timeout %s",
2527
0
            filename, linenum, arg);
2528
0
      }
2529
1
      opt_array_append(filename, linenum, keyword,
2530
1
          &strs, &nstrs, arg);
2531
1
    }
2532
1
    if (nstrs == 0) {
2533
0
      fatal("%s line %d: no %s specified",
2534
0
          filename, linenum, keyword);
2535
0
    }
2536
1
    if (found && *activep) {
2537
1
      options->channel_timeouts = strs;
2538
1
      options->num_channel_timeouts = nstrs;
2539
1
      strs = NULL; /* transferred */
2540
1
      nstrs = 0;
2541
1
    }
2542
1
    break;
2543
2544
0
  case oVersionAddendum:
2545
0
    if (str == NULL || *str == '\0')
2546
0
      fatal("%s line %d: %s missing argument.",
2547
0
          filename, linenum, keyword);
2548
0
    len = strspn(str, WHITESPACE);
2549
0
    if (strchr(str + len, '\r') != NULL) {
2550
0
      fatal("%.200s line %d: Invalid %s argument",
2551
0
          filename, linenum, keyword);
2552
0
    }
2553
0
    if ((arg = strchr(line, '#')) != NULL) {
2554
0
      *arg = '\0';
2555
0
      rtrim(line);
2556
0
    }
2557
0
    if (*activep && options->version_addendum == NULL) {
2558
0
      if (strcasecmp(str + len, "none") == 0)
2559
0
        options->version_addendum = xstrdup("");
2560
0
      else
2561
0
        options->version_addendum = xstrdup(str + len);
2562
0
    }
2563
0
    argv_consume(&ac);
2564
0
    break;
2565
2566
0
  case oRefuseConnection:
2567
0
    arg = argv_next(&ac, &av);
2568
0
    if (!arg || *arg == '\0') {
2569
0
      error("%.200s line %d: Missing argument.",
2570
0
          filename, linenum);
2571
0
      goto out;
2572
0
    }
2573
0
    if (*activep) {
2574
0
      fatal("%.200s line %d: RefuseConnection: %s",
2575
0
          filename, linenum, arg);
2576
0
    }
2577
0
    break;
2578
2579
0
  case oDeprecated:
2580
0
    debug("%s line %d: Deprecated option \"%s\"",
2581
0
        filename, linenum, keyword);
2582
0
    argv_consume(&ac);
2583
0
    break;
2584
2585
0
  case oUnsupported:
2586
0
    error("%s line %d: Unsupported option \"%s\"",
2587
0
        filename, linenum, keyword);
2588
0
    argv_consume(&ac);
2589
0
    break;
2590
2591
0
  default:
2592
0
    error("%s line %d: Unimplemented opcode %d",
2593
0
        filename, linenum, opcode);
2594
0
    goto out;
2595
424
  }
2596
2597
  /* Check that there is no garbage at end of line. */
2598
424
  if (ac > 0) {
2599
0
    error("%.200s line %d: keyword %s extra arguments "
2600
0
        "at end of line", filename, linenum, keyword);
2601
0
    goto out;
2602
0
  }
2603
2604
  /* success */
2605
424
  ret = 0;
2606
424
 out:
2607
424
  free_canon_cnames(cnames, ncnames);
2608
424
  opt_array_free2(strs, NULL, nstrs);
2609
424
  argv_free(oav, oac);
2610
424
  return ret;
2611
424
}
2612
2613
/*
2614
 * Reads the config file and modifies the options accordingly.  Options
2615
 * should already be initialized before this call.  This never returns if
2616
 * there is an error.  If the file does not exist, this returns 0.
2617
 */
2618
int
2619
read_config_file(const char *filename, struct passwd *pw, const char *host,
2620
    const char *original_host, const char *remote_command, Options *options, int flags,
2621
    int *want_final_pass)
2622
285
{
2623
285
  int active = 1;
2624
2625
285
  return read_config_file_depth(filename, pw, host, original_host,
2626
285
      remote_command, options, flags, &active, want_final_pass, 0);
2627
285
}
2628
2629
285
#define READCONF_MAX_DEPTH  16
2630
static int
2631
read_config_file_depth(const char *filename, struct passwd *pw,
2632
    const char *host, const char *original_host, const char *remote_command,
2633
    Options *options, int flags, int *activep, int *want_final_pass, int depth)
2634
285
{
2635
285
  FILE *f;
2636
285
  char *line = NULL;
2637
285
  size_t linesize = 0;
2638
285
  int linenum;
2639
285
  int bad_options = 0;
2640
2641
285
  if (depth < 0 || depth > READCONF_MAX_DEPTH)
2642
0
    fatal("Too many recursive configuration includes");
2643
2644
285
  if ((f = fopen(filename, "r")) == NULL)
2645
0
    return 0;
2646
2647
285
  if (flags & SSHCONF_CHECKPERM) {
2648
0
    struct stat sb;
2649
2650
0
    if (fstat(fileno(f), &sb) == -1)
2651
0
      fatal("fstat %s: %s", filename, strerror(errno));
2652
0
    if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
2653
0
        (sb.st_mode & 022) != 0))
2654
0
      fatal("Bad owner or permissions on %s", filename);
2655
0
  }
2656
2657
285
  debug("Reading configuration data %.200s", filename);
2658
2659
  /*
2660
   * Mark that we are now processing the options.  This flag is turned
2661
   * on/off by Host specifications.
2662
   */
2663
285
  linenum = 0;
2664
1.77k
  while (getline(&line, &linesize, f) != -1) {
2665
    /* Update line number counter. */
2666
1.49k
    linenum++;
2667
    /*
2668
     * Trim out comments and strip whitespace.
2669
     * NB - preserve newlines, they are needed to reproduce
2670
     * line numbers later for error messages.
2671
     */
2672
1.49k
    if (process_config_line_depth(options, pw, host, original_host,
2673
1.49k
        remote_command, line, filename, linenum, activep, flags,
2674
1.49k
        want_final_pass, depth) != 0)
2675
0
      bad_options++;
2676
1.49k
  }
2677
285
  free(line);
2678
285
  fclose(f);
2679
285
  if (bad_options > 0)
2680
0
    fatal("%s: terminating, %d bad configuration options",
2681
285
        filename, bad_options);
2682
285
  return 1;
2683
285
}
2684
2685
/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
2686
int
2687
option_clear_or_none(const char *o)
2688
798
{
2689
798
  return o == NULL || strcasecmp(o, "none") == 0;
2690
798
}
2691
2692
/*
2693
 * Returns 1 if CanonicalizePermittedCNAMEs have been specified, 0 otherwise.
2694
 * Allowed to be called on non-final configuration.
2695
 */
2696
int
2697
config_has_permitted_cnames(Options *options)
2698
0
{
2699
0
  if (options->num_permitted_cnames == 1 &&
2700
0
      strcasecmp(options->permitted_cnames[0].source_list, "none") == 0 &&
2701
0
      strcmp(options->permitted_cnames[0].target_list, "") == 0)
2702
0
    return 0;
2703
0
  return options->num_permitted_cnames > 0;
2704
0
}
2705
2706
/*
2707
 * Initializes options to special values that indicate that they have not yet
2708
 * been set.  Read_config_file will only set options with this value. Options
2709
 * are processed in the following order: command line, user config file,
2710
 * system config file.  Last, fill_default_options is called.
2711
 */
2712
2713
void
2714
initialize_options(Options * options)
2715
285
{
2716
285
  memset(options, 'X', sizeof(*options));
2717
285
  options->host_arg = NULL;
2718
285
  options->forward_agent = -1;
2719
285
  options->forward_agent_sock_path = NULL;
2720
285
  options->forward_x11 = -1;
2721
285
  options->forward_x11_trusted = -1;
2722
285
  options->forward_x11_timeout = -1;
2723
285
  options->stdio_forward_host = NULL;
2724
285
  options->stdio_forward_port = 0;
2725
285
  options->clear_forwardings = -1;
2726
285
  options->exit_on_forward_failure = -1;
2727
285
  options->xauth_location = NULL;
2728
285
  options->fwd_opts.gateway_ports = -1;
2729
285
  options->fwd_opts.streamlocal_bind_mask = (mode_t)-1;
2730
285
  options->fwd_opts.streamlocal_bind_unlink = -1;
2731
285
  options->pubkey_authentication = -1;
2732
285
  options->gss_authentication = -1;
2733
285
  options->gss_deleg_creds = -1;
2734
285
  options->password_authentication = -1;
2735
285
  options->kbd_interactive_authentication = -1;
2736
285
  options->kbd_interactive_devices = NULL;
2737
285
  options->hostbased_authentication = -1;
2738
285
  options->batch_mode = -1;
2739
285
  options->check_host_ip = -1;
2740
285
  options->strict_host_key_checking = -1;
2741
285
  options->compression = -1;
2742
285
  options->tcp_keep_alive = -1;
2743
285
  options->port = -1;
2744
285
  options->address_family = -1;
2745
285
  options->connection_attempts = -1;
2746
285
  options->connection_timeout = -1;
2747
285
  options->number_of_password_prompts = -1;
2748
285
  options->ciphers = NULL;
2749
285
  options->macs = NULL;
2750
285
  options->kex_algorithms = NULL;
2751
285
  options->hostkeyalgorithms = NULL;
2752
285
  options->ca_sign_algorithms = NULL;
2753
285
  options->num_identity_files = 0;
2754
285
  memset(options->identity_keys, 0, sizeof(options->identity_keys));
2755
285
  options->num_certificate_files = 0;
2756
285
  memset(options->certificates, 0, sizeof(options->certificates));
2757
285
  options->hostname = NULL;
2758
285
  options->host_key_alias = NULL;
2759
285
  options->proxy_command = NULL;
2760
285
  options->jump_user = NULL;
2761
285
  options->jump_host = NULL;
2762
285
  options->jump_port = -1;
2763
285
  options->jump_extra = NULL;
2764
285
  options->user = NULL;
2765
285
  options->escape_char = -1;
2766
285
  options->num_system_hostfiles = 0;
2767
285
  options->num_user_hostfiles = 0;
2768
285
  options->local_forwards = NULL;
2769
285
  options->num_local_forwards = 0;
2770
285
  options->remote_forwards = NULL;
2771
285
  options->num_remote_forwards = 0;
2772
285
  options->permitted_remote_opens = NULL;
2773
285
  options->num_permitted_remote_opens = 0;
2774
285
  options->log_facility = SYSLOG_FACILITY_NOT_SET;
2775
285
  options->log_level = SYSLOG_LEVEL_NOT_SET;
2776
285
  options->num_log_verbose = 0;
2777
285
  options->log_verbose = NULL;
2778
285
  options->preferred_authentications = NULL;
2779
285
  options->bind_address = NULL;
2780
285
  options->bind_interface = NULL;
2781
285
  options->pkcs11_provider = NULL;
2782
285
  options->sk_provider = NULL;
2783
285
  options->enable_ssh_keysign = -1;
2784
285
  options->no_host_authentication_for_localhost = -1;
2785
285
  options->identities_only = -1;
2786
285
  options->rekey_limit = -1;
2787
285
  options->rekey_interval = -1;
2788
285
  options->verify_host_key_dns = -1;
2789
285
  options->server_alive_interval = -1;
2790
285
  options->server_alive_count_max = -1;
2791
285
  options->send_env = NULL;
2792
285
  options->num_send_env = 0;
2793
285
  options->setenv = NULL;
2794
285
  options->num_setenv = 0;
2795
285
  options->control_path = NULL;
2796
285
  options->control_master = -1;
2797
285
  options->control_persist = -1;
2798
285
  options->control_persist_timeout = 0;
2799
285
  options->hash_known_hosts = -1;
2800
285
  options->tun_open = -1;
2801
285
  options->tun_local = -1;
2802
285
  options->tun_remote = -1;
2803
285
  options->local_command = NULL;
2804
285
  options->permit_local_command = -1;
2805
285
  options->remote_command = NULL;
2806
285
  options->add_keys_to_agent = -1;
2807
285
  options->add_keys_to_agent_lifespan = -1;
2808
285
  options->identity_agent = NULL;
2809
285
  options->visual_host_key = -1;
2810
285
  options->ip_qos_interactive = -1;
2811
285
  options->ip_qos_bulk = -1;
2812
285
  options->request_tty = -1;
2813
285
  options->session_type = -1;
2814
285
  options->stdin_null = -1;
2815
285
  options->fork_after_authentication = -1;
2816
285
  options->proxy_use_fdpass = -1;
2817
285
  options->ignored_unknown = NULL;
2818
285
  options->num_canonical_domains = 0;
2819
285
  options->num_permitted_cnames = 0;
2820
285
  options->canonicalize_max_dots = -1;
2821
285
  options->canonicalize_fallback_local = -1;
2822
285
  options->canonicalize_hostname = -1;
2823
285
  options->revoked_host_keys = NULL;
2824
285
  options->num_revoked_host_keys = 0;
2825
285
  options->fingerprint_hash = -1;
2826
285
  options->update_hostkeys = -1;
2827
285
  options->hostbased_accepted_algos = NULL;
2828
285
  options->pubkey_accepted_algos = NULL;
2829
285
  options->known_hosts_command = NULL;
2830
285
  options->required_rsa_size = -1;
2831
285
  options->warn_weak_crypto = -1;
2832
285
  options->enable_escape_commandline = -1;
2833
285
  options->obscure_keystroke_timing_interval = -1;
2834
285
  options->tag = NULL;
2835
285
  options->channel_timeouts = NULL;
2836
285
  options->num_channel_timeouts = 0;
2837
285
  options->version_addendum = NULL;
2838
285
}
2839
2840
/*
2841
 * A petite version of fill_default_options() that just fills the options
2842
 * needed for hostname canonicalization to proceed.
2843
 */
2844
void
2845
fill_default_options_for_canonicalization(Options *options)
2846
0
{
2847
0
  if (options->canonicalize_max_dots == -1)
2848
0
    options->canonicalize_max_dots = 1;
2849
0
  if (options->canonicalize_fallback_local == -1)
2850
0
    options->canonicalize_fallback_local = 1;
2851
0
  if (options->canonicalize_hostname == -1)
2852
0
    options->canonicalize_hostname = SSH_CANONICALISE_NO;
2853
0
}
2854
2855
/*
2856
 * Called after processing other sources of option data, this fills those
2857
 * options for which no value has been specified with their default values.
2858
 */
2859
int
2860
fill_default_options(Options * options)
2861
0
{
2862
0
  char *all_cipher, *all_mac, *all_kex, *all_key, *all_sig;
2863
0
  char *def_cipher, *def_mac, *def_kex, *def_key, *def_sig;
2864
0
  int ret = -1, r;
2865
2866
0
  if (options->forward_agent == -1)
2867
0
    options->forward_agent = 0;
2868
0
  if (options->forward_x11 == -1)
2869
0
    options->forward_x11 = 0;
2870
0
  if (options->forward_x11_trusted == -1)
2871
0
    options->forward_x11_trusted = 0;
2872
0
  if (options->forward_x11_timeout == -1)
2873
0
    options->forward_x11_timeout = 1200;
2874
  /*
2875
   * stdio forwarding (-W) changes the default for these but we defer
2876
   * setting the values so they can be overridden.
2877
   */
2878
0
  if (options->exit_on_forward_failure == -1)
2879
0
    options->exit_on_forward_failure =
2880
0
        options->stdio_forward_host != NULL ? 1 : 0;
2881
0
  if (options->clear_forwardings == -1)
2882
0
    options->clear_forwardings =
2883
0
        options->stdio_forward_host != NULL ? 1 : 0;
2884
0
  if (options->clear_forwardings == 1)
2885
0
    clear_forwardings(options);
2886
2887
0
  if (options->xauth_location == NULL)
2888
0
    options->xauth_location = xstrdup(_PATH_XAUTH);
2889
0
  if (options->fwd_opts.gateway_ports == -1)
2890
0
    options->fwd_opts.gateway_ports = 0;
2891
0
  if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1)
2892
0
    options->fwd_opts.streamlocal_bind_mask = 0177;
2893
0
  if (options->fwd_opts.streamlocal_bind_unlink == -1)
2894
0
    options->fwd_opts.streamlocal_bind_unlink = 0;
2895
0
  if (options->pubkey_authentication == -1)
2896
0
    options->pubkey_authentication = SSH_PUBKEY_AUTH_ALL;
2897
0
  if (options->gss_authentication == -1)
2898
0
    options->gss_authentication = 0;
2899
0
  if (options->gss_deleg_creds == -1)
2900
0
    options->gss_deleg_creds = 0;
2901
0
  if (options->password_authentication == -1)
2902
0
    options->password_authentication = 1;
2903
0
  if (options->kbd_interactive_authentication == -1)
2904
0
    options->kbd_interactive_authentication = 1;
2905
0
  if (options->hostbased_authentication == -1)
2906
0
    options->hostbased_authentication = 0;
2907
0
  if (options->batch_mode == -1)
2908
0
    options->batch_mode = 0;
2909
0
  if (options->check_host_ip == -1)
2910
0
    options->check_host_ip = 0;
2911
0
  if (options->strict_host_key_checking == -1)
2912
0
    options->strict_host_key_checking = SSH_STRICT_HOSTKEY_ASK;
2913
0
  if (options->compression == -1)
2914
0
    options->compression = 0;
2915
0
  if (options->tcp_keep_alive == -1)
2916
0
    options->tcp_keep_alive = SSH_KEEPALIVES_TRANSPORT;
2917
0
  if (options->port == -1)
2918
0
    options->port = 0; /* Filled in ssh_connect. */
2919
0
  if (options->address_family == -1)
2920
0
    options->address_family = AF_UNSPEC;
2921
0
  if (options->connection_attempts == -1)
2922
0
    options->connection_attempts = 1;
2923
0
  if (options->number_of_password_prompts == -1)
2924
0
    options->number_of_password_prompts = 3;
2925
  /* options->hostkeyalgorithms, default set in myproposals.h */
2926
0
  if (options->add_keys_to_agent == -1) {
2927
0
    options->add_keys_to_agent = 0;
2928
0
    options->add_keys_to_agent_lifespan = 0;
2929
0
  }
2930
0
  if (options->num_identity_files == 0) {
2931
0
    add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_RSA, 0);
2932
0
    add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA, 0);
2933
0
    add_identity_file(options, "~/",
2934
0
        _PATH_SSH_CLIENT_ID_ECDSA_SK, 0);
2935
0
    add_identity_file(options, "~/",
2936
0
        _PATH_SSH_CLIENT_ID_ED25519, 0);
2937
0
    add_identity_file(options, "~/",
2938
0
        _PATH_SSH_CLIENT_ID_ED25519_SK, 0);
2939
0
    add_identity_file(options, "~/",
2940
0
        _PATH_SSH_CLIENT_ID_MLDSA44_ED25519, 0);
2941
0
  }
2942
0
  if (options->escape_char == -1)
2943
0
    options->escape_char = '~';
2944
0
  if (options->num_system_hostfiles == 0) {
2945
0
    options->system_hostfiles[options->num_system_hostfiles++] =
2946
0
        xstrdup(_PATH_SSH_SYSTEM_HOSTFILE);
2947
0
    options->system_hostfiles[options->num_system_hostfiles++] =
2948
0
        xstrdup(_PATH_SSH_SYSTEM_HOSTFILE2);
2949
0
  }
2950
0
  if (options->update_hostkeys == -1) {
2951
0
    if (options->verify_host_key_dns <= 0 &&
2952
0
        (options->num_user_hostfiles == 0 ||
2953
0
        (options->num_user_hostfiles == 1 && strcmp(options->
2954
0
        user_hostfiles[0], _PATH_SSH_USER_HOSTFILE) == 0)))
2955
0
      options->update_hostkeys = SSH_UPDATE_HOSTKEYS_YES;
2956
0
    else
2957
0
      options->update_hostkeys = SSH_UPDATE_HOSTKEYS_NO;
2958
0
  }
2959
0
  if (options->num_user_hostfiles == 0) {
2960
0
    options->user_hostfiles[options->num_user_hostfiles++] =
2961
0
        xstrdup(_PATH_SSH_USER_HOSTFILE);
2962
0
    options->user_hostfiles[options->num_user_hostfiles++] =
2963
0
        xstrdup(_PATH_SSH_USER_HOSTFILE2);
2964
0
  }
2965
0
  if (options->log_level == SYSLOG_LEVEL_NOT_SET)
2966
0
    options->log_level = SYSLOG_LEVEL_INFO;
2967
0
  if (options->log_facility == SYSLOG_FACILITY_NOT_SET)
2968
0
    options->log_facility = SYSLOG_FACILITY_USER;
2969
0
  if (options->no_host_authentication_for_localhost == -1)
2970
0
    options->no_host_authentication_for_localhost = 0;
2971
0
  if (options->identities_only == -1)
2972
0
    options->identities_only = 0;
2973
0
  if (options->enable_ssh_keysign == -1)
2974
0
    options->enable_ssh_keysign = 0;
2975
0
  if (options->rekey_limit == -1)
2976
0
    options->rekey_limit = 0;
2977
0
  if (options->rekey_interval == -1)
2978
0
    options->rekey_interval = 0;
2979
0
  if (options->verify_host_key_dns == -1)
2980
0
    options->verify_host_key_dns = 0;
2981
0
  if (options->server_alive_interval == -1)
2982
0
    options->server_alive_interval = 0;
2983
0
  if (options->server_alive_count_max == -1)
2984
0
    options->server_alive_count_max = 3;
2985
0
  if (options->control_master == -1)
2986
0
    options->control_master = 0;
2987
0
  if (options->control_persist == -1) {
2988
0
    options->control_persist = 0;
2989
0
    options->control_persist_timeout = 0;
2990
0
  }
2991
0
  if (options->hash_known_hosts == -1)
2992
0
    options->hash_known_hosts = 0;
2993
0
  if (options->tun_open == -1)
2994
0
    options->tun_open = SSH_TUNMODE_NO;
2995
0
  if (options->tun_local == -1)
2996
0
    options->tun_local = SSH_TUNID_ANY;
2997
0
  if (options->tun_remote == -1)
2998
0
    options->tun_remote = SSH_TUNID_ANY;
2999
0
  if (options->permit_local_command == -1)
3000
0
    options->permit_local_command = 0;
3001
0
  if (options->visual_host_key == -1)
3002
0
    options->visual_host_key = 0;
3003
0
  if (options->ip_qos_interactive == -1)
3004
0
    options->ip_qos_interactive = IPTOS_DSCP_EF;
3005
0
  if (options->ip_qos_bulk == -1)
3006
0
    options->ip_qos_bulk = IPTOS_DSCP_CS0;
3007
0
  if (options->request_tty == -1)
3008
0
    options->request_tty = REQUEST_TTY_AUTO;
3009
0
  if (options->session_type == -1)
3010
0
    options->session_type = SESSION_TYPE_DEFAULT;
3011
0
  if (options->stdin_null == -1)
3012
0
    options->stdin_null = 0;
3013
0
  if (options->fork_after_authentication == -1)
3014
0
    options->fork_after_authentication = 0;
3015
0
  if (options->proxy_use_fdpass == -1)
3016
0
    options->proxy_use_fdpass = 0;
3017
0
  if (options->canonicalize_max_dots == -1)
3018
0
    options->canonicalize_max_dots = 1;
3019
0
  if (options->canonicalize_fallback_local == -1)
3020
0
    options->canonicalize_fallback_local = 1;
3021
0
  if (options->canonicalize_hostname == -1)
3022
0
    options->canonicalize_hostname = SSH_CANONICALISE_NO;
3023
0
  if (options->fingerprint_hash == -1)
3024
0
    options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
3025
#ifdef ENABLE_SK_INTERNAL
3026
  if (options->sk_provider == NULL)
3027
    options->sk_provider = xstrdup("internal");
3028
#else
3029
0
  if (options->sk_provider == NULL)
3030
0
    options->sk_provider = xstrdup("$SSH_SK_PROVIDER");
3031
0
#endif
3032
0
  if (options->required_rsa_size == -1)
3033
0
    options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
3034
0
  if (options->warn_weak_crypto == -1)
3035
0
    options->warn_weak_crypto = 1;
3036
0
  if (options->enable_escape_commandline == -1)
3037
0
    options->enable_escape_commandline = 0;
3038
0
  if (options->obscure_keystroke_timing_interval == -1) {
3039
0
    options->obscure_keystroke_timing_interval =
3040
0
        SSH_KEYSTROKE_DEFAULT_INTERVAL_MS;
3041
0
  }
3042
3043
  /* Expand KEX name lists */
3044
0
  all_cipher = cipher_alg_list(',', 0);
3045
0
  all_mac = mac_alg_list(',');
3046
0
  all_kex = kex_alg_list(',');
3047
0
  all_key = sshkey_alg_list(0, 0, 1, ',');
3048
0
  all_sig = sshkey_alg_list(0, 1, 1, ',');
3049
  /* remove unsupported algos from default lists */
3050
0
  def_cipher = match_filter_allowlist(KEX_CLIENT_ENCRYPT, all_cipher);
3051
0
  def_mac = match_filter_allowlist(KEX_CLIENT_MAC, all_mac);
3052
0
  def_kex = match_filter_allowlist(KEX_CLIENT_KEX, all_kex);
3053
0
  def_key = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key);
3054
0
  def_sig = match_filter_allowlist(SSH_ALLOWED_CA_SIGALGS, all_sig);
3055
0
#define ASSEMBLE(what, defaults, all) \
3056
0
  do { \
3057
0
    if ((r = kex_assemble_names(&options->what, \
3058
0
        defaults, all)) != 0) { \
3059
0
      error_fr(r, "%s", #what); \
3060
0
      goto fail; \
3061
0
    } \
3062
0
  } while (0)
3063
0
  options->kex_algorithms_set = options->kex_algorithms != NULL;
3064
0
  ASSEMBLE(ciphers, def_cipher, all_cipher);
3065
0
  ASSEMBLE(macs, def_mac, all_mac);
3066
0
  ASSEMBLE(kex_algorithms, def_kex, all_kex);
3067
0
  ASSEMBLE(hostbased_accepted_algos, def_key, all_key);
3068
0
  ASSEMBLE(pubkey_accepted_algos, def_key, all_key);
3069
0
  ASSEMBLE(ca_sign_algorithms, def_sig, all_sig);
3070
0
#undef ASSEMBLE
3071
3072
0
#define CLEAR_ON_NONE(v) \
3073
0
  do { \
3074
0
    if (option_clear_or_none(v)) { \
3075
0
      free(v); \
3076
0
      v = NULL; \
3077
0
    } \
3078
0
  } while(0)
3079
0
#define CLEAR_ON_NONE_ARRAY(v, nv, none) \
3080
0
  do { \
3081
0
    if (options->nv == 1 && \
3082
0
        strcasecmp(options->v[0], none) == 0) { \
3083
0
      free(options->v[0]); \
3084
0
      free(options->v); \
3085
0
      options->v = NULL; \
3086
0
      options->nv = 0; \
3087
0
    } \
3088
0
  } while (0)
3089
0
  CLEAR_ON_NONE(options->local_command);
3090
0
  CLEAR_ON_NONE(options->remote_command);
3091
0
  CLEAR_ON_NONE(options->proxy_command);
3092
0
  CLEAR_ON_NONE(options->control_path);
3093
0
  CLEAR_ON_NONE(options->pkcs11_provider);
3094
0
  CLEAR_ON_NONE(options->sk_provider);
3095
0
  CLEAR_ON_NONE(options->known_hosts_command);
3096
0
  CLEAR_ON_NONE_ARRAY(channel_timeouts, num_channel_timeouts, "none");
3097
0
  CLEAR_ON_NONE_ARRAY(revoked_host_keys, num_revoked_host_keys, "none");
3098
0
#undef CLEAR_ON_NONE
3099
0
#undef CLEAR_ON_NONE_ARRAY
3100
0
  if (options->jump_host != NULL &&
3101
0
      strcmp(options->jump_host, "none") == 0 &&
3102
0
      options->jump_port == 0 && options->jump_user == NULL) {
3103
0
    free(options->jump_host);
3104
0
    options->jump_host = NULL;
3105
0
  }
3106
0
  if (options->num_permitted_cnames == 1 &&
3107
0
      !config_has_permitted_cnames(options)) {
3108
    /* clean up CanonicalizePermittedCNAMEs=none */
3109
0
    free(options->permitted_cnames[0].source_list);
3110
0
    free(options->permitted_cnames[0].target_list);
3111
0
    memset(options->permitted_cnames, '\0',
3112
0
        sizeof(*options->permitted_cnames));
3113
0
    options->num_permitted_cnames = 0;
3114
0
  }
3115
  /* options->identity_agent distinguishes NULL from 'none' */
3116
  /* options->user will be set in the main program if appropriate */
3117
  /* options->hostname will be set in the main program if appropriate */
3118
  /* options->host_key_alias should not be set by default */
3119
  /* options->preferred_authentications will be set in ssh */
3120
3121
  /* success */
3122
0
  ret = 0;
3123
0
 fail:
3124
0
  free(all_cipher);
3125
0
  free(all_mac);
3126
0
  free(all_kex);
3127
0
  free(all_key);
3128
0
  free(all_sig);
3129
0
  free(def_cipher);
3130
0
  free(def_mac);
3131
0
  free(def_kex);
3132
0
  free(def_key);
3133
0
  free(def_sig);
3134
0
  return ret;
3135
0
}
3136
3137
void
3138
free_options(Options *o)
3139
0
{
3140
0
  int i;
3141
3142
0
  if (o == NULL)
3143
0
    return;
3144
3145
0
#define FREE_ARRAY(type, n, a) \
3146
0
  do { \
3147
0
    type _i; \
3148
0
    for (_i = 0; _i < (n); _i++) \
3149
0
      free((a)[_i]); \
3150
0
  } while (0)
3151
3152
0
  free(o->forward_agent_sock_path);
3153
0
  free(o->xauth_location);
3154
0
  FREE_ARRAY(u_int, o->num_log_verbose, o->log_verbose);
3155
0
  free(o->log_verbose);
3156
0
  free(o->ciphers);
3157
0
  free(o->macs);
3158
0
  free(o->hostkeyalgorithms);
3159
0
  free(o->kex_algorithms);
3160
0
  free(o->ca_sign_algorithms);
3161
0
  free(o->hostname);
3162
0
  free(o->host_key_alias);
3163
0
  free(o->proxy_command);
3164
0
  free(o->user);
3165
0
  FREE_ARRAY(u_int, o->num_system_hostfiles, o->system_hostfiles);
3166
0
  FREE_ARRAY(u_int, o->num_user_hostfiles, o->user_hostfiles);
3167
0
  free(o->preferred_authentications);
3168
0
  free(o->bind_address);
3169
0
  free(o->bind_interface);
3170
0
  free(o->pkcs11_provider);
3171
0
  free(o->sk_provider);
3172
0
  for (i = 0; i < o->num_identity_files; i++) {
3173
0
    free(o->identity_files[i]);
3174
0
    sshkey_free(o->identity_keys[i]);
3175
0
  }
3176
0
  for (i = 0; i < o->num_certificate_files; i++) {
3177
0
    free(o->certificate_files[i]);
3178
0
    sshkey_free(o->certificates[i]);
3179
0
  }
3180
0
  free(o->identity_agent);
3181
0
  for (i = 0; i < o->num_local_forwards; i++) {
3182
0
    free(o->local_forwards[i].listen_host);
3183
0
    free(o->local_forwards[i].listen_path);
3184
0
    free(o->local_forwards[i].connect_host);
3185
0
    free(o->local_forwards[i].connect_path);
3186
0
  }
3187
0
  free(o->local_forwards);
3188
0
  for (i = 0; i < o->num_remote_forwards; i++) {
3189
0
    free(o->remote_forwards[i].listen_host);
3190
0
    free(o->remote_forwards[i].listen_path);
3191
0
    free(o->remote_forwards[i].connect_host);
3192
0
    free(o->remote_forwards[i].connect_path);
3193
0
  }
3194
0
  free(o->remote_forwards);
3195
0
  free(o->stdio_forward_host);
3196
0
  FREE_ARRAY(u_int, o->num_send_env, o->send_env);
3197
0
  free(o->send_env);
3198
0
  FREE_ARRAY(u_int, o->num_setenv, o->setenv);
3199
0
  free(o->setenv);
3200
0
  free(o->control_path);
3201
0
  free(o->local_command);
3202
0
  free(o->remote_command);
3203
0
  FREE_ARRAY(int, o->num_canonical_domains, o->canonical_domains);
3204
0
  for (i = 0; i < o->num_permitted_cnames; i++) {
3205
0
    free(o->permitted_cnames[i].source_list);
3206
0
    free(o->permitted_cnames[i].target_list);
3207
0
  }
3208
0
  FREE_ARRAY(u_int, o->num_revoked_host_keys, o->revoked_host_keys);
3209
0
  free(o->revoked_host_keys);
3210
0
  free(o->hostbased_accepted_algos);
3211
0
  free(o->pubkey_accepted_algos);
3212
0
  free(o->jump_user);
3213
0
  free(o->jump_host);
3214
0
  free(o->jump_extra);
3215
0
  free(o->ignored_unknown);
3216
0
  explicit_bzero(o, sizeof(*o));
3217
0
#undef FREE_ARRAY
3218
0
}
3219
3220
struct fwdarg {
3221
  char *arg;
3222
  int ispath;
3223
};
3224
3225
/*
3226
 * parse_fwd_field
3227
 * parses the next field in a port forwarding specification.
3228
 * sets fwd to the parsed field and advances p past the colon
3229
 * or sets it to NULL at end of string.
3230
 * returns 0 on success, else non-zero.
3231
 */
3232
static int
3233
parse_fwd_field(char **p, struct fwdarg *fwd)
3234
10
{
3235
10
  char *ep, *cp = *p;
3236
10
  int ispath = 0;
3237
3238
10
  if (*cp == '\0') {
3239
4
    *p = NULL;
3240
4
    return -1;  /* end of string */
3241
4
  }
3242
3243
  /*
3244
   * A field escaped with square brackets is used literally.
3245
   * XXX - allow ']' to be escaped via backslash?
3246
   */
3247
6
  if (*cp == '[') {
3248
    /* find matching ']' */
3249
0
    for (ep = cp + 1; *ep != ']' && *ep != '\0'; ep++) {
3250
0
      if (*ep == '/')
3251
0
        ispath = 1;
3252
0
    }
3253
    /* no matching ']' or not at end of field. */
3254
0
    if (ep[0] != ']' || (ep[1] != ':' && ep[1] != '\0'))
3255
0
      return -1;
3256
    /* NUL terminate the field and advance p past the colon */
3257
0
    *ep++ = '\0';
3258
0
    if (*ep != '\0')
3259
0
      *ep++ = '\0';
3260
0
    fwd->arg = cp + 1;
3261
0
    fwd->ispath = ispath;
3262
0
    *p = ep;
3263
0
    return 0;
3264
0
  }
3265
3266
190
  for (cp = *p; *cp != '\0'; cp++) {
3267
186
    switch (*cp) {
3268
0
    case '\\':
3269
0
      memmove(cp, cp + 1, strlen(cp + 1) + 1);
3270
0
      if (*cp == '\0')
3271
0
        return -1;
3272
0
      break;
3273
62
    case '/':
3274
62
      ispath = 1;
3275
62
      break;
3276
2
    case ':':
3277
2
      *cp++ = '\0';
3278
2
      goto done;
3279
186
    }
3280
186
  }
3281
6
done:
3282
6
  fwd->arg = *p;
3283
6
  fwd->ispath = ispath;
3284
6
  *p = cp;
3285
6
  return 0;
3286
6
}
3287
3288
/*
3289
 * parse_forward
3290
 * parses a string containing a port forwarding specification of the form:
3291
 *   dynamicfwd == 0
3292
 *  [listenhost:]listenport|listenpath:connecthost:connectport|connectpath
3293
 *  listenpath:connectpath
3294
 *   dynamicfwd == 1
3295
 *  [listenhost:]listenport
3296
 * returns number of arguments parsed or zero on error
3297
 */
3298
int
3299
parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd)
3300
4
{
3301
4
  struct fwdarg fwdargs[4];
3302
4
  char *p, *cp;
3303
4
  int i, err;
3304
3305
4
  memset(fwd, 0, sizeof(*fwd));
3306
4
  memset(fwdargs, 0, sizeof(fwdargs));
3307
3308
  /*
3309
   * We expand environment variables before checking if we think they're
3310
   * paths so that if ${VAR} expands to a fully qualified path it is
3311
   * treated as a path.
3312
   */
3313
4
  cp = p = dollar_expand(&err, fwdspec);
3314
4
  if (p == NULL || err)
3315
0
    return 0;
3316
3317
  /* skip leading spaces */
3318
4
  while (isspace((u_char)*cp))
3319
42
    cp++;
3320
3321
10
  for (i = 0; i < 4; ++i) {
3322
10
    if (parse_fwd_field(&cp, &fwdargs[i]) != 0)
3323
4
      break;
3324
10
  }
3325
3326
  /* Check for trailing garbage */
3327
4
  if (cp != NULL && *cp != '\0') {
3328
0
    i = 0;  /* failure */
3329
0
  }
3330
3331
4
  switch (i) {
3332
2
  case 1:
3333
2
    if (fwdargs[0].ispath) {
3334
2
      fwd->listen_path = xstrdup(fwdargs[0].arg);
3335
2
      fwd->listen_port = PORT_STREAMLOCAL;
3336
2
    } else {
3337
0
      fwd->listen_host = NULL;
3338
0
      fwd->listen_port = a2port(fwdargs[0].arg);
3339
0
    }
3340
2
    fwd->connect_host = xstrdup("socks");
3341
2
    break;
3342
3343
2
  case 2:
3344
2
    if (fwdargs[0].ispath && fwdargs[1].ispath) {
3345
2
      fwd->listen_path = xstrdup(fwdargs[0].arg);
3346
2
      fwd->listen_port = PORT_STREAMLOCAL;
3347
2
      fwd->connect_path = xstrdup(fwdargs[1].arg);
3348
2
      fwd->connect_port = PORT_STREAMLOCAL;
3349
2
    } else if (fwdargs[1].ispath) {
3350
0
      fwd->listen_host = NULL;
3351
0
      fwd->listen_port = a2port(fwdargs[0].arg);
3352
0
      fwd->connect_path = xstrdup(fwdargs[1].arg);
3353
0
      fwd->connect_port = PORT_STREAMLOCAL;
3354
0
    } else {
3355
0
      fwd->listen_host = xstrdup(fwdargs[0].arg);
3356
0
      fwd->listen_port = a2port(fwdargs[1].arg);
3357
0
      fwd->connect_host = xstrdup("socks");
3358
0
    }
3359
2
    break;
3360
3361
0
  case 3:
3362
0
    if (fwdargs[0].ispath) {
3363
0
      fwd->listen_path = xstrdup(fwdargs[0].arg);
3364
0
      fwd->listen_port = PORT_STREAMLOCAL;
3365
0
      fwd->connect_host = xstrdup(fwdargs[1].arg);
3366
0
      fwd->connect_port = a2port(fwdargs[2].arg);
3367
0
    } else if (fwdargs[2].ispath) {
3368
0
      fwd->listen_host = xstrdup(fwdargs[0].arg);
3369
0
      fwd->listen_port = a2port(fwdargs[1].arg);
3370
0
      fwd->connect_path = xstrdup(fwdargs[2].arg);
3371
0
      fwd->connect_port = PORT_STREAMLOCAL;
3372
0
    } else {
3373
0
      fwd->listen_host = NULL;
3374
0
      fwd->listen_port = a2port(fwdargs[0].arg);
3375
0
      fwd->connect_host = xstrdup(fwdargs[1].arg);
3376
0
      fwd->connect_port = a2port(fwdargs[2].arg);
3377
0
    }
3378
0
    break;
3379
3380
0
  case 4:
3381
0
    fwd->listen_host = xstrdup(fwdargs[0].arg);
3382
0
    fwd->listen_port = a2port(fwdargs[1].arg);
3383
0
    fwd->connect_host = xstrdup(fwdargs[2].arg);
3384
0
    fwd->connect_port = a2port(fwdargs[3].arg);
3385
0
    break;
3386
0
  default:
3387
0
    i = 0; /* failure */
3388
4
  }
3389
3390
4
  free(p);
3391
3392
4
  if (dynamicfwd) {
3393
2
    if (!(i == 1 || i == 2))
3394
0
      goto fail_free;
3395
2
  } else {
3396
2
    if (!(i == 3 || i == 4)) {
3397
2
      if (fwd->connect_path == NULL &&
3398
0
          fwd->listen_path == NULL)
3399
0
        goto fail_free;
3400
2
    }
3401
2
    if (fwd->connect_port <= 0 && fwd->connect_path == NULL)
3402
0
      goto fail_free;
3403
2
  }
3404
3405
4
  if ((fwd->listen_port < 0 && fwd->listen_path == NULL) ||
3406
4
      (!remotefwd && fwd->listen_port == 0))
3407
0
    goto fail_free;
3408
4
  if (fwd->connect_host != NULL &&
3409
2
      strlen(fwd->connect_host) >= NI_MAXHOST)
3410
0
    goto fail_free;
3411
  /*
3412
   * XXX - if connecting to a remote socket, max sun len may not
3413
   * match this host
3414
   */
3415
4
  if (fwd->connect_path != NULL &&
3416
2
      strlen(fwd->connect_path) >= PATH_MAX_SUN)
3417
0
    goto fail_free;
3418
4
  if (fwd->listen_host != NULL &&
3419
0
      strlen(fwd->listen_host) >= NI_MAXHOST)
3420
0
    goto fail_free;
3421
4
  if (fwd->listen_path != NULL &&
3422
4
      strlen(fwd->listen_path) >= PATH_MAX_SUN)
3423
0
    goto fail_free;
3424
3425
4
  return (i);
3426
3427
0
 fail_free:
3428
0
  free(fwd->connect_host);
3429
0
  fwd->connect_host = NULL;
3430
0
  free(fwd->connect_path);
3431
0
  fwd->connect_path = NULL;
3432
0
  free(fwd->listen_host);
3433
0
  fwd->listen_host = NULL;
3434
0
  free(fwd->listen_path);
3435
0
  fwd->listen_path = NULL;
3436
0
  return (0);
3437
4
}
3438
3439
int
3440
ssh_valid_hostname(const char *s)
3441
0
{
3442
0
  size_t i;
3443
3444
0
  if (*s == '-')
3445
0
    return 0;
3446
0
  for (i = 0; s[i] != 0; i++) {
3447
0
    if (strchr("'`\"$\\;&<>|(){},", s[i]) != NULL ||
3448
0
        isspace((u_char)s[i]) || iscntrl((u_char)s[i]))
3449
0
      return 0;
3450
0
  }
3451
0
  return 1;
3452
0
}
3453
3454
int
3455
ssh_valid_ruser(const char *s)
3456
0
{
3457
0
  size_t i;
3458
3459
0
  if (*s == '-')
3460
0
    return 0;
3461
0
  for (i = 0; s[i] != 0; i++) {
3462
0
    if (iscntrl((u_char)s[i]))
3463
0
      return 0;
3464
0
    if (strchr("'`\";&<>|(){}$\\", s[i]) != NULL)
3465
0
      return 0;
3466
    /* Disallow '-' after whitespace */
3467
0
    if (isspace((u_char)s[i]) && s[i + 1] == '-')
3468
0
      return 0;
3469
0
  }
3470
0
  return 1;
3471
0
}
3472
3473
int
3474
parse_jump(const char *s, Options *o, int strict, int active)
3475
0
{
3476
0
  char *orig = NULL, *sdup = NULL, *cp;
3477
0
  char *tmp_user = NULL, *tmp_host = NULL, *host = NULL, *user = NULL;
3478
0
  int r, ret = -1, tmp_port = -1, port = -1, first = 1;
3479
3480
0
  if (strcasecmp(s, "none") == 0) {
3481
0
    if (active && o->jump_host == NULL) {
3482
0
      o->jump_host = xstrdup("none");
3483
0
      o->jump_port = 0;
3484
0
    }
3485
0
    return 0;
3486
0
  }
3487
3488
0
  orig = xstrdup(s);
3489
0
  if ((cp = strchr(orig, '#')) != NULL)
3490
0
    *cp = '\0';
3491
0
  rtrim(orig);
3492
3493
0
  active &= o->proxy_command == NULL && o->jump_host == NULL;
3494
0
  sdup = xstrdup(orig);
3495
0
  do {
3496
    /* Work backwards through string */
3497
0
    if ((cp = strrchr(sdup, ',')) == NULL)
3498
0
      cp = sdup; /* last */
3499
0
    else
3500
0
      *cp++ = '\0';
3501
3502
0
    r = parse_ssh_uri(cp, &tmp_user, &tmp_host, &tmp_port);
3503
0
    if (r == -1 || (r == 1 && parse_user_host_port(cp,
3504
0
        &tmp_user, &tmp_host, &tmp_port) != 0))
3505
0
      goto out; /* error already logged */
3506
0
    if (strict) {
3507
0
      if (!ssh_valid_hostname(tmp_host)) {
3508
0
        error_f("invalid hostname \"%s\"", tmp_host);
3509
0
        goto out;
3510
0
      }
3511
0
      if (tmp_user != NULL && !ssh_valid_ruser(tmp_user)) {
3512
0
        error_f("invalid username \"%s\"", tmp_user);
3513
0
        goto out;
3514
0
      }
3515
0
    }
3516
0
    if (first) {
3517
0
      user = tmp_user;
3518
0
      host = tmp_host;
3519
0
      port = tmp_port;
3520
0
      tmp_user = tmp_host = NULL; /* transferred */
3521
0
    }
3522
0
    first = 0; /* only check syntax for subsequent hosts */
3523
0
    free(tmp_user);
3524
0
    free(tmp_host);
3525
0
    tmp_user = tmp_host = NULL;
3526
0
    tmp_port = -1;
3527
0
  } while (cp != sdup);
3528
3529
  /* success */
3530
0
  if (active) {
3531
0
    o->jump_user = user;
3532
0
    o->jump_host = host;
3533
0
    o->jump_port = port;
3534
0
    o->proxy_command = xstrdup("none");
3535
0
    user = host = NULL; /* transferred */
3536
0
    if (orig != NULL && (cp = strrchr(orig, ',')) != NULL) {
3537
0
      o->jump_extra = xstrdup(orig);
3538
0
      o->jump_extra[cp - orig] = '\0';
3539
0
    }
3540
0
  }
3541
0
  ret = 0;
3542
0
 out:
3543
0
  free(orig);
3544
0
  free(sdup);
3545
0
  free(tmp_user);
3546
0
  free(tmp_host);
3547
0
  free(user);
3548
0
  free(host);
3549
0
  return ret;
3550
0
}
3551
3552
int
3553
parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp)
3554
0
{
3555
0
  char *user = NULL, *host = NULL, *path = NULL;
3556
0
  int r, port;
3557
3558
0
  r = parse_uri("ssh", uri, &user, &host, &port, &path);
3559
0
  if (r == 0 && path != NULL)
3560
0
    r = -1;   /* path not allowed */
3561
0
  if (r == 0) {
3562
0
    if (userp != NULL) {
3563
0
      *userp = user;
3564
0
      user = NULL;
3565
0
    }
3566
0
    if (hostp != NULL) {
3567
0
      *hostp = host;
3568
0
      host = NULL;
3569
0
    }
3570
0
    if (portp != NULL)
3571
0
      *portp = port;
3572
0
  }
3573
0
  free(user);
3574
0
  free(host);
3575
0
  free(path);
3576
0
  return r;
3577
0
}
3578
3579
/* XXX the following is a near-verbatim copy from servconf.c; refactor */
3580
static const char *
3581
fmt_multistate_int(int val, const struct multistate *m)
3582
0
{
3583
0
  u_int i;
3584
3585
0
  for (i = 0; m[i].key != NULL; i++) {
3586
0
    if (m[i].value == val)
3587
0
      return m[i].key;
3588
0
  }
3589
0
  return "UNKNOWN";
3590
0
}
3591
3592
static const char *
3593
fmt_intarg(OpCodes code, int val)
3594
0
{
3595
0
  if (val == -1)
3596
0
    return "unset";
3597
0
  switch (code) {
3598
0
  case oAddressFamily:
3599
0
    return fmt_multistate_int(val, multistate_addressfamily);
3600
0
  case oCompression:
3601
0
    return fmt_multistate_int(val, multistate_compression);
3602
0
  case oVerifyHostKeyDNS:
3603
0
  case oUpdateHostkeys:
3604
0
    return fmt_multistate_int(val, multistate_yesnoask);
3605
0
  case oStrictHostKeyChecking:
3606
0
    return fmt_multistate_int(val, multistate_strict_hostkey);
3607
0
  case oControlMaster:
3608
0
    return fmt_multistate_int(val, multistate_controlmaster);
3609
0
  case oTunnel:
3610
0
    return fmt_multistate_int(val, multistate_tunnel);
3611
0
  case oRequestTTY:
3612
0
    return fmt_multistate_int(val, multistate_requesttty);
3613
0
  case oSessionType:
3614
0
    return fmt_multistate_int(val, multistate_sessiontype);
3615
0
  case oCanonicalizeHostname:
3616
0
    return fmt_multistate_int(val, multistate_canonicalizehostname);
3617
0
  case oAddKeysToAgent:
3618
0
    return fmt_multistate_int(val, multistate_yesnoaskconfirm);
3619
0
  case oPubkeyAuthentication:
3620
0
    return fmt_multistate_int(val, multistate_pubkey_auth);
3621
0
  case oTCPKeepAlive:
3622
0
    return fmt_multistate_int(val, multistate_keepalives);
3623
0
  case oFingerprintHash:
3624
0
    return ssh_digest_alg_name(val);
3625
0
  default:
3626
0
    switch (val) {
3627
0
    case 0:
3628
0
      return "no";
3629
0
    case 1:
3630
0
      return "yes";
3631
0
    default:
3632
0
      return "UNKNOWN";
3633
0
    }
3634
0
  }
3635
0
}
3636
3637
static const char *
3638
lookup_opcode_name(OpCodes code)
3639
882
{
3640
882
  u_int i;
3641
3642
74.0k
  for (i = 0; keywords[i].name != NULL; i++)
3643
74.0k
    if (keywords[i].opcode == code)
3644
882
      return(keywords[i].name);
3645
0
  return "UNKNOWN";
3646
882
}
3647
3648
static void
3649
dump_cfg_int(OpCodes code, int val)
3650
0
{
3651
0
  if (code == oObscureKeystrokeTiming) {
3652
0
    if (val == 0) {
3653
0
      printf("%s no\n", lookup_opcode_name(code));
3654
0
      return;
3655
0
    } else if (val == SSH_KEYSTROKE_DEFAULT_INTERVAL_MS) {
3656
0
      printf("%s yes\n", lookup_opcode_name(code));
3657
0
      return;
3658
0
    }
3659
    /* FALLTHROUGH */
3660
0
  }
3661
0
  printf("%s %d\n", lookup_opcode_name(code), val);
3662
0
}
3663
3664
static void
3665
dump_cfg_fmtint(OpCodes code, int val)
3666
0
{
3667
0
  printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val));
3668
0
}
3669
3670
static void
3671
dump_cfg_string(OpCodes code, const char *val)
3672
0
{
3673
0
  if (val == NULL)
3674
0
    return;
3675
0
  printf("%s %s\n", lookup_opcode_name(code), val);
3676
0
}
3677
3678
static void
3679
dump_cfg_strarray(OpCodes code, u_int count, char **vals)
3680
0
{
3681
0
  u_int i;
3682
3683
0
  for (i = 0; i < count; i++)
3684
0
    printf("%s %s\n", lookup_opcode_name(code), vals[i]);
3685
0
}
3686
3687
static void
3688
dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals)
3689
0
{
3690
0
  u_int i;
3691
3692
0
  printf("%s", lookup_opcode_name(code));
3693
0
  if (count == 0)
3694
0
    printf(" none");
3695
0
  for (i = 0; i < count; i++)
3696
0
    printf(" %s",  vals[i]);
3697
0
  printf("\n");
3698
0
}
3699
3700
static void
3701
dump_cfg_forwards(OpCodes code, u_int count, const struct Forward *fwds)
3702
0
{
3703
0
  const struct Forward *fwd;
3704
0
  u_int i;
3705
3706
  /* oDynamicForward */
3707
0
  for (i = 0; i < count; i++) {
3708
0
    fwd = &fwds[i];
3709
0
    if (code == oDynamicForward && fwd->connect_host != NULL &&
3710
0
        strcmp(fwd->connect_host, "socks") != 0)
3711
0
      continue;
3712
0
    if (code == oLocalForward && fwd->connect_host != NULL &&
3713
0
        strcmp(fwd->connect_host, "socks") == 0)
3714
0
      continue;
3715
0
    printf("%s", lookup_opcode_name(code));
3716
0
    if (fwd->listen_port == PORT_STREAMLOCAL)
3717
0
      printf(" %s", fwd->listen_path);
3718
0
    else if (fwd->listen_host == NULL)
3719
0
      printf(" %d", fwd->listen_port);
3720
0
    else {
3721
0
      printf(" [%s]:%d",
3722
0
          fwd->listen_host, fwd->listen_port);
3723
0
    }
3724
0
    if (code != oDynamicForward) {
3725
0
      if (fwd->connect_port == PORT_STREAMLOCAL)
3726
0
        printf(" %s", fwd->connect_path);
3727
0
      else if (fwd->connect_host == NULL)
3728
0
        printf(" %d", fwd->connect_port);
3729
0
      else {
3730
0
        printf(" [%s]:%d",
3731
0
            fwd->connect_host, fwd->connect_port);
3732
0
      }
3733
0
    }
3734
0
    printf("\n");
3735
0
  }
3736
0
}
3737
3738
void
3739
dump_client_config(Options *o, const char *host)
3740
0
{
3741
0
  int i, r;
3742
0
  char buf[8], *all_key;
3743
3744
  /*
3745
   * Expand HostKeyAlgorithms name lists. This isn't handled in
3746
   * fill_default_options() like the other algorithm lists because
3747
   * the host key algorithms are by default dynamically chosen based
3748
   * on the host's keys found in known_hosts.
3749
   */
3750
0
  all_key = sshkey_alg_list(0, 0, 1, ',');
3751
0
  if ((r = kex_assemble_names(&o->hostkeyalgorithms, kex_default_pk_alg(),
3752
0
      all_key)) != 0)
3753
0
    fatal_fr(r, "expand HostKeyAlgorithms");
3754
0
  free(all_key);
3755
3756
  /* Most interesting options first: user, host, port */
3757
0
  dump_cfg_string(oHost, o->host_arg);
3758
0
  dump_cfg_string(oUser, o->user);
3759
0
  dump_cfg_string(oHostname, host);
3760
0
  dump_cfg_int(oPort, o->port);
3761
3762
  /* Flag options */
3763
0
  dump_cfg_fmtint(oAddressFamily, o->address_family);
3764
0
  dump_cfg_fmtint(oBatchMode, o->batch_mode);
3765
0
  dump_cfg_fmtint(oCanonicalizeFallbackLocal, o->canonicalize_fallback_local);
3766
0
  dump_cfg_fmtint(oCanonicalizeHostname, o->canonicalize_hostname);
3767
0
  dump_cfg_fmtint(oCheckHostIP, o->check_host_ip);
3768
0
  dump_cfg_fmtint(oCompression, o->compression);
3769
0
  dump_cfg_fmtint(oControlMaster, o->control_master);
3770
0
  dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign);
3771
0
  dump_cfg_fmtint(oClearAllForwardings, o->clear_forwardings);
3772
0
  dump_cfg_fmtint(oExitOnForwardFailure, o->exit_on_forward_failure);
3773
0
  dump_cfg_fmtint(oFingerprintHash, o->fingerprint_hash);
3774
0
  dump_cfg_fmtint(oForwardX11, o->forward_x11);
3775
0
  dump_cfg_fmtint(oForwardX11Trusted, o->forward_x11_trusted);
3776
0
  dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
3777
#ifdef GSSAPI
3778
  dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
3779
  dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
3780
#endif /* GSSAPI */
3781
0
  dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
3782
0
  dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
3783
0
  dump_cfg_fmtint(oIdentitiesOnly, o->identities_only);
3784
0
  dump_cfg_fmtint(oKbdInteractiveAuthentication, o->kbd_interactive_authentication);
3785
0
  dump_cfg_fmtint(oNoHostAuthenticationForLocalhost, o->no_host_authentication_for_localhost);
3786
0
  dump_cfg_fmtint(oPasswordAuthentication, o->password_authentication);
3787
0
  dump_cfg_fmtint(oPermitLocalCommand, o->permit_local_command);
3788
0
  dump_cfg_fmtint(oProxyUseFdpass, o->proxy_use_fdpass);
3789
0
  dump_cfg_fmtint(oPubkeyAuthentication, o->pubkey_authentication);
3790
0
  dump_cfg_fmtint(oRequestTTY, o->request_tty);
3791
0
  dump_cfg_fmtint(oSessionType, o->session_type);
3792
0
  dump_cfg_fmtint(oStdinNull, o->stdin_null);
3793
0
  dump_cfg_fmtint(oForkAfterAuthentication, o->fork_after_authentication);
3794
0
  dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink);
3795
0
  dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking);
3796
0
  dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive);
3797
0
  dump_cfg_fmtint(oTunnel, o->tun_open);
3798
0
  dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns);
3799
0
  dump_cfg_fmtint(oVisualHostKey, o->visual_host_key);
3800
0
  dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys);
3801
0
  dump_cfg_fmtint(oEnableEscapeCommandline, o->enable_escape_commandline);
3802
0
  dump_cfg_fmtint(oWarnWeakCrypto, o->warn_weak_crypto);
3803
3804
  /* Integer options */
3805
0
  dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots);
3806
0
  dump_cfg_int(oConnectionAttempts, o->connection_attempts);
3807
0
  dump_cfg_int(oForwardX11Timeout, o->forward_x11_timeout);
3808
0
  dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts);
3809
0
  dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max);
3810
0
  dump_cfg_int(oServerAliveInterval, o->server_alive_interval);
3811
0
  dump_cfg_int(oRequiredRSASize, o->required_rsa_size);
3812
0
  dump_cfg_int(oObscureKeystrokeTiming,
3813
0
      o->obscure_keystroke_timing_interval);
3814
3815
  /* String options */
3816
0
  dump_cfg_string(oBindAddress, o->bind_address);
3817
0
  dump_cfg_string(oBindInterface, o->bind_interface);
3818
0
  dump_cfg_string(oCiphers, o->ciphers);
3819
0
  dump_cfg_string(oControlPath, o->control_path);
3820
0
  dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms);
3821
0
  dump_cfg_string(oHostKeyAlias, o->host_key_alias);
3822
0
  dump_cfg_string(oHostbasedAcceptedAlgorithms, o->hostbased_accepted_algos);
3823
0
  dump_cfg_string(oIdentityAgent, o->identity_agent);
3824
0
  dump_cfg_string(oIgnoreUnknown, o->ignored_unknown);
3825
0
  dump_cfg_string(oKbdInteractiveDevices, o->kbd_interactive_devices);
3826
0
  dump_cfg_string(oKexAlgorithms, o->kex_algorithms);
3827
0
  dump_cfg_string(oCASignatureAlgorithms, o->ca_sign_algorithms);
3828
0
  dump_cfg_string(oLocalCommand, o->local_command);
3829
0
  dump_cfg_string(oRemoteCommand, o->remote_command);
3830
0
  dump_cfg_string(oLogLevel, log_level_name(o->log_level));
3831
0
  dump_cfg_string(oMacs, o->macs);
3832
0
#ifdef ENABLE_PKCS11
3833
0
  dump_cfg_string(oPKCS11Provider, o->pkcs11_provider);
3834
0
#endif
3835
0
  dump_cfg_string(oSecurityKeyProvider, o->sk_provider);
3836
0
  dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
3837
0
  dump_cfg_string(oPubkeyAcceptedAlgorithms, o->pubkey_accepted_algos);
3838
0
  dump_cfg_string(oXAuthLocation, o->xauth_location);
3839
0
  dump_cfg_string(oKnownHostsCommand, o->known_hosts_command);
3840
0
  dump_cfg_string(oTag, o->tag);
3841
0
  dump_cfg_string(oVersionAddendum, o->version_addendum);
3842
3843
  /* Forwards */
3844
0
  dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards);
3845
0
  dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards);
3846
0
  dump_cfg_forwards(oRemoteForward, o->num_remote_forwards, o->remote_forwards);
3847
3848
  /* String array options */
3849
0
  dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files);
3850
0
  dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains);
3851
0
  dump_cfg_strarray(oCertificateFile, o->num_certificate_files, o->certificate_files);
3852
0
  dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles);
3853
0
  dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles);
3854
0
  dump_cfg_strarray_oneline(oRevokedHostKeys, o->num_revoked_host_keys, o->revoked_host_keys);
3855
0
  dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env);
3856
0
  dump_cfg_strarray(oSetEnv, o->num_setenv, o->setenv);
3857
0
  dump_cfg_strarray_oneline(oLogVerbose,
3858
0
      o->num_log_verbose, o->log_verbose);
3859
0
  dump_cfg_strarray_oneline(oChannelTimeout,
3860
0
      o->num_channel_timeouts, o->channel_timeouts);
3861
3862
  /* Special cases */
3863
3864
  /* PermitRemoteOpen */
3865
0
  if (o->num_permitted_remote_opens == 0)
3866
0
    printf("%s any\n", lookup_opcode_name(oPermitRemoteOpen));
3867
0
  else
3868
0
    dump_cfg_strarray_oneline(oPermitRemoteOpen,
3869
0
        o->num_permitted_remote_opens, o->permitted_remote_opens);
3870
3871
  /* AddKeysToAgent */
3872
0
  if (o->add_keys_to_agent_lifespan <= 0)
3873
0
    dump_cfg_fmtint(oAddKeysToAgent, o->add_keys_to_agent);
3874
0
  else {
3875
0
    printf("addkeystoagent%s %d\n",
3876
0
        o->add_keys_to_agent == 3 ? " confirm" : "",
3877
0
        o->add_keys_to_agent_lifespan);
3878
0
  }
3879
3880
  /* oForwardAgent */
3881
0
  if (o->forward_agent_sock_path == NULL)
3882
0
    dump_cfg_fmtint(oForwardAgent, o->forward_agent);
3883
0
  else
3884
0
    dump_cfg_string(oForwardAgent, o->forward_agent_sock_path);
3885
3886
  /* oConnectTimeout */
3887
0
  if (o->connection_timeout == -1)
3888
0
    printf("connecttimeout none\n");
3889
0
  else
3890
0
    dump_cfg_int(oConnectTimeout, o->connection_timeout);
3891
3892
  /* oTunnelDevice */
3893
0
  printf("tunneldevice");
3894
0
  if (o->tun_local == SSH_TUNID_ANY)
3895
0
    printf(" any");
3896
0
  else
3897
0
    printf(" %d", o->tun_local);
3898
0
  if (o->tun_remote == SSH_TUNID_ANY)
3899
0
    printf(":any");
3900
0
  else
3901
0
    printf(":%d", o->tun_remote);
3902
0
  printf("\n");
3903
3904
  /* oCanonicalizePermittedCNAMEs */
3905
0
  printf("canonicalizepermittedcnames");
3906
0
  if (o->num_permitted_cnames == 0)
3907
0
    printf(" none");
3908
0
  for (i = 0; i < o->num_permitted_cnames; i++) {
3909
0
    printf(" %s:%s", o->permitted_cnames[i].source_list,
3910
0
        o->permitted_cnames[i].target_list);
3911
0
  }
3912
0
  printf("\n");
3913
3914
  /* oControlPersist */
3915
0
  if (o->control_persist == 0 || o->control_persist_timeout == 0)
3916
0
    dump_cfg_fmtint(oControlPersist, o->control_persist);
3917
0
  else
3918
0
    dump_cfg_int(oControlPersist, o->control_persist_timeout);
3919
3920
  /* oEscapeChar */
3921
0
  if (o->escape_char == SSH_ESCAPECHAR_NONE)
3922
0
    printf("escapechar none\n");
3923
0
  else {
3924
0
    vis(buf, o->escape_char, VIS_WHITE, 0);
3925
0
    printf("escapechar %s\n", buf);
3926
0
  }
3927
3928
  /* oIPQoS */
3929
0
  printf("ipqos %s ", iptos2str(o->ip_qos_interactive));
3930
0
  printf("%s\n", iptos2str(o->ip_qos_bulk));
3931
3932
  /* oRekeyLimit */
3933
0
  printf("rekeylimit %llu %d\n",
3934
0
      (unsigned long long)o->rekey_limit, o->rekey_interval);
3935
3936
  /* oStreamLocalBindMask */
3937
0
  printf("streamlocalbindmask 0%o\n",
3938
0
      o->fwd_opts.streamlocal_bind_mask);
3939
3940
  /* oLogFacility */
3941
0
  printf("syslogfacility %s\n", log_facility_name(o->log_facility));
3942
3943
  /* oProxyCommand / oProxyJump */
3944
0
  if (o->jump_host == NULL)
3945
0
    dump_cfg_string(oProxyCommand, o->proxy_command);
3946
0
  else {
3947
    /* Check for numeric addresses */
3948
0
    i = strchr(o->jump_host, ':') != NULL ||
3949
0
        strspn(o->jump_host, "1234567890.") == strlen(o->jump_host);
3950
0
    snprintf(buf, sizeof(buf), "%d", o->jump_port);
3951
0
    printf("proxyjump %s%s%s%s%s%s%s%s%s\n",
3952
        /* optional additional jump spec */
3953
0
        o->jump_extra == NULL ? "" : o->jump_extra,
3954
0
        o->jump_extra == NULL ? "" : ",",
3955
        /* optional user */
3956
0
        o->jump_user == NULL ? "" : o->jump_user,
3957
0
        o->jump_user == NULL ? "" : "@",
3958
        /* opening [ if hostname is numeric */
3959
0
        i ? "[" : "",
3960
        /* mandatory hostname */
3961
0
        o->jump_host,
3962
        /* closing ] if hostname is numeric */
3963
0
        i ? "]" : "",
3964
        /* optional port number */
3965
0
        o->jump_port <= 0 ? "" : ":",
3966
0
        o->jump_port <= 0 ? "" : buf);
3967
0
  }
3968
0
}