1162271Srwatson/*-
2162271Srwatson * Copyright (c) 2006 nCircle Network Security, Inc.
3172106Srwatson * Copyright (c) 2007 Robert N. M. Watson
4162271Srwatson * All rights reserved.
5162271Srwatson *
6162271Srwatson * This software was developed by Robert N. M. Watson for the TrustedBSD
7162271Srwatson * Project under contract to nCircle Network Security, Inc.
8162271Srwatson *
9162271Srwatson * Redistribution and use in source and binary forms, with or without
10162271Srwatson * modification, are permitted provided that the following conditions
11162271Srwatson * are met:
12162271Srwatson * 1. Redistributions of source code must retain the above copyright
13162271Srwatson *    notice, this list of conditions and the following disclaimer.
14162271Srwatson * 2. Redistributions in binary form must reproduce the above copyright
15162271Srwatson *    notice, this list of conditions and the following disclaimer in the
16162271Srwatson *    documentation and/or other materials provided with the distribution.
17162271Srwatson *
18162271Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19162271Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20162271Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21162271Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR, NCIRCLE NETWORK SECURITY,
22162271Srwatson * INC., OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23162271Srwatson * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24162271Srwatson * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25162271Srwatson * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26162271Srwatson * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27162271Srwatson * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28162271Srwatson * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29162271Srwatson *
30162271Srwatson * $FreeBSD$
31162271Srwatson */
32162271Srwatson
33162271Srwatson/*
34162271Srwatson * There are three cases in which the file system will clear the setuid or
35172106Srwatson * setgid bits on a file when running unprivileged:
36162271Srwatson *
37162271Srwatson * - When the file is chown()'d and either of the uid or the gid is changed.
38172106Srwatson *   (currently, only changing the file gid applies, as privilege is required
39172106Srwatson *   to change the uid).
40162271Srwatson *
41172106Srwatson * - The file is written to successfully.
42162271Srwatson *
43162271Srwatson * - An extended attribute of the file is written to successfully.
44162271Srwatson *
45172106Srwatson * In each case, check that the flags are cleared if unprivileged, and that
46172106Srwatson * they aren't cleared if privileged.
47172106Srwatson *
48172106Srwatson * We can't use expect() as we're looking for side-effects rather than
49172106Srwatson * success/failure of the system call.
50162271Srwatson */
51162271Srwatson
52162271Srwatson#include <sys/types.h>
53162271Srwatson#include <sys/extattr.h>
54162271Srwatson#include <sys/stat.h>
55162271Srwatson
56162271Srwatson#include <err.h>
57162271Srwatson#include <fcntl.h>
58162271Srwatson#include <stdlib.h>
59162271Srwatson#include <string.h>
60162271Srwatson#include <unistd.h>
61162271Srwatson
62162271Srwatson#include "main.h"
63162271Srwatson
64172106Srwatsonstatic char fpath[1024];
65172106Srwatsonstatic int fpath_initialized;
66162271Srwatson
67162271Srwatson/*
68172106Srwatson * If running as root, check that SUID is still set; otherwise, check that it
69172106Srwatson * is not.
70162271Srwatson */
71162271Srwatsonstatic void
72172106Srwatsonconfirm_sugid(char *test_case, int asroot, int injail)
73162271Srwatson{
74162271Srwatson	struct stat sb;
75162271Srwatson
76172106Srwatson	if (stat(fpath, &sb) < 0) {
77172106Srwatson		warn("%s stat(%s)", test_case, fpath);
78172106Srwatson		return;
79162271Srwatson	}
80172106Srwatson	if (asroot) {
81172106Srwatson		if (!(sb.st_mode & S_ISUID))
82172106Srwatson			warnx("%s(root, %s): !SUID", test_case, injail ?
83172106Srwatson			    "jail" : "!jail");
84172106Srwatson	} else {
85172106Srwatson		if (sb.st_mode & S_ISUID)
86172106Srwatson			warnx("%s(!root, %s): SUID", test_case, injail ?
87172106Srwatson			    "jail" : "!jail");
88162271Srwatson	}
89162271Srwatson}
90162271Srwatson
91172106Srwatsonint
92172106Srwatsonpriv_vfs_clearsugid_setup(int asroot, int injail, struct test *test)
93162271Srwatson{
94162271Srwatson
95172106Srwatson	setup_file("priv_vfs_clearsugid_setup: fpath", fpath, UID_OWNER,
96172106Srwatson	    GID_OTHER, 0600 | S_ISUID);
97172106Srwatson	fpath_initialized = 1;
98172106Srwatson	return (0);
99162271Srwatson}
100162271Srwatson
101172106Srwatsonvoid
102172106Srwatsonpriv_vfs_clearsugid_chgrp(int asroot, int injail, struct test *test)
103172106Srwatson{
104172106Srwatson
105172106Srwatson	if (chown(fpath, -1, asroot ? GID_WHEEL : GID_OWNER) < 0)
106172106Srwatson		err(-1, "priv_vfs_clearsugid_chgrp(%s, %s): chrgrp",
107172106Srwatson		    asroot ? "root" : "!root", injail ? "jail" : "!jail");
108172106Srwatson	confirm_sugid("priv_vfs_clearsugid_chgrp", asroot, injail);
109172106Srwatson}
110172106Srwatson
111162271Srwatson#define	EA_NAMESPACE	EXTATTR_NAMESPACE_USER
112162271Srwatson#define	EA_NAME		"clearsugid"
113162271Srwatson#define	EA_DATA		"test"
114162271Srwatson#define	EA_SIZE		(strlen(EA_DATA))
115172106Srwatson
116162271Srwatsonvoid
117172106Srwatsonpriv_vfs_clearsugid_extattr(int asroot, int injail, struct test *test)
118162271Srwatson{
119162271Srwatson
120172106Srwatson	if (extattr_set_file(fpath, EA_NAMESPACE, EA_NAME, EA_DATA, EA_SIZE)
121172106Srwatson	    < 0)
122172106Srwatson		err(-1,
123172106Srwatson		    "priv_vfs_clearsugid_extattr(%s, %s): extattr_set_file",
124172106Srwatson		    asroot ? "root" : "!root", injail ? "jail" : "!jail");
125172106Srwatson	confirm_sugid("priv_vfs_clearsugid_extattr", asroot, injail);
126172106Srwatson}
127162271Srwatson
128172106Srwatsonvoid
129172106Srwatsonpriv_vfs_clearsugid_write(int asroot, int injail, struct test *test)
130172106Srwatson{
131172106Srwatson	int fd;
132162271Srwatson
133162271Srwatson	fd = open(fpath, O_RDWR);
134172106Srwatson	if (fd < 0)
135172106Srwatson		err(-1, "priv_vfs_clearsugid_write(%s, %s): open",
136172106Srwatson		    asroot ? "root" : "!root", injail ? "jail" : "!jail");
137172106Srwatson	if (write(fd, EA_DATA, EA_SIZE) < 0)
138172106Srwatson		err(-1, "priv_vfs_clearsugid_write(%s, %s): write",
139172106Srwatson		    asroot ? "root" : "!root", injail ? "jail" : "!jail");
140172106Srwatson	(void)close(fd);
141172106Srwatson	confirm_sugid("priv_vfs_clearsugid_write", asroot, injail);
142172106Srwatson}
143162271Srwatson
144172106Srwatsonvoid
145172106Srwatsonpriv_vfs_clearsugid_cleanup(int asroot, int injail, struct test *test)
146172106Srwatson{
147162271Srwatson
148172106Srwatson	if (fpath_initialized) {
149172106Srwatson		(void)unlink(fpath);
150172106Srwatson		fpath_initialized = 0;
151162271Srwatson	}
152162271Srwatson}
153