static const char rcsid[] = "$Id: options.c,v 1.129 2004/10/22 00:22:19 will Exp $"; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Process a global xxx=yyy option */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "super.h" #include "options.h" /* KEEP SORTED IN INCREASING ORDER! (If you don't, we do sort the list * later, but you may as well do your bit to decrease entropy :-) */ Option opts[] = { { "DIE", LOCAL, option_die }, /* OBSOLETE */ { "addgroups", LIST|GLOBAL|LOCAL, option_groups }, { "arg", GLOBAL|LOCAL, option_arg }, { "argv0", LOCAL, option_argv0 }, { "auth", GLOBAL|LOCAL, option_auth }, { "authprompt", GLOBAL|LOCAL, option_authprompt }, { "authtype", GLOBAL|LOCAL, option_authtype }, { "authuser", GLOBAL|LOCAL, option_authuser }, { "cd", GLOBAL|LOCAL, option_cd }, { "checkvar", LIST|LOCAL, option_checkvar }, { "die", LOCAL, option_die }, { "egid", LOCAL, option_egid }, { "env", LIST|GLOBAL|LOCAL, option_env }, { "euid", LOCAL, option_euid }, { "fd", LIST|LOCAL, option_fd }, { "gethostbyname", GLOBAL, option_gethostbyname }, { "gid", LOCAL, option_gid }, { "group_slash", GLOBAL, option_group_slash }, { "groups", LIST|GLOBAL|LOCAL, option_groups }, { "info", LOCAL, option_info }, { "lang", GLOBAL, option_lang }, { "logfile", GLOBAL, option_logfile }, { "loguid", GLOBAL, option_loguid }, { "mail", GLOBAL|LOCAL, option_mail }, { "mailany", GLOBAL|LOCAL, option_mailany }, { "maxenvlen", GLOBAL|LOCAL, option_maxenvlen }, { "maxlen", GLOBAL|LOCAL, option_maxlen }, { "nargs", GLOBAL|LOCAL, option_nargs }, { "nice", GLOBAL|LOCAL, option_nice }, { "owner", GLOBAL|LOCAL, option_owner }, { "password", GLOBAL|LOCAL, option_password }, { "patterns", GLOBAL, option_patterns }, { "print", LOCAL, option_print }, { "pwprompt", GLOBAL|LOCAL, option_authprompt }, { "relative_path", GLOBAL, option_relative_path }, { "renewtime", GLOBAL, option_renewtime }, { "rlog_host", GLOBAL, option_rlog_host }, { "setenv", GLOBAL|LOCAL, option_setenv }, { "syslog", GLOBAL, option_syslog }, { "syslog_success", GLOBAL, option_syslog_success }, { "syslog_error", GLOBAL, option_syslog_error }, { "timeout", GLOBAL|LOCAL, option_timeout }, { "timestampbyhost", GLOBAL, option_timestampbyhost }, { "timestampuid", GLOBAL, option_timestampuid }, { "u+g", LOCAL, option_u_g }, { "uid", LOCAL, option_uid }, { "umask", GLOBAL|LOCAL, option_umask }, { "", 0, NULL }, }; /* OK, so it's a really stupid hack, but it does let me have all option_xxx() * functions take the same argument lists; those that are LIST types, and * need an extra list argument passed, can pick it up here. */ static char **optionlist; void sort_optlist() { /* Sort the options list. This is just an insertion sort, but * it'll work well for almost-sorted data. */ int i, j; int N = NELEM(opts)-1; /* use NELEM-1 because the last element is * the empty end-of-list marker. */ for (i=1; i < N; i++) { for (j=i; j && strcmp(opts[j-1].name, opts[j].name) > 0; j--) { Option o; /* Swap elements; use memcpy in case struct assignment isn't * allowed by this compiler. */ memcpy((void *) &o, (void *) &opts[j-1], sizeof(opts[0])); memcpy((void *) &opts[j-1], (void *) &opts[j], sizeof(opts[0])); memcpy((void *) &opts[j], (void *) &o, sizeof(opts[0])); } } } /* binary search in options list for option word. */ Option * find_option(word) char *word; { int i, wordlen, namelen; static int firsttime=1; Option *lower = &opts[0]; Option *upper = &opts[NELEM(opts)-1]; /* 1 past last non-empty elt */ Option *mid; char *sep; if (firsttime) { sort_optlist(); firsttime=0; } if ((strncmp(word, "arg", 3) == 0) && strchr("0123456789", word[3])) { /* The argMMM[-NNN] option has to be treated as if it were "arg". */ word = "arg"; wordlen = 3; } else if (!(sep = strchr(word, OPTION_SEP))) { Error(0, 0, "%t\n\tsuper.tab option `%s' is missing separator `%c'\n", word, OPTION_SEP); return NULL; } else { /* word length up to the separator */ wordlen = sep - word; } while (lower < upper) { mid = lower + (upper - lower) / 2; namelen = strlen(mid->name); if ((i=strncmp(word, mid->name, namelen)) < 0) { upper = mid; } else if (i > 0) { lower = mid + 1; } else if (wordlen == namelen) { /* exact match */ return mid; } else { /* It didn't compare right. If wordlen < namelen, the strncmp() * above would have failed, hence we know that wordlen > namelen, * hence hence word > mid... */ lower = mid + 1; } } return NULL; } int handle_option(word, value, isglobal) char *word; /* The input xxxyyy string; NULL means clear settings */ char *value; /* pts to yyy in word; can be NULL if wd is NULL */ int isglobal; /* bool: are we processing a global or local entry? */ { Option *opt; /* We only want to print debug info at just the right points, else we * get too much junk output. So define a macro to print our stuff, then * invoke it as necessary. Result: messier programming but better output. */ #define DoDebug() \ { if (debug>1) fprintf(stderr, \ "\thandle_option(): word=<%s> value=<%s>; is %s\n", \ word, value, isglobal ? "global" : "local"); } if (!word) return option_clear_settings(word, NULL, isglobal); if (*word == '!') { DoDebug(); return Error(0, 0, "%t\n\tsuper.tab syntax error: \n\ options cannot be negated: <%s>\n", word); } opt = find_option(word); if (opt) { if (isglobal && !(opt->flags & GLOBAL)) { DoDebug(); return Error(0, 0, "%t\n\t`%s' may not be used as a global option\n", word); } if (!isglobal && !(opt->flags & LOCAL)) { DoDebug(); return Error(0, 0, "%t\n\t`%s' may not be used as a local option\n", word); } if (opt->flags & LIST) { /* Brace-expand arg list and pass */ char **globlist; int i; if ((i=globbraces(value, 1, &globlist)) != 0) { /* Option argument */ DoDebug(); return Error(0, 0, "%tMissing `%c'.\n", i); } optionlist = globlist; } return opt->process(word, value, isglobal); } else { DoDebug(); if (value-word == 5 && strncmp(word, "time", 4) == 0) { return Error(0, 0, "%t\n\tUnrecognized %s option `%s'.\n\ \tPerhaps you meant to use the condition `time~%s'?\n", isglobal ? "global" : "local", word, value); } else { return Error(0, 0, "%t\n\tInvalid %s option `%s'.\n", isglobal ? "global" : "local", word); } } /* NOTREACHED */ return 0; /* Unreachable statement. Including this shuts up a warning * from some compilers, but generates a warning from others. */ #undef DoDebug } int option_clear_settings(word, s, isglobal) char *word; char *s; int isglobal; { if (isglobal) return option_global_reset_settings(); else return option_local_clear_settings(); /* NOTREACHED */ } int option_global_reset_settings() { /* reset global user/group/host patterns pointers, so that more names * form a new list, and are not appended to the old one. * Similarly for time~pattern pointers. * *** Note that we don't free any pattern space! * *** We assume that there is plenty of memory in the computer * *** to slurp up all _global_ patterns. */ if (debug>1) fprintf(stderr, "\toption_global_reset_settings()\n"); globalinfo.user_clear = 1; globalinfo.time_clear = 1; return 0; } int option_local_clear_settings() { /* Clear local settings */ int i; int maxfd = MAXFD; if (debug>1) fprintf(stderr, "\toption_local_clear_settings()\n"); maxfd = MAXFD; if (localinfo.info) free(localinfo.info); localinfo.info = NULL; if (localinfo.die) free(localinfo.die); localinfo.die = NULL; if (localinfo.print) free(localinfo.print); localinfo.print = NULL; if (localinfo.chdir_path) free(localinfo.chdir_path); localinfo.chdir_path = NULL; if (localinfo.argv0) free(localinfo.argv0); localinfo.argv0 = NULL; *localinfo.euser = *localinfo.user = '\0'; *localinfo.egroup = *localinfo.group = '\0'; *localinfo.u_g = '\0'; if (localinfo.env) blkfree(localinfo.env); if (globalinfo.env) { if (!(localinfo.env = blkdup(globalinfo.env))) (void) Error(0, 2, "failed to alloc space for envvar list.\n"); } else { localinfo.env = NULL; } localinfo.setenv[0] = NULL; for (i=0; i<=MAXSETENV; i++) { if (localinfo.setenv[i]) free(localinfo.setenv[i]); localinfo.setenv[i]=NULL; } localinfo.maxlen1arg = globalinfo.maxlen1arg; localinfo.maxlenargs = globalinfo.maxlenargs; strcpy(localinfo.owner, globalinfo.owner); if (localinfo.fdlist) free(localinfo.fdlist); localinfo.fdlist = NULL; if (!localinfo.fd) { localinfo.fd = (int *) malloc(sizeof(int) * (maxfd + 1)); if (!localinfo.fd) return Error(1, 0, "%t\n\tFailed to malloc space for file descriptor list: "); } for (i=0; i<=maxfd; i++) { localinfo.fd[i] = 0; } localinfo.mailcmd[0] = '\0'; localinfo.mail_success = -1; /* Don't let local groups default to global groups, because then we * can't tell if local groups were assigned, and we want to set priority * to be (1) local groups=xxx; (2) local u+g=xxx; (3) global groups=xxx. */ localinfo.ngroups = GROUPS_NOTSET; localinfo.groups_added = 0; error_command = globalinfo.mailcmd[0] ? globalinfo.mailcmd : NULL; init_umask(0); localinfo.file_uid = UID_NOTSET; localinfo.file_gid = GID_NOTSET; localinfo.nice_incr = globalinfo.nice_incr; free_TimeList(&localinfo.time); free_Simple2List(&localinfo.userpats); free_SimpleList(&localinfo.origtext); localinfo.usr_args[0] = globalinfo.usr_args[0]; localinfo.usr_args[1] = globalinfo.usr_args[1]; /* special case: don't initialize localinfo argpats to be * a copy of globalinfo argpats. That's because the * argpats accumulate, and we mustn't accumulate a pile * of local options on top of a copy of the global ones. * When we need to use them, we'll copy from global value * if the local value is still a null ptr. */ ARfree(localinfo.argpats.next); localinfo.argpats.next = NULL; if (localinfo.checkvar) blkfree(localinfo.checkvar); localinfo.checkvar = NULL; /* should use struct assignment, but that's not portable to * all k&r compilers. */ localinfo.authinfo.required = globalinfo.authinfo.required; localinfo.authinfo.method = globalinfo.authinfo.method; localinfo.authinfo.timeout = globalinfo.authinfo.timeout; localinfo.authinfo.renewtime = globalinfo.authinfo.renewtime; localinfo.authinfo.perhost = globalinfo.authinfo.perhost; if (localinfo.authinfo.prompt) free(localinfo.authinfo.prompt); localinfo.authinfo.prompt = globalinfo.authinfo.prompt; strcpy(localinfo.authinfo.user, globalinfo.authinfo.user); strcpy(localinfo.authinfo.ts_user, globalinfo.authinfo.ts_user); return 0; } /***************************************************************************/ /* */ /***************************************************************************/ static int option_auth(word, s, isglobal) char *word; char *s; int isglobal; { int required; if (debug) fprintf(stderr, "\toption:auth=%s (%s)\n", s, isglobal ? "global" : "local"); if (strcmp(s, "y") == 0) { required = 1; } else if (strcmp(s, "n") == 0) { required = 0; } else { return Error(0, 0, "%t\n\tInvalid option value \ in `%s' -- value must be `y' or `n'\n", word); } if (required && using_user_supertab) { return Error(0, 0, "%t\n\tUser-supplied .supertab files may not do \ authentication checking -- sorry.\n"); } if (isglobal) globalinfo.authinfo.required = required; else localinfo.authinfo.required = required; return 0; } static int option_authprompt(word, s, isglobal) char *word; char *s; int isglobal; { char *str; if (debug) fprintf(stderr, "\toption:authprompt=%s (%s)\n", s, isglobal ? "global" : "local"); if (using_user_supertab) { return Error(0, 0, "%t\n\tUser-supplied .supertab files may not do \ password checking -- sorry.\n"); } str = strdup(s); if (!str) return Error(0, 0, "%t\n\tfailed to malloc space for authprompt=<%s>\n", s); if (isglobal) globalinfo.authinfo.prompt = str; else localinfo.authinfo.prompt = str; return 0; } static int option_authtype(word, s, isglobal) char *word; char *s; int isglobal; { int method; if (debug) fprintf(stderr, "\toption:authtype=%s (%s)\n", s, isglobal ? "global" : "local"); if (strcmp(s, "pam") == 0) { method = SUPER_AUTH_PAM; } else if (strcmp(s, "password") == 0) { method = SUPER_AUTH_PASSWORD; } else { return Error(0, 0, "%t\n\tInvalid option value \ in `%s' -- value must be `pam' or `password'\n", word); } if (isglobal) globalinfo.authinfo.method = method; else localinfo.authinfo.method = method; return 0; } static int option_authuser(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:authuser=%s (%s)\n", s, isglobal ? "global" : "local"); /* Don't do any logging if debugging or just checking syntax! */ if (check_syntax || it_came_from_cmdline) { Error(0, 0, "%t\n\tIgnoring authuser option, due to debug mode\n"); return 0; } if (isglobal) strcpy(globalinfo.authinfo.user, s); else strcpy(localinfo.authinfo.user, s); return 0; } /***************************************************************************/ /* */ /***************************************************************************/ static int option_arg(word, pat, isglobal) char *word; char *pat; int isglobal; { /* Must be argMMM-NNNSSS or argNNNSSS */ int i, iarg1, iarg2; char eq; ArgRangePat *argpats; if (debug) fprintf(stderr, "\toption:looks like argMMM-NNN=%s (%s)...\n", pat, isglobal ? "global" : "local"); if (isglobal) { argpats = &globalinfo.argpats; } else { argpats = &localinfo.argpats; } /* Check that word matches argNNN=SSS */ i = sscanf(word, "arg%d-%d%c", &iarg1, &iarg2, &eq); if (i != 3 || eq != OPTION_SEP) { i = sscanf(word, "arg%d%c", &iarg1, &eq); if (i == 2) i = 3; iarg2 = iarg1; } if (debug) fprintf(stderr, "\toption:is arg%d-%d=%s (%s)\n", iarg1, iarg2, pat, isglobal ? "global" : "local"); if (i != 3 || eq != OPTION_SEP || iarg1 <= 0 || iarg2 < iarg1) return Error(0, 0, "%t\n\tImproper use of argNNN%cXXX option\n", OPTION_SEP); /* Put pattern into the argpat list. */ { char buf[500]; if (strlen(pat) > sizeof(buf)-3) return Error(0, 0, "%t\n\tArgument pattern <%s> too long (max %d)\n", pat, sizeof(buf)-3); anchor(pat, buf); if (ARinsert(argpats, iarg1, iarg2, buf) == -1) { return Error(0, 0, "Failed to malloc space for argument pattern <%s>\n", buf); } } return 0; } static int option_argv0(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:argv0=%s (%s)\n", s, isglobal ? "global" : "local"); localinfo.argv0 = strdup(s); if (!localinfo.argv0) return Error(0, 0, "Failed to malloc space for argv0=<%s>\n", s); return 0; } static int option_cd(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:cd=%s (%s)\n", s, isglobal ? "global" : "local"); if (isglobal) { globalinfo.chdir_path = strdup(s); if (!globalinfo.chdir_path) return Error(0, 0, "Failed to malloc space for cd=<%s>\n", s); } else { localinfo.chdir_path = strdup(s); if (!localinfo.chdir_path) return Error(0, 0, "Failed to malloc space for cd=<%s>\n", s); } return 0; } static int option_checkvar(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:checkvar=%s (%s)\n", s, isglobal ? "global" : "local"); if (isglobal) { return Error(0, 0, "internal error -- option_checkvar called as global!\n"); } else { localinfo.checkvar = optionlist; } return 0; } static int option_die(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:die=%s (%s)\n", s, isglobal ? "global" : "local"); localinfo.die = strdup(s); if (!localinfo.die) return Error(0, 0, "%t\n\tfailed to malloc space for die=<%s>\n", s); return 0; } static int option_egid(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:egid=%s (%s)\n", s, isglobal ? "global" : "local"); stringcopy(localinfo.egroup, s, sizeof(localinfo.egroup)); return 0; } static int option_euid(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:euid=%s (%s)\n", s, isglobal ? "global" : "local"); stringcopy(localinfo.euser, s, sizeof(localinfo.euser)); return 0; } static int option_env(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:env=%s (%s)\n", s, isglobal ? "global" : "local"); if (isglobal) { globalinfo.env = optionlist; } else { localinfo.env = optionlist; } return 0; } static int option_maxenvlen(word, s, isglobal) char *word; char *s; int isglobal; { int i, m; if (debug) fprintf(stderr, "\toption:env=%s (%s)\n", s, isglobal ? "global" : "local"); i = sscanf(s, "%d", &m); if (i != 1) { return Error(0, 0, "%t\n\tmaxenvlen must be maxenvlen=nnn.\n", word); } if (isglobal) { globalinfo.maxenvlen = m; } else { localinfo.maxenvlen = m; } return 0; } static int option_fd(word, s, isglobal) char *word; char *s; int isglobal; { int n; int iwd; char *p, *wd; int maxfd = MAXFD; if (debug) fprintf(stderr, "\toption:fd=%s (%s)\n", s, isglobal ? "global" : "local"); localinfo.fdlist = strdup(s); if (!localinfo.fdlist) return Error(0, 0, "Failed to malloc space for copy of fd=<%s>\n", s); for (iwd=0; (wd=optionlist[iwd]); iwd++) { if (((n=strtol(wd, &p, 0)) >= 0) && n <= maxfd && p != wd) localinfo.fd[n] = 1; else return Error(0, 0, "%t\n\tRidiculous value in file descriptor list: `%s'\n", word); } blkfree(optionlist); return 0; } static int option_gid(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:gid=%s (%s)\n", s, isglobal ? "global" : "local"); stringcopy(localinfo.group, s, sizeof(localinfo.group)); return 0; } static int option_group_slash(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:group_slash=%s (%s)\n", s, isglobal ? "global" : "local"); if (strcmp(s, "n") == 0) { globalinfo.group_slash = 0; } else { globalinfo.group_slash = 1; if (strcmp(s, "y") != 0) return Error(0, 0, "%t\n\tInvalid option value \ in `%s' -- value must be `y' or `n'\n", word); } return 0; } #ifndef HAVE_GETGROUPS static int option_groups(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:%s=%s (%s)\n", word, s, isglobal ? "global" : "local"); return Error(0, 0, "Option %s= not supported on this machine\n", word); } #else static int option_groups(word, s, isglobal) char *word; char *s; int isglobal; { int i, is_addgroups, *ngroups_p; int iwd; char *wd; GETGROUPS_T *group_p; if (debug) fprintf(stderr, "\toption:%s=%s (%s)\n", word, s, isglobal ? "global" : "local"); if (strcmp(word, "groups") == 0) { is_addgroups = 0; } else if (strcmp(word, "addgroups") != 0) { is_addgroups = 1; } else { return Error(0, 0, "%tInternal error:\n\ \tDon't know what to do when option isn't groups or addgroups!\n"); } if (isglobal) { group_p = globalinfo.groups; ngroups_p = &globalinfo.ngroups; globalinfo.groups_added = is_addgroups; } else { group_p = localinfo.groups; ngroups_p = &localinfo.ngroups; localinfo.groups_added = is_addgroups; } if (*ngroups_p != GROUPS_NOTSET) return Error(0, 0, "%t\n\t\tCan't have multiple addgroups=xxx and/or \ groups=xxx options in one entry\n"); for (*ngroups_p = 0, iwd=0; (wd=optionlist[iwd]); iwd++) { i = findgid(1, wd); if (i == -1) { *ngroups_p = 0; return Error(0, 0, "%t\n\tCan't set gid: no such group as `%s' in group file.\n", wd); } else { *group_p++ = (GETGROUPS_T) i; (*ngroups_p)++; } } blkfree(optionlist); return (wd) ? Error(0, 0, "%t\n\ttoo many supplementary \ groups (max=%d) in option %s=...", word) : 0; } #endif static int option_info(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:info=%s (%s)\n", s, isglobal ? "global" : "local"); localinfo.info = strdup(s); if (!localinfo.info) return Error(0, 0, "Failed to malloc space for info=<%s>\n", s); return 0; } static int option_lang(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:lang=%s (%s)\n", s, isglobal ? "global" : "local"); #ifndef HAVE_LOCALE_H return Error(0, 0, "%t\n\tOption `lang' isn't supported on this host, \ because the locale functions aren't available\n"); #endif if (!setlocale(LC_TIME, s)) return Error(0, 0, "%t\n\tCan't set time locale to `%s'. \ No such locale?\n", s); /* Update the list of day names */ readtime_init(); return 0; } static int option_logfile(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:logfile=%s (%s)\n", s, isglobal ? "global" : "local"); /* Don't do any logging if debugging or just checking syntax! */ if (check_syntax || it_came_from_cmdline) { Error(0, 0, "%t\n\tIgnoring logfile option, due to debug mode\n"); return 0; } strcpy(globalinfo.log.filename, s); globalinfo.log.newfile = 1; return 0; } static int option_loguid(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:loguid=%s (%s)\n", s, isglobal ? "global" : "local"); /* Don't do any logging if debugging or just checking syntax! */ if (check_syntax || it_came_from_cmdline) { Error(0, 0, "%t\n\tIgnoring loguid option, due to debug mode\n"); return 0; } strcpy(globalinfo.log.user, s); globalinfo.log.newuid = 1; return 0; } static int option_mail(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:mail=%s (%s)\n", s, isglobal ? "global" : "local"); /* Don't do any logging if just checking syntax! */ if (check_syntax) return 0; if (isglobal) { if (strlen(s) > sizeof(globalinfo.mailcmd)-1) return Error(0, 0, "%t\n\tRidiculous length of string: `%s'.\n\ \tMaximum permitted length is %d; the entry used %d\n", word, sizeof(globalinfo.mailcmd)-1, strlen(s)); strcpy(globalinfo.mailcmd, s); error_command = globalinfo.mailcmd; globalinfo.mail_success = 0; } else { if (strlen(s) > sizeof(localinfo.mailcmd)-1) return Error(0, 0, "%t\n\tRidiculous length of string: `%s'.\n\ \tMaximum permitted length is %d; the entry used %d\n", word, sizeof(localinfo.mailcmd)-1, strlen(s)); strcpy(localinfo.mailcmd, s); error_command = localinfo.mailcmd; localinfo.mail_success = 0; } return 0; } static int option_mailany(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:mailany=%s (%s)\n", s, isglobal ? "global" : "local"); /* Don't do any logging if just checking syntax! */ if (check_syntax) return 0; if (isglobal) { if (strlen(s) > sizeof(globalinfo.mailcmd)-1) return Error(0, 0, "%t\n\tRidiculous length of string: `%s'.\n\ \tMaximum permitted length is %d; the entry used %d\n", word, sizeof(globalinfo.mailcmd)-1, strlen(s)); strcpy(globalinfo.mailcmd, s); error_command = globalinfo.mailcmd; globalinfo.mail_success = 1; } else { if (strlen(s) > sizeof(localinfo.mailcmd)-1) return Error(0, 0, "%t\n\tRidiculous length of string: `%s'.\n\ \tMaximum permitted length is %d; the entry used %d\n", word, sizeof(localinfo.mailcmd)-1, strlen(s)); strcpy(localinfo.mailcmd, s); error_command = localinfo.mailcmd; localinfo.mail_success = 1; } return 0; } static int option_nargs(word, s, isglobal) char *word; char *s; int isglobal; { int i, m, n; int *nargsp; if (debug) fprintf(stderr, "\toption:nargs=%s (%s)\n", s, isglobal ? "global" : "local"); if (isglobal) { nargsp = globalinfo.usr_args; } else { nargsp = localinfo.usr_args; } i = sscanf(s, "%u-%u", &m, &n); switch(i) { case 1: nargsp[0] = nargsp[1] = m; break; case 2: nargsp[0] = m; nargsp[1] = n; break; default: return Error(0, 0, "%t\n\tnargs must be nargs=nnn or nargs=mmm-nnn.\n", word); } return 0; } static int option_maxlen(word, s, isglobal) char *word; char *s; int isglobal; { int i; long m, n; if (debug) fprintf(stderr, "\toption:maxlen=%s (%s)\n", s, isglobal ? "global" : "local"); i = sscanf(s, "%ld,%ld", &m, &n); switch(i) { case 1: n = m; break; case 2: break; default: return Error(0, 0, "%t\n\tmaxlen must be maxlen=nnn or maxlen=mmm,nnn.\n", word); } if (isglobal) { globalinfo.maxlen1arg = m; globalinfo.maxlenargs = n; } else { localinfo.maxlen1arg = m; localinfo.maxlenargs = n; } return 0; } static int option_nice(word, s, isglobal) char *word; char *s; int isglobal; { int nice_incr; char *p; if (debug) fprintf(stderr, "\toption:nice=%s (%s)\n", s, isglobal ? "global" : "local"); nice_incr = strtol(s, &p, 0); if (p == word) return Error(0, 0, "%t\n\tillegal value in `nice=%s'\n", word); store_nice_incr(nice_incr, isglobal); return 0; } static int option_owner(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:owner=%s (%s)\n", s, isglobal ? "global" : "local"); if (isglobal) stringcopy(globalinfo.owner, s, sizeof(globalinfo.owner)); else stringcopy(localinfo.owner, s, sizeof(localinfo.owner)); return 0; } static int option_password(word, s, isglobal) char *word; char *s; int isglobal; { int required; if (debug) fprintf(stderr, "\toption:password=%s (%s)\n", s, isglobal ? "global" : "local"); if (strcmp(s, "n") == 0) { required = 0; } else { required = 1; if (strcmp(s, "y") != 0) return Error(0, 0, "%t\n\tInvalid option value \ in `%s' -- value must be `y' or `n'\n", word); } if (required && using_user_supertab) { return Error(0, 0, "%t\n\tUser-supplied .supertab files may not do \ password checking -- sorry.\n"); } if (isglobal) { globalinfo.authinfo.required = required; if (required) globalinfo.authinfo.method = SUPER_AUTH_PASSWORD; } else { localinfo.authinfo.required = required; if (required) localinfo.authinfo.method = SUPER_AUTH_PASSWORD; } return 0; } static int option_patterns(word, pat_name, isglobal) char *word; char *pat_name; int isglobal; { char *p; if (debug) fprintf(stderr, "\toption:patterns=%s (%s)\n", pat_name, isglobal ? "global" : "local"); if (strcmp(pat_name, "shell") == 0) { pat_compile = shell_compile; pat_compare = shell_compare; need_re_anchor = 0; } else if (strcmp(pat_name, "regex") == 0) { pat_compile = s_re_comp; pat_compare = s_re_exec; need_re_anchor = 1; #ifdef HAVE_POSIX_REGEX } else if (strncmp(pat_name, "posix", 5) == 0 && (pat_name[5] == '/' || strlen(pat_name) == 5)) { char *buf = strdup(pat_name); if (!buf) { return Error(0, 0, "%t\n\tCan't malloc space for copy of <%s>\n", pat_name); } /* Check the options: we allow: * posix[/extended][/icase] */ p = strtok(buf, "/"); if (strcmp(p, "posix") != 0) { return Error(0, 0, "%t\n\tInternal error: got pat type <%s> when altready checked it's simply `posix'!\n", p); } for (p=strtok(NULL, "/"); p; p=strtok(NULL, "/")) { if (strcmp(p, "extended") == 0) { p_regcomp_flags(REG_EXTENDED); } else if (strcmp(p, "icase") == 0) { p_regcomp_flags(REG_ICASE); } else { return Error(0, 0, "%t\n\tUnknown posix pattern variant `%s'.\n", p); } } free(buf); pat_compile = p_compile; pat_compare = p_compare; need_re_anchor = 1; #endif } else { #ifdef HAVE_POSIX_REGEX return Error(0, 0, "%t\n\tInvalid pattern type `%s'. \ Valid: \"shell\", \"regex\", and \"posix[/extended][/icase]\"\n", pat_name); #else return Error(0, 0, "%t\n\tInvalid pattern type `%s'. \ Valid: \"shell\" and \"regex\"\n", pat_name); #endif } add_variable("PATTERNS", pat_name); return 0; } static int option_print(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:print=%s (%s)\n", s, isglobal ? "global" : "local"); localinfo.print = strdup(s); if (!localinfo.print) return Error(0, 0, "%t\n\tfailed to malloc space for print=<%s>\n", s); return 0; } static int option_relative_path(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:relative_path=%s (%s)\n", s, isglobal ? "global" : "local"); if (strcmp(s, "n") == 0) { globalinfo.relative_path = 0; } else { globalinfo.relative_path = 1; if (strcmp(s, "y") != 0) return Error(0, 0, "%t\n\tInvalid option value \ in `%s' -- value must be `y' or `n'\n", word); } return 0; } static int option_renewtime(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:renewtime=%s (%s)\n", s, isglobal ? "global" : "local"); if (strcmp(s, "y") == 0) { globalinfo.authinfo.renewtime = 1; } else { globalinfo.authinfo.renewtime = 0; if (strcmp(s, "n") != 0) return Error(0, 0, "%t\n\tInvalid option value \ in `%s' -- value must be `y' or `n'\n", word); } return 0; } static int option_rlog_host(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:rlog_host=%s (%s)\n", s, isglobal ? "global" : "local"); #ifndef USE_RSYSLOG return Error(0, 0, "%t\n\tOption rlog_host is not allowed, because \ this copy of super was compiled with rsyslog disabled.\n"); #endif if (*s == '\0') return Error(0, 0, "%t\n\tInvalid option value in `%s' \ -- empty value not allowed.\n", word); error_rlog_host = strdup(s); return 0; } static int option_setenv(word, s, isglobal) char *word; char *s; int isglobal; { char **p; int i; if (debug) fprintf(stderr, "\toption:setenv=%s (%s)\n", s, isglobal ? "global" : "local"); /* s must have form v=xxx, where v is any string not including `='. */ if (*s == '=' || !strchr(s+1, '=')) return Error(0, 0, "%t\n\tbad syntax for setenv%cvar=xxx; you used `setenv%c%s'.\n", OPTION_SEP, OPTION_SEP, s); /* Append this variable: first skip past already-set variables... */ if (isglobal) { for (i=0, p=globalinfo.setenv; p[i] && i < MAXSETENV; i++) ; /* ...then add this definition */ if (i == MAXSETENV) { return Error(0, 0, "%t\n\ttoo many global setenv%cvar=xxx entries; max is %d.\n", OPTION_SEP, MAXSETENV); } else { char *t = strdup(s); if (!t) return Error(0, 0, "Failed to malloc space for setenv using <%s>\n", s); globalinfo.setenv[i++] = t; globalinfo.setenv[i] = NULL; } } else { /* islocal */ for (i=0, p=localinfo.setenv; p[i] && i < MAXSETENV; i++) ; /* ...then add this definition */ if (i == MAXSETENV) { return Error(0, 0, "%t\n\ttoo many local setenv%cvar=xxx entries; max is %d.\n", OPTION_SEP, MAXSETENV); } else { char *t = strdup(s); if (!t) return Error(0, 0, "Failed to malloc space for setenv using <%s>\n", s); localinfo.setenv[i++] = t; localinfo.setenv[i] = NULL; } } return 0; } static int option_syslog(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:syslog=%s (%s)\n", s, isglobal ? "global" : "local"); /* Don't do any logging if just checking syntax! */ if (check_syntax) return 0; if (strcmp(s, "n") == 0) { error_syslog = 0; } else { error_syslog = 1; if (strcmp(s, "y") != 0) return Error(0, 0, "%t\n\tInvalid option value in `%s' \ -- value must be `y' or `n'.\n", word); #ifndef HAVE_SYSLOG return Error(0, 0, "%t\n\tCan't use option syslog -- \ not compiled with HAVE_SYSLOG defined.\n"); #endif } return 0; } static int option_syslog_error(word, s, isglobal) char *word; char *s; int isglobal; { int syslogcode; if (debug) fprintf(stderr, "\toption:syslog_error=%s (%s)\n", s, isglobal ? "global" : "local"); #ifndef HAVE_SYSLOG return Error(0, 0, "%t\n\tCan't use option syslog_error -- \ not compiled with HAVE_SYSLOG defined.\n"); #endif if (read_syslogcode(s, &syslogcode) == -1) return -1; error_priority = syslogcode; if (debug) fprintf(stderr, "\t\t\t=%#x\n", syslogcode); return 0; } static int option_syslog_success(word, s, isglobal) char *word; char *s; int isglobal; { int syslogcode; if (debug) fprintf(stderr, "\toption:syslog_success=%s (%s)\n", s, isglobal ? "global" : "local"); #ifndef HAVE_SYSLOG return Error(0, 0, "%t\n\tCan't use option syslog_success -- \ not compiled with HAVE_SYSLOG defined.\n"); #endif if (read_syslogcode(s, &syslogcode) == -1) return -1; globalinfo.log.syslog_success = syslogcode; if (debug) fprintf(stderr, "\t\t\t=%#x\n", syslogcode); return 0; } static int option_timeout(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:timeout=%s (%s)\n", s, isglobal ? "global" : "local"); if (isglobal) globalinfo.authinfo.timeout = atoi(s); else localinfo.authinfo.timeout = atoi(s); return 0; } static int option_timestampbyhost(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:timestampbyhost=%s (%s)\n", s, isglobal ? "global" : "local"); if (strcmp(s, "n") == 0) { globalinfo.authinfo.perhost = 0; } else if (strcmp(s, "y") == 0) { globalinfo.authinfo.perhost = 1; } else { return Error(0, 0, "%t\n\tInvalid option value in `%s' -- \ value must be `y' or `n'.\n", word); } return 0; } static int option_timestampuid(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:timestampuid=%s (%s)\n", s, isglobal ? "global" : "local"); strcpy(globalinfo.authinfo.ts_user, s); return 0; } static int option_u_g(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:u+g=%s (%s)\n", s, isglobal ? "global" : "local"); stringcopy(localinfo.u_g, s, sizeof(localinfo.u_g)); return 0; } static int option_uid(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:uid=%s (%s)\n", s, isglobal ? "global" : "local"); stringcopy(localinfo.user, s, sizeof(localinfo.user)); return 0; } static int option_umask(word, s, isglobal) char *word; char *s; int isglobal; { char *p; int mask; if (debug) fprintf(stderr, "\toption:umask=%s (%s)\n", s, isglobal ? "global" : "local"); if (strcmp(s, "") == 0) { mask = userinfo.orig_mask; } else if (strcmp(s, "") == 0) { return Error(0, 0, "%t\n\tThe value isn't allowed in the umask=xxx option\n", word); } else { mask = strtol(s, &p, 0); if (mask < 0 || p == word) return Error(0, 0, "%t\n\tillegal value in `%s'\n", word); } store_umask(mask, isglobal); return 0; } static int option_gethostbyname(word, s, isglobal) char *word; char *s; int isglobal; { if (debug) fprintf(stderr, "\toption:gethostbyname=%s (%s)\n", s, isglobal ? "global" : "local"); if (strcmp(s, "n") == 0) { globalinfo.gethostbyname = 0; } else { globalinfo.gethostbyname = 1; if (strcmp(s, "y") != 0) return Error(0, 0, "%t\n\tInvalid option value \ in `%s' -- value must be `y' or `n'\n", word); } return 0; }