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/types.h>
29#include <sys/event.h>
30#include <sys/socket.h>
31#include <sys/time.h>
32
33#include <assert.h>
34#include <errno.h>
35#include <nsswitch.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
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 * 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	union {
156		struct cmsghdr hdr;
157		char pad[CMSG_SPACE(sizeof(struct cmsgcred))];
158	} cmsg;
159	struct msghdr mhdr;
160	struct iovec iov;
161	struct cmsgcred *cred;
162	int elem_type;
163
164	TRACE_IN(on_query_startup);
165	assert(qstate != NULL);
166
167	memset(&mhdr, 0, sizeof(mhdr));
168	mhdr.msg_iov = &iov;
169	mhdr.msg_iovlen = 1;
170	mhdr.msg_control = &cmsg;
171	mhdr.msg_controllen = sizeof(cmsg);
172
173	memset(&iov, 0, sizeof(iov));
174	iov.iov_base = &elem_type;
175	iov.iov_len = sizeof(elem_type);
176
177	if (recvmsg(qstate->sockfd, &mhdr, 0) == -1) {
178		TRACE_OUT(on_query_startup);
179		return (-1);
180	}
181
182	if (mhdr.msg_controllen != CMSG_SPACE(sizeof(struct cmsgcred)) ||
183	    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_NSCD_EID_CHECKING) || defined(NS_STRICT_NSCD_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_NSCD_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 = calloc(1,
336			write_request->entry_length + 1);
337		assert(write_request->entry != NULL);
338
339		write_request->cache_key = calloc(1,
340			write_request->cache_key_size +
341			qstate->eid_str_length);
342		assert(write_request->cache_key != NULL);
343		memcpy(write_request->cache_key, qstate->eid_str,
344			qstate->eid_str_length);
345
346		if (write_request->data_size != 0) {
347			write_request->data = calloc(1,
348				write_request->data_size);
349			assert(write_request->data != NULL);
350		}
351
352		qstate->kevent_watermark = write_request->entry_length +
353			write_request->cache_key_size +
354			write_request->data_size;
355		qstate->process_func = on_write_request_read2;
356	}
357
358	TRACE_OUT(on_write_request_read1);
359	return (0);
360}
361
362static int
363on_write_request_read2(struct query_state *qstate)
364{
365	struct cache_write_request	*write_request;
366	ssize_t	result;
367
368	TRACE_IN(on_write_request_read2);
369	write_request = get_cache_write_request(&qstate->request);
370
371	result = qstate->read_func(qstate, write_request->entry,
372		write_request->entry_length);
373	result += qstate->read_func(qstate, write_request->cache_key +
374		qstate->eid_str_length, write_request->cache_key_size);
375	if (write_request->data_size != 0)
376		result += qstate->read_func(qstate, write_request->data,
377			write_request->data_size);
378
379	if (result != (ssize_t)qstate->kevent_watermark) {
380		TRACE_OUT(on_write_request_read2);
381		return (-1);
382	}
383	write_request->cache_key_size += qstate->eid_str_length;
384
385	qstate->kevent_watermark = 0;
386	if (write_request->data_size != 0)
387		qstate->process_func = on_write_request_process;
388	else
389	    	qstate->process_func = on_negative_write_request_process;
390	TRACE_OUT(on_write_request_read2);
391	return (0);
392}
393
394static	int
395on_write_request_process(struct query_state *qstate)
396{
397	struct cache_write_request	*write_request;
398	struct cache_write_response	*write_response;
399	cache_entry c_entry;
400
401	TRACE_IN(on_write_request_process);
402	init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
403	write_response = get_cache_write_response(&qstate->response);
404	write_request = get_cache_write_request(&qstate->request);
405
406	qstate->config_entry = configuration_find_entry(
407		s_configuration, write_request->entry);
408
409	if (qstate->config_entry == NULL) {
410		write_response->error_code = ENOENT;
411
412		LOG_ERR_2("write_request", "can't find configuration"
413		    " entry '%s'. aborting request", write_request->entry);
414		goto fin;
415	}
416
417	if (qstate->config_entry->enabled == 0) {
418		write_response->error_code = EACCES;
419
420		LOG_ERR_2("write_request",
421			"configuration entry '%s' is disabled",
422			write_request->entry);
423		goto fin;
424	}
425
426	if (qstate->config_entry->perform_actual_lookups != 0) {
427		write_response->error_code = EOPNOTSUPP;
428
429		LOG_ERR_2("write_request",
430			"entry '%s' performs lookups by itself: "
431			"can't write to it", write_request->entry);
432		goto fin;
433	}
434
435	configuration_lock_rdlock(s_configuration);
436	c_entry = find_cache_entry(s_cache,
437		qstate->config_entry->positive_cache_params.cep.entry_name);
438	configuration_unlock(s_configuration);
439	if (c_entry != NULL) {
440		configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
441		qstate->config_entry->positive_cache_entry = c_entry;
442		write_response->error_code = cache_write(c_entry,
443			write_request->cache_key,
444	    		write_request->cache_key_size,
445	    		write_request->data,
446			write_request->data_size);
447		configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
448
449		if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
450		    (qstate->config_entry->common_query_timeout.tv_usec != 0))
451			memcpy(&qstate->timeout,
452				&qstate->config_entry->common_query_timeout,
453				sizeof(struct timeval));
454
455	} else
456		write_response->error_code = -1;
457
458fin:
459	qstate->kevent_filter = EVFILT_WRITE;
460	qstate->kevent_watermark = sizeof(int);
461	qstate->process_func = on_write_response_write1;
462
463	TRACE_OUT(on_write_request_process);
464	return (0);
465}
466
467static int
468on_negative_write_request_process(struct query_state *qstate)
469{
470	struct cache_write_request	*write_request;
471	struct cache_write_response	*write_response;
472	cache_entry c_entry;
473
474	TRACE_IN(on_negative_write_request_process);
475	init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
476	write_response = get_cache_write_response(&qstate->response);
477	write_request = get_cache_write_request(&qstate->request);
478
479	qstate->config_entry = configuration_find_entry	(
480		s_configuration, write_request->entry);
481
482	if (qstate->config_entry == NULL) {
483		write_response->error_code = ENOENT;
484
485		LOG_ERR_2("negative_write_request",
486			"can't find configuration"
487		   	" entry '%s'. aborting request", write_request->entry);
488		goto fin;
489	}
490
491	if (qstate->config_entry->enabled == 0) {
492		write_response->error_code = EACCES;
493
494		LOG_ERR_2("negative_write_request",
495			"configuration entry '%s' is disabled",
496			write_request->entry);
497		goto fin;
498	}
499
500	if (qstate->config_entry->perform_actual_lookups != 0) {
501		write_response->error_code = EOPNOTSUPP;
502
503		LOG_ERR_2("negative_write_request",
504			"entry '%s' performs lookups by itself: "
505			"can't write to it", write_request->entry);
506		goto fin;
507	} else {
508#ifdef NS_NSCD_EID_CHECKING
509		if (check_query_eids(qstate) != 0) {
510			write_response->error_code = EPERM;
511			goto fin;
512		}
513#endif
514	}
515
516	configuration_lock_rdlock(s_configuration);
517	c_entry = find_cache_entry(s_cache,
518		qstate->config_entry->negative_cache_params.cep.entry_name);
519	configuration_unlock(s_configuration);
520	if (c_entry != NULL) {
521		configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
522		qstate->config_entry->negative_cache_entry = c_entry;
523		write_response->error_code = cache_write(c_entry,
524			write_request->cache_key,
525	    		write_request->cache_key_size,
526	    		negative_data,
527			sizeof(negative_data));
528		configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
529
530		if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
531		    (qstate->config_entry->common_query_timeout.tv_usec != 0))
532			memcpy(&qstate->timeout,
533				&qstate->config_entry->common_query_timeout,
534				sizeof(struct timeval));
535	} else
536		write_response->error_code = -1;
537
538fin:
539	qstate->kevent_filter = EVFILT_WRITE;
540	qstate->kevent_watermark = sizeof(int);
541	qstate->process_func = on_write_response_write1;
542
543	TRACE_OUT(on_negative_write_request_process);
544	return (0);
545}
546
547static int
548on_write_response_write1(struct query_state *qstate)
549{
550	struct cache_write_response	*write_response;
551	ssize_t	result;
552
553	TRACE_IN(on_write_response_write1);
554	write_response = get_cache_write_response(&qstate->response);
555	result = qstate->write_func(qstate, &write_response->error_code,
556		sizeof(int));
557	if (result != sizeof(int)) {
558		TRACE_OUT(on_write_response_write1);
559		return (-1);
560	}
561
562	finalize_comm_element(&qstate->request);
563	finalize_comm_element(&qstate->response);
564
565	qstate->kevent_watermark = sizeof(int);
566	qstate->kevent_filter = EVFILT_READ;
567	qstate->process_func = on_rw_mapper;
568
569	TRACE_OUT(on_write_response_write1);
570	return (0);
571}
572
573/*
574 * The functions below are used to process read requests.
575 * - on_read_request_read1 and on_read_request_read2 read the request itself
576 * - on_read_request_process processes it
577 * - on_read_response_write1 and on_read_response_write2 send the response
578 */
579static int
580on_read_request_read1(struct query_state *qstate)
581{
582	struct cache_read_request *read_request;
583	ssize_t	result;
584
585	TRACE_IN(on_read_request_read1);
586	if (qstate->kevent_watermark == 0)
587		qstate->kevent_watermark = sizeof(size_t) * 2;
588	else {
589		init_comm_element(&qstate->request, CET_READ_REQUEST);
590		read_request = get_cache_read_request(&qstate->request);
591
592		result = qstate->read_func(qstate,
593	    		&read_request->entry_length, sizeof(size_t));
594		result += qstate->read_func(qstate,
595	    		&read_request->cache_key_size, sizeof(size_t));
596
597		if (result != sizeof(size_t) * 2) {
598			TRACE_OUT(on_read_request_read1);
599			return (-1);
600		}
601
602		if (BUFSIZE_INVALID(read_request->entry_length) ||
603			BUFSIZE_INVALID(read_request->cache_key_size)) {
604			TRACE_OUT(on_read_request_read1);
605			return (-1);
606		}
607
608		read_request->entry = calloc(1,
609			read_request->entry_length + 1);
610		assert(read_request->entry != NULL);
611
612		read_request->cache_key = calloc(1,
613			read_request->cache_key_size +
614			qstate->eid_str_length);
615		assert(read_request->cache_key != NULL);
616		memcpy(read_request->cache_key, qstate->eid_str,
617			qstate->eid_str_length);
618
619		qstate->kevent_watermark = read_request->entry_length +
620			read_request->cache_key_size;
621		qstate->process_func = on_read_request_read2;
622	}
623
624	TRACE_OUT(on_read_request_read1);
625	return (0);
626}
627
628static int
629on_read_request_read2(struct query_state *qstate)
630{
631	struct cache_read_request	*read_request;
632	ssize_t	result;
633
634	TRACE_IN(on_read_request_read2);
635	read_request = get_cache_read_request(&qstate->request);
636
637	result = qstate->read_func(qstate, read_request->entry,
638		read_request->entry_length);
639	result += qstate->read_func(qstate,
640		read_request->cache_key + qstate->eid_str_length,
641		read_request->cache_key_size);
642
643	if (result != (ssize_t)qstate->kevent_watermark) {
644		TRACE_OUT(on_read_request_read2);
645		return (-1);
646	}
647	read_request->cache_key_size += qstate->eid_str_length;
648
649	qstate->kevent_watermark = 0;
650	qstate->process_func = on_read_request_process;
651
652	TRACE_OUT(on_read_request_read2);
653	return (0);
654}
655
656static int
657on_read_request_process(struct query_state *qstate)
658{
659	struct cache_read_request *read_request;
660	struct cache_read_response *read_response;
661	cache_entry	c_entry, neg_c_entry;
662
663	struct agent	*lookup_agent;
664	struct common_agent *c_agent;
665	int res;
666
667	TRACE_IN(on_read_request_process);
668	init_comm_element(&qstate->response, CET_READ_RESPONSE);
669	read_response = get_cache_read_response(&qstate->response);
670	read_request = get_cache_read_request(&qstate->request);
671
672	qstate->config_entry = configuration_find_entry(
673		s_configuration, read_request->entry);
674	if (qstate->config_entry == NULL) {
675		read_response->error_code = ENOENT;
676
677		LOG_ERR_2("read_request",
678			"can't find configuration "
679	    		"entry '%s'. aborting request", read_request->entry);
680	    	goto fin;
681	}
682
683	if (qstate->config_entry->enabled == 0) {
684		read_response->error_code = EACCES;
685
686		LOG_ERR_2("read_request",
687			"configuration entry '%s' is disabled",
688			read_request->entry);
689		goto fin;
690	}
691
692	/*
693	 * if we perform lookups by ourselves, then we don't need to separate
694	 * cache entries by euid and egid
695	 */
696	if (qstate->config_entry->perform_actual_lookups != 0)
697		memset(read_request->cache_key, 0, qstate->eid_str_length);
698	else {
699#ifdef NS_NSCD_EID_CHECKING
700		if (check_query_eids(qstate) != 0) {
701		/* if the lookup is not self-performing, we check for clients euid/egid */
702			read_response->error_code = EPERM;
703			goto fin;
704		}
705#endif
706	}
707
708	configuration_lock_rdlock(s_configuration);
709	c_entry = find_cache_entry(s_cache,
710		qstate->config_entry->positive_cache_params.cep.entry_name);
711	neg_c_entry = find_cache_entry(s_cache,
712		qstate->config_entry->negative_cache_params.cep.entry_name);
713	configuration_unlock(s_configuration);
714	if ((c_entry != NULL) && (neg_c_entry != NULL)) {
715		configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
716		qstate->config_entry->positive_cache_entry = c_entry;
717		read_response->error_code = cache_read(c_entry,
718	    		read_request->cache_key,
719	    		read_request->cache_key_size, NULL,
720	    		&read_response->data_size);
721
722		if (read_response->error_code == -2) {
723			read_response->data = malloc(
724				read_response->data_size);
725			assert(read_response->data != NULL);
726			read_response->error_code = cache_read(c_entry,
727				read_request->cache_key,
728		    		read_request->cache_key_size,
729		    		read_response->data,
730		    		&read_response->data_size);
731		}
732		configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
733
734		configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
735		qstate->config_entry->negative_cache_entry = neg_c_entry;
736		if (read_response->error_code == -1) {
737			read_response->error_code = cache_read(neg_c_entry,
738				read_request->cache_key,
739				read_request->cache_key_size, NULL,
740				&read_response->data_size);
741
742			if (read_response->error_code == -2) {
743				read_response->data = malloc(
744					read_response->data_size);
745				assert(read_response->data != NULL);
746				read_response->error_code = cache_read(neg_c_entry,
747					read_request->cache_key,
748		    			read_request->cache_key_size,
749		    			read_response->data,
750		    			&read_response->data_size);
751			}
752		}
753		configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
754
755		if ((read_response->error_code == -1) &&
756			(qstate->config_entry->perform_actual_lookups != 0)) {
757			free(read_response->data);
758			read_response->data = NULL;
759			read_response->data_size = 0;
760
761			lookup_agent = find_agent(s_agent_table,
762				read_request->entry, COMMON_AGENT);
763
764			if ((lookup_agent != NULL) &&
765			(lookup_agent->type == COMMON_AGENT)) {
766				c_agent = (struct common_agent *)lookup_agent;
767				res = c_agent->lookup_func(
768					read_request->cache_key +
769						qstate->eid_str_length,
770					read_request->cache_key_size -
771						qstate->eid_str_length,
772					&read_response->data,
773					&read_response->data_size);
774
775				if (res == NS_SUCCESS) {
776					read_response->error_code = 0;
777					configuration_lock_entry(
778						qstate->config_entry,
779						CELT_POSITIVE);
780					cache_write(c_entry,
781						read_request->cache_key,
782	    					read_request->cache_key_size,
783	    					read_response->data,
784						read_response->data_size);
785					configuration_unlock_entry(
786						qstate->config_entry,
787						CELT_POSITIVE);
788				} else if ((res == NS_NOTFOUND) ||
789					  (res == NS_RETURN)) {
790					configuration_lock_entry(
791						  qstate->config_entry,
792						  CELT_NEGATIVE);
793					cache_write(neg_c_entry,
794						read_request->cache_key,
795						read_request->cache_key_size,
796						negative_data,
797						sizeof(negative_data));
798					configuration_unlock_entry(
799						  qstate->config_entry,
800						  CELT_NEGATIVE);
801
802					read_response->error_code = 0;
803					read_response->data = NULL;
804					read_response->data_size = 0;
805				}
806			}
807		}
808
809		if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
810		    (qstate->config_entry->common_query_timeout.tv_usec != 0))
811			memcpy(&qstate->timeout,
812				&qstate->config_entry->common_query_timeout,
813				sizeof(struct timeval));
814	} else
815		read_response->error_code = -1;
816
817fin:
818	qstate->kevent_filter = EVFILT_WRITE;
819	if (read_response->error_code == 0)
820		qstate->kevent_watermark = sizeof(int) + sizeof(size_t);
821	else
822		qstate->kevent_watermark = sizeof(int);
823	qstate->process_func = on_read_response_write1;
824
825	TRACE_OUT(on_read_request_process);
826	return (0);
827}
828
829static int
830on_read_response_write1(struct query_state *qstate)
831{
832	struct cache_read_response	*read_response;
833	ssize_t	result;
834
835	TRACE_IN(on_read_response_write1);
836	read_response = get_cache_read_response(&qstate->response);
837
838	result = qstate->write_func(qstate, &read_response->error_code,
839		sizeof(int));
840
841	if (read_response->error_code == 0) {
842		result += qstate->write_func(qstate, &read_response->data_size,
843			sizeof(size_t));
844		if (result != (ssize_t)qstate->kevent_watermark) {
845			TRACE_OUT(on_read_response_write1);
846			return (-1);
847		}
848
849		qstate->kevent_watermark = read_response->data_size;
850		qstate->process_func = on_read_response_write2;
851	} else {
852		if (result != (ssize_t)qstate->kevent_watermark) {
853			TRACE_OUT(on_read_response_write1);
854			return (-1);
855		}
856
857		qstate->kevent_watermark = 0;
858		qstate->process_func = NULL;
859	}
860
861	TRACE_OUT(on_read_response_write1);
862	return (0);
863}
864
865static int
866on_read_response_write2(struct query_state *qstate)
867{
868	struct cache_read_response	*read_response;
869	ssize_t	result;
870
871	TRACE_IN(on_read_response_write2);
872	read_response = get_cache_read_response(&qstate->response);
873	if (read_response->data_size > 0) {
874		result = qstate->write_func(qstate, read_response->data,
875			read_response->data_size);
876		if (result != (ssize_t)qstate->kevent_watermark) {
877			TRACE_OUT(on_read_response_write2);
878			return (-1);
879		}
880	}
881
882	finalize_comm_element(&qstate->request);
883	finalize_comm_element(&qstate->response);
884
885	qstate->kevent_watermark = sizeof(int);
886	qstate->kevent_filter = EVFILT_READ;
887	qstate->process_func = on_rw_mapper;
888	TRACE_OUT(on_read_response_write2);
889	return (0);
890}
891
892/*
893 * The functions below are used to process write requests.
894 * - on_transform_request_read1 and on_transform_request_read2 read the
895 *   request itself
896 * - on_transform_request_process processes it
897 * - on_transform_response_write1 sends the response
898 */
899static int
900on_transform_request_read1(struct query_state *qstate)
901{
902	struct cache_transform_request *transform_request;
903	ssize_t	result;
904
905	TRACE_IN(on_transform_request_read1);
906	if (qstate->kevent_watermark == 0)
907		qstate->kevent_watermark = sizeof(size_t) + sizeof(int);
908	else {
909		init_comm_element(&qstate->request, CET_TRANSFORM_REQUEST);
910		transform_request =
911			get_cache_transform_request(&qstate->request);
912
913		result = qstate->read_func(qstate,
914	    		&transform_request->entry_length, sizeof(size_t));
915		result += qstate->read_func(qstate,
916	    		&transform_request->transformation_type, sizeof(int));
917
918		if (result != sizeof(size_t) + sizeof(int)) {
919			TRACE_OUT(on_transform_request_read1);
920			return (-1);
921		}
922
923		if ((transform_request->transformation_type != TT_USER) &&
924		    (transform_request->transformation_type != TT_ALL)) {
925			TRACE_OUT(on_transform_request_read1);
926			return (-1);
927		}
928
929		if (transform_request->entry_length != 0) {
930			if (BUFSIZE_INVALID(transform_request->entry_length)) {
931				TRACE_OUT(on_transform_request_read1);
932				return (-1);
933			}
934
935			transform_request->entry = calloc(1,
936				transform_request->entry_length + 1);
937			assert(transform_request->entry != NULL);
938
939			qstate->process_func = on_transform_request_read2;
940		} else
941			qstate->process_func = on_transform_request_process;
942
943		qstate->kevent_watermark = transform_request->entry_length;
944	}
945
946	TRACE_OUT(on_transform_request_read1);
947	return (0);
948}
949
950static int
951on_transform_request_read2(struct query_state *qstate)
952{
953	struct cache_transform_request	*transform_request;
954	ssize_t	result;
955
956	TRACE_IN(on_transform_request_read2);
957	transform_request = get_cache_transform_request(&qstate->request);
958
959	result = qstate->read_func(qstate, transform_request->entry,
960		transform_request->entry_length);
961
962	if (result != (ssize_t)qstate->kevent_watermark) {
963		TRACE_OUT(on_transform_request_read2);
964		return (-1);
965	}
966
967	qstate->kevent_watermark = 0;
968	qstate->process_func = on_transform_request_process;
969
970	TRACE_OUT(on_transform_request_read2);
971	return (0);
972}
973
974static int
975on_transform_request_process(struct query_state *qstate)
976{
977	struct cache_transform_request *transform_request;
978	struct cache_transform_response *transform_response;
979	struct configuration_entry *config_entry;
980	size_t	i, size;
981
982	TRACE_IN(on_transform_request_process);
983	init_comm_element(&qstate->response, CET_TRANSFORM_RESPONSE);
984	transform_response = get_cache_transform_response(&qstate->response);
985	transform_request = get_cache_transform_request(&qstate->request);
986
987	switch (transform_request->transformation_type) {
988	case TT_USER:
989		if (transform_request->entry == NULL) {
990			size = configuration_get_entries_size(s_configuration);
991			for (i = 0; i < size; ++i) {
992			    config_entry = configuration_get_entry(
993				s_configuration, i);
994
995			    if (config_entry->perform_actual_lookups == 0)
996			    	clear_config_entry_part(config_entry,
997				    qstate->eid_str, qstate->eid_str_length);
998			}
999		} else {
1000			qstate->config_entry = configuration_find_entry(
1001				s_configuration, transform_request->entry);
1002
1003			if (qstate->config_entry == NULL) {
1004				LOG_ERR_2("transform_request",
1005					"can't find configuration"
1006		   			" entry '%s'. aborting request",
1007					transform_request->entry);
1008				transform_response->error_code = -1;
1009				goto fin;
1010			}
1011
1012			if (qstate->config_entry->perform_actual_lookups != 0) {
1013				LOG_ERR_2("transform_request",
1014					"can't transform the cache entry %s"
1015					", because it ised for actual lookups",
1016					transform_request->entry);
1017				transform_response->error_code = -1;
1018				goto fin;
1019			}
1020
1021			clear_config_entry_part(qstate->config_entry,
1022				qstate->eid_str, qstate->eid_str_length);
1023		}
1024		break;
1025	case TT_ALL:
1026		if (qstate->euid != 0)
1027			transform_response->error_code = -1;
1028		else {
1029			if (transform_request->entry == NULL) {
1030				size = configuration_get_entries_size(
1031					s_configuration);
1032				for (i = 0; i < size; ++i) {
1033				    clear_config_entry(
1034					configuration_get_entry(
1035						s_configuration, i));
1036				}
1037			} else {
1038				qstate->config_entry = configuration_find_entry(
1039					s_configuration,
1040					transform_request->entry);
1041
1042				if (qstate->config_entry == NULL) {
1043					LOG_ERR_2("transform_request",
1044						"can't find configuration"
1045		   				" entry '%s'. aborting request",
1046						transform_request->entry);
1047					transform_response->error_code = -1;
1048					goto fin;
1049				}
1050
1051				clear_config_entry(qstate->config_entry);
1052			}
1053		}
1054		break;
1055	default:
1056		transform_response->error_code = -1;
1057	}
1058
1059fin:
1060	qstate->kevent_watermark = 0;
1061	qstate->process_func = on_transform_response_write1;
1062	TRACE_OUT(on_transform_request_process);
1063	return (0);
1064}
1065
1066static int
1067on_transform_response_write1(struct query_state *qstate)
1068{
1069	struct cache_transform_response	*transform_response;
1070	ssize_t	result;
1071
1072	TRACE_IN(on_transform_response_write1);
1073	transform_response = get_cache_transform_response(&qstate->response);
1074	result = qstate->write_func(qstate, &transform_response->error_code,
1075		sizeof(int));
1076	if (result != sizeof(int)) {
1077		TRACE_OUT(on_transform_response_write1);
1078		return (-1);
1079	}
1080
1081	finalize_comm_element(&qstate->request);
1082	finalize_comm_element(&qstate->response);
1083
1084	qstate->kevent_watermark = 0;
1085	qstate->process_func = NULL;
1086	TRACE_OUT(on_transform_response_write1);
1087	return (0);
1088}
1089
1090/*
1091 * Checks if the client's euid and egid do not differ from its uid and gid.
1092 * Returns 0 on success.
1093 */
1094int
1095check_query_eids(struct query_state *qstate)
1096{
1097
1098	return ((qstate->uid != qstate->euid) || (qstate->gid != qstate->egid) ? -1 : 0);
1099}
1100
1101/*
1102 * Uses the qstate fields to process an "alternate" read - when the buffer is
1103 * too large to be received during one socket read operation
1104 */
1105ssize_t
1106query_io_buffer_read(struct query_state *qstate, void *buf, size_t nbytes)
1107{
1108	size_t remaining;
1109	ssize_t	result;
1110
1111	TRACE_IN(query_io_buffer_read);
1112	if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
1113		return (-1);
1114
1115	assert(qstate->io_buffer_p <=
1116		qstate->io_buffer + qstate->io_buffer_size);
1117	remaining = qstate->io_buffer + qstate->io_buffer_size -
1118		qstate->io_buffer_p;
1119	if (nbytes < remaining)
1120		result = nbytes;
1121	else
1122		result = remaining;
1123
1124	memcpy(buf, qstate->io_buffer_p, result);
1125	qstate->io_buffer_p += result;
1126
1127	if (remaining == 0) {
1128		free(qstate->io_buffer);
1129		qstate->io_buffer = NULL;
1130
1131		qstate->write_func = query_socket_write;
1132		qstate->read_func = query_socket_read;
1133	}
1134
1135	TRACE_OUT(query_io_buffer_read);
1136	return (result);
1137}
1138
1139/*
1140 * Uses the qstate fields to process an "alternate" write - when the buffer is
1141 * too large to be sent during one socket write operation
1142 */
1143ssize_t
1144query_io_buffer_write(struct query_state *qstate, const void *buf,
1145	size_t nbytes)
1146{
1147	size_t remaining;
1148	ssize_t	result;
1149
1150	TRACE_IN(query_io_buffer_write);
1151	if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
1152		return (-1);
1153
1154	assert(qstate->io_buffer_p <=
1155		qstate->io_buffer + qstate->io_buffer_size);
1156	remaining = qstate->io_buffer + qstate->io_buffer_size -
1157		qstate->io_buffer_p;
1158	if (nbytes < remaining)
1159		result = nbytes;
1160	else
1161		result = remaining;
1162
1163	memcpy(qstate->io_buffer_p, buf, result);
1164	qstate->io_buffer_p += result;
1165
1166	if (remaining == 0) {
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 < 0 || (size_t)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 < 0 || (size_t)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 = calloc(1, sizeof(*retval));
1232	assert(retval != NULL);
1233
1234	retval->sockfd = sockfd;
1235	retval->kevent_filter = EVFILT_READ;
1236	retval->kevent_watermark = kevent_watermark;
1237
1238	retval->euid = euid;
1239	retval->egid = egid;
1240	retval->uid = retval->gid = -1;
1241
1242	if (asprintf(&retval->eid_str, "%d_%d_", retval->euid,
1243		retval->egid) == -1) {
1244		free(retval);
1245		return (NULL);
1246	}
1247	retval->eid_str_length = strlen(retval->eid_str);
1248
1249	init_comm_element(&retval->request, CET_UNDEFINED);
1250	init_comm_element(&retval->response, CET_UNDEFINED);
1251	retval->process_func = on_query_startup;
1252	retval->destroy_func = on_query_destroy;
1253
1254	retval->write_func = query_socket_write;
1255	retval->read_func = query_socket_read;
1256
1257	get_time_func(&retval->creation_time);
1258	retval->timeout.tv_sec = s_configuration->query_timeout;
1259	retval->timeout.tv_usec = 0;
1260
1261	TRACE_OUT(init_query_state);
1262	return (retval);
1263}
1264
1265void
1266destroy_query_state(struct query_state *qstate)
1267{
1268
1269	TRACE_IN(destroy_query_state);
1270	if (qstate->eid_str != NULL)
1271	    free(qstate->eid_str);
1272
1273	if (qstate->io_buffer != NULL)
1274		free(qstate->io_buffer);
1275
1276	qstate->destroy_func(qstate);
1277	free(qstate);
1278	TRACE_OUT(destroy_query_state);
1279}
1280