Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F145034641
readconf.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
100 KB
Referenced Files
None
Subscribers
None
readconf.c
View Options
/* $OpenBSD: readconf.c,v 1.380 2023/07/17 06:16:33 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
* Functions for reading the configuration files.
*
* As far as I am concerned, the code I have written for this software
* can be used freely for any purpose. Any derived versions of this
* software must be clearly marked as such, and if the derived work is
* incompatible with the protocol description in the RFC file, it must be
* called by a name other than "ssh" or "Secure Shell".
*/
#include
"includes.h"
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<sys/socket.h>
#include
<sys/wait.h>
#include
<sys/un.h>
#include
<net/if.h>
#include
<netinet/in.h>
#include
<netinet/in_systm.h>
#include
<netinet/ip.h>
#include
<arpa/inet.h>
#include
<ctype.h>
#include
<errno.h>
#include
<fcntl.h>
#ifdef HAVE_IFADDRS_H
#
include
<ifaddrs.h>
#endif
#include
<limits.h>
#include
<netdb.h>
#ifdef HAVE_PATHS_H
#
include
<paths.h>
#endif
#include
<pwd.h>
#include
<signal.h>
#include
<stdio.h>
#include
<string.h>
#include
<stdarg.h>
#include
<unistd.h>
#ifdef USE_SYSTEM_GLOB
#
include
<glob.h>
#else
#
include
"openbsd-compat/glob.h"
#endif
#ifdef HAVE_UTIL_H
#include
<util.h>
#endif
#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
#
include
<vis.h>
#endif
#include
"xmalloc.h"
#include
"ssh.h"
#include
"ssherr.h"
#include
"cipher.h"
#include
"pathnames.h"
#include
"log.h"
#include
"sshkey.h"
#include
"misc.h"
#include
"readconf.h"
#include
"match.h"
#include
"kex.h"
#include
"mac.h"
#include
"uidswap.h"
#include
"myproposal.h"
#include
"digest.h"
/* Format of the configuration file:
# Configuration data is parsed as follows:
# 1. command line options
# 2. user-specific file
# 3. system-wide file
# Any configuration value is only changed the first time it is set.
# Thus, host-specific definitions should be at the beginning of the
# configuration file, and defaults at the end.
# Host-specific declarations. These may override anything above. A single
# host may match multiple declarations; these are processed in the order
# that they are given in.
Host *.ngs.fi ngs.fi
User foo
Host fake.com
Hostname another.host.name.real.org
User blaah
Port 34289
ForwardX11 no
ForwardAgent no
Host books.com
RemoteForward 9999 shadows.cs.hut.fi:9999
Ciphers 3des-cbc
Host fascist.blob.com
Port 23123
User tylonen
PasswordAuthentication no
Host puukko.hut.fi
User t35124p
ProxyCommand ssh-proxy %h %p
Host *.fr
PublicKeyAuthentication no
Host *.su
Ciphers aes128-ctr
PasswordAuthentication no
Host vpn.fake.com
Tunnel yes
TunnelDevice 3
# Defaults for various options
Host *
ForwardAgent no
ForwardX11 no
PasswordAuthentication yes
StrictHostKeyChecking yes
TcpKeepAlive no
IdentityFile ~/.ssh/identity
Port 22
EscapeChar ~
*/
static
int
read_config_file_depth
(
const
char
*
filename
,
struct
passwd
*
pw
,
const
char
*
host
,
const
char
*
original_host
,
Options
*
options
,
int
flags
,
int
*
activep
,
int
*
want_final_pass
,
int
depth
);
static
int
process_config_line_depth
(
Options
*
options
,
struct
passwd
*
pw
,
const
char
*
host
,
const
char
*
original_host
,
char
*
line
,
const
char
*
filename
,
int
linenum
,
int
*
activep
,
int
flags
,
int
*
want_final_pass
,
int
depth
);
/* Keyword tokens. */
typedef
enum
{
oBadOption
,
oHost
,
oMatch
,
oInclude
,
oTag
,
oForwardAgent
,
oForwardX11
,
oForwardX11Trusted
,
oForwardX11Timeout
,
oGatewayPorts
,
oExitOnForwardFailure
,
oPasswordAuthentication
,
oXAuthLocation
,
oIdentityFile
,
oHostname
,
oPort
,
oRemoteForward
,
oLocalForward
,
oPermitRemoteOpen
,
oCertificateFile
,
oAddKeysToAgent
,
oIdentityAgent
,
oUser
,
oEscapeChar
,
oProxyCommand
,
oGlobalKnownHostsFile
,
oUserKnownHostsFile
,
oConnectionAttempts
,
oBatchMode
,
oCheckHostIP
,
oStrictHostKeyChecking
,
oCompression
,
oTCPKeepAlive
,
oNumberOfPasswordPrompts
,
oLogFacility
,
oLogLevel
,
oLogVerbose
,
oCiphers
,
oMacs
,
oPubkeyAuthentication
,
oKbdInteractiveAuthentication
,
oKbdInteractiveDevices
,
oHostKeyAlias
,
oDynamicForward
,
oPreferredAuthentications
,
oHostbasedAuthentication
,
oHostKeyAlgorithms
,
oBindAddress
,
oBindInterface
,
oPKCS11Provider
,
oClearAllForwardings
,
oNoHostAuthenticationForLocalhost
,
oEnableSSHKeysign
,
oRekeyLimit
,
oVerifyHostKeyDNS
,
oConnectTimeout
,
oAddressFamily
,
oGssAuthentication
,
oGssDelegateCreds
,
oServerAliveInterval
,
oServerAliveCountMax
,
oIdentitiesOnly
,
oSendEnv
,
oSetEnv
,
oControlPath
,
oControlMaster
,
oControlPersist
,
oHashKnownHosts
,
oTunnel
,
oTunnelDevice
,
oLocalCommand
,
oPermitLocalCommand
,
oRemoteCommand
,
oVisualHostKey
,
oKexAlgorithms
,
oIPQoS
,
oRequestTTY
,
oSessionType
,
oStdinNull
,
oForkAfterAuthentication
,
oIgnoreUnknown
,
oProxyUseFdpass
,
oCanonicalDomains
,
oCanonicalizeHostname
,
oCanonicalizeMaxDots
,
oCanonicalizeFallbackLocal
,
oCanonicalizePermittedCNAMEs
,
oStreamLocalBindMask
,
oStreamLocalBindUnlink
,
oRevokedHostKeys
,
oFingerprintHash
,
oUpdateHostkeys
,
oHostbasedAcceptedAlgorithms
,
oPubkeyAcceptedAlgorithms
,
oCASignatureAlgorithms
,
oProxyJump
,
oSecurityKeyProvider
,
oKnownHostsCommand
,
oRequiredRSASize
,
oEnableEscapeCommandline
,
oIgnore
,
oIgnoredUnknownOption
,
oDeprecated
,
oUnsupported
}
OpCodes
;
/* Textual representations of the tokens. */
static
struct
{
const
char
*
name
;
OpCodes
opcode
;
}
keywords
[]
=
{
/* Deprecated options */
{
"protocol"
,
oIgnore
},
/* NB. silently ignored */
{
"cipher"
,
oDeprecated
},
{
"fallbacktorsh"
,
oDeprecated
},
{
"globalknownhostsfile2"
,
oDeprecated
},
{
"rhostsauthentication"
,
oDeprecated
},
{
"userknownhostsfile2"
,
oDeprecated
},
{
"useroaming"
,
oDeprecated
},
{
"usersh"
,
oDeprecated
},
{
"useprivilegedport"
,
oDeprecated
},
/* Unsupported options */
{
"afstokenpassing"
,
oUnsupported
},
{
"kerberosauthentication"
,
oUnsupported
},
{
"kerberostgtpassing"
,
oUnsupported
},
{
"rsaauthentication"
,
oUnsupported
},
{
"rhostsrsaauthentication"
,
oUnsupported
},
{
"compressionlevel"
,
oUnsupported
},
/* Sometimes-unsupported options */
#if defined(GSSAPI)
{
"gssapiauthentication"
,
oGssAuthentication
},
{
"gssapidelegatecredentials"
,
oGssDelegateCreds
},
# else
{
"gssapiauthentication"
,
oUnsupported
},
{
"gssapidelegatecredentials"
,
oUnsupported
},
#endif
#ifdef ENABLE_PKCS11
{
"pkcs11provider"
,
oPKCS11Provider
},
{
"smartcarddevice"
,
oPKCS11Provider
},
# else
{
"smartcarddevice"
,
oUnsupported
},
{
"pkcs11provider"
,
oUnsupported
},
#endif
{
"forwardagent"
,
oForwardAgent
},
{
"forwardx11"
,
oForwardX11
},
{
"forwardx11trusted"
,
oForwardX11Trusted
},
{
"forwardx11timeout"
,
oForwardX11Timeout
},
{
"exitonforwardfailure"
,
oExitOnForwardFailure
},
{
"xauthlocation"
,
oXAuthLocation
},
{
"gatewayports"
,
oGatewayPorts
},
{
"passwordauthentication"
,
oPasswordAuthentication
},
{
"kbdinteractiveauthentication"
,
oKbdInteractiveAuthentication
},
{
"kbdinteractivedevices"
,
oKbdInteractiveDevices
},
{
"challengeresponseauthentication"
,
oKbdInteractiveAuthentication
},
/* alias */
{
"skeyauthentication"
,
oKbdInteractiveAuthentication
},
/* alias */
{
"tisauthentication"
,
oKbdInteractiveAuthentication
},
/* alias */
{
"pubkeyauthentication"
,
oPubkeyAuthentication
},
{
"dsaauthentication"
,
oPubkeyAuthentication
},
/* alias */
{
"hostbasedauthentication"
,
oHostbasedAuthentication
},
{
"identityfile"
,
oIdentityFile
},
{
"identityfile2"
,
oIdentityFile
},
/* obsolete */
{
"identitiesonly"
,
oIdentitiesOnly
},
{
"certificatefile"
,
oCertificateFile
},
{
"addkeystoagent"
,
oAddKeysToAgent
},
{
"identityagent"
,
oIdentityAgent
},
{
"hostname"
,
oHostname
},
{
"hostkeyalias"
,
oHostKeyAlias
},
{
"proxycommand"
,
oProxyCommand
},
{
"port"
,
oPort
},
{
"ciphers"
,
oCiphers
},
{
"macs"
,
oMacs
},
{
"remoteforward"
,
oRemoteForward
},
{
"localforward"
,
oLocalForward
},
{
"permitremoteopen"
,
oPermitRemoteOpen
},
{
"user"
,
oUser
},
{
"host"
,
oHost
},
{
"match"
,
oMatch
},
{
"tag"
,
oTag
},
{
"escapechar"
,
oEscapeChar
},
{
"globalknownhostsfile"
,
oGlobalKnownHostsFile
},
{
"userknownhostsfile"
,
oUserKnownHostsFile
},
{
"connectionattempts"
,
oConnectionAttempts
},
{
"batchmode"
,
oBatchMode
},
{
"checkhostip"
,
oCheckHostIP
},
{
"stricthostkeychecking"
,
oStrictHostKeyChecking
},
{
"compression"
,
oCompression
},
{
"tcpkeepalive"
,
oTCPKeepAlive
},
{
"keepalive"
,
oTCPKeepAlive
},
/* obsolete */
{
"numberofpasswordprompts"
,
oNumberOfPasswordPrompts
},
{
"syslogfacility"
,
oLogFacility
},
{
"loglevel"
,
oLogLevel
},
{
"logverbose"
,
oLogVerbose
},
{
"dynamicforward"
,
oDynamicForward
},
{
"preferredauthentications"
,
oPreferredAuthentications
},
{
"hostkeyalgorithms"
,
oHostKeyAlgorithms
},
{
"casignaturealgorithms"
,
oCASignatureAlgorithms
},
{
"bindaddress"
,
oBindAddress
},
{
"bindinterface"
,
oBindInterface
},
{
"clearallforwardings"
,
oClearAllForwardings
},
{
"enablesshkeysign"
,
oEnableSSHKeysign
},
{
"verifyhostkeydns"
,
oVerifyHostKeyDNS
},
{
"nohostauthenticationforlocalhost"
,
oNoHostAuthenticationForLocalhost
},
{
"rekeylimit"
,
oRekeyLimit
},
{
"connecttimeout"
,
oConnectTimeout
},
{
"addressfamily"
,
oAddressFamily
},
{
"serveraliveinterval"
,
oServerAliveInterval
},
{
"serveralivecountmax"
,
oServerAliveCountMax
},
{
"sendenv"
,
oSendEnv
},
{
"setenv"
,
oSetEnv
},
{
"controlpath"
,
oControlPath
},
{
"controlmaster"
,
oControlMaster
},
{
"controlpersist"
,
oControlPersist
},
{
"hashknownhosts"
,
oHashKnownHosts
},
{
"include"
,
oInclude
},
{
"tunnel"
,
oTunnel
},
{
"tunneldevice"
,
oTunnelDevice
},
{
"localcommand"
,
oLocalCommand
},
{
"permitlocalcommand"
,
oPermitLocalCommand
},
{
"remotecommand"
,
oRemoteCommand
},
{
"visualhostkey"
,
oVisualHostKey
},
{
"kexalgorithms"
,
oKexAlgorithms
},
{
"ipqos"
,
oIPQoS
},
{
"requesttty"
,
oRequestTTY
},
{
"sessiontype"
,
oSessionType
},
{
"stdinnull"
,
oStdinNull
},
{
"forkafterauthentication"
,
oForkAfterAuthentication
},
{
"proxyusefdpass"
,
oProxyUseFdpass
},
{
"canonicaldomains"
,
oCanonicalDomains
},
{
"canonicalizefallbacklocal"
,
oCanonicalizeFallbackLocal
},
{
"canonicalizehostname"
,
oCanonicalizeHostname
},
{
"canonicalizemaxdots"
,
oCanonicalizeMaxDots
},
{
"canonicalizepermittedcnames"
,
oCanonicalizePermittedCNAMEs
},
{
"streamlocalbindmask"
,
oStreamLocalBindMask
},
{
"streamlocalbindunlink"
,
oStreamLocalBindUnlink
},
{
"revokedhostkeys"
,
oRevokedHostKeys
},
{
"fingerprinthash"
,
oFingerprintHash
},
{
"updatehostkeys"
,
oUpdateHostkeys
},
{
"hostbasedacceptedalgorithms"
,
oHostbasedAcceptedAlgorithms
},
{
"hostbasedkeytypes"
,
oHostbasedAcceptedAlgorithms
},
/* obsolete */
{
"pubkeyacceptedalgorithms"
,
oPubkeyAcceptedAlgorithms
},
{
"pubkeyacceptedkeytypes"
,
oPubkeyAcceptedAlgorithms
},
/* obsolete */
{
"ignoreunknown"
,
oIgnoreUnknown
},
{
"proxyjump"
,
oProxyJump
},
{
"securitykeyprovider"
,
oSecurityKeyProvider
},
{
"knownhostscommand"
,
oKnownHostsCommand
},
{
"requiredrsasize"
,
oRequiredRSASize
},
{
"enableescapecommandline"
,
oEnableEscapeCommandline
},
/* Client VersionAddendum - retired in bffe60ead024 */
{
"versionaddendum"
,
oDeprecated
},
{
NULL
,
oBadOption
}
};
static
const
char
*
lookup_opcode_name
(
OpCodes
code
);
const
char
*
kex_default_pk_alg
(
void
)
{
static
char
*
pkalgs
;
if
(
pkalgs
==
NULL
)
{
char
*
all_key
;
all_key
=
sshkey_alg_list
(
0
,
0
,
1
,
','
);
pkalgs
=
match_filter_allowlist
(
KEX_DEFAULT_PK_ALG
,
all_key
);
free
(
all_key
);
}
return
pkalgs
;
}
char
*
ssh_connection_hash
(
const
char
*
thishost
,
const
char
*
host
,
const
char
*
portstr
,
const
char
*
user
)
{
struct
ssh_digest_ctx
*
md
;
u_char
conn_hash
[
SSH_DIGEST_MAX_LENGTH
];
if
((
md
=
ssh_digest_start
(
SSH_DIGEST_SHA1
))
==
NULL
||
ssh_digest_update
(
md
,
thishost
,
strlen
(
thishost
))
<
0
||
ssh_digest_update
(
md
,
host
,
strlen
(
host
))
<
0
||
ssh_digest_update
(
md
,
portstr
,
strlen
(
portstr
))
<
0
||
ssh_digest_update
(
md
,
user
,
strlen
(
user
))
<
0
||
ssh_digest_final
(
md
,
conn_hash
,
sizeof
(
conn_hash
))
<
0
)
fatal_f
(
"mux digest failed"
);
ssh_digest_free
(
md
);
return
tohex
(
conn_hash
,
ssh_digest_bytes
(
SSH_DIGEST_SHA1
));
}
/*
* Adds a local TCP/IP port forward to options. Never returns if there is an
* error.
*/
void
add_local_forward
(
Options
*
options
,
const
struct
Forward
*
newfwd
)
{
struct
Forward
*
fwd
;
int
i
;
/* Don't add duplicates */
for
(
i
=
0
;
i
<
options
->
num_local_forwards
;
i
++
)
{
if
(
forward_equals
(
newfwd
,
options
->
local_forwards
+
i
))
return
;
}
options
->
local_forwards
=
xreallocarray
(
options
->
local_forwards
,
options
->
num_local_forwards
+
1
,
sizeof
(
*
options
->
local_forwards
));
fwd
=
&
options
->
local_forwards
[
options
->
num_local_forwards
++
];
fwd
->
listen_host
=
newfwd
->
listen_host
;
fwd
->
listen_port
=
newfwd
->
listen_port
;
fwd
->
listen_path
=
newfwd
->
listen_path
;
fwd
->
connect_host
=
newfwd
->
connect_host
;
fwd
->
connect_port
=
newfwd
->
connect_port
;
fwd
->
connect_path
=
newfwd
->
connect_path
;
}
/*
* Adds a remote TCP/IP port forward to options. Never returns if there is
* an error.
*/
void
add_remote_forward
(
Options
*
options
,
const
struct
Forward
*
newfwd
)
{
struct
Forward
*
fwd
;
int
i
;
/* Don't add duplicates */
for
(
i
=
0
;
i
<
options
->
num_remote_forwards
;
i
++
)
{
if
(
forward_equals
(
newfwd
,
options
->
remote_forwards
+
i
))
return
;
}
options
->
remote_forwards
=
xreallocarray
(
options
->
remote_forwards
,
options
->
num_remote_forwards
+
1
,
sizeof
(
*
options
->
remote_forwards
));
fwd
=
&
options
->
remote_forwards
[
options
->
num_remote_forwards
++
];
fwd
->
listen_host
=
newfwd
->
listen_host
;
fwd
->
listen_port
=
newfwd
->
listen_port
;
fwd
->
listen_path
=
newfwd
->
listen_path
;
fwd
->
connect_host
=
newfwd
->
connect_host
;
fwd
->
connect_port
=
newfwd
->
connect_port
;
fwd
->
connect_path
=
newfwd
->
connect_path
;
fwd
->
handle
=
newfwd
->
handle
;
fwd
->
allocated_port
=
0
;
}
static
void
clear_forwardings
(
Options
*
options
)
{
int
i
;
for
(
i
=
0
;
i
<
options
->
num_local_forwards
;
i
++
)
{
free
(
options
->
local_forwards
[
i
].
listen_host
);
free
(
options
->
local_forwards
[
i
].
listen_path
);
free
(
options
->
local_forwards
[
i
].
connect_host
);
free
(
options
->
local_forwards
[
i
].
connect_path
);
}
if
(
options
->
num_local_forwards
>
0
)
{
free
(
options
->
local_forwards
);
options
->
local_forwards
=
NULL
;
}
options
->
num_local_forwards
=
0
;
for
(
i
=
0
;
i
<
options
->
num_remote_forwards
;
i
++
)
{
free
(
options
->
remote_forwards
[
i
].
listen_host
);
free
(
options
->
remote_forwards
[
i
].
listen_path
);
free
(
options
->
remote_forwards
[
i
].
connect_host
);
free
(
options
->
remote_forwards
[
i
].
connect_path
);
}
if
(
options
->
num_remote_forwards
>
0
)
{
free
(
options
->
remote_forwards
);
options
->
remote_forwards
=
NULL
;
}
options
->
num_remote_forwards
=
0
;
options
->
tun_open
=
SSH_TUNMODE_NO
;
}
void
add_certificate_file
(
Options
*
options
,
const
char
*
path
,
int
userprovided
)
{
int
i
;
if
(
options
->
num_certificate_files
>=
SSH_MAX_CERTIFICATE_FILES
)
fatal
(
"Too many certificate files specified (max %d)"
,
SSH_MAX_CERTIFICATE_FILES
);
/* Avoid registering duplicates */
for
(
i
=
0
;
i
<
options
->
num_certificate_files
;
i
++
)
{
if
(
options
->
certificate_file_userprovided
[
i
]
==
userprovided
&&
strcmp
(
options
->
certificate_files
[
i
],
path
)
==
0
)
{
debug2_f
(
"ignoring duplicate key %s"
,
path
);
return
;
}
}
options
->
certificate_file_userprovided
[
options
->
num_certificate_files
]
=
userprovided
;
options
->
certificate_files
[
options
->
num_certificate_files
++
]
=
xstrdup
(
path
);
}
void
add_identity_file
(
Options
*
options
,
const
char
*
dir
,
const
char
*
filename
,
int
userprovided
)
{
char
*
path
;
int
i
;
if
(
options
->
num_identity_files
>=
SSH_MAX_IDENTITY_FILES
)
fatal
(
"Too many identity files specified (max %d)"
,
SSH_MAX_IDENTITY_FILES
);
if
(
dir
==
NULL
)
/* no dir, filename is absolute */
path
=
xstrdup
(
filename
);
else
if
(
xasprintf
(
&
path
,
"%s%s"
,
dir
,
filename
)
>=
PATH_MAX
)
fatal
(
"Identity file path %s too long"
,
path
);
/* Avoid registering duplicates */
for
(
i
=
0
;
i
<
options
->
num_identity_files
;
i
++
)
{
if
(
options
->
identity_file_userprovided
[
i
]
==
userprovided
&&
strcmp
(
options
->
identity_files
[
i
],
path
)
==
0
)
{
debug2_f
(
"ignoring duplicate key %s"
,
path
);
free
(
path
);
return
;
}
}
options
->
identity_file_userprovided
[
options
->
num_identity_files
]
=
userprovided
;
options
->
identity_files
[
options
->
num_identity_files
++
]
=
path
;
}
int
default_ssh_port
(
void
)
{
static
int
port
;
struct
servent
*
sp
;
if
(
port
==
0
)
{
sp
=
getservbyname
(
SSH_SERVICE_NAME
,
"tcp"
);
port
=
sp
?
ntohs
(
sp
->
s_port
)
:
SSH_DEFAULT_PORT
;
}
return
port
;
}
/*
* Execute a command in a shell.
* Return its exit status or -1 on abnormal exit.
*/
static
int
execute_in_shell
(
const
char
*
cmd
)
{
char
*
shell
;
pid_t
pid
;
int
status
;
if
((
shell
=
getenv
(
"SHELL"
))
==
NULL
)
shell
=
_PATH_BSHELL
;
if
(
access
(
shell
,
X_OK
)
==
-1
)
{
fatal
(
"Shell
\"
%s
\"
is not executable: %s"
,
shell
,
strerror
(
errno
));
}
debug
(
"Executing command: '%.500s'"
,
cmd
);
/* Fork and execute the command. */
if
((
pid
=
fork
())
==
0
)
{
char
*
argv
[
4
];
if
(
stdfd_devnull
(
1
,
1
,
0
)
==
-1
)
fatal_f
(
"stdfd_devnull failed"
);
closefrom
(
STDERR_FILENO
+
1
);
argv
[
0
]
=
shell
;
argv
[
1
]
=
"-c"
;
argv
[
2
]
=
xstrdup
(
cmd
);
argv
[
3
]
=
NULL
;
execv
(
argv
[
0
],
argv
);
error
(
"Unable to execute '%.100s': %s"
,
cmd
,
strerror
(
errno
));
/* Die with signal to make this error apparent to parent. */
ssh_signal
(
SIGTERM
,
SIG_DFL
);
kill
(
getpid
(),
SIGTERM
);
_exit
(
1
);
}
/* Parent. */
if
(
pid
==
-1
)
fatal_f
(
"fork: %.100s"
,
strerror
(
errno
));
while
(
waitpid
(
pid
,
&
status
,
0
)
==
-1
)
{
if
(
errno
!=
EINTR
&&
errno
!=
EAGAIN
)
fatal_f
(
"waitpid: %s"
,
strerror
(
errno
));
}
if
(
!
WIFEXITED
(
status
))
{
error
(
"command '%.100s' exited abnormally"
,
cmd
);
return
-1
;
}
debug3
(
"command returned status %d"
,
WEXITSTATUS
(
status
));
return
WEXITSTATUS
(
status
);
}
/*
* Check whether a local network interface address appears in CIDR pattern-
* list 'addrlist'. Returns 1 if matched or 0 otherwise.
*/
static
int
check_match_ifaddrs
(
const
char
*
addrlist
)
{
#ifdef HAVE_IFADDRS_H
struct
ifaddrs
*
ifa
,
*
ifaddrs
=
NULL
;
int
r
,
found
=
0
;
char
addr
[
NI_MAXHOST
];
socklen_t
salen
;
if
(
getifaddrs
(
&
ifaddrs
)
!=
0
)
{
error
(
"match localnetwork: getifaddrs failed: %s"
,
strerror
(
errno
));
return
0
;
}
for
(
ifa
=
ifaddrs
;
ifa
!=
NULL
;
ifa
=
ifa
->
ifa_next
)
{
if
(
ifa
->
ifa_addr
==
NULL
||
ifa
->
ifa_name
==
NULL
||
(
ifa
->
ifa_flags
&
IFF_UP
)
==
0
)
continue
;
switch
(
ifa
->
ifa_addr
->
sa_family
)
{
case
AF_INET
:
salen
=
sizeof
(
struct
sockaddr_in
);
break
;
case
AF_INET6
:
salen
=
sizeof
(
struct
sockaddr_in6
);
break
;
#ifdef AF_LINK
case
AF_LINK
:
/* ignore */
continue
;
#endif
/* AF_LINK */
default
:
debug2_f
(
"interface %s: unsupported address family %d"
,
ifa
->
ifa_name
,
ifa
->
ifa_addr
->
sa_family
);
continue
;
}
if
((
r
=
getnameinfo
(
ifa
->
ifa_addr
,
salen
,
addr
,
sizeof
(
addr
),
NULL
,
0
,
NI_NUMERICHOST
))
!=
0
)
{
debug2_f
(
"interface %s getnameinfo failed: %s"
,
ifa
->
ifa_name
,
gai_strerror
(
r
));
continue
;
}
debug3_f
(
"interface %s addr %s"
,
ifa
->
ifa_name
,
addr
);
if
(
addr_match_cidr_list
(
addr
,
addrlist
)
==
1
)
{
debug3_f
(
"matched interface %s: address %s in %s"
,
ifa
->
ifa_name
,
addr
,
addrlist
);
found
=
1
;
break
;
}
}
freeifaddrs
(
ifaddrs
);
return
found
;
#else
/* HAVE_IFADDRS_H */
error
(
"match localnetwork: not supported on this platform"
);
return
0
;
#endif
/* HAVE_IFADDRS_H */
}
/*
* Parse and execute a Match directive.
*/
static
int
match_cfg_line
(
Options
*
options
,
char
**
condition
,
struct
passwd
*
pw
,
const
char
*
host_arg
,
const
char
*
original_host
,
int
final_pass
,
int
*
want_final_pass
,
const
char
*
filename
,
int
linenum
)
{
char
*
arg
,
*
oattrib
,
*
attrib
,
*
cmd
,
*
cp
=
*
condition
,
*
host
,
*
criteria
;
const
char
*
ruser
;
int
r
,
port
,
this_result
,
result
=
1
,
attributes
=
0
,
negate
;
char
thishost
[
NI_MAXHOST
],
shorthost
[
NI_MAXHOST
],
portstr
[
NI_MAXSERV
];
char
uidstr
[
32
];
/*
* Configuration is likely to be incomplete at this point so we
* must be prepared to use default values.
*/
port
=
options
->
port
<=
0
?
default_ssh_port
()
:
options
->
port
;
ruser
=
options
->
user
==
NULL
?
pw
->
pw_name
:
options
->
user
;
if
(
final_pass
)
{
host
=
xstrdup
(
options
->
hostname
);
}
else
if
(
options
->
hostname
!=
NULL
)
{
/* NB. Please keep in sync with ssh.c:main() */
host
=
percent_expand
(
options
->
hostname
,
"h"
,
host_arg
,
(
char
*
)
NULL
);
}
else
{
host
=
xstrdup
(
host_arg
);
}
debug2
(
"checking match for '%s' host %s originally %s"
,
cp
,
host
,
original_host
);
while
((
oattrib
=
attrib
=
strdelim
(
&
cp
))
&&
*
attrib
!=
'\0'
)
{
/* Terminate on comment */
if
(
*
attrib
==
'#'
)
{
cp
=
NULL
;
/* mark all arguments consumed */
break
;
}
arg
=
criteria
=
NULL
;
this_result
=
1
;
if
((
negate
=
(
attrib
[
0
]
==
'!'
)))
attrib
++
;
/* Criterion "all" has no argument and must appear alone */
if
(
strcasecmp
(
attrib
,
"all"
)
==
0
)
{
if
(
attributes
>
1
||
((
arg
=
strdelim
(
&
cp
))
!=
NULL
&&
*
arg
!=
'\0'
&&
*
arg
!=
'#'
))
{
error
(
"%.200s line %d: '%s' cannot be combined "
"with other Match attributes"
,
filename
,
linenum
,
oattrib
);
result
=
-1
;
goto
out
;
}
if
(
arg
!=
NULL
&&
*
arg
==
'#'
)
cp
=
NULL
;
/* mark all arguments consumed */
if
(
result
)
result
=
negate
?
0
:
1
;
goto
out
;
}
attributes
++
;
/* criteria "final" and "canonical" have no argument */
if
(
strcasecmp
(
attrib
,
"canonical"
)
==
0
||
strcasecmp
(
attrib
,
"final"
)
==
0
)
{
/*
* If the config requests "Match final" then remember
* this so we can perform a second pass later.
*/
if
(
strcasecmp
(
attrib
,
"final"
)
==
0
&&
want_final_pass
!=
NULL
)
*
want_final_pass
=
1
;
r
=
!!
final_pass
;
/* force bitmask member to boolean */
if
(
r
==
(
negate
?
1
:
0
))
this_result
=
result
=
0
;
debug3
(
"%.200s line %d: %smatched '%s'"
,
filename
,
linenum
,
this_result
?
""
:
"not "
,
oattrib
);
continue
;
}
/* All other criteria require an argument */
if
((
arg
=
strdelim
(
&
cp
))
==
NULL
||
*
arg
==
'\0'
||
*
arg
==
'#'
)
{
error
(
"Missing Match criteria for %s"
,
attrib
);
result
=
-1
;
goto
out
;
}
if
(
strcasecmp
(
attrib
,
"host"
)
==
0
)
{
criteria
=
xstrdup
(
host
);
r
=
match_hostname
(
host
,
arg
)
==
1
;
if
(
r
==
(
negate
?
1
:
0
))
this_result
=
result
=
0
;
}
else
if
(
strcasecmp
(
attrib
,
"originalhost"
)
==
0
)
{
criteria
=
xstrdup
(
original_host
);
r
=
match_hostname
(
original_host
,
arg
)
==
1
;
if
(
r
==
(
negate
?
1
:
0
))
this_result
=
result
=
0
;
}
else
if
(
strcasecmp
(
attrib
,
"user"
)
==
0
)
{
criteria
=
xstrdup
(
ruser
);
r
=
match_pattern_list
(
ruser
,
arg
,
0
)
==
1
;
if
(
r
==
(
negate
?
1
:
0
))
this_result
=
result
=
0
;
}
else
if
(
strcasecmp
(
attrib
,
"localuser"
)
==
0
)
{
criteria
=
xstrdup
(
pw
->
pw_name
);
r
=
match_pattern_list
(
pw
->
pw_name
,
arg
,
0
)
==
1
;
if
(
r
==
(
negate
?
1
:
0
))
this_result
=
result
=
0
;
}
else
if
(
strcasecmp
(
attrib
,
"localnetwork"
)
==
0
)
{
if
(
addr_match_cidr_list
(
NULL
,
arg
)
==
-1
)
{
/* Error already printed */
result
=
-1
;
goto
out
;
}
r
=
check_match_ifaddrs
(
arg
)
==
1
;
if
(
r
==
(
negate
?
1
:
0
))
this_result
=
result
=
0
;
}
else
if
(
strcasecmp
(
attrib
,
"tagged"
)
==
0
)
{
criteria
=
xstrdup
(
options
->
tag
==
NULL
?
""
:
options
->
tag
);
r
=
match_pattern_list
(
criteria
,
arg
,
0
)
==
1
;
if
(
r
==
(
negate
?
1
:
0
))
this_result
=
result
=
0
;
}
else
if
(
strcasecmp
(
attrib
,
"exec"
)
==
0
)
{
char
*
conn_hash_hex
,
*
keyalias
;
if
(
gethostname
(
thishost
,
sizeof
(
thishost
))
==
-1
)
fatal
(
"gethostname: %s"
,
strerror
(
errno
));
strlcpy
(
shorthost
,
thishost
,
sizeof
(
shorthost
));
shorthost
[
strcspn
(
thishost
,
"."
)]
=
'\0'
;
snprintf
(
portstr
,
sizeof
(
portstr
),
"%d"
,
port
);
snprintf
(
uidstr
,
sizeof
(
uidstr
),
"%llu"
,
(
unsigned
long
long
)
pw
->
pw_uid
);
conn_hash_hex
=
ssh_connection_hash
(
thishost
,
host
,
portstr
,
ruser
);
keyalias
=
options
->
host_key_alias
?
options
->
host_key_alias
:
host
;
cmd
=
percent_expand
(
arg
,
"C"
,
conn_hash_hex
,
"L"
,
shorthost
,
"d"
,
pw
->
pw_dir
,
"h"
,
host
,
"k"
,
keyalias
,
"l"
,
thishost
,
"n"
,
original_host
,
"p"
,
portstr
,
"r"
,
ruser
,
"u"
,
pw
->
pw_name
,
"i"
,
uidstr
,
(
char
*
)
NULL
);
free
(
conn_hash_hex
);
if
(
result
!=
1
)
{
/* skip execution if prior predicate failed */
debug3
(
"%.200s line %d: skipped exec "
"
\"
%.100s
\"
"
,
filename
,
linenum
,
cmd
);
free
(
cmd
);
continue
;
}
r
=
execute_in_shell
(
cmd
);
if
(
r
==
-1
)
{
fatal
(
"%.200s line %d: match exec "
"'%.100s' error"
,
filename
,
linenum
,
cmd
);
}
criteria
=
xstrdup
(
cmd
);
free
(
cmd
);
/* Force exit status to boolean */
r
=
r
==
0
;
if
(
r
==
(
negate
?
1
:
0
))
this_result
=
result
=
0
;
}
else
{
error
(
"Unsupported Match attribute %s"
,
attrib
);
result
=
-1
;
goto
out
;
}
debug3
(
"%.200s line %d: %smatched '%s%s%.100s%s' "
,
filename
,
linenum
,
this_result
?
""
:
"not "
,
oattrib
,
criteria
==
NULL
?
""
:
"
\"
"
,
criteria
==
NULL
?
""
:
criteria
,
criteria
==
NULL
?
""
:
"
\"
"
);
free
(
criteria
);
}
if
(
attributes
==
0
)
{
error
(
"One or more attributes required for Match"
);
result
=
-1
;
goto
out
;
}
out
:
if
(
result
!=
-1
)
debug2
(
"match %sfound"
,
result
?
""
:
"not "
);
*
condition
=
cp
;
free
(
host
);
return
result
;
}
/* Remove environment variable by pattern */
static
void
rm_env
(
Options
*
options
,
const
char
*
arg
,
const
char
*
filename
,
int
linenum
)
{
u_int
i
,
j
,
onum_send_env
=
options
->
num_send_env
;
/* Remove an environment variable */
for
(
i
=
0
;
i
<
options
->
num_send_env
;
)
{
if
(
!
match_pattern
(
options
->
send_env
[
i
],
arg
+
1
))
{
i
++
;
continue
;
}
debug3
(
"%s line %d: removing environment %s"
,
filename
,
linenum
,
options
->
send_env
[
i
]);
free
(
options
->
send_env
[
i
]);
options
->
send_env
[
i
]
=
NULL
;
for
(
j
=
i
;
j
<
options
->
num_send_env
-
1
;
j
++
)
{
options
->
send_env
[
j
]
=
options
->
send_env
[
j
+
1
];
options
->
send_env
[
j
+
1
]
=
NULL
;
}
options
->
num_send_env
--
;
/* NB. don't increment i */
}
if
(
onum_send_env
!=
options
->
num_send_env
)
{
options
->
send_env
=
xrecallocarray
(
options
->
send_env
,
onum_send_env
,
options
->
num_send_env
,
sizeof
(
*
options
->
send_env
));
}
}
/*
* Returns the number of the token pointed to by cp or oBadOption.
*/
static
OpCodes
parse_token
(
const
char
*
cp
,
const
char
*
filename
,
int
linenum
,
const
char
*
ignored_unknown
)
{
int
i
;
for
(
i
=
0
;
keywords
[
i
].
name
;
i
++
)
if
(
strcmp
(
cp
,
keywords
[
i
].
name
)
==
0
)
return
keywords
[
i
].
opcode
;
if
(
ignored_unknown
!=
NULL
&&
match_pattern_list
(
cp
,
ignored_unknown
,
1
)
==
1
)
return
oIgnoredUnknownOption
;
error
(
"%s: line %d: Bad configuration option: %s"
,
filename
,
linenum
,
cp
);
return
oBadOption
;
}
/* Multistate option parsing */
struct
multistate
{
char
*
key
;
int
value
;
};
static
const
struct
multistate
multistate_flag
[]
=
{
{
"true"
,
1
},
{
"false"
,
0
},
{
"yes"
,
1
},
{
"no"
,
0
},
{
NULL
,
-1
}
};
static
const
struct
multistate
multistate_yesnoask
[]
=
{
{
"true"
,
1
},
{
"false"
,
0
},
{
"yes"
,
1
},
{
"no"
,
0
},
{
"ask"
,
2
},
{
NULL
,
-1
}
};
static
const
struct
multistate
multistate_strict_hostkey
[]
=
{
{
"true"
,
SSH_STRICT_HOSTKEY_YES
},
{
"false"
,
SSH_STRICT_HOSTKEY_OFF
},
{
"yes"
,
SSH_STRICT_HOSTKEY_YES
},
{
"no"
,
SSH_STRICT_HOSTKEY_OFF
},
{
"ask"
,
SSH_STRICT_HOSTKEY_ASK
},
{
"off"
,
SSH_STRICT_HOSTKEY_OFF
},
{
"accept-new"
,
SSH_STRICT_HOSTKEY_NEW
},
{
NULL
,
-1
}
};
static
const
struct
multistate
multistate_yesnoaskconfirm
[]
=
{
{
"true"
,
1
},
{
"false"
,
0
},
{
"yes"
,
1
},
{
"no"
,
0
},
{
"ask"
,
2
},
{
"confirm"
,
3
},
{
NULL
,
-1
}
};
static
const
struct
multistate
multistate_addressfamily
[]
=
{
{
"inet"
,
AF_INET
},
{
"inet6"
,
AF_INET6
},
{
"any"
,
AF_UNSPEC
},
{
NULL
,
-1
}
};
static
const
struct
multistate
multistate_controlmaster
[]
=
{
{
"true"
,
SSHCTL_MASTER_YES
},
{
"yes"
,
SSHCTL_MASTER_YES
},
{
"false"
,
SSHCTL_MASTER_NO
},
{
"no"
,
SSHCTL_MASTER_NO
},
{
"auto"
,
SSHCTL_MASTER_AUTO
},
{
"ask"
,
SSHCTL_MASTER_ASK
},
{
"autoask"
,
SSHCTL_MASTER_AUTO_ASK
},
{
NULL
,
-1
}
};
static
const
struct
multistate
multistate_tunnel
[]
=
{
{
"ethernet"
,
SSH_TUNMODE_ETHERNET
},
{
"point-to-point"
,
SSH_TUNMODE_POINTOPOINT
},
{
"true"
,
SSH_TUNMODE_DEFAULT
},
{
"yes"
,
SSH_TUNMODE_DEFAULT
},
{
"false"
,
SSH_TUNMODE_NO
},
{
"no"
,
SSH_TUNMODE_NO
},
{
NULL
,
-1
}
};
static
const
struct
multistate
multistate_requesttty
[]
=
{
{
"true"
,
REQUEST_TTY_YES
},
{
"yes"
,
REQUEST_TTY_YES
},
{
"false"
,
REQUEST_TTY_NO
},
{
"no"
,
REQUEST_TTY_NO
},
{
"force"
,
REQUEST_TTY_FORCE
},
{
"auto"
,
REQUEST_TTY_AUTO
},
{
NULL
,
-1
}
};
static
const
struct
multistate
multistate_sessiontype
[]
=
{
{
"none"
,
SESSION_TYPE_NONE
},
{
"subsystem"
,
SESSION_TYPE_SUBSYSTEM
},
{
"default"
,
SESSION_TYPE_DEFAULT
},
{
NULL
,
-1
}
};
static
const
struct
multistate
multistate_canonicalizehostname
[]
=
{
{
"true"
,
SSH_CANONICALISE_YES
},
{
"false"
,
SSH_CANONICALISE_NO
},
{
"yes"
,
SSH_CANONICALISE_YES
},
{
"no"
,
SSH_CANONICALISE_NO
},
{
"always"
,
SSH_CANONICALISE_ALWAYS
},
{
NULL
,
-1
}
};
static
const
struct
multistate
multistate_pubkey_auth
[]
=
{
{
"true"
,
SSH_PUBKEY_AUTH_ALL
},
{
"false"
,
SSH_PUBKEY_AUTH_NO
},
{
"yes"
,
SSH_PUBKEY_AUTH_ALL
},
{
"no"
,
SSH_PUBKEY_AUTH_NO
},
{
"unbound"
,
SSH_PUBKEY_AUTH_UNBOUND
},
{
"host-bound"
,
SSH_PUBKEY_AUTH_HBOUND
},
{
NULL
,
-1
}
};
static
const
struct
multistate
multistate_compression
[]
=
{
#ifdef WITH_ZLIB
{
"yes"
,
COMP_ZLIB
},
#endif
{
"no"
,
COMP_NONE
},
{
NULL
,
-1
}
};
static
int
parse_multistate_value
(
const
char
*
arg
,
const
char
*
filename
,
int
linenum
,
const
struct
multistate
*
multistate_ptr
)
{
int
i
;
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%s line %d: missing argument."
,
filename
,
linenum
);
return
-1
;
}
for
(
i
=
0
;
multistate_ptr
[
i
].
key
!=
NULL
;
i
++
)
{
if
(
strcasecmp
(
arg
,
multistate_ptr
[
i
].
key
)
==
0
)
return
multistate_ptr
[
i
].
value
;
}
return
-1
;
}
/*
* Processes a single option line as used in the configuration files. This
* only sets those values that have not already been set.
*/
int
process_config_line
(
Options
*
options
,
struct
passwd
*
pw
,
const
char
*
host
,
const
char
*
original_host
,
char
*
line
,
const
char
*
filename
,
int
linenum
,
int
*
activep
,
int
flags
)
{
return
process_config_line_depth
(
options
,
pw
,
host
,
original_host
,
line
,
filename
,
linenum
,
activep
,
flags
,
NULL
,
0
);
}
#define WHITESPACE " \t\r\n"
static
int
process_config_line_depth
(
Options
*
options
,
struct
passwd
*
pw
,
const
char
*
host
,
const
char
*
original_host
,
char
*
line
,
const
char
*
filename
,
int
linenum
,
int
*
activep
,
int
flags
,
int
*
want_final_pass
,
int
depth
)
{
char
*
str
,
**
charptr
,
*
endofnumber
,
*
keyword
,
*
arg
,
*
arg2
,
*
p
;
char
**
cpptr
,
***
cppptr
,
fwdarg
[
256
];
u_int
i
,
*
uintptr
,
uvalue
,
max_entries
=
0
;
int
r
,
oactive
,
negated
,
opcode
,
*
intptr
,
value
,
value2
,
cmdline
=
0
;
int
remotefwd
,
dynamicfwd
,
ca_only
=
0
;
LogLevel
*
log_level_ptr
;
SyslogFacility
*
log_facility_ptr
;
long
long
val64
;
size_t
len
;
struct
Forward
fwd
;
const
struct
multistate
*
multistate_ptr
;
struct
allowed_cname
*
cname
;
glob_t
gl
;
const
char
*
errstr
;
char
**
oav
=
NULL
,
**
av
;
int
oac
=
0
,
ac
;
int
ret
=
-1
;
if
(
activep
==
NULL
)
{
/* We are processing a command line directive */
cmdline
=
1
;
activep
=
&
cmdline
;
}
/* Strip trailing whitespace. Allow \f (form feed) at EOL only */
if
((
len
=
strlen
(
line
))
==
0
)
return
0
;
for
(
len
--
;
len
>
0
;
len
--
)
{
if
(
strchr
(
WHITESPACE
"
\f
"
,
line
[
len
])
==
NULL
)
break
;
line
[
len
]
=
'\0'
;
}
str
=
line
;
/* Get the keyword. (Each line is supposed to begin with a keyword). */
if
((
keyword
=
strdelim
(
&
str
))
==
NULL
)
return
0
;
/* Ignore leading whitespace. */
if
(
*
keyword
==
'\0'
)
keyword
=
strdelim
(
&
str
);
if
(
keyword
==
NULL
||
!*
keyword
||
*
keyword
==
'\n'
||
*
keyword
==
'#'
)
return
0
;
/* Match lowercase keyword */
lowercase
(
keyword
);
/* Prepare to parse remainder of line */
if
(
str
!=
NULL
)
str
+=
strspn
(
str
,
WHITESPACE
);
if
(
str
==
NULL
||
*
str
==
'\0'
)
{
error
(
"%s line %d: no argument after keyword
\"
%s
\"
"
,
filename
,
linenum
,
keyword
);
return
-1
;
}
opcode
=
parse_token
(
keyword
,
filename
,
linenum
,
options
->
ignored_unknown
);
if
(
argv_split
(
str
,
&
oac
,
&
oav
,
1
)
!=
0
)
{
error
(
"%s line %d: invalid quotes"
,
filename
,
linenum
);
return
-1
;
}
ac
=
oac
;
av
=
oav
;
switch
(
opcode
)
{
case
oBadOption
:
/* don't panic, but count bad options */
goto
out
;
case
oIgnore
:
argv_consume
(
&
ac
);
break
;
case
oIgnoredUnknownOption
:
debug
(
"%s line %d: Ignored unknown option
\"
%s
\"
"
,
filename
,
linenum
,
keyword
);
argv_consume
(
&
ac
);
break
;
case
oConnectTimeout
:
intptr
=
&
options
->
connection_timeout
;
parse_time
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%s line %d: missing time value."
,
filename
,
linenum
);
goto
out
;
}
if
(
strcmp
(
arg
,
"none"
)
==
0
)
value
=
-1
;
else
if
((
value
=
convtime
(
arg
))
==
-1
)
{
error
(
"%s line %d: invalid time value."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
activep
&&
*
intptr
==
-1
)
*
intptr
=
value
;
break
;
case
oForwardAgent
:
intptr
=
&
options
->
forward_agent
;
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%s line %d: missing argument."
,
filename
,
linenum
);
goto
out
;
}
value
=
-1
;
multistate_ptr
=
multistate_flag
;
for
(
i
=
0
;
multistate_ptr
[
i
].
key
!=
NULL
;
i
++
)
{
if
(
strcasecmp
(
arg
,
multistate_ptr
[
i
].
key
)
==
0
)
{
value
=
multistate_ptr
[
i
].
value
;
break
;
}
}
if
(
value
!=
-1
)
{
if
(
*
activep
&&
*
intptr
==
-1
)
*
intptr
=
value
;
break
;
}
/* ForwardAgent wasn't 'yes' or 'no', assume a path */
if
(
*
activep
&&
*
intptr
==
-1
)
*
intptr
=
1
;
charptr
=
&
options
->
forward_agent_sock_path
;
goto
parse_agent_path
;
case
oForwardX11
:
intptr
=
&
options
->
forward_x11
;
parse_flag
:
multistate_ptr
=
multistate_flag
;
parse_multistate
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
((
value
=
parse_multistate_value
(
arg
,
filename
,
linenum
,
multistate_ptr
))
==
-1
)
{
error
(
"%s line %d: unsupported option
\"
%s
\"
."
,
filename
,
linenum
,
arg
);
goto
out
;
}
if
(
*
activep
&&
*
intptr
==
-1
)
*
intptr
=
value
;
break
;
case
oForwardX11Trusted
:
intptr
=
&
options
->
forward_x11_trusted
;
goto
parse_flag
;
case
oForwardX11Timeout
:
intptr
=
&
options
->
forward_x11_timeout
;
goto
parse_time
;
case
oGatewayPorts
:
intptr
=
&
options
->
fwd_opts
.
gateway_ports
;
goto
parse_flag
;
case
oExitOnForwardFailure
:
intptr
=
&
options
->
exit_on_forward_failure
;
goto
parse_flag
;
case
oPasswordAuthentication
:
intptr
=
&
options
->
password_authentication
;
goto
parse_flag
;
case
oKbdInteractiveAuthentication
:
intptr
=
&
options
->
kbd_interactive_authentication
;
goto
parse_flag
;
case
oKbdInteractiveDevices
:
charptr
=
&
options
->
kbd_interactive_devices
;
goto
parse_string
;
case
oPubkeyAuthentication
:
multistate_ptr
=
multistate_pubkey_auth
;
intptr
=
&
options
->
pubkey_authentication
;
goto
parse_multistate
;
case
oHostbasedAuthentication
:
intptr
=
&
options
->
hostbased_authentication
;
goto
parse_flag
;
case
oGssAuthentication
:
intptr
=
&
options
->
gss_authentication
;
goto
parse_flag
;
case
oGssDelegateCreds
:
intptr
=
&
options
->
gss_deleg_creds
;
goto
parse_flag
;
case
oBatchMode
:
intptr
=
&
options
->
batch_mode
;
goto
parse_flag
;
case
oCheckHostIP
:
intptr
=
&
options
->
check_host_ip
;
goto
parse_flag
;
case
oVerifyHostKeyDNS
:
intptr
=
&
options
->
verify_host_key_dns
;
multistate_ptr
=
multistate_yesnoask
;
goto
parse_multistate
;
case
oStrictHostKeyChecking
:
intptr
=
&
options
->
strict_host_key_checking
;
multistate_ptr
=
multistate_strict_hostkey
;
goto
parse_multistate
;
case
oCompression
:
intptr
=
&
options
->
compression
;
multistate_ptr
=
multistate_compression
;
goto
parse_multistate
;
case
oTCPKeepAlive
:
intptr
=
&
options
->
tcp_keep_alive
;
goto
parse_flag
;
case
oNoHostAuthenticationForLocalhost
:
intptr
=
&
options
->
no_host_authentication_for_localhost
;
goto
parse_flag
;
case
oNumberOfPasswordPrompts
:
intptr
=
&
options
->
number_of_password_prompts
;
goto
parse_int
;
case
oRekeyLimit
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
if
(
strcmp
(
arg
,
"default"
)
==
0
)
{
val64
=
0
;
}
else
{
if
(
scan_scaled
(
arg
,
&
val64
)
==
-1
)
{
error
(
"%.200s line %d: Bad number '%s': %s"
,
filename
,
linenum
,
arg
,
strerror
(
errno
));
goto
out
;
}
if
(
val64
!=
0
&&
val64
<
16
)
{
error
(
"%.200s line %d: RekeyLimit too small"
,
filename
,
linenum
);
goto
out
;
}
}
if
(
*
activep
&&
options
->
rekey_limit
==
-1
)
options
->
rekey_limit
=
val64
;
if
(
ac
!=
0
)
{
/* optional rekey interval present */
if
(
strcmp
(
av
[
0
],
"none"
)
==
0
)
{
(
void
)
argv_next
(
&
ac
,
&
av
);
/* discard */
break
;
}
intptr
=
&
options
->
rekey_interval
;
goto
parse_time
;
}
break
;
case
oIdentityFile
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
activep
)
{
intptr
=
&
options
->
num_identity_files
;
if
(
*
intptr
>=
SSH_MAX_IDENTITY_FILES
)
{
error
(
"%.200s line %d: Too many identity files "
"specified (max %d)."
,
filename
,
linenum
,
SSH_MAX_IDENTITY_FILES
);
goto
out
;
}
add_identity_file
(
options
,
NULL
,
arg
,
flags
&
SSHCONF_USERCONF
);
}
break
;
case
oCertificateFile
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
activep
)
{
intptr
=
&
options
->
num_certificate_files
;
if
(
*
intptr
>=
SSH_MAX_CERTIFICATE_FILES
)
{
error
(
"%.200s line %d: Too many certificate "
"files specified (max %d)."
,
filename
,
linenum
,
SSH_MAX_CERTIFICATE_FILES
);
goto
out
;
}
add_certificate_file
(
options
,
arg
,
flags
&
SSHCONF_USERCONF
);
}
break
;
case
oXAuthLocation
:
charptr
=&
options
->
xauth_location
;
goto
parse_string
;
case
oUser
:
charptr
=
&
options
->
user
;
parse_string
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
activep
&&
*
charptr
==
NULL
)
*
charptr
=
xstrdup
(
arg
);
break
;
case
oGlobalKnownHostsFile
:
cpptr
=
(
char
**
)
&
options
->
system_hostfiles
;
uintptr
=
&
options
->
num_system_hostfiles
;
max_entries
=
SSH_MAX_HOSTS_FILES
;
parse_char_array
:
i
=
0
;
value
=
*
uintptr
==
0
;
/* was array empty when we started? */
while
((
arg
=
argv_next
(
&
ac
,
&
av
))
!=
NULL
)
{
if
(
*
arg
==
'\0'
)
{
error
(
"%s line %d: keyword %s empty argument"
,
filename
,
linenum
,
keyword
);
goto
out
;
}
/* Allow "none" only in first position */
if
(
strcasecmp
(
arg
,
"none"
)
==
0
)
{
if
(
i
>
0
||
ac
>
0
)
{
error
(
"%s line %d: keyword %s
\"
none
\"
"
"argument must appear alone."
,
filename
,
linenum
,
keyword
);
goto
out
;
}
}
i
++
;
if
(
*
activep
&&
value
)
{
if
((
*
uintptr
)
>=
max_entries
)
{
error
(
"%s line %d: too many %s "
"entries."
,
filename
,
linenum
,
keyword
);
goto
out
;
}
cpptr
[(
*
uintptr
)
++
]
=
xstrdup
(
arg
);
}
}
break
;
case
oUserKnownHostsFile
:
cpptr
=
(
char
**
)
&
options
->
user_hostfiles
;
uintptr
=
&
options
->
num_user_hostfiles
;
max_entries
=
SSH_MAX_HOSTS_FILES
;
goto
parse_char_array
;
case
oHostname
:
charptr
=
&
options
->
hostname
;
goto
parse_string
;
case
oTag
:
charptr
=
&
options
->
tag
;
goto
parse_string
;
case
oHostKeyAlias
:
charptr
=
&
options
->
host_key_alias
;
goto
parse_string
;
case
oPreferredAuthentications
:
charptr
=
&
options
->
preferred_authentications
;
goto
parse_string
;
case
oBindAddress
:
charptr
=
&
options
->
bind_address
;
goto
parse_string
;
case
oBindInterface
:
charptr
=
&
options
->
bind_interface
;
goto
parse_string
;
case
oPKCS11Provider
:
charptr
=
&
options
->
pkcs11_provider
;
goto
parse_string
;
case
oSecurityKeyProvider
:
charptr
=
&
options
->
sk_provider
;
goto
parse_string
;
case
oKnownHostsCommand
:
charptr
=
&
options
->
known_hosts_command
;
goto
parse_command
;
case
oProxyCommand
:
charptr
=
&
options
->
proxy_command
;
/* Ignore ProxyCommand if ProxyJump already specified */
if
(
options
->
jump_host
!=
NULL
)
charptr
=
&
options
->
jump_host
;
/* Skip below */
parse_command
:
if
(
str
==
NULL
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
len
=
strspn
(
str
,
WHITESPACE
"="
);
if
(
*
activep
&&
*
charptr
==
NULL
)
*
charptr
=
xstrdup
(
str
+
len
);
argv_consume
(
&
ac
);
break
;
case
oProxyJump
:
if
(
str
==
NULL
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
len
=
strspn
(
str
,
WHITESPACE
"="
);
/* XXX use argv? */
if
(
parse_jump
(
str
+
len
,
options
,
*
activep
)
==
-1
)
{
error
(
"%.200s line %d: Invalid ProxyJump
\"
%s
\"
"
,
filename
,
linenum
,
str
+
len
);
goto
out
;
}
argv_consume
(
&
ac
);
break
;
case
oPort
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
value
=
a2port
(
arg
);
if
(
value
<=
0
)
{
error
(
"%.200s line %d: Bad port '%s'."
,
filename
,
linenum
,
arg
);
goto
out
;
}
if
(
*
activep
&&
options
->
port
==
-1
)
options
->
port
=
value
;
break
;
case
oConnectionAttempts
:
intptr
=
&
options
->
connection_attempts
;
parse_int
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
((
errstr
=
atoi_err
(
arg
,
&
value
))
!=
NULL
)
{
error
(
"%s line %d: integer value %s."
,
filename
,
linenum
,
errstr
);
goto
out
;
}
if
(
*
activep
&&
*
intptr
==
-1
)
*
intptr
=
value
;
break
;
case
oCiphers
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
arg
!=
'-'
&&
!
ciphers_valid
(
*
arg
==
'+'
||
*
arg
==
'^'
?
arg
+
1
:
arg
)){
error
(
"%.200s line %d: Bad SSH2 cipher spec '%s'."
,
filename
,
linenum
,
arg
?
arg
:
"<NONE>"
);
goto
out
;
}
if
(
*
activep
&&
options
->
ciphers
==
NULL
)
options
->
ciphers
=
xstrdup
(
arg
);
break
;
case
oMacs
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
arg
!=
'-'
&&
!
mac_valid
(
*
arg
==
'+'
||
*
arg
==
'^'
?
arg
+
1
:
arg
))
{
error
(
"%.200s line %d: Bad SSH2 MAC spec '%s'."
,
filename
,
linenum
,
arg
?
arg
:
"<NONE>"
);
goto
out
;
}
if
(
*
activep
&&
options
->
macs
==
NULL
)
options
->
macs
=
xstrdup
(
arg
);
break
;
case
oKexAlgorithms
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
arg
!=
'-'
&&
!
kex_names_valid
(
*
arg
==
'+'
||
*
arg
==
'^'
?
arg
+
1
:
arg
))
{
error
(
"%.200s line %d: Bad SSH2 KexAlgorithms '%s'."
,
filename
,
linenum
,
arg
?
arg
:
"<NONE>"
);
goto
out
;
}
if
(
*
activep
&&
options
->
kex_algorithms
==
NULL
)
options
->
kex_algorithms
=
xstrdup
(
arg
);
break
;
case
oHostKeyAlgorithms
:
charptr
=
&
options
->
hostkeyalgorithms
;
ca_only
=
0
;
parse_pubkey_algos
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
arg
!=
'-'
&&
!
sshkey_names_valid2
(
*
arg
==
'+'
||
*
arg
==
'^'
?
arg
+
1
:
arg
,
1
,
ca_only
))
{
error
(
"%s line %d: Bad key types '%s'."
,
filename
,
linenum
,
arg
?
arg
:
"<NONE>"
);
goto
out
;
}
if
(
*
activep
&&
*
charptr
==
NULL
)
*
charptr
=
xstrdup
(
arg
);
break
;
case
oCASignatureAlgorithms
:
charptr
=
&
options
->
ca_sign_algorithms
;
ca_only
=
1
;
goto
parse_pubkey_algos
;
case
oLogLevel
:
log_level_ptr
=
&
options
->
log_level
;
arg
=
argv_next
(
&
ac
,
&
av
);
value
=
log_level_number
(
arg
);
if
(
value
==
SYSLOG_LEVEL_NOT_SET
)
{
error
(
"%.200s line %d: unsupported log level '%s'"
,
filename
,
linenum
,
arg
?
arg
:
"<NONE>"
);
goto
out
;
}
if
(
*
activep
&&
*
log_level_ptr
==
SYSLOG_LEVEL_NOT_SET
)
*
log_level_ptr
=
(
LogLevel
)
value
;
break
;
case
oLogFacility
:
log_facility_ptr
=
&
options
->
log_facility
;
arg
=
argv_next
(
&
ac
,
&
av
);
value
=
log_facility_number
(
arg
);
if
(
value
==
SYSLOG_FACILITY_NOT_SET
)
{
error
(
"%.200s line %d: unsupported log facility '%s'"
,
filename
,
linenum
,
arg
?
arg
:
"<NONE>"
);
goto
out
;
}
if
(
*
log_facility_ptr
==
-1
)
*
log_facility_ptr
=
(
SyslogFacility
)
value
;
break
;
case
oLogVerbose
:
cppptr
=
&
options
->
log_verbose
;
uintptr
=
&
options
->
num_log_verbose
;
i
=
0
;
while
((
arg
=
argv_next
(
&
ac
,
&
av
))
!=
NULL
)
{
if
(
*
arg
==
'\0'
)
{
error
(
"%s line %d: keyword %s empty argument"
,
filename
,
linenum
,
keyword
);
goto
out
;
}
/* Allow "none" only in first position */
if
(
strcasecmp
(
arg
,
"none"
)
==
0
)
{
if
(
i
>
0
||
ac
>
0
)
{
error
(
"%s line %d: keyword %s
\"
none
\"
"
"argument must appear alone."
,
filename
,
linenum
,
keyword
);
goto
out
;
}
}
i
++
;
if
(
*
activep
&&
*
uintptr
==
0
)
{
*
cppptr
=
xrecallocarray
(
*
cppptr
,
*
uintptr
,
*
uintptr
+
1
,
sizeof
(
**
cppptr
));
(
*
cppptr
)[(
*
uintptr
)
++
]
=
xstrdup
(
arg
);
}
}
break
;
case
oLocalForward
:
case
oRemoteForward
:
case
oDynamicForward
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
remotefwd
=
(
opcode
==
oRemoteForward
);
dynamicfwd
=
(
opcode
==
oDynamicForward
);
if
(
!
dynamicfwd
)
{
arg2
=
argv_next
(
&
ac
,
&
av
);
if
(
arg2
==
NULL
||
*
arg2
==
'\0'
)
{
if
(
remotefwd
)
dynamicfwd
=
1
;
else
{
error
(
"%.200s line %d: Missing target "
"argument."
,
filename
,
linenum
);
goto
out
;
}
}
else
{
/* construct a string for parse_forward */
snprintf
(
fwdarg
,
sizeof
(
fwdarg
),
"%s:%s"
,
arg
,
arg2
);
}
}
if
(
dynamicfwd
)
strlcpy
(
fwdarg
,
arg
,
sizeof
(
fwdarg
));
if
(
parse_forward
(
&
fwd
,
fwdarg
,
dynamicfwd
,
remotefwd
)
==
0
)
{
error
(
"%.200s line %d: Bad forwarding specification."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
activep
)
{
if
(
remotefwd
)
{
add_remote_forward
(
options
,
&
fwd
);
}
else
{
add_local_forward
(
options
,
&
fwd
);
}
}
break
;
case
oPermitRemoteOpen
:
uintptr
=
&
options
->
num_permitted_remote_opens
;
cppptr
=
&
options
->
permitted_remote_opens
;
uvalue
=
*
uintptr
;
/* modified later */
i
=
0
;
while
((
arg
=
argv_next
(
&
ac
,
&
av
))
!=
NULL
)
{
arg2
=
xstrdup
(
arg
);
/* Allow any/none only in first position */
if
(
strcasecmp
(
arg
,
"none"
)
==
0
||
strcasecmp
(
arg
,
"any"
)
==
0
)
{
if
(
i
>
0
||
ac
>
0
)
{
error
(
"%s line %d: keyword %s
\"
%s
\"
"
"argument must appear alone."
,
filename
,
linenum
,
keyword
,
arg
);
free
(
arg2
);
goto
out
;
}
}
else
{
p
=
hpdelim
(
&
arg
);
if
(
p
==
NULL
)
{
fatal
(
"%s line %d: missing host in %s"
,
filename
,
linenum
,
lookup_opcode_name
(
opcode
));
}
p
=
cleanhostname
(
p
);
/*
* don't want to use permitopen_port to avoid
* dependency on channels.[ch] here.
*/
if
(
arg
==
NULL
||
(
strcmp
(
arg
,
"*"
)
!=
0
&&
a2port
(
arg
)
<=
0
))
{
fatal
(
"%s line %d: bad port number "
"in %s"
,
filename
,
linenum
,
lookup_opcode_name
(
opcode
));
}
}
if
(
*
activep
&&
uvalue
==
0
)
{
opt_array_append
(
filename
,
linenum
,
lookup_opcode_name
(
opcode
),
cppptr
,
uintptr
,
arg2
);
}
free
(
arg2
);
i
++
;
}
if
(
i
==
0
)
fatal
(
"%s line %d: missing %s specification"
,
filename
,
linenum
,
lookup_opcode_name
(
opcode
));
break
;
case
oClearAllForwardings
:
intptr
=
&
options
->
clear_forwardings
;
goto
parse_flag
;
case
oHost
:
if
(
cmdline
)
{
error
(
"Host directive not supported as a command-line "
"option"
);
goto
out
;
}
*
activep
=
0
;
arg2
=
NULL
;
while
((
arg
=
argv_next
(
&
ac
,
&
av
))
!=
NULL
)
{
if
(
*
arg
==
'\0'
)
{
error
(
"%s line %d: keyword %s empty argument"
,
filename
,
linenum
,
keyword
);
goto
out
;
}
if
((
flags
&
SSHCONF_NEVERMATCH
)
!=
0
)
{
argv_consume
(
&
ac
);
break
;
}
negated
=
*
arg
==
'!'
;
if
(
negated
)
arg
++
;
if
(
match_pattern
(
host
,
arg
))
{
if
(
negated
)
{
debug
(
"%.200s line %d: Skipping Host "
"block because of negated match "
"for %.100s"
,
filename
,
linenum
,
arg
);
*
activep
=
0
;
argv_consume
(
&
ac
);
break
;
}
if
(
!*
activep
)
arg2
=
arg
;
/* logged below */
*
activep
=
1
;
}
}
if
(
*
activep
)
debug
(
"%.200s line %d: Applying options for %.100s"
,
filename
,
linenum
,
arg2
);
break
;
case
oMatch
:
if
(
cmdline
)
{
error
(
"Host directive not supported as a command-line "
"option"
);
goto
out
;
}
value
=
match_cfg_line
(
options
,
&
str
,
pw
,
host
,
original_host
,
flags
&
SSHCONF_FINAL
,
want_final_pass
,
filename
,
linenum
);
if
(
value
<
0
)
{
error
(
"%.200s line %d: Bad Match condition"
,
filename
,
linenum
);
goto
out
;
}
*
activep
=
(
flags
&
SSHCONF_NEVERMATCH
)
?
0
:
value
;
/*
* If match_cfg_line() didn't consume all its arguments then
* arrange for the extra arguments check below to fail.
*/
if
(
str
==
NULL
||
*
str
==
'\0'
)
argv_consume
(
&
ac
);
break
;
case
oEscapeChar
:
intptr
=
&
options
->
escape_char
;
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
if
(
strcmp
(
arg
,
"none"
)
==
0
)
value
=
SSH_ESCAPECHAR_NONE
;
else
if
(
arg
[
1
]
==
'\0'
)
value
=
(
u_char
)
arg
[
0
];
else
if
(
arg
[
0
]
==
'^'
&&
arg
[
2
]
==
0
&&
(
u_char
)
arg
[
1
]
>=
64
&&
(
u_char
)
arg
[
1
]
<
128
)
value
=
(
u_char
)
arg
[
1
]
&
31
;
else
{
error
(
"%.200s line %d: Bad escape character."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
activep
&&
*
intptr
==
-1
)
*
intptr
=
value
;
break
;
case
oAddressFamily
:
intptr
=
&
options
->
address_family
;
multistate_ptr
=
multistate_addressfamily
;
goto
parse_multistate
;
case
oEnableSSHKeysign
:
intptr
=
&
options
->
enable_ssh_keysign
;
goto
parse_flag
;
case
oIdentitiesOnly
:
intptr
=
&
options
->
identities_only
;
goto
parse_flag
;
case
oServerAliveInterval
:
intptr
=
&
options
->
server_alive_interval
;
goto
parse_time
;
case
oServerAliveCountMax
:
intptr
=
&
options
->
server_alive_count_max
;
goto
parse_int
;
case
oSendEnv
:
while
((
arg
=
argv_next
(
&
ac
,
&
av
))
!=
NULL
)
{
if
(
*
arg
==
'\0'
||
strchr
(
arg
,
'='
)
!=
NULL
)
{
error
(
"%s line %d: Invalid environment name."
,
filename
,
linenum
);
goto
out
;
}
if
(
!*
activep
)
continue
;
if
(
*
arg
==
'-'
)
{
/* Removing an env var */
rm_env
(
options
,
arg
,
filename
,
linenum
);
continue
;
}
opt_array_append
(
filename
,
linenum
,
lookup_opcode_name
(
opcode
),
&
options
->
send_env
,
&
options
->
num_send_env
,
arg
);
}
break
;
case
oSetEnv
:
value
=
options
->
num_setenv
;
while
((
arg
=
argv_next
(
&
ac
,
&
av
))
!=
NULL
)
{
if
(
strchr
(
arg
,
'='
)
==
NULL
)
{
error
(
"%s line %d: Invalid SetEnv."
,
filename
,
linenum
);
goto
out
;
}
if
(
!*
activep
||
value
!=
0
)
continue
;
if
(
lookup_setenv_in_list
(
arg
,
options
->
setenv
,
options
->
num_setenv
)
!=
NULL
)
{
debug2
(
"%s line %d: ignoring duplicate env "
"name
\"
%.64s
\"
"
,
filename
,
linenum
,
arg
);
continue
;
}
opt_array_append
(
filename
,
linenum
,
lookup_opcode_name
(
opcode
),
&
options
->
setenv
,
&
options
->
num_setenv
,
arg
);
}
break
;
case
oControlPath
:
charptr
=
&
options
->
control_path
;
goto
parse_string
;
case
oControlMaster
:
intptr
=
&
options
->
control_master
;
multistate_ptr
=
multistate_controlmaster
;
goto
parse_multistate
;
case
oControlPersist
:
/* no/false/yes/true, or a time spec */
intptr
=
&
options
->
control_persist
;
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing ControlPersist"
" argument."
,
filename
,
linenum
);
goto
out
;
}
value
=
0
;
value2
=
0
;
/* timeout */
if
(
strcmp
(
arg
,
"no"
)
==
0
||
strcmp
(
arg
,
"false"
)
==
0
)
value
=
0
;
else
if
(
strcmp
(
arg
,
"yes"
)
==
0
||
strcmp
(
arg
,
"true"
)
==
0
)
value
=
1
;
else
if
((
value2
=
convtime
(
arg
))
>=
0
)
value
=
1
;
else
{
error
(
"%.200s line %d: Bad ControlPersist argument."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
activep
&&
*
intptr
==
-1
)
{
*
intptr
=
value
;
options
->
control_persist_timeout
=
value2
;
}
break
;
case
oHashKnownHosts
:
intptr
=
&
options
->
hash_known_hosts
;
goto
parse_flag
;
case
oTunnel
:
intptr
=
&
options
->
tun_open
;
multistate_ptr
=
multistate_tunnel
;
goto
parse_multistate
;
case
oTunnelDevice
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
value
=
a2tun
(
arg
,
&
value2
);
if
(
value
==
SSH_TUNID_ERR
)
{
error
(
"%.200s line %d: Bad tun device."
,
filename
,
linenum
);
goto
out
;
}
if
(
*
activep
&&
options
->
tun_local
==
-1
)
{
options
->
tun_local
=
value
;
options
->
tun_remote
=
value2
;
}
break
;
case
oLocalCommand
:
charptr
=
&
options
->
local_command
;
goto
parse_command
;
case
oPermitLocalCommand
:
intptr
=
&
options
->
permit_local_command
;
goto
parse_flag
;
case
oRemoteCommand
:
charptr
=
&
options
->
remote_command
;
goto
parse_command
;
case
oVisualHostKey
:
intptr
=
&
options
->
visual_host_key
;
goto
parse_flag
;
case
oInclude
:
if
(
cmdline
)
{
error
(
"Include directive not supported as a "
"command-line option"
);
goto
out
;
}
value
=
0
;
while
((
arg
=
argv_next
(
&
ac
,
&
av
))
!=
NULL
)
{
if
(
*
arg
==
'\0'
)
{
error
(
"%s line %d: keyword %s empty argument"
,
filename
,
linenum
,
keyword
);
goto
out
;
}
/*
* Ensure all paths are anchored. User configuration
* files may begin with '~/' but system configurations
* must not. If the path is relative, then treat it
* as living in ~/.ssh for user configurations or
* /etc/ssh for system ones.
*/
if
(
*
arg
==
'~'
&&
(
flags
&
SSHCONF_USERCONF
)
==
0
)
{
error
(
"%.200s line %d: bad include path %s."
,
filename
,
linenum
,
arg
);
goto
out
;
}
if
(
!
path_absolute
(
arg
)
&&
*
arg
!=
'~'
)
{
xasprintf
(
&
arg2
,
"%s/%s"
,
(
flags
&
SSHCONF_USERCONF
)
?
"~/"
_PATH_SSH_USER_DIR
:
SSHDIR
,
arg
);
}
else
arg2
=
xstrdup
(
arg
);
memset
(
&
gl
,
0
,
sizeof
(
gl
));
r
=
glob
(
arg2
,
GLOB_TILDE
,
NULL
,
&
gl
);
if
(
r
==
GLOB_NOMATCH
)
{
debug
(
"%.200s line %d: include %s matched no "
"files"
,
filename
,
linenum
,
arg2
);
free
(
arg2
);
continue
;
}
else
if
(
r
!=
0
)
{
error
(
"%.200s line %d: glob failed for %s."
,
filename
,
linenum
,
arg2
);
goto
out
;
}
free
(
arg2
);
oactive
=
*
activep
;
for
(
i
=
0
;
i
<
gl
.
gl_pathc
;
i
++
)
{
debug3
(
"%.200s line %d: Including file %s "
"depth %d%s"
,
filename
,
linenum
,
gl
.
gl_pathv
[
i
],
depth
,
oactive
?
""
:
" (parse only)"
);
r
=
read_config_file_depth
(
gl
.
gl_pathv
[
i
],
pw
,
host
,
original_host
,
options
,
flags
|
SSHCONF_CHECKPERM
|
(
oactive
?
0
:
SSHCONF_NEVERMATCH
),
activep
,
want_final_pass
,
depth
+
1
);
if
(
r
!=
1
&&
errno
!=
ENOENT
)
{
error
(
"Can't open user config file "
"%.100s: %.100s"
,
gl
.
gl_pathv
[
i
],
strerror
(
errno
));
globfree
(
&
gl
);
goto
out
;
}
/*
* don't let Match in includes clobber the
* containing file's Match state.
*/
*
activep
=
oactive
;
if
(
r
!=
1
)
value
=
-1
;
}
globfree
(
&
gl
);
}
if
(
value
!=
0
)
ret
=
value
;
break
;
case
oIPQoS
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
((
value
=
parse_ipqos
(
arg
))
==
-1
)
{
error
(
"%s line %d: Bad IPQoS value: %s"
,
filename
,
linenum
,
arg
);
goto
out
;
}
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
arg
==
NULL
)
value2
=
value
;
else
if
((
value2
=
parse_ipqos
(
arg
))
==
-1
)
{
error
(
"%s line %d: Bad IPQoS value: %s"
,
filename
,
linenum
,
arg
);
goto
out
;
}
if
(
*
activep
&&
options
->
ip_qos_interactive
==
-1
)
{
options
->
ip_qos_interactive
=
value
;
options
->
ip_qos_bulk
=
value2
;
}
break
;
case
oRequestTTY
:
intptr
=
&
options
->
request_tty
;
multistate_ptr
=
multistate_requesttty
;
goto
parse_multistate
;
case
oSessionType
:
intptr
=
&
options
->
session_type
;
multistate_ptr
=
multistate_sessiontype
;
goto
parse_multistate
;
case
oStdinNull
:
intptr
=
&
options
->
stdin_null
;
goto
parse_flag
;
case
oForkAfterAuthentication
:
intptr
=
&
options
->
fork_after_authentication
;
goto
parse_flag
;
case
oIgnoreUnknown
:
charptr
=
&
options
->
ignored_unknown
;
goto
parse_string
;
case
oProxyUseFdpass
:
intptr
=
&
options
->
proxy_use_fdpass
;
goto
parse_flag
;
case
oCanonicalDomains
:
value
=
options
->
num_canonical_domains
!=
0
;
i
=
0
;
while
((
arg
=
argv_next
(
&
ac
,
&
av
))
!=
NULL
)
{
if
(
*
arg
==
'\0'
)
{
error
(
"%s line %d: keyword %s empty argument"
,
filename
,
linenum
,
keyword
);
goto
out
;
}
/* Allow "none" only in first position */
if
(
strcasecmp
(
arg
,
"none"
)
==
0
)
{
if
(
i
>
0
||
ac
>
0
)
{
error
(
"%s line %d: keyword %s
\"
none
\"
"
"argument must appear alone."
,
filename
,
linenum
,
keyword
);
goto
out
;
}
}
i
++
;
if
(
!
valid_domain
(
arg
,
1
,
&
errstr
))
{
error
(
"%s line %d: %s"
,
filename
,
linenum
,
errstr
);
goto
out
;
}
if
(
!*
activep
||
value
)
continue
;
if
(
options
->
num_canonical_domains
>=
MAX_CANON_DOMAINS
)
{
error
(
"%s line %d: too many hostname suffixes."
,
filename
,
linenum
);
goto
out
;
}
options
->
canonical_domains
[
options
->
num_canonical_domains
++
]
=
xstrdup
(
arg
);
}
break
;
case
oCanonicalizePermittedCNAMEs
:
value
=
options
->
num_permitted_cnames
!=
0
;
i
=
0
;
while
((
arg
=
argv_next
(
&
ac
,
&
av
))
!=
NULL
)
{
/*
* Either 'none' (only in first position), '*' for
* everything or 'list:list'
*/
if
(
strcasecmp
(
arg
,
"none"
)
==
0
)
{
if
(
i
>
0
||
ac
>
0
)
{
error
(
"%s line %d: keyword %s
\"
none
\"
"
"argument must appear alone."
,
filename
,
linenum
,
keyword
);
goto
out
;
}
arg2
=
""
;
}
else
if
(
strcmp
(
arg
,
"*"
)
==
0
)
{
arg2
=
arg
;
}
else
{
lowercase
(
arg
);
if
((
arg2
=
strchr
(
arg
,
':'
))
==
NULL
||
arg2
[
1
]
==
'\0'
)
{
error
(
"%s line %d: "
"Invalid permitted CNAME
\"
%s
\"
"
,
filename
,
linenum
,
arg
);
goto
out
;
}
*
arg2
=
'\0'
;
arg2
++
;
}
i
++
;
if
(
!*
activep
||
value
)
continue
;
if
(
options
->
num_permitted_cnames
>=
MAX_CANON_DOMAINS
)
{
error
(
"%s line %d: too many permitted CNAMEs."
,
filename
,
linenum
);
goto
out
;
}
cname
=
options
->
permitted_cnames
+
options
->
num_permitted_cnames
++
;
cname
->
source_list
=
xstrdup
(
arg
);
cname
->
target_list
=
xstrdup
(
arg2
);
}
break
;
case
oCanonicalizeHostname
:
intptr
=
&
options
->
canonicalize_hostname
;
multistate_ptr
=
multistate_canonicalizehostname
;
goto
parse_multistate
;
case
oCanonicalizeMaxDots
:
intptr
=
&
options
->
canonicalize_max_dots
;
goto
parse_int
;
case
oCanonicalizeFallbackLocal
:
intptr
=
&
options
->
canonicalize_fallback_local
;
goto
parse_flag
;
case
oStreamLocalBindMask
:
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing StreamLocalBindMask "
"argument."
,
filename
,
linenum
);
goto
out
;
}
/* Parse mode in octal format */
value
=
strtol
(
arg
,
&
endofnumber
,
8
);
if
(
arg
==
endofnumber
||
value
<
0
||
value
>
0777
)
{
error
(
"%.200s line %d: Bad mask."
,
filename
,
linenum
);
goto
out
;
}
options
->
fwd_opts
.
streamlocal_bind_mask
=
(
mode_t
)
value
;
break
;
case
oStreamLocalBindUnlink
:
intptr
=
&
options
->
fwd_opts
.
streamlocal_bind_unlink
;
goto
parse_flag
;
case
oRevokedHostKeys
:
charptr
=
&
options
->
revoked_host_keys
;
goto
parse_string
;
case
oFingerprintHash
:
intptr
=
&
options
->
fingerprint_hash
;
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
if
((
value
=
ssh_digest_alg_by_name
(
arg
))
==
-1
)
{
error
(
"%.200s line %d: Invalid hash algorithm
\"
%s
\"
."
,
filename
,
linenum
,
arg
);
goto
out
;
}
if
(
*
activep
&&
*
intptr
==
-1
)
*
intptr
=
value
;
break
;
case
oUpdateHostkeys
:
intptr
=
&
options
->
update_hostkeys
;
multistate_ptr
=
multistate_yesnoask
;
goto
parse_multistate
;
case
oHostbasedAcceptedAlgorithms
:
charptr
=
&
options
->
hostbased_accepted_algos
;
ca_only
=
0
;
goto
parse_pubkey_algos
;
case
oPubkeyAcceptedAlgorithms
:
charptr
=
&
options
->
pubkey_accepted_algos
;
ca_only
=
0
;
goto
parse_pubkey_algos
;
case
oAddKeysToAgent
:
arg
=
argv_next
(
&
ac
,
&
av
);
arg2
=
argv_next
(
&
ac
,
&
av
);
value
=
parse_multistate_value
(
arg
,
filename
,
linenum
,
multistate_yesnoaskconfirm
);
value2
=
0
;
/* unlimited lifespan by default */
if
(
value
==
3
&&
arg2
!=
NULL
)
{
/* allow "AddKeysToAgent confirm 5m" */
if
((
value2
=
convtime
(
arg2
))
==
-1
)
{
error
(
"%s line %d: invalid time value."
,
filename
,
linenum
);
goto
out
;
}
}
else
if
(
value
==
-1
&&
arg2
==
NULL
)
{
if
((
value2
=
convtime
(
arg
))
==
-1
)
{
error
(
"%s line %d: unsupported option"
,
filename
,
linenum
);
goto
out
;
}
value
=
1
;
/* yes */
}
else
if
(
value
==
-1
||
arg2
!=
NULL
)
{
error
(
"%s line %d: unsupported option"
,
filename
,
linenum
);
goto
out
;
}
if
(
*
activep
&&
options
->
add_keys_to_agent
==
-1
)
{
options
->
add_keys_to_agent
=
value
;
options
->
add_keys_to_agent_lifespan
=
value2
;
}
break
;
case
oIdentityAgent
:
charptr
=
&
options
->
identity_agent
;
arg
=
argv_next
(
&
ac
,
&
av
);
if
(
!
arg
||
*
arg
==
'\0'
)
{
error
(
"%.200s line %d: Missing argument."
,
filename
,
linenum
);
goto
out
;
}
parse_agent_path
:
/* Extra validation if the string represents an env var. */
if
((
arg2
=
dollar_expand
(
&
r
,
arg
))
==
NULL
||
r
)
{
error
(
"%.200s line %d: Invalid environment expansion "
"%s."
,
filename
,
linenum
,
arg
);
goto
out
;
}
free
(
arg2
);
/* check for legacy environment format */
if
(
arg
[
0
]
==
'$'
&&
arg
[
1
]
!=
'{'
&&
!
valid_env_name
(
arg
+
1
))
{
error
(
"%.200s line %d: Invalid environment name %s."
,
filename
,
linenum
,
arg
);
goto
out
;
}
if
(
*
activep
&&
*
charptr
==
NULL
)
*
charptr
=
xstrdup
(
arg
);
break
;
case
oEnableEscapeCommandline
:
intptr
=
&
options
->
enable_escape_commandline
;
goto
parse_flag
;
case
oRequiredRSASize
:
intptr
=
&
options
->
required_rsa_size
;
goto
parse_int
;
case
oDeprecated
:
debug
(
"%s line %d: Deprecated option
\"
%s
\"
"
,
filename
,
linenum
,
keyword
);
argv_consume
(
&
ac
);
break
;
case
oUnsupported
:
error
(
"%s line %d: Unsupported option
\"
%s
\"
"
,
filename
,
linenum
,
keyword
);
argv_consume
(
&
ac
);
break
;
default
:
error
(
"%s line %d: Unimplemented opcode %d"
,
filename
,
linenum
,
opcode
);
goto
out
;
}
/* Check that there is no garbage at end of line. */
if
(
ac
>
0
)
{
error
(
"%.200s line %d: keyword %s extra arguments "
"at end of line"
,
filename
,
linenum
,
keyword
);
goto
out
;
}
/* success */
ret
=
0
;
out
:
argv_free
(
oav
,
oac
);
return
ret
;
}
/*
* Reads the config file and modifies the options accordingly. Options
* should already be initialized before this call. This never returns if
* there is an error. If the file does not exist, this returns 0.
*/
int
read_config_file
(
const
char
*
filename
,
struct
passwd
*
pw
,
const
char
*
host
,
const
char
*
original_host
,
Options
*
options
,
int
flags
,
int
*
want_final_pass
)
{
int
active
=
1
;
return
read_config_file_depth
(
filename
,
pw
,
host
,
original_host
,
options
,
flags
,
&
active
,
want_final_pass
,
0
);
}
#define READCONF_MAX_DEPTH 16
static
int
read_config_file_depth
(
const
char
*
filename
,
struct
passwd
*
pw
,
const
char
*
host
,
const
char
*
original_host
,
Options
*
options
,
int
flags
,
int
*
activep
,
int
*
want_final_pass
,
int
depth
)
{
FILE
*
f
;
char
*
line
=
NULL
;
size_t
linesize
=
0
;
int
linenum
;
int
bad_options
=
0
;
if
(
depth
<
0
||
depth
>
READCONF_MAX_DEPTH
)
fatal
(
"Too many recursive configuration includes"
);
if
((
f
=
fopen
(
filename
,
"r"
))
==
NULL
)
return
0
;
if
(
flags
&
SSHCONF_CHECKPERM
)
{
struct
stat
sb
;
if
(
fstat
(
fileno
(
f
),
&
sb
)
==
-1
)
fatal
(
"fstat %s: %s"
,
filename
,
strerror
(
errno
));
if
(((
sb
.
st_uid
!=
0
&&
sb
.
st_uid
!=
getuid
())
||
(
sb
.
st_mode
&
022
)
!=
0
))
fatal
(
"Bad owner or permissions on %s"
,
filename
);
}
debug
(
"Reading configuration data %.200s"
,
filename
);
/*
* Mark that we are now processing the options. This flag is turned
* on/off by Host specifications.
*/
linenum
=
0
;
while
(
getline
(
&
line
,
&
linesize
,
f
)
!=
-1
)
{
/* Update line number counter. */
linenum
++
;
/*
* Trim out comments and strip whitespace.
* NB - preserve newlines, they are needed to reproduce
* line numbers later for error messages.
*/
if
(
process_config_line_depth
(
options
,
pw
,
host
,
original_host
,
line
,
filename
,
linenum
,
activep
,
flags
,
want_final_pass
,
depth
)
!=
0
)
bad_options
++
;
}
free
(
line
);
fclose
(
f
);
if
(
bad_options
>
0
)
fatal
(
"%s: terminating, %d bad configuration options"
,
filename
,
bad_options
);
return
1
;
}
/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
int
option_clear_or_none
(
const
char
*
o
)
{
return
o
==
NULL
||
strcasecmp
(
o
,
"none"
)
==
0
;
}
/*
* Returns 1 if CanonicalizePermittedCNAMEs have been specified, 0 otherwise.
* Allowed to be called on non-final configuration.
*/
int
config_has_permitted_cnames
(
Options
*
options
)
{
if
(
options
->
num_permitted_cnames
==
1
&&
strcasecmp
(
options
->
permitted_cnames
[
0
].
source_list
,
"none"
)
==
0
&&
strcmp
(
options
->
permitted_cnames
[
0
].
target_list
,
""
)
==
0
)
return
0
;
return
options
->
num_permitted_cnames
>
0
;
}
/*
* Initializes options to special values that indicate that they have not yet
* been set. Read_config_file will only set options with this value. Options
* are processed in the following order: command line, user config file,
* system config file. Last, fill_default_options is called.
*/
void
initialize_options
(
Options
*
options
)
{
memset
(
options
,
'X'
,
sizeof
(
*
options
));
options
->
host_arg
=
NULL
;
options
->
forward_agent
=
-1
;
options
->
forward_agent_sock_path
=
NULL
;
options
->
forward_x11
=
-1
;
options
->
forward_x11_trusted
=
-1
;
options
->
forward_x11_timeout
=
-1
;
options
->
stdio_forward_host
=
NULL
;
options
->
stdio_forward_port
=
0
;
options
->
clear_forwardings
=
-1
;
options
->
exit_on_forward_failure
=
-1
;
options
->
xauth_location
=
NULL
;
options
->
fwd_opts
.
gateway_ports
=
-1
;
options
->
fwd_opts
.
streamlocal_bind_mask
=
(
mode_t
)
-1
;
options
->
fwd_opts
.
streamlocal_bind_unlink
=
-1
;
options
->
pubkey_authentication
=
-1
;
options
->
gss_authentication
=
-1
;
options
->
gss_deleg_creds
=
-1
;
options
->
password_authentication
=
-1
;
options
->
kbd_interactive_authentication
=
-1
;
options
->
kbd_interactive_devices
=
NULL
;
options
->
hostbased_authentication
=
-1
;
options
->
batch_mode
=
-1
;
options
->
check_host_ip
=
-1
;
options
->
strict_host_key_checking
=
-1
;
options
->
compression
=
-1
;
options
->
tcp_keep_alive
=
-1
;
options
->
port
=
-1
;
options
->
address_family
=
-1
;
options
->
connection_attempts
=
-1
;
options
->
connection_timeout
=
-1
;
options
->
number_of_password_prompts
=
-1
;
options
->
ciphers
=
NULL
;
options
->
macs
=
NULL
;
options
->
kex_algorithms
=
NULL
;
options
->
hostkeyalgorithms
=
NULL
;
options
->
ca_sign_algorithms
=
NULL
;
options
->
num_identity_files
=
0
;
memset
(
options
->
identity_keys
,
0
,
sizeof
(
options
->
identity_keys
));
options
->
num_certificate_files
=
0
;
memset
(
options
->
certificates
,
0
,
sizeof
(
options
->
certificates
));
options
->
hostname
=
NULL
;
options
->
host_key_alias
=
NULL
;
options
->
proxy_command
=
NULL
;
options
->
jump_user
=
NULL
;
options
->
jump_host
=
NULL
;
options
->
jump_port
=
-1
;
options
->
jump_extra
=
NULL
;
options
->
user
=
NULL
;
options
->
escape_char
=
-1
;
options
->
num_system_hostfiles
=
0
;
options
->
num_user_hostfiles
=
0
;
options
->
local_forwards
=
NULL
;
options
->
num_local_forwards
=
0
;
options
->
remote_forwards
=
NULL
;
options
->
num_remote_forwards
=
0
;
options
->
permitted_remote_opens
=
NULL
;
options
->
num_permitted_remote_opens
=
0
;
options
->
log_facility
=
SYSLOG_FACILITY_NOT_SET
;
options
->
log_level
=
SYSLOG_LEVEL_NOT_SET
;
options
->
num_log_verbose
=
0
;
options
->
log_verbose
=
NULL
;
options
->
preferred_authentications
=
NULL
;
options
->
bind_address
=
NULL
;
options
->
bind_interface
=
NULL
;
options
->
pkcs11_provider
=
NULL
;
options
->
sk_provider
=
NULL
;
options
->
enable_ssh_keysign
=
-
1
;
options
->
no_host_authentication_for_localhost
=
-
1
;
options
->
identities_only
=
-
1
;
options
->
rekey_limit
=
-
1
;
options
->
rekey_interval
=
-1
;
options
->
verify_host_key_dns
=
-1
;
options
->
server_alive_interval
=
-1
;
options
->
server_alive_count_max
=
-1
;
options
->
send_env
=
NULL
;
options
->
num_send_env
=
0
;
options
->
setenv
=
NULL
;
options
->
num_setenv
=
0
;
options
->
control_path
=
NULL
;
options
->
control_master
=
-1
;
options
->
control_persist
=
-1
;
options
->
control_persist_timeout
=
0
;
options
->
hash_known_hosts
=
-1
;
options
->
tun_open
=
-1
;
options
->
tun_local
=
-1
;
options
->
tun_remote
=
-1
;
options
->
local_command
=
NULL
;
options
->
permit_local_command
=
-1
;
options
->
remote_command
=
NULL
;
options
->
add_keys_to_agent
=
-1
;
options
->
add_keys_to_agent_lifespan
=
-1
;
options
->
identity_agent
=
NULL
;
options
->
visual_host_key
=
-1
;
options
->
ip_qos_interactive
=
-1
;
options
->
ip_qos_bulk
=
-1
;
options
->
request_tty
=
-1
;
options
->
session_type
=
-1
;
options
->
stdin_null
=
-1
;
options
->
fork_after_authentication
=
-1
;
options
->
proxy_use_fdpass
=
-1
;
options
->
ignored_unknown
=
NULL
;
options
->
num_canonical_domains
=
0
;
options
->
num_permitted_cnames
=
0
;
options
->
canonicalize_max_dots
=
-1
;
options
->
canonicalize_fallback_local
=
-1
;
options
->
canonicalize_hostname
=
-1
;
options
->
revoked_host_keys
=
NULL
;
options
->
fingerprint_hash
=
-1
;
options
->
update_hostkeys
=
-1
;
options
->
hostbased_accepted_algos
=
NULL
;
options
->
pubkey_accepted_algos
=
NULL
;
options
->
known_hosts_command
=
NULL
;
options
->
required_rsa_size
=
-1
;
options
->
enable_escape_commandline
=
-1
;
options
->
tag
=
NULL
;
}
/*
* A petite version of fill_default_options() that just fills the options
* needed for hostname canonicalization to proceed.
*/
void
fill_default_options_for_canonicalization
(
Options
*
options
)
{
if
(
options
->
canonicalize_max_dots
==
-1
)
options
->
canonicalize_max_dots
=
1
;
if
(
options
->
canonicalize_fallback_local
==
-1
)
options
->
canonicalize_fallback_local
=
1
;
if
(
options
->
canonicalize_hostname
==
-1
)
options
->
canonicalize_hostname
=
SSH_CANONICALISE_NO
;
}
/*
* Called after processing other sources of option data, this fills those
* options for which no value has been specified with their default values.
*/
int
fill_default_options
(
Options
*
options
)
{
char
*
all_cipher
,
*
all_mac
,
*
all_kex
,
*
all_key
,
*
all_sig
;
char
*
def_cipher
,
*
def_mac
,
*
def_kex
,
*
def_key
,
*
def_sig
;
int
ret
=
0
,
r
;
if
(
options
->
forward_agent
==
-1
)
options
->
forward_agent
=
0
;
if
(
options
->
forward_x11
==
-1
)
options
->
forward_x11
=
0
;
if
(
options
->
forward_x11_trusted
==
-1
)
options
->
forward_x11_trusted
=
0
;
if
(
options
->
forward_x11_timeout
==
-1
)
options
->
forward_x11_timeout
=
1200
;
/*
* stdio forwarding (-W) changes the default for these but we defer
* setting the values so they can be overridden.
*/
if
(
options
->
exit_on_forward_failure
==
-1
)
options
->
exit_on_forward_failure
=
options
->
stdio_forward_host
!=
NULL
?
1
:
0
;
if
(
options
->
clear_forwardings
==
-1
)
options
->
clear_forwardings
=
options
->
stdio_forward_host
!=
NULL
?
1
:
0
;
if
(
options
->
clear_forwardings
==
1
)
clear_forwardings
(
options
);
if
(
options
->
xauth_location
==
NULL
)
options
->
xauth_location
=
xstrdup
(
_PATH_XAUTH
);
if
(
options
->
fwd_opts
.
gateway_ports
==
-1
)
options
->
fwd_opts
.
gateway_ports
=
0
;
if
(
options
->
fwd_opts
.
streamlocal_bind_mask
==
(
mode_t
)
-1
)
options
->
fwd_opts
.
streamlocal_bind_mask
=
0177
;
if
(
options
->
fwd_opts
.
streamlocal_bind_unlink
==
-1
)
options
->
fwd_opts
.
streamlocal_bind_unlink
=
0
;
if
(
options
->
pubkey_authentication
==
-1
)
options
->
pubkey_authentication
=
SSH_PUBKEY_AUTH_ALL
;
if
(
options
->
gss_authentication
==
-1
)
options
->
gss_authentication
=
0
;
if
(
options
->
gss_deleg_creds
==
-1
)
options
->
gss_deleg_creds
=
0
;
if
(
options
->
password_authentication
==
-1
)
options
->
password_authentication
=
1
;
if
(
options
->
kbd_interactive_authentication
==
-1
)
options
->
kbd_interactive_authentication
=
1
;
if
(
options
->
hostbased_authentication
==
-1
)
options
->
hostbased_authentication
=
0
;
if
(
options
->
batch_mode
==
-1
)
options
->
batch_mode
=
0
;
if
(
options
->
check_host_ip
==
-1
)
options
->
check_host_ip
=
0
;
if
(
options
->
strict_host_key_checking
==
-1
)
options
->
strict_host_key_checking
=
SSH_STRICT_HOSTKEY_ASK
;
if
(
options
->
compression
==
-1
)
options
->
compression
=
0
;
if
(
options
->
tcp_keep_alive
==
-1
)
options
->
tcp_keep_alive
=
1
;
if
(
options
->
port
==
-1
)
options
->
port
=
0
;
/* Filled in ssh_connect. */
if
(
options
->
address_family
==
-1
)
options
->
address_family
=
AF_UNSPEC
;
if
(
options
->
connection_attempts
==
-1
)
options
->
connection_attempts
=
1
;
if
(
options
->
number_of_password_prompts
==
-1
)
options
->
number_of_password_prompts
=
3
;
/* options->hostkeyalgorithms, default set in myproposals.h */
if
(
options
->
add_keys_to_agent
==
-1
)
{
options
->
add_keys_to_agent
=
0
;
options
->
add_keys_to_agent_lifespan
=
0
;
}
if
(
options
->
num_identity_files
==
0
)
{
add_identity_file
(
options
,
"~/"
,
_PATH_SSH_CLIENT_ID_RSA
,
0
);
#ifdef OPENSSL_HAS_ECC
add_identity_file
(
options
,
"~/"
,
_PATH_SSH_CLIENT_ID_ECDSA
,
0
);
add_identity_file
(
options
,
"~/"
,
_PATH_SSH_CLIENT_ID_ECDSA_SK
,
0
);
#endif
add_identity_file
(
options
,
"~/"
,
_PATH_SSH_CLIENT_ID_ED25519
,
0
);
add_identity_file
(
options
,
"~/"
,
_PATH_SSH_CLIENT_ID_ED25519_SK
,
0
);
add_identity_file
(
options
,
"~/"
,
_PATH_SSH_CLIENT_ID_XMSS
,
0
);
add_identity_file
(
options
,
"~/"
,
_PATH_SSH_CLIENT_ID_DSA
,
0
);
}
if
(
options
->
escape_char
==
-1
)
options
->
escape_char
=
'~'
;
if
(
options
->
num_system_hostfiles
==
0
)
{
options
->
system_hostfiles
[
options
->
num_system_hostfiles
++
]
=
xstrdup
(
_PATH_SSH_SYSTEM_HOSTFILE
);
options
->
system_hostfiles
[
options
->
num_system_hostfiles
++
]
=
xstrdup
(
_PATH_SSH_SYSTEM_HOSTFILE2
);
}
if
(
options
->
update_hostkeys
==
-1
)
{
if
(
options
->
verify_host_key_dns
<=
0
&&
(
options
->
num_user_hostfiles
==
0
||
(
options
->
num_user_hostfiles
==
1
&&
strcmp
(
options
->
user_hostfiles
[
0
],
_PATH_SSH_USER_HOSTFILE
)
==
0
)))
options
->
update_hostkeys
=
SSH_UPDATE_HOSTKEYS_YES
;
else
options
->
update_hostkeys
=
SSH_UPDATE_HOSTKEYS_NO
;
}
if
(
options
->
num_user_hostfiles
==
0
)
{
options
->
user_hostfiles
[
options
->
num_user_hostfiles
++
]
=
xstrdup
(
_PATH_SSH_USER_HOSTFILE
);
options
->
user_hostfiles
[
options
->
num_user_hostfiles
++
]
=
xstrdup
(
_PATH_SSH_USER_HOSTFILE2
);
}
if
(
options
->
log_level
==
SYSLOG_LEVEL_NOT_SET
)
options
->
log_level
=
SYSLOG_LEVEL_INFO
;
if
(
options
->
log_facility
==
SYSLOG_FACILITY_NOT_SET
)
options
->
log_facility
=
SYSLOG_FACILITY_USER
;
if
(
options
->
no_host_authentication_for_localhost
==
-
1
)
options
->
no_host_authentication_for_localhost
=
0
;
if
(
options
->
identities_only
==
-1
)
options
->
identities_only
=
0
;
if
(
options
->
enable_ssh_keysign
==
-1
)
options
->
enable_ssh_keysign
=
0
;
if
(
options
->
rekey_limit
==
-1
)
options
->
rekey_limit
=
0
;
if
(
options
->
rekey_interval
==
-1
)
options
->
rekey_interval
=
0
;
if
(
options
->
verify_host_key_dns
==
-1
)
options
->
verify_host_key_dns
=
0
;
if
(
options
->
server_alive_interval
==
-1
)
options
->
server_alive_interval
=
0
;
if
(
options
->
server_alive_count_max
==
-1
)
options
->
server_alive_count_max
=
3
;
if
(
options
->
control_master
==
-1
)
options
->
control_master
=
0
;
if
(
options
->
control_persist
==
-1
)
{
options
->
control_persist
=
0
;
options
->
control_persist_timeout
=
0
;
}
if
(
options
->
hash_known_hosts
==
-1
)
options
->
hash_known_hosts
=
0
;
if
(
options
->
tun_open
==
-1
)
options
->
tun_open
=
SSH_TUNMODE_NO
;
if
(
options
->
tun_local
==
-1
)
options
->
tun_local
=
SSH_TUNID_ANY
;
if
(
options
->
tun_remote
==
-1
)
options
->
tun_remote
=
SSH_TUNID_ANY
;
if
(
options
->
permit_local_command
==
-1
)
options
->
permit_local_command
=
0
;
if
(
options
->
visual_host_key
==
-1
)
options
->
visual_host_key
=
0
;
if
(
options
->
ip_qos_interactive
==
-1
)
options
->
ip_qos_interactive
=
IPTOS_DSCP_AF21
;
if
(
options
->
ip_qos_bulk
==
-1
)
options
->
ip_qos_bulk
=
IPTOS_DSCP_CS1
;
if
(
options
->
request_tty
==
-1
)
options
->
request_tty
=
REQUEST_TTY_AUTO
;
if
(
options
->
session_type
==
-1
)
options
->
session_type
=
SESSION_TYPE_DEFAULT
;
if
(
options
->
stdin_null
==
-1
)
options
->
stdin_null
=
0
;
if
(
options
->
fork_after_authentication
==
-1
)
options
->
fork_after_authentication
=
0
;
if
(
options
->
proxy_use_fdpass
==
-1
)
options
->
proxy_use_fdpass
=
0
;
if
(
options
->
canonicalize_max_dots
==
-1
)
options
->
canonicalize_max_dots
=
1
;
if
(
options
->
canonicalize_fallback_local
==
-1
)
options
->
canonicalize_fallback_local
=
1
;
if
(
options
->
canonicalize_hostname
==
-1
)
options
->
canonicalize_hostname
=
SSH_CANONICALISE_NO
;
if
(
options
->
fingerprint_hash
==
-1
)
options
->
fingerprint_hash
=
SSH_FP_HASH_DEFAULT
;
#ifdef ENABLE_SK_INTERNAL
if
(
options
->
sk_provider
==
NULL
)
options
->
sk_provider
=
xstrdup
(
"internal"
);
#else
if
(
options
->
sk_provider
==
NULL
)
options
->
sk_provider
=
xstrdup
(
"$SSH_SK_PROVIDER"
);
#endif
if
(
options
->
required_rsa_size
==
-1
)
options
->
required_rsa_size
=
SSH_RSA_MINIMUM_MODULUS_SIZE
;
if
(
options
->
enable_escape_commandline
==
-1
)
options
->
enable_escape_commandline
=
0
;
/* Expand KEX name lists */
all_cipher
=
cipher_alg_list
(
','
,
0
);
all_mac
=
mac_alg_list
(
','
);
all_kex
=
kex_alg_list
(
','
);
all_key
=
sshkey_alg_list
(
0
,
0
,
1
,
','
);
all_sig
=
sshkey_alg_list
(
0
,
1
,
1
,
','
);
/* remove unsupported algos from default lists */
def_cipher
=
match_filter_allowlist
(
KEX_CLIENT_ENCRYPT
,
all_cipher
);
def_mac
=
match_filter_allowlist
(
KEX_CLIENT_MAC
,
all_mac
);
def_kex
=
match_filter_allowlist
(
KEX_CLIENT_KEX
,
all_kex
);
def_key
=
match_filter_allowlist
(
KEX_DEFAULT_PK_ALG
,
all_key
);
def_sig
=
match_filter_allowlist
(
SSH_ALLOWED_CA_SIGALGS
,
all_sig
);
#define ASSEMBLE(what, defaults, all) \
do { \
if ((r = kex_assemble_names(&options->what, \
defaults, all)) != 0) { \
error_fr(r, "%s", #what); \
goto fail; \
} \
} while (0)
ASSEMBLE
(
ciphers
,
def_cipher
,
all_cipher
);
ASSEMBLE
(
macs
,
def_mac
,
all_mac
);
ASSEMBLE
(
kex_algorithms
,
def_kex
,
all_kex
);
ASSEMBLE
(
hostbased_accepted_algos
,
def_key
,
all_key
);
ASSEMBLE
(
pubkey_accepted_algos
,
def_key
,
all_key
);
ASSEMBLE
(
ca_sign_algorithms
,
def_sig
,
all_sig
);
#undef ASSEMBLE
#define CLEAR_ON_NONE(v) \
do { \
if (option_clear_or_none(v)) { \
free(v); \
v = NULL; \
} \
} while(0)
CLEAR_ON_NONE
(
options
->
local_command
);
CLEAR_ON_NONE
(
options
->
remote_command
);
CLEAR_ON_NONE
(
options
->
proxy_command
);
CLEAR_ON_NONE
(
options
->
control_path
);
CLEAR_ON_NONE
(
options
->
revoked_host_keys
);
CLEAR_ON_NONE
(
options
->
pkcs11_provider
);
CLEAR_ON_NONE
(
options
->
sk_provider
);
CLEAR_ON_NONE
(
options
->
known_hosts_command
);
if
(
options
->
jump_host
!=
NULL
&&
strcmp
(
options
->
jump_host
,
"none"
)
==
0
&&
options
->
jump_port
==
0
&&
options
->
jump_user
==
NULL
)
{
free
(
options
->
jump_host
);
options
->
jump_host
=
NULL
;
}
if
(
options
->
num_permitted_cnames
==
1
&&
!
config_has_permitted_cnames
(
options
))
{
/* clean up CanonicalizePermittedCNAMEs=none */
free
(
options
->
permitted_cnames
[
0
].
source_list
);
free
(
options
->
permitted_cnames
[
0
].
target_list
);
memset
(
options
->
permitted_cnames
,
'\0'
,
sizeof
(
*
options
->
permitted_cnames
));
options
->
num_permitted_cnames
=
0
;
}
/* options->identity_agent distinguishes NULL from 'none' */
/* options->user will be set in the main program if appropriate */
/* options->hostname will be set in the main program if appropriate */
/* options->host_key_alias should not be set by default */
/* options->preferred_authentications will be set in ssh */
/* success */
ret
=
0
;
fail
:
free
(
all_cipher
);
free
(
all_mac
);
free
(
all_kex
);
free
(
all_key
);
free
(
all_sig
);
free
(
def_cipher
);
free
(
def_mac
);
free
(
def_kex
);
free
(
def_key
);
free
(
def_sig
);
return
ret
;
}
void
free_options
(
Options
*
o
)
{
int
i
;
if
(
o
==
NULL
)
return
;
#define FREE_ARRAY(type, n, a) \
do { \
type _i; \
for (_i = 0; _i < (n); _i++) \
free((a)[_i]); \
} while (0)
free
(
o
->
forward_agent_sock_path
);
free
(
o
->
xauth_location
);
FREE_ARRAY
(
u_int
,
o
->
num_log_verbose
,
o
->
log_verbose
);
free
(
o
->
log_verbose
);
free
(
o
->
ciphers
);
free
(
o
->
macs
);
free
(
o
->
hostkeyalgorithms
);
free
(
o
->
kex_algorithms
);
free
(
o
->
ca_sign_algorithms
);
free
(
o
->
hostname
);
free
(
o
->
host_key_alias
);
free
(
o
->
proxy_command
);
free
(
o
->
user
);
FREE_ARRAY
(
u_int
,
o
->
num_system_hostfiles
,
o
->
system_hostfiles
);
FREE_ARRAY
(
u_int
,
o
->
num_user_hostfiles
,
o
->
user_hostfiles
);
free
(
o
->
preferred_authentications
);
free
(
o
->
bind_address
);
free
(
o
->
bind_interface
);
free
(
o
->
pkcs11_provider
);
free
(
o
->
sk_provider
);
for
(
i
=
0
;
i
<
o
->
num_identity_files
;
i
++
)
{
free
(
o
->
identity_files
[
i
]);
sshkey_free
(
o
->
identity_keys
[
i
]);
}
for
(
i
=
0
;
i
<
o
->
num_certificate_files
;
i
++
)
{
free
(
o
->
certificate_files
[
i
]);
sshkey_free
(
o
->
certificates
[
i
]);
}
free
(
o
->
identity_agent
);
for
(
i
=
0
;
i
<
o
->
num_local_forwards
;
i
++
)
{
free
(
o
->
local_forwards
[
i
].
listen_host
);
free
(
o
->
local_forwards
[
i
].
listen_path
);
free
(
o
->
local_forwards
[
i
].
connect_host
);
free
(
o
->
local_forwards
[
i
].
connect_path
);
}
free
(
o
->
local_forwards
);
for
(
i
=
0
;
i
<
o
->
num_remote_forwards
;
i
++
)
{
free
(
o
->
remote_forwards
[
i
].
listen_host
);
free
(
o
->
remote_forwards
[
i
].
listen_path
);
free
(
o
->
remote_forwards
[
i
].
connect_host
);
free
(
o
->
remote_forwards
[
i
].
connect_path
);
}
free
(
o
->
remote_forwards
);
free
(
o
->
stdio_forward_host
);
FREE_ARRAY
(
u_int
,
o
->
num_send_env
,
o
->
send_env
);
free
(
o
->
send_env
);
FREE_ARRAY
(
u_int
,
o
->
num_setenv
,
o
->
setenv
);
free
(
o
->
setenv
);
free
(
o
->
control_path
);
free
(
o
->
local_command
);
free
(
o
->
remote_command
);
FREE_ARRAY
(
int
,
o
->
num_canonical_domains
,
o
->
canonical_domains
);
for
(
i
=
0
;
i
<
o
->
num_permitted_cnames
;
i
++
)
{
free
(
o
->
permitted_cnames
[
i
].
source_list
);
free
(
o
->
permitted_cnames
[
i
].
target_list
);
}
free
(
o
->
revoked_host_keys
);
free
(
o
->
hostbased_accepted_algos
);
free
(
o
->
pubkey_accepted_algos
);
free
(
o
->
jump_user
);
free
(
o
->
jump_host
);
free
(
o
->
jump_extra
);
free
(
o
->
ignored_unknown
);
explicit_bzero
(
o
,
sizeof
(
*
o
));
#undef FREE_ARRAY
}
struct
fwdarg
{
char
*
arg
;
int
ispath
;
};
/*
* parse_fwd_field
* parses the next field in a port forwarding specification.
* sets fwd to the parsed field and advances p past the colon
* or sets it to NULL at end of string.
* returns 0 on success, else non-zero.
*/
static
int
parse_fwd_field
(
char
**
p
,
struct
fwdarg
*
fwd
)
{
char
*
ep
,
*
cp
=
*
p
;
int
ispath
=
0
;
if
(
*
cp
==
'\0'
)
{
*
p
=
NULL
;
return
-1
;
/* end of string */
}
/*
* A field escaped with square brackets is used literally.
* XXX - allow ']' to be escaped via backslash?
*/
if
(
*
cp
==
'['
)
{
/* find matching ']' */
for
(
ep
=
cp
+
1
;
*
ep
!=
']'
&&
*
ep
!=
'\0'
;
ep
++
)
{
if
(
*
ep
==
'/'
)
ispath
=
1
;
}
/* no matching ']' or not at end of field. */
if
(
ep
[
0
]
!=
']'
||
(
ep
[
1
]
!=
':'
&&
ep
[
1
]
!=
'\0'
))
return
-1
;
/* NUL terminate the field and advance p past the colon */
*
ep
++
=
'\0'
;
if
(
*
ep
!=
'\0'
)
*
ep
++
=
'\0'
;
fwd
->
arg
=
cp
+
1
;
fwd
->
ispath
=
ispath
;
*
p
=
ep
;
return
0
;
}
for
(
cp
=
*
p
;
*
cp
!=
'\0'
;
cp
++
)
{
switch
(
*
cp
)
{
case
'\\'
:
memmove
(
cp
,
cp
+
1
,
strlen
(
cp
+
1
)
+
1
);
if
(
*
cp
==
'\0'
)
return
-1
;
break
;
case
'/'
:
ispath
=
1
;
break
;
case
':'
:
*
cp
++
=
'\0'
;
goto
done
;
}
}
done
:
fwd
->
arg
=
*
p
;
fwd
->
ispath
=
ispath
;
*
p
=
cp
;
return
0
;
}
/*
* parse_forward
* parses a string containing a port forwarding specification of the form:
* dynamicfwd == 0
* [listenhost:]listenport|listenpath:connecthost:connectport|connectpath
* listenpath:connectpath
* dynamicfwd == 1
* [listenhost:]listenport
* returns number of arguments parsed or zero on error
*/
int
parse_forward
(
struct
Forward
*
fwd
,
const
char
*
fwdspec
,
int
dynamicfwd
,
int
remotefwd
)
{
struct
fwdarg
fwdargs
[
4
];
char
*
p
,
*
cp
;
int
i
,
err
;
memset
(
fwd
,
0
,
sizeof
(
*
fwd
));
memset
(
fwdargs
,
0
,
sizeof
(
fwdargs
));
/*
* We expand environment variables before checking if we think they're
* paths so that if ${VAR} expands to a fully qualified path it is
* treated as a path.
*/
cp
=
p
=
dollar_expand
(
&
err
,
fwdspec
);
if
(
p
==
NULL
||
err
)
return
0
;
/* skip leading spaces */
while
(
isspace
((
u_char
)
*
cp
))
cp
++
;
for
(
i
=
0
;
i
<
4
;
++
i
)
{
if
(
parse_fwd_field
(
&
cp
,
&
fwdargs
[
i
])
!=
0
)
break
;
}
/* Check for trailing garbage */
if
(
cp
!=
NULL
&&
*
cp
!=
'\0'
)
{
i
=
0
;
/* failure */
}
switch
(
i
)
{
case
1
:
if
(
fwdargs
[
0
].
ispath
)
{
fwd
->
listen_path
=
xstrdup
(
fwdargs
[
0
].
arg
);
fwd
->
listen_port
=
PORT_STREAMLOCAL
;
}
else
{
fwd
->
listen_host
=
NULL
;
fwd
->
listen_port
=
a2port
(
fwdargs
[
0
].
arg
);
}
fwd
->
connect_host
=
xstrdup
(
"socks"
);
break
;
case
2
:
if
(
fwdargs
[
0
].
ispath
&&
fwdargs
[
1
].
ispath
)
{
fwd
->
listen_path
=
xstrdup
(
fwdargs
[
0
].
arg
);
fwd
->
listen_port
=
PORT_STREAMLOCAL
;
fwd
->
connect_path
=
xstrdup
(
fwdargs
[
1
].
arg
);
fwd
->
connect_port
=
PORT_STREAMLOCAL
;
}
else
if
(
fwdargs
[
1
].
ispath
)
{
fwd
->
listen_host
=
NULL
;
fwd
->
listen_port
=
a2port
(
fwdargs
[
0
].
arg
);
fwd
->
connect_path
=
xstrdup
(
fwdargs
[
1
].
arg
);
fwd
->
connect_port
=
PORT_STREAMLOCAL
;
}
else
{
fwd
->
listen_host
=
xstrdup
(
fwdargs
[
0
].
arg
);
fwd
->
listen_port
=
a2port
(
fwdargs
[
1
].
arg
);
fwd
->
connect_host
=
xstrdup
(
"socks"
);
}
break
;
case
3
:
if
(
fwdargs
[
0
].
ispath
)
{
fwd
->
listen_path
=
xstrdup
(
fwdargs
[
0
].
arg
);
fwd
->
listen_port
=
PORT_STREAMLOCAL
;
fwd
->
connect_host
=
xstrdup
(
fwdargs
[
1
].
arg
);
fwd
->
connect_port
=
a2port
(
fwdargs
[
2
].
arg
);
}
else
if
(
fwdargs
[
2
].
ispath
)
{
fwd
->
listen_host
=
xstrdup
(
fwdargs
[
0
].
arg
);
fwd
->
listen_port
=
a2port
(
fwdargs
[
1
].
arg
);
fwd
->
connect_path
=
xstrdup
(
fwdargs
[
2
].
arg
);
fwd
->
connect_port
=
PORT_STREAMLOCAL
;
}
else
{
fwd
->
listen_host
=
NULL
;
fwd
->
listen_port
=
a2port
(
fwdargs
[
0
].
arg
);
fwd
->
connect_host
=
xstrdup
(
fwdargs
[
1
].
arg
);
fwd
->
connect_port
=
a2port
(
fwdargs
[
2
].
arg
);
}
break
;
case
4
:
fwd
->
listen_host
=
xstrdup
(
fwdargs
[
0
].
arg
);
fwd
->
listen_port
=
a2port
(
fwdargs
[
1
].
arg
);
fwd
->
connect_host
=
xstrdup
(
fwdargs
[
2
].
arg
);
fwd
->
connect_port
=
a2port
(
fwdargs
[
3
].
arg
);
break
;
default
:
i
=
0
;
/* failure */
}
free
(
p
);
if
(
dynamicfwd
)
{
if
(
!
(
i
==
1
||
i
==
2
))
goto
fail_free
;
}
else
{
if
(
!
(
i
==
3
||
i
==
4
))
{
if
(
fwd
->
connect_path
==
NULL
&&
fwd
->
listen_path
==
NULL
)
goto
fail_free
;
}
if
(
fwd
->
connect_port
<=
0
&&
fwd
->
connect_path
==
NULL
)
goto
fail_free
;
}
if
((
fwd
->
listen_port
<
0
&&
fwd
->
listen_path
==
NULL
)
||
(
!
remotefwd
&&
fwd
->
listen_port
==
0
))
goto
fail_free
;
if
(
fwd
->
connect_host
!=
NULL
&&
strlen
(
fwd
->
connect_host
)
>=
NI_MAXHOST
)
goto
fail_free
;
/*
* XXX - if connecting to a remote socket, max sun len may not
* match this host
*/
if
(
fwd
->
connect_path
!=
NULL
&&
strlen
(
fwd
->
connect_path
)
>=
PATH_MAX_SUN
)
goto
fail_free
;
if
(
fwd
->
listen_host
!=
NULL
&&
strlen
(
fwd
->
listen_host
)
>=
NI_MAXHOST
)
goto
fail_free
;
if
(
fwd
->
listen_path
!=
NULL
&&
strlen
(
fwd
->
listen_path
)
>=
PATH_MAX_SUN
)
goto
fail_free
;
return
(
i
);
fail_free
:
free
(
fwd
->
connect_host
);
fwd
->
connect_host
=
NULL
;
free
(
fwd
->
connect_path
);
fwd
->
connect_path
=
NULL
;
free
(
fwd
->
listen_host
);
fwd
->
listen_host
=
NULL
;
free
(
fwd
->
listen_path
);
fwd
->
listen_path
=
NULL
;
return
(
0
);
}
int
parse_jump
(
const
char
*
s
,
Options
*
o
,
int
active
)
{
char
*
orig
,
*
sdup
,
*
cp
;
char
*
host
=
NULL
,
*
user
=
NULL
;
int
r
,
ret
=
-1
,
port
=
-1
,
first
;
active
&=
o
->
proxy_command
==
NULL
&&
o
->
jump_host
==
NULL
;
orig
=
sdup
=
xstrdup
(
s
);
/* Remove comment and trailing whitespace */
if
((
cp
=
strchr
(
orig
,
'#'
))
!=
NULL
)
*
cp
=
'\0'
;
rtrim
(
orig
);
first
=
active
;
do
{
if
(
strcasecmp
(
s
,
"none"
)
==
0
)
break
;
if
((
cp
=
strrchr
(
sdup
,
','
))
==
NULL
)
cp
=
sdup
;
/* last */
else
*
cp
++
=
'\0'
;
if
(
first
)
{
/* First argument and configuration is active */
r
=
parse_ssh_uri
(
cp
,
&
user
,
&
host
,
&
port
);
if
(
r
==
-1
||
(
r
==
1
&&
parse_user_host_port
(
cp
,
&
user
,
&
host
,
&
port
)
!=
0
))
goto
out
;
}
else
{
/* Subsequent argument or inactive configuration */
r
=
parse_ssh_uri
(
cp
,
NULL
,
NULL
,
NULL
);
if
(
r
==
-1
||
(
r
==
1
&&
parse_user_host_port
(
cp
,
NULL
,
NULL
,
NULL
)
!=
0
))
goto
out
;
}
first
=
0
;
/* only check syntax for subsequent hosts */
}
while
(
cp
!=
sdup
);
/* success */
if
(
active
)
{
if
(
strcasecmp
(
s
,
"none"
)
==
0
)
{
o
->
jump_host
=
xstrdup
(
"none"
);
o
->
jump_port
=
0
;
}
else
{
o
->
jump_user
=
user
;
o
->
jump_host
=
host
;
o
->
jump_port
=
port
;
o
->
proxy_command
=
xstrdup
(
"none"
);
user
=
host
=
NULL
;
if
((
cp
=
strrchr
(
s
,
','
))
!=
NULL
&&
cp
!=
s
)
{
o
->
jump_extra
=
xstrdup
(
s
);
o
->
jump_extra
[
cp
-
s
]
=
'\0'
;
}
}
}
ret
=
0
;
out
:
free
(
orig
);
free
(
user
);
free
(
host
);
return
ret
;
}
int
parse_ssh_uri
(
const
char
*
uri
,
char
**
userp
,
char
**
hostp
,
int
*
portp
)
{
char
*
user
=
NULL
,
*
host
=
NULL
,
*
path
=
NULL
;
int
r
,
port
;
r
=
parse_uri
(
"ssh"
,
uri
,
&
user
,
&
host
,
&
port
,
&
path
);
if
(
r
==
0
&&
path
!=
NULL
)
r
=
-1
;
/* path not allowed */
if
(
r
==
0
)
{
if
(
userp
!=
NULL
)
{
*
userp
=
user
;
user
=
NULL
;
}
if
(
hostp
!=
NULL
)
{
*
hostp
=
host
;
host
=
NULL
;
}
if
(
portp
!=
NULL
)
*
portp
=
port
;
}
free
(
user
);
free
(
host
);
free
(
path
);
return
r
;
}
/* XXX the following is a near-vebatim copy from servconf.c; refactor */
static
const
char
*
fmt_multistate_int
(
int
val
,
const
struct
multistate
*
m
)
{
u_int
i
;
for
(
i
=
0
;
m
[
i
].
key
!=
NULL
;
i
++
)
{
if
(
m
[
i
].
value
==
val
)
return
m
[
i
].
key
;
}
return
"UNKNOWN"
;
}
static
const
char
*
fmt_intarg
(
OpCodes
code
,
int
val
)
{
if
(
val
==
-1
)
return
"unset"
;
switch
(
code
)
{
case
oAddressFamily
:
return
fmt_multistate_int
(
val
,
multistate_addressfamily
);
case
oVerifyHostKeyDNS
:
case
oUpdateHostkeys
:
return
fmt_multistate_int
(
val
,
multistate_yesnoask
);
case
oStrictHostKeyChecking
:
return
fmt_multistate_int
(
val
,
multistate_strict_hostkey
);
case
oControlMaster
:
return
fmt_multistate_int
(
val
,
multistate_controlmaster
);
case
oTunnel
:
return
fmt_multistate_int
(
val
,
multistate_tunnel
);
case
oRequestTTY
:
return
fmt_multistate_int
(
val
,
multistate_requesttty
);
case
oSessionType
:
return
fmt_multistate_int
(
val
,
multistate_sessiontype
);
case
oCanonicalizeHostname
:
return
fmt_multistate_int
(
val
,
multistate_canonicalizehostname
);
case
oAddKeysToAgent
:
return
fmt_multistate_int
(
val
,
multistate_yesnoaskconfirm
);
case
oPubkeyAuthentication
:
return
fmt_multistate_int
(
val
,
multistate_pubkey_auth
);
case
oFingerprintHash
:
return
ssh_digest_alg_name
(
val
);
default
:
switch
(
val
)
{
case
0
:
return
"no"
;
case
1
:
return
"yes"
;
default
:
return
"UNKNOWN"
;
}
}
}
static
const
char
*
lookup_opcode_name
(
OpCodes
code
)
{
u_int
i
;
for
(
i
=
0
;
keywords
[
i
].
name
!=
NULL
;
i
++
)
if
(
keywords
[
i
].
opcode
==
code
)
return
(
keywords
[
i
].
name
);
return
"UNKNOWN"
;
}
static
void
dump_cfg_int
(
OpCodes
code
,
int
val
)
{
printf
(
"%s %d
\n
"
,
lookup_opcode_name
(
code
),
val
);
}
static
void
dump_cfg_fmtint
(
OpCodes
code
,
int
val
)
{
printf
(
"%s %s
\n
"
,
lookup_opcode_name
(
code
),
fmt_intarg
(
code
,
val
));
}
static
void
dump_cfg_string
(
OpCodes
code
,
const
char
*
val
)
{
if
(
val
==
NULL
)
return
;
printf
(
"%s %s
\n
"
,
lookup_opcode_name
(
code
),
val
);
}
static
void
dump_cfg_strarray
(
OpCodes
code
,
u_int
count
,
char
**
vals
)
{
u_int
i
;
for
(
i
=
0
;
i
<
count
;
i
++
)
printf
(
"%s %s
\n
"
,
lookup_opcode_name
(
code
),
vals
[
i
]);
}
static
void
dump_cfg_strarray_oneline
(
OpCodes
code
,
u_int
count
,
char
**
vals
)
{
u_int
i
;
printf
(
"%s"
,
lookup_opcode_name
(
code
));
if
(
count
==
0
)
printf
(
" none"
);
for
(
i
=
0
;
i
<
count
;
i
++
)
printf
(
" %s"
,
vals
[
i
]);
printf
(
"
\n
"
);
}
static
void
dump_cfg_forwards
(
OpCodes
code
,
u_int
count
,
const
struct
Forward
*
fwds
)
{
const
struct
Forward
*
fwd
;
u_int
i
;
/* oDynamicForward */
for
(
i
=
0
;
i
<
count
;
i
++
)
{
fwd
=
&
fwds
[
i
];
if
(
code
==
oDynamicForward
&&
fwd
->
connect_host
!=
NULL
&&
strcmp
(
fwd
->
connect_host
,
"socks"
)
!=
0
)
continue
;
if
(
code
==
oLocalForward
&&
fwd
->
connect_host
!=
NULL
&&
strcmp
(
fwd
->
connect_host
,
"socks"
)
==
0
)
continue
;
printf
(
"%s"
,
lookup_opcode_name
(
code
));
if
(
fwd
->
listen_port
==
PORT_STREAMLOCAL
)
printf
(
" %s"
,
fwd
->
listen_path
);
else
if
(
fwd
->
listen_host
==
NULL
)
printf
(
" %d"
,
fwd
->
listen_port
);
else
{
printf
(
" [%s]:%d"
,
fwd
->
listen_host
,
fwd
->
listen_port
);
}
if
(
code
!=
oDynamicForward
)
{
if
(
fwd
->
connect_port
==
PORT_STREAMLOCAL
)
printf
(
" %s"
,
fwd
->
connect_path
);
else
if
(
fwd
->
connect_host
==
NULL
)
printf
(
" %d"
,
fwd
->
connect_port
);
else
{
printf
(
" [%s]:%d"
,
fwd
->
connect_host
,
fwd
->
connect_port
);
}
}
printf
(
"
\n
"
);
}
}
void
dump_client_config
(
Options
*
o
,
const
char
*
host
)
{
int
i
,
r
;
char
buf
[
8
],
*
all_key
;
/*
* Expand HostKeyAlgorithms name lists. This isn't handled in
* fill_default_options() like the other algorithm lists because
* the host key algorithms are by default dynamically chosen based
* on the host's keys found in known_hosts.
*/
all_key
=
sshkey_alg_list
(
0
,
0
,
1
,
','
);
if
((
r
=
kex_assemble_names
(
&
o
->
hostkeyalgorithms
,
kex_default_pk_alg
(),
all_key
))
!=
0
)
fatal_fr
(
r
,
"expand HostKeyAlgorithms"
);
free
(
all_key
);
/* Most interesting options first: user, host, port */
dump_cfg_string
(
oHost
,
o
->
host_arg
);
dump_cfg_string
(
oUser
,
o
->
user
);
dump_cfg_string
(
oHostname
,
host
);
dump_cfg_int
(
oPort
,
o
->
port
);
/* Flag options */
dump_cfg_fmtint
(
oAddressFamily
,
o
->
address_family
);
dump_cfg_fmtint
(
oBatchMode
,
o
->
batch_mode
);
dump_cfg_fmtint
(
oCanonicalizeFallbackLocal
,
o
->
canonicalize_fallback_local
);
dump_cfg_fmtint
(
oCanonicalizeHostname
,
o
->
canonicalize_hostname
);
dump_cfg_fmtint
(
oCheckHostIP
,
o
->
check_host_ip
);
dump_cfg_fmtint
(
oCompression
,
o
->
compression
);
dump_cfg_fmtint
(
oControlMaster
,
o
->
control_master
);
dump_cfg_fmtint
(
oEnableSSHKeysign
,
o
->
enable_ssh_keysign
);
dump_cfg_fmtint
(
oClearAllForwardings
,
o
->
clear_forwardings
);
dump_cfg_fmtint
(
oExitOnForwardFailure
,
o
->
exit_on_forward_failure
);
dump_cfg_fmtint
(
oFingerprintHash
,
o
->
fingerprint_hash
);
dump_cfg_fmtint
(
oForwardX11
,
o
->
forward_x11
);
dump_cfg_fmtint
(
oForwardX11Trusted
,
o
->
forward_x11_trusted
);
dump_cfg_fmtint
(
oGatewayPorts
,
o
->
fwd_opts
.
gateway_ports
);
#ifdef GSSAPI
dump_cfg_fmtint
(
oGssAuthentication
,
o
->
gss_authentication
);
dump_cfg_fmtint
(
oGssDelegateCreds
,
o
->
gss_deleg_creds
);
#endif
/* GSSAPI */
dump_cfg_fmtint
(
oHashKnownHosts
,
o
->
hash_known_hosts
);
dump_cfg_fmtint
(
oHostbasedAuthentication
,
o
->
hostbased_authentication
);
dump_cfg_fmtint
(
oIdentitiesOnly
,
o
->
identities_only
);
dump_cfg_fmtint
(
oKbdInteractiveAuthentication
,
o
->
kbd_interactive_authentication
);
dump_cfg_fmtint
(
oNoHostAuthenticationForLocalhost
,
o
->
no_host_authentication_for_localhost
);
dump_cfg_fmtint
(
oPasswordAuthentication
,
o
->
password_authentication
);
dump_cfg_fmtint
(
oPermitLocalCommand
,
o
->
permit_local_command
);
dump_cfg_fmtint
(
oProxyUseFdpass
,
o
->
proxy_use_fdpass
);
dump_cfg_fmtint
(
oPubkeyAuthentication
,
o
->
pubkey_authentication
);
dump_cfg_fmtint
(
oRequestTTY
,
o
->
request_tty
);
dump_cfg_fmtint
(
oSessionType
,
o
->
session_type
);
dump_cfg_fmtint
(
oStdinNull
,
o
->
stdin_null
);
dump_cfg_fmtint
(
oForkAfterAuthentication
,
o
->
fork_after_authentication
);
dump_cfg_fmtint
(
oStreamLocalBindUnlink
,
o
->
fwd_opts
.
streamlocal_bind_unlink
);
dump_cfg_fmtint
(
oStrictHostKeyChecking
,
o
->
strict_host_key_checking
);
dump_cfg_fmtint
(
oTCPKeepAlive
,
o
->
tcp_keep_alive
);
dump_cfg_fmtint
(
oTunnel
,
o
->
tun_open
);
dump_cfg_fmtint
(
oVerifyHostKeyDNS
,
o
->
verify_host_key_dns
);
dump_cfg_fmtint
(
oVisualHostKey
,
o
->
visual_host_key
);
dump_cfg_fmtint
(
oUpdateHostkeys
,
o
->
update_hostkeys
);
dump_cfg_fmtint
(
oEnableEscapeCommandline
,
o
->
enable_escape_commandline
);
/* Integer options */
dump_cfg_int
(
oCanonicalizeMaxDots
,
o
->
canonicalize_max_dots
);
dump_cfg_int
(
oConnectionAttempts
,
o
->
connection_attempts
);
dump_cfg_int
(
oForwardX11Timeout
,
o
->
forward_x11_timeout
);
dump_cfg_int
(
oNumberOfPasswordPrompts
,
o
->
number_of_password_prompts
);
dump_cfg_int
(
oServerAliveCountMax
,
o
->
server_alive_count_max
);
dump_cfg_int
(
oServerAliveInterval
,
o
->
server_alive_interval
);
dump_cfg_int
(
oRequiredRSASize
,
o
->
required_rsa_size
);
/* String options */
dump_cfg_string
(
oBindAddress
,
o
->
bind_address
);
dump_cfg_string
(
oBindInterface
,
o
->
bind_interface
);
dump_cfg_string
(
oCiphers
,
o
->
ciphers
);
dump_cfg_string
(
oControlPath
,
o
->
control_path
);
dump_cfg_string
(
oHostKeyAlgorithms
,
o
->
hostkeyalgorithms
);
dump_cfg_string
(
oHostKeyAlias
,
o
->
host_key_alias
);
dump_cfg_string
(
oHostbasedAcceptedAlgorithms
,
o
->
hostbased_accepted_algos
);
dump_cfg_string
(
oIdentityAgent
,
o
->
identity_agent
);
dump_cfg_string
(
oIgnoreUnknown
,
o
->
ignored_unknown
);
dump_cfg_string
(
oKbdInteractiveDevices
,
o
->
kbd_interactive_devices
);
dump_cfg_string
(
oKexAlgorithms
,
o
->
kex_algorithms
);
dump_cfg_string
(
oCASignatureAlgorithms
,
o
->
ca_sign_algorithms
);
dump_cfg_string
(
oLocalCommand
,
o
->
local_command
);
dump_cfg_string
(
oRemoteCommand
,
o
->
remote_command
);
dump_cfg_string
(
oLogLevel
,
log_level_name
(
o
->
log_level
));
dump_cfg_string
(
oMacs
,
o
->
macs
);
#ifdef ENABLE_PKCS11
dump_cfg_string
(
oPKCS11Provider
,
o
->
pkcs11_provider
);
#endif
dump_cfg_string
(
oSecurityKeyProvider
,
o
->
sk_provider
);
dump_cfg_string
(
oPreferredAuthentications
,
o
->
preferred_authentications
);
dump_cfg_string
(
oPubkeyAcceptedAlgorithms
,
o
->
pubkey_accepted_algos
);
dump_cfg_string
(
oRevokedHostKeys
,
o
->
revoked_host_keys
);
dump_cfg_string
(
oXAuthLocation
,
o
->
xauth_location
);
dump_cfg_string
(
oKnownHostsCommand
,
o
->
known_hosts_command
);
dump_cfg_string
(
oTag
,
o
->
tag
);
/* Forwards */
dump_cfg_forwards
(
oDynamicForward
,
o
->
num_local_forwards
,
o
->
local_forwards
);
dump_cfg_forwards
(
oLocalForward
,
o
->
num_local_forwards
,
o
->
local_forwards
);
dump_cfg_forwards
(
oRemoteForward
,
o
->
num_remote_forwards
,
o
->
remote_forwards
);
/* String array options */
dump_cfg_strarray
(
oIdentityFile
,
o
->
num_identity_files
,
o
->
identity_files
);
dump_cfg_strarray_oneline
(
oCanonicalDomains
,
o
->
num_canonical_domains
,
o
->
canonical_domains
);
dump_cfg_strarray
(
oCertificateFile
,
o
->
num_certificate_files
,
o
->
certificate_files
);
dump_cfg_strarray_oneline
(
oGlobalKnownHostsFile
,
o
->
num_system_hostfiles
,
o
->
system_hostfiles
);
dump_cfg_strarray_oneline
(
oUserKnownHostsFile
,
o
->
num_user_hostfiles
,
o
->
user_hostfiles
);
dump_cfg_strarray
(
oSendEnv
,
o
->
num_send_env
,
o
->
send_env
);
dump_cfg_strarray
(
oSetEnv
,
o
->
num_setenv
,
o
->
setenv
);
dump_cfg_strarray_oneline
(
oLogVerbose
,
o
->
num_log_verbose
,
o
->
log_verbose
);
/* Special cases */
/* PermitRemoteOpen */
if
(
o
->
num_permitted_remote_opens
==
0
)
printf
(
"%s any
\n
"
,
lookup_opcode_name
(
oPermitRemoteOpen
));
else
dump_cfg_strarray_oneline
(
oPermitRemoteOpen
,
o
->
num_permitted_remote_opens
,
o
->
permitted_remote_opens
);
/* AddKeysToAgent */
if
(
o
->
add_keys_to_agent_lifespan
<=
0
)
dump_cfg_fmtint
(
oAddKeysToAgent
,
o
->
add_keys_to_agent
);
else
{
printf
(
"addkeystoagent%s %d
\n
"
,
o
->
add_keys_to_agent
==
3
?
" confirm"
:
""
,
o
->
add_keys_to_agent_lifespan
);
}
/* oForwardAgent */
if
(
o
->
forward_agent_sock_path
==
NULL
)
dump_cfg_fmtint
(
oForwardAgent
,
o
->
forward_agent
);
else
dump_cfg_string
(
oForwardAgent
,
o
->
forward_agent_sock_path
);
/* oConnectTimeout */
if
(
o
->
connection_timeout
==
-1
)
printf
(
"connecttimeout none
\n
"
);
else
dump_cfg_int
(
oConnectTimeout
,
o
->
connection_timeout
);
/* oTunnelDevice */
printf
(
"tunneldevice"
);
if
(
o
->
tun_local
==
SSH_TUNID_ANY
)
printf
(
" any"
);
else
printf
(
" %d"
,
o
->
tun_local
);
if
(
o
->
tun_remote
==
SSH_TUNID_ANY
)
printf
(
":any"
);
else
printf
(
":%d"
,
o
->
tun_remote
);
printf
(
"
\n
"
);
/* oCanonicalizePermittedCNAMEs */
printf
(
"canonicalizePermittedcnames"
);
if
(
o
->
num_permitted_cnames
==
0
)
printf
(
" none"
);
for
(
i
=
0
;
i
<
o
->
num_permitted_cnames
;
i
++
)
{
printf
(
" %s:%s"
,
o
->
permitted_cnames
[
i
].
source_list
,
o
->
permitted_cnames
[
i
].
target_list
);
}
printf
(
"
\n
"
);
/* oControlPersist */
if
(
o
->
control_persist
==
0
||
o
->
control_persist_timeout
==
0
)
dump_cfg_fmtint
(
oControlPersist
,
o
->
control_persist
);
else
dump_cfg_int
(
oControlPersist
,
o
->
control_persist_timeout
);
/* oEscapeChar */
if
(
o
->
escape_char
==
SSH_ESCAPECHAR_NONE
)
printf
(
"escapechar none
\n
"
);
else
{
vis
(
buf
,
o
->
escape_char
,
VIS_WHITE
,
0
);
printf
(
"escapechar %s
\n
"
,
buf
);
}
/* oIPQoS */
printf
(
"ipqos %s "
,
iptos2str
(
o
->
ip_qos_interactive
));
printf
(
"%s
\n
"
,
iptos2str
(
o
->
ip_qos_bulk
));
/* oRekeyLimit */
printf
(
"rekeylimit %llu %d
\n
"
,
(
unsigned
long
long
)
o
->
rekey_limit
,
o
->
rekey_interval
);
/* oStreamLocalBindMask */
printf
(
"streamlocalbindmask 0%o
\n
"
,
o
->
fwd_opts
.
streamlocal_bind_mask
);
/* oLogFacility */
printf
(
"syslogfacility %s
\n
"
,
log_facility_name
(
o
->
log_facility
));
/* oProxyCommand / oProxyJump */
if
(
o
->
jump_host
==
NULL
)
dump_cfg_string
(
oProxyCommand
,
o
->
proxy_command
);
else
{
/* Check for numeric addresses */
i
=
strchr
(
o
->
jump_host
,
':'
)
!=
NULL
||
strspn
(
o
->
jump_host
,
"1234567890."
)
==
strlen
(
o
->
jump_host
);
snprintf
(
buf
,
sizeof
(
buf
),
"%d"
,
o
->
jump_port
);
printf
(
"proxyjump %s%s%s%s%s%s%s%s%s
\n
"
,
/* optional additional jump spec */
o
->
jump_extra
==
NULL
?
""
:
o
->
jump_extra
,
o
->
jump_extra
==
NULL
?
""
:
","
,
/* optional user */
o
->
jump_user
==
NULL
?
""
:
o
->
jump_user
,
o
->
jump_user
==
NULL
?
""
:
"@"
,
/* opening [ if hostname is numeric */
i
?
"["
:
""
,
/* mandatory hostname */
o
->
jump_host
,
/* closing ] if hostname is numeric */
i
?
"]"
:
""
,
/* optional port number */
o
->
jump_port
<=
0
?
""
:
":"
,
o
->
jump_port
<=
0
?
""
:
buf
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Mon, Apr 27, 9:06 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28434975
Default Alt Text
readconf.c (100 KB)
Attached To
Mode
rG FreeBSD src repository
Attached
Detach File
Event Timeline
Log In to Comment