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
0
{
348
0
  struct ssh_digest_ctx *md;
349
0
  u_char conn_hash[SSH_DIGEST_MAX_LENGTH];
350
351
0
  if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL ||
352
0
      ssh_digest_update(md, thishost, strlen(thishost)) < 0 ||
353
0
      ssh_digest_update(md, host, strlen(host)) < 0 ||
354
0
      ssh_digest_update(md, portstr, strlen(portstr)) < 0 ||
355
0
      ssh_digest_update(md, user, strlen(user)) < 0 ||
356
0
      ssh_digest_update(md, jumphost, strlen(jumphost)) < 0 ||
357
0
      ssh_digest_final(md, conn_hash, sizeof(conn_hash)) < 0)
358
0
    fatal_f("mux digest failed");
359
0
  ssh_digest_free(md);
360
0
  return tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
361
0
}
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
442
{
371
442
  struct Forward *fwd;
372
442
  int i;
373
374
  /* Don't add duplicates */
375
97.9k
  for (i = 0; i < options->num_local_forwards; i++) {
376
97.4k
    if (forward_equals(newfwd, options->local_forwards + i))
377
0
      return;
378
97.4k
  }
379
442
  options->local_forwards = xreallocarray(options->local_forwards,
380
442
      options->num_local_forwards + 1,
381
442
      sizeof(*options->local_forwards));
382
442
  fwd = &options->local_forwards[options->num_local_forwards++];
383
384
442
  fwd->listen_host = newfwd->listen_host;
385
442
  fwd->listen_port = newfwd->listen_port;
386
442
  fwd->listen_path = newfwd->listen_path;
387
442
  fwd->connect_host = newfwd->connect_host;
388
442
  fwd->connect_port = newfwd->connect_port;
389
442
  fwd->connect_path = newfwd->connect_path;
390
442
}
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
165
{
400
165
  struct Forward *fwd;
401
165
  int i;
402
403
  /* Don't add duplicates */
404
13.6k
  for (i = 0; i < options->num_remote_forwards; i++) {
405
13.5k
    if (forward_equals(newfwd, options->remote_forwards + i))
406
0
      return;
407
13.5k
  }
408
165
  options->remote_forwards = xreallocarray(options->remote_forwards,
409
165
      options->num_remote_forwards + 1,
410
165
      sizeof(*options->remote_forwards));
411
165
  fwd = &options->remote_forwards[options->num_remote_forwards++];
412
413
165
  fwd->listen_host = newfwd->listen_host;
414
165
  fwd->listen_port = newfwd->listen_port;
415
165
  fwd->listen_path = newfwd->listen_path;
416
165
  fwd->connect_host = newfwd->connect_host;
417
165
  fwd->connect_port = newfwd->connect_port;
418
165
  fwd->connect_path = newfwd->connect_path;
419
165
  fwd->handle = newfwd->handle;
420
165
  fwd->allocated_port = 0;
421
165
}
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
0
{
511
0
  static int port;
512
0
  struct servent *sp;
513
514
0
  if (port == 0) {
515
0
    sp = getservbyname(SSH_SERVICE_NAME, "tcp");
516
0
    port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
517
0
  }
518
0
  return port;
519
0
}
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
0
{
648
0
  char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
649
0
  char uidstr[32], *conn_hash_hex, *keyalias, *jmphost, *ruser;
650
0
  char *host, *ret;
651
0
  int port;
652
653
0
  port = options->port <= 0 ? default_ssh_port() : options->port;
654
0
  ruser = options->user == NULL ? pw->pw_name : options->user;
655
0
  if (final_pass) {
656
0
    host = xstrdup(options->hostname);
657
0
  } 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
0
  } else {
662
0
    host = xstrdup(host_arg);
663
0
  }
664
0
  if (gethostname(thishost, sizeof(thishost)) == -1)
665
0
    fatal("gethostname: %s", strerror(errno));
666
0
  jmphost = option_clear_or_none(options->jump_host) ?
667
0
      "" : options->jump_host;
668
0
  strlcpy(shorthost, thishost, sizeof(shorthost));
669
0
  shorthost[strcspn(thishost, ".")] = '\0';
670
0
  snprintf(portstr, sizeof(portstr), "%d", port);
671
0
  snprintf(uidstr, sizeof(uidstr), "%llu",
672
0
      (unsigned long long)pw->pw_uid);
673
0
  conn_hash_hex = ssh_connection_hash(thishost, host,
674
0
      portstr, ruser, jmphost);
675
0
  keyalias = options->host_key_alias ?  options->host_key_alias : host;
676
677
0
  ret = (is_include_path ? percent_dollar_expand : percent_expand)(path,
678
0
      "C", conn_hash_hex,
679
0
      "L", shorthost,
680
0
      "d", pw->pw_dir,
681
0
      "h", host,
682
0
      "k", keyalias,
683
0
      "l", thishost,
684
0
      "n", original_host,
685
0
      "p", portstr,
686
0
      "r", ruser,
687
0
      "u", pw->pw_name,
688
0
      "i", uidstr,
689
0
      "j", jmphost,
690
0
      (char *)NULL);
