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
46 uint64_t
make_client_id(pid_t pid,int token)47 make_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
58 notify_state_t *
_notify_lib_notify_state_new(uint32_t flags,uint32_t table_size)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
102 void
_notify_lib_notify_state_free(notify_state_t * ns)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
128 static client_t *
_internal_client_new(notify_state_t * ns,pid_t pid,int token)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
154 static void
_internal_client_release(notify_state_t * ns,client_t * c)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
196 static name_info_t *
_internal_new_name(notify_state_t * ns,const char * name)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
228 static void
_internal_insert_controlled_name(notify_state_t * ns,name_info_t * n)229 _internal_insert_controlled_name(notify_state_t *ns, name_info_t *n)
230 {
231 unsigned 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
265 static void
_internal_remove_controlled_name(notify_state_t * ns,name_info_t * n)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
295 static uint32_t
_internal_check_access(notify_state_t * ns,const char * name,uid_t uid,gid_t gid,int req)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
343 uint32_t
_notify_lib_check_controlled_access(notify_state_t * ns,char * name,uid_t uid,gid_t gid,int req)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
357 uint32_t
_notify_lib_port_proc_new(notify_state_t * ns,mach_port_t port,pid_t proc,uint32_t state,dispatch_source_t src)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
382 portproc_data_t *
_notify_lib_port_proc_find(notify_state_t * ns,mach_port_t port,pid_t proc)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
402 void
_notify_lib_port_proc_release(notify_state_t * ns,mach_port_t port,pid_t proc)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 */
437 static uint32_t
_internal_send(notify_state_t * ns,client_t * c)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
569 uint32_t
_notify_lib_post_client(notify_state_t * ns,client_t * c)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
584 static uint32_t
_internal_post_name(notify_state_t * ns,name_info_t * n,uid_t uid,gid_t gid)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 */
610 uint32_t
_notify_lib_post(notify_state_t * ns,const char * name,uid_t uid,gid_t gid)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
633 uint32_t
_notify_lib_post_nid(notify_state_t * ns,uint64_t nid,uid_t uid,gid_t gid)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
656 static void
_internal_release_name_info(notify_state_t * ns,name_info_t * n)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 */
677 static void
_internal_cancel(notify_state_t * ns,uint64_t cid)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
699 void
_notify_lib_cancel(notify_state_t * ns,pid_t pid,int token)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
713 void
_notify_lib_suspend(notify_state_t * ns,pid_t pid,int token)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
735 uint32_t
_notify_lib_resume(notify_state_t * ns,pid_t pid,int token)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
769 void
_notify_lib_suspend_proc(notify_state_t * ns,pid_t pid)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
784 void
_notify_lib_resume_proc(notify_state_t * ns,pid_t pid)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
825 void
_notify_lib_suspend_port(notify_state_t * ns,mach_port_t port)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
840 void
_notify_lib_resume_port(notify_state_t * ns,mach_port_t port)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 */
885 void
_notify_lib_cancel_proc(notify_state_t * ns,pid_t pid)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 */
923 void
_notify_lib_cancel_port(notify_state_t * ns,mach_port_t port)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 */
961 uint32_t
_notify_lib_check(notify_state_t * ns,pid_t pid,int token,int * check)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 */
1005 uint32_t
_notify_lib_peek(notify_state_t * ns,pid_t pid,int token,int * val)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
1038 int *
_notify_lib_check_addr(notify_state_t * ns,pid_t pid,int token)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 */
1074 uint32_t
_notify_lib_get_state(notify_state_t * ns,uint64_t nid,uint64_t * state,uid_t uid,gid_t gid)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 (void)uid;
1080 (void)gid;
1081
1082 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1083 if (state == NULL) return NOTIFY_STATUS_FAILED;
1084
1085 *state = 0;
1086
1087 if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1088
1089 n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid);
1090
1091 if (n == NULL)
1092 {
1093 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1094 return NOTIFY_STATUS_INVALID_NAME;
1095 }
1096
1097 #ifdef GET_STATE_AUTH_CHECK
1098 int auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_READ);
1099 if (auth != 0)
1100 {
1101 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1102 return NOTIFY_STATUS_NOT_AUTHORIZED;
1103 }
1104 #endif
1105
1106 *state = n->state;
1107
1108 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1109 return NOTIFY_STATUS_OK;
1110 }
1111
1112 /*
1113 * Set state value for a name.
1114 */
1115 uint32_t
_notify_lib_set_state(notify_state_t * ns,uint64_t nid,uint64_t state,uid_t uid,gid_t gid)1116 _notify_lib_set_state(notify_state_t *ns, uint64_t nid, uint64_t state, uid_t uid, gid_t gid)
1117 {
1118 name_info_t *n;
1119 int auth;
1120
1121 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1122
1123 if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1124
1125 n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid);
1126
1127 if (n == NULL)
1128 {
1129 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1130 return NOTIFY_STATUS_INVALID_NAME;
1131 }
1132
1133 auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_WRITE);
1134 if (auth != 0)
1135 {
1136 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1137 return NOTIFY_STATUS_NOT_AUTHORIZED;
1138 }
1139
1140 n->state = state;
1141 n->state_time = mach_absolute_time();
1142
1143 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1144 return NOTIFY_STATUS_OK;
1145 }
1146
1147 static uint32_t
_internal_register_common(notify_state_t * ns,const char * name,pid_t pid,int token,uid_t uid,gid_t gid,client_t ** outc)1148 _internal_register_common(notify_state_t *ns, const char *name, pid_t pid, int token, uid_t uid, gid_t gid, client_t **outc)
1149 {
1150 client_t *c;
1151 name_info_t *n;
1152 int is_new_name;
1153 uint32_t status;
1154
1155 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1156 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1157 if (outc == NULL) return NOTIFY_STATUS_OK;
1158
1159 status = _internal_check_access(ns, name, uid, gid, NOTIFY_ACCESS_READ);
1160 if (status != NOTIFY_STATUS_OK) return NOTIFY_STATUS_NOT_AUTHORIZED;
1161
1162 *outc = NULL;
1163 is_new_name = 0;
1164
1165 n = (name_info_t *)_nc_table_find(ns->name_table, name);
1166 if (n == NULL)
1167 {
1168 is_new_name = 1;
1169
1170 n = _internal_new_name(ns, name);
1171 if (n == NULL) return NOTIFY_STATUS_FAILED;
1172 }
1173
1174 c = _internal_client_new(ns, pid, token);
1175 if (c == NULL)
1176 {
1177 if (is_new_name == 1)
1178 {
1179 _nc_table_delete(ns->name_table, n->name);
1180 _nc_list_release_list(n->subscriptions);
1181 free(n);
1182 ns->stat_name_free++;
1183 }
1184
1185 return NOTIFY_STATUS_FAILED;
1186 }
1187
1188 n->refcount++;
1189
1190 c->name_info = n;
1191 n->subscriptions = _nc_list_prepend(n->subscriptions, _nc_list_new(c));
1192
1193 *outc = c;
1194
1195 return NOTIFY_STATUS_OK;
1196 }
1197
1198 /*
1199 * Register for signal.
1200 * Returns the client_id;
1201 */
1202 uint32_t
_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)1203 _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)
1204 {
1205 client_t *c;
1206 uint32_t status;
1207
1208 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1209 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1210
1211 c = NULL;
1212
1213 if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1214
1215 status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
1216 if (status != NOTIFY_STATUS_OK)
1217 {
1218 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1219 return status;
1220 }
1221
1222 c->notify_type = NOTIFY_TYPE_SIGNAL;
1223 c->pid = pid;
1224 c->sig = sig;
1225 *out_nid = c->name_info->name_id;
1226
1227 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1228 return NOTIFY_STATUS_OK;
1229 }
1230
1231 /*
1232 * Register for notification on a file descriptor.
1233 * Returns the client_id;
1234 */
1235 uint32_t
_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)1236 _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)
1237 {
1238 client_t *c;
1239 uint32_t status;
1240
1241 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1242 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1243
1244 c = NULL;
1245
1246 if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1247
1248 status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
1249 if (status != NOTIFY_STATUS_OK)
1250 {
1251 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1252 return status;
1253 }
1254
1255 c->notify_type = NOTIFY_TYPE_FILE;
1256 c->fd = fd;
1257 *out_nid = c->name_info->name_id;
1258
1259 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1260 return NOTIFY_STATUS_OK;
1261 }
1262
1263 /*
1264 * Register for notification on a mach port.
1265 * Returns the client_id;
1266 */
1267 uint32_t
_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)1268 _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)
1269 {
1270 client_t *c;
1271 uint32_t status;
1272
1273 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1274 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1275
1276 c = NULL;
1277
1278 if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1279
1280 status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
1281 if (status != NOTIFY_STATUS_OK)
1282 {
1283 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1284 return status;
1285 }
1286
1287 c->notify_type = NOTIFY_TYPE_PORT;
1288 c->port = port;
1289 *out_nid = c->name_info->name_id;
1290
1291 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1292 return NOTIFY_STATUS_OK;
1293 }
1294
1295 /*
1296 * Plain registration - only for notify_check()
1297 * Returns the client_id.
1298 */
1299 uint32_t
_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)1300 _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)
1301 {
1302 client_t *c;
1303 uint32_t status;
1304
1305 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1306 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1307
1308 c = NULL;
1309
1310 if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1311
1312 status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
1313 if (status != NOTIFY_STATUS_OK)
1314 {
1315 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1316 return status;
1317 }
1318
1319 if (slot == (uint32_t)SLOT_NONE)
1320 {
1321 c->notify_type = NOTIFY_TYPE_PLAIN;
1322 }
1323 else
1324 {
1325 c->notify_type = NOTIFY_TYPE_MEMORY;
1326 c->name_info->slot = slot;
1327 }
1328
1329 *out_nid = c->name_info->name_id;
1330
1331 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1332 return NOTIFY_STATUS_OK;
1333 }
1334
1335 uint32_t
_notify_lib_set_owner(notify_state_t * ns,const char * name,uid_t uid,gid_t gid)1336 _notify_lib_set_owner(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
1337 {
1338 name_info_t *n;
1339
1340 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1341 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1342
1343 if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1344
1345 n = (name_info_t *)_nc_table_find(ns->name_table, name);
1346 if (n == NULL)
1347 {
1348 /* create new name */
1349 n = _internal_new_name(ns, name);
1350 if (n == NULL)
1351 {
1352 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1353 return NOTIFY_STATUS_FAILED;
1354 }
1355
1356 /*
1357 * Setting the refcount here allows the namespace to be "pre-populated"
1358 * with controlled names. notifyd does this for reserved names in
1359 * its configuration file.
1360 */
1361 n->refcount++;
1362 }
1363
1364 n->uid = uid;
1365 n->gid = gid;
1366
1367 _internal_insert_controlled_name(ns, n);
1368
1369 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1370 return NOTIFY_STATUS_OK;
1371 }
1372
1373 uint32_t
_notify_lib_get_owner(notify_state_t * ns,const char * name,uint32_t * uid,uint32_t * gid)1374 _notify_lib_get_owner(notify_state_t *ns, const char *name, uint32_t *uid, uint32_t *gid)
1375 {
1376 name_info_t *n;
1377 unsigned int i;
1378 int nlen, len;
1379
1380 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1381 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1382
1383 if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1384
1385 n = (name_info_t *)_nc_table_find(ns->name_table, name);
1386 if (n != NULL)
1387 {
1388 *uid = n->uid;
1389 *gid = n->gid;
1390 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1391 return NOTIFY_STATUS_OK;
1392 }
1393
1394 len = strlen(name);
1395
1396 for (i = 0; i < ns->controlled_name_count; i++)
1397 {
1398 n = ns->controlled_name[i];
1399 if (n == NULL) break;
1400
1401 nlen = strlen(n->name);
1402
1403 if (!strcmp(n->name, name))
1404 {
1405 *uid = n->uid;
1406 *gid = n->gid;
1407 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1408 return NOTIFY_STATUS_OK;
1409 }
1410
1411 /* check if this key is a prefix */
1412 if (nlen >= len) continue;
1413 if (strncmp(n->name, name, nlen)) continue;
1414
1415 *uid = n->uid;
1416 *gid = n->gid;
1417
1418 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1419 return NOTIFY_STATUS_OK;
1420 }
1421
1422 *uid = 0;
1423 *gid = 0;
1424
1425 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1426 return NOTIFY_STATUS_OK;
1427 }
1428
1429 uint32_t
_notify_lib_set_access(notify_state_t * ns,const char * name,uint32_t mode)1430 _notify_lib_set_access(notify_state_t *ns, const char *name, uint32_t mode)
1431 {
1432 name_info_t *n;
1433
1434 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1435 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1436
1437 if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1438
1439 n = (name_info_t *)_nc_table_find(ns->name_table, name);
1440 if (n == NULL)
1441 {
1442 /* create new name */
1443 n = _internal_new_name(ns, name);
1444 if (n == NULL)
1445 {
1446 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1447 return NOTIFY_STATUS_FAILED;
1448 }
1449
1450 /*
1451 * Setting the refcount here allows the namespace to be "pre-populated"
1452 * with controlled names. notifyd does this for reserved names in
1453 * its configuration file.
1454 */
1455 n->refcount++;
1456 }
1457
1458 n->access = mode;
1459
1460 _internal_insert_controlled_name(ns, n);
1461
1462 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1463 return NOTIFY_STATUS_OK;
1464 }
1465
1466 uint32_t
_notify_lib_get_access(notify_state_t * ns,const char * name,uint32_t * mode)1467 _notify_lib_get_access(notify_state_t *ns, const char *name, uint32_t *mode)
1468 {
1469 name_info_t *n;
1470 unsigned int i;
1471 int nlen, len;
1472
1473 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1474 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1475
1476 if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1477
1478 n = (name_info_t *)_nc_table_find(ns->name_table, name);
1479 if (n != NULL)
1480 {
1481 *mode = n->access;
1482
1483 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1484 return NOTIFY_STATUS_OK;
1485 }
1486
1487 len = strlen(name);
1488
1489 for (i = 0; i < ns->controlled_name_count; i++)
1490 {
1491 n = ns->controlled_name[i];
1492 if (n == NULL) break;
1493 if (n->name == NULL) continue;
1494
1495 nlen = strlen(n->name);
1496
1497 if (!strcmp(n->name, name))
1498 {
1499 *mode = n->access;
1500
1501 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1502 return NOTIFY_STATUS_OK;
1503 }
1504
1505 /* check if this key is a prefix */
1506 if (nlen >= len) continue;
1507 if (strncmp(n->name, name, nlen)) continue;
1508
1509 *mode = n->access;
1510
1511 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1512 return NOTIFY_STATUS_OK;
1513 }
1514
1515 *mode = NOTIFY_ACCESS_DEFAULT;
1516
1517 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1518 return NOTIFY_STATUS_OK;
1519 }
1520
1521 uint32_t
_notify_lib_release_name(notify_state_t * ns,const char * name,uid_t uid,gid_t gid)1522 _notify_lib_release_name(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
1523 {
1524 name_info_t *n;
1525
1526 (void)gid;
1527
1528 if (ns == NULL) return NOTIFY_STATUS_FAILED;
1529 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1530
1531 if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1532
1533 n = (name_info_t *)_nc_table_find(ns->name_table, name);
1534 if (n == NULL)
1535 {
1536 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1537 return NOTIFY_STATUS_INVALID_NAME;
1538 }
1539
1540 /* Owner and root may release */
1541 if ((n->uid != uid) && (uid != 0))
1542 {
1543 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1544 return NOTIFY_STATUS_NOT_AUTHORIZED;
1545 }
1546
1547 _internal_release_name_info(ns, n);
1548
1549 if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1550 return NOTIFY_STATUS_OK;
1551 }
1552