diff options
Diffstat (limited to 'servconf.c')
| -rw-r--r-- | servconf.c | 288 |
1 files changed, 201 insertions, 87 deletions
diff --git a/servconf.c b/servconf.c index 0f0d090686b1..c0f6af0bee9a 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.326 2018/03/01 20:32:16 markus Exp $ */ +/* $OpenBSD: servconf.c,v 1.340 2018/08/12 20:19:13 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved @@ -45,13 +45,13 @@ #include "xmalloc.h" #include "ssh.h" #include "log.h" -#include "buffer.h" +#include "sshbuf.h" #include "misc.h" #include "servconf.h" #include "compat.h" #include "pathnames.h" #include "cipher.h" -#include "key.h" +#include "sshkey.h" #include "kex.h" #include "mac.h" #include "match.h" @@ -59,6 +59,7 @@ #include "groupaccess.h" #include "canohost.h" #include "packet.h" +#include "ssherr.h" #include "hostfile.h" #include "auth.h" #include "myproposal.h" @@ -71,7 +72,7 @@ static void add_one_listen_addr(ServerOptions *, const char *, /* Use of privilege separation or not */ extern int use_privsep; -extern Buffer cfg; +extern struct sshbuf *cfg; /* Initializes the server options to their default values. */ @@ -130,6 +131,7 @@ initialize_server_options(ServerOptions *options) options->challenge_response_authentication = -1; options->permit_empty_passwd = -1; options->permit_user_env = -1; + options->permit_user_env_whitelist = NULL; options->compression = -1; options->rekey_limit = -1; options->rekey_interval = -1; @@ -158,8 +160,10 @@ initialize_server_options(ServerOptions *options) options->client_alive_count_max = -1; options->num_authkeys_files = 0; options->num_accept_env = 0; + options->num_setenv = 0; options->permit_tun = -1; options->permitted_opens = NULL; + options->permitted_listens = NULL; options->adm_forced_command = NULL; options->chroot_directory = NULL; options->authorized_keys_command = NULL; @@ -187,15 +191,29 @@ option_clear_or_none(const char *o) static void assemble_algorithms(ServerOptions *o) { - if (kex_assemble_names(KEX_SERVER_ENCRYPT, &o->ciphers) != 0 || - kex_assemble_names(KEX_SERVER_MAC, &o->macs) != 0 || - kex_assemble_names(KEX_SERVER_KEX, &o->kex_algorithms) != 0 || - kex_assemble_names(KEX_DEFAULT_PK_ALG, - &o->hostkeyalgorithms) != 0 || - kex_assemble_names(KEX_DEFAULT_PK_ALG, - &o->hostbased_key_types) != 0 || - kex_assemble_names(KEX_DEFAULT_PK_ALG, &o->pubkey_key_types) != 0) - fatal("kex_assemble_names failed"); + char *all_cipher, *all_mac, *all_kex, *all_key; + int r; + + all_cipher = cipher_alg_list(',', 0); + all_mac = mac_alg_list(','); + all_kex = kex_alg_list(','); + all_key = sshkey_alg_list(0, 0, 1, ','); +#define ASSEMBLE(what, defaults, all) \ + do { \ + if ((r = kex_assemble_names(&o->what, defaults, all)) != 0) \ + fatal("%s: %s: %s", __func__, #what, ssh_err(r)); \ + } while (0) + ASSEMBLE(ciphers, KEX_SERVER_ENCRYPT, all_cipher); + ASSEMBLE(macs, KEX_SERVER_MAC, all_mac); + ASSEMBLE(kex_algorithms, KEX_SERVER_KEX, all_kex); + ASSEMBLE(hostkeyalgorithms, KEX_DEFAULT_PK_ALG, all_key); + ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, all_key); + ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, all_key); +#undef ASSEMBLE + free(all_cipher); + free(all_mac); + free(all_kex); + free(all_key); } static void @@ -327,8 +345,10 @@ fill_default_server_options(ServerOptions *options) options->challenge_response_authentication = 1; if (options->permit_empty_passwd == -1) options->permit_empty_passwd = 0; - if (options->permit_user_env == -1) + if (options->permit_user_env == -1) { options->permit_user_env = 0; + options->permit_user_env_whitelist = NULL; + } if (options->compression == -1) options->compression = COMP_DELAYED; if (options->rekey_limit == -1) @@ -372,9 +392,9 @@ fill_default_server_options(ServerOptions *options) if (options->permit_tun == -1) options->permit_tun = SSH_TUNMODE_NO; if (options->ip_qos_interactive == -1) - options->ip_qos_interactive = IPTOS_LOWDELAY; + options->ip_qos_interactive = IPTOS_DSCP_AF21; if (options->ip_qos_bulk == -1) - options->ip_qos_bulk = IPTOS_THROUGHPUT; + options->ip_qos_bulk = IPTOS_DSCP_CS1; if (options->version_addendum == NULL) options->version_addendum = xstrdup(""); if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) @@ -461,8 +481,8 @@ typedef enum { sHostKeyAlgorithms, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, - sAcceptEnv, sPermitTunnel, - sMatch, sPermitOpen, sForceCommand, sChrootDirectory, + sAcceptEnv, sSetEnv, sPermitTunnel, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sAllowAgentForwarding, sHostCertificate, sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, @@ -544,7 +564,7 @@ static struct { { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, - { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */ + { "skeyauthentication", sDeprecated, SSHCFG_GLOBAL }, { "checkmail", sDeprecated, SSHCFG_GLOBAL }, { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, { "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, @@ -592,11 +612,13 @@ static struct { { "authorizedkeysfile2", sDeprecated, SSHCFG_ALL }, { "useprivilegeseparation", sDeprecated, SSHCFG_GLOBAL}, { "acceptenv", sAcceptEnv, SSHCFG_ALL }, + { "setenv", sSetEnv, SSHCFG_ALL }, { "permittunnel", sPermitTunnel, SSHCFG_ALL }, { "permittty", sPermitTTY, SSHCFG_ALL }, { "permituserrc", sPermitUserRC, SSHCFG_ALL }, { "match", sMatch, SSHCFG_ALL }, { "permitopen", sPermitOpen, SSHCFG_ALL }, + { "permitlisten", sPermitListen, SSHCFG_ALL }, { "forcecommand", sForceCommand, SSHCFG_ALL }, { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, @@ -632,6 +654,20 @@ static struct { { -1, NULL } }; +/* Returns an opcode name from its number */ + +static const char * +lookup_opcode_name(ServerOpCodes code) +{ + u_int i; + + for (i = 0; keywords[i].name != NULL; i++) + if (keywords[i].opcode == code) + return(keywords[i].name); + return "UNKNOWN"; +} + + /* * Returns the number of the token pointed to by cp or sBadOption. */ @@ -814,43 +850,59 @@ process_queued_listen_addrs(ServerOptions *options) } /* - * Inform channels layer of permitopen options from configuration. + * Inform channels layer of permitopen options for a single forwarding + * direction (local/remote). */ -void -process_permitopen(struct ssh *ssh, ServerOptions *options) +static void +process_permitopen_list(struct ssh *ssh, ServerOpCodes opcode, + char **opens, u_int num_opens) { u_int i; int port; char *host, *arg, *oarg; + int where = opcode == sPermitOpen ? FORWARD_LOCAL : FORWARD_REMOTE; + const char *what = lookup_opcode_name(opcode); - channel_clear_adm_permitted_opens(ssh); - if (options->num_permitted_opens == 0) + channel_clear_permission(ssh, FORWARD_ADM, where); + if (num_opens == 0) return; /* permit any */ /* handle keywords: "any" / "none" */ - if (options->num_permitted_opens == 1 && - strcmp(options->permitted_opens[0], "any") == 0) + if (num_opens == 1 && strcmp(opens[0], "any") == 0) return; - if (options->num_permitted_opens == 1 && - strcmp(options->permitted_opens[0], "none") == 0) { - channel_disable_adm_local_opens(ssh); + if (num_opens == 1 && strcmp(opens[0], "none") == 0) { + channel_disable_admin(ssh, where); return; } /* Otherwise treat it as a list of permitted host:port */ - for (i = 0; i < options->num_permitted_opens; i++) { - oarg = arg = xstrdup(options->permitted_opens[i]); + for (i = 0; i < num_opens; i++) { + oarg = arg = xstrdup(opens[i]); host = hpdelim(&arg); if (host == NULL) - fatal("%s: missing host in PermitOpen", __func__); + fatal("%s: missing host in %s", __func__, what); host = cleanhostname(host); if (arg == NULL || ((port = permitopen_port(arg)) < 0)) - fatal("%s: bad port number in PermitOpen", __func__); + fatal("%s: bad port number in %s", __func__, what); /* Send it to channels layer */ - channel_add_adm_permitted_opens(ssh, host, port); + channel_add_permission(ssh, FORWARD_ADM, + where, host, port); free(oarg); } } +/* + * Inform channels layer of permitopen options from configuration. + */ +void +process_permitopen(struct ssh *ssh, ServerOptions *options) +{ + process_permitopen_list(ssh, sPermitOpen, + options->permitted_opens, options->num_permitted_opens); + process_permitopen_list(ssh, sPermitListen, + options->permitted_listens, + options->num_permitted_listens); +} + struct connection_info * get_connection_info(int populate, int use_dns) { @@ -1144,12 +1196,12 @@ process_server_config_line(ServerOptions *options, char *line, const char *filename, int linenum, int *activep, struct connection_info *connectinfo) { - char *cp, **charptr, *arg, *arg2, *p; + char *cp, ***chararrayptr, **charptr, *arg, *arg2, *p; int cmdline = 0, *intptr, value, value2, n, port; SyslogFacility *log_facility_ptr; LogLevel *log_level_ptr; ServerOpCodes opcode; - u_int i, flags = 0; + u_int i, *uintptr, uvalue, flags = 0; size_t len; long long val64; const struct multistate *multistate_ptr; @@ -1480,7 +1532,29 @@ process_server_config_line(ServerOptions *options, char *line, case sPermitUserEnvironment: intptr = &options->permit_user_env; - goto parse_flag; + charptr = &options->permit_user_env_whitelist; + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: missing argument.", + filename, linenum); + value = 0; + p = NULL; + if (strcmp(arg, "yes") == 0) + value = 1; + else if (strcmp(arg, "no") == 0) + value = 0; + else { + /* Pattern-list specified */ + value = 1; + p = xstrdup(arg); + } + if (*activep && *intptr == -1) { + *intptr = value; + *charptr = p; + p = NULL; + } + free(p); + break; case sCompression: intptr = &options->compression; @@ -1769,6 +1843,19 @@ process_server_config_line(ServerOptions *options, char *line, } break; + case sSetEnv: + uvalue = options->num_setenv; + while ((arg = strdelimw(&cp)) && *arg != '\0') { + if (strchr(arg, '=') == NULL) + fatal("%s line %d: Invalid environment.", + filename, linenum); + if (!*activep || uvalue != 0) + continue; + array_append(filename, linenum, "SetEnv", + &options->setenv, &options->num_setenv, arg); + } + break; + case sPermitTunnel: intptr = &options->permit_tun; arg = strdelim(&cp); @@ -1799,36 +1886,57 @@ process_server_config_line(ServerOptions *options, char *line, *activep = value; break; + case sPermitListen: case sPermitOpen: + if (opcode == sPermitListen) { + uintptr = &options->num_permitted_listens; + chararrayptr = &options->permitted_listens; + } else { + uintptr = &options->num_permitted_opens; + chararrayptr = &options->permitted_opens; + } arg = strdelim(&cp); if (!arg || *arg == '\0') - fatal("%s line %d: missing PermitOpen specification", - filename, linenum); - value = options->num_permitted_opens; /* modified later */ + fatal("%s line %d: missing %s specification", + filename, linenum, lookup_opcode_name(opcode)); + uvalue = *uintptr; /* modified later */ if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) { - if (*activep && value == 0) { - options->num_permitted_opens = 1; - options->permitted_opens = xcalloc(1, - sizeof(*options->permitted_opens)); - options->permitted_opens[0] = xstrdup(arg); + if (*activep && uvalue == 0) { + *uintptr = 1; + *chararrayptr = xcalloc(1, + sizeof(**chararrayptr)); + (*chararrayptr)[0] = xstrdup(arg); } break; } for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) { - arg2 = xstrdup(arg); - p = hpdelim(&arg); - if (p == NULL) - fatal("%s line %d: missing host in PermitOpen", - filename, linenum); - p = cleanhostname(p); - if (arg == NULL || ((port = permitopen_port(arg)) < 0)) - fatal("%s line %d: bad port number in " - "PermitOpen", filename, linenum); - if (*activep && value == 0) { + if (opcode == sPermitListen && + strchr(arg, ':') == NULL) { + /* + * Allow bare port number for PermitListen + * to indicate a wildcard listen host. + */ + xasprintf(&arg2, "*:%s", arg); + } else { + arg2 = xstrdup(arg); + p = hpdelim(&arg); + if (p == NULL) { + fatal("%s line %d: missing host in %s", + filename, linenum, + lookup_opcode_name(opcode)); + } + p = cleanhostname(p); + } + if (arg == NULL || + ((port = permitopen_port(arg)) < 0)) { + fatal("%s line %d: bad port number in %s", + filename, linenum, + lookup_opcode_name(opcode)); + } + if (*activep && uvalue == 0) { array_append(filename, linenum, - "PermitOpen", - &options->permitted_opens, - &options->num_permitted_opens, arg2); + lookup_opcode_name(opcode), + chararrayptr, uintptr, arg2); } free(arg2); } @@ -1951,7 +2059,7 @@ process_server_config_line(ServerOptions *options, char *line, case sAuthenticationMethods: if (options->num_auth_methods == 0) { value = 0; /* seen "any" pseudo-method */ - value2 = 0; /* sucessfully parsed any method */ + value2 = 0; /* successfully parsed any method */ while ((arg = strdelim(&cp)) && *arg != '\0') { if (strcmp(arg, "any") == 0) { if (options->num_auth_methods > 0) { @@ -2056,22 +2164,21 @@ process_server_config_line(ServerOptions *options, char *line, /* Reads the server configuration file. */ void -load_server_config(const char *filename, Buffer *conf) +load_server_config(const char *filename, struct sshbuf *conf) { - char line[4096], *cp; + char *line = NULL, *cp; + size_t linesize = 0; FILE *f; - int lineno = 0; + int r, lineno = 0; debug2("%s: filename %s", __func__, filename); if ((f = fopen(filename, "r")) == NULL) { perror(filename); exit(1); } - buffer_clear(conf); - while (fgets(line, sizeof(line), f)) { + sshbuf_reset(conf); + while (getline(&line, &linesize, f) != -1) { lineno++; - if (strlen(line) == sizeof(line) - 1) - fatal("%s line %d too long", filename, lineno); /* * Trim out comments and strip whitespace * NB - preserve newlines, they are needed to reproduce @@ -2080,12 +2187,14 @@ load_server_config(const char *filename, Buffer *conf) if ((cp = strchr(line, '#')) != NULL) memcpy(cp, "\n", 2); cp = line + strspn(line, " \t\r"); - - buffer_append(conf, cp, strlen(cp)); + if ((r = sshbuf_put(conf, cp, strlen(cp))) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); } - buffer_append(conf, "\0", 1); + free(line); + if ((r = sshbuf_put_u8(conf, 0)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); fclose(f); - debug2("%s: done config len = %d", __func__, buffer_len(conf)); + debug2("%s: done config len = %zu", __func__, sshbuf_len(conf)); } void @@ -2095,7 +2204,7 @@ parse_server_match_config(ServerOptions *options, ServerOptions mo; initialize_server_options(&mo); - parse_server_config(&mo, "reprocess config", &cfg, connectinfo); + parse_server_config(&mo, "reprocess config", cfg, connectinfo); copy_set_server_options(options, &mo, 0); } @@ -2135,7 +2244,7 @@ int parse_server_match_testspec(struct connection_info *ci, char *spec) * * If the preauth flag is set, we do not bother copying the string or * array values that are not used pre-authentication, because any that we - * do use must be explictly sent in mm_getpwnamallow(). + * do use must be explicitly sent in mm_getpwnamallow(). */ void copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) @@ -2239,13 +2348,13 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) #undef M_CP_STRARRAYOPT void -parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, - struct connection_info *connectinfo) +parse_server_config(ServerOptions *options, const char *filename, + struct sshbuf *conf, struct connection_info *connectinfo) { int active, linenum, bad_options = 0; char *cp, *obuf, *cbuf; - debug2("%s: config %s len %d", __func__, filename, buffer_len(conf)); + debug2("%s: config %s len %zu", __func__, filename, sshbuf_len(conf)); if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL) fatal("%s: sshbuf_dup_string failed", __func__); @@ -2307,17 +2416,6 @@ fmt_intarg(ServerOpCodes code, int val) } } -static const char * -lookup_opcode_name(ServerOpCodes 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(ServerOpCodes code, int val) { @@ -2471,7 +2569,6 @@ dump_config(ServerOptions *o) dump_cfg_fmtint(sStrictModes, o->strict_modes); dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive); dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd); - dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env); dump_cfg_fmtint(sCompression, o->compression); dump_cfg_fmtint(sGatewayPorts, o->fwd_opts.gateway_ports); dump_cfg_fmtint(sUseDNS, o->use_dns); @@ -2528,6 +2625,7 @@ dump_config(ServerOptions *o) dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups); dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups); dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env); + dump_cfg_strarray(sSetEnv, o->num_setenv, o->setenv); dump_cfg_strarray_oneline(sAuthenticationMethods, o->num_auth_methods, o->auth_methods); @@ -2562,4 +2660,20 @@ dump_config(ServerOptions *o) printf(" %s", o->permitted_opens[i]); } printf("\n"); + printf("permitlisten"); + if (o->num_permitted_listens == 0) + printf(" any"); + else { + for (i = 0; i < o->num_permitted_listens; i++) + printf(" %s", o->permitted_listens[i]); + } + printf("\n"); + + if (o->permit_user_env_whitelist == NULL) { + dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env); + } else { + printf("permituserenvironment %s\n", + o->permit_user_env_whitelist); + } + } |
