#include "idmapd.h"
#include "idmap_priv.h"
#include "ns_sldap.h"
#include "nldaputils.h"
#include <assert.h>
#define _F_GETPWNAM "(&(objectClass=posixAccount)(uid=%s))"
#define _F_GETPWNAM_SSD "(&(%%s)(uid=%s))"
#define _F_GETPWWNAMWK \
"(&(objectClass=posixAccount)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
#define _F_GETPWWNAMWK_SSD "(&(%%s)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
#define _F_GETPWWNAMDOM \
"(&(objectClass=posixAccount)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
#define _F_GETPWWNAMDOM_SSD "(&(%%s)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
#define _F_GETPWUID "(&(objectClass=posixAccount)(uidNumber=%u))"
#define _F_GETPWUID_SSD "(&(%%s)(uidNumber=%u))"
#define _F_GETGRNAM "(&(objectClass=posixGroup)(cn=%s))"
#define _F_GETGRNAM_SSD "(&(%%s)(cn=%s))"
#define _F_GETGRWNAMWK \
"(&(objectClass=posixGroup)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
#define _F_GETGRWNAMWK_SSD "(&(%%s)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
#define _F_GETGRWNAMDOM \
"(&(objectClass=posixGroup)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
#define _F_GETGRWNAMDOM_SSD "(&(%%s)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
#define _F_GETGRGID "(&(objectClass=posixGroup)(gidNumber=%u))"
#define _F_GETGRGID_SSD "(&(%%s)(gidNumber=%u))"
#define UID "uid"
#define CN "cn"
#define UIDNUMBER "uidnumber"
#define GIDNUMBER "gidnumber"
#define DN "dn"
#define IS_NLDAP_RC_FATAL(x) ((x == NS_LDAP_MEMORY) ? 1 : 0)
typedef struct idmap_nldap_q {
char **winname;
char **windomain;
char **unixname;
uid_t *pid;
char **dn;
char **attr;
char **value;
int is_user;
idmap_retcode *rc;
int lrc;
ns_ldap_result_t *result;
ns_ldap_error_t *errorp;
char *filter;
char *udata;
} idmap_nldap_q_t;
typedef struct idmap_nldap_query_state {
const char *nldap_winname_attr;
const char *defdom;
int nqueries;
int qid;
int flag;
ns_ldap_list_batch_t *batch;
idmap_nldap_q_t queries[1];
} idmap_nldap_query_state_t;
static
int
merge_SSD_filter(const ns_ldap_search_desc_t *desc,
char **realfilter, const void *userdata)
{
int len;
char *checker;
if (realfilter == NULL)
return (NS_LDAP_INVALID_PARAM);
*realfilter = NULL;
if (desc == NULL || desc->filter == NULL || userdata == NULL)
return (NS_LDAP_INVALID_PARAM);
len = 0;
checker = (char *)userdata;
do {
checker = strchr(checker, '%');
if (checker != NULL) {
if (len > 0 || *(checker + 1) != 's')
return (NS_LDAP_INVALID_PARAM);
len++;
checker += 2;
} else if (len != 1)
return (NS_LDAP_INVALID_PARAM);
} while (checker != NULL);
len = strlen(userdata) + strlen(desc->filter) + 1;
*realfilter = (char *)malloc(len);
if (*realfilter == NULL)
return (NS_LDAP_MEMORY);
(void) sprintf(*realfilter, (char *)userdata, desc->filter);
return (NS_LDAP_SUCCESS);
}
static
char
hex_char(int n)
{
return ("0123456789abcdef"[n & 0xf]);
}
char *
sanitize_for_ldap_filter(const char *str)
{
const char *p;
char *q, *s_str = NULL;
int n;
for (p = str, n = 0; *p; p++)
if (*p == '*' || *p == '(' || *p == ')' ||
*p == '\\' || *p == '%')
n++;
if (n == 0)
return ((char *)str);
s_str = calloc(1, n * 2 + strlen(str) + 1);
if (s_str == NULL)
return (NULL);
for (p = str, q = s_str; *p; p++) {
if (*p == '*' || *p == '(' || *p == ')' ||
*p == '\\' || *p == '%') {
*q++ = '\\';
*q++ = hex_char(*p >> 4);
*q++ = hex_char(*p & 0xf);
} else
*q++ = *p;
}
return (s_str);
}
static
idmap_retcode
nldaprc2retcode(int rc)
{
switch (rc) {
case NS_LDAP_SUCCESS:
case NS_LDAP_SUCCESS_WITH_INFO:
return (IDMAP_SUCCESS);
case NS_LDAP_NOTFOUND:
return (IDMAP_ERR_NOTFOUND);
case NS_LDAP_MEMORY:
return (IDMAP_ERR_MEMORY);
case NS_LDAP_CONFIG:
return (IDMAP_ERR_NS_LDAP_CFG);
case NS_LDAP_OP_FAILED:
return (IDMAP_ERR_NS_LDAP_OP_FAILED);
case NS_LDAP_PARTIAL:
return (IDMAP_ERR_NS_LDAP_PARTIAL);
case NS_LDAP_INTERNAL:
return (IDMAP_ERR_INTERNAL);
case NS_LDAP_INVALID_PARAM:
return (IDMAP_ERR_ARG);
default:
return (IDMAP_ERR_OTHER);
}
}
static
idmap_retcode
idmap_nldap_lookup_batch_start(int nqueries, idmap_nldap_query_state_t **qs)
{
idmap_nldap_query_state_t *s;
s = calloc(1, sizeof (*s) +
(nqueries - 1) * sizeof (idmap_nldap_q_t));
if (s == NULL)
return (IDMAP_ERR_MEMORY);
if (__ns_ldap_list_batch_start(&s->batch) != NS_LDAP_SUCCESS) {
free(s);
return (IDMAP_ERR_MEMORY);
}
s->nqueries = nqueries;
s->flag = NS_LDAP_KEEP_CONN;
*qs = s;
return (IDMAP_SUCCESS);
}
static
idmap_retcode
idmap_nldap_bywinname_batch_add(idmap_nldap_query_state_t *qs,
const char *winname, const char *windomain, int is_user,
char **dn, char **attr, char **value,
char **unixname, uid_t *pid, idmap_retcode *rc)
{
idmap_nldap_q_t *q;
const char *db, *filter, *udata;
int flen, ulen, wksid = 0;
char *s_winname, *s_windomain;
const char **attrs;
const char *pwd_attrs[] = {UID, UIDNUMBER, NULL, NULL};
const char *grp_attrs[] = {CN, GIDNUMBER, NULL, NULL};
s_winname = s_windomain = NULL;
q = &(qs->queries[qs->qid++]);
q->unixname = unixname;
q->pid = pid;
q->rc = rc;
q->is_user = is_user;
q->dn = dn;
q->attr = attr;
q->value = value;
if (is_user) {
db = "passwd";
if (lookup_wksids_name2sid(winname, NULL, NULL, NULL, NULL,
NULL, NULL) == IDMAP_SUCCESS) {
filter = _F_GETPWWNAMWK;
udata = _F_GETPWWNAMWK_SSD;
wksid = 1;
} else if (windomain != NULL) {
filter = _F_GETPWWNAMDOM;
udata = _F_GETPWWNAMDOM_SSD;
} else {
*q->rc = IDMAP_ERR_DOMAIN_NOTFOUND;
goto errout;
}
pwd_attrs[2] = qs->nldap_winname_attr;
attrs = pwd_attrs;
} else {
db = "group";
if (lookup_wksids_name2sid(winname, NULL, NULL, NULL, NULL,
NULL, NULL) == IDMAP_SUCCESS) {
filter = _F_GETGRWNAMWK;
udata = _F_GETGRWNAMWK_SSD;
wksid = 1;
} else if (windomain != NULL) {
filter = _F_GETGRWNAMDOM;
udata = _F_GETGRWNAMDOM_SSD;
} else {
*q->rc = IDMAP_ERR_DOMAIN_NOTFOUND;
goto errout;
}
grp_attrs[2] = qs->nldap_winname_attr;
attrs = grp_attrs;
}
s_winname = sanitize_for_ldap_filter(winname);
if (s_winname == NULL) {
*q->rc = IDMAP_ERR_MEMORY;
goto errout;
}
if (windomain != NULL) {
s_windomain = sanitize_for_ldap_filter(windomain);
if (s_windomain == NULL) {
*q->rc = IDMAP_ERR_MEMORY;
goto errout;
}
}
if (wksid) {
flen = snprintf(NULL, 0, filter, qs->nldap_winname_attr,
s_winname) + 1;
ulen = snprintf(NULL, 0, udata, qs->nldap_winname_attr,
s_winname) + 1;
} else {
flen = snprintf(NULL, 0, filter, qs->nldap_winname_attr,
s_winname, s_windomain) + 1;
ulen = snprintf(NULL, 0, udata, qs->nldap_winname_attr,
s_winname, s_windomain) + 1;
}
q->filter = malloc(flen);
if (q->filter == NULL) {
*q->rc = IDMAP_ERR_MEMORY;
goto errout;
}
q->udata = malloc(ulen);
if (q->udata == NULL) {
*q->rc = IDMAP_ERR_MEMORY;
goto errout;
}
if (wksid) {
(void) snprintf(q->filter, flen, filter,
qs->nldap_winname_attr, s_winname);
(void) snprintf(q->udata, ulen, udata,
qs->nldap_winname_attr, s_winname);
} else {
(void) snprintf(q->filter, flen, filter,
qs->nldap_winname_attr, s_winname, s_windomain);
(void) snprintf(q->udata, ulen, udata,
qs->nldap_winname_attr, s_winname, s_windomain);
}
if (s_winname != winname)
free(s_winname);
if (s_windomain != windomain)
free(s_windomain);
q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
&q->errorp, &q->lrc, NULL, q->udata);
if (IS_NLDAP_RC_FATAL(q->lrc))
return (nldaprc2retcode(q->lrc));
return (IDMAP_SUCCESS);
errout:
if (s_winname != winname)
free(s_winname);
if (s_windomain != windomain)
free(s_windomain);
return (*q->rc);
}
static
idmap_retcode
idmap_nldap_bypid_batch_add(idmap_nldap_query_state_t *qs,
uid_t pid, int is_user, char **dn, char **attr, char **value,
char **winname, char **windomain,
char **unixname, idmap_retcode *rc)
{
idmap_nldap_q_t *q;
const char *db, *filter, *udata;
int len;
const char **attrs;
const char *pwd_attrs[] = {UID, NULL, NULL};
const char *grp_attrs[] = {CN, NULL, NULL};
q = &(qs->queries[qs->qid++]);
q->winname = winname;
q->windomain = windomain;
q->unixname = unixname;
q->rc = rc;
q->is_user = is_user;
q->dn = dn;
q->attr = attr;
q->value = value;
if (is_user) {
db = "passwd";
filter = _F_GETPWUID;
udata = _F_GETPWUID_SSD;
pwd_attrs[1] = qs->nldap_winname_attr;
attrs = pwd_attrs;
} else {
db = "group";
filter = _F_GETGRGID;
udata = _F_GETGRGID_SSD;
grp_attrs[1] = qs->nldap_winname_attr;
attrs = grp_attrs;
}
len = snprintf(NULL, 0, filter, pid) + 1;
q->filter = malloc(len);
if (q->filter == NULL) {
*q->rc = IDMAP_ERR_MEMORY;
return (IDMAP_ERR_MEMORY);
}
(void) snprintf(q->filter, len, filter, pid);
len = snprintf(NULL, 0, udata, pid) + 1;
q->udata = malloc(len);
if (q->udata == NULL) {
*q->rc = IDMAP_ERR_MEMORY;
return (IDMAP_ERR_MEMORY);
}
(void) snprintf(q->udata, len, udata, pid);
q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
&q->errorp, &q->lrc, NULL, q->udata);
if (IS_NLDAP_RC_FATAL(q->lrc))
return (nldaprc2retcode(q->lrc));
return (IDMAP_SUCCESS);
}
static
idmap_retcode
idmap_nldap_byunixname_batch_add(idmap_nldap_query_state_t *qs,
const char *unixname, int is_user,
char **dn, char **attr, char **value,
char **winname, char **windomain, uid_t *pid, idmap_retcode *rc)
{
idmap_nldap_q_t *q;
const char *db, *filter, *udata;
int len;
char *s_unixname = NULL;
const char **attrs;
const char *pwd_attrs[] = {UIDNUMBER, NULL, NULL};
const char *grp_attrs[] = {GIDNUMBER, NULL, NULL};
q = &(qs->queries[qs->qid++]);
q->winname = winname;
q->windomain = windomain;
q->pid = pid;
q->rc = rc;
q->is_user = is_user;
q->dn = dn;
q->attr = attr;
q->value = value;
if (is_user) {
db = "passwd";
filter = _F_GETPWNAM;
udata = _F_GETPWNAM_SSD;
pwd_attrs[1] = qs->nldap_winname_attr;
attrs = pwd_attrs;
} else {
db = "group";
filter = _F_GETGRNAM;
udata = _F_GETGRNAM_SSD;
grp_attrs[1] = qs->nldap_winname_attr;
attrs = grp_attrs;
}
s_unixname = sanitize_for_ldap_filter(unixname);
if (s_unixname == NULL) {
*q->rc = IDMAP_ERR_MEMORY;
return (IDMAP_ERR_MEMORY);
}
len = snprintf(NULL, 0, filter, s_unixname) + 1;
q->filter = malloc(len);
if (q->filter == NULL) {
if (s_unixname != unixname)
free(s_unixname);
*q->rc = IDMAP_ERR_MEMORY;
return (IDMAP_ERR_MEMORY);
}
(void) snprintf(q->filter, len, filter, s_unixname);
len = snprintf(NULL, 0, udata, s_unixname) + 1;
q->udata = malloc(len);
if (q->udata == NULL) {
if (s_unixname != unixname)
free(s_unixname);
*q->rc = IDMAP_ERR_MEMORY;
return (IDMAP_ERR_MEMORY);
}
(void) snprintf(q->udata, len, udata, s_unixname);
if (s_unixname != unixname)
free(s_unixname);
q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
&q->errorp, &q->lrc, NULL, q->udata);
if (IS_NLDAP_RC_FATAL(q->lrc))
return (nldaprc2retcode(q->lrc));
return (IDMAP_SUCCESS);
}
static
void
idmap_nldap_lookup_batch_release(idmap_nldap_query_state_t *qs)
{
idmap_nldap_q_t *q;
int i;
if (qs->batch != NULL)
(void) __ns_ldap_list_batch_release(qs->batch);
for (i = 0; i < qs->qid; i++) {
q = &(qs->queries[i]);
free(q->filter);
free(q->udata);
if (q->errorp != NULL)
(void) __ns_ldap_freeError(&q->errorp);
if (q->result != NULL)
(void) __ns_ldap_freeResult(&q->result);
}
free(qs);
}
static
idmap_retcode
idmap_nldap_lookup_batch_end(idmap_nldap_query_state_t *qs)
{
idmap_nldap_q_t *q;
int i;
ns_ldap_entry_t *entry;
char **val, *end, *str, *name, *dom;
idmap_retcode rc = IDMAP_SUCCESS;
(void) __ns_ldap_list_batch_end(qs->batch);
qs->batch = NULL;
for (i = 0; i < qs->qid; i++) {
q = &(qs->queries[i]);
*q->rc = nldaprc2retcode(q->lrc);
if (*q->rc != IDMAP_SUCCESS)
continue;
if (q->result == NULL ||
!q->result->entries_count ||
(entry = q->result->entry) == NULL ||
!entry->attr_count) {
*q->rc = IDMAP_ERR_NOTFOUND;
continue;
}
if (q->pid != NULL) {
val = __ns_ldap_getAttr(entry,
(q->is_user) ? UIDNUMBER : GIDNUMBER);
if (val != NULL && *val != NULL)
*q->pid = strtoul(*val, &end, 10);
}
if (q->unixname != NULL) {
val = __ns_ldap_getAttr(entry,
(q->is_user) ? UID : CN);
if (val != NULL && *val != NULL) {
*q->unixname = strdup(*val);
if (*q->unixname == NULL) {
rc = *q->rc = IDMAP_ERR_MEMORY;
goto out;
}
}
}
if (q->dn != NULL) {
val = __ns_ldap_getAttr(entry, DN);
if (val != NULL && *val != NULL) {
*q->dn = strdup(*val);
if (*q->dn == NULL) {
rc = *q->rc = IDMAP_ERR_MEMORY;
goto out;
}
}
}
if (q->attr != NULL) {
*q->attr = strdup(qs->nldap_winname_attr);
if (*q->attr == NULL) {
rc = *q->rc = IDMAP_ERR_MEMORY;
goto out;
}
}
val = __ns_ldap_getAttr(entry, qs->nldap_winname_attr);
if (val == NULL || *val == NULL)
continue;
if (q->value != NULL) {
*q->value = strdup(*val);
if (*q->value == NULL) {
rc = *q->rc = IDMAP_ERR_MEMORY;
goto out;
}
}
if (q->winname == NULL && q->windomain == NULL)
continue;
name = dom = NULL;
if (lookup_wksids_name2sid(*val, NULL, NULL, NULL, NULL, NULL,
NULL) == IDMAP_SUCCESS) {
name = *val;
dom = NULL;
} else if ((str = strchr(*val, '\\')) != NULL) {
*str = '\0';
name = str + 1;
dom = *val;
} else if ((str = strrchr(*val, '@')) != NULL) {
*str = '\0';
name = *val;
dom = str + 1;
} else {
idmapdlog(LOG_INFO, "Domain-less "
"winname (%s) found in Native LDAP", *val);
*q->rc = IDMAP_ERR_NS_LDAP_BAD_WINNAME;
continue;
}
if (q->winname != NULL) {
*q->winname = strdup(name);
if (*q->winname == NULL) {
rc = *q->rc = IDMAP_ERR_MEMORY;
goto out;
}
}
if (q->windomain != NULL && dom != NULL) {
*q->windomain = strdup(dom);
if (*q->windomain == NULL) {
rc = *q->rc = IDMAP_ERR_MEMORY;
goto out;
}
}
}
out:
(void) idmap_nldap_lookup_batch_release(qs);
return (rc);
}
idmap_retcode
nldap_lookup_batch(lookup_state_t *state, idmap_mapping_batch *batch,
idmap_ids_res *result)
{
idmap_retcode retcode, rc1;
int i, add;
idmap_mapping *req;
idmap_id_res *res;
idmap_nldap_query_state_t *qs = NULL;
idmap_how *how;
if (state->nldap_nqueries == 0)
return (IDMAP_SUCCESS);
retcode = idmap_nldap_lookup_batch_start(state->nldap_nqueries, &qs);
if (retcode != IDMAP_SUCCESS) {
idmapdlog(LOG_ERR,
"Failed to create batch for native LDAP lookup");
goto out;
}
qs->nldap_winname_attr = state->nldap_winname_attr;
qs->defdom = state->defdom;
for (i = 0, add = 0; i < batch->idmap_mapping_batch_len; i++) {
req = &batch->idmap_mapping_batch_val[i];
res = &result->ids.ids_val[i];
retcode = IDMAP_SUCCESS;
if (!(req->direction & _IDMAP_F_LOOKUP_NLDAP))
continue;
if (IS_ID_SID(req->id1)) {
assert(req->id1name != NULL &&
(res->id.idtype == IDMAP_UID ||
res->id.idtype == IDMAP_GID));
if (req->id2name != NULL &&
res->id.idmap_id_u.uid != IDMAP_SENTINEL_PID) {
res->retcode = IDMAP_SUCCESS;
continue;
}
free(req->id2name);
req->id2name = NULL;
add = 1;
idmap_how_clear(&res->info.how);
res->info.src = IDMAP_MAP_SRC_NEW;
how = &res->info.how;
how->map_type = IDMAP_MAP_TYPE_DS_NLDAP;
retcode = idmap_nldap_bywinname_batch_add(
qs, req->id1name, req->id1domain,
(res->id.idtype == IDMAP_UID) ? 1 : 0,
&how->idmap_how_u.nldap.dn,
&how->idmap_how_u.nldap.attr,
&how->idmap_how_u.nldap.value,
&req->id2name, &res->id.idmap_id_u.uid,
&res->retcode);
} else if (IS_ID_UID(req->id1) || IS_ID_GID(req->id1)) {
if (req->id2name != NULL) {
res->retcode = IDMAP_SUCCESS;
continue;
}
free(req->id2domain);
req->id2domain = NULL;
idmap_how_clear(&res->info.how);
res->info.src = IDMAP_MAP_SRC_NEW;
how = &res->info.how;
how->map_type = IDMAP_MAP_TYPE_DS_NLDAP;
if (req->id1.idmap_id_u.uid != IDMAP_SENTINEL_PID) {
add = 1;
retcode = idmap_nldap_bypid_batch_add(
qs, req->id1.idmap_id_u.uid,
(req->id1.idtype == IDMAP_UID) ? 1 : 0,
&how->idmap_how_u.nldap.dn,
&how->idmap_how_u.nldap.attr,
&how->idmap_how_u.nldap.value,
&req->id2name, &req->id2domain,
(req->id1name == NULL) ?
&req->id1name : NULL,
&res->retcode);
} else if (req->id1name != NULL) {
add = 1;
retcode = idmap_nldap_byunixname_batch_add(
qs, req->id1name,
(req->id1.idtype == IDMAP_UID) ? 1 : 0,
&how->idmap_how_u.nldap.dn,
&how->idmap_how_u.nldap.attr,
&how->idmap_how_u.nldap.value,
&req->id2name, &req->id2domain,
&req->id1.idmap_id_u.uid, &res->retcode);
}
}
if (retcode != IDMAP_SUCCESS)
break;
}
if (!add)
idmap_nldap_lookup_batch_release(qs);
else if (retcode != IDMAP_SUCCESS)
idmap_nldap_lookup_batch_release(qs);
else
retcode = idmap_nldap_lookup_batch_end(qs);
out:
for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
req = &batch->idmap_mapping_batch_val[i];
res = &result->ids.ids_val[i];
if (!(req->direction & _IDMAP_F_LOOKUP_NLDAP))
continue;
req->direction &= ~(_IDMAP_F_LOOKUP_NLDAP);
if (retcode != IDMAP_SUCCESS) {
res->retcode = retcode;
continue;
}
if (!add)
continue;
if (res->retcode == IDMAP_SUCCESS &&
req->id2name != NULL &&
res->id.idmap_id_u.sid.prefix == NULL &&
(IS_ID_UID(req->id1) || IS_ID_GID(req->id1))) {
rc1 = lookup_name2sid(state->cache,
req->id2name, req->id2domain, -1,
NULL, NULL,
&res->id.idmap_id_u.sid.prefix,
&res->id.idmap_id_u.sid.rid,
&res->id.idtype,
req, 1);
if (rc1 == IDMAP_ERR_NOTFOUND) {
req->direction |= _IDMAP_F_LOOKUP_AD;
state->ad_nqueries++;
} else
res->retcode = rc1;
}
if (res->retcode != IDMAP_SUCCESS &&
res->retcode != IDMAP_ERR_NS_LDAP_BAD_WINNAME &&
!(IDMAP_FATAL_ERROR(res->retcode))) {
idmap_how_clear(&res->info.how);
res->retcode = IDMAP_SUCCESS;
}
}
state->nldap_nqueries = 0;
return (retcode);
}