[sudo-workers] [PATCH] regexp support added to command line arguments

Cyrille Lefevre cyrille.lefevre-lists at laposte.net
Mon Mar 21 22:04:53 MDT 2016


Hi,

Here is an implementation of the regexp support in addition to the
existing wildcard support.

By regexp support, I mean POSIX extended regular expression, aka
regex(7).

Regexp's are automatically anchored by ^( and )$ to match the whole
command line arguments.

The following syntax has been implemented :

Cmnd_Alias TEST = ERE: cmnd args
or
root ALL=(ALL) NOPASSWD:, ERE: cmnd args

For instance, cmnd is still proceeded through fnmatch, args are
anchored as ^(args)$.

The ERE: token has been chosen to allow future extention using PCRE:
for example.

It whould even possible to allow some modifier after the colon such as
"i" at least for systems which does'n care of case on arguments ?!

To process cmnd through regexp, it would be possible to introduce a
new token such as CERE: or a modifier such as c ou C with the risk of
a future use by PCRE.

It would be also be possible to implement a "regexp" Defaults' keyword,
but that was not done right now.

The attached diff as been generated using :

hg diff -X 'plugins/sudoers/gram.[ch]' -X 'plugins/sudoers/toke.c' \
-X aclocal.m4 -X configure -X config.h.in -X ltmain.sh -X 'm4/*' \
  > sudo_regexp.patch

List of modified files :

diff -r 8407163d6832 configure.ac
diff -r 8407163d6832 doc/sudoers.mdoc.in
diff -r 8407163d6832 examples/sudoers
diff -r 8407163d6832 plugins/sudoers/gram.y
diff -r 8407163d6832 plugins/sudoers/ldap.c
diff -r 8407163d6832 plugins/sudoers/match.c
diff -r 8407163d6832 plugins/sudoers/parse.c
diff -r 8407163d6832 plugins/sudoers/parse.h
diff -r 8407163d6832 plugins/sudoers/sssd.c
diff -r 8407163d6832 plugins/sudoers/toke.l
diff -r 8407163d6832 plugins/sudoers/visudo_json.c

Hope I don't miss something :)

Regards,

Cyrille Lefevre
-- 
mailto:Cyrille.Lefevre-lists at laposte.net

-------------- next part --------------
diff -r 8407163d6832 configure.ac
--- a/configure.ac	Thu Mar 17 10:13:51 2016 -0600
+++ b/configure.ac	Tue Mar 22 03:38:49 2016 +0100
@@ -2573,6 +2573,13 @@
     SUDO_APPEND_COMPAT_EXP(sudo_futimens)
     AC_CHECK_FUNCS([futimes futimesat futime], [break])
 ])
