lkml.org 
[lkml]   [2003]   [Aug]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Subject[RFC/PATCH] CKRM rule-based classification engine (RBCE)
From
Date
Module implementing a Rule-based classification Engine (RBCE)  for use
with Class-based Kernel Resource Management.

Requires ckcore-A01-2.6.0-test2 patch posted earlier.
To define classification rules, classes and policies, you also need
userspace
libraries and a control program which can be downloaded from

http://ckrm.sourceforge.net/downloads/rbce-A01.tar.gz

For more details, please refer to
http://ckrm.sf.net

and earlier postings on lkml.

-- Shailabh

-----------------------------------------------------------------------------------
Makefile
-----------------------------------------------------------------------------------
DIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
# KDIR is the CKRM enabled Linux kernel src tree
KDIR := /usr/src/linux

obj-m := rbcemod.o

rbcemem-objs := rbcemod.o

default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
rm -f *.o *.ko *~ core

-----------------------------------------------------------------------------------
rbcemod.mod.c
-----------------------------------------------------------------------------------

#include <linux/module.h>
#include <linux/vermagic.h>
#include <linux/compiler.h>

MODULE_INFO(vermagic, VERMAGIC_STRING);

static const char __module_depends[]
__attribute_used__
__attribute__((section(".modinfo"))) =
"depends=";


-----------------------------------------------------------------------------------
rbcemod.c
-----------------------------------------------------------------------------------
/* Rule-based Classification Engine (RBCE) module
*
* Copyright (C) Hubertus Franke, IBM Corp. 2003
* (C) Chandra Seetharaman, IBM Corp. 2003
*
* Module for loading of classification policies and providing
* a user API for Class-based Kernel Resource Management (CKRM)
*
* Latest version, more details at http://ckrm.sf.net
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/

/* Changes
*
* 28 Aug 2003
* Created. First cut with much scope for cleanup !
*/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/ckrm.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>

#include "../include/rbce.h"


MODULE_AUTHOR("Hubertus Franke, Chandra Seetharaman (IBM)");
MODULE_DESCRIPTION("Rule Based Classification Engine Module for CKRM");
MODULE_LICENSE("GPL");

static char modname[] = "rbce";
static int rbce_major = 179; // driver's assigned major number

struct named_obj_hdr {
struct list_head link;
rbce_id_t id;
char name[RBCE_MAX_NAME_LEN];
};

struct rbce_class {
struct named_obj_hdr obj;

struct ckrm_cpu_class *cpu_class;
struct ckrm_mem_class *mem_class;
struct ckrm_net_class *net_class;
struct ckrm_io_class *io_class;
};

struct rbce_rule_term {
rbce_rule_op_t op;
union {
char cmd[RBCE_MAX_COMMAND_LEN];
char tag[RBCE_MAX_TAG_LEN];
long id;
struct rbce_rule *deprule;
};
};

struct rbce_rule {
struct named_obj_hdr obj;
struct rbce_class *target_class;
int num_terms;
struct rbce_rule_term *terms;
};

#define RBCE_RULE_NAME 1
#define RBCE_RULE_ID 2

struct rbce_policy {
struct named_obj_hdr obj;
rbce_policy_type_t type;
struct rbce_class *def_class;
rwlock_t rwlock;
struct list_head classes;
struct list_head rules;
};

void rbce_classify(struct task_struct *);


extern int errno;

int rbcedebug = 1;
#define cprintk(lvl,x) if ((lvl) <= rbcedebug) printk x

// /proc stuff for debugging

static ssize_t
my_write(struct file *file, const char *buf, size_t count, loff_t *p)
{
if (count) {
char c;

if (get_user(c, buf)) {
return -EFAULT;
}
if (c < '0' || c > '9') {
return -EINVAL;
}
rbcedebug = c - '0';
}
return count;
}

static int
my_read(struct file *file, char *ubuf, size_t size, loff_t *ppos)
{
int len;
char kbuf[10];

if (size < 5) {
return -ENOMEM;
}
len = sprintf(kbuf, "%d\n", rbcedebug);
if (copy_to_user(ubuf, kbuf, len+1)) {
return -EFAULT;
}
*ppos += len;
return len;
}

static struct file_operations my_proc_operations = {
.read = my_read,
.write = my_write,
};

// Forward declarations

static void reclassify_tasks(struct rbce_class *);
static void rbce_classify_l(struct task_struct *);

/*-----------------------------------------------------------------------*/
rwlock_t global_rwlock = RW_LOCK_UNLOCKED;
/* This lock protects "policies" list and cur_policy.
* Anybody that needs to access any policy in the "policies" list
* or cur_policy have to get this lock in read mode and then get the
* (reader/writer depending on the operation) lock on that specific
* policy. After getting the policy's lock this global lock can be
* dropped.
*
* Insertions of policy to the "policies" list and changing cur_policy
* must be done with this lock held in write mode.
*
* Both the locks must be held(read or write) while deferencing the
* class pointer in the task data structure.
*/
LIST_HEAD(policies);

static struct rbce_policy default_policy = {
.obj = {
.link = LIST_HEAD_INIT(default_policy.obj.link),
.id = (rbce_id_t)&default_policy,
.name = "default policy"
},
.type = RBCE_POLICY_MONITOR,
.rwlock = RW_LOCK_UNLOCKED,
.classes = LIST_HEAD_INIT(default_policy.classes),
.rules = LIST_HEAD_INIT(default_policy.rules)
};

