readconf.c revision cd7d5faf5bbb52336a6f85578a90b31a648ac3fa
/*
* 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".
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "includes.h"
RCSID("$OpenBSD: readconf.c,v 1.100 2002/06/19 00:27:55 deraadt Exp $");
#include "ssh.h"
#include "xmalloc.h"
#include "compat.h"
#include "cipher.h"
#include "pathnames.h"
#include "log.h"
#include "readconf.h"
#include "match.h"
#include "misc.h"
#include "kex.h"
#include "mac.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
Cipher 3des
Host fascist.blob.com
Port 23123
User tylonen
RhostsAuthentication no
PasswordAuthentication no
Host puukko.hut.fi
User t35124p
ProxyCommand ssh-proxy %h %p
Host *.fr
PublicKeyAuthentication no
Host *.su
Cipher none
PasswordAuthentication no
# Defaults for various options
Host *
ForwardAgent no
ForwardX11 no
RhostsAuthentication yes
PasswordAuthentication yes
RSAAuthentication yes
RhostsRSAAuthentication yes
StrictHostKeyChecking yes
KeepAlives no
Port 22
EscapeChar ~
*/
/* Keyword tokens. */
typedef enum {
#endif
#ifdef GSSAPI
#ifdef GSI
#endif /* GSI */
#endif /* GSSAPI */
#endif
#ifdef AFS
#endif
} OpCodes;
/* Textual representations of the tokens. */
static struct {
const char *name;
} keywords[] = {
{ "forwardagent", oForwardAgent },
{ "forwardx11", oForwardX11 },
{ "forwardx11trusted", oForwardX11Trusted },
{ "xauthlocation", oXAuthLocation },
{ "gatewayports", oGatewayPorts },
{ "useprivilegedport", oUsePrivilegedPort },
{ "rhostsauthentication", oRhostsAuthentication },
{ "passwordauthentication", oPasswordAuthentication },
{ "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
{ "kbdinteractivedevices", oKbdInteractiveDevices },
{ "rsaauthentication", oRSAAuthentication },
{ "pubkeyauthentication", oPubkeyAuthentication },
{ "rhostsrsaauthentication", oRhostsRSAAuthentication },
{ "hostbasedauthentication", oHostbasedAuthentication },
{ "challengeresponseauthentication", oChallengeResponseAuthentication },
{ "kerberosauthentication", oKerberosAuthentication },
#endif
#ifdef GSSAPI
{ "gssapikeyexchange", oGssKeyEx },
{ "gssapiauthentication", oGssAuthentication },
{ "gssapidelegatecredentials", oGssDelegateCreds },
#ifdef GSI
/* For backwards compatability with old 1.2.27 client code */
{ "forwardgssapiglobuslimitedproxy", oGssGlobusDelegateLimitedCreds },
#endif /* GSI */
#endif /* GSSAPI */
{ "kerberostgtpassing", oKerberosTgtPassing },
#endif
#ifdef AFS
{ "afstokenpassing", oAFSTokenPassing },
#endif
{ "fallbacktorsh", oFallBackToRsh },
{ "usersh", oUseRsh },
{ "identityfile", oIdentityFile },
{ "hostname", oHostName },
{ "hostkeyalias", oHostKeyAlias },
{ "proxycommand", oProxyCommand },
{ "port", oPort },
{ "cipher", oCipher },
{ "ciphers", oCiphers },
{ "macs", oMacs },
{ "protocol", oProtocol },
{ "remoteforward", oRemoteForward },
{ "localforward", oLocalForward },
{ "user", oUser },
{ "host", oHost },
{ "escapechar", oEscapeChar },
{ "globalknownhostsfile", oGlobalKnownHostsFile },
{ "globalknownhostsfile2", oGlobalKnownHostsFile2 },
{ "connectionattempts", oConnectionAttempts },
{ "batchmode", oBatchMode },
{ "checkhostip", oCheckHostIP },
{ "stricthostkeychecking", oStrictHostKeyChecking },
{ "compression", oCompression },
{ "compressionlevel", oCompressionLevel },
{ "keepalive", oKeepAlives },
{ "numberofpasswordprompts", oNumberOfPasswordPrompts },
{ "loglevel", oLogLevel },
{ "dynamicforward", oDynamicForward },
{ "preferredauthentications", oPreferredAuthentications },
{ "hostkeyalgorithms", oHostKeyAlgorithms },
{ "bindaddress", oBindAddress },
{ "smartcarddevice", oSmartcardDevice },
{ "clearallforwardings", oClearAllForwardings },
{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost },
{ "rekeylimit", oRekeyLimit },
{ "connecttimeout", oConnectTimeout },
{ "serveraliveinterval", oServerAliveInterval },
{ "serveralivecountmax", oServerAliveCountMax },
{ "disablebanner", oDisableBanner },
{ "hashknownhosts", oHashKnownHosts },
{ "ignoreifunknown", oIgnoreIfUnknown },
{ "useopensslengine", oUseOpenSSLEngine },
{ NULL, oBadOption }
};
/*
* error.
*/
void
{
#ifndef NO_IPPORT_RESERVED_CONCEPT
extern uid_t original_real_uid;
fatal("Privileged ports can only be forwarded by root.");
#endif
}
/*
* an error.
*/
void
{
fatal("Too many remote forwards (max %d).",
}
static void
{
int i;
for (i = 0; i < options->num_local_forwards; i++) {
}
options->num_local_forwards = 0;
for (i = 0; i < options->num_remote_forwards; i++) {
}
options->num_remote_forwards = 0;
}
/*
* Returns the number of the token pointed to by cp or oBadOption.
*/
static OpCodes
{
u_int i;
debug("%s: line %d: unknown configuration option: %s",
return oBadOption;
}
/*
* Processes a single option line as used in the configuration files. This
* only sets those values that have not already been set.
*/
int
int *activep)
{
s = line;
/* Get the keyword. (Each line is supposed to begin with a keyword). */
/* Ignore leading whitespace. */
if (*keyword == '\0')
return 0;
switch (opcode) {
case oBadOption:
error("we can't have more than %d unknown options:",
for (i = 0; i < MAX_UNKNOWN_OPTIONS; ++i) {
error("%s:%d:%s",
}
fatal("too many unknown options found, can't continue");
}
/* unknown options will be processed later */
return (0);
/* NOTREACHED */
case oConnectTimeout:
fatal("%s line %d: missing time value.",
fatal("%s line %d: invalid time value.",
break;
case oForwardAgent:
value = 0; /* To avoid compiler warning... */
value = 1;
value = 0;
else
break;
case oForwardX11:
goto parse_flag;
case oForwardX11Trusted:
goto parse_flag;
case oGatewayPorts:
goto parse_flag;
case oUsePrivilegedPort:
goto parse_flag;
case oRhostsAuthentication:
goto parse_flag;
case oPasswordAuthentication:
goto parse_flag;
goto parse_flag;
case oKbdInteractiveDevices:
goto parse_string;
case oPubkeyAuthentication:
goto parse_flag;
case oRSAAuthentication:
goto parse_flag;
case oRhostsRSAAuthentication:
goto parse_flag;
case oHostbasedAuthentication:
goto parse_flag;
goto parse_flag;
case oKerberosAuthentication:
goto parse_flag;
#endif
#ifdef GSSAPI
case oGssKeyEx:
goto parse_flag;
case oGssAuthentication:
goto parse_flag;
case oGssDelegateCreds:
goto parse_flag;
#ifdef GSI
goto parse_flag;
#endif /* GSI */
#endif /* GSSAPI */
case oKerberosTgtPassing:
goto parse_flag;
#endif
#ifdef AFS
case oAFSTokenPassing:
goto parse_flag;
#endif
case oFallBackToRsh:
goto parse_flag;
case oUseRsh:
goto parse_flag;
case oBatchMode:
goto parse_flag;
case oCheckHostIP:
goto parse_flag;
case oStrictHostKeyChecking:
value = 0; /* To avoid compiler warning... */
value = 1;
value = 0;
value = 2;
else
break;
case oCompression:
goto parse_flag;
case oKeepAlives:
goto parse_flag;
goto parse_flag;
case oNumberOfPasswordPrompts:
goto parse_int;
case oCompressionLevel:
goto parse_int;
case oRekeyLimit:
if (arg == endofnumber)
switch (toupper(*endofnumber)) {
case '\0':
scale = 1;
break;
case 'K':
break;
case 'M':
break;
case 'G':
break;
default:
fatal("%.200s line %d: Invalid RekeyLimit suffix",
}
/* detect integer wrap and too-large limits */
fatal("%.200s line %d: RekeyLimit too large",
if (val64 < 16)
fatal("%.200s line %d: RekeyLimit too small",
break;
case oIdentityFile:
if (*activep) {
if (*intptr >= SSH_MAX_IDENTITY_FILES)
fatal("%.200s line %d: Too many identity files specified (max %d).",
}
break;
case oXAuthLocation:
goto parse_string;
case oUser:
break;
case oGlobalKnownHostsFile:
goto parse_string;
case oUserKnownHostsFile:
goto parse_string;
case oGlobalKnownHostsFile2:
goto parse_string;
case oUserKnownHostsFile2:
goto parse_string;
case oHostName:
goto parse_string;
case oHostKeyAlias:
goto parse_string;
goto parse_string;
case oBindAddress:
goto parse_string;
case oSmartcardDevice:
goto parse_string;
case oProxyCommand:
}
else
return 0;
case oPort:
/* Octal, decimal, or hex format? */
if (arg == endofnumber)
break;
case oConnectionAttempts:
goto parse_int;
case oCipher:
if (value == -1)
fatal("%.200s line %d: Bad cipher '%s'.",
break;
case oCiphers:
if (!ciphers_valid(arg))
fatal("%.200s line %d: Bad SSH2 cipher spec '%s'.",
break;
case oMacs:
fatal("%.200s line %d: Bad SSH2 Mac spec '%s'.",
break;
case oHostKeyAlgorithms:
if (!key_names_valid2(arg))
fatal("%.200s line %d: Bad protocol 2 host key algorithms '%s'.",
break;
case oProtocol:
if (value == SSH_PROTO_UNKNOWN)
fatal("%.200s line %d: Bad protocol spec '%s'.",
break;
case oLogLevel:
if (value == SYSLOG_LEVEL_NOT_SET)
fatal("%.200s line %d: unsupported log level '%s'",
break;
case oLocalForward:
case oRemoteForward:
fatal("%.200s line %d: Missing port argument.",
fatal("%.200s line %d: Missing target argument.",
/* construct a string for parse_forward */
fatal("%.200s line %d: Bad forwarding specification.",
if (*activep) {
if (opcode == oLocalForward)
else if (opcode == oRemoteForward)
}
break;
case oDynamicForward:
fatal("%.200s line %d: Missing port argument.",
fatal("%.200s line %d: Bad dynamic forwarding specification.",
}
if (*activep) {
}
break;
case oClearAllForwardings:
goto parse_flag;
case oHost:
*activep = 0;
*activep = 1;
break;
}
/* Avoid garbage check below, as strdelim is done. */
return 0;
case oEscapeChar:
else {
fatal("%.200s line %d: Bad escape character.",
/* NOTREACHED */
value = 0; /* Avoid compiler warning. */
}
break;
case oServerAliveInterval:
goto parse_time;
case oServerAliveCountMax:
goto parse_int;
case oHashKnownHosts:
goto parse_flag;
case oDisableBanner:
break;
else
fatal("%.200s line %d: Bad yes/no/in-exec-mode "
break;
case oIgnoreIfUnknown:
goto parse_string;
case oUseOpenSSLEngine:
goto parse_flag;
case oDeprecated:
debug("%s line %d: Deprecated option \"%s\"",
return 0;
default:
}
/* Check that there is no garbage at end of line. */
fatal("%.200s line %d: garbage at end of line; \"%.200s\".",
}
return 0;
}
/*
* 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
{
FILE *f;
char line[1024];
/* Open the file. */
if (!f)
return 0;
/*
* Mark that we are now processing the options. This flag is turned
*/
active = 1;
linenum = 0;
/* Update line number counter. */
linenum++;
}
fclose(f);
return 1;
}
/*
* 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
{
#ifdef GSSAPI
#ifdef GSI
#endif /* GSI */
#endif /* GSSAPI */
#endif
#endif
#ifdef AFS
#endif
options->num_identity_files = 0;
options->num_local_forwards = 0;
options->num_remote_forwards = 0;
options->unknown_opts_num = 0;
}
/*
* Called after processing other sources of option data, this fills those
* options for which no value has been specified with their default values.
*/
void
{
int len;
options->forward_agent = 0;
options->forward_x11 = 0;
/*
* Unlike OpenSSH, we keep backward compatibility for '-X' option
* which means that X11 forwarding is trusted by default.
*/
options->gateway_ports = 0;
options->use_privileged_port = 0;
options->rhosts_authentication = 0;
#ifdef GSSAPI
options->gss_deleg_creds = 0;
#ifdef GSI
#endif /* GSI */
#endif /* GSSAPI */
#endif
#endif
#ifdef AFS
#endif
options->batch_mode = 0;
options->compression = 0;
/* Selected in ssh_login(). */
/* options->ciphers, default set in myproposals.h */
/* options->macs, default set in myproposals.h */
/* options->hostkeyalgorithms, default set in myproposals.h */
if (options->num_identity_files == 0) {
}
}
}
options->rekey_limit = 0;
options->fallback_to_rsh = 0;
options->server_alive_interval = 0;
options->hash_known_hosts = 0;
options->disable_banner = 0;
/* options->proxy_command should not be set by default */
/* 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 */
/* options->ignore_if_unknown should not be set by default */
}
/*
* Parses a string containing a port forwarding specification of one of the
* two forms, short or long:
*
* [listenhost:]listenport
* [listenhost:]listenport:connecthost:connectport
*
* short forwarding specification is used for dynamic port forwarding and for
* port forwarding cancelation in process_cmdline(). The function returns number
* of arguments parsed or zero on any error.
*/
int
{
int i;
/* skip leading spaces */
cp++;
for (i = 0; i < 5; ++i)
break;
goto fail_free;
switch (i) {
case 0:
goto fail_free;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
}
xfree(p);
goto fail_free;
return (i);
if (p != NULL)
xfree(p);
return (0);
}
/*
* Process previously stored unknown options. When this function is called we
* already have IgnoreIfUnknown set so finally we can decide whether each
* unknown option is to be ignored or not.
*/
void
{
int m, i, bad_options = 0;
/* if there is no unknown option we are done */
if (options->unknown_opts_num == 0)
return;
/*
* Now go through the list of unknown options and report any one that
* is not explicitly listed in IgnoreIfUnknown option. If at least one
* such as that is found it's a show stopper.
*/
for (i = 0; i < options->unknown_opts_num; ++i) {
m = 0;
else
if (m == 1) {
debug("%s: line %d: ignoring unknown option: %s",
}
else {
error("%s: line %d: unknown configuration option: %s",
bad_options++;
}
}
/* exit if we found at least one unignored unknown option */
if (bad_options > 0)
fatal("terminating, %d bad configuration option(s)",
}