summaryrefslogtreecommitdiff
path: root/utils/captest.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/captest.c')
-rw-r--r--utils/captest.c265
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;
+}
+