diff options
Diffstat (limited to 'source4/lib/ldb/nssldb')
-rw-r--r-- | source4/lib/ldb/nssldb/ldb-grp.c | 427 | ||||
-rw-r--r-- | source4/lib/ldb/nssldb/ldb-nss.c | 401 | ||||
-rw-r--r-- | source4/lib/ldb/nssldb/ldb-nss.h | 86 | ||||
-rw-r--r-- | source4/lib/ldb/nssldb/ldb-pwd.c | 241 | ||||
-rw-r--r-- | source4/lib/ldb/nssldb/nsstest.c | 433 |
5 files changed, 1588 insertions, 0 deletions
diff --git a/source4/lib/ldb/nssldb/ldb-grp.c b/source4/lib/ldb/nssldb/ldb-grp.c new file mode 100644 index 0000000000..f33ec65c55 --- /dev/null +++ b/source4/lib/ldb/nssldb/ldb-grp.c @@ -0,0 +1,427 @@ +/* + 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 Library General Public + License as published by the Free Software Foundation; either + version 2 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 Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#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->base, + LDB_SCOPE_SUBTREE, + _LDB_NSS_GRENT_FILTER, + _ldb_nss_gr_attrs, + &_ldb_nss_ctx->gr_res); + 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->base, + LDB_SCOPE_SUBTREE, + filter, + _ldb_nss_gr_attrs, + &gr_res); + 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->base, + LDB_SCOPE_SUBTREE, + filter, + _ldb_nss_gr_attrs, + &gr_res); + 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->base, + LDB_SCOPE_SUBTREE, + filter, + attrs, + &uid_res); + 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/source4/lib/ldb/nssldb/ldb-nss.c b/source4/lib/ldb/nssldb/ldb-nss.c new file mode 100644 index 0000000000..3206f38a10 --- /dev/null +++ b/source4/lib/ldb/nssldb/ldb-nss.c @@ -0,0 +1,401 @@ +/* + 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 Library General Public + License as published by the Free Software Foundation; either + version 2 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 Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#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; + + ret = ldb_global_init(); + if (ret != 0) { + goto failed; + } + + _ldb_nss_ctx->ldb = ldb_init(_ldb_nss_ctx); + 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_explode(_ldb_nss_ctx, _LDB_NSS_BASEDN); + if (_ldb_nss_ctx->base == NULL) { + 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; + 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; + 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; + + 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/source4/lib/ldb/nssldb/ldb-nss.h b/source4/lib/ldb/nssldb/ldb-nss.h new file mode 100644 index 0000000000..c780a21e81 --- /dev/null +++ b/source4/lib/ldb/nssldb/ldb-nss.h @@ -0,0 +1,86 @@ +/* + 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 Library General Public + License as published by the Free Software Foundation; either + version 2 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 Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#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; + const 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/source4/lib/ldb/nssldb/ldb-pwd.c b/source4/lib/ldb/nssldb/ldb-pwd.c new file mode 100644 index 0000000000..e4bafdcf7c --- /dev/null +++ b/source4/lib/ldb/nssldb/ldb-pwd.c @@ -0,0 +1,241 @@ +/* + 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 Library General Public + License as published by the Free Software Foundation; either + version 2 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 Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#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->base, + LDB_SCOPE_SUBTREE, + _LDB_NSS_PWENT_FILTER, + _ldb_nss_pw_attrs, + &_ldb_nss_ctx->pw_res); + 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->base, + LDB_SCOPE_SUBTREE, + filter, + _ldb_nss_pw_attrs, + &res); + 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->base, + LDB_SCOPE_SUBTREE, + filter, + _ldb_nss_pw_attrs, + &res); + 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; +} + diff --git a/source4/lib/ldb/nssldb/nsstest.c b/source4/lib/ldb/nssldb/nsstest.c new file mode 100644 index 0000000000..ea3964fc4e --- /dev/null +++ b/source4/lib/ldb/nssldb/nsstest.c @@ -0,0 +1,433 @@ +/* + Unix SMB/CIFS implementation. + + nss tester + + Copyright (C) Andrew Tridgell 2001-2004 + + 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 2 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <nss.h> +#include <dlfcn.h> +#include <pwd.h> +#include <grp.h> +#include <string.h> +#include <sys/types.h> + +typedef enum nss_status NSS_STATUS; + +static const char *so_path = "lib/libnss_ldb.so.2"; +static char *nss_name; +static int nss_errno; +static NSS_STATUS last_error; +static int total_errors; + +static void *find_fn(const char *name) +{ + char s[1024]; + static void *h; + void *res; + + snprintf(s,sizeof(s), "_nss_%s_%s", nss_name, name); + + if (!h) { + h = dlopen(so_path, RTLD_LAZY); + } + if (!h) { + printf("Can't open shared library %s : %s\n", so_path, dlerror()); + exit(1); + } + res = dlsym(h, s); + if (!res) { + printf("Can't find function %s : %s\n", s, dlerror()); + return NULL; + } + return res; +} + +static void report_nss_error(const char *who, NSS_STATUS status) +{ + last_error = status; + total_errors++; + printf("ERROR %s: NSS_STATUS=%d %d (nss_errno=%d)\n", + who, status, NSS_STATUS_SUCCESS, nss_errno); +} + +static struct passwd *nss_getpwent(void) +{ + NSS_STATUS (*_nss_getpwent_r)(struct passwd *, char *, + size_t , int *) = find_fn("getpwent_r"); + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + status = _nss_getpwent_r(&pwd, buf, sizeof(buf), &nss_errno); + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("getpwent", status); + return NULL; + } + return &pwd; +} + +static struct passwd *nss_getpwnam(const char *name) +{ + NSS_STATUS (*_nss_getpwnam_r)(const char *, struct passwd *, char *, + size_t , int *) = find_fn("getpwnam_r"); + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + status = _nss_getpwnam_r(name, &pwd, buf, sizeof(buf), &nss_errno); + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("getpwnam", status); + return NULL; + } + return &pwd; +} + +static struct passwd *nss_getpwuid(uid_t uid) +{ + NSS_STATUS (*_nss_getpwuid_r)(uid_t , struct passwd *, char *, + size_t , int *) = find_fn("getpwuid_r"); + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + status = _nss_getpwuid_r(uid, &pwd, buf, sizeof(buf), &nss_errno); + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("getpwuid", status); + return NULL; + } + return &pwd; +} + +static void nss_setpwent(void) +{ + NSS_STATUS (*_nss_setpwent)(void) = find_fn("setpwent"); + NSS_STATUS status; + status = _nss_setpwent(); + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("setpwent", status); + } +} + +static void nss_endpwent(void) +{ + NSS_STATUS (*_nss_endpwent)(void) = find_fn("endpwent"); + NSS_STATUS status; + status = _nss_endpwent(); + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("endpwent", status); + } +} + + +static struct group *nss_getgrent(void) +{ + NSS_STATUS (*_nss_getgrent_r)(struct group *, char *, + size_t , int *) = find_fn("getgrent_r"); + static struct group grp; + static char *buf; + static int buflen = 1024; + NSS_STATUS status; + + if (!buf) buf = malloc(buflen); + +again: + status = _nss_getgrent_r(&grp, buf, buflen, &nss_errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = realloc(buf, buflen); + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("getgrent", status); + return NULL; + } + return &grp; +} + +static struct group *nss_getgrnam(const char *name) +{ + NSS_STATUS (*_nss_getgrnam_r)(const char *, struct group *, char *, + size_t , int *) = find_fn("getgrnam_r"); + static struct group grp; + static char *buf; + static int buflen = 1000; + NSS_STATUS status; + + if (!buf) buf = malloc(buflen); +again: + status = _nss_getgrnam_r(name, &grp, buf, buflen, &nss_errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = realloc(buf, buflen); + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("getgrnam", status); + return NULL; + } + return &grp; +} + +static struct group *nss_getgrgid(gid_t gid) +{ + NSS_STATUS (*_nss_getgrgid_r)(gid_t , struct group *, char *, + size_t , int *) = find_fn("getgrgid_r"); + static struct group grp; + static char *buf; + static int buflen = 1000; + NSS_STATUS status; + + if (!buf) buf = malloc(buflen); +again: + status = _nss_getgrgid_r(gid, &grp, buf, buflen, &nss_errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = realloc(buf, buflen); + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("getgrgid", status); + return NULL; + } + return &grp; +} + +static void nss_setgrent(void) +{ + NSS_STATUS (*_nss_setgrent)(void) = find_fn("setgrent"); + NSS_STATUS status; + status = _nss_setgrent(); + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("setgrent", status); + } +} + +static void nss_endgrent(void) +{ + NSS_STATUS (*_nss_endgrent)(void) = find_fn("endgrent"); + NSS_STATUS status; + status = _nss_endgrent(); + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("endgrent", status); + } +} + +static int nss_initgroups(char *user, gid_t group, gid_t **groups, long int *start, long int *size) +{ + NSS_STATUS (*_nss_initgroups)(char *, gid_t , long int *, + long int *, gid_t **, long int , int *) = + find_fn("initgroups_dyn"); + NSS_STATUS status; + + if (!_nss_initgroups) return NSS_STATUS_UNAVAIL; + + status = _nss_initgroups(user, group, start, size, groups, 0, &nss_errno); + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("initgroups", status); + } + return status; +} + +static void print_passwd(struct passwd *pwd) +{ + printf("%s:%s:%d:%d:%s:%s:%s\n", + pwd->pw_name, + pwd->pw_passwd, + pwd->pw_uid, + pwd->pw_gid, + pwd->pw_gecos, + pwd->pw_dir, + pwd->pw_shell); +} + +static void print_group(struct group *grp) +{ + int i; + printf("%s:%s:%d: ", + grp->gr_name, + grp->gr_passwd, + grp->gr_gid); + + if (!grp->gr_mem[0]) { + printf("\n"); + return; + } + + for (i=0; grp->gr_mem[i+1]; i++) { + printf("%s, ", grp->gr_mem[i]); + } + printf("%s\n", grp->gr_mem[i]); +} + +static void nss_test_initgroups(char *name, gid_t gid) +{ + long int size = 16; + long int start = 1; + gid_t *groups = NULL; + int i; + NSS_STATUS status; + + groups = (gid_t *)malloc(size * sizeof(gid_t)); + groups[0] = gid; + + status = nss_initgroups(name, gid, &groups, &start, &size); + if (status == NSS_STATUS_UNAVAIL) { + printf("No initgroups fn\n"); + return; + } + + for (i=0; i<start-1; i++) { + printf("%d, ", groups[i]); + } + printf("%d\n", groups[i]); +} + + +static void nss_test_users(void) +{ + struct passwd *pwd; + + nss_setpwent(); + /* loop over all users */ + while ((pwd = nss_getpwent())) { + printf("Testing user %s\n", pwd->pw_name); + printf("getpwent: "); print_passwd(pwd); + pwd = nss_getpwuid(pwd->pw_uid); + if (!pwd) { + total_errors++; + printf("ERROR: can't getpwuid\n"); + continue; + } + printf("getpwuid: "); print_passwd(pwd); + pwd = nss_getpwnam(pwd->pw_name); + if (!pwd) { + total_errors++; + printf("ERROR: can't getpwnam\n"); + continue; + } + printf("getpwnam: "); print_passwd(pwd); + printf("initgroups: "); nss_test_initgroups(pwd->pw_name, pwd->pw_gid); + printf("\n"); + } + nss_endpwent(); +} + +static void nss_test_groups(void) +{ + struct group *grp; + + nss_setgrent(); + /* loop over all groups */ + while ((grp = nss_getgrent())) { + printf("Testing group %s\n", grp->gr_name); + printf("getgrent: "); print_group(grp); + grp = nss_getgrnam(grp->gr_name); + if (!grp) { + total_errors++; + printf("ERROR: can't getgrnam\n"); + continue; + } + printf("getgrnam: "); print_group(grp); + grp = nss_getgrgid(grp->gr_gid); + if (!grp) { + total_errors++; + printf("ERROR: can't getgrgid\n"); + continue; + } + printf("getgrgid: "); print_group(grp); + printf("\n"); + } + nss_endgrent(); +} + +static void nss_test_errors(void) +{ + struct passwd *pwd; + struct group *grp; + + pwd = getpwnam("nosuchname"); + if (pwd || last_error != NSS_STATUS_NOTFOUND) { + total_errors++; + printf("ERROR Non existant user gave error %d\n", last_error); + } + + pwd = getpwuid(0xFFF0); + if (pwd || last_error != NSS_STATUS_NOTFOUND) { + total_errors++; + printf("ERROR Non existant uid gave error %d\n", last_error); + } + + grp = getgrnam("nosuchgroup"); + if (grp || last_error != NSS_STATUS_NOTFOUND) { + total_errors++; + printf("ERROR Non existant group gave error %d\n", last_error); + } + + grp = getgrgid(0xFFF0); + if (grp || last_error != NSS_STATUS_NOTFOUND) { + total_errors++; + printf("ERROR Non existant gid gave error %d\n", last_error); + } +} + + int main(int argc, char *argv[]) +{ + char *p; + + if (argc > 1) so_path = argv[1]; + + p = strrchr(so_path, '_'); + if (!p) { + printf("Badly formed name for .so - must be libnss_FOO.so\n"); + exit(1); + } + nss_name = strdup(p+1); + p = strchr(nss_name, '.'); + if (p) *p = 0; + + printf("so_path=%s nss_name=%s\n\n", so_path, nss_name); + + nss_test_users(); + nss_test_groups(); + nss_test_errors(); + + printf("total_errors=%d\n", total_errors); + + return total_errors; +} |