Home | History | Annotate | Download | only in utils
      1 /*
      2  * captest.c - A program that demonstrates and outputs capabilities
      3  * Copyright (c) 2009 Red Hat Inc., Durham, North Carolina.
      4  * All Rights Reserved.
      5  *
      6  * This software may be freely redistributed and/or modified under the
      7  * terms of the GNU General Public License as published by the Free
      8  * Software Foundation; either version 2, or (at your option) any
      9  * later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  * GNU General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program; see the file COPYING. If not, write to the
     18  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     19  *
     20  * Authors:
     21  *   Steve Grubb <sgrubb (at) redhat.com>
     22  *
     23  */
     24 #include "config.h"
     25 #include <unistd.h>
     26 #include <stdio.h>
     27 #include <fcntl.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <errno.h>
     31 #include <cap-ng.h>
     32 #include <sys/prctl.h>
     33 #ifdef HAVE_LINUX_SECUREBITS_H
     34 #include <linux/securebits.h>
     35 #endif
     36 
     37 /* children can't get caps back */
     38 #ifndef SECURE_NOROOT
     39 #define SECURE_NOROOT                   0
     40 #endif
     41 #ifndef SECURE_NOROOT_LOCKED
     42 #define SECURE_NOROOT_LOCKED            1  /* make bit-0 immutable */
     43 #endif
     44 /* Setuid apps run by uid 0 don't get caps back */
     45 #ifndef SECURE_NO_SETUID_FIXUP
     46 #define SECURE_NO_SETUID_FIXUP          2
     47 #endif
     48 #ifndef SECURE_NO_SETUID_FIXUP_LOCKED
     49 #define SECURE_NO_SETUID_FIXUP_LOCKED   3  /* make bit-2 immutable */
     50 #endif
     51 
     52 static int text = 0, no_child = 0, lock = 0;
     53 
     54 static void report(void)
     55 {
     56 	int rc, escalated = 0, need_comma = 0;
     57 	uid_t uid, euid, suid;
     58 	gid_t gid, egid, sgid;
     59 
     60 	// Refresh what we have for capabilities
     61 	if (capng_get_caps_process()) {
     62 		printf("Error getting capabilities\n");
     63 		exit(1);
     64 	}
     65 
     66 	// Check user credentials
     67 	getresuid(&uid, &euid, &suid);
     68 	getresgid(&gid, &egid, &sgid);
     69 	if (no_child) {
     70 		if ((uid != euid && uid != 0) ||
     71 					capng_have_capability(CAPNG_EFFECTIVE,
     72 						 CAP_SETUID)) {
     73 			printf("Attempting to regain root...");
     74 			setuid(0);
     75 			getresuid(&uid, &euid, &suid);
     76 			if (uid == 0) {
     77 				printf("SUCCESS - PRIVILEGE ESCALATION POSSIBLE\n");
     78 				setgid(0);
     79 				getresgid(&gid, &egid, &sgid);
     80 				escalated = 1;
     81 			} else
     82 				printf("FAILED\n");
     83 		}
     84 		printf("Child ");
     85 	}
     86 	printf("User  credentials uid:%d euid:%d suid:%d\n", uid, euid, suid);
     87 	if (no_child)
     88 		printf("Child ");
     89 	printf("Group credentials gid:%d egid:%d sgid:%d\n", gid, egid, sgid);
     90 	if (uid != euid || gid != egid)
     91 		printf("Note: app has mismatching credentials!!\n");
     92 
     93 	// Check capabilities
     94 	if (text) {
     95 		if (capng_have_capabilities(CAPNG_SELECT_CAPS) == CAPNG_NONE) {
     96 			if (no_child)
     97 				printf("Child capabilities: none\n");
     98 			else
     99 				printf("Current capabilities: none\n");
    100 		} else {
    101 			if (no_child)
    102 				printf("Child ");
    103 			printf("Effective: ");
    104 			capng_print_caps_text(CAPNG_PRINT_STDOUT,
    105 					CAPNG_EFFECTIVE);
    106 			printf("\n");
    107 			if (no_child)
    108 				printf("Child ");
    109 			printf("Permitted: ");
    110 			capng_print_caps_text(CAPNG_PRINT_STDOUT,
    111 					CAPNG_PERMITTED);
    112 			printf("\n");
    113 			if (no_child)
    114 				printf("Child ");
    115 			printf("Inheritable: ");
    116 			capng_print_caps_text(CAPNG_PRINT_STDOUT,
    117 					CAPNG_INHERITABLE);
    118 			printf("\n");
    119 			if (no_child)
    120 				printf("Child ");
    121 			printf("Bounding Set: ");
    122 			capng_print_caps_text(CAPNG_PRINT_STDOUT,
    123 					CAPNG_BOUNDING_SET);
    124 			printf("\n");
    125 		}
    126 	} else {
    127 		if (capng_have_capabilities(CAPNG_SELECT_CAPS) == CAPNG_NONE) {
    128 			if (no_child)
    129 				printf("Child capabilities: none\n");
    130 			else
    131 				printf("Current capabilities: none\n");
    132 		} else {
    133 			if (no_child)
    134 				printf("Child capabilities:\n");
    135 			capng_print_caps_numeric(CAPNG_PRINT_STDOUT,
    136 					CAPNG_SELECT_BOTH);
    137 		}
    138 	}
    139 
    140 	// Now check securebits flags
    141 #ifdef PR_SET_SECUREBITS
    142 	if (no_child)
    143 		printf("Child ");
    144 	printf("securebits flags: ");
    145 	rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NOROOT);
    146 	if (rc & (1 << SECURE_NOROOT)) {
    147 		printf("NOROOT");
    148 		need_comma = 1;
    149 	}
    150 	rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NOROOT_LOCKED);
    151 	if (rc & (1 << SECURE_NOROOT_LOCKED)) {
    152 		if (need_comma)
    153 			printf(", ");
    154 		printf("NOROOT_LOCKED");
    155 		need_comma = 1;
    156 	}
    157 	rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NO_SETUID_FIXUP);
    158 	if (rc & (1 << SECURE_NO_SETUID_FIXUP)) {
    159 		if (need_comma)
    160 			printf(", ");
    161 		printf("NO_SETUID_FIXUP");
    162 		need_comma = 1;
    163 	}
    164 	rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NO_SETUID_FIXUP_LOCKED);
    165 	if (rc & (1 << SECURE_NO_SETUID_FIXUP_LOCKED)) {
    166 		if (need_comma)
    167 			printf(", ");
    168 		printf("NO_SETUID_FIXUP_LOCKED");
    169 		need_comma = 1;
    170 	}
    171 	if (need_comma == 0)
    172 		printf("none");
    173 	printf("\n");
    174 #endif
    175 	// Now do child process checks
    176 	if (no_child == 0 || escalated) {
    177 		printf("Attempting direct access to shadow...");
    178 		if (access("/etc/shadow", R_OK) == 0)
    179 			printf("SUCCESS\n");
    180 		else
    181 			printf("FAILED (%s)\n", strerror(errno));
    182 	}
    183 	if (no_child == 0) {
    184 		printf("Attempting to access shadow by child process...");
    185 		rc = system("cat /etc/shadow > /dev/null 2>&1");
    186 		if (rc == 0)
    187 			printf("SUCCESS\n");
    188 		else
    189 			printf("FAILED\n");
    190 		if (text)
    191 			system("/usr/bin/captest --no-child --text");
    192 		else
    193 			system("/usr/bin/captest --no-child");
    194 	}
    195 }
    196 
    197 static void usage(void)
    198 {
    199 	printf("usage: captest [ --drop-all | --drop-caps | --id ] [ --lock ] [ --text ]\n");
    200 }
    201 
    202 int main(int argc, char *argv[])
    203 {
    204 	int which = 0, i;
    205 
    206 	for (i = 1; i < argc; i++) {
    207 		if (strcmp(argv[i], "--text") == 0)
    208 			text = 1;
    209 		else if (strcmp(argv[i], "--no-child") == 0)
    210 			no_child = 1;
    211 		else if (strcmp(argv[i], "--lock") == 0)
    212 			lock = 1;
    213 		else if (strcmp(argv[i], "--drop-all") == 0)
    214 			which = 1;
    215 		else if (strcmp(argv[i], "--drop-caps") == 0)
    216 			which = 2;
    217 		else if (strcmp(argv[i], "--id") == 0)
    218 			which = 3;
    219 		else {
    220 			usage();
    221 			return 0;
    222 		}
    223 	}
    224 	switch (which)
    225 	{
    226 		case 1:
    227 			capng_clear(CAPNG_SELECT_BOTH);
    228 			if (lock)
    229 				capng_lock();
    230 			capng_apply(CAPNG_SELECT_BOTH);
    231 			report();
    232 			break;
    233 		case 2:
    234 			capng_clear(CAPNG_SELECT_CAPS);
    235 			if (lock)
    236 				capng_lock();
    237 			capng_apply(CAPNG_SELECT_CAPS);
    238 			report();
    239 			break;
    240 		case 3: {
    241 			int rc;
    242 
    243 			capng_clear(CAPNG_SELECT_BOTH);
    244 			capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED,
    245 					CAP_CHOWN);
    246 			rc = capng_change_id(99, 99,
    247 				CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING);
    248 			if (rc < 0) {
    249 				printf("Error changing uid: %d\n", rc);
    250 				capng_print_caps_text(CAPNG_PRINT_STDOUT,
    251 					CAPNG_EFFECTIVE);
    252 				printf("\n");
    253 				exit(1);
    254 			}
    255 			printf("Keeping CAP_CHOWN to show capabilities across uid change.\n");
    256 			report();
    257 			} break;
    258 		case 0:
    259 			if (lock)
    260 				capng_lock();
    261 			report();
    262 			break;
    263 	}
    264 	return 0;
    265 }
    266 
    267