version 1.144, 2005/08/06 10:03:12 |
version 1.144.2.2, 2006/10/06 03:19:33 |
|
|
|
/* $OpenBSD$ */ |
/* |
/* |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* All rights reserved |
* All rights reserved |
|
|
* called by a name other than "ssh" or "Secure Shell". |
* called by a name other than "ssh" or "Secure Shell". |
*/ |
*/ |
|
|
#include "includes.h" |
#include <sys/types.h> |
RCSID("$OpenBSD$"); |
#include <sys/socket.h> |
|
|
|
#include <netdb.h> |
|
#include <pwd.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <signal.h> |
|
#include <unistd.h> |
|
#include <stdarg.h> |
|
|
|
#include "xmalloc.h" |
#include "ssh.h" |
#include "ssh.h" |
#include "log.h" |
#include "log.h" |
|
#include "buffer.h" |
#include "servconf.h" |
#include "servconf.h" |
#include "xmalloc.h" |
|
#include "compat.h" |
#include "compat.h" |
#include "pathnames.h" |
#include "pathnames.h" |
#include "misc.h" |
#include "misc.h" |
#include "cipher.h" |
#include "cipher.h" |
|
#include "key.h" |
#include "kex.h" |
#include "kex.h" |
#include "mac.h" |
#include "mac.h" |
|
#include "match.h" |
|
#include "channels.h" |
|
#include "groupaccess.h" |
|
|
static void add_listen_addr(ServerOptions *, char *, u_short); |
static void add_listen_addr(ServerOptions *, char *, u_short); |
static void add_one_listen_addr(ServerOptions *, char *, u_short); |
static void add_one_listen_addr(ServerOptions *, char *, u_short); |
|
|
/* Use of privilege separation or not */ |
/* Use of privilege separation or not */ |
extern int use_privsep; |
extern int use_privsep; |
|
extern Buffer cfg; |
|
|
/* Initializes the server options to their default values. */ |
/* Initializes the server options to their default values. */ |
|
|
|
|
options->authorized_keys_file = NULL; |
options->authorized_keys_file = NULL; |
options->authorized_keys_file2 = NULL; |
options->authorized_keys_file2 = NULL; |
options->num_accept_env = 0; |
options->num_accept_env = 0; |
|
options->permit_tun = -1; |
/* Needs to be accessable in many places */ |
options->num_permitted_opens = -1; |
use_privsep = -1; |
options->adm_forced_command = NULL; |
} |
} |
|
|
void |
void |
|
|
} |
} |
if (options->authorized_keys_file == NULL) |
if (options->authorized_keys_file == NULL) |
options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS; |
options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS; |
|
if (options->permit_tun == -1) |
|
options->permit_tun = SSH_TUNMODE_NO; |
|
|
/* Turn privilege separation on by default */ |
/* Turn privilege separation on by default */ |
if (use_privsep == -1) |
if (use_privsep == -1) |
|
|
sBanner, sUseDNS, sHostbasedAuthentication, |
sBanner, sUseDNS, sHostbasedAuthentication, |
sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, |
sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, |
sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, |
sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, |
sGssAuthentication, sGssCleanupCreds, sAcceptEnv, |
sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, |
|
sMatch, sPermitOpen, sForceCommand, |
sUsePrivilegeSeparation, |
sUsePrivilegeSeparation, |
sDeprecated, sUnsupported |
sDeprecated, sUnsupported |
} ServerOpCodes; |
} ServerOpCodes; |
|
|
|
#define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ |
|
#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ |
|
#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) |
|
|
/* Textual representation of the tokens. */ |
/* Textual representation of the tokens. */ |
static struct { |
static struct { |
const char *name; |
const char *name; |
ServerOpCodes opcode; |
ServerOpCodes opcode; |
|
u_int flags; |
} keywords[] = { |
} keywords[] = { |
{ "port", sPort }, |
{ "port", sPort, SSHCFG_GLOBAL }, |
{ "hostkey", sHostKeyFile }, |
{ "hostkey", sHostKeyFile, SSHCFG_GLOBAL }, |
{ "hostdsakey", sHostKeyFile }, /* alias */ |
{ "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */ |
{ "pidfile", sPidFile }, |
{ "pidfile", sPidFile, SSHCFG_GLOBAL }, |
{ "serverkeybits", sServerKeyBits }, |
{ "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL }, |
{ "logingracetime", sLoginGraceTime }, |
{ "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL }, |
{ "keyregenerationinterval", sKeyRegenerationTime }, |
{ "keyregenerationinterval", sKeyRegenerationTime, SSHCFG_GLOBAL }, |
{ "permitrootlogin", sPermitRootLogin }, |
{ "permitrootlogin", sPermitRootLogin, SSHCFG_GLOBAL }, |
{ "syslogfacility", sLogFacility }, |
{ "syslogfacility", sLogFacility, SSHCFG_GLOBAL }, |
{ "loglevel", sLogLevel }, |
{ "loglevel", sLogLevel, SSHCFG_GLOBAL }, |
{ "rhostsauthentication", sDeprecated }, |
{ "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL }, |
{ "rhostsrsaauthentication", sRhostsRSAAuthentication }, |
{ "rhostsrsaauthentication", sRhostsRSAAuthentication, SSHCFG_GLOBAL }, |
{ "hostbasedauthentication", sHostbasedAuthentication }, |
{ "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_GLOBAL }, |
{ "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly }, |
{ "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_GLOBAL }, |
{ "rsaauthentication", sRSAAuthentication }, |
{ "rsaauthentication", sRSAAuthentication, SSHCFG_GLOBAL }, |
{ "pubkeyauthentication", sPubkeyAuthentication }, |
{ "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, |
{ "dsaauthentication", sPubkeyAuthentication }, /* alias */ |
{ "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */ |
#ifdef KRB5 |
#ifdef KRB5 |
{ "kerberosauthentication", sKerberosAuthentication }, |
{ "kerberosauthentication", sKerberosAuthentication, SSHCFG_GLOBAL }, |
{ "kerberosorlocalpasswd", sKerberosOrLocalPasswd }, |
{ "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_GLOBAL }, |
{ "kerberosticketcleanup", sKerberosTicketCleanup }, |
{ "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_GLOBAL }, |
{ "kerberosgetafstoken", sKerberosGetAFSToken }, |
{ "kerberosgetafstoken", sKerberosGetAFSToken, SSHCFG_GLOBAL }, |
#else |
#else |
{ "kerberosauthentication", sUnsupported }, |
{ "kerberosauthentication", sUnsupported, SSHCFG_GLOBAL }, |
{ "kerberosorlocalpasswd", sUnsupported }, |
{ "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, |
{ "kerberosticketcleanup", sUnsupported }, |
{ "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, |
{ "kerberosgetafstoken", sUnsupported }, |
{ "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, |
#endif |
#endif |
{ "kerberostgtpassing", sUnsupported }, |
{ "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, |
{ "afstokenpassing", sUnsupported }, |
{ "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, |
#ifdef GSSAPI |
#ifdef GSSAPI |
{ "gssapiauthentication", sGssAuthentication }, |
{ "gssapiauthentication", sGssAuthentication, SSHCFG_GLOBAL }, |
{ "gssapicleanupcredentials", sGssCleanupCreds }, |
{ "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, |
#else |
#else |
{ "gssapiauthentication", sUnsupported }, |
{ "gssapiauthentication", sUnsupported, SSHCFG_GLOBAL }, |
{ "gssapicleanupcredentials", sUnsupported }, |
{ "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, |
#endif |
#endif |
{ "passwordauthentication", sPasswordAuthentication }, |
{ "passwordauthentication", sPasswordAuthentication, SSHCFG_GLOBAL }, |
{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication }, |
{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_GLOBAL }, |
{ "challengeresponseauthentication", sChallengeResponseAuthentication }, |
{ "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, |
{ "skeyauthentication", sChallengeResponseAuthentication }, /* alias */ |
{ "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */ |
{ "checkmail", sDeprecated }, |
{ "checkmail", sDeprecated, SSHCFG_GLOBAL }, |
{ "listenaddress", sListenAddress }, |
{ "listenaddress", sListenAddress, SSHCFG_GLOBAL }, |
{ "addressfamily", sAddressFamily }, |
{ "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, |
{ "printmotd", sPrintMotd }, |
{ "printmotd", sPrintMotd, SSHCFG_GLOBAL }, |
{ "printlastlog", sPrintLastLog }, |
{ "printlastlog", sPrintLastLog, SSHCFG_GLOBAL }, |
{ "ignorerhosts", sIgnoreRhosts }, |
{ "ignorerhosts", sIgnoreRhosts, SSHCFG_GLOBAL }, |
{ "ignoreuserknownhosts", sIgnoreUserKnownHosts }, |
{ "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL }, |
{ "x11forwarding", sX11Forwarding }, |
{ "x11forwarding", sX11Forwarding, SSHCFG_ALL }, |
{ "x11displayoffset", sX11DisplayOffset }, |
{ "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL }, |
{ "x11uselocalhost", sX11UseLocalhost }, |
{ "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, |
{ "xauthlocation", sXAuthLocation }, |
{ "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, |
{ "strictmodes", sStrictModes }, |
{ "strictmodes", sStrictModes, SSHCFG_GLOBAL }, |
{ "permitemptypasswords", sEmptyPasswd }, |
{ "permitemptypasswords", sEmptyPasswd, SSHCFG_GLOBAL }, |
{ "permituserenvironment", sPermitUserEnvironment }, |
{ "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, |
{ "uselogin", sUseLogin }, |
{ "uselogin", sUseLogin, SSHCFG_GLOBAL }, |
{ "compression", sCompression }, |
{ "compression", sCompression, SSHCFG_GLOBAL }, |
{ "tcpkeepalive", sTCPKeepAlive }, |
{ "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, |
{ "keepalive", sTCPKeepAlive }, /* obsolete alias */ |
{ "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, /* obsolete alias */ |
{ "allowtcpforwarding", sAllowTcpForwarding }, |
{ "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL }, |
{ "allowusers", sAllowUsers }, |
{ "allowusers", sAllowUsers, SSHCFG_GLOBAL }, |
{ "denyusers", sDenyUsers }, |
{ "denyusers", sDenyUsers, SSHCFG_GLOBAL }, |
{ "allowgroups", sAllowGroups }, |
{ "allowgroups", sAllowGroups, SSHCFG_GLOBAL }, |
{ "denygroups", sDenyGroups }, |
{ "denygroups", sDenyGroups, SSHCFG_GLOBAL }, |
{ "ciphers", sCiphers }, |
{ "ciphers", sCiphers, SSHCFG_GLOBAL }, |
{ "macs", sMacs }, |
{ "macs", sMacs, SSHCFG_GLOBAL }, |
{ "protocol", sProtocol }, |
{ "protocol", sProtocol, SSHCFG_GLOBAL }, |
{ "gatewayports", sGatewayPorts }, |
{ "gatewayports", sGatewayPorts, SSHCFG_ALL }, |
{ "subsystem", sSubsystem }, |
{ "subsystem", sSubsystem, SSHCFG_GLOBAL }, |
{ "maxstartups", sMaxStartups }, |
{ "maxstartups", sMaxStartups, SSHCFG_GLOBAL }, |
{ "maxauthtries", sMaxAuthTries }, |
{ "maxauthtries", sMaxAuthTries, SSHCFG_GLOBAL }, |
{ "banner", sBanner }, |
{ "banner", sBanner, SSHCFG_GLOBAL }, |
{ "usedns", sUseDNS }, |
{ "usedns", sUseDNS, SSHCFG_GLOBAL }, |
{ "verifyreversemapping", sDeprecated }, |
{ "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL }, |
{ "reversemappingcheck", sDeprecated }, |
{ "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL }, |
{ "clientaliveinterval", sClientAliveInterval }, |
{ "clientaliveinterval", sClientAliveInterval, SSHCFG_GLOBAL }, |
{ "clientalivecountmax", sClientAliveCountMax }, |
{ "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, |
{ "authorizedkeysfile", sAuthorizedKeysFile }, |
{ "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_GLOBAL }, |
{ "authorizedkeysfile2", sAuthorizedKeysFile2 }, |
{ "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL }, |
{ "useprivilegeseparation", sUsePrivilegeSeparation}, |
{ "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL}, |
{ "acceptenv", sAcceptEnv }, |
{ "acceptenv", sAcceptEnv, SSHCFG_GLOBAL }, |
{ NULL, sBadOption } |
{ "permittunnel", sPermitTunnel, SSHCFG_GLOBAL }, |
|
{ "match", sMatch, SSHCFG_ALL }, |
|
{ "permitopen", sPermitOpen, SSHCFG_ALL }, |
|
{ "forcecommand", sForceCommand, SSHCFG_ALL }, |
|
{ NULL, sBadOption, 0 } |
}; |
}; |
|
|
/* |
/* |
|
|
|
|
static ServerOpCodes |
static ServerOpCodes |
parse_token(const char *cp, const char *filename, |
parse_token(const char *cp, const char *filename, |
int linenum) |
int linenum, u_int *flags) |
{ |
{ |
u_int i; |
u_int i; |
|
|
for (i = 0; keywords[i].name; i++) |
for (i = 0; keywords[i].name; i++) |
if (strcasecmp(cp, keywords[i].name) == 0) |
if (strcasecmp(cp, keywords[i].name) == 0) { |
|
*flags = keywords[i].flags; |
return keywords[i].opcode; |
return keywords[i].opcode; |
|
} |
|
|
error("%s: line %d: Bad configuration option: %s", |
error("%s: line %d: Bad configuration option: %s", |
filename, linenum, cp); |
filename, linenum, cp); |
|
|
options->listen_addrs = aitop; |
options->listen_addrs = aitop; |
} |
} |
|
|
|
/* |
|
* The strategy for the Match blocks is that the config file is parsed twice. |
|
* |
|
* The first time is at startup. activep is initialized to 1 and the |
|
* directives in the global context are processed and acted on. Hitting a |
|
* Match directive unsets activep and the directives inside the block are |
|
* checked for syntax only. |
|
* |
|
* The second time is after a connection has been established but before |
|
* authentication. activep is initialized to 2 and global config directives |
|
* are ignored since they have already been processed. If the criteria in a |
|
* Match block is met, activep is set and the subsequent directives |
|
* processed and actioned until EOF or another Match block unsets it. Any |
|
* options set are copied into the main server config. |
|
* |
|
* Potential additions/improvements: |
|
* - Add Match support for pre-kex directives, eg Protocol, Ciphers. |
|
* |
|
* - Add a Tag directive (idea from David Leonard) ala pf, eg: |
|
* Match Address 192.168.0.* |
|
* Tag trusted |
|
* Match Group wheel |
|
* Tag trusted |
|
* Match Tag trusted |
|
* AllowTcpForwarding yes |
|
* GatewayPorts clientspecified |
|
* [...] |
|
* |
|
* - Add a PermittedChannelRequests directive |
|
* Match Group shell |
|
* PermittedChannelRequests session,forwarded-tcpip |
|
*/ |
|
|
|
static int |
|
match_cfg_line_group(const char *grps, int line, const char *user) |
|
{ |
|
int result = 0; |
|
u_int ngrps = 0; |
|
char *arg, *p, *cp, *grplist[MAX_MATCH_GROUPS]; |
|
struct passwd *pw; |
|
|
|
/* |
|
* Even if we do not have a user yet, we still need to check for |
|
* valid syntax. |
|
*/ |
|
arg = cp = xstrdup(grps); |
|
while ((p = strsep(&cp, ",")) != NULL && *p != '\0') { |
|
if (ngrps >= MAX_MATCH_GROUPS) { |
|
error("line %d: too many groups in Match Group", line); |
|
result = -1; |
|
goto out; |
|
} |
|
grplist[ngrps++] = p; |
|
} |
|
|
|
if (user == NULL) |
|
goto out; |
|
|
|
if ((pw = getpwnam(user)) == NULL) { |
|
debug("Can't match group at line %d because user %.100s does " |
|
"not exist", line, user); |
|
} else if (ga_init(pw->pw_name, pw->pw_gid) == 0) { |
|
debug("Can't Match group because user %.100s not in any group " |
|
"at line %d", user, line); |
|
} else if (ga_match(grplist, ngrps) != 1) { |
|
debug("user %.100s does not match group %.100s at line %d", |
|
user, arg, line); |
|
} else { |
|
debug("user %.100s matched group %.100s at line %d", user, |
|
arg, line); |
|
result = 1; |
|
} |
|
out: |
|
ga_free(); |
|
xfree(arg); |
|
return result; |
|
} |
|
|
|
static int |
|
match_cfg_line(char **condition, int line, const char *user, const char *host, |
|
const char *address) |
|
{ |
|
int result = 1; |
|
char *arg, *attrib, *cp = *condition; |
|
size_t len; |
|
|
|
if (user == NULL) |
|
debug3("checking syntax for 'Match %s'", cp); |
|
else |
|
debug3("checking match for '%s' user %s host %s addr %s", cp, |
|
user ? user : "(null)", host ? host : "(null)", |
|
address ? address : "(null)"); |
|
|
|
while ((attrib = strdelim(&cp)) && *attrib != '\0') { |
|
if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { |
|
error("Missing Match criteria for %s", attrib); |
|
return -1; |
|
} |
|
len = strlen(arg); |
|
if (strcasecmp(attrib, "user") == 0) { |
|
if (!user) { |
|
result = 0; |
|
continue; |
|
} |
|
if (match_pattern_list(user, arg, len, 0) != 1) |
|
result = 0; |
|
else |
|
debug("user %.100s matched 'User %.100s' at " |
|
"line %d", user, arg, line); |
|
} else if (strcasecmp(attrib, "group") == 0) { |
|
switch (match_cfg_line_group(arg, line, user)) { |
|
case -1: |
|
return -1; |
|
case 0: |
|
result = 0; |
|
} |
|
} else if (strcasecmp(attrib, "host") == 0) { |
|
if (!host) { |
|
result = 0; |
|
continue; |
|
} |
|
if (match_hostname(host, arg, len) != 1) |
|
result = 0; |
|
else |
|
debug("connection from %.100s matched 'Host " |
|
"%.100s' at line %d", host, arg, line); |
|
} else if (strcasecmp(attrib, "address") == 0) { |
|
debug("address '%s' arg '%s'", address, arg); |
|
if (!address) { |
|
result = 0; |
|
continue; |
|
} |
|
if (match_hostname(address, arg, len) != 1) |
|
result = 0; |
|
else |
|
debug("connection from %.100s matched 'Address " |
|
"%.100s' at line %d", address, arg, line); |
|
} else { |
|
error("Unsupported Match attribute %s", attrib); |
|
return -1; |
|
} |
|
} |
|
if (user != NULL) |
|
debug3("match %sfound", result ? "" : "not "); |
|
*condition = cp; |
|
return result; |
|
} |
|
|
|
#define WHITESPACE " \t\r\n" |
|
|
int |
int |
process_server_config_line(ServerOptions *options, char *line, |
process_server_config_line(ServerOptions *options, char *line, |
const char *filename, int linenum) |
const char *filename, int linenum, int *activep, const char *user, |
|
const char *host, const char *address) |
{ |
{ |
char *cp, **charptr, *arg, *p; |
char *cp, **charptr, *arg, *p; |
int *intptr, value, n; |
int cmdline = 0, *intptr, value, n; |
ServerOpCodes opcode; |
ServerOpCodes opcode; |
u_short port; |
u_short port; |
u_int i; |
u_int i, flags = 0; |
|
size_t len; |
|
|
cp = line; |
cp = line; |
arg = strdelim(&cp); |
if ((arg = strdelim(&cp)) == NULL) |
|
return 0; |
/* Ignore leading whitespace */ |
/* Ignore leading whitespace */ |
if (*arg == '\0') |
if (*arg == '\0') |
arg = strdelim(&cp); |
arg = strdelim(&cp); |
|
|
return 0; |
return 0; |
intptr = NULL; |
intptr = NULL; |
charptr = NULL; |
charptr = NULL; |
opcode = parse_token(arg, filename, linenum); |
opcode = parse_token(arg, filename, linenum, &flags); |
|
|
|
if (activep == NULL) { /* We are processing a command line directive */ |
|
cmdline = 1; |
|
activep = &cmdline; |
|
} |
|
if (*activep && opcode != sMatch) |
|
debug3("%s:%d setting %s %s", filename, linenum, arg, cp); |
|
if (*activep == 0 && !(flags & SSHCFG_MATCH)) { |
|
if (user == NULL) { |
|
fatal("%s line %d: Directive '%s' is not allowed " |
|
"within a Match block", filename, linenum, arg); |
|
} else { /* this is a directive we have already processed */ |
|
while (arg) |
|
arg = strdelim(&cp); |
|
return 0; |
|
} |
|
} |
|
|
switch (opcode) { |
switch (opcode) { |
case sBadOption: |
case sBadOption: |
return -1; |
return -1; |
|
|
fatal("%s line %d: missing integer value.", |
fatal("%s line %d: missing integer value.", |
filename, linenum); |
filename, linenum); |
value = atoi(arg); |
value = atoi(arg); |
if (*intptr == -1) |
if (*activep && *intptr == -1) |
*intptr = value; |
*intptr = value; |
break; |
break; |
|
|
|
|
if (!arg || *arg == '\0') |
if (!arg || *arg == '\0') |
fatal("%s line %d: missing file name.", |
fatal("%s line %d: missing file name.", |
filename, linenum); |
filename, linenum); |
if (*charptr == NULL) { |
if (*activep && *charptr == NULL) { |
*charptr = tilde_expand_filename(arg, getuid()); |
*charptr = tilde_expand_filename(arg, getuid()); |
/* increase optional counter */ |
/* increase optional counter */ |
if (intptr != NULL) |
if (intptr != NULL) |
|
|
else |
else |
fatal("%s line %d: Bad yes/no argument: %s", |
fatal("%s line %d: Bad yes/no argument: %s", |
filename, linenum, arg); |
filename, linenum, arg); |
if (*intptr == -1) |
if (*activep && *intptr == -1) |
*intptr = value; |
*intptr = value; |
break; |
break; |
|
|
|
|
case sDenyUsers: |
case sDenyUsers: |
while ((arg = strdelim(&cp)) && *arg != '\0') { |
while ((arg = strdelim(&cp)) && *arg != '\0') { |
if (options->num_deny_users >= MAX_DENY_USERS) |
if (options->num_deny_users >= MAX_DENY_USERS) |
fatal( "%s line %d: too many deny users.", |
fatal("%s line %d: too many deny users.", |
filename, linenum); |
filename, linenum); |
options->deny_users[options->num_deny_users++] = |
options->deny_users[options->num_deny_users++] = |
xstrdup(arg); |
xstrdup(arg); |
|
|
if (!arg || *arg == '\0') |
if (!arg || *arg == '\0') |
fatal("%s line %d: Missing subsystem name.", |
fatal("%s line %d: Missing subsystem name.", |
filename, linenum); |
filename, linenum); |
|
if (!*activep) { |
|
arg = strdelim(&cp); |
|
break; |
|
} |
for (i = 0; i < options->num_subsystems; i++) |
for (i = 0; i < options->num_subsystems; i++) |
if (strcmp(arg, options->subsystem_name[i]) == 0) |
if (strcmp(arg, options->subsystem_name[i]) == 0) |
fatal("%s line %d: Subsystem '%s' already defined.", |
fatal("%s line %d: Subsystem '%s' already defined.", |
|
|
fatal("%s line %d: Missing subsystem command.", |
fatal("%s line %d: Missing subsystem command.", |
filename, linenum); |
filename, linenum); |
options->subsystem_command[options->num_subsystems] = xstrdup(arg); |
options->subsystem_command[options->num_subsystems] = xstrdup(arg); |
|
|
|
/* Collect arguments (separate to executable) */ |
|
p = xstrdup(arg); |
|
len = strlen(p) + 1; |
|
while ((arg = strdelim(&cp)) != NULL && *arg != '\0') { |
|
len += 1 + strlen(arg); |
|
p = xrealloc(p, 1, len); |
|
strlcat(p, " ", len); |
|
strlcat(p, arg, len); |
|
} |
|
options->subsystem_args[options->num_subsystems] = p; |
options->num_subsystems++; |
options->num_subsystems++; |
break; |
break; |
|
|
|
|
*/ |
*/ |
case sAuthorizedKeysFile: |
case sAuthorizedKeysFile: |
case sAuthorizedKeysFile2: |
case sAuthorizedKeysFile2: |
charptr = (opcode == sAuthorizedKeysFile ) ? |
charptr = (opcode == sAuthorizedKeysFile) ? |
&options->authorized_keys_file : |
&options->authorized_keys_file : |
&options->authorized_keys_file2; |
&options->authorized_keys_file2; |
goto parse_filename; |
goto parse_filename; |
|
|
if (options->num_accept_env >= MAX_ACCEPT_ENV) |
if (options->num_accept_env >= MAX_ACCEPT_ENV) |
fatal("%s line %d: too many allow env.", |
fatal("%s line %d: too many allow env.", |
filename, linenum); |
filename, linenum); |
|
if (!*activep) |
|
break; |
options->accept_env[options->num_accept_env++] = |
options->accept_env[options->num_accept_env++] = |
xstrdup(arg); |
xstrdup(arg); |
} |
} |
break; |
break; |
|
|
|
case sPermitTunnel: |
|
intptr = &options->permit_tun; |
|
arg = strdelim(&cp); |
|
if (!arg || *arg == '\0') |
|
fatal("%s line %d: Missing yes/point-to-point/" |
|
"ethernet/no argument.", filename, linenum); |
|
value = 0; /* silence compiler */ |
|
if (strcasecmp(arg, "ethernet") == 0) |
|
value = SSH_TUNMODE_ETHERNET; |
|
else if (strcasecmp(arg, "point-to-point") == 0) |
|
value = SSH_TUNMODE_POINTOPOINT; |
|
else if (strcasecmp(arg, "yes") == 0) |
|
value = SSH_TUNMODE_YES; |
|
else if (strcasecmp(arg, "no") == 0) |
|
value = SSH_TUNMODE_NO; |
|
else |
|
fatal("%s line %d: Bad yes/point-to-point/ethernet/" |
|
"no argument: %s", filename, linenum, arg); |
|
if (*intptr == -1) |
|
*intptr = value; |
|
break; |
|
|
|
case sMatch: |
|
if (cmdline) |
|
fatal("Match directive not supported as a command-line " |
|
"option"); |
|
value = match_cfg_line(&cp, linenum, user, host, address); |
|
if (value < 0) |
|
fatal("%s line %d: Bad Match condition", filename, |
|
linenum); |
|
*activep = value; |
|
break; |
|
|
|
case sPermitOpen: |
|
arg = strdelim(&cp); |
|
if (!arg || *arg == '\0') |
|
fatal("%s line %d: missing PermitOpen specification", |
|
filename, linenum); |
|
if (strcmp(arg, "any") == 0) { |
|
if (*activep) { |
|
channel_clear_adm_permitted_opens(); |
|
options->num_permitted_opens = 0; |
|
} |
|
break; |
|
} |
|
for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) { |
|
p = hpdelim(&arg); |
|
if (p == NULL) |
|
fatal("%s line %d: missing host in PermitOpen", |
|
filename, linenum); |
|
p = cleanhostname(p); |
|
if (arg == NULL || (port = a2port(arg)) == 0) |
|
fatal("%s line %d: bad port number in " |
|
"PermitOpen", filename, linenum); |
|
if (*activep && options->num_permitted_opens == -1) { |
|
channel_clear_adm_permitted_opens(); |
|
options->num_permitted_opens = |
|
channel_add_adm_permitted_opens(p, port); |
|
} |
|
} |
|
break; |
|
|
|
case sForceCommand: |
|
if (cp == NULL) |
|
fatal("%.200s line %d: Missing argument.", filename, |
|
linenum); |
|
len = strspn(cp, WHITESPACE); |
|
if (*activep && options->adm_forced_command == NULL) |
|
options->adm_forced_command = xstrdup(cp + len); |
|
return 0; |
|
|
case sDeprecated: |
case sDeprecated: |
logit("%s line %d: Deprecated option %s", |
logit("%s line %d: Deprecated option %s", |
filename, linenum, arg); |
filename, linenum, arg); |
|
|
} |
} |
|
|
void |
void |
parse_server_config(ServerOptions *options, const char *filename, Buffer *conf) |
parse_server_match_config(ServerOptions *options, const char *user, |
|
const char *host, const char *address) |
{ |
{ |
int linenum, bad_options = 0; |
ServerOptions mo; |
|
|
|
initialize_server_options(&mo); |
|
parse_server_config(&mo, "reprocess config", &cfg, user, host, address); |
|
copy_set_server_options(options, &mo); |
|
} |
|
|
|
/* Copy any (supported) values that are set */ |
|
void |
|
copy_set_server_options(ServerOptions *dst, ServerOptions *src) |
|
{ |
|
if (src->allow_tcp_forwarding != -1) |
|
dst->allow_tcp_forwarding = src->allow_tcp_forwarding; |
|
if (src->gateway_ports != -1) |
|
dst->gateway_ports = src->gateway_ports; |
|
if (src->adm_forced_command != NULL) { |
|
if (dst->adm_forced_command != NULL) |
|
xfree(dst->adm_forced_command); |
|
dst->adm_forced_command = src->adm_forced_command; |
|
} |
|
if (src->x11_display_offset != -1) |
|
dst->x11_display_offset = src->x11_display_offset; |
|
if (src->x11_forwarding != -1) |
|
dst->x11_forwarding = src->x11_forwarding; |
|
if (src->x11_use_localhost != -1) |
|
dst->x11_use_localhost = src->x11_use_localhost; |
|
} |
|
|
|
void |
|
parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, |
|
const char *user, const char *host, const char *address) |
|
{ |
|
int active, linenum, bad_options = 0; |
char *cp, *obuf, *cbuf; |
char *cp, *obuf, *cbuf; |
|
|
debug2("%s: config %s len %d", __func__, filename, buffer_len(conf)); |
debug2("%s: config %s len %d", __func__, filename, buffer_len(conf)); |
|
|
obuf = cbuf = xstrdup(buffer_ptr(conf)); |
obuf = cbuf = xstrdup(buffer_ptr(conf)); |
|
active = user ? 0 : 1; |
linenum = 1; |
linenum = 1; |
while ((cp = strsep(&cbuf, "\n")) != NULL) { |
while ((cp = strsep(&cbuf, "\n")) != NULL) { |
if (process_server_config_line(options, cp, filename, |
if (process_server_config_line(options, cp, filename, |
linenum++) != 0) |
linenum++, &active, user, host, address) != 0) |
bad_options++; |
bad_options++; |
} |
} |
xfree(obuf); |
xfree(obuf); |