1/* 2 * Copyright (c) 2003-2012 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <sys/types.h> 25#include <stdio.h> 26#include <fcntl.h> 27#include <stdlib.h> 28#include <unistd.h> 29#include <sys/socket.h> 30#include <netinet/in.h> 31#include <sys/un.h> 32#include <sys/ipc.h> 33#include <signal.h> 34#include <mach/mach.h> 35#include <mach/mach_time.h> 36#include <errno.h> 37#include <pthread.h> 38 39#include "libnotify.h" 40#include "notify.h" 41#include "notify_internal.h" 42 43#define USER_PROTECTED_UID_PREFIX "user.uid." 44#define USER_PROTECTED_UID_PREFIX_LEN 9 45 46uint64_t 47make_client_id(pid_t pid, int token) 48{ 49 uint64_t cid; 50 51 cid = pid; 52 cid <<= 32; 53 cid |= token; 54 55 return cid; 56} 57 58notify_state_t * 59_notify_lib_notify_state_new(uint32_t flags, uint32_t table_size) 60{ 61 notify_state_t *ns; 62 63 ns = (notify_state_t *)calloc(1, sizeof(notify_state_t)); 64 if (ns == NULL) return NULL; 65 66 ns->flags = flags; 67 ns->sock = -1; 68 69 if (ns->flags & NOTIFY_STATE_USE_LOCKS) 70 { 71 ns->lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); 72 if (ns->lock == NULL) 73 { 74 free(ns); 75 return NULL; 76 } 77 78 pthread_mutex_init(ns->lock, NULL); 79 } 80 81 ns->name_table = _nc_table_new(table_size); 82 ns->name_id_table = _nc_table_new(table_size); 83 ns->client_table = _nc_table_new(table_size); 84 ns->port_table = _nc_table_new(table_size); 85 ns->proc_table = _nc_table_new(table_size); 86 87 if ((ns->name_table == NULL) || (ns->name_id_table == NULL) || (ns->client_table == NULL) || (ns->port_table == NULL) || (ns->proc_table == NULL)) 88 { 89 free(ns->lock); 90 _nc_table_free(ns->name_table); 91 _nc_table_free(ns->name_id_table); 92 _nc_table_free(ns->client_table); 93 _nc_table_free(ns->port_table); 94 _nc_table_free(ns->proc_table); 95 free(ns); 96 return NULL; 97 } 98 99 return ns; 100} 101 102void 103_notify_lib_notify_state_free(notify_state_t *ns) 104{ 105 if (ns == NULL) return; 106 107 _nc_table_free(ns->name_table); 108 _nc_table_free(ns->name_id_table); 109 _nc_table_free(ns->client_table); 110 _nc_table_free(ns->port_table); 111 _nc_table_free(ns->proc_table); 112 113 if (ns->lock != NULL) 114 { 115 pthread_mutex_destroy(ns->lock); 116 free(ns->lock); 117 } 118 119 if (ns->sock != -1) 120 { 121 shutdown(ns->sock, 2); 122 close(ns->sock); 123 } 124 125 if (ns->controlled_name != NULL) free(ns->controlled_name); 126} 127 128static client_t * 129_internal_client_new(notify_state_t *ns, pid_t pid, int token) 130{ 131 client_t *c; 132 uint64_t cid = make_client_id(pid, token); 133 134 if (ns == NULL) return NULL; 135 136 /* detect duplicates - should never happen, but it would be bad */ 137 c = _nc_table_find_64(ns->client_table, cid); 138 if (c != NULL) return NULL; 139 140 c = calloc(1, sizeof(client_t)); 141 if (c == NULL) return NULL; 142 143 ns->stat_client_alloc++; 144 145 c->client_id = cid; 146 c->pid = pid; 147 c->send_val = token; 148 149 _nc_table_insert_64(ns->client_table, cid, c); 150 151 return c; 152} 153 154static void 155_internal_client_release(notify_state_t *ns, client_t *c) 156{ 157 uint64_t cid; 158 159 if (ns == NULL) return; 160 if (c == NULL) return; 161 162 cid = c->client_id; 163 _nc_table_delete_64(ns->client_table, cid); 164 165 switch (c->notify_type) 166 { 167 case NOTIFY_TYPE_SIGNAL: 168 { 169 break; 170 } 171 case NOTIFY_TYPE_FILE: 172 { 173 if (c->fd > 0) close(c->fd); 174 c->fd = -1; 175 break; 176 } 177 case NOTIFY_TYPE_PORT: 178 { 179 if (c->port != MACH_PORT_NULL) 180 { 181 /* release my send right to the port */ 182 mach_port_deallocate(mach_task_self(), c->port); 183 } 184 break; 185 } 186 default: 187 { 188 break; 189 } 190 } 191 192 free(c); 193 ns->stat_client_free++; 194} 195 196static name_info_t * 197_internal_new_name(notify_state_t *ns, const char *name) 198{ 199 name_info_t *n; 200 size_t namelen; 201 202 if (ns == NULL) return NULL; 203 if (name == NULL) return NULL; 204 205 namelen = strlen(name) + 1; 206 207 n = (name_info_t *)calloc(1, sizeof(name_info_t) + namelen); 208 if (n == NULL) return NULL; 209 210 ns->stat_name_alloc++; 211 212 n->name = (char *)n + sizeof(name_info_t); 213 memcpy(n->name, name, namelen); 214 215 notify_globals_t globals = _notify_globals(); 216 n->name_id = globals->name_id++; 217 218 n->access = NOTIFY_ACCESS_DEFAULT; 219 n->slot = (uint32_t)-1; 220 n->val = 1; 221 222 _nc_table_insert_no_copy(ns->name_table, n->name, n); 223 _nc_table_insert_64(ns->name_id_table, n->name_id, n); 224 225 return n; 226} 227 228static void 229_internal_insert_controlled_name(notify_state_t *ns, name_info_t *n) 230{ 231 int i, j; 232 233 if (ns == NULL) return; 234 if (n == NULL) return; 235 236 if (ns->controlled_name == NULL) ns->controlled_name_count = 0; 237 238 for (i = 0; i < ns->controlled_name_count; i++) 239 { 240 if (ns->controlled_name[i] == n) return; 241 } 242 243 ns->controlled_name = (name_info_t **)reallocf(ns->controlled_name, (ns->controlled_name_count + 1) * sizeof(name_info_t *)); 244 245 /* 246 * Insert name in reverse sorted order (longer names preceed shorter names). 247 * this means that in _internal_check_access, we check subspaces from the bottom up 248 * i.e. we check access for the "deepest" controlled subspace. 249 */ 250 251 for (i = 0; i < ns->controlled_name_count; i++) 252 { 253 if (strcmp(n->name, ns->controlled_name[i]->name) > 0) break; 254 } 255 256 for (j = ns->controlled_name_count; j > i; j--) 257 { 258 ns->controlled_name[j] = ns->controlled_name[j-1]; 259 } 260 261 ns->controlled_name[i] = n; 262 ns->controlled_name_count++; 263} 264 265static void 266_internal_remove_controlled_name(notify_state_t *ns, name_info_t *n) 267{ 268 uint32_t i, j; 269 270 for (i = 0; i < ns->controlled_name_count; i++) 271 { 272 if (ns->controlled_name[i] == n) 273 { 274 for (j = i + 1; j < ns->controlled_name_count; j++) 275 { 276 ns->controlled_name[j-1] = ns->controlled_name[j]; 277 } 278 279 ns->controlled_name_count--; 280 if (ns->controlled_name_count == 0) 281 { 282 free(ns->controlled_name); 283 ns->controlled_name = NULL; 284 } 285 else 286 { 287 ns->controlled_name = (name_info_t **)reallocf(ns->controlled_name, ns->controlled_name_count * sizeof(name_info_t *)); 288 } 289 290 return; 291 } 292 } 293} 294 295static uint32_t 296_internal_check_access(notify_state_t *ns, const char *name, uid_t uid, gid_t gid, int req) 297{ 298 uint32_t i, len, plen; 299 name_info_t *p; 300 char str[64]; 301 302 if (ns == NULL) return NOTIFY_STATUS_FAILED; 303 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 304 305 /* root may do anything */ 306 if (uid == 0) return NOTIFY_STATUS_OK; 307 308 /* if name has "user.uid." as a prefix, it is a user-protected namespace */ 309 if (!strncmp(name, USER_PROTECTED_UID_PREFIX, USER_PROTECTED_UID_PREFIX_LEN)) 310 { 311 snprintf(str, sizeof(str) - 1, "%s%d", USER_PROTECTED_UID_PREFIX, uid); 312 len = strlen(str); 313 314 /* user <uid> may access user.uid.<uid> or a subtree name */ 315 if ((!strncmp(name, str, len)) && ((name[len] == '\0') || (name[len] == '.'))) return NOTIFY_STATUS_OK; 316 return NOTIFY_STATUS_NOT_AUTHORIZED; 317 } 318 319 len = strlen(name); 320 321 if (ns->controlled_name == NULL) ns->controlled_name_count = 0; 322 for (i = 0; i < ns->controlled_name_count; i++) 323 { 324 p = ns->controlled_name[i]; 325 if (p == NULL) break; 326 if (p->name == NULL) continue; 327 328 plen = strlen(p->name); 329 if (plen > len) continue; 330 if (strncmp(p->name, name, plen)) continue; 331 332 /* Found a match or a prefix, check if restrictions apply to this uid/gid */ 333 if ((p->uid == uid) && (p->access & (req << NOTIFY_ACCESS_USER_SHIFT))) break; 334 if ((p->gid == gid) && (p->access & (req << NOTIFY_ACCESS_GROUP_SHIFT))) break; 335 if (p->access & (req << NOTIFY_ACCESS_OTHER_SHIFT)) break; 336 337 return NOTIFY_STATUS_NOT_AUTHORIZED; 338 } 339 340 return NOTIFY_STATUS_OK; 341} 342 343uint32_t 344_notify_lib_check_controlled_access(notify_state_t *ns, char *name, uid_t uid, gid_t gid, int req) 345{ 346 uint32_t status; 347 348 if (ns == NULL) return NOTIFY_STATUS_FAILED; 349 350 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 351 status = _internal_check_access(ns, name, uid, gid, req); 352 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 353 354 return status; 355} 356 357uint32_t 358_notify_lib_port_proc_new(notify_state_t *ns, mach_port_t port, pid_t proc, uint32_t state, dispatch_source_t src) 359{ 360 portproc_data_t *pdata; 361 362 if (ns == NULL) return NOTIFY_STATUS_FAILED; 363 if ((proc == 0) && (port == MACH_PORT_NULL)) return NOTIFY_STATUS_FAILED; 364 365 pdata = (portproc_data_t *)calloc(1, sizeof(portproc_data_t)); 366 if (pdata == NULL) return NOTIFY_STATUS_FAILED; 367 368 ns->stat_portproc_alloc++; 369 370 pdata->refcount = 1; 371 pdata->flags = state; 372 pdata->src = src; 373 374 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 375 if (proc == 0) _nc_table_insert_n(ns->port_table, port, pdata); 376 else _nc_table_insert_n(ns->proc_table, proc, pdata); 377 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 378 379 return NOTIFY_STATUS_OK; 380} 381 382portproc_data_t * 383_notify_lib_port_proc_find(notify_state_t *ns, mach_port_t port, pid_t proc) 384{ 385 portproc_data_t *pdata = NULL; 386 387 if (ns == NULL) return NULL; 388 if ((proc == 0) && (port == MACH_PORT_NULL)) return NULL; 389 390 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 391 392 if (proc == 0) pdata = _nc_table_find_n(ns->port_table, port); 393 else pdata = _nc_table_find_n(ns->proc_table, proc); 394 395 if (pdata != NULL) pdata->refcount++; 396 397 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 398 399 return pdata; 400} 401 402void 403_notify_lib_port_proc_release(notify_state_t *ns, mach_port_t port, pid_t proc) 404{ 405 portproc_data_t *pdata = NULL; 406 407 if (ns == NULL) return; 408 if ((proc == 0) && (port == MACH_PORT_NULL)) return; 409 410 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 411 412 if (proc == 0) pdata = _nc_table_find_n(ns->port_table, port); 413 else pdata = _nc_table_find_n(ns->proc_table, proc); 414 415 if (pdata != NULL) 416 { 417 if (pdata->refcount > 0) pdata->refcount--; 418 if (pdata->refcount == 0) 419 { 420 if (proc == 0) _nc_table_delete_n(ns->port_table, port); 421 else _nc_table_delete_n(ns->proc_table, proc); 422 423 dispatch_source_cancel(pdata->src); 424 dispatch_release(pdata->src); 425 426 free(pdata); 427 ns->stat_portproc_free++; 428 } 429 } 430 431 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 432} 433 434/* 435 * Send notification to a subscriber 436 */ 437static uint32_t 438_internal_send(notify_state_t *ns, client_t *c) 439{ 440 uint32_t send; 441 portproc_data_t *pdata; 442 443 if (ns == NULL) return NOTIFY_STATUS_FAILED; 444 if (c == NULL) return NOTIFY_STATUS_FAILED; 445 446 if (c->state & NOTIFY_CLIENT_STATE_SUSPENDED) 447 { 448 c->state |= NOTIFY_CLIENT_STATE_PENDING; 449 return NOTIFY_STATUS_OK; 450 } 451 452 pdata = _nc_table_find_n(ns->proc_table, c->pid); 453 if ((pdata != NULL) && (pdata->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED)) 454 { 455 c->suspend_count++; 456 c->state |= NOTIFY_CLIENT_STATE_SUSPENDED; 457 c->state |= NOTIFY_CLIENT_STATE_PENDING; 458 return NOTIFY_STATUS_OK; 459 } 460 461 send = c->send_val; 462 463 switch (c->notify_type) 464 { 465 case NOTIFY_TYPE_SIGNAL: 466 { 467 int rc = 0; 468 469 if (c->pid == NOTIFY_CLIENT_SELF) rc = kill(getpid(), c->sig); 470 else rc = kill(c->pid, c->sig); 471 472 if (rc != 0) return NOTIFY_STATUS_FAILED; 473 474 c->state &= ~NOTIFY_CLIENT_STATE_PENDING; 475 c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; 476 477 return NOTIFY_STATUS_OK; 478 } 479 480 case NOTIFY_TYPE_FILE: 481 { 482 ssize_t len; 483 484 if (c->fd >= 0) 485 { 486 send = htonl(send); 487 len = write(c->fd, &send, sizeof(uint32_t)); 488 if (len != sizeof(uint32_t)) 489 { 490 close(c->fd); 491 c->fd = -1; 492 return NOTIFY_STATUS_FAILED; 493 } 494 } 495 496 c->state &= ~NOTIFY_CLIENT_STATE_PENDING; 497 c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; 498 499 return NOTIFY_STATUS_OK; 500 } 501 502 case NOTIFY_TYPE_PORT: 503 { 504 kern_return_t kstatus; 505 mach_msg_empty_send_t msg; 506 mach_msg_option_t opts = MACH_SEND_MSG | MACH_SEND_TIMEOUT; 507 508 pdata = _nc_table_find_n(ns->port_table, c->port); 509 if ((pdata != NULL) && (pdata->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED)) 510 { 511 c->suspend_count++; 512 c->state |= NOTIFY_CLIENT_STATE_SUSPENDED; 513 c->state |= NOTIFY_CLIENT_STATE_PENDING; 514 return NOTIFY_STATUS_OK; 515 } 516 517 if (ns->flags & NOTIFY_STATE_ENABLE_RESEND) opts |= MACH_SEND_NOTIFY; 518 519 memset(&msg, 0, sizeof(mach_msg_empty_send_t)); 520 msg.header.msgh_size = sizeof(mach_msg_empty_send_t); 521 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO); 522 msg.header.msgh_local_port = MACH_PORT_NULL; 523 msg.header.msgh_remote_port = c->port; 524 msg.header.msgh_id = (mach_msg_id_t)send; 525 526 kstatus = mach_msg(&msg.header, opts, msg.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 527 528 if (kstatus == MACH_SEND_TIMED_OUT) 529 { 530 /* deallocate port rights obtained via pseudo-receive after failed mach_msg() send */ 531 mach_msg_destroy(&msg.header); 532 if (ns->flags & NOTIFY_STATE_ENABLE_RESEND) 533 { 534 /* 535 * Suspend on timeout. 536 * notifyd will get a MACH_NOTIFY_SEND_POSSIBLE and trigger a retry. 537 * c->suspend_count must be zero, or we would not be trying to send. 538 */ 539 c->suspend_count++; 540 c->state |= NOTIFY_CLIENT_STATE_SUSPENDED; 541 c->state |= NOTIFY_CLIENT_STATE_PENDING; 542 c->state |= NOTIFY_CLIENT_STATE_TIMEOUT; 543 544 return NOTIFY_STATUS_OK; 545 } 546 547 return NOTIFY_STATUS_FAILED; 548 } 549 else if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 550 551 c->state &= ~NOTIFY_CLIENT_STATE_PENDING; 552 c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; 553 554 return NOTIFY_STATUS_OK; 555 } 556 557 default: 558 { 559 break; 560 } 561 } 562 563 c->state &= ~NOTIFY_CLIENT_STATE_PENDING; 564 c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; 565 566 return NOTIFY_STATUS_OK; 567} 568 569uint32_t 570_notify_lib_post_client(notify_state_t *ns, client_t *c) 571{ 572 uint32_t status; 573 574 if (ns == NULL) return NOTIFY_STATUS_FAILED; 575 if (c == NULL) return NOTIFY_STATUS_FAILED; 576 577 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 578 status = _internal_send(ns, c); 579 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 580 581 return status; 582} 583 584static uint32_t 585_internal_post_name(notify_state_t *ns, name_info_t *n, uid_t uid, gid_t gid) 586{ 587 int auth; 588 list_t *l; 589 client_t *c; 590 591 if (n == NULL) return NOTIFY_STATUS_INVALID_NAME; 592 593 auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_WRITE); 594 if (auth != 0) return NOTIFY_STATUS_NOT_AUTHORIZED; 595 596 n->val++; 597 598 for (l = n->subscriptions; l != NULL; l = _nc_list_next(l)) 599 { 600 c = _nc_list_data(l); 601 if (c != NULL) _internal_send(ns, c); 602 } 603 604 return NOTIFY_STATUS_OK; 605} 606 607/* 608 * Notify subscribers of this name. 609 */ 610uint32_t 611_notify_lib_post(notify_state_t *ns, const char *name, uid_t uid, gid_t gid) 612{ 613 name_info_t *n; 614 uint32_t status; 615 616 if (ns == NULL) return NOTIFY_STATUS_FAILED; 617 618 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 619 620 n = (name_info_t *)_nc_table_find(ns->name_table, name); 621 if (n == NULL) 622 { 623 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 624 return NOTIFY_STATUS_INVALID_NAME; 625 } 626 627 status = _internal_post_name(ns, n, uid, gid); 628 629 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 630 return status; 631} 632 633uint32_t 634_notify_lib_post_nid(notify_state_t *ns, uint64_t nid, uid_t uid, gid_t gid) 635{ 636 name_info_t *n; 637 uint32_t status; 638 639 if (ns == NULL) return NOTIFY_STATUS_FAILED; 640 641 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 642 643 n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid); 644 if (n == NULL) 645 { 646 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 647 return NOTIFY_STATUS_INVALID_NAME; 648 } 649 650 status = _internal_post_name(ns, n, uid, gid); 651 652 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 653 return status; 654} 655 656static void 657_internal_release_name_info(notify_state_t *ns, name_info_t *n) 658{ 659 if (ns == NULL) return; 660 if (n == NULL) return; 661 662 if (n->refcount > 0) n->refcount--; 663 if (n->refcount == 0) 664 { 665 _internal_remove_controlled_name(ns, n); 666 _nc_table_delete(ns->name_table, n->name); 667 _nc_table_delete_64(ns->name_id_table, n->name_id); 668 _nc_list_release_list(n->subscriptions); 669 free(n); 670 ns->stat_name_free++; 671 } 672} 673 674/* 675 * Cancel (delete) a client 676 */ 677static void 678_internal_cancel(notify_state_t *ns, uint64_t cid) 679{ 680 client_t *c; 681 name_info_t *n; 682 683 if (ns == NULL) return; 684 685 c = NULL; 686 n = NULL; 687 688 c = _nc_table_find_64(ns->client_table, cid); 689 if (c == NULL) return; 690 691 n = c->name_info; 692 if (n == NULL) return; 693 694 n->subscriptions =_nc_list_find_release(n->subscriptions, c); 695 _internal_client_release(ns, c); 696 _internal_release_name_info(ns, n); 697} 698 699void 700_notify_lib_cancel(notify_state_t *ns, pid_t pid, int token) 701{ 702 uint64_t cid; 703 704 if (ns == NULL) return; 705 706 cid = make_client_id(pid, token); 707 708 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 709 _internal_cancel(ns, cid); 710 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 711} 712 713void 714_notify_lib_suspend(notify_state_t *ns, pid_t pid, int token) 715{ 716 client_t *c; 717 uint64_t cid; 718 719 if (ns == NULL) return; 720 721 cid = make_client_id(pid, token); 722 723 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 724 725 c = _nc_table_find_64(ns->client_table, cid); 726 if (c != NULL) 727 { 728 c->state |= NOTIFY_CLIENT_STATE_SUSPENDED; 729 if (c->suspend_count < UINT32_MAX) c->suspend_count++; 730 } 731 732 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 733} 734 735uint32_t 736_notify_lib_resume(notify_state_t *ns, pid_t pid, int token) 737{ 738 client_t *c; 739 uint64_t cid; 740 uint32_t status = NOTIFY_STATUS_OK; 741 742 if (ns == NULL) return NOTIFY_STATUS_FAILED; 743 744 cid = make_client_id(pid, token); 745 746 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 747 748 c = _nc_table_find_64(ns->client_table, cid); 749 if (c != NULL) 750 { 751 if (c->suspend_count > 0) c->suspend_count--; 752 if (c->suspend_count == 0) 753 { 754 c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED; 755 c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; 756 757 if (c->state & NOTIFY_CLIENT_STATE_PENDING) 758 { 759 status = _internal_send(ns, c); 760 } 761 } 762 } 763 764 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 765 766 return status; 767} 768 769void 770_notify_lib_suspend_proc(notify_state_t *ns, pid_t pid) 771{ 772 portproc_data_t *pdata; 773 774 if (ns == NULL) return; 775 776 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 777 778 pdata = _nc_table_find_n(ns->proc_table, pid); 779 if (pdata != NULL) pdata->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED; 780 781 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 782} 783 784void 785_notify_lib_resume_proc(notify_state_t *ns, pid_t pid) 786{ 787 client_t *c; 788 void *tt; 789 portproc_data_t *pdata; 790 791 if (ns == NULL) return; 792 793 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 794 795 /* Resume all subscriptions for this process */ 796 pdata = _nc_table_find_n(ns->proc_table, pid); 797 if (pdata != NULL) pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED; 798 799 tt = _nc_table_traverse_start(ns->client_table); 800 while (tt != NULL) 801 { 802 c = _nc_table_traverse(ns->client_table, tt); 803 if (c == NULL) break; 804 805 if (c->pid == pid) 806 { 807 if (c->suspend_count > 0) c->suspend_count--; 808 if (c->suspend_count == 0) 809 { 810 c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED; 811 c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; 812 813 if (c->state & NOTIFY_CLIENT_STATE_PENDING) 814 { 815 _internal_send(ns, c); 816 } 817 } 818 } 819 } 820 _nc_table_traverse_end(ns->client_table, tt); 821 822 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 823} 824 825void 826_notify_lib_suspend_port(notify_state_t *ns, mach_port_t port) 827{ 828 portproc_data_t *pdata; 829 830 if (ns == NULL) return; 831 832 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 833 834 pdata = _nc_table_find_n(ns->port_table, port); 835 if (pdata != NULL) pdata->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED; 836 837 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 838} 839 840void 841_notify_lib_resume_port(notify_state_t *ns, mach_port_t port) 842{ 843 client_t *c; 844 void *tt; 845 portproc_data_t *pdata; 846 847 if (ns == NULL) return; 848 849 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 850 851 /* Resume all subscriptions with this port */ 852 pdata = _nc_table_find_n(ns->port_table, port); 853 if (pdata != NULL) pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED; 854 855 tt = _nc_table_traverse_start(ns->client_table); 856 while (tt != NULL) 857 { 858 c = _nc_table_traverse(ns->client_table, tt); 859 if (c == NULL) break; 860 861 if (c->port == port) 862 { 863 if (c->suspend_count > 0) c->suspend_count--; 864 if (c->suspend_count == 0) 865 { 866 c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED; 867 c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT; 868 869 if (c->state & NOTIFY_CLIENT_STATE_PENDING) 870 { 871 _internal_send(ns, c); 872 } 873 } 874 } 875 } 876 _nc_table_traverse_end(ns->client_table, tt); 877 878 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 879} 880 881/* 882 * Delete all clients for a process 883 * N.B. notifyd does not use this routine. 884 */ 885void 886_notify_lib_cancel_proc(notify_state_t *ns, pid_t pid) 887{ 888 client_t *c; 889 void *tt; 890 list_t *l, *x; 891 892 if (ns == NULL) return; 893 894 x = NULL; 895 896 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 897 898 tt = _nc_table_traverse_start(ns->client_table); 899 while (tt != NULL) 900 { 901 c = _nc_table_traverse(ns->client_table, tt); 902 if (c == NULL) break; 903 904 if (c->pid == pid) x = _nc_list_prepend(x, _nc_list_new(c)); 905 } 906 _nc_table_traverse_end(ns->client_table, tt); 907 908 for (l = x; l != NULL; l = _nc_list_next(l)) 909 { 910 c = _nc_list_data(l); 911 _internal_cancel(ns, c->client_id); 912 } 913 914 _nc_list_release_list(x); 915 916 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 917} 918 919/* 920 * Delete all clients for a port 921 * N.B. notifyd does not use this routine. 922 */ 923void 924_notify_lib_cancel_port(notify_state_t *ns, mach_port_t port) 925{ 926 client_t *c; 927 void *tt; 928 list_t *l, *x; 929 930 if (ns == NULL) return; 931 932 x = NULL; 933 934 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 935 936 tt = _nc_table_traverse_start(ns->client_table); 937 while (tt != NULL) 938 { 939 c = _nc_table_traverse(ns->client_table, tt); 940 if (c == NULL) break; 941 942 if (c->port == port) x = _nc_list_prepend(x, _nc_list_new(c)); 943 } 944 _nc_table_traverse_end(ns->client_table, tt); 945 946 for (l = x; l != NULL; l = _nc_list_next(l)) 947 { 948 c = _nc_list_data(l); 949 _internal_cancel(ns, c->client_id); 950 } 951 952 _nc_list_release_list(x); 953 954 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 955} 956 957/* 958 * Check if a name has changed since the last time this client checked. 959 * Returns true, false, or error. 960 */ 961uint32_t 962_notify_lib_check(notify_state_t *ns, pid_t pid, int token, int *check) 963{ 964 client_t *c; 965 uint64_t cid; 966 967 if (ns == NULL) return NOTIFY_STATUS_FAILED; 968 if (check == NULL) return NOTIFY_STATUS_FAILED; 969 970 cid = make_client_id(pid, token); 971 972 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 973 974 c = _nc_table_find_64(ns->client_table, cid); 975 976 if (c == NULL) 977 { 978 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 979 return NOTIFY_STATUS_INVALID_TOKEN; 980 } 981 982 if (c->name_info == NULL) 983 { 984 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 985 return NOTIFY_STATUS_INVALID_TOKEN; 986 } 987 988 if (c->name_info->val == c->lastval) 989 { 990 *check = 0; 991 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 992 return NOTIFY_STATUS_OK; 993 } 994 995 c->lastval = c->name_info->val; 996 *check = 1; 997 998 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 999 return NOTIFY_STATUS_OK; 1000} 1001 1002/* 1003 * SPI: get value for a name. 1004 */ 1005uint32_t 1006_notify_lib_peek(notify_state_t *ns, pid_t pid, int token, int *val) 1007{ 1008 client_t *c; 1009 uint64_t cid; 1010 1011 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1012 if (val == NULL) return NOTIFY_STATUS_FAILED; 1013 1014 cid = make_client_id(pid, token); 1015 1016 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1017 1018 c = _nc_table_find_64(ns->client_table, cid); 1019 1020 if (c == NULL) 1021 { 1022 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1023 return NOTIFY_STATUS_INVALID_TOKEN; 1024 } 1025 1026 if (c->name_info == NULL) 1027 { 1028 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1029 return NOTIFY_STATUS_INVALID_TOKEN; 1030 } 1031 1032 *val = c->name_info->val; 1033 1034 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1035 return NOTIFY_STATUS_OK; 1036} 1037 1038int * 1039_notify_lib_check_addr(notify_state_t *ns, pid_t pid, int token) 1040{ 1041 client_t *c; 1042 int *addr; 1043 uint64_t cid; 1044 1045 if (ns == NULL) return NULL; 1046 1047 cid = make_client_id(pid, token); 1048 1049 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1050 1051 c = _nc_table_find_64(ns->client_table, cid); 1052 1053 if (c == NULL) 1054 { 1055 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1056 return NULL; 1057 } 1058 1059 if (c->name_info == NULL) 1060 { 1061 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1062 return NULL; 1063 } 1064 1065 addr = (int *)&(c->name_info->val); 1066 1067 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1068 return addr; 1069} 1070 1071/* 1072 * Get state value for a name. 1073 */ 1074uint32_t 1075_notify_lib_get_state(notify_state_t *ns, uint64_t nid, uint64_t *state, uid_t uid, gid_t gid) 1076{ 1077 name_info_t *n; 1078 1079 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1080 if (state == NULL) return NOTIFY_STATUS_FAILED; 1081 1082 *state = 0; 1083 1084 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1085 1086 n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid); 1087 1088 if (n == NULL) 1089 { 1090 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1091 return NOTIFY_STATUS_INVALID_NAME; 1092 } 1093 1094#ifdef GET_STATE_AUTH_CHECK 1095 int auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_READ); 1096 if (auth != 0) 1097 { 1098 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1099 return NOTIFY_STATUS_NOT_AUTHORIZED; 1100 } 1101#endif 1102 1103 *state = n->state; 1104 1105 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1106 return NOTIFY_STATUS_OK; 1107} 1108 1109/* 1110 * Set state value for a name. 1111 */ 1112uint32_t 1113_notify_lib_set_state(notify_state_t *ns, uint64_t nid, uint64_t state, uid_t uid, gid_t gid) 1114{ 1115 name_info_t *n; 1116 int auth; 1117 1118 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1119 1120 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1121 1122 n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid); 1123 1124 if (n == NULL) 1125 { 1126 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1127 return NOTIFY_STATUS_INVALID_NAME; 1128 } 1129 1130 auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_WRITE); 1131 if (auth != 0) 1132 { 1133 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1134 return NOTIFY_STATUS_NOT_AUTHORIZED; 1135 } 1136 1137 n->state = state; 1138 n->state_time = mach_absolute_time(); 1139 1140 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1141 return NOTIFY_STATUS_OK; 1142} 1143 1144static uint32_t 1145_internal_register_common(notify_state_t *ns, const char *name, pid_t pid, int token, uid_t uid, gid_t gid, client_t **outc) 1146{ 1147 client_t *c; 1148 name_info_t *n; 1149 int is_new_name; 1150 uint32_t status; 1151 1152 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1153 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1154 if (outc == NULL) return NOTIFY_STATUS_OK; 1155 1156 status = _internal_check_access(ns, name, uid, gid, NOTIFY_ACCESS_READ); 1157 if (status != NOTIFY_STATUS_OK) return NOTIFY_STATUS_NOT_AUTHORIZED; 1158 1159 *outc = NULL; 1160 is_new_name = 0; 1161 1162 n = (name_info_t *)_nc_table_find(ns->name_table, name); 1163 if (n == NULL) 1164 { 1165 is_new_name = 1; 1166 1167 n = _internal_new_name(ns, name); 1168 if (n == NULL) return NOTIFY_STATUS_FAILED; 1169 } 1170 1171 c = _internal_client_new(ns, pid, token); 1172 if (c == NULL) 1173 { 1174 if (is_new_name == 1) 1175 { 1176 _nc_table_delete(ns->name_table, n->name); 1177 _nc_list_release_list(n->subscriptions); 1178 free(n); 1179 ns->stat_name_free++; 1180 } 1181 1182 return NOTIFY_STATUS_FAILED; 1183 } 1184 1185 n->refcount++; 1186 1187 c->name_info = n; 1188 n->subscriptions = _nc_list_prepend(n->subscriptions, _nc_list_new(c)); 1189 1190 *outc = c; 1191 1192 return NOTIFY_STATUS_OK; 1193} 1194 1195/* 1196 * Register for signal. 1197 * Returns the client_id; 1198 */ 1199uint32_t 1200_notify_lib_register_signal(notify_state_t *ns, const char *name, pid_t pid, int token, uint32_t sig, uid_t uid, gid_t gid, uint64_t *out_nid) 1201{ 1202 client_t *c; 1203 uint32_t status; 1204 1205 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1206 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1207 1208 c = NULL; 1209 1210 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1211 1212 status = _internal_register_common(ns, name, pid, token, uid, gid, &c); 1213 if (status != NOTIFY_STATUS_OK) 1214 { 1215 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1216 return status; 1217 } 1218 1219 c->notify_type = NOTIFY_TYPE_SIGNAL; 1220 c->pid = pid; 1221 c->sig = sig; 1222 *out_nid = c->name_info->name_id; 1223 1224 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1225 return NOTIFY_STATUS_OK; 1226} 1227 1228/* 1229 * Register for notification on a file descriptor. 1230 * Returns the client_id; 1231 */ 1232uint32_t 1233_notify_lib_register_file_descriptor(notify_state_t *ns, const char *name, pid_t pid, int token, int fd, uid_t uid, gid_t gid, uint64_t *out_nid) 1234{ 1235 client_t *c; 1236 uint32_t status; 1237 1238 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1239 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1240 1241 c = NULL; 1242 1243 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1244 1245 status = _internal_register_common(ns, name, pid, token, uid, gid, &c); 1246 if (status != NOTIFY_STATUS_OK) 1247 { 1248 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1249 return status; 1250 } 1251 1252 c->notify_type = NOTIFY_TYPE_FILE; 1253 c->fd = fd; 1254 *out_nid = c->name_info->name_id; 1255 1256 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1257 return NOTIFY_STATUS_OK; 1258} 1259 1260/* 1261 * Register for notification on a mach port. 1262 * Returns the client_id; 1263 */ 1264uint32_t 1265_notify_lib_register_mach_port(notify_state_t *ns, const char *name, pid_t pid, int token, mach_port_t port, uid_t uid, gid_t gid, uint64_t *out_nid) 1266{ 1267 client_t *c; 1268 uint32_t status; 1269 1270 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1271 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1272 1273 c = NULL; 1274 1275 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1276 1277 status = _internal_register_common(ns, name, pid, token, uid, gid, &c); 1278 if (status != NOTIFY_STATUS_OK) 1279 { 1280 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1281 return status; 1282 } 1283 1284 c->notify_type = NOTIFY_TYPE_PORT; 1285 c->port = port; 1286 *out_nid = c->name_info->name_id; 1287 1288 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1289 return NOTIFY_STATUS_OK; 1290} 1291 1292/* 1293 * Plain registration - only for notify_check() 1294 * Returns the client_id. 1295 */ 1296uint32_t 1297_notify_lib_register_plain(notify_state_t *ns, const char *name, pid_t pid, int token, uint32_t slot, uint32_t uid, uint32_t gid, uint64_t *out_nid) 1298{ 1299 client_t *c; 1300 uint32_t status; 1301 1302 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1303 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1304 1305 c = NULL; 1306 1307 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1308 1309 status = _internal_register_common(ns, name, pid, token, uid, gid, &c); 1310 if (status != NOTIFY_STATUS_OK) 1311 { 1312 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1313 return status; 1314 } 1315 1316 if (slot == SLOT_NONE) 1317 { 1318 c->notify_type = NOTIFY_TYPE_PLAIN; 1319 } 1320 else 1321 { 1322 c->notify_type = NOTIFY_TYPE_MEMORY; 1323 c->name_info->slot = slot; 1324 } 1325 1326 *out_nid = c->name_info->name_id; 1327 1328 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1329 return NOTIFY_STATUS_OK; 1330} 1331 1332uint32_t 1333_notify_lib_set_owner(notify_state_t *ns, const char *name, uid_t uid, gid_t gid) 1334{ 1335 name_info_t *n; 1336 1337 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1338 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1339 1340 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1341 1342 n = (name_info_t *)_nc_table_find(ns->name_table, name); 1343 if (n == NULL) 1344 { 1345 /* create new name */ 1346 n = _internal_new_name(ns, name); 1347 if (n == NULL) 1348 { 1349 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1350 return NOTIFY_STATUS_FAILED; 1351 } 1352 1353 /* 1354 * Setting the refcount here allows the namespace to be "pre-populated" 1355 * with controlled names. notifyd does this for reserved names in 1356 * its configuration file. 1357 */ 1358 n->refcount++; 1359 } 1360 1361 n->uid = uid; 1362 n->gid = gid; 1363 1364 _internal_insert_controlled_name(ns, n); 1365 1366 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1367 return NOTIFY_STATUS_OK; 1368} 1369 1370uint32_t 1371_notify_lib_get_owner(notify_state_t *ns, const char *name, uint32_t *uid, uint32_t *gid) 1372{ 1373 name_info_t *n; 1374 int i, nlen, len; 1375 1376 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1377 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1378 1379 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1380 1381 n = (name_info_t *)_nc_table_find(ns->name_table, name); 1382 if (n != NULL) 1383 { 1384 *uid = n->uid; 1385 *gid = n->gid; 1386 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1387 return NOTIFY_STATUS_OK; 1388 } 1389 1390 len = strlen(name); 1391 1392 for (i = 0; i < ns->controlled_name_count; i++) 1393 { 1394 n = ns->controlled_name[i]; 1395 if (n == NULL) break; 1396 1397 nlen = strlen(n->name); 1398 1399 if (!strcmp(n->name, name)) 1400 { 1401 *uid = n->uid; 1402 *gid = n->gid; 1403 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1404 return NOTIFY_STATUS_OK; 1405 } 1406 1407 /* check if this key is a prefix */ 1408 if (nlen >= len) continue; 1409 if (strncmp(n->name, name, nlen)) continue; 1410 1411 *uid = n->uid; 1412 *gid = n->gid; 1413 1414 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1415 return NOTIFY_STATUS_OK; 1416 } 1417 1418 *uid = 0; 1419 *gid = 0; 1420 1421 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1422 return NOTIFY_STATUS_OK; 1423} 1424 1425uint32_t 1426_notify_lib_set_access(notify_state_t *ns, const char *name, uint32_t mode) 1427{ 1428 name_info_t *n; 1429 1430 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1431 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1432 1433 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1434 1435 n = (name_info_t *)_nc_table_find(ns->name_table, name); 1436 if (n == NULL) 1437 { 1438 /* create new name */ 1439 n = _internal_new_name(ns, name); 1440 if (n == NULL) 1441 { 1442 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1443 return NOTIFY_STATUS_FAILED; 1444 } 1445 1446 /* 1447 * Setting the refcount here allows the namespace to be "pre-populated" 1448 * with controlled names. notifyd does this for reserved names in 1449 * its configuration file. 1450 */ 1451 n->refcount++; 1452 } 1453 1454 n->access = mode; 1455 1456 _internal_insert_controlled_name(ns, n); 1457 1458 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1459 return NOTIFY_STATUS_OK; 1460} 1461 1462uint32_t 1463_notify_lib_get_access(notify_state_t *ns, const char *name, uint32_t *mode) 1464{ 1465 name_info_t *n; 1466 int i, nlen, len; 1467 1468 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1469 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1470 1471 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1472 1473 n = (name_info_t *)_nc_table_find(ns->name_table, name); 1474 if (n != NULL) 1475 { 1476 *mode = n->access; 1477 1478 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1479 return NOTIFY_STATUS_OK; 1480 } 1481 1482 len = strlen(name); 1483 1484 for (i = 0; i < ns->controlled_name_count; i++) 1485 { 1486 n = ns->controlled_name[i]; 1487 if (n == NULL) break; 1488 if (n->name == NULL) continue; 1489 1490 nlen = strlen(n->name); 1491 1492 if (!strcmp(n->name, name)) 1493 { 1494 *mode = n->access; 1495 1496 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1497 return NOTIFY_STATUS_OK; 1498 } 1499 1500 /* check if this key is a prefix */ 1501 if (nlen >= len) continue; 1502 if (strncmp(n->name, name, nlen)) continue; 1503 1504 *mode = n->access; 1505 1506 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1507 return NOTIFY_STATUS_OK; 1508 } 1509 1510 *mode = NOTIFY_ACCESS_DEFAULT; 1511 1512 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1513 return NOTIFY_STATUS_OK; 1514} 1515 1516uint32_t 1517_notify_lib_release_name(notify_state_t *ns, const char *name, uid_t uid, gid_t gid) 1518{ 1519 name_info_t *n; 1520 1521 if (ns == NULL) return NOTIFY_STATUS_FAILED; 1522 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1523 1524 if (ns->lock != NULL) pthread_mutex_lock(ns->lock); 1525 1526 n = (name_info_t *)_nc_table_find(ns->name_table, name); 1527 if (n == NULL) 1528 { 1529 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1530 return NOTIFY_STATUS_INVALID_NAME; 1531 } 1532 1533 /* Owner and root may release */ 1534 if ((n->uid != uid) && (uid != 0)) 1535 { 1536 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1537 return NOTIFY_STATUS_NOT_AUTHORIZED; 1538 } 1539 1540 _internal_release_name_info(ns, n); 1541 1542 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); 1543 return NOTIFY_STATUS_OK; 1544} 1545