Subversion Repositories general

Compare Revisions

Ignore whitespace Rev 1119 → Rev 1261

/freebsd/mac_settime/trunk/src/mac_settime.4
0,0 → 1,181
.\" 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 AUTHORS 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 AUTHORS 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.
.\"
.\"
.Dd November 4, 2005
.Dt MAC_SETTIME 4
.Os
.Sh NAME
.Nm mac_settime
.Nd "set system time policy"
.Sh SYNOPSIS
To load the set system time policy module at boot time,
place the following line in your kernel configuration file:
.Bd -ragged -offset indent
.Cd "options MAC"
.Ed
.Pp
and place the following line in
.Xr loader.conf 5 :
.Pp
.Dl "mac_settime_load=""YES"""
.Pp
then compile the module and copy it to your kernel modules directory
(e.g. /boot/kernel or /boot/modules)
.Sh DESCRIPTION
The
.Nm
policy allows administrators to define who is allowed to set and adjust system time.
.Pp
In order to use the
.Nm
policy, the
.Va kern.usersettime
and
.Va kern.useradjtime
.Xr sysctl 8
MIBs should be set to 1 to disable kernel security check.
.Pp
If system time has to be changed from jail, additionaly the
.Va kern.jailsettime
and
.Va kern.jailadjtime
.Xr sysctl 8
MIBs should be set to 1.
.Pp
The following
.Xr sysctl 8
MIBs are available for fine-tuning the enforcement of this MAC policy.
All
.Xr sysctl 8
variables, except
.Va security.mac.portacl.rules ,
can also be set as
.Xr loader 8
tunables in
.Xr loader.conf 5 .
.Bl -tag -width indent
.It Va security.mac.settime.enabled
Enforce the
.Nm
policy.
(Default: 1).
.Pp
The MIB ca alse be set as
.Xr loader 8
tunables in
.Xr loader.conf 5 .
.It Va security.mac.settime.rules
The set time access control list is specified as list of rules, separated by semicolon or new line.
Rules are applied in given order, first match wins.
If no match found time setting is denied.
Each rule has the following format:
.Pp
.D1 Ar action Oo not Oc Ar idtype Ar idrange Oo not Oc Ar jailtype Ar jailidrange
.Pp
If some specification (id or jail) is omited it means "any".
The
.Li not
keyword negates the match.
Underscore can be used in place of space.
.Bl -tag -width ".Ar action"
.It Ar action
Describes the result of the rule, either
.Li allow
or
.Li deny .
.It Ar idtype
Describes the type of subject match to be performed.
Either
.Li uid
for user ID matching, or
.Li gid
for group ID matching.
.It Ar idrange : Bro Ar id | id Ns \&- Ns Ar id Ns Brc Ns Op , Ns Ar idrange
The user or group IDs range (depending on
.Ar idtype )
allowed to set system time.
.Bf -emphasis
NOTE: User and group names are not valid; only the actual ID numbers
may be used.
.Ef
.It Ar jailtype
Describes which jail match to be performed.
Either
.Li nojail
for the main system, or
.Li jail
for some jail, id range must be specified.
.It Ar jailidrange : Bro Ar jailid | jailid Ns \&- Ns Ar jailid Ns Brc Ns Op , Ns Ar jailidrange
IDs of jail allowed to set system time.
.Pp
.El
.Bf -emphasis
NOTE: MAC security policies may not override other security system policies
by allowing accesses that they may deny, such as
.Va kern.useradjtime /
.Va kern.jailadjtime /
.Va kern.usersettime /
.Va kern.jailsettime .
.Ef
If the internal kernel security checks are not disabled, the
.Nm
entry will not function
(i.e., even the specified user/group/jail may not be able to set system time).
.Sh EXAMPLES
To allow some user to set system time set
.Va security.mac.settime.rules
.Xr sysctl 8
MIBs to:
.Pp
.Dl "allow uid 2000 nojail"
.Pp
To additionaly allow root to set time from several jails set the
.Va security.mac.settime.rules
to:
.Pp
.Dl "allow uid 2000 nojail"
.Dl "allow uid 0 jail 4,5-9"
.Pp
If the MIB is set from /etc/sysctl.conf no spaces and new lines are allowed by /etc/rc.d/sysctl, so
the last example can be written in another form:
.Pp
.Dl "allow_uid_2000_nojail;allow_uid_0_jail_4,5-9"
.Pp
.Sh SEE ALSO
.Xr mac 3 ,
.Xr ip 4 ,
.Xr mac_biba 4 ,
.Xr mac_bsdextended 4 ,
.Xr mac_ifoff 4 ,
.Xr mac_mls 4 ,
.Xr mac_none 4 ,
.Xr mac_partition 4 ,
.Xr mac_seeotheruids 4 ,
.Xr mac_portacl 4 ,
.Xr mac_test 4 ,
.Xr mac 9
.Sh HISTORY
MAC first appeared in
.Fx 5.0
/freebsd/mac_settime/trunk/src/mac_settime.c
0,0 → 1,525
/*-
* 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.
*
*/
 
/*
* "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,6
.PATH: .
 
KMOD= mac_settime
SRCS= mac_settime.c
 
.include <bsd.kmod.mk>