summaryrefslogtreecommitdiffstats
path: root/src/providers/proxy/proxy_id.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/providers/proxy/proxy_id.c')
-rw-r--r--src/providers/proxy/proxy_id.c1155
1 files changed, 1155 insertions, 0 deletions
diff --git a/src/providers/proxy/proxy_id.c b/src/providers/proxy/proxy_id.c
new file mode 100644
index 000000000..8536b9387
--- /dev/null
+++ b/src/providers/proxy/proxy_id.c
@@ -0,0 +1,1155 @@
+/*
+ SSSD
+
+ proxy_id.c
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2010 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "providers/proxy/proxy.h"
+
+/* =Getpwnam-wrapper======================================================*/
+
+static int delete_user(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain, const char *name);
+
+static int get_pw_name(TALLOC_CTX *mem_ctx,
+ struct proxy_id_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ const char *name)
+{
+ TALLOC_CTX *tmpctx;
+ struct passwd *pwd;
+ enum nss_status status;
+ char *buffer;
+ size_t buflen;
+ int ret;
+
+ DEBUG(7, ("Searching user by name (%s)\n", name));
+
+ tmpctx = talloc_new(mem_ctx);
+ if (!tmpctx) {
+ return ENOMEM;
+ }
+
+ pwd = talloc_zero(tmpctx, struct passwd);
+ if (!pwd) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(tmpctx, buflen);
+ if (!buffer) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* FIXME: should we move this call outside the transaction to keep the
+ * transaction as short as possible ? */
+ status = ctx->ops.getpwnam_r(name, pwd, buffer, buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_NOTFOUND:
+
+ DEBUG(7, ("User %s not found.\n", name));
+ ret = delete_user(tmpctx, sysdb, dom, name);
+ if (ret) {
+ goto done;
+ }
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("User %s found: (%s, %d, %d)\n",
+ name, pwd->pw_name, pwd->pw_uid, pwd->pw_gid));
+
+ /* uid=0 or gid=0 are invalid values */
+ /* also check that the id is in the valid range for this domain */
+ if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) ||
+ OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("User [%s] filtered out! (id out of range)\n", name));
+ ret = delete_user(tmpctx, sysdb, dom, name);
+ if (ret) {
+ goto done;
+ }
+ break;
+ }
+
+ ret = sysdb_store_user(tmpctx, sysdb, dom,
+ pwd->pw_name,
+ pwd->pw_passwd,
+ pwd->pw_uid,
+ pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell,
+ NULL, ctx->entry_cache_timeout);
+ if (ret) {
+ goto done;
+ }
+ break;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ ret = ENXIO;
+ goto done;
+
+ default:
+ ret = EIO;
+ goto done;
+ }
+
+done:
+ talloc_zfree(tmpctx);
+ if (ret) {
+ DEBUG(2, ("proxy -> getpwnam_r failed for '%s' <%d>\n",
+ name, status));
+ }
+ return ret;
+}
+
+static int delete_user(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain, const char *name)
+{
+ struct ldb_dn *dn;
+
+ DEBUG(7, ("User %s does not exist (or is invalid) on remote server,"
+ " deleting!\n", name));
+
+ dn = sysdb_user_dn(sysdb, mem_ctx, domain->name, name);
+ if (!dn) {
+ return ENOMEM;
+ }
+
+ return sysdb_delete_entry(sysdb, dn, true);
+}
+
+/* =Getpwuid-wrapper======================================================*/
+
+static int get_pw_uid(TALLOC_CTX *mem_ctx,
+ struct proxy_id_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ uid_t uid)
+{
+ TALLOC_CTX *tmpctx;
+ struct passwd *pwd;
+ enum nss_status status;
+ char *buffer;
+ size_t buflen;
+ bool del_user = false;
+ int ret;
+
+ DEBUG(7, ("Searching user by uid (%d)\n", uid));
+
+ tmpctx = talloc_new(mem_ctx);
+ if (!tmpctx) {
+ return ENOMEM;
+ }
+
+ pwd = talloc_zero(tmpctx, struct passwd);
+ if (!pwd) {
+ ret = ENOMEM;
+ DEBUG(1, ("proxy -> getpwuid_r failed for '%d': [%d] %s\n",
+ uid, ret, strerror(ret)));
+ return ret;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(tmpctx, buflen);
+ if (!buffer) {
+ ret = ENOMEM;
+ DEBUG(1, ("proxy -> getpwuid_r failed for '%d': [%d] %s\n",
+ uid, ret, strerror(ret)));
+ return ret;
+ }
+
+ status = ctx->ops.getpwuid_r(uid, pwd, buffer, buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_NOTFOUND:
+
+ DEBUG(7, ("User %d not found.\n", uid));
+ del_user = true;
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("User %d found (%s, %d, %d)\n",
+ uid, pwd->pw_name, pwd->pw_uid, pwd->pw_gid));
+
+ /* uid=0 or gid=0 are invalid values */
+ /* also check that the id is in the valid range for this domain */
+ if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) ||
+ OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
+ pwd->pw_name));
+ del_user = true;
+ break;
+ }
+
+ ret = sysdb_store_user(tmpctx, sysdb, dom,
+ pwd->pw_name,
+ pwd->pw_passwd,
+ pwd->pw_uid,
+ pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell,
+ NULL, ctx->entry_cache_timeout);
+ if (ret) {
+ goto done;
+ }
+ break;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ ret = ENXIO;
+ goto done;
+
+ default:
+ ret = EIO;
+ goto done;
+ }
+
+ if (del_user) {
+ DEBUG(7, ("User %d does not exist (or is invalid) on remote server,"
+ " deleting!\n", uid));
+
+ ret = sysdb_delete_user(tmpctx, sysdb, dom, NULL, uid);
+ if (ret) {
+ goto done;
+ }
+ }
+
+done:
+ talloc_zfree(tmpctx);
+ if (ret) {
+ DEBUG(2, ("proxy -> getpwuid_r failed for '%d' <%d>\n", uid, status));
+ }
+ return ret;
+}
+
+/* =Getpwent-wrapper======================================================*/
+
+static int enum_users(TALLOC_CTX *mem_ctx,
+ struct proxy_id_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom)
+{
+ TALLOC_CTX *tmpctx;
+ bool in_transaction = false;
+ struct passwd *pwd;
+ enum nss_status status;
+ size_t buflen;
+ char *buffer;
+ char *newbuf;
+ int ret;
+
+ DEBUG(7, ("Enumerating users\n"));
+
+ tmpctx = talloc_new(mem_ctx);
+ if (!tmpctx) {
+ return ENOMEM;
+ }
+
+ pwd = talloc_zero(tmpctx, struct passwd);
+ if (!pwd) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(tmpctx, buflen);
+ if (!buffer) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret) {
+ goto done;
+ }
+ in_transaction = true;
+
+ status = ctx->ops.setpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+again:
+ /* always zero out the pwd structure */
+ memset(pwd, 0, sizeof(struct passwd));
+
+ /* get entry */
+ status = ctx->ops.getpwent_r(pwd, buffer, buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_TRYAGAIN:
+ /* buffer too small ? */
+ if (buflen < MAX_BUF_SIZE) {
+ buflen *= 2;
+ }
+ if (buflen > MAX_BUF_SIZE) {
+ buflen = MAX_BUF_SIZE;
+ }
+ newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
+ if (!newbuf) {
+ ret = ENOMEM;
+ goto done;
+ }
+ buffer = newbuf;
+ goto again;
+
+ case NSS_STATUS_NOTFOUND:
+
+ /* we are done here */
+ DEBUG(7, ("Enumeration completed.\n"));
+
+ ret = sysdb_transaction_commit(sysdb);
+ in_transaction = false;
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("User found (%s, %d, %d)\n",
+ pwd->pw_name, pwd->pw_uid, pwd->pw_gid));
+
+ /* uid=0 or gid=0 are invalid values */
+ /* also check that the id is in the valid range for this domain */
+ if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) ||
+ OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
+ pwd->pw_name));
+
+ goto again; /* skip */
+ }
+
+ ret = sysdb_store_user(tmpctx, sysdb, dom,
+ pwd->pw_name,
+ pwd->pw_passwd,
+ pwd->pw_uid,
+ pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell,
+ NULL, ctx->entry_cache_timeout);
+ if (ret) {
+ /* Do not fail completely on errors.
+ * Just report the failure to save and go on */
+ DEBUG(2, ("Failed to store user %s. Ignoring.\n",
+ pwd->pw_name));
+ }
+ goto again; /* next */
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ ret = ENXIO;
+ break;
+
+ default:
+ ret = EIO;
+ DEBUG(2, ("proxy -> getpwent_r failed (%d)[%s]\n",
+ ret, strerror(ret)));
+ break;
+ }
+
+done:
+ talloc_zfree(tmpctx);
+ if (in_transaction) {
+ sysdb_transaction_cancel(sysdb);
+ }
+ ctx->ops.endpwent();
+ return ret;
+}
+
+/* =Getgrnam-wrapper======================================================*/
+
+#define DEBUG_GR_MEM(level, grp) \
+ do { \
+ if (debug_level >= level) { \
+ if (!grp->gr_mem || !grp->gr_mem[0]) { \
+ DEBUG(level, ("Group %s has no members!\n", \
+ grp->gr_name)); \
+ } else { \
+ int i = 0; \
+ while (grp->gr_mem[i]) { \
+ /* count */ \
+ i++; \
+ } \
+ DEBUG(level, ("Group %s has %d members!\n", \
+ grp->gr_name, i)); \
+ } \
+ } \
+ } while(0)
+
+static int get_gr_name(TALLOC_CTX *mem_ctx,
+ struct proxy_id_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ const char *name)
+{
+ TALLOC_CTX *tmpctx;
+ struct group *grp;
+ enum nss_status status;
+ char *buffer;
+ char *newbuf;
+ size_t buflen;
+ bool delete_group = false;
+ struct sysdb_attrs *members;
+ int ret;
+
+ DEBUG(7, ("Searching group by name (%s)\n", name));
+
+ tmpctx = talloc_new(mem_ctx);
+ if (!tmpctx) {
+ return ENOMEM;
+ }
+
+ grp = talloc(tmpctx, struct group);
+ if (!grp) {
+ ret = ENOMEM;
+ DEBUG(1, ("proxy -> getgrnam_r failed for '%s': [%d] %s\n",
+ name, ret, strerror(ret)));
+ return ret;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(tmpctx, buflen);
+ if (!buffer) {
+ ret = ENOMEM;
+ DEBUG(1, ("proxy -> getgrnam_r failed for '%s': [%d] %s\n",
+ name, ret, strerror(ret)));
+ return ret;
+ }
+
+ /* FIXME: should we move this call outside the transaction to keep the
+ * transaction as short as possible ? */
+again:
+ /* always zero out the grp structure */
+ memset(grp, 0, sizeof(struct group));
+
+ status = ctx->ops.getgrnam_r(name, grp, buffer, buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_TRYAGAIN:
+ /* buffer too small ? */
+ if (buflen < MAX_BUF_SIZE) {
+ buflen *= 2;
+ }
+ if (buflen > MAX_BUF_SIZE) {
+ buflen = MAX_BUF_SIZE;
+ }
+ newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
+ if (!newbuf) {
+ ret = ENOMEM;
+ goto done;
+ }
+ buffer = newbuf;
+ goto again;
+
+ case NSS_STATUS_NOTFOUND:
+
+ DEBUG(7, ("Group %s not found.\n", name));
+ delete_group = true;
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("Group %s found: (%s, %d)\n",
+ name, grp->gr_name, grp->gr_gid));
+
+ /* gid=0 is an invalid value */
+ /* also check that the id is in the valid range for this domain */
+ if (OUT_OF_ID_RANGE(grp->gr_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
+ name));
+ delete_group = true;
+ break;
+ }
+
+ DEBUG_GR_MEM(7, grp);
+
+ if (grp->gr_mem && grp->gr_mem[0]) {
+ members = sysdb_new_attrs(tmpctx);
+ if (!members) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = sysdb_attrs_users_from_str_list(members, SYSDB_MEMBER,
+ dom->name,
+ (const char **)grp->gr_mem);
+ if (ret) {
+ goto done;
+ }
+ } else {
+ members = NULL;
+ }
+
+ ret = sysdb_store_group(tmpctx, sysdb, dom,
+ grp->gr_name,
+ grp->gr_gid,
+ members,
+ ctx->entry_cache_timeout);
+ if (ret) {
+ goto done;
+ }
+ break;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ ret = ENXIO;
+ goto done;
+
+ default:
+ ret = EIO;
+ goto done;
+ }
+
+ if (delete_group) {
+ struct ldb_dn *dn;
+
+ DEBUG(7, ("Group %s does not exist (or is invalid) on remote server,"
+ " deleting!\n", name));
+
+ dn = sysdb_group_dn(sysdb, tmpctx, dom->name, name);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_delete_entry(sysdb, dn, true);
+ if (ret) {
+ goto done;
+ }
+ }
+
+done:
+ talloc_zfree(tmpctx);
+ if (ret) {
+ DEBUG(2, ("proxy -> getgrnam_r failed for '%s' <%d>\n",
+ name, status));
+ }
+ return ret;
+}
+
+/* =Getgrgid-wrapper======================================================*/
+
+static int get_gr_gid(TALLOC_CTX *mem_ctx,
+ struct proxy_id_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ gid_t gid)
+{
+ TALLOC_CTX *tmpctx;
+ struct group *grp;
+ enum nss_status status;
+ char *buffer;
+ char *newbuf;
+ size_t buflen;
+ bool delete_group = false;
+ struct sysdb_attrs *members;
+ int ret;
+
+ DEBUG(7, ("Searching group by gid (%d)\n", gid));
+
+ tmpctx = talloc_new(mem_ctx);
+ if (!tmpctx) {
+ return ENOMEM;
+ }
+
+ grp = talloc(tmpctx, struct group);
+ if (!grp) {
+ ret = ENOMEM;
+ DEBUG(1, ("proxy -> getgrgid_r failed for '%d': [%d] %s\n",
+ gid, ret, strerror(ret)));
+ return ret;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(tmpctx, buflen);
+ if (!buffer) {
+ ret = ENOMEM;
+ DEBUG(1, ("proxy -> getgrgid_r failed for '%d': [%d] %s\n",
+ gid, ret, strerror(ret)));
+ return ret;
+ }
+
+again:
+ /* always zero out the group structure */
+ memset(grp, 0, sizeof(struct group));
+
+ status = ctx->ops.getgrgid_r(gid, grp, buffer, buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_TRYAGAIN:
+ /* buffer too small ? */
+ if (buflen < MAX_BUF_SIZE) {
+ buflen *= 2;
+ }
+ if (buflen > MAX_BUF_SIZE) {
+ buflen = MAX_BUF_SIZE;
+ }
+ newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
+ if (!newbuf) {
+ ret = ENOMEM;
+ goto done;
+ }
+ buffer = newbuf;
+ goto again;
+
+ case NSS_STATUS_NOTFOUND:
+
+ DEBUG(7, ("Group %d not found.\n", gid));
+ delete_group = true;
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("Group %d found (%s, %d)\n",
+ gid, grp->gr_name, grp->gr_gid));
+
+ /* gid=0 is an invalid value */
+ /* also check that the id is in the valid range for this domain */
+ if (OUT_OF_ID_RANGE(grp->gr_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
+ grp->gr_name));
+ delete_group = true;
+ break;
+ }
+
+ DEBUG_GR_MEM(7, grp);
+
+ if (grp->gr_mem && grp->gr_mem[0]) {
+ members = sysdb_new_attrs(tmpctx);
+ if (!members) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = sysdb_attrs_users_from_str_list(members, SYSDB_MEMBER,
+ dom->name,
+ (const char **)grp->gr_mem);
+ if (ret) {
+ goto done;
+ }
+ } else {
+ members = NULL;
+ }
+
+ ret = sysdb_store_group(tmpctx, sysdb, dom,
+ grp->gr_name,
+ grp->gr_gid,
+ members,
+ ctx->entry_cache_timeout);
+ if (ret) {
+ goto done;
+ }
+ break;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ ret = ENXIO;
+ goto done;
+
+ default:
+ ret = EIO;
+ goto done;
+ }
+
+ if (delete_group) {
+
+ DEBUG(7, ("Group %d does not exist (or is invalid) on remote server,"
+ " deleting!\n", gid));
+
+ ret = sysdb_delete_group(tmpctx, sysdb, dom, NULL, gid);
+ if (ret) {
+ goto done;
+ }
+ }
+
+done:
+ talloc_zfree(tmpctx);
+ if (ret) {
+ DEBUG(2, ("proxy -> getgrgid_r failed for '%d' <%d>\n",
+ gid, status));
+ }
+ return ret;
+}
+
+/* =Getgrent-wrapper======================================================*/
+
+static int enum_groups(TALLOC_CTX *mem_ctx,
+ struct proxy_id_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom)
+{
+ TALLOC_CTX *tmpctx;
+ bool in_transaction = false;
+ struct group *grp;
+ enum nss_status status;
+ size_t buflen;
+ char *buffer;
+ struct sysdb_attrs *members;
+ char *newbuf;
+ int ret;
+
+ DEBUG(7, ("Enumerating groups\n"));
+
+ tmpctx = talloc_new(mem_ctx);
+ if (!tmpctx) {
+ return ENOMEM;
+ }
+
+ grp = talloc(tmpctx, struct group);
+ if (!grp) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(tmpctx, buflen);
+ if (!buffer) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret) {
+ goto done;
+ }
+ in_transaction = true;
+
+ status = ctx->ops.setgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+again:
+ /* always zero out the grp structure */
+ memset(grp, 0, sizeof(struct group));
+
+ /* get entry */
+ status = ctx->ops.getgrent_r(grp, buffer, buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_TRYAGAIN:
+ /* buffer too small ? */
+ if (buflen < MAX_BUF_SIZE) {
+ buflen *= 2;
+ }
+ if (buflen > MAX_BUF_SIZE) {
+ buflen = MAX_BUF_SIZE;
+ }
+ newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
+ if (!newbuf) {
+ ret = ENOMEM;
+ goto done;
+ }
+ buffer = newbuf;
+ goto again;
+
+ case NSS_STATUS_NOTFOUND:
+
+ /* we are done here */
+ DEBUG(7, ("Enumeration completed.\n"));
+
+ ret = sysdb_transaction_commit(sysdb);
+ in_transaction = false;
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("Group found (%s, %d)\n",
+ grp->gr_name, grp->gr_gid));
+
+ /* gid=0 is an invalid value */
+ /* also check that the id is in the valid range for this domain */
+ if (OUT_OF_ID_RANGE(grp->gr_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
+ grp->gr_name));
+
+ goto again; /* skip */
+ }
+
+ DEBUG_GR_MEM(7, grp);
+
+ if (grp->gr_mem && grp->gr_mem[0]) {
+ members = sysdb_new_attrs(tmpctx);
+ if (!members) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = sysdb_attrs_users_from_str_list(members, SYSDB_MEMBER,
+ dom->name,
+ (const char **)grp->gr_mem);
+ if (ret) {
+ goto done;
+ }
+ } else {
+ members = NULL;
+ }
+
+ ret = sysdb_store_group(tmpctx, sysdb, dom,
+ grp->gr_name,
+ grp->gr_gid,
+ members,
+ ctx->entry_cache_timeout);
+ if (ret) {
+ /* Do not fail completely on errors.
+ * Just report the failure to save and go on */
+ DEBUG(2, ("Failed to store group. Ignoring.\n"));
+ }
+ goto again; /* next */
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ ret = ENXIO;
+ break;
+
+ default:
+ ret = EIO;
+ DEBUG(2, ("proxy -> getgrent_r failed (%d)[%s]\n",
+ ret, strerror(ret)));
+ break;
+ }
+
+done:
+ talloc_zfree(tmpctx);
+ if (in_transaction) {
+ sysdb_transaction_cancel(sysdb);
+ }
+ ctx->ops.endgrent();
+ return ret;
+}
+
+
+/* =Initgroups-wrapper====================================================*/
+
+static int get_initgr_groups_process(TALLOC_CTX *memctx,
+ struct proxy_id_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ struct passwd *pwd);
+
+static int get_initgr(TALLOC_CTX *mem_ctx,
+ struct proxy_id_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ const char *name)
+{
+ TALLOC_CTX *tmpctx;
+ bool in_transaction = false;
+ struct passwd *pwd;
+ enum nss_status status;
+ char *buffer;
+ size_t buflen;
+ int ret;
+
+ tmpctx = talloc_new(mem_ctx);
+ if (!tmpctx) {
+ return ENOMEM;
+ }
+
+ pwd = talloc_zero(tmpctx, struct passwd);
+ if (!pwd) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(tmpctx, buflen);
+ if (!buffer) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret) {
+ goto done;
+ }
+ in_transaction = true;
+
+ /* FIXME: should we move this call outside the transaction to keep the
+ * transaction as short as possible ? */
+ status = ctx->ops.getpwnam_r(name, pwd, buffer, buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_NOTFOUND:
+
+ DEBUG(7, ("User %s not found.\n", name));
+ ret = delete_user(tmpctx, sysdb, dom, name);
+ if (ret) {
+ goto done;
+ }
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ /* uid=0 or gid=0 are invalid values */
+ /* also check that the id is in the valid range for this domain */
+ if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) ||
+ OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
+ name));
+ ret = delete_user(tmpctx, sysdb, dom, name);
+ break;
+ }
+
+ ret = sysdb_store_user(tmpctx, sysdb, dom,
+ pwd->pw_name,
+ pwd->pw_passwd,
+ pwd->pw_uid,
+ pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell,
+ NULL, ctx->entry_cache_timeout);
+ if (ret) {
+ goto done;
+ }
+
+ ret = get_initgr_groups_process(tmpctx, ctx, sysdb, dom, pwd);
+ if (ret == EOK) {
+ ret = sysdb_transaction_commit(sysdb);
+ in_transaction = true;
+ }
+ break;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ ret = ENXIO;
+ break;
+
+ default:
+ DEBUG(2, ("proxy -> getpwnam_r failed for '%s' <%d>\n",
+ name, status));
+ ret = EIO;
+ break;
+ }
+
+done:
+ talloc_zfree(tmpctx);
+ if (in_transaction) {
+ sysdb_transaction_cancel(sysdb);
+ }
+ return ret;
+}
+
+static int get_initgr_groups_process(TALLOC_CTX *memctx,
+ struct proxy_id_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ struct passwd *pwd)
+{
+ enum nss_status status;
+ long int limit;
+ long int size;
+ long int num;
+ long int num_gids;
+ gid_t *gids;
+ int ret;
+ int i;
+
+ num_gids = 0;
+ limit = 4096;
+ num = 4096;
+ size = num*sizeof(gid_t);
+ gids = talloc_size(memctx, size);
+ if (!gids) {
+ return ENOMEM;
+ }
+
+again:
+ /* FIXME: should we move this call outside the transaction to keep the
+ * transaction as short as possible ? */
+ status = ctx->ops.initgroups_dyn(pwd->pw_name, pwd->pw_gid, &num_gids,
+ &num, &gids, limit, &ret);
+ switch (status) {
+ case NSS_STATUS_TRYAGAIN:
+ /* buffer too small ? */
+ if (size < MAX_BUF_SIZE) {
+ num *= 2;
+ size = num*sizeof(gid_t);
+ }
+ if (size > MAX_BUF_SIZE) {
+ size = MAX_BUF_SIZE;
+ num = size/sizeof(gid_t);
+ }
+ limit = num;
+ gids = talloc_realloc_size(memctx, gids, size);
+ if (!gids) {
+ return ENOMEM;
+ }
+ goto again; /* retry with more memory */
+
+ case NSS_STATUS_SUCCESS:
+ DEBUG(4, ("User [%s] appears to be member of %lu groups\n",
+ pwd->pw_name, num_gids));
+
+ for (i = 0; i < num_gids; i++) {
+ ret = get_gr_gid(memctx, ctx, sysdb, dom, gids[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+ break;
+
+ default:
+ ret = EIO;
+ DEBUG(2, ("proxy -> initgroups_dyn failed (%d)[%s]\n",
+ ret, strerror(ret)));
+ break;
+ }
+
+ return ret;
+}
+
+/* =Proxy_Id-Functions====================================================*/
+
+void proxy_get_account_info(struct be_req *breq)
+{
+ struct be_acct_req *ar;
+ struct proxy_id_ctx *ctx;
+ struct tevent_context *ev;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *domain;
+ uid_t uid;
+ gid_t gid;
+ int ret;
+
+ ar = talloc_get_type(breq->req_data, struct be_acct_req);
+ ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data,
+ struct proxy_id_ctx);
+ ev = breq->be_ctx->ev;
+ sysdb = breq->be_ctx->sysdb;
+ domain = breq->be_ctx->domain;
+
+ if (be_is_offline(breq->be_ctx)) {
+ return proxy_reply(breq, DP_ERR_OFFLINE, EAGAIN, "Offline");
+ }
+
+ /* for now we support only core attrs */
+ if (ar->attr_type != BE_ATTR_CORE) {
+ return proxy_reply(breq, DP_ERR_FATAL, EINVAL, "Invalid attr type");
+ }
+
+ switch (ar->entry_type & 0xFFF) {
+ case BE_REQ_USER: /* user */
+ switch (ar->filter_type) {
+ case BE_FILTER_NAME:
+ if (strchr(ar->filter_value, '*')) {
+ ret = enum_users(breq, ctx, sysdb, domain);
+ } else {
+ ret = get_pw_name(breq, ctx, sysdb, domain, ar->filter_value);
+ }
+ break;
+
+ case BE_FILTER_IDNUM:
+ if (strchr(ar->filter_value, '*')) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ EINVAL, "Invalid attr type");
+ } else {
+ char *endptr;
+ errno = 0;
+ uid = (uid_t)strtol(ar->filter_value, &endptr, 0);
+ if (errno || *endptr || (ar->filter_value == endptr)) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ EINVAL, "Invalid attr type");
+ }
+ ret = get_pw_uid(breq, ctx, sysdb, domain, uid);
+ }
+ break;
+ default:
+ return proxy_reply(breq, DP_ERR_FATAL,
+ EINVAL, "Invalid filter type");
+ }
+ break;
+
+ case BE_REQ_GROUP: /* group */
+ switch (ar->filter_type) {
+ case BE_FILTER_NAME:
+ if (strchr(ar->filter_value, '*')) {
+ ret = enum_groups(breq, ctx, sysdb, domain);
+ } else {
+ ret = get_gr_name(breq, ctx, sysdb, domain, ar->filter_value);
+ }
+ break;
+ case BE_FILTER_IDNUM:
+ if (strchr(ar->filter_value, '*')) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ EINVAL, "Invalid attr type");
+ } else {
+ char *endptr;
+ errno = 0;
+ gid = (gid_t)strtol(ar->filter_value, &endptr, 0);
+ if (errno || *endptr || (ar->filter_value == endptr)) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ EINVAL, "Invalid attr type");
+ }
+ ret = get_gr_gid(breq, ctx, sysdb, domain, gid);
+ }
+ break;
+ default:
+ return proxy_reply(breq, DP_ERR_FATAL,
+ EINVAL, "Invalid filter type");
+ }
+ break;
+
+ case BE_REQ_INITGROUPS: /* init groups for user */
+ if (ar->filter_type != BE_FILTER_NAME) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ EINVAL, "Invalid filter type");
+ }
+ if (strchr(ar->filter_value, '*')) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ EINVAL, "Invalid filter value");
+ }
+ if (ctx->ops.initgroups_dyn == NULL) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ ENODEV, "Initgroups call not supported");
+ }
+ ret = get_initgr(breq, ctx, sysdb, domain, ar->filter_value);
+ break;
+
+ default: /*fail*/
+ return proxy_reply(breq, DP_ERR_FATAL,
+ EINVAL, "Invalid request type");
+ }
+
+ if (ret) {
+ if (ret == ENXIO) {
+ DEBUG(2, ("proxy returned UNAVAIL error, going offline!\n"));
+ be_mark_offline(breq->be_ctx);
+ }
+ proxy_reply(breq, DP_ERR_FATAL, ret, NULL);
+ return;
+ }
+ proxy_reply(breq, DP_ERR_OK, EOK, NULL);
+}