Index: head/lib/libc/net/nscachedcli.c =================================================================== --- head/lib/libc/net/nscachedcli.c (revision 158256) +++ head/lib/libc/net/nscachedcli.c (revision 158257) @@ -1,576 +1,576 @@ /*- * Copyright (c) 2005 Michael Bushkov * 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "nscachedcli.h" #define NS_DEFAULT_CACHED_IO_TIMEOUT 4 static int safe_write(struct cached_connection_ *, const void *, size_t); static int safe_read(struct cached_connection_ *, void *, size_t); static int send_credentials(struct cached_connection_ *, int); /* * safe_write writes data to the specified connection and tries to do it in * the very safe manner. We ensure, that we can write to the socket with * kevent. If the data_size can't be sent in one piece, then it would be * splitted. */ static int safe_write(struct cached_connection_ *connection, const void *data, size_t data_size) { struct kevent eventlist; int nevents; size_t result; ssize_t s_result; struct timespec timeout; if (data_size == 0) return (0); timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; timeout.tv_nsec = 0; result = 0; do { nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, &timeout); if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { s_result = _write(connection->sockfd, data + result, eventlist.data < data_size - result ? eventlist.data : data_size - result); if (s_result == -1) return (-1); else result += s_result; if (eventlist.flags & EV_EOF) return (result < data_size ? -1 : 0); } else return (-1); } while (result < data_size); return (0); } /* * safe_read reads data from connection and tries to do it in the very safe * and stable way. It uses kevent to ensure, that the data are availabe for * reading. If the amount of data to be read is too large, then they would * be splitted. */ static int safe_read(struct cached_connection_ *connection, void *data, size_t data_size) { struct kevent eventlist; size_t result; ssize_t s_result; struct timespec timeout; int nevents; if (data_size == 0) return (0); timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; timeout.tv_nsec = 0; result = 0; do { nevents = kevent(connection->read_queue, NULL, 0, &eventlist, 1, &timeout); if (nevents == 1 && eventlist.filter == EVFILT_READ) { s_result = _read(connection->sockfd, data + result, eventlist.data <= data_size - result ? eventlist.data : data_size - result); if (s_result == -1) return (-1); else result += s_result; if (eventlist.flags & EV_EOF) return (result < data_size ? -1 : 0); } else return (-1); } while (result < data_size); return (0); } /* * Sends the credentials information to the connection along with the * communication element type. */ static int send_credentials(struct cached_connection_ *connection, int type) { struct kevent eventlist; int nevents; ssize_t result; int res; struct msghdr cred_hdr; struct iovec iov; struct { struct cmsghdr hdr; - struct cmsgcred creds; + char cred[CMSG_SPACE(sizeof(struct cmsgcred))]; } cmsg; memset(&cmsg, 0, sizeof(cmsg)); - cmsg.hdr.cmsg_len = sizeof(cmsg); + cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_CREDS; 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); + cred_hdr.msg_control = (caddr_t)&cmsg; + cred_hdr.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred)); iov.iov_base = &type; iov.iov_len = sizeof(int); EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, NOTE_LOWAT, sizeof(int), NULL); res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, NULL); if (nevents == 1 && eventlist.filter == EVFILT_WRITE) { result = (_sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ? -1 : 0; EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); return (result); } else return (-1); } /* * Opens the connection with the specified params. Initializes all kqueues. */ struct cached_connection_ * __open_cached_connection(struct cached_connection_params const *params) { struct cached_connection_ *retval; struct kevent eventlist; struct sockaddr_un client_address; int client_address_len, client_socket; int res; assert(params != NULL); client_socket = _socket(PF_LOCAL, SOCK_STREAM, 0); client_address.sun_family = PF_LOCAL; strncpy(client_address.sun_path, params->socket_path, sizeof(client_address.sun_path)); client_address_len = sizeof(client_address.sun_family) + strlen(client_address.sun_path) + 1; res = _connect(client_socket, (struct sockaddr *)&client_address, client_address_len); if (res == -1) { _close(client_socket); return (NULL); } _fcntl(client_socket, F_SETFL, O_NONBLOCK); retval = malloc(sizeof(struct cached_connection_)); assert(retval != NULL); memset(retval, 0, sizeof(struct cached_connection_)); retval->sockfd = client_socket; retval->write_queue = kqueue(); assert(retval->write_queue != -1); EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL); retval->read_queue = kqueue(); assert(retval->read_queue != -1); EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL); res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL); return (retval); } void __close_cached_connection(struct cached_connection_ *connection) { assert(connection != NULL); _close(connection->sockfd); _close(connection->read_queue); _close(connection->write_queue); free(connection); } /* * This function is very close to the cache_write function of the caching * library, which is used in the caching daemon. It caches the data with the * specified key in the cache entry with entry_name. */ int __cached_write(struct cached_connection_ *connection, const char *entry_name, const char *key, size_t key_size, const char *data, size_t data_size) { size_t name_size; int error_code; int result; error_code = -1; result = 0; result = send_credentials(connection, CET_WRITE_REQUEST); if (result != 0) goto fin; name_size = strlen(entry_name); result = safe_write(connection, &name_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, &key_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, &data_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, entry_name, name_size); if (result != 0) goto fin; result = safe_write(connection, key, key_size); if (result != 0) goto fin; result = safe_write(connection, data, data_size); if (result != 0) goto fin; result = safe_read(connection, &error_code, sizeof(int)); if (result != 0) error_code = -1; fin: return (error_code); } /* * This function is very close to the cache_read function of the caching * library, which is used in the caching daemon. It reads cached data with the * specified key from the cache entry with entry_name. */ int __cached_read(struct cached_connection_ *connection, const char *entry_name, const char *key, size_t key_size, char *data, size_t *data_size) { size_t name_size, result_size; int error_code, rec_error_code; int result; assert(connection != NULL); result = 0; error_code = -1; result = send_credentials(connection, CET_READ_REQUEST); if (result != 0) goto fin; name_size = strlen(entry_name); result = safe_write(connection, &name_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, &key_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, entry_name, name_size); if (result != 0) goto fin; result = safe_write(connection, key, key_size); if (result != 0) goto fin; result = safe_read(connection, &rec_error_code, sizeof(int)); if (result != 0) goto fin; if (rec_error_code != 0) { error_code = rec_error_code; goto fin; } result = safe_read(connection, &result_size, sizeof(size_t)); if (result != 0) goto fin; if (result_size > *data_size) { *data_size = result_size; error_code = -2; goto fin; } result = safe_read(connection, data, result_size); if (result != 0) goto fin; *data_size = result_size; error_code = 0; fin: return (error_code); } /* * Initializes the mp_write_session. For such a session the new connection * would be opened. The data should be written to the session with * __cached_mp_write function. The __close_cached_mp_write_session function * should be used to submit session and __abandon_cached_mp_write_session - to * abandon it. When the session is submitted, the whole se */ struct cached_connection_ * __open_cached_mp_write_session(struct cached_connection_params const *params, const char *entry_name) { struct cached_connection_ *connection, *retval; size_t name_size; int error_code; int result; retval = NULL; connection = __open_cached_connection(params); if (connection == NULL) return (NULL); connection->mp_flag = 1; result = send_credentials(connection, CET_MP_WRITE_SESSION_REQUEST); if (result != 0) goto fin; name_size = strlen(entry_name); result = safe_write(connection, &name_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, entry_name, name_size); if (result != 0) goto fin; result = safe_read(connection, &error_code, sizeof(int)); if (result != 0) goto fin; if (error_code != 0) result = error_code; fin: if (result != 0) __close_cached_connection(connection); else retval = connection; return (retval); } /* * Adds new portion of data to the opened write session */ int __cached_mp_write(struct cached_connection_ *ws, const char *data, size_t data_size) { int request, result; int error_code; error_code = -1; request = CET_MP_WRITE_SESSION_WRITE_REQUEST; result = safe_write(ws, &request, sizeof(int)); if (result != 0) goto fin; result = safe_write(ws, &data_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(ws, data, data_size); if (result != 0) goto fin; result = safe_read(ws, &error_code, sizeof(int)); if (result != 0) error_code = -1; fin: return (error_code); } /* * Abandons all operations with the write session. All data, that were written * to the session before, are discarded. */ int __abandon_cached_mp_write_session(struct cached_connection_ *ws) { int notification; int result; notification = CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION; result = safe_write(ws, ¬ification, sizeof(int)); __close_cached_connection(ws); return (result); } /* * Gracefully closes the write session. The data, that were previously written * to the session, are committed. */ int __close_cached_mp_write_session(struct cached_connection_ *ws) { int notification; int result; notification = CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION; result = safe_write(ws, ¬ification, sizeof(int)); __close_cached_connection(ws); return (0); } struct cached_connection_ * __open_cached_mp_read_session(struct cached_connection_params const *params, const char *entry_name) { struct cached_connection_ *connection, *retval; size_t name_size; int error_code; int result; retval = NULL; connection = __open_cached_connection(params); if (connection == NULL) return (NULL); connection->mp_flag = 1; result = send_credentials(connection, CET_MP_READ_SESSION_REQUEST); if (result != 0) goto fin; name_size = strlen(entry_name); result = safe_write(connection, &name_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, entry_name, name_size); if (result != 0) goto fin; result = safe_read(connection, &error_code, sizeof(int)); if (result != 0) goto fin; if (error_code != 0) result = error_code; fin: if (result != 0) __close_cached_connection(connection); else retval = connection; return (retval); } int __cached_mp_read(struct cached_connection_ *rs, char *data, size_t *data_size) { size_t result_size; int error_code, rec_error_code; int request, result; error_code = -1; request = CET_MP_READ_SESSION_READ_REQUEST; result = safe_write(rs, &request, sizeof(int)); if (result != 0) goto fin; result = safe_read(rs, &rec_error_code, sizeof(int)); if (result != 0) goto fin; if (rec_error_code != 0) { error_code = rec_error_code; goto fin; } result = safe_read(rs, &result_size, sizeof(size_t)); if (result != 0) goto fin; if (result_size > *data_size) { *data_size = result_size; error_code = -2; goto fin; } result = safe_read(rs, data, result_size); if (result != 0) goto fin; *data_size = result_size; error_code = 0; fin: return (error_code); } int __close_cached_mp_read_session(struct cached_connection_ *rs) { __close_cached_connection(rs); return (0); } Index: head/usr.sbin/cached/query.c =================================================================== --- head/usr.sbin/cached/query.c (revision 158256) +++ head/usr.sbin/cached/query.c (revision 158257) @@ -1,1278 +1,1280 @@ /*- * Copyright (c) 2005 Michael Bushkov * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #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; + struct cmsgcred *cred; int elem_type; struct { struct cmsghdr hdr; - struct cmsgcred creds; + char cred[CMSG_SPACE(sizeof(struct cmsgcred))]; } 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); + cred_hdr.msg_control = (caddr_t)&cmsg; + cred_hdr.msg_controllen = CMSG_LEN(sizeof(struct cmsgcred)); 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 + if (cmsg.hdr.cmsg_len < CMSG_LEN(sizeof(struct cmsgcred)) || 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; + cred = (struct cmsgcred *)CMSG_DATA(&cmsg); + qstate->uid = cred->cmcred_uid; + qstate->gid = cred->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); } Index: head/usr.sbin/nscd/query.c =================================================================== --- head/usr.sbin/nscd/query.c (revision 158256) +++ head/usr.sbin/nscd/query.c (revision 158257) @@ -1,1278 +1,1280 @@ /*- * Copyright (c) 2005 Michael Bushkov * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #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; + struct cmsgcred *cred; int elem_type; struct { struct cmsghdr hdr; - struct cmsgcred creds; + char cred[CMSG_SPACE(sizeof(struct cmsgcred))]; } 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); + cred_hdr.msg_control = (caddr_t)&cmsg; + cred_hdr.msg_controllen = CMSG_LEN(sizeof(struct cmsgcred)); 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 + if (cmsg.hdr.cmsg_len < CMSG_LEN(sizeof(struct cmsgcred)) || 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; + cred = (struct cmsgcred *)CMSG_DATA(&cmsg); + qstate->uid = cred->cmcred_uid; + qstate->gid = cred->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); }