diff --git a/include/sudo_util.h b/include/sudo_util.h index 7437099f9..25060c36a 100644 --- a/include/sudo_util.h +++ b/include/sudo_util.h @@ -208,8 +208,8 @@ __dso_public int sudo_getgrouplist2_v1(const char *name, gid_t basegid, GETGROUP #define sudo_getgrouplist2(_a, _b, _c, _d) sudo_getgrouplist2_v1((_a), (_b), (_c), (_d)) /* host_port.c */ -__dso_public bool sudo_parse_host_port_v1(char *str, char **hostp, char **portp, char *defport); -#define sudo_parse_host_port(_a, _b, _c, _d) sudo_parse_host_port_v1((_a), (_b), (_c), (_d)) +__dso_public bool sudo_parse_host_port_v1(char *str, char **hostp, char **portp, bool *tlsp, char *defport, char *defport_tls); +#define sudo_parse_host_port(_a, _b, _c, _d, _e, _f) sudo_parse_host_port_v1((_a), (_b), (_c), (_d), (_e), (_f)) /* key_val.c */ __dso_public char *sudo_new_key_val_v1(const char *key, const char *value); diff --git a/lib/util/host_port.c b/lib/util/host_port.c index e2ecde6da..b8415e6cb 100644 --- a/lib/util/host_port.c +++ b/lib/util/host_port.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2019 Todd C. Miller + * Copyright (c) 2019-2020 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -28,6 +28,7 @@ #include #include #include +#include #include "sudo_gettext.h" /* must be included before sudo_compat.h */ #include "sudo_compat.h" @@ -40,10 +41,12 @@ * Fills in hostp and portp which may point within str, which is modified. */ bool -sudo_parse_host_port_v1(char *str, char **hostp, char **portp, char *defport) +sudo_parse_host_port_v1(char *str, char **hostp, char **portp, bool *tlsp, + char *defport, char *defport_tls) { - char *port, *host = str; + char *flags, *port, *host = str; bool ret = false; + bool tls = false; debug_decl(sudo_parse_host_port, SUDO_DEBUG_UTIL); /* Check for IPv6 address like [::0] followed by optional port */ @@ -56,11 +59,17 @@ sudo_parse_host_port_v1(char *str, char **hostp, char **portp, char *defport) goto done; } *port++ = '\0'; - if (*port == ':') { - port++; - } else if (*port == '\0') { - port = NULL; /* no port specified */ - } else { + switch (*port) { + case ':': + port++; + break; + case '\0': + port = NULL; /* no port specified */ + break; + case '(': + /* flag, handled below */ + break; + default: sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "invalid IPv6 address %s", str); goto done; @@ -71,13 +80,24 @@ sudo_parse_host_port_v1(char *str, char **hostp, char **portp, char *defport) *port++ = '\0'; } + /* Check for optional tls flag at the end. */ + flags = strchr(port ? port : host, '('); + if (flags != NULL) { + if (strcasecmp(flags, "(tls)") == 0) + tls = true; + *flags = '\0'; + if (port == flags) + port = NULL; + } + if (port == NULL) - port = defport; + port = tls ? defport_tls : defport; else if (*port == '\0') goto done; *hostp = host; *portp = port; + *tlsp = tls; ret = true; diff --git a/lib/util/regress/host_port/host_port_test.c b/lib/util/regress/host_port/host_port_test.c index 520a04db9..3454c8025 100644 --- a/lib/util/regress/host_port/host_port_test.c +++ b/lib/util/regress/host_port/host_port_test.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2010 Todd C. Miller + * Copyright (c) 2019-2020 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -47,22 +47,34 @@ struct host_port_test { const char *str; /* input string */ const char *host; /* parsed host */ const char *port; /* parsed port */ + bool tls; /* parsed TLS flag */ char *defport; /* default port */ + char *defport_tls; /* default port */ bool ret; /* return value */ }; static struct host_port_test test_data[] = { - { "xerxes", "xerxes", "12345", "12345", true }, - { "xerxes:12345", "xerxes", "12345", "67890", true }, - { "127.0.0.1", "127.0.0.1", "12345", "12345", true }, - { "127.0.0.1:12345", "127.0.0.1", "12345", "67890", true }, - { "[::1]", "::1", "12345", "12345", true }, - { "[::1]:12345", "::1", "12345", "67890", true }, + /* No TLS */ + { "xerxes", "xerxes", "12345", false, "12345", NULL, true }, + { "xerxes:12345", "xerxes", "12345", false, "67890", NULL, true }, + { "127.0.0.1", "127.0.0.1", "12345", false, "12345", NULL, true }, + { "127.0.0.1:12345", "127.0.0.1", "12345", false, "67890", NULL, true }, + { "[::1]", "::1", "12345", false, "12345", NULL, true }, + { "[::1]:12345", "::1", "12345", false, "67890", NULL, true }, - { "xerxes:", NULL, NULL, "12345", false }, /* missing port */ - { "127.0.0.1:", NULL, NULL, "12345", false }, /* missing port */ - { "[::1:12345", NULL, NULL, "67890", false }, /* missing bracket */ - { "[::1]:", NULL, NULL, "12345", false }, /* missing port */ + /* With TLS */ + { "xerxes(tls)", "xerxes", "12345", true, "5678", "12345", true }, + { "xerxes:12345(tls)", "xerxes", "12345", true, "5678", "67890", true }, + { "127.0.0.1(tls)", "127.0.0.1", "12345", true, "5678", "12345", true }, + { "127.0.0.1:12345(tls)", "127.0.0.1", "12345", true, "5678", "67890", true }, + { "[::1](tls)", "::1", "12345", true, "5678", "12345", true }, + { "[::1]:12345(tls)", "::1", "12345", true, "5678", "67890", true }, + + /* Errors */ + { "xerxes:", NULL, NULL, false, "12345", NULL, false }, /* missing port */ + { "127.0.0.1:", NULL, NULL, false, "12345", NULL, false }, /* missing port */ + { "[::1:12345", NULL, NULL, false, "67890", NULL, false }, /* missing bracket */ + { "[::1]:", NULL, NULL, false, "12345", NULL, false }, /* missing port */ { NULL } }; @@ -71,18 +83,20 @@ main(int argc, char *argv[]) { int i, errors = 0, ntests = 0; char *host, *port, *copy = NULL; - bool ret; + bool ret, tls; initprogname(argc > 0 ? argv[0] : "host_port_test"); for (i = 0; test_data[i].str != NULL; i++) { host = port = NULL; + tls = false; free(copy); if ((copy = strdup(test_data[i].str)) == NULL) sudo_fatal_nodebug(NULL); ntests++; - ret = sudo_parse_host_port(copy, &host, &port, test_data[i].defport); + ret = sudo_parse_host_port(copy, &host, &port, &tls, + test_data[i].defport, test_data[i].defport_tls); if (ret != test_data[i].ret) { sudo_warnx_nodebug("test #%d: %s: returned %s, expected %s", ntests, test_data[i].str, ret ? "true" : "false", @@ -117,6 +131,13 @@ main(int argc, char *argv[]) errors++; continue; } + if (tls != test_data[i].tls) { + sudo_warnx_nodebug("test #%d: %s: bad tls, expected %s, got %s", + ntests, test_data[i].str, test_data[i].tls ? "true" : "false", + tls ? "true" : "false"); + errors++; + continue; + } } free(copy); if (ntests != 0) { diff --git a/logsrvd/logsrvd_conf.c b/logsrvd/logsrvd_conf.c index d3fe336b2..d1c0eacc9 100644 --- a/logsrvd/logsrvd_conf.c +++ b/logsrvd/logsrvd_conf.c @@ -379,7 +379,7 @@ cb_listen_address(struct logsrvd_config *config, const char *str) { struct addrinfo hints, *res, *res0 = NULL; char *copy, *host, *port; - bool ret = false; + bool tls, ret = false; int error; debug_decl(cb_iolog_mode, SUDO_DEBUG_UTIL); @@ -389,7 +389,8 @@ cb_listen_address(struct logsrvd_config *config, const char *str) } /* Parse host[:port] */ - if (!sudo_parse_host_port(copy, &host, &port, DEFAULT_PORT_STR)) + if (!sudo_parse_host_port(copy, &host, &port, &tls, DEFAULT_PORT_STR, + DEFAULT_PORT_STR)) goto done; if (host[0] == '*' && host[1] == '\0') host = NULL; diff --git a/plugins/sudoers/iolog_client.c b/plugins/sudoers/iolog_client.c index f8723dbbc..13a7c7489 100644 --- a/plugins/sudoers/iolog_client.c +++ b/plugins/sudoers/iolog_client.c @@ -215,11 +215,13 @@ log_server_connect(struct sudoers_str_list *servers, bool tcp_keepalive, char *copy, *host, *port; const char *cause = NULL; int sock = -1; + bool tls; debug_decl(restore_nproc, SUDOERS_DEBUG_UTIL); STAILQ_FOREACH(server, servers, entries) { copy = strdup(server->str); - if (!sudo_parse_host_port(copy, &host, &port, DEFAULT_PORT_STR)) { + if (!sudo_parse_host_port(copy, &host, &port, &tls, DEFAULT_PORT_STR, + DEFAULT_PORT_STR)) { free(copy); continue; }