xref: /aosp_15_r20/external/ltp/testcases/kernel/security/cap_bound/cap_bounds_rw.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
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");
58 		}
59 		if (ret == 1) {
60 			tst_resm(TFAIL,
61 				 "Bit %d should have been dropped but wasn't",
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");
78 		}
79 		if (ret == 0) {
80 			tst_resm(TFAIL,
81 				 "Bit %d wasn't yet dropped, but isn't in bounding set",
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",
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 #if HAVE_DECL_PR_CAPBSET_DROP
119 	ret = prctl(PR_CAPBSET_DROP, MAX(INSANE, CAP_LAST_CAP + 1));
120 #else
121 	errno = ENOSYS;
122 	ret = -1;
123 #endif
124 	if (ret != -1) {
125 		tst_resm(TFAIL, "prctl(PR_CAPBSET_DROP, %d) returned %d",
126 			 MAX(INSANE, CAP_LAST_CAP + 1), ret);
127 		tst_resm(TINFO, " %d is should not exist",
128 			 MAX(INSANE, CAP_LAST_CAP + 1));
129 		tst_exit();
130 	}
131 	for (i = 0; i <= cap_last_cap; i++) {
132 #if HAVE_DECL_PR_CAPBSET_DROP
133 		ret = prctl(PR_CAPBSET_DROP, i);
134 #else
135 		errno = ENOSYS;
136 		ret = -1;
137 #endif
138 		if (ret != 0) {
139 			tst_resm(TFAIL,
140 				 "prctl(PR_CAPBSET_DROP, %d) returned %d", i,
141 				 ret);
142 			if (ret == -1)
143 				tst_resm(TINFO, "errno was %d", errno);
144 			tst_exit();
145 		}
146 		ret = check_remaining_caps(i);
147 		if (ret > 0) {
148 			tst_brkm(TFAIL,
149 				 NULL,
150 				 "after dropping bits 0..%d, %d was still in bounding set",
151 				 i, ret);
152 		} else if (ret < 0) {
153 			tst_brkm(TFAIL,
154 				 NULL,
155 				 "after dropping bits 0..%d, %d was not in bounding set",
156 				 i, -ret);
157 		}
158 	}
159 	tst_resm(TPASS, "PR_CAPBSET_DROP tests passed");
160 #else
161 	tst_resm(TCONF, "System doesn't have POSIX capabilities.");
162 #endif
163 	tst_exit();
164 }
165