diff options
Diffstat (limited to 'server/providers/proxy.c')
-rw-r--r-- | server/providers/proxy.c | 2521 |
1 files changed, 0 insertions, 2521 deletions
diff --git a/server/providers/proxy.c b/server/providers/proxy.c deleted file mode 100644 index 12bb25ec7..000000000 --- a/server/providers/proxy.c +++ /dev/null @@ -1,2521 +0,0 @@ -/* - 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; -} |