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 * negative entries) and also for all multipart entries.
82 */
83 static void
clear_config_entry(struct configuration_entry * config_entry)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
clear_config_entry_part(struct configuration_entry * config_entry,const char * eid_str,size_t eid_str_length)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
on_query_startup(struct query_state * qstate)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
on_rw_mapper(struct query_state * qstate)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
on_query_destroy(struct query_state * qstate)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
on_write_request_read1(struct query_state * qstate)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
on_write_request_read2(struct query_state * qstate)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
on_write_request_process(struct query_state * qstate)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
on_negative_write_request_process(struct query_state * qstate)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
on_write_response_write1(struct query_state * qstate)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
on_read_request_read1(struct query_state * qstate)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
on_read_request_read2(struct query_state * qstate)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
on_read_request_process(struct query_state * qstate)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
on_read_response_write1(struct query_state * qstate)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
on_read_response_write2(struct query_state * qstate)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
on_transform_request_read1(struct query_state * qstate)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
on_transform_request_read2(struct query_state * qstate)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
on_transform_request_process(struct query_state * qstate)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
on_transform_response_write1(struct query_state * qstate)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
check_query_eids(struct query_state * qstate)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
query_io_buffer_read(struct query_state * qstate,void * buf,size_t nbytes)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
query_io_buffer_write(struct query_state * qstate,const void * buf,size_t nbytes)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
query_socket_read(struct query_state * qstate,void * buf,size_t nbytes)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
query_socket_write(struct query_state * qstate,const void * buf,size_t nbytes)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 *
init_query_state(int sockfd,size_t kevent_watermark,uid_t euid,gid_t egid)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
destroy_query_state(struct query_state * qstate)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 }
1280