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