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