You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
6.6 KiB
267 lines
6.6 KiB
/*
|
|
* 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;
|
|
}
|
|
|