static struct rbce_class default_class = {
.obj = {
.link = LIST_HEAD_INIT(default_policy.obj.link),
.id = (rbce_id_t)&default_class,
.name = "default class"
},
};

static struct rbce_policy *cur_policy = &default_policy;

inline static rbce_id_t getid(void *obj) { return (rbce_id_t) obj; }

static struct named_obj_hdr*
find_named_obj(struct list_head *lst, char *name)
{
struct named_obj_hdr *pos;

list_for_each_entry(pos, lst, link) {
if (!strcmp(pos->name, name))
return pos;
}
return NULL;
}

static struct named_obj_hdr*
find_id_obj(struct list_head *lst, rbce_id_t id)
{
struct named_obj_hdr *pos;

list_for_each_entry(pos, lst, link) {
if (pos->id == id)
return pos;
}
return NULL;
}

static inline struct rbce_policy*
find_policy_name(char *name)
{
return (struct rbce_policy*) find_named_obj(&policies, name);
}

static inline struct rbce_policy*
find_policy_id(rbce_id_t id)
{
return (struct rbce_policy*) find_id_obj(&policies, id);
}

static inline struct rbce_class*
find_class_name(struct rbce_policy *policy, char *name)
{
return (struct rbce_class*) find_named_obj(&policy->classes, name);
}

static inline struct rbce_class*
find_class_id(struct rbce_policy *policy, rbce_id_t id)
{
return (struct rbce_class*) find_id_obj(&policy->classes, id);
}

static inline struct rbce_rule*
find_rule_name(struct rbce_policy *policy, char *name)
{
return (struct rbce_rule*) find_named_obj(&policy->rules, name);
}

static inline struct rbce_rule*
find_rule_id(struct rbce_policy *policy, rbce_id_t id)
{
return (struct rbce_rule*) find_id_obj(&policy->rules, id);
}

static rbce_id_t
create_policy(struct rbce_policy_descr *udescr)
{
rbce_id_t polid = 0, classid;
struct rbce_policy_descr kdescr;
struct rbce_policy *policy;
struct rbce_class *newcls;
static int firsttime = 1;

if (firsttime) {
struct proc_dir_entry *entry;

firsttime = 0;
entry = create_proc_entry("rbce_debug", S_IWUGO|S_IRUGO, NULL);
if (entry) {
entry->proc_fops = &my_proc_operations;
}
}

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_policy_descr))) {
return (rbce_id_t)-EFAULT;
}

if (!strlen(kdescr.name) || ((kdescr.type != RBCE_POLICY_MONITOR) &&
(kdescr.type != RBCE_POLICY_MANAGE))) {
return (rbce_id_t)-EINVAL;
}

write_lock(&global_rwlock);
policy = find_policy_name(kdescr.name);
if (policy) {
polid = (rbce_id_t)-EINVAL;
goto out;
}
policy = kmalloc(sizeof(struct rbce_policy), GFP_KERNEL);
if (!policy) {
polid = (rbce_id_t)-ENOMEM;
goto out;
}
polid = getid(policy);
policy->obj.id = polid;
strcpy(policy->obj.name, kdescr.name);
policy->type = kdescr.type;
rwlock_init(&policy->rwlock);
INIT_LIST_HEAD(&policy->classes);
INIT_LIST_HEAD(&policy->rules);
list_add_tail(&policy->obj.link, &policies);

// Create the default class.
newcls = kmalloc(sizeof(struct rbce_class), GFP_KERNEL);
if (!newcls) {
polid = (rbce_id_t)(-ENOMEM);
goto out_free;
}
classid = getid(newcls);
policy->def_class = newcls;
newcls->obj.id = classid;
strcpy(newcls->obj.name, "policy_default_class");
newcls->cpu_class = ckrm_dflt_cpu_class(&default_class);
newcls->mem_class = ckrm_dflt_mem_class(&default_class);
newcls->net_class = ckrm_dflt_net_class(&default_class);
newcls->io_class = ckrm_dflt_io_class(&default_class);

list_add_tail(&newcls->obj.link, &policy->classes);
goto out;

out_free:
kfree(policy);

out:
write_unlock(&global_rwlock);
cprintk(3,("create_policy polid %ld\n", polid));
return polid;
}

static int
commit_policy(struct rbce_policy_descr *udescr)
{
struct rbce_policy_descr kdescr;
struct rbce_policy *policy, *old_policy;
struct rbce_class *cls;
int rc = 0;

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_policy_descr))) {
return -EFAULT;
}

if (kdescr.policy_id <= 0) {
return (rbce_id_t)-EINVAL;
}

write_lock(&global_rwlock);
policy = find_policy_id(kdescr.policy_id);
if (!policy) {
rc = -EINVAL;
goto out;
}

if (policy == cur_policy) {
rc = -EBUSY;
goto out;
}

// Get the cur_policy's write lock to protect the current readers.
write_lock(&cur_policy->rwlock);

// Reclassify all tasks in all classes in the old policy.
// Strictly speaking, there should be no tasks in the class.... we
// should ASSERT this state instead of reclassifying the tasks.
list_for_each_entry(cls, &cur_policy->classes, obj.link) {
reclassify_tasks(cls);
}

