static const char rcsid[] = "$Id: super.c,v 1.592 2004/10/21 21:35:25 will Exp $"; /* The code should compile with either ANSI C or K&R compilers. */ /* * Copyright (c) 1993 by California Institute of Technology. * Written by William Deich. Not derived from licensed software. * You may distribute under the terms of either the GNU General Public * License or the Artistic License, as specified in the README file. */ #include "super.h" #include "version.h" /* * Super allows users to execute other programs, particularly * scripts, as root (or another user/group), without unduly * compromising security. * * Use: * * $0 -h, $0 -V * ...give usage info or print just the version number. * * $0 [-H | -f] [-F superfile] [ -T hh[:mm][/day] ] [-U uid] [-G gid] * ...lists allowed commands. * * $0 [-o file] command [args...] * ...execute command. * * $0 -b * ...lists builtin variables (useful when creating super.tab). * * $0 [-d | -D] [-F superfile] [-T time] [-U uid] [-G gid] [-M mach] \ * [-o file] [--] [commandname [args...]] * ...debug mode. * * $0 -c [superfile] * ...just check syntax of the superfile. * * Options: * -b -- print the "builtin" variables, then exit. * -c [superfile] -- check syntax of superfile, but don't execute anything. * -d -- debug mode: show what would be done, * but don't actually execute a command. * -D -- verbose debug mode: same as -d, plus tell more about variables. * -F superfile -- names the super file to use; for testing only. * No command will actually be executed. * -G gid -- act as if the invoking user was group gid; for testing only. * No command will actually be executed. * -M mach -- act as if the machine (hostname) was mach; for testing only. * No command will actually be executed. * -T hh[:mm][/day] -- act as if the command is executed at the specified * time; for testing only. No command will actually be executed. * -h -- usage: print usage info, then exit. * -H -- a long-winded listing of allowed commands for this user, * then exit. * -f -- (just the facts, ma'm) a version of -H that prints no extra * info, just a list of what you can execute, using the format: * Cmd FullPath [initial args] * Cmd FullPath [initial args] * ... * Useful for scripts that want a list of what you may execute. * -S -- force stdin to be used for reading passwords. * -U uid -- act as if the invoking user was user uid; for testing only. * No command will actually be executed. * -V -- print version information, then exit. * * The super.tab file names each command that super will execute, and * says who can use it. See super.tab.summary for an overview. */ /* Non-STD-C implementations do not have uniform ways of pasting strings * together. So we will compose the actual superfile and super.init * names at runtime. */ char superfile_writable[MAXPATHLEN+100];/* The super.tab file -- put it * into a char array so we can * overwrite it later, if nec. */ char *superfile = superfile_writable; char superfile_init[MAXPATHLEN+100]; /* The super.init file */ /* Global info */ GlobalInfo globalinfo = { "", /* owner (required owner of file) */ NULL, /* chdir_path (char *) */ 0, /* relative_path (bool) */ 0, /* group_slash (bool) */ MAXENVLEN, /* maximum length of environment variable defn */ NULL, /* additional permitted envvars */ 0, /* nice_incr (int) */ 0, /* mask (umask) */ MAXLEN1ARG, /* maximum length of a single argument */ MAXLENARGS, /* maximum length of all arguments, combined */ {-1,-1}, /* min, max number of args (-1 = no limit) */ {NULL,0,0,NULL}, /* argpats */ { /* Global password requirements */ 0, /* bool: is authentication required? */ SUPER_AUTH_PASSWORD, /* authentication method, if required */ 5, /* timeout (min) */ 0, /* renewtime (bool) */ 1, /* perhost (bool) */ "", /* authuser */ "", /* user (user who owns timestampuid file) */ NULL}, /* prompt */ {0,NULL,NULL}, /* userbefore: list of u/g/h pats before per-cmd pats */ {0,NULL,NULL}, /* userafter: list of u/g/h pats after per-cmd pats */ {0,NULL}, /* b_a_text: list of original text for above */ 1, /* user_clear: (bool: clear if new val seen) */ {{0,0,0,0}, /* timebefore: permitted times before per-cmd pats */ NULL}, {{0,0,0,0}, /* timeafter: permitted times after per-cmd pats */ NULL}, 1, /* time_clear: clear if new val seen */ 0, /* use_after: set to !0 when we see <> */ { NULL, /* log: FILE *fp */ "", /* filename */ "", /* user: value of loguid=xxx */ 0, /* uid: UID under which we open logfile */ -1, /* pid: PID of the logger process */ 0, /* newfile: !0 if logfile given but not yet used */ 0, /* newuid: !0 if loguid given but not yet used */ LOG_INFO }, /* syslog() priority for success message */ "", /* mailcmd */ 0, /* mail_success (bool) */ 1, /* use gethostbyname (bool) */ {0}, /* groups[]: gid's of supplementary groups */ GROUPS_NOTSET, /* ngroups: number of supplementary groups */ 0, /* groups_added */ {NULL}, /* first element of the setenv array. We keep this * at end of global struct, so that we don't have * to initialize every element in order to reach other * struct elements. */ }; /* The list of currently open files */ FileList *currfile = NULL; char authInitMsg1[1024] = ""; /* message from auth init, if any */ char authInitMsg2[1024] = ""; /* suppl message from auth init, if any */ int authInitErrno = 0; /* set to !0, if there's an errno to go * with the authInitMsg1. */ /* The struct of what things we've matched */ Conditions matches; /* The localinfo struct is filled in a bit at a time until it completely * describes the caller, the program to invoke, any options * set in the control line, etc. */ UserInfo userinfo; LocalInfo localinfo = { {NULL, -1, 0, "", 0, 0}, /* Initialize ProgMatch empty */ /* Other elements that need * start initializers are done * through explict assignment. */ }; extern char *s_re_comp P__(( char *)); /* regular-expression compiler */ int shell_compare P__(( char * )); /* s_re_comp()-style i/f to wildmat() */ extern int s_re_exec P__(( char * )); /* regular-expression comparison */ char *shell_compile P__(( char * )); /* s_re_exec()-style i/f to wildmat() */ char *prog; /* this program */ int debug=0; /* Set by the debug options flag */ int use_stdin=0; /* Set by the -S flag */ int it_came_from_cmdline=0; /* Set by -F/-T/-U/-G/-M flags */ int check_syntax=0; /* Set by the -c options flag */ int using_user_supertab = 0; /* !0 means using a user's .supertab */ SimpleList Var_Value = {NULL, NULL}; /* Routines used to compile/match user/group/host patterns */ char *(*pat_compile) P__((char *)) = s_re_comp; int (*pat_compare) P__((char *)) = s_re_exec; int need_re_anchor = 1; /* For strqtokS */ unsigned char my_qm[256]; /* our quotemarks */ unsigned char my_cc[256]; /* our comment characters */ /* Core dump restrictions were shamelessly borrowed from Wietse * Venema's logdaemon code, to disable core dumps with cleartext * or shadow passwords. */ #ifdef RLIMIT_CORE struct rlimit old_core_limit; struct rlimit new_core_limit; #endif /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int main(argc, argv) int argc; char **argv; { int status, n_builtin; char *s, *cmd, *path, *user; char **arglist, **envp; extern char *error_prog; int iarg, givehelp, giveversion, verbosity, printvars; char *o_file; /* The argument to -o xxx */ #ifdef RLIMIT_CORE getrlimit(RLIMIT_CORE, &old_core_limit); new_core_limit.rlim_cur = 0; new_core_limit.rlim_max = old_core_limit.rlim_max; setrlimit(RLIMIT_CORE, &new_core_limit); #endif check_stdio(); sprintf(superfile_writable, "%s/%s", SUPERDIR, "super.tab"); sprintf(superfile_init, "%s/%s", SUPERDIR, "super.init"); s = strrchr(argv[0], '/'); prog = (s && *(s+1)) ? s+1 : argv[0]; error_prog = ONETRUENAME; /* same as prog, but used by Error() */ debug = check_syntax = giveversion = givehelp = printvars = 0; verbosity = HELP_BASIC; /* only matters if *givehelp != 0 */ o_file = NULL; error_srcfile = superfile; /* error messages w/ line nums refer to this */ error_line = -1; if (add_variable("$", "$") == -1) /* Built-in variable $$ -> "$" */ return 1; if (init_userinfo() == -1) /* Initialize userinfo struct. */ return 1; init_globalinfo(); /* Initialize globalinfo struct. */ init_localinfo(); /* Initialize localinfo struct. */ add_variable("PATTERNS", "regex"); /* default pattern lookup */ add_builtin_variables(); /* Define variables that describe this system */ if (get_encrypted_pw()!=0) { /* Get caller's encrypted password while we */ /* definitely have privs. This allows */ /* .supertab files to do password checking. */ *userinfo.encr = '\0'; *userinfo.salt = '\0'; if (geteuid() != 0) { sprintf(authInitMsg2, "\tWon't be able to do password-checking. \ Note: this copy of super is not running setuid-root, \ so it can't check passwords on modern Unix systems.\n\n"); } else { sprintf(authInitMsg2, "\tWon't be able to do authentication \ (password-checking, or whatnot).\n\n"); } } /* Decide if we were invoked as "super cmd args...", or * as a link: cmd args... */ if (strcmp(prog, ONETRUENAME) == 0) { /* Invoked as super [options] cmd [arg ...]. */ iarg = do_options(argc, argv, &givehelp, &giveversion, &verbosity, &printvars, &o_file); if (iarg < 0 || (argv[0] == NULL && !(givehelp || giveversion))) { /* User screwed up; give minimal help */ fprintf(stderr, "Type %s -h for usage information.\n", prog); exit(0); } if (o_file) { /* Cmd is last component of the o_file path, e.g. * -o a/b/c means the cmd is "c". */ if ((cmd = strrchr(o_file, '/'))) { /* found last slash in path */ cmd++; if (*cmd == '\0') cmd = NULL; /* shouldn't ever happen */ } else { /* there is no slash */ cmd = o_file; } argv += iarg - 1; /* Skip over the super options */ } else { /* Cmd is next argument on cmd line, if any */ cmd = argv[iarg]; argv += iarg; /* Skip over the super options */ if (!cmd || !*cmd) { /* No cmd was given after options. */ if (!giveversion) /* If user didn't give -V, and didn't */ givehelp = 1; /* ...give cmd, default is givehelp */ cmd = NULL; } } /* Test if we simply print variables, then bail out. */ if (printvars) { printf("Builtin variables:\n"); hprint(print_variable); exit(0); } } else { /* It's been invoked via link to super. Therefore any options * go to the command, not to super. */ s = strrchr(argv[0], '/'); cmd = (s && *(s+1)) ? s+1 : argv[0]; } if (debug) debug_hello(); init_strqtokS(); /* * Check if we need to switch to processing a user's super file */ using_user_supertab = 0; if (o_file) { /* Have `-o file' */ using_user_supertab = 1; user_supertab(o_file, 1, cmd); } else if (cmd && (s=strchr(cmd, ':'))) { /* Have user:command */ using_user_supertab = 1; user = cmd; if (s == user) Error(0, 1, "Commands may not begin with `:'\n"); *s = '\0'; /* null-terminate the `user' part */ cmd = s+1; if (!*cmd) { /* No cmd given after `user:' */ if (!giveversion) /* If user didn't give -V, and didn't */ givehelp = 1; /* ...give cmd, default is givehelp */ cmd = NULL; } user_supertab(user, 0, cmd); } /* Set a variable that a super.tab (or super.init) can use to determine * if we're processing a user's .supertab or not. */ if (add_variable("IS_USERTAB", using_user_supertab ? "yes" : "no") == -1) exit(1); if (check_syntax) Error(0,0, "Checking syntax of superfile `%s'\n", superfile); /* Print version if explicitly requested or if giving help */ if ((givehelp && verbosity != HELP_FACTS) || giveversion) (void) printf("%s version %s patchlevel %s\n", prog, Version, Patchlevel); /* If giving version, and not giving help, and there's no command, stop */ if (giveversion && !givehelp && !cmd) exit(0); /* Check for permission to execute, and change uid/gid as necessary */ if ((path = approve(cmd, givehelp, verbosity)) == NULL) { return 1; } else if (*path == '\0') { return 0; } /* Get the arglist for the command, and null-terminate cmd if not * already done so. Do this before things like get_owner, because * newargs() parses paths that look like "command args" and separates * out the command part. */ arglist = newargs(path, argv, &n_builtin); /* Check argument lengths */ if (check_arglistlen(argv) != 0) return 1; /* Determine ownership of the program */ if (get_owner(path, &localinfo.file_uid, &localinfo.file_gid) != 0) return 1; /* Sanity check */ if (check_syntax) Error(0, 2, "Abort: shouldn't ever get here when check_syntax is set!\n"); /* Check that the file to execute has proper ownership */ if (check_owner() != 0) return 1; /* If nice increment is negative, we have to do it here, while we * are still root (note that the uid= option can be used to run * as non-root, and we can't do negative nice's as non-root). Positive * nice increments are done later, just before exec'ing, so that we * don't have to do the rest of _this_ program at reduced priority. */ if (localinfo.nice_incr < 0) { if (it_came_from_cmdline) { Error(0, 0, "Not applying nice increment = %d because of flag -F/-T/-U/-G/-M.\n", rcl_nice_incr()); } else if (set_nice_incr() == -1) { return 1; } } /* Check authorization requirements. * Just as for negative nice increment, this require root privileges * to read a shadow password file or switch to another uid for writing * the timestamp file. */ if (check_auth(cmd) != 0) return 1; /* Set uid/gid if necessary */ if (using_user_supertab) { /* Already changed to user's uid/gid earlier */ status = 0; } else { status = set_u_g(); } /* Button up for security, and get a modified environment */ envp = buttonup(cmd); if (!envp) return 1; /* Change directory if requested */ if (status != -1) status = set_chdir(); /* Set the umask value */ set_umask(); if (debug) { debug_print(path, arglist, envp, n_builtin); if (status == -1) { fprintf(stderr, "\n\t(This command would not be executed \ due to previously-reported problem)\n"); } else { fprintf(stderr, "\n\t(Your command is ok, but isn't executed in debug mode.)\n"); } return 0; } else if (it_came_from_cmdline) { debug_print(path, arglist, envp, n_builtin); fprintf(stderr, "\n\t(Your command is ok, but isn't executed because of one or more \ -F/-T/-U/-G/-M flags.)\n"); return 0; } if (status == -1) { return 1; } /* Do checks required by a checkvar=a,b,c... list */ if (check_var_value() != 0) return 1; /* Log an informational message at LOG_INFO priority, not at * the usual error priority. */ { #ifdef HAVE_SYSLOG extern int error_priority; int old_pri = error_priority; error_priority = globalinfo.log.syslog_success; #endif logmsg(cmd, arglist); #ifdef HAVE_SYSLOG error_priority = old_pri; #endif } /* Close the logfile writer, if any: we are done logging, and going * to exec the prog. */ close_writer(); /* If nice increment is positive, we do it here, just before * exec'ing. Negative nice increments were done earlier, before * any change-uid's. */ if (localinfo.nice_incr > 0) if (set_nice_incr() == -1) return 1; /* If print option is set, write message before executing command */ if (localinfo.print) puts(localinfo.print); #ifdef RLIMIT_CORE /* Re-enable core dumps. */ setrlimit(RLIMIT_CORE, &old_core_limit); #endif if (execve(path, arglist, envp) == -1) { #ifdef INTERPRETER_HACK if (errno == ENOEXEC) { /* Open the file, check for "#!interpreter [argument]" */ FILE *fp; char *interp, *argument, line1[1024]; if ((fp = fopen(path, "r")) && /* open the file */ fgets(line1, sizeof(line1), fp) && /* read first line */ strchr(line1, '\n') && /* not too long? */ (strncmp(line1, "#!", 2) == 0) && /* begins "#!"? */ (interp = strtok(line1+2, " \t\n"))) { /* has interpreter? */ argument = strtok(NULL, " \t\n"); /* get opt argument */ /* Adjust the arglist -- recall it has room for this */ if (argument) { arglist -= 2; arglist[0] = arglist[2]; arglist[1] = argument; arglist[2] = path; } else { arglist--; arglist[0] = arglist[1]; arglist[1] = path; } (void) execve(interp, arglist, envp); } } #endif /* If here, we failed to exec the prog. Re-open the logfile we * closed above and write a message. */ if (*globalinfo.log.filename != '\0') { int save_errno = errno; opensuperlog(); errno = save_errno; } (void) Error(1,1, "command `%-.500s': Couldn't exec `%s': ", cmd, path); } return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Set options; return index of arg that follows options, or -1 on error. * N.B. The flags are assumed to be initialized BY THE CALLER. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int do_options(argc, argv, givehelp, giveversion, verbosity, printvars, o_file) int argc; char **argv; int *givehelp, *giveversion, *verbosity, *printvars; char **o_file; { int iarg; char *mhost, *s, *t; #ifndef HAVE_LOCALTIME char daynum; #endif int takes_optional_superfile; long l; struct passwd *pw; /* NOTE WELL: THE CALLER IS EXPECTED TO HAVE INITIALIZED THE ARGUMENTS * TO THE DESIRED DEFAULT VALUES. It's not repeated here because * this routine isn't called for super invoked via symlink, and * it's misleading to give the impression that the options are always * going to be init'd here. */ *verbosity = HELP_BASIC; /* only matters if *givehelp != 0 */ for (iarg = 1; iarg < argc && argv[iarg][0] == '-'; iarg++) { if (strcmp(argv[iarg], "--") == 0) { /* End of options */ iarg++; break; } if (checkarg(argv[iarg]) != 0) return -1; for (s = &argv[iarg][1]; *s; s++) { takes_optional_superfile = 0; switch (*s) { case 'c': check_syntax = 1; takes_optional_superfile = 1; break; case 'F': it_came_from_cmdline = 1; takes_optional_superfile = 1; break; case 'G': it_came_from_cmdline = 1; if (*(s+1)) { /* User gave -Ggid */ t = s+1; s += strlen(s) - 1; } else if ((iarg+1 < argc) && argv[iarg+1][0] != '-') { /* User gave -G gid */ iarg++; if (checkarg(argv[iarg]) != 0) return -1; t = argv[iarg]; } else { return Error(0, 0, "No gid specified after option -G\n"); } l = findgid(1, localinfo.group); if (l == -1) return Error(0,0,"Invalid gid specified after option -G\n"); else userinfo.orig_gid = l; userinfo.caller.pw_gid = l; break; case 'M': it_came_from_cmdline = 1; if (*(s+1)) { /* User gave -Mmach */ mhost = s+1; s += strlen(s) - 1; } else if ((iarg+1 < argc) && argv[iarg+1][0] != '-') { /* User gave -M mach */ iarg++; if (checkarg(argv[iarg]) != 0) return -1; mhost = argv[iarg]; } else { return Error(0, 0, "No machine specified after option -M\n"); } if (strlen(mhost) > sizeof(userinfo.hostname)-1) Error(0, 1, "Machine name too long -- max allowed is %d\n", sizeof(userinfo.hostname)-1); strcpy(userinfo.hostname, mhost); if (canonicalize_hostname(userinfo.hostname, sizeof(userinfo.hostname)) == -1) return -1; /* also set lowercase version */ strcpy(userinfo.lc_hostname, userinfo.hostname); strtolower(userinfo.lc_hostname); /* also adjust short form to match */ if ((t = strchr(mhost, '.'))) *t = '\0'; if (add_variable("HOST", mhost) == -1) exit(1); break; case 'T': it_came_from_cmdline = 1; if (*(s+1)) { /* User gave -Ttime */ t = s+1; s += strlen(s) - 1; } else if ((iarg+1 < argc) && argv[iarg+1][0] != '-') { /* User gave -T time */ iarg++; if (checkarg(argv[iarg]) != 0) return -1; t = argv[iarg]; } else { return Error(0, 0, "No time specified after option -T\n"); } if (readtime(t, &userinfo.ourtime.min, &userinfo.ourtime.day) != 0) return -1; break; case 'U': it_came_from_cmdline = 1; if (*(s+1)) { /* User gave -Uuid */ t = s+1; s += strlen(s) - 1; } else if ((iarg+1 < argc) && argv[iarg+1][0] != '-') { /* User gave -U uid */ iarg++; if (checkarg(argv[iarg]) != 0) return -1; t = argv[iarg]; } else { return Error(0, 0, "No uid specified after option -U\n"); } pw = getpwentry(1, t); if (!pw) { return -1; } else { /* Act as if we really were the named user */ userinfo.orig_uid = pw->pw_uid; userinfo.orig_gid = pw->pw_gid; userinfo.caller.pw_uid = pw->pw_uid; userinfo.caller.pw_gid = pw->pw_gid; if (!(userinfo.caller.pw_name = strdup(pw->pw_name))) (void) Error(0, 2, "failed to malloc space for passwd struct field.\n"); if (!(userinfo.caller.pw_passwd = strdup(pw->pw_passwd))) (void) Error(0, 2, "failed to malloc space for passwd struct field.\n"); if (!(userinfo.caller.pw_dir = strdup(pw->pw_dir))) (void) Error(0, 2, "failed to malloc space passwd struct field.\n"); fprintf(stderr, "\n\tActing as if uid=%d (%s), gid=%d\n\n", pw->pw_uid, pw->pw_name, pw->pw_gid); } break; case 'o': if (*(s+1)) { /* User gave `-ofile' */ *o_file = s+1; s += strlen(s) - 1; } else if ((iarg+1 < argc) && argv[iarg+1][0] != '-') { /* User gave `-o file' */ iarg++; if (checkarg(argv[iarg]) != 0) return -1; *o_file = argv[iarg]; } else { return Error(0, 0, "No file specified after option -o\n"); } /* The `-o file' is always the end of options -- everything * else is an argument to the user's command. * Note that we have to return here (or else "goto") else * we won't break out of the outer for-loop. */ iarg++; return iarg; case 'b': *printvars = 1; break; case 'd': debug = 1; break; case 'D': debug = 2; break; case 'S': use_stdin = 1; break; break; case 'V': *giveversion = 1; break; case '?': *givehelp = 1; *verbosity = HELP_USAGE; break; case 'h': *givehelp = 1; *verbosity = HELP_USAGE; break; case 'f': *givehelp = 1; *verbosity = HELP_FACTS; break; case 'H': *givehelp = 1; *verbosity = HELP_FULL; break; default: return Error(0, 0, "Unrecognized option `%c'\n", *s); } /* Check for optional superfile */ if (takes_optional_superfile) { if (*(s+1)) { /* User gave -Xfile (where X is -c/-F) */ superfile = s+1; s += strlen(s) - 1; if (*o_file) { Error(0, 0, "*** Warning: requested file named \ by `-o %s' is overridden by file `%s'", *o_file, superfile); *o_file = NULL; } } else if ((iarg+1 < argc) && argv[iarg+1][0] != '-') { /* User gave -X file */ iarg++; if (checkarg(argv[iarg]) != 0) return -1; superfile = argv[iarg]; if (*o_file) { Error(0, 0, "*** Warning: requested file named \ by `-o %s' is overridden by file `%s'", *o_file, superfile); *o_file = NULL; } } else { /* No file was given. Use the default super file */ } /* Verify that this user has permission to access the file */ if (access(superfile, R_OK) == -1) Error(1, 1, "You can't read `%s': ", superfile); error_srcfile = superfile; if (strcmp(superfile, SUPERFILE) != 0) { /* User has selected a different file. Don't allow * data-driven attacks via a user-supplied superfile: * if our real uid isn't root, reset our effective uid * to the real uid. */ if (getuid() != 0) { setuid(getuid()); fprintf(stderr, "\t** Since you have supplied a super.tab file that isn't the default,\n"); fprintf(stderr, "\t** and your real uid isn't root, we're going to change back to your\n"); fprintf(stderr, "\t** real uid for this test. That protects us against attacks via\n"); fprintf(stderr, "\t** nasty constructions inside user-supplied super.tab files.\n"); fprintf(stderr, "\t** Not that we don't trust you...\n\n"); fprintf(stderr, "\t** Now using: ruid=%d euid=%d **\n\n", getuid(), geteuid()); } } } } } /* Default operation: give help if no args */ if (argc == 1) *givehelp = 1; return iarg; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Print the debug startup lines */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void debug_hello() { char *s = dayname(userinfo.ourtime.day); fprintf(stderr, "\n\tExecuting: %s -%c:\n", prog, (debug < 2) ? 'd' : 'D'); fprintf(stderr, "\tYou are: user=%s gid=%d hostname=%s\n\n", userinfo.caller.pw_name, userinfo.caller.pw_gid, userinfo.hostname); fprintf(stderr, "\tStart time=%d:%02d/%s (hr*60+min=%d daycode=%d)\n", userinfo.ourtime.min/60, userinfo.ourtime.min%60, s, userinfo.ourtime.min, userinfo.ourtime.day); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Print the debug info */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void debug_print(path, arglist, envp, n_builtin) char *path; char **arglist; char **envp; int n_builtin; { char *s, *t, **p, **sp; char *cdpath; int isglobal, j, iarg; ArgRangePat *argpats, *arp; fprintf(stderr, "==============================================================\n"); fprintf(stderr, "\nSuper file is `%s'\n", superfile); fprintf(stderr, "\n\tPermitted times for execution (in reverse input order):\n"); if (globalinfo.timebefore.next != 0 || localinfo.time.next != 0 || globalinfo.timeafter.next != 0) { for (j = 0; j<3; j++) { TimeList *tl; switch (j) { case 0: isglobal=1; tl = globalinfo.timeafter.next; break; case 1: isglobal=0; tl = localinfo.time.next; break; default: isglobal=1; tl = globalinfo.timebefore.next; break; } for ( ; tl ; tl=tl->next) { fprintf(stderr, "\t\t%stime~%d:%02d-%d:%02d/%s (%s)\n", tl->te.invert ? "!" : "", tl->te.begin / 60, tl->te.begin % 60, tl->te.end / 60, tl->te.end % 60, dayname(tl->te.day), isglobal ? "global def" : "per-cmd def"); } } } else { fputs(" (unrestricted)\n", stderr); } fputs("\n", stderr); (void) fprintf(stderr, "\tCommand: <%s>\n", arglist[0]); (void) fprintf(stderr, "\tPath: <%s>\n", path); for (sp=arglist; *sp; sp++) ; (void) fprintf(stderr, "\tArgc: %d\n", (int) (sp - arglist)); (void) fprintf(stderr, "\tMax len, 1 arg: %ld Max len, all args: %ld\n", localinfo.maxlen1arg, localinfo.maxlenargs); for (sp=arglist; *sp; sp++) { iarg = sp - arglist; (void) fprintf(stderr, "\tArgv[%d]: <%s>\n", iarg, *sp); if (iarg > n_builtin) { argpats = (localinfo.argpats.next) ? localinfo.argpats.next : globalinfo.argpats.next; for (arp=ARnext(argpats, iarg-n_builtin); arp; arp = ARnext(arp->next, iarg-n_builtin)) { fprintf(stderr, "\t\tMust match pattern: %s\n", arp->pat); } } } if (localinfo.usr_args[0] < 0) { (void) fprintf(stderr, "\tAny number of user-entered args allowed.\n"); } else if (localinfo.usr_args[0] == localinfo.usr_args[1] && localinfo.usr_args[0] == 0) { (void) fprintf(stderr, "\tNo user-entered args are allowed.\n"); } else if (localinfo.usr_args[0] == localinfo.usr_args[1]) { (void) fprintf(stderr, "\t%d user-entered arg%s required.\n", localinfo.usr_args[0], localinfo.usr_args[0] == 1? " is" : "s are"); } else { (void) fprintf(stderr, "\t%d - %d user-entered args are required.\n", localinfo.usr_args[0], localinfo.usr_args[1]); } (void) fprintf(stderr, "\tCommand executes with nice increment = %d.\n", rcl_nice_incr()); (void) fprintf(stderr, "\tCommand executes with umask set to 0%o.\n", rcl_umask()); cdpath = localinfo.chdir_path ? localinfo.chdir_path : globalinfo.chdir_path; (void) fprintf(stderr, "\tCommand executes with working directory = %s\n", cdpath && *cdpath ? cdpath : ""); (void) fprintf(stderr, "\tMax length of user-supplied env. var's (name+value): "); if (localinfo.maxenvlen < 0) fprintf(stderr, "unrestricted\n"); else fprintf(stderr, "%d chars\n", localinfo.maxenvlen); (void) fprintf(stderr, "\tAdditional permitted user's environment variables:\n"); if (localinfo.env == NULL || localinfo.env[0] == NULL) { fprintf(stderr, "\t\t(none)\n"); } else { for (p=localinfo.env; *p; p++) (void) fprintf(stderr, "\t\t%s\n", *p); } (void) fprintf(stderr, "\tEnvironment variables defined with setenv=var=:\n"); if (localinfo.setenv[0] == NULL && globalinfo.setenv[0] == NULL) { (void) printf("\t\t(none)\n"); } else { for (p=globalinfo.setenv; *p; p++) (void) printf("\t\t%s\n", *p); for (p=localinfo.setenv; *p; p++) (void) printf("\t\t%s\n", *p); } (void) fprintf(stderr, "\tComplete list of environment variables and values:\n"); if (envp[0] == NULL) { (void) printf("\t\t(none)\n"); } else { for (p=envp; *p; p++) (void) printf("\t\t%s\n", *p); } (void) fprintf(stderr, "\tFile descriptors not to be closed:\n\t\t0,1,2"); if (localinfo.fdlist) (void) fprintf(stderr, ",%s", localinfo.fdlist); (void) fprintf(stderr, "\n\n\tID's:\treal effective\n"); (void) fprintf(stderr, "\tuid\t%d\t%d\n", getuid(), geteuid()); (void) fprintf(stderr, "\tgid\t%d\t%d\n", getgid(), getegid()); #ifdef HAVE_GETGROUPS { GETGROUPS_T groups[NGROUPS_MAX]; int ng = Getgroups(NGROUPS_MAX, groups); (void) fprintf(stderr, "\tSupplementary groups list:\n\t\t"); if (ng == 0) { (void) fprintf(stderr, "(none)"); } else { for (j=0; jpw_name; (void) construct_user_superfile(user); if (stat(superfile, &stu) == -1) { Error(1, 1, "Can't stat file `%s': ", superfile); } if (stu.st_dev != sto.st_dev || stu.st_ino != sto.st_ino) { Error(0, 1, "The file `%s' points to a file owned by `%s', \ but it isn't the expected file `%s'\n", file_or_user, pass->pw_name, superfile); } } else { /* Have username */ user = file_or_user; pass = construct_user_superfile(user); } error_srcfile = superfile; if (initgroups(user, pass->pw_gid) == -1) Error(1, 1, "Can't set groups to those of user %s, group %d:\n\t\ Command <%s:%-.500s>; uid=%d, euid=%d: ", user, pass->pw_gid, user, cmd, getuid(), geteuid()); if ((i=setgid(pass->pw_gid)) == -1) { Error(1, 1, "Can't set gid to %d: ", pass->pw_gid); } else if ((j=getgid()) != pass->pw_gid) { Error(1, 1, "setgid(gid=%d) returned %d, but getgid() returned %d!", pass->pw_gid, i, j); } if ((i=setuid(pass->pw_uid)) == -1) { Error(1, 1, "Can't set uid to %d: ", pass->pw_uid); } else if ((j=getuid()) != pass->pw_uid) { Error(0, 1, "setuid(uid=%d) returned %d, but getuid() returned %d!", pass->pw_uid, i, j); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Construct's a user's .supertab file, and place it into the superfile. * Return a pointer to the passwd struct for the user. * Exit if there are any errors. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct passwd * construct_user_superfile(user) char *user; { struct passwd *pass; char *s; superfile[0] = '\0'; if (!(pass = getpwnam(user))) Error(0, 1, "No such user as `%s'\n", user); if (pass->pw_dir[0] == '\0') Error(0, 1, "No home directory for user `%s'?!\n", user); if ((strlen(pass->pw_dir) + strlen(PERUSER_SUPERFILE) + 1) > sizeof(superfile_writable)) Error(0, 1, "User %s's +<%s> pathlen exceeds %d?!\n", user, PERUSER_SUPERFILE, sizeof(superfile_writable)); (void) strcpy(superfile, pass->pw_dir); s = superfile + strlen(superfile) - 1; if (*s != '/') *(++s) = '/'; strcpy(s+1, PERUSER_SUPERFILE); return pass; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Log a super call -- If "args" isn't a null ptr, it's printed inside * parentheses, with whitespace separating the arguments. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void logmsg(cmd, args) char *cmd; char **args; { char *ec, *logbuf, **ap; int e; int loglen = strlen(cmd) + 4; /* Determine buffer length needed to hold all arguments */ if (args) for (ap = args; *ap; ap++) loglen += strlen(*ap) + 1; if (!(logbuf = malloc(loglen))) (void) Error(0, 2, "failed to malloc space for logging command\n"); if (args) { sprintf(logbuf, "%s (", cmd); for (ap = args; *ap; ) { strcat(logbuf, *ap++); strcat(logbuf, " "); } logbuf[loglen-3] = ')'; logbuf[loglen-2] = '\n'; logbuf[loglen-1] = '\0'; } else { sprintf(logbuf, "%s\n", cmd); } /* Log the message using Error(), but * - make sure msg doesn't go to stderr; * - if not mail_success, don't let msg go to error_command, either. */ e = error_stderr; ec = error_command; if (localinfo.mail_success == 0 || (localinfo.mail_success == -1 && globalinfo.mail_success==0)) error_command = NULL; error_stderr = 0; Error(0, 0, "%s", logbuf); error_stderr = e; error_command = ec; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Get the arglist for the command, and null-terminate cmd if nec */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char ** newargs(path_plus, argv, n_builtin) char *path_plus; /* string = "path [options]". Null-terminate and put * options into front of arglist. */ char **argv; /* rest of arguments for arglist (placed after * the options from the path_plus string). */ int *n_builtin; /* returned w/ number of args in path_plus */ { int nuser, nalloc, iarg, nargs, nxtra; char **arglist, **ap; char *s; ArgRangePat *argpats, *arp; /* Count user-entered args. */ for (ap = argv; *ap; ) ap++; /* Check number of user-entered args is ok. */ nargs = ap - argv - 1; if (localinfo.usr_args[0] >= 0) { if (nargs < localinfo.usr_args[0] || nargs > localinfo.usr_args[1]) { if (localinfo.usr_args[0] == localinfo.usr_args[1] && localinfo.usr_args[0] == 0) (void) Error(0, 2, "you may not give any arguments to `%-.500s'\n", argv[0]); else if (localinfo.usr_args[0] == localinfo.usr_args[1]) (void) Error(0, 2, "You must give %d argument%s to `%-.500s'\n", localinfo.usr_args[0], localinfo.usr_args[0] == 1 ? "" : "s", argv[0]); else (void) Error(0, 2, "You must give %d - %d arguments to `%-.500s'\n", localinfo.usr_args[0], localinfo.usr_args[1], argv[0]); } } /* Check that each user-entered argument matches its pattern, if given */ for (iarg = 1; iarg <= nargs; iarg++) { argpats = (localinfo.argpats.next) ? localinfo.argpats.next : globalinfo.argpats.next; for (arp=ARnext(argpats, iarg); arp; arp = ARnext(arp->next, iarg)) { if (arp->pat && match_pattern(0, 1, argv[iarg], arp->pat) != 1) (void) Error(0, 2, "Your argument #%d <%-.500s> must match pattern <%s>\n", iarg, argv[iarg], arp->pat); } } /* Start off with space for user-entered args + 100 args in super.tab. * We'll re-alloc if necessary. */ nuser = (ap - argv) + 3; nalloc = nuser + 100; arglist = (char **) malloc(sizeof(char *) * nalloc); if (!arglist) (void) Error(1, 2, "failed to malloc space for %d ptrs: ", nalloc); /* Leave room for two extra args at front, in case we are handling * the "#! interpreter [opt]" file for OS's that don't support it. */ arglist += 2; /* Copy the extra args from super.tab to the arglist, * re-allocing the arglist as the number of args grows. * First set up arglist[0]: depending on the value of localinfo.argv0, * either copy argv[0] to the arglist, or use the super.tab-supplied value. */ s=strqtokS(path_plus, SEP, QM, "", 1); /* FullPath */ if (localinfo.argv0 && (strcmp(localinfo.argv0, "") == 0)) { /* Use the actual path */ arglist[0] = s; argv++; } else if (localinfo.argv0) { /* Use the path named in the argv0 option in the super.tab file */ arglist[0] = localinfo.argv0; argv++; } else { /* Use the name by which this command was invoked. */ arglist[0] = *argv++; } for(nxtra=0, ap = &arglist[1], s=strqtokS(NULL, SEP, NULL, NULL, 1); s; s = strqtokS(NULL, SEP, NULL, NULL, 1)) { nxtra++; if (nuser + nxtra >= nalloc) { char **newarglist; nalloc *= 2; newarglist = (char **) realloc((void *) arglist, nalloc); if (!newarglist) (void) Error(1, 2, "failed to realloc space for %d ptrs: ", nalloc); ap = newarglist + (ap - arglist); arglist = newarglist; } *ap++ = s; } /* Now add the user-supplied args at the end */ *n_builtin = ap - arglist - 1; while (*argv) *ap++ = *argv++; *ap = NULL; return arglist; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Get a safe environment for execution of the command */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char ** buttonup(cmd) char *cmd; /* name of command being started */ { /* Depending on the ability to close-on-exec, either: * close all descriptors save 0,1,2 and the super.tab-specified fd list; * or mark them close-on-exec. * Resets all signal-handling to SIG_DFL. * Discards all env. variables save for TERM, LINES, COLUMNS, and * any variables listed in the super.tab file. * Don't allow TERM to have any but [-/:+._a-zA-Z0-9]. * Don't allow LINES, COLUMNS to have anything but digits. * To these are added reasonable values for IFS, PATH, USER, HOME. * USER and HOME refer to the uid under which the command is executed; * LOGNAME is set to the same as USER, and SUPERCMD is set to cmd. * ORIG_USER, ORIG_LOGNAME, and ORIG_HOME refer to the user who invoked * super (these are values computed by super, not passed in by the caller). * CALLER and CALLER_HOME are set to the same as ORIG_USER and ORIG_HOME, * respectively. * Returned: * NULL on error; * otherwise, a pointer to the modified environment list. */ int i, j, n, fd, maxfd; char **p, *s; int fd_log; static char *env[200]; static char User[100]; /* USER */ static char Logname[100]; /* LOGNAME (same as USER) */ static char Home[MAXPATHLEN+5]; /* HOME */ static char OrigUser[100]; /* ORIG_USER */ static char Caller[100]; /* CALLER (same as ORIG_USER) */ static char OrigLogname[100]; /* ORIG_LOGNAME */ static char OrigHome[MAXPATHLEN+9]; /* ORIG_HOME */ static char CallerHome[MAXPATHLEN+12]; /* CALLER_HOME (same as ORIG_HOME) */ static char SafePath[1206]; /* SAFE_PATH */ static char Cmd[1205]; /* SUPERCMD */ #ifndef signal /* If signal() isn't a macro, then declare it explicitly -- it's too * much of a mess to figure out whether a given operating system * has declared it or not. (Any __STDC__ system should be declaring * it, but there are several OS's that seem to mess this up.) */ SIGNAL_T (*signal())(); #endif /* don't close logfile yet */ fd_log = globalinfo.log.fp ? fileno(globalinfo.log.fp) : -1; maxfd = MAXFD; #ifdef HAVE_FCNTL_H #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif for (fd=3; fd <= maxfd; fd++) if (localinfo.fd[fd] == 0 && fd != fd_log) (void) fcntl(fd, F_SETFD, FD_CLOEXEC); #else #ifdef HAVE_IOCTL_FIOCLEX for (fd=3; fd <= maxfd; fd++) if (localinfo.fd[fd] == 0 && fd != fd_log) (void) ioctl(fd, FIOCLEX, NULL); #else for (fd=3; fd <= maxfd; fd++) if (localinfo.fd[fd] == 0 && fd != fd_log) (void) close(fd); #endif #endif for (i=0; i sizeof(SafePath)-6) { Error(0, 0, "%t\n\tRidiculously long SAFE_PATH.\n"); return NULL; } /* I've screwed up SAFE_PATH so often (and so have others) that I'm gonna * put this check into the code... */ if (strncmp(SAFE_PATH, "PATH=", 5) == 0) (void) strcpy(SafePath, SAFE_PATH); else (void) sprintf(SafePath, "PATH=%s", SAFE_PATH); if (strlen(cmd) > sizeof(Cmd)-5) { Error(0, 0, "%t\n\tRidiculously long original string.\n"); return NULL; } (void) sprintf(Cmd, "SUPERCMD=%s", cmd); (void) strcpy(Home, "HOME="); (void) getlogdir(s, Home+5); (void) sprintf(OrigHome, "ORIG_HOME=%s", userinfo.caller.pw_dir); (void) sprintf(CallerHome, "CALLER_HOME=%s", userinfo.caller.pw_dir); i = 0; env[i] = Getenv("TERM"); if (env[i] && checkenv("TERM", env[i]+5, "^[-/:+._a-zA-Z0-9]*$") != -1) i++; env[i] = Getenv("LINES"); if (env[i] && checkenv("LINES", env[i]+6, "^[0-9]*$") != -1) i++; env[i] = Getenv("COLUMNS"); if (env[i] && checkenv("COLUMNS", env[i]+8, "^[0-9]*$") != -1) i++; env[i++] = SAFE_IFS; env[i++] = SafePath; env[i++] = User; env[i++] = Logname; env[i++] = Home; env[i++] = Cmd; env[i++] = OrigUser; env[i++] = Caller; env[i++] = OrigLogname; env[i++] = OrigHome; env[i++] = CallerHome; /* Now add the extra environment variables requested in the * super.tab file. Make a linear search each time, so that we * don't define the same name twice. Yes, this will be extraordinarily * inefficient if there are many variables, but in real life, there * aren't too many to do this... */ for (p=localinfo.env; p && *p && i < NELEM(env)-1; p++) { n = strlen(*p); for (j=0; j < i; j++) { if (strncmp(env[j], *p, n) == 0 && env[j][n] == '=') { /* name is already in use, at index j. */ break; } } s = Getenv(*p); if (s) { env[j] = s; if (strlen(env[j])+1 > localinfo.maxenvlen) { Error(0, 0, "%t\n\tDefinition for envvar %s exceeds \ maxenvlen=%d.\n", *p, localinfo.maxenvlen); return NULL; } if (j == i) { i++; } } } for (p = globalinfo.setenv; *p && i < NELEM(env)-1 ; ) { n = strchr(*p, '=') + 1 - *p; for (j=0; j < i; j++) { if (strncmp(env[j], *p, n) == 0) { /* name is already in use, at index j. */ break; } } env[j] = *p++; if (j == i) { i++; } } for (p = localinfo.setenv; *p && i < NELEM(env)-1 ; ) { n = strchr(*p, '=') + 1 - *p; for (j=0; j < i; j++) { if (strncmp(env[j], *p, n) == 0) { /* name is already in use, at index j. */ break; } } env[j] = *p++; if (j == i) { i++; } } if (i >= NELEM(env)-1) { Error(0, 0, "%t\n\tAsked to save too many \ environment variables (max allowed %d).\n", NELEM(env)-1); return NULL; } env[i] = (char *) NULL; return &env[0]; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Check that all arguments are within the permissible limits. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int check_arglistlen(argv) char **argv; { /* Check that all arguments meet the length restrictions. * A length restriction < 0 means unlimited size. * Returns 0 if all ok; else prints error message and returns 1. */ long totlen; long l; char **p; if (localinfo.maxlen1arg < 0 && localinfo.maxlenargs < 0) return 0; for (totlen = 0, p = argv; *p; p++) { l = (long) strlen(*p) + 1; totlen += l; if (localinfo.maxlen1arg >= 0 && l > localinfo.maxlen1arg) return Error(0, 0, "Maximum length of each argument = %d chars\n", localinfo.maxlen1arg); } if (localinfo.maxlenargs >= 0 && totlen > localinfo.maxlenargs) return Error(0, 0, "Maximum total length of arguments = %d chars\n", localinfo.maxlenargs); return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Take various system information (from sysinfo(), uname(), etc) * and turn it into variables. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void add_builtin_variables() { char *s, buf[1000]; if (add_variable("HOSTNAME", userinfo.hostname) == -1) exit(1); /* Add an unqualified version of HOSTNAME */ strcpy(buf, userinfo.hostname); if ((s=strchr(buf, '.'))) { *s = '\0'; } if (add_variable("HOST", buf) == -1) exit(1); if (add_sysinfo_variables() == -1) /* Makes empty def'ns if no sysinfo() */ exit(1); if (add_uname_variables() == -1) /* Makes empty def'ns if no uname() */ exit(1); #ifdef HAVE_GETDOMAINNAME if (getdomainname(buf, sizeof(buf)) != -1) { if (add_variable("NIS_DOMAIN", buf) == -1) exit(1); } #else if (add_variable("NIS_DOMAIN", "") == -1) exit(1); #endif } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Initialize the globalinfo struct. Only call this once; thereafter, use * option_global_clear_settings(). Note that very little work is done in * this routine, because almost all fields are properly initialized in the * structure declaration/initialization statement. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void init_globalinfo() { int i; init_umask(1); for (i=0; i<=MAXSETENV; i++) globalinfo.setenv[i]=NULL; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Initialize the localinfo struct. Only call this once; thereafter, use * option_local_clear_settings(). */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void init_localinfo() { int i; localinfo.info = localinfo.chdir_path = localinfo.die = localinfo.print = NULL; localinfo.env = NULL; for (i=0; i<=MAXSETENV; i++) localinfo.setenv[i]=NULL; localinfo.fdlist = NULL; localinfo.mask = -1; localinfo.maxenvlen = globalinfo.maxenvlen; localinfo.maxlen1arg = globalinfo.maxlen1arg; localinfo.maxlenargs = globalinfo.maxlenargs; localinfo.file_uid = UID_NOTSET; localinfo.file_gid = GID_NOTSET; localinfo.nice_incr = globalinfo.nice_incr; localinfo.checkvar = NULL; /* Don't init to global ngroups, because we need to be able to tell * later on whether local groups=xxx or global groups=xxx was used. */ localinfo.ngroups = GROUPS_NOTSET; localinfo.groups_added = 0; localinfo.argpats.pat = NULL; localinfo.argpats.arg1 = -1; localinfo.argpats.arg2 = -1; localinfo.argpats.next = NULL; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Look up some of the most basic user information. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int init_userinfo() { struct passwd *usrpw; userinfo.ourtime.start = time(NULL); #ifdef HAVE_LOCALTIME { struct tm *tm_p = localtime(&userinfo.ourtime.start); userinfo.ourtime.min = tm_p->tm_hour*60 + tm_p->tm_min; userinfo.ourtime.day = tm_p->tm_wday; } #else userinfo.ourtime.min = (userinfo.ourtime.start/60) % (24*60); userinfo.ourtime.day = daynum(userinfo.ourtime.start); #endif /* * We want the hostname (fully-qualified if possible), as well as a * lower-cased version (to try and deal with mixed case hostnames). */ if (get_canonical_hostname(userinfo.hostname, sizeof(userinfo.hostname)) == -1) return -1; strcpy(userinfo.lc_hostname, userinfo.hostname); strtolower(userinfo.lc_hostname); userinfo.orig_uid = getuid(); userinfo.orig_gid = getgid(); usrpw = getpwuid(userinfo.orig_uid); if (!usrpw) return Error(0, 0, "Couldn't get your password entry."); memcpy(&userinfo.caller, (void *) usrpw, sizeof(struct passwd)); if (add_variable("CALLER", userinfo.caller.pw_name) == -1) return -1; if (add_variable("CALLER_HOME", userinfo.caller.pw_dir) == -1) return -1; /* Since the string fields that we need are overwritten by later * calls to getpwxxx(), make private copies: */ if (!(userinfo.caller.pw_name = strdup(usrpw->pw_name))) (void) Error(0, 2, "failed to malloc space for passwd struct field.\n"); if (!(userinfo.caller.pw_passwd = strdup(usrpw->pw_passwd))) (void) Error(0, 2, "failed to malloc space for passwd struct field.\n"); if (!(userinfo.caller.pw_dir = strdup(usrpw->pw_dir))) (void) Error(0, 2, "failed to malloc space passwd struct field.\n"); error_user = userinfo.caller.pw_name; userinfo.orig_mask = umask(022); /* Get orig umask, and set to 022 */ (void) umask(userinfo.orig_mask); /* ...so restore curr val */ return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Store the desired umask; set the actual umask; recall the desired umask */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void init_umask(is_global) int is_global; { if (is_global) { globalinfo.mask = userinfo.orig_mask; } else { localinfo.mask = -1; } } void store_umask(mask, is_global) int mask; int is_global; { if (is_global) globalinfo.mask = mask; else localinfo.mask = mask; } void set_umask() { umask( (localinfo.mask >= 0) ? localinfo.mask : globalinfo.mask); } int rcl_umask() { return (localinfo.mask >= 0) ? localinfo.mask : globalinfo.mask; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Change directory, if required */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int set_chdir() { char *path = localinfo.chdir_path ? localinfo.chdir_path : globalinfo.chdir_path; if (!path || !*path) return 0; if (chdir(path) == -1) return Error(1, 0, "Failed to change directory to ``%s'': ", path); return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Make sure that stdin, stdout, stderr are all open; die * if we cannot do so. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void check_stdio() { struct stat stat; if (fstat(0, &stat) == -1 && !freopen("/dev/null", "r", stdin)) { exit(1); } if (fstat(1, &stat) == -1 && !freopen("/dev/null", "w", stdout)) { exit(1); } if (fstat(2, &stat) == -1 && !freopen("/dev/null", "w", stderr)) { exit(1); } #ifdef HAVE_FILENO if (fileno(stdin) != 0) { exit(1); } if (fileno(stdout) != 1) { exit(1); } if (fileno(stderr) != 2) { exit(1); } #endif } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Store/set/recall niceness */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void store_nice_incr(nice_incr, is_global) int nice_incr; int is_global; { if (is_global) globalinfo.nice_incr = nice_incr; else localinfo.nice_incr = nice_incr; } int set_nice_incr() { if (localinfo.nice_incr && nice(localinfo.nice_incr) == -1) return Error(1, 0, "Failed to apply a ``nice'' increment = %d: ", localinfo.nice_incr); return 0; } int rcl_nice_incr() { return localinfo.nice_incr; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Frees all elements in a SimpleList, except the one it's given. * The "next" field of that element is set NULL. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void free_SimpleList(sl) SimpleList *sl; { SimpleList *slp; if (!sl || !sl->next) return; slp = sl->next; sl->next = NULL; for (sl=sl->next ; sl; sl = slp) { slp = sl->next; free(sl->pat); free(sl); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Frees all elements in a Simple2List, except the one it's given. * The "next" field of that element is set NULL. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void free_Simple2List(sl) Simple2List *sl; { Simple2List *slp; if (!sl || !sl->next) return; slp = sl->next; sl->next = NULL; for (sl=sl->next ; sl; sl = slp) { slp = sl->next; free(sl->pat); free(sl); } }