PIM - pluggable identification modules library


#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.

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:

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>)