3 * Copyright (c) 1996, 1998-2001 Todd C. Miller <Todd.Miller@courtesan.com>
6 * This code is derived from software contributed by Chris Jepeway.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
22 * 4. Products derived from this software may not be called "Sudo" nor
23 * may "Sudo" appear in their names without specific prior written
24 * permission from the author.
26 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
27 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
29 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
32 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 * XXX - the whole opFOO naming thing is somewhat bogus.
41 * XXX - the way things are stored for printmatches is stupid,
42 * they should be stored as elements in an array and then
43 * list_matches() can format things the way it wants.
48 #include <sys/types.h>
49 #include <sys/param.h>
58 #endif /* STDC_HEADERS */
62 # ifdef HAVE_STRINGS_H
65 #endif /* HAVE_STRING_H */
68 #endif /* HAVE_UNISTD_H */
70 #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
72 #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
73 #if defined(YYBISON) && defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
75 #endif /* YYBISON && HAVE_ALLOCA_H && !__GNUC__ */
78 #endif /* HAVE_LSEARCH */
84 #include "emul/search.h"
85 #endif /* HAVE_LSEARCH */
88 static const char rcsid[] = "$Sudo: parse.yacc,v 1.180 2002/03/16 00:44:47 millert Exp $";
94 extern int sudolineno, parse_error;
96 int clearaliases = TRUE;
97 int printmatches = FALSE;
108 #define RUNAS_ALIAS 4
111 * The matching stack, initial space allocated in init_parser().
113 struct matchstack *match;
114 int top = 0, stacksize = 0;
118 if (top >= stacksize) { \
119 while ((stacksize += STACKINCREMENT) < top); \
120 match = (struct matchstack *) erealloc(match, sizeof(struct matchstack) * stacksize); \
122 match[top].user = -1; \
123 match[top].cmnd = -1; \
124 match[top].host = -1; \
125 match[top].runas = -1; \
126 match[top].nopass = def_flag(I_AUTHENTICATE) ? -1 : TRUE; \
132 if (top >= stacksize) { \
133 while ((stacksize += STACKINCREMENT) < top); \
134 match = (struct matchstack *) erealloc(match, sizeof(struct matchstack) * stacksize); \
136 match[top].user = match[top-1].user; \
137 match[top].cmnd = match[top-1].cmnd; \
138 match[top].host = match[top-1].host; \
139 match[top].runas = match[top-1].runas; \
140 match[top].nopass = match[top-1].nopass; \
147 yyerror("matching stack underflow"); \
153 * Shortcuts for append()
155 #define append_cmnd(s, p) append(s, &cm_list[cm_list_len].cmnd, \
156 &cm_list[cm_list_len].cmnd_len, &cm_list[cm_list_len].cmnd_size, p)
158 #define append_runas(s, p) append(s, &cm_list[cm_list_len].runas, \
159 &cm_list[cm_list_len].runas_len, &cm_list[cm_list_len].runas_size, p)
161 #define append_entries(s, p) append(s, &ga_list[ga_list_len-1].entries, \
162 &ga_list[ga_list_len-1].entries_len, \
163 &ga_list[ga_list_len-1].entries_size, p)
166 * The stack for printmatches. A list of allowed commands for the user.
168 static struct command_match *cm_list = NULL;
169 static size_t cm_list_len = 0, cm_list_size = 0;
172 * List of Cmnd_Aliases and expansions for `sudo -l'
174 static int in_alias = FALSE;
175 static size_t ga_list_len = 0, ga_list_size = 0;
176 static struct generic_alias *ga_list = NULL;
179 * Does this Defaults list pertain to this user?
181 static int defaults_matches = 0;
186 static int add_alias __P((char *, int, int));
187 static void append __P((char *, char **, size_t *, size_t *, char *));
188 static void expand_ga_list __P((void));
189 static void expand_match_list __P((void));
190 static aliasinfo *find_alias __P((char *, int));
191 static int more_aliases __P((void));
192 void init_parser __P((void));
193 void yyerror __P((char *));
199 /* Save the line the first error occurred on. */
200 if (errorlineno == -1)
201 errorlineno = sudolineno ? sudolineno - 1 : 0;
204 (void) fprintf(stderr, ">>> sudoers file: %s, line %d <<<\n", s,
205 sudolineno ? sudolineno - 1 : 0);
207 (void) fprintf(stderr, "<*> ");
217 struct sudo_command command;
221 %start file /* special start symbol */
222 %token <command> COMMAND /* absolute pathname w/ optional args */
223 %token <string> ALIAS /* an UPPERCASE alias name */
224 %token <string> DEFVAR /* a Defaults variable name */
225 %token <string> NTWKADDR /* w.x.y.z */
226 %token <string> NETGROUP /* a netgroup (+NAME) */
227 %token <string> USERGROUP /* a usergroup (%NAME) */
228 %token <string> WORD /* a word */
229 %token <tok> DEFAULTS /* Defaults entry */
230 %token <tok> DEFAULTS_HOST /* Host-specific defaults entry */
231 %token <tok> DEFAULTS_USER /* User-specific defaults entry */
232 %token <tok> RUNAS /* ( runas_list ) */
233 %token <tok> NOPASSWD /* no passwd req for command */
234 %token <tok> PASSWD /* passwd req for command (default) */
235 %token <tok> ALL /* ALL keyword */
236 %token <tok> COMMENT /* comment and/or carriage return */
237 %token <tok> HOSTALIAS /* Host_Alias keyword */
238 %token <tok> CMNDALIAS /* Cmnd_Alias keyword */
239 %token <tok> USERALIAS /* User_Alias keyword */
240 %token <tok> RUNASALIAS /* Runas_Alias keyword */
241 %token <tok> ':' '=' ',' '!' '+' '-' /* union member tokens */
245 * NOTE: these are not true booleans as there are actually 3 possible values:
246 * 1) TRUE (positive match)
247 * 0) FALSE (negative match due to a '!' somewhere)
248 * -1) No match (don't change the value of *_matches)
252 %type <BOOLEAN> runasuser
253 %type <BOOLEAN> oprunasuser
254 %type <BOOLEAN> runaslist
267 | { push; } userlist privileges {
268 while (top && user_matches != TRUE)
271 | USERALIAS useraliases
273 | HOSTALIAS hostaliases
275 | CMNDALIAS cmndaliases
277 | RUNASALIAS runasaliases
283 defaults_line : defaults_type defaults_list
285 defaults_type : DEFAULTS {
286 defaults_matches = TRUE;
288 | DEFAULTS_USER { push; } userlist {
289 defaults_matches = user_matches;
292 | DEFAULTS_HOST { push; } hostlist {
293 defaults_matches = host_matches;
298 defaults_list : defaults_entry
299 | defaults_entry ',' defaults_list
301 defaults_entry : DEFVAR {
302 if (defaults_matches == TRUE &&
303 !set_default($1, NULL, TRUE)) {
310 if (defaults_matches == TRUE &&
311 !set_default($2, NULL, FALSE)) {
318 if (defaults_matches == TRUE &&
319 !set_default($1, $3, TRUE)) {
327 if (defaults_matches == TRUE &&
328 !set_default($1, $3, '+')) {
336 if (defaults_matches == TRUE &&
337 !set_default($1, $3, '-')) {
345 privileges : privilege
346 | privileges ':' privilege
349 privilege : hostlist '=' cmndspeclist {
351 * We already did a push if necessary in
352 * cmndspec so just reset some values so
353 * the next 'privilege' gets a clean slate.
357 if (def_flag(I_AUTHENTICATE))
377 if (addr_matches($1))
384 if (netgr_matches($1, user_host, user_shost, NULL))
391 if (hostname_matches(user_shost, user_host, $1) == 0)
398 aliasinfo *aip = find_alias($1, HOST_ALIAS);
400 /* could be an all-caps hostname */
403 else if (strcasecmp(user_shost, $1) == 0)
407 (void) fprintf(stderr,
408 "%s: undeclared Host_Alias `%s' referenced near line %d\n",
409 (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
421 cmndspeclist : cmndspec
422 | cmndspeclist ',' cmndspec
425 cmndspec : runasspec nopasswd opcmnd {
427 * Push the entry onto the stack if it is worth
428 * saving and clear cmnd_matches for next cmnd.
430 * We need to save at least one entry on
431 * the stack so sudoers_lookup() can tell that
432 * the user was listed in sudoers. Also, we
433 * need to be able to tell whether or not a
434 * user was listed for this specific host.
436 * If keepall is set and the user matches then
437 * we need to keep entries around too...
439 if (user_matches != -1 && host_matches != -1 &&
440 cmnd_matches != -1 && runas_matches != -1)
442 else if (user_matches != -1 && (top == 1 ||
443 (top == 2 && host_matches != -1 &&
444 match[0].host == -1)))
446 else if (user_matches == TRUE && keepall)
457 if (printmatches == TRUE) {
458 if (in_alias == TRUE)
459 append_entries("!", ", ");
460 else if (host_matches == TRUE &&
461 user_matches == TRUE)
462 append_cmnd("!", NULL);
470 runasspec : /* empty */ {
471 if (printmatches == TRUE && host_matches == TRUE &&
472 user_matches == TRUE) {
473 if (runas_matches == -1) {
474 cm_list[cm_list_len].runas_len = 0;
476 /* Inherit runas data. */
477 cm_list[cm_list_len].runas =
478 estrdup(cm_list[cm_list_len-1].runas);
479 cm_list[cm_list_len].runas_len =
480 cm_list[cm_list_len-1].runas_len;
481 cm_list[cm_list_len].runas_size =
482 cm_list[cm_list_len-1].runas_size;
486 * If this is the first entry in a command list
487 * then check against default runas user.
489 if (runas_matches == -1)
490 runas_matches = (strcmp(*user_runas,
491 def_str(I_RUNAS_DEFAULT)) == 0);
494 runas_matches = ($2 == TRUE ? TRUE : FALSE);
498 runaslist : oprunasuser { ; }
499 | runaslist ',' oprunasuser {
500 /* Later entries override earlier ones. */
508 oprunasuser : runasuser { ; }
510 if (printmatches == TRUE) {
511 if (in_alias == TRUE)
512 append_entries("!", ", ");
513 else if (host_matches == TRUE &&
514 user_matches == TRUE)
515 append_runas("!", ", ");
518 /* Set $$ to the negation of runasuser */
519 $$ = ($3 == -1 ? -1 : ! $3);
523 if (printmatches == TRUE) {
524 if (in_alias == TRUE)
525 append_entries($1, ", ");
526 else if (host_matches == TRUE &&
527 user_matches == TRUE)
528 append_runas($1, ", ");
530 if (strcmp($1, *user_runas) == 0)
537 if (printmatches == TRUE) {
538 if (in_alias == TRUE)
539 append_entries($1, ", ");
540 else if (host_matches == TRUE &&
541 user_matches == TRUE)
542 append_runas($1, ", ");
544 if (usergr_matches($1, *user_runas))
551 if (printmatches == TRUE) {
552 if (in_alias == TRUE)
553 append_entries($1, ", ");
554 else if (host_matches == TRUE &&
555 user_matches == TRUE)
556 append_runas($1, ", ");
558 if (netgr_matches($1, NULL, NULL, *user_runas))
565 aliasinfo *aip = find_alias($1, RUNAS_ALIAS);
567 if (printmatches == TRUE) {
568 if (in_alias == TRUE)
569 append_entries($1, ", ");
570 else if (host_matches == TRUE &&
571 user_matches == TRUE)
572 append_runas($1, ", ");
574 /* could be an all-caps username */
577 else if (strcmp($1, *user_runas) == 0)
581 (void) fprintf(stderr,
582 "%s: undeclared Runas_Alias `%s' referenced near line %d\n",
583 (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
594 if (printmatches == TRUE) {
595 if (in_alias == TRUE)
596 append_entries("ALL", ", ");
597 else if (host_matches == TRUE &&
598 user_matches == TRUE)
599 append_runas("ALL", ", ");
605 nopasswd : /* empty */ {
606 /* Inherit NOPASSWD/PASSWD status. */
607 if (printmatches == TRUE && host_matches == TRUE &&
608 user_matches == TRUE) {
609 if (no_passwd == TRUE)
610 cm_list[cm_list_len].nopasswd = TRUE;
612 cm_list[cm_list_len].nopasswd = FALSE;
617 if (printmatches == TRUE && host_matches == TRUE &&
618 user_matches == TRUE)
619 cm_list[cm_list_len].nopasswd = TRUE;
623 if (printmatches == TRUE && host_matches == TRUE &&
624 user_matches == TRUE)
625 cm_list[cm_list_len].nopasswd = FALSE;
630 if (printmatches == TRUE) {
631 if (in_alias == TRUE)
632 append_entries("ALL", ", ");
633 else if (host_matches == TRUE &&
634 user_matches == TRUE) {
635 append_cmnd("ALL", NULL);
644 safe_cmnd = estrdup(user_cmnd);
649 if (printmatches == TRUE) {
650 if (in_alias == TRUE)
651 append_entries($1, ", ");
652 else if (host_matches == TRUE &&
653 user_matches == TRUE) {
654 append_cmnd($1, NULL);
659 if ((aip = find_alias($1, CMND_ALIAS)))
663 (void) fprintf(stderr,
664 "%s: undeclared Cmnd_Alias `%s' referenced near line %d\n",
665 (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
676 if (printmatches == TRUE) {
677 if (in_alias == TRUE) {
678 append_entries($1.cmnd, ", ");
680 append_entries($1.args, " ");
682 if (host_matches == TRUE &&
683 user_matches == TRUE) {
684 append_cmnd($1.cmnd, NULL);
686 append_cmnd($1.args, " ");
691 if (command_matches(user_cmnd, user_args,
703 hostaliases : hostalias
704 | hostaliases ':' hostalias
707 hostalias : ALIAS { push; } '=' hostlist {
708 if ((host_matches != -1 || pedantic) &&
709 !add_alias($1, HOST_ALIAS, host_matches))
716 | hostlist ',' ophost
719 cmndaliases : cmndalias
720 | cmndaliases ':' cmndalias
725 if (printmatches == TRUE) {
727 /* Allocate space for ga_list if necessary. */
729 ga_list[ga_list_len-1].type = CMND_ALIAS;
730 ga_list[ga_list_len-1].alias = estrdup($1);
733 if ((cmnd_matches != -1 || pedantic) &&
734 !add_alias($1, CMND_ALIAS, cmnd_matches))
739 if (printmatches == TRUE)
744 cmndlist : opcmnd { ; }
745 | cmndlist ',' opcmnd
748 runasaliases : runasalias
749 | runasaliases ':' runasalias
753 if (printmatches == TRUE) {
755 /* Allocate space for ga_list if necessary. */
757 ga_list[ga_list_len-1].type = RUNAS_ALIAS;
758 ga_list[ga_list_len-1].alias = estrdup($1);
761 if (($4 != -1 || pedantic) &&
762 !add_alias($1, RUNAS_ALIAS, $4))
766 if (printmatches == TRUE)
771 useraliases : useralias
772 | useraliases ':' useralias
775 useralias : ALIAS { push; } '=' userlist {
776 if ((user_matches != -1 || pedantic) &&
777 !add_alias($1, USER_ALIAS, user_matches))
785 | userlist ',' opuser
798 if (strcmp($1, user_name) == 0)
805 if (usergr_matches($1, user_name))
812 if (netgr_matches($1, NULL, NULL, user_name))
819 aliasinfo *aip = find_alias($1, USER_ALIAS);
821 /* could be an all-caps username */
824 else if (strcmp($1, user_name) == 0)
828 (void) fprintf(stderr,
829 "%s: undeclared User_Alias `%s' referenced near line %d\n",
830 (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
845 #define MOREALIASES (32)
846 aliasinfo *aliases = NULL;
852 * Compare two aliasinfo structures, strcmp() style.
853 * Note that we do *not* compare their values.
860 aliasinfo *ai1, *ai2;
862 ai1 = (aliasinfo *) a1;
863 ai2 = (aliasinfo *) a2;
864 if ((r = strcmp(ai1->name, ai2->name)) == 0)
865 r = ai1->type - ai2->type;
871 * Compare two generic_alias structures, strcmp() style.
874 genaliascmp(entry, key)
875 const VOID *entry, *key;
878 struct generic_alias *ga1, *ga2;
880 ga1 = (struct generic_alias *) key;
881 ga2 = (struct generic_alias *) entry;
882 if ((r = strcmp(ga1->alias, ga2->alias)) == 0)
883 r = ga1->type - ga2->type;
890 * Adds the named alias of the specified type to the aliases list.
893 add_alias(alias, type, val)
902 if (naliases >= nslots && !more_aliases()) {
903 (void) snprintf(s, sizeof(s), "Out of memory defining alias `%s'",
911 ai.name = estrdup(alias);
912 onaliases = naliases;
914 aip = (aliasinfo *) lsearch((VOID *)&ai, (VOID *)aliases, &naliases,
915 sizeof(ai), aliascmp);
917 (void) snprintf(s, sizeof(s), "Aliases corrupted defining alias `%s'",
922 if (onaliases == naliases) {
923 (void) snprintf(s, sizeof(s), "Alias `%s' already defined", alias);
932 * Searches for the named alias of the specified type.
935 find_alias(alias, type)
944 return((aliasinfo *) lfind((VOID *)&ai, (VOID *)aliases, &naliases,
945 sizeof(ai), aliascmp));
949 * Allocates more space for the aliases list.
955 nslots += MOREALIASES;
956 if (nslots == MOREALIASES)
957 aliases = (aliasinfo *) malloc(nslots * sizeof(aliasinfo));
959 aliases = (aliasinfo *) realloc(aliases, nslots * sizeof(aliasinfo));
961 return(aliases != NULL);
965 * Lists the contents of the aliases list.
972 for (n = 0; n < naliases; n++) {
973 if (aliases[n].val == -1)
976 switch (aliases[n].type) {
978 (void) puts("HOST_ALIAS");
982 (void) puts("CMND_ALIAS");
986 (void) puts("USER_ALIAS");
990 (void) puts("RUNAS_ALIAS");
993 (void) printf("\t%s: %d\n", aliases[n].name, aliases[n].val);
998 * Lists the contents of cm_list and ga_list for `sudo -l'.
1005 struct generic_alias *ga, key;
1007 (void) printf("User %s may run the following commands on this host:\n",
1009 for (i = 0; i < cm_list_len; i++) {
1011 /* Print the runas list. */
1012 (void) fputs(" ", stdout);
1013 if (cm_list[i].runas) {
1014 (void) putchar('(');
1015 p = strtok(cm_list[i].runas, ", ");
1017 if (p != cm_list[i].runas)
1018 (void) fputs(", ", stdout);
1021 key.type = RUNAS_ALIAS;
1022 if ((ga = (struct generic_alias *) lfind((VOID *) &key,
1023 (VOID *) &ga_list[0], &ga_list_len, sizeof(key), genaliascmp)))
1024 (void) fputs(ga->entries, stdout);
1026 (void) fputs(p, stdout);
1027 } while ((p = strtok(NULL, ", ")));
1028 (void) fputs(") ", stdout);
1030 (void) printf("(%s) ", def_str(I_RUNAS_DEFAULT));
1033 /* Is a password required? */
1034 if (cm_list[i].nopasswd == TRUE && def_flag(I_AUTHENTICATE))
1035 (void) fputs("NOPASSWD: ", stdout);
1036 else if (cm_list[i].nopasswd == FALSE && !def_flag(I_AUTHENTICATE))
1037 (void) fputs("PASSWD: ", stdout);
1039 /* Print the actual command or expanded Cmnd_Alias. */
1040 key.alias = cm_list[i].cmnd;
1041 key.type = CMND_ALIAS;
1042 if ((ga = (struct generic_alias *) lfind((VOID *) &key,
1043 (VOID *) &ga_list[0], &ga_list_len, sizeof(key), genaliascmp)))
1044 (void) puts(ga->entries);
1046 (void) puts(cm_list[i].cmnd);
1049 /* Be nice and free up space now that we are done. */
1050 for (i = 0; i < ga_list_len; i++) {
1051 free(ga_list[i].alias);
1052 free(ga_list[i].entries);
1057 for (i = 0; i < cm_list_len; i++) {
1058 free(cm_list[i].runas);
1059 free(cm_list[i].cmnd);
1068 * Appends a source string to the destination, optionally prefixing a separator.
1071 append(src, dstp, dst_len, dst_size, separator)
1073 size_t *dst_len, *dst_size;
1076 size_t src_len = strlen(src);
1080 * Only add the separator if there is something to separate from.
1081 * If the last char is a '!', don't apply the separator (XXX).
1083 if (separator && dst && dst[*dst_len - 1] != '!')
1084 src_len += strlen(separator);
1088 /* Assumes dst will be NULL if not set. */
1090 dst = (char *) emalloc(BUFSIZ);
1096 /* Allocate more space if necessary. */
1097 if (*dst_size <= *dst_len + src_len) {
1098 while (*dst_size <= *dst_len + src_len)
1099 *dst_size += BUFSIZ;
1101 dst = (char *) erealloc(dst, *dst_size);
1105 /* Copy src -> dst adding a separator if appropriate and adjust len. */
1107 *dst_len += src_len;
1110 (void) strcat(dst, separator);
1111 (void) strcat(dst, src);
1115 * Frees up space used by the aliases list and resets the associated counters.
1123 for (n = 0; n < naliases; n++)
1124 free(aliases[n].name);
1128 naliases = nslots = 0;
1132 * Increments ga_list_len, allocating more space as necessary.
1138 if (++ga_list_len >= ga_list_size) {
1139 while ((ga_list_size += STACKINCREMENT) < ga_list_len)
1141 ga_list = (struct generic_alias *)
1142 erealloc(ga_list, sizeof(struct generic_alias) * ga_list_size);
1145 ga_list[ga_list_len - 1].entries = NULL;
1149 * Increments cm_list_len, allocating more space as necessary.
1155 if (++cm_list_len >= cm_list_size) {
1156 while ((cm_list_size += STACKINCREMENT) < cm_list_len)
1158 if (cm_list == NULL)
1159 cm_list_len = 0; /* start at 0 since it is a subscript */
1160 cm_list = (struct command_match *)
1161 erealloc(cm_list, sizeof(struct command_match) * cm_list_size);
1164 cm_list[cm_list_len].runas = cm_list[cm_list_len].cmnd = NULL;
1165 cm_list[cm_list_len].nopasswd = FALSE;
1169 * Frees up spaced used by a previous parser run and allocates new space
1170 * for various data structures.
1176 /* Free up old data structures if we run the parser more than once. */
1181 parse_error = FALSE;
1186 /* Allocate space for the matching stack. */
1187 stacksize = STACKINCREMENT;
1188 match = (struct matchstack *) emalloc(sizeof(struct matchstack) * stacksize);
1190 /* Allocate space for the match list (for `sudo -l'). */
1191 if (printmatches == TRUE)
1192 expand_match_list();