| /* |
| * 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 "config.h" |
| #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; |
| } |
| |