+AC_CHECK_FUNCS([regfree], [AC_DEFINE(HAVE_REGEX)], [
+    AC_LIBOBJ(regcomp)
+    AC_LIBOBJ(regerror)
+    AC_LIBOBJ(regexec)
+    AC_LIBOBJ(regfree)
+    SUDO_APPEND_COMPAT_EXP(sudo_regcomp sudo_regerror sudo_regexec sudo_regfree)
+])
 SUDO_FUNC_FNMATCH([AC_DEFINE(HAVE_FNMATCH)], [
     AC_LIBOBJ(fnmatch)
     SUDO_APPEND_COMPAT_EXP(sudo_fnmatch)
@@ -4301,6 +4308,7 @@
 AH_TEMPLATE(HAVE_PAM, [Define to 1 if you use PAM authentication.])
 AH_TEMPLATE(HAVE_PAM_LOGIN, [Define to 1 if you use a specific PAM session for sudo -i.])
 AH_TEMPLATE(HAVE_PROJECT_H, [Define to 1 if you have the <project.h> header file.])
+AH_TEMPLATE(HAVE_REGEX, [Define to 1 if you have the `regex' functions.])
 AH_TEMPLATE(HAVE_SECURID, [Define to 1 if you use SecurID for authentication.])
 AH_TEMPLATE(HAVE_SELINUX, [Define to 1 to enable SELinux RBAC support.])
 AH_TEMPLATE(HAVE_SETKEYCREATECON, [Define to 1 if you have the `setkeycreatecon' function.])
diff -r 8407163d6832 doc/sudoers.mdoc.in
--- a/doc/sudoers.mdoc.in	Thu Mar 17 10:13:51 2016 -0600
+++ b/doc/sudoers.mdoc.in	Tue Mar 22 03:38:49 2016 +0100
@@ -746,6 +746,8 @@
 		"sha384" ':' digest |
 		"sha512" ':' digest
 
+Regexp_Spec ::= "ERE" ':'
+
 Cmnd_List ::= Cmnd |
               Cmnd ',' Cmnd_List
 
@@ -753,7 +755,7 @@
                  file name args |
                  file name '""'
 
-Cmnd ::= Digest_Spec? '!'* command name |
+Cmnd ::= Digest_Spec? Regexp_Spec? '!'* command name |
          '!'* directory |
          '!'* "sudoedit" |
          '!'* Cmnd_Alias
@@ -818,6 +820,14 @@
 If a
 .Li command name
 is prefixed with a
+.Li Regexp_Spec ,
+the command line arguments may be specified as POSIX extended regular
+expression instead of shell-style wildcards.
+Also, command line arguments are automatically anchored between ^( and )$.
+.Pp
+If a
+.Li command name
+is prefixed with a
 .Li Digest_Spec ,
 the command will only match successfully if it can be verified
 using the specified SHA-2 digest.
@@ -1480,6 +1490,12 @@
 .Ed
 .Pp
 which is probably not what was intended.
+.Pp
+To circonvent this kind of side effect, you may use a sudoers entry like:
+.Bd -literal -offset 4n
+%operator ALL = ERE: /bin/cat /var/log/messages[.0-9]+
+.Ed
+.Pp
 In most cases it is better to do command line processing
 outside of the
 .Em sudoers
@@ -4668,7 +4684,9 @@
 .Xr fnmatch 3 ,
 .Xr glob 3 ,
 .Xr mktemp 3 ,
+.Xr regex 3 ,
 .Xr strftime 3 ,
+.Xr regex 7 ,
 .Xr sudo.conf @mansectform@ ,
 .Xr sudoers.ldap @mansectform@ ,
 .Xr sudo @mansectsu@ ,
diff -r 8407163d6832 examples/sudoers
--- a/examples/sudoers	Thu Mar 17 10:13:51 2016 -0600
+++ b/examples/sudoers	Tue Mar 22 03:38:49 2016 +0100
@@ -59,6 +59,8 @@
 Cmnd_Alias	VIPW = /usr/sbin/vipw, /usr/bin/passwd, /usr/bin/chsh, \
 		       /usr/bin/chfn
 Cmnd_Alias	PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
+Cmnd_Alias	SECURELOG = /bin/cat /var/log/secure, /bin/zcat /var/log/secure[.0-9]+.gz,\
+			    ERE: /usr/bin/?zcat /var/log/secure[.0-9bxz]+
 
 ##
 # User specification
@@ -82,7 +84,7 @@
 
 # operator may run maintenance commands and anything in /usr/oper/bin/
 operator	ALL = DUMPS, KILL, SHUTDOWN, HALT, REBOOT, PRINTING,\
-		sudoedit /etc/printcap, /usr/oper/bin/
+		sudoedit /etc/printcap, /usr/oper/bin/, SECURELOG
 
 # joe may su only to operator
 joe		ALL = /usr/bin/su operator
diff -r 8407163d6832 plugins/sudoers/gram.y
--- a/plugins/sudoers/gram.y	Thu Mar 17 10:13:51 2016 -0600
+++ b/plugins/sudoers/gram.y	Tue Mar 22 03:38:49 2016 +0100
@@ -114,6 +114,7 @@
 %token <tok>	 FOLLOW			/* follow symbolic links */
 %token <tok>	 NOFOLLOW		/* don't follow symbolic links */
 %token <tok>	 ALL			/* ALL keyword */
+%token <tok>	 REGEXP			/* ERE keyword */
 %token <tok>	 COMMENT		/* comment and/or carriage return */
 %token <tok>	 HOSTALIAS		/* Host_Alias keyword */
 %token <tok>	 CMNDALIAS		/* Cmnd_Alias keyword */
@@ -138,6 +139,7 @@
 %type <defaults>  defaults_list
 %type <member>	  cmnd
 %type <member>	  opcmnd
+%type <member>	  opregexp
 %type <member>	  digcmnd
 %type <member>	  cmndlist
 %type <member>	  host
@@ -463,10 +465,10 @@
 			}
 		;
 
-digcmnd		:	opcmnd {
+digcmnd		:	opregexp {
 			    $$ = $1;
 			}
-		|	digest opcmnd {
+		|	digest opregexp {
 			    if ($2->type != COMMAND) {
 				sudoerserror(N_("a digest requires a path name"));
 				YYERROR;
@@ -477,6 +479,16 @@
 			}
 		;
 
+opregexp	:	opcmnd {
+			    $$ = $1;
+			    $$->regexp = false;
+			}
+		|	REGEXP opcmnd {
+			    $$ = $2;
+			    $$->regexp = true;
+			}
+		;
+
 opcmnd		:	cmnd {
 			    $$ = $1;
 			    $$->negated = false;
@@ -485,7 +497,6 @@
 			    $$ = $2;
 			    $$->negated = true;
 			}
-		;
 
 rolespec	:	ROLE '=' WORD {
 			    $$ = $3;
diff -r 8407163d6832 plugins/sudoers/ldap.c
--- a/plugins/sudoers/ldap.c	Thu Mar 17 10:13:51 2016 -0600
+++ b/plugins/sudoers/ldap.c	Tue Mar 22 03:38:49 2016 +0100
@@ -943,8 +943,8 @@
 {
     struct sudo_digest digest, *allowed_digest = NULL;
     struct berval **bv, **p;
-    char *allowed_cmnd, *allowed_args, *val;
-    bool foundbang;
+    char *allowed_cmnd, *allowed_args, *val, *cp;
+    bool foundbang, regexp = false;
     int ret = UNSPEC;
     debug_decl(sudo_ldap_check_command, SUDOERS_DEBUG_LDAP)
 
@@ -969,13 +969,25 @@
 	/* check for sha-2 digest */
 	allowed_digest = sudo_ldap_extract_digest(&val, &digest);
 
+	cp = val;
+	if (!strncmp(cp, "ERE", 3)) {
+	    cp += 3;
+	    while (*cp && *cp == ' ')
+	    	cp++;
+	    if (*cp == ':') {
+		while (*++cp && *cp == ' ') {}
+		regexp = true;
+	    } else
+		cp = val;
+	}
+
 	/* check for !command */
-	if (*val == '!') {
+	if (*cp == '!') {
 	    foundbang = true;
-	    allowed_cmnd = val + 1;	/* !command */
+	    allowed_cmnd = cp + 1;	/* !command */
 	} else {
 	    foundbang = false;
-	    allowed_cmnd = val;		/* command */
+	    allowed_cmnd = cp;		/* command */
 	}
 
 	/* split optional args away from command */
@@ -984,7 +996,7 @@
 	    *allowed_args++ = '\0';
 
 	/* check the command like normal */
-	if (command_matches(allowed_cmnd, allowed_args, allowed_digest)) {
+	if (command_matches(allowed_cmnd, allowed_args, allowed_digest, regexp)) {
 	    /*
 	     * If allowed (no bang) set ret but keep on checking.
 	     * If disallowed (bang), exit loop.
@@ -1429,7 +1441,7 @@
 	escaped_host = sudo_ldap_value_dup(user_runhost);
 	if (user_runhost == user_srunhost)
 	    escaped_shost = escaped_host;
-	else 
+	else
 	    escaped_shost = sudo_ldap_value_dup(user_srunhost);
 	if (escaped_host == NULL || escaped_shost == NULL)
 	    goto oom;
diff -r 8407163d6832 plugins/sudoers/match.c
--- a/plugins/sudoers/match.c	Thu Mar 17 10:13:51 2016 -0600
+++ b/plugins/sudoers/match.c	Tue Mar 22 03:38:49 2016 +0100
@@ -64,6 +64,11 @@
 #include "parse.h"
 #include <gram.h>
 
+#ifdef HAVE_REGEX
+# include <regex.h>
+#else
+# include "compat/regex.h"
+#endif /* HAVE_REGEX */
 #ifdef HAVE_FNMATCH
 # include <fnmatch.h>
 #else
@@ -79,10 +84,10 @@
 
 static bool command_matches_dir(const char *sudoers_dir, size_t dlen);
 #ifndef SUDOERS_NAME_MATCH
-static bool command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args);
+static bool command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, bool regexp);
 #endif
-static bool command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args);
-static bool command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest);
+static bool command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args, bool regexp);
+static bool command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest, bool regexp);
 
 /*
  * Returns true if string 's' contains meta characters.
@@ -341,7 +346,7 @@
 	    break;
 	case COMMAND:
 	    c = (struct sudo_command *)m->name;
-	    if (command_matches(c->cmnd, c->args, c->digest))
+	    if (command_matches(c->cmnd, c->args, c->digest, m->regexp))
 		matched = !m->negated;
 	    break;
     }
@@ -349,7 +354,7 @@
 }
 
 static bool
-command_args_match(const char *sudoers_cmnd, const char *sudoers_args)
+command_args_match(const char *sudoers_cmnd, const char *sudoers_args, bool regexp)
 {
     int flags = 0;
     debug_decl(command_args_match, SUDOERS_DEBUG_MATCH)
@@ -369,7 +374,41 @@
 	/* For sudoedit, all args are assumed to be pathnames. */
 	if (strcmp(sudoers_cmnd, "sudoedit") == 0)
 	    flags = FNM_PATHNAME;
-	if (fnmatch(sudoers_args, user_args ? user_args : "", flags) == 0)
+	if (!flags && regexp) {
+	    regex_t preg;
+	    int rc;
+	    size_t len;
+	    char *cp;
+
+	    len = strlen(sudoers_args) + 5; /* ^()$\0 */
+	    cp = malloc(len);
+	    if (!cp) {
+		sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+		debug_return_bool(false);
+	    }
+	    strlcpy(cp, "^(", len);
+	    strlcat(cp, sudoers_args, len);
+	    strlcat(cp, ")$", len);
+	    rc = regcomp(&preg, cp, REG_EXTENDED|REG_NOSUB);
+	    if (rc == 0)
+	        rc = regexec(&preg, user_args ? user_args : "", 0, NULL, 0);
+	    if (rc != REG_NOMATCH) {
+		size_t siz = regerror(rc, &preg, NULL, 0);
+		char *buf = malloc(siz);
+
+		if (!buf)
+		    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+		else {
+		    regerror(rc, &preg, buf, siz);
+		    sudo_warnx(U_("%s: %s"), __func__, buf);
+		    free(buf);
+	        }
+	    }
+	    regfree(&preg);
+	    free(cp);
+	    if (!rc)
+		debug_return_bool(true);
+	} else if (fnmatch(sudoers_args, user_args ? user_args : "", flags) == 0)
 	    debug_return_bool(true);
     }
     debug_return_bool(false);
@@ -380,7 +419,7 @@
  * otherwise, return true if user_cmnd names one of the inodes in path.
  */
 bool
-command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest)
+command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest, bool regexp)
 {
     bool rc = false;
     debug_decl(command_matches, SUDOERS_DEBUG_MATCH)
@@ -395,7 +434,7 @@
 	 */
 	if (strcmp(sudoers_cmnd, "sudoedit") == 0 &&
 	    strcmp(user_cmnd, "sudoedit") == 0 &&
-	    command_args_match(sudoers_cmnd, sudoers_args)) {
+	    command_args_match(sudoers_cmnd, sudoers_args, regexp)) {
 	    /* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */
 	    rc = true;
 	}
@@ -408,15 +447,15 @@
 	 * use glob(3) and/or fnmatch(3) to do the matching.
 	 */
 #ifdef SUDOERS_NAME_MATCH
-	rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args);
+	rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, regexp);
 #else
 	if (def_fast_glob)
-	    rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args);
+	    rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, regexp);
 	else
