]> granicus.if.org Git - sudo/commitdiff
Add sudo_strsplit(), similar to strtok_r() but non-destructive and
authorTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 26 May 2015 21:46:41 +0000 (15:46 -0600)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 26 May 2015 21:46:41 +0000 (15:46 -0600)
operates on non-C strings (requires a length parameter).

MANIFEST
include/sudo_util.h
lib/util/Makefile.in
lib/util/strsplit.c [new file with mode: 0644]
lib/util/util.exp.in
plugins/sudoers/sudoers.c

index 5360972ab87f5ee8479ec683eccabfcab42c78fe..6cfcaf351b21e8d095bd066892b7f8badc97784d 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -162,6 +162,7 @@ lib/util/strlcpy.c
 lib/util/strndup.c
 lib/util/strnlen.c
 lib/util/strsignal.c
+lib/util/strsplit.c
 lib/util/strtobool.c
 lib/util/strtoid.c
 lib/util/strtomode.c
index 57f1ee437d98546924efdc29811756cabc466812..f49e4d69b391e0dac12c4e408869b1a185b4a4e8 100644 (file)
@@ -201,6 +201,10 @@ __dso_public int sudo_secure_file_v1(const char *path, uid_t uid, gid_t gid, str
 __dso_public int sudo_setgroups_v1(int ngids, const GETGROUPS_T *gids);
 #define sudo_setgroups(_a, _b) sudo_setgroups_v1((_a), (_b))
 
+/* strsplit.c */
+__dso_public const char *sudo_strsplit_v1(const char *str, const char *endstr, const char *sep, const char **last);
+#define sudo_strsplit(_a, _b, _c, _d) sudo_strsplit_v1(_a, _b, _c, _d)
+
 /* strtobool.c */
 __dso_public int sudo_strtobool_v1(const char *str);
 #define sudo_strtobool(_a) sudo_strtobool_v1((_a))
index 46610c75bd506d221ecb7394b50978a8945f6a7b..6c52be61dc69b09d692332f48e29031c9261f9ad 100644 (file)
@@ -101,8 +101,9 @@ SHELL = @SHELL@
 
 LTOBJS = alloc.lo event.lo fatal.lo key_val.lo gethostname.lo gettime.lo \
         gidlist.lo lbuf.lo locking.lo parseln.lo progname.lo secure_path.lo \
-        setgroups.lo strtobool.lo strtoid.lo strtomode.lo sudo_conf.lo \
-        sudo_debug.lo sudo_dso.lo term.lo ttysize.lo @COMMON_OBJS@ @LTLIBOBJS@
+        setgroups.lo strsplit.lo strtobool.lo strtoid.lo strtomode.lo \
+        sudo_conf.lo sudo_debug.lo sudo_dso.lo term.lo ttysize.lo \
+        @COMMON_OBJS@ @LTLIBOBJS@
 
 ATOFOO_TEST_OBJS = atofoo_test.lo
 
@@ -333,10 +334,9 @@ conf_test.lo: $(srcdir)/regress/sudo_conf/conf_test.c \
               $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
               $(top_builddir)/config.h
        $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/sudo_conf/conf_test.c
-event.lo: $(srcdir)/event.c $(incdir)/compat/stdbool.h $(incdir)/sudo_alloc.h \
-          $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
-          $(incdir)/sudo_event.h $(incdir)/sudo_fatal.h $(incdir)/sudo_queue.h \
-          $(incdir)/sudo_util.h $(top_builddir)/config.h
+event.lo: $(srcdir)/event.c $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
+          $(incdir)/sudo_debug.h $(incdir)/sudo_event.h $(incdir)/sudo_fatal.h \
+          $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(top_builddir)/config.h
        $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/event.c
 event_poll.lo: $(srcdir)/event_poll.c $(incdir)/compat/stdbool.h \
                $(incdir)/sudo_alloc.h $(incdir)/sudo_compat.h \
@@ -345,15 +345,15 @@ event_poll.lo: $(srcdir)/event_poll.c $(incdir)/compat/stdbool.h \
                $(top_builddir)/config.h
        $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/event_poll.c
 event_select.lo: $(srcdir)/event_select.c $(incdir)/compat/stdbool.h \
-                 $(incdir)/sudo_alloc.h $(incdir)/sudo_compat.h \
-                 $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
-                 $(incdir)/sudo_fatal.h $(incdir)/sudo_queue.h \
-                 $(incdir)/sudo_util.h $(top_builddir)/config.h
+                 $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
+                 $(incdir)/sudo_event.h $(incdir)/sudo_fatal.h \
+                 $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
+                 $(top_builddir)/config.h
        $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/event_select.c
-fatal.lo: $(srcdir)/fatal.c $(incdir)/compat/stdbool.h $(incdir)/sudo_alloc.h \
-          $(incdir)/sudo_compat.h $(incdir)/sudo_fatal.h \
-          $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \
-          $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(top_builddir)/config.h
+fatal.lo: $(srcdir)/fatal.c $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
+          $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
+          $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
+          $(top_builddir)/config.h
        $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/fatal.c
 fnm_test.lo: $(srcdir)/regress/fnmatch/fnm_test.c $(incdir)/compat/fnmatch.h \
              $(incdir)/sudo_compat.h $(top_builddir)/config.h
@@ -492,6 +492,11 @@ strnlen.lo: $(srcdir)/strnlen.c $(incdir)/sudo_compat.h $(top_builddir)/config.h
 strsignal.lo: $(srcdir)/strsignal.c $(incdir)/sudo_compat.h \
               $(incdir)/sudo_gettext.h $(top_builddir)/config.h
        $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strsignal.c
+strsplit.lo: $(srcdir)/strsplit.c $(incdir)/compat/stdbool.h \
+             $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
+             $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
+             $(top_builddir)/config.h
+       $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strsplit.c
 strtobool.lo: $(srcdir)/strtobool.c $(incdir)/compat/stdbool.h \
               $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
               $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
diff --git a/lib/util/strsplit.c b/lib/util/strsplit.c
new file mode 100644 (file)
index 0000000..15aaf17
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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 <sys/types.h>
+
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif /* STDC_HEADERS */
+#include <errno.h>
+#include <grp.h>
+
+#include "sudo_compat.h"
+#include "sudo_debug.h"
+#include "sudo_util.h"
+
+/*
+ * Like strtok_r but non-destructive and works w/o a NUL terminator.
+ * TODO: Optimize by storing current char in a variable ch
+ */
+const char *
+sudo_strsplit_v1(const char *str, const char *endstr, const char *sep, const char **last)
+{
+    const char *cp, *s;
+    debug_decl(sudo_strsplit, SUDO_DEBUG_UTIL)
+
+    /* If no str specified, use last ptr (if any). */
+    if (str == NULL)
+       str = *last;
+
+    /* Skip leading separator characters. */
+    while (str < endstr) {
+       for (s = sep; *s != '\0'; s++) {
+           if (*str == *s) {
+               str++;
+               break;
+           }
+       }
+       if (*s == '\0')
+           break;
+    }
+
+    /* Empty string? */
+    if (str >= endstr) {
+       *last = endstr;
+       debug_return_ptr(NULL);
+    }
+
+    /* Scan str until we hit a char from sep. */
+    for (cp = str; cp < endstr; cp++) {
+       for (s = sep; *s != '\0'; s++) {
+           if (*cp == *s)
+               break;
+       }
+       if (*s != '\0')
+           break;
+    }
+    *last = cp;
+    debug_return_const_ptr(str);
+}
index 127db1c410552d844cc607fb4ac64cefb93ecdf1..5694567a68f4272db4d83f462d3668764336bfb4 100644 (file)
@@ -80,6 +80,7 @@ sudo_parseln_v1
 sudo_secure_dir_v1
 sudo_secure_file_v1
 sudo_setgroups_v1
+sudo_strsplit_v1
 sudo_strtobool_v1
 sudo_strtoid_v1
 sudo_strtomode_v1
index 0c2e5aeae327ba841ed666e17fa5f1f85f974b97..e59075a93a8fe5dc5fb34fefed5efbe12cd48f23 100644 (file)
@@ -1046,48 +1046,60 @@ sudoers_cleanup(void)
 }
 
 static char *
