diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2002-06-08 01:53:44 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2002-06-08 01:53:44 +0000 |
commit | 16c2ffab14daba8ec85369749a7f5da0849d2d55 (patch) | |
tree | 913bd4afb3022d2751aea865e7a9a0c6d046fff1 /usr.sbin/lpr/common_source | |
parent | f1da2a2b8fda92ebc29cd063f882352d44750caf (diff) |
Remove setuid root from lp*. lpr needs to be setuid daemon so the
files it creates are not owned by the user spooling them but the
others (lpc, lpq, lprm) can get away with setgid daemon. lpd runs
as user daemon for most things, only changing its uid to 0 for
things that must be done as root.
For the time being, don't require connections to come from a reserved
port since lpq/lpr/lprm can't acquire that w/o setuid root. In the
near future we will have a mechanism for select non-root processes
to grab reserved ports.
The upshot of this is that spool directories must be writable by
group daemon and the files within the spool dirs must be owned by
daemon.
Diffstat (limited to 'usr.sbin/lpr/common_source')
-rw-r--r-- | usr.sbin/lpr/common_source/common.c | 59 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/common_vars.c | 7 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/displayq.c | 80 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/lp.h | 24 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/rmjob.c | 72 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/startdaemon.c | 14 |
6 files changed, 166 insertions, 90 deletions
diff --git a/usr.sbin/lpr/common_source/common.c b/usr.sbin/lpr/common_source/common.c index b27da16cd8d..1772d30b7c9 100644 --- a/usr.sbin/lpr/common_source/common.c +++ b/usr.sbin/lpr/common_source/common.c @@ -1,4 +1,4 @@ -/* $OpenBSD: common.c,v 1.17 2002/05/20 23:13:50 millert Exp $ */ +/* $OpenBSD: common.c,v 1.18 2002/06/08 01:53:43 millert Exp $ */ /* $NetBSD: common.c,v 1.21 2000/08/09 14:28:50 itojun Exp $ */ /* @@ -43,7 +43,7 @@ #if 0 static const char sccsid[] = "@(#)common.c 8.5 (Berkeley) 4/28/95"; #else -static const char rcsid[] = "$OpenBSD: common.c,v 1.17 2002/05/20 23:13:50 millert Exp $"; +static const char rcsid[] = "$OpenBSD: common.c,v 1.18 2002/06/08 01:53:43 millert Exp $"; #endif #endif /* not lint */ @@ -58,6 +58,7 @@ static const char rcsid[] = "$OpenBSD: common.c,v 1.17 2002/05/20 23:13:50 mille #include <dirent.h> #include <errno.h> +#include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> @@ -163,11 +164,15 @@ retry: for (r = res; r; r = r->ai_next) { trial++; retryport: - seteuid(euid); + PRIV_START; s = rresvport_af(&lport, r->ai_family); - seteuid(uid); - if (s < 0) - return(-1); + PRIV_END; + if (s < 0) { + /* fall back to non-privileged port */ + if (errno != EACCES || + (s = socket(r->ai_family, SOCK_STREAM, 0)) < 0) + return(-1); + } siginterrupt(SIGINT, 1); if (connect(s, r->ai_addr, r->ai_addrlen) < 0) { error = errno; @@ -246,9 +251,9 @@ getq(namelist) struct stat stbuf; DIR *dirp; - seteuid(euid); + PRIV_START; dirp = opendir(SD); - seteuid(uid); + PRIV_END; if (dirp == NULL) return(-1); if (fstat(dirp->dd_fd, &stbuf) < 0) @@ -267,12 +272,12 @@ getq(namelist) while ((d = readdir(dirp)) != NULL) { if (d->d_name[0] != 'c' || d->d_name[1] != 'f') continue; /* daemon control files only */ - seteuid(euid); + PRIV_START; if (stat(d->d_name, &stbuf) < 0) { - seteuid(uid); + PRIV_END; continue; /* Doesn't exist */ } - seteuid(uid); + PRIV_END; q = (struct queue *)malloc(sizeof(time_t)+strlen(d->d_name)+1); if (q == NULL) goto errdone; @@ -340,9 +345,14 @@ checkremote(void) remote = 0; /* assume printer is local on failure */ - if (RM == NULL) + if (RM == NULL || *RM == '\0') return NULL; + /* XXX */ + remote = 1; + return NULL; /* XXX -- for local testing only! */ + /* XXX */ + /* get the local interface addresses */ siginterrupt(SIGINT, 1); if (getifaddrs(&ifap) < 0) { @@ -452,3 +462,28 @@ fatal(const char *msg, ...) (void)putchar('\n'); exit(1); } + +int +safe_open(const char *path, int flags, mode_t mode) +{ + int fd, serrno; + struct stat stbuf; + + if ((fd = open(path, flags|O_NONBLOCK, mode)) < 0 || + fstat(fd, &stbuf) < 0) { + if (fd >= 0) { + serrno = errno; + close(fd); + errno = serrno; + } + return (-1); + } + if (!S_ISREG(stbuf.st_mode)) { + close(fd); + errno = EACCES; + return (-1); + } + if (mode) + (void)fchmod(fd, mode); + return (fd); +} diff --git a/usr.sbin/lpr/common_source/common_vars.c b/usr.sbin/lpr/common_source/common_vars.c index 241bfad642e..2b874166af2 100644 --- a/usr.sbin/lpr/common_source/common_vars.c +++ b/usr.sbin/lpr/common_source/common_vars.c @@ -1,4 +1,4 @@ -/* $OpenBSD: common_vars.c,v 1.1 2002/05/20 23:13:50 millert Exp $ */ +/* $OpenBSD: common_vars.c,v 1.2 2002/06/08 01:53:43 millert Exp $ */ /* $NetBSD: common.c,v 1.15 1999/09/26 10:32:27 mrg Exp $ */ /* @@ -40,7 +40,7 @@ */ #ifndef lint -static const char rcsid[] = "$OpenBSD: common_vars.c,v 1.1 2002/05/20 23:13:50 millert Exp $"; +static const char rcsid[] = "$OpenBSD: common_vars.c,v 1.2 2002/06/08 01:53:43 millert Exp $"; #endif /* not lint */ #include <sys/param.h> @@ -53,5 +53,6 @@ char host[MAXHOSTNAMELEN+1]; /* host machine name */ char *from = host; /* client's machine name */ char *printcapdb[2] = { _PATH_PRINTCAP, 0 }; char *bp; /* pointer into printcap buffer. */ -uid_t uid, euid; /* real and effective uids */ u_int wait_time = 300; /* time out after 5 minutes by default */ +uid_t real_uid, effective_uid; +gid_t real_gid, effective_gid; diff --git a/usr.sbin/lpr/common_source/displayq.c b/usr.sbin/lpr/common_source/displayq.c index 67dea6390de..a58abcb56b8 100644 --- a/usr.sbin/lpr/common_source/displayq.c +++ b/usr.sbin/lpr/common_source/displayq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: displayq.c,v 1.17 2002/05/28 18:16:03 millert Exp $ */ +/* $OpenBSD: displayq.c,v 1.18 2002/06/08 01:53:43 millert Exp $ */ /* $NetBSD: displayq.c,v 1.21 2001/08/30 00:51:50 itojun Exp $ */ /* @@ -38,23 +38,25 @@ #if 0 static const char sccsid[] = "@(#)displayq.c 8.4 (Berkeley) 4/28/95"; #else -static const char rcsid[] = "$OpenBSD: displayq.c,v 1.17 2002/05/28 18:16:03 millert Exp $"; +static const char rcsid[] = "$OpenBSD: displayq.c,v 1.18 2002/06/08 01:53:43 millert Exp $"; #endif #endif /* not lint */ #include <sys/param.h> -#include <sys/stat.h> #include <sys/file.h> - -#include <signal.h> -#include <fcntl.h> #include <sys/ioctl.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> #include <dirent.h> -#include <unistd.h> +#include <fcntl.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> +#include <unistd.h> + #include "lp.h" #include "lp.local.h" #include "pathnames.h" @@ -139,25 +141,24 @@ displayq(int format) * Print out local queue * Find all the control files in the spooling directory */ - seteuid(euid); + PRIV_START; if (chdir(SD) < 0) fatal("cannot chdir to spooling directory"); - seteuid(uid); + PRIV_END; if ((nitems = getq(&queue)) < 0) fatal("cannot examine spooling area\n"); - seteuid(euid); + PRIV_START; ret = stat(LO, &statb); - seteuid(uid); + PRIV_END; if (ret >= 0) { if (statb.st_mode & 0100) { if (remote) printf("%s: ", host); printf("Warning: %s is down: ", printer); - seteuid(euid); - fd = open(ST, O_RDONLY); - seteuid(uid); - if (fd >= 0) { - (void)flock(fd, LOCK_SH); + PRIV_START; + fd = safe_open(ST, O_RDONLY|O_NOFOLLOW, 0); + PRIV_END; + if (fd >= 0 && flock(fd, LOCK_SH) == 0) { while ((i = read(fd, line, sizeof(line))) > 0) (void)fwrite(line, 1, i, stdout); (void)close(fd); /* unlocks as well */ @@ -172,12 +173,14 @@ displayq(int format) } if (nitems) { - seteuid(euid); - fp = fopen(LO, "r"); - seteuid(uid); - if (fp == NULL) + PRIV_START; + fd = safe_open(LO, O_RDONLY|O_NOFOLLOW, 0); + PRIV_END; + if (fd < 0 || (fp = fdopen(fd, "r")) == NULL) { + if (fd >= 0) + close(fd); nodaemon(); - else { + } else { /* get daemon pid */ cp = current; ecp = cp + sizeof(current) - 1; @@ -190,11 +193,11 @@ displayq(int format) if (i <= 0) { ret = -1; } else { - seteuid(euid); + PRIV_START; ret = kill(i, 0); - seteuid(uid); + PRIV_END; } - if (ret < 0) { + if (ret < 0 && errno != EPERM) { nodaemon(); } else { /* read current file name */ @@ -210,11 +213,10 @@ displayq(int format) */ if (remote) printf("%s: ", host); - seteuid(euid); - fd = open(ST, O_RDONLY); - seteuid(uid); - if (fd >= 0) { - (void)flock(fd, LOCK_SH); + PRIV_START; + fd = safe_open(ST, O_RDONLY|O_NOFOLLOW, 0); + PRIV_END; + if (fd >= 0 && flock(fd, LOCK_SH) == 0) { while ((i = read(fd, line, sizeof(line))) > 0) (void)fwrite(line, 1, i, stdout); (void)close(fd); /* unlocks as well */ @@ -331,17 +333,21 @@ header(void) void inform(char *cf) { - int j; - FILE *cfp; + int fd, j; + FILE *cfp = NULL; /* * There's a chance the control file has gone away * in the meantime; if this is the case just keep going */ - seteuid(euid); - if ((cfp = fopen(cf, "r")) == NULL) + PRIV_START; + fd = safe_open(cf, O_RDONLY|O_NOFOLLOW, 0); + PRIV_END; + if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) { + if (fd >= 0) + close(fd); return; - seteuid(uid); + } if (rank < 0) rank = 0; @@ -465,10 +471,10 @@ dump(char *nfile, char *file, int copies) printf("%s", nfile); col += n+fill; } - seteuid(euid); + PRIV_START; if (*file && !stat(file, &lbuf)) totsize += copies * lbuf.st_size; - seteuid(uid); + PRIV_END; } /* diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h index 9a3cedb372a..2734028e37d 100644 --- a/usr.sbin/lpr/common_source/lp.h +++ b/usr.sbin/lpr/common_source/lp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: lp.h,v 1.9 2002/05/20 23:13:50 millert Exp $ */ +/* $OpenBSD: lp.h,v 1.10 2002/06/08 01:53:43 millert Exp $ */ /* $NetBSD: lp.h,v 1.14 2000/04/16 14:43:58 mrg Exp $ */ /* @@ -92,7 +92,8 @@ extern int remote; /* true if sending files to a remote host */ extern char *printcapdb[]; /* printcap database array */ extern u_int wait_time; /* time to wait for remote responses */ -extern uid_t uid, euid; /* real and effective user id's */ +extern uid_t real_uid, effective_uid; +extern gid_t real_gid, effective_gid; extern volatile sig_atomic_t gotintr; @@ -104,6 +105,23 @@ struct queue { char q_name[MAXNAMLEN+1]; /* control file name */ }; +/* + * Macros to raise/lower permissions. + */ +#define PRIV_START do { \ + int save_errno = errno; \ + (void)seteuid(effective_uid); \ + (void)setegid(effective_gid); \ + errno = save_errno; \ +} while (0) + +#define PRIV_END do { \ + int save_errno = errno; \ + (void)setegid(real_gid); \ + (void)seteuid(real_uid); \ + errno = save_errno; \ +} while (0) + #include <sys/cdefs.h> __BEGIN_DECLS @@ -125,7 +143,6 @@ int inlist(char *, char *); int iscf(struct dirent *); int isowner(char *, char *); void ldump(char *, char *, int); -int lockchk(char *); void prank(int); void process(char *); void rmjob(void); @@ -134,4 +151,5 @@ void show(char *, char *, int); int startdaemon(char *); void nodaemon(void); void delay(int); +int safe_open(const char *, int, mode_t); __END_DECLS diff --git a/usr.sbin/lpr/common_source/rmjob.c b/usr.sbin/lpr/common_source/rmjob.c index c3712e246a8..1e7f13dbd56 100644 --- a/usr.sbin/lpr/common_source/rmjob.c +++ b/usr.sbin/lpr/common_source/rmjob.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rmjob.c,v 1.13 2002/05/20 23:13:50 millert Exp $ */ +/* $OpenBSD: rmjob.c,v 1.14 2002/06/08 01:53:43 millert Exp $ */ /* $NetBSD: rmjob.c,v 1.16 2000/04/16 14:43:58 mrg Exp $ */ /* @@ -38,7 +38,7 @@ #if 0 static const char sccsid[] = "@(#)rmjob.c 8.2 (Berkeley) 4/28/95"; #else -static const char rcsid[] = "$OpenBSD: rmjob.c,v 1.13 2002/05/20 23:13:50 millert Exp $"; +static const char rcsid[] = "$OpenBSD: rmjob.c,v 1.14 2002/06/08 01:53:43 millert Exp $"; #endif #endif /* not lint */ @@ -46,6 +46,7 @@ static const char rcsid[] = "$OpenBSD: rmjob.c,v 1.13 2002/05/20 23:13:50 miller #include <signal.h> #include <errno.h> +#include <fcntl.h> #include <dirent.h> #include <unistd.h> #include <stdlib.h> @@ -76,6 +77,7 @@ static char current[NAME_MAX]; /* active control file name */ static void do_unlink(char *); static void alarmer(int); +static int lockchk(char *); void rmjob(void) @@ -122,23 +124,24 @@ rmjob(void) person = root; } - seteuid(euid); + PRIV_START; if (chdir(SD) < 0) fatal("cannot chdir to spool directory"); if ((nitems = scandir(".", &files, iscf, NULL)) < 0) fatal("cannot access spool directory"); - seteuid(uid); + PRIV_END; if (nitems) { /* - * Check for an active printer daemon (in which case we - * kill it if it is reading our file) then remove stuff - * (after which we have to restart the daemon). + * Check for an active printer daemon. If one is running + * and it is reading our file, kill it, then remove stuff. + * Lastly, restart the daemon if it is not (or no longer) + * running. */ if (lockchk(LO) && chk(current)) { - seteuid(euid); + PRIV_START; assasinated = kill(cur_daemon, SIGINT) == 0; - seteuid(uid); + PRIV_END; if (!assasinated) fatal("cannot kill printer daemon"); } @@ -159,23 +162,27 @@ rmjob(void) /* * Process a lock file: collect the pid of the active - * daemon and the file name of the active spool entry. + * daemon and the file name of the active spool entry. * Return boolean indicating existence of a lock file. */ -int +static int lockchk(char *s) { - FILE *fp; - int i, n; - - seteuid(euid); - if ((fp = fopen(s, "r")) == NULL) { + FILE *fp = NULL; + int fd, i, n; + + /* NOTE: lock file is owned by root, not the user. */ + PRIV_START; + fd = safe_open(s, O_RDONLY|O_NOFOLLOW, 0); + PRIV_END; + if (fd < 0 || (fp = fdopen(fd, "r")) == NULL) { + if (fd >= 0) + close(fd); if (errno == EACCES) fatal("can't access lock file"); else return(0); } - seteuid(uid); if (!getline(fp)) { (void)fclose(fp); return(0); /* no daemon present */ @@ -203,14 +210,19 @@ lockchk(char *s) void process(char *file) { - FILE *cfp; + FILE *cfp = NULL; + int fd; if (!chk(file)) return; - seteuid(euid); - if ((cfp = fopen(file, "r")) == NULL) + PRIV_START; + fd = safe_open(file, O_RDONLY|O_NOFOLLOW, 0); + PRIV_END; + if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) { + if (fd >= 0) + close(fd); fatal("cannot open %s", file); - seteuid(uid); + } while (getline(cfp)) { switch (line[0]) { case 'U': /* unlink associated files */ @@ -230,9 +242,9 @@ do_unlink(char *file) if (from != host) printf("%s: ", host); - seteuid(euid); + PRIV_START; ret = unlink(file); - seteuid(uid); + PRIV_END; printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file); } @@ -242,9 +254,9 @@ do_unlink(char *file) int chk(char *file) { - int *r, n; + int *r, n, fd; char **u, *cp; - FILE *cfp; + FILE *cfp = NULL; /* * Check for valid cf file name (mostly checking current). @@ -258,10 +270,14 @@ chk(char *file) /* * get the owner's name from the control file. */ - seteuid(euid); - if ((cfp = fopen(file, "r")) == NULL) + PRIV_START; + fd = safe_open(file, O_RDONLY|O_NOFOLLOW, 0); + PRIV_END; + if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) { + if (fd >= 0) + close(fd); return(0); - seteuid(uid); + } while (getline(cfp)) { if (line[0] == 'P') break; diff --git a/usr.sbin/lpr/common_source/startdaemon.c b/usr.sbin/lpr/common_source/startdaemon.c index 8a25bfdcbd0..cf4e868cf17 100644 --- a/usr.sbin/lpr/common_source/startdaemon.c +++ b/usr.sbin/lpr/common_source/startdaemon.c @@ -1,4 +1,4 @@ -/* $OpenBSD: startdaemon.c,v 1.7 2002/05/20 23:13:50 millert Exp $ */ +/* $OpenBSD: startdaemon.c,v 1.8 2002/06/08 01:53:43 millert Exp $ */ /* $NetBSD: startdaemon.c,v 1.10 1998/07/18 05:04:39 lukem Exp $ */ /* @@ -38,7 +38,7 @@ #if 0 static const char sccsid[] = "@(#)startdaemon.c 8.2 (Berkeley) 4/17/94"; #else -static const char rcsid[] = "$OpenBSD: startdaemon.c,v 1.7 2002/05/20 23:13:50 millert Exp $"; +static const char rcsid[] = "$OpenBSD: startdaemon.c,v 1.8 2002/06/08 01:53:43 millert Exp $"; #endif #endif /* not lint */ @@ -53,13 +53,13 @@ static const char rcsid[] = "$OpenBSD: startdaemon.c,v 1.7 2002/05/20 23:13:50 m #include <unistd.h> #include <string.h> #include <signal.h> + #include "lp.h" #include "pathnames.h" /* * Tell the printer daemon that there are new files in the spool directory. */ - int startdaemon(char *printer) { @@ -79,23 +79,23 @@ startdaemon(char *printer) #ifndef SUN_LEN #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) #endif - seteuid(euid); siginterrupt(SIGINT, 1); + PRIV_START; if (connect(s, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) { if (errno == EINTR && gotintr) { + PRIV_END; siginterrupt(SIGINT, 0); - seteuid(uid); close(s); return(0); } + PRIV_END; siginterrupt(SIGINT, 0); - seteuid(uid); perror("connect"); (void)close(s); return(0); } + PRIV_END; siginterrupt(SIGINT, 0); - seteuid(uid); n = snprintf(buf, sizeof(buf), "\1%s\n", printer); if (n >= sizeof(buf) || n == -1) { close(s); |