summaryrefslogtreecommitdiffstats
path: root/src/providers/proxy/proxy_id.c
diff options
context:
space:
mode:
authorStephen Gallagher <sgallagh@redhat.com>2010-06-25 11:28:20 -0400
committerStephen Gallagher <sgallagh@redhat.com>2010-06-30 07:28:26 -0400
commit2dd3faebcd3cfd00efda38ffd2585d675e696b12 (patch)
tree76b74b05e63ef3fffeae6318a44a62d78b7c0545 /src/providers/proxy/proxy_id.c
parentb19739caff3b15bbf79855f7f0339add73e64cce (diff)
downloadsssd-2dd3faebcd3cfd00efda38ffd2585d675e696b12.tar.gz
sssd-2dd3faebcd3cfd00efda38ffd2585d675e696b12.tar.xz
sssd-2dd3faebcd3cfd00efda38ffd2585d675e696b12.zip
Split proxy.c into smaller files
proxy.c was growing too large to manage (and some graphical development tools could no longer open it because of memory limitations). This patch splits proxy.c into the following files: proxy_init.c: Setup routines for the plugin proxy_id.c: Functions to handle user and group lookups proxy_auth.c: Functions to handle PAM interactions proxy_common.c: Common utility routines
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);
+}