apple / cups

Apple CUPS Sources
https://www.cups.org
Apache License 2.0
1.95k stars 464 forks source link

cups cachedir of /var/tmp/cups means check_permissions() could own an arbitrary file to root #1941

Closed michaelrsweet closed 18 years ago

michaelrsweet commented 18 years ago

Version: 1.2-current CUPS.org User: jlovell

check_permissions() is susceptable to a "time of check–time of use" race condition by using stat() and chown()/chmod().

Hopefully the attached patch fixes it -- but please double check it.

This is radr://4697828

Thanks!

michaelrsweet commented 18 years ago

CUPS.org User: mike

The patch opens directories as regular files which does not work on all of the operating systems we support. Even if "open" works, the mkdir + open strategy still has a race condition...

Also, we generally don't want to abort cupsd (return 0 from cupsdReadConfiguration) if check_permissions fails, since that will prevent cupsd from running as a non-root user, for example.

Finally, check_permissions() was never designed to provide absolute protection of the checked files or directories - we just want to correct common install/config errors so that printing works. Since we don't check before every file/directory operation, any user with sufficient permissions can change things after cupsd.conf is loaded and check_permissions is run...

....

It looks like the issue is that a user might have created a file called /var/tmp/cups, which would prevent cupsd from creating its directory? Would it be better to just put CacheDir in /var/spool/cups/cache on OSX instead?

michaelrsweet commented 18 years ago

CUPS.org User: jlovell

Yes, the main issue is the window of time between stat/mkdir/chown in the public directory /var/tmp. Moving the cache dir to /var/spool/cups should be good enough. Still, having check_permissions() avoid the insecure pattern of file operations seems like a good thing...

FWIW: http://developer.apple.com/documentation/Security/Conceptual/SecureCodingGuide/Articles/RaceConditions.html

Thanks.

michaelrsweet commented 18 years ago

CUPS.org User: mike

Again, the whole point of check_permissions() is not to provide security but to correct simple configuration or installation errors.

Changing from chmod/chown/stat to open/fchmod/fchown/fstat/lstat will not make the scheduler any more secure, and in fact will introduce new problems such as the OS-dependent "open a directory" code and the fact that your changes to use fstat and lstat will prevent otherwise valid use of symlinks.

Even if I remove the symlink prevention code, any changes made by check_permissions() are not protected against later changes made after the check. In order to truly prevent this kind of race condition, we would need to modify the scheduler to keep all files and directories open in exclusive mode, however that would prevent valid access by child processes and would not work with most network file systems.

In short, there is little benefit to changing check_permissions() as you propose.

....

That said, I'm OK with updating things so that a check_permissions() failure (vs. a warning) will prevent the scheduler from running. That would be consistent with our "help but don't hang the user" policy... :)

michaelrsweet commented 18 years ago

CUPS.org User: mike

Fixed in Subversion repository.

The attached patch aborts cupsd if we find any hard config file/directory errors. I've also changed the default CacheDir to /var/spool/cups/cache when compiling on OSX...

michaelrsweet commented 18 years ago

CUPS.org User: jlovell

The changes mostly look good... wouldn't it be better to use lstat in place of stat? If Linux's /var/cache is world writable then a symbolic link of /var/cache/cups would allow any directory on the system to be chown'd to root.

Thanks.

michaelrsweet commented 18 years ago

CUPS.org User: mike

Again, we have to support symlinks - many of our customers use them to relocate some CUPS files to separate filesystems. If we used lstat() then all of those customers' installations would break.

In any case, /var/cache is a FHS-defined system directory and is never shipped world-writable.

michaelrsweet commented 18 years ago

"str1941.patch":

Index: conf.c

--- conf.c (revision 5856) +++ conf.c (working copy) @@ -636,22 +636,24 @@

- SystemGroupIDs[0], 1, 1);

+#if defined(HAVE_LIBSSL) || defined(HAVE_GNUTLS)

@@ -1181,28 +1227,47 @@

michaelrsweet commented 18 years ago

"str1941esp.patch":

Index: conf.c

--- conf.c (revision 5902) +++ conf.c (working copy) @@ -636,22 +636,22 @@

- SystemGroupIDs[0], 1, 1);

- check_permissions(ServerRoot, "passwd.md5", 0600, User, Group, 0, 0);

/*

-static int /* O - 0 on success, -1 on error / +static int / O - 0 on success, -1 on error, 1 on warning _/ checkpermissions(const char *filename, / I - File/directory name / const char *suffix, / I - Additional file/directory name / int mode, / I - Permissions */ @@ -1158,7 +1160,7 @@ dir_created = 1; } else

@@ -1203,7 +1205,7 @@ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to change permissions of \"%s\" - %s", filename, strerror(errno));