/* * captest.c - A program that demonstrates and outputs capabilities * Copyright (c) 2009 Red Hat Inc., Durham, North Carolina. * All Rights Reserved. * * This software may be freely redistributed and/or modified under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Authors: * Steve Grubb * */ #include "config.h" #include #include #include #include #include #include #include #include #ifdef HAVE_LINUX_SECUREBITS_H #include #endif /* children can't get caps back */ #ifndef SECURE_NOROOT #define SECURE_NOROOT 0 #endif #ifndef SECURE_NOROOT_LOCKED #define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */ #endif /* Setuid apps run by uid 0 don't get caps back */ #ifndef SECURE_NO_SETUID_FIXUP #define SECURE_NO_SETUID_FIXUP 2 #endif #ifndef SECURE_NO_SETUID_FIXUP_LOCKED #define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */ #endif static int text = 0, no_child = 0, lock = 0; static void report(void) { int rc, escalated = 0, need_comma = 0; uid_t uid, euid, suid; gid_t gid, egid, sgid; // Refresh what we have for capabilities if (capng_get_caps_process()) { printf("Error getting capabilities\n"); exit(1); } // Check user credentials getresuid(&uid, &euid, &suid); getresgid(&gid, &egid, &sgid); if (no_child) { if ((uid != euid && uid != 0) || capng_have_capability(CAPNG_EFFECTIVE, CAP_SETUID)) { printf("Attempting to regain root..."); setuid(0); getresuid(&uid, &euid, &suid); if (uid == 0) { printf("SUCCESS - PRIVILEGE ESCALATION POSSIBLE\n"); setgid(0); getresgid(&gid, &egid, &sgid); escalated = 1; } else printf("FAILED\n"); } printf("Child "); } printf("User credentials uid:%d euid:%d suid:%d\n", uid, euid, suid); if (no_child) printf("Child "); printf("Group credentials gid:%d egid:%d sgid:%d\n", gid, egid, sgid); if (uid != euid || gid != egid) printf("Note: app has mismatching credentials!!\n"); // Check capabilities if (text) { if (capng_have_capabilities(CAPNG_SELECT_CAPS) == CAPNG_NONE) { if (no_child) printf("Child capabilities: none\n"); else printf("Current capabilities: none\n"); } else { if (no_child) printf("Child "); printf("Effective: "); capng_print_caps_text(CAPNG_PRINT_STDOUT, CAPNG_EFFECTIVE); printf("\n"); if (no_child) printf("Child "); printf("Permitted: "); capng_print_caps_text(CAPNG_PRINT_STDOUT, CAPNG_PERMITTED); printf("\n"); if (no_child) printf("Child "); printf("Inheritable: "); capng_print_caps_text(CAPNG_PRINT_STDOUT, CAPNG_INHERITABLE); printf("\n"); if (no_child) printf("Child "); printf("Bounding Set: "); capng_print_caps_text(CAPNG_PRINT_STDOUT, CAPNG_BOUNDING_SET); printf("\n"); } } else { if (capng_have_capabilities(CAPNG_SELECT_CAPS) == CAPNG_NONE) { if (no_child) printf("Child capabilities: none\n"); else printf("Current capabilities: none\n"); } else { if (no_child) printf("Child capabilities:\n"); capng_print_caps_numeric(CAPNG_PRINT_STDOUT, CAPNG_SELECT_BOTH); } } // Now check securebits flags #ifdef PR_SET_SECUREBITS if (no_child) printf("Child "); printf("securebits flags: "); rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NOROOT); if (rc & (1 << SECURE_NOROOT)) { printf("NOROOT"); need_comma = 1; } rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NOROOT_LOCKED); if (rc & (1 << SECURE_NOROOT_LOCKED)) { if (need_comma) printf(", "); printf("NOROOT_LOCKED"); need_comma = 1; } rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NO_SETUID_FIXUP); if (rc & (1 << SECURE_NO_SETUID_FIXUP)) { if (need_comma) printf(", "); printf("NO_SETUID_FIXUP"); need_comma = 1; } rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NO_SETUID_FIXUP_LOCKED); if (rc & (1 << SECURE_NO_SETUID_FIXUP_LOCKED)) { if (need_comma) printf(", "); printf("NO_SETUID_FIXUP_LOCKED"); need_comma = 1; } if (need_comma == 0) printf("none"); printf("\n"); #endif // Now do child process checks if (no_child == 0 || escalated) { printf("Attempting direct access to shadow..."); if (access("/etc/shadow", R_OK) == 0) printf("SUCCESS\n"); else printf("FAILED (%s)\n", strerror(errno)); } if (no_child == 0) { printf("Attempting to access shadow by child process..."); rc = system("cat /etc/shadow > /dev/null 2>&1"); if (rc == 0) printf("SUCCESS\n"); else printf("FAILED\n"); if (text) system("/usr/bin/captest --no-child --text"); else system("/usr/bin/captest --no-child"); } } static void usage(void) { printf("usage: captest [ --drop-all | --drop-caps | --id ] [ --lock ] [ --text ]\n"); } int main(int argc, char *argv[]) { int which = 0, i; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--text") == 0) text = 1; else if (strcmp(argv[i], "--no-child") == 0) no_child = 1; else if (strcmp(argv[i], "--lock") == 0) lock = 1; else if (strcmp(argv[i], "--drop-all") == 0) which = 1; else if (strcmp(argv[i], "--drop-caps") == 0) which = 2; else if (strcmp(argv[i], "--id") == 0) which = 3; else { usage(); return 0; } } switch (which) { case 1: capng_clear(CAPNG_SELECT_BOTH); if (lock) capng_lock(); capng_apply(CAPNG_SELECT_BOTH); report(); break; case 2: capng_clear(CAPNG_SELECT_CAPS); if (lock) capng_lock(); capng_apply(CAPNG_SELECT_CAPS); report(); break; case 3: { int rc; capng_clear(CAPNG_SELECT_BOTH); capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_CHOWN); rc = capng_change_id(99, 99, CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING); if (rc < 0) { printf("Error changing uid: %d\n", rc); capng_print_caps_text(CAPNG_PRINT_STDOUT, CAPNG_EFFECTIVE); printf("\n"); exit(1); } printf("Keeping CAP_CHOWN to show capabilities across uid change.\n"); report(); } break; case 0: if (lock) capng_lock(); report(); break; } return 0; }