query.c revision 158257
1/*-
2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/usr.sbin/nscd/query.c 158257 2006-05-02 23:02:32Z ume $");
30
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <sys/time.h>
34#include <sys/event.h>
35#include <assert.h>
36#include <errno.h>
37#include <nsswitch.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include "config.h"
42#include "debug.h"
43#include "query.h"
44#include "log.h"
45#include "mp_ws_query.h"
46#include "mp_rs_query.h"
47#include "singletons.h"
48
49static const char negative_data[1] = { 0 };
50
51extern	void get_time_func(struct timeval *);
52
53static 	void clear_config_entry(struct configuration_entry *);
54static 	void clear_config_entry_part(struct configuration_entry *,
55	const char *, size_t);
56
57static	int on_query_startup(struct query_state *);
58static	void on_query_destroy(struct query_state *);
59
60static	int on_read_request_read1(struct query_state *);
61static	int on_read_request_read2(struct query_state *);
62static	int on_read_request_process(struct query_state *);
63static	int on_read_response_write1(struct query_state *);
64static	int on_read_response_write2(struct query_state *);
65
66static	int on_rw_mapper(struct query_state *);
67
68static	int on_transform_request_read1(struct query_state *);
69static	int on_transform_request_read2(struct query_state *);
70static	int on_transform_request_process(struct query_state *);
71static	int on_transform_response_write1(struct query_state *);
72
73static	int on_write_request_read1(struct query_state *);
74static	int on_write_request_read2(struct query_state *);
75static	int on_negative_write_request_process(struct query_state *);
76static	int on_write_request_process(struct query_state *);
77static	int on_write_response_write1(struct query_state *);
78
79/*
80 * Clears the specified configuration entry (clears the cache for positive and
81 * and negative entries) and also for all multipart entries.
82 */
83static void
84clear_config_entry(struct configuration_entry *config_entry)
85{
86	size_t i;
87
88	TRACE_IN(clear_config_entry);
89	configuration_lock_entry(config_entry, CELT_POSITIVE);
90	if (config_entry->positive_cache_entry != NULL)
91		transform_cache_entry(
92			config_entry->positive_cache_entry,
93			CTT_CLEAR);
94	configuration_unlock_entry(config_entry, CELT_POSITIVE);
95
96	configuration_lock_entry(config_entry, CELT_NEGATIVE);
97	if (config_entry->negative_cache_entry != NULL)
98		transform_cache_entry(
99			config_entry->negative_cache_entry,
100			CTT_CLEAR);
101	configuration_unlock_entry(config_entry, CELT_NEGATIVE);
102
103	configuration_lock_entry(config_entry, CELT_MULTIPART);
104	for (i = 0; i < config_entry->mp_cache_entries_size; ++i)
105		transform_cache_entry(
106			config_entry->mp_cache_entries[i],
107			CTT_CLEAR);
108	configuration_unlock_entry(config_entry, CELT_MULTIPART);
109
110	TRACE_OUT(clear_config_entry);
111}
112
113/*
114 * Clears the specified configuration entry by deleting only the elements,
115 * that are owned by the user with specified eid_str.
116 */
117static void
118clear_config_entry_part(struct configuration_entry *config_entry,
119	const char *eid_str, size_t eid_str_length)
120{
121	cache_entry *start, *finish, *mp_entry;
122	TRACE_IN(clear_config_entry_part);
123	configuration_lock_entry(config_entry, CELT_POSITIVE);
124	if (config_entry->positive_cache_entry != NULL)
125		transform_cache_entry_part(
126			config_entry->positive_cache_entry,
127			CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT);
128	configuration_unlock_entry(config_entry, CELT_POSITIVE);
129
130	configuration_lock_entry(config_entry, CELT_NEGATIVE);
131	if (config_entry->negative_cache_entry != NULL)
132		transform_cache_entry_part(
133			config_entry->negative_cache_entry,
134			CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT);
135	configuration_unlock_entry(config_entry, CELT_NEGATIVE);
136
137	configuration_lock_entry(config_entry, CELT_MULTIPART);
138	if (configuration_entry_find_mp_cache_entries(config_entry,
139		eid_str, &start, &finish) == 0) {
140		for (mp_entry = start; mp_entry != finish; ++mp_entry)
141			transform_cache_entry(*mp_entry, CTT_CLEAR);
142	}
143	configuration_unlock_entry(config_entry, CELT_MULTIPART);
144
145	TRACE_OUT(clear_config_entry_part);
146}
147
148/*
149 * This function is assigned to the query_state structue on its creation.
150 * It's main purpose is to receive credentials from the client.
151 */
152static int
153on_query_startup(struct query_state *qstate)
154{
155	struct msghdr	cred_hdr;
156	struct iovec	iov;
157	struct cmsgcred *cred;
158	int elem_type;
159
160	struct {
161		struct cmsghdr	hdr;
162		char cred[CMSG_SPACE(sizeof(struct cmsgcred))];
163	} cmsg;
164
165	TRACE_IN(on_query_startup);
166	assert(qstate != NULL);
167
168	memset(&cred_hdr, 0, sizeof(struct msghdr));
169	cred_hdr.msg_iov = &iov;
170	cred_hdr.msg_iovlen = 1;
171	cred_hdr.msg_control = (caddr_t)&cmsg;
172	cred_hdr.msg_controllen = CMSG_LEN(sizeof(struct cmsgcred));
173
174	memset(&iov, 0, sizeof(struct iovec));
175	iov.iov_base = &elem_type;
176	iov.iov_len = sizeof(int);
177
178	if (recvmsg(qstate->sockfd, &cred_hdr, 0) == -1) {
179		TRACE_OUT(on_query_startup);
180		return (-1);
181	}
182
183	if (cmsg.hdr.cmsg_len < CMSG_LEN(sizeof(struct cmsgcred))
184		|| cmsg.hdr.cmsg_level != SOL_SOCKET
185		|| cmsg.hdr.cmsg_type != SCM_CREDS) {
186		TRACE_OUT(on_query_startup);
187		return (-1);
188	}
189
190	cred = (struct cmsgcred *)CMSG_DATA(&cmsg);
191	qstate->uid = cred->cmcred_uid;
192	qstate->gid = cred->cmcred_gid;
193
194#if defined(NS_CACHED_EID_CHECKING) || defined(NS_STRICT_CACHED_EID_CHECKING)
195/*
196 * This check is probably a bit redundant - per-user cache is always separated
197 * by the euid/egid pair
198 */
199	if (check_query_eids(qstate) != 0) {
200#ifdef NS_STRICT_CACHED_EID_CHECKING
201		TRACE_OUT(on_query_startup);
202		return (-1);
203#else
204		if ((elem_type != CET_READ_REQUEST) &&
205			(elem_type != CET_MP_READ_SESSION_REQUEST) &&
206			(elem_type != CET_WRITE_REQUEST) &&
207			(elem_type != CET_MP_WRITE_SESSION_REQUEST)) {
208			TRACE_OUT(on_query_startup);
209			return (-1);
210		}
211#endif
212	}
213#endif
214
215	switch (elem_type) {
216	case CET_WRITE_REQUEST:
217		qstate->process_func = on_write_request_read1;
218		break;
219	case CET_READ_REQUEST:
220		qstate->process_func = on_read_request_read1;
221		break;
222	case CET_TRANSFORM_REQUEST:
223		qstate->process_func = on_transform_request_read1;
224		break;
225	case CET_MP_WRITE_SESSION_REQUEST:
226		qstate->process_func = on_mp_write_session_request_read1;
227		break;
228	case CET_MP_READ_SESSION_REQUEST:
229		qstate->process_func = on_mp_read_session_request_read1;
230		break;
231	default:
232		TRACE_OUT(on_query_startup);
233		return (-1);
234	}
235
236	qstate->kevent_watermark = 0;
237	TRACE_OUT(on_query_startup);
238	return (0);
239}
240
241/*
242 * on_rw_mapper is used to process multiple read/write requests during
243 * one connection session. It's never called in the beginning (on query_state
244 * creation) as it does not process the multipart requests and does not
245 * receive credentials
246 */
247static int
248on_rw_mapper(struct query_state *qstate)
249{
250	ssize_t	result;
251	int	elem_type;
252
253	TRACE_IN(on_rw_mapper);
254	if (qstate->kevent_watermark == 0) {
255		qstate->kevent_watermark = sizeof(int);
256	} else {
257		result = qstate->read_func(qstate, &elem_type, sizeof(int));
258		if (result != sizeof(int)) {
259			TRACE_OUT(on_rw_mapper);
260			return (-1);
261		}
262
263		switch (elem_type) {
264		case CET_WRITE_REQUEST:
265			qstate->kevent_watermark = sizeof(size_t);
266			qstate->process_func = on_write_request_read1;
267		break;
268		case CET_READ_REQUEST:
269			qstate->kevent_watermark = sizeof(size_t);
270			qstate->process_func = on_read_request_read1;
271		break;
272		default:
273			TRACE_OUT(on_rw_mapper);
274			return (-1);
275		break;
276		}
277	}
278	TRACE_OUT(on_rw_mapper);
279	return (0);
280}
281
282/*
283 * The default query_destroy function
284 */
285static void
286on_query_destroy(struct query_state *qstate)
287{
288
289	TRACE_IN(on_query_destroy);
290	finalize_comm_element(&qstate->response);
291	finalize_comm_element(&qstate->request);
292	TRACE_OUT(on_query_destroy);
293}
294
295/*
296 * The functions below are used to process write requests.
297 * - on_write_request_read1 and on_write_request_read2 read the request itself
298 * - on_write_request_process processes it (if the client requests to
299 *    cache the negative result, the on_negative_write_request_process is used)
300 * - on_write_response_write1 sends the response
301 */
302static int
303on_write_request_read1(struct query_state *qstate)
304{
305	struct cache_write_request	*write_request;
306	ssize_t	result;
307
308	TRACE_IN(on_write_request_read1);
309	if (qstate->kevent_watermark == 0)
310		qstate->kevent_watermark = sizeof(size_t) * 3;
311	else {
312		init_comm_element(&qstate->request, CET_WRITE_REQUEST);
313		write_request = get_cache_write_request(&qstate->request);
314
315		result = qstate->read_func(qstate, &write_request->entry_length,
316	    		sizeof(size_t));
317		result += qstate->read_func(qstate,
318	    		&write_request->cache_key_size, sizeof(size_t));
319		result += qstate->read_func(qstate,
320	    		&write_request->data_size, sizeof(size_t));
321
322		if (result != sizeof(size_t) * 3) {
323			TRACE_OUT(on_write_request_read1);
324			return (-1);
325		}
326
327		if (BUFSIZE_INVALID(write_request->entry_length) ||
328			BUFSIZE_INVALID(write_request->cache_key_size) ||
329			(BUFSIZE_INVALID(write_request->data_size) &&
330			(write_request->data_size != 0))) {
331			TRACE_OUT(on_write_request_read1);
332			return (-1);
333		}
334
335		write_request->entry = (char *)malloc(
336			write_request->entry_length + 1);
337		assert(write_request->entry != NULL);
338		memset(write_request->entry, 0,
339			write_request->entry_length + 1);
340
341		write_request->cache_key = (char *)malloc(
342			write_request->cache_key_size +
343			qstate->eid_str_length);
344		assert(write_request->cache_key != NULL);
345		memcpy(write_request->cache_key, qstate->eid_str,
346			qstate->eid_str_length);
347		memset(write_request->cache_key + qstate->eid_str_length, 0,
348			write_request->cache_key_size);
349
350		if (write_request->data_size != 0) {
351			write_request->data = (char *)malloc(
352				write_request->data_size);
353			assert(write_request->data != NULL);
354			memset(write_request->data, 0,
355				write_request->data_size);
356		}
357
358		qstate->kevent_watermark = write_request->entry_length +
359			write_request->cache_key_size +
360			write_request->data_size;
361		qstate->process_func = on_write_request_read2;
362	}
363
364	TRACE_OUT(on_write_request_read1);
365	return (0);
366}
367
368static int
369on_write_request_read2(struct query_state *qstate)
370{
371	struct cache_write_request	*write_request;
372	ssize_t	result;
373
374	TRACE_IN(on_write_request_read2);
375	write_request = get_cache_write_request(&qstate->request);
376
377	result = qstate->read_func(qstate, write_request->entry,
378		write_request->entry_length);
379	result += qstate->read_func(qstate, write_request->cache_key +
380		qstate->eid_str_length, write_request->cache_key_size);
381	if (write_request->data_size != 0)
382		result += qstate->read_func(qstate, write_request->data,
383			write_request->data_size);
384
385	if (result != qstate->kevent_watermark) {
386		TRACE_OUT(on_write_request_read2);
387		return (-1);
388	}
389	write_request->cache_key_size += qstate->eid_str_length;
390
391	qstate->kevent_watermark = 0;
392	if (write_request->data_size != 0)
393		qstate->process_func = on_write_request_process;
394	else
395	    	qstate->process_func = on_negative_write_request_process;
396	TRACE_OUT(on_write_request_read2);
397	return (0);
398}
399
400static	int
401on_write_request_process(struct query_state *qstate)
402{
403	struct cache_write_request	*write_request;
404	struct cache_write_response	*write_response;
405	cache_entry c_entry;
406
407	TRACE_IN(on_write_request_process);
408	init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
409	write_response = get_cache_write_response(&qstate->response);
410	write_request = get_cache_write_request(&qstate->request);
411
412	qstate->config_entry = configuration_find_entry(
413		s_configuration, write_request->entry);
414
415	if (qstate->config_entry == NULL) {
416		write_response->error_code = ENOENT;
417
418		LOG_ERR_2("write_request", "can't find configuration"
419		    " entry '%s'. aborting request", write_request->entry);
420		goto fin;
421	}
422
423	if (qstate->config_entry->enabled == 0) {
424		write_response->error_code = EACCES;
425
426		LOG_ERR_2("write_request",
427			"configuration entry '%s' is disabled",
428			write_request->entry);
429		goto fin;
430	}
431
432	if (qstate->config_entry->perform_actual_lookups != 0) {
433		write_response->error_code = EOPNOTSUPP;
434
435		LOG_ERR_2("write_request",
436			"entry '%s' performs lookups by itself: "
437			"can't write to it", write_request->entry);
438		goto fin;
439	}
440
441	configuration_lock_rdlock(s_configuration);
442	c_entry = find_cache_entry(s_cache,
443    		qstate->config_entry->positive_cache_params.entry_name);
444	configuration_unlock(s_configuration);
445	if (c_entry != NULL) {
446		configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
447		qstate->config_entry->positive_cache_entry = c_entry;
448		write_response->error_code = cache_write(c_entry,
449			write_request->cache_key,
450	    		write_request->cache_key_size,
451	    		write_request->data,
452			write_request->data_size);
453		configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
454
455		if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
456		    (qstate->config_entry->common_query_timeout.tv_usec != 0))
457			memcpy(&qstate->timeout,
458				&qstate->config_entry->common_query_timeout,
459				sizeof(struct timeval));
460
461	} else
462		write_response->error_code = -1;
463
464fin:
465	qstate->kevent_filter = EVFILT_WRITE;
466	qstate->kevent_watermark = sizeof(int);
467	qstate->process_func = on_write_response_write1;
468
469	TRACE_OUT(on_write_request_process);
470	return (0);
471}
472
473static int
474on_negative_write_request_process(struct query_state *qstate)
475{
476	struct cache_write_request	*write_request;
477	struct cache_write_response	*write_response;
478	cache_entry c_entry;
479
480	TRACE_IN(on_negative_write_request_process);
481	init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
482	write_response = get_cache_write_response(&qstate->response);
483	write_request = get_cache_write_request(&qstate->request);
484
485	qstate->config_entry = configuration_find_entry	(
486		s_configuration, write_request->entry);
487
488	if (qstate->config_entry == NULL) {
489		write_response->error_code = ENOENT;
490
491		LOG_ERR_2("negative_write_request",
492			"can't find configuration"
493		   	" entry '%s'. aborting request", write_request->entry);
494		goto fin;
495	}
496
497	if (qstate->config_entry->enabled == 0) {
498		write_response->error_code = EACCES;
499
500		LOG_ERR_2("negative_write_request",
501			"configuration entry '%s' is disabled",
502			write_request->entry);
503		goto fin;
504	}
505
506	if (qstate->config_entry->perform_actual_lookups != 0) {
507		write_response->error_code = EOPNOTSUPP;
508
509		LOG_ERR_2("negative_write_request",
510			"entry '%s' performs lookups by itself: "
511			"can't write to it", write_request->entry);
512		goto fin;
513	} else {
514#ifdef NS_CACHED_EID_CHECKING
515		if (check_query_eids(qstate) != 0) {
516			write_response->error_code = EPERM;
517			goto fin;
518		}
519#endif
520	}
521
522	configuration_lock_rdlock(s_configuration);
523	c_entry = find_cache_entry(s_cache,
524    		qstate->config_entry->negative_cache_params.entry_name);
525	configuration_unlock(s_configuration);
526	if (c_entry != NULL) {
527		configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
528		qstate->config_entry->negative_cache_entry = c_entry;
529		write_response->error_code = cache_write(c_entry,
530			write_request->cache_key,
531	    		write_request->cache_key_size,
532	    		negative_data,
533			sizeof(negative_data));
534		configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
535
536		if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
537		    (qstate->config_entry->common_query_timeout.tv_usec != 0))
538			memcpy(&qstate->timeout,
539				&qstate->config_entry->common_query_timeout,
540				sizeof(struct timeval));
541	} else
542		write_response->error_code = -1;
543
544fin:
545	qstate->kevent_filter = EVFILT_WRITE;
546	qstate->kevent_watermark = sizeof(int);
547	qstate->process_func = on_write_response_write1;
548
549	TRACE_OUT(on_negative_write_request_process);
550	return (0);
551}
552
553static int
554on_write_response_write1(struct query_state *qstate)
555{
556	struct cache_write_response	*write_response;
557	ssize_t	result;
558
559	TRACE_IN(on_write_response_write1);
560	write_response = get_cache_write_response(&qstate->response);
561	result = qstate->write_func(qstate, &write_response->error_code,
562		sizeof(int));
563	if (result != sizeof(int)) {
564		TRACE_OUT(on_write_response_write1);
565		return (-1);
566	}
567
568	finalize_comm_element(&qstate->request);
569	finalize_comm_element(&qstate->response);
570
571	qstate->kevent_watermark = sizeof(int);
572	qstate->kevent_filter = EVFILT_READ;
573	qstate->process_func = on_rw_mapper;
574
575	TRACE_OUT(on_write_response_write1);
576	return (0);
577}
578
579/*
580 * The functions below are used to process read requests.
581 * - on_read_request_read1 and on_read_request_read2 read the request itself
582 * - on_read_request_process processes it
583 * - on_read_response_write1 and on_read_response_write2 send the response
584 */
585static int
586on_read_request_read1(struct query_state *qstate)
587{
588	struct cache_read_request *read_request;
589	ssize_t	result;
590
591	TRACE_IN(on_read_request_read1);
592	if (qstate->kevent_watermark == 0)
593		qstate->kevent_watermark = sizeof(size_t) * 2;
594	else {
595		init_comm_element(&qstate->request, CET_READ_REQUEST);
596		read_request = get_cache_read_request(&qstate->request);
597
598		result = qstate->read_func(qstate,
599	    		&read_request->entry_length, sizeof(size_t));
600		result += qstate->read_func(qstate,
601	    		&read_request->cache_key_size, sizeof(size_t));
602
603		if (result != sizeof(size_t) * 2) {
604			TRACE_OUT(on_read_request_read1);
605			return (-1);
606		}
607
608		if (BUFSIZE_INVALID(read_request->entry_length) ||
609			BUFSIZE_INVALID(read_request->cache_key_size)) {
610			TRACE_OUT(on_read_request_read1);
611			return (-1);
612		}
613
614		read_request->entry = (char *)malloc(
615			read_request->entry_length + 1);
616		assert(read_request->entry != NULL);
617		memset(read_request->entry, 0, read_request->entry_length + 1);
618
619		read_request->cache_key = (char *)malloc(
620			read_request->cache_key_size +
621			qstate->eid_str_length);
622		assert(read_request->cache_key != NULL);
623		memcpy(read_request->cache_key, qstate->eid_str,
624			qstate->eid_str_length);
625		memset(read_request->cache_key + qstate->eid_str_length, 0,
626			read_request->cache_key_size);
627
628		qstate->kevent_watermark = read_request->entry_length +
629			read_request->cache_key_size;
630		qstate->process_func = on_read_request_read2;
631	}
632
633	TRACE_OUT(on_read_request_read1);
634	return (0);
635}
636
637static int
638on_read_request_read2(struct query_state *qstate)
639{
640	struct cache_read_request	*read_request;
641	ssize_t	result;
642
643	TRACE_IN(on_read_request_read2);
644	read_request = get_cache_read_request(&qstate->request);
645
646	result = qstate->read_func(qstate, read_request->entry,
647		read_request->entry_length);
648	result += qstate->read_func(qstate,
649		read_request->cache_key + qstate->eid_str_length,
650		read_request->cache_key_size);
651
652	if (result != qstate->kevent_watermark) {
653		TRACE_OUT(on_read_request_read2);
654		return (-1);
655	}
656	read_request->cache_key_size += qstate->eid_str_length;
657
658	qstate->kevent_watermark = 0;
659	qstate->process_func = on_read_request_process;
660
661	TRACE_OUT(on_read_request_read2);
662	return (0);
663}
664
665static int
666on_read_request_process(struct query_state *qstate)
667{
668	struct cache_read_request *read_request;
669	struct cache_read_response *read_response;
670	cache_entry	c_entry, neg_c_entry;
671
672	struct agent	*lookup_agent;
673	struct common_agent *c_agent;
674	int res;
675
676	TRACE_IN(on_read_request_process);
677	init_comm_element(&qstate->response, CET_READ_RESPONSE);
678	read_response = get_cache_read_response(&qstate->response);
679	read_request = get_cache_read_request(&qstate->request);
680
681	qstate->config_entry = configuration_find_entry(
682		s_configuration, read_request->entry);
683	if (qstate->config_entry == NULL) {
684		read_response->error_code = ENOENT;
685
686		LOG_ERR_2("read_request",
687			"can't find configuration "
688	    		"entry '%s'. aborting request", read_request->entry);
689	    	goto fin;
690	}
691
692	if (qstate->config_entry->enabled == 0) {
693		read_response->error_code = EACCES;
694
695		LOG_ERR_2("read_request",
696			"configuration entry '%s' is disabled",
697			read_request->entry);
698		goto fin;
699	}
700
701	/*
702	 * if we perform lookups by ourselves, then we don't need to separate
703	 * cache entries by euid and egid
704	 */
705	if (qstate->config_entry->perform_actual_lookups != 0)
706		memset(read_request->cache_key, 0, qstate->eid_str_length);
707	else {
708#ifdef NS_CACHED_EID_CHECKING
709		if (check_query_eids(qstate) != 0) {
710		/* if the lookup is not self-performing, we check for clients euid/egid */
711			read_response->error_code = EPERM;
712			goto fin;
713		}
714#endif
715	}
716
717	configuration_lock_rdlock(s_configuration);
718	c_entry = find_cache_entry(s_cache,
719    		qstate->config_entry->positive_cache_params.entry_name);
720	neg_c_entry = find_cache_entry(s_cache,
721		qstate->config_entry->negative_cache_params.entry_name);
722	configuration_unlock(s_configuration);
723	if ((c_entry != NULL) && (neg_c_entry != NULL)) {
724		configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
725		qstate->config_entry->positive_cache_entry = c_entry;
726		read_response->error_code = cache_read(c_entry,
727	    		read_request->cache_key,
728	    		read_request->cache_key_size, NULL,
729	    		&read_response->data_size);
730
731		if (read_response->error_code == -2) {
732			read_response->data = (char *)malloc(
733		    		read_response->data_size);
734			assert(read_response != NULL);
735			read_response->error_code = cache_read(c_entry,
736				read_request->cache_key,
737		    		read_request->cache_key_size,
738		    		read_response->data,
739		    		&read_response->data_size);
740		}
741		configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
742
743		configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
744		qstate->config_entry->negative_cache_entry = neg_c_entry;
745		if (read_response->error_code == -1) {
746			read_response->error_code = cache_read(neg_c_entry,
747				read_request->cache_key,
748				read_request->cache_key_size, NULL,
749				&read_response->data_size);
750
751			if (read_response->error_code == -2) {
752				read_response->error_code = 0;
753				read_response->data = NULL;
754				read_response->data_size = 0;
755			}
756		}
757		configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
758
759		if ((read_response->error_code == -1) &&
760			(qstate->config_entry->perform_actual_lookups != 0)) {
761			free(read_response->data);
762			read_response->data = NULL;
763			read_response->data_size = 0;
764
765			lookup_agent = find_agent(s_agent_table,
766				read_request->entry, COMMON_AGENT);
767
768			if ((lookup_agent != NULL) &&
769			(lookup_agent->type == COMMON_AGENT)) {
770				c_agent = (struct common_agent *)lookup_agent;
771				res = c_agent->lookup_func(
772					read_request->cache_key +
773						qstate->eid_str_length,
774					read_request->cache_key_size -
775						qstate->eid_str_length,
776					&read_response->data,
777					&read_response->data_size);
778
779				if (res == NS_SUCCESS) {
780					read_response->error_code = 0;
781					configuration_lock_entry(
782						qstate->config_entry,
783						CELT_POSITIVE);
784					cache_write(c_entry,
785						read_request->cache_key,
786	    					read_request->cache_key_size,
787	    					read_response->data,
788						read_response->data_size);
789					configuration_unlock_entry(
790						qstate->config_entry,
791						CELT_POSITIVE);
792				} else if ((res == NS_NOTFOUND) ||
793					  (res == NS_RETURN)) {
794					configuration_lock_entry(
795						  qstate->config_entry,
796						  CELT_NEGATIVE);
797					cache_write(neg_c_entry,
798						read_request->cache_key,
799						read_request->cache_key_size,
800						negative_data,
801						sizeof(negative_data));
802					configuration_unlock_entry(
803						  qstate->config_entry,
804						  CELT_NEGATIVE);
805
806					read_response->error_code = 0;
807					read_response->data = NULL;
808					read_response->data_size = 0;
809				}
810			}
811		}
812
813		if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
814		    (qstate->config_entry->common_query_timeout.tv_usec != 0))
815			memcpy(&qstate->timeout,
816				&qstate->config_entry->common_query_timeout,
817				sizeof(struct timeval));
818	} else
819		read_response->error_code = -1;
820
821fin:
822	qstate->kevent_filter = EVFILT_WRITE;
823	if (read_response->error_code == 0)
824		qstate->kevent_watermark = sizeof(int) + sizeof(size_t);
825	else
826		qstate->kevent_watermark = sizeof(int);
827	qstate->process_func = on_read_response_write1;
828
829	TRACE_OUT(on_read_request_process);
830	return (0);
831}
832
833static int
834on_read_response_write1(struct query_state *qstate)
835{
836	struct cache_read_response	*read_response;
837	ssize_t	result;
838
839	TRACE_IN(on_read_response_write1);
840	read_response = get_cache_read_response(&qstate->response);
841
842	result = qstate->write_func(qstate, &read_response->error_code,
843		sizeof(int));
844
845	if (read_response->error_code == 0) {
846		result += qstate->write_func(qstate, &read_response->data_size,
847			sizeof(size_t));
848		if (result != qstate->kevent_watermark) {
849			TRACE_OUT(on_read_response_write1);
850			return (-1);
851		}
852
853		qstate->kevent_watermark = read_response->data_size;
854		qstate->process_func = on_read_response_write2;
855	} else {
856		if (result != qstate->kevent_watermark) {
857			TRACE_OUT(on_read_response_write1);
858			return (-1);
859		}
860
861		qstate->kevent_watermark = 0;
862		qstate->process_func = NULL;
863	}
864
865	TRACE_OUT(on_read_response_write1);
866	return (0);
867}
868
869static int
870on_read_response_write2(struct query_state *qstate)
871{
872	struct cache_read_response	*read_response;
873	ssize_t	result;
874
875	TRACE_IN(on_read_response_write2);
876	read_response = get_cache_read_response(&qstate->response);
877	if (read_response->data_size > 0) {
878		result = qstate->write_func(qstate, read_response->data,
879			read_response->data_size);
880		if (result != qstate->kevent_watermark) {
881			TRACE_OUT(on_read_response_write2);
882			return (-1);
883		}
884	}
885
886	finalize_comm_element(&qstate->request);
887	finalize_comm_element(&qstate->response);
888
889	qstate->kevent_watermark = sizeof(int);
890	qstate->kevent_filter = EVFILT_READ;
891	qstate->process_func = on_rw_mapper;
892	TRACE_OUT(on_read_response_write2);
893	return (0);
894}
895
896/*
897 * The functions below are used to process write requests.
898 * - on_transform_request_read1 and on_transform_request_read2 read the
899 *   request itself
900 * - on_transform_request_process processes it
901 * - on_transform_response_write1 sends the response
902 */
903static int
904on_transform_request_read1(struct query_state *qstate)
905{
906	struct cache_transform_request *transform_request;
907	ssize_t	result;
908
909	TRACE_IN(on_transform_request_read1);
910	if (qstate->kevent_watermark == 0)
911		qstate->kevent_watermark = sizeof(size_t) + sizeof(int);
912	else {
913		init_comm_element(&qstate->request, CET_TRANSFORM_REQUEST);
914		transform_request =
915			get_cache_transform_request(&qstate->request);
916
917		result = qstate->read_func(qstate,
918	    		&transform_request->entry_length, sizeof(size_t));
919		result += qstate->read_func(qstate,
920	    		&transform_request->transformation_type, sizeof(int));
921
922		if (result != sizeof(size_t) + sizeof(int)) {
923			TRACE_OUT(on_transform_request_read1);
924			return (-1);
925		}
926
927		if ((transform_request->transformation_type != TT_USER) &&
928		    (transform_request->transformation_type != TT_ALL)) {
929			TRACE_OUT(on_transform_request_read1);
930			return (-1);
931		}
932
933		if (transform_request->entry_length != 0) {
934			if (BUFSIZE_INVALID(transform_request->entry_length)) {
935				TRACE_OUT(on_transform_request_read1);
936				return (-1);
937			}
938
939			transform_request->entry = (char *)malloc(
940				transform_request->entry_length + 1);
941			assert(transform_request->entry != NULL);
942			memset(transform_request->entry, 0,
943				transform_request->entry_length + 1);
944
945			qstate->process_func = on_transform_request_read2;
946		} else
947			qstate->process_func = on_transform_request_process;
948
949		qstate->kevent_watermark = transform_request->entry_length;
950	}
951
952	TRACE_OUT(on_transform_request_read1);
953	return (0);
954}
955
956static int
957on_transform_request_read2(struct query_state *qstate)
958{
959	struct cache_transform_request	*transform_request;
960	ssize_t	result;
961
962	TRACE_IN(on_transform_request_read2);
963	transform_request = get_cache_transform_request(&qstate->request);
964
965	result = qstate->read_func(qstate, transform_request->entry,
966		transform_request->entry_length);
967
968	if (result != qstate->kevent_watermark) {
969		TRACE_OUT(on_transform_request_read2);
970		return (-1);
971	}
972
973	qstate->kevent_watermark = 0;
974	qstate->process_func = on_transform_request_process;
975
976	TRACE_OUT(on_transform_request_read2);
977	return (0);
978}
979
980static int
981on_transform_request_process(struct query_state *qstate)
982{
983	struct cache_transform_request *transform_request;
984	struct cache_transform_response *transform_response;
985	struct configuration_entry *config_entry;
986	size_t	i, size;
987
988	TRACE_IN(on_transform_request_process);
989	init_comm_element(&qstate->response, CET_TRANSFORM_RESPONSE);
990	transform_response = get_cache_transform_response(&qstate->response);
991	transform_request = get_cache_transform_request(&qstate->request);
992
993	switch (transform_request->transformation_type) {
994	case TT_USER:
995		if (transform_request->entry == NULL) {
996			size = configuration_get_entries_size(s_configuration);
997			for (i = 0; i < size; ++i) {
998			    config_entry = configuration_get_entry(
999				s_configuration, i);
1000
1001			    if (config_entry->perform_actual_lookups == 0)
1002			    	clear_config_entry_part(config_entry,
1003				    qstate->eid_str, qstate->eid_str_length);
1004			}
1005		} else {
1006			qstate->config_entry = configuration_find_entry(
1007				s_configuration, transform_request->entry);
1008
1009			if (qstate->config_entry == NULL) {
1010				LOG_ERR_2("transform_request",
1011					"can't find configuration"
1012		   			" entry '%s'. aborting request",
1013					transform_request->entry);
1014				transform_response->error_code = -1;
1015				goto fin;
1016			}
1017
1018			if (qstate->config_entry->perform_actual_lookups != 0) {
1019				LOG_ERR_2("transform_request",
1020					"can't transform the cache entry %s"
1021					", because it ised for actual lookups",
1022					transform_request->entry);
1023				transform_response->error_code = -1;
1024				goto fin;
1025			}
1026
1027			clear_config_entry_part(qstate->config_entry,
1028				qstate->eid_str, qstate->eid_str_length);
1029		}
1030		break;
1031	case TT_ALL:
1032		if (qstate->euid != 0)
1033			transform_response->error_code = -1;
1034		else {
1035			if (transform_request->entry == NULL) {
1036				size = configuration_get_entries_size(
1037					s_configuration);
1038				for (i = 0; i < size; ++i) {
1039				    clear_config_entry(
1040					configuration_get_entry(
1041						s_configuration, i));
1042				}
1043			} else {
1044				qstate->config_entry = configuration_find_entry(
1045					s_configuration,
1046					transform_request->entry);
1047
1048				if (qstate->config_entry == NULL) {
1049					LOG_ERR_2("transform_request",
1050						"can't find configuration"
1051		   				" entry '%s'. aborting request",
1052						transform_request->entry);
1053					transform_response->error_code = -1;
1054					goto fin;
1055				}
1056
1057				clear_config_entry(qstate->config_entry);
1058			}
1059		}
1060		break;
1061	default:
1062		transform_response->error_code = -1;
1063	}
1064
1065fin:
1066	qstate->kevent_watermark = 0;
1067	qstate->process_func = on_transform_response_write1;
1068	TRACE_OUT(on_transform_request_process);
1069	return (0);
1070}
1071
1072static int
1073on_transform_response_write1(struct query_state *qstate)
1074{
1075	struct cache_transform_response	*transform_response;
1076	ssize_t	result;
1077
1078	TRACE_IN(on_transform_response_write1);
1079	transform_response = get_cache_transform_response(&qstate->response);
1080	result = qstate->write_func(qstate, &transform_response->error_code,
1081		sizeof(int));
1082	if (result != sizeof(int)) {
1083		TRACE_OUT(on_transform_response_write1);
1084		return (-1);
1085	}
1086
1087	finalize_comm_element(&qstate->request);
1088	finalize_comm_element(&qstate->response);
1089
1090	qstate->kevent_watermark = 0;
1091	qstate->process_func = NULL;
1092	TRACE_OUT(on_transform_response_write1);
1093	return (0);
1094}
1095
1096/*
1097 * Checks if the client's euid and egid do not differ from its uid and gid.
1098 * Returns 0 on success.
1099 */
1100int
1101check_query_eids(struct query_state *qstate)
1102{
1103
1104	return ((qstate->uid != qstate->euid) || (qstate->gid != qstate->egid) ? -1 : 0);
1105}
1106
1107/*
1108 * Uses the qstate fields to process an "alternate" read - when the buffer is
1109 * too large to be received during one socket read operation
1110 */
1111ssize_t
1112query_io_buffer_read(struct query_state *qstate, void *buf, size_t nbytes)
1113{
1114	ssize_t	result;
1115
1116	TRACE_IN(query_io_buffer_read);
1117	if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
1118		return (-1);
1119
1120	if (nbytes < qstate->io_buffer + qstate->io_buffer_size -
1121			qstate->io_buffer_p)
1122		result = nbytes;
1123	else
1124		result = qstate->io_buffer + qstate->io_buffer_size -
1125			qstate->io_buffer_p;
1126
1127	memcpy(buf, qstate->io_buffer_p, result);
1128	qstate->io_buffer_p += result;
1129
1130	if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) {
1131		free(qstate->io_buffer);
1132		qstate->io_buffer = NULL;
1133
1134		qstate->write_func = query_socket_write;
1135		qstate->read_func = query_socket_read;
1136	}
1137
1138	TRACE_OUT(query_io_buffer_read);
1139	return (result);
1140}
1141
1142/*
1143 * Uses the qstate fields to process an "alternate" write - when the buffer is
1144 * too large to be sent during one socket write operation
1145 */
1146ssize_t
1147query_io_buffer_write(struct query_state *qstate, const void *buf,
1148	size_t nbytes)
1149{
1150	ssize_t	result;
1151
1152	TRACE_IN(query_io_buffer_write);
1153	if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
1154		return (-1);
1155
1156	if (nbytes < qstate->io_buffer + qstate->io_buffer_size -
1157			qstate->io_buffer_p)
1158		result = nbytes;
1159	else
1160		result = qstate->io_buffer + qstate->io_buffer_size -
1161		qstate->io_buffer_p;
1162
1163	memcpy(qstate->io_buffer_p, buf, result);
1164	qstate->io_buffer_p += result;
1165
1166	if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) {
1167		qstate->use_alternate_io = 1;
1168		qstate->io_buffer_p = qstate->io_buffer;
1169
1170		qstate->write_func = query_socket_write;
1171		qstate->read_func = query_socket_read;
1172	}
1173
1174	TRACE_OUT(query_io_buffer_write);
1175	return (result);
1176}
1177
1178/*
1179 * The default "read" function, which reads data directly from socket
1180 */
1181ssize_t
1182query_socket_read(struct query_state *qstate, void *buf, size_t nbytes)
1183{
1184	ssize_t	result;
1185
1186	TRACE_IN(query_socket_read);
1187	if (qstate->socket_failed != 0) {
1188		TRACE_OUT(query_socket_read);
1189		return (-1);
1190	}
1191
1192	result = read(qstate->sockfd, buf, nbytes);
1193	if ((result == -1) || (result < nbytes))
1194		qstate->socket_failed = 1;
1195
1196	TRACE_OUT(query_socket_read);
1197	return (result);
1198}
1199
1200/*
1201 * The default "write" function, which writes data directly to socket
1202 */
1203ssize_t
1204query_socket_write(struct query_state *qstate, const void *buf, size_t nbytes)
1205{
1206	ssize_t	result;
1207
1208	TRACE_IN(query_socket_write);
1209	if (qstate->socket_failed != 0) {
1210		TRACE_OUT(query_socket_write);
1211		return (-1);
1212	}
1213
1214	result = write(qstate->sockfd, buf, nbytes);
1215	if ((result == -1) || (result < nbytes))
1216		qstate->socket_failed = 1;
1217
1218	TRACE_OUT(query_socket_write);
1219	return (result);
1220}
1221
1222/*
1223 * Initializes the query_state structure by filling it with the default values.
1224 */
1225struct query_state *
1226init_query_state(int sockfd, size_t kevent_watermark, uid_t euid, gid_t egid)
1227{
1228	struct query_state	*retval;
1229
1230	TRACE_IN(init_query_state);
1231	retval = (struct query_state *)malloc(sizeof(struct query_state));
1232	assert(retval != NULL);
1233	memset(retval, 0, sizeof(struct query_state));
1234
1235	retval->sockfd = sockfd;
1236	retval->kevent_filter = EVFILT_READ;
1237	retval->kevent_watermark = kevent_watermark;
1238
1239	retval->euid = euid;
1240	retval->egid = egid;
1241	retval->uid = retval->gid = -1;
1242
1243	if (asprintf(&retval->eid_str, "%d_%d_", retval->euid,
1244		retval->egid) == -1) {
1245		free(retval);
1246		return (NULL);
1247	}
1248	retval->eid_str_length = strlen(retval->eid_str);
1249
1250	init_comm_element(&retval->request, CET_UNDEFINED);
1251	init_comm_element(&retval->response, CET_UNDEFINED);
1252	retval->process_func = on_query_startup;
1253	retval->destroy_func = on_query_destroy;
1254
1255	retval->write_func = query_socket_write;
1256	retval->read_func = query_socket_read;
1257
1258	get_time_func(&retval->creation_time);
1259	memcpy(&retval->timeout, &s_configuration->query_timeout,
1260		sizeof(struct timeval));
1261
1262	TRACE_OUT(init_query_state);
1263	return (retval);
1264}
1265
1266void
1267destroy_query_state(struct query_state *qstate)
1268{
1269
1270	TRACE_IN(destroy_query_state);
1271	if (qstate->eid_str != NULL)
1272	    free(qstate->eid_str);
1273
1274	if (qstate->io_buffer != NULL)
1275		free(qstate->io_buffer);
1276
1277	qstate->destroy_func(qstate);
1278	free(qstate);
1279	TRACE_OUT(destroy_query_state);
1280}
1281