Rule Set Based Access Control - Module Registration (REG)

| # Rule Set Based Access Control (RSBAC) for Linux - Module Registration

# Module Registration (REG)

## Basics

REG is not a decision module. It is an interface to register your own decision module, which can, but need not, be implemented as a Linux kernel module. It allows registration for all relevant calls to decision code as well as for maintenance calls to the data structure implementation. From 1.1.1-pre4 onwards, it also allows for registration of system call functions to the REG syscall dispatcher.

You can find example kernel modules in rsbac/adf/reg/adf_sample*.c. If you chose 'Compile REG samples' in kernel configuration, the examples will be compiled and installed like all other modules.

If you prefer separate compilation of modules, there is a copy of these examples with a working Makefile and (from 1.1.1-pre4 onwards) a demonstration syscall tool in Documentation/rsbac/reg_samples.

Please note that this text is targeted for RSBAC version 1.1.1-pre4 onwards, because of the added functionality and (hopefully) easier use.

## Module Registration

All decision module registration handling is done with handles of type rsbac_reg_handle_t, which is just a signed 32 Bit integer. As often, positive values are used as real values and negative ones for errors.

REG data type and function definitions can be found in include/rsbac/reg.h, other type definitions are in include/rsbac/types.h. These header files should always be included by your model implementation.

A full list of RSBAC requests with possible targets is in targetsrequests.htm.

### Callback Functions

You can register the following functions:

- Decision function, called before access is granted. Return value is one of NOT_GRANTED, GRANTED, DO_NOT_CARE or UNDEFINED, as defined in include/rsbac/types.h.

    int rsbac_reg_request_func_t
      ( enum  rsbac_adf_request_t request,
              rsbac_pid_t caller_pid,
        enum  rsbac_target_t target,
        union rsbac_target_id_t tid,
        enum  rsbac_attribute_t attr,
        union rsbac_attribute_value_t attr_val,
        rsbac_uid_t owner); /* process owner */

- Notification function, called from most system calls as notification for data update after system call functionality has been performed. The parameters are the same as above, except for new_target and new_tid, where possible new objects are specified (e.g. new file in request CREATE).

    int rsbac_reg_set_attr_func_t
      ( enum  rsbac_adf_request_t request,
              rsbac_pid_t caller_pid,
        enum  rsbac_target_t target,
        union rsbac_target_id_t tid,
        enum  rsbac_target_t new_target,
        union rsbac_target_id_t new_tid,
        enum  rsbac_attribute_t attr,
        union rsbac_attribute_value_t attr_val,
        rsbac_uid_t owner); /* process owner */

- Overwrite decision function. This function returns TRUE or FALSE values, whether the file specified by dentry should be zeroed on truncation or deletion. If one model says yes, zeroing is performed.

    boolean rsbac_reg_need_overwrite_func
      (struct dentry * dentry_p);

- rsbac_write callback function. This function is called regularly by rsbacd or when requested via rsbac_write system call to allow for regular tasks to be done, usually writing dirty attribute lists to disks.
  The parameter need_lock tells, whether the lock_kernel() / unlock_kernel() functions must be placed around write-to-disk functions. Unfortunately, this differs depending on the rsbac_write caller function. The return value is the number of lists actually written (0 or more) or a negative error value, e.g. the value returned by rsbac_write_open.

    int rsbac_reg_write_func_t(boolean need_lock);

- rsbac_check callback function. This function is called when requested via rsbac_check system call to trigger consistency checks, usually once per day.
  If correct is not 0, errors should be automatically corrected. If check_inode is not 0, referenced inode numbers should also be checked. The return value is 0 on success or an error code otherwise.

    int rsbac_reg_check_func_t(int correct, int check_inode);

- Filesystem mount notification function. This function is called whenever a device has been mounted.

    int rsbac_reg_mount_func_t(kdev_t kdev);

- Filesystem umount notification function. This function is called whenever a device has been unmounted.

    int rsbac_reg_umount_func_t(kdev_t kdev);

### Registration Functions

For module registration, you first have to choose a positive, signed 32 Bit integer as your personal, secret handle. All registration related actions will later require this unique handle. If your handle happens to be already in use, registration will fail with an error value of -RSBAC_EEXIST.

The struct rsbac_reg_entry holds all necessary values. Functions can be left out by setting their pointer to NULL. Your module will later be identified in REG messages and proc files by its name. The current maximum name length is 30 characters.

