From 1760c9822376b69f97e71321cc2740190762940a Mon Sep 17 00:00:00 2001 From: dtucker Date: Tue, 10 Jun 2008 13:01:51 +0000 Subject: [PATCH] - dtucker@cvs.openbsd.org 2008/06/10 04:50:25 [sshd.c channels.h channels.c log.c servconf.c log.h servconf.h sshd.8] Add extended test mode (-T) and connection parameters for test mode (-C). -T causes sshd to write its effective configuration to stdout and exit. -C causes any relevant Match rules to be applied before output. The combination allows tesing of the parser and config files. ok deraadt djm --- ChangeLog | 6 ++ channels.c | 13 ++- channels.h | 3 +- log.c | 24 +++++- log.h | 6 +- servconf.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++--- servconf.h | 3 +- sshd.8 | 32 ++++++- sshd.c | 47 ++++++++++- 9 files changed, 353 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2fa7e7dd..99ae4063 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,12 @@ - djm@cvs.openbsd.org 2008/06/10 04:17:46 [sshd_config.5] better reference for pattern-list + - dtucker@cvs.openbsd.org 2008/06/10 04:50:25 + [sshd.c channels.h channels.c log.c servconf.c log.h servconf.h sshd.8] + Add extended test mode (-T) and connection parameters for test mode (-C). + -T causes sshd to write its effective configuration to stdout and exit. + -C causes any relevant Match rules to be applied before output. The + combination allows tesing of the parser and config files. ok deraadt djm 20080609 - (dtucker) OpenBSD CVS Sync diff --git a/channels.c b/channels.c index 99b23d75..6808d3a0 100644 --- a/channels.c +++ b/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.277 2008/05/09 16:17:51 markus Exp $ */ +/* $OpenBSD: channels.c,v 1.278 2008/06/10 04:50:25 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2784,6 +2784,17 @@ channel_clear_adm_permitted_opens(void) num_adm_permitted_opens = 0; } +void +channel_print_adm_permitted_opens(void) +{ + static int i; + + for (i = 0; i < num_adm_permitted_opens; i++) + if (permitted_adm_opens[i].host_to_connect != NULL) + printf(" %s:%d", permitted_adm_opens[i].host_to_connect, + permitted_adm_opens[i].port_to_connect); +} + /* Try to start non-blocking connect to next host in cctx list */ static int connect_next(struct channel_connect *cctx) diff --git a/channels.h b/channels.h index ec2435df..dc1f483e 100644 --- a/channels.h +++ b/channels.h @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.h,v 1.92 2008/05/09 16:21:13 markus Exp $ */ +/* $OpenBSD: channels.h,v 1.93 2008/06/10 04:50:25 dtucker Exp $ */ /* * Author: Tatu Ylonen @@ -235,6 +235,7 @@ void channel_add_permitted_opens(char *, int); int channel_add_adm_permitted_opens(char *, int); void channel_clear_permitted_opens(void); void channel_clear_adm_permitted_opens(void); +void channel_print_adm_permitted_opens(void); int channel_input_port_forward_request(int, int); Channel *channel_connect_to(const char *, u_short, char *, char *); Channel *channel_connect_by_listen_address(u_short, char *, char *); diff --git a/log.c b/log.c index fae5b043..4a8239b9 100644 --- a/log.c +++ b/log.c @@ -1,4 +1,4 @@ -/* $OpenBSD: log.c,v 1.40 2007/05/17 07:50:31 djm Exp $ */ +/* $OpenBSD: log.c,v 1.41 2008/06/10 04:50:25 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -114,6 +114,17 @@ log_facility_number(char *name) return SYSLOG_FACILITY_NOT_SET; } +const char * +log_facility_name(SyslogFacility facility) +{ + u_int i; + + for (i = 0; log_facilities[i].name; i++) + if (log_facilities[i].val == facility) + return log_facilities[i].name; + return NULL; +} + LogLevel log_level_number(char *name) { @@ -126,6 +137,17 @@ log_level_number(char *name) return SYSLOG_LEVEL_NOT_SET; } +const char * +log_level_name(LogLevel level) +{ + u_int i; + + for (i = 0; log_levels[i].name != NULL; i++) + if (log_levels[i].val == level) + return log_levels[i].name; + return NULL; +} + /* Error messages that should be logged. */ void diff --git a/log.h b/log.h index 7a8c5707..fa0996ad 100644 --- a/log.h +++ b/log.h @@ -1,4 +1,4 @@ -/* $OpenBSD: log.h,v 1.15 2006/08/18 09:13:25 deraadt Exp $ */ +/* $OpenBSD: log.h,v 1.16 2008/06/10 04:50:25 dtucker Exp $ */ /* * Author: Tatu Ylonen @@ -49,7 +49,9 @@ typedef enum { void log_init(char *, LogLevel, SyslogFacility, int); SyslogFacility log_facility_number(char *); -LogLevel log_level_number(char *); +const char * log_facility_name(SyslogFacility); +LogLevel log_level_number(char *); +const char * log_level_name(LogLevel); void fatal(const char *, ...) __dead __attribute__((format(printf, 1, 2))); void error(const char *, ...) __attribute__((format(printf, 1, 2))); diff --git a/servconf.c b/servconf.c index 07a20103..63704fb3 100644 --- a/servconf.c +++ b/servconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.c,v 1.181 2008/06/10 03:57:27 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.182 2008/06/10 04:50:25 dtucker Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -23,6 +23,7 @@ #include #include #include +#include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" @@ -417,6 +418,17 @@ static struct { { NULL, sBadOption, 0 } }; +static struct { + int val; + char *text; +} tunmode_desc[] = { + { SSH_TUNMODE_NO, "no" }, + { SSH_TUNMODE_POINTOPOINT, "point-to-point" }, + { SSH_TUNMODE_ETHERNET, "ethernet" }, + { SSH_TUNMODE_YES, "yes" }, + { -1, NULL } +}; + /* * Returns the number of the token pointed to by cp or sBadOption. */ @@ -1211,16 +1223,13 @@ process_server_config_line(ServerOptions *options, char *line, 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 + value = -1; + for (i = 0; tunmode_desc[i].val != -1; i++) + if (strcmp(tunmode_desc[i].text, arg) == 0) { + value = tunmode_desc[i].val; + break; + } + if (value == -1) fatal("%s line %d: Bad yes/point-to-point/ethernet/" "no argument: %s", filename, linenum, arg); if (*intptr == -1) @@ -1426,3 +1435,213 @@ parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, fatal("%s: terminating, %d bad configuration options", filename, bad_options); } + +static const char * +fmt_intarg(ServerOpCodes code, int val) +{ + if (code == sAddressFamily) { + switch (val) { + case AF_INET: + return "inet"; + case AF_INET6: + return "inet6"; + case AF_UNSPEC: + return "any"; + default: + return "UNKNOWN"; + } + } + if (code == sPermitRootLogin) { + switch (val) { + case PERMIT_NO_PASSWD: + return "without-passord"; + case PERMIT_FORCED_ONLY: + return "forced-commands-only"; + case PERMIT_YES: + return "yes"; + } + } + if (code == sProtocol) { + switch (val) { + case SSH_PROTO_1: + return "1"; + case SSH_PROTO_2: + return "2"; + case (SSH_PROTO_1|SSH_PROTO_2): + return "2,1"; + default: + return "UNKNOWN"; + } + } + if (code == sGatewayPorts && val == 2) + return "clientspecified"; + if (code == sCompression && val == COMP_DELAYED) + return "delayed"; + switch (val) { + case -1: + return "unset"; + case 0: + return "no"; + case 1: + return "yes"; + } + return "UNKNOWN"; +} + +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) +{ + printf("%s %d\n", lookup_opcode_name(code), val); +} + +static void +dump_cfg_fmtint(ServerOpCodes code, int val) +{ + printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val)); +} + +static void +dump_cfg_string(ServerOpCodes code, const char *val) +{ + if (val == NULL) + return; + printf("%s %s\n", lookup_opcode_name(code), val); +} + +static void +dump_cfg_strarray(ServerOpCodes 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]); +} + +void +dump_config(ServerOptions *o) +{ + u_int i; + int ret; + struct addrinfo *ai; + char addr[NI_MAXHOST], port[NI_MAXSERV], *s = NULL; + + /* these are usually at the top of the config */ + for (i = 0; i < o->num_ports; i++) + printf("port %d\n", o->ports[i]); + dump_cfg_fmtint(sProtocol, o->protocol); + dump_cfg_fmtint(sAddressFamily, o->address_family); + + /* ListenAddress must be after Port */ + for (ai = o->listen_addrs; ai; ai = ai->ai_next) { + if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, + sizeof(addr), port, sizeof(port), + NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { + error("getnameinfo failed: %.100s", + (ret != EAI_SYSTEM) ? gai_strerror(ret) : + strerror(errno)); + } else { + if (ai->ai_family == AF_INET6) + printf("listenaddress [%s]:%s\n", addr, port); + else + printf("listenaddress %s:%s\n", addr, port); + } + } + + /* integer arguments */ + dump_cfg_int(sServerKeyBits, o->server_key_bits); + dump_cfg_int(sLoginGraceTime, o->login_grace_time); + dump_cfg_int(sKeyRegenerationTime, o->key_regeneration_time); + dump_cfg_int(sX11DisplayOffset, o->x11_display_offset); + dump_cfg_int(sMaxAuthTries, o->max_authtries); + dump_cfg_int(sClientAliveInterval, o->client_alive_interval); + dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max); + + /* formatted integer arguments */ + dump_cfg_fmtint(sPermitRootLogin, o->permit_root_login); + dump_cfg_fmtint(sIgnoreRhosts, o->ignore_rhosts); + dump_cfg_fmtint(sIgnoreUserKnownHosts, o->ignore_user_known_hosts); + dump_cfg_fmtint(sRhostsRSAAuthentication, o->rhosts_rsa_authentication); + dump_cfg_fmtint(sHostbasedAuthentication, o->hostbased_authentication); + dump_cfg_fmtint(sHostbasedUsesNameFromPacketOnly, + o->hostbased_uses_name_from_packet_only); + dump_cfg_fmtint(sRSAAuthentication, o->rsa_authentication); + dump_cfg_fmtint(sPubkeyAuthentication, o->pubkey_authentication); + dump_cfg_fmtint(sKerberosAuthentication, o->kerberos_authentication); + dump_cfg_fmtint(sKerberosOrLocalPasswd, o->kerberos_or_local_passwd); + dump_cfg_fmtint(sKerberosTicketCleanup, o->kerberos_ticket_cleanup); + dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); + dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); + dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); + dump_cfg_fmtint(sKbdInteractiveAuthentication, + o->kbd_interactive_authentication); + dump_cfg_fmtint(sChallengeResponseAuthentication, + o->challenge_response_authentication); + dump_cfg_fmtint(sPrintMotd, o->print_motd); + dump_cfg_fmtint(sPrintLastLog, o->print_lastlog); + dump_cfg_fmtint(sX11Forwarding, o->x11_forwarding); + dump_cfg_fmtint(sX11UseLocalhost, o->x11_use_localhost); + 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(sUseLogin, o->use_login); + dump_cfg_fmtint(sCompression, o->compression); + dump_cfg_fmtint(sGatewayPorts, o->gateway_ports); + dump_cfg_fmtint(sUseDNS, o->use_dns); + dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); + dump_cfg_fmtint(sUsePrivilegeSeparation, use_privsep); + + /* string arguments */ + dump_cfg_string(sPidFile, o->pid_file); + dump_cfg_string(sXAuthLocation, o->xauth_location); + dump_cfg_string(sCiphers, o->ciphers); + dump_cfg_string(sMacs, o->macs); + dump_cfg_string(sBanner, o->banner); + dump_cfg_string(sAuthorizedKeysFile, o->authorized_keys_file); + dump_cfg_string(sAuthorizedKeysFile2, o->authorized_keys_file2); + dump_cfg_string(sForceCommand, o->adm_forced_command); + + /* string arguments requiring a lookup */ + dump_cfg_string(sLogLevel, log_level_name(o->log_level)); + dump_cfg_string(sLogFacility, log_facility_name(o->log_facility)); + + /* string array arguments */ + dump_cfg_strarray(sHostKeyFile, o->num_host_key_files, + o->host_key_files); + dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users); + dump_cfg_strarray(sDenyUsers, o->num_deny_users, o->deny_users); + 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); + + /* other arguments */ + for (i = 0; i < o->num_subsystems; i++) + printf("subsystem %s %s\n", o->subsystem_name[i], + o->subsystem_args[i]); + + printf("maxstartups %d:%d:%d\n", o->max_startups_begin, + o->max_startups_rate, o->max_startups); + + for (i = 0; tunmode_desc[i].val != -1; i++) + if (tunmode_desc[i].val == o->permit_tun) { + s = tunmode_desc[i].text; + break; + } + dump_cfg_string(sPermitTunnel, s); + + printf("permitopen"); + channel_print_adm_permitted_opens(); + printf("\n"); +} diff --git a/servconf.h b/servconf.h index 819a028c..40ac64f1 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.84 2008/05/08 12:21:16 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.85 2008/06/10 04:50:25 dtucker Exp $ */ /* * Author: Tatu Ylonen @@ -161,5 +161,6 @@ void parse_server_config(ServerOptions *, const char *, Buffer *, void parse_server_match_config(ServerOptions *, const char *, const char *, const char *); void copy_set_server_options(ServerOptions *, ServerOptions *, int); +void dump_config(ServerOptions *); #endif /* SERVCONF_H */ diff --git a/sshd.8 b/sshd.8 index 8cf9420c..d6535dd6 100644 --- a/sshd.8 +++ b/sshd.8 @@ -34,7 +34,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.241 2008/03/27 22:37:57 jmc Exp $ +.\" $OpenBSD: sshd.8,v 1.242 2008/06/10 04:50:25 dtucker Exp $ .Dd $Mdocdate$ .Dt SSHD 8 .Os @@ -44,8 +44,9 @@ .Sh SYNOPSIS .Nm sshd .Bk -words -.Op Fl 46Ddeiqt +.Op Fl 46DTdeiqt .Op Fl b Ar bits +.Op Fl C Ar connection_spec .Op Fl f Ar config_file .Op Fl g Ar login_grace_time .Op Fl h Ar host_key_file @@ -197,6 +198,33 @@ Only check the validity of the configuration file and sanity of the keys. This is useful for updating .Nm reliably as configuration options may change. +.It Fl T +Extended test mode. +Check the validity of the configuration file, output the effective configuration +to stdout and then exit. +Optionally, +.Cm Match +rules may be applied by specifying the connection parameters using one or more +.Fl C +options. +.It Fl C +Specify the connection parameters to use for the the +.Fl T +extended test mode. +If provided, any +.Cm Match +directives in the configuration file +that would apply to the specified user, host and address will be set before +the configuration is written to standard output. +The connection parameters are supplied as keyword=value pairs. +The keywords are +.Dq user , +.Dq host +and +.Dq addr +All are required and may be supplied in any order, either with multiple +.Fl C +options or as a comma-separated list. .It Fl u Ar len This option is used to specify the size of the field in the diff --git a/sshd.c b/sshd.c index aefbaaa4..ccff65d0 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.357 2008/05/08 12:02:23 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.358 2008/06/10 04:50:25 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1240,8 +1240,9 @@ main(int ac, char **av) int opt, i, on = 1; int sock_in = -1, sock_out = -1, newsock = -1; const char *remote_ip; + char *test_user = NULL, *test_host = NULL, *test_addr = NULL; int remote_port; - char *line; + char *line, *p, *cp; int config_s[2] = { -1 , -1 }; Key *key; Authctxt *authctxt; @@ -1276,7 +1277,7 @@ main(int ac, char **av) initialize_server_options(&options); /* Parse command-line arguments. */ - while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:dDeiqrtQR46")) != -1) { + while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:C:dDeiqrtQRT46")) != -1) { switch (opt) { case '4': options.address_family = AF_INET; @@ -1354,6 +1355,25 @@ main(int ac, char **av) case 't': test_flag = 1; break; + case 'T': + test_flag = 2; + break; + case 'C': + cp = optarg; + while ((p = strsep(&cp, ",")) && *p != '\0') { + if (strncmp(p, "addr=", 5) == 0) + test_addr = xstrdup(p + 5); + else if (strncmp(p, "host=", 5) == 0) + test_host = xstrdup(p + 5); + else if (strncmp(p, "user=", 5) == 0) + test_user = xstrdup(p + 5); + else { + fprintf(stderr, "Invalid test " + "mode specification %s\n", p); + exit(1); + } + } + break; case 'u': utmp_len = (u_int)strtonum(optarg, 0, MAXHOSTNAMELEN+1, NULL); if (utmp_len > MAXHOSTNAMELEN) { @@ -1415,6 +1435,20 @@ main(int ac, char **av) sensitive_data.have_ssh1_key = 0; sensitive_data.have_ssh2_key = 0; + /* + * If we're doing an extended config test, make sure we have all of + * the parameters we need. If we're not doing an extended test, + * do not silently ignore connection test params. + */ + if (test_flag >= 2 && (test_user != NULL || test_host != NULL || test_addr != NULL) + && (test_user == NULL || test_host == NULL || test_addr == NULL)) + fatal("user, host and addr are all required when testing " + "Match configs"); + if (test_flag < 2 && (test_user != NULL || test_host != NULL || + test_addr != NULL)) + fatal("Config test connection parameter (-C) provided without " + "test mode (-T)"); + /* Fetch our configuration */ buffer_init(&cfg); if (rexeced_flag) @@ -1543,6 +1577,13 @@ main(int ac, char **av) "world-writable.", _PATH_PRIVSEP_CHROOT_DIR); } + if (test_flag > 1) { + if (test_user != NULL && test_addr != NULL && test_host != NULL) + parse_server_match_config(&options, test_user, + test_host, test_addr); + dump_config(&options); + } + /* Configuration looks good, so exit if in test mode. */ if (test_flag) exit(0); -- 2.45.1