[BACK]Return to parse.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sudo

Annotation of src/usr.bin/sudo/parse.c, Revision 1.12

1.1       millert     1: /*
1.12    ! millert     2:  * Copyright (c) 1996, 1998-2004 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       millert     3:  *
1.12    ! millert     4:  * Permission to use, copy, modify, and distribute this software for any
        !             5:  * purpose with or without fee is hereby granted, provided that the above
        !             6:  * copyright notice and this permission notice appear in all copies.
1.1       millert     7:  *
1.12    ! millert     8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !             9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1       millert    15:  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
                     16:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.11      millert    17:  *
                     18:  * Sponsored in part by the Defense Advanced Research Projects
                     19:  * Agency (DARPA) and Air Force Research Laboratory, Air Force
                     20:  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
1.1       millert    21:  */
                     22:
                     23: #include "config.h"
                     24:
1.6       millert    25: #include <sys/types.h>
                     26: #include <sys/param.h>
                     27: #include <sys/stat.h>
1.1       millert    28: #include <stdio.h>
                     29: #ifdef STDC_HEADERS
                     30: # include <stdlib.h>
1.6       millert    31: # include <stddef.h>
                     32: #else
                     33: # ifdef HAVE_STDLIB_H
                     34: #  include <stdlib.h>
                     35: # endif
1.1       millert    36: #endif /* STDC_HEADERS */
1.6       millert    37: #ifdef HAVE_STRING_H
                     38: # include <string.h>
                     39: #else
                     40: # ifdef HAVE_STRINGS_H
                     41: #  include <strings.h>
                     42: # endif
                     43: #endif /* HAVE_STRING_H */
1.1       millert    44: #ifdef HAVE_UNISTD_H
                     45: # include <unistd.h>
                     46: #endif /* HAVE_UNISTD_H */
1.2       millert    47: #ifdef HAVE_FNMATCH
1.1       millert    48: # include <fnmatch.h>
1.10      millert    49: #endif /* HAVE_FNMATCH */
1.1       millert    50: #ifdef HAVE_NETGROUP_H
                     51: # include <netgroup.h>
                     52: #endif /* HAVE_NETGROUP_H */
                     53: #include <ctype.h>
                     54: #include <pwd.h>
                     55: #include <grp.h>
                     56: #include <netinet/in.h>
                     57: #include <arpa/inet.h>
                     58: #include <netdb.h>
1.6       millert    59: #ifdef HAVE_DIRENT_H
1.1       millert    60: # include <dirent.h>
                     61: # define NAMLEN(dirent) strlen((dirent)->d_name)
                     62: #else
                     63: # define dirent direct
                     64: # define NAMLEN(dirent) (dirent)->d_namlen
1.6       millert    65: # ifdef HAVE_SYS_NDIR_H
1.1       millert    66: #  include <sys/ndir.h>
                     67: # endif
1.6       millert    68: # ifdef HAVE_SYS_DIR_H
1.1       millert    69: #  include <sys/dir.h>
                     70: # endif
1.6       millert    71: # ifdef HAVE_NDIR_H
1.1       millert    72: #  include <ndir.h>
                     73: # endif
                     74: #endif
                     75:
                     76: #include "sudo.h"
                     77: #include "parse.h"
                     78: #include "interfaces.h"
                     79:
                     80: #ifndef HAVE_FNMATCH
                     81: # include "emul/fnmatch.h"
                     82: #endif /* HAVE_FNMATCH */
                     83:
                     84: #ifndef lint
1.12    ! millert    85: static const char rcsid[] = "$Sudo: parse.c,v 1.161 2004/08/24 18:01:13 millert Exp $";
1.1       millert    86: #endif /* lint */
                     87:
                     88: /*
                     89:  * Globals
                     90:  */
                     91: int parse_error = FALSE;
