Rule Set Based Access Control - Generic Lists

| # Rule Set Based Access Control (RSBAC) for Linux - Generic Lists

# Generic Lists

## Basics

In version 1.1.2, a generic list management has been added. It provides an infrastructure to simply define and register a list or a list of sublists, which can optionally be kept persistent or have a /proc/rsbac-info/backup entry. All management, like SMP locking, on-disk storage etc., is done internally without bothering the registering module.

Keeping and using generic list managment has some advantages:

- Huge reduction of list handling code
- Bug avoidance instead of boring copy-and-paste for several lists
- No SMP locking design needed
- Central optimization
- No disk access from modules necessary
- On-disk data tracking and garbage collection for unused data

Examples of list usage can be found in REG sample 3 and in rsbac/data_structures/pm_data_structures.c, which has already been ported to generic lists and lost two thirds of its source code on the way.

## List Registration

All list registration handling is done with abstract handles of type rsbac_list_handle_t, which is just a void pointer. As usual, the NULL value is used for 'undefined' or 'not yet registered'.

List data type and function definitions can be found in include/rsbac/lists.h, other type definitions are in include/rsbac/types.h. These header files should always be included for registration.

### Callback Functions

The following help functions can be provided by the registrant:

- Function to compare two descriptors, returns 0, if equal, a negative value, if desc1 < desc2 and a positive value, if desc1 > desc2 (like memcmp). Used for lookup and list optimization.
  Note: Non-0 values are only used for list optimization and do not necessarily imply a real order of values.

    int rsbac_list_compare_function_t
      (void * desc1, void * desc2);

- Conversion function to upconvert old on-disk descs and datas to actual version, must return 0 on success or error otherwise
  Attention: if old or new data_size is 0, the respective data pointer is NULL!

    int rsbac_list_conv_function_t(
            void * old_desc,
            void * old_data,
            void * new_desc,
            void * new_data);

- Callback function to return an upconvert function for on-disk-version, if versions differ.
  Note: Lists implementation does not assume anything about your version number apart from being of type rsbac_version_t, which is just a 32Bit unsigned integer. Use it as you like..

    rsbac_list_conv_function_t *
      rsbac_list_get_conv_t(rsbac_version_t old_version);

### The list_info Structures

The rsbac_list_info and rsbac_lists_lol_info structs contain all data that will be stored persistently, i.e. written to disk. They have the following items:

- list_version: a simple __u32 version number for the list. If old on-disk version is
  different, conversion is tried (depending on flags and get_conv function)
- key: secret __u32 key, which must be the same as in on-disk version, if persistent
- desc_size: size of the descriptor. An error is returned, if list exists and value differs. It is internally set to sizeof(__u32) for u32 call variants.
- data_size: size of data. An error is returned, if list exists and value differs. Set to 0 for sets without data.
- subdesc_size (list-of-lists only): size of the descriptor of the sublist. An error is returned, if list exists and and value differs. It is internally set to sizeof(__u32) for u32 call variants.
- subdata_size (list-of-lists only): size of sublist data. An error is returned, if list exists and value differs. Set to 0 for sets without data
- max_age: seconds until unchanged list file (no add or remove) will be purged. Maximum value is RSBAC_LIST_MAX_AGE_LIMIT (more than 10 years), use 0 for unlimited lifetime. (purging not yet implemented - only reused without key, please cleanup by hand)

    struct rsbac_list_info_t
      {
        rsbac_version_t version;
        rsbac_list_key_t key;
        __u32 desc_size;
        __u32 data_size;
        rsbac_time_t max_age;
      };

    struct rsbac_list_lol_info_t
      {
        rsbac_version_t version;
        rsbac_list_key_t key;
        __u32 desc_size;
        __u32 data_size;
        __u32 subdesc_size;
        __u32 subdata_size;
        rsbac_time_t max_age;
      };

### Registration Functions

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

The list_info structs (s.a.) hold all necessary values. Functions can be left out by setting their pointer to NULL. Your module will later be identified in list messages and proc files by its name, access will require the handle returned by registration. The current maximum name length is 30 characters.

Registration is quite easy. It usually happens in the init_module() function of kernel modules, deregistration in cleanup_module(). Unloading a kernel module without deregistration will keep all registered lists in memory and prevent their next registration until you reboot!

Registration flags are:

- RSBAC_LIST_PERSIST: Make persistent, i.e., save to and restore from disk
- RSBAC_LIST_IGNORE_OLD: Ignore old list contents (still checks key, if list exists on disk)
- RSBAC_LIST_IGNORE_UNSUPP_VERSION: Ignore old list contents, if version upconversion is not supported (no get_conv, or get_conv returned NULL) - without this flag, registration fails, if list cannot be converted.
- RSBAC_LIST_NO_WRITE: Temporarily disallow writing list to disk, e.g. for upgrade tests. Also see no_write functions below.
- RSBAC_LIST_BACKUP: Provide a binary backup file as /proc/rsbac-info/backup/listfilename

