Looks good to me, ok nicm

On Mon, Nov 02, 2015 at 02:26:15PM -0700, Todd C. Miller wrote:
> at(1) tries to run as little code as possible with privileges.  This
> creates a false sense of security since if there is an overflow an
> attacker can easily change the effective gid anyway.
> 
> The only place we really need to drop the setgid crontab is when
> reading a file with the -f flag.
> 
>  - todd
> 
> Index: usr.bin/at/at.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/at/at.c,v
> retrieving revision 1.66
> diff -u -p -u -r1.66 at.c
> --- usr.bin/at/at.c   28 Oct 2015 20:17:31 -0000      1.66
> +++ usr.bin/at/at.c   2 Nov 2015 21:16:09 -0000
> @@ -35,7 +35,6 @@
>  
>  #include "cron.h"
>  #include "at.h"
> -#include "privs.h"
>  #include <limits.h>
>  
>  #define ALARMC 10            /* Number of seconds to wait for timeout */
> @@ -56,6 +55,9 @@ char vflag = 0;                     /* show completed but 
>  char force = 0;                      /* suppress errors (atrm) */
>  char interactive = 0;                /* interactive mode (atrm) */
>  static int send_mail = 0;    /* whether we are sending mail */
> +uid_t user_uid;                      /* user's real uid */
> +gid_t user_gid;                      /* user's real gid */
> +gid_t spool_gid;             /* gid for writing to at spool */
>  
>  static void sigc(int);
>  static void alarmc(int);
> @@ -77,11 +79,8 @@ static __dead void
>  panic(const char *a)
>  {
>       (void)fprintf(stderr, "%s: %s\n", ProgramName, a);
> -     if (fcreated) {
> -             PRIV_START;
> +     if (fcreated)
>               unlink(atfile);
> -             PRIV_END;
> -     }
>  
>       exit(EXIT_FAILURE);
>  }
> @@ -93,11 +92,8 @@ static __dead void
>  panic2(const char *a, const char *b)
>  {
>       (void)fprintf(stderr, "%s: %s%s\n", ProgramName, a, b);
> -     if (fcreated) {
> -             PRIV_START;
> +     if (fcreated)
>               unlink(atfile);
> -             PRIV_END;
> -     }
>  
>       exit(EXIT_FAILURE);
>  }
> @@ -110,11 +106,8 @@ perr(const char *a)
>  {
>       if (!force)
>               perror(a);
> -     if (fcreated) {
> -             PRIV_START;
> +     if (fcreated)
>               unlink(atfile);
> -             PRIV_END;
> -     }
>  
>       exit(EXIT_FAILURE);
>  }
> @@ -135,11 +128,8 @@ static void
>  sigc(int signo)
>  {
>       /* If the user presses ^C, remove the spool file and exit. */
> -     if (fcreated) {
> -             PRIV_START;
> +     if (fcreated)
>               (void)unlink(atfile);
> -             PRIV_END;
> -     }
>  
>       _exit(EXIT_FAILURE);
>  }
> @@ -204,8 +194,6 @@ writefile(const char *cwd, time_t runtim
>       act.sa_flags = 0;
>       sigaction(SIGINT, &act, NULL);
>  
> -     PRIV_START;
> -
>       if ((lockdes = open(AT_DIR, O_RDONLY, 0)) < 0)
>               perr("Cannot open jobs dir");
>  
> @@ -241,11 +229,9 @@ writefile(const char *cwd, time_t runtim
>       if ((fd2 = dup(fdes)) < 0)
>               perr("Error in dup() of job file");
>  
> -     if (fchown(fd2, real_uid, real_gid) != 0)
> +     if (fchown(fd2, user_uid, user_gid) != 0)
>               perr("Cannot give away file");
>  
> -     PRIV_END;
> -
>       /*
>        * We've successfully created the file; let's set the flag so it
>        * gets removed in case of an interrupt or error.
> @@ -268,7 +254,7 @@ writefile(const char *cwd, time_t runtim
>  
>       if ((mailname == NULL) || (mailname[0] == '\0') ||
>           (strlen(mailname) > MAX_UNAME) || (getpwnam(mailname) == NULL)) {
> -             pass_entry = getpwuid(real_uid);
> +             pass_entry = getpwuid(user_uid);
>               if (pass_entry != NULL)
>                       mailname = pass_entry->pw_name;
>       }
> @@ -279,7 +265,7 @@ writefile(const char *cwd, time_t runtim
>        * that, /bin/sh.
>        */
>       if ((shell = getenv("SHELL")) == NULL || *shell == '\0') {
> -             pass_entry = getpwuid(real_uid);
> +             pass_entry = getpwuid(user_uid);
>               if (pass_entry != NULL && *pass_entry->pw_shell != '\0')
>                       shell = pass_entry->pw_shell;
>               else
> @@ -287,7 +273,7 @@ writefile(const char *cwd, time_t runtim
>       }
>  
>       (void)fprintf(fp, "#!/bin/sh\n# atrun uid=%lu gid=%lu\n# mail %*s %d\n",
> -         (unsigned long)real_uid, (unsigned long)real_gid,
> +         (unsigned long)user_uid, (unsigned long)user_gid,
>           MAX_UNAME, mailname, send_mail);
>  
>       /* Write out the umask at the time of invocation */
> @@ -394,9 +380,7 @@ writefile(const char *cwd, time_t runtim
>       (void)close(fd2);
>  
>       /* Poke cron so it knows to reload the at spool. */
> -     PRIV_START;
>       poke_daemon(AT_DIR, RELOAD_AT);
> -     PRIV_END;
>  
>       runtime = *localtime(&runtimer);
>       strftime(timestr, TIMESIZE, "%a %b %e %T %Y", &runtime);
> @@ -485,7 +469,7 @@ list_jobs(int argc, char **argv, int cou
>               for (i = 0; i < argc; i++) {
>                       if ((pw = getpwnam(argv[i])) == NULL)
>                               panic2(argv[i], ": invalid user name");
> -                     if (pw->pw_uid != real_uid && real_uid != 0)
> +                     if (pw->pw_uid != user_uid && user_uid != 0)
>                               panic("Only the superuser may display other 
> users' jobs");
>                       uids[i] = pw->pw_uid;
>               }
> @@ -494,16 +478,12 @@ list_jobs(int argc, char **argv, int cou
>  
>       shortformat = strcmp(ProgramName, "at") == 0;
>  
> -     PRIV_START;
> -
>       if (chdir(AT_DIR) != 0)
>               perr2("Cannot change to ", AT_DIR);
>  
>       if ((spool = opendir(".")) == NULL)
>               perr2("Cannot open ", AT_DIR);
>  
> -     PRIV_END;
> -
>       if (fstat(dirfd(spool), &stbuf) != 0)
>               perr2("Cannot stat ", AT_DIR);
>  
> @@ -520,19 +500,15 @@ list_jobs(int argc, char **argv, int cou
>  
>       /* Loop over every file in the directory. */
>       while ((dirent = readdir(spool)) != NULL) {
> -             PRIV_START;
> -
>               if (stat(dirent->d_name, &stbuf) != 0)
>                       perr2("Cannot stat in ", AT_DIR);
>  
> -             PRIV_END;
> -
>               /*
>                * See it's a regular file and has its x bit turned on and
>                * is the user's
>                */
>               if (!S_ISREG(stbuf.st_mode)
> -                 || ((stbuf.st_uid != real_uid) && !(real_uid == 0))
> +                 || ((stbuf.st_uid != user_uid) && !(user_uid == 0))
>                   || !(S_IXUSR & stbuf.st_mode || vflag))
>                       continue;
>  
> @@ -638,16 +614,12 @@ process_jobs(int argc, char **argv, int 
>       int job_matches, jobs_len, uids_len;
>       int error, i, ch, changed;
>  
> -     PRIV_START;
> -
>       if (chdir(AT_DIR) != 0)
>               perr2("Cannot change to ", AT_DIR);
>  
>       if ((spool = opendir(".")) == NULL)
>               perr2("Cannot open ", AT_DIR);
>  
> -     PRIV_END;
> -
>       /* Convert argv into a list of jobs and uids. */
>       jobs = NULL;
>       uids = NULL;
> @@ -663,7 +635,7 @@ process_jobs(int argc, char **argv, int 
>                           *(ep + 2) == '\0' && l > 0 && l < INT_MAX)
>                               jobs[jobs_len++] = argv[i];
>                       else if ((pw = getpwnam(argv[i])) != NULL) {
> -                             if (real_uid != pw->pw_uid && real_uid != 0) {
> +                             if (user_uid != pw->pw_uid && user_uid != 0) {
>                                       fprintf(stderr, "%s: Only the superuser"
>                                           " may %s other users' jobs\n",
>                                           ProgramName, what == ATRM
> @@ -679,13 +651,10 @@ process_jobs(int argc, char **argv, int 
>       /* Loop over every file in the directory */
>       changed = 0;
>       while ((dirent = readdir(spool)) != NULL) {
> -
> -             PRIV_START;
>               if (stat(dirent->d_name, &stbuf) != 0)
>                       perr2("Cannot stat in ", AT_DIR);
> -             PRIV_END;
>  
> -             if (stbuf.st_uid != real_uid && real_uid != 0)
> +             if (stbuf.st_uid != user_uid && user_uid != 0)
>                       continue;
>  
>               if (strtot(dirent->d_name, &ep, &runtimer) == -1)
> @@ -718,8 +687,6 @@ process_jobs(int argc, char **argv, int 
>               if (job_matches) {
>                       switch (what) {
>                       case ATRM:
> -                             PRIV_START;
> -
>                               if (!interactive ||
>                                   (interactive && rmok(runtimer))) {
>                                       if (unlink(dirent->d_name) == 0)
> @@ -731,18 +698,10 @@ process_jobs(int argc, char **argv, int 
>                                                   "%s removed\n",
>                                                   dirent->d_name);
>                               }
> -
> -                             PRIV_END;
> -
>                               break;
>  
>                       case CAT:
> -                             PRIV_START;
> -
>                               fp = fopen(dirent->d_name, "r");
> -
> -                             PRIV_END;
> -
>                               if (!fp)
>                                       perr("Cannot open file");
>  
> @@ -773,12 +732,10 @@ process_jobs(int argc, char **argv, int 
>  
>       /* If we modied the spool, poke cron so it knows to reload. */
>       if (changed) {
> -             PRIV_START;
>               if (chdir(CRONDIR) != 0)
>                       perror(CRONDIR);
>               else
>                       poke_daemon(AT_DIR, RELOAD_AT);
> -             PRIV_END;
>       }
>  
>       return (error);
> @@ -870,22 +827,14 @@ ttime(char *arg)
>  static int
>  check_permission(void)
>  {
> -     int ok;
> -     uid_t uid = geteuid();
>       struct passwd *pw;
>  
> -     if ((pw = getpwuid(uid)) == NULL) {
> +     if ((pw = getpwuid(user_uid)) == NULL) {
>               perror("Cannot access password database");
>               exit(EXIT_FAILURE);
>       }
>  
> -     PRIV_START;
> -
> -     ok = allowed(pw->pw_name, AT_ALLOW, AT_DENY);
> -
> -     PRIV_END;
> -
> -     return (ok);
> +     return (allowed(pw->pw_name, AT_ALLOW, AT_DENY));
>  }
>  
>  static __dead void
> @@ -942,7 +891,9 @@ main(int argc, char **argv)
>       else
>               ProgramName = argv[0];
>  
> -     RELINQUISH_PRIVS;
> +     user_uid = getuid();
> +     user_gid = getgid();
> +     spool_gid = getegid();
>  
>       /* find out what this program is supposed to do */
>       if (strcmp(ProgramName, "atq") == 0) {
> @@ -1046,8 +997,12 @@ main(int argc, char **argv)
>       case AT:
>       case BATCH:
>               if (atinput != NULL) {
> +                     if (setegid(user_gid) != 0)
> +                             perr("setegid(user_gid)");
>                       if (freopen(atinput, "r", stdin) == NULL)
>                               perr("Cannot open input file");
> +                     if (setegid(spool_gid) != 0)
> +                             perr("setegid(spool_gid)");
>               }
>               break;
>       default:
> Index: usr.bin/at/privs.h
> ===================================================================
> RCS file: usr.bin/at/privs.h
> diff -N usr.bin/at/privs.h
> --- usr.bin/at/privs.h        2 Jul 2010 23:40:09 -0000       1.10
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,131 +0,0 @@
> -/*   $OpenBSD: privs.h,v 1.10 2010/07/02 23:40:09 krw Exp $  */
> -
> -/*
> - *  privs.h - header for privileged operations
> - *  Copyright (C) 1993  Thomas Koenig
> - *
> - * Redistribution and use in source and binary forms, with or without
> - * modification, are permitted provided that the following conditions
> - * are met:
> - * 1. Redistributions of source code must retain the above copyright
> - *    notice, this list of conditions and the following disclaimer.
> - * 2. The name of the author(s) may not be used to endorse or promote
> - *    products derived from this software without specific prior written
> - *    permission.
> - *
> - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
> - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
> - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
> - * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
> - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> - */
> -
> -#ifndef _PRIVS_H
> -#define _PRIVS_H
> -
> -#include <unistd.h>
> -
> -/* Relinquish privileges temporarily for a setuid or setgid program
> - * with the option of getting them back later.  This is done by
> - * utilizing POSIX saved user and groups ids (or setreuid amd setregid if
> - * POSIX saved ids are not available).  Call RELINQUISH_PRIVS once
> - * at the beginning of the main program.  This will cause all operations
> - * to be executed with the real userid.  When you need the privileges
> - * of the setuid/setgid invocation, call PRIV_START; when you no longer
> - * need it, call PRIV_END.  Note that it is an error to call PRIV_START
> - * and not PRIV_END within the same function.
> - *
> - * Use RELINQUISH_PRIVS_ROOT(a,b) if your program started out running
> - * as root, and you want to drop back the effective userid to a
> - * and the effective group id to b, with the option to get them back
> - * later.
> - *
> - * Problems: Do not use return between PRIV_START and PRIV_END; this
> - * will cause the program to continue running in an unprivileged
> - * state.
> - *
> - * It is NOT safe to call exec(), system() or popen() with a user-
> - * supplied program (i.e. without carefully checking PATH and any
> - * library load paths) with relinquished privileges; the called program
> - * can acquire them just as easily.  Set both effective and real userid
> - * to the real userid before calling any of them.
> - */
> -
> -#ifndef MAIN_PROGRAM
> -extern
> -#endif
> -uid_t real_uid, effective_uid;
> -
> -#ifndef MAIN_PROGRAM
> -extern
> -#endif
> -gid_t real_gid, effective_gid;
> -
> -#ifdef HAVE_SAVED_UIDS
> -
> -#define RELINQUISH_PRIVS do {                        \
> -      real_uid = getuid();                   \
> -      effective_uid = geteuid();             \
> -      real_gid = getgid();                   \
> -      effective_gid = getegid();             \
> -      setegid(real_gid);                     \
> -      seteuid(real_uid);                     \
> -} while (0)
> -
> -#define RELINQUISH_PRIVS_ROOT(a, b) do {     \
> -     real_uid = (a);                         \
> -     effective_uid = geteuid();              \
> -     real_gid = (b);                         \
> -     effective_gid = getegid();              \
> -     setegid(real_gid);                      \
> -     seteuid(real_uid);                      \
> -} while (0)
> -
> -#define PRIV_START do {                              \
> -     seteuid(effective_uid);                 \
> -     setegid(effective_gid);                 \
> -} while (0)
> -
> -#define PRIV_END do {                                \
> -     setegid(real_gid);                      \
> -     seteuid(real_uid);                      \
> -} while (0)
> -
> -#else /* HAVE_SAVED_UIDS */
> -
> -#define RELINQUISH_PRIVS do {                        \
> -      real_uid = getuid();                   \
> -      effective_uid = geteuid();             \
> -      real_gid = getgid();                   \
> -      effective_gid = getegid();             \
> -      setregid(effective_gid, real_gid);     \
> -      setreuid(effective_uid, real_uid);     \
> -} while (0)
> -
> -#define RELINQUISH_PRIVS_ROOT(a, b) do {     \
> -     real_uid = (a);                         \
> -     effective_uid = geteuid();              \
> -     real_gid = (b);                         \
> -     effective_gid = getegid();              \
> -     setregid(effective_gid, real_gid);      \
> -     setreuid(effective_uid, real_uid);      \
> -} while (0)
> -
> -#define PRIV_START do {                              \
> -     setreuid(real_uid, effective_uid);      \
> -     setregid(real_gid, effective_gid);      \
> -} while (0)
> -
> -#define PRIV_END do {                                \
> -     setregid(effective_gid, real_gid);      \
> -     setreuid(effective_uid, real_uid);      \
> -} while (0)
> -
> -#endif /* HAVE_SAVED_UIDS */
> -
> -#endif /* _PRIVS_H */
> 

Reply via email to