diff -uNr elsa-1.4/elsa_classification_rules.conf elsa-1.4_automatic_classifier/elsa_classification_rules.conf --- elsa-1.4/elsa_classification_rules.conf 1970-01-01 05:30:00.000000000 +0530 +++ elsa-1.4_automatic_classifier/elsa_classification_rules.conf 2007-08-09 15:35:52.000000000 +0530 @@ -0,0 +1,35 @@ +# This is configuration file for elsa classifier based on selinux security contexts. +# The format of file is quite simple +# On new line, you need to specify security context (e.g. "user_u:system_r:sendmail_t") +# followed by one blank space as dilimiter +# and then followed by list of job-id's to which this security context should belong. +# +# The list of job-id is having numbers seperated by comma. +# There is upper limit on how many jobid's you can specify with one security context +# by default, its 50, you can modify it by changing the value of +# MAX_GROUPS_PER_RULE in file job_daemon/jobd.h, and then recomiling it. +# +# There is some basic wild-character support provided, that can be used for added flexibility. +# Character '*' can be put at any place in security context +# It can replace user name, role or domain. +# '*' signify that, that part of security_context will be ignored. +# +# For example, if you want any process with "httpd_t" domain, started by any user +# should go to jobd 3, then u can write it as follow +#*:*:httpd_t 3 +# +# If you want to group all processes from same user then you can use following rule +#user_u:*:* 4 +# +# You can have default rule as follows, which will group all remaining processes +#*:*:* 5 +# +# Note: the order of rules is important, Matching starts from top to bottom +# so, give specific rules in the begining, and generic rules in the end. +# +# We recemend that the job-id's given should be in sequence, +# please avoid random job-id's as jobd will create jobs for all ids in the range. +# +user_u:system_r:sendmail_t:* 1,2 +*:system_r:sendmail_t:* 3,4 +*:system_r:httpd_t:* 5,6 diff -uNr elsa-1.4/HACKING.txt elsa-1.4_automatic_classifier/HACKING.txt --- elsa-1.4/HACKING.txt 1970-01-01 05:30:00.000000000 +0530 +++ elsa-1.4_automatic_classifier/HACKING.txt 2007-08-09 15:35:52.000000000 +0530 @@ -0,0 +1,135 @@ + +=========================================================================================================== + Why this automatic classifier required ? + +The idea behind using automatic classifier is to reduce the burdan of sys_admin from +reconfiguring the ELSA process membership, everytime system or jobd or any process restarts. + +In current situation, ELSA provides tools like "jobmng", "elsa" which has to be used +by sys-admins for manually classifying the processes into JOB groups. +As these classifications are based on PID's of processes and are not stored +permanantly, everytime system or jobd restarts, all classifications has to be +done again. This process can't be automated by scripting because PID's of new +processes wont be same. + +With this solution, system administrator needs to configure "elsa_classification_rules.conf" +file once, and then all processes will automatically classified with reference +to given policies. It wont require re-configuration even after system of jobd +daemon restarts. Whenever any new process is created, it will be automatically +classified. + + +=========================================================================================================== +Note: Our solution will work only when selinux is enabled (even permissive mode will do), +as we need selinux for giving security context of processes. If selinux is not available then +still ELSA will work, but there wont be any automatic classification of processes. +You can check the status of selinux by 'sestatus' command. + +We use selinux security context for identifying processes. +The advantage with selinux security contexts are that +1. They are persistant (i.e. they will remain same even after reboots) +2. They are flexible (i.e. Security context will differ depending on who is executing, + which program is being executed, and in what context it has been executed ) +3. They are configurable (i.e. you can change selinux policies to set selinux contexts as per your needs. + There are many user-friendly tools which helps in giving security context i.e. "seedit" ) +4. We are keeping ELSA classification rules seperate from selinux internal policies + This keeps ELSA classification management very simple. + You need to configure just one simple file. at "/etc/elsa_classification_rules.conf" +5. We provide added flexibility by supporting wild-characters in security context + (see "Guidelines for configuring elsa_classification_rules.conf" for more details ) + +=========================================================================================================== + Using Automatic classifier + +After installing ELSA by "make install", a configuration file will be created in file +"/etc/elsa_classification_rules.conf". You need to modify this file to add rules as per +your requirements. Once rules are created, then "jobd" daemon can be started, which will +start classifying the processes automatically. + + +=========================================================================================================== + Guidelines to configure "elsa_classification_rules.conf" file + +This is simple text file, normally located at "/etc/elsa_classification_rules.conf". +The format is + + +Here, is the security context of a process that needs to be classified. +You can get security_context of any running process by using "ps -Z" command. + + is list of job-ids, seperated by character "," (comma) + +This rule says that Process with given "security_context" should go in JOBS specified +in "list_of_jobs_it_belongs". + +Currently, rule parsing is not very flexible, so +you need to give new rule in new line, +do not keep in leading or trailing spaces. +keep just one blank-space between and . +comments are allowed by giving "#" character in the begining of line. +Giving "#" at any other place won't work. +Currently, we are not supporting sensitivity part of security_context, it will +be ignored in any kind of comparision. + +We are working on making file parsing more flexible, but it may take some time. + + +The list of job-id is having numbers seperated by comma. +There is upper limit on how many jobid's you can specify with one security context +by default, its 50, you can modify it by changing the value of +MAX_GROUPS_PER_RULE in file job_daemon/jobd.h, and then recomiling it. + +There is some basic wild-character support provided, that can be used for added flexibility. +Character '*' can be put at any place in security context +It can replace user_name, role or domain. +'*' signify that, that part of security_context will be ignored. + +For example, if you want any process with "httpd_t" domain, started by any user +should go to jobd 3, then u can write it as follow +*:*:httpd_t 3 + +If you want to group all processes from same user then you can use following rule +user_u:*:* 4 + +You can have default rule as follows, which will group all remaining processes +*:*:* 5 + +Note: the order of rules is important, Matching starts from top to bottom +so, give specific rules in the begining, and generic rules in the end. + +We recemend that the job-id's given should be in sequence, +please avoid random job-id's as jobd will create jobs for all ids in the range. + +=========================================================================================================== + How it works internally ? + +At system startup time, "/etc/elsa_classification_rules.conf" file will be read for rules +that will be used for classification (function: job_daemon/classifier.c:file_parse()). + +After this maximum number of default groups will be found out by function find_default_group_number () +(function: job_daemon/classifier.c:find_default_group_number()). + +Now once ELSA initialization is done, then we create empty jobs that are specified in configuration file +by function (function: job_daemon/jobd.c:create_jobs_for_automatic_loading(no_of_jobs)). +This step is required because processes can come in any order, and if a process which is supposed to go +to job-id 5 comes before all other groups then we need to have job-id 5 ready. +Currently ELSA creates jobs in sequential order, thats why we create all jobs in the begining and keep them +ready for future use. +We are doing this by adding pid 1 repeatedly by using "do_add_req (1,0)" and then removing it, +everytime we add pid 1, it creates new group, and then we delete pid 1 at end from all groups. + +In recv_sk_nl, we have added support for "PROC_EVENT_EXEC" which calls function "security_context_classifier" +The support for "PROC_EVENT_EXEC" is needed because security context are normally changed in EXEC system call. +To handle this change in security context, this "PROC_EVENT_EXEC" message handling was needed. + +Even in case of "PROC_EVENT_FORK", we have modified "fork_handler" to call "security_context_classifier" +function first. If process can be classified on basis of security context, then that has given preference +over default classification which follows if "security_context_classifier" is not able to classify the +process. +This provides fail-safe machanism that even if selinux is not working, default ELSA behaviour will be there. + +"security_context_classifier" (Function: job_daemon/jobd.c:security_context_classifier) +This function takes pid of process to be classified, and find out the list of jobs to which it should +belong by using function "get_process_classification" function. +Then it uses "do_add_req" function to add specified process in corresponding group. +=========================================================================================================== diff -uNr elsa-1.4/HOWTO elsa-1.4_automatic_classifier/HOWTO --- elsa-1.4/HOWTO 2006-09-07 18:30:21.000000000 +0530 +++ elsa-1.4_automatic_classifier/HOWTO 2007-08-09 15:35:52.000000000 +0530 @@ -79,3 +79,67 @@ Enjoy, The ELSA Team + +=========================================================================================================== + Using SELinux based Automatic classifier + +After installing ELSA by "make install", a configuration file will be created in file +"/etc/elsa_classification_rules.conf". You need to modify this file to add rules as per +your requirements. Once rules are created, then "jobd" daemon can be started, which will +start classifying the processes automatically. + + +=========================================================================================================== + Guidelines to configure "elsa_classification_rules.conf" file + +This is simple text file, normally located at "/etc/elsa_classification_rules.conf". +The format is + + +Here, is the security context of a process that needs to be classified. +You can get security_context of any running process by using "ps -Z" command. + + is list of job-ids, seperated by character "," (comma) + +This rule says that Process with given "security_context" should go in JOBS specified +in "list_of_jobs_it_belongs". + +Currently, rule parsing is not very flexible, so +you need to give new rule in new line, +do not keep in leading or trailing spaces. +keep just one blank-space between and . +comments are allowed by giving "#" character in the begining of line. +Giving "#" at any other place won't work. +Currently, we are not supporting sensitivity part of security_context, it will +be ignored in any kind of comparision. + +We are working on making file parsing more flexible, but it may take some time. + + +The list of job-id is having numbers seperated by comma. +There is upper limit on how many jobid's you can specify with one security context +by default, its 50, you can modify it by changing the value of +MAX_GROUPS_PER_RULE in file job_daemon/jobd.h, and then recomiling it. + +There is some basic wild-character support provided, that can be used for added flexibility. +Character '*' can be put at any place in security context +It can replace user_name, role or domain. +'*' signify that, that part of security_context will be ignored. + +For example, if you want any process with "httpd_t" domain, started by any user +should go to jobd 3, then u can write it as follow +*:*:httpd_t 3 + +If you want to group all processes from same user then you can use following rule +user_u:*:* 4 + +You can have default rule as follows, which will group all remaining processes +*:*:* 5 + +Note: the order of rules is important, Matching starts from top to bottom +so, give specific rules in the begining, and generic rules in the end. + +We recemend that the job-id's given should be in sequence, +please avoid random job-id's as jobd will create jobs for all ids in the range. + +=========================================================================================================== diff -uNr elsa-1.4/INSTALL elsa-1.4_automatic_classifier/INSTALL --- elsa-1.4/INSTALL 2006-09-08 12:31:50.000000000 +0530 +++ elsa-1.4_automatic_classifier/INSTALL 2007-08-09 15:35:52.000000000 +0530 @@ -61,3 +61,10 @@ The installation is done through the webmin configuration interface. When installing, you can access to ELSA module via the webmin's system interface. + + +4) Using SELinux based automatic process classifier +Note: This automatic classifier solution will work only when selinux is enabled (even permissive mode will do). +We will be using libselinux to make some selinux related system calls. +You can check the status of selinux by 'sestatus' command. + diff -uNr elsa-1.4/job_daemon/classifier.c elsa-1.4_automatic_classifier/job_daemon/classifier.c --- elsa-1.4/job_daemon/classifier.c 1970-01-01 05:30:00.000000000 +0530 +++ elsa-1.4_automatic_classifier/job_daemon/classifier.c 2007-08-09 15:35:52.000000000 +0530 @@ -0,0 +1,386 @@ +#include +#include +#include +#include +#include +#include +#include /* for getpidcon function */ + +#include "jobd.h" + + + +struct security_context_list{ + + char security_context[buffer_size]; // security context assosicated + int elsa_group[MAX_GROUPS_PER_RULE]; // list of jobs with which given security context is assosiated. + struct security_context_list *security_context_nxt; // link list pointer for next. + +}; + +struct security_context_list *head; // global head pointer which points to start of linked list of security context rules. + + + +void parse_group(char *string,int groups[]); + + + + +/* file_parse function to parse the config file that has the information + about security context and corresponding elsa group +*/ +struct security_context_list * file_parse () +{ + FILE *fd; + char buff[buffer_size+1]; + char *parse_ptr, *save_ptr,*tmp,*tmp_str; + char delimiter[] = " "; + struct security_context_list *security_context_tmp,*security_context_tmp1,*security_context_tmp2; + int len; + + head =NULL; + + //To open the config the file + fd = fopen(CONFIG_FILE_PATH, "r"); + if (fd == NULL) + { + printf("Error: not able to open configuration file %s in read mode\nCheck if file is present\n", CONFIG_FILE_PATH ); + exit (1); + } + + while(fgets(buff,buffer_size,fd) != NULL) + { + len = strlen (buff); + if (len > 0 ) + { + if ( buff[len-1]== '\n') + { + buff[len-1] = '\0'; + } + } + else + { + continue ; + } + parse_ptr = buff; + + // check if string is empty or not + for (; *parse_ptr ==' ' || *parse_ptr == '\t' || *parse_ptr == '\v' ; ++parse_ptr ); + if (parse_ptr == NULL ) + { + if( feof (fd)) + { + break ; + } + continue ; + } + parse_ptr = buff; + if(*parse_ptr == '#') + { + continue; + } + + security_context_tmp = malloc(sizeof(struct security_context_list)); + parse_ptr = buff; + + tmp = strtok_r(parse_ptr,delimiter,&save_ptr); + strcpy (security_context_tmp->security_context,tmp); + security_context_tmp->security_context_nxt = NULL; + + if(head == NULL) + { + head = security_context_tmp; + } + else + { + security_context_tmp1 = head; + while(security_context_tmp1 != NULL) + { + security_context_tmp2 = security_context_tmp1; + security_context_tmp1 = security_context_tmp1->security_context_nxt; + } + security_context_tmp1 = security_context_tmp; + security_context_tmp2->security_context_nxt = security_context_tmp1; + } + + parse_ptr = NULL; + tmp = NULL; + + + tmp = strtok_r(parse_ptr,delimiter,&save_ptr); + tmp_str=malloc(sizeof(strlen(tmp))); + strcpy(tmp_str,tmp); + + + parse_group(tmp_str,security_context_tmp->elsa_group); + + + } //end of while + + return head; + + +} + +//To assign values of elsa groups in the array of security_context_list +// We assume that one process can belong to more than one jobs. +// this function will parse comma seperated list of groupids. +void parse_group(char *string,int groups[]) + +{ + char *tmp_pt,*save_ptr,*tmp_ptr; + char delimiter[] = ","; + int i,group_id; + + i = 0 ; + for(tmp_ptr=string; ; tmp_ptr=NULL) + { + tmp_pt=strtok_r(tmp_ptr,delimiter,&save_ptr); + if(tmp_pt == NULL) + { + break; + } + group_id = atoi(tmp_pt); + groups[i] = group_id; + i++; + if ( i >= MAX_GROUPS_PER_RULE ) + break ; + tmp_ptr = NULL ; + } +} + + +/* +Finds the largest job number specified in configuration. +This information is required so that, all those empty groups can be created at system initialization +*/ + +int find_default_group_number() +{ + int i,max; + struct security_context_list *security_context_tmp; + + security_context_tmp = head; + max=0; + + while(security_context_tmp != NULL) + { + for(i=0; (i < MAX_GROUPS_PER_RULE) && ( security_context_tmp->elsa_group[i] > 0 ) ;i++) + { + if(security_context_tmp->elsa_group[i] > max) + { + max = security_context_tmp->elsa_group[i]; + } + } + security_context_tmp = security_context_tmp->security_context_nxt; + } + return max; +} + + + +//////////////////////////////////////////////////// + +//search_security_context function searches given s_context +//in provided policies with considering '*' as wild-card. +//This wildcard allows you to skip some part of policy matching. +//You are allowed to specify policies in following manner. + +//user_t:role_t:type_t 5 +//user_t:*:type_t 7 +// *:role_t:* 2,4 +// *:*:* 1,3 + +// Order of rules is important, put specific rules first, and then put generic rules +// on success returns the pointer to security_context_list structure containing matching rule +// on failure returns NULL + +struct security_context_list * search_security_context ( char * s_context ) +{ + int is_failed ; + char *rule_part, *context_part ; + struct security_context_list *temp1 ; + + if ( s_context == NULL || !(*s_context) ) return NULL ; + + + + for (temp1=head; temp1 !=NULL ; temp1=temp1->security_context_nxt) + { + rule_part = temp1->security_context ; + context_part = s_context ; + is_failed = 0 ; + + // if this rule is empty, following rules also should be empty then. + if (!(*rule_part)) break ; // it means, all rule after this one are empty. +// JOBD_DEBUG ( "Rule no %d present\n",rule_number ); +// JOBD_DEBUG ( "Comparing %s = %s \n", rule_part, s_context); + + + // seperate and compare the user. + if ( *rule_part == '*' ) + { + // if it is '*', then its wild character, and dont need to be matched + // forword the rule part till next ':' + for ( ; *rule_part && *rule_part != ':' ; rule_part++ ) ; + + // If there is unexpected end of line then throw error + if ( !(*rule_part) ) + { + JOBD_DEBUG ( "Error in rule of rule base %s \n", + temp1->security_context) ; + is_failed = 1 ; + continue ; + } + + // forword the context part till next ':' + for ( ; *context_part && *context_part != ':' ; context_part++ ) ; + + // If there is unexpected end of line then throw error + if ( !(*context_part) ) + { + JOBD_DEBUG ( "Error in matching rule of rule base %s with context %s\n", + temp1->security_context, s_context) ; + is_failed = 1 ; + continue ; + } + } + else + { + // Its not wild-char now. so compare rule_part and context_part character by character. + // till next ':' + for ( ; *rule_part && *rule_part != ':' ; rule_part++, context_part++ ) + { + if ( *rule_part != *context_part ) + { + is_failed = 1 ; + break ; + } + } // end for: till next ':' + if (is_failed) continue ; + + // If any of string terminates unexpectedly, then return with error + if ( !(*rule_part && *context_part)) + { + is_failed = 1 ; + continue ; + } + } // end if: if '*' + + // move and pass the seperator ':' + rule_part++ ; + context_part++ ; + + + // seperate and compare the role. + if ( *rule_part == '*' ) + { + // if it is '*', then its wild character, and dont need to be matched + // forword the rule part till next ':' + for ( ; *rule_part && *rule_part != ':' ; rule_part++ ) ; + + // If there is unexpected end of line then throw error + if ( !(*rule_part) ) + { + JOBD_DEBUG ( "Error in rule of rule base %s \n",temp1->security_context) ; + is_failed = 1 ; + continue ; + } + + // forword the context part till next ':' + for ( ; *context_part && *context_part != ':' ; context_part++ ) ; + + // If there is unexpected end of line then throw error + if ( !(*context_part) ) + { + JOBD_DEBUG ( "Error in matching rule of rule base %s with context %s\n", temp1->security_context, s_context ) ; + is_failed = 1 ; + continue ; + } + } + else + { + // Its not wild-char now. so compare rule_part and context_part character by character. + // till next ':' + for ( ; *rule_part && *rule_part != ':' ; rule_part++,context_part++ ) + { + if ( *rule_part != *context_part ) + { + is_failed = 1 ; + break ; + } + } // end for: till next ':' + // If any of string terminates unexpectedly, then return with error + + if ( is_failed ) continue ; + + if ( !(*rule_part && *context_part)) + { + is_failed = 1 ; + continue ; + } + } + // move and pass the seperator ':' + rule_part++ ; + context_part++ ; + + // seperate and compare the domain. + if ( *rule_part == '*' ) + { + // wildcard match successfull :-) + return temp1 ; + } + else + { + // Its not wild-char now. so compare rule_part and context_part character by character. + // till next ':' + for ( ; *rule_part && *rule_part != ':' ; rule_part++,context_part++ ) + { + if ( *rule_part != *context_part ) + { + is_failed = 1 ; + break ; + } + } // end for: till next ':' + + if (is_failed ) continue ; + + // match successfull :-) + return temp1 ; + } + } // end for : for each rule in rule base. + return NULL; +} + +/* +Function classify_process will take the pid and will return the list of group to which this process should belong. +on success, it returns array of job_id's +on failure, it returns NULL. +*/ +int * get_process_classification ( int pid ) +{ + struct security_context_list * matched_rule ; + int ret, i ; + security_context_t s_context ; + + + ret = getpidcon ( pid, &s_context ) ; + if ( ret == -1 ) + { + JOBD_DEBUG("[get_process_classification] getpidcon failed to get s_context for %d", pid ); + return NULL ; + } + // now ret will contain jobid in which process should go. + matched_rule = search_security_context (s_context) ; + if (matched_rule == NULL ) + { + return NULL ; + } + + for ( i = 0 ;(i< MAX_GROUPS_PER_RULE)&& ( matched_rule->elsa_group[i] > 0 ) ; ++i ) + { + JOBD_DEBUG("[get_process_classification] [%d => %s]==>J# %d", pid, s_context, matched_rule->elsa_group[i] ); + } + return matched_rule->elsa_group ; +} // end function : diff -uNr elsa-1.4/job_daemon/jobd.c elsa-1.4_automatic_classifier/job_daemon/jobd.c --- elsa-1.4/job_daemon/jobd.c 2006-11-06 18:33:55.000000000 +0530 +++ elsa-1.4_automatic_classifier/job_daemon/jobd.c 2007-08-09 15:35:52.000000000 +0530 @@ -37,13 +37,18 @@ #include #undef _LINUX_TIME_H + #include "jobd.h" +#define DEBUG_JOBD + #ifdef DEBUG_JOBD #define JOBD_DEBUG(args...) syslog(LOG_INFO, args) #else #define JOBD_DEBUG(args...) do {} while(0) -#endif /* DEBUG_JOBD */ +#endif // DEBUG_JOBD + + #define JOBD_LOGIDENT "jobd" #define PROC_BASE "/proc" @@ -52,6 +57,12 @@ sizeof(struct cn_msg) + \ sizeof(int)) + +#include "classifier.c" + +// forword declaration +int security_context_classifier (int pid ) ; + /* item stored in jobs_ht */ struct ht_elmt { pid_t pid; /* process ID (the key for the hash table ) */ @@ -78,7 +89,6 @@ static const char *log_ident = JOBD_LOGIDENT; - /** * cn_fork_listen - */ @@ -225,11 +235,14 @@ JOBD_DEBUG("[update_jobs_list] pid %d in job %d", pid, jid); } + /** * fork_handler - fork handler * @ppid: parent PID * @cpid: child PID * + * First it tries to classify with the security context of process and + * configuration file provided, If it fails then * It checks if the parent that creates a child is in a job. If it is, * we add the child in the same job, otherwise we do nothing. * It's the main property needed to do per group of processes @@ -239,6 +252,17 @@ { struct ht_elmt *new_elmt; /* the new element: the child */ struct ht_elmt *parent; /* the parent if there is one */ + int ret; + ret= 0; + + // selinux security context based classifier. + ret = security_context_classifier(cpid); + if(ret == 0) + { + return; + } + + // Now trying for default classification. /* * we check if the parent is already registered in the jobs @@ -279,7 +303,7 @@ * equal to 0, it creates a new job. * Returns the jid or -1 if an error occured. */ -static int do_add_req(pid_t pid, int jid) +int do_add_req(pid_t pid, int jid) { struct ht_elmt *elmtp; /* a pointer to an element of the jobs hash table */ @@ -577,6 +601,7 @@ int ptgid; int cpid; int ctgid; + int process_pid ; /* Clear the buffer */ memset(buff, '\0', sizeof(buff)); @@ -595,16 +620,31 @@ case NLMSG_DONE: msg = (struct cn_msg *)NLMSG_DATA(hdr); ev = (struct proc_event *)msg->data; - if (ev->what == PROC_EVENT_FORK) { + switch ( ev->what ) + { + case PROC_EVENT_FORK : cpuid = ev->cpu; ppid = ev->event_data.fork.parent_pid; ptgid = ev->event_data.fork.parent_tgid; cpid = ev->event_data.fork.child_pid; ctgid = ev->event_data.fork.child_tgid; - JOBD_DEBUG("[seq#%d] %d %d - %d %d", - msg->seq, ppid, ptgid, cpid, ctgid); + JOBD_DEBUG("[seq#%d]NL fork PP# %d %d - CP# %d %d", msg->seq, ppid, ptgid, cpid, ctgid); fork_handler(ppid, cpid); - } + break ; + + case PROC_EVENT_EXEC : + // Adding PROC_EVENT_EXEC because most of the time, security context change + // after EXEC system call. + process_pid = ev->event_data.exec.process_pid; + JOBD_DEBUG("[seq#%d]NL EXEC P# %d", msg->seq, process_pid ); + security_context_classifier (process_pid); // it will classify given process + break ; + + default : + ctgid = 0 ; // read it as NOP +// JOBD_DEBUG("Other message" ); + + } // end switch : break; case NLMSG_ERROR: syslog(LOG_INFO, "recv_sk_nl error"); @@ -698,6 +738,73 @@ return 0; } + + +/** +function : create_jobs_for_automatic_loading () +It creates jobs which are needed by policies. +All jobs specified in policies will be created here + +This step is required because processes can come in any order, and if a process which is supposed to go +to job-id 5 comes before all other groups then we need to have job-id 5 ready. +Currently ELSA creates jobs in sequential order, thats why we create all jobs in the begining and keep them +ready for future use. +*/ +void create_jobs_for_automatic_loading (int max_jobs ) +{ + int i ; + JOBD_DEBUG("[PREPARING_SYSTEM] BEGIN Creating %d jobs", max_jobs); + for ( i = 1 ; i <= max_jobs ; ++i ) + { +// printf ( "Adding %dth time process 1 in next joblist\n", i ); + do_add_req (1,0); + } + for ( i = 1 ; i <= max_jobs ; ++i ) + { +// printf ( "removing %dth time process 1 from %d joblist\n", i,i ); + do_remove_req (1,i); + } + JOBD_DEBUG("[PREPARING_SYSTEM] END Created %d jobs", max_jobs); + +} // end function : + + +// Function security_context_classifier takes the pid and classify it as per rules provided +// in configuration file CONFIG_FILE_PATH +int security_context_classifier (int pid ) +{ + int i, ret ; + int * job_id_list ; + + JOBD_DEBUG("[security_context_classifier] begin pid %d", pid ); + job_id_list = get_process_classification (pid); + if ( job_id_list == NULL ) + { + JOBD_DEBUG("[security_context_classifier] no classification for P# %d", pid ); + return -1 ; + } + for ( i = 0 ; (i < MAX_GROUPS_PER_RULE) && ( job_id_list[i] > 0 ) ; ++i ) + { + JOBD_DEBUG("[security_context_classifier] P# %d classified in J# %d", pid, job_id_list[i] ); + // now force the movement of process + ret = do_add_req (pid,job_id_list[i]); + if ( ret > 0 ) + { + JOBD_DEBUG("[security_context_classifier] P# %d successfully added to J# %d", pid, ret ); + } + else + { + JOBD_DEBUG("[security_context_classifier] Failed in adding P# %d to J# %d", pid, ret ); + } + + } // end for : + + JOBD_DEBUG("[security_context_classifier] end pid %d", pid ); + return 0 ; +} // end function: security_context_classifier + + + /** * main - Principal routine and also a river rising in eastern Germany. * @@ -717,12 +824,18 @@ struct sockaddr_un sa_unix; /* describs an PF_UNIX socket */ fd_set fds; /* file descriptor set */ int max_fds; + int default_group_number; /* jobid's required by automatic classifier */ + + + file_parse(); // read the rules for classifications. + default_group_number = find_default_group_number(); // number of jobs that are needed by automatic classifier if (daemonize(0, 0) < 0) { perror("daemonize"); exit(EXIT_FAILURE); } + /* open connection to the system logger */ openlog(log_ident, LOG_PID, LOG_INFO); @@ -743,6 +856,11 @@ g_hash_table_new_full(&g_int_hash, &g_int_equal, NULL, &destroy_data); + + // create default jobs for automatic classification + create_jobs_for_automatic_loading (default_group_number) ; + + /* * Now we can start the communication. * First, the jobd daemon listens for fork creation and it also diff -uNr elsa-1.4/job_daemon/jobd.h elsa-1.4_automatic_classifier/job_daemon/jobd.h --- elsa-1.4/job_daemon/jobd.h 2005-04-21 17:05:48.000000000 +0530 +++ elsa-1.4_automatic_classifier/job_daemon/jobd.h 2007-08-09 15:40:08.000000000 +0530 @@ -8,14 +8,26 @@ #define __JOBD_H #include +#include #define ADD_REQ 1 #define REMOVE_REQ 2 #define GETINFO_REQ 3 #define QUIT_REQ 4 +// maximum length for security context string +#define buffer_size 500 + +// number of jobs one process can belong +#define MAX_GROUPS_PER_RULE 50 + + +//#define DEBUG_JOBD // for debugging. + + #define JOBD_SK_PATH "/tmp/sock_jobd" #define JOBD_EXCH_PATH "/tmp/sock_exch_jobd" +#define CONFIG_FILE_PATH "/etc/elsa_classification_rules.conf" /* message used for local interprocess communication */ struct jobd_msg { @@ -24,4 +36,5 @@ unsigned int jid; /* jobs identifier */ }; + #endif /* __JOBD_H */ diff -uNr elsa-1.4/job_daemon/Makefile elsa-1.4_automatic_classifier/job_daemon/Makefile --- elsa-1.4/job_daemon/Makefile 2005-10-18 14:30:41.000000000 +0530 +++ elsa-1.4_automatic_classifier/job_daemon/Makefile 2007-08-09 15:35:52.000000000 +0530 @@ -3,7 +3,7 @@ CC = gcc INCLUDES += $(shell pkg-config --cflags --libs glib-2.0) $(USER_INCLUDES) #CFLAGS = -g -O2 -Wall -DDEBUG_JOBD # $(INCLUDES) -CFLAGS = -g -O2 -Wall +CFLAGS = -g -O2 -Wall -lselinux CPPFLAGS = $(INCLUDES) SPROGS = jobd diff -uNr elsa-1.4/Makefile elsa-1.4_automatic_classifier/Makefile --- elsa-1.4/Makefile 2005-06-22 18:09:21.000000000 +0530 +++ elsa-1.4_automatic_classifier/Makefile 2007-08-09 15:35:52.000000000 +0530 @@ -8,6 +8,7 @@ $(MAKE) -C job_daemon install $(MAKE) -C man install $(MAKE) -C utils install + cp elsa_classification_rules.conf /etc/ uninstall: $(MAKE) -C job_daemon uninstall @@ -19,3 +20,4 @@ $(MAKE) -C job_daemon clean $(MAKE) -C man clean $(MAKE) -C utils clean + rm -f /etc/elsa_classification_rules.conf diff -uNr elsa-1.4/README elsa-1.4_automatic_classifier/README --- elsa-1.4/README 2006-09-05 12:13:08.000000000 +0530 +++ elsa-1.4_automatic_classifier/README 2007-08-09 15:35:52.000000000 +0530 @@ -143,3 +143,77 @@ All comments are welcome, The ELSA Team + +=========================================================================================================== + Why this automatic classifier required ? + +The idea behind using automatic classifier is to reduce the burdan of sys_admin from +reconfiguring the ELSA process membership, everytime system or jobd or any process restarts. + +In current situation, ELSA provides tools like "jobmng", "elsa" which has to be used +by sys-admins for manually classifying the processes into JOB groups. +As these classifications are based on PID's of processes and are not stored +permanantly, everytime system or jobd restarts, all classifications has to be +done again. This process can't be automated by scripting because PID's of new +processes wont be same. + +With this solution, system administrator needs to configure "elsa_classification_rules.conf" +file once, and then all processes will automatically classified with reference +to given policies. It wont require re-configuration even after system of jobd +daemon restarts. Whenever any new process is created, it will be automatically +classified. + + +=========================================================================================================== +Note: Our solution will work only when selinux is enabled (even permissive mode will do), +as we need selinux for giving security context of processes. If selinux is not available then +still ELSA will work, but there wont be any automatic classification of processes. +You can check the status of selinux by 'sestatus' command. + +We use selinux security context for identifying processes. +The advantage with selinux security contexts are that +1. They are persistant (i.e. they will remain same even after reboots) +2. They are flexible (i.e. Security context will differ depending on who is executing, + which program is being executed, and in what context it has been executed ) +3. They are configurable (i.e. you can change selinux policies to set selinux contexts as per your needs. + There are many user-friendly tools which helps in giving security context i.e. "seedit" ) +4. We are keeping ELSA classification rules seperate from selinux internal policies + This keeps ELSA classification management very simple. + You need to configure just one simple file. at "/etc/elsa_classification_rules.conf" +5. We provide added flexibility by supporting wild-characters in security context + (see "Guidelines for configuring elsa_classification_rules.conf" for more details ) + + +=========================================================================================================== + How it works internally ? + +At system startup time, "/etc/elsa_classification_rules.conf" file will be read for rules +that will be used for classification (function: job_daemon/classifier.c:file_parse()). + +After this maximum number of default groups will be found out by function find_default_group_number () +(function: job_daemon/classifier.c:find_default_group_number()). + +Now once ELSA initialization is done, then we create empty jobs that are specified in configuration file +by function (function: job_daemon/jobd.c:create_jobs_for_automatic_loading(no_of_jobs)). +This step is required because processes can come in any order, and if a process which is supposed to go +to job-id 5 comes before all other groups then we need to have job-id 5 ready. +Currently ELSA creates jobs in sequential order, thats why we create all jobs in the begining and keep them +ready for future use. +We are doing this by adding pid 1 repeatedly by using "do_add_req (1,0)" and then removing it, +everytime we add pid 1, it creates new group, and then we delete pid 1 at end from all groups. + +In recv_sk_nl, we have added support for "PROC_EVENT_EXEC" which calls function "security_context_classifier" +The support for "PROC_EVENT_EXEC" is needed because security context are normally changed in EXEC system call. +To handle this change in security context, this "PROC_EVENT_EXEC" message handling was needed. + +Even in case of "PROC_EVENT_FORK", we have modified "fork_handler" to call "security_context_classifier" +function first. If process can be classified on basis of security context, then that has given preference +over default classification which follows if "security_context_classifier" is not able to classify the +process. +This provides fail-safe machanism that even if selinux is not working, default ELSA behaviour will be there. + +"security_context_classifier" (Function: job_daemon/jobd.c:security_context_classifier) +This function takes pid of process to be classified, and find out the list of jobs to which it should +belong by using function "get_process_classification" function. +Then it uses "do_add_req" function to add specified process in corresponding group. +===========================================================================================================