The switch value determines, whether your module will be switched on or off at the very beginning. For security reasons, switching only works, if enabled in the kernel config.

    struct rsbac_reg_entry_t
      {
        rsbac_reg_handle_t                handle;
        char                              name[RSBAC_REG_NAME_LEN+1];
        rsbac_reg_request_func_t        * request_func;
        rsbac_reg_set_attr_func_t       * set_attr_func;
        rsbac_reg_need_overwrite_func_t * need_overwrite_func;
        rsbac_reg_write_func_t          * write_func;
        rsbac_reg_mount_func_t          * mount_func;
        rsbac_reg_umount_func_t         * umount_func;
        boolean                           switch_on; /* turned on initially? */
      };

Given this struct, registration is quite easy. Usually registration happens in the init_module() function of kernel modules, deregistration in cleanup_module(). Unloading a REG registered module without deregistration will kill your system!

The only thing added to the entry struct is a REG version value, which allows a version check for modules shipped as binaries. It should always be set to RSBAC_REG_VERSION. The registration function will return your positive handle or a negative error code (see include/rsbac/reg.h for more details).

    rsbac_reg_handle_t rsbac_reg_register
      (       rsbac_version_t    version,
       struct rsbac_reg_entry_t  entry);

Once successfully registered, you can switch the module on or off, if enabled in kernel config. As usual, this function will return 0 on success and a negative error code otherwise.

    int rsbac_reg_switch
      (rsbac_reg_handle_t handle, boolean value);

Unregistering is now simple.

    int rsbac_reg_unregister
      (rsbac_reg_handle_t handle);

OK, now your module should be working. If it also registered syscalls or proc entries, you should now have a look at its status.

Let me hint at the sample modules again - in the beginning using them as a base will be a good choice as well as save you a lot of typing.

## Syscall Registration

Registration of system calls is similar to that of the decision module itself. As with decision functions, there is no official limit on the number of syscalls you can register. Only the handles have to be different each time.

For security reasons, different handle values should be used for registration and syscall dispatching - syscall dispatcher handles must be public to be of any use.

Once you system call has been registered, it can be invoked by calling the official system call

    int sys_rsbac_reg
      (rsbac_reg_handle_t handle,
       void * arg);

The handle is the dispatcher handle you registered your syscall function with, the arg pointer will be passed directly to your function. Its return value will be the system call return value.

### Callback Functions

Your syscall function should look as follows:

    int rsbac_reg_syscall_func_t(void * data);

The pointer this function will get is a user space pointer. To work with your data, you will have to get it from user space first using

    #include <rsbac/helpers.h>
    int rsbac_get_user
      (unsigned char * kern_p, unsigned char * user_p, int size);

Your function's return code is passed back to the calling process.

### Registration Functions

For syscall registration, you now have to choose a positive, signed 32 Bit integer as your personal, secret registration handle and another positive, signed 32 Bit value as your public syscall dispatcher handle. All registration related actions will later require the unique secret handle. If one of your handles happens to be already in use, registration will fail with an error value of -RSBAC_EEXIST.

The struct rsbac_reg_syscall_entry holds all necessary values. Nothing but the name can be left out. Your system call will later be identified in REG messages and proc files by its name and its public dispatcher handle. The current maximum name length is 30 characters.

    struct rsbac_reg_syscall_entry_t
      {
        rsbac_reg_handle_t                registration_handle;
        rsbac_reg_handle_t                dispatcher_handle;
        char                              name[RSBAC_REG_NAME_LEN+1];
        rsbac_reg_syscall_func_t        * syscall_func;
      };

Given this struct, registration is similar to decision module registration. Unloading a module with a registered syscall without deregistration will kill your system on the next call to the syscall function!

Again added to the entry struct is a REG version value, which allows a version check for modules shipped as binaries. It should always be set to RSBAC_REG_VERSION. The registration function will return your positive handle or a negative error code (see include/rsbac/reg.h for more details).

    rsbac_reg_handle_t rsbac_reg_register_syscall
      (       rsbac_version_t            version,
       struct rsbac_reg_syscall_entry_t  entry);

Once successfully registered, you can use your new system call.Unregistering is now simple.

    int rsbac_reg_unregister_syscall
      (rsbac_reg_handle_t registration_handle);

Another hint at the sample modules: in the beginning using them as a base will be a good choice as well as save you a lot of typing.

## RSBAC Helper Functions

The RSBAC framework provides a lot of helper functions and variables, which should be used by your module, if appropiate. Here is a list of the most important ones.

### Generic Lists

