Subversion Repositories general

Compare Revisions

Ignore whitespace Rev 1110 → Rev 1111

/FreeBSD/mac_settime/trunk/src/module/Makefile.mac_settime
File deleted
/FreeBSD/mac_settime/trunk/src/module/mac_settime.c
File deleted
/FreeBSD/mac_settime/trunk/src/module/Makefile.modules
File deleted
/FreeBSD/mac_settime/trunk/src/module/files
File deleted
/FreeBSD/mac_settime/trunk/src/mac_settime.c
0,0 → 1,526
/*-
* Copyright 2005, Anatoli Klassen <anatoli@aksoft.net>
* 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.
*
* $FreeBSD$
*/
 
/*
* "allow uid 1,2,4-5" - any of specified users in main machine or any jail
* "allow gid 123 jail 1-10" - a group in any of specified jail
* "allow nojail" - any user on main machine
* "deny not jail 3" - any user in jail 3
*
* rules are applied in given order, first match wins;
* if some specification is omit it means "any", last rule is always "deny"
*
* #sysctl security.mac.settime.rules="rule1; rule2; ruleN"
*
* rules := *([:rule:] *([;|\n] *[:rule:] *)*)?
* rule := (allow|deny) +((not)? +(uid|gid) +[:digits-range:])?
* ((not)? +(nojail|(jail +[:digits-range:]))?
* digits-range := (\d+|(\d+-\d+))(,(\d+|(\d+-\d+)))*
* tab and underscore can be used instead of space
*
*/
 
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/libkern.h>
#include <sys/mac.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/mutex.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
#include <sys/mac_policy.h>
 
SYSCTL_DECL(_security_mac);
 
SYSCTL_NODE(_security_mac, OID_AUTO, settime, CTLFLAG_RW, 0,
"mac_settime policy controls");
 
static int mac_settime_enabled = 1;
SYSCTL_INT(_security_mac_settime, OID_AUTO, enabled, CTLFLAG_RW,
&mac_settime_enabled, 0, "Enforce settime policy");
TUNABLE_INT("security.mac.settime.enabled", &mac_settime_enabled);
 
static MALLOC_DEFINE(M_SETTIME, "settime rule", "Rules for mac_settime");
 
#define MAC_RULE_STRING_LEN 10240
 
#define RULE_NONE 0
#define RULE_UID 1
#define RULE_GID 2
#define RULE_NOJAIL 1
#define RULE_JAIL 2
 
struct rule {
int allow; /* 0 == "deny", 1 == "allow" */
int id_not; /* 0 == "", 1 == "not" */
int id_type; /* 0 == "" - no check, RULE_UID == "uid", RULE_GID == "gid" */
int id_count; /* number of id ranges */
int *id; /* uid or gid ranges */
int jail_not; /* 0 == "", 1 == "not" */
int jail_type; /* 0 == "" - no check, RULE_NOJAIL == "nojail",
RULE_JAIL == "jail" */
int jailid_count; /* number of jail id ranges */
int *jailid; /* jail id ranges */
 
TAILQ_ENTRY(rule) r_entries;
};
 
#define ALLOW_STRING "allow"
#define DENY_STRING "deny"
#define NOT_STRING "not"
#define GID_STRING "gid"
#define UID_STRING "uid"
#define NOJAIL_STRING "nojail"
#define JAIL_STRING "jail"
 
static struct mtx rule_mtx;
static TAILQ_HEAD(rulehead, rule) rule_head;
static char rule_string[MAC_RULE_STRING_LEN];
 
static void
delete_rules(struct rulehead *head)
{
struct rule *rule;
 
while ((rule = TAILQ_FIRST(head)) != NULL) {
TAILQ_REMOVE(head, rule, r_entries);
free(rule->id, M_SETTIME);
free(rule->jailid, M_SETTIME);
free(rule, M_SETTIME);
}
}
 
static void
destroy(struct mac_policy_conf *mpc)
{
 
mtx_destroy(&rule_mtx);
delete_rules(&rule_head);
}
 
static void
init(struct mac_policy_conf *mpc)
{
 
mtx_init(&rule_mtx, "rule_mtx", NULL, MTX_DEF);
TAILQ_INIT(&rule_head);
}
 
