aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/nscd/query.c
diff options
context:
space:
mode:
authorHajimu UMEMOTO <ume@FreeBSD.org>2006-04-28 12:03:38 +0000
committerHajimu UMEMOTO <ume@FreeBSD.org>2006-04-28 12:03:38 +0000
commit06a99fe36f0aac93e7689da6b3f07b727750691f (patch)
treed796503361cc28eb3b9eaa593876abd826a2cf81 /usr.sbin/nscd/query.c
parent18486a5ee3fa9ccdf2a2d2b48581bc0aa614fb80 (diff)
downloadsrc-06a99fe36f0aac93e7689da6b3f07b727750691f.tar.gz
src-06a99fe36f0aac93e7689da6b3f07b727750691f.zip
- Extend the nsswitch to support Services, Protocols and Rpc
databases. - Make nsswitch support caching. Submitted by: Michael Bushkov <bushman__at__rsu.ru> Sponsored by: Google Summer of Code 2005
Notes
Notes: svn path=/head/; revision=158115
Diffstat (limited to 'usr.sbin/nscd/query.c')
-rw-r--r--usr.sbin/nscd/query.c1278
1 files changed, 1278 insertions, 0 deletions
diff --git a/usr.sbin/nscd/query.c b/usr.sbin/nscd/query.c
new file mode 100644
index 000000000000..28882c3d01df
--- /dev/null
+++ b/usr.sbin/nscd/query.c
@@ -0,0 +1,1278 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <assert.h>
+#include <errno.h>
+#include <nsswitch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "query.h"
+#include "log.h"
+#include "mp_ws_query.h"
+#include "mp_rs_query.h"
+#include "singletons.h"
+
+static const char negative_data[1] = { 0 };
+
+extern void get_time_func(struct timeval *);
+
+static void clear_config_entry(struct configuration_entry *);
+static void clear_config_entry_part(struct configuration_entry *,
+ const char *, size_t);
+
+static int on_query_startup(struct query_state *);
+static void on_query_destroy(struct query_state *);
+
+static int on_read_request_read1(struct query_state *);
+static int on_read_request_read2(struct query_state *);
+static int on_read_request_process(struct query_state *);
+static int on_read_response_write1(struct query_state *);
+static int on_read_response_write2(struct query_state *);
+
+static int on_rw_mapper(struct query_state *);
+
+static int on_transform_request_read1(struct query_state *);
+static int on_transform_request_read2(struct query_state *);
+static int on_transform_request_process(struct query_state *);
+static int on_transform_response_write1(struct query_state *);
+
+static int on_write_request_read1(struct query_state *);
+static int on_write_request_read2(struct query_state *);
+static int on_negative_write_request_process(struct query_state *);
+static int on_write_request_process(struct query_state *);
+static int on_write_response_write1(struct query_state *);
+
+/*
+ * Clears the specified configuration entry (clears the cache for positive and
+ * and negative entries) and also for all multipart entries.
+ */
+static void
+clear_config_entry(struct configuration_entry *config_entry)
+{
+ size_t i;
+
+ TRACE_IN(clear_config_entry);
+ configuration_lock_entry(config_entry, CELT_POSITIVE);
+ if (config_entry->positive_cache_entry != NULL)
+ transform_cache_entry(
+ config_entry->positive_cache_entry,
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(config_entry, CELT_NEGATIVE);
+ if (config_entry->negative_cache_entry != NULL)
+ transform_cache_entry(
+ config_entry->negative_cache_entry,
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_NEGATIVE);
+
+ configuration_lock_entry(config_entry, CELT_MULTIPART);
+ for (i = 0; i < config_entry->mp_cache_entries_size; ++i)
+ transform_cache_entry(
+ config_entry->mp_cache_entries[i],
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_MULTIPART);
+
+ TRACE_OUT(clear_config_entry);
+}
+
+/*
+ * Clears the specified configuration entry by deleting only the elements,
+ * that are owned by the user with specified eid_str.
+ */
+static void
+clear_config_entry_part(struct configuration_entry *config_entry,
+ const char *eid_str, size_t eid_str_length)
+{
+ cache_entry *start, *finish, *mp_entry;
+ TRACE_IN(clear_config_entry_part);
+ configuration_lock_entry(config_entry, CELT_POSITIVE);
+ if (config_entry->positive_cache_entry != NULL)
+ transform_cache_entry_part(
+ config_entry->positive_cache_entry,
+ CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT);
+ configuration_unlock_entry(config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(config_entry, CELT_NEGATIVE);
+ if (config_entry->negative_cache_entry != NULL)
+ transform_cache_entry_part(
+ config_entry->negative_cache_entry,
+ CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT);
+ configuration_unlock_entry(config_entry, CELT_NEGATIVE);
+
+ configuration_lock_entry(config_entry, CELT_MULTIPART);
+ if (configuration_entry_find_mp_cache_entries(config_entry,
+ eid_str, &start, &finish) == 0) {
+ for (mp_entry = start; mp_entry != finish; ++mp_entry)
+ transform_cache_entry(*mp_entry, CTT_CLEAR);
+ }
+ configuration_unlock_entry(config_entry, CELT_MULTIPART);
+
+ TRACE_OUT(clear_config_entry_part);
+}
+
+/*
+ * This function is assigned to the query_state structue on its creation.
+ * It's main purpose is to receive credentials from the client.
+ */
+static int
+on_query_startup(struct query_state *qstate)
+{
+ struct msghdr cred_hdr;
+ struct iovec iov;
+ int elem_type;
+
+ struct {
+ struct cmsghdr hdr;
+ struct cmsgcred creds;
+ } cmsg;
+
+ TRACE_IN(on_query_startup);
+ assert(qstate != NULL);
+
+ memset(&cred_hdr, 0, sizeof(struct msghdr));
+ cred_hdr.msg_iov = &iov;
+ cred_hdr.msg_iovlen = 1;
+ cred_hdr.msg_control = &cmsg;
+ cred_hdr.msg_controllen = sizeof(cmsg);
+
+ memset(&iov, 0, sizeof(struct iovec));
+ iov.iov_base = &elem_type;
+ iov.iov_len = sizeof(int);
+
+ if (recvmsg(qstate->sockfd, &cred_hdr, 0) == -1) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ if (cmsg.hdr.cmsg_len != sizeof cmsg
+ || cmsg.hdr.cmsg_level != SOL_SOCKET
+ || cmsg.hdr.cmsg_type != SCM_CREDS) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ qstate->uid = cmsg.creds.cmcred_uid;
+ qstate->gid = cmsg.creds.cmcred_gid;
+
+#if defined(NS_CACHED_EID_CHECKING) || defined(NS_STRICT_CACHED_EID_CHECKING)
+/*
+ * This check is probably a bit redundant - per-user cache is always separated
+ * by the euid/egid pair
+ */
+ if (check_query_eids(qstate) != 0) {
+#ifdef NS_STRICT_CACHED_EID_CHECKING
+ TRACE_OUT(on_query_startup);
+ return (-1);
+#else
+ if ((elem_type != CET_READ_REQUEST) &&
+ (elem_type != CET_MP_READ_SESSION_REQUEST) &&
+ (elem_type != CET_WRITE_REQUEST) &&
+ (elem_type != CET_MP_WRITE_SESSION_REQUEST)) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+#endif
+ }
+#endif
+
+ switch (elem_type) {
+ case CET_WRITE_REQUEST:
+ qstate->process_func = on_write_request_read1;
+ break;
+ case CET_READ_REQUEST:
+ qstate->process_func = on_read_request_read1;
+ break;
+ case CET_TRANSFORM_REQUEST:
+ qstate->process_func = on_transform_request_read1;
+ break;
+ case CET_MP_WRITE_SESSION_REQUEST:
+ qstate->process_func = on_mp_write_session_request_read1;
+ break;
+ case CET_MP_READ_SESSION_REQUEST:
+ qstate->process_func = on_mp_read_session_request_read1;
+ break;
+ default:
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ TRACE_OUT(on_query_startup);
+ return (0);
+}
+
+/*
+ * on_rw_mapper is used to process multiple read/write requests during
+ * one connection session. It's never called in the beginning (on query_state
+ * creation) as it does not process the multipart requests and does not
+ * receive credentials
+ */
+static int
+on_rw_mapper(struct query_state *qstate)
+{
+ ssize_t result;
+ int elem_type;
+
+ TRACE_IN(on_rw_mapper);
+ if (qstate->kevent_watermark == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ } else {
+ result = qstate->read_func(qstate, &elem_type, sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_rw_mapper);
+ return (-1);
+ }
+
+ switch (elem_type) {
+ case CET_WRITE_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func = on_write_request_read1;
+ break;
+ case CET_READ_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func = on_read_request_read1;
+ break;
+ default:
+ TRACE_OUT(on_rw_mapper);
+ return (-1);
+ break;
+ }
+ }
+ TRACE_OUT(on_rw_mapper);
+ return (0);
+}
+
+/*
+ * The default query_destroy function
+ */
+static void
+on_query_destroy(struct query_state *qstate)
+{
+
+ TRACE_IN(on_query_destroy);
+ finalize_comm_element(&qstate->response);
+ finalize_comm_element(&qstate->request);
+ TRACE_OUT(on_query_destroy);
+}
+
+/*
+ * The functions below are used to process write requests.
+ * - on_write_request_read1 and on_write_request_read2 read the request itself
+ * - on_write_request_process processes it (if the client requests to
+ * cache the negative result, the on_negative_write_request_process is used)
+ * - on_write_response_write1 sends the response
+ */
+static int
+on_write_request_read1(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_write_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) * 3;
+ else {
+ init_comm_element(&qstate->request, CET_WRITE_REQUEST);
+ write_request = get_cache_write_request(&qstate->request);
+
+ result = qstate->read_func(qstate, &write_request->entry_length,
+ sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &write_request->cache_key_size, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &write_request->data_size, sizeof(size_t));
+
+ if (result != sizeof(size_t) * 3) {
+ TRACE_OUT(on_write_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(write_request->entry_length) ||
+ BUFSIZE_INVALID(write_request->cache_key_size) ||
+ (BUFSIZE_INVALID(write_request->data_size) &&
+ (write_request->data_size != 0))) {
+ TRACE_OUT(on_write_request_read1);
+ return (-1);
+ }
+
+ write_request->entry = (char *)malloc(
+ write_request->entry_length + 1);
+ assert(write_request->entry != NULL);
+ memset(write_request->entry, 0,
+ write_request->entry_length + 1);
+
+ write_request->cache_key = (char *)malloc(
+ write_request->cache_key_size +
+ qstate->eid_str_length);
+ assert(write_request->cache_key != NULL);
+ memcpy(write_request->cache_key, qstate->eid_str,
+ qstate->eid_str_length);
+ memset(write_request->cache_key + qstate->eid_str_length, 0,
+ write_request->cache_key_size);
+
+ if (write_request->data_size != 0) {
+ write_request->data = (char *)malloc(
+ write_request->data_size);
+ assert(write_request->data != NULL);
+ memset(write_request->data, 0,
+ write_request->data_size);
+ }
+
+ qstate->kevent_watermark = write_request->entry_length +
+ write_request->cache_key_size +
+ write_request->data_size;
+ qstate->process_func = on_write_request_read2;
+ }
+
+ TRACE_OUT(on_write_request_read1);
+ return (0);
+}
+
+static int
+on_write_request_read2(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_write_request_read2);
+ write_request = get_cache_write_request(&qstate->request);
+
+ result = qstate->read_func(qstate, write_request->entry,
+ write_request->entry_length);
+ result += qstate->read_func(qstate, write_request->cache_key +
+ qstate->eid_str_length, write_request->cache_key_size);
+ if (write_request->data_size != 0)
+ result += qstate->read_func(qstate, write_request->data,
+ write_request->data_size);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_write_request_read2);
+ return (-1);
+ }
+ write_request->cache_key_size += qstate->eid_str_length;
+
+ qstate->kevent_watermark = 0;
+ if (write_request->data_size != 0)
+ qstate->process_func = on_write_request_process;
+ else
+ qstate->process_func = on_negative_write_request_process;
+ TRACE_OUT(on_write_request_read2);
+ return (0);
+}
+
+static int
+on_write_request_process(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ struct cache_write_response *write_response;
+ cache_entry c_entry;
+
+ TRACE_IN(on_write_request_process);
+ init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
+ write_response = get_cache_write_response(&qstate->response);
+ write_request = get_cache_write_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, write_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ write_response->error_code = ENOENT;
+
+ LOG_ERR_2("write_request", "can't find configuration"
+ " entry '%s'. aborting request", write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ write_response->error_code = EACCES;
+
+ LOG_ERR_2("write_request",
+ "configuration entry '%s' is disabled",
+ write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ write_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("write_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", write_request->entry);
+ goto fin;
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->positive_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if (c_entry != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
+ qstate->config_entry->positive_cache_entry = c_entry;
+ write_response->error_code = cache_write(c_entry,
+ write_request->cache_key,
+ write_request->cache_key_size,
+ write_request->data,
+ write_request->data_size);
+ configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+
+ } else
+ write_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_write_response_write1;
+
+ TRACE_OUT(on_write_request_process);
+ return (0);
+}
+
+static int
+on_negative_write_request_process(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ struct cache_write_response *write_response;
+ cache_entry c_entry;
+
+ TRACE_IN(on_negative_write_request_process);
+ init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
+ write_response = get_cache_write_response(&qstate->response);
+ write_request = get_cache_write_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry (
+ s_configuration, write_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ write_response->error_code = ENOENT;
+
+ LOG_ERR_2("negative_write_request",
+ "can't find configuration"
+ " entry '%s'. aborting request", write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ write_response->error_code = EACCES;
+
+ LOG_ERR_2("negative_write_request",
+ "configuration entry '%s' is disabled",
+ write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ write_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("negative_write_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", write_request->entry);
+ goto fin;
+ } else {
+#ifdef NS_CACHED_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ write_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->negative_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if (c_entry != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
+ qstate->config_entry->negative_cache_entry = c_entry;
+ write_response->error_code = cache_write(c_entry,
+ write_request->cache_key,
+ write_request->cache_key_size,
+ negative_data,
+ sizeof(negative_data));
+ configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+ } else
+ write_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_write_response_write1;
+
+ TRACE_OUT(on_negative_write_request_process);
+ return (0);
+}
+
+static int
+on_write_response_write1(struct query_state *qstate)
+{
+ struct cache_write_response *write_response;
+ ssize_t result;
+
+ TRACE_IN(on_write_response_write1);
+ write_response = get_cache_write_response(&qstate->response);
+ result = qstate->write_func(qstate, &write_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_write_response_write1);
+ return (-1);
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_READ;
+ qstate->process_func = on_rw_mapper;
+
+ TRACE_OUT(on_write_response_write1);
+ return (0);
+}
+
+/*
+ * The functions below are used to process read requests.
+ * - on_read_request_read1 and on_read_request_read2 read the request itself
+ * - on_read_request_process processes it
+ * - on_read_response_write1 and on_read_response_write2 send the response
+ */
+static int
+on_read_request_read1(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ ssize_t result;
+
+ TRACE_IN(on_read_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) * 2;
+ else {
+ init_comm_element(&qstate->request, CET_READ_REQUEST);
+ read_request = get_cache_read_request(&qstate->request);
+
+ result = qstate->read_func(qstate,
+ &read_request->entry_length, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &read_request->cache_key_size, sizeof(size_t));
+
+ if (result != sizeof(size_t) * 2) {
+ TRACE_OUT(on_read_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(read_request->entry_length) ||
+ BUFSIZE_INVALID(read_request->cache_key_size)) {
+ TRACE_OUT(on_read_request_read1);
+ return (-1);
+ }
+
+ read_request->entry = (char *)malloc(
+ read_request->entry_length + 1);
+ assert(read_request->entry != NULL);
+ memset(read_request->entry, 0, read_request->entry_length + 1);
+
+ read_request->cache_key = (char *)malloc(
+ read_request->cache_key_size +
+ qstate->eid_str_length);
+ assert(read_request->cache_key != NULL);
+ memcpy(read_request->cache_key, qstate->eid_str,
+ qstate->eid_str_length);
+ memset(read_request->cache_key + qstate->eid_str_length, 0,
+ read_request->cache_key_size);
+
+ qstate->kevent_watermark = read_request->entry_length +
+ read_request->cache_key_size;
+ qstate->process_func = on_read_request_read2;
+ }
+
+ TRACE_OUT(on_read_request_read1);
+ return (0);
+}
+
+static int
+on_read_request_read2(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ ssize_t result;
+
+ TRACE_IN(on_read_request_read2);
+ read_request = get_cache_read_request(&qstate->request);
+
+ result = qstate->read_func(qstate, read_request->entry,
+ read_request->entry_length);
+ result += qstate->read_func(qstate,
+ read_request->cache_key + qstate->eid_str_length,
+ read_request->cache_key_size);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_request_read2);
+ return (-1);
+ }
+ read_request->cache_key_size += qstate->eid_str_length;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_read_request_process;
+
+ TRACE_OUT(on_read_request_read2);
+ return (0);
+}
+
+static int
+on_read_request_process(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ struct cache_read_response *read_response;
+ cache_entry c_entry, neg_c_entry;
+
+ struct agent *lookup_agent;
+ struct common_agent *c_agent;
+ int res;
+
+ TRACE_IN(on_read_request_process);
+ init_comm_element(&qstate->response, CET_READ_RESPONSE);
+ read_response = get_cache_read_response(&qstate->response);
+ read_request = get_cache_read_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, read_request->entry);
+ if (qstate->config_entry == NULL) {
+ read_response->error_code = ENOENT;
+
+ LOG_ERR_2("read_request",
+ "can't find configuration "
+ "entry '%s'. aborting request", read_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ read_response->error_code = EACCES;
+
+ LOG_ERR_2("read_request",
+ "configuration entry '%s' is disabled",
+ read_request->entry);
+ goto fin;
+ }
+
+ /*
+ * if we perform lookups by ourselves, then we don't need to separate
+ * cache entries by euid and egid
+ */
+ if (qstate->config_entry->perform_actual_lookups != 0)
+ memset(read_request->cache_key, 0, qstate->eid_str_length);
+ else {
+#ifdef NS_CACHED_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ /* if the lookup is not self-performing, we check for clients euid/egid */
+ read_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->positive_cache_params.entry_name);
+ neg_c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->negative_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if ((c_entry != NULL) && (neg_c_entry != NULL)) {
+ configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
+ qstate->config_entry->positive_cache_entry = c_entry;
+ read_response->error_code = cache_read(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size, NULL,
+ &read_response->data_size);
+
+ if (read_response->error_code == -2) {
+ read_response->data = (char *)malloc(
+ read_response->data_size);
+ assert(read_response != NULL);
+ read_response->error_code = cache_read(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ read_response->data,
+ &read_response->data_size);
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
+ qstate->config_entry->negative_cache_entry = neg_c_entry;
+ if (read_response->error_code == -1) {
+ read_response->error_code = cache_read(neg_c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size, NULL,
+ &read_response->data_size);
+
+ if (read_response->error_code == -2) {
+ read_response->error_code = 0;
+ read_response->data = NULL;
+ read_response->data_size = 0;
+ }
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
+
+ if ((read_response->error_code == -1) &&
+ (qstate->config_entry->perform_actual_lookups != 0)) {
+ free(read_response->data);
+ read_response->data = NULL;
+ read_response->data_size = 0;
+
+ lookup_agent = find_agent(s_agent_table,
+ read_request->entry, COMMON_AGENT);
+
+ if ((lookup_agent != NULL) &&
+ (lookup_agent->type == COMMON_AGENT)) {
+ c_agent = (struct common_agent *)lookup_agent;
+ res = c_agent->lookup_func(
+ read_request->cache_key +
+ qstate->eid_str_length,
+ read_request->cache_key_size -
+ qstate->eid_str_length,
+ &read_response->data,
+ &read_response->data_size);
+
+ if (res == NS_SUCCESS) {
+ read_response->error_code = 0;
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_POSITIVE);
+ cache_write(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ read_response->data,
+ read_response->data_size);
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_POSITIVE);
+ } else if ((res == NS_NOTFOUND) ||
+ (res == NS_RETURN)) {
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_NEGATIVE);
+ cache_write(neg_c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ negative_data,
+ sizeof(negative_data));
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_NEGATIVE);
+
+ read_response->error_code = 0;
+ read_response->data = NULL;
+ read_response->data_size = 0;
+ }
+ }
+ }
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+ } else
+ read_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ if (read_response->error_code == 0)
+ qstate->kevent_watermark = sizeof(int) + sizeof(size_t);
+ else
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_read_response_write1;
+
+ TRACE_OUT(on_read_request_process);
+ return (0);
+}
+
+static int
+on_read_response_write1(struct query_state *qstate)
+{
+ struct cache_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_read_response_write1);
+ read_response = get_cache_read_response(&qstate->response);
+
+ result = qstate->write_func(qstate, &read_response->error_code,
+ sizeof(int));
+
+ if (read_response->error_code == 0) {
+ result += qstate->write_func(qstate, &read_response->data_size,
+ sizeof(size_t));
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write1);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = read_response->data_size;
+ qstate->process_func = on_read_response_write2;
+ } else {
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write1);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+
+ TRACE_OUT(on_read_response_write1);
+ return (0);
+}
+
+static int
+on_read_response_write2(struct query_state *qstate)
+{
+ struct cache_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_read_response_write2);
+ read_response = get_cache_read_response(&qstate->response);
+ if (read_response->data_size > 0) {
+ result = qstate->write_func(qstate, read_response->data,
+ read_response->data_size);
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write2);
+ return (-1);
+ }
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_READ;
+ qstate->process_func = on_rw_mapper;
+ TRACE_OUT(on_read_response_write2);
+ return (0);
+}
+
+/*
+ * The functions below are used to process write requests.
+ * - on_transform_request_read1 and on_transform_request_read2 read the
+ * request itself
+ * - on_transform_request_process processes it
+ * - on_transform_response_write1 sends the response
+ */
+static int
+on_transform_request_read1(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ ssize_t result;
+
+ TRACE_IN(on_transform_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) + sizeof(int);
+ else {
+ init_comm_element(&qstate->request, CET_TRANSFORM_REQUEST);
+ transform_request =
+ get_cache_transform_request(&qstate->request);
+
+ result = qstate->read_func(qstate,
+ &transform_request->entry_length, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &transform_request->transformation_type, sizeof(int));
+
+ if (result != sizeof(size_t) + sizeof(int)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ if ((transform_request->transformation_type != TT_USER) &&
+ (transform_request->transformation_type != TT_ALL)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ if (transform_request->entry_length != 0) {
+ if (BUFSIZE_INVALID(transform_request->entry_length)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ transform_request->entry = (char *)malloc(
+ transform_request->entry_length + 1);
+ assert(transform_request->entry != NULL);
+ memset(transform_request->entry, 0,
+ transform_request->entry_length + 1);
+
+ qstate->process_func = on_transform_request_read2;
+ } else
+ qstate->process_func = on_transform_request_process;
+
+ qstate->kevent_watermark = transform_request->entry_length;
+ }
+
+ TRACE_OUT(on_transform_request_read1);
+ return (0);
+}
+
+static int
+on_transform_request_read2(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ ssize_t result;
+
+ TRACE_IN(on_transform_request_read2);
+ transform_request = get_cache_transform_request(&qstate->request);
+
+ result = qstate->read_func(qstate, transform_request->entry,
+ transform_request->entry_length);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_transform_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_transform_request_process;
+
+ TRACE_OUT(on_transform_request_read2);
+ return (0);
+}
+
+static int
+on_transform_request_process(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ struct cache_transform_response *transform_response;
+ struct configuration_entry *config_entry;
+ size_t i, size;
+
+ TRACE_IN(on_transform_request_process);
+ init_comm_element(&qstate->response, CET_TRANSFORM_RESPONSE);
+ transform_response = get_cache_transform_response(&qstate->response);
+ transform_request = get_cache_transform_request(&qstate->request);
+
+ switch (transform_request->transformation_type) {
+ case TT_USER:
+ if (transform_request->entry == NULL) {
+ size = configuration_get_entries_size(s_configuration);
+ for (i = 0; i < size; ++i) {
+ config_entry = configuration_get_entry(
+ s_configuration, i);
+
+ if (config_entry->perform_actual_lookups == 0)
+ clear_config_entry_part(config_entry,
+ qstate->eid_str, qstate->eid_str_length);
+ }
+ } else {
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, transform_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ LOG_ERR_2("transform_request",
+ "can't find configuration"
+ " entry '%s'. aborting request",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ LOG_ERR_2("transform_request",
+ "can't transform the cache entry %s"
+ ", because it ised for actual lookups",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ clear_config_entry_part(qstate->config_entry,
+ qstate->eid_str, qstate->eid_str_length);
+ }
+ break;
+ case TT_ALL:
+ if (qstate->euid != 0)
+ transform_response->error_code = -1;
+ else {
+ if (transform_request->entry == NULL) {
+ size = configuration_get_entries_size(
+ s_configuration);
+ for (i = 0; i < size; ++i) {
+ clear_config_entry(
+ configuration_get_entry(
+ s_configuration, i));
+ }
+ } else {
+ qstate->config_entry = configuration_find_entry(
+ s_configuration,
+ transform_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ LOG_ERR_2("transform_request",
+ "can't find configuration"
+ " entry '%s'. aborting request",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ clear_config_entry(qstate->config_entry);
+ }
+ }
+ break;
+ default:
+ transform_response->error_code = -1;
+ }
+
+fin:
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_transform_response_write1;
+ TRACE_OUT(on_transform_request_process);
+ return (0);
+}
+
+static int
+on_transform_response_write1(struct query_state *qstate)
+{
+ struct cache_transform_response *transform_response;
+ ssize_t result;
+
+ TRACE_IN(on_transform_response_write1);
+ transform_response = get_cache_transform_response(&qstate->response);
+ result = qstate->write_func(qstate, &transform_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_transform_response_write1);
+ return (-1);
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_transform_response_write1);
+ return (0);
+}
+
+/*
+ * Checks if the client's euid and egid do not differ from its uid and gid.
+ * Returns 0 on success.
+ */
+int
+check_query_eids(struct query_state *qstate)
+{
+
+ return ((qstate->uid != qstate->euid) || (qstate->gid != qstate->egid) ? -1 : 0);
+}
+
+/*
+ * Uses the qstate fields to process an "alternate" read - when the buffer is
+ * too large to be received during one socket read operation
+ */
+ssize_t
+query_io_buffer_read(struct query_state *qstate, void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_io_buffer_read);
+ if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
+ return (-1);
+
+ if (nbytes < qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p)
+ result = nbytes;
+ else
+ result = qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p;
+
+ memcpy(buf, qstate->io_buffer_p, result);
+ qstate->io_buffer_p += result;
+
+ if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) {
+ free(qstate->io_buffer);
+ qstate->io_buffer = NULL;
+
+ qstate->write_func = query_socket_write;
+ qstate->read_func = query_socket_read;
+ }
+
+ TRACE_OUT(query_io_buffer_read);
+ return (result);
+}
+
+/*
+ * Uses the qstate fields to process an "alternate" write - when the buffer is
+ * too large to be sent during one socket write operation
+ */
+ssize_t
+query_io_buffer_write(struct query_state *qstate, const void *buf,
+ size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_io_buffer_write);
+ if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
+ return (-1);
+
+ if (nbytes < qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p)
+ result = nbytes;
+ else
+ result = qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p;
+
+ memcpy(qstate->io_buffer_p, buf, result);
+ qstate->io_buffer_p += result;
+
+ if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) {
+ qstate->use_alternate_io = 1;
+ qstate->io_buffer_p = qstate->io_buffer;
+
+ qstate->write_func = query_socket_write;
+ qstate->read_func = query_socket_read;
+ }
+
+ TRACE_OUT(query_io_buffer_write);
+ return (result);
+}
+
+/*
+ * The default "read" function, which reads data directly from socket
+ */
+ssize_t
+query_socket_read(struct query_state *qstate, void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_socket_read);
+ if (qstate->socket_failed != 0) {
+ TRACE_OUT(query_socket_read);
+ return (-1);
+ }
+
+ result = read(qstate->sockfd, buf, nbytes);
+ if ((result == -1) || (result < nbytes))
+ qstate->socket_failed = 1;
+
+ TRACE_OUT(query_socket_read);
+ return (result);
+}
+
+/*
+ * The default "write" function, which writes data directly to socket
+ */
+ssize_t
+query_socket_write(struct query_state *qstate, const void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_socket_write);
+ if (qstate->socket_failed != 0) {
+ TRACE_OUT(query_socket_write);
+ return (-1);
+ }
+
+ result = write(qstate->sockfd, buf, nbytes);
+ if ((result == -1) || (result < nbytes))
+ qstate->socket_failed = 1;
+
+ TRACE_OUT(query_socket_write);
+ return (result);
+}
+
+/*
+ * Initializes the query_state structure by filling it with the default values.
+ */
+struct query_state *
+init_query_state(int sockfd, size_t kevent_watermark, uid_t euid, gid_t egid)
+{
+ struct query_state *retval;
+
+ TRACE_IN(init_query_state);
+ retval = (struct query_state *)malloc(sizeof(struct query_state));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct query_state));
+
+ retval->sockfd = sockfd;
+ retval->kevent_filter = EVFILT_READ;
+ retval->kevent_watermark = kevent_watermark;
+
+ retval->euid = euid;
+ retval->egid = egid;
+ retval->uid = retval->gid = -1;
+
+ if (asprintf(&retval->eid_str, "%d_%d_", retval->euid,
+ retval->egid) == -1) {
+ free(retval);
+ return (NULL);
+ }
+ retval->eid_str_length = strlen(retval->eid_str);
+
+ init_comm_element(&retval->request, CET_UNDEFINED);
+ init_comm_element(&retval->response, CET_UNDEFINED);
+ retval->process_func = on_query_startup;
+ retval->destroy_func = on_query_destroy;
+
+ retval->write_func = query_socket_write;
+ retval->read_func = query_socket_read;
+
+ get_time_func(&retval->creation_time);
+ memcpy(&retval->timeout, &s_configuration->query_timeout,
+ sizeof(struct timeval));
+
+ TRACE_OUT(init_query_state);
+ return (retval);
+}
+
+void
+destroy_query_state(struct query_state *qstate)
+{
+
+ TRACE_IN(destroy_query_state);
+ if (qstate->eid_str != NULL)
+ free(qstate->eid_str);
+
+ if (qstate->io_buffer != NULL)
+ free(qstate->io_buffer);
+
+ qstate->destroy_func(qstate);
+ free(qstate);
+ TRACE_OUT(destroy_query_state);
+}