1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU General Public License as published by the
4 * Free Software Foundation; either version 2, or (at your option) any
5 * later version.
6 *
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along
13 * with this program; if not, write to the Free Software Foundation, Inc.,
14 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
15 */
16
17 #include <sys/prctl.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <limits.h>
21 #include <errno.h>
22
23 #include "c.h"
24 #include "caputils.h"
25 #include "pathnames.h"
26 #include "procfs.h"
27
28 static int test_cap(unsigned int cap)
29 {
30 /* prctl returns 0 or 1 for valid caps, -1 otherwise */
31 return prctl(PR_CAPBSET_READ, cap, 0, 0, 0) >= 0;
32 }
33
34 static int cap_last_by_bsearch(int *ret)
35 {
36 /* starting with cap=INT_MAX means we always know
37 * that cap1 is invalid after the first iteration */
38 int cap = INT_MAX;
39 unsigned int cap0 = 0, cap1 = INT_MAX;
40
41 while ((int)cap0 < cap) {
42 if (test_cap(cap))
43 cap0 = cap;
44 else
45 cap1 = cap;
46
47 cap = (cap0 + cap1) / 2U;
48 }
49
50 *ret = cap;
51 return 0;
52 }
53
54 static int cap_last_by_procfs(int *ret)
55 {
56 FILE *f = fopen(_PATH_PROC_CAPLASTCAP, "r");
57 int rc = -EINVAL;
58
59 *ret = 0;
60
61 if (f && fd_is_procfs(fileno(f))) {
62 int cap;
63
64 /* we check if the cap after this one really isn't valid */
65 if (fscanf(f, "%d", &cap) == 1 &&
66 cap < INT_MAX && !test_cap(cap + 1)) {
67
68 *ret = cap;
69 rc = 0;
70 }
71 }
72
73 if (f)
74 fclose(f);
75 return rc;
76 }
77
78 int cap_last_cap(void)
79 {
80 static int cap = -1;
81
82 if (cap != -1)
83 return cap;
84 if (cap_last_by_procfs(&cap) < 0)
85 cap_last_by_bsearch(&cap);
86
87 return cap;
88 }
89
90 #ifdef TEST_PROGRAM_CAPUTILS
91 int main(int argc, char *argv[])
92 {
93 int rc = 0, cap;
94
95 if (argc < 2) {
96 fprintf(stderr, "usage: %1$s --last-by-procfs\n"
97 " %1$s --last-by-bsearch\n"
98 " %1$s --last\n",
99 program_invocation_short_name);
100 return EXIT_FAILURE;
101 }
102
103 if (strcmp(argv[1], "--last-by-procfs") == 0) {
104 rc = cap_last_by_procfs(&cap);
105 if (rc == 0)
106 printf("last cap: %d\n", cap);
107
108 } else if (strcmp(argv[1], "--last-by-bsearch") == 0) {
109 rc = cap_last_by_bsearch(&cap);
110 if (rc == 0)
111 printf("last cap: %d\n", cap);
112
113 } else if (strcmp(argv[1], "--last") == 0)
114 printf("last cap: %d\n", cap_last_cap());
115
116 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
117 }
118 #endif