From version 1.1.2, RSBAC provides persistent generic lists and lists of lists with custom index and data sizes and automatic backup proc file generation. It is strongly recommended to use them for all data storage.

### Value to Name Conversion

All these functions require preallocated space for the name string. For convenience, the same char pointer is returned. A declaration of

    char name[RSBAC_MAXNAMELEN];

is sufficient for all functions except for target_id_name, if logging of full path has been enabled in kernel config. In this case, one page (Linux macro PAGE_SIZE) should do.

These and several name-to-value functions can also be used in user space tools, just copy getname.c, compile it to getname.o and link that to your program. Look into RSBAC admin tools to see how to use this.

    #include <rsbac/getname.h>

-     char * get_request_name
        (char * request_name,
         enum  rsbac_adf_request_t request);

-     char * get_attribute_name
        (char * attr_name,
         enum rsbac_attribute_t attr);

-     char * get_scd_type_name
        (char * res_name,
         enum rsbac_scd_type_t res);

-     char * get_target_name
        (char * target_type_name,
         enum   rsbac_target_t target,
         char * target_id_name,
         union  rsbac_target_id_t tid);
      /* returns target_type_name. */
      /* target_id_name includes full identification
         with id numbers and path */

-     char * get_switch_target_name
        (char * switch_name,
         enum  rsbac_switch_target_t target);
      /* fixed module names */

-     char * get_error_name
        (char * error_name,
         int error);
      /* RSBAC error names only, other values are
         returned as decimal numbers. */

### Logging

    #include <rsbac/debug.h>

-     extern int rsbac_printk(const char *, ...);
      /* log to rsbac log - use like printk(9) */

### Access to User Space

    #include <rsbac/helpers.h>

-     int rsbac_get_user
        (unsigned char * kern_p, unsigned char * user_p, int len);
      /* get data from user space */

-     int rsbac_put_user
        (unsigned char * kern_p, unsigned char * user_p, int len);
      /* put data to user space */

-     char * rsbac_getname(const char * name);
      /* allocate a page and copy name from userspace */

-     void rsbac_putname(const char * name);
      /* deallocate the page */

### General Attributes

Please do not change any values or remove items, unless you know exactly what you are doing - other models depend on them.

    #include <rsbac/aci.h>

-     int rsbac_get_attr
        (enum  rsbac_target_t target,
         union rsbac_target_id_t tid,
         enum  rsbac_attribute_t attr,
         union rsbac_attribute_value_t * attr_val_p,
         boolean inherit);
      /* read an attribute value, possibly inherited */

-     int rsbac_set_attr
        (enum  rsbac_target_t target,
         union rsbac_target_id_t tid,
         enum  rsbac_attribute_t attr,
         union rsbac_attribute_value_t attr_val);
      /* modify attribute value - automatically creates
         a list item, if not yet there */

-     int rsbac_remove_target
        (enum rsbac_target_t target,
         union rsbac_target_id_t tid);
      /* remove list item -> reset all values to defaults */

### Access to /rsbac Dir and Files

Please note that file access must not be done with normal syscall functions - those are intercepted and possibly blocked. Hold rsbac_write_sem from before opening until file is closed.

Warning: reading and writing to and from kernel space requires the segment descriptor to be changed - see reg_sample2.c as an example how to do all this.

    #include <rsbac/aci_data_structures.h>

-     kdev_t rsbac_root_dev; /* System root device */

-     extern struct semaphore rsbac_write_sem;
      /* semaphore, to be held when writing to rsbac dir */

-     int rsbac_read_open (char *filename,
                           struct file *file,
                           kdev_t dev);
      /* read-open a file in protected rsbac dir on
         device dev. filename must be basename (no /). */

-     int rsbac_write_open(char *filename,
                           struct file *file,
                           kdev_t kdev);
      /* same for write-open */

-     void rsbac_read_close(struct file *);
      /* close with cleanup */

-     void rsbac_write_close(struct file *);

### Proc file/dir registration

Please use the standard Linux kernel functions create_proc_entry() and remove_proc_entry() to register to these directories.

    #include <rsbac/procfs.h>

-     struct proc_dir_entry * proc_rsbac_root_p;
      /* rsbac proc root for /proc/rsbac-info */

-     struct proc_dir_entry * proc_rsbac_backup_p;
      /* /proc/rsbac-info/backup for ds raw backup */

To top of page

Back to RSBAC Homepage -

Questions, tips, etc.

03-Dec-01, -ao