-resolve_editor(const char *ed, size_t edlen, int nfiles, char **files, int *argc_out, char ***argv_out)
+resolve_editor(const char *ed, size_t edlen, int nfiles, char **files,
+    int *argc_out, char ***argv_out)
 {
-    char *cp, **nargv, *editor, *editor_path = NULL;
-    int ac, i, nargc;
-    bool wasblank;
+    char **nargv, *editor, *editor_path = NULL;
+    const char *cp, *ep, *tmp;
+    const char *edend = ed + edlen;
+    int nargc;
     debug_decl(resolve_editor, SUDOERS_DEBUG_PLUGIN)
 
-    /* Note: editor becomes part of argv_out and is not freed. */
-    editor = sudo_emalloc(edlen + 1);
-    memcpy(editor, ed, edlen);
-    editor[edlen] = '\0';
-
     /*
-     * Split editor into an argument vector; editor is reused (do not free).
+     * Split editor into an argument vector, including files to edit.
      * The EDITOR and VISUAL environment variables may contain command
      * line args so look for those and alloc space for them too.
      */
-    nargc = 1;
-    for (wasblank = false, cp = editor; *cp != '\0'; cp++) {
-       if (isblank((unsigned char) *cp))
-           wasblank = true;
-       else if (wasblank) {
-           wasblank = false;
-           nargc++;
-       }
+    cp = sudo_strsplit(ed, edend, " \t", &ep);
+    if (cp == NULL)
+       debug_return_str(NULL);
+    editor = strndup(cp, (size_t)(ep - cp));
+    if (editor == NULL) {
+       sudo_warnx(U_("unable to allocate memory"));
+       debug_return_str(NULL);
     }
+
     /* If we can't find the editor in the user's PATH, give up. */
-    cp = strtok(editor, " \t");
-    if (cp == NULL ||
-       find_path(cp, &editor_path, NULL, getenv("PATH"), 0) != FOUND) {
-       sudo_efree(editor);
+    if (find_path(editor, &editor_path, NULL, getenv("PATH"), 0) != FOUND) {
+       free(editor);
+       errno = ENOENT;
        debug_return_str(NULL);
     }
-    nargv = (char **) sudo_emallocarray(nargc + 1 + nfiles + 1, sizeof(char *));
-    for (ac = 0; cp != NULL && ac < nargc; ac++) {
-       nargv[ac] = cp;
-       cp = strtok(NULL, " \t");
+
+    /* Count rest of arguments and allocate editor argv. */
+    for (nargc = 1, tmp = ep; sudo_strsplit(NULL, edend, " \t", &tmp) != NULL; )
+       nargc++;
+    nargv = reallocarray(NULL, nargc + 1 + nfiles + 1, sizeof(char *));
+    if (nargv == NULL) {
+       sudo_warnx(U_("unable to allocate memory"));
+       free(editor);
+       debug_return_str(NULL);
+    }
+
+    /* Fill in editor argv (assumes files[] is NULL-terminated). */
+    nargv[0] = editor;
+    for (nargc = 1; (cp = sudo_strsplit(NULL, edend, " \t", &ep)) != NULL; nargc++) {
+       nargv[nargc] = strndup(cp, (size_t)(ep - cp));
+       if (nargv[nargc] == NULL) {
+           sudo_warnx(U_("unable to allocate memory"));
+           while (nargc--)
+               free(nargv[nargc]);
+           debug_return_str(NULL);
+       }
     }
-    nargv[ac++] = "--";
-    for (i = 0; i < nfiles; )
-       nargv[ac++] = files[i++];
-    nargv[ac] = NULL;
+    nargv[nargc++] = "--";
+    while ((nargv[nargc++] = *files++) != NULL)
+       continue;
 
     *argc_out = nargc;
     *argv_out = nargv;
@@ -1118,6 +1130,8 @@ find_editor(int nfiles, char **files, int *argc_out, char ***argv_out)
        if ((editor = getenv(*ev)) != NULL && *editor != '\0') {
            editor_path = resolve_editor(editor, strlen(editor), nfiles,
                files, argc_out, argv_out);
+           if (editor_path == NULL && errno != ENOENT)
+               debug_return_str(NULL);
        }
     }
     if (editor_path == NULL) {
@@ -1129,6 +1143,8 @@ find_editor(int nfiles, char **files, int *argc_out, char ***argv_out)
            else
                len = strlen(cp);
            editor_path = resolve_editor(cp, len, nfiles, files, argc_out, argv_out);
+           if (editor_path == NULL && errno != ENOENT)
+               debug_return_str(NULL);
            cp = ep + 1;
        } while (ep != NULL && editor_path == NULL);
     }