diff options
author | Hajimu UMEMOTO <ume@FreeBSD.org> | 2006-04-28 12:03:38 +0000 |
---|---|---|
committer | Hajimu UMEMOTO <ume@FreeBSD.org> | 2006-04-28 12:03:38 +0000 |
commit | 06a99fe36f0aac93e7689da6b3f07b727750691f (patch) | |
tree | d796503361cc28eb3b9eaa593876abd826a2cf81 /usr.sbin/nscd/query.c | |
parent | 18486a5ee3fa9ccdf2a2d2b48581bc0aa614fb80 (diff) | |
download | src-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.c | 1278 |
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); +} |