xref: /freebsd/usr.sbin/nscd/query.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
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 
49 static const char negative_data[1] = { 0 };
50 
51 extern	void get_time_func(struct timeval *);
52 
53 static 	void clear_config_entry(struct configuration_entry *);
54 static 	void clear_config_entry_part(struct configuration_entry *,
55 	const char *, size_t);
56 
57 static	int on_query_startup(struct query_state *);
58 static	void on_query_destroy(struct query_state *);
59 
60 static	int on_read_request_read1(struct query_state *);
61 static	int on_read_request_read2(struct query_state *);
62 static	int on_read_request_process(struct query_state *);
63 static	int on_read_response_write1(struct query_state *);
64 static	int on_read_response_write2(struct query_state *);
65 
66 static	int on_rw_mapper(struct query_state *);
67 
68 static	int on_transform_request_read1(struct query_state *);
69 static	int on_transform_request_read2(struct query_state *);
70 static	int on_transform_request_process(struct query_state *);
71 static	int on_transform_response_write1(struct query_state *);
72 
73 static	int on_write_request_read1(struct query_state *);
74 static	int on_write_request_read2(struct query_state *);
75 static	int on_negative_write_request_process(struct query_state *);
76 static	int on_write_request_process(struct query_state *);
77 static	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  */
83 static void
84 clear_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  */
117 static void
118 clear_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  */
152 static int
153 on_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  */
247 static int
248 on_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  */
285 static void
286 on_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  */
302 static int
303 on_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 
362 static int
363 on_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 
394 static	int
395 on_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 
458 fin:
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 
467 static int
468 on_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 
538 fin:
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 
547 static int
548 on_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  */
579 static int
580 on_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 
628 static int
629 on_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 
656 static int
657 on_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 
817 fin:
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 
829 static int
830 on_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 
865 static int
866 on_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  */
899 static int
900 on_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 
950 static int
951 on_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 
974 static int
975 on_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 
1059 fin:
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 
1066 static int
1067 on_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  */
1094 int
1095 check_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  */
1105 ssize_t
1106 query_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  */
1143 ssize_t
1144 query_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  */
1181 ssize_t
1182 query_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  */
1203 ssize_t
1204 query_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  */
1225 struct query_state *
1226 init_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 
1265 void
1266 destroy_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