-	    rc = command_matches_glob(sudoers_cmnd, sudoers_args);
+	    rc = command_matches_glob(sudoers_cmnd, sudoers_args, regexp);
 #endif
     } else {
-	rc = command_matches_normal(sudoers_cmnd, sudoers_args, digest);
+	rc = command_matches_normal(sudoers_cmnd, sudoers_args, digest, regexp);
     }
 done:
     sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
@@ -428,7 +467,7 @@
 }
 
 static bool
-command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args)
+command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args, bool regexp)
 {
     debug_decl(command_matches_fnmatch, SUDOERS_DEBUG_MATCH)
 
@@ -441,7 +480,7 @@
      */
     if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
 	debug_return_bool(false);
-    if (command_args_match(sudoers_cmnd, sudoers_args)) {
+    if (command_args_match(sudoers_cmnd, sudoers_args, regexp)) {
 	/* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */
 	debug_return_bool(true);
     }
@@ -450,7 +489,7 @@
 
 #ifndef SUDOERS_NAME_MATCH
 static bool
-command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args)
+command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, bool regexp)
 {
     struct stat sudoers_stat;
     size_t dlen;
@@ -515,7 +554,7 @@
     if (cp == NULL)
 	debug_return_bool(false);
 
-    if (command_args_match(sudoers_cmnd, sudoers_args)) {
+    if (command_args_match(sudoers_cmnd, sudoers_args, regexp)) {
 	/* safe_cmnd was set above. */
 	debug_return_bool(true);
     }
@@ -525,7 +564,7 @@
 
 #ifdef SUDOERS_NAME_MATCH
 static bool
-command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest)
+command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest, bool regexp)
 {
     size_t dlen;
     debug_decl(command_matches_normal, SUDOERS_DEBUG_MATCH)
@@ -537,7 +576,7 @@
 	debug_return_bool(command_matches_dir(sudoers_cmnd, dlen));
 
     if (strcmp(user_cmnd, sudoers_cmnd) == 0) {
-	if (command_args_match(sudoers_cmnd, sudoers_args)) {
+	if (command_args_match(sudoers_cmnd, sudoers_args, regexp)) {
 	    free(safe_cmnd);
 	    if ((safe_cmnd = strdup(sudoers_cmnd)) != NULL)
 		debug_return_bool(true);
@@ -695,7 +734,7 @@
 }
 
 static bool
-command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest)
+command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest, bool regexp)
 {
     struct stat sudoers_stat;
     const char *base;
@@ -727,7 +766,7 @@
 	(user_stat->st_dev != sudoers_stat.st_dev ||
 	user_stat->st_ino != sudoers_stat.st_ino))
 	debug_return_bool(false);
-    if (!command_args_match(sudoers_cmnd, sudoers_args))
+    if (!command_args_match(sudoers_cmnd, sudoers_args, regexp))
 	debug_return_bool(false);
     if (cmnd_fd != -1) {
 	close(cmnd_fd);
diff -r 8407163d6832 plugins/sudoers/parse.c
--- a/plugins/sudoers/parse.c	Thu Mar 17 10:13:51 2016 -0600
+++ b/plugins/sudoers/parse.c	Tue Mar 22 03:38:49 2016 +0100
@@ -772,8 +772,8 @@
     }
     matched:
     if (match != NULL && !match->negated) {
-	const int len = sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s\n",
-	    safe_cmnd, user_args ? " " : "", user_args ? user_args : "");
+	const int len = sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s%s\n",
+	    match->regexp ? "ERE: " : "", safe_cmnd, user_args ? " " : "", user_args ? user_args : "");
 	rval = len == -1 ? -1 : 0;
     }
 done:
