v1.0.30.0

Name

libaccdb - The Unified Account Database

libaccdb API

libaccdb provides some essential functions for applications wishing to access a user database. It also contains some common used code, such as password generation routines. Applications that wish to use ACCDB must link against it, usually with gcc's -laccdb parameter.

#include <accdb.h>

struct accdb_module *accdb_load(const char *module);
void accdb_unload(struct accdb_module *mp);

accdb_load() opens a back-end module (module.so and/or module.dll), acquires all necessary symbols and returns them in a malloc()ed struct accdb_module, which is then to be freed with accdb_unload(). In most cases, module will be just be a zero-terminated string with an asterisk, which says to use the default ACCDB back-end, defined in ./vetc/libaccdb/accdb or /etc/libaccdb/accdb, variable DEFAULT_BACKEND. Since Vitalnix v1.0.28.0, a sub-module can be specified by adding a colon and the name (i.e. "perl:shadow.pl").

On failure, accdb_load() returns NULL and sets errno to the ones set by system calls, plus EFAULT if no q_open() function in the ACCDB module could be found. ENOENT is returned if no default module was defined.

struct accdb_module *res = accdb_load("*");
res->open(res->state);
res->useradd(...);
res->close(res->state);
accdb_unload(res);

The back-end interface is discussed in its own chapter.

Configuration file parsing

While developing the Vitalnix Suite, I came to a point where I realized I had common used code duplicated in nearly every file that used ACCDB in some way, so I decided to move that code part to ACCDB since it is a central place.

Configuration files are based on the key=value scheme, and lines beginning with a hash mark (#) are ignored, as are empty lines and unrecognized keys:

# From vetc/autouid
# Minimum / maximum values for automatic UID selection

UID_MIN=100
UID_MAX=65000

# Minimum / maximum values for automatic GID selection
GID_MIN=100
GID_MAX=65000

ACCDB provides two (one) function to deal with configuration files. The second is just for handling multiple configuration files at once.

// From accdb.h
struct rconfig_opt {
    const char *key;
    char type;
    void *ptr;
    int (*callback)(const char *, char, void *, void *);
    void *uptr;
};

enum {
    RCONFIG_ONE = 1 << 0,
};

int accdb_rconfig(const char *file, struct rconfig_opt *table);
int accdb_rconfig_pv(const char **pv, const char *file, struct rconfig_opt *table, unsigned long flags);

accdb_rconfig() will return >0 to indicate success, or <0 (= errno) to signal an error. accdb_rconfig_pv() will return the number of files successfully parsed. Types for rconfig_opt.type can be:

.type typeof(.ptr) Expected input
RCONF_BYTE byte *
unsigned char *
uint8_t *
a single byte (any further bytes in the value are ignored)
RCONF_UCHAR unsigned char * a number (range 0 to 255). It might be written in any notation strtol() and similar understand. (This usually includes at least octal, decimal and hexadecimal. This applies to all "number"s in this table.)
RCONF_CHAR char * a number (range -128 to 127)
RCONF_USHORT unsigned short * a number (range 0 to 65535)
RCONF_SHORT short * a number (range -32768 to 32767)
RCONF_UINT unsigned int * a number (range dependent on size)
RCONF_INT int * a number
RCONF_ULONG unsigned long * a number (range 0 to 4294967295)
RCONF_LONG long * a number (range -2147483648 to 2147483647)
RCONF_FLOAT float * a floating point number
RCONF_DOUBLE double * a double precision floating point number
RCONF_BOOL byte * "yes", "no", or "1", "0" or "on", "off"
RCONF_IBOOL int * "yes", "no", or "1", "0" or "on", "off"
RCONF_STRING char ** any number of chars (string is automatically allocated)
RCONF_CB none a pointer to the string is passed to a custom function in ptr
RCONF_ULLONG unsigned long long * a number (range 0 to 2^64-1)
RCONF_LONG long long * a number (range -2^63 to 2^63-1)

.callback is called when the corresponding option has been parsed and .callback is not NULL. Since the options can be arranged in any order in the configuration file, you should take care when assuming that other keys/ptrs have already been filled. uptr can be an argument of your choice, i.e.:

int got_min, got_max;

struct rconfig_opt table[] = {
    {"UID_MIN", RCONF_LONG, &uid_min, set_flag, &got_min},
    {"UID_MAX", RCONF_LONG, &uid_max, set_flag, &got_max},
    {NULL},
};

static int set_flag(const char *key, byte fmt, void *ptr, void *uptr) {
    *(int *)uptr = 1;
}

To start parsing a file, call the accdb_rconfig() function with the corresponding parameters. If you want to read configuration files from different paths, i.e. to build up on default values, you can use accdb_rconfig_pv() like the next code example. (pv = path vector)

struct rconfig_opt table[] = {
    {"UID_MIN", RCONF_LONG, &uid_min, NULL},
    {"UID_MAX", RCONF_LONG, &uid_max, NULL},
    {NULL},
};

const char *pv_a[] = {"/etc", "/usr/local/etc", NULL}, *pv_b[] = {".", "/etc", NULL};

// Will parse /etc/configfile
accdb_rconfig("/etc/configfile", table);

// Will parse /etc/configfile and /usr/local/etc/configfile
accdb_rconfig_pv(pv_a, "configfile", table, 0);

// Will only parse the first successful file
accdb_rconfig_pv(pv_b, "configfile", table, RCONFIG_ONE);

The call to accdb_rconfig() will either return 1 for success, 0 for no success (actually 0 is never returned) and -errno for an error. The next call, with pv_a will parse /etc/configfile, etc. (pv from left to right). No value is returned. The call with pv_b will only read the first successful opened file, i.e. if you have both ./configfile and /etc/configfile, only the former is read if it can be opened.

Password generation

#include <accdb.h>

int accdb_genpw(char *plain, size_t len, unsigned long flags);
int accdb_cryptpw(const char *key, const char *salt, int meth, char **crypted);

accdb_genpw() generates a random password. It is configured to use libHX's independent random generator layer, to use /dev/urandom where possible (otherwise uses libc's rand()). The new password of length len is put into plain. A trailing '\0' character is appended, so plain must be 1 bigger than the value of len. If salt is NULL, a new salt will be generated. The salt parameter is usually only used for password authentication.

flags is a bitmask, which may consists of these options: GENPW_PHONEMIC uses a different algorithm, to choose passwords which can be spoken and (may be | is) easy to remember. GENPW_ONE_CASE specifies that there should be at least one upper-case character in the plain password; GENPW_ONE_DIGIT is the same for a digit, respectively.

accdb_cryptpw() takes a plain text key -- mostly this a plaintext password -- and encrypts it, being usable for /etc/shadow or similar. Space for the resulting crypted string is allocated within accdb_cryptpw(), and is put into *crypted. Be sure to free() it when you are done with it. meth specifies the encryption to use. Valid values are CRYPW_DES, CRYPW_MD5 and CRYPW_BLOWFISH. Blowfish is the preferred algorithm nowadays which provides maximum security of these three.

char pw[11], *cr;
accdb_genpw(pw, 10, GENPW_PHONEMIC | GENPW_ONE_DIGIT | GENPW_ONE_CASE);
accdb_cryptpw(pw, NULL, CRYPW_BLOWFISH, &cr);

DES and MD5 crypt is only available if your libc has them. Blowfish encryption routines are always available since I have included them in Vitalnix.


April 25 2004 http://vitalnix.sf.net/