1.3       millert    92: extern int keepall;
1.1       millert    93: extern FILE *yyin, *yyout;
                     94:
                     95: /*
                     96:  * Prototypes
                     97:  */
                     98: static int has_meta    __P((char *));
                     99:        void init_parser        __P((void));
                    100:
                    101: /*
                    102:  * Look up the user in the sudoers file and check to see if they are
                    103:  * allowed to run the specified command on this host as the target user.
                    104:  */
                    105: int
1.6       millert   106: sudoers_lookup(pwflag)
                    107:     int pwflag;
1.1       millert   108: {
1.12    ! millert   109:     int error, nopass;
        !           110:     enum def_tupple pwcheck;
1.1       millert   111:
                    112:     /* We opened _PATH_SUDOERS in check_sudoers() so just rewind it. */
                    113:     rewind(sudoers_fp);
                    114:     yyin = sudoers_fp;
                    115:     yyout = stdout;
                    116:
                    117:     /* Allocate space for data structures in the parser. */
                    118:     init_parser();
                    119:
1.12    ! millert   120:     /* If pwcheck *could* be "all" or "any", keep more state. */
1.6       millert   121:     if (pwflag > 0)
1.3       millert   122:        keepall = TRUE;
                    123:
1.12    ! millert   124:     /* Need to be runas user while stat'ing things in the parser. */
        !           125:     set_perms(PERM_RUNAS);
1.1       millert   126:     error = yyparse();
                    127:
                    128:     /* Close the sudoers file now that we are done with it. */
                    129:     (void) fclose(sudoers_fp);
                    130:     sudoers_fp = NULL;
                    131:
1.12    ! millert   132:     if (error || parse_error) {
        !           133:        set_perms(PERM_ROOT);
1.1       millert   134:        return(VALIDATE_ERROR);
1.12    ! millert   135:     }
1.1       millert   136:
                    137:     /*
1.5       millert   138:      * The pw options may have changed during sudoers parse so we
                    139:      * wait until now to set this.
                    140:      */
1.6       millert   141:     if (pwflag)
1.12    ! millert   142:        pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
1.6       millert   143:     else
                    144:        pwcheck = 0;
1.5       millert   145:
                    146:     /*
1.1       millert   147:      * Assume the worst.  If the stack is empty the user was
                    148:      * not mentioned at all.
                    149:      */
1.12    ! millert   150:     if (def_authenticate)
1.2       millert   151:        error = VALIDATE_NOT_OK;
                    152:     else
                    153:        error = VALIDATE_NOT_OK | FLAG_NOPASS;
1.5       millert   154:     if (pwcheck) {
1.12    ! millert   155:        SET(error, FLAG_NO_CHECK);
1.3       millert   156:     } else {
1.12    ! millert   157:        SET(error, FLAG_NO_HOST);
1.1       millert   158:        if (!top)
1.12    ! millert   159:            SET(error, FLAG_NO_USER);
1.3       millert   160:     }
1.1       millert   161:
                    162:     /*
1.12    ! millert   163:      * Only check the actual command if pwflag is not set.
1.3       millert   164:      * It is set for the "validate", "list" and "kill" pseudo-commands.
1.1       millert   165:      * Always check the host and user.
                    166:      */
1.6       millert   167:     nopass = -1;
1.12    ! millert   168:     if (pwflag) {
1.6       millert   169:        int found;
1.3       millert   170:
1.12    ! millert   171:        if (pwcheck == always && def_authenticate)
        !           172:            nopass = FLAG_CHECK_USER;
        !           173:        else if (pwcheck == never || !def_authenticate)
1.3       millert   174:            nopass = FLAG_NOPASS;
                    175:        found = 0;
1.1       millert   176:        while (top) {
                    177:            if (host_matches == TRUE) {
1.3       millert   178:                found = 1;
1.12    ! millert   179:                if (pwcheck == any && no_passwd == TRUE)
1.3       millert   180:                    nopass = FLAG_NOPASS;
1.12    ! millert   181:                else if (pwcheck == all && nopass != 0)
1.3       millert   182:                    nopass = (no_passwd == TRUE) ? FLAG_NOPASS : 0;
1.1       millert   183:            }
                    184:            top--;
                    185:        }
1.3       millert   186:        if (found) {
1.12    ! millert   187:            set_perms(PERM_ROOT);
1.3       millert   188:            if (nopass == -1)
                    189:                nopass = 0;
                    190:            return(VALIDATE_OK | nopass);
                    191:        }
                    192:     } else {
1.1       millert   193:        while (top) {
                    194:            if (host_matches == TRUE) {
1.12    ! millert   195:                CLR(error, FLAG_NO_HOST);
        !           196:                if (runas_matches == TRUE && cmnd_matches == TRUE) {
        !           197:                    /*
        !           198:                     * User was granted access to cmnd on host as user.
        !           199:                     */
        !           200:                    set_perms(PERM_ROOT);
        !           201:                    return(VALIDATE_OK |
        !           202:                        (no_passwd == TRUE ? FLAG_NOPASS : 0) |
        !           203:                        (no_execve == TRUE ? FLAG_NOEXEC : 0));
        !           204:                } else if ((runas_matches == TRUE && cmnd_matches == FALSE) ||
        !           205:                    (runas_matches == FALSE && cmnd_matches == TRUE)) {
        !           206:                    /*
        !           207:                     * User was explicitly denied access to cmnd on host.
        !           208:                     */
        !           209:                    set_perms(PERM_ROOT);
        !           210:                    return(VALIDATE_NOT_OK |
        !           211:                        (no_passwd == TRUE ? FLAG_NOPASS : 0) |
        !           212:                        (no_execve == TRUE ? FLAG_NOEXEC : 0));
1.1       millert   213:                }
                    214:            }
                    215:            top--;
                    216:        }
1.3       millert   217:     }
1.12    ! millert   218:     set_perms(PERM_ROOT);
1.1       millert   219:
                    220:     /*
1.12    ! millert   221:      * The user was neither explicitly granted nor denied access.
1.1       millert   222:      */
1.6       millert   223:     if (nopass == -1)
                    224:        nopass = 0;
                    225:     return(error | nopass);
1.1       millert   226: }
                    227:
                    228: /*
                    229:  * If path doesn't end in /, return TRUE iff cmnd & path name the same inode;
1.12    ! millert   230:  * otherwise, return TRUE if user_cmnd names one of the inodes in path.
1.1       millert   231:  */
                    232: int
