The PWDB Library Guide: Implementation

Next Previous Contents

## 3. Implementation

This will get completed as we develop the library

## 3.1 The core (public) functions

`libpwdb` offers the following generic interface:

### Initialization and termination

- `int pwdb_start(void)`

  Initialize the library for use by the current application Will read the configuration file and publicize the database policies (as they are listed in `/etc/pwdb.conf`) in `const int *pwdb_policy` and `const int *pwdb_group_policy`. The library maintains a count of the number of times it has been `pwdb_start()`ed.

- `int pwdb_end(void)`

  Once the `pwdb_start()`ed count returns to zero, this function closes down the library and free()'s all memory allocated by it. Any attempt to `pwdb_end()` the library more times than it has been `pwdb_start()`ed will cause `PWDB_ABORT` to be returned. This feature can be used by an application to guarantee the library is shutdown..

  > `

  >         while (pwdb_end() == PWDB_SUCCESS);

  > `

### Structure management

- `int pwdb_entry_delete(const struct pwdb_entry **e)`

  `free()` the memory associated with the pointer `*e`. Its value will be overwritten with `'\\0'`s before the memory is `free()`d. This is reassuring from the point of view of minimizing security problems. This function should be used to liberate a `pwdb_entry` returned by `pwdb_get_entry()`.

- `int pwdb_get_entry(const struct pwdb *p, const char *entry, const struct pwdb_entry **e);`

  Read (and duplicate) an entry from the argument pwdb structure. Should the requested entry not prove to be present, `PWDB_NOT_FOUND` is returned.

- `int pwdb_set_entry(const struct pwdb *p, const char *entry, const void *datum, const int length, int (*compare)(const void *, const void *, int), int (*strvalue)(const void *, char *, int), int max_strval_size))`

  Set an entry in the argument pwdb structure. One can delete an entry from a struct pwdb by calling this function with `NULL` for `datum` and `-1` for `length`.

  The two functions passed as arguments in this call are as follows:

  `int compare(const void *value1, const void *value2, int length)`

    compare the two entry `value`s. They are both of given `length` in `char`s.

  `int strvalue(const void *value, char *buffer, int length)`

    Produce a text version of the given entry `value`. The `buffer` is the destination of the output text and the `length` is that of the `*value` in bytes. Note, the buffer is guaranteed by the calling process to be long enough for the output text.

- `int pwdb_delete(const struct pwdb **old)`

  Applications use this to liberate the memory associated with a `pwdb` structure. Following a successful `free()`, `*old` is set to `NULL`.

- `int pwdb_new(const struct pwdb **new, int life_sec)`

  Applications can request a new (blank) `pwdb` structure with this call. Note that it is returned as a `const` structure. This is to prevent the application from altering the structure directly. An application should use the library functions to modify this structure. The `life_sec` time is used to limit the length of time this pwdb structure will be valid for. It is some number of seconds from the present. If `life_sec` is non-zero, the `pwdb` structure will expire in that many seconds. Zero indicates the `pwdb` structure will non expire.

  > `

  >     int pwdb_merge(const struct pwdb *target, const struct pwdb *source,
  >                    int overwrite)

  > `

  this function copies the elements of `source` to `target`. If `overwrite` is `PWDB_TRUE` then all elements of `source` that are also in `target` will be overwritten by this call.

- `int pwdb_expire(const struct pwdb *p, int life_sec)` This function can shorten the lifetime of a the referenced `struct pwdb`. It computes the expiry time from the present with respect to `life_sec` and if this is before the expiration time currently associated with the `*p` structure, it shortens the life of the structure accordingly.

  Note, it is not possible to extend the life of a `pwdb` structure, only to shorten it. An argument of `0` or less will result in the immediate expiry of the `pwdb` structure.

- `int pwdb_source(const struct pwdb *old, const pwdb_type *src, const char *class, const char *name, const int id)` This function is used to set the source of the indicate `pwdb` structure. The argument `src` is a pointer to a list of `pwdb_type` entries. This list is terminated with a `_PWDB_MAX_TYPES` item. Valid types are listed in the `<security/pwdb_public.h>` file. The remaining arguments are used to initialize the caching facilities.

### Database query functions

- `int pwdb_locate(const char *class, const pwdb_type *src, const char *name, const int id, struct pwdb **p)`

  Obtain the entry for a given name and/or id in the indicated database. If `*p` is not `NULL` the database-module may choose to use the information it contains. It is intended that information obtained with this function is merged with that of the original `*p` structure. If `*p` is `NULL`, then a struct pwdb is allocated by the `pwdb_locate()` function in the first module used (and eventually merged with the subsequent modules, depending on the local setup). The `class` is the kind of database we are searching, examples include `user` and `group`.