// XXX Need to clear the resource information in the classes of
"policy"
// before dropping the policy's rwlock.
write_unlock(&cur_policy->rwlock);

old_policy = cur_policy;
cur_policy = policy;

out:
write_unlock(&global_rwlock);
return rc;
}

static int
decommit_policy(struct rbce_policy_descr *udescr)
{
struct rbce_policy_descr kdescr;
struct rbce_policy *policy;
struct rbce_class *cls;
int rc = 0;

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_policy_descr))) {
return -EFAULT;
}

if (kdescr.policy_id <= 0) {
return (rbce_id_t)-EINVAL;
}

write_lock(&global_rwlock);
policy = find_policy_id(kdescr.policy_id);
if (!policy || policy != cur_policy) {
rc = -EINVAL;
goto out;
}
if (policy == &default_policy) {// paranoia
goto out;
}

// Get the cur_policy's write lock to protect the current readers.
write_lock(&cur_policy->rwlock);
cur_policy = &default_policy;

// Reclassify all tasks in all classes in the decommited policy.
list_for_each_entry(cls, &policy->classes, obj.link) {
reclassify_tasks(cls);
}
write_unlock(&cur_policy->rwlock);

out:
write_unlock(&global_rwlock);
return rc;
}

static int
delete_policy(struct rbce_policy_descr *udescr)
{
struct rbce_policy_descr kdescr;
struct rbce_policy *policy;
struct list_head *lh1, *lh2;
struct rbce_rule *rule;

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_policy_descr))) {
return -EFAULT;
}

if (kdescr.policy_id <= 0) {
return (rbce_id_t)-EINVAL;
}

write_lock(&global_rwlock);
policy = find_policy_id(kdescr.policy_id);
if (!policy || policy == cur_policy) {
write_unlock(&global_rwlock);
return (rbce_id_t)-EINVAL;
}

// Get the policy's write lock to protect the current readers, if any
write_lock(&policy->rwlock);

list_del(&policy->obj.link);
write_unlock(&policy->rwlock);
write_unlock(&global_rwlock);

// delete the rules
list_for_each_safe(lh1, lh2, &policy->rules) {
list_del(lh1);
rule = (struct rbce_rule *) lh1;
kfree(rule->terms);
kfree(rule);
}

// delete the classses
list_for_each_safe(lh1, lh2, &policy->classes) {
list_del(lh1);
kfree(lh1);
}

// delete the policy
kfree(policy);

return 0;
}

static int
set_policy_type(struct rbce_policy_descr *udescr)
{
struct rbce_policy_descr kdescr;
struct rbce_policy *policy;
int rc = -EINVAL;

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_policy_descr))) {
return -EFAULT;
}

if (kdescr.policy_id <= 0) {
return (rbce_id_t)-EINVAL;
}

if (kdescr.type != RBCE_POLICY_MONITOR &&
kdescr.type != RBCE_POLICY_MANAGE) {
return rc;
}

read_lock(&global_rwlock);
policy = find_policy_id(kdescr.policy_id);
if (policy && policy != &default_policy) {
// can't change type of default policy.
write_lock(&policy->rwlock);
read_unlock(&global_rwlock);
policy->type = kdescr.type;
write_unlock(&policy->rwlock);
rc = 0;
} else {
read_unlock(&global_rwlock);
}

return rc;
}

static int
get_policy_type(struct rbce_policy_descr *udescr)
{
struct rbce_policy_descr kdescr;
struct rbce_policy *policy;
int rc = -EINVAL;

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_policy_descr))) {
return -EFAULT;
}

if (kdescr.policy_id <= 0) {
return rc;
}

read_lock(&global_rwlock);
policy = find_policy_id(kdescr.policy_id);
if (policy) {
read_lock(&policy->rwlock);
rc = policy->type;
read_unlock(&policy->rwlock);
}
read_unlock(&global_rwlock);

return rc;
}

static rbce_id_t
get_policy_byname(struct rbce_policy_descr *udescr)
{
struct rbce_policy_descr kdescr;
struct rbce_policy *policy;
int rc = -EINVAL;

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_policy_descr))) {
return -EFAULT;
}
read_lock(&global_rwlock);
policy = find_policy_name(kdescr.name);
if (policy) {
read_lock(&policy->rwlock);
rc = getid(policy);
read_unlock(&policy->rwlock);
}
read_unlock(&global_rwlock);
return rc;
}

static rbce_id_t
create_class(struct rbce_class_descr *udescr)
{
struct rbce_class_descr kdescr;
struct rbce_policy *policy;
struct rbce_class *newcls = NULL;
rbce_id_t classid;

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_class_descr))) {
return -EFAULT;
}

read_lock(&global_rwlock);
policy = find_policy_id(kdescr.policy_id);
if (policy == NULL) {
read_unlock(&global_rwlock);
classid = (rbce_id_t)(-EINVAL);
goto out;
}
write_lock(&policy->rwlock);
read_unlock(&global_rwlock);

if (find_class_name(policy, kdescr.name)) {
classid = (rbce_id_t)(-EINVAL);
goto out_unlock;
}

newcls = kmalloc(sizeof(struct rbce_class), GFP_KERNEL);
if (!newcls) {
classid = (rbce_id_t)(-ENOMEM);
goto out_unlock;
}
classid = getid(newcls);
newcls->obj.id = classid;
strncpy(newcls->obj.name, kdescr.name,RBCE_MAX_NAME_LEN);
list_add_tail(&newcls->obj.link, &policy->classes);

