1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Windows to Solaris Identity Mapping kernel API
29  * This module provides the kernel cache.
30  */
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 
34 
35 #include <sys/types.h>
36 #include <sys/avl.h>
37 #include <sys/systm.h>
38 #include <sys/sysmacros.h>
39 #include <sys/ksynch.h>
40 #include <sys/cmn_err.h>
41 #include <sys/kidmap.h>
42 #include "idmap_prot.h"
43 #include "kidmap_priv.h"
44 
45 
46 /*
47  * External functions
48  */
49 extern	uintptr_t	space_fetch(char *key);
50 extern	int		space_store(char *key, uintptr_t ptr);
51 
52 
53 /*
54  * Internal definitions and functions
55  */
56 
57 #define	CACHE_TRIGGER_SIZE	8192
58 #define	CACHE_PURGE_INTERVAL	(60 * 3)
59 
60 typedef struct sid_prefix_node {
61 	avl_node_t	avl_link;
62 	const char 	*sid_prefix;
63 } sid_prefix_node_t;
64 
65 
66 typedef struct entry {
67 	avl_node_t	avl_link;
68 	const char 	*sid_prefix;
69 	uint32_t	rid;
70 	uid_t		pid;
71 	int		is_user;
72 	time_t		ttl;
73 } entry_t;
74 
75 typedef int (*avl_comp_fn)(const void*, const void*);
76 
77 
78 struct sid_prefix_store {
79 	struct avl_tree	tree;
80 	krwlock_t	lock;
81 };
82 
83 struct sid_prefix_store *kidmap_sid_prefix_store = NULL;
84 
85 
86 
87 static void
88 kidmap_cache_purge_avl(idmap_avl_cache_t *cache);
89 
90 /*
91  * kidmap_strdup() copied from uts/common/fs/sockfs/nl7c.c
92  */
93 static char *
94 kidmap_strdup(const char *s)
95 {
96 	int	len = strlen(s) + 1;
97 	char	*ret = kmem_alloc(len, KM_SLEEP);
98 
99 	bcopy(s, ret, len);
100 	return (ret);
101 }
102 
103 
104 static int
105 kidmap_compare_sid(const entry_t *entry1, const entry_t *entry2)
106 {
107 	int comp = entry2->rid - entry1->rid;
108 
109 	if (comp == 0)
110 		comp = strcmp(entry2->sid_prefix, entry1->sid_prefix);
111 
112 	if (comp < 0)
113 		comp = -1;
114 	else if (comp > 0)
115 		comp = 1;
116 
117 	return (comp);
118 }
119 
120 
121 static int
122 kidmap_compare_pid(const entry_t *entry1, const entry_t *entry2)
123 {
124 	int comp = entry2->pid - entry1->pid;
125 
126 	if (comp == 0)
127 		comp = entry2->is_user - entry1->is_user;
128 
129 	if (comp < 0)
130 		comp = -1;
131 	else if (comp > 0)
132 		comp = 1;
133 
134 	return (comp);
135 }
136 
137 
138 static int
139 kidmap_compare_sid_prefix(const sid_prefix_node_t *entry1,
140 			const sid_prefix_node_t *entry2)
141 {
142 	int comp;
143 
144 	comp = strcmp(entry2->sid_prefix, entry1->sid_prefix);
145 
146 	if (comp < 0)
147 		comp = -1;
148 	else if (comp > 0)
149 		comp = 1;
150 
151 	return (comp);
152 }
153 
154 
155 void
156 kidmap_cache_create(idmap_cache_t *cache)
157 {
158 	typedef int (*comp)(const void*, const void*);
159 
160 	rw_init(&cache->sid.lock, NULL, RW_DRIVER, NULL);
161 	avl_create(&cache->sid.tree, (avl_comp_fn)kidmap_compare_sid,
162 	    sizeof (entry_t), offsetof(entry_t, avl_link));
163 	mutex_init(&cache->sid.mutex, NULL, MUTEX_DEFAULT, NULL);
164 	cache->sid.state = CACHE_CREATED;
165 	cache->sid.purge_time = 0;
166 
167 	rw_init(&cache->pid.lock, NULL, RW_DRIVER, NULL);
168 	avl_create(&cache->pid.tree, (avl_comp_fn)kidmap_compare_pid,
169 	    sizeof (entry_t), offsetof(entry_t, avl_link));
170 	mutex_init(&cache->pid.mutex, NULL, MUTEX_DEFAULT, NULL);
171 	cache->pid.state = CACHE_CREATED;
172 	cache->pid.purge_time = 0;
173 }
174 
175 
176 void
177 kidmap_cache_delete(idmap_cache_t *cache)
178 {
179 	entry_t *entry;
180 	void *cookie;
181 
182 	cookie = NULL;
183 	while ((entry = avl_destroy_nodes(&cache->pid.tree, &cookie))
184 	    != NULL) {
185 		kmem_free(entry, sizeof (entry_t));
186 	}
187 	avl_destroy(&cache->pid.tree);
188 	rw_destroy(&cache->pid.lock);
189 	mutex_destroy(&cache->pid.mutex);
190 
191 	cookie = NULL;
192 	while ((entry = avl_destroy_nodes(&cache->sid.tree, &cookie))
193 	    != NULL) {
194 		kmem_free(entry, sizeof (entry_t));
195 	}
196 	avl_destroy(&cache->sid.tree);
197 	rw_destroy(&cache->sid.lock);
198 	mutex_destroy(&cache->sid.mutex);
199 }
200 
201 
202 int
203 kidmap_cache_lookupbypid(idmap_cache_t *cache, const char **sid_prefix,
204 			uint32_t *rid, uid_t pid, int is_user)
205 
206 {
207 	entry_t		entry;
208 	entry_t		*result;
209 	avl_index_t	where;
210 	int		status;
211 	time_t		now = gethrestime_sec();
212 
213 	entry.pid = pid;
214 	entry.is_user = is_user;
215 
216 	rw_enter(&cache->pid.lock, RW_READER);
217 
218 	result = avl_find(&cache->pid.tree, &entry, &where);
219 
220 	if (result && result->ttl > now) {
221 		*sid_prefix = result->sid_prefix;
222 		*rid = result->rid;
223 		status = IDMAP_SUCCESS;
224 	} else
225 		status = IDMAP_ERR_NOMAPPING;
226 
227 	rw_exit(&cache->pid.lock);
228 
229 	return (status);
230 }
231 
232 
233 int
234 kidmap_cache_lookupbysid(idmap_cache_t *cache, const char *sid_prefix,
235 			uint32_t rid, uid_t *pid, int *is_user)
236 {
237 	entry_t		entry;
238 	entry_t		*result;
239 	avl_index_t	where;
240 	int		status;
241 	time_t		now = gethrestime_sec();
242 
243 	entry.sid_prefix = sid_prefix;
244 	entry.rid = rid;
245 
246 	rw_enter(&cache->sid.lock, RW_READER);
247 
248 	result = avl_find(&cache->sid.tree, &entry, &where);
249 
250 	if (result && result->ttl > now) {
251 		*pid = result->pid;
252 		*is_user = result->is_user;
253 		status = IDMAP_SUCCESS;
254 	} else
255 		status = IDMAP_ERR_NOMAPPING;
256 
257 	rw_exit(&cache->sid.lock);
258 
259 	return (status);
260 }
261 
262 
263 void
264 kidmap_cache_addbypid(idmap_cache_t *cache, const char *sid_prefix,
265 			uint32_t rid, uid_t pid, int is_user, time_t ttl)
266 {
267 	entry_t		find;
268 	entry_t		*result;
269 	entry_t		*new;
270 	avl_index_t	where;
271 	int		purge_required = FALSE;
272 
273 	find.pid = pid;
274 	find.is_user = is_user;
275 
276 	rw_enter(&cache->pid.lock, RW_WRITER);
277 	result = avl_find(&cache->pid.tree, &find, &where);
278 
279 	if (result) {
280 		result->sid_prefix = sid_prefix;
281 		result->rid = rid;
282 		result->ttl = ttl;
283 	} else {
284 		new = kmem_alloc(sizeof (entry_t), KM_SLEEP);
285 		new->pid = pid;
286 		new->is_user = is_user;
287 		new->sid_prefix = sid_prefix;
288 		new->rid = rid;
289 		new->ttl = ttl;
290 
291 		avl_insert(&cache->pid.tree, new, where);
292 		if ((avl_numnodes(&cache->pid.tree) > CACHE_TRIGGER_SIZE) &&
293 		    (cache->pid.purge_time + CACHE_PURGE_INTERVAL <
294 		    gethrestime_sec()))
295 			purge_required = TRUE;
296 	}
297 
298 	rw_exit(&cache->pid.lock);
299 
300 	if (purge_required)
301 		kidmap_cache_purge_avl(&cache->pid);
302 }
303 
304 
305 void
306 kidmap_cache_addbysid(idmap_cache_t *cache, const char *sid_prefix,
307 			uint32_t rid, uid_t pid, int is_user, time_t ttl)
308 
309 {
310 	entry_t find;
311 	entry_t *result;
312 	entry_t *new;
313 	avl_index_t where;
314 	int purge_required = FALSE;
315 
316 	find.sid_prefix = sid_prefix;
317 	find.rid = rid;
318 
319 	rw_enter(&cache->sid.lock, RW_WRITER);
320 	result = avl_find(&cache->sid.tree, &find, &where);
321 
322 	if (result) {
323 		result->pid = pid;
324 		result->is_user = is_user;
325 		result->ttl = ttl;
326 	} else {
327 		new = kmem_alloc(sizeof (entry_t), KM_SLEEP);
328 		new->pid = pid;
329 		new->is_user = is_user;
330 		new->sid_prefix = sid_prefix;
331 		new->rid = rid;
332 		new->ttl = ttl;
333 
334 		avl_insert(&cache->sid.tree, new, where);
335 
336 		if ((avl_numnodes(&cache->sid.tree) > CACHE_TRIGGER_SIZE) &&
337 		    (cache->sid.purge_time + CACHE_PURGE_INTERVAL <
338 		    gethrestime_sec()))
339 			purge_required = TRUE;
340 	}
341 
342 	rw_exit(&cache->sid.lock);
343 
344 	if (purge_required)
345 		kidmap_cache_purge_avl(&cache->sid);
346 }
347 
348 
349 static void
350 kidmap_cache_purge_avl(idmap_avl_cache_t *cache)
351 {
352 	time_t		now = gethrestime_sec();
353 	entry_t		*curr;
354 	entry_t		*prev = NULL;
355 
356 	mutex_enter(&cache->mutex);
357 	if (cache->state != CACHE_CREATED) {
358 			mutex_exit(&cache->mutex);
359 			return;
360 	}
361 	cache->state = CACHE_PURGING;
362 	mutex_exit(&cache->mutex);
363 
364 	rw_enter(&cache->lock, RW_READER);
365 	curr = avl_first(&cache->tree);
366 	while (curr != NULL) {
367 		if (curr->ttl < now) {
368 			/* Old entry to remove - we need a write lock */
369 			if (rw_tryupgrade(&cache->lock) == 0) {
370 				/*
371 				 * Could not upgrade lock so release lock
372 				 * and aquire the write lock. It is valid to
373 				 * release abd re-aquire the lock as there
374 				 * can only be one purge routine running on an
375 				 * avl tree and no other routine removes
376 				 * entries.
377 				 */
378 				rw_exit(&cache->lock);
379 				rw_enter(&cache->lock, RW_WRITER);
380 			}
381 			/* Old entry to remove */
382 			avl_remove(&cache->tree, curr);
383 			rw_downgrade(&cache->lock);
384 
385 			curr = prev;
386 			if (curr == NULL) {
387 				/* We removed the first entery */
388 				curr = avl_first(&cache->tree);
389 				continue;
390 			}
391 		}
392 		prev = curr;
393 		curr = AVL_NEXT(&cache->tree, curr);
394 	}
395 	rw_exit(&cache->lock);
396 
397 	mutex_enter(&cache->mutex);
398 	cache->state = CACHE_CREATED;
399 	cache->purge_time = now;
400 	mutex_exit(&cache->mutex);
401 }
402 
403 void
404 kidmap_sid_prefix_store_init(void)
405 {
406 	kidmap_sid_prefix_store = (struct sid_prefix_store *)
407 	    space_fetch("SUNW,idmap_sid_prefix");
408 	if (kidmap_sid_prefix_store == NULL) {
409 		kidmap_sid_prefix_store = kmem_alloc(
410 		    sizeof (struct sid_prefix_store), KM_SLEEP);
411 		rw_init(&kidmap_sid_prefix_store->lock, NULL, RW_DRIVER, NULL);
412 		avl_create(&kidmap_sid_prefix_store->tree,
413 		    (avl_comp_fn)kidmap_compare_sid_prefix,
414 		    sizeof (sid_prefix_node_t),
415 		    offsetof(sid_prefix_node_t, avl_link));
416 		(void) space_store("SUNW,idmap_sid_prefix",
417 		    (uintptr_t)kidmap_sid_prefix_store);
418 	} else {
419 		/*
420 		 * The AVL comparison function must be re-initialised on
421 		 * re-load because may not be loaded into the same
422 		 * address space.
423 		 */
424 		kidmap_sid_prefix_store->tree.avl_compar =
425 		    (avl_comp_fn)kidmap_compare_sid_prefix;
426 	}
427 }
428 
429 
430 const char *
431 kidmap_find_sid_prefix(const char *sid_prefix) {
432 	sid_prefix_node_t 	find;
433 	sid_prefix_node_t	*result;
434 	sid_prefix_node_t 	*new;
435 	avl_index_t		where;
436 
437 	if (sid_prefix == NULL || *sid_prefix == '\0')
438 		return (NULL);
439 
440 	find.sid_prefix = sid_prefix;
441 
442 
443 	rw_enter(&kidmap_sid_prefix_store->lock, RW_READER);
444 
445 	result = avl_find(&kidmap_sid_prefix_store->tree, &find, &where);
446 
447 	if (result) {
448 		rw_exit(&kidmap_sid_prefix_store->lock);
449 		return (result->sid_prefix);
450 	}
451 
452 	if (rw_tryupgrade(&kidmap_sid_prefix_store->lock) == 0) {
453 		/*
454 		 * Could not upgrade lock so release lock
455 		 * and aquire the write lock
456 		 */
457 		rw_exit(&kidmap_sid_prefix_store->lock);
458 		rw_enter(&kidmap_sid_prefix_store->lock, RW_WRITER);
459 
460 		result = avl_find(&kidmap_sid_prefix_store->tree,
461 			&find, &where);
462 		if (result) {
463 			rw_exit(&kidmap_sid_prefix_store->lock);
464 			return (result->sid_prefix);
465 		}
466 	}
467 
468 	new = kmem_alloc(sizeof (sid_prefix_node_t), KM_SLEEP);
469 	new->sid_prefix = kidmap_strdup(sid_prefix);
470 	avl_insert(&kidmap_sid_prefix_store->tree, new, where);
471 	rw_exit(&kidmap_sid_prefix_store->lock);
472 
473 	return (new->sid_prefix);
474 }
475