There are several registration parameters additional to the list_info struct:

- ds_version: for binary modules, must be RSBAC_LIST_VERSION. If version differs, return error.
- handle_p: for all list accesses, an opaque handle of type rsbac_list_handle is put into *handle_p.
- flags: or'd combination of registration flags (s.a.)
- compare: for lookup and list optimization, can be NULL, then memcmp(desc1, desc2, desc_size) is used (s.a.)
- subcompare: for item lookup and optimization of sublist, can be NULL, then memcmp(desc1, desc2, desc_size) is used (s.a.)
- get_conv: function to deliver conversion function for given version. Can be NULL for no conversion. (s.a.)
- get_subconv: function to deliver sublist item conversion function for given version. Can be NULL for no conversion. (s.a.)
- name: the on-disk name, must be distinct and should be max. 7 or 8.2 chars (maxlen of RSBAC_LIST_MAX_FILENAME supported) (only used for statistics, if non-persistent)
- device: the device to read list from or to save list to - use 0 for root dev (ignored, if non-persistent)

    int rsbac_list_register(
      rsbac_version_t ds_version,
      rsbac_list_handle_t *handle_p,
      struct rsbac_list_info_t info,
      u_int flags,
      rsbac_list_compare_function_t * compare,
      rsbac_list_get_conv_t * get_conv,
      char * name,
      kdev_t device);

    int rsbac_list_lol_register(
      rsbac_version_t ds_version,
      rsbac_list_handle_t *handle_p,
      struct rsbac_list_lol_info_t info,
      u_int flags,
      rsbac_list_compare_function_t * compare,
      rsbac_list_compare_function_t * subcompare,
      rsbac_list_get_conv_t * get_conv,
      rsbac_list_get_conv_t * get_subconv,
      char * name,
      kdev_t device);

Once successfully registered, you can use the (possibly restored) lists with the access functions, which are described below.

If the list is no longer needed, you can either destroy it, then the on-disk version is deleted, or detach from it, then the list is stored to disk and removed from memory only. For security, you need both your handle and the key from the list_info struct.

    int rsbac_list_destroy(
      rsbac_list_handle_t * handle_p, rsbac_list_key_t key);

    int rsbac_list_lol_destroy(
      rsbac_list_handle_t * handle_p, rsbac_list_key_t key);

Finally, you can set or unset your list's no_write flag to temporarily enable or disable on-disk storage. This can e.g. be used for a simple transaction scheme. The no_write value is either TRUE (do not write) or FALSE (do write).

    int rsbac_list_no_write(
      rsbac_list_handle_t handle,
      rsbac_list_key_t key,
      boolean no_write);

    int rsbac_list_lol_no_write(
      rsbac_list_handle_t handle,
      rsbac_list_key_t key,
      boolean no_write);

OK, your list is working. You should now have a look at its status, which is shown in /proc/rsbac-info/gen_lists.

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

## Using the Lists

Items can be added, modified, removed, looked up, retrieved and counted. For most functions, there is also a wrapper for unsigned 32 bit integer descriptors, so that you can pass values instead of pointers.

### Add or modify items

If item for desc exists, the data is updated. data can be NULL, if list is registered with data_size 0 (used as set).

    int rsbac_list_add(
      rsbac_list_handle_t handle,
      void * desc,
      void * data);

    int rsbac_list_add_u32
      (rsbac_list_handle_t handle, __u32 desc, void * data);

Add list of lists sublist item, item for desc must exist.

    int rsbac_list_lol_subadd(
      rsbac_list_handle_t handle,
      void * desc,
      void * subdesc,
      void * subdata);

    int rsbac_list_lol_subadd_u32(
      rsbac_list_handle_t handle,
      __u32 desc,
      __u32 subdesc,
      void * subdata);

Add list of lists top level item.

    int rsbac_list_lol_add(
      rsbac_list_handle_t handle,
      void * desc,
      void * data);

    int rsbac_list_lol_add_u32
      (rsbac_list_handle_t handle, __u32 desc, void * data);

### Remove items

    int rsbac_list_remove
      (rsbac_list_handle_t handle, void * desc);

    int rsbac_list_remove_u32
      (rsbac_list_handle_t handle, __u32 desc);

Remove all items.

    int rsbac_list_remove_all(rsbac_list_handle_t handle);

