From owner-freebsd-bugs Mon Aug 14 01:10:05 1995 Return-Path: bugs-owner Received: (from majordom@localhost) by freefall.FreeBSD.org (8.6.11/8.6.6) id BAA04280 for bugs-outgoing; Mon, 14 Aug 1995 01:10:05 -0700 Received: (from gnats@localhost) by freefall.FreeBSD.org (8.6.11/8.6.6) id BAA04270 ; Mon, 14 Aug 1995 01:10:02 -0700 Resent-Date: Mon, 14 Aug 1995 01:10:02 -0700 Resent-Message-Id: <199508140810.BAA04270@freefall.FreeBSD.org> Resent-From: gnats (GNATS Management) Resent-To: freebsd-bugs Resent-Reply-To: FreeBSD-gnats@freefall.FreeBSD.org, wosch@cs.tu-berlin.de Received: from mail.cs.tu-berlin.de (mail.cs.tu-berlin.de [130.149.17.13]) by freefall.FreeBSD.org (8.6.11/8.6.6) with ESMTP id BAA04246 for ; Mon, 14 Aug 1995 01:09:30 -0700 Received: from localhost.cs.tu-berlin.de ([130.149.1.128]) by mail.cs.tu-berlin.de (8.6.12/8.6.12) with ESMTP id JAA09021; Mon, 14 Aug 1995 09:56:38 +0200 Received: (from wosch@localhost) by localhost (8.6.9/8.6.9) id SAA13412; Sun, 13 Aug 1995 18:17:21 +0200 Message-Id: <199508131617.SAA13412@localhost> Date: Sun, 13 Aug 1995 18:17:21 +0200 From: Wolfram Schneider Reply-To: wosch@cs.tu-berlin.de To: FreeBSD-gnats-submit@freebsd.org Cc: Paul Vixie , wosch@cs.tu-berlin.de X-Send-Pr-Version: 3.2 Subject: bin/683: cron(8) Sender: bugs-owner@freebsd.org Precedence: bulk >Number: 683 >Category: bin >Synopsis: cron(8) >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Mon Aug 14 01:10:01 PDT 1995 >Last-Modified: >Originator: Wolfram Schneider >Organization: >Release: FreeBSD 2.0-ALPHA i386 >Environment: >Description: - cron(8) does not show or explain debug flags - debug flag 'test' cause endless loop - cron start ever a shell. Use exec if there are no meta chars. >How-To-Repeat: >Fix: --- 1.1 1995/08/13 14:32:09 +++ cron.c 1995/08/13 14:32:21 @@ -46,7 +46,16 @@ static void usage() { + char **dflags; + fprintf(stderr, "usage: %s [-x debugflag[,...]]\n", ProgramName); + fprintf(stderr, "\ndebugflags: "); + + for(dflags = DebugFlagNames; *dflags; dflags++) { + fprintf(stderr, "%s ", *dflags); + } + fprintf(stderr, "\n"); + exit(ERROR_EXIT); } @@ -116,7 +125,7 @@ cron_sync(); while (TRUE) { # if DEBUGGING - if (!(DebugFlags & DTEST)) + /* if (!(DebugFlags & DTEST)) */ # endif /*DEBUGGING*/ cron_sleep(); @@ -288,8 +297,10 @@ { int argch; - while (EOF != (argch = getopt(argc, argv, "x:"))) { + while (EOF != (argch = getopt(argc, argv, "h?x:"))) { switch (argch) { + case '?': + case 'h': default: usage(); case 'x': --- 1.1 1995/08/13 10:08:46 +++ do_command.c 1995/08/13 15:23:53 @@ -33,6 +33,22 @@ static void child_process __P((entry *, user *)), do_univ __P((user *)); +void do_exec __P((entry *)); + +#if !defined(__FreeBSD__) && !defined(EXEC_SHELL) +# warning "Only tested for FreeBSD." +# warning "Use #define EXEC_SHELL if you have some" +# warning "trouble with theses changes." +#endif + +# if !defined(EXEC_SHELL) +char **brk_string(char *, int *); +char *emalloc(u_int); +void enomem(); +int exectp(const char *file, char *const argv[], char *envp[]); + +#include +# endif /* EXEC_SHELL */ void do_command(e, u) @@ -215,23 +231,9 @@ /* exec the command. */ - { - char *shell = env_get("SHELL", e->envp); -# if DEBUGGING - if (DebugFlags & DTEST) { - fprintf(stderr, - "debug DTEST is on, not exec'ing command.\n"); - fprintf(stderr, - "\tcmd='%s' shell='%s'\n", e->cmd, shell); - _exit(OK_EXIT); - } -# endif /*DEBUGGING*/ - execle(shell, shell, "-c", e->cmd, (char *)0, e->envp); - fprintf(stderr, "execl: couldn't exec `%s'\n", shell); - perror("execl"); - _exit(ERROR_EXIT); - } + do_exec(e); + break; default: /* parent process */ @@ -499,3 +501,381 @@ (void) universe(U_ATT); #endif } + + +/* exec the command. + */ + +void +do_exec(e) + entry *e; +{ + + char *shell = env_get("SHELL", e->envp); + + + /* use ever shell -c for executing programs */ +# if defined(EXEC_SHELL) + +# if DEBUGGING + if (DebugFlags & DTEST) { + fprintf(stderr, + "debug DTEST is on, not exec'ing command.\n"); + fprintf(stderr, + "\tcmd='%s' shell='%s'\n", e->cmd, shell); + _exit(OK_EXIT); + } +# endif /*DEBUGGING*/ + execle(shell, shell, "-c", e->cmd, (char *)0, e->envp); + fprintf(stderr, "execl: couldn't exec `%s'\n", shell); + perror("execl"); + _exit(ERROR_EXIT); + + + +# else + /* try exec if there are no meta chars, + * otherweise use sh -c for executing programs + * + * code borrowed from 4.4BSD make(1) + */ + + static char meta[256]; + char *cp; /* Pointer to string of shell meta-characters */ + char **av; /* Argument vector for thing to exec */ + int argc; /* Number of arguments in av or 0 if not + * dynamically allocated */ + int i; + char **a; + + for(i = 0; i < 256; i++) + meta[i] = 0; + + for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; + cp++) { + meta[(unsigned char) *cp] = 1; + } + + /* + * The null character serves as a sentinel in the string. + */ + meta[0] = 1; + + /* + * Search for meta characters in the command. If there are no meta + * characters, there's no need to execute a shell to execute the + * command. + */ + for (cp = e->cmd; !meta[(unsigned char)*cp]; cp++) { + continue; + } + + /* + * If *cp isn't the null character, we hit a "meta" character + * or command ist not a absolute path and need to pass the + * command off to the shell. + */ + + if (*cp != '\0') { + +# if DEBUGGING + if (DebugFlags & DTEST) { + fprintf(stderr, + "debug DTEST is on, not exec'ing command.\n"); + fprintf(stderr, "Meta char: `%s' `%s'\n", cp, e->cmd); + _exit(OK_EXIT); + } +# endif /*DEBUGGING*/ + execle(shell, shell, "-c", e->cmd, (char *)0, e->envp); + fprintf(stderr, "execle: couldn't exec `%s'\n", shell); + perror("execle"); + _exit(ERROR_EXIT); + } + + else { + /* + * No meta-characters, so no need to exec a shell. Break the command + * into words to form an argument vector we can execute. + * brk_string sticks our name in av[0], so we have to + * skip over it... + */ + + av = brk_string(e->cmd, &argc); + a = av; + +# if DEBUGGING + if (DebugFlags & DTEST) { + fprintf(stderr, + "debug DTEST is on, not exec'ing command.\n"); + + fprintf(stderr, "No meta chars: `%s'\n", e->cmd); + fprintf(stderr, "exect(\"%s\"", *av); + + for(a = av; *a; a++) { + fprintf(stderr, ", \"%s\"", *a); + } + fprintf(stderr, ", e->envp);\nargc: %d\n", argc); + + _exit(OK_EXIT); + } +# endif /*DEBUGGING*/ + + /* exectp + * + * searching for an executable file if the specified file + * name does not contain a slash ``/'' character. + * The search path is the path specified in the environment + * by ``PATH'' variable. If this variable isn't + * specified, the default path ``/bin:/usr/bin:'' is used + */ + + (void)exectp(av[0], av, e->envp); + + fprintf(stderr, "exectp: couldn't exec `%s'\n", av[0]); + perror("exectp"); + _exit(ERROR_EXIT); + + } +} +#endif /* EXEC_SHELL */ + +# if !defined(EXEC_SHELL) +/* following code borrowed from 4.4BSD make(1) */ + +/*- + * brk_string -- + * Fracture a string into an array of words (as delineated by tabs or + * spaces) taking quotation marks into account. Leading tabs/spaces + * are ignored. + * + * returns -- + * Pointer to the array of pointers to the words. To make life easier, + * the first word is always the value of the .MAKE variable. + */ +char ** +brk_string(str, store_argc) + register char *str; + int *store_argc; +{ + static int argmax, curlen; + static char **argv, *buf; + register int argc, ch; + register char inquote, *p, *start, *t; + int len; + + if (!argv) { + argv = (char **)emalloc((argmax = 50) * sizeof(char *)); + } + + /* skip leading space chars. */ + for (; *str == ' ' || *str == '\t'; ++str) + continue; + + /* allocate room for a copy of the string */ + if ((len = strlen(str) + 1) > curlen) + buf = emalloc(curlen = len); + + /* + * copy the string; at the same time, parse backslashes, + * quotes and build the argument list. + */ + argc = 0; + inquote = '\0'; + + for (p = str, start = t = buf;; ++p) { + switch(ch = *p) { + case '"': + case '\'': + if (inquote) { + if (inquote == ch) + inquote = '\0'; + else + break; + } else { + inquote = (char) ch; + start = t; + continue; + } + /* FALLTHROUGH */ + case ' ': + case '\t': + if (inquote) + break; + if (!start) + continue; + /* FALLTHROUGH */ + case '\n': + case '\0': + /* + * end of a token -- make sure there's enough argv + * space and save off a pointer. + */ + *t++ = '\0'; + if (argc == argmax) { + argmax *= 2; /* ramp up fast */ + if (!(argv = (char **)realloc(argv, + argmax * sizeof(char *)))) + enomem(); + } + argv[argc++] = start; + start = (char *)NULL; + if (ch == '\n' || ch == '\0') + goto done; + continue; + case '\\': + switch (ch = *++p) { + case '\0': + case '\n': + /* hmmm; fix it up as best we can */ + ch = '\\'; + --p; + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + } + break; + } + if (!start) + start = t; + *t++ = (char) ch; + } +done: argv[argc] = (char *)NULL; + *store_argc = argc; + return(argv); +} + +/* + * emalloc -- + * malloc, but die on error. + */ +char * +emalloc(len) + u_int len; +{ + char *p; + + if (!(p = malloc(len))) + enomem(); + return(p); +} + +/* + * enomem -- + * die when out of memory. + */ +void +enomem() +{ + (void)fprintf(stderr, "cron: %s.\n", strerror(errno)); + exit(2); +} + + +/* code borrowed from 4.4BSD libc exec(3) */ +int +exectp(name, argv, envp) + const char *name; + char * const *argv; + char **envp; +{ + static int memsize; + static char **memp; + register int cnt, lp, ln; + register char *p; + int eacces, etxtbsy; + char *bp, *cur, *path, buf[MAXPATHLEN]; + + /* If it's an absolute or relative path name, it's easy. */ + if (index(name, '/')) { + bp = (char *)name; + cur = path = NULL; + goto retry; + } + bp = buf; + + /* Get the path we're searching. */ + if (!(path = env_get("PATH", envp))) + path = _PATH_DEFPATH; + cur = path = strdup(path); + + eacces = etxtbsy = 0; + while (p = strsep(&cur, ":")) { + /* + * It's a SHELL path -- double, leading and trailing colons + * mean the current directory. + */ + if (!*p) { + p = "."; + lp = 1; + } else + lp = strlen(p); + ln = strlen(name); + + /* + * If the path is too long complain. This is a possible + * security issue; given a way to make the path too long + * the user may execute the wrong program. + */ + if (lp + ln + 2 > sizeof(buf)) { + (void)write(STDERR_FILENO, "exectp: ", 8); + (void)write(STDERR_FILENO, p, lp); + (void)write(STDERR_FILENO, ": path too long\n", 16); + continue; + } + bcopy(p, buf, lp); + buf[lp] = '/'; + bcopy(name, buf + lp + 1, ln); + buf[lp + ln + 1] = '\0'; + +retry: (void)execve(bp, argv, envp); + switch(errno) { + case EACCES: + eacces = 1; + break; + case ENOENT: + break; + case ENOEXEC: + for (cnt = 0; argv[cnt]; ++cnt); + if ((cnt + 2) * sizeof(char *) > memsize) { + memsize = (cnt + 2) * sizeof(char *); + if ((memp = realloc(memp, memsize)) == NULL) { + memsize = 0; + goto done; + } + } + memp[0] = "sh"; + memp[1] = bp; + bcopy(argv + 1, memp + 2, cnt * sizeof(char *)); + (void)execve(_PATH_BSHELL, memp, envp); + goto done; + case ETXTBSY: + if (etxtbsy < 3) + (void)sleep(++etxtbsy); + goto retry; + default: + goto done; + } + } + if (eacces) + errno = EACCES; + else if (!errno) + errno = ENOENT; +done: if (path) + free(path); + return (-1); +} + +#endif >Audit-Trail: >Unformatted: