lib/caputils: add fall back for last cap using prctl.

This allows the rest of the programs using cap_last_cap to trust the
value returned by it, since it will either be obtained from procfs
(straight from kernel) or with prctl.

Also checked if the file under /proc is actually mounted in a procfs.
This commit is contained in:
Érico Rolim 2020-11-15 01:34:41 -03:00
parent e411b8a63d
commit 5d95818757
1 changed files with 40 additions and 13 deletions

View File

@ -14,32 +14,59 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <sys/prctl.h>
#include <stdio.h>
#include <limits.h>
#include "caputils.h"
#include "procutils.h"
#include "pathnames.h"
static int test_cap(unsigned int cap)
{
/* prctl returns 0 or 1 for valid caps, -1 otherwise */
return prctl(PR_CAPBSET_READ, cap, 0, 0, 0) >= 0;
}
int cap_last_cap(void)
{
/* CAP_LAST_CAP is untrustworthy. */
static int ret = -1;
int matched;
static int cap = -1;
FILE *f;
if (ret != -1)
return ret;
if (cap != -1)
return cap;
/* try to read value from kernel, check that the path is
* indeed in a procfs mount */
f = fopen(_PATH_PROC_CAPLASTCAP, "r");
if (!f) {
ret = CAP_LAST_CAP; /* guess */
return ret;
if (f) {
int matched = 0;
if (proc_is_procfs(fileno(f))) {
matched = fscanf(f, "%d", &cap);
}
fclose(f);
/* we check if the cap after this one really isn't valid */
if (matched == 1 && cap < INT_MAX && !test_cap(cap + 1))
return cap;
}
matched = fscanf(f, "%d", &ret);
fclose(f);
/* if it wasn't possible to read the file in /proc,
* fall back to binary search over capabilities */
if (matched != 1)
ret = CAP_LAST_CAP; /* guess */
/* starting with cap=INT_MAX means we always know
* that cap1 is invalid after the first iteration */
unsigned int cap0 = 0, cap1 = INT_MAX;
cap = INT_MAX;
while ((int)cap0 < cap) {
if (test_cap(cap)) {
cap0 = cap;
} else {
cap1 = cap;
}
cap = (cap0 + cap1) / 2U;
}
return ret;
return cap;
}