static int
parse_range_step(char *token, int *count, int **array)
{
long n1, n2;
int error;
int end;
 
error = 0;
end = 0;
*count = 0;
while (!end && !error) {
/* not start with digit - to avoid +/- recognition */
if (*token < '0' || *token > '9') {
error = EINVAL;
break;
}
 
n1 = strtol(token, &token, 10);
switch (*token) {
case '\0': /* end of string */
n2 = n1;
end = 1;
break;
case ',': /* single value */
token++;
n2 = n1;
break;
case '-': /* interval */
token++;
/* not start with digit */
if (*token < '0' || *token > '9')
error = EINVAL;
else {
n2 = strtol(token, &token, 10);
switch (*token) {
case '\0': /* end of string */
end = 1;
break;
case ',': /* continue */
token++;
break;
default:
error = EINVAL;
}
}
break;
default:
error = EINVAL;
}
 
if (!error) {
if (array != NULL) {
(*array)[*count * 2] = (int)n1;
(*array)[*count * 2 + 1] = (int)n2;
}
(*count)++;
}
}
 
return (error);
}
 
static int
parse_range(char *token, int *count, int **array)
{
int error;
 
/* parse first time to determine number of intervals */
error = parse_range_step(token, count, NULL);
if (error) {
*array = NULL;
return (error);
}
 
*array = malloc(*count * 2 * sizeof(int), M_SETTIME, M_WAITOK);
 
/* parse once again - to fill the array */
error = parse_range_step(token, count, array);
 
if (error) {
free(*array, M_SETTIME);
*array = NULL;
}
 
return (error);
}
 
static int
parse_rule(char *s, struct rule **rule)
{
enum parse_state {
STATE_BEFORE_ACTION,
STATE_AFTER_ACTION,
STATE_AFTER_NOT1,
STATE_AFTER_IDTYPE,
STATE_AFTER_ID,
STATE_AFTER_NOT2,
STATE_AFTER_JAIL,
STATE_END
};
 
int error;
int not;
char *token;
struct rule *r;
enum parse_state state;
 
r = malloc(sizeof(*r), M_SETTIME, M_ZERO | M_WAITOK);
r->id = NULL; /* some arch where (pointer)0 != (binary)0 ? */
r->jailid = NULL;
 
error = 0;
not = 0;
state = STATE_BEFORE_ACTION;
 
while (!error && (token = strsep(&s, " \t_")) != NULL) {
if (strlen(token) == 0)
continue;
 
switch(state) {
case STATE_BEFORE_ACTION:
not = 0;
if (strcmp(token, ALLOW_STRING) == 0) {
r->allow = 1;
state = STATE_AFTER_ACTION;
} else if (strcmp(token, DENY_STRING) == 0) {
r->allow = 0;
state = STATE_AFTER_ACTION;
} else
error = EINVAL;
break;
 
case STATE_AFTER_ACTION:
if (strcmp(token, NOT_STRING) == 0) {
state = STATE_AFTER_NOT1;
not = 1;
break;
}
/* FALLTHROUGH */
 
case STATE_AFTER_NOT1:
if (strcmp(token, UID_STRING) == 0) {
r->id_type = RULE_UID;
r->id_not = not;
state = STATE_AFTER_IDTYPE;
break;
} else if (strcmp(token, GID_STRING) == 0) {
r->id_type = RULE_GID;
r->id_not = not;
state = STATE_AFTER_IDTYPE;
break;
}
/* FALLTHROUGH */
 
/* attention: order of cases doesn't match order of tokens */
case STATE_AFTER_ID:
if (strcmp(token, NOT_STRING) == 0) {
if (STATE_AFTER_NOT1 == state)
error = EINVAL; /* double 'not' */
else {
state = STATE_AFTER_NOT2;
not = 1;
}
break;
}
/* FALLTHROUGH */
 
case STATE_AFTER_NOT2:
if (strcmp(token, NOJAIL_STRING) == 0) {
r->jail_type = RULE_NOJAIL;
r->jail_not = not;
state = STATE_END;
} else if(strcmp(token, JAIL_STRING) == 0) {
r->jail_type = RULE_JAIL;
r->jail_not = not;
state = STATE_AFTER_JAIL;
} else
error = EINVAL;
break;
 
case STATE_AFTER_IDTYPE:
error = parse_range(token, &(r->id_count), &(r->id));
state = STATE_AFTER_ID;
break;
 
case STATE_AFTER_JAIL:
error = parse_range(token, &(r->jailid_count), &(r->jailid));
state = STATE_END;
break;
 
case STATE_END:
error = EINVAL; /* something after end of rule */
break;
}
}
 
/* check for unexpected end of rule */
if (STATE_BEFORE_ACTION != state && STATE_AFTER_ACTION != state &&
STATE_END != state && STATE_AFTER_ID != state)
error = EINVAL;
 
if (error || STATE_BEFORE_ACTION == state) { /* error or spaces only */
free(r, M_SETTIME);
*rule = NULL;
}
else
*rule = r;
 
return (error);
}
 
