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