#include <sm/gen.h>
SM_RCSID("@(#)$Id: mbdb.c,v 1.41 2009/06/19 22:02:26 guenther Exp $")
#include <sys/param.h>
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <stdlib.h>
#include <setjmp.h>
#include <unistd.h>
#include <sm/limits.h>
#include <sm/conf.h>
#include <sm/assert.h>
#include <sm/bitops.h>
#include <sm/errstring.h>
#include <sm/heap.h>
#include <sm/mbdb.h>
#include <sm/string.h>
# ifdef EX_OK
# undef EX_OK
# endif
#include <sm/sysexits.h>
#if LDAPMAP
# if _LDAP_EXAMPLE_
# include <sm/ldap.h>
# endif
#endif
typedef struct
{
char *mbdb_typename;
int (*mbdb_initialize) __P((char *));
int (*mbdb_lookup) __P((char *name, SM_MBDB_T *user));
void (*mbdb_terminate) __P((void));
} SM_MBDB_TYPE_T;
static int mbdb_pw_initialize __P((char *));
static int mbdb_pw_lookup __P((char *name, SM_MBDB_T *user));
static void mbdb_pw_terminate __P((void));
#if LDAPMAP
# if _LDAP_EXAMPLE_
static struct sm_ldap_struct LDAPLMAP;
static int mbdb_ldap_initialize __P((char *));
static int mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user));
static void mbdb_ldap_terminate __P((void));
# endif
#endif
static SM_MBDB_TYPE_T SmMbdbTypes[] =
{
{ "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate },
#if LDAPMAP
# if _LDAP_EXAMPLE_
{ "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate },
# endif
#endif
{ NULL, NULL, NULL, NULL }
};
static SM_MBDB_TYPE_T *SmMbdbType = &SmMbdbTypes[0];
int
sm_mbdb_initialize(mbdb)
char *mbdb;
{
size_t namelen;
int err;
char *name;
char *arg;
SM_MBDB_TYPE_T *t;
SM_REQUIRE(mbdb != NULL);
name = mbdb;
arg = strchr(mbdb, '.');
if (arg == NULL)
namelen = strlen(name);
else
{
namelen = arg - name;
++arg;
}
for (t = SmMbdbTypes; t->mbdb_typename != NULL; ++t)
{
if (strlen(t->mbdb_typename) == namelen &&
strncmp(name, t->mbdb_typename, namelen) == 0)
{
err = EX_OK;
if (t->mbdb_initialize != NULL)
err = t->mbdb_initialize(arg);
if (err == EX_OK)
SmMbdbType = t;
return err;
}
}
return EX_UNAVAILABLE;
}
void
sm_mbdb_terminate()
{
if (SmMbdbType->mbdb_terminate != NULL)
SmMbdbType->mbdb_terminate();
}
int
sm_mbdb_lookup(name, user)
char *name;
SM_MBDB_T *user;
{
int ret = EX_NOUSER;
if (SmMbdbType->mbdb_lookup != NULL)
ret = SmMbdbType->mbdb_lookup(name, user);
return ret;
}
void
sm_mbdb_frompw(user, pw)
SM_MBDB_T *user;
struct passwd *pw;
{
SM_REQUIRE(user != NULL);
(void) sm_strlcpy(user->mbdb_name, pw->pw_name,
sizeof(user->mbdb_name));
user->mbdb_uid = pw->pw_uid;
user->mbdb_gid = pw->pw_gid;
sm_pwfullname(pw->pw_gecos, pw->pw_name, user->mbdb_fullname,
sizeof(user->mbdb_fullname));
(void) sm_strlcpy(user->mbdb_homedir, pw->pw_dir,
sizeof(user->mbdb_homedir));
(void) sm_strlcpy(user->mbdb_shell, pw->pw_shell,
sizeof(user->mbdb_shell));
}
#if _FFR_HANDLE_ISO8859_GECOS
static char Latin1ToASCII[128] =
{
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33,
99, 80, 36, 89, 124, 36, 34, 99, 97, 60, 45, 45, 114, 45, 111, 42,
50, 51, 39, 117, 80, 46, 44, 49, 111, 62, 42, 42, 42, 63, 65, 65,
65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, 68, 78, 79,
79, 79, 79, 79, 88, 79, 85, 85, 85, 85, 89, 80, 66, 97, 97, 97, 97,
97, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, 100, 110,
111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, 112, 121
};
#endif
void
sm_pwfullname(gecos, user, buf, buflen)
register char *gecos;
char *user;
char *buf;
size_t buflen;
{
register char *p;
register char *bp = buf;
if (*gecos == '*')
gecos++;
for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
{
if (bp >= &buf[buflen - 1])
{
(void) sm_strlcpy(buf, user, buflen);
return;
}
if (*p == '&')
{
(void) sm_strlcpy(bp, user, buflen - (bp - buf));
*bp = toupper(*bp);
bp += strlen(bp);
}
else
{
#if _FFR_HANDLE_ISO8859_GECOS
if ((unsigned char) *p >= 128)
*bp++ = Latin1ToASCII[(unsigned char) *p - 128];
else
#endif
*bp++ = *p;
}
}
*bp = '\0';
}
static int
mbdb_pw_initialize(arg)
char *arg;
{
return EX_OK;
}
static int
mbdb_pw_lookup(name, user)
char *name;
SM_MBDB_T *user;
{
struct passwd *pw;
#ifdef HESIOD
{
char *p;
for (p = name; *p != '\0'; p++)
if (!isascii(*p) || !isdigit(*p))
break;
if (*p == '\0')
return EX_NOUSER;
}
#endif
errno = 0;
pw = getpwnam(name);
if (pw == NULL)
{
#if 0
switch (errno)
{
case 0:
return EX_NOUSER;
case EIO:
return EX_OSERR;
default:
return EX_TEMPFAIL;
}
#endif
return EX_NOUSER;
}
sm_mbdb_frompw(user, pw);
return EX_OK;
}
static void
mbdb_pw_terminate()
{
endpwent();
}
#if LDAPMAP
# if _LDAP_EXAMPLE_
# define MBDB_LDAP_LABEL "MailboxDatabase"
# ifndef MBDB_LDAP_FILTER
# define MBDB_LDAP_FILTER "(&(objectClass=posixAccount)(uid=%0))"
# endif
# ifndef MBDB_DEFAULT_LDAP_BASEDN
# define MBDB_DEFAULT_LDAP_BASEDN NULL
# endif
# ifndef MBDB_DEFAULT_LDAP_SERVER
# define MBDB_DEFAULT_LDAP_SERVER NULL
# endif
static int
mbdb_ldap_initialize(arg)
char *arg;
{
sm_ldap_clear(&LDAPLMAP);
LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN;
LDAPLMAP.ldap_host = MBDB_DEFAULT_LDAP_SERVER;
LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER;
LDAPLMAP.ldap_sizelimit = 1;
if (arg != NULL && *arg != '\0')
{
char *new;
char *sep;
size_t len;
len = strlen(arg) + 1;
new = sm_malloc(len);
if (new == NULL)
return EX_TEMPFAIL;
(void) sm_strlcpy(new, arg, len);
sep = strrchr(new, '@');
if (sep != NULL)
{
*sep++ = '\0';
LDAPLMAP.ldap_host = sep;
}
LDAPLMAP.ldap_base = new;
}
return EX_OK;
}
#define NEED_FULLNAME 0x01
#define NEED_HOMEDIR 0x02
#define NEED_SHELL 0x04
#define NEED_UID 0x08
#define NEED_GID 0x10
static int
mbdb_ldap_lookup(name, user)
char *name;
SM_MBDB_T *user;
{
int msgid;
int need;
int ret;
int save_errno;
LDAPMessage *entry;
BerElement *ber;
char *attr = NULL;
if (strlen(name) >= sizeof(user->mbdb_name))
{
errno = EINVAL;
return EX_NOUSER;
}
if (LDAPLMAP.ldap_filter == NULL)
{
errno = EFAULT;
return EX_TEMPFAIL;
}
if (LDAPLMAP.ldap_pid != getpid())
{
LDAPLMAP.ldap_ld = NULL;
}
if (LDAPLMAP.ldap_ld == NULL)
{
if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP))
return EX_TEMPFAIL;
}
sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP);
msgid = sm_ldap_search(&LDAPLMAP, name);
if (msgid == -1)
{
save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE;
# ifdef LDAP_SERVER_DOWN
if (errno == LDAP_SERVER_DOWN)
{
sm_ldap_close(&LDAPLMAP);
}
# endif
errno = save_errno;
return EX_TEMPFAIL;
}
ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1,
(LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL :
&(LDAPLMAP.ldap_timeout)),
&(LDAPLMAP.ldap_res));
if (ret != LDAP_RES_SEARCH_RESULT &&
ret != LDAP_RES_SEARCH_ENTRY)
{
if (ret == 0)
errno = ETIMEDOUT;
else
errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
ret = EX_TEMPFAIL;
goto abort;
}
entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res);
if (entry == NULL)
{
int rc;
save_errno = ldap_parse_result(LDAPLMAP.ldap_ld,
LDAPLMAP.ldap_res, &rc, NULL,
NULL, NULL, NULL, 0);
if (save_errno == LDAP_SUCCESS)
save_errno = rc;
if (save_errno == LDAP_SUCCESS)
{
errno = ENOENT;
ret = EX_NOUSER;
}
else
{
errno = save_errno;
ret = EX_TEMPFAIL;
}
goto abort;
}
# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
# endif
ret = EX_OK;
need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID;
for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber);
attr != NULL;
attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber))
{
char **vals;
vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr);
if (vals == NULL)
{
errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
if (errno == LDAP_SUCCESS)
{
ldap_memfree(attr);
continue;
}
errno += E_LDAPBASE;
ret = EX_TEMPFAIL;
goto abort;
}
# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
# endif
if (vals[0] == NULL || vals[0][0] == '\0')
goto skip;
if (strcasecmp(attr, "gecos") == 0)
{
if (!bitset(NEED_FULLNAME, need) ||
strlen(vals[0]) >= sizeof(user->mbdb_fullname))
goto skip;
sm_pwfullname(vals[0], name, user->mbdb_fullname,
sizeof(user->mbdb_fullname));
need &= ~NEED_FULLNAME;
}
else if (strcasecmp(attr, "homeDirectory") == 0)
{
if (!bitset(NEED_HOMEDIR, need) ||
strlen(vals[0]) >= sizeof(user->mbdb_homedir))
goto skip;
(void) sm_strlcpy(user->mbdb_homedir, vals[0],
sizeof(user->mbdb_homedir));
need &= ~NEED_HOMEDIR;
}
else if (strcasecmp(attr, "loginShell") == 0)
{
if (!bitset(NEED_SHELL, need) ||
strlen(vals[0]) >= sizeof(user->mbdb_shell))
goto skip;
(void) sm_strlcpy(user->mbdb_shell, vals[0],
sizeof(user->mbdb_shell));
need &= ~NEED_SHELL;
}
else if (strcasecmp(attr, "uidNumber") == 0)
{
char *p;
if (!bitset(NEED_UID, need))
goto skip;
for (p = vals[0]; *p != '\0'; p++)
{
if (p == vals[0] && *p == '-')
{
if (*(p + 1) == '\0')
goto skip;
}
else if (!isascii(*p) || !isdigit(*p))
goto skip;
}
user->mbdb_uid = atoi(vals[0]);
need &= ~NEED_UID;
}
else if (strcasecmp(attr, "gidNumber") == 0)
{
char *p;
if (!bitset(NEED_GID, need))
goto skip;
for (p = vals[0]; *p != '\0'; p++)
{
if (p == vals[0] && *p == '-')
{
if (*(p + 1) == '\0')
goto skip;
}
else if (!isascii(*p) || !isdigit(*p))
goto skip;
}
user->mbdb_gid = atoi(vals[0]);
need &= ~NEED_GID;
}
skip:
ldap_value_free(vals);
ldap_memfree(attr);
}
errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
if (errno != LDAP_SUCCESS &&
errno != LDAP_DECODING_ERROR)
{
errno += E_LDAPBASE;
ret = EX_TEMPFAIL;
goto abort;
}
abort:
save_errno = errno;
if (attr != NULL)
{
ldap_memfree(attr);
attr = NULL;
}
if (LDAPLMAP.ldap_res != NULL)
{
ldap_msgfree(LDAPLMAP.ldap_res);
LDAPLMAP.ldap_res = NULL;
}
if (ret == EX_OK)
{
if (need == 0)
{
(void) sm_strlcpy(user->mbdb_name, name,
sizeof(user->mbdb_name));
save_errno = 0;
}
else
{
ret = EX_NOUSER;
save_errno = EINVAL;
}
}
errno = save_errno;
return ret;
}
static void
mbdb_ldap_terminate()
{
sm_ldap_close(&LDAPLMAP);
}
# endif
#endif