691
0
  free(host);
692
0
  free(conn_hash_hex);
693
0
  return ret;
694
0
}
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
0
{
929
0
  u_int i, j, onum_send_env = options->num_send_env;
930
931
  /* Remove an environment variable */
932
0
  for (i = 0; i < options->num_send_env; ) {
933
0
    if (!match_pattern(options->send_env[i], arg + 1)) {
934
0
      i++;
935
0
      continue;
936
0
    }
937
0
    debug3("%s line %d: removing environment %s",
938
0
        filename, linenum, options->send_env[i]);
939
0
    free(options->send_env[i]);
940
0
    options->send_env[i] = NULL;
941
0
    for (j = i; j < options->num_send_env - 1; j++) {
942
0
      options->send_env[j] = options->send_env[j + 1];
943
0
      options->send_env[j + 1] = NULL;
944
0
    }
945
0
    options->num_send_env--;
946
    /* NB. don't increment i */
947
0
  }
948
0
  if (onum_send_env != options->num_send_env) {
949
0
    options->send_env = xrecallocarray(options->send_env,
950
0
        onum_send_env, options->num_send_env,
951
0
        sizeof(*options->send_env));
952
0
  }
953
0
}
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
0
{
962
0
  int i;
963
964
0
  for (i = 0; keywords[i].name; i++)
965
0
    if (strcmp(cp, keywords[i].name) == 0)
966
0
      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
0
{
978
0
  u_int i;
979
980
0
  if (cnames == NULL || n == 0)
981
0
    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
0
{
1115
0
  int i;
1116
1117
0
  if (!arg || *arg == '\0') {
1118
0
    error("%s line %d: missing argument.", filename, linenum);
1119
0
    return -1;
1120
0
  }
1121
0
  for (i = 0; multistate_ptr[i].key != NULL; i++) {
1122
0
    if (strcasecmp(arg, multistate_ptr[i].key) == 0)
1123
0
      return multistate_ptr[i].value;
1124
0
  }
1125
0
  return -1;
1126
0
}
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
0
#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
0
{
1148
0
  char *str, **charptr, *endofnumber, *keyword, *arg, *arg2, *p;
1149
0
  char **cpptr, ***cppptr, fwdarg[256];
1150
0
  u_int i, *uintptr, max_entries = 0;
1151
0
  int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0;
1152
0
  int remotefwd, dynamicfwd, ca_only = 0, found = 0;
1153
0
  LogLevel *log_level_ptr;
1154
0
  SyslogFacility *log_facility_ptr;
1155
0
  long long val64;
1156
0
  size_t len;
1157
0
  struct Forward fwd;
1158
0
  const struct multistate *multistate_ptr;
1159
0
  glob_t gl;
1160
0
  const char *errstr;
1161
0
  char **oav = NULL, **av;
1162
0
  int oac = 0, ac;
1163
0
  int ret = -1;
1164
0
  struct allowed_cname *cnames = NULL;
1165
0
  u_int ncnames = 0;
1166
0
  char **strs = NULL; /* string array arguments; freed implicitly */
1167
0
  u_int nstrs = 0;
1168
1169
0
  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
0
  if ((len = strlen(line)) == 0)
1176
0
    return 0;
1177
0
  for (len--; len > 0; len--) {
1178
0
    if (strchr(WHITESPACE "\f", line[len]) == NULL)
1179
0
      break;
1180
0
    line[len] = '\0';
1181
0
  }
1182
1183
0
  str = line;
1184
  /* Get the keyword. (Each line is supposed to begin with a keyword). */
1185
0
  if ((keyword = strdelim(&str)) == NULL)
1186
0
    return 0;
1187
  /* Ignore leading whitespace. */
1188
0
  if (*keyword == '\0')
1189
0
    keyword = strdelim(&str);
1190
0
  if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#')
1191
0
    return 0;
1192
  /* Match lowercase keyword */
1193
0
  lowercase(keyword);
1194
1195
  /* Prepare to parse remainder of line */
1196
0
  if (str != NULL)
1197
0
    str += strspn(str, WHITESPACE);
1198
0
  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
0
  opcode = parse_token(keyword, filename, linenum,
1204
0
      options->ignored_unknown);
1205
0
  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
0
  ac = oac;
1210
0
  av = oav;
1211
1212
0
  switch (opcode) {
1213
0
  case oBadOption:
1214
    /* don't panic, but count bad options */
1215
0
    goto out;
1216
0
  case oIgnore:
1217
0
    argv_consume(&ac);
1218
0
    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
0
parse_time:
1227
0
    arg = argv_next(&ac, &av);
1228
0
    if (!arg || *arg == '\0') {
1229
0
      error("%s line %d: missing time value.",
1230
0
          filename, linenum);
1231
0
      goto out;
1232
0
    }
1233
0
    if (strcmp(arg, "none") == 0)
1234
0
      value = -1;
1235
0
    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
0
    if (*activep && *intptr == -1)
1241
0
      *intptr = value;
1242
0
    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
0
  case oRekeyLimit:
1372
0
    arg = argv_next(&ac, &av);
1373
0
    if (!arg || *arg == '\0') {
1374
0
      error("%.200s line %d: Missing argument.", filename,
1375
0
          linenum);
1376
0
      goto out;
1377
0
    }
1378
0
    if (strcmp(arg, "default") == 0) {
1379
0
      val64 = 0;
1380
0
    } else {
1381
0
      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
0
      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
0
    }
1392
0
    if (*activep && options->rekey_limit == -1)
1393
0
      options->rekey_limit = val64;
1394
0
    if (ac != 0) { /* optional rekey interval present */
1395
0
      if (strcmp(av[0], "none") == 0) {
1396
0
        (void)argv_next(&ac, &av);  /* discard */
1397
0
        break;
1398
0
      }
1399
0
      intptr = &options->rekey_interval;
1400
0
      goto parse_time;
1401
0
    }
1402
0
    break;
1403
1404
0
  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
0
  case oUser:
1450
0
    charptr = &options->user;
1451
0
parse_string:
1452
0
    arg = argv_next(&ac, &av);
1453
0
    if (!arg || *arg == '\0') {
1454
0
      error("%.200s line %d: Missing argument.",
1455
0
          filename, linenum);
1456
0
      goto out;
1457
0
    }
1458
0
    if (*activep && *charptr == NULL)
1459
0
      *charptr = xstrdup(arg);
1460
0
    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
0
parse_char_array:
1467
0
    i = 0;
1468
0
    value = *uintptr == 0; /* was array empty when we started? */
1469
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
1470
0
      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
0
      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
0
      i++;
1485
0
      if (*activep && value) {
1486
0
        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
0
        cpptr[(*uintptr)++] = xstrdup(arg);
1493
0
      }
1494
0
    }
1495
0
    break;
1496
1497
0
  case oUserKnownHostsFile:
1498
0
    cpptr = (char **)&options->user_hostfiles;
1499
0
    uintptr = &options->num_user_hostfiles;
1500
0
    max_entries = SSH_MAX_HOSTS_FILES;
1501
0
    goto parse_char_array;
1502
1503
0
  case oHostname:
1504
0
    charptr = &options->hostname;
1505
0
    goto parse_string;
1506
1507
0
  case oTag:
1508
0
    charptr = &options->tag;
1509
0
    goto parse_string;
1510
1511
0
  case oHostKeyAlias:
1512
0
    charptr = &options->host_key_alias;
1513
0
    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
0
  case oSecurityKeyProvider:
1532
0
    charptr = &options->sk_provider;
1533
0
    goto parse_string;
1534
1535
0
  case oKnownHostsCommand:
1536
0
    charptr = &options->known_hosts_command;
1537
0
    goto parse_command;
1538
1539
0
  case oProxyCommand:
1540
0
    charptr = &options->proxy_command;
1541
0
parse_command:
1542
0
    if (str == NULL) {
1543
0
      error("%.200s line %d: Missing argument.",
1544
0
          filename, linenum);
1545
0
      goto out;
1546
0
    }
1547
0
    len = strspn(str, WHITESPACE "=");
1548
0
    if (*activep && *charptr == NULL)
1549
0
      *charptr = xstrdup(str + len);
1550
0
    argv_consume(&ac);
1551
0
    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
0
  case oPort:
1570
0
    arg = argv_next(&ac, &av);
1571
0
    if (!arg || *arg == '\0') {
1572
0
      error("%.200s line %d: Missing argument.",
1573
0
          filename, linenum);
1574
0
      goto out;
1575
0
    }
1576
0
    value = a2port(arg);
1577
0
    if (value <= 0) {
1578
0
      error("%.200s line %d: Bad port '%s'.",
1579
0
          filename, linenum, arg);
1580
0
      goto out;
1581
0
    }
1582
0
    if (*activep && options->port == -1)
1583
0
      options->port = value;
1584
0
    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
0
  case oKexAlgorithms:
1634
0
    arg = argv_next(&ac, &av);
1635
0
    if (!arg || *arg == '\0') {
1636
0
      error("%.200s line %d: Missing argument.",
1637
0
          filename, linenum);
1638
0
      goto out;
1639
0
    }
1640
0
    if (*arg != '-' &&
1641
0
        !kex_names_valid(*arg == '+' || *arg == '^' ?
1642
0
        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
0
    if (*activep && options->kex_algorithms == NULL)
1648
0
      options->kex_algorithms = xstrdup(arg);
1649
0
    break;
1650
1651
0
  case oHostKeyAlgorithms:
1652
0
    charptr = &options->hostkeyalgorithms;
1653
0
    ca_only = 0;
1654
0
parse_pubkey_algos:
1655
0
    arg = argv_next(&ac, &av);
1656
0
    if (!arg || *arg == '\0') {
1657
0
      error("%.200s line %d: Missing argument.",
1658
0
          filename, linenum);
1659
0
      goto out;
1660
0
    }
1661
0
    if (*arg != '-' &&
1662
0
        !sshkey_names_valid2(*arg == '+' || *arg == '^' ?
1663
0
        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
0
    if (*activep && *charptr == NULL)
1669
0
      *charptr = xstrdup(arg);
1670
0
    break;
1671
1672
0
  case oCASignatureAlgorithms:
1673
0
    charptr = &options->ca_sign_algorithms;
1674
0
    ca_only = 1;
1675
0
    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
0
  case oLogVerbose:
1704
0
    cppptr = &options->log_verbose;
1705
0
    uintptr = &options->num_log_verbose;
1706
0
    i = 0;
1707
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
1708
0
      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
0
      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
0
      i++;
1723
0
      if (*activep && *uintptr == 0) {
1724
0
        *cppptr = xrecallocarray(*cppptr, *uintptr,
1725
0
            *uintptr + 1, sizeof(**cppptr));
1726
0
        (*cppptr)[(*uintptr)++] = xstrdup(arg);
1727
0
      }
1728
0
    }
1729
0
    break;
1730
1731
0
  case oLocalForward:
1732
0
  case oRemoteForward:
1733
0
  case oDynamicForward:
1734
0
    arg = argv_next(&ac, &av);
1735
0
    if (!arg || *arg == '\0') {
1736
0
      error("%.200s line %d: Missing argument.",
1737
0
          filename, linenum);
1738
0
      goto out;
1739
0
    }
1740
1741
0
    remotefwd = (opcode == oRemoteForward);
1742
0
    dynamicfwd = (opcode == oDynamicForward);
1743
1744
0
    if (!dynamicfwd) {
1745
0
      arg2 = argv_next(&ac, &av);
1746
0
      if (arg2 == NULL || *arg2 == '\0') {
1747
0
        if (remotefwd)
1748
0
          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
0
      } else {
1755
        /* construct a string for parse_forward */
1756
0
        snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg,
1757
0
            arg2);
1758
0
      }
1759
0
    }
1760
0
    if (dynamicfwd)
1761
0
      strlcpy(fwdarg, arg, sizeof(fwdarg));
1762
1763
0
    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
0
    if (*activep) {
1770
0
      if (remotefwd) {
1771
0
        add_remote_forward(options, &fwd);
1772
0
      } else {
1773
0
        add_local_forward(options, &fwd);
1774
0
      }
1775
0
    }
1776
0
    break;
1777
1778
0
  case oPermitRemoteOpen:
1779
0
    uintptr = &options->num_permitted_remote_opens;
1780
0
    cppptr = &options->permitted_remote_opens;
1781
0
    found = *uintptr == 0;
1782
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
1783
0
      arg2 = xstrdup(arg);
1784
      /* Allow any/none only in first position */
1785
0
      if (strcasecmp(arg, "none") == 0 ||
1786
0
          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
0
      } else {
1795
0
        p = hpdelim(&arg);
1796
0
        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
0
        p = cleanhostname(p);
1802
        /*
1803
         * don't want to use permitopen_port to avoid
1804
         * dependency on channels.[ch] here.
1805
         */
1806
0
        if (arg == NULL || (strcmp(arg, "*") != 0 &&
1807
0
            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
0
      }
1813
0
      opt_array_append(filename, linenum,
1814
0
          lookup_opcode_name(opcode),
1815
0
          &strs, &nstrs, arg2);
1816
0
      free(arg2);
1817
0
    }
1818
0
    if (nstrs == 0)
1819
0
      fatal("%s line %d: missing %s specification",
1820
0
          filename, linenum, lookup_opcode_name(opcode));
1821
0
    if (found && *activep) {
1822
0
      *cppptr = strs;
1823
0
      *uintptr = nstrs;
1824
0
      strs = NULL; /* transferred */
1825
0
      nstrs = 0;
1826
0
    }
1827
0
    break;
1828
1829
0
  case oClearAllForwardings:
1830
0
    intptr = &options->clear_forwardings;
1831
0
    goto parse_flag;
1832
1833
0
  case oHost:
1834
0
    if (cmdline) {
1835
0
      error("Host directive not supported as a command-line "
1836
0
          "option");
1837
0
      goto out;
1838
0
    }
1839
0
    *activep = 0;
1840
0
    arg2 = NULL;
1841
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
1842
0
      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
0
      if ((flags & SSHCONF_NEVERMATCH) != 0) {
1848
0
        argv_consume(&ac);
1849
0
        break;
1850
0
      }
1851
0
      negated = *arg == '!';
1852
0
      if (negated)
1853
0
        arg++;
1854
0
      if (match_pattern(host, arg)) {
1855
0
        if (negated) {
1856
0
          debug("%.200s line %d: Skipping Host "
1857
0
              "block because of negated match "
1858
0
              "for %.100s", filename, linenum,
1859
0
              arg);
1860
0
          *activep = 0;
1861
0
          argv_consume(&ac);
1862
0
          break;
1863
0
        }
1864
0
        if (!*activep)
1865
0
          arg2 = arg; /* logged below */
1866
0
        *activep = 1;
1867
0
      }
1868
0
    }
1869
0
    if (*activep)
1870
0
      debug("%.200s line %d: Applying options for %.100s",
1871
0
          filename, linenum, arg2);
1872
0
    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
0
  case oSendEnv:
1937
    /* XXX appends to list; doesn't respect first-match-wins */
1938
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
1939
0
      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
0
      found = 1;
1945
0
      if (!*activep)
1946
0
        continue;
1947
0
      if (*arg == '-') {
1948
        /* Removing an env var */
1949
0
        rm_env(options, arg, filename, linenum);
1950
0
        continue;
1951
0
      }
1952
0
      opt_array_append(filename, linenum,
1953
0
          lookup_opcode_name(opcode),
1954
0
          &options->send_env, &options->num_send_env, arg);
1955
0
    }
1956
0
    if (!found) {
1957
0
      fatal("%s line %d: no %s specified",
1958
0
          filename, linenum, keyword);
1959
0
    }
1960
0
    break;
1961
1962
0
  case oSetEnv:
1963
0
    found = options->num_setenv == 0;
1964
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
1965
0
      if (strchr(arg, '=') == NULL) {
1966
0
        error("%s line %d: Invalid SetEnv.",
1967
0
            filename, linenum);
1968
0
        goto out;
1969
0
      }
1970
0
      if (lookup_setenv_in_list(arg, strs, nstrs) != NULL) {
1971
0
        debug2("%s line %d: ignoring duplicate env "
1972
0
            "name \"%.64s\"", filename, linenum, arg);
1973
0
        continue;
1974
0
      }
1975
0
      opt_array_append(filename, linenum,
1976
0
          lookup_opcode_name(opcode),
1977
0
          &strs, &nstrs, arg);
1978
0
    }
1979
0
    if (nstrs == 0) {
1980
0
      fatal("%s line %d: no %s specified",
1981
0
          filename, linenum, keyword);
1982
0
    }
1983
0
    if (found && *activep) {
1984
0
      options->setenv = strs;
1985
0
      options->num_setenv = nstrs;
1986
0
      strs = NULL; /* transferred */
1987
0
      nstrs = 0;
1988
0
    }
1989
0
    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
0
  case oInclude:
2073
0
    if (cmdline) {
2074
0
      error("Include directive not supported as a "
2075
0
          "command-line option");
2076
0
      goto out;
2077
0
    }
2078
0
    value = 0;
2079
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
2080
0
      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
0
      if ((p = expand_match_exec_or_include_path(arg,
2087
0
          options, pw, host, original_host,
2088
0
          flags & SSHCONF_FINAL, 1)) == NULL) {
2089
0
        error("%.200s line %d: Unable to expand user "
2090
0
            "config file '%.100s'",
2091
0
            filename, linenum, arg);
2092
0
        continue;
2093
0
      }
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
0
      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
0
      if (!path_absolute(p) && *p != '~') {
2107
0
        xasprintf(&arg2, "%s/%s",
2108
0
            (flags & SSHCONF_USERCONF) ?
2109
0
            "~/" _PATH_SSH_USER_DIR : SSHDIR, p);
2110
0
      } else {
2111
0
        arg2 = xstrdup(p);
2112
0
      }
2113
0
      free(p);
2114
0
      memset(&gl, 0, sizeof(gl));
2115
0
      r = glob(arg2, GLOB_TILDE, NULL, &gl);
2116
0
      if (r == GLOB_NOMATCH) {
2117
0
        debug("%.200s line %d: include %s matched no "
2118
0
            "files",filename, linenum, arg2);
2119
0
        free(arg2);
2120
0
        continue;
2121
0
      } 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
0
    if (value != 0)
2157
0
      ret = value;
2158
0
    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
0
  case oIgnoreUnknown:
2212
0
    charptr = &options->ignored_unknown;
2213
0
    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
0
  case oCanonicalizePermittedCNAMEs:
2252
0
    found = options->num_permitted_cnames == 0;
2253
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
2254
      /*
2255
       * Either 'none' (only in first position), '*' for
2256
       * everything or 'list:list'
2257
       */
2258
0
      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
0
      } else if (strcmp(arg, "*") == 0) {
2267
0
        arg2 = arg;
2268
0
      } else {
2269
0
        lowercase(arg);
2270
0
        if ((arg2 = strchr(arg, ':')) == NULL ||
2271
0
            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
0
        *arg2 = '\0';
2278
0
        arg2++;
2279
0
      }
2280
0
      cnames = xrecallocarray(cnames, ncnames, ncnames + 1,
2281
0
          sizeof(*cnames));
2282
0
      cnames[ncnames].source_list = xstrdup(arg);
2283
0
      cnames[ncnames].target_list = xstrdup(arg2);
2284
0
      ncnames++;
2285
0
    }
2286
0
    if (ncnames == 0) {
2287
0
      fatal("%s line %d: no %s specified",
2288
0
          filename, linenum, keyword);
2289
0
    }
2290
0
    if (found && *activep) {
2291
0
      options->permitted_cnames = cnames;
2292
0
      options->num_permitted_cnames = ncnames;
2293
0
      cnames = NULL; /* transferred */
2294
0
      ncnames = 0;
2295
0
    }
2296
    /* un-transferred cnames is cleaned up before exit */
2297
0
    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
0
  case oRevokedHostKeys:
2335
0
    uintptr = &options->num_revoked_host_keys;
2336
0
    cppptr = &options->revoked_host_keys;
2337
0
    found = *uintptr == 0;
2338
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
2339
0
      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
0
      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
0
      opt_array_append(filename, linenum, keyword,
2354
0
          &strs, &nstrs, arg);
2355
0
    }
2356
0
    if (nstrs == 0) {
2357
0
      fatal("%s line %d: no %s specified",
2358
0
          filename, linenum, keyword);
2359
0
    }
2360
0
    if (found && *activep) {
2361
0
      *cppptr = strs;
2362
0
      *uintptr = nstrs;
2363
0
      strs = NULL; /* transferred */
2364
0
      nstrs = 0;
2365
0
    }
2366
0
    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
0
  case oAddKeysToAgent:
2401
0
    arg = argv_next(&ac, &av);
2402
0
    arg2 = argv_next(&ac, &av);
2403
0
    value = parse_multistate_value(arg, filename, linenum,
2404
0
        multistate_yesnoaskconfirm);
2405
0
    value2 = 0; /* unlimited lifespan by default */
2406
0
    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
0
    } else if (value == -1 && arg2 == NULL) {
2414
0
      if ((value2 = convtime(arg)) == -1) {
2415
0
        error("%s line %d: unsupported option",
2416
0
            filename, linenum);
2417
0
        goto out;
2418
0
      }
2419
0
      value = 1; /* yes */
2420
0
    } 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