newcls->cpu_class = ckrm_alloc_cpu_class(newcls);
newcls->mem_class = ckrm_alloc_mem_class(newcls);
newcls->net_class = ckrm_alloc_net_class(newcls);
newcls->io_class = ckrm_alloc_io_class(newcls);

out_unlock:
write_unlock(&policy->rwlock);
out:
cprintk(3,("create_class classid %ld\n", classid));
return classid;
}

static int
delete_class(struct rbce_delete_class_descr *udescr)
{
struct rbce_delete_class_descr kdescr;
struct rbce_class *cls;
struct rbce_rule *rule;
struct rbce_policy *policy;
int rc = 0;

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_delete_class_descr))) {
return -EFAULT;
}

read_lock(&global_rwlock);
policy = find_policy_id(kdescr.policy_id);
if (policy == NULL) {
read_unlock(&global_rwlock);
return -EINVAL;
}
write_lock(&policy->rwlock);

if ((cls = find_class_id(policy, kdescr.class_id)) != NULL) {
// Make sure that there are no rules ending in this class.
list_for_each_entry(rule, &policy->rules, obj.link) {
if (cls == rule->target_class) {
rc = -EINVAL;
goto out_unlock;
}
}
list_del(&cls->obj.link);
reclassify_tasks(cls);
ckrm_free_cpu_class(cls->cpu_class);
ckrm_free_mem_class(cls->mem_class);
ckrm_free_net_class(cls->net_class);
ckrm_free_io_class(cls->io_class);
} else {
rc = -EINVAL;
}

out_unlock:
write_unlock(&policy->rwlock);
read_unlock(&global_rwlock);
return rc;
}

static rbce_id_t
get_class_byname(struct rbce_class_descr *udescr)
{
struct rbce_class_descr kdescr;
struct rbce_policy *policy;
struct rbce_class *cls;
rbce_id_t classid = -EINVAL;

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_class_descr))) {
return (rbce_id_t)-EFAULT;
}

read_lock(&global_rwlock);
policy = find_policy_id(kdescr.policy_id);
if (policy == NULL) {
read_unlock(&global_rwlock);
return classid;
}
read_lock(&policy->rwlock);
read_unlock(&global_rwlock);

if ((cls = find_class_name(policy, kdescr.name)) != NULL) {
classid = getid(cls);
}
read_unlock(&policy->rwlock);

return classid;
}

static rbce_id_t
create_rule(struct rbce_rule_descr *udescr)
{
struct rbce_rule_term_descr *kterms = NULL;
struct rbce_rule_descr kdescr;
struct rbce_rule *newrule = NULL;
struct rbce_rule_term *terms = NULL;
struct rbce_class *targetcls;
struct rbce_policy *policy;
int i;
rbce_id_t ruleid;
struct list_head *after = NULL;

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_rule_descr))) {
return (rbce_id_t)-EFAULT;
}

kterms = kmalloc(kdescr.num_terms *
sizeof(struct rbce_rule_term_descr), GFP_KERNEL);
if (!kterms) {
return (rbce_id_t)-ENOMEM;
}

if (copy_from_user(kterms, kdescr.terms,
sizeof(struct rbce_rule_term_descr) * kdescr.num_terms)) {
kfree(kterms);
return (rbce_id_t)-EFAULT;
}

terms = kmalloc(kdescr.num_terms * sizeof(struct rbce_rule_term),
GFP_KERNEL);
if (!terms) {
kfree(kterms);
return (rbce_id_t)-ENOMEM;
}

read_lock(&global_rwlock);
ruleid = (rbce_id_t)(-EINVAL);

/* find policy and target class and verify all rules */
if ((policy = find_policy_id(kdescr.policy_id)) == NULL ||
(targetcls = (struct rbce_class*)
find_id_obj(&policy->classes, kdescr.class_id)) == NULL) {
read_unlock(&global_rwlock);
goto out_free;
}
write_lock(&policy->rwlock);
read_unlock(&global_rwlock);

for (i = 0; i < kdescr.num_terms; i++) {
switch(kterms[i].op) {
case RBCE_RULE_CMD_PATH:
case RBCE_RULE_CMD:
strcpy(terms[i].cmd, kterms[i].ruledata.cmd);
break;
case RBCE_RULE_REAL_UID:
case RBCE_RULE_REAL_GID:
case RBCE_RULE_EFFECTIVE_UID:
case RBCE_RULE_EFFECTIVE_GID:
terms[i].id = kterms[i].ruledata.id;
break;
case RBCE_RULE_APP_TAG:
strcpy(terms[i].tag, kterms[i].ruledata.tag);
break;
case RBCE_RULE_DEP_RULE:
terms[i].deprule = find_rule_id(policy, kterms[i].ruledata.dep_rule);
if (i != 0 || terms[i].deprule == NULL) {
//dependent rule must be the first term and must exist
goto out_unlock;
}
break;
default:
goto out_unlock;
}
terms[i].op = kterms[i].op;
}

newrule = kmalloc(sizeof(struct rbce_rule), GFP_KERNEL);
if (!newrule) {
ruleid = (rbce_id_t)-ENOMEM;
goto out_unlock;
}

