version 1.205, 2013/08/20 00:11:37 |
version 1.206, 2013/10/14 22:22:02 |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <sys/socket.h> |
#include <sys/socket.h> |
|
#include <sys/wait.h> |
|
|
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <netinet/in_systm.h> |
#include <netinet/in_systm.h> |
|
|
|
|
#include <ctype.h> |
#include <ctype.h> |
#include <errno.h> |
#include <errno.h> |
|
#include <fcntl.h> |
#include <netdb.h> |
#include <netdb.h> |
|
#include <paths.h> |
|
#include <pwd.h> |
#include <signal.h> |
#include <signal.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <string.h> |
#include <string.h> |
|
|
#include "buffer.h" |
#include "buffer.h" |
#include "kex.h" |
#include "kex.h" |
#include "mac.h" |
#include "mac.h" |
|
#include "uidswap.h" |
|
|
/* Format of the configuration file: |
/* Format of the configuration file: |
|
|
|
|
|
|
typedef enum { |
typedef enum { |
oBadOption, |
oBadOption, |
|
oHost, oMatch, |
oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, |
oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, |
oGatewayPorts, oExitOnForwardFailure, |
oGatewayPorts, oExitOnForwardFailure, |
oPasswordAuthentication, oRSAAuthentication, |
oPasswordAuthentication, oRSAAuthentication, |
oChallengeResponseAuthentication, oXAuthLocation, |
oChallengeResponseAuthentication, oXAuthLocation, |
oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, |
oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, |
oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, |
oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, |
oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, |
oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, |
oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, |
oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, |
oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, |
oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, |
|
|
{ "localforward", oLocalForward }, |
{ "localforward", oLocalForward }, |
{ "user", oUser }, |
{ "user", oUser }, |
{ "host", oHost }, |
{ "host", oHost }, |
|
{ "match", oMatch }, |
{ "escapechar", oEscapeChar }, |
{ "escapechar", oEscapeChar }, |
{ "globalknownhostsfile", oGlobalKnownHostsFile }, |
{ "globalknownhostsfile", oGlobalKnownHostsFile }, |
{ "globalknownhostsfile2", oDeprecated }, |
{ "globalknownhostsfile2", oDeprecated }, |
|
|
options->identity_files[options->num_identity_files++] = path; |
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; |
|
} |
|
|
/* |
/* |
* Returns the number of the token pointed to by cp or oBadOption. |
* 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, *command_string; |
|
pid_t pid; |
|
int devnull, status; |
|
extern uid_t original_real_uid; |
|
|
|
if ((shell = getenv("SHELL")) == NULL) |
|
shell = _PATH_BSHELL; |
|
|
|
/* |
|
* Use "exec" to avoid "sh -c" processes on some platforms |
|
* (e.g. Solaris) |
|
*/ |
|
xasprintf(&command_string, "exec %s", cmd); |
|
|
|
/* Need this to redirect subprocess stdin/out */ |
|
if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) |
|
fatal("open(/dev/null): %s", strerror(errno)); |
|
|
|
debug("Executing command: '%.500s'", cmd); |
|
|
|
/* Fork and execute the command. */ |
|
if ((pid = fork()) == 0) { |
|
char *argv[4]; |
|
|
|
/* Child. Permanently give up superuser privileges. */ |
|
permanently_drop_suid(original_real_uid); |
|
|
|
/* Redirect child stdin and stdout. Leave stderr */ |
|
if (dup2(devnull, STDIN_FILENO) == -1) |
|
fatal("dup2: %s", strerror(errno)); |
|
if (dup2(devnull, STDOUT_FILENO) == -1) |
|
fatal("dup2: %s", strerror(errno)); |
|
if (devnull > STDERR_FILENO) |
|
close(devnull); |
|
closefrom(STDERR_FILENO + 1); |
|
|
|
argv[0] = shell; |
|
argv[1] = "-c"; |
|
argv[2] = command_string; |
|
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. */ |
|
signal(SIGTERM, SIG_DFL); |
|
kill(getpid(), SIGTERM); |
|
_exit(1); |
|
} |
|
/* Parent. */ |
|
if (pid < 0) |
|
fatal("%s: fork: %.100s", __func__, strerror(errno)); |
|
|
|
close(devnull); |
|
free(command_string); |
|
|
|
while (waitpid(pid, &status, 0) == -1) { |
|
if (errno != EINTR && errno != EAGAIN) |
|
fatal("%s: waitpid: %s", __func__, strerror(errno)); |
|
} |
|
if (!WIFEXITED(status)) { |
|
error("command '%.100s' exited abnormally", cmd); |
|
return -1; |
|
} |
|
debug3("command returned status %d", WEXITSTATUS(status)); |
|
return WEXITSTATUS(status); |
|
} |
|
|
|
/* |
|
* Parse and execute a Match directive. |
|
*/ |
|
static int |
|
match_cfg_line(Options *options, char **condition, struct passwd *pw, |
|
const char *host_arg, const char *filename, int linenum) |
|
{ |
|
char *arg, *attrib, *cmd, *cp = *condition; |
|
const char *ruser, *host; |
|
int r, port, result = 1; |
|
size_t len; |
|
char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; |
|
|
|
/* |
|
* 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; |
|
host = options->hostname == NULL ? host_arg : options->hostname; |
|
|
|
debug3("checking match for '%s' host %s", cp, host); |
|
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, "host") == 0) { |
|
if (match_hostname(host, arg, len) != 1) |
|
result = 0; |
|
else |
|
debug("%.200s line %d: matched 'Host %.100s' ", |
|
filename, linenum, host); |
|
} else if (strcasecmp(attrib, "originalhost") == 0) { |
|
if (match_hostname(host_arg, arg, len) != 1) |
|
result = 0; |
|
else |
|
debug("%.200s line %d: matched " |
|
"'OriginalHost %.100s' ", |
|
filename, linenum, host_arg); |
|
} else if (strcasecmp(attrib, "user") == 0) { |
|
if (match_pattern_list(ruser, arg, len, 0) != 1) |
|
result = 0; |
|
else |
|
debug("%.200s line %d: matched 'User %.100s' ", |
|
filename, linenum, ruser); |
|
} else if (strcasecmp(attrib, "localuser") == 0) { |
|
if (match_pattern_list(pw->pw_name, arg, len, 0) != 1) |
|
result = 0; |
|
else |
|
debug("%.200s line %d: matched " |
|
"'LocalUser %.100s' ", |
|
filename, linenum, pw->pw_name); |
|
} else if (strcasecmp(attrib, "command") == 0) { |
|
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); |
|
|
|
cmd = percent_expand(arg, |
|
"L", shorthost, |
|
"d", pw->pw_dir, |
|
"h", host, |
|
"l", thishost, |
|
"n", host_arg, |
|
"p", portstr, |
|
"r", ruser, |
|
"u", pw->pw_name, |
|
(char *)NULL); |
|
r = execute_in_shell(cmd); |
|
if (r == -1) { |
|
fatal("%.200s line %d: match command '%.100s' " |
|
"error", filename, linenum, cmd); |
|
} else if (r == 0) { |
|
debug("%.200s line %d: matched " |
|
"'Command \"%.100s\"' ", |
|
filename, linenum, cmd); |
|
} else |
|
result = 0; |
|
free(cmd); |
|
} else { |
|
error("Unsupported Match attribute %s", attrib); |
|
return -1; |
|
} |
|
} |
|
debug3("match %sfound", result ? "" : "not "); |
|
*condition = cp; |
|
return result; |
|
} |
|
|
|
/* |
|
* Returns the number of the token pointed to by cp or oBadOption. |
|
*/ |
static OpCodes |
static OpCodes |
parse_token(const char *cp, const char *filename, int linenum, |
parse_token(const char *cp, const char *filename, int linenum, |
const char *ignored_unknown) |
const char *ignored_unknown) |
|
|
* only sets those values that have not already been set. |
* only sets those values that have not already been set. |
*/ |
*/ |
#define WHITESPACE " \t\r\n" |
#define WHITESPACE " \t\r\n" |
|
|
int |
int |
process_config_line(Options *options, const char *host, |
process_config_line(Options *options, struct passwd *pw, const char *host, |
char *line, const char *filename, int linenum, |
char *line, const char *filename, int linenum, int *activep, int userconfig) |
int *activep, int userconfig) |
|
{ |
{ |
char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; |
char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; |
char **cpptr, fwdarg[256]; |
char **cpptr, fwdarg[256]; |
u_int i, *uintptr, max_entries = 0; |
u_int i, *uintptr, max_entries = 0; |
int negated, opcode, *intptr, value, value2; |
int negated, opcode, *intptr, value, value2, cmdline = 0; |
LogLevel *log_level_ptr; |
LogLevel *log_level_ptr; |
long long val64; |
long long val64; |
size_t len; |
size_t len; |
Forward fwd; |
Forward fwd; |
|
|
|
if (activep == NULL) { /* We are processing a command line directive */ |
|
cmdline = 1; |
|
activep = &cmdline; |
|
} |
|
|
/* Strip trailing whitespace */ |
/* Strip trailing whitespace */ |
for (len = strlen(line) - 1; len > 0; len--) { |
for (len = strlen(line) - 1; len > 0; len--) { |
if (strchr(WHITESPACE, line[len]) == NULL) |
if (strchr(WHITESPACE, line[len]) == NULL) |
|
|
goto parse_flag; |
goto parse_flag; |
|
|
case oHost: |
case oHost: |
|
if (cmdline) |
|
fatal("Host directive not supported as a command-line " |
|
"option"); |
*activep = 0; |
*activep = 0; |
arg2 = NULL; |
arg2 = NULL; |
while ((arg = strdelim(&s)) != NULL && *arg != '\0') { |
while ((arg = strdelim(&s)) != NULL && *arg != '\0') { |
|
|
/* Avoid garbage check below, as strdelim is done. */ |
/* Avoid garbage check below, as strdelim is done. */ |
return 0; |
return 0; |
|
|
|
case oMatch: |
|
if (cmdline) |
|
fatal("Host directive not supported as a command-line " |
|
"option"); |
|
value = match_cfg_line(options, &s, pw, host, |
|
filename, linenum); |
|
if (value < 0) |
|
fatal("%.200s line %d: Bad Match condition", filename, |
|
linenum); |
|
*activep = value; |
|
break; |
|
|
case oEscapeChar: |
case oEscapeChar: |
intptr = &options->escape_char; |
intptr = &options->escape_char; |
arg = strdelim(&s); |
arg = strdelim(&s); |
|
|
*/ |
*/ |
|
|
int |
int |
read_config_file(const char *filename, const char *host, Options *options, |
read_config_file(const char *filename, struct passwd *pw, const char *host, |
int flags) |
Options *options, int flags) |
{ |
{ |
FILE *f; |
FILE *f; |
char line[1024]; |
char line[1024]; |
|
|
while (fgets(line, sizeof(line), f)) { |
while (fgets(line, sizeof(line), f)) { |
/* Update line number counter. */ |
/* Update line number counter. */ |
linenum++; |
linenum++; |
if (process_config_line(options, host, line, filename, linenum, |
if (process_config_line(options, pw, host, line, filename, |
&active, flags & SSHCONF_USERCONF) != 0) |
linenum, &active, flags & SSHCONF_USERCONF) != 0) |
bad_options++; |
bad_options++; |
} |
} |
fclose(f); |
fclose(f); |