- `int pwdb_request(const char *class, const pwdb_type *src, const char *entry, struct pwdb **p)`

  This function will further query the database, for an entry that may depend on the entries already present in the `*p` structure. The `entry`s that can be appended to the existing `*p` structure are defined for the `class` of database. For example, one may request the "groupids" `entry` from `"group"` class, which will search the group database for the list of groups to which a given user belongs. The name of the user is passed as an entry in the preexisting `pwdb` structure.

- `int pwdb_replace(const char *class, const pwdb_type *src, const char *name, const int id, struct pwdb **p)`

  Add/replace the entry for a name and/or id in the indicated list of databases. The fields for the new database entries are taken from the `p` argument. The `src` argument is the list of entries that will be updated.

- `int pwdb_remove(const char *class, const pwdb_type *src, const char *name, const int id, struct pwdb **p)`

  Remove the entry for the indicated `name` and/or `id` in the indicated database. If not `NULL` the remove function may obtain access information from the `p` argument.

  Note, this function only acts on the specified `class` of database. If reference to a `name` or `id` is present in another `class` of database, then it is the responsibility of the application to purge these databases too. The `pwdb_remove()` function is not sufficiently powerful to follow up on cross-references.

  As an example of the above concern, consider the removal of `name` ``joe'' from the `"user"` database. The `"group"` database entries that refer to ``joe'' as a group-member are unaffected by this `pwdb_remove()` request. Instead, it is the responsibility of the calling application to search for such entries and systematically `pwdb_remove()` them.

- `int pwdb_support(const char *class, const pwdb_type *src, const char *entry_name)`

  Indicate whether the given entry name is supported by the given database(s). `PWDB_SUCCESS` indicates yes. Anything else is NO (or something more specific, could be ``yes, if you supply a pass_phrase'' for example).

- `int pwdb_flags(const char *class, const pwdb_type *db, pwdb_flag *flag_p)`

  In order to know in advance whether a process is able to read/modify a specified database, this command is provided by each module. The input arguments are the `class` of database (`user`, `group` etc.), `db` (the database(s) we are going to use) and some storage space for the returned flags.

  Valid return flags which can be logically OR'd together are:

  `PWDB_F_NOACCESS`

    insufficient privilege to access database

  `PWDB_F_NOUPDATE`

    insufficient privilege to alter an entry

  `PWDB_F_PASS_PHRASE`

    to access the database, the process must supply a "pass_phrase" entry with a preallocated pwdb structure (use pwdb_new() call)

  ..and..

    more flags are likely to be added

  To establish if a given flag is set the following macro is supplied:

- `pwdb_on(flag, PWDB_F_XXX)`

  it returns TRUE(`1`) or FALSE(`0`).

### Diagnostics

- `const char *pwdb_strerror(int pwdb_error)`

  return a textual description of the indicated return value.

  Valid return codes are:

  `PWDB_SUCCESS`

    task completed successfully

  `PWDB_BAD_REQUEST`

    request not recognized

  `PWDB_TOO_WEAK`

    insufficient privilege for operation

  `PWDB_ABORT`

    internal failure - seek help

  `PWDB_BLOCKED`

    another process has locked resource

  `PWDB_MALLOC`

    insufficient memory for operation

  `PWDB_NOT_FOUND`

    requested item was not found

  `PWDB_PASS_PHRASE_REQD`

    pass_phrase needed to satisfy request here the application should supply a pwdb structure with a "pass_phrase" entry and call the function again

  `PWDB_CONF_ERR`

    there is a problem with the PWDB_CONF file.

  `PWDB_EXPIRED`

    the referenced pwdb structure has expired it is no longer valid and should be deleted.

  `PWDB_UNSUPPORTED`

    this function is not supported by some module (not supported means also unimplemented, for a while...)

  `PWDB_TIMEOUT`

    a timeout occured while performing the function. Presently could show up only when using RADIUS interface.

### Types of database

The pwdb_type of database a request is associated with is given by one of the following values:

`PWDB_DEFAULT`

  no database indicated, use configured list

`PWDB_UNIX`

  generic /etc/passwd and /etc/group files

`PWDB_SHADOW`

  /etc/shadow and /etc/gshadow Intended to supplement other databases

`PWDB_NIS`

  Use NIS server

`PWDB_RADIUS`

  Use RADIUS server

`PWDB_DECNIS`

  Use a NIS server configured for Digital Equipment Corp Enhanced Security, or a Solaris server with an adjunct NIS password file.

- `const char *pwdb_db_name(pwdb_type src)`

  return a character representation of the database functions

## 3.2 Functions supplied by database modules

Each module must provide 7 functions to the generic pwdb interface. They are registered with the generic interface via a structure of the following form:

> `

>     struct _pwdb_module {
>         pwdb_type type;                    /* type of database (code) */
>         const char *name;                  /* type of database (text) */
>         const char *class;                 /* class of database (user/group) */

>         /* FUNCTIONS: used to access the relevant database */

>         int (*locate)(const char *name, const int id, const struct pwdb **p);
>         int (*request)(const char *entry_name, const struct pwdb **p);
>         int (*replace)(const char *name, const int id, const struct pwdb **p);
>         int (*delete)(const char *name, const int id, const struct pwdb **p);
>         int (*support)(const char *entry_name);
>         int (*flags)(pwdb_flag *flags);
>         int (*cleanup)(int code);
>     };

> `