@@ -785,7 +785,7 @@
  */
 static void
 print_member_int(struct sudo_lbuf *lbuf, char *name, int type, int negated,
-    const char *separator, int alias_type)
+    const char *separator, int alias_type, bool regexp)
 {
     struct alias *a;
     struct member *m;
@@ -801,6 +801,8 @@
 	    break;
 	case COMMAND:
 	    c = (struct sudo_command *) name;
+	    if (regexp)
+		sudo_lbuf_append(lbuf, "ERE: ");
 	    if (negated)
 		sudo_lbuf_append(lbuf, "!");
 	    sudo_lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->cmnd);
@@ -816,7 +818,7 @@
 			sudo_lbuf_append(lbuf, "%s", separator);
 		    print_member_int(lbuf, m->name, m->type,
 			negated ? !m->negated : m->negated, separator,
-			alias_type);
+			alias_type, m->regexp);
 		}
 		alias_put(a);
 		break;
@@ -832,12 +834,12 @@
 static void
 print_member(struct sudo_lbuf *lbuf, struct member *m, int alias_type)
 {
-    print_member_int(lbuf, m->name, m->type, m->negated, ", ", alias_type);
+    print_member_int(lbuf, m->name, m->type, m->negated, ", ", alias_type, m->regexp);
 }
 
 static void
 print_member_sep(struct sudo_lbuf *lbuf, struct member *m,
     const char *separator, int alias_type)
 {
-    print_member_int(lbuf, m->name, m->type, m->negated, separator, alias_type);
+    print_member_int(lbuf, m->name, m->type, m->negated, separator, alias_type, m->regexp);
 }
