===== Basics =====
Generic list management 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 (not yet fully implemented)
Examples of list usage can be found in REG sample 3 and in rsbac/data_structures/*_data_structures.c, which have been ported to generic lists and lost much of their 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);
* Function to compare two datas, returns 0, if equal, and another value, if not. Used for lookup by data.
Note: List optimization is based on descriptors, so data lookup is always linear search from first to last element in list order.
int rsbac_list_data_compare_function_t
(void * data1, void * data2);
* 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);
There also is a function useful when using %%__u32%% values as descriptors. It compares two %%__u32%% descriptors and can be used instead of implementing your own rsbac_list_compare_function_t function.
int rsbac_list_compare_u32(void * desc1, void * desc2);
The following function returns the Generic List registration version
rsbac_version_t rsbac_list_version(void);
==== 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
* RSBAC_LIST_DEF_DATA: Use provided default data, return it for unexisting items and automatically create and cleanup items with default data as necessary.
Note: only items with 0 ttl (unlimited) get removed, and lol items with default data only get removed, if they have no subitems.
* RSBAC_LIST_DEF_SUBDATA: Use provided default subitem data, return it for unexisting subitems and automatically create and cleanup subitems with default data as necessary.
Note: only subitems with 0 ttl (unlimited) get removed.
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.)
* def_data: default data value for flag RSBAC_LIST_DEF_DATA (if NULL, flag is cleared)
* def_subdata: default subdata value for flag RSBAC_LIST_DEF_SUBDATA (if NULL, flag is cleared)
* 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,
void * def_data,
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,
void * def_data,
void * def_subdata,
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. Also time-to-live property has been added: when specified, the item gets automatically removed after the given amount of seconds.
==== 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 with time-to-live in seconds. Set the value of ttl to 0 for unlimited (default), or to RSBAC_LIST_TTL_KEEP to keep the old ttl value of an existing item.
int rsbac_list_add_ttl(
rsbac_list_handle_t handle,
rsbac_time_t ttl,
void * desc,
void * data);
int rsbac_list_add_ttl_u32(
rsbac_list_handle_t handle,
rsbac_time_t ttl,
__u32 desc,
void * data);
Add list of lists sublist item. The item for desc must exist, if list has been registered without RSBAC_LIST_DEF_DATA, otherwise it is created on the fly.
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 sublist item with ttl. Set the value of ttl to 0 for unlimited (default), or to RSBAC_LIST_TTL_KEEP to keep the old ttl value of an existing item.
int rsbac_list_lol_subadd_ttl(
rsbac_list_handle_t handle,
rsbac_time_t ttl,
void * desc,
void * subdesc,
void * subdata);
int rsbac_list_lol_subadd_ttl_u32(
rsbac_list_handle_t handle,
rsbac_time_t ttl,
__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);
Add list of lists top level item with ttl. Set the value of ttl to 0 for unlimited (default), or to RSBAC_LIST_TTL_KEEP to keep the old ttl value of an existing item.
int rsbac_list_lol_add_ttl(
rsbac_list_handle_t handle,
rsbac_time_t ttl,
void * desc,
void * data);
int rsbac_list_lol_add_ttl_u32(
rsbac_list_handle_t handle,
rsbac_time_t ttl,
__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);
Remove all subitems from list
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);
Remove item from the top-level list of a list-of-lists
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);
Remove all items from a list-of-lists
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);
Also get time-to-live. Both ttl_p and data can be NULL, they are then simply not returned
int rsbac_list_get_data_ttl(
rsbac_list_handle_t handle,
rsbac_time_t * ttl_p,
void * desc,
void * data);
int rsbac_list_get_data_ttl_u32(
rsbac_list_handle_t handle,
rsbac_time_t * ttl_p,
__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);
Also get time-to-live. Both ttl_p and data can be NULL, they are then simply not returned
int rsbac_list_lol_get_subdata_ttl(
rsbac_list_handle_t handle,
rsbac_time_t * ttl_p,
void * desc,
void * subdesc,
void * subdata);
int rsbac_list_lol_get_subdata_ttl_u32(
rsbac_list_handle_t handle,
rsbac_time_t * ttl_p,
__u32 desc,
__u32 subdesc,
void * data);
Get data from the top-level item.
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);
Also get time-to-live. Both ttl_p and data can be NULL, they are then simply not returned
int rsbac_list_lol_get_data_ttl(
rsbac_list_handle_t handle,
rsbac_time_t * ttl_p,
void * desc,
void * data);
int rsbac_list_lol_get_data_ttl_u32(
rsbac_list_handle_t handle,
rsbac_time_t * ttl_p,
__u32 desc,
void * data);
==== Retrieve item desc by data ====
Item desc is copied - we cannot give a pointer, because item could be removed. If no compare function is provided (NULL value), memcmp is used.
Note: The stored list value is always used as first and the data value given here is always used as second parameter to the compare function, so you can use different types for storage and lookup.
int rsbac_list_get_desc(
rsbac_list_handle_t handle,
void * desc,
void * data,
rsbac_list_data_compare_function_t compare);
int rsbac_list_get_desc_u32(
rsbac_list_handle_t handle,
void * desc,
__u32 data);
Get maximum descriptor value in the list (according to its compare function).
int rsbac_list_get_max_desc(
rsbac_list_handle_t handle,
void * desc);
==== 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_subdesc_ttl(
rsbac_list_handle_t handle,
void * desc,
void ** array_p,
rsbac_time_t ** ttl_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_get_all_items_ttl(
rsbac_list_handle_t handle,
void ** array_p,
rsbac_time_t ** ttl_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_subitems_ttl(
rsbac_list_handle_t handle,
void * desc,
void ** array_p,
rsbac_time_t ** ttl_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.