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@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 52static int text = 0, no_child = 0, lock = 0; 53 54static 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 197static void usage(void) 198{ 199 printf("usage: captest [ --drop-all | --drop-caps | --id ] [ --lock ] [ --text ]\n"); 200} 201 202int 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