summaryrefslogtreecommitdiffstats
path: root/src/providers/proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/providers/proxy.c')
-rw-r--r--src/providers/proxy.c2521
1 files changed, 2521 insertions, 0 deletions
diff --git a/src/providers/proxy.c b/src/providers/proxy.c
new file mode 100644
index 000000000..12bb25ec7
--- /dev/null
+++ b/src/providers/proxy.c
@@ -0,0 +1,2521 @@
+/*
+ SSSD
+
+ Proxy Module
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008-2009
+
+ 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 <nss.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dlfcn.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+
+#include "util/util.h"
+#include "providers/dp_backend.h"
+#include "db/sysdb.h"
+
+struct proxy_nss_ops {
+ enum nss_status (*getpwnam_r)(const char *name, struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*setpwent)(void);
+ enum nss_status (*getpwent_r)(struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*endpwent)(void);
+
+ enum nss_status (*getgrnam_r)(const char *name, struct group *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*getgrgid_r)(gid_t gid, struct group *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*setgrent)(void);
+ enum nss_status (*getgrent_r)(struct group *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*endgrent)(void);
+ enum nss_status (*initgroups_dyn)(const char *user, gid_t group,
+ long int *start, long int *size,
+ gid_t **groups, long int limit,
+ int *errnop);
+};
+
+struct proxy_ctx {
+ struct be_ctx *be;
+ int entry_cache_timeout;
+ struct proxy_nss_ops ops;
+};
+
+struct proxy_auth_ctx {
+ struct be_ctx *be;
+ char *pam_target;
+};
+
+struct authtok_conv {
+ uint32_t authtok_size;
+ uint8_t *authtok;
+};
+
+static int proxy_internal_conv(int num_msg, const struct pam_message **msgm,
+ struct pam_response **response,
+ void *appdata_ptr) {
+ int i;
+ struct pam_response *reply;
+ struct authtok_conv *auth_data;
+
+ auth_data = talloc_get_type(appdata_ptr, struct authtok_conv);
+
+ if (num_msg <= 0) return PAM_CONV_ERR;
+
+ reply = (struct pam_response *) calloc(num_msg,
+ sizeof(struct pam_response));
+ if (reply == NULL) return PAM_CONV_ERR;
+
+ for (i=0; i < num_msg; i++) {
+ switch( msgm[i]->msg_style ) {
+ case PAM_PROMPT_ECHO_OFF:
+ DEBUG(4, ("Conversation message: [%s]\n", msgm[i]->msg));
+ reply[i].resp_retcode = 0;
+ reply[i].resp = calloc(auth_data->authtok_size + 1,
+ sizeof(char));
+ if (reply[i].resp == NULL) goto failed;
+ memcpy(reply[i].resp, auth_data->authtok, auth_data->authtok_size);
+
+ break;
+ default:
+ DEBUG(1, ("Conversation style %d not supported.\n",
+ msgm[i]->msg_style));
+ goto failed;
+ }
+ }
+
+ *response = reply;
+ reply = NULL;
+
+ return PAM_SUCCESS;
+
+failed:
+ free(reply);
+ return PAM_CONV_ERR;
+}
+
+static void proxy_pam_handler_cache_done(struct tevent_req *treq);
+static void proxy_reply(struct be_req *req, int dp_err,
+ int error, const char *errstr);
+
+static void proxy_pam_handler(struct be_req *req) {
+ int ret;
+ int pam_status;
+ pam_handle_t *pamh=NULL;
+ struct authtok_conv *auth_data;
+ struct pam_conv conv;
+ struct pam_data *pd;
+ struct proxy_auth_ctx *ctx;;
+ bool cache_auth_data = false;
+
+ pd = talloc_get_type(req->req_data, struct pam_data);
+
+ switch (pd->cmd) {
+ case SSS_PAM_AUTHENTICATE:
+ ctx = talloc_get_type(req->be_ctx->bet_info[BET_AUTH].pvt_bet_data,
+ struct proxy_auth_ctx);
+ break;
+ case SSS_PAM_CHAUTHTOK:
+ case SSS_PAM_CHAUTHTOK_PRELIM:
+ ctx = talloc_get_type(req->be_ctx->bet_info[BET_CHPASS].pvt_bet_data,
+ struct proxy_auth_ctx);
+ break;
+ case SSS_PAM_ACCT_MGMT:
+ ctx = talloc_get_type(req->be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
+ struct proxy_auth_ctx);
+ break;
+ case SSS_PAM_SETCRED:
+ case SSS_PAM_OPEN_SESSION:
+ case SSS_PAM_CLOSE_SESSION:
+ pd->pam_status = PAM_SUCCESS;
+ proxy_reply(req, DP_ERR_OK, EOK, NULL);
+ return;
+ default:
+ DEBUG(1, ("Unsupported PAM task.\n"));
+ pd->pam_status = PAM_MODULE_UNKNOWN;
+ proxy_reply(req, DP_ERR_OK, EINVAL, "Unsupported PAM task");
+ return;
+ }
+
+ conv.conv=proxy_internal_conv;
+ auth_data = talloc_zero(req, struct authtok_conv);
+ conv.appdata_ptr=auth_data;
+
+ ret = pam_start(ctx->pam_target, pd->user, &conv, &pamh);
+ if (ret == PAM_SUCCESS) {
+ DEBUG(1, ("Pam transaction started.\n"));
+ ret = pam_set_item(pamh, PAM_TTY, pd->tty);
+ if (ret != PAM_SUCCESS) {
+ DEBUG(1, ("Setting PAM_TTY failed: %s.\n", pam_strerror(pamh, ret)));
+ }
+ ret = pam_set_item(pamh, PAM_RUSER, pd->ruser);
+ if (ret != PAM_SUCCESS) {
+ DEBUG(1, ("Setting PAM_RUSER failed: %s.\n", pam_strerror(pamh, ret)));
+ }
+ ret = pam_set_item(pamh, PAM_RHOST, pd->rhost);
+ if (ret != PAM_SUCCESS) {
+ DEBUG(1, ("Setting PAM_RHOST failed: %s.\n", pam_strerror(pamh, ret)));
+ }
+ switch (pd->cmd) {
+ case SSS_PAM_AUTHENTICATE:
+ auth_data->authtok_size = pd->authtok_size;
+ auth_data->authtok = pd->authtok;
+ pam_status = pam_authenticate(pamh, 0);
+ if ((pam_status == PAM_SUCCESS) &&
+ (req->be_ctx->domain->cache_credentials)) {
+ cache_auth_data = true;
+ }
+ break;
+ case SSS_PAM_SETCRED:
+ pam_status=pam_setcred(pamh, 0);
+ break;
+ case SSS_PAM_ACCT_MGMT:
+ pam_status=pam_acct_mgmt(pamh, 0);
+ break;
+ case SSS_PAM_OPEN_SESSION:
+ pam_status=pam_open_session(pamh, 0);
+ break;
+ case SSS_PAM_CLOSE_SESSION:
+ pam_status=pam_close_session(pamh, 0);
+ break;
+ case SSS_PAM_CHAUTHTOK:
+ if (pd->priv != 1) {
+ auth_data->authtok_size = pd->authtok_size;
+ auth_data->authtok = pd->authtok;
+ pam_status = pam_authenticate(pamh, 0);
+ if (pam_status != PAM_SUCCESS) break;
+ }
+ auth_data->authtok_size = pd->newauthtok_size;
+ auth_data->authtok = pd->newauthtok;
+ pam_status = pam_chauthtok(pamh, 0);
+ if ((pam_status == PAM_SUCCESS) &&
+ (req->be_ctx->domain->cache_credentials)) {
+ cache_auth_data = true;
+ }
+ break;
+ case SSS_PAM_CHAUTHTOK_PRELIM:
+ if (pd->priv != 1) {
+ auth_data->authtok_size = pd->authtok_size;
+ auth_data->authtok = pd->authtok;
+ pam_status = pam_authenticate(pamh, 0);
+ } else {
+ pam_status = PAM_SUCCESS;
+ }
+ break;
+ default:
+ DEBUG(1, ("unknown PAM call"));
+ pam_status=PAM_ABORT;
+ }
+
+ DEBUG(4, ("Pam result: [%d][%s]\n", pam_status,
+ pam_strerror(pamh, pam_status)));
+
+ if (pam_status == PAM_AUTHINFO_UNAVAIL) {
+ be_mark_offline(req->be_ctx);
+ }
+
+ ret = pam_end(pamh, pam_status);
+ if (ret != PAM_SUCCESS) {
+ pamh=NULL;
+ DEBUG(1, ("Cannot terminate pam transaction.\n"));
+ }
+
+ } else {
+ DEBUG(1, ("Failed to initialize pam transaction.\n"));
+ pam_status = PAM_SYSTEM_ERR;
+ }
+
+ pd->pam_status = pam_status;
+
+ if (cache_auth_data) {
+ struct tevent_req *subreq;
+ char *password;
+
+ password = talloc_size(req, auth_data->authtok_size + 1);
+ if (!password) {
+ /* password caching failures are not fatal errors */
+ return proxy_reply(req, DP_ERR_OK, EOK, NULL);
+ }
+ memcpy(password, auth_data->authtok, auth_data->authtok_size);
+ password[auth_data->authtok_size] = '\0';
+ talloc_set_destructor((TALLOC_CTX *)password, password_destructor);
+
+ subreq = sysdb_cache_password_send(req, req->be_ctx->ev,
+ req->be_ctx->sysdb, NULL,
+ req->be_ctx->domain,
+ pd->user, password);
+ if (!subreq) {
+ /* password caching failures are not fatal errors */
+ return proxy_reply(req, DP_ERR_OK, EOK, NULL);
+ }
+ tevent_req_set_callback(subreq, proxy_pam_handler_cache_done, req);
+ }
+
+ proxy_reply(req, DP_ERR_OK, EOK, NULL);
+}
+
+static void proxy_pam_handler_cache_done(struct tevent_req *subreq)
+{
+ struct be_req *req = tevent_req_callback_data(subreq, struct be_req);
+ int ret;
+
+ /* password caching failures are not fatal errors */
+ ret = sysdb_cache_password_recv(subreq);
+ talloc_zfree(subreq);
+
+ /* so we just log it any return */
+ if (ret) {
+ DEBUG(2, ("Failed to cache password (%d)[%s]!?\n",
+ ret, strerror(ret)));
+ }
+
+ return proxy_reply(req, DP_ERR_OK, EOK, NULL);
+}
+
+static void proxy_reply(struct be_req *req, int dp_err,
+ int error, const char *errstr)
+{
+ return req->fn(req, dp_err, error, errstr);
+}
+
+/* =Common-proxy-tevent_req-utils=========================================*/
+
+#define DEFAULT_BUFSIZE 4096
+#define MAX_BUF_SIZE 1024*1024 /* max 1MiB */
+
+struct proxy_state {
+ struct tevent_context *ev;
+ struct proxy_ctx *ctx;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *domain;
+ const char *name;
+
+ struct sysdb_handle *handle;
+ struct passwd *pwd;
+ struct group *grp;
+ uid_t uid;
+ gid_t gid;
+};
+
+static void proxy_default_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ int ret;
+
+ ret = sysdb_transaction_commit_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int proxy_default_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+
+/* =Getpwnam-wrapper======================================================*/
+
+static void get_pw_name_process(struct tevent_req *subreq);
+static void get_pw_name_remove_done(struct tevent_req *subreq);
+static void get_pw_name_add_done(struct tevent_req *subreq);
+
+static struct tevent_req *get_pw_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct proxy_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ struct tevent_req *req, *subreq;
+ struct proxy_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct proxy_state);
+ if (!req) return NULL;
+
+ memset(state, 0, sizeof(struct proxy_state));
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sysdb = sysdb;
+ state->domain = domain;
+ state->name = name;
+
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, get_pw_name_process, req);
+
+ return req;
+}
+
+static void get_pw_name_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ struct proxy_ctx *ctx = state->ctx;
+ struct sss_domain_info *dom = ctx->be->domain;
+ enum nss_status status;
+ char *buffer;
+ size_t buflen;
+ bool delete_user = false;
+ int ret;
+
+ DEBUG(7, ("Searching user by name (%s)\n", state->name));
+
+ ret = sysdb_transaction_recv(subreq, state, &state->handle);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ talloc_zfree(subreq);
+
+ state->pwd = talloc(state, struct passwd);
+ if (!state->pwd) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(state, buflen);
+ if (!buffer) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ /* FIXME: should we move this call outside the transaction to keep the
+ * transaction as short as possible ? */
+ status = ctx->ops.getpwnam_r(state->name, state->pwd,
+ buffer, buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_NOTFOUND:
+
+ DEBUG(7, ("User %s not found.\n", state->name));
+ delete_user = true;
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("User %s found: (%s, %d, %d)\n",
+ state->name, state->pwd->pw_name,
+ state->pwd->pw_uid, state->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(state->pwd->pw_uid, dom->id_min, dom->id_max) ||
+ OUT_OF_ID_RANGE(state->pwd->pw_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
+ state->name));
+ delete_user = true;
+ break;
+ }
+
+ subreq = sysdb_store_user_send(state, state->ev, state->handle,
+ state->domain,
+ state->pwd->pw_name,
+ state->pwd->pw_passwd,
+ state->pwd->pw_uid,
+ state->pwd->pw_gid,
+ state->pwd->pw_gecos,
+ state->pwd->pw_dir,
+ state->pwd->pw_shell,
+ NULL, ctx->entry_cache_timeout);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_pw_name_add_done, req);
+ return;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ tevent_req_error(req, ENXIO);
+ return;
+
+ default:
+ DEBUG(2, ("proxy -> getpwnam_r failed for '%s' <%d>\n",
+ state->name, status));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ if (delete_user) {
+ struct ldb_dn *dn;
+
+ DEBUG(7, ("User %s does not exist (or is invalid) on remote server,"
+ " deleting!\n", state->name));
+
+ dn = sysdb_user_dn(state->sysdb, state,
+ state->domain->name, state->name);
+ if (!dn) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ subreq = sysdb_delete_entry_send(state, state->ev, state->handle, dn, true);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_pw_name_remove_done, req);
+ }
+}
+
+static void get_pw_name_add_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ int ret;
+
+ ret = sysdb_store_user_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, proxy_default_done, req);
+}
+
+static void get_pw_name_remove_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ int ret;
+
+ ret = sysdb_delete_entry_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, proxy_default_done, req);
+}
+
+/* =Getpwuid-wrapper======================================================*/
+
+static void get_pw_uid_process(struct tevent_req *subreq);
+static void get_pw_uid_remove_done(struct tevent_req *subreq);
+
+static struct tevent_req *get_pw_uid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct proxy_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ uid_t uid)
+{
+ struct tevent_req *req, *subreq;
+ struct proxy_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct proxy_state);
+ if (!req) return NULL;
+
+ memset(state, 0, sizeof(struct proxy_state));
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sysdb = sysdb;
+ state->domain = domain;
+ state->uid = uid;
+
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, get_pw_uid_process, req);
+
+ return req;
+}
+
+static void get_pw_uid_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ struct proxy_ctx *ctx = state->ctx;
+ struct sss_domain_info *dom = ctx->be->domain;
+ enum nss_status status;
+ char *buffer;
+ size_t buflen;
+ bool delete_user = false;
+ int ret;
+
+ DEBUG(7, ("Searching user by uid (%d)\n", state->uid));
+
+ ret = sysdb_transaction_recv(subreq, state, &state->handle);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ talloc_zfree(subreq);
+
+ state->pwd = talloc(state, struct passwd);
+ if (!state->pwd) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(state, buflen);
+ if (!buffer) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ /* always zero out the pwd structure */
+ memset(state->pwd, 0, sizeof(struct passwd));
+
+ status = ctx->ops.getpwuid_r(state->uid, state->pwd,
+ buffer, buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_NOTFOUND:
+
+ DEBUG(7, ("User %d not found.\n", state->uid));
+ delete_user = true;
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("User %d found (%s, %d, %d)\n",
+ state->uid, state->pwd->pw_name,
+ state->pwd->pw_uid, state->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(state->pwd->pw_uid, dom->id_min, dom->id_max) ||
+ OUT_OF_ID_RANGE(state->pwd->pw_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
+ state->name));
+ delete_user = true;
+ break;
+ }
+
+ subreq = sysdb_store_user_send(state, state->ev, state->handle,
+ state->domain,
+ state->pwd->pw_name,
+ state->pwd->pw_passwd,
+ state->pwd->pw_uid,
+ state->pwd->pw_gid,
+ state->pwd->pw_gecos,
+ state->pwd->pw_dir,
+ state->pwd->pw_shell,
+ NULL, ctx->entry_cache_timeout);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_pw_name_add_done, req);
+ return;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ tevent_req_error(req, ENXIO);
+ return;
+
+ default:
+ DEBUG(2, ("proxy -> getpwnam_r failed for '%s' <%d>\n",
+ state->name, status));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ if (delete_user) {
+ DEBUG(7, ("User %d does not exist (or is invalid) on remote server,"
+ " deleting!\n", state->uid));
+
+ subreq = sysdb_delete_user_send(state, state->ev,
+ NULL, state->handle,
+ state->domain,
+ NULL, state->uid);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_pw_uid_remove_done, req);
+ }
+}
+
+static void get_pw_uid_remove_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ int ret;
+
+ ret = sysdb_delete_user_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret && ret != ENOENT) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, proxy_default_done, req);
+}
+
+/* =Getpwent-wrapper======================================================*/
+
+struct enum_users_state {
+ struct tevent_context *ev;
+ struct proxy_ctx *ctx;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *domain;
+ struct sysdb_handle *handle;
+
+ struct passwd *pwd;
+
+ size_t buflen;
+ char *buffer;
+
+ bool in_transaction;
+};
+
+static void enum_users_process(struct tevent_req *subreq);
+
+static struct tevent_req *enum_users_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct proxy_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain)
+{
+ struct tevent_req *req, *subreq;
+ struct enum_users_state *state;
+ enum nss_status status;
+
+ DEBUG(7, ("Enumerating users\n"));
+
+ req = tevent_req_create(mem_ctx, &state, struct enum_users_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sysdb = sysdb;
+ state->domain = domain;
+ state->handle = NULL;
+
+ state->pwd = talloc(state, struct passwd);
+ if (!state->pwd) {
+ tevent_req_error(req, ENOMEM);
+ goto fail;
+ }
+
+ state->buflen = DEFAULT_BUFSIZE;
+ state->buffer = talloc_size(state, state->buflen);
+ if (!state->buffer) {
+ tevent_req_error(req, ENOMEM);
+ goto fail;
+ }
+
+ state->in_transaction = false;
+
+ status = ctx->ops.setpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ tevent_req_error(req, EIO);
+ goto fail;
+ }
+
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, enum_users_process, req);
+
+ return req;
+
+fail:
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void enum_users_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct enum_users_state *state = tevent_req_data(req,
+ struct enum_users_state);
+ struct proxy_ctx *ctx = state->ctx;
+ struct sss_domain_info *dom = ctx->be->domain;
+ enum nss_status status;
+ char *newbuf;
+ int ret;
+
+ if (!state->in_transaction) {
+ ret = sysdb_transaction_recv(subreq, state, &state->handle);
+ if (ret) {
+ goto fail;
+ }
+ talloc_zfree(subreq);
+
+ state->in_transaction = true;
+ } else {
+ ret = sysdb_store_user_recv(subreq);
+ if (ret) {
+ /* Do not fail completely on errors.
+ * Just report the failure to save and go on */
+ DEBUG(2, ("Failed to store user. Ignoring.\n"));
+ }
+ talloc_zfree(subreq);
+ }
+
+again:
+ /* always zero out the pwd structure */
+ memset(state->pwd, 0, sizeof(struct passwd));
+
+ /* get entry */
+ status = ctx->ops.getpwent_r(state->pwd,
+ state->buffer, state->buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_TRYAGAIN:
+ /* buffer too small ? */
+ if (state->buflen < MAX_BUF_SIZE) {
+ state->buflen *= 2;
+ }
+ if (state->buflen > MAX_BUF_SIZE) {
+ state->buflen = MAX_BUF_SIZE;
+ }
+ newbuf = talloc_realloc_size(state, state->buffer, state->buflen);
+ if (!newbuf) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ state->buffer = newbuf;
+ goto again;
+
+ case NSS_STATUS_NOTFOUND:
+
+ /* we are done here */
+ DEBUG(7, ("Enumeration completed.\n"));
+
+ ctx->ops.endpwent();
+ subreq = sysdb_transaction_commit_send(state, state->ev,
+ state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, proxy_default_done, req);
+ return;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("User found (%s, %d, %d)\n", state->pwd->pw_name,
+ state->pwd->pw_uid, state->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(state->pwd->pw_uid, dom->id_min, dom->id_max) ||
+ OUT_OF_ID_RANGE(state->pwd->pw_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
+ state->pwd->pw_name));
+
+ goto again; /* skip */
+ }
+
+ subreq = sysdb_store_user_send(state, state->ev, state->handle,
+ state->domain,
+ state->pwd->pw_name,
+ state->pwd->pw_passwd,
+ state->pwd->pw_uid,
+ state->pwd->pw_gid,
+ state->pwd->pw_gecos,
+ state->pwd->pw_dir,
+ state->pwd->pw_shell,
+ NULL, ctx->entry_cache_timeout);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, enum_users_process, req);
+ return;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ ret = ENXIO;
+ goto fail;
+
+ default:
+ DEBUG(2, ("proxy -> getpwent_r failed (%d)[%s]\n",
+ ret, strerror(ret)));
+ goto fail;
+ }
+
+fail:
+ ctx->ops.endpwent();
+ tevent_req_error(req, ret);
+}
+
+/* =Getgrnam-wrapper======================================================*/
+
+#define DEBUG_GR_MEM(level, state) \
+ do { \
+ if (debug_level >= level) { \
+ if (!state->grp->gr_mem || !state->grp->gr_mem[0]) { \
+ DEBUG(level, ("Group %s has no members!\n", \
+ state->grp->gr_name)); \
+ } else { \
+ int i = 0; \
+ while (state->grp->gr_mem[i]) { \
+ /* count */ \
+ i++; \
+ } \
+ DEBUG(level, ("Group %s has %d members!\n", \
+ state->grp->gr_name, i)); \
+ } \
+ } \
+ } while(0)
+
+static void get_gr_name_process(struct tevent_req *subreq);
+static void get_gr_name_remove_done(struct tevent_req *subreq);
+static void get_gr_name_add_done(struct tevent_req *subreq);
+
+static struct tevent_req *get_gr_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct proxy_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ struct tevent_req *req, *subreq;
+ struct proxy_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct proxy_state);
+ if (!req) return NULL;
+
+ memset(state, 0, sizeof(struct proxy_state));
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sysdb = sysdb;
+ state->domain = domain;
+ state->name = name;
+
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, get_gr_name_process, req);
+
+ return req;
+}
+
+static void get_gr_name_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ struct proxy_ctx *ctx = state->ctx;
+ struct sss_domain_info *dom = ctx->be->domain;
+ 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", state->name));
+
+ ret = sysdb_transaction_recv(subreq, state, &state->handle);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ talloc_zfree(subreq);
+
+ state->grp = talloc(state, struct group);
+ if (!state->grp) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(state, buflen);
+ if (!buffer) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ /* 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(state->grp, 0, sizeof(struct group));
+
+ status = ctx->ops.getgrnam_r(state->name, state->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(state, buffer, buflen);
+ if (!newbuf) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ buffer = newbuf;
+ goto again;
+
+ case NSS_STATUS_NOTFOUND:
+
+ DEBUG(7, ("Group %s not found.\n", state->name));
+ delete_group = true;
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("Group %s found: (%s, %d)\n", state->name,
+ state->grp->gr_name, state->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(state->grp->gr_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
+ state->name));
+ delete_group = true;
+ break;
+ }
+
+ DEBUG_GR_MEM(7, state);
+
+ if (state->grp->gr_mem && state->grp->gr_mem[0]) {
+ members = sysdb_new_attrs(state);
+ if (!members) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ ret = sysdb_attrs_users_from_str_list(members, SYSDB_MEMBER,
+ state->domain->name,
+ (const char **)state->grp->gr_mem);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ } else {
+ members = NULL;
+ }
+
+ subreq = sysdb_store_group_send(state, state->ev, state->handle,
+ state->domain,
+ state->grp->gr_name,
+ state->grp->gr_gid,
+ members,
+ ctx->entry_cache_timeout);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_gr_name_add_done, req);
+ return;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ tevent_req_error(req, ENXIO);
+ return;
+
+ default:
+ DEBUG(2, ("proxy -> getgrnam_r failed for '%s' <%d>\n",
+ state->name, status));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ if (delete_group) {
+ struct ldb_dn *dn;
+
+ DEBUG(7, ("Group %s does not exist (or is invalid) on remote server,"
+ " deleting!\n", state->name));
+
+ dn = sysdb_group_dn(state->sysdb, state,
+ state->domain->name, state->name);
+ if (!dn) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ subreq = sysdb_delete_entry_send(state, state->ev, state->handle, dn, true);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_gr_name_remove_done, req);
+ }
+}
+
+static void get_gr_name_add_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ int ret;
+
+ ret = sysdb_store_group_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, proxy_default_done, req);
+}
+
+static void get_gr_name_remove_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ int ret;
+
+ ret = sysdb_delete_entry_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, proxy_default_done, req);
+}
+
+/* =Getgrgid-wrapper======================================================*/
+
+static void get_gr_gid_process(struct tevent_req *subreq);
+static void get_gr_gid_remove_done(struct tevent_req *subreq);
+
+static struct tevent_req *get_gr_gid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct proxy_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ gid_t gid)
+{
+ struct tevent_req *req, *subreq;
+ struct proxy_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct proxy_state);
+ if (!req) return NULL;
+
+ memset(state, 0, sizeof(struct proxy_state));
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sysdb = sysdb;
+ state->domain = domain;
+ state->gid = gid;
+
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, get_gr_gid_process, req);
+
+ return req;
+}
+
+static void get_gr_gid_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ struct proxy_ctx *ctx = state->ctx;
+ struct sss_domain_info *dom = ctx->be->domain;
+ 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", state->gid));
+
+ ret = sysdb_transaction_recv(subreq, state, &state->handle);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ talloc_zfree(subreq);
+
+ state->grp = talloc(state, struct group);
+ if (!state->grp) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(state, buflen);
+ if (!buffer) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+again:
+ /* always zero out the group structure */
+ memset(state->grp, 0, sizeof(struct group));
+
+ status = ctx->ops.getgrgid_r(state->gid, state->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(state, buffer, buflen);
+ if (!newbuf) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ buffer = newbuf;
+ goto again;
+
+ case NSS_STATUS_NOTFOUND:
+
+ DEBUG(7, ("Group %d not found.\n", state->gid));
+ delete_group = true;
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("Group %d found (%s, %d)\n", state->gid,
+ state->grp->gr_name, state->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(state->grp->gr_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
+ state->grp->gr_name));
+ delete_group = true;
+ break;
+ }
+
+ DEBUG_GR_MEM(7, state);
+
+ if (state->grp->gr_mem && state->grp->gr_mem[0]) {
+ members = sysdb_new_attrs(state);
+ if (!members) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ ret = sysdb_attrs_users_from_str_list(members, SYSDB_MEMBER,
+ state->domain->name,
+ (const char **)state->grp->gr_mem);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ } else {
+ members = NULL;
+ }
+
+ subreq = sysdb_store_group_send(state, state->ev, state->handle,
+ state->domain,
+ state->grp->gr_name,
+ state->grp->gr_gid,
+ members,
+ ctx->entry_cache_timeout);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_gr_name_add_done, req);
+ return;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ tevent_req_error(req, ENXIO);
+ return;
+
+ default:
+ DEBUG(2, ("proxy -> getgrgid_r failed for '%d' <%d>\n",
+ state->gid, status));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ if (delete_group) {
+
+ DEBUG(7, ("Group %d does not exist (or is invalid) on remote server,"
+ " deleting!\n", state->gid));
+
+ subreq = sysdb_delete_group_send(state, state->ev,
+ NULL, state->handle,
+ state->domain,
+ NULL, state->gid);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_gr_gid_remove_done, req);
+ }
+}
+
+static void get_gr_gid_remove_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ int ret;
+
+ ret = sysdb_delete_group_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret && ret != ENOENT) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, proxy_default_done, req);
+}
+
+/* =Getgrent-wrapper======================================================*/
+
+struct enum_groups_state {
+ struct tevent_context *ev;
+ struct proxy_ctx *ctx;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *domain;
+ struct sysdb_handle *handle;
+
+ struct group *grp;
+
+ size_t buflen;
+ char *buffer;
+
+ bool in_transaction;
+};
+
+static void enum_groups_process(struct tevent_req *subreq);
+
+static struct tevent_req *enum_groups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct proxy_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain)
+{
+ struct tevent_req *req, *subreq;
+ struct enum_groups_state *state;
+ enum nss_status status;
+
+ DEBUG(7, ("Enumerating groups\n"));
+
+ req = tevent_req_create(mem_ctx, &state, struct enum_groups_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sysdb = sysdb;
+ state->domain = domain;
+ state->handle = NULL;
+
+ state->grp = talloc(state, struct group);
+ if (!state->grp) {
+ tevent_req_error(req, ENOMEM);
+ goto fail;
+ }
+
+ state->buflen = DEFAULT_BUFSIZE;
+ state->buffer = talloc_size(state, state->buflen);
+ if (!state->buffer) {
+ tevent_req_error(req, ENOMEM);
+ goto fail;
+ }
+
+ state->in_transaction = false;
+
+ status = ctx->ops.setgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ tevent_req_error(req, EIO);
+ goto fail;
+ }
+
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, enum_groups_process, req);
+
+ return req;
+
+fail:
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void enum_groups_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct enum_groups_state *state = tevent_req_data(req,
+ struct enum_groups_state);
+ struct proxy_ctx *ctx = state->ctx;
+ struct sss_domain_info *dom = ctx->be->domain;
+ enum nss_status status;
+ struct sysdb_attrs *members;
+ char *newbuf;
+ int ret;
+
+ if (!state->in_transaction) {
+ ret = sysdb_transaction_recv(subreq, state, &state->handle);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ talloc_zfree(subreq);
+
+ state->in_transaction = true;
+ } else {
+ ret = sysdb_store_group_recv(subreq);
+ 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"));
+ }
+ talloc_zfree(subreq);
+ }
+
+again:
+ /* always zero out the grp structure */
+ memset(state->grp, 0, sizeof(struct group));
+
+ /* get entry */
+ status = ctx->ops.getgrent_r(state->grp,
+ state->buffer, state->buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_TRYAGAIN:
+ /* buffer too small ? */
+ if (state->buflen < MAX_BUF_SIZE) {
+ state->buflen *= 2;
+ }
+ if (state->buflen > MAX_BUF_SIZE) {
+ state->buflen = MAX_BUF_SIZE;
+ }
+ newbuf = talloc_realloc_size(state, state->buffer, state->buflen);
+ if (!newbuf) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ state->buffer = newbuf;
+ goto again;
+
+ case NSS_STATUS_NOTFOUND:
+
+ /* we are done here */
+ DEBUG(7, ("Enumeration completed.\n"));
+
+ ctx->ops.endgrent();
+ subreq = sysdb_transaction_commit_send(state, state->ev,
+ state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, proxy_default_done, req);
+ return;
+
+ case NSS_STATUS_SUCCESS:
+
+ DEBUG(7, ("Group found (%s, %d)\n",
+ state->grp->gr_name, state->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(state->grp->gr_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
+ state->grp->gr_name));
+
+ goto again; /* skip */
+ }
+
+ DEBUG_GR_MEM(7, state);
+
+ if (state->grp->gr_mem && state->grp->gr_mem[0]) {
+ members = sysdb_new_attrs(state);
+ if (!members) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ ret = sysdb_attrs_users_from_str_list(members, SYSDB_MEMBER,
+ state->domain->name,
+ (const char **)state->grp->gr_mem);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ } else {
+ members = NULL;
+ }
+
+ subreq = sysdb_store_group_send(state, state->ev, state->handle,
+ state->domain,
+ state->grp->gr_name,
+ state->grp->gr_gid,
+ members,
+ ctx->entry_cache_timeout);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, enum_groups_process, req);
+ return;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ ret = ENXIO;
+ goto fail;
+
+ default:
+ DEBUG(2, ("proxy -> getgrent_r failed (%d)[%s]\n",
+ ret, strerror(ret)));
+ goto fail;
+ }
+
+fail:
+ ctx->ops.endgrent();
+ tevent_req_error(req, ret);
+}
+
+
+/* =Initgroups-wrapper====================================================*/
+
+static void get_initgr_process(struct tevent_req *subreq);
+static void get_initgr_groups_process(struct tevent_req *subreq);
+static void get_initgr_groups_done(struct tevent_req *subreq);
+static struct tevent_req *get_groups_by_gid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sysdb_handle *handle,
+ struct proxy_ctx *ctx,
+ struct sss_domain_info *domain,
+ gid_t *gids, int num_gids);
+static int get_groups_by_gid_recv(struct tevent_req *req);
+static void get_groups_by_gid_process(struct tevent_req *subreq);
+static struct tevent_req *get_group_from_gid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sysdb_handle *handle,
+ struct proxy_ctx *ctx,
+ struct sss_domain_info *domain,
+ gid_t gid);
+static int get_group_from_gid_recv(struct tevent_req *req);
+static void get_group_from_gid_send_del_done(struct tevent_req *subreq);
+static void get_group_from_gid_send_add_done(struct tevent_req *subreq);
+
+
+static struct tevent_req *get_initgr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct proxy_ctx *ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ struct tevent_req *req, *subreq;
+ struct proxy_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct proxy_state);
+ if (!req) return NULL;
+
+ memset(state, 0, sizeof(struct proxy_state));
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sysdb = sysdb;
+ state->domain = domain;
+ state->name = name;
+
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, get_initgr_process, req);
+
+ return req;
+}
+
+static void get_initgr_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ struct proxy_ctx *ctx = state->ctx;
+ struct sss_domain_info *dom = ctx->be->domain;
+ enum nss_status status;
+ char *buffer;
+ size_t buflen;
+ bool delete_user = false;
+ int ret;
+
+ ret = sysdb_transaction_recv(subreq, state, &state->handle);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ talloc_zfree(subreq);
+
+ state->pwd = talloc(state, struct passwd);
+ if (!state->pwd) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(state, buflen);
+ if (!buffer) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ /* FIXME: should we move this call outside the transaction to keep the
+ * transaction as short as possible ? */
+ status = ctx->ops.getpwnam_r(state->name, state->pwd,
+ buffer, buflen, &ret);
+
+ switch (status) {
+ case NSS_STATUS_NOTFOUND:
+
+ delete_user = true;
+ 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(state->pwd->pw_uid, dom->id_min, dom->id_max) ||
+ OUT_OF_ID_RANGE(state->pwd->pw_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
+ state->name));
+ delete_user = true;
+ break;
+ }
+
+ subreq = sysdb_store_user_send(state, state->ev, state->handle,
+ state->domain,
+ state->pwd->pw_name,
+ state->pwd->pw_passwd,
+ state->pwd->pw_uid,
+ state->pwd->pw_gid,
+ state->pwd->pw_gecos,
+ state->pwd->pw_dir,
+ state->pwd->pw_shell,
+ NULL, ctx->entry_cache_timeout);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_initgr_groups_process, req);
+ return;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ tevent_req_error(req, ENXIO);
+ return;
+
+ default:
+ DEBUG(2, ("proxy -> getpwnam_r failed for '%s' <%d>\n",
+ state->name, status));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ if (delete_user) {
+ struct ldb_dn *dn;
+
+ dn = sysdb_user_dn(state->sysdb, state,
+ state->domain->name, state->name);
+ if (!dn) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ subreq = sysdb_delete_entry_send(state, state->ev, state->handle, dn, true);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_pw_name_remove_done, req);
+ }
+}
+
+static void get_initgr_groups_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ struct proxy_ctx *ctx = state->ctx;
+ enum nss_status status;
+ long int limit;
+ long int size;
+ long int num;
+ long int num_gids;
+ gid_t *gids;
+ int ret;
+
+ ret = sysdb_store_user_recv(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ talloc_zfree(subreq);
+
+ num_gids = 0;
+ limit = 4096;
+ num = 4096;
+ size = num*sizeof(gid_t);
+ gids = talloc_size(state, size);
+ if (!gids) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ state->gid = state->pwd->pw_gid;
+
+again:
+ /* FIXME: should we move this call outside the transaction to keep the
+ * transaction as short as possible ? */
+ status = ctx->ops.initgroups_dyn(state->name, state->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(state, gids, size);
+ if (!gids) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ goto again; /* retry with more memory */
+
+ case NSS_STATUS_SUCCESS:
+ DEBUG(4, ("User [%s] appears to be member of %lu groups\n",
+ state->name, num_gids));
+
+ subreq = get_groups_by_gid_send(state, state->ev, state->handle,
+ state->ctx, state->domain,
+ gids, num_gids);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_initgr_groups_done, req);
+ break;
+
+ default:
+ DEBUG(2, ("proxy -> initgroups_dyn failed (%d)[%s]\n",
+ ret, strerror(ret)));
+ tevent_req_error(req, EIO);
+ return;
+ }
+}
+
+static void get_initgr_groups_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct proxy_state *state = tevent_req_data(req,
+ struct proxy_state);
+ int ret;
+
+ ret = get_groups_by_gid_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, proxy_default_done, req);
+}
+
+struct get_groups_state {
+ struct tevent_context *ev;
+ struct sysdb_handle *handle;
+ struct proxy_ctx *ctx;
+ struct sss_domain_info *domain;
+
+ gid_t *gids;
+ int num_gids;
+ int cur_gid;
+};
+
+static struct tevent_req *get_groups_by_gid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sysdb_handle *handle,
+ struct proxy_ctx *ctx,
+ struct sss_domain_info *domain,
+ gid_t *gids, int num_gids)
+{
+ struct tevent_req *req, *subreq;
+ struct get_groups_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct get_groups_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->handle = handle;
+ state->ctx = ctx;
+ state->domain = domain;
+ state->gids = gids;
+ state->num_gids = num_gids;
+ state->cur_gid = 0;
+
+ subreq = get_group_from_gid_send(state, ev, handle, ctx, domain, gids[0]);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, get_groups_by_gid_process, req);
+
+ return req;
+}
+
+static void get_groups_by_gid_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct get_groups_state *state = tevent_req_data(req,
+ struct get_groups_state);
+ int ret;
+
+ ret = get_group_from_gid_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->cur_gid++;
+ if (state->cur_gid >= state->num_gids) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = get_group_from_gid_send(state,
+ state->ev, state->handle,
+ state->ctx, state->domain,
+ state->gids[state->cur_gid]);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, get_groups_by_gid_process, req);
+}
+
+static int get_groups_by_gid_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static struct tevent_req *get_group_from_gid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sysdb_handle *handle,
+ struct proxy_ctx *ctx,
+ struct sss_domain_info *domain,
+ gid_t gid)
+{
+ struct tevent_req *req, *subreq;
+ struct proxy_state *state;
+ struct sss_domain_info *dom = ctx->be->domain;
+ enum nss_status status;
+ char *buffer;
+ char *newbuf;
+ size_t buflen;
+ bool delete_group = false;
+ struct sysdb_attrs *members;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct proxy_state);
+ if (!req) return NULL;
+
+ memset(state, 0, sizeof(struct proxy_state));
+
+ state->ev = ev;
+ state->handle = handle;
+ state->ctx = ctx;
+ state->domain = domain;
+ state->gid = gid;
+
+ state->grp = talloc(state, struct group);
+ if (!state->grp) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = talloc_size(state, buflen);
+ if (!buffer) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+again:
+ /* always zero out the grp structure */
+ memset(state->grp, 0, sizeof(struct group));
+
+ status = ctx->ops.getgrgid_r(state->gid, state->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(state, buffer, buflen);
+ if (!newbuf) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ buffer = newbuf;
+ goto again;
+
+ case NSS_STATUS_NOTFOUND:
+
+ delete_group = true;
+ break;
+
+ case NSS_STATUS_SUCCESS:
+
+ /* gid=0 is an invalid value */
+ /* also check that the id is in the valid range for this domain */
+ if (OUT_OF_ID_RANGE(state->grp->gr_gid, dom->id_min, dom->id_max)) {
+
+ DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
+ state->grp->gr_name));
+ delete_group = true;
+ break;
+ }
+
+ if (state->grp->gr_mem && state->grp->gr_mem[0]) {
+ members = sysdb_new_attrs(state);
+ if (!members) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ ret = sysdb_attrs_users_from_str_list(members, SYSDB_MEMBER,
+ state->domain->name,
+ (const char **)state->grp->gr_mem);
+ if (ret) {
+ goto fail;
+ }
+ } else {
+ members = NULL;
+ }
+
+ subreq = sysdb_store_group_send(state, state->ev, state->handle,
+ state->domain,
+ state->grp->gr_name,
+ state->grp->gr_gid,
+ members,
+ ctx->entry_cache_timeout);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, get_group_from_gid_send_add_done, req);
+ break;
+
+ case NSS_STATUS_UNAVAIL:
+ /* "remote" backend unavailable. Enter offline mode */
+ ret = ENXIO;
+ goto fail;
+
+ default:
+ DEBUG(2, ("proxy -> getgrgid_r failed for '%d' <%d>\n",
+ state->gid, status));
+ ret = EIO;
+ goto fail;
+ }
+
+ if (delete_group) {
+ subreq = sysdb_delete_group_send(state, state->ev,
+ NULL, state->handle,
+ state->domain,
+ NULL, state->gid);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, get_group_from_gid_send_del_done, req);
+ }
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void get_group_from_gid_send_add_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ int ret;
+
+ ret = sysdb_store_group_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static void get_group_from_gid_send_del_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ int ret;
+
+ ret = sysdb_delete_entry_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret && ret != ENOENT) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int get_group_from_gid_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+
+/* =Proxy_Id-Functions====================================================*/
+
+static void proxy_get_account_info_done(struct tevent_req *subreq);
+
+/* TODO: See if we can use async_req code */
+static void proxy_get_account_info(struct be_req *breq)
+{
+ struct tevent_req *subreq;
+ struct be_acct_req *ar;
+ struct proxy_ctx *ctx;
+ struct tevent_context *ev;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *domain;
+ uid_t uid;
+ gid_t gid;
+
+ 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_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, '*')) {
+ subreq = enum_users_send(breq, ev, ctx,
+ sysdb, domain);
+ if (!subreq) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ ENOMEM, "Out of memory");
+ }
+ tevent_req_set_callback(subreq,
+ proxy_get_account_info_done, breq);
+ return;
+ } else {
+ subreq = get_pw_name_send(breq, ev, ctx,
+ sysdb, domain,
+ ar->filter_value);
+ if (!subreq) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ ENOMEM, "Out of memory");
+ }
+ tevent_req_set_callback(subreq,
+ proxy_get_account_info_done, breq);
+ return;
+ }
+ 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");
+ }
+ subreq = get_pw_uid_send(breq, ev, ctx,
+ sysdb, domain, uid);
+ if (!subreq) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ ENOMEM, "Out of memory");
+ }
+ tevent_req_set_callback(subreq,
+ proxy_get_account_info_done, breq);
+ return;
+ }
+ 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, '*')) {
+ subreq = enum_groups_send(breq, ev, ctx,
+ sysdb, domain);
+ if (!subreq) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ ENOMEM, "Out of memory");
+ }
+ tevent_req_set_callback(subreq,
+ proxy_get_account_info_done, breq);
+ return;
+ } else {
+ subreq = get_gr_name_send(breq, ev, ctx,
+ sysdb, domain,
+ ar->filter_value);
+ if (!subreq) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ ENOMEM, "Out of memory");
+ }
+ tevent_req_set_callback(subreq,
+ proxy_get_account_info_done, breq);
+ return;
+ }
+ 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");
+ }
+ subreq = get_gr_gid_send(breq, ev, ctx,
+ sysdb, domain, gid);
+ if (!subreq) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ ENOMEM, "Out of memory");
+ }
+ tevent_req_set_callback(subreq,
+ proxy_get_account_info_done, breq);
+ return;
+ }
+ 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");
+ }
+ subreq = get_initgr_send(breq, ev, ctx, sysdb,
+ domain, ar->filter_value);
+ if (!subreq) {
+ return proxy_reply(breq, DP_ERR_FATAL,
+ ENOMEM, "Out of memory");
+ }
+ tevent_req_set_callback(subreq,
+ proxy_get_account_info_done, breq);
+ return;
+
+ default: /*fail*/
+ break;
+ }
+
+ return proxy_reply(breq, DP_ERR_FATAL,
+ EINVAL, "Invalid request type");
+}
+
+static void proxy_get_account_info_done(struct tevent_req *subreq)
+{
+ struct be_req *breq = tevent_req_callback_data(subreq,
+ struct be_req);
+ int ret;
+ ret = proxy_default_recv(subreq);
+ talloc_zfree(subreq);
+ 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);
+}
+
+static void proxy_shutdown(struct be_req *req)
+{
+ /* TODO: Clean up any internal data */
+ req->fn(req, DP_ERR_OK, EOK, NULL);
+}
+
+static void proxy_auth_shutdown(struct be_req *req)
+{
+ talloc_free(req->be_ctx->bet_info[BET_AUTH].pvt_bet_data);
+ req->fn(req, DP_ERR_OK, EOK, NULL);
+}
+
+struct bet_ops proxy_id_ops = {
+ .handler = proxy_get_account_info,
+ .finalize = proxy_shutdown
+};
+
+struct bet_ops proxy_auth_ops = {
+ .handler = proxy_pam_handler,
+ .finalize = proxy_auth_shutdown
+};
+
+struct bet_ops proxy_access_ops = {
+ .handler = proxy_pam_handler,
+ .finalize = proxy_auth_shutdown
+};
+
+struct bet_ops proxy_chpass_ops = {
+ .handler = proxy_pam_handler,
+ .finalize = proxy_auth_shutdown
+};
+
+static void *proxy_dlsym(void *handle, const char *functemp, char *libname)
+{
+ char *funcname;
+ void *funcptr;
+
+ funcname = talloc_asprintf(NULL, functemp, libname);
+ if (funcname == NULL) return NULL;
+
+ funcptr = dlsym(handle, funcname);
+ talloc_free(funcname);
+
+ return funcptr;
+}
+
+int sssm_proxy_init(struct be_ctx *bectx,
+ struct bet_ops **ops, void **pvt_data)
+{
+ struct proxy_ctx *ctx;
+ char *libname;
+ char *libpath;
+ void *handle;
+ int ret;
+
+ ctx = talloc_zero(bectx, struct proxy_ctx);
+ if (!ctx) {
+ return ENOMEM;
+ }
+ ctx->be = bectx;
+
+ ret = confdb_get_int(bectx->cdb, ctx, bectx->conf_path,
+ CONFDB_DOMAIN_ENTRY_CACHE_TIMEOUT, 600,
+ &ctx->entry_cache_timeout);
+ if (ret != EOK) goto done;
+
+ ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path,
+ CONFDB_PROXY_LIBNAME, NULL, &libname);
+ if (ret != EOK) goto done;
+ if (libname == NULL) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ libpath = talloc_asprintf(ctx, "libnss_%s.so.2", libname);
+ if (!libpath) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ handle = dlopen(libpath, RTLD_NOW);
+ if (!handle) {
+ DEBUG(0, ("Unable to load %s module with path, error: %s\n",
+ libpath, dlerror()));
+ ret = ELIBACC;
+ goto done;
+ }
+
+ ctx->ops.getpwnam_r = proxy_dlsym(handle, "_nss_%s_getpwnam_r", libname);
+ if (!ctx->ops.getpwnam_r) {
+ DEBUG(0, ("Failed to load NSS fns, error: %s\n", dlerror()));
+ ret = ELIBBAD;
+ goto done;
+ }
+
+ ctx->ops.getpwuid_r = proxy_dlsym(handle, "_nss_%s_getpwuid_r", libname);
+ if (!ctx->ops.getpwuid_r) {
+ DEBUG(0, ("Failed to load NSS fns, error: %s\n", dlerror()));
+ ret = ELIBBAD;
+ goto done;
+ }
+
+ ctx->ops.setpwent = proxy_dlsym(handle, "_nss_%s_setpwent", libname);
+ if (!ctx->ops.setpwent) {
+ DEBUG(0, ("Failed to load NSS fns, error: %s\n", dlerror()));
+ ret = ELIBBAD;
+ goto done;
+ }
+
+ ctx->ops.getpwent_r = proxy_dlsym(handle, "_nss_%s_getpwent_r", libname);
+ if (!ctx->ops.getpwent_r) {
+ DEBUG(0, ("Failed to load NSS fns, error: %s\n", dlerror()));
+ ret = ELIBBAD;
+ goto done;
+ }
+
+ ctx->ops.endpwent = proxy_dlsym(handle, "_nss_%s_endpwent", libname);
+ if (!ctx->ops.endpwent) {
+ DEBUG(0, ("Failed to load NSS fns, error: %s\n", dlerror()));
+ ret = ELIBBAD;
+ goto done;
+ }
+
+ ctx->ops.getgrnam_r = proxy_dlsym(handle, "_nss_%s_getgrnam_r", libname);
+ if (!ctx->ops.getgrnam_r) {
+ DEBUG(0, ("Failed to load NSS fns, error: %s\n", dlerror()));
+ ret = ELIBBAD;
+ goto done;
+ }
+
+ ctx->ops.getgrgid_r = proxy_dlsym(handle, "_nss_%s_getgrgid_r", libname);
+ if (!ctx->ops.getgrgid_r) {
+ DEBUG(0, ("Failed to load NSS fns, error: %s\n", dlerror()));
+ ret = ELIBBAD;
+ goto done;
+ }
+
+ ctx->ops.setgrent = proxy_dlsym(handle, "_nss_%s_setgrent", libname);
+ if (!ctx->ops.setgrent) {
+ DEBUG(0, ("Failed to load NSS fns, error: %s\n", dlerror()));
+ ret = ELIBBAD;
+ goto done;
+ }
+
+ ctx->ops.getgrent_r = proxy_dlsym(handle, "_nss_%s_getgrent_r", libname);
+ if (!ctx->ops.getgrent_r) {
+ DEBUG(0, ("Failed to load NSS fns, error: %s\n", dlerror()));
+ ret = ELIBBAD;
+ goto done;
+ }
+
+ ctx->ops.endgrent = proxy_dlsym(handle, "_nss_%s_endgrent", libname);
+ if (!ctx->ops.endgrent) {
+ DEBUG(0, ("Failed to load NSS fns, error: %s\n", dlerror()));
+ ret = ELIBBAD;
+ goto done;
+ }
+
+ ctx->ops.initgroups_dyn = proxy_dlsym(handle, "_nss_%s_initgroups_dyn",
+ libname);
+ if (!ctx->ops.initgroups_dyn) {
+ DEBUG(1, ("The '%s' library does not provides the "
+ "_nss_XXX_initgroups_dyn function!\n"
+ "initgroups will be slow as it will require "
+ "full groups enumeration!\n", libname));
+ }
+
+ *ops = &proxy_id_ops;
+ *pvt_data = ctx;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(ctx);
+ }
+ return ret;
+}
+
+int sssm_proxy_auth_init(struct be_ctx *bectx,
+ struct bet_ops **ops, void **pvt_data)
+{
+ struct proxy_auth_ctx *ctx;
+ int ret;
+
+ ctx = talloc(bectx, struct proxy_auth_ctx);
+ if (!ctx) {
+ return ENOMEM;
+ }
+ ctx->be = bectx;
+
+ ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path,
+ CONFDB_PROXY_PAM_TARGET, NULL,
+ &ctx->pam_target);
+ if (ret != EOK) goto done;
+ if (!ctx->pam_target) {
+ DEBUG(1, ("Missing option proxy_pam_target.\n"));
+ ret = EINVAL;
+ goto done;
+ }
+
+ *ops = &proxy_auth_ops;
+ *pvt_data = ctx;
+
+done:
+ if (ret != EOK) {
+ talloc_free(ctx);
+ }
+ return ret;
+}
+
+int sssm_proxy_access_init(struct be_ctx *bectx,
+ struct bet_ops **ops, void **pvt_data)
+{
+ int ret;
+ ret = sssm_proxy_auth_init(bectx, ops, pvt_data);
+ *ops = &proxy_access_ops;
+ return ret;
+}
+
+int sssm_proxy_chpass_init(struct be_ctx *bectx,
+ struct bet_ops **ops, void **pvt_data)
+{
+ int ret;
+ ret = sssm_proxy_auth_init(bectx, ops, pvt_data);
+ *ops = &proxy_chpass_ops;
+ return ret;
+}