Subject: Re: per-user /tmp
To: Elad Efrat <elad@NetBSD.org>
From: Jason Thorpe <thorpej@shagadelic.org>
List: tech-security
Date: 02/03/2007 20:29:10
On Feb 3, 2007, at 9:23 AM, Elad Efrat wrote:
> hi,
>
> attached are some diffs & instructions on how we can add support for
> per-user /tmp directories. the motivation is of course to avoid /tmp
> races.
I like this in concept, but I think we can polish it up a little.
Relying on magic is convenient, but not necessarily elegant :-)
Specifically, I think it would be good to formalize this in API
somehow, perhaps even engaging with other open source OS projects to
standardize on a "private temp area" API. For example, perhaps
mkstemp(3) and friends could be changed to understand some simple
format strings, such as %T (that would expand to ${TMPDIR} if set, or
some reasonable default like _PATH_TMP if not set).
That said, having the magic would be good for legacy programs...
So, first of all, I don't like littering the top-level directory with
extra stuff. So, let's pick a generic place to put the "real" temp
dirs... like maybe /private/tmp (hey, there's prior art for this, at
least :-)
It would be nice of setusercontext(3) could take care of making sure
the private temp directory exists, rather than making login(1) do it.
setusercontext(3) could also set TMPDIR in the environment -- this
would make any well-behaved program DTRT out of the box.
Anyway, I don't have a lot of time to think about this right now, but
there's some food for thought, anyway... "Discuss!" :-)
>
> 1. enable magic links:
>
> # sysctl -w vfs.generic.magiclinks=1
>
> 2. decide on a directory that should be the per-user /tmp base, and
> set the symlink:
>
> # rmdir /tmp
> # mkdir /rtmp
> # ln -s /rtmp/@uid /tmp
>
> note that /rtmp in this case should be the mount-point if /tmp is
> a tmpfs or something.
>
> 3. each user using /tmp should have a directory under /rtmp named
> after
> the uid, set 0700. for example:
>
> # mkdir /rtmp/1000 -m 0700
> # chown user:user /rtmp/1000
>
> the attached login.diff will help doing that automatically for
> users
> when they login. it will read a string variable 'per-user-tmp' from
> login.conf during login, creating the directory (if it's not
> already
> there).
>
> for example:
>
> # from login.conf:
> default:\
> :per-user-tmp=/rtmp:
>
> will set /rtmp as the per-user /tmp base.
>
> 4. if the above goes in, it will of course be disabled by default.
>
> thoughts?
>
> -e.
> Index: login.c
> ===================================================================
> RCS file: /usr/cvs/src/usr.bin/login/login.c,v
> retrieving revision 1.94
> diff -u -p -r1.94 login.c
> --- login.c 17 Jan 2007 00:21:43 -0000 1.94
> +++ login.c 2 Feb 2007 06:22:14 -0000
> @@ -188,6 +188,7 @@ main(int argc, char *argv[])
> #ifdef LOGIN_CAP
> char *shell = NULL;
> login_cap_t *lc = NULL;
> + char *per_user_tmp;
> #endif
>
> tbuf[0] = '\0';
> @@ -596,6 +597,22 @@ main(int argc, char *argv[])
> environ = envinit;
>
> #ifdef LOGIN_CAP
> + /* Create per-user temporary directories if needed. */
> + per_user_tmp = login_getcapstr(lc, "per-user-tmp", NULL, NULL);
> + if (per_user_tmp != NULL) {
> + char *tmp_dir;
> +
> + /* Ignore errors here. */
> + if (asprintf(&tmp_dir, "%s/%u", per_user_tmp,
> + pwd->pw_uid) != -1) {
> + (void)mkdir(tmp_dir, S_IRWXU);
> + (void)chown(tmp_dir, pwd->pw_uid, pwd->pw_gid);
> + free(tmp_dir);
> + }
> + }
> +#endif /* LOGIN_CAP */
> +
> +#ifdef LOGIN_CAP
> if (nested == NULL && setusercontext(lc, pwd, pwd->pw_uid,
> LOGIN_SETLOGIN) != 0) {
> syslog(LOG_ERR, "setusercontext failed");
> Index: login_pam.c
> ===================================================================
> RCS file: /usr/cvs/src/usr.bin/login/login_pam.c,v
> retrieving revision 1.17
> diff -u -p -r1.17 login_pam.c
> --- login_pam.c 17 Apr 2006 16:29:44 -0000 1.17
> +++ login_pam.c 2 Feb 2007 06:20:21 -0000
> @@ -144,6 +144,7 @@ main(int argc, char *argv[])
> int status;
> char *saved_term;
> char **pamenv;
> + char *per_user_tmp;
>
> tbuf[0] = '\0';
> pwprompt = NULL;
> @@ -643,6 +644,20 @@ skip_auth:
> free(pamenv);
> }
>
> + /* Create per-user temporary directories if needed. */
> + per_user_tmp = login_getcapstr(lc, "per-user-tmp", NULL, NULL);
> + if (per_user_tmp != NULL) {
> + char *tmp_dir;
> +
> + /* Ignore errors here. */
> + if (asprintf(&tmp_dir, "%s/%u", per_user_tmp,
> + pwd->pw_uid) != -1) {
> + (void)mkdir(tmp_dir, S_IRWXU);
> + (void)chown(tmp_dir, pwd->pw_uid, pwd->pw_gid);
> + free(tmp_dir);
> + }
> + }
> +
> /* This drops root privs */
> if (setusercontext(lc, pwd, pwd->pw_uid,
> (LOGIN_SETALL & ~LOGIN_SETLOGIN)) != 0) {
-- thorpej