summaryrefslogtreecommitdiffstats
path: root/lib/ldb/nssldb
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /lib/ldb/nssldb
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--lib/ldb/nssldb/README.txt34
-rw-r--r--lib/ldb/nssldb/ldb-grp.c429
-rw-r--r--lib/ldb/nssldb/ldb-nss.c395
-rw-r--r--lib/ldb/nssldb/ldb-nss.h84
-rw-r--r--lib/ldb/nssldb/ldb-pwd.c242
5 files changed, 1184 insertions, 0 deletions
diff --git a/lib/ldb/nssldb/README.txt b/lib/ldb/nssldb/README.txt
new file mode 100644
index 0000000..7f5877c
--- /dev/null
+++ b/lib/ldb/nssldb/README.txt
@@ -0,0 +1,34 @@
+
+This test code requires a tdb that is configured for to use the asq module.
+You can do that adding the following record to a tdb:
+
+dn: @MODULES
+@LIST: asq
+
+Other modules can be used as well (like rdn_name for example)
+
+The uidNumber 0 and the gidNumber 0 are considered invalid.
+
+The user records should contain the following attributes:
+uid (required) the user name
+userPassword (optional) the user password (if not present "LDB" is
+ returned in the password field)
+uidNumber (required) the user uid
+gidNumber (required) the user primary gid
+gecos (optional) the GECOS
+homeDirectory (required) the home directory
+loginShell (required) the login shell
+memberOf (required) all the groups the user is member of should
+ be reported here using their DNs. The
+ primary group as well.
+
+The group accounts should contain the following attributes:
+cn (required) the group name
+uesrPassword (optional) the group password (if not present "LDB" is
+ returned in the password field)
+gidNumber (required) the group gid
+member (optional) the DNs of the member users, also the ones
+ that have this group as primary
+
+
+SSS
diff --git a/lib/ldb/nssldb/ldb-grp.c b/lib/ldb/nssldb/ldb-grp.c
new file mode 100644
index 0000000..5e7556d
--- /dev/null
+++ b/lib/ldb/nssldb/ldb-grp.c
@@ -0,0 +1,429 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ldb-nss.h"
+
+extern struct _ldb_nss_context *_ldb_nss_ctx;
+
+const char *_ldb_nss_gr_attrs[] = {
+ "cn",
+ "userPassword",
+ "gidNumber",
+ NULL
+};
+
+const char *_ldb_nss_mem_attrs[] = {
+ "uid",
+ NULL
+};
+
+#define _NSS_LDB_ENOMEM(amem) \
+ do { \
+ if ( ! amem) { \
+ errno = ENOMEM; \
+ talloc_free(memctx); \
+ return NSS_STATUS_UNAVAIL; \
+ } \
+ } while(0)
+
+/* This setgrent, getgrent, endgrent is not very efficient */
+
+NSS_STATUS _nss_ldb_setgrent(void)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->gr_cur = 0;
+ if (_ldb_nss_ctx->gr_res != NULL) {
+ talloc_free(_ldb_nss_ctx->gr_res);
+ _ldb_nss_ctx->gr_res = NULL;
+ }
+
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &_ldb_nss_ctx->gr_res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_gr_attrs,
+ _LDB_NSS_GRENT_FILTER);
+ if (ret != LDB_SUCCESS) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_endgrent(void)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->gr_cur = 0;
+ if (_ldb_nss_ctx->gr_res) {
+ talloc_free(_ldb_nss_ctx->gr_res);
+ _ldb_nss_ctx->gr_res = NULL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getgrent_r(struct group *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ struct ldb_result *res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ *errnop = 0;
+
+ if (_ldb_nss_ctx->gr_cur >= _ldb_nss_ctx->gr_res->count) {
+ /* already returned all entries */
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ res = talloc_zero(_ldb_nss_ctx->gr_res, struct ldb_result);
+ if ( ! res) {
+ errno = *errnop = ENOMEM;
+ _ldb_nss_ctx->gr_cur++; /* skip this entry */
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ ret = _ldb_nss_group_request(&res,
+ _ldb_nss_ctx->gr_res->msgs[_ldb_nss_ctx->gr_cur]->dn,
+ _ldb_nss_mem_attrs,
+ "member");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ talloc_free(res);
+ _ldb_nss_ctx->gr_cur++; /* skip this entry */
+ return ret;
+ }
+
+ ret = _ldb_nss_fill_group(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ _ldb_nss_ctx->gr_res->msgs[_ldb_nss_ctx->gr_cur],
+ res);
+
+ talloc_free(res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ if (ret != NSS_STATUS_TRYAGAIN) {
+ _ldb_nss_ctx->gr_cur++; /* skip this entry */
+ }
+ return ret;
+ }
+
+ /* this entry is ok, increment counter to nex entry */
+ _ldb_nss_ctx->gr_cur++;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getgrnam_r(const char *name, struct group *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ TALLOC_CTX *ctx;
+ struct ldb_result *gr_res;
+ struct ldb_result *mem_res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ ctx = talloc_new(_ldb_nss_ctx->ldb);
+ if ( ! ctx) {
+ *errnop = errno = ENOMEM;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* build the filter for this uid */
+ filter = talloc_asprintf(ctx, _LDB_NSS_GRNAM_FILTER, name);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &gr_res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_gr_attrs,
+ filter);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ talloc_steal(ctx, gr_res);
+
+ /* if none found return */
+ if (gr_res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (gr_res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ mem_res = talloc_zero(ctx, struct ldb_result);
+ if ( ! mem_res) {
+ errno = *errnop = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ ret = _ldb_nss_group_request(&mem_res,
+ gr_res->msgs[0]->dn,
+ _ldb_nss_mem_attrs,
+ "member");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ goto done;
+ }
+
+ ret = _ldb_nss_fill_group(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ gr_res->msgs[0],
+ mem_res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ ret = NSS_STATUS_SUCCESS;
+done:
+ talloc_free(ctx);
+ return ret;
+}
+
+NSS_STATUS _nss_ldb_getgrgid_r(gid_t gid, struct group *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ TALLOC_CTX *ctx;
+ struct ldb_result *gr_res;
+ struct ldb_result *mem_res;
+
+ if (gid == 0) { /* we don't serve root gid by policy */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ ctx = talloc_new(_ldb_nss_ctx->ldb);
+ if ( ! ctx) {
+ *errnop = errno = ENOMEM;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* build the filter for this uid */
+ filter = talloc_asprintf(ctx, _LDB_NSS_GRGID_FILTER, gid);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &gr_res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_gr_attrs,
+ filter);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ talloc_steal(ctx, gr_res);
+
+ /* if none found return */
+ if (gr_res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (gr_res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ mem_res = talloc_zero(ctx, struct ldb_result);
+ if ( ! mem_res) {
+ errno = *errnop = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ ret = _ldb_nss_group_request(&mem_res,
+ gr_res->msgs[0]->dn,
+ _ldb_nss_mem_attrs,
+ "member");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ goto done;
+ }
+
+ ret = _ldb_nss_fill_group(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ gr_res->msgs[0],
+ mem_res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ ret = NSS_STATUS_SUCCESS;
+done:
+ talloc_free(ctx);
+ return ret;
+}
+
+NSS_STATUS _nss_ldb_initgroups_dyn(const char *user, gid_t group, long int *start, long int *size, gid_t **groups, long int limit, int *errnop)
+{
+ int ret;
+ char *filter;
+ const char * attrs[] = { "uidNumber", "gidNumber", NULL };
+ struct ldb_result *uid_res;
+ struct ldb_result *mem_res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ mem_res = talloc_zero(_ldb_nss_ctx, struct ldb_result);
+ if ( ! mem_res) {
+ errno = *errnop = ENOMEM;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* build the filter for this name */
+ filter = talloc_asprintf(mem_res, _LDB_NSS_PWNAM_FILTER, user);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &uid_res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ attrs,
+ filter);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ talloc_steal(mem_res, uid_res);
+
+ /* if none found return */
+ if (uid_res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (uid_res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ ret = _ldb_nss_group_request(&mem_res,
+ uid_res->msgs[0]->dn,
+ attrs,
+ "memberOf");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ goto done;
+ }
+
+ ret = _ldb_nss_fill_initgr(group,
+ limit,
+ start,
+ size,
+ groups,
+ errnop,
+ mem_res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ ret = NSS_STATUS_SUCCESS;
+
+done:
+ talloc_free(mem_res);
+ return ret;
+}
diff --git a/lib/ldb/nssldb/ldb-nss.c b/lib/ldb/nssldb/ldb-nss.c
new file mode 100644
index 0000000..92b0635
--- /dev/null
+++ b/lib/ldb/nssldb/ldb-nss.c
@@ -0,0 +1,395 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ldb-nss.h"
+
+struct _ldb_nss_context *_ldb_nss_ctx = NULL;
+
+NSS_STATUS _ldb_nss_init(void)
+{
+ int ret;
+
+ pid_t mypid = getpid();
+
+ if (_ldb_nss_ctx != NULL) {
+ if (_ldb_nss_ctx->pid == mypid) {
+ /* already initialized */
+ return NSS_STATUS_SUCCESS;
+ } else {
+ /* we are in a forked child now, reinitialize */
+ talloc_free(_ldb_nss_ctx);
+ _ldb_nss_ctx = NULL;
+ }
+ }
+
+ _ldb_nss_ctx = talloc_named(NULL, 0, "_ldb_nss_ctx(%u)", mypid);
+ if (_ldb_nss_ctx == NULL) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ _ldb_nss_ctx->pid = mypid;
+
+ _ldb_nss_ctx->ldb = ldb_init(_ldb_nss_ctx, NULL);
+ if (_ldb_nss_ctx->ldb == NULL) {
+ goto failed;
+ }
+
+ ret = ldb_connect(_ldb_nss_ctx->ldb, _LDB_NSS_URL, LDB_FLG_RDONLY, NULL);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ _ldb_nss_ctx->base = ldb_dn_new(_ldb_nss_ctx, _ldb_nss_ctx->ldb, _LDB_NSS_BASEDN);
+ if ( ! ldb_dn_validate(_ldb_nss_ctx->base)) {
+ goto failed;
+ }
+
+ _ldb_nss_ctx->pw_cur = 0;
+ _ldb_nss_ctx->pw_res = NULL;
+ _ldb_nss_ctx->gr_cur = 0;
+ _ldb_nss_ctx->gr_res = NULL;
+
+ return NSS_STATUS_SUCCESS;
+
+failed:
+ /* talloc_free(_ldb_nss_ctx); */
+ _ldb_nss_ctx = NULL;
+ return NSS_STATUS_UNAVAIL;
+}
+
+NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *msg)
+{
+ int len;
+ int bufpos;
+ const char *tmp;
+
+ bufpos = 0;
+
+ /* get username */
+ tmp = ldb_msg_find_attr_as_string(msg, "uid", NULL);
+ if (tmp == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_name = &buffer[bufpos];
+ bufpos += len;
+
+ /* get userPassword */
+ tmp = ldb_msg_find_attr_as_string(msg, "userPassword", NULL);
+ if (tmp == NULL) {
+ tmp = "LDB";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_passwd = &buffer[bufpos];
+ bufpos += len;
+
+ /* this backend never serves an uid 0 user */
+ result->pw_uid = ldb_msg_find_attr_as_int(msg, "uidNumber", 0);
+ if (result->pw_uid == 0) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ result->pw_gid = ldb_msg_find_attr_as_int(msg, "gidNumber", 0);
+ if (result->pw_gid == 0) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* get gecos */
+ tmp = ldb_msg_find_attr_as_string(msg, "gecos", NULL);
+ if (tmp == NULL) {
+ tmp = "";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_gecos = &buffer[bufpos];
+ bufpos += len;
+
+ /* get homeDirectory */
+ tmp = ldb_msg_find_attr_as_string(msg, "homeDirectory", NULL);
+ if (tmp == NULL) {
+ tmp = "";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_dir = &buffer[bufpos];
+ bufpos += len;
+
+ /* get shell */
+ tmp = ldb_msg_find_attr_as_string(msg, "loginShell", NULL);
+ if (tmp == NULL) {
+ tmp = "";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_shell = &buffer[bufpos];
+ bufpos += len;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _ldb_nss_fill_group(struct group *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *group,
+ struct ldb_result *members)
+{
+ const char *tmp;
+ size_t len;
+ size_t bufpos;
+ size_t lsize;
+ unsigned int i;
+
+ bufpos = 0;
+
+ /* get group name */
+ tmp = ldb_msg_find_attr_as_string(group, "cn", NULL);
+ if (tmp == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->gr_name = &buffer[bufpos];
+ bufpos += len;
+
+ /* get userPassword */
+ tmp = ldb_msg_find_attr_as_string(group, "userPassword", NULL);
+ if (tmp == NULL) {
+ tmp = "LDB";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->gr_passwd = &buffer[bufpos];
+ bufpos += len;
+
+ result->gr_gid = ldb_msg_find_attr_as_int(group, "gidNumber", 0);
+ if (result->gr_gid == 0) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* check if there is enough memory for the list of pointers */
+ lsize = (members->count + 1) * sizeof(char *);
+
+ /* align buffer on pointer boundary */
+ bufpos += (sizeof(char*) - ((unsigned long)(buffer) % sizeof(char*)));
+ if ((buflen - bufpos) < lsize) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ result->gr_mem = (char **)&buffer[bufpos];
+ bufpos += lsize;
+
+ for (i = 0; i < members->count; i++) {
+ tmp = ldb_msg_find_attr_as_string(members->msgs[i], "uid", NULL);
+ if (tmp == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->gr_mem[i] = &buffer[bufpos];
+ bufpos += len;
+ }
+
+ result->gr_mem[i] = NULL;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _ldb_nss_fill_initgr(gid_t group,
+ long int limit,
+ long int *start,
+ long int *size,
+ gid_t **groups,
+ int *errnop,
+ struct ldb_result *grlist)
+{
+ NSS_STATUS ret;
+ unsigned int i;
+
+ for (i = 0; i < grlist->count; i++) {
+
+ if (limit && (*start > limit)) {
+ /* TODO: warn no all groups were reported */
+ *errnop = 0;
+ ret = NSS_STATUS_SUCCESS;
+ goto done;
+ }
+
+ if (*start == *size) {
+ /* buffer full, enlarge it */
+ long int gs;
+ gid_t *gm;
+
+ gs = (*size) + 32;
+ if (limit && (gs > limit)) {
+ gs = limit;
+ }
+
+ gm = (gid_t *)realloc((*groups), gs * sizeof(gid_t));
+ if ( ! gm) {
+ *errnop = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ *groups = gm;
+ *size = gs;
+ }
+
+ (*groups)[*start] = ldb_msg_find_attr_as_int(grlist->msgs[i], "gidNumber", 0);
+ if ((*groups)[*start] == 0 || (*groups)[*start] == group) {
+ /* skip root group or primary group */
+ continue;
+ }
+ (*start)++;
+
+ }
+
+ *errnop = 0;
+ ret = NSS_STATUS_SUCCESS;
+done:
+ return ret;
+}
+
+#define _LDB_NSS_ALLOC_CHECK(mem) do { if (!mem) { errno = ENOMEM; return NSS_STATUS_UNAVAIL; } } while(0)
+
+NSS_STATUS _ldb_nss_group_request(struct ldb_result **_res,
+ struct ldb_dn *group_dn,
+ const char * const *attrs,
+ const char *mattr)
+{
+ struct ldb_control **ctrls;
+ struct ldb_control *ctrl;
+ struct ldb_asq_control *asqc;
+ struct ldb_request *req;
+ int ret;
+ struct ldb_result *res = *_res;
+
+ ctrls = talloc_array(res, struct ldb_control *, 2);
+ _LDB_NSS_ALLOC_CHECK(ctrls);
+
+ ctrl = talloc(ctrls, struct ldb_control);
+ _LDB_NSS_ALLOC_CHECK(ctrl);
+
+ asqc = talloc(ctrl, struct ldb_asq_control);
+ _LDB_NSS_ALLOC_CHECK(asqc);
+
+ asqc->source_attribute = talloc_strdup(asqc, mattr);
+ _LDB_NSS_ALLOC_CHECK(asqc->source_attribute);
+
+ asqc->request = 1;
+ asqc->src_attr_len = strlen(asqc->source_attribute);
+ ctrl->oid = LDB_CONTROL_ASQ_OID;
+ ctrl->critical = 1;
+ ctrl->data = asqc;
+ ctrls[0] = ctrl;
+ ctrls[1] = NULL;
+
+ ret = ldb_build_search_req(
+ &req,
+ _ldb_nss_ctx->ldb,
+ res,
+ group_dn,
+ LDB_SCOPE_BASE,
+ "(objectClass=*)",
+ attrs,
+ ctrls,
+ res,
+ ldb_search_default_callback);
+
+ if (ret != LDB_SUCCESS) {
+ errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ ldb_set_timeout(_ldb_nss_ctx->ldb, req, 0);
+
+ ret = ldb_request(_ldb_nss_ctx->ldb, req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ } else {
+ talloc_free(req);
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ talloc_free(req);
+ return NSS_STATUS_SUCCESS;
+}
+
diff --git a/lib/ldb/nssldb/ldb-nss.h b/lib/ldb/nssldb/ldb-nss.h
new file mode 100644
index 0000000..583876f
--- /dev/null
+++ b/lib/ldb/nssldb/ldb-nss.h
@@ -0,0 +1,84 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LDB_NSS
+#define _LDB_NSS
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include <nss.h>
+#include <pwd.h>
+#include <grp.h>
+
+#define _LDB_NSS_URL "etc/users.ldb"
+#define _LDB_NSS_BASEDN "CN=Users,CN=System"
+#define _LDB_NSS_PWENT_FILTER "(&(objectClass=posixAccount)(!(uidNumber=0))(!(gidNumber=0)))"
+#define _LDB_NSS_PWUID_FILTER "(&(objectClass=posixAccount)(uidNumber=%d)(!(gidNumber=0)))"
+#define _LDB_NSS_PWNAM_FILTER "(&(objectClass=posixAccount)(uid=%s)(!(uidNumber=0))(!(gidNumber=0)))"
+
+#define _LDB_NSS_GRENT_FILTER "(&(objectClass=posixGroup)(!(gidNumber=0)))"
+#define _LDB_NSS_GRGID_FILTER "(&(objectClass=posixGroup)(gidNumber=%d)))"
+#define _LDB_NSS_GRNAM_FILTER "(&(objectClass=posixGroup)(cn=%s)(!(gidNumber=0)))"
+
+typedef enum nss_status NSS_STATUS;
+
+struct _ldb_nss_context {
+
+ pid_t pid;
+
+ struct ldb_context *ldb;
+ struct ldb_dn *base;
+
+ int pw_cur;
+ struct ldb_result *pw_res;
+
+ int gr_cur;
+ struct ldb_result *gr_res;
+};
+
+NSS_STATUS _ldb_nss_init(void);
+
+NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *msg);
+
+NSS_STATUS _ldb_nss_fill_group(struct group *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *group,
+ struct ldb_result *members);
+
+NSS_STATUS _ldb_nss_fill_initgr(gid_t group,
+ long int limit,
+ long int *start,
+ long int *size,
+ gid_t **groups,
+ int *errnop,
+ struct ldb_result *grlist);
+
+NSS_STATUS _ldb_nss_group_request(struct ldb_result **res,
+ struct ldb_dn *group_dn,
+ const char * const *attrs,
+ const char *mattr);
+
+#endif /* _LDB_NSS */
diff --git a/lib/ldb/nssldb/ldb-pwd.c b/lib/ldb/nssldb/ldb-pwd.c
new file mode 100644
index 0000000..6ab103a
--- /dev/null
+++ b/lib/ldb/nssldb/ldb-pwd.c
@@ -0,0 +1,242 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ldb-nss.h"
+
+extern struct _ldb_nss_context *_ldb_nss_ctx;
+
+const char *_ldb_nss_pw_attrs[] = {
+ "uid",
+ "userPassword",
+ "uidNumber",
+ "gidNumber",
+ "gecos",
+ "homeDirectory",
+ "loginShell",
+ NULL
+};
+
+NSS_STATUS _nss_ldb_setpwent(void)
+{
+ int ret;
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->pw_cur = 0;
+ if (_ldb_nss_ctx->pw_res != NULL) {
+ talloc_free(_ldb_nss_ctx->pw_res);
+ _ldb_nss_ctx->pw_res = NULL;
+ }
+
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &_ldb_nss_ctx->pw_res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_pw_attrs,
+ _LDB_NSS_PWENT_FILTER);
+ if (ret != LDB_SUCCESS) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_endpwent(void)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->pw_cur = 0;
+ if (_ldb_nss_ctx->pw_res) {
+ talloc_free(_ldb_nss_ctx->pw_res);
+ _ldb_nss_ctx->pw_res = NULL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getpwent_r(struct passwd *result_buf,
+ char *buffer,
+ int buflen,
+ int *errnop)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ *errnop = 0;
+
+ if (_ldb_nss_ctx->pw_cur >= _ldb_nss_ctx->pw_res->count) {
+ /* already returned all entries */
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ret = _ldb_nss_fill_passwd(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ _ldb_nss_ctx->pw_res->msgs[_ldb_nss_ctx->pw_cur]);
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->pw_cur++;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getpwuid_r(uid_t uid, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ struct ldb_result *res;
+
+ if (uid == 0) { /* we don't serve root uid by policy */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ /* build the filter for this uid */
+ filter = talloc_asprintf(_ldb_nss_ctx, _LDB_NSS_PWUID_FILTER, uid);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_pw_attrs,
+ filter);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* if none found return */
+ if (res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* fill in the passwd struct */
+ ret = _ldb_nss_fill_passwd(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ res->msgs[0]);
+
+done:
+ talloc_free(filter);
+ talloc_free(res);
+ return ret;
+}
+
+NSS_STATUS _nss_ldb_getpwnam_r(const char *name, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ struct ldb_result *res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ /* build the filter for this name */
+ filter = talloc_asprintf(_ldb_nss_ctx, _LDB_NSS_PWNAM_FILTER, name);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_pw_attrs,
+ filter);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* if none found return */
+ if (res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* fill in the passwd struct */
+ ret = _ldb_nss_fill_passwd(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ res->msgs[0]);
+
+done:
+ talloc_free(filter);
+ talloc_free(res);
+ return ret;
+}
+