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 } 1280