ruleid = getid(newrule);
newrule->obj.id = ruleid;
strcpy(newrule->obj.name, kdescr.name);
newrule->target_class = targetcls;
newrule->num_terms = kdescr.num_terms;
newrule->terms = terms;
switch(kdescr.insert_after) {
case 0:
// Add at the head.
list_add(&newrule->obj.link, &policy->rules);
break;
default:
// Add after the specified rule.
after = (struct list_head *)
find_rule_id(policy, kdescr.insert_after);
if (!after) {
goto out_unlock;
}
list_add(&newrule->obj.link, after);
break;
case -1:
// Add at the tail.
list_add_tail(&newrule->obj.link, &policy->rules);

}
write_unlock(&policy->rwlock);
kfree(kterms);
goto out;

out_unlock:
write_unlock(&policy->rwlock);
out_free:
if (newrule)
kfree(newrule);
if (kterms)
kfree(kterms);
if (terms)
kfree(terms);
out:
cprintk(3,("create_rule ruleid %ld\n", ruleid));
return ruleid;
}

static rbce_id_t
change_rule(struct rbce_rule_descr *udescr)
{
struct rbce_rule_term_descr *kterms = NULL;
struct rbce_rule_descr kdescr;
struct rbce_rule *rule;
struct rbce_rule_term *terms = NULL;
struct rbce_class *targetcls;
struct rbce_policy *policy;
int i;
rbce_id_t ruleid;
struct list_head *after = NULL;

kterms = kmalloc(kdescr.num_terms *
sizeof(struct rbce_rule_term_descr), GFP_KERNEL);
if (!kterms) {
return (rbce_id_t)-ENOMEM;
}

if (copy_from_user(&kterms, kdescr.terms,
sizeof(struct rbce_rule_term_descr) * kdescr.num_terms)) {
kfree(kterms);
return (rbce_id_t)-EFAULT;
}

terms = kmalloc(kdescr.num_terms * sizeof(struct rbce_rule_term),
GFP_KERNEL);
if (!terms) {
kfree(kterms);
return (rbce_id_t)-ENOMEM;
}

ruleid = (rbce_id_t)(-EINVAL);

/* find policy and target class and verify all rules */
read_lock(&global_rwlock);
if ((policy = find_policy_id(kdescr.policy_id)) == NULL ||
(targetcls = (struct rbce_class*)
find_id_obj(&policy->classes, kdescr.class_id)) == NULL ||
(rule = find_rule_id(policy, kdescr.rule_id)) == NULL) {
read_unlock(&global_rwlock);
goto out_free;
}
write_lock(&policy->rwlock);
read_unlock(&global_rwlock);

for (i = 0; i < kdescr.num_terms; i++) {
switch(kterms[i].op) {
case RBCE_RULE_CMD_PATH:
case RBCE_RULE_CMD:
strcpy(terms[i].cmd, kterms[i].ruledata.cmd);
break;
case RBCE_RULE_REAL_UID:
case RBCE_RULE_REAL_GID:
case RBCE_RULE_EFFECTIVE_UID:
case RBCE_RULE_EFFECTIVE_GID:
terms[i].id = kterms[i].ruledata.id;
break;
case RBCE_RULE_APP_TAG:
strcpy(terms[i].tag, kterms[i].ruledata.tag);
break;
case RBCE_RULE_DEP_RULE:
terms[i].deprule = find_rule_id(policy, kterms[i].ruledata.dep_rule);
if (i != 0 || terms[i].deprule == NULL) {
// dependent rule must be the first term
// that rule must exist
goto out_unlock;
}
break;
default:
goto out_unlock;
}
terms[i].op = kterms[i].op;
}

// Free the old terms.
list_del(&rule->obj.link);
kfree(rule->terms);

switch(kdescr.insert_after) {
case 0:
// Add at the head.
list_add(&rule->obj.link, &policy->rules);
break;
default:
// Add after the specified rule.
after = (struct list_head *)
find_rule_id(policy, kdescr.insert_after);
if (!after) {
// XXX need to put back the original rule in this case.
goto out_unlock;
}
list_add(&rule->obj.link, after);
break;
case -1:
// Add at the tail.
list_add_tail(&rule->obj.link, &policy->rules);

}
ruleid = getid(rule);
rule->obj.id = ruleid;
rule->target_class = targetcls;
rule->num_terms = kdescr.num_terms;
rule->terms = terms;
write_unlock(&policy->rwlock);
kfree(kterms);
goto out;

out_unlock:
write_unlock(&policy->rwlock);
out_free:
kfree(kterms);
kfree(terms);
out:
return ruleid;
}

static rbce_id_t
delete_rule(struct rbce_delete_rule_descr *udescr)
{
struct rbce_delete_rule_descr kdescr;
struct rbce_policy *policy;
struct rbce_rule *rule;

if (copy_from_user(&kdescr, udescr,
sizeof(struct rbce_delete_rule_descr))) {
return (rbce_id_t)-EFAULT;
}

read_lock(&global_rwlock);
if ((policy = find_policy_id(kdescr.policy_id)) == NULL ||
(rule = find_rule_id(policy, kdescr.rule_id)) == NULL) {
read_unlock(&global_rwlock);
return (rbce_id_t)-EINVAL;
}
write_lock(&policy->rwlock);
read_unlock(&global_rwlock);
list_del(&rule->obj.link);
write_unlock(&policy->rwlock);
kfree(rule->terms);
kfree(rule);
return 0;
}

