]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/nscd/query.c
MFC r322124:
[FreeBSD/stable/10.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         struct {
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 = (caddr_t)&cmsg;
175         cred_hdr.msg_controllen = CMSG_LEN(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 (cmsg.hdr.cmsg_len < CMSG_LEN(sizeof(struct cmsgcred))
187                 || cmsg.hdr.cmsg_level != SOL_SOCKET
188                 || cmsg.hdr.cmsg_type != SCM_CREDS) {
189                 TRACE_OUT(on_query_startup);
190                 return (-1);
191         }
192
193         cred = (struct cmsgcred *)CMSG_DATA(&cmsg);
194         qstate->uid = cred->cmcred_uid;
195         qstate->gid = cred->cmcred_gid;
196
197 #if defined(NS_NSCD_EID_CHECKING) || defined(NS_STRICT_NSCD_EID_CHECKING)
198 /*
199  * This check is probably a bit redundant - per-user cache is always separated
200  * by the euid/egid pair
201  */
202         if (check_query_eids(qstate) != 0) {
203 #ifdef NS_STRICT_NSCD_EID_CHECKING
204                 TRACE_OUT(on_query_startup);
205                 return (-1);
206 #else
207                 if ((elem_type != CET_READ_REQUEST) &&
208                         (elem_type != CET_MP_READ_SESSION_REQUEST) &&
209                         (elem_type != CET_WRITE_REQUEST) &&
210                         (elem_type != CET_MP_WRITE_SESSION_REQUEST)) {
211                         TRACE_OUT(on_query_startup);
212                         return (-1);
213                 }
214 #endif
215         }
216 #endif
217
218         switch (elem_type) {
219         case CET_WRITE_REQUEST:
220                 qstate->process_func = on_write_request_read1;
221                 break;
222         case CET_READ_REQUEST:
223                 qstate->process_func = on_read_request_read1;
224                 break;
225         case CET_TRANSFORM_REQUEST:
226                 qstate->process_func = on_transform_request_read1;
227                 break;
228         case CET_MP_WRITE_SESSION_REQUEST:
229                 qstate->process_func = on_mp_write_session_request_read1;
230                 break;
231         case CET_MP_READ_SESSION_REQUEST:
232                 qstate->process_func = on_mp_read_session_request_read1;
233                 break;
234         default:
235                 TRACE_OUT(on_query_startup);
236                 return (-1);
237         }
238
239         qstate->kevent_watermark = 0;
240         TRACE_OUT(on_query_startup);
241         return (0);
242 }
243
244 /*
245  * on_rw_mapper is used to process multiple read/write requests during
246  * one connection session. It's never called in the beginning (on query_state
247  * creation) as it does not process the multipart requests and does not
248  * receive credentials
249  */
250 static int
251 on_rw_mapper(struct query_state *qstate)
252 {
253         ssize_t result;
254         int     elem_type;
255
256         TRACE_IN(on_rw_mapper);
257         if (qstate->kevent_watermark == 0) {
258                 qstate->kevent_watermark = sizeof(int);
259         } else {
260                 result = qstate->read_func(qstate, &elem_type, sizeof(int));
261                 if (result != sizeof(int)) {
262                         TRACE_OUT(on_rw_mapper);
263                         return (-1);
264                 }
265
266                 switch (elem_type) {
267                 case CET_WRITE_REQUEST:
268                         qstate->kevent_watermark = sizeof(size_t);
269                         qstate->process_func = on_write_request_read1;
270                 break;
271                 case CET_READ_REQUEST:
272                         qstate->kevent_watermark = sizeof(size_t);
273                         qstate->process_func = on_read_request_read1;
274                 break;
275                 default:
276                         TRACE_OUT(on_rw_mapper);
277                         return (-1);
278                 break;
279                 }
280         }
281         TRACE_OUT(on_rw_mapper);
282         return (0);
283 }
284
285 /*
286  * The default query_destroy function
287  */
288 static void
289 on_query_destroy(struct query_state *qstate)
290 {
291
292         TRACE_IN(on_query_destroy);
293         finalize_comm_element(&qstate->response);
294         finalize_comm_element(&qstate->request);
295         TRACE_OUT(on_query_destroy);
296 }
297
298 /*
299  * The functions below are used to process write requests.
300  * - on_write_request_read1 and on_write_request_read2 read the request itself
301  * - on_write_request_process processes it (if the client requests to
302  *    cache the negative result, the on_negative_write_request_process is used)
303  * - on_write_response_write1 sends the response
304  */
305 static int
306 on_write_request_read1(struct query_state *qstate)
307 {
308         struct cache_write_request      *write_request;
309         ssize_t result;
310
311         TRACE_IN(on_write_request_read1);
312         if (qstate->kevent_watermark == 0)
313                 qstate->kevent_watermark = sizeof(size_t) * 3;
314         else {
315                 init_comm_element(&qstate->request, CET_WRITE_REQUEST);
316                 write_request = get_cache_write_request(&qstate->request);
317
318                 result = qstate->read_func(qstate, &write_request->entry_length,
319                         sizeof(size_t));
320                 result += qstate->read_func(qstate,
321                         &write_request->cache_key_size, sizeof(size_t));
322                 result += qstate->read_func(qstate,
323                         &write_request->data_size, sizeof(size_t));
324
325                 if (result != sizeof(size_t) * 3) {
326                         TRACE_OUT(on_write_request_read1);
327                         return (-1);
328                 }
329
330                 if (BUFSIZE_INVALID(write_request->entry_length) ||
331                         BUFSIZE_INVALID(write_request->cache_key_size) ||
332                         (BUFSIZE_INVALID(write_request->data_size) &&
333                         (write_request->data_size != 0))) {
334                         TRACE_OUT(on_write_request_read1);
335                         return (-1);
336                 }
337
338                 write_request->entry = calloc(1,
339                         write_request->entry_length + 1);
340                 assert(write_request->entry != NULL);
341
342                 write_request->cache_key = calloc(1,
343                         write_request->cache_key_size +
344                         qstate->eid_str_length);
345                 assert(write_request->cache_key != NULL);
346                 memcpy(write_request->cache_key, qstate->eid_str,
347                         qstate->eid_str_length);
348
349                 if (write_request->data_size != 0) {
350                         write_request->data = calloc(1,
351                                 write_request->data_size);
352                         assert(write_request->data != NULL);
353                 }
354
355                 qstate->kevent_watermark = write_request->entry_length +
356                         write_request->cache_key_size +
357                         write_request->data_size;
358                 qstate->process_func = on_write_request_read2;
359         }
360
361         TRACE_OUT(on_write_request_read1);
362         return (0);
363 }
364
365 static int
366 on_write_request_read2(struct query_state *qstate)
367 {
368         struct cache_write_request      *write_request;
369         ssize_t result;
370
371         TRACE_IN(on_write_request_read2);
372         write_request = get_cache_write_request(&qstate->request);
373
374         result = qstate->read_func(qstate, write_request->entry,
375                 write_request->entry_length);
376         result += qstate->read_func(qstate, write_request->cache_key +
377                 qstate->eid_str_length, write_request->cache_key_size);
378         if (write_request->data_size != 0)
379                 result += qstate->read_func(qstate, write_request->data,
380                         write_request->data_size);
381
382         if (result != (ssize_t)qstate->kevent_watermark) {
383                 TRACE_OUT(on_write_request_read2);
384                 return (-1);
385         }
386         write_request->cache_key_size += qstate->eid_str_length;
387
388         qstate->kevent_watermark = 0;
389         if (write_request->data_size != 0)
390                 qstate->process_func = on_write_request_process;
391         else
392                 qstate->process_func = on_negative_write_request_process;
393         TRACE_OUT(on_write_request_read2);
394         return (0);
395 }
396
397 static  int
398 on_write_request_process(struct query_state *qstate)
399 {
400         struct cache_write_request      *write_request;
401         struct cache_write_response     *write_response;
402         cache_entry c_entry;
403
404         TRACE_IN(on_write_request_process);
405         init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
406         write_response = get_cache_write_response(&qstate->response);
407         write_request = get_cache_write_request(&qstate->request);
408
409         qstate->config_entry = configuration_find_entry(
410                 s_configuration, write_request->entry);
411
412         if (qstate->config_entry == NULL) {
413                 write_response->error_code = ENOENT;
414
415                 LOG_ERR_2("write_request", "can't find configuration"
416                     " entry '%s'. aborting request", write_request->entry);
417                 goto fin;
418         }
419
420         if (qstate->config_entry->enabled == 0) {
421                 write_response->error_code = EACCES;
422
423                 LOG_ERR_2("write_request",
424                         "configuration entry '%s' is disabled",
425                         write_request->entry);
426                 goto fin;
427         }
428
429         if (qstate->config_entry->perform_actual_lookups != 0) {
430                 write_response->error_code = EOPNOTSUPP;
431
432                 LOG_ERR_2("write_request",
433                         "entry '%s' performs lookups by itself: "
434                         "can't write to it", write_request->entry);
435                 goto fin;
436         }
437
438         configuration_lock_rdlock(s_configuration);
439         c_entry = find_cache_entry(s_cache,
440                 qstate->config_entry->positive_cache_params.cep.entry_name);
441         configuration_unlock(s_configuration);
442         if (c_entry != NULL) {
443                 configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
444                 qstate->config_entry->positive_cache_entry = c_entry;
445                 write_response->error_code = cache_write(c_entry,
446                         write_request->cache_key,
447                         write_request->cache_key_size,
448                         write_request->data,
449                         write_request->data_size);
450                 configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
451
452                 if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
453                     (qstate->config_entry->common_query_timeout.tv_usec != 0))
454                         memcpy(&qstate->timeout,
455                                 &qstate->config_entry->common_query_timeout,
456                                 sizeof(struct timeval));
457
458         } else
459                 write_response->error_code = -1;
460
461 fin:
462         qstate->kevent_filter = EVFILT_WRITE;
463         qstate->kevent_watermark = sizeof(int);
464         qstate->process_func = on_write_response_write1;
465
466         TRACE_OUT(on_write_request_process);
467         return (0);
468 }
469
470 static int
471 on_negative_write_request_process(struct query_state *qstate)
472 {
473         struct cache_write_request      *write_request;
474         struct cache_write_response     *write_response;
475         cache_entry c_entry;
476
477         TRACE_IN(on_negative_write_request_process);
478         init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
479         write_response = get_cache_write_response(&qstate->response);
480         write_request = get_cache_write_request(&qstate->request);
481
482         qstate->config_entry = configuration_find_entry (
483                 s_configuration, write_request->entry);
484
485         if (qstate->config_entry == NULL) {
486                 write_response->error_code = ENOENT;
487
488                 LOG_ERR_2("negative_write_request",
489                         "can't find configuration"
490                         " entry '%s'. aborting request", write_request->entry);
491                 goto fin;
492         }
493
494         if (qstate->config_entry->enabled == 0) {
495                 write_response->error_code = EACCES;
496
497                 LOG_ERR_2("negative_write_request",
498                         "configuration entry '%s' is disabled",
499                         write_request->entry);
500                 goto fin;
501         }
502
503         if (qstate->config_entry->perform_actual_lookups != 0) {
504                 write_response->error_code = EOPNOTSUPP;
505
506                 LOG_ERR_2("negative_write_request",
507                         "entry '%s' performs lookups by itself: "
508                         "can't write to it", write_request->entry);
509                 goto fin;
510         } else {
511 #ifdef NS_NSCD_EID_CHECKING
512                 if (check_query_eids(qstate) != 0) {
513                         write_response->error_code = EPERM;
514                         goto fin;
515                 }
516 #endif
517         }
518
519         configuration_lock_rdlock(s_configuration);
520         c_entry = find_cache_entry(s_cache,
521                 qstate->config_entry->negative_cache_params.cep.entry_name);
522         configuration_unlock(s_configuration);
523         if (c_entry != NULL) {
524                 configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
525                 qstate->config_entry->negative_cache_entry = c_entry;
526                 write_response->error_code = cache_write(c_entry,
527                         write_request->cache_key,
528                         write_request->cache_key_size,
529                         negative_data,
530                         sizeof(negative_data));
531                 configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
532
533                 if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
534                     (qstate->config_entry->common_query_timeout.tv_usec != 0))
535                         memcpy(&qstate->timeout,
536                                 &qstate->config_entry->common_query_timeout,
537                                 sizeof(struct timeval));
538         } else
539                 write_response->error_code = -1;
540
541 fin:
542         qstate->kevent_filter = EVFILT_WRITE;
543         qstate->kevent_watermark = sizeof(int);
544         qstate->process_func = on_write_response_write1;
545
546         TRACE_OUT(on_negative_write_request_process);
547         return (0);
548 }
549
550 static int
551 on_write_response_write1(struct query_state *qstate)
552 {
553         struct cache_write_response     *write_response;
554         ssize_t result;
555
556         TRACE_IN(on_write_response_write1);
557         write_response = get_cache_write_response(&qstate->response);
558         result = qstate->write_func(qstate, &write_response->error_code,
559                 sizeof(int));
560         if (result != sizeof(int)) {
561                 TRACE_OUT(on_write_response_write1);
562                 return (-1);
563         }
564
565         finalize_comm_element(&qstate->request);
566         finalize_comm_element(&qstate->response);
567
568         qstate->kevent_watermark = sizeof(int);
569         qstate->kevent_filter = EVFILT_READ;
570         qstate->process_func = on_rw_mapper;
571
572         TRACE_OUT(on_write_response_write1);
573         return (0);
574 }
575
576 /*
577  * The functions below are used to process read requests.
578  * - on_read_request_read1 and on_read_request_read2 read the request itself
579  * - on_read_request_process processes it
580  * - on_read_response_write1 and on_read_response_write2 send the response
581  */
582 static int
583 on_read_request_read1(struct query_state *qstate)
584 {
585         struct cache_read_request *read_request;
586         ssize_t result;
587
588         TRACE_IN(on_read_request_read1);
589         if (qstate->kevent_watermark == 0)
590                 qstate->kevent_watermark = sizeof(size_t) * 2;
591         else {
592                 init_comm_element(&qstate->request, CET_READ_REQUEST);
593                 read_request = get_cache_read_request(&qstate->request);
594
595                 result = qstate->read_func(qstate,
596                         &read_request->entry_length, sizeof(size_t));
597                 result += qstate->read_func(qstate,
598                         &read_request->cache_key_size, sizeof(size_t));
599
600                 if (result != sizeof(size_t) * 2) {
601                         TRACE_OUT(on_read_request_read1);
602                         return (-1);
603                 }
604
605                 if (BUFSIZE_INVALID(read_request->entry_length) ||
606                         BUFSIZE_INVALID(read_request->cache_key_size)) {
607                         TRACE_OUT(on_read_request_read1);
608                         return (-1);
609                 }
610
611                 read_request->entry = calloc(1,
612                         read_request->entry_length + 1);
613                 assert(read_request->entry != NULL);
614
615                 read_request->cache_key = calloc(1,
616                         read_request->cache_key_size +
617                         qstate->eid_str_length);
618                 assert(read_request->cache_key != NULL);
619                 memcpy(read_request->cache_key, qstate->eid_str,
620                         qstate->eid_str_length);
621
622                 qstate->kevent_watermark = read_request->entry_length +
623                         read_request->cache_key_size;
624                 qstate->process_func = on_read_request_read2;
625         }
626
627         TRACE_OUT(on_read_request_read1);
628         return (0);
629 }
630
631 static int
632 on_read_request_read2(struct query_state *qstate)
633 {
634         struct cache_read_request       *read_request;
635         ssize_t result;
636
637         TRACE_IN(on_read_request_read2);
638         read_request = get_cache_read_request(&qstate->request);
639
640         result = qstate->read_func(qstate, read_request->entry,
641                 read_request->entry_length);
642         result += qstate->read_func(qstate,
643                 read_request->cache_key + qstate->eid_str_length,
644                 read_request->cache_key_size);
645
646         if (result != (ssize_t)qstate->kevent_watermark) {
647                 TRACE_OUT(on_read_request_read2);
648                 return (-1);
649         }
650         read_request->cache_key_size += qstate->eid_str_length;
651
652         qstate->kevent_watermark = 0;
653         qstate->process_func = on_read_request_process;
654
655         TRACE_OUT(on_read_request_read2);
656         return (0);
657 }
658
659 static int
660 on_read_request_process(struct query_state *qstate)
661 {
662         struct cache_read_request *read_request;
663         struct cache_read_response *read_response;
664         cache_entry     c_entry, neg_c_entry;
665
666         struct agent    *lookup_agent;
667         struct common_agent *c_agent;
668         int res;
669
670         TRACE_IN(on_read_request_process);
671         init_comm_element(&qstate->response, CET_READ_RESPONSE);
672         read_response = get_cache_read_response(&qstate->response);
673         read_request = get_cache_read_request(&qstate->request);
674
675         qstate->config_entry = configuration_find_entry(
676                 s_configuration, read_request->entry);
677         if (qstate->config_entry == NULL) {
678                 read_response->error_code = ENOENT;
679
680                 LOG_ERR_2("read_request",
681                         "can't find configuration "
682                         "entry '%s'. aborting request", read_request->entry);
683                 goto fin;
684         }
685
686         if (qstate->config_entry->enabled == 0) {
687                 read_response->error_code = EACCES;
688
689                 LOG_ERR_2("read_request",
690                         "configuration entry '%s' is disabled",
691                         read_request->entry);
692                 goto fin;
693         }
694
695         /*
696          * if we perform lookups by ourselves, then we don't need to separate
697          * cache entries by euid and egid
698          */
699         if (qstate->config_entry->perform_actual_lookups != 0)
700                 memset(read_request->cache_key, 0, qstate->eid_str_length);
701         else {
702 #ifdef NS_NSCD_EID_CHECKING
703                 if (check_query_eids(qstate) != 0) {
704                 /* if the lookup is not self-performing, we check for clients euid/egid */
705                         read_response->error_code = EPERM;
706                         goto fin;
707                 }
708 #endif
709         }
710
711         configuration_lock_rdlock(s_configuration);
712         c_entry = find_cache_entry(s_cache,
713                 qstate->config_entry->positive_cache_params.cep.entry_name);
714         neg_c_entry = find_cache_entry(s_cache,
715                 qstate->config_entry->negative_cache_params.cep.entry_name);
716         configuration_unlock(s_configuration);
717         if ((c_entry != NULL) && (neg_c_entry != NULL)) {
718                 configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
719                 qstate->config_entry->positive_cache_entry = c_entry;
720                 read_response->error_code = cache_read(c_entry,
721                         read_request->cache_key,
722                         read_request->cache_key_size, NULL,
723                         &read_response->data_size);
724
725                 if (read_response->error_code == -2) {
726                         read_response->data = malloc(
727                                 read_response->data_size);
728                         assert(read_response->data != NULL);
729                         read_response->error_code = cache_read(c_entry,
730                                 read_request->cache_key,
731                                 read_request->cache_key_size,
732                                 read_response->data,
733                                 &read_response->data_size);
734                 }
735                 configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
736
737                 configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
738                 qstate->config_entry->negative_cache_entry = neg_c_entry;
739                 if (read_response->error_code == -1) {
740                         read_response->error_code = cache_read(neg_c_entry,
741                                 read_request->cache_key,
742                                 read_request->cache_key_size, NULL,
743                                 &read_response->data_size);
744
745                         if (read_response->error_code == -2) {
746                                 read_response->data = malloc(
747                                         read_response->data_size);
748                                 assert(read_response->data != NULL);
749                                 read_response->error_code = cache_read(neg_c_entry,
750                                         read_request->cache_key,
751                                         read_request->cache_key_size,
752                                         read_response->data,
753                                         &read_response->data_size);
754                         }
755                 }
756                 configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
757
758                 if ((read_response->error_code == -1) &&
759                         (qstate->config_entry->perform_actual_lookups != 0)) {
760                         free(read_response->data);
761                         read_response->data = NULL;
762                         read_response->data_size = 0;
763
764                         lookup_agent = find_agent(s_agent_table,
765                                 read_request->entry, COMMON_AGENT);
766
767                         if ((lookup_agent != NULL) &&
768                         (lookup_agent->type == COMMON_AGENT)) {
769                                 c_agent = (struct common_agent *)lookup_agent;
770                                 res = c_agent->lookup_func(
771                                         read_request->cache_key +
772                                                 qstate->eid_str_length,
773                                         read_request->cache_key_size -
774                                                 qstate->eid_str_length,
775                                         &read_response->data,
776                                         &read_response->data_size);
777
778                                 if (res == NS_SUCCESS) {
779                                         read_response->error_code = 0;
780                                         configuration_lock_entry(
781                                                 qstate->config_entry,
782                                                 CELT_POSITIVE);
783                                         cache_write(c_entry,
784                                                 read_request->cache_key,
785                                                 read_request->cache_key_size,
786                                                 read_response->data,
787                                                 read_response->data_size);
788                                         configuration_unlock_entry(
789                                                 qstate->config_entry,
790                                                 CELT_POSITIVE);
791                                 } else if ((res == NS_NOTFOUND) ||
792                                           (res == NS_RETURN)) {
793                                         configuration_lock_entry(
794                                                   qstate->config_entry,
795                                                   CELT_NEGATIVE);
796                                         cache_write(neg_c_entry,
797                                                 read_request->cache_key,
798                                                 read_request->cache_key_size,
799                                                 negative_data,
800                                                 sizeof(negative_data));
801                                         configuration_unlock_entry(
802                                                   qstate->config_entry,
803                                                   CELT_NEGATIVE);
804
805                                         read_response->error_code = 0;
806                                         read_response->data = NULL;
807                                         read_response->data_size = 0;
808                                 }
809                         }
810                 }
811
812                 if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
813                     (qstate->config_entry->common_query_timeout.tv_usec != 0))
814                         memcpy(&qstate->timeout,
815                                 &qstate->config_entry->common_query_timeout,
816                                 sizeof(struct timeval));
817         } else
818                 read_response->error_code = -1;
819
820 fin:
821         qstate->kevent_filter = EVFILT_WRITE;
822         if (read_response->error_code == 0)
823                 qstate->kevent_watermark = sizeof(int) + sizeof(size_t);
824         else
825                 qstate->kevent_watermark = sizeof(int);
826         qstate->process_func = on_read_response_write1;
827
828         TRACE_OUT(on_read_request_process);
829         return (0);
830 }
831
832 static int
833 on_read_response_write1(struct query_state *qstate)
834 {
835         struct cache_read_response      *read_response;
836         ssize_t result;
837
838         TRACE_IN(on_read_response_write1);
839         read_response = get_cache_read_response(&qstate->response);
840
841         result = qstate->write_func(qstate, &read_response->error_code,
842                 sizeof(int));
843
844         if (read_response->error_code == 0) {
845                 result += qstate->write_func(qstate, &read_response->data_size,
846                         sizeof(size_t));
847                 if (result != (ssize_t)qstate->kevent_watermark) {
848                         TRACE_OUT(on_read_response_write1);
849                         return (-1);
850                 }
851
852                 qstate->kevent_watermark = read_response->data_size;
853                 qstate->process_func = on_read_response_write2;
854         } else {
855                 if (result != (ssize_t)qstate->kevent_watermark) {
856                         TRACE_OUT(on_read_response_write1);
857                         return (-1);
858                 }
859
860                 qstate->kevent_watermark = 0;
861                 qstate->process_func = NULL;
862         }
863
864         TRACE_OUT(on_read_response_write1);
865         return (0);
866 }
867
868 static int
869 on_read_response_write2(struct query_state *qstate)
870 {
871         struct cache_read_response      *read_response;
872         ssize_t result;
873
874         TRACE_IN(on_read_response_write2);
875         read_response = get_cache_read_response(&qstate->response);
876         if (read_response->data_size > 0) {
877                 result = qstate->write_func(qstate, read_response->data,
878                         read_response->data_size);
879                 if (result != (ssize_t)qstate->kevent_watermark) {
880                         TRACE_OUT(on_read_response_write2);
881                         return (-1);
882                 }
883         }
884
885         finalize_comm_element(&qstate->request);
886         finalize_comm_element(&qstate->response);
887
888         qstate->kevent_watermark = sizeof(int);
889         qstate->kevent_filter = EVFILT_READ;
890         qstate->process_func = on_rw_mapper;
891         TRACE_OUT(on_read_response_write2);
892         return (0);
893 }
894
895 /*
896  * The functions below are used to process write requests.
897  * - on_transform_request_read1 and on_transform_request_read2 read the
898  *   request itself
899  * - on_transform_request_process processes it
900  * - on_transform_response_write1 sends the response
901  */
902 static int
903 on_transform_request_read1(struct query_state *qstate)
904 {
905         struct cache_transform_request *transform_request;
906         ssize_t result;
907
908         TRACE_IN(on_transform_request_read1);
909         if (qstate->kevent_watermark == 0)
910                 qstate->kevent_watermark = sizeof(size_t) + sizeof(int);
911         else {
912                 init_comm_element(&qstate->request, CET_TRANSFORM_REQUEST);
913                 transform_request =
914                         get_cache_transform_request(&qstate->request);
915
916                 result = qstate->read_func(qstate,
917                         &transform_request->entry_length, sizeof(size_t));
918                 result += qstate->read_func(qstate,
919                         &transform_request->transformation_type, sizeof(int));
920
921                 if (result != sizeof(size_t) + sizeof(int)) {
922                         TRACE_OUT(on_transform_request_read1);
923                         return (-1);
924                 }
925
926                 if ((transform_request->transformation_type != TT_USER) &&
927                     (transform_request->transformation_type != TT_ALL)) {
928                         TRACE_OUT(on_transform_request_read1);
929                         return (-1);
930                 }
931
932                 if (transform_request->entry_length != 0) {
933                         if (BUFSIZE_INVALID(transform_request->entry_length)) {
934                                 TRACE_OUT(on_transform_request_read1);
935                                 return (-1);
936                         }
937
938                         transform_request->entry = calloc(1,
939                                 transform_request->entry_length + 1);
940                         assert(transform_request->entry != NULL);
941
942                         qstate->process_func = on_transform_request_read2;
943                 } else
944                         qstate->process_func = on_transform_request_process;
945
946                 qstate->kevent_watermark = transform_request->entry_length;
947         }
948
949         TRACE_OUT(on_transform_request_read1);
950         return (0);
951 }
952
953 static int
954 on_transform_request_read2(struct query_state *qstate)
955 {
956         struct cache_transform_request  *transform_request;
957         ssize_t result;
958
959         TRACE_IN(on_transform_request_read2);
960         transform_request = get_cache_transform_request(&qstate->request);
961
962         result = qstate->read_func(qstate, transform_request->entry,
963                 transform_request->entry_length);
964
965         if (result != (ssize_t)qstate->kevent_watermark) {
966                 TRACE_OUT(on_transform_request_read2);
967                 return (-1);
968         }
969
970         qstate->kevent_watermark = 0;
971         qstate->process_func = on_transform_request_process;
972
973         TRACE_OUT(on_transform_request_read2);
974         return (0);
975 }
976
977 static int
978 on_transform_request_process(struct query_state *qstate)
979 {
980         struct cache_transform_request *transform_request;
981         struct cache_transform_response *transform_response;
982         struct configuration_entry *config_entry;
983         size_t  i, size;
984
985         TRACE_IN(on_transform_request_process);
986         init_comm_element(&qstate->response, CET_TRANSFORM_RESPONSE);
987         transform_response = get_cache_transform_response(&qstate->response);
988         transform_request = get_cache_transform_request(&qstate->request);
989
990         switch (transform_request->transformation_type) {
991         case TT_USER:
992                 if (transform_request->entry == NULL) {
993                         size = configuration_get_entries_size(s_configuration);
994                         for (i = 0; i < size; ++i) {
995                             config_entry = configuration_get_entry(
996                                 s_configuration, i);
997
998                             if (config_entry->perform_actual_lookups == 0)
999                                 clear_config_entry_part(config_entry,
1000                                     qstate->eid_str, qstate->eid_str_length);
1001                         }
1002                 } else {
1003                         qstate->config_entry = configuration_find_entry(
1004                                 s_configuration, transform_request->entry);
1005
1006                         if (qstate->config_entry == NULL) {
1007                                 LOG_ERR_2("transform_request",
1008                                         "can't find configuration"
1009                                         " entry '%s'. aborting request",
1010                                         transform_request->entry);
1011                                 transform_response->error_code = -1;
1012                                 goto fin;
1013                         }
1014
1015                         if (qstate->config_entry->perform_actual_lookups != 0) {
1016                                 LOG_ERR_2("transform_request",
1017                                         "can't transform the cache entry %s"
1018                                         ", because it ised for actual lookups",
1019                                         transform_request->entry);
1020                                 transform_response->error_code = -1;
1021                                 goto fin;
1022                         }
1023
1024                         clear_config_entry_part(qstate->config_entry,
1025                                 qstate->eid_str, qstate->eid_str_length);
1026                 }
1027                 break;
1028         case TT_ALL:
1029                 if (qstate->euid != 0)
1030                         transform_response->error_code = -1;
1031                 else {
1032                         if (transform_request->entry == NULL) {
1033                                 size = configuration_get_entries_size(
1034                                         s_configuration);
1035                                 for (i = 0; i < size; ++i) {
1036                                     clear_config_entry(
1037                                         configuration_get_entry(
1038                                                 s_configuration, i));
1039                                 }
1040                         } else {
1041                                 qstate->config_entry = configuration_find_entry(
1042                                         s_configuration,
1043                                         transform_request->entry);
1044
1045                                 if (qstate->config_entry == NULL) {
1046                                         LOG_ERR_2("transform_request",
1047                                                 "can't find configuration"
1048                                                 " entry '%s'. aborting request",
1049                                                 transform_request->entry);
1050                                         transform_response->error_code = -1;
1051                                         goto fin;
1052                                 }
1053
1054                                 clear_config_entry(qstate->config_entry);
1055                         }
1056                 }
1057                 break;
1058         default:
1059                 transform_response->error_code = -1;
1060         }
1061
1062 fin:
1063         qstate->kevent_watermark = 0;
1064         qstate->process_func = on_transform_response_write1;
1065         TRACE_OUT(on_transform_request_process);
1066         return (0);
1067 }
1068
1069 static int
1070 on_transform_response_write1(struct query_state *qstate)
1071 {
1072         struct cache_transform_response *transform_response;
1073         ssize_t result;
1074
1075         TRACE_IN(on_transform_response_write1);
1076         transform_response = get_cache_transform_response(&qstate->response);
1077         result = qstate->write_func(qstate, &transform_response->error_code,
1078                 sizeof(int));
1079         if (result != sizeof(int)) {
1080                 TRACE_OUT(on_transform_response_write1);
1081                 return (-1);
1082         }
1083
1084         finalize_comm_element(&qstate->request);
1085         finalize_comm_element(&qstate->response);
1086
1087         qstate->kevent_watermark = 0;
1088         qstate->process_func = NULL;
1089         TRACE_OUT(on_transform_response_write1);
1090         return (0);
1091 }
1092
1093 /*
1094  * Checks if the client's euid and egid do not differ from its uid and gid.
1095  * Returns 0 on success.
1096  */
1097 int
1098 check_query_eids(struct query_state *qstate)
1099 {
1100
1101         return ((qstate->uid != qstate->euid) || (qstate->gid != qstate->egid) ? -1 : 0);
1102 }
1103
1104 /*
1105  * Uses the qstate fields to process an "alternate" read - when the buffer is
1106  * too large to be received during one socket read operation
1107  */
1108 ssize_t
1109 query_io_buffer_read(struct query_state *qstate, void *buf, size_t nbytes)
1110 {
1111         size_t remaining;
1112         ssize_t result;
1113
1114         TRACE_IN(query_io_buffer_read);
1115         if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
1116                 return (-1);
1117
1118         assert(qstate->io_buffer_p <=
1119                 qstate->io_buffer + qstate->io_buffer_size);
1120         remaining = qstate->io_buffer + qstate->io_buffer_size -
1121                 qstate->io_buffer_p;
1122         if (nbytes < remaining)
1123                 result = nbytes;
1124         else
1125                 result = remaining;
1126
1127         memcpy(buf, qstate->io_buffer_p, result);
1128         qstate->io_buffer_p += result;
1129
1130         if (remaining == 0) {
1131                 free(qstate->io_buffer);
1132                 qstate->io_buffer = NULL;
1133
1134                 qstate->write_func = query_socket_write;
1135                 qstate->read_func = query_socket_read;
1136         }
1137
1138         TRACE_OUT(query_io_buffer_read);
1139         return (result);
1140 }
1141
1142 /*
1143  * Uses the qstate fields to process an "alternate" write - when the buffer is
1144  * too large to be sent during one socket write operation
1145  */
1146 ssize_t
1147 query_io_buffer_write(struct query_state *qstate, const void *buf,
1148         size_t nbytes)
1149 {
1150         size_t remaining;
1151         ssize_t result;
1152
1153         TRACE_IN(query_io_buffer_write);
1154         if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
1155                 return (-1);
1156
1157         assert(qstate->io_buffer_p <=
1158                 qstate->io_buffer + qstate->io_buffer_size);
1159         remaining = qstate->io_buffer + qstate->io_buffer_size -
1160                 qstate->io_buffer_p;
1161         if (nbytes < remaining)
1162                 result = nbytes;
1163         else
1164                 result = remaining;
1165
1166         memcpy(qstate->io_buffer_p, buf, result);
1167         qstate->io_buffer_p += result;
1168
1169         if (remaining == 0) {
1170                 qstate->use_alternate_io = 1;
1171                 qstate->io_buffer_p = qstate->io_buffer;
1172
1173                 qstate->write_func = query_socket_write;
1174                 qstate->read_func = query_socket_read;
1175         }
1176
1177         TRACE_OUT(query_io_buffer_write);
1178         return (result);
1179 }
1180
1181 /*
1182  * The default "read" function, which reads data directly from socket
1183  */
1184 ssize_t
1185 query_socket_read(struct query_state *qstate, void *buf, size_t nbytes)
1186 {
1187         ssize_t result;
1188
1189         TRACE_IN(query_socket_read);
1190         if (qstate->socket_failed != 0) {
1191                 TRACE_OUT(query_socket_read);
1192                 return (-1);
1193         }
1194
1195         result = read(qstate->sockfd, buf, nbytes);
1196         if (result < 0 || (size_t)result < nbytes)
1197                 qstate->socket_failed = 1;
1198
1199         TRACE_OUT(query_socket_read);
1200         return (result);
1201 }
1202
1203 /*
1204  * The default "write" function, which writes data directly to socket
1205  */
1206 ssize_t
1207 query_socket_write(struct query_state *qstate, const void *buf, size_t nbytes)
1208 {
1209         ssize_t result;
1210
1211         TRACE_IN(query_socket_write);
1212         if (qstate->socket_failed != 0) {
1213                 TRACE_OUT(query_socket_write);
1214                 return (-1);
1215         }
1216
1217         result = write(qstate->sockfd, buf, nbytes);
1218         if (result < 0 || (size_t)result < nbytes)
1219                 qstate->socket_failed = 1;
1220
1221         TRACE_OUT(query_socket_write);
1222         return (result);
1223 }
1224
1225 /*
1226  * Initializes the query_state structure by filling it with the default values.
1227  */
1228 struct query_state *
1229 init_query_state(int sockfd, size_t kevent_watermark, uid_t euid, gid_t egid)
1230 {
1231         struct query_state      *retval;
1232
1233         TRACE_IN(init_query_state);
1234         retval = calloc(1, sizeof(*retval));
1235         assert(retval != NULL);
1236
1237         retval->sockfd = sockfd;
1238         retval->kevent_filter = EVFILT_READ;
1239         retval->kevent_watermark = kevent_watermark;
1240
1241         retval->euid = euid;
1242         retval->egid = egid;
1243         retval->uid = retval->gid = -1;
1244
1245         if (asprintf(&retval->eid_str, "%d_%d_", retval->euid,
1246                 retval->egid) == -1) {
1247                 free(retval);
1248                 return (NULL);
1249         }
1250         retval->eid_str_length = strlen(retval->eid_str);
1251
1252         init_comm_element(&retval->request, CET_UNDEFINED);
1253         init_comm_element(&retval->response, CET_UNDEFINED);
1254         retval->process_func = on_query_startup;
1255         retval->destroy_func = on_query_destroy;
1256
1257         retval->write_func = query_socket_write;
1258         retval->read_func = query_socket_read;
1259
1260         get_time_func(&retval->creation_time);
1261         retval->timeout.tv_sec = s_configuration->query_timeout;
1262         retval->timeout.tv_usec = 0;
1263
1264         TRACE_OUT(init_query_state);
1265         return (retval);
1266 }
1267
1268 void
1269 destroy_query_state(struct query_state *qstate)
1270 {
1271
1272         TRACE_IN(destroy_query_state);
1273         if (qstate->eid_str != NULL)
1274             free(qstate->eid_str);
1275
1276         if (qstate->io_buffer != NULL)
1277                 free(qstate->io_buffer);
1278
1279         qstate->destroy_func(qstate);
1280         free(qstate);
1281         TRACE_OUT(destroy_query_state);
1282 }