xref: /aosp_15_r20/external/libcap/libcap/cap_text.c (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1 /*
2  * Copyright (c) 1997-8,2007-8,2019,2021 Andrew G Morgan <[email protected]>
3  * Copyright (c) 1997 Andrew Main <[email protected]>
4  *
5  * This file deals with exchanging internal and textual
6  * representations of capability sets.
7  */
8 
9 #ifndef _GNU_SOURCE
10 #define _GNU_SOURCE
11 #endif
12 
13 #include <stdio.h>
14 
15 #define LIBCAP_PLEASE_INCLUDE_ARRAY
16 #include "libcap.h"
17 
18 static char const *_cap_names[__CAP_BITS] = LIBCAP_CAP_NAMES;
19 
20 #include <ctype.h>
21 #include <limits.h>
22 
23 #ifdef INCLUDE_GPERF_OUTPUT
24 /* we need to include it after #define _GNU_SOURCE is set */
25 #include INCLUDE_GPERF_OUTPUT
26 #endif
27 
28 /* Maximum output text length */
29 #define CAP_TEXT_SIZE    (__CAP_NAME_SIZE * __CAP_MAXBITS)
30 
31 /*
32  * Parse a textual representation of capabilities, returning an internal
33  * representation.
34  */
35 
36 #define raise_cap_mask(flat, c)  (flat)[CAP_TO_INDEX(c)] |= CAP_TO_MASK(c)
37 
setbits(cap_t a,const __u32 * b,cap_flag_t set,unsigned blks)38 static void setbits(cap_t a, const __u32 *b, cap_flag_t set, unsigned blks)
39 {
40     int n;
41     for (n = blks; n--; ) {
42 	a->u[n].flat[set] |= b[n];
43     }
44 }
45 
clrbits(cap_t a,const __u32 * b,cap_flag_t set,unsigned blks)46 static void clrbits(cap_t a, const __u32 *b, cap_flag_t set, unsigned blks)
47 {
48     int n;
49     for (n = blks; n--; )
50 	a->u[n].flat[set] &= ~b[n];
51 }
52 
namcmp(char const * str,char const * nam)53 static char const *namcmp(char const *str, char const *nam)
54 {
55     while (*nam && tolower((unsigned char)*str) == *nam) {
56 	str++;
57 	nam++;
58     }
59     if (*nam || isalnum((unsigned char)*str) || *str == '_')
60 	return NULL;
61     return str;
62 }
63 
64 /*
65  * forceall forces all of the kernel named capabilities to be assigned
66  * the masked value, and zeroed otherwise. Note, if the kernel is ahead
67  * of libcap, the upper bits will be referred to by number.
68  */
forceall(__u32 * flat,__u32 value,unsigned blks)69 static void forceall(__u32 *flat, __u32 value, unsigned blks)
70 {
71     unsigned n;
72     cap_value_t cmb = cap_max_bits();
73     for (n = blks; n--; ) {
74 	unsigned base = 32*n;
75 	__u32 mask = 0;
76 	if (cmb >= base + 32) {
77 	    mask = ~0;
78 	} else if (cmb > base) {
79 	    mask = (unsigned) ((1ULL << (cmb % 32)) - 1);
80 	}
81 	flat[n] = value & mask;
82     }
83 
84     return;
85 }
86 
lookupname(char const ** strp)87 static int lookupname(char const **strp)
88 {
89     union {
90 	char const *constp;
91 	char *p;
92     } str;
93 
94     str.constp = *strp;
95     if (isdigit(*str.constp)) {
96 	unsigned long n = strtoul(str.constp, &str.p, 0);
97 	if (n >= __CAP_MAXBITS)
98 	    return -1;
99 	*strp = str.constp;
100 	return n;
101     } else {
102 	int c;
103 	size_t len;
104 
105 	for (len=0; (c = str.constp[len]); ++len) {
106 	    if (!(isalpha(c) || (c == '_'))) {
107 		break;
108 	    }
109 	}
110 
111 #ifdef GPERF_DOWNCASE
112 	const struct __cap_token_s *token_info;
113 
114 	token_info = __cap_lookup_name(str.constp, len);
115 	if (token_info != NULL) {
116 	    *strp = str.constp + len;
117 	    return token_info->index;
118 	}
119 #else /* ie., ndef GPERF_DOWNCASE */
120 	char const *s;
121 	unsigned n = cap_max_bits();
122 	if (n > __CAP_BITS) {
123 	    n = __CAP_BITS;
124 	}
125 	while (n--) {
126 	    if (_cap_names[n] && (s = namcmp(str.constp, _cap_names[n]))) {
127 		*strp = s;
128 		return n;
129 	    }
130 	}
131 #endif /* def GPERF_DOWNCASE */
132 
133 	return -1;   	/* No definition available */
134     }
135 }
136 
cap_from_text(const char * str)137 cap_t cap_from_text(const char *str)
138 {
139     cap_t res;
140     int n;
141     unsigned cap_blks;
142 
143     if (str == NULL) {
144 	_cap_debug("bad argument");
145 	errno = EINVAL;
146 	return NULL;
147     }
148 
149     if (!(res = cap_init()))
150 	return NULL;
151 
152     switch (res->head.version) {
153     case _LINUX_CAPABILITY_VERSION_1:
154 	cap_blks = _LINUX_CAPABILITY_U32S_1;
155 	break;
156     case _LINUX_CAPABILITY_VERSION_2:
157 	cap_blks = _LINUX_CAPABILITY_U32S_2;
158 	break;
159     case _LINUX_CAPABILITY_VERSION_3:
160 	cap_blks = _LINUX_CAPABILITY_U32S_3;
161 	break;
162     default:
163 	cap_free(res);
164 	errno = EINVAL;
165 	return NULL;
166     }
167 
168     _cap_debug("%s", str);
169 
170     for (;;) {
171 	__u32 list[__CAP_BLKS];
172 	char op;
173 	int flags = 0, listed=0;
174 
175 	memset(list, 0, sizeof(__u32)*__CAP_BLKS);
176 
177 	/* skip leading spaces */
178 	while (isspace((unsigned char)*str))
179 	    str++;
180 	if (!*str) {
181 	    _cap_debugcap("e = ", *res, CAP_EFFECTIVE);
182 	    _cap_debugcap("i = ", *res, CAP_INHERITABLE);
183 	    _cap_debugcap("p = ", *res, CAP_PERMITTED);
184 
185 	    return res;
186 	}
187 
188 	/* identify caps specified by this clause */
189 	if (isalnum((unsigned char)*str) || *str == '_') {
190 	    for (;;) {
191 		if (namcmp(str, "all")) {
192 		    str += 3;
193 		    forceall(list, ~0, cap_blks);
194 		} else {
195 		    n = lookupname(&str);
196 		    if (n == -1)
197 			goto bad;
198 		    raise_cap_mask(list, n);
199 		}
200 		if (*str != ',')
201 		    break;
202 		if (!isalnum((unsigned char)*++str) && *str != '_')
203 		    goto bad;
204 	    }
205 	    listed = 1;
206 	} else if (*str == '+' || *str == '-') {
207 	    goto bad;                    /* require a list of capabilities */
208 	} else {
209 	    forceall(list, ~0, cap_blks);
210 	}
211 
212 	/* identify first operation on list of capabilities */
213 	op = *str++;
214 	if (op == '=' && (*str == '+' || *str == '-')) {
215 	    if (!listed)
216 		goto bad;
217 	    op = (*str++ == '+' ? 'P':'M'); /* skip '=' and take next op */
218 	} else if (op != '+' && op != '-' && op != '=')
219 	    goto bad;
220 
221 	/* cycle through list of actions */
222 	do {
223 	    _cap_debug("next char = '%c'", *str);
224 	    if (*str && !isspace(*str)) {
225 		switch (*str++) {    /* Effective, Inheritable, Permitted */
226 		case 'e':
227 		    flags |= LIBCAP_EFF;
228 		    break;
229 		case 'i':
230 		    flags |= LIBCAP_INH;
231 		    break;
232 		case 'p':
233 		    flags |= LIBCAP_PER;
234 		    break;
235 		default:
236 		    goto bad;
237 		}
238 	    } else if (op != '=') {
239 		_cap_debug("only '=' can be followed by space");
240 		goto bad;
241 	    }
242 
243 	    _cap_debug("how to read?");
244 	    switch (op) {               /* how do we interpret the caps? */
245 	    case '=':
246 	    case 'P':                                              /* =+ */
247 	    case 'M':                                              /* =- */
248 		clrbits(res, list, CAP_EFFECTIVE, cap_blks);
249 		clrbits(res, list, CAP_PERMITTED, cap_blks);
250 		clrbits(res, list, CAP_INHERITABLE, cap_blks);
251 		if (op == 'M')
252 		    goto minus;
253 		/* fall through */
254 	    case '+':
255 		if (flags & LIBCAP_EFF)
256 		    setbits(res, list, CAP_EFFECTIVE, cap_blks);
257 		if (flags & LIBCAP_PER)
258 		    setbits(res, list, CAP_PERMITTED, cap_blks);
259 		if (flags & LIBCAP_INH)
260 		    setbits(res, list, CAP_INHERITABLE, cap_blks);
261 		break;
262 	    case '-':
263 	    minus:
264 		if (flags & LIBCAP_EFF)
265 		    clrbits(res, list, CAP_EFFECTIVE, cap_blks);
266 		if (flags & LIBCAP_PER)
267 		    clrbits(res, list, CAP_PERMITTED, cap_blks);
268 		if (flags & LIBCAP_INH)
269 		    clrbits(res, list, CAP_INHERITABLE, cap_blks);
270 		break;
271 	    }
272 
273 	    /* new directive? */
274 	    if (*str == '+' || *str == '-') {
275 		if (!listed) {
276 		    _cap_debug("for + & - must list capabilities");
277 		    goto bad;
278 		}
279 		flags = 0;                       /* reset the flags */
280 		op = *str++;
281 		if (!isalpha(*str))
282 		    goto bad;
283 	    }
284 	} while (*str && !isspace(*str));
285 	_cap_debug("next clause");
286     }
287 
288 bad:
289     cap_free(res);
290     res = NULL;
291     errno = EINVAL;
292     return res;
293 }
294 
295 /*
296  * lookup a capability name and return its numerical value
297  */
cap_from_name(const char * name,cap_value_t * value_p)298 int cap_from_name(const char *name, cap_value_t *value_p)
299 {
300     int n;
301 
302     if (((n = lookupname(&name)) >= 0) && (value_p != NULL)) {
303 	*value_p = (unsigned) n;
304     }
305     return -(n < 0);
306 }
307 
308 /*
309  * Convert a single capability index number into a string representation
310  */
cap_to_name(cap_value_t cap)311 char *cap_to_name(cap_value_t cap)
312 {
313     char *tmp, *result;
314 
315     if ((cap >= 0) && (cap < __CAP_BITS)) {
316 	return _libcap_strdup(_cap_names[cap]);
317     }
318     if (asprintf(&tmp, "%u", cap) <= 0) {
319 	_cap_debug("asprintf filed");
320 	return NULL;
321     }
322 
323     result = _libcap_strdup(tmp);
324     free(tmp);
325     return result;
326 }
327 
328 /*
329  * Convert an internal representation to a textual one. The textual
330  * representation is stored in static memory. It will be overwritten
331  * on the next occasion that this function is called.
332  */
333 
getstateflags(cap_t caps,int capno)334 static int getstateflags(cap_t caps, int capno)
335 {
336     int f = 0;
337 
338     if (isset_cap(caps, capno, CAP_EFFECTIVE)) {
339 	f |= LIBCAP_EFF;
340     }
341     if (isset_cap(caps, capno, CAP_PERMITTED)) {
342 	f |= LIBCAP_PER;
343     }
344     if (isset_cap(caps, capno, CAP_INHERITABLE)) {
345 	f |= LIBCAP_INH;
346     }
347 
348     return f;
349 }
350 
351 /*
352  * This code assumes that the longest named capability is longer than
353  * the decimal text representation of __CAP_MAXBITS. This is very true
354  * at the time of writing and likely to remain so. However, we have
355  * a test in cap_text to validate it at build time.
356  */
357 #define CAP_TEXT_BUFFER_ZONE 100
358 
cap_to_text(cap_t caps,ssize_t * length_p)359 char *cap_to_text(cap_t caps, ssize_t *length_p)
360 {
361     char buf[CAP_TEXT_SIZE+CAP_TEXT_BUFFER_ZONE];
362     char *p, *base;
363     int histo[8];
364     int m, t;
365     unsigned n;
366 
367     /* Check arguments */
368     if (!good_cap_t(caps)) {
369 	errno = EINVAL;
370 	return NULL;
371     }
372 
373     _cap_debugcap("e = ", *caps, CAP_EFFECTIVE);
374     _cap_debugcap("i = ", *caps, CAP_INHERITABLE);
375     _cap_debugcap("p = ", *caps, CAP_PERMITTED);
376 
377     memset(histo, 0, sizeof(histo));
378 
379     /* default prevailing state to the named bits */
380     cap_value_t cmb = cap_max_bits();
381     for (n = 0; n < cmb; n++)
382 	histo[getstateflags(caps, n)]++;
383 
384     /* find which combination of capability sets shares the most bits
385        we bias to preferring non-set (m=0) with the >= 0 test. Failing
386        to do this causes strange things to happen with older systems
387        that don't know about bits 32+. */
388     for (m=t=7; t--; )
389 	if (histo[t] >= histo[m])
390 	    m = t;
391 
392     /* blank is not a valid capability set */
393     base = buf;
394     p = sprintf(buf, "=%s%s%s",
395 		(m & LIBCAP_EFF) ? "e" : "",
396 		(m & LIBCAP_INH) ? "i" : "",
397 		(m & LIBCAP_PER) ? "p" : "" ) + buf;
398 
399     for (t = 8; t--; ) {
400 	if (t == m || !histo[t]) {
401 	    continue;
402 	}
403 	*p++ = ' ';
404 	for (n = 0; n < cmb; n++) {
405 	    if (getstateflags(caps, n) == t) {
406 	        char *this_cap_name = cap_to_name(n);
407 		if (this_cap_name == NULL) {
408 		    return NULL;
409 		}
410 	        if ((strlen(this_cap_name) + (p - buf)) > CAP_TEXT_SIZE) {
411 		    cap_free(this_cap_name);
412 		    errno = ERANGE;
413 		    return NULL;
414 	        }
415 	        p += sprintf(p, "%s,", this_cap_name);
416 	        cap_free(this_cap_name);
417 	    }
418 	}
419 	p--;
420 	n = t & ~m;
421 	if (n) {
422 	    char op = '+';
423 	    if (base[0] == '=' && base[1] == ' ') {
424 		/*
425 		 * Special case all lowered default "= foo,...+eip
426 		 * ..." as "foo,...=eip ...". (Equivalent but shorter.)
427 		 */
428 		base += 2;
429 		op = '=';
430 	    }
431 	    p += sprintf(p, "%c%s%s%s", op,
432 			 (n & LIBCAP_EFF) ? "e" : "",
433 			 (n & LIBCAP_INH) ? "i" : "",
434 			 (n & LIBCAP_PER) ? "p" : "");
435 	}
436 	n = ~t & m;
437 	if (n) {
438 	    p += sprintf(p, "-%s%s%s",
439 			 (n & LIBCAP_EFF) ? "e" : "",
440 			 (n & LIBCAP_INH) ? "i" : "",
441 			 (n & LIBCAP_PER) ? "p" : "");
442 	}
443 	if (p - buf > CAP_TEXT_SIZE) {
444 	    errno = ERANGE;
445 	    return NULL;
446 	}
447     }
448 
449     /* capture remaining unnamed bits - which must all be +. */
450     memset(histo, 0, sizeof(histo));
451     for (n = cmb; n < __CAP_MAXBITS; n++)
452 	histo[getstateflags(caps, n)]++;
453 
454     for (t = 8; t-- > 1; ) {
455 	if (!histo[t]) {
456 	    continue;
457 	}
458 	*p++ = ' ';
459 	for (n = cmb; n < __CAP_MAXBITS; n++) {
460 	    if (getstateflags(caps, n) == t) {
461 		char *this_cap_name = cap_to_name(n);
462 		if (this_cap_name == NULL) {
463 		    return NULL;
464 		}
465 	        if ((strlen(this_cap_name) + (p - buf)) > CAP_TEXT_SIZE) {
466 		    cap_free(this_cap_name);
467 		    errno = ERANGE;
468 		    return NULL;
469 	        }
470 		p += sprintf(p, "%s,", this_cap_name);
471 		cap_free(this_cap_name);
472 	    }
473 	}
474 	p--;
475 	p += sprintf(p, "+%s%s%s",
476 		     (t & LIBCAP_EFF) ? "e" : "",
477 		     (t & LIBCAP_INH) ? "i" : "",
478 		     (t & LIBCAP_PER) ? "p" : "");
479 	if (p - buf > CAP_TEXT_SIZE) {
480 	    errno = ERANGE;
481 	    return NULL;
482 	}
483     }
484 
485     _cap_debug("%s", base);
486     if (length_p) {
487 	*length_p = p - base;
488     }
489 
490     return (_libcap_strdup(base));
491 }
492 
493 /*
494  * cap_mode_name returns a text token naming the specified mode.
495  */
cap_mode_name(cap_mode_t flavor)496 const char *cap_mode_name(cap_mode_t flavor) {
497     switch (flavor) {
498     case CAP_MODE_NOPRIV:
499 	return "NOPRIV";
500     case CAP_MODE_PURE1E_INIT:
501 	return "PURE1E_INIT";
502     case CAP_MODE_PURE1E:
503 	return "PURE1E";
504     case CAP_MODE_UNCERTAIN:
505 	return "UNCERTAIN";
506     case CAP_MODE_HYBRID:
507 	return "HYBRID";
508     default:
509 	return "UNKNOWN";
510     }
511 }
512 
513 /*
514  * cap_iab_to_text serializes an iab into a canonical text
515  * representation.
516  */
cap_iab_to_text(cap_iab_t iab)517 char *cap_iab_to_text(cap_iab_t iab)
518 {
519     char buf[CAP_TEXT_SIZE+CAP_TEXT_BUFFER_ZONE];
520     char *p = buf;
521     cap_value_t c, cmb = cap_max_bits();
522     int first = 1;
523 
524     if (good_cap_iab_t(iab)) {
525 	_cap_mu_lock(&iab->mutex);
526 	for (c = 0; c < cmb; c++) {
527 	    int keep = 0;
528 	    int o = c >> 5;
529 	    __u32 bit = 1U << (c & 31);
530 	    __u32 ib = iab->i[o] & bit;
531 	    __u32 ab = iab->a[o] & bit;
532 	    __u32 nbb = iab->nb[o] & bit;
533 	    if (!(nbb | ab | ib)) {
534 		continue;
535 	    }
536 	    if (!first) {
537 		*p++ = ',';
538 	    }
539 	    if (nbb) {
540 		*p++ = '!';
541 		keep = 1;
542 	    }
543 	    if (ab) {
544 		*p++ = '^';
545 		keep = 1;
546 	    } else if (nbb && ib) {
547 		*p++ = '%';
548 	    }
549 	    if (keep || ib) {
550 		if (c < __CAP_BITS) {
551 		    strcpy(p, _cap_names[c]);
552 		} else {
553 		    sprintf(p, "%u", c);
554 		}
555 		p += strlen(p);
556 		first = 0;
557 	    }
558 	}
559 	_cap_mu_unlock(&iab->mutex);
560     }
561     *p = '\0';
562     return _libcap_strdup(buf);
563 }
564 
cap_iab_from_text(const char * text)565 cap_iab_t cap_iab_from_text(const char *text)
566 {
567     cap_iab_t iab = cap_iab_init();
568     if (iab == NULL) {
569 	return iab;
570     }
571     if (text != NULL) {
572 	unsigned flags;
573 	for (flags = 0; *text; text++) {
574 	    /* consume prefixes */
575 	    switch (*text) {
576 	    case '!':
577 		flags |= LIBCAP_IAB_NB_FLAG;
578 		continue;
579 	    case '^':
580 		flags |= LIBCAP_IAB_IA_FLAG;
581 		continue;
582 	    case '%':
583 		flags |= LIBCAP_IAB_I_FLAG;
584 		continue;
585 	    default:
586 		break;
587 	    }
588 	    if (!flags) {
589 		flags = LIBCAP_IAB_I_FLAG;
590 	    }
591 
592 	    /* consume cap name */
593 	    cap_value_t c = lookupname(&text);
594 	    if (c == -1) {
595 		goto cleanup;
596 	    }
597 	    unsigned o = c >> 5;
598 	    __u32 mask = 1U << (c & 31);
599 	    if (flags & LIBCAP_IAB_I_FLAG) {
600 		iab->i[o] |= mask;
601 	    }
602 	    if (flags & LIBCAP_IAB_A_FLAG) {
603 		iab->a[o] |= mask;
604 	    }
605 	    if (flags & LIBCAP_IAB_NB_FLAG) {
606 		iab->nb[o] |= mask;
607 	    }
608 
609 	    /* rest should be end or comma */
610 	    if (*text == '\0') {
611 		break;
612 	    }
613 	    if (*text != ',') {
614 		goto cleanup;
615 	    }
616 	    flags = 0;
617 	}
618     }
619     return iab;
620 
621 cleanup:
622     cap_free(iab);
623     errno = EINVAL;
624     return NULL;
625 }
626 
_parse_hex32(const char * c)627 static __u32 _parse_hex32(const char *c)
628 {
629     int i;
630     __u32 v = 0;
631     for (i=0; i < 8; i++, c++) {
632 	v <<= 4;
633 	if (*c == 0 || *c < '0') {
634 	    return 0;
635 	} else if (*c <= '9') {
636 	    v += *c - '0';
637 	} else if (*c > 'f') {
638 	    return 0;
639 	} else if (*c >= 'a') {
640 	    v += *c + 10 - 'a';
641 	} else if (*c < 'A') {
642 	    return 0;
643 	} else if (*c <= 'F') {
644 	    v += *c + 10 - 'A';
645 	} else {
646 	    return 0;
647 	}
648     }
649     return v;
650 }
651 
652 /*
653  * _parse_vec_string converts the hex dumps in /proc/<pid>/current into
654  * an array of u32s - masked as per the forceall() mask.
655  */
_parse_vec_string(__u32 * vals,const char * c,int invert)656 static __u32 _parse_vec_string(__u32 *vals, const char *c, int invert)
657 {
658     int i;
659     int words = strlen(c)/8;
660     if (words > _LIBCAP_CAPABILITY_U32S) {
661 	return 0;
662     }
663     forceall(vals, ~0, words);
664     for (i = 0; i < words; i++) {
665 	__u32 val = _parse_hex32(c+8*(words-1-i));
666 	if (invert) {
667 	    val = ~val;
668 	}
669 	vals[i] &= val;
670     }
671     return ~0;
672 }
673 
674 /*
675  * libcap believes this is the root of the mounted "/proc"
676  * filesystem. (NULL == "/proc".)
677  */
678 static char *_cap_proc_dir;
679 
680 /*
681  * If the constructor is called (see cap_alloc.c) then we'll need the
682  * corresponding destructor.
683  */
_cleanup_libcap(void)684 __attribute__((destructor (300))) static void _cleanup_libcap(void)
685 {
686     if (_cap_proc_dir == NULL) {
687 	return;
688     }
689     cap_free(_cap_proc_dir);
690     _cap_proc_dir = NULL;
691 }
692 
693 /*
694  * cap_proc_root reads and (optionally: when root != NULL) changes
695  * libcap's notion of where the "/proc" filesystem is mounted. It
696  * defaults to the value "/proc". Note, this is a global value and not
697  * considered thread safe to write - so the client should take
698  * suitable care when changing it. Further, libcap will allocate
699  * memory for storing the replacement root, and it is this memory that
700  * is returned. So, when changing the value, the caller should
701  * cap_free(the-return-value) when done with it.
702  *
703  * A return value of NULL implies the default is in effect "/proc".
704  */
cap_proc_root(const char * root)705 char *cap_proc_root(const char *root)
706 {
707     char *old = _cap_proc_dir;
708     if (root != NULL) {
709 	_cap_proc_dir = _libcap_strdup(root);
710     }
711     return old;
712 }
713 
714 #define PROC_LINE_MAX (8 + 8*_LIBCAP_CAPABILITY_U32S + 100)
715 /*
716  * cap_iab_get_pid fills an IAB tuple from the content of
717  * /proc/<pid>/status. Linux doesn't support syscall access to the
718  * needed information, so we parse it out of that file.
719  */
cap_iab_get_pid(pid_t pid)720 cap_iab_t cap_iab_get_pid(pid_t pid)
721 {
722     cap_iab_t iab;
723     char *path;
724     FILE *file;
725     char line[PROC_LINE_MAX];
726     const char *proc_root = _cap_proc_dir;
727 
728     if (proc_root == NULL) {
729 	proc_root = "/proc";
730     }
731     if (asprintf(&path, "%s/%d/status", proc_root, pid) <= 0) {
732 	return NULL;
733     }
734     file = fopen(path, "r");
735     free(path);
736     if (file == NULL) {
737 	return NULL;
738     }
739 
740     iab = cap_iab_init();
741     uint ok = 0;
742     if (iab != NULL) {
743 	while (fgets(line, PROC_LINE_MAX-1, file) != NULL) {
744 	    if (strncmp("Cap", line, 3) != 0) {
745 		continue;
746 	    }
747 	    if (strncmp("Inh:\t", line+3, 5) == 0) {
748 		ok = (_parse_vec_string(iab->i, line+8, 0) &
749 		    LIBCAP_IAB_I_FLAG) | ok;
750 		continue;
751 	    }
752 	    if (strncmp("Bnd:\t", line+3, 5) == 0) {
753 		ok = (_parse_vec_string(iab->nb, line+8, 1) &
754 		      LIBCAP_IAB_NB_FLAG) | ok;
755 		continue;
756 	    }
757 	    if (strncmp("Amb:\t", line+3, 5) == 0) {
758 		ok = (_parse_vec_string(iab->a, line+8, 0) &
759 		      LIBCAP_IAB_A_FLAG) | ok;
760 		continue;
761 	    }
762 	}
763     }
764     if (ok != (LIBCAP_IAB_IA_FLAG | LIBCAP_IAB_NB_FLAG)) {
765 	cap_free(iab);
766 	iab = NULL;
767     }
768     fclose(file);
769     return iab;
770 }
771