diff options
Diffstat (limited to 'utils/captest.c')
-rw-r--r-- | utils/captest.c | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/utils/captest.c b/utils/captest.c new file mode 100644 index 0000000..2009d3c --- /dev/null +++ b/utils/captest.c @@ -0,0 +1,265 @@ +/* + * 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 <sgrubb@redhat.com> + * + */ +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <cap-ng.h> +#include <sys/prctl.h> +#ifdef HAVE_LINUX_SECUREBITS_H +#include <linux/securebits.h> +#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; +} + |