static rbce_id_t
get_rule_bytype(struct rbce_rule_descr *udescr, int type_info)
{
struct rbce_rule_descr kdescr;
struct rbce_rule *rule = NULL;
struct rbce_rule_term *terms = NULL;
struct rbce_policy *policy;
int num_terms;
rbce_id_t ruleid;

if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_rule_descr))) {
return (rbce_id_t)-EFAULT;
}

if (kdescr.policy_id <= 0) {
return (rbce_id_t)-EINVAL;
}

read_lock(&global_rwlock);
ruleid = (rbce_id_t)(-EINVAL);

if ((policy = find_policy_id(kdescr.policy_id)) == NULL) {
read_unlock(&global_rwlock);
return ruleid;
}

switch (type_info) {
case RBCE_RULE_NAME:
rule = find_rule_name(policy, kdescr.name);
break;
case RBCE_RULE_ID:
rule = find_rule_id(policy, kdescr.rule_id);
break;
default:
read_unlock(&global_rwlock);
return ruleid;
}

read_lock(&policy->rwlock);
read_unlock(&global_rwlock);

kdescr.rule_id = ruleid = getid(rule);
kdescr.class_id = getid(rule->target_class);
num_terms = rule->num_terms;
if (kdescr.num_terms >= num_terms) {
terms = kmalloc(num_terms * sizeof(struct rbce_rule_term),
GFP_KERNEL);
if (terms == NULL) {
ruleid = -ENOMEM;
read_unlock(&policy->rwlock);
} else {
memcpy(terms, rule->terms, num_terms *
sizeof(struct rbce_rule_term));
read_unlock(&policy->rwlock);
if (copy_to_user(kdescr.terms, terms, num_terms *
sizeof(struct rbce_rule_term))) {
ruleid = -EFAULT;
kdescr.terms = NULL;
}
kfree(terms);
}
} else {
read_unlock(&policy->rwlock);
}
kdescr.num_terms = num_terms;
kdescr.insert_after = 0;
return ruleid;
}

static void
reclassify_all_tasks(struct rbce_class *cls)
{
struct task_struct *proc, *thread;

// Could call rbce_classify() without holding the following locks, but
// grabbing the locks at this level will reduce the number of
// lock/unlocks of global_rwlock and cur_policy->rwlock.
read_lock(&global_rwlock);
read_lock(&cur_policy->rwlock);

read_lock(&tasklist_lock);
do_each_thread(proc, thread) {
/* todo: need to descriminate based on <cls> */
rbce_classify_l(thread);
} while_each_thread(proc, thread);
read_unlock(&tasklist_lock);

read_unlock(&cur_policy->rwlock);
read_unlock(&global_rwlock);
return;
}

static int
get_my_class(struct rbce_my_info_descr * udescr)
{
#if 0
char c_name[RBCE_MAX_NAME_LEN];
char p_name[RBCE_MAX_NAME_LEN];

// To avoid copying to user land while holding the locks, copy
// the contents to local variables first.
read_lock(&global_rwlock);
read_lock(&cur_policy->rwlock);
strcpy(c_name, current->class->obj.name);
strcpy(p_name, cur_policy->obj.name);
read_unlock(&cur_policy->rwlock);
read_unlock(&global_rwlock);

if (copy_to_user(udescr->class_name, c_name, RBCE_MAX_NAME_LEN-1)) {
return -EFAULT;
}
if (copy_to_user(udescr->policy_name, p_name, RBCE_MAX_NAME_LEN-1)) {
return -EFAULT;
}
#endif
return 0;
}

rbce_id_t
rbce_ctl(rbce_op_t op, void *data)
{
char utag[RBCE_MAX_TAG_LEN];

switch (op) {
case RBCE_CREATE_POLICY:
return create_policy((struct rbce_policy_descr*) data);

case RBCE_COMMIT_POLICY:
return commit_policy((struct rbce_policy_descr*) data);

case RBCE_DECOMMIT_POLICY:
return decommit_policy((struct rbce_policy_descr*) data);

case RBCE_DELETE_POLICY:
return delete_policy((struct rbce_policy_descr *) data);

case RBCE_SET_POLICY_TYPE:
return set_policy_type((struct rbce_policy_descr *) data);

case RBCE_GET_POLICY_TYPE:
return get_policy_type((struct rbce_policy_descr *) data);

case RBCE_GET_POLICY_BYNAME:
return get_policy_byname((struct rbce_policy_descr *) data);

case RBCE_CREATE_CLASS:
return create_class((struct rbce_class_descr*) data);

case RBCE_DELETE_CLASS:
return delete_class((struct rbce_delete_class_descr*) data);

case RBCE_GET_CLASS_BYNAME:
return get_class_byname((struct rbce_class_descr*) data);

case RBCE_SET_CLASS_SHARES:
case RBCE_GET_CLASS_SHARES:
case RBCE_GET_RESOURCE_USAGE:
case RBCE_GET_CUM_RESOURCE_USAGE:
return -ENOSYS;

case RBCE_CREATE_RULE:
return create_rule((struct rbce_rule_descr*) data);

case RBCE_DELETE_RULE:
return delete_rule((struct rbce_delete_rule_descr*) data);

case RBCE_CHANGE_RULE:
return change_rule((struct rbce_rule_descr*) data);

case RBCE_GET_RULE_BYNAME:
return get_rule_bytype((struct rbce_rule_descr*) data,
RBCE_RULE_NAME);

case RBCE_GET_RULE_BYID:
return get_rule_bytype((struct rbce_rule_descr*) data, RBCE_RULE_ID);

case RBCE_RECLASSIFY_ALL_TASKS:
reclassify_all_tasks(NULL);
return 0;

case RBCE_GET_MY_INFO:
return get_my_class((struct rbce_my_info_descr *) data);

case RBCE_SET_TAG:
if (copy_from_user(current->ckrm_client_data, utag,
RBCE_MAX_TAG_LEN-1))
return -EFAULT;
rbce_classify(current);
return 0;

case RBCE_GET_TAG:
if (copy_from_user(utag, current->ckrm_client_data,
RBCE_MAX_TAG_LEN-1))
return -EFAULT;
return 0;

default:
return -EINVAL;
}
}