/* this will destroy the given string */
static int
parse_rules(char *s, struct rulehead *head)
{
int error;
char *token;
struct rule *r;
 
error = 0;
while (!error && (token = strsep(&s, ";\n")) != NULL) {
if (strlen(token) == 0)
continue;
 
error = parse_rule(token, &r);
if (error)
break;
 
if (r != NULL)
TAILQ_INSERT_TAIL(head, r, r_entries);
}
 
if (error)
delete_rules(head);
 
return (error);
}
 
/*
* Note: due to races, there is not a single serializable order
* between parallel calls to the sysctl.
*/
static int
sysctl_rules(SYSCTL_HANDLER_ARGS)
{
char *string, *copy_string, *new_string;
struct rulehead head, save_head;
int error;
 
new_string = NULL;
if (req->newptr == NULL) {
new_string = malloc(MAC_RULE_STRING_LEN, M_SETTIME,
M_WAITOK | M_ZERO);
strcpy(new_string, rule_string);
string = new_string;
} else
string = rule_string;
 
error = sysctl_handle_string(oidp, string, MAC_RULE_STRING_LEN, req);
if (error)
goto out;
 
if (req->newptr != NULL) {
copy_string = strdup(string, M_SETTIME);
TAILQ_INIT(&head);
error = parse_rules(copy_string, &head);
free(copy_string, M_SETTIME);
if (error)
goto out;
 
TAILQ_INIT(&save_head);
mtx_lock(&rule_mtx);
TAILQ_CONCAT(&save_head, &rule_head, r_entries);
TAILQ_CONCAT(&rule_head, &head, r_entries);
strcpy(rule_string, string);
mtx_unlock(&rule_mtx);
delete_rules(&save_head);
}
out:
if (new_string != NULL)
free(new_string, M_SETTIME);
return (error);
}
 
SYSCTL_PROC(_security_mac_settime, OID_AUTO, rules,
CTLTYPE_STRING|CTLFLAG_RW, 0, 0, sysctl_rules, "A", "Rules");
 
static int
range_match(int value, int ranges_count, int *ranges, int not)
{
int i, match;
 
match = 0;
for (i = 0; i < ranges_count; i++)
if (ranges[i * 2] <= value && value <= ranges[i * 2 + 1]) {
match = 1;
break;
}
 
if (not)
match = !match;
 
return (match);
}
 
static int
rule_match(struct ucred *cred, struct rule *r)
{
int match_id, match_jail;
 
switch (r->id_type) {
case RULE_NONE:
match_id = 1;
break;
case RULE_UID:
match_id = range_match(cred->cr_uid, r->id_count, r->id, r->id_not);
break;
case RULE_GID:
/* FIXME implement for other groups */
match_id = range_match(cred->cr_gid, r->id_count, r->id, r->id_not);
break;
default:
printf("Unknown rule id type: %d\n", r->id_type);
match_id = 0;
}
 
switch(r->jail_type) {
case RULE_NONE:
match_jail = 1;
break;
case RULE_NOJAIL:
match_jail = !jailed(cred);
break;
case RULE_JAIL:
if (cred->cr_prison == NULL)
match_jail = 0;
else
match_jail = range_match(cred->cr_prison->pr_id,
r->jailid_count, r->jailid, r->jail_not);
break;
default:
printf("Unknown rule jail type: %d\n", r->jail_type);
match_jail = 0;
}
 
return (match_id && match_jail);
}
 
static int
rules_check(struct ucred *cred)
{
struct rule *rule;
int error;
 
error = EPERM;
 
mtx_lock(&rule_mtx);
for (rule = TAILQ_FIRST(&rule_head);
rule != NULL;
rule = TAILQ_NEXT(rule, r_entries)) {
if (rule_match(cred, rule)) {
if (rule->allow)
error = 0;
break;
}
}
mtx_unlock(&rule_mtx);
 
return (error);
}
 
static int
check_system_settime(struct ucred *cred)
{
if (mac_settime_enabled == 0)
return (0);
 
return (rules_check(cred));
}
 
static struct mac_policy_ops mac_settime_ops =
{
.mpo_destroy = destroy,
.mpo_init = init,
.mpo_check_system_settime = check_system_settime,
};
 
MAC_POLICY_SET(&mac_settime_ops, mac_settime,
"MAC/settime", MPC_LOADTIME_FLAG_UNLOADOK, NULL);
 
/FreeBSD/mac_settime/trunk/src/Makefile
0,0 → 1,8
# $FreeBSD$
 
.PATH: ${.CURDIR}/../../security/mac_settime
 
KMOD= mac_settime
SRCS= mac_settime.c
 
.include <bsd.kmod.mk>