Escape/unescape commas when serializing/deserializing a stringlist.

This commit is contained in:
Todd C. Miller
2022-01-28 08:52:42 -07:00
parent 9b93961b3e
commit 5ea0bf4ee2
9 changed files with 340 additions and 53 deletions

View File

@@ -799,6 +799,7 @@ plugins/sudoers/regress/parser/check_digest.out.ok
plugins/sudoers/regress/parser/check_fill.c plugins/sudoers/regress/parser/check_fill.c
plugins/sudoers/regress/parser/check_gentime.c plugins/sudoers/regress/parser/check_gentime.c
plugins/sudoers/regress/parser/check_hexchar.c plugins/sudoers/regress/parser/check_hexchar.c
plugins/sudoers/regress/serialize_list/check_serialize_list.c
plugins/sudoers/regress/starttime/check_starttime.c plugins/sudoers/regress/starttime/check_starttime.c
plugins/sudoers/regress/sudoers/test1.in plugins/sudoers/regress/sudoers/test1.in
plugins/sudoers/regress/sudoers/test1.json.ok plugins/sudoers/regress/sudoers/test1.json.ok
@@ -1011,6 +1012,7 @@ plugins/sudoers/regress/visudo/test8.out.ok
plugins/sudoers/regress/visudo/test8.sh plugins/sudoers/regress/visudo/test8.sh
plugins/sudoers/regress/visudo/test9.out.ok plugins/sudoers/regress/visudo/test9.out.ok
plugins/sudoers/regress/visudo/test9.sh plugins/sudoers/regress/visudo/test9.sh
plugins/sudoers/serialize_list.c
plugins/sudoers/set_perms.c plugins/sudoers/set_perms.c
plugins/sudoers/solaris_audit.c plugins/sudoers/solaris_audit.c
plugins/sudoers/solaris_audit.h plugins/sudoers/solaris_audit.h
@@ -1047,6 +1049,7 @@ plugins/sudoers/toke_util.c
plugins/sudoers/tsdump.c plugins/sudoers/tsdump.c
plugins/sudoers/tsgetgrpw.c plugins/sudoers/tsgetgrpw.c
plugins/sudoers/tsgetgrpw.h plugins/sudoers/tsgetgrpw.h
plugins/sudoers/unesc_str.c
plugins/sudoers/visudo.c plugins/sudoers/visudo.c
plugins/system_group/Makefile.in plugins/system_group/Makefile.in
plugins/system_group/system_group.c plugins/system_group/system_group.c

View File

