[BACK]Return to users.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / users

File: [local] / src / usr.bin / users / users.c (download)

Revision 1.14, Fri Aug 3 16:02:53 2018 UTC (5 years, 10 months ago) by deraadt
Branch: MAIN
CVS Tags: OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, OPENBSD_6_5_BASE, OPENBSD_6_5, OPENBSD_6_4_BASE, OPENBSD_6_4
Changes since 1.13: +3 -1 lines

unveil _PATH_UTMP at startup.  Time for a commentary:

There is a TOCTOU between unveil() and open() which should always be
considered, since a path is being supplied twice to the kernel.  First
unveil()s define which paths remain in scope, then secondly open()s
try to access paths in scope.  The unveil() generates a vnode
reservation against the final path resolution (including symbolic link
collapse).  Before the open() occurs, root could replace the path with
symbolic traversal pointing elsewhere.  Then open() will traverse a
path which fails to discover the reserved vnode, and thus fail with
ENOENT.  The TOCTOU sequence doesn't succeed against the new path, it
*always fails*.  (Unless the symlink resolves to another unveil'd
vnode object, but that is not new behaviour).

So once a process is running with veiled filesystem view, we can
consider such a symlink change action as PERMANENTLY visible to this
process and correctly contained to the scoped view, rather than the
previous behaviour of being TRANSIENT and global in view.  So this is
not a real race, security implications will be narrow, and generally
the old symlink-race case is the less secure.

When we add this unveil+open TOCTOU scenario to a program, we should
consider who can perform such a symlink snap, and whether behaviour
change to the program is more disruptive than the risks prevented
through filesystem hiding.  How does a program behave if a file
disappears due to active interference?  Are users (and scripts) used
to operating in a racey best-effort way, and is the additional
strictness strangling their freedom to run shitty stuff?

A few general rules for base programs can avoid problems in this area:
don't en masse unveil argv[], then process argv[] in a second phase.
Don't unveil args which get placed into TZ, TERM, and some other
environment variables, unless you completely understand what libc is
doing.

/*	$OpenBSD: users.c,v 1.14 2018/08/03 16:02:53 deraadt Exp $	*/
/*	$NetBSD: users.c,v 1.5 1994/12/20 15:58:19 jtc Exp $	*/

/*
 * Copyright (c) 1980, 1987, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * 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. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
 */

#include <sys/types.h>

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utmp.h>

typedef char	namebuf[UT_NAMESIZE];

int scmp(const void *, const void *);

int
main(int argc, char *argv[])
{
	namebuf *names = NULL;
	int ncnt = 0;
	int nmax = 0;
	int cnt;
	struct utmp utmp;
	int ch;

	if (unveil(_PATH_UTMP, "r") == -1)
		err(1, "unveil");
	if (pledge("stdio rpath", NULL) == -1)
		err(1, "pledge");

	while ((ch = getopt(argc, argv, "")) != -1)
		switch(ch) {
		case '?':
		default:
			(void)fprintf(stderr, "usage: users\n");
			exit(1);
		}
	argc -= optind;
	argv += optind;

	if (!freopen(_PATH_UTMP, "r", stdin)) {
		err(1, "can't open %s", _PATH_UTMP);
		/* NOTREACHED */
	}

	while (fread((char *)&utmp, sizeof(utmp), 1, stdin) == 1) {
		if (*utmp.ut_name) {
			if (ncnt >= nmax) {
				size_t newmax = nmax + 32;
				namebuf *newnames;

				newnames = reallocarray(names, newmax,
				    sizeof(*names));

				if (newnames == NULL) {
					err(1, NULL);
					/* NOTREACHED */
				}
				names = newnames;
				nmax = newmax;
			}

			(void)strncpy(names[ncnt], utmp.ut_name, UT_NAMESIZE);
			++ncnt;
		}
	}

	if (ncnt) {
		qsort(names, ncnt, UT_NAMESIZE, scmp);
		(void)printf("%.*s", UT_NAMESIZE, names[0]);
		for (cnt = 1; cnt < ncnt; ++cnt)
			if (strncmp(names[cnt], names[cnt - 1], UT_NAMESIZE))
				(void)printf(" %.*s", UT_NAMESIZE, names[cnt]);
		(void)printf("\n");
	}
	exit(0);
}

int
scmp(const void *p, const void *q)
{
	return(strncmp((char *) p, (char *) q, UT_NAMESIZE));
}