For the functions above taking a `name` and an `id` entry, the application may choose to leave one unspecified with the following defaults:

`PWDB_NAME_UNKNOWN`

  just look at the id field

`PWDB_ID_UNKNOWN`

  just look at the name field

In the case that the application supplies neither the `name` or the `id`, the module functions will try to obtain the relevant information from the argument `pwdb` structure.

It is legal for both the `name` and `id` to be specified. In this case they must both match an entry in the database to satisfy one of the above function calls. If both values are supplied and there is no entry in the database which matches them, `PWDB_BAD_REQUEST` is returned.

The structure is registered via an entry in the modules list (see pwdb_module.c).

## 3.3 Standard entries in pwdb structures

The following are standard entries in the pwdb structure. They can be read/written with calls to `pwdb_g/set_entry`.

First, we consider the `"user"` class of databases. For these, two entries are mandatory. They correspond to the name of the user and the user's uid.

`user`

  character string; the user's login id.

`uid`

  uid_t; the user's user-id.

The next entries are named by convention. Where possible new database functions should map these entries into their corresponding fields. These entries correspond to the entries in the `/etc/passwd` file.

`passwd`

  character string; the encrypted password for the user.

`defer_pass`

  This entry is intended to take care of situations that the normal `passwd` field is not used for the password. The `defer_pass` entry contains a character string that has typically two functions:

  - it indicates that the password is to be stored in the database that sets this entry.
  - it serves as a substitution string for databases that would normally contain the password entry.

  For example, for a `unix+shadow` setup, `defer_pass` would have the value ```x`''. The `unix` (no `shadow`) value for this entry is ``U'' which implies that the `passwd` field came from the user's entry in the `/etc/passwd` file.

`gid`

  gid_t; the user's principal group-id.

`group`

  character string; naming the user's principal group.

`gecos`

  character string; giving a more complete name for the user. It is conventional for this field to contain office and other information concerning the real-world identity of the user.

`dir`

  character string; the home directory of the user.

`shell`

  character string; the shell that the user prefers to use.

These entries correspond to the entries in the `/etc/group` file in addition to the user and gid entries above. They can be `pwdb_request()`d from the "group" class of databases.

`groups`

  character string; listing the group memberships of the user. The field separators are commas -- no spaces.

`groupids`

  array of `gid_t`; an array containing the group id's of the user in numerical form.

The following are intended to correspond to `/etc/shadow` entries.

`last_change`

  long integer; day of last change of password

`min_change`

  long integer; minimum number of days between password changes

`max_change`

  integer; maximum number of days between password changes

`warn_change`

  long integer; number of days to warn user to change their password before it expires

`defer_change`

  long integer; number of days after a user's password has expired before the user is denied access

`expire`

  long integer; day the user's account expires

`shadow_flags`

  long integer; reserved for use by underlying shadow implementation

The following is the entry used to supply a clear-text password for access to the database.

`pass_phrase`

  character string; this is the password required to access the user's record in a database

When integrating another database format the implementor is strongly encouraged to try to reuse the entries above to the extent they are appropriate. Should there be an absent entry in any database, the database management functions should be able to supply a reasonable default but only when updating its database.

## 3.4 Helper functions

### Public functions

- `char *_pwdb_delete_string(char *s)`

  overwrite the string '`s`' and return `NULL`. usage:

  `old_ptr = _pwdb_delete(old_ptr);`

- `char *_pwdb_dup_string(const char *s)`

  `malloc()` a copy of the string '`s`'. Return its address or `NULL` if `s == NULL` or on error.

  this memory will not be `free()`'d by a call to `pwdb_end()`.

- `void pwdb_print_pwdb_struct(const struct pwdb *p)`

  Dump the contents of `*p` to the `stderr`. Useful for debugging.

### Private functions

- `static struct pwdb *_pwd_check(const struct pwdb *p)`

  Establish if the pwdb structure was allocated by the library This library should not honor requests from elsewhere. return the local version (non-const) of this structure or NULL on error.

  this function is strictly designed for the use of the generic code. Both modules and applications should never need to call it.

## 3.5 Typical usage

Here is a skeleton usage for a login type program.

> `

>         pwdb_start();
>         pwdb_locate("user", PWDB_DEFAULT, username, PWDB_ID_UNKNOWN, &pw);
>         pwdb_request_group("group", PWDB_DEFAULT, "groupids", &pw);
>         pwdb_get_entry(pw, "uid", &e1);
>         pwdb_get_entry(pw, "gid", &e2);
>         pwdb_get_entry(pw, "groupids", &e3);
>         pwdb_end();

> `

Next Previous Contents