static void
rbce_set_task_classes(struct task_struct *tsk, struct rbce_class *cls)
{
cprintk(1,("__classify %d<%s> -> <%s>\n",
tsk->pid, tsk->comm, cls->obj.name));

/* Don't set directly

tsk->cpu_class = cls->cpu_class;
tsk->mem_class = cls->cpu_class;
tsk->net_class = cls->net_class;
tsk->io_class = cls->io_class;

*/

ckrm_cpu_change_class(tsk, cls->cpu_class);
ckrm_mem_change_class(tsk, cls->mem_class);
ckrm_io_change_class(tsk, cls->io_class);
ckrm_net_change_class(tsk, cls->net_class);

}


/*====================== Start Classification Engine
=======================*/

static int
match_cmd(char *tsk_comm, char *cmd_exp, int fullpath)
{
char *c, *t, *last_ast, next_c;
char tsk_command[128];

/* first we need to retrieve the full name of the tsk */
if (fullpath) {
strncpy(tsk_command, tsk_comm, 16);
tsk_command[16] = '\0';
} else {
// Make sure there is no '/' in the command expression.
c = cmd_exp;
while(*c && *c != '/') c++;
if (*c) {
return 0;
}

// Get only the command name from tsk_comm.
c = strrchr (tsk_comm, '/');
if (c != NULL) {
strcpy(tsk_command, c+1);
} else {
strcpy(tsk_command, tsk_comm);
}
}
cprintk(4,("tsk_command |%s| tsk_comm |%s| cmd_exp |%s|\n",
tsk_command, tsk_comm, cmd_exp));

/* now faithfully assume the entire pathname of the file is in
tsk_command */

/* we now have to effectively implement a regular expression
* for now assume
* '?' any single character
* '*' one or more '?'
* rest must match
*/

c = cmd_exp;
t = tsk_command;
last_ast = NULL;
next_c = '\0';

while (*c && *t) {
switch (*c) {
case '?':
if (*t == '/') {
return 0;
}
c++; t++;
continue;
case '*':
if (*t == '/') {
return 0;
}
// eat up all '*' in c
while (*(c+1) == '*') c++;
next_c = '\0';
last_ast = c;
//t++; // Add this for matching '*' with "one" or more chars.
while (*t && (*t != *(c+1)) && *t != '/') t++;
if (*t == *(c+1)) {
c++;
if (*c != '/') {
if (*c == '?') {
if (*t == '/') {
return 0;
}
t++; c++;
}
next_c = *c;
if (*c) {
if (*t == '/') {
return 0;
}
t++; c++;
if (!*c && *t)
c = last_ast;
}
} else {
last_ast = NULL;
}
continue;
}
return 0;
case '/':
next_c = '\0';
/*FALLTHRU*/
default:
if (*t == *c && next_c != *t) {
c++, t++;
continue;
} else {
/* reset to last asterix and continue from there */
if (last_ast) {
c = last_ast;
} else {
return 0;
}
}
}
}

/* check for trailing "*" */
while (*c == '*') c++;

return (!*c && !*t);
}

static int
evaluate_rule(struct task_struct *tsk,struct rbce_rule *rule)
{
struct rbce_rule_term *term;
char *task_cmd;
int i;
int rc = 1;

/* we count down to anticipate deprules to listed first */

// XXX Full command name is between mm->arg_start & mm->arg_end.
// for now use tsk->comm
task_cmd = tsk->comm;

for (i = rule->num_terms-1, term = &rule->terms[i];
(rc == 1) && (i >= 0); i--, term-- ) {

switch(term->op) {

case RBCE_RULE_CMD_PATH:
rc &= match_cmd(task_cmd, term->cmd, 1);
break;
case RBCE_RULE_CMD:
rc &= match_cmd(task_cmd, term->cmd, 0);
break;
case RBCE_RULE_REAL_UID:
rc &= (tsk->uid == term->id);
break;
case RBCE_RULE_REAL_GID:
rc &= (tsk->gid == term->id);
break;
case RBCE_RULE_EFFECTIVE_UID:
rc &= (tsk->euid == term->id);
break;
case RBCE_RULE_EFFECTIVE_GID:
rc &= (tsk->egid == term->id);
break;
case RBCE_RULE_APP_TAG:
rc &= !strcmp(tsk->ckrm_client_data, term->tag);
break;
case RBCE_RULE_DEP_RULE:
rc &= evaluate_rule(tsk, term->deprule);
break;
}
}
return rc;
}