0
    if (*activep && options->add_keys_to_agent == -1) {
2426
0
      options->add_keys_to_agent = value;
2427
0
      options->add_keys_to_agent_lifespan = value2;
2428
0
    }
2429
0
    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
0
  case oChannelTimeout:
2514
0
    found = options->num_channel_timeouts == 0;
2515
0
    while ((arg = argv_next(&ac, &av)) != NULL) {
2516
      /* Allow "none" only in first position */
2517
0
      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
0
      } else if (parse_pattern_interval(arg,
2525
0
          NULL, NULL) != 0) {
2526
0
        fatal("%s line %d: invalid channel timeout %s",
2527
0
            filename, linenum, arg);
2528
0
      }
2529
0
      opt_array_append(filename, linenum, keyword,
2530
0
          &strs, &nstrs, arg);
2531
0
    }
2532
0
    if (nstrs == 0) {
2533
0
      fatal("%s line %d: no %s specified",
2534
0
          filename, linenum, keyword);
2535
0
    }
2536
0
    if (found && *activep) {
2537
0
      options->channel_timeouts = strs;
2538
0
      options->num_channel_timeouts = nstrs;
2539
0
      strs = NULL; /* transferred */
2540
0
      nstrs = 0;
2541
0
    }
2542
0
    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
0
  }
