#include <pim.h>
unsigned int PIM_set_service(char
*new_service)
char *PIM_get_service(void))
#include <pim_internals.h>
PIM_function *init_user(char *args,char
**config)
PIM_function *init_group(char *args,char
**config)
This manual page documents the use of the PIM library, both for PIM modules developpers and for PIM-aware applications writers.
The PIM library primary's objective is to enable the system administrator to setup a different identification policy on a per application basis, even with applications that do not provide this possibility.
This implies that PIM's action must be transparent for the
application. Basically, developpers do not have anything
particular to do in order to write PIM-compliant code: they can
call getpwnam(3), getpwuid(3),
getpwent(3), endpwent(3), and the
corresponding group management series of functions as usual,
and PIM will transparently handle the job.
A few details still may be of utility or may need to be taken into account, though.
dlopen(3).pim(7) for detail.At last, an additional function is available to PIM-aware applications. It enables the application to specify the name of the associated PIM service, overriding the PIM_SERVICE environment variable's value. Note that this function should be called only once during program's initialization, and whenever the PIM service is changed in the program's configuration. Calls under different circumstances should be avoided, because the service setting is a time consuming operation. Here is the call syntax:
unsigned int PIM_set_service(char
*new_service)
Where new_service is the name of the PIM service to use from
now on. The function returns 0 on error (and then sets errno
appropriately), and a non 0 value on success. Note the
new_service pointer is used as is and thus should remain
valid until the next call to PIM_set_service.
The current value of the PIM service string is readable through a call to the PIM_get_service function:
char
*PIM_get_service(void)
This function returns the value of the current service in a static buffer. It doesn't fail.
None of these additional functions is reentrant. They mustn't be called from signal handlers or from concurrent threads.
Writing a PIM module is not a very complicated task, but many details are to be considered. PIM modules writers should remember that their code will frequently be called from daemons (FTP servers, HTTP servers, etc), hence security sensitive applications. Keep this in mind: this is your and other's security you are playing with.
A PIM module may support user management functions, or group
management functions, or both. If a module provides support for
user management functions, it must also provide a
init_user function, with the following prototype:
PIM_function *init_user(char *args,char
**config)
If support for group management functions is provided, the following function must be provided too:
PIM_function *init_group(char *args,char
**config)
Both of these functions are called during PIM's initialization and each time PIM_set_service is called. They are not required to be signal safe or reentrant.
The args parameter is the module's
configuration string, and is given as is. It is up to the
module's developper to decide what the syntax of this line is.
Remember it is currently limited to about 8000 characters.
The config parameter is provided to the module
for internal use. The module may or may not use this parameter.
Each subsequent call to the module's functions will provide the
value of config back. It is up to the module to
allocate memory and make config point to this
memory when init_user or init_group
are called. A different config pointer is used for
user management functions and group management functions.
At last, init_user and init_group
should return NULL if something went wrong, or an array of
function pointers if the module's initialization is
successful.
The returned statically allocated array must contain five
pointers, in the following order:
getpwnam/getgrnam replacement,
getpwuid/getgrgid replacement,
getpwent/getgrent replacement,
setpwent/setgrent replacement,
endpwent/endgrent replacement.
All of these functions must be provided. They have the following prototype:
void function_name(PIM_func_dialog
*dialog,void *current_result,char *config)
The dialog argument has the following fields:
PIM_status status, that should be set to
PIM_S_ERROR if an error happens that prevents
the function from working, or to PIM_S_OK if the
function worked (even if it encountered a minor error, for
instance if it couldn't sucessfully close a file), this field
should also be set to PIM_S_NOTHING if the
function didn't do anything, and should not have been called,
for instance, a replacement for setgrent(3)
called before the equivalent replacement for
getgrent(3) should set status to
PIM_S_NOTHING. Note this is not an error condition,
though. It only means the application is probably ill
coded.PIM_id_or_nam parameter this is the
parameter that has been given by the caller to the wrapped
function: if this function expects to receive an UID,
parameter.uid will be this UID. Alternatively,
parameter.gid and parameter.name may be
used for a GID or a string.void *return_value the value that the
replacement function would return to the caller of the
wrapped function (for instance, this will be a pointer to a
statically allocated struct passwd for a
getpwnam(3) replacement).The current_result parameter has a self
explanatory name: it points to the result that PIM thinks it
will return to the caller. This may change depending on the
result of the current and further, if any, function
replacements calls. Replacement functions should not modify
this directly, it will be done later by pim, depending on their
return values, and the configuration file.
The config parameter is the value of the
pointer that init_user or init_group
may have initialized.
At last, these function replacements should set
errno appropriately when an error occurs or when they
simply don't have any entry matching the request. For instance,
getpwnam(3) and getpwuid(3)
replacements should set errno to ENOENT when they
don't have any entry matching the request.
Two simple samples of PIM modules are the
pim_unix_user.c and pim_unix_group.c that
are included in the modules directory in the main
PIM distribution.
At last, for testing purposes, two applications programs are
provided with the PIM distribution: pwtest and
grtest. Run them without arguments for usage
instructions.
As stated above, PIM modules may have a considerable impact
on system security. Care should thus be taken when writing
them. All functions return codes should be checked, yes, even
calls to malloc(3) and close(2).
Also, as PIM modules can be called from any application, the status of standard files (stdin, stdout, and stderr) will be unknown, these files shouldn't be used by modules. Output should be directed to syslog(3). Log messages should clearly state the module's name, and be self-explanatory. Remember that a failure in a module probably means a partial system failure, thus other modules will also probably fail, and this will generate a lot of errors in the log system. Thus, all messages should include the module's name.
Also note that a calling program does not expect
getpwnam() to cause program termination. Thus, calls to
_exit(2) and exit(3) may break the
caller's security model and mustn't be employed.
At last, as PIM modules may be called from an unsafe environment, PIM modules behaviour mustn't depend on environment variables, in particular, when they call external libraries that rely on environment variables, these variables should be explicitly set or unset by the module.
pim(7), pim(5),
pam(7)
Brieuc "BBP" Jeunhomme (<bbp@via.ecp.fr>)