@@ -158,8 +158,8 @@ PROGS = sudoers.la visudo sudoreplay cvtsudoers testsudoers
# Regression tests # Regression tests
TEST_PROGS = check_addr check_base64 check_digest check_editor \ TEST_PROGS = check_addr check_base64 check_digest check_editor \
check_env_pattern check_exptilde check_fill check_gentime \ check_env_pattern check_exptilde check_fill check_gentime \
check_hexchar check_iolog_plugin check_starttime \ check_hexchar check_iolog_plugin check_serialize_list \
check_unesc @SUDOERS_TEST_PROGS@ check_starttime check_unesc @SUDOERS_TEST_PROGS@
# Fuzzers # Fuzzers
LIB_FUZZING_ENGINE = @FUZZ_ENGINE@ LIB_FUZZING_ENGINE = @FUZZ_ENGINE@
@@ -185,9 +185,9 @@ SUDOERS_OBJS = $(AUTH_OBJS) audit.lo boottime.lo check.lo editor.lo env.lo \
sudoers_hooks.lo env_pattern.lo file.lo find_path.lo \ sudoers_hooks.lo env_pattern.lo file.lo find_path.lo \
fmtsudoers.lo gc.lo goodpath.lo group_plugin.lo interfaces.lo \ fmtsudoers.lo gc.lo goodpath.lo group_plugin.lo interfaces.lo \
iolog.lo iolog_path_escapes.lo locale.lo log_client.lo \ iolog.lo iolog_path_escapes.lo locale.lo log_client.lo \
logging.lo parse.lo policy.lo prompt.lo set_perms.lo \ logging.lo parse.lo policy.lo prompt.lo serialize_list.lo \
starttime.lo strlcpy_unesc.lo strvec_join.lo sudo_nss.lo \ set_perms.lo starttime.lo strlcpy_unesc.lo strvec_join.lo \
sudoers.lo timestamp.lo @SUDOERS_OBJS@ sudo_nss.lo sudoers.lo timestamp.lo unesc_str.lo @SUDOERS_OBJS@
SUDOERS_IOBJS = $(SUDOERS_OBJS:.lo=.i) SUDOERS_IOBJS = $(SUDOERS_OBJS:.lo=.i)
@@ -241,17 +241,22 @@ CHECK_HEXCHAR_OBJS = check_hexchar.o hexchar.lo sudoers_debug.lo
CHECK_IOLOG_PLUGIN_OBJS = check_iolog_plugin.o iolog.lo log_client.lo \ CHECK_IOLOG_PLUGIN_OBJS = check_iolog_plugin.o iolog.lo log_client.lo \
locale.lo pwutil.lo pwutil_impl.lo redblack.lo \ locale.lo pwutil.lo pwutil_impl.lo redblack.lo \
strlist.lo sudoers_debug.lo strlist.lo sudoers_debug.lo unesc_str.lo
CHECK_SYMBOLS_OBJS = check_symbols.o CHECK_SYMBOLS_OBJS = check_symbols.o
CHECK_STARTTIME_OBJS = check_starttime.o starttime.lo sudoers_debug.lo CHECK_STARTTIME_OBJS = check_starttime.o starttime.lo sudoers_debug.lo
CHECK_UNESC_OBJS = check_unesc.o strlcpy_unesc.lo strvec_join.lo sudoers_debug.lo CHECK_UNESC_OBJS = check_unesc.o strlcpy_unesc.lo strvec_join.lo \
sudoers_debug.lo unesc_str.lo
CHECK_SERIALIZE_LIST_OBJS = check_serialize_list.lo serialize_list.lo \
sudoers_debug.lo
FUZZ_POLICY_OBJS = editor.lo env.lo env_pattern.lo fuzz_policy.o fuzz_stubs.o \ FUZZ_POLICY_OBJS = editor.lo env.lo env_pattern.lo fuzz_policy.o fuzz_stubs.o \
gc.lo iolog_path_escapes.lo locale.lo policy.lo \ gc.lo iolog_path_escapes.lo locale.lo policy.lo \
strlcpy_unesc.lo strvec_join.lo sudoers.lo sudoers_hooks.lo serialize_list.lo strlcpy_unesc.lo strvec_join.lo \
sudoers.lo sudoers_hooks.lo
FUZZ_POLICY_CORPUS = $(srcdir)/regress/corpus/seed/policy/policy.* FUZZ_POLICY_CORPUS = $(srcdir)/regress/corpus/seed/policy/policy.*
@@ -386,6 +391,9 @@ check_hexchar: $(CHECK_HEXCHAR_OBJS) $(LIBUTIL)
check_iolog_plugin: $(CHECK_IOLOG_PLUGIN_OBJS) $(LIBUTIL) $(LIBIOLOG) $(LIBLOGSRV) check_iolog_plugin: $(CHECK_IOLOG_PLUGIN_OBJS) $(LIBUTIL) $(LIBIOLOG) $(LIBLOGSRV)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_PLUGIN_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBIOLOG) $(LIBLOGSRV) @LIBTLS@ $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_PLUGIN_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBIOLOG) $(LIBLOGSRV) @LIBTLS@
check_serialize_list: $(CHECK_SERIALIZE_LIST_OBJS) $(LIBUTIL)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_SERIALIZE_LIST_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS)
check_starttime: $(CHECK_STARTTIME_OBJS) $(LIBUTIL) check_starttime: $(CHECK_STARTTIME_OBJS) $(LIBUTIL)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_STARTTIME_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_STARTTIME_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS)
@@ -659,6 +667,7 @@ check: $(TEST_PROGS) visudo testsudoers cvtsudoers check-fuzzer
./check_hexchar || rval=`expr $$rval + $$?`; \ ./check_hexchar || rval=`expr $$rval + $$?`; \
mkdir -p regress/iolog_plugin; \ mkdir -p regress/iolog_plugin; \
./check_iolog_plugin regress/iolog_plugin/iolog || rval=`expr $$rval + $$?`; \ ./check_iolog_plugin regress/iolog_plugin/iolog || rval=`expr $$rval + $$?`; \
./check_serialize_list || rval=`expr $$rval + $$?`; \
./check_starttime || rval=`expr $$rval + $$?`; \ ./check_starttime || rval=`expr $$rval + $$?`; \
./check_unesc || rval=`expr $$rval + $$?`; \ ./check_unesc || rval=`expr $$rval + $$?`; \
if test -f check_symbols; then \ if test -f check_symbols; then \
@@ -1259,6 +1268,34 @@ check_iolog_plugin.i: $(srcdir)/regress/iolog_plugin/check_iolog_plugin.c \
$(CC) -E -o $@ $(CPPFLAGS) $< $(CC) -E -o $@ $(CPPFLAGS) $<
check_iolog_plugin.plog: check_iolog_plugin.i check_iolog_plugin.plog: check_iolog_plugin.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/iolog_plugin/check_iolog_plugin.c --i-file $< --output-file $@ rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/iolog_plugin/check_iolog_plugin.c --i-file $< --output-file $@
check_serialize_list.lo: \
$(srcdir)/regress/serialize_list/check_serialize_list.c \
$(devdir)/def_data.h $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_eventlog.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/defaults.h \
$(srcdir)/logging.h $(srcdir)/parse.h \
$(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
$(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/serialize_list/check_serialize_list.c
check_serialize_list.i: \
$(srcdir)/regress/serialize_list/check_serialize_list.c \
$(devdir)/def_data.h $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_eventlog.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/defaults.h \
$(srcdir)/logging.h $(srcdir)/parse.h \
$(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
$(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(CC) -E -o $@ $(CPPFLAGS) $<
check_serialize_list.plog: check_serialize_list.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/serialize_list/check_serialize_list.c --i-file $< --output-file $@
check_starttime.o: $(srcdir)/regress/starttime/check_starttime.c \ check_starttime.o: $(srcdir)/regress/starttime/check_starttime.c \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_plugin.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_plugin.h \
@@ -2671,6 +2708,30 @@ securid5.i: $(authdir)/securid5.c $(authdir)/sudo_auth.h $(devdir)/def_data.h \
$(CC) -E -o $@ $(CPPFLAGS) $< $(CC) -E -o $@ $(CPPFLAGS) $<
securid5.plog: securid5.i securid5.plog: securid5.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(authdir)/securid5.c --i-file $< --output-file $@ rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(authdir)/securid5.c --i-file $< --output-file $@
serialize_list.lo: $(srcdir)/serialize_list.c $(devdir)/def_data.h \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_eventlog.h $(incdir)/sudo_fatal.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \
$(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
$(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/serialize_list.c
serialize_list.i: $(srcdir)/serialize_list.c $(devdir)/def_data.h \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_eventlog.h $(incdir)/sudo_fatal.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \
$(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
$(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(CC) -E -o $@ $(CPPFLAGS) $<
serialize_list.plog: serialize_list.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/serialize_list.c --i-file $< --output-file $@
set_perms.lo: $(srcdir)/set_perms.c $(devdir)/def_data.h \ set_perms.lo: $(srcdir)/set_perms.c $(devdir)/def_data.h \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
@@ -3213,6 +3274,12 @@ tsgetgrpw.i: $(srcdir)/tsgetgrpw.c $(devdir)/def_data.h \
$(CC) -E -o $@ $(CPPFLAGS) $< $(CC) -E -o $@ $(CPPFLAGS) $<
tsgetgrpw.plog: tsgetgrpw.i tsgetgrpw.plog: tsgetgrpw.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/tsgetgrpw.c --i-file $< --output-file $@ rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/tsgetgrpw.c --i-file $< --output-file $@
unesc_str.lo: $(srcdir)/unesc_str.c
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/unesc_str.c
unesc_str.i: $(srcdir)/unesc_str.c
$(CC) -E -o $@ $(CPPFLAGS) $<
unesc_str.plog: unesc_str.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/unesc_str.c --i-file $< --output-file $@
visudo.o: $(srcdir)/visudo.c $(devdir)/def_data.h $(devdir)/gram.h \ visudo.o: $(srcdir)/visudo.c $(devdir)/def_data.h $(devdir)/gram.h \
$(incdir)/compat/getopt.h $(incdir)/compat/stdbool.h \ $(incdir)/compat/getopt.h $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \

View File

@@ -211,7 +211,6 @@ free_iolog_details(void)
/* /*
* Convert a comma-separated list to a string list. * Convert a comma-separated list to a string list.
* XXX - handle escaped commas
*/ */
static struct sudoers_str_list * static struct sudoers_str_list *
deserialize_stringlist(const char *s) deserialize_stringlist(const char *s)
@@ -235,6 +234,7 @@ deserialize_stringlist(const char *s)
free(str); free(str);
goto bad; goto bad;
} }
unescape_string(str->str);
STAILQ_INSERT_TAIL(strlist, str, entries); STAILQ_INSERT_TAIL(strlist, str, entries);
} }
if (STAILQ_EMPTY(strlist)) if (STAILQ_EMPTY(strlist))
@@ -265,9 +265,9 @@ set_passprompt_regex(const char *cstr)
goto bad; goto bad;
} }
/* XXX - handle escaped commas */
for ((cp = strtok_r(str, ",", &last)); cp != NULL; for ((cp = strtok_r(str, ",", &last)); cp != NULL;
(cp = strtok_r(NULL, ",", &last))) { (cp = strtok_r(NULL, ",", &last))) {
unescape_string(cp);
if (!iolog_pwfilt_add(handle, cp)) if (!iolog_pwfilt_add(handle, cp))
goto bad; goto bad;
} }

View File

@@ -572,46 +572,6 @@ bad:
debug_return_int(MODE_ERROR); debug_return_int(MODE_ERROR);
} }
/*
* Convert struct list_members to a comma-separated string with
* the given variable name.
* XXX - escape commas in member values
*/
static char *
serialize_list(const char *varname, struct list_members *members)
{
struct list_member *lm, *next;
size_t len, result_size;
char *result;
debug_decl(serialize_list, SUDOERS_DEBUG_PLUGIN);
result_size = strlen(varname) + 1;
SLIST_FOREACH(lm, members, entries) {
result_size += strlen(lm->value) + 1;
}
if ((result = malloc(result_size)) == NULL)
goto bad;
/* No need to check len for overflow here. */
len = strlcpy(result, varname, result_size);
result[len++] = '=';
result[len] = '\0';
SLIST_FOREACH_SAFE(lm, members, entries, next) {
len = strlcat(result, lm->value, result_size);
if (len + (next != NULL) >= result_size) {
sudo_warnx(U_("internal error, %s overflow"), __func__);
goto bad;
}
if (next != NULL) {
result[len++] = ',';
result[len] = '\0';
}
}
debug_return_str(result);
bad:
free(result);
debug_return_str(NULL);
}
/* /*
* Store the execution environment and other front-end settings. * Store the execution environment and other front-end settings.
* Builds up the command_info list and sets argv and envp. * Builds up the command_info list and sets argv and envp.

View File

@@ -0,0 +1,82 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2022 Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SUDO_ERROR_WRAP 0
#include "sudoers.h"
sudo_dso_public int main(int argc, char *argv[]);
static void
test_serialize_list(int *ntests_out, int *errors_out)
{
int ntests = *ntests_out;
int errors = *errors_out;
const char *expected = "myvar=a value with spaces,this\\,and\\,that,\\,";
struct list_members members = SLIST_HEAD_INITIALIZER(members);
struct list_member lm1, lm2, lm3;
char *result;
lm1.value = "a value with spaces";
lm2.value = "this,and,that";
lm3.value = ",";
SLIST_INSERT_HEAD(&members, &lm3, entries);
SLIST_INSERT_HEAD(&members, &lm2, entries);
SLIST_INSERT_HEAD(&members, &lm1, entries);
ntests++;
result = serialize_list("myvar", &members);
if (result == NULL) {
sudo_warnx("serialize_list returns NULL");
++errors;
goto done;
}
ntests++;
if (strcmp(result, expected) != 0) {
sudo_warnx("got \"%s\", expected \"%s\"", result, expected);
++errors;
goto done;
}
done:
*ntests_out = ntests;
*errors_out = errors;
}
int
main(int argc, char *argv[])
{
int ntests = 0, errors = 0;
initprogname(argc > 0 ? argv[0] : "check_serialize_list");
test_serialize_list(&ntests, &errors);
if (ntests != 0) {
printf("%s: %d tests run, %d errors, %d%% success rate\n",
getprogname(), ntests, errors, (ntests - errors) * 100 / ntests);
}
exit(errors);
}

View File

@@ -1,7 +1,7 @@
/* /*
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
* *
* Copyright (c) 2021 Todd C. Miller <Todd.Miller@sudo.ws> * Copyright (c) 2021-2022 Todd C. Miller <Todd.Miller@sudo.ws>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -31,7 +31,10 @@ struct test_data {
char *result; char *result;
size_t result_len; size_t result_len;
size_t bufsize; size_t bufsize;
} test_data[] = { };
/* strlcpy_unescape() does not unescape whitespace */
static struct test_data strlcpy_unescape_test_data[] = {
{ "\\\0ABC", "\\", 1, 2 }, /* 1 */ { "\\\0ABC", "\\", 1, 2 }, /* 1 */
{ "\\ \\;", "\\ ;", 3, 4 }, /* 2 */ { "\\ \\;", "\\ ;", 3, 4 }, /* 2 */
{ "\\\t\\;", "\\\t;", 3, 4 }, /* 3 */ { "\\\t\\;", "\\\t;", 3, 4 }, /* 3 */
@@ -43,6 +46,20 @@ struct test_data {
{ NULL } { NULL }
}; };
/* unescape_string() _does_ unescape whitespace */
static struct test_data unescape_string_test_data[] = {
{ "foo\\ bar", "foo bar", 7, 8 }, /* 1 */
{ "foo\\,bar", "foo,bar", 7, 8 }, /* 2 */
{ "baz \\", "baz \\", 5, 5 }, /* 3 */
{ "\\foo", "foo", 3, 4 }, /* 4 */
{ "var=aaa,b\\,b", "var=aaa,b,b", 11, 12 }, /* 5 */
{ "\\a\\ b\\ c\\\\", "a b c\\", 6, 10 }, /* 6 */
{ "\\", "\\", 1, 1 }, /* 7 */
{ "foo", "foo", 3, 3 }, /* 8 */
{ "", "", 0, 0 }, /* 9 */
{ NULL }
};
sudo_dso_public int main(int argc, char *argv[]); sudo_dso_public int main(int argc, char *argv[]);
static void static void
@@ -54,7 +71,7 @@ test_strlcpy_unescape(int *ntests_out, int *errors_out)
char buf[1024]; char buf[1024];
size_t len; size_t len;
for (td = test_data; td->input != NULL; td++) { for (td = strlcpy_unescape_test_data; td->input != NULL; td++) {
ntests++; ntests++;
memset(buf, 'A', sizeof(buf)); memset(buf, 'A', sizeof(buf));
len = strlcpy_unescape(buf, td->input, td->bufsize); len = strlcpy_unescape(buf, td->input, td->bufsize);
@@ -85,6 +102,31 @@ test_strlcpy_unescape(int *ntests_out, int *errors_out)
*errors_out = errors; *errors_out = errors;
} }
static void
test_unescape_string(int *ntests_out, int *errors_out)
{
int ntests = *ntests_out;
int errors = *errors_out;
struct test_data *td;
char buf[1024];
for (td = unescape_string_test_data; td->input != NULL; td++) {
ntests++;
memset(buf, 'A', sizeof(buf));
memcpy(buf, td->input, td->bufsize);
buf[td->bufsize] = '\0';
unescape_string(buf);
if (strcmp(td->result, buf) != 0) {
sudo_warnx("%d: \"%s\": got \"%s\", expected \"%s\"",
ntests, td->input, buf, td->result);
errors++;
}
}
*ntests_out = ntests;
*errors_out = errors;
}
static void static void
test_strvec_join(char sep, int *ntests_out, int *errors_out) test_strvec_join(char sep, int *ntests_out, int *errors_out)
{ {
@@ -132,6 +174,9 @@ main(int argc, char *argv[])
/* strlcpy_unescape tests */ /* strlcpy_unescape tests */
test_strlcpy_unescape(&ntests, &errors); test_strlcpy_unescape(&ntests, &errors);
/* unescape_string test */
test_unescape_string(&ntests, &errors);
/* strvec_join test */ /* strvec_join test */
test_strvec_join(' ', &ntests, &errors); test_strvec_join(' ', &ntests, &errors);
test_strvec_join('\n', &ntests, &errors); test_strvec_join('\n', &ntests, &errors);

View File

@@ -0,0 +1,82 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2019, 2022 Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This is an open source non-commercial project. Dear PVS-Studio, please check it.
* PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sudoers.h"
/*
* Convert struct list_members to a comma-separated string with
* the given variable name. Escapes backslashes and commas.
*/
char *
serialize_list(const char *varname, struct list_members *members)
{
struct list_member *lm, *next;
size_t len, result_size;
char *cp, *result;
debug_decl(serialize_list, SUDOERS_DEBUG_PLUGIN);
result_size = strlen(varname) + 1;
SLIST_FOREACH(lm, members, entries) {
for (cp = lm->value; *cp != '\0'; cp++) {
result_size++;
if (*cp == '\\' || *cp == ',')
result_size++;
}
result_size++;
}
if ((result = malloc(result_size)) == NULL)
goto bad;
/* No need to check len for overflow here. */
len = strlcpy(result, varname, result_size);
result[len++] = '=';
SLIST_FOREACH_SAFE(lm, members, entries, next) {
for (cp = lm->value; *cp != '\0'; cp++) {
bool escape = (*cp == '\\' || *cp == ',');
if (len + 1 + escape >= result_size) {
sudo_warnx(U_("internal error, %s overflow"), __func__);
goto bad;
}
if (escape)
result[len++] = '\\';
result[len++] = *cp;
}
if (next != NULL) {
if (len + 1 >= result_size) {
sudo_warnx(U_("internal error, %s overflow"), __func__);
goto bad;
}
result[len++] = ',';
}
result[len] = '\0';
}
debug_return_str(result);
bad:
free(result);
debug_return_str(NULL);
}

View File

@@ -467,4 +467,10 @@ size_t strlcpy_unescape(char *dst, const char *src, size_t size);
/* strvec_join.c */ /* strvec_join.c */
char *strvec_join(char *const argv[], char sep, size_t (*cpy)(char *, const char *, size_t)); char *strvec_join(char *const argv[], char sep, size_t (*cpy)(char *, const char *, size_t));
/* unesc_str.c */
void unescape_string(char *str);
/* serialize_list.c */
char *serialize_list(const char *varname, struct list_members *members);
#endif /* SUDOERS_SUDOERS_H */ #endif /* SUDOERS_SUDOERS_H */

View File

@@ -0,0 +1,42 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2022 Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This is an open source non-commercial project. Dear PVS-Studio, please check it.
* PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
*/
#include <string.h>
/*
* Remove backslash escape chars.
*/
void
unescape_string(char *str)
{
char *cp = str;
char *ep = str + strlen(str);
while ((cp = strchr(cp, '\\')) != NULL) {
if (cp[1] == '\0')
break;
memmove(cp, cp + 1, (size_t)(ep - cp));
cp++;
ep--;
}
}