2596
2597
  /* Check that there is no garbage at end of line. */
2598
0
  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
0
  ret = 0;
2606
0
 out:
2607
0
  free_canon_cnames(cnames, ncnames);
2608
0
  opt_array_free2(strs, NULL, nstrs);
2609
0
  argv_free(oav, oac);
2610
0
  return ret;
2611
0
}
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
0
{
2623
0
  int active = 1;
2624
2625
0
  return read_config_file_depth(filename, pw, host, original_host,
2626
0
      remote_command, options, flags, &active, want_final_pass, 0);
2627
0
}
2628
2629
0
#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
0
{
2635
0
  FILE *f;
2636
0
  char *line = NULL;
2637
0
  size_t linesize = 0;
2638
0
  int linenum;
2639
0
  int bad_options = 0;
2640
2641
0
  if (depth < 0 || depth > READCONF_MAX_DEPTH)
2642
0
    fatal("Too many recursive configuration includes");
2643
2644
0
  if ((f = fopen(filename, "r")) == NULL)
2645
0
    return 0;
2646
2647
0
  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
0
  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
0
  linenum = 0;
2664
0
  while (getline(&line, &linesize, f) != -1) {
2665
    /* Update line number counter. */
2666
0
    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
0
    if (process_config_line_depth(options, pw, host, original_host,
2673
0
        remote_command, line, filename, linenum, activep, flags,
2674
0
        want_final_pass, depth) != 0)
2675
0
      bad_options++;
2676
0
  }
2677
0
  free(line);
2678
0
  fclose(f);
2679
0
  if (bad_options > 0)
2680
0
    fatal("%s: terminating, %d bad configuration options",
2681
0
        filename, bad_options);
2682
0
  return 1;
2683
0
}
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
0
{
2689
0
  return o == NULL || strcasecmp(o, "none") == 0;
2690
0
}
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
0
{
2716
0
  memset(options, 'X', sizeof(*options));
2717
0
  options->host_arg = NULL;
2718
0
  options->forward_agent = -1;
2719
0
  options->forward_agent_sock_path = NULL;
2720
0
  options->forward_x11 = -1;
2721
0
  options->forward_x11_trusted = -1;
2722
0
  options->forward_x11_timeout = -1;
2723
0
  options->stdio_forward_host = NULL;
2724
0
  options->stdio_forward_port = 0;
2725
0
  options->clear_forwardings = -1;
2726
0
  options->exit_on_forward_failure = -1;
2727
0
  options->xauth_location = NULL;
2728
0
  options->fwd_opts.gateway_ports = -1;
2729
0
  options->fwd_opts.streamlocal_bind_mask = (mode_t)-1;
2730
0
  options->fwd_opts.streamlocal_bind_unlink = -1;
2731
0
  options->pubkey_authentication = -1;
2732
0
  options->gss_authentication = -1;
2733
0
  options->gss_deleg_creds = -1;
2734
0
  options->password_authentication = -1;
2735
0
  options->kbd_interactive_authentication = -1;
2736
0
  options->kbd_interactive_devices = NULL;
2737
0
  options->hostbased_authentication = -1;
2738
0
  options->batch_mode = -1;
2739
0
  options->check_host_ip = -1;
2740
0
  options->strict_host_key_checking = -1;
2741
0
  options->compression = -1;
2742
0
  options->tcp_keep_alive = -1;
2743
0
  options->port = -1;
2744
0
  options->address_family = -1;
2745
0
  options->connection_attempts = -1;
2746
0
  options->connection_timeout = -1;
2747
0
  options->number_of_password_prompts = -1;
2748
0
  options->ciphers = NULL;
2749
0
  options->macs = NULL;
2750
0
  options->kex_algorithms = NULL;
2751
0
  options->hostkeyalgorithms = NULL;
2752
0
  options->ca_sign_algorithms = NULL;
2753
0
  options->num_identity_files = 0;
2754
0
  memset(options->identity_keys, 0, sizeof(options->identity_keys));
2755
0
  options->num_certificate_files = 0;
2756
0
  memset(options->certificates, 0, sizeof(options->certificates));
2757
0
  options->hostname = NULL;
2758
0
  options->host_key_alias = NULL;
2759
0
  options->proxy_command = NULL;
2760
0
  options->jump_user = NULL;
2761
0
  options->jump_host = NULL;
2762
0
  options->jump_port = -1;
2763
0
  options->jump_extra = NULL;
2764
0
  options->user = NULL;
2765
0
  options->escape_char = -1;
2766
0
  options->num_system_hostfiles = 0;
2767
0
  options->num_user_hostfiles = 0;
2768
0
  options->local_forwards = NULL;
2769
0
  options->num_local_forwards = 0;
2770
0
  options->remote_forwards = NULL;
2771
0
  options->num_remote_forwards = 0;
2772
0
  options->permitted_remote_opens = NULL;
2773
0
  options->num_permitted_remote_opens = 0;
2774
0
  options->log_facility = SYSLOG_FACILITY_NOT_SET;
2775
0
  options->log_level = SYSLOG_LEVEL_NOT_SET;
2776
0
  options->num_log_verbose = 0;
2777
0
  options->log_verbose = NULL;
2778
0
  options->preferred_authentications = NULL;
2779
0
  options->bind_address = NULL;
2780
0
  options->bind_interface = NULL;
2781
0
  options->pkcs11_provider = NULL;
2782
0
  options->sk_provider = NULL;
2783
0
  options->enable_ssh_keysign = -1;
2784
0
  options->no_host_authentication_for_localhost = -1;
2785
0
  options->identities_only = -1;
2786
0
  options->rekey_limit = -1;
2787
0
  options->rekey_interval = -1;
2788
0
  options->verify_host_key_dns = -1;
2789
0
  options->server_alive_interval = -1;
2790
0
  options->server_alive_count_max = -1;
2791
0
  options->send_env = NULL;
2792
0
  options->num_send_env = 0;
2793
0
  options->setenv = NULL;
2794
0
  options->num_setenv = 0;
2795
0
  options->control_path = NULL;
2796
0
  options->control_master = -1;
2797
0
  options->control_persist = -1;
2798
0
  options->control_persist_timeout = 0;
2799
0
  options->hash_known_hosts = -1;
2800
0
  options->tun_open = -1;
2801
0
  options->tun_local = -1;
2802
0
  options->tun_remote = -1;
2803
0
  options->local_command = NULL;
2804
0
  options->permit_local_command = -1;
2805
0
  options->remote_command = NULL;
2806
0
  options->add_keys_to_agent = -1;
2807
0
  options->add_keys_to_agent_lifespan = -1;
2808
0
  options->identity_agent = NULL;
2809
0
  options->visual_host_key = -1;
2810
0
  options->ip_qos_interactive = -1;
2811
0
  options->ip_qos_bulk = -1;
2812
0
  options->request_tty = -1;
2813
0
  options->session_type = -1;
2814
0
  options->stdin_null = -1;
2815
0
  options->fork_after_authentication = -1;
2816
0
  options->proxy_use_fdpass = -1;
2817
0
  options->ignored_unknown = NULL;
2818
0
  options->num_canonical_domains = 0;
2819
0
  options->num_permitted_cnames = 0;
2820
0
  options->canonicalize_max_dots = -1;
2821
0
  options->canonicalize_fallback_local = -1;
2822
0
  options->canonicalize_hostname = -1;
2823
0
  options->revoked_host_keys = NULL;
2824
0
  options->num_revoked_host_keys = 0;
2825
0
  options->fingerprint_hash = -1;
2826
0
  options->update_hostkeys = -1;
2827
0
  options->hostbased_accepted_algos = NULL;
2828
0
  options->pubkey_accepted_algos = NULL;
2829
0
  options->known_hosts_command = NULL;
2830
0
  options->required_rsa_size = -1;
2831
0
  options->warn_weak_crypto = -1;
2832
0
  options->enable_escape_commandline = -1;
2833
0
  options->obscure_keystroke_timing_interval = -1;
2834
0
  options->tag = NULL;
2835
0
  options->channel_timeouts = NULL;
2836
0
  options->num_channel_timeouts = 0;
2837
0
  options->version_addendum = NULL;
2838
0
}
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
0
{
3235
0
  char *ep, *cp = *p;
3236
0
  int ispath = 0;
3237
3238
0
  if (*cp == '\0') {
3239
0
    *p = NULL;
3240
0
    return -1;  /* end of string */
3241
0
  }
3242
3243
  /*
3244
   * A field escaped with square brackets is used literally.
3245
   * XXX - allow ']' to be escaped via backslash?
3246
   */
3247
0
  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
0
  for (cp = *p; *cp != '\0'; cp++) {
3267
0
    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
0
    case '/':
3274
0
      ispath = 1;
3275
0
      break;
3276
0
    case ':':
3277
0
      *cp++ = '\0';
3278
0
      goto done;
3279
0
    }
3280
0
  }