/*
* global lock(in read or write mode) and cur_policy's lock(write mode)
* must be held while calling this function.
*/
static void
rbce_classify_l(struct task_struct *tsk)
{
struct rbce_class *cls = NULL;

struct rbce_rule *rule;

list_for_each_entry(rule, &cur_policy->rules, obj.link) {
if (evaluate_rule(tsk, rule) && rule->target_class) {
cls = rule->target_class;
break;
}
}
if (cls == NULL)
cls = cur_policy->def_class;
rbce_set_task_classes(tsk,cls);
}

/*
* Must be called with both global_lock and policy_lock(of the policy to
* which the class belongs) held(in read or write mode).
*/
static void
reclassify_tasks(struct rbce_class *cls)
{
reclassify_all_tasks(cls);
}

/*
* Called from the kernel routines from the following system calls:
* execve()
* setuid(), setgid() and family
* set_my_tag()
*/
void
rbce_classify(struct task_struct *tsk)
{
cprintk(3,("enter rbce_classify\n"));
read_lock(&global_rwlock);
read_lock(&cur_policy->rwlock);

rbce_classify_l(tsk);

read_unlock(&cur_policy->rwlock);
read_unlock(&global_rwlock);
cprintk(3,("exit rbce_classify\n"));
return;
}

/*
* Called from the kernel routines from the following system calls:
* fork(), after copy_process()
*/
void
rbce_fork_task(struct task_struct *tsk)
{
cprintk(2,("fork task %d\n",tsk->pid));

// memset(current->tag, 0, RBCE_MAX_TAG_LEN); // tag not inherited.
rbce_classify(tsk);
return;
}

/*
* To be called from exit()
*/
void
rbce_exit_task(struct task_struct *tsk)
{
cprintk(2,("Exit task %d\n",tsk->pid));

#if 0
read_lock(&global_rwlock);
read_lock(&cur_policy->rwlock);

spin_lock(&tsk->class->lock);
list_del(&tsk->rbce_link);
spin_unlock(&tsk->class->lock);

read_unlock(&cur_policy->rwlock);
read_unlock(&global_rwlock);
#endif
return;
}

void
rbce_exec_task(struct task_struct *tsk, char *filename)
{

cprintk(2,("Exec task %d<%s><%s>\n",tsk->pid,tsk->comm,filename));

// memset(current->tag, 0, RBCE_MAX_TAG_LEN); // tag not inherited.
rbce_classify(tsk);
return;
}

void
rbce_id_change(void)
{
struct task_struct *tsk = current;
cprintk(2,("ID change pid=%d\n",tsk->pid));

rbce_classify(current);
return;
}

/*================ calback testing only ==================*/
void
rbce_useradd(struct user_struct *up)
{
cprintk(2,("useradd\n"));
}

void
rbce_userdel(struct user_struct *up)
{
cprintk(2,("userdel\n"));
}

/*====================== end classification engine
=======================*/

struct ckrm_callbacks call_table = {
.fork = rbce_fork_task,
.exec = rbce_exec_task,
.exit = rbce_exit_task,
.id = rbce_id_change,
.useradd = rbce_useradd,
.userdel = rbce_useradd,
};

/* ===================== initialization ==========================*/

static int rbce_open(struct inode *inode, struct file *filp)
{
if (! try_module_get(THIS_MODULE)) {
cprintk(1,("rbce_open failed\n"));
return -EFAULT;
}

cprintk(1,("rbce_open() by <%s>\n",current->comm));
return 0;
}

static int rbce_release(struct inode *inode, struct file *filp)
{
cprintk(1,("rbce_close() by <%s>\n",current->comm));
module_put(THIS_MODULE);
return 0;
}


static int rbce_ioctl( struct inode *inode, struct file *file,
unsigned int ioctl_num, unsigned long ioctl_param)
{
cprintk(1,("rbc_ioctl %d\n",ioctl_num));
return rbce_ctl ( ioctl_num, (void*)ioctl_param );
}


static struct file_operations rbce_fops = {
owner: THIS_MODULE,
ioctl: rbce_ioctl,
open: rbce_open,
release: rbce_release,
};

/* ===================== initialization ==========================*/


int init_rbce ( void )
{
int rc;

printk( "<1>\nInstalling \'%s\' module\n", modname );
if ((rc = register_chrdev( rbce_major, modname, &rbce_fops )) != 0) {
printk("can't register device\n");
return rc;
}
default_policy.def_class = &default_class;

default_class.cpu_class = ckrm_dflt_cpu_class(&default_class);
default_class.mem_class = ckrm_dflt_mem_class(&default_class);
default_class.net_class = ckrm_dflt_net_class(&default_class);
default_class.io_class = ckrm_dflt_io_class(&default_class);

ckrm_register_engine(&call_table);
return 0;
}


void exit_rbce ( void )
{
printk( "<1>Removing \'%s\' module\n", modname );
ckrm_unregister_engine();
unregister_chrdev( rbce_major, modname );
}


/* ===================== System call interfaces
==========================*/

module_init(init_rbce);
module_exit(exit_rbce);







-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 13:48    [W:0.139 / U:1.152 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site