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