xref: /NextBSD/lib/libnotify/libnotify.c (revision 33da5adc555b3bc29986eeadca03829e4ad06b1e)
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