3281
0
done:
3282
0
  fwd->arg = *p;
3283
0
  fwd->ispath = ispath;
3284
0
  *p = cp;
3285
0
  return 0;
3286
0
}
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
0
{
3301
0
  struct fwdarg fwdargs[4];
3302
0
  char *p, *cp;
3303
0
  int i, err;
3304
3305
0
  memset(fwd, 0, sizeof(*fwd));
3306
0
  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
0
  cp = p = dollar_expand(&err, fwdspec);
3314
0
  if (p == NULL || err)
3315
0
    return 0;
3316
3317
  /* skip leading spaces */
3318
0
  while (isspace((u_char)*cp))
3319
0
    cp++;
3320
3321
0
  for (i = 0; i < 4; ++i) {
3322
0
    if (parse_fwd_field(&cp, &fwdargs[i]) != 0)
3323
0
      break;
3324
0
  }
3325
3326
  /* Check for trailing garbage */
3327
0
  if (cp != NULL && *cp != '\0') {
3328
0
    i = 0;  /* failure */
3329
0
  }
3330
3331
0
  switch (i) {
3332
0
  case 1:
3333
0
    if (fwdargs[0].ispath) {
3334
0
      fwd->listen_path = xstrdup(fwdargs[0].arg);
3335
0
      fwd->listen_port = PORT_STREAMLOCAL;
3336
0
    } else {
3337
0
      fwd->listen_host = NULL;
3338
0
      fwd->listen_port = a2port(fwdargs[0].arg);
3339
0
    }
3340
0
    fwd->connect_host = xstrdup("socks");
3341
0
    break;
3342
3343
0
  case 2:
3344
0
    if (fwdargs[0].ispath && fwdargs[1].ispath) {
3345
0
      fwd->listen_path = xstrdup(fwdargs[0].arg);
3346
0
      fwd->listen_port = PORT_STREAMLOCAL;
3347
0
      fwd->connect_path = xstrdup(fwdargs[1].arg);
3348
0
      fwd->connect_port = PORT_STREAMLOCAL;
3349
0
    } 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
0
    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
0
  }
3389
3390
0
  free(p);
3391
3392
0
  if (dynamicfwd) {
3393
0
    if (!(i == 1 || i == 2))
3394
0
      goto fail_free;
3395
0
  } else {
3396
0
    if (!(i == 3 || i == 4)) {
3397
0
      if (fwd->connect_path == NULL &&
3398
0
          fwd->listen_path == NULL)
3399
0
        goto fail_free;
3400
0
    }
3401
0
    if (fwd->connect_port <= 0 && fwd->connect_path == NULL)
3402
0
      goto fail_free;
3403
0
  }
3404
3405
0
  if ((fwd->listen_port < 0 && fwd->listen_path == NULL) ||
3406
0
      (!remotefwd && fwd->listen_port == 0))
3407
0
    goto fail_free;
3408
0
  if (fwd->connect_host != NULL &&
3409
0
      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
0
  if (fwd->connect_path != NULL &&
3416
0
      strlen(fwd->connect_path) >= PATH_MAX_SUN)
3417
0
    goto fail_free;
3418
0
  if (fwd->listen_host != NULL &&
3419
0
      strlen(fwd->listen_host) >= NI_MAXHOST)
3420
0
    goto fail_free;
3421
0
  if (fwd->listen_path != NULL &&
3422
0
      strlen(fwd->listen_path) >= PATH_MAX_SUN)
3423
0
    goto fail_free;
3424
3425
0
  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
0
}
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
0
{
3640
0
  u_int i;
3641
3642
0
  for (i = 0; keywords[i].name != NULL; i++)
3643
0
    if (keywords[i].opcode == code)
3644
0
      return(keywords[i].name);
3645
0
  return "UNKNOWN";
3646
0
}
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
}