#include <pwd.h>
#include <grp.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <libuutil.h>
#include <note.h>
#include <errno.h>
#include "idmapd.h"
#include "directory.h"
#include "directory_private.h"
#include <rpcsvc/idmap_prot.h>
#include "directory_server_impl.h"
#include "sidutil.h"
static directory_error_t machine_sid_dav(directory_values_rpc *lvals,
unsigned int rid);
static directory_error_t directory_provider_nsswitch_populate(
directory_entry_rpc *pent, struct passwd *pwd, struct group *grp,
idmap_utf8str_list *attrs);
static
directory_error_t
directory_provider_nsswitch_get(
directory_entry_rpc *del,
idmap_utf8str_list *ids,
idmap_utf8str types,
idmap_utf8str_list *attrs)
{
int i;
RDLOCK_CONFIG();
int host_name_len = strlen(_idmapdstate.hostname);
char my_host_name[host_name_len + 1];
(void) strcpy(my_host_name, _idmapdstate.hostname);
int machine_sid_len = strlen(_idmapdstate.cfg->pgcfg.machine_sid);
char my_machine_sid[machine_sid_len + 1];
(void) strcpy(my_machine_sid, _idmapdstate.cfg->pgcfg.machine_sid);
UNLOCK_CONFIG();
for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
struct passwd *pwd = NULL;
struct group *grp = NULL;
directory_error_t de;
int type;
type = *types;
if (*(types+1) != '\0')
types++;
if (del[i].status != DIRECTORY_NOT_FOUND)
continue;
char *id = ids->idmap_utf8str_list_val[i];
if (type == DIRECTORY_ID_SID[0]) {
if (strncasecmp(id, my_machine_sid, machine_sid_len) !=
0)
continue;
if (id[machine_sid_len] != '-')
continue;
char *p;
uint32_t rid =
strtoul(id + machine_sid_len + 1, &p, 10);
if (*p != '\0')
continue;
if (rid < LOCALRID_UID_MIN) {
continue;
}
if (rid <= LOCALRID_UID_MAX) {
errno = 0;
pwd = getpwuid(rid - LOCALRID_UID_MIN);
if (pwd == NULL) {
if (errno == 0)
continue;
char buf[40];
int err = errno;
(void) snprintf(buf, sizeof (buf),
"%d", err);
directory_entry_set_error(&del[i],
directory_error("errno.getpwuid",
"getpwuid: %2 (%1)",
buf, strerror(err), NULL));
continue;
}
} else if (rid >= LOCALRID_GID_MIN &&
rid <= LOCALRID_GID_MAX) {
errno = 0;
grp = getgrgid(rid - LOCALRID_GID_MIN);
if (grp == NULL) {
if (errno == 0)
continue;
char buf[40];
int err = errno;
(void) snprintf(buf, sizeof (buf),
"%d", err);
directory_entry_set_error(&del[i],
directory_error("errno.getgrgid",
"getgrgid: %2 (%1)",
buf, strerror(err), NULL));
continue;
}
} else
continue;
} else {
int id_len = strlen(id);
char name[id_len + 1];
char domain[id_len + 1];
split_name(name, domain, id);
if (domain[0] != '\0') {
if (!domain_eq(domain, my_host_name))
continue;
}
if (type != DIRECTORY_ID_GROUP[0]) {
errno = 0;
pwd = getpwnam(name);
if (pwd == NULL && errno != 0) {
char buf[40];
int err = errno;
(void) snprintf(buf, sizeof (buf),
"%d", err);
directory_entry_set_error(&del[i],
directory_error("errno.getpwnam",
"getpwnam: %2 (%1)",
buf, strerror(err), NULL));
continue;
}
}
if (type != DIRECTORY_ID_USER[0]) {
errno = 0;
grp = getgrnam(name);
if (grp == NULL && errno != 0) {
char buf[40];
int err = errno;
(void) snprintf(buf, sizeof (buf),
"%d", err);
directory_entry_set_error(&del[i],
directory_error("errno.getgrnam",
"getgrnam: %2 (%1)",
buf, strerror(err), NULL));
continue;
}
}
}
if (pwd == NULL && grp == NULL)
continue;
de = directory_provider_nsswitch_populate(&del[i], pwd, grp,
attrs);
if (de != NULL) {
directory_entry_set_error(&del[i], de);
de = NULL;
continue;
}
}
return (NULL);
}
static
directory_error_t
directory_provider_nsswitch_populate(
directory_entry_rpc *pent,
struct passwd *pwd,
struct group *grp,
idmap_utf8str_list *attrs)
{
int j;
directory_values_rpc *llvals;
int nattrs;
if (pwd != NULL && grp != NULL) {
return directory_error("Ambiguous.Name",
"Ambiguous name, is both a user and a group",
NULL);
}
nattrs = attrs->idmap_utf8str_list_len;
llvals = calloc(nattrs, sizeof (directory_values_rpc));
if (llvals == NULL)
goto nomem;
pent->directory_entry_rpc_u.attrs.attrs_val = llvals;
pent->directory_entry_rpc_u.attrs.attrs_len = nattrs;
pent->status = DIRECTORY_FOUND;
for (j = 0; j < nattrs; j++) {
directory_values_rpc *val;
char *a;
directory_error_t de;
a = attrs->idmap_utf8str_list_val[j];
val = &llvals[j];
val->found = FALSE;
de = NULL;
if (pwd != NULL) {
if (uu_strcaseeq(a, "cn")) {
const char *p = pwd->pw_name;
de = str_list_dav(val, &p, 1);
} else if (uu_strcaseeq(a, "objectClass")) {
static const char *objectClasses[] = {
"top",
"posixAccount",
};
de = str_list_dav(val, objectClasses,
UU_NELEM(objectClasses));
} else if (uu_strcaseeq(a, "gidNumber")) {
de = uint_list_dav(val, &pwd->pw_gid, 1);
} else if (uu_strcaseeq(a, "objectSid")) {
de = machine_sid_dav(val,
pwd->pw_uid + LOCALRID_UID_MIN);
} else if (uu_strcaseeq(a, "displayName")) {
const char *p = pwd->pw_gecos;
de = str_list_dav(val, &p, 1);
} else if (uu_strcaseeq(a, "distinguishedName")) {
char *dn;
RDLOCK_CONFIG();
(void) asprintf(&dn,
"uid=%s,ou=people,dc=%s",
pwd->pw_name, _idmapdstate.hostname);
UNLOCK_CONFIG();
if (dn == NULL)
goto nomem;
const char *cdn = dn;
de = str_list_dav(val, &cdn, 1);
free(dn);
} else if (uu_strcaseeq(a, "uid")) {
const char *p = pwd->pw_name;
de = str_list_dav(val, &p, 1);
} else if (uu_strcaseeq(a, "uidNumber")) {
de = uint_list_dav(val, &pwd->pw_uid, 1);
} else if (uu_strcaseeq(a, "gecos")) {
const char *p = pwd->pw_gecos;
de = str_list_dav(val, &p, 1);
} else if (uu_strcaseeq(a, "homeDirectory")) {
const char *p = pwd->pw_dir;
de = str_list_dav(val, &p, 1);
} else if (uu_strcaseeq(a, "loginShell")) {
const char *p = pwd->pw_shell;
de = str_list_dav(val, &p, 1);
} else if (uu_strcaseeq(a, "x-sun-canonicalName")) {
char *canon;
RDLOCK_CONFIG();
(void) asprintf(&canon, "%s@%s",
pwd->pw_name, _idmapdstate.hostname);
UNLOCK_CONFIG();
if (canon == NULL)
goto nomem;
const char *ccanon = canon;
de = str_list_dav(val, &ccanon, 1);
free(canon);
} else if (uu_strcaseeq(a, "x-sun-provider")) {
const char *provider = "UNIX-passwd";
de = str_list_dav(val, &provider, 1);
}
} else if (grp != NULL) {
if (uu_strcaseeq(a, "cn")) {
const char *p = grp->gr_name;
de = str_list_dav(val, &p, 1);
} else if (uu_strcaseeq(a, "objectClass")) {
static const char *objectClasses[] = {
"top",
"posixGroup",
};
de = str_list_dav(val, objectClasses,
UU_NELEM(objectClasses));
} else if (uu_strcaseeq(a, "gidNumber")) {
de = uint_list_dav(val, &grp->gr_gid, 1);
} else if (uu_strcaseeq(a, "objectSid")) {
de = machine_sid_dav(val,
grp->gr_gid + LOCALRID_GID_MIN);
} else if (uu_strcaseeq(a, "displayName")) {
const char *p = grp->gr_name;
de = str_list_dav(val, &p, 1);
} else if (uu_strcaseeq(a, "distinguishedName")) {
char *dn;
RDLOCK_CONFIG();
(void) asprintf(&dn,
"cn=%s,ou=group,dc=%s",
grp->gr_name, _idmapdstate.hostname);
UNLOCK_CONFIG();
if (dn == NULL)
goto nomem;
const char *cdn = dn;
de = str_list_dav(val, &cdn, 1);
free(dn);
} else if (uu_strcaseeq(a, "memberUid")) {
const char * const *members =
(const char * const *)grp->gr_mem;
de = str_list_dav(val, members, 0);
} else if (uu_strcaseeq(a, "x-sun-canonicalName")) {
char *canon;
RDLOCK_CONFIG();
(void) asprintf(&canon, "%s@%s",
grp->gr_name, _idmapdstate.hostname);
UNLOCK_CONFIG();
if (canon == NULL)
goto nomem;
const char *ccanon = canon;
de = str_list_dav(val, &ccanon, 1);
free(canon);
} else if (uu_strcaseeq(a, "x-sun-provider")) {
const char *provider = "UNIX-group";
de = str_list_dav(val, &provider, 1);
}
}
if (de != NULL)
return (de);
}
return (NULL);
nomem:
return (directory_error("ENOMEM.users",
"No memory allocating return value for user lookup", NULL));
}
static
directory_error_t
machine_sid_dav(directory_values_rpc *lvals, unsigned int rid)
{
sid_t *sid;
directory_error_t de;
RDLOCK_CONFIG();
int len = strlen(_idmapdstate.cfg->pgcfg.machine_sid);
char buf[len + 100];
(void) snprintf(buf, sizeof (buf), "%s-%u",
_idmapdstate.cfg->pgcfg.machine_sid, rid);
UNLOCK_CONFIG();
sid = sid_fromstr(buf);
if (sid == NULL)
goto nomem;
sid_to_le(sid);
de = bin_list_dav(lvals, sid, 1, sid_len(sid));
sid_free(sid);
return (de);
nomem:
return (directory_error("ENOMEM.machine_sid_dav",
"Out of memory allocating return value for lookup", NULL));
}
struct directory_provider_static directory_provider_nsswitch = {
"files",
directory_provider_nsswitch_get,
};