1.12    ! millert   233: command_matches(sudoers_cmnd, sudoers_args)
        !           234:     char *sudoers_cmnd;
1.1       millert   235:     char *sudoers_args;
                    236: {
1.12    ! millert   237:     struct stat sudoers_stat;
        !           238:     struct dirent *dent;
        !           239:     char buf[PATH_MAX];
1.1       millert   240:     DIR *dirp;
                    241:
1.12    ! millert   242:     /* Check for pseudo-commands */
        !           243:     if (strchr(user_cmnd, '/') == NULL) {
        !           244:        /*
        !           245:         * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND
        !           246:         *  a) there are no args in sudoers OR
        !           247:         *  b) there are no args on command line and none req by sudoers OR
        !           248:         *  c) there are args in sudoers and on command line and they match
        !           249:         */
        !           250:        if (strcmp(sudoers_cmnd, "sudoedit") != 0 ||
        !           251:            strcmp(user_cmnd, "sudoedit") != 0)
        !           252:            return(FALSE);
        !           253:        if (!sudoers_args ||
        !           254:            (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
        !           255:            (sudoers_args &&
        !           256:             fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
        !           257:            if (safe_cmnd)
        !           258:                free(safe_cmnd);
        !           259:            safe_cmnd = estrdup(sudoers_cmnd);
        !           260:            return(TRUE);
        !           261:        } else
1.1       millert   262:            return(FALSE);
                    263:     }
                    264:
                    265:     /*
1.12    ! millert   266:      * If sudoers_cmnd has meta characters in it, use fnmatch(3)
        !           267:      * to do the matching.
1.1       millert   268:      */
1.12    ! millert   269:     if (has_meta(sudoers_cmnd)) {
1.1       millert   270:        /*
                    271:         * Return true if fnmatch(3) succeeds AND
                    272:         *  a) there are no args in sudoers OR
                    273:         *  b) there are no args on command line and none required by sudoers OR
                    274:         *  c) there are args in sudoers and on command line and they match
                    275:         * else return false.
                    276:         */
1.12    ! millert   277:        if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
1.1       millert   278:            return(FALSE);
                    279:        if (!sudoers_args ||
1.12    ! millert   280:            (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
        !           281:            (sudoers_args &&
        !           282:             fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
1.1       millert   283:            if (safe_cmnd)
                    284:                free(safe_cmnd);
                    285:            safe_cmnd = estrdup(user_cmnd);
                    286:            return(TRUE);
                    287:        } else
                    288:            return(FALSE);
                    289:     } else {
1.12    ! millert   290:        size_t dlen = strlen(sudoers_cmnd);
        !           291:
1.1       millert   292:        /*
                    293:         * No meta characters
                    294:         * Check to make sure this is not a directory spec (doesn't end in '/')
                    295:         */
1.12    ! millert   296:        if (sudoers_cmnd[dlen - 1] != '/') {
        !           297:            char *base;
1.1       millert   298:
1.12    ! millert   299:            /* Only proceed if user_base and basename(sudoers_cmnd) match */
        !           300:            if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
        !           301:                base = sudoers_cmnd;
1.1       millert   302:            else
1.12    ! millert   303:                base++;
        !           304:            if (strcmp(user_base, base) != 0 ||
        !           305:                stat(sudoers_cmnd, &sudoers_stat) == -1)
1.1       millert   306:                return(FALSE);
                    307:
                    308:            /*
                    309:             * Return true if inode/device matches AND
                    310:             *  a) there are no args in sudoers OR
                    311:             *  b) there are no args on command line and none req by sudoers OR
                    312:             *  c) there are args in sudoers and on command line and they match
                    313:             */
1.12    ! millert   314:            if (user_stat->st_dev != sudoers_stat.st_dev ||
        !           315:                user_stat->st_ino != sudoers_stat.st_ino)
1.1       millert   316:                return(FALSE);
                    317:            if (!sudoers_args ||
1.12    ! millert   318:                (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
1.1       millert   319:                (sudoers_args &&
1.12    ! millert   320:                 fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
1.1       millert   321:                if (safe_cmnd)
                    322:                    free(safe_cmnd);
1.12    ! millert   323:                safe_cmnd = estrdup(sudoers_cmnd);
1.1       millert   324:                return(TRUE);
                    325:            } else
                    326:                return(FALSE);
                    327:        }
                    328:
                    329:        /*
1.12    ! millert   330:         * Grot through sudoers_cmnd's directory entries, looking for user_base.
1.1       millert   331:         */
1.12    ! millert   332:        dirp = opendir(sudoers_cmnd);
1.1       millert   333:        if (dirp == NULL)
                    334:            return(FALSE);
                    335:
1.12    ! millert   336:        if (strlcpy(buf, sudoers_cmnd, sizeof(buf)) >= sizeof(buf))
        !           337:            return(FALSE);
1.1       millert   338:        while ((dent = readdir(dirp)) != NULL) {
1.12    ! millert   339:            /* ignore paths > PATH_MAX (XXX - log) */
        !           340:            buf[dlen] = '\0';
        !           341:            if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
1.1       millert   342:                continue;
                    343:
                    344:            /* only stat if basenames are the same */
1.12    ! millert   345:            if (strcmp(user_base, dent->d_name) != 0 ||
        !           346:                stat(buf, &sudoers_stat) == -1)
1.1       millert   347:                continue;
1.12    ! millert   348:            if (user_stat->st_dev == sudoers_stat.st_dev &&
        !           349:                user_stat->st_ino == sudoers_stat.st_ino) {
1.1       millert   350:                if (safe_cmnd)
                    351:                    free(safe_cmnd);
                    352:                safe_cmnd = estrdup(buf);
                    353:                break;
                    354:            }
                    355:        }
                    356:
                    357:        closedir(dirp);
                    358:        return(dent != NULL);
                    359:     }
                    360: }
                    361:
                    362: /*
                    363:  * Returns TRUE if "n" is one of our ip addresses or if
                    364:  * "n" is a network that we are on, else returns FALSE.
                    365:  */
                    366: int
                    367: addr_matches(n)
                    368:     char *n;
                    369: {
                    370:     int i;
                    371:     char *m;
                    372:     struct in_addr addr, mask;
                    373:
                    374:     /* If there's an explicit netmask, use it. */
                    375:     if ((m = strchr(n, '/'))) {
                    376:        *m++ = '\0';
                    377:        addr.s_addr = inet_addr(n);
                    378:        if (strchr(m, '.'))
                    379:            mask.s_addr = inet_addr(m);
1.6       millert   380:        else {
                    381:            i = 32 - atoi(m);
                    382:            mask.s_addr = 0xffffffff;
                    383:            mask.s_addr >>= i;
                    384:            mask.s_addr <<= i;
                    385:            mask.s_addr = htonl(mask.s_addr);
                    386:        }
1.1       millert   387:        *(m - 1) = '/';
                    388:
                    389:        for (i = 0; i < num_interfaces; i++)
                    390:            if ((interfaces[i].addr.s_addr & mask.s_addr) == addr.s_addr)
                    391:                return(TRUE);
                    392:     } else {
                    393:        addr.s_addr = inet_addr(n);
                    394:
                    395:        for (i = 0; i < num_interfaces; i++)
                    396:            if (interfaces[i].addr.s_addr == addr.s_addr ||
                    397:                (interfaces[i].addr.s_addr & interfaces[i].netmask.s_addr)
                    398:                == addr.s_addr)
                    399:                return(TRUE);
                    400:     }
                    401:
                    402:     return(FALSE);
                    403: }
                    404:
                    405: /*
1.4       millert   406:  * Returns 0 if the hostname matches the pattern and non-zero otherwise.
                    407:  */
                    408: int
                    409: hostname_matches(shost, lhost, pattern)
                    410:     char *shost;
                    411:     char *lhost;
                    412:     char *pattern;
                    413: {
                    414:     if (has_meta(pattern)) {
                    415:        if (strchr(pattern, '.'))
                    416:            return(fnmatch(pattern, lhost, FNM_CASEFOLD));
                    417:        else
                    418:            return(fnmatch(pattern, shost, FNM_CASEFOLD));
                    419:     } else {
                    420:        if (strchr(pattern, '.'))
                    421:            return(strcasecmp(lhost, pattern));
                    422:        else
                    423:            return(strcasecmp(shost, pattern));
                    424:     }
                    425: }
                    426:
                    427: /*
1.12    ! millert   428:  *  Returns TRUE if the user/uid from sudoers matches the specified user/uid,
        !           429:  *  else returns FALSE.
        !           430:  */
        !           431: int
        !           432: userpw_matches(sudoers_user, user, pw)
        !           433:     char *sudoers_user;
        !           434:     char *user;
        !           435:     struct passwd *pw;
        !           436: {
        !           437:     if (pw != NULL && *sudoers_user == '#') {
        !           438:        uid_t uid = atoi(sudoers_user + 1);
        !           439:        if (uid == pw->pw_uid)
        !           440:            return(1);
        !           441:     }
        !           442:     return(strcmp(sudoers_user, user) == 0);
        !           443: }
        !           444:
        !           445: /*
1.1       millert   446:  *  Returns TRUE if the given user belongs to the named group,
                    447:  *  else returns FALSE.
1.12    ! millert   448:  *  XXX - reduce the number of passwd/group lookups
1.1       millert   449:  */
                    450: int
1.12    ! millert   451: usergr_matches(group, user, pw)
1.1       millert   452:     char *group;
                    453:     char *user;
1.12    ! millert   454:     struct passwd *pw;
1.1       millert   455: {
                    456:     struct group *grp;
1.12    ! millert   457:     gid_t pw_gid;
1.1       millert   458:     char **cur;
                    459:
                    460:     /* make sure we have a valid usergroup, sudo style */
                    461:     if (*group++ != '%')
                    462:        return(FALSE);
                    463:
1.12    ! millert   464:     /* look up user's primary gid in the passwd file */
        !           465:     if (pw == NULL && (pw = getpwnam(user)) == NULL)
1.1       millert   466:        return(FALSE);
1.12    ! millert   467:     pw_gid = pw->pw_gid;
1.1       millert   468:
1.12    ! millert   469:     if ((grp = getgrnam(group)) == NULL)
1.1       millert   470:        return(FALSE);
                    471:
1.12    ! millert   472:     /* check against user's primary (passwd file) gid */
        !           473:     if (grp->gr_gid == pw_gid)
1.1       millert   474:        return(TRUE);
                    475:
1.12    ! millert   476:     /* check to see if user is explicitly listed in the group */
        !           477:     for (cur = grp->gr_mem; *cur; cur++) {
1.1       millert   478:        if (strcmp(*cur, user) == 0)
                    479:            return(TRUE);
                    480:     }
                    481:
                    482:     return(FALSE);
                    483: }
                    484:
                    485: /*
                    486:  * Returns TRUE if "host" and "user" belong to the netgroup "netgr",
1.3       millert   487:  * else return FALSE.  Either of "host", "shost" or "user" may be NULL
1.1       millert   488:  * in which case that argument is not checked...
                    489:  */
                    490: int
1.3       millert   491: netgr_matches(netgr, host, shost, user)
1.1       millert   492:     char *netgr;
                    493:     char *host;
1.3       millert   494:     char *shost;
1.1       millert   495:     char *user;
                    496: {
                    497: #ifdef HAVE_GETDOMAINNAME
                    498:     static char *domain = (char *) -1;
                    499: #else
                    500:     static char *domain = NULL;
                    501: #endif /* HAVE_GETDOMAINNAME */
                    502:
                    503:     /* make sure we have a valid netgroup, sudo style */
                    504:     if (*netgr++ != '+')
                    505:        return(FALSE);
                    506:
                    507: #ifdef HAVE_GETDOMAINNAME
                    508:     /* get the domain name (if any) */
                    509:     if (domain == (char *) -1) {
                    510:        domain = (char *) emalloc(MAXHOSTNAMELEN);
                    511:        if (getdomainname(domain, MAXHOSTNAMELEN) == -1 || *domain == '\0') {
                    512:            free(domain);
                    513:            domain = NULL;
                    514:        }
                    515:     }
                    516: #endif /* HAVE_GETDOMAINNAME */
                    517:
                    518: #ifdef HAVE_INNETGR
1.3       millert   519:     if (innetgr(netgr, host, user, domain))
                    520:        return(TRUE);
                    521:     else if (host != shost && innetgr(netgr, shost, user, domain))
                    522:        return(TRUE);
                    523: #endif /* HAVE_INNETGR */
                    524:
1.1       millert   525:     return(FALSE);
                    526: }
                    527:
                    528: /*
                    529:  * Returns TRUE if "s" has shell meta characters in it,
                    530:  * else returns FALSE.
                    531:  */
                    532: static int
                    533: has_meta(s)
                    534:     char *s;
                    535: {
1.4       millert   536:     char *t;
1.12    ! millert   537:
1.1       millert   538:     for (t = s; *t; t++) {
                    539:        if (*t == '\\' || *t == '?' || *t == '*' || *t == '[' || *t == ']')
                    540:            return(TRUE);
                    541:     }
                    542:     return(FALSE);
                    543: }