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