1/*
2 * captest.c - A program that demonstrates and outputs capabilities
3 * Copyright (c) 2009 Red Hat Inc., Durham, North Carolina.
4 * All Rights Reserved.
5 *
6 * This software may be freely redistributed and/or modified under the
7 * terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; see the file COPYING. If not, write to the
18 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * Authors:
21 *   Steve Grubb <sgrubb@redhat.com>
22 *
23 */
24#include "config.h"
25#include <unistd.h>
26#include <stdio.h>
27#include <fcntl.h>
28#include <stdlib.h>
29#include <string.h>
30#include <errno.h>
31#include <cap-ng.h>
32#include <sys/prctl.h>
33#ifdef HAVE_LINUX_SECUREBITS_H
34#include <linux/securebits.h>
35#endif
36
37/* children can't get caps back */
38#ifndef SECURE_NOROOT
39#define SECURE_NOROOT                   0
40#endif
41#ifndef SECURE_NOROOT_LOCKED
42#define SECURE_NOROOT_LOCKED            1  /* make bit-0 immutable */
43#endif
44/* Setuid apps run by uid 0 don't get caps back */
45#ifndef SECURE_NO_SETUID_FIXUP
46#define SECURE_NO_SETUID_FIXUP          2
47#endif
48#ifndef SECURE_NO_SETUID_FIXUP_LOCKED
49#define SECURE_NO_SETUID_FIXUP_LOCKED   3  /* make bit-2 immutable */
50#endif
51
52static int text = 0, no_child = 0, lock = 0;
53
54static void report(void)
55{
56	int rc, escalated = 0, need_comma = 0;
57	uid_t uid, euid, suid;
58	gid_t gid, egid, sgid;
59
60	// Refresh what we have for capabilities
61	if (capng_get_caps_process()) {
62		printf("Error getting capabilities\n");
63		exit(1);
64	}
65
66	// Check user credentials
67	getresuid(&uid, &euid, &suid);
68	getresgid(&gid, &egid, &sgid);
69	if (no_child) {
70		if ((uid != euid && uid != 0) ||
71					capng_have_capability(CAPNG_EFFECTIVE,
72						 CAP_SETUID)) {
73			printf("Attempting to regain root...");
74			setuid(0);
75			getresuid(&uid, &euid, &suid);
76			if (uid == 0) {
77				printf("SUCCESS - PRIVILEGE ESCALATION POSSIBLE\n");
78				setgid(0);
79				getresgid(&gid, &egid, &sgid);
80				escalated = 1;
81			} else
82				printf("FAILED\n");
83		}
84		printf("Child ");
85	}
86	printf("User  credentials uid:%d euid:%d suid:%d\n", uid, euid, suid);
87	if (no_child)
88		printf("Child ");
89	printf("Group credentials gid:%d egid:%d sgid:%d\n", gid, egid, sgid);
90	if (uid != euid || gid != egid)
91		printf("Note: app has mismatching credentials!!\n");
92
93	// Check capabilities
94	if (text) {
95		if (capng_have_capabilities(CAPNG_SELECT_CAPS) == CAPNG_NONE) {
96			if (no_child)
97				printf("Child capabilities: none\n");
98			else
99				printf("Current capabilities: none\n");
100		} else {
101			if (no_child)
102				printf("Child ");
103			printf("Effective: ");
104			capng_print_caps_text(CAPNG_PRINT_STDOUT,
105					CAPNG_EFFECTIVE);
106			printf("\n");
107			if (no_child)
108				printf("Child ");
109			printf("Permitted: ");
110			capng_print_caps_text(CAPNG_PRINT_STDOUT,
111					CAPNG_PERMITTED);
112			printf("\n");
113			if (no_child)
114				printf("Child ");
115			printf("Inheritable: ");
116			capng_print_caps_text(CAPNG_PRINT_STDOUT,
117					CAPNG_INHERITABLE);
118			printf("\n");
119			if (no_child)
120				printf("Child ");
121			printf("Bounding Set: ");
122			capng_print_caps_text(CAPNG_PRINT_STDOUT,
123					CAPNG_BOUNDING_SET);
124			printf("\n");
125		}
126	} else {
127		if (capng_have_capabilities(CAPNG_SELECT_CAPS) == CAPNG_NONE) {
128			if (no_child)
129				printf("Child capabilities: none\n");
130			else
131				printf("Current capabilities: none\n");
132		} else {
133			if (no_child)
134				printf("Child capabilities:\n");
135			capng_print_caps_numeric(CAPNG_PRINT_STDOUT,
136					CAPNG_SELECT_BOTH);
137		}
138	}
139
140	// Now check securebits flags
141#ifdef PR_SET_SECUREBITS
142	if (no_child)
143		printf("Child ");
144	printf("securebits flags: ");
145	rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NOROOT);
146	if (rc & (1 << SECURE_NOROOT)) {
147		printf("NOROOT");
148		need_comma = 1;
149	}
150	rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NOROOT_LOCKED);
151	if (rc & (1 << SECURE_NOROOT_LOCKED)) {
152		if (need_comma)
153			printf(", ");
154		printf("NOROOT_LOCKED");
155		need_comma = 1;
156	}
157	rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NO_SETUID_FIXUP);
158	if (rc & (1 << SECURE_NO_SETUID_FIXUP)) {
159		if (need_comma)
160			printf(", ");
161		printf("NO_SETUID_FIXUP");
162		need_comma = 1;
163	}
164	rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NO_SETUID_FIXUP_LOCKED);
165	if (rc & (1 << SECURE_NO_SETUID_FIXUP_LOCKED)) {
166		if (need_comma)
167			printf(", ");
168		printf("NO_SETUID_FIXUP_LOCKED");
169		need_comma = 1;
170	}
171	if (need_comma == 0)
172		printf("none");
173	printf("\n");
174#endif
175	// Now do child process checks
176	if (no_child == 0 || escalated) {
177		printf("Attempting direct access to shadow...");
178		if (access("/etc/shadow", R_OK) == 0)
179			printf("SUCCESS\n");
180		else
181			printf("FAILED (%s)\n", strerror(errno));
182	}
183	if (no_child == 0) {
184		printf("Attempting to access shadow by child process...");
185		rc = system("cat /etc/shadow > /dev/null 2>&1");
186		if (rc == 0)
187			printf("SUCCESS\n");
188		else
189			printf("FAILED\n");
190		if (text)
191			system("/usr/bin/captest --no-child --text");
192		else
193			system("/usr/bin/captest --no-child");
194	}
195}
196
197static void usage(void)
198{
199	printf("usage: captest [ --drop-all | --drop-caps | --id ] [ --lock ] [ --text ]\n");
200}
201
202int main(int argc, char *argv[])
203{
204	int which = 0, i;
205
206	for (i = 1; i < argc; i++) {
207		if (strcmp(argv[i], "--text") == 0)
208			text = 1;
209		else if (strcmp(argv[i], "--no-child") == 0)
210			no_child = 1;
211		else if (strcmp(argv[i], "--lock") == 0)
212			lock = 1;
213		else if (strcmp(argv[i], "--drop-all") == 0)
214			which = 1;
215		else if (strcmp(argv[i], "--drop-caps") == 0)
216			which = 2;
217		else if (strcmp(argv[i], "--id") == 0)
218			which = 3;
219		else {
220			usage();
221			return 0;
222		}
223	}
224	switch (which)
225	{
226		case 1:
227			capng_clear(CAPNG_SELECT_BOTH);
228			if (lock)
229				capng_lock();
230			capng_apply(CAPNG_SELECT_BOTH);
231			report();
232			break;
233		case 2:
234			capng_clear(CAPNG_SELECT_CAPS);
235			if (lock)
236				capng_lock();
237			capng_apply(CAPNG_SELECT_CAPS);
238			report();
239			break;
240		case 3: {
241			int rc;
242
243			capng_clear(CAPNG_SELECT_BOTH);
244			capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED,
245					CAP_CHOWN);
246			rc = capng_change_id(99, 99,
247				CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING);
248			if (rc < 0) {
249				printf("Error changing uid: %d\n", rc);
250				capng_print_caps_text(CAPNG_PRINT_STDOUT,
251					CAPNG_EFFECTIVE);
252				printf("\n");
253				exit(1);
254			}
255			printf("Keeping CAP_CHOWN to show capabilities across uid change.\n");
256			report();
257			} break;
258		case 0:
259			if (lock)
260				capng_lock();
261			report();
262			break;
263	}
264	return 0;
265}
266
267