Unverified Commit 3dd7584b authored by Shawn Webb's avatar Shawn Webb
Browse files

Introduce basic Trusted Path Execution (TPE) support



This introduces a new command to secadm: tpe. To enable TPE, use
`secadm tpe -T`. To disable TPE, use `secadm tpe -t`. To set the GID,
use `secadm tpe -g <gid>`. To enforce TPE for everyone, use `secadm
tpe -A`. To invert the GID, use `secadm tpe -g`. The GID by default is
0.

TODO:
    1) Documentation
    2) Support tpe in secadm.rules(5)
Signed-off-by: Shawn Webb's avatarShawn Webb <shawn.webb@hardenedbsd.org>
parent a800229d
......@@ -4,6 +4,7 @@ SRCS= secadm.c \
secadm_sysctl.c \
secadm_vnode.c \
integriforce.c \
tpe.c \
vnode_if.h
CFLAGS+= -g -O0 -I${.CURDIR}/../libsecadm
......
......@@ -51,6 +51,7 @@ secadm_sysctl_handler(SYSCTL_HANDLER_ARGS)
secadm_reply_t reply;
secadm_rule_t *rule;
int err, i, rn;
uint32_t flags;
if (!(req->newptr) || (req->newlen != sizeof(secadm_command_t))) {
return (EINVAL);
......@@ -78,7 +79,9 @@ secadm_sysctl_handler(SYSCTL_HANDLER_ARGS)
case secadm_cmd_del_rule:
case secadm_cmd_enable_rule:
case secadm_cmd_disable_rule:
case secadm_cmd_set_whitelist_mode:
case secadm_cmd_set_tpe_flags:
case secadm_cmd_set_tpe_gid:
case secadm_cmd_set_integriforce_flags:
if (req->td->td_ucred->cr_uid) {
printf("[SECADM] Denied attempt to sysctl by "
"(%s) uid:%d jail:%d\n",
......@@ -336,7 +339,7 @@ secadm_sysctl_handler(SYSCTL_HANDLER_ARGS)
break;
case secadm_cmd_set_whitelist_mode:
case secadm_cmd_set_integriforce_flags:
entry = get_prison_list_entry(
req->td->td_ucred->cr_prison->pr_id);
......@@ -360,7 +363,57 @@ secadm_sysctl_handler(SYSCTL_HANDLER_ARGS)
break;
case secadm_cmd_get_whitelist_mode:
case secadm_cmd_set_tpe_flags:
entry = get_prison_list_entry(
req->td->td_ucred->cr_prison->pr_id);
PE_RLOCK(entry);
/* Reusing i to get the flag */
err = copyin(cmd.sc_data, &i, sizeof(int));
if (err == 0) {
flags = 0;
if (i & SECADM_TPE_ENABLED) {
flags |= SECADM_TPE_ENABLED;
flags &= ~(SECADM_TPE_DISABLED);
}
if (i & SECADM_TPE_DISABLED) {
flags &= ~(SECADM_TPE_ENABLED);
}
if (i & SECADM_TPE_ALL) {
flags |= SECADM_TPE_ALL;
}
if (i & SECADM_TPE_INVERT) {
flags |= SECADM_TPE_INVERT;
}
entry->sp_tpe_flags = flags;
reply.sr_code = secadm_reply_success;
} else {
reply.sr_code = secadm_reply_fail;
}
PE_RUNLOCK(entry);
break;
case secadm_cmd_set_tpe_gid:
entry = get_prison_list_entry(
req->td->td_ucred->cr_prison->pr_id);
PE_RLOCK(entry);
/* Reusing i to get the flag */
err = copyin(cmd.sc_data, &i, sizeof(int));
if (err == 0) {
entry->sp_tpe_gid = (gid_t)i;
reply.sr_code = secadm_reply_success;
} else {
reply.sr_code = secadm_reply_fail;
}
PE_RUNLOCK(entry);
break;
case secadm_cmd_get_integriforce_flags:
entry = get_prison_list_entry(
req->td->td_ucred->cr_prison->pr_id);
......@@ -372,6 +425,30 @@ secadm_sysctl_handler(SYSCTL_HANDLER_ARGS)
break;
case secadm_cmd_get_tpe_flags:
entry = get_prison_list_entry(
req->td->td_ucred->cr_prison->pr_id);
PE_RLOCK(entry);
copyout(&(entry->sp_tpe_flags), reply.sr_data, sizeof(int));
PE_RUNLOCK(entry);
reply.sr_code = secadm_reply_success;
break;
case secadm_cmd_get_tpe_gid:
entry = get_prison_list_entry(
req->td->td_ucred->cr_prison->pr_id);
PE_RLOCK(entry);
copyout(&(entry->sp_tpe_gid), reply.sr_data, sizeof(int));
PE_RUNLOCK(entry);
reply.sr_code = secadm_reply_success;
break;
default:
printf("secadm_sysctl: unknown command!\n");
......
......@@ -65,6 +65,10 @@ secadm_vnode_check_exec(struct ucred *ucred, struct vnode *vp,
entry = get_prison_list_entry(ucred->cr_prison->pr_id);
if ((err = tpe_check(imgp, entry))) {
return (err);
}
PE_RLOCK(entry);
if (entry->sp_num_integriforce_rules) {
key.sk_type = secadm_integriforce_rule;
......
/*-
* Copyright (c) 2016 Shawn Webb <shawn.webb@hardenedbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/fcntl.h>
#include <sys/imgact.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/libkern.h>
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/pax.h>
#include <sys/proc.h>
#include <sys/stat.h>
#include <sys/sx.h>
#include <sys/tree.h>
#include <sys/vnode.h>
#include <security/mac/mac_policy.h>
#include "secadm.h"
int
tpe_check(struct image_params *imgp, secadm_prison_entry_t *entry)
{
char *path, *p1, *newpath;
struct nameidata nd;
struct vattr vap;
int err;
err = 0;
newpath = NULL;
if (!(entry->sp_tpe_flags & SECADM_TPE_ENABLED)) {
return (0);
}
if ((entry->sp_tpe_flags & SECADM_TPE_ALL) != SECADM_TPE_ALL) {
if (entry->sp_tpe_flags & SECADM_TPE_INVERT) {
if (curthread->td_ucred->cr_gid == entry->sp_tpe_gid) {
return (0);
}
} else {
if (curthread->td_ucred->cr_gid != entry->sp_tpe_gid) {
return (0);
}
}
}
if (imgp->args == NULL) {
return (0);
}
if (imgp->args == (struct image_args *)0xdeadc0de || imgp->args == (struct image_args *)0xdeadc0dedeadc0de) {
return (0);
}
err = 0;
path = imgp->args->fname;
if (path == NULL) {
return (0);
}
p1 = strrchr(path, '/');
if (p1 == NULL) {
return (0);
}
newpath = malloc((p1 - path) + 1, M_SECADM, M_WAITOK | M_ZERO);
strncpy(newpath, path, p1 - path);
memset(&nd, 0x00, sizeof(nd));
NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE, newpath, curthread);
if ((err = namei(&nd))) {
free(newpath, M_SECADM);
NDFREE(&nd, NDF_ONLY_PNBUF);
return (err);
}
err = VOP_GETATTR(nd.ni_vp, &vap, curthread->td_ucred);
if (err) {
err = 0;
goto cleanup;
}
if (vap.va_uid != 0) {
err = EPERM;
goto cleanup;
}
if (vap.va_mode & (S_IWGRP | S_IWOTH)) {
err = EPERM;
goto cleanup;
}
cleanup:
vput(nd.ni_vp);
NDFREE(&nd, NDF_ONLY_PNBUF);
free(newpath, M_SECADM);
return (err);
}
......@@ -120,7 +120,7 @@ _secadm_integriforce_flags_ops(int mode)
memset(&reply, 0x00, sizeof(secadm_reply_t));
cmd.sc_version = SECADM_VERSION;
cmd.sc_type = secadm_cmd_set_whitelist_mode;
cmd.sc_type = secadm_cmd_set_integriforce_flags;
cmd.sc_data = &mode;
if ((err = _secadm_sysctl(&cmd, &reply))) {
......@@ -131,7 +131,61 @@ _secadm_integriforce_flags_ops(int mode)
}
int
secadm_set_whitelist_mode(int mode)
_secadm_set_tpe_flags_ops(uint32_t flags)
{
secadm_command_t cmd;
secadm_reply_t reply;
int err;
memset(&cmd, 0x00, sizeof(secadm_command_t));
memset(&reply, 0x00, sizeof(secadm_reply_t));
cmd.sc_version = SECADM_VERSION;
cmd.sc_type = secadm_cmd_set_tpe_flags;
cmd.sc_data = &flags;
if ((err = _secadm_sysctl(&cmd, &reply))) {
fprintf(stderr, "secadm_rule_ops. error code: %d\n", err);
}
return (err);
}
int
_secadm_set_tpe_gid_ops(gid_t gid)
{
secadm_command_t cmd;
secadm_reply_t reply;
int err;
memset(&cmd, 0x00, sizeof(secadm_command_t));
memset(&reply, 0x00, sizeof(secadm_reply_t));
cmd.sc_version = SECADM_VERSION;
cmd.sc_type = secadm_cmd_set_tpe_gid;
cmd.sc_data = &gid;
if ((err = _secadm_sysctl(&cmd, &reply))) {
fprintf(stderr, "secadm_rule_ops. error code: %d\n", err);
}
return (err);
}
int
secadm_set_tpe_gid(gid_t gid)
{
return (_secadm_set_tpe_gid_ops(gid));
}
int
secadm_set_tpe_flags(uint32_t flags)
{
return (_secadm_set_tpe_flags_ops(flags));
}
int
secadm_set_integriforce_flags(int mode)
{
return (_secadm_integriforce_flags_ops(mode));
}
......@@ -358,7 +412,7 @@ secadm_get_num_rules(void)
}
int
secadm_get_whitelist_mode(void)
secadm_get_integriforce_flags(void)
{
secadm_command_t cmd;
secadm_reply_t reply;
......@@ -368,7 +422,30 @@ secadm_get_whitelist_mode(void)
memset(&reply, 0x00, sizeof(secadm_reply_t));
cmd.sc_version = SECADM_VERSION;
cmd.sc_type = secadm_cmd_get_whitelist_mode;
cmd.sc_type = secadm_cmd_get_integriforce_flags;
reply.sr_data = &flags;
if ((err = _secadm_sysctl(&cmd, &reply))) {
fprintf(stderr, "unable to get the flags. error code: %d\n", err);
return (0);
}
return (flags);
}
uint32_t
secadm_get_tpe_flags(void)
{
secadm_command_t cmd;
secadm_reply_t reply;
uint32_t flags;
int err;
memset(&cmd, 0x00, sizeof(secadm_command_t));
memset(&reply, 0x00, sizeof(secadm_reply_t));
cmd.sc_version = SECADM_VERSION;
cmd.sc_type = secadm_cmd_get_tpe_flags;
reply.sr_data = &flags;
if ((err = _secadm_sysctl(&cmd, &reply))) {
......@@ -379,6 +456,29 @@ secadm_get_whitelist_mode(void)
return (flags);
}
gid_t
secadm_get_tpe_gid(void)
{
secadm_command_t cmd;
secadm_reply_t reply;
gid_t gid;
int err;
memset(&cmd, 0x00, sizeof(secadm_command_t));
memset(&reply, 0x00, sizeof(secadm_reply_t));
cmd.sc_version = SECADM_VERSION;
cmd.sc_type = secadm_cmd_get_tpe_gid;
reply.sr_data = &gid;
if ((err = _secadm_sysctl(&cmd, &reply))) {
fprintf(stderr, "unable to get the flags. error code: %d\n", err);
return (0);
}
return (gid);
}
void
secadm_free_rule(secadm_rule_t *rule)
{
......
/*-
* Copyright (c) 2014,2015 Shawn Webb <shawn.webb@hardenedbsd.org>
* Copyright (c) 2014,2016 Shawn Webb <shawn.webb@hardenedbsd.org>
* Copyright (c) 2015 Brian Salcedo <brian.salcedo@hardenedbsd.org>
* All rights reserved.
*
......@@ -76,6 +76,11 @@
#define SECADM_INTEGRIFORCE_FLAGS_NONE 0x00000000
#define SECADM_INTEGRIFORCE_FLAGS_WHITELIST 0x00000001
#define SECADM_TPE_DISABLED 0x00000000
#define SECADM_TPE_ENABLED 0x00000001
#define SECADM_TPE_ALL 0x00000002
#define SECADM_TPE_INVERT 0x00000004
#define SECADM_SHA1_DIGEST_LEN 20
#define SECADM_SHA256_DIGEST_LEN 32
......@@ -97,8 +102,12 @@ typedef enum secadm_command_type {
secadm_cmd_get_rule_path,
secadm_cmd_get_rule_hash,
secadm_cmd_get_num_rules,
secadm_cmd_set_whitelist_mode,
secadm_cmd_get_whitelist_mode
secadm_cmd_set_integriforce_flags,
secadm_cmd_get_integriforce_flags,
secadm_cmd_set_tpe_flags,
secadm_cmd_get_tpe_flags,
secadm_cmd_set_tpe_gid,
secadm_cmd_get_tpe_gid
} secadm_command_type_t;
typedef struct secadm_command {
......@@ -218,11 +227,18 @@ secadm_rule_t *secadm_get_rule(int);
size_t secadm_get_num_rules(void);
void secadm_free_rule(secadm_rule_t *);
int secadm_validate_rule(secadm_rule_t *);
int secadm_get_whitelist_mode(void);
int secadm_set_whitelist_mode(int);
int secadm_get_integriforce_flags(void);
int secadm_set_integriforce_flags(int);
int secadm_set_tpe_gid(gid_t);
int secadm_set_tpe_flags(uint32_t);
uint32_t secadm_get_tpe_flags(void);
int secadm_set_tpe_gid(gid_t);
gid_t secadm_get_tpe_gid(void);
#ifdef _KERNEL
struct secadm_prison_entry;
int get_mntonname_vattr(struct thread *, u_char *, char *, struct vattr *);
void kernel_free_rule(secadm_rule_t *);
void kernel_flush_ruleset(int);
......@@ -248,6 +264,8 @@ int secadm_rule_cmp(secadm_rule_t *, secadm_rule_t *);
int do_integriforce_check(secadm_rule_t *, struct vattr *, struct vnode *,
struct ucred *);
int tpe_check(struct image_params *imgp, struct secadm_prison_entry *);
MALLOC_DECLARE(M_SECADM);
RB_HEAD(secadm_rules_tree, secadm_rule);
RB_PROTOTYPE(secadm_rules_tree, secadm_rule, sr_tree, secadm_rule_cmp);
......@@ -285,6 +303,8 @@ typedef struct secadm_prison_entry {
int sp_id;
int sp_integriforce_flags;
struct sx sp_lock;
gid_t sp_tpe_gid;
uint32_t sp_tpe_flags;
SLIST_ENTRY(secadm_prison_entry) sp_entries;
} secadm_prison_entry_t;
......
......@@ -57,6 +57,7 @@ int disable_action(int, char **);
int version_action(int, char **);
int get_action(int, char **);
int set_action(int, char **);
int tpe_action(int, char **);
void free_ruleset(secadm_rule_t *);
......@@ -142,6 +143,12 @@ struct secadm_commands {
"Set various secadm options",
set_action
},
{
"tpe",
"<options>",
"Set various Trusted Path Execution (TPE) options",
tpe_action
},
{
"get",
"<options>",
......@@ -378,6 +385,7 @@ load_action(int argc, char **argv)
ucl_object_iter_t it = NULL;
struct ucl_parser *parser;
int n = 0, err;
int flags;
if (argc < 3) {
usage(1, argv);
......@@ -485,7 +493,13 @@ load_action(int argc, char **argv)
if (validate == 0) {
cur = ucl_lookup_path(top, "secadm.whitelist_mode");
if (cur) {
if (secadm_set_whitelist_mode(ucl_object_toboolean(cur))) {
if (ucl_object_toboolean(cur)) {
flags = SECADM_INTEGRIFORCE_FLAGS_WHITELIST;
} else {
flags = SECADM_INTEGRIFORCE_FLAGS_NONE;
}
if (secadm_set_integriforce_flags(flags)) {
fprintf(stderr, "[-] Could not set whitelist mode\n");
ucl_parser_free(parser);
......@@ -520,7 +534,7 @@ set_action(int argc, char **argv)
switch (ch) {
case 'w':
printf("Unsetting whitelist\n");
if (secadm_set_whitelist_mode(0)) {
if (secadm_set_integriforce_flags(SECADM_INTEGRIFORCE_FLAGS_NONE)) {
fprintf(stderr, "[-] Could not unset whitelist mode\n");
return (1);
}
......@@ -529,7 +543,7 @@ set_action(int argc, char **argv)
case 'W':
printf("Setting whitelist\n");
if (secadm_set_whitelist_mode(1)) {
if (secadm_set_integriforce_flags(SECADM_INTEGRIFORCE_FLAGS_WHITELIST)) {
fprintf(stderr, "[-] Could not set whitelist mode\n");
return (1);
}
......@@ -545,12 +559,86 @@ set_action(int argc, char **argv)
return (0);
}
int
tpe_action(int argc, char **argv)
{
int ch;
unsigned int gid;
uint32_t flags;
flags = secadm_get_tpe_flags();
optind = 2;
while ((ch = getopt(argc, argv, "AITaitg:")) != -1) {
switch (ch) {
case 'A':
flags |= SECADM_TPE_ALL;
printf("TPE: All will be set\n");
break;
case 'a':
flags &= ~(SECADM_TPE_ALL);
printf("TPE: All will be unset\n");
break;
case 'I':
flags |= SECADM_TPE_INVERT;
printf("TPE: Invert will be set\n");
break;
case 'i':
flags &= ~(SECADM_TPE_INVERT);
printf("TPE: Invert will be unset\n");
break;
case 'g':
if (sscanf(optarg, "%u", &gid) != 1) {
fprintf(stderr, "[-] TPE GID must be an integer\n");
return (1);
}
secadm_set_tpe_gid((gid_t)gid);
printf("TPE: GID will be set to: %u\n", gid);
break;
case 'T':
printf("TPE: Enabled will be set\n");
flags |= SECADM_TPE_ENABLED;
break;
case 't':
printf("TPE: Enabled will be unset\n");
flags &= ~(SECADM_TPE_ENABLED);
break;
default:
usage(argc, argv);
return (1);
}
}
if (secadm_set_tpe_flags(flags)) {
fprintf(stderr, "[-] Could not set TPE\n");
return (1);
}
return (0);
}
int
get_action(int argc, char **argv)
{
int flags;
gid_t gid;
flags = secadm_get_whitelist_mode();
flags = secadm_get_integriforce_flags();
if ((flags & SECADM_INTEGRIFORCE_FLAGS_WHITELIST) ==
SECADM_INTEGRIFORCE_FLAGS_WHITELIST) {
printf("Whitelist:\ton\n");
......@@ -558,6 +646,21 @@ get_action(int argc, char **argv)
printf("Whitelist:\toff\n");
}
flags = (int)secadm_get_tpe_flags();
if ((flags & SECADM_TPE_ENABLED)) {
printf("TPE:\t\ton\n");
if (flags & SECADM_TPE_ALL) {