Remove item from sublist - also succeeds, if item for desc or subdesc does not exist.

    int rsbac_list_lol_subremove(
      rsbac_list_handle_t handle,
      void * desc,
      void * subdesc);

    int rsbac_list_lol_subremove_u32
      (rsbac_list_handle_t handle, __u32 desc, __u32 subdesc);

    int rsbac_list_lol_subremove_all
      (rsbac_list_handle_t handle, void * desc);

    int rsbac_list_lol_subremove_all_u32
      (rsbac_list_handle_t handle, __u32 desc);

    int rsbac_list_lol_remove(
      rsbac_list_handle_t handle,
      void * desc);

    int rsbac_list_lol_remove_u32
      (rsbac_list_handle_t handle, __u32 desc);

    int rsbac_list_lol_remove_all(rsbac_list_handle_t handle);

### Retrieve item data

Item data is always copied - we cannot give a pointer, because item could be removed.

    int rsbac_list_get_data(
      rsbac_list_handle_t handle,
      void * desc,
      void * data);

    int rsbac_list_get_data_u32(
      rsbac_list_handle_t handle,
      __u32 desc,
      void * data);

Get data from a subitem.

    int rsbac_list_lol_get_subdata(
      rsbac_list_handle_t handle,
      void * desc,
      void * subdesc,
      void * subdata);

    int rsbac_list_lol_get_subdata_u32(
      rsbac_list_handle_t handle,
      __u32 desc,
      __u32 subdesc,
      void * data);

    int rsbac_list_lol_get_data(
      rsbac_list_handle_t handle,
      void * desc,
      void * data);

    int rsbac_list_lol_get_data_u32(
      rsbac_list_handle_t handle,
      __u32 desc,
      void * data);

### Lookup items

Items can be simply checked for existence. All functions return TRUE, if item exists, and FALSE, if not.

    int rsbac_list_exist(
      rsbac_list_handle_t handle,
      void * desc);

    int rsbac_list_exist_u32(
      rsbac_list_handle_t handle,
      __u32 desc);

    int rsbac_list_lol_subexist(
      rsbac_list_handle_t handle,
      void * desc,
      void * subdesc);

    int rsbac_list_lol_subexist_u32(
      rsbac_list_handle_t handle,
      __u32 desc,
      __u32 subdesc)

    int rsbac_list_lol_exist(
      rsbac_list_handle_t handle,
      void * desc);

    int rsbac_list_lol_exist_u32(
      rsbac_list_handle_t handle,
      __u32 desc);

### Count items

All functions return the number of elements or a negative error code.

    long rsbac_list_count(rsbac_list_handle_t handle);

    long rsbac_list_lol_subcount(
      rsbac_list_handle_t handle,
      void * desc);

    long rsbac_list_lol_all_subcount
      (rsbac_list_handle_t handle);

    long rsbac_list_lol_count(rsbac_list_handle_t handle);

### Retrieve all items and/or all data

All functions return the number of elements or a negative error code.

Get array of all descriptors. If the return value is positive, *array_p contains a pointer to a vmalloc'd array of descs, otherwise *array_p is set to NULL. If *array_p has been set, caller must call vfree(*array_p) after use!

    long rsbac_list_get_all_desc(
      rsbac_list_handle_t handle,
      void ** array_p);

    long rsbac_list_lol_get_all_subdesc(
      rsbac_list_handle_t handle,
      void * desc,
      void ** array_p);

    long rsbac_list_lol_get_all_desc(
      rsbac_list_handle_t handle,
      void ** array_p);

Get array of all datas. If the return value is positive, *array_p contains a pointer to a vmalloc'd array of datas, otherwise *array_p is set to NULL. If *array_p has been set, caller must call vfree(*array_p) after use!

    long rsbac_list_get_all_data(
      rsbac_list_handle_t handle,
      void ** array_p);

    long rsbac_list_lol_get_all_subdata(
      rsbac_list_handle_t handle,
      void * desc,
      void ** array_p);

    long rsbac_list_lol_get_all_data(
      rsbac_list_handle_t handle,
      void ** array_p);

Get item size.

    int rsbac_list_get_item_size
      (rsbac_list_handle_t handle);

    int rsbac_list_lol_get_subitem_size
      (rsbac_list_handle_t handle);

    int rsbac_list_lol_get_item_size
      (rsbac_list_handle_t handle);

Get array of all items. If the return value is positive, *array_p contains a pointer to a vmalloc'd array of items, where desc and data are placed directly behind each other. If *array_p has been set, caller must call vfree(*array_p) after use!

    long rsbac_list_get_all_items(
      rsbac_list_handle_t handle,
      void ** array_p);

    long rsbac_list_lol_get_all_subitems(
      rsbac_list_handle_t handle,
      void * desc,
      void ** array_p);

    long rsbac_list_lol_get_all_items(
      rsbac_list_handle_t handle,
      void ** array_p);

## RSBAC Helper Functions

The RSBAC framework provides a lot of helper functions and variables, which should be used by your module, if appropiate. You can find a list of the most important ones in the REG description.

To top of page

Back to RSBAC Homepage -

Questions, tips, etc.

03-Dec-01, -ao