xref: /titanic_52/usr/src/cmd/nscd/getgr.c (revision 82d33c01b078ed404a986a369750cdb4743773fb)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Routines to handle getgr* calls in nscd
31  */
32 
33 #include <assert.h>
34 #include <errno.h>
35 #include <memory.h>
36 #include <signal.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/door.h>
41 #include <sys/stat.h>
42 #include <sys/time.h>
43 #include <sys/types.h>
44 #include <sys/wait.h>
45 #include <thread.h>
46 #include <unistd.h>
47 #include <ucred.h>
48 #include <nss_common.h>
49 
50 #include <getxby_door.h>
51 #include "server_door.h"
52 #include "nscd.h"
53 
54 static hash_t *uid_hash;
55 static hash_t *nam_hash;
56 static mutex_t  group_lock = DEFAULTMUTEX;
57 static waiter_t group_wait;
58 
59 static void getgr_gidkeepalive(int keep, int interval);
60 static void getgr_namekeepalive(int keep, int interval);
61 static int update_gr_bucket(nsc_bucket_t **old, nsc_bucket_t *new,
62 	int callnumber);
63 static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen);
64 static void do_findgids(nsc_bucket_t *ptr, int *table, int gid);
65 static void do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam);
66 static void do_invalidate(nsc_bucket_t **ptr, int callnumber);
67 static void getgr_invalidate_unlocked(void);
68 
69 
70 void
71 getgr_init(void)
72 {
73 	uid_hash = make_ihash(current_admin.group.nsc_suggestedsize);
74 	nam_hash = make_hash(current_admin.group.nsc_suggestedsize);
75 
76 }
77 
78 static void
79 do_invalidate(nsc_bucket_t **ptr, int callnumber)
80 {
81 	if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) {
82 		/* leave pending calls alone */
83 		update_gr_bucket(ptr, NULL, callnumber);
84 	}
85 }
86 
87 static void
88 do_findgids(nsc_bucket_t *ptr, int *table, int gid)
89 {
90 
91 	/*
92 	 * be careful with ptr - it may be -1 or NULL.
93 	 */
94 	if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
95 		insertn(table, ptr->nsc_hits, gid);
96 	}
97 }
98 
99 static void
100 do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam)
101 {
102 
103 	/*
104 	 * be careful with ptr - it may be -1 or NULL.
105 	 */
106 
107 	if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
108 		char *tmp = (char *)insertn(table, ptr->nsc_hits,
109 					(int)strdup(gnam));
110 		if (tmp != (char *)-1)
111 			free(tmp);
112 	}
113 }
114 
115 void
116 getgr_revalidate(void)
117 {
118 	for (;;) {
119 		int slp;
120 		int interval;
121 		int count;
122 
123 		slp = current_admin.group.nsc_pos_ttl;
124 
125 		if (slp < 60) {
126 			slp = 60;
127 		}
128 
129 		if ((count = current_admin.group.nsc_keephot) != 0) {
130 			interval = (slp / 2)/count;
131 			if (interval == 0) interval = 1;
132 			sleep(slp * 2 / 3);
133 			getgr_gidkeepalive(count, interval);
134 			getgr_namekeepalive(count, interval);
135 		} else {
136 			sleep(slp);
137 		}
138 	}
139 }
140 
141 static void
142 getgr_gidkeepalive(int keep, int interval)
143 {
144 	int *table;
145 	nsc_data_t  ping;
146 	int i;
147 
148 	if (!keep)
149 		return;
150 
151 	table = maken(keep);
152 	mutex_lock(&group_lock);
153 	operate_hash(uid_hash, do_findgids, (char *)table);
154 	mutex_unlock(&group_lock);
155 
156 	for (i = 1; i <= keep; i++) {
157 	    ping.nsc_call.nsc_callnumber = GETGRGID;
158 	    if ((ping.nsc_call.nsc_u.gid = table[keep + 1 + i]) == -1)
159 		continue; /* unused slot in table */
160 	    launch_update(&ping.nsc_call);
161 	    sleep(interval);
162 	}
163 	free(table);
164 }
165 
166 static void
167 getgr_namekeepalive(int keep, int interval)
168 {
169 	int *table;
170 	union {
171 		nsc_data_t  ping;
172 		char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN];
173 	} u;
174 
175 	int i;
176 
177 	if (!keep)
178 		return;
179 
180 	table = maken(keep);
181 	mutex_lock(&group_lock);
182 	operate_hash(nam_hash, do_findgnams, (char *)table);
183 	mutex_unlock(&group_lock);
184 
185 	for (i = 1; i <= keep; i++) {
186 		char *tmp;
187 		u.ping.nsc_call.nsc_callnumber = GETGRNAM;
188 
189 		if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
190 			continue; /* unused slot in table */
191 
192 		strcpy(u.ping.nsc_call.nsc_u.name, tmp);
193 
194 		launch_update(&u.ping.nsc_call);
195 		sleep(interval);
196 	}
197 
198 	for (i = 1; i <= keep; i++) {
199 		char *tmp;
200 		if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
201 			free(tmp);
202 	}
203 
204 	free(table);
205 }
206 
207 
208 /*
209  *   This routine marks all entries as invalid
210  *
211  */
212 
213 void
214 getgr_invalidate(void)
215 {
216 	mutex_lock(&group_lock);
217 	getgr_invalidate_unlocked();
218 	mutex_unlock(&group_lock);
219 }
220 
221 static void
222 getgr_invalidate_unlocked(void)
223 {
224 	operate_hash_addr(nam_hash, do_invalidate, (char *)GETGRNAM);
225 	operate_hash_addr(uid_hash, do_invalidate, (char *)GETGRGID);
226 	current_admin.group.nsc_invalidate_count++;
227 }
228 
229 void
230 getgr_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now)
231 {
232 	int		out_of_date;
233 	nsc_bucket_t	*retb;
234 	char 		**bucket;
235 
236 	static time_t	lastmod;
237 
238 	int bufferspace = maxsize - sizeof (nsc_return_t);
239 
240 	if (current_admin.group.nsc_enabled == 0) {
241 		out->nsc_return_code = NOSERVER;
242 		out->nsc_bufferbytesused = sizeof (*out);
243 		return;
244 	}
245 
246 	mutex_lock(&group_lock);
247 
248 	if (current_admin.group.nsc_check_files) {
249 		struct stat buf;
250 
251 		if (stat("/etc/group", &buf) < 0) {
252 			/*EMPTY*/;
253 		} else if (lastmod == 0) {
254 			lastmod = buf.st_mtime;
255 		} else if (lastmod < buf.st_mtime) {
256 			getgr_invalidate_unlocked();
257 			lastmod = buf.st_mtime;
258 		}
259 	}
260 
261 	if (current_admin.debug_level >= DBG_ALL) {
262 		if (MASKUPDATEBIT(in->nsc_callnumber) == GETGRGID) {
263 			logit("getgr_lookup: looking for gid %d\n",
264 				in->nsc_u.gid);
265 		} else {
266 			logit("getgr_lookup: looking for name %s\n",
267 				in->nsc_u.name);
268 		}
269 	}
270 
271 	for (;;) {
272 		if (MASKUPDATEBIT(in->nsc_callnumber) == GETGRGID) {
273 			bucket = get_hash(uid_hash, (char *)in->nsc_u.gid);
274 		} else {
275 			if (strlen(in->nsc_u.name) > NSCDMAXNAMELEN) {
276 				ucred_t *uc = NULL;
277 
278 				if (door_ucred(&uc) != 0) {
279 					logit("getgr_lookup: Name too long, "
280 					    "but no user credential: %s\n",
281 					    strerror(errno));
282 				} else {
283 					logit("getgr_lookup: Name too long "
284 					    "from pid %d uid %d\n",
285 					    ucred_getpid(uc),
286 					    ucred_getruid(uc));
287 					ucred_free(uc);
288 				}
289 
290 				out->nsc_errno = NSS_NOTFOUND;
291 				out->nsc_return_code = NOTFOUND;
292 				out->nsc_bufferbytesused = sizeof (*out);
293 				goto getout;
294 			}
295 			bucket = get_hash(nam_hash, in->nsc_u.name);
296 		}
297 
298 		if (*bucket == (char *)-1) {	/* pending lookup */
299 			if (get_clearance(in->nsc_callnumber) != 0) {
300 				/*
301 				 * no threads available
302 				 * cannot process now
303 				 */
304 				out->nsc_return_code = NOSERVER;
305 				out->nsc_bufferbytesused = sizeof (*out);
306 				current_admin.group.nsc_throttle_count++;
307 				goto getout;
308 			}
309 			nscd_wait(&group_wait, &group_lock, bucket);
310 			release_clearance(in->nsc_callnumber);
311 			continue; /* go back and relookup hash bucket */
312 		}
313 		break;
314 	}
315 
316 	/*
317 	 * check for no name_service mode
318 	 */
319 
320 	if (*bucket == NULL && current_admin.avoid_nameservice) {
321 		out->nsc_return_code = NOTFOUND;
322 		out->nsc_bufferbytesused = sizeof (*out);
323 	} else if ((*bucket == NULL) ||	/* New entry in name service */
324 	    (in->nsc_callnumber & UPDATEBIT) || /* needs updating */
325 	    (out_of_date = (!current_admin.avoid_nameservice &&
326 		(current_admin.group.nsc_old_data_ok == 0) &&
327 		(((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) {
328 		/* time has expired */
329 		int saved_errno;
330 		int saved_hits = 0;
331 		struct group *p;
332 
333 		if (get_clearance(in->nsc_callnumber) != 0) {
334 			/* no threads available */
335 			out->nsc_return_code = NOSERVER;
336 			/* cannot process now */
337 			out->nsc_bufferbytesused = sizeof (*out);
338 			current_admin.group.nsc_throttle_count++;
339 			goto getout;
340 		}
341 
342 		if (*bucket != NULL) {
343 			saved_hits = ((nsc_bucket_t *)*bucket)->nsc_hits;
344 		}
345 
346 		/*
347 		 *  block any threads accessing this bucket if data is
348 		 *  non-existent out of date
349 		 */
350 
351 		if (*bucket == NULL || out_of_date) {
352 			update_gr_bucket((nsc_bucket_t **)bucket,
353 					(nsc_bucket_t *)-1,
354 					in->nsc_callnumber);
355 		} else {
356 			/*
357 			 * if still not -1 bucket we are doing update...
358 			 * mark to prevent pileups of threads if the name
359 			 * service is hanging....
360 			 */
361 			((nsc_bucket_t *)(*bucket))->nsc_status |=
362 			    ST_UPDATE_PENDING;
363 			/* cleared by deletion of old data */
364 		}
365 		mutex_unlock(&group_lock);
366 
367 		if (MASKUPDATEBIT(in->nsc_callnumber) == GETGRGID) {
368 			p = _uncached_getgrgid_r(in->nsc_u.gid, &out->nsc_u.grp,
369 			    out->nsc_u.buff + sizeof (struct group),
370 			    bufferspace);
371 			saved_errno = errno;
372 		} else {
373 			p = _uncached_getgrnam_r(in->nsc_u.name,
374 				&out->nsc_u.grp,
375 				out->nsc_u.buff + sizeof (struct group),
376 				bufferspace);
377 			saved_errno = errno;
378 		}
379 
380 		mutex_lock(&group_lock);
381 
382 		release_clearance(in->nsc_callnumber);
383 
384 		if (p == NULL) { /* data not found */
385 			if (current_admin.debug_level >= DBG_CANT_FIND) {
386 				if (MASKUPDATEBIT(in->nsc_callnumber) ==
387 				    GETGRGID) {
388 			logit("getgr_lookup: nscd COULDN'T FIND gid %d\n",
389 						in->nsc_u.gid);
390 				} else {
391 		logit("getgr_lookup: nscd COULDN'T FIND group name %s\n",
392 						in->nsc_u.name);
393 				}
394 			}
395 
396 
397 			if (!(UPDATEBIT & in->nsc_callnumber))
398 			    current_admin.group.nsc_neg_cache_misses++;
399 
400 			retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t));
401 
402 			retb->nsc_refcount = 1;
403 			retb->nsc_data.nsc_bufferbytesused =
404 				sizeof (nsc_return_t);
405 			retb->nsc_data.nsc_return_code = NOTFOUND;
406 			retb->nsc_data.nsc_errno = saved_errno;
407 			memcpy(out, &retb->nsc_data,
408 				retb->nsc_data.nsc_bufferbytesused);
409 			update_gr_bucket((nsc_bucket_t **)bucket,
410 					retb,
411 					in->nsc_callnumber);
412 			goto getout;
413 		} else {
414 			if (current_admin.debug_level >= DBG_ALL) {
415 				if (MASKUPDATEBIT(in->nsc_callnumber) ==
416 				    GETGRGID) {
417 		logit("getgr_lookup: nscd FOUND gid %d\n",
418 						in->nsc_u.gid);
419 				} else {
420 		logit("getgr_lookup: nscd FOUND group name %s\n",
421 						in->nsc_u.name);
422 				}
423 			}
424 			if (!(UPDATEBIT & in->nsc_callnumber))
425 			    current_admin.group.nsc_pos_cache_misses++;
426 
427 			retb = fixbuffer(out, bufferspace);
428 			update_gr_bucket((nsc_bucket_t **)bucket,
429 					retb,
430 					in->nsc_callnumber);
431 			if (saved_hits)
432 				retb->nsc_hits = saved_hits;
433 		}
434 	} else { 	/* found entry in cache */
435 		retb = (nsc_bucket_t *)*bucket;
436 
437 		retb->nsc_hits++;
438 
439 		memcpy(out, &(retb->nsc_data),
440 			retb->nsc_data.nsc_bufferbytesused);
441 
442 		if (out->nsc_return_code == SUCCESS) {
443 			if (!(UPDATEBIT & in->nsc_callnumber))
444 			    current_admin.group.nsc_pos_cache_hits++;
445 			if (current_admin.debug_level >= DBG_ALL) {
446 				if (MASKUPDATEBIT(in->nsc_callnumber) ==
447 				    GETGRGID) {
448 			logit("getgr_lookup: found gid %d in cache\n",
449 						in->nsc_u.gid);
450 				} else {
451 			logit("getgr_lookup: found name %s in cache\n",
452 						in->nsc_u.name);
453 				}
454 			}
455 		} else {
456 			if (!(UPDATEBIT & in->nsc_callnumber))
457 			    current_admin.group.nsc_neg_cache_hits++;
458 			if (current_admin.debug_level >= DBG_ALL) {
459 				if (MASKUPDATEBIT(in->nsc_callnumber) ==
460 				    GETGRGID) {
461 		logit("getgr_lookup: %d marked as NOT FOUND in cache.\n",
462 						in->nsc_u.gid);
463 				} else {
464 		logit("getgr_lookup: %s marked as NOT FOUND in cache.\n",
465 						in->nsc_u.name);
466 				}
467 			}
468 		}
469 
470 		if ((retb->nsc_timestamp < now) &&
471 			!(in->nsc_callnumber & UPDATEBIT) &&
472 			!(retb->nsc_status & ST_UPDATE_PENDING)) {
473 		logit("launch update since time = %d\n", retb->nsc_timestamp);
474 			retb->nsc_status |= ST_UPDATE_PENDING;
475 			/* cleared by deletion of old data */
476 			launch_update(in);
477 		}
478 	}
479 
480 getout:
481 
482 	mutex_unlock(&group_lock);
483 }
484 
485 /*ARGSUSED*/
486 static int
487 update_gr_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber)
488 {
489 	if (*old != NULL && *old != (nsc_bucket_t *)-1) { /* old data exists */
490 		free(*old);
491 		current_admin.group.nsc_entries--;
492 	}
493 
494 	/*
495 	 *  we can do this before reseting *old since we're holding the lock
496 	 */
497 
498 	else if (*old == (nsc_bucket_t *)-1) {
499 		nscd_signal(&group_wait, (char **)old);
500 	}
501 
502 
503 	*old = new;
504 
505 	if ((new != NULL) && (new != (nsc_bucket_t *)-1)) {
506 		/* real data, not just update pending or invalidate */
507 
508 		new->nsc_hits = 1;
509 		new->nsc_status = 0;
510 		new->nsc_refcount = 1;
511 		current_admin.group.nsc_entries++;
512 
513 		if (new->nsc_data.nsc_return_code == SUCCESS) {
514 			new->nsc_timestamp = time(NULL) +
515 				current_admin.group.nsc_pos_ttl;
516 		} else {
517 			new->nsc_timestamp = time(NULL) +
518 				current_admin.group.nsc_neg_ttl;
519 		}
520 	}
521 	return (0);
522 }
523 
524 
525 /*ARGSUSED*/
526 static nsc_bucket_t *
527 fixbuffer(nsc_return_t *in, int maxlen)
528 {
529 	int	group_members;
530 	int	i;
531 	nsc_bucket_t *retb;
532 	nsc_return_t *out;
533 	char 	*dest;
534 	int 	offset;
535 	int 	strs;
536 	char **members;
537 	int pwlen;
538 
539 	/*
540 	 * find out the size of the data block we're going to need
541 	 */
542 
543 	strs = 0;
544 	strs += 1 + strlen(in->nsc_u.grp.gr_name);
545 	pwlen = strlen(in->nsc_u.grp.gr_passwd);
546 	if (pwlen < 4)
547 	    pwlen = 4;
548 	strs += 1 + pwlen;
549 
550 	group_members = 0;
551 	while (in->nsc_u.grp.gr_mem[group_members]) {
552 		strs += 1 + strlen(in->nsc_u.grp.gr_mem[group_members]);
553 		group_members++;
554 	}
555 
556 	strs += (group_members+1) * sizeof (char *);
557 
558 	/*
559 	 * allocate it and copy it in
560 	 * code doesn't assume packing order in original buffer
561 	 */
562 
563 	if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) {
564 		return (NULL);
565 	}
566 
567 	out = &(retb->nsc_data);
568 	out->nsc_bufferbytesused = strs + ((int)&out->nsc_u.grp - (int)out) +
569 					sizeof (struct group);
570 	out->nsc_return_code 	= SUCCESS;
571 	out->nsc_errno 		= 0;
572 
573 
574 	out->nsc_u.grp.gr_gid = in->nsc_u.grp.gr_gid;
575 
576 	dest = retb->nsc_data.nsc_u.buff + sizeof (struct group);
577 	offset = (int)dest;
578 
579 	members = (char **)dest;
580 	out->nsc_u.grp.gr_mem = (char **)(dest - offset);
581 	dest += (group_members+1) * sizeof (char *);
582 
583 
584 	strcpy(dest, in->nsc_u.grp.gr_name);
585 	strs = 1 + strlen(in->nsc_u.grp.gr_name);
586 	out->nsc_u.grp.gr_name = dest - offset;
587 	dest += strs;
588 
589 	strcpy(dest, in->nsc_u.grp.gr_passwd);
590 	strs = 1 + pwlen;
591 	out->nsc_u.grp.gr_passwd = dest - offset;
592 	dest += strs;
593 
594 	for (i = 0; i < group_members; i++) {
595 		members[i] = dest - offset;
596 		strcpy(dest, in->nsc_u.grp.gr_mem[i]);
597 		strs = 1 + strlen(in->nsc_u.grp.gr_mem[i]);
598 		dest += strs;
599 	}
600 	members[i] = NULL; /* null terminate list */
601 	memcpy(in, out, out->nsc_bufferbytesused);
602 
603 	return (retb);
604 }
605 
606 void
607 getgr_uid_reaper()
608 {
609 	nsc_reaper("gr_uid", uid_hash, &current_admin.group, &group_lock);
610 }
611 
612 void
613 getgr_nam_reaper()
614 {
615 	nsc_reaper("gr_nam", nam_hash, &current_admin.group, &group_lock);
616 }
617