diff -r 8407163d6832 plugins/sudoers/parse.h
--- a/plugins/sudoers/parse.h	Thu Mar 17 10:13:51 2016 -0600
+++ b/plugins/sudoers/parse.h	Tue Mar 22 03:38:49 2016 +0100
@@ -196,6 +196,7 @@
     char *name;				/* member name */
     short type;				/* type (see gram.h) */
     short negated;			/* negated via '!'? */
+    short regexp;			/* pattern if not regexp */
 };
 
 struct runascontainer {
@@ -250,7 +251,7 @@
 bool addr_matches(char *n);
 
 /* match.c */
-bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest);
+bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest, bool regexp);
 bool group_matches(const char *sudoers_group, const struct group *gr);
 bool hostname_matches(const char *shost, const char *lhost, const char *pattern);
 bool netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user);
diff -r 8407163d6832 plugins/sudoers/sssd.c
--- a/plugins/sudoers/sssd.c	Thu Mar 17 10:13:51 2016 -0600
+++ b/plugins/sudoers/sssd.c	Tue Mar 22 03:38:49 2016 +0100
@@ -946,10 +946,10 @@
 sudo_sss_check_command(struct sudo_sss_handle *handle,
     struct sss_sudo_rule *rule, int *setenv_implied)
 {
-    char **val_array = NULL, *val;
+    char **val_array = NULL, *val, *cp;
     char *allowed_cmnd, *allowed_args;
     int ret = UNSPEC;
-    bool foundbang;
+    bool foundbang, regexp = false;
     unsigned int i;
     struct sudo_digest digest, *allowed_digest = NULL;
     debug_decl(sudo_sss_check_command, SUDOERS_DEBUG_SSSD);
@@ -986,13 +986,25 @@
         /* check for sha-2 digest */
 	allowed_digest = sudo_sss_extract_digest(&val, &digest);
 
+	cp = val;
+	if (!strncmp(cp, "ERE", 3)) {
+	    cp += 3;
+	    while (*cp && *cp == ' ')
+	    	cp++;
+	    if (*cp == ':') {
+		while (*++cp && *cp == ' ') {}
+		regexp = true;
+	    } else
+		cp = val;
+	}
+
 	/* check for !command */
-	if (*val == '!') {
+	if (*cp == '!') {
 	    foundbang = true;
-	    allowed_cmnd = val + 1;	/* !command */
+	    allowed_cmnd = cp + 1;	/* !command */
 	} else {
 	    foundbang = false;
-	    allowed_cmnd = val;		/* command */
+	    allowed_cmnd = cp;		/* command */
 	}
 
 	/* split optional args away from command */
@@ -1001,7 +1013,7 @@
 	    *allowed_args++ = '\0';
 
 	/* check the command like normal */
-	if (command_matches(allowed_cmnd, allowed_args, allowed_digest)) {
+	if (command_matches(allowed_cmnd, allowed_args, allowed_digest, regexp)) {
 	    /*
 	     * If allowed (no bang) set ret but keep on checking.
 	     * If disallowed (bang), exit loop.
diff -r 8407163d6832 plugins/sudoers/toke.l
--- a/plugins/sudoers/toke.l	Thu Mar 17 10:13:51 2016 -0600
+++ b/plugins/sudoers/toke.l	Tue Mar 22 03:38:49 2016 +0100
@@ -72,7 +72,7 @@
 uid_t sudoers_uid = SUDOERS_UID;
 gid_t sudoers_gid = SUDOERS_GID;
 
-static bool continued, sawspace;
+static bool continued, sawspace, sawregexp;
 static int prev_state;
 static yy_size_t digest_len;
 
@@ -112,6 +112,7 @@
 
 %s	GOTDEFS
 %x	GOTCMND
+%x	GOTREGEXP
 %x	STARTDEFS
 %x	INDEFS
 %x	INSTR
@@ -222,8 +223,7 @@
 			}
 }
 
-<GOTCMND>{
-    \\[\*\?\[\]\!]	{
+<GOTCMND>\\[][*?!]	{
 			    /* quoted fnmatch glob char, pass verbatim */
 			    LEXTRACE("QUOTEDCHAR ");
 			    if (!fill_args(sudoerstext, 2, sawspace))
@@ -231,6 +231,15 @@
 			    sawspace = false;
 			}
 
+<GOTREGEXP>\\[[^.$()|*+?{\\] {
+			    /* quoted regexp glob char, pass verbatim */
+			    LEXTRACE("QUOTEDCHAR ");
+			    if (!fill_args(sudoerstext, 2, sawspace))
+				yyterminate();
+			    sawspace = false;
+			}
+
+<GOTCMND,GOTREGEXP>{
     \\[:\\,= \t#]	{
 			    /* quoted sudoers special char, strip backslash */
 			    LEXTRACE("QUOTEDCHAR ");
@@ -464,6 +473,12 @@
 			    	LEXRETURN(NOFOLLOW);
 			}
 
+ERE[[:blank:]]*:	{
+				LEXTRACE("REGEXP ");
+				sawregexp = true;
+				LEXRETURN(REGEXP);
+			}
+
 <INITIAL,GOTDEFS>(\+|\%|\%:) {
 			    /* empty group or netgroup */
 			    LEXTRACE("ERROR ");
@@ -623,7 +638,11 @@
 				    yyterminate();
 				LEXRETURN(COMMAND);
 			    } else {
-				BEGIN GOTCMND;
+			        if (sawregexp) {
+			            sawregexp = false;
+				    BEGIN GOTREGEXP;
+				} else
+				    BEGIN GOTCMND;
 				LEXTRACE("COMMAND ");
 				if (!fill_cmnd(sudoerstext, sudoersleng))
 				    yyterminate();
@@ -896,6 +915,7 @@
     istacksize = idepth = 0;
     sudolineno = 1;
     keepopen = false;
+    sawregexp = false;
     sawspace = false;
     continued = false;
     prev_state = INITIAL;
diff -r 8407163d6832 plugins/sudoers/visudo_json.c
--- a/plugins/sudoers/visudo_json.c	Thu Mar 17 10:13:51 2016 -0600
+++ b/plugins/sudoers/visudo_json.c	Tue Mar 22 03:38:49 2016 +0100
@@ -233,7 +233,7 @@
     debug_decl(print_command_json, SUDOERS_DEBUG_UTIL)
 
     printstr_json(fp, "{", NULL, NULL, indent);
-    if (m->negated || c->digest != NULL) {
+    if (m->negated || c->digest != NULL || m->regexp) {
 	putc('\n', fp);
 	indent += 4;
     } else {
@@ -277,6 +277,14 @@
 	print_pair_json(fp, NULL, digest_name, &value, NULL, indent);
     }
 
+    /* Command may be a regexp. */
+    if (m->regexp) {
+	fputs(",\n", fp);
+	value.type = JSON_BOOL;
+	value.u.boolean = true;
+	print_pair_json(fp, NULL, "regexp", &value, NULL, indent);
+    }
+
     /* Command may be negated. */
     if (m->negated) {
 	fputs(",\n", fp);



More information about the sudo-workers mailing list