1 /******************************************************************************/
2 /*                                                                            */
3 /* Copyright (c) International Business Machines  Corp., 2007, 2008           */
4 /*                                                                            */
5 /* This program is free software;  you can redistribute it and/or modify      */
6 /* it under the terms of the GNU General Public License as published by       */
7 /* the Free Software Foundation; either version 2 of the License, or          */
8 /* (at your option) any later version.                                        */
9 /*                                                                            */
10 /* This program is distributed in the hope that it will be useful,            */
11 /* but WITHOUT ANY WARRANTY;  without even the implied warranty of            */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See                  */
13 /* the GNU General Public License for more details.                           */
14 /*                                                                            */
15 /* You should have received a copy of the GNU General Public License          */
16 /* along with this program;  if not, write to the Free Software               */
17 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA    */
18 /*                                                                            */
19 /******************************************************************************/
20 /*
21  * File: cap_bounds_rw
22  * Author: Serge Hallyn
23  * Purpose: test dropping capabilities from bounding set
24  */
25 
26 #include <errno.h>
27 #include "config.h"
28 #if HAVE_SYS_CAPABILITY_H
29 #include <linux/types.h>
30 #include <sys/capability.h>
31 #endif
32 #include <sys/prctl.h>
33 #include <unistd.h>
34 #include "test.h"
35 
36 #define PROC_CAP_LAST "/proc/sys/kernel/cap_last_cap"
37 
38 char *TCID = "cap_bounds_rw";
39 int TST_TOTAL = 1;
40 static int cap_last_cap;
41 
check_remaining_caps(int lastdropped)42 int check_remaining_caps(int lastdropped)
43 {
44 	int i;
45 	int ret;
46 
47 	for (i = 0; i <= lastdropped; i++) {
48 #if HAVE_DECL_PR_CAPBSET_READ
49 		ret = prctl(PR_CAPBSET_READ, i);
50 #else
51 		errno = ENOSYS;
52 		ret = -1;
53 #endif
54 		if (ret == -1) {
55 			tst_brkm(TBROK,
56 				 NULL,
57 				 "Failed to read bounding set during sanity check\n");
58 		}
59 		if (ret == 1) {
60 			tst_resm(TFAIL,
61 				 "Bit %d should have been dropped but wasn't\n",
62 				 i);
63 			return i;
64 		}
65 	}
66 #ifdef HAVE_LIBCAP
67 	for (; i <= cap_last_cap; i++) {
68 #if HAVE_DECL_PR_CAPBSET_READ
69 		ret = prctl(PR_CAPBSET_READ, i);
70 #else
71 		errno = ENOSYS;
72 		ret = -1;
73 #endif
74 		if (ret == -1) {
75 			tst_brkm(TBROK,
76 				 NULL,
77 				 "Failed to read bounding set during sanity check\n");
78 		}
79 		if (ret == 0) {
80 			tst_resm(TFAIL,
81 				 "Bit %d wasn't yet dropped, but isn't in bounding set\n",
82 				 i);
83 			return -i;
84 		}
85 	}
86 #endif
87 	return 0;
88 }
89 
main(void)90 int main(void)
91 {
92 	int ret = 1;
93 	int i;
94 
95 #ifdef HAVE_LIBCAP
96 	cap_last_cap = CAP_LAST_CAP;
97 	if (access(PROC_CAP_LAST, R_OK) == 0) {
98 		SAFE_FILE_SCANF(NULL, PROC_CAP_LAST, "%d", &cap_last_cap);
99 		if (cap_last_cap > CAP_LAST_CAP)
100 		       cap_last_cap = CAP_LAST_CAP;
101 	}
102 #if HAVE_DECL_PR_CAPBSET_DROP
103 	ret = prctl(PR_CAPBSET_READ, -1);
104 #else
105 	errno = ENOSYS;
106 	ret = -1;
107 #endif
108 	if (ret != -1) {
109 		tst_brkm(TFAIL, NULL,
110 			 "prctl(PR_CAPBSET_DROP, -1) returned %d\n",
111 			 ret);
112 	}
113 	/* Ideally I'd check CAP_LAST_CAP+1, but userspace
114 	 * tends to be far too unreliable to trust CAP_LAST_CAP>
115 	 * We could test using kernel API, but that's what we're
116 	 * testing...  So let's take an insanely high value */
117 #define INSANE 63
118 #define max(x,y) (x > y ? x : y)
119 #if HAVE_DECL_PR_CAPBSET_DROP
120 	ret = prctl(PR_CAPBSET_DROP, max(INSANE, CAP_LAST_CAP + 1));
121 #else
122 	errno = ENOSYS;
123 	ret = -1;
124 #endif
125 	if (ret != -1) {
126 		tst_resm(TFAIL, "prctl(PR_CAPBSET_DROP, %d) returned %d\n",
127 			 max(INSANE, CAP_LAST_CAP + 1), ret);
128 		tst_resm(TINFO, " %d is should not exist\n",
129 			 max(INSANE, CAP_LAST_CAP + 1));
130 		tst_exit();
131 	}
132 	for (i = 0; i <= cap_last_cap; i++) {
133 #if HAVE_DECL_PR_CAPBSET_DROP
134 		ret = prctl(PR_CAPBSET_DROP, i);
135 #else
136 		errno = ENOSYS;
137 		ret = -1;
138 #endif
139 		if (ret != 0) {
140 			tst_resm(TFAIL,
141 				 "prctl(PR_CAPBSET_DROP, %d) returned %d\n", i,
142 				 ret);
143 			if (ret == -1)
144 				tst_resm(TINFO, "errno was %d\n", errno);
145 			tst_exit();
146 		}
147 		ret = check_remaining_caps(i);
148 		if (ret > 0) {
149 			tst_brkm(TFAIL,
150 				 NULL,
151 				 "after dropping bits 0..%d, %d was still in bounding set\n",
152 				 i, ret);
153 		} else if (ret < 0) {
154 			tst_brkm(TFAIL,
155 				 NULL,
156 				 "after dropping bits 0..%d, %d was not in bounding set\n",
157 				 i, -ret);
158 		}
159 	}
160 	tst_resm(TPASS, "PR_CAPBSET_DROP tests passed\n");
161 #else
162 	tst_resm(TCONF, "System doesn't have POSIX capabilities.");
163 #endif
164 	tst_exit();
165 }
166