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 2008 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 #include "lint.h"
30 #include <mtlib.h>
31 #include <sys/types.h>
32 #include <errno.h>
33 #include <pwd.h>
34 #include <nss_dbdefs.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <synch.h>
38 #include <sys/param.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <getxby_door.h>
43 #include <sys/door.h>
44 #include <procfs.h>
45 #include <door.h>
46 #include <sys/mman.h>
47 #include "libc.h"
48 #include "tsd.h"
49 #include "base_conversion.h"
50 
51 /* nss<->door hints */
52 static mutex_t	hints_lock = DEFAULTMUTEX;
53 static size_t	door_bsize = 0;
54 static size_t	door_nbsize = 0;
55 static int	proc_is_cache = -1;
56 
57 /* library<->nscd door interaction apis */
58 
59 /*
60  *
61  * Routine that actually performs the door call.
62  * Note that we cache a file descriptor.  We do
63  * the following to prevent disasters:
64  *
65  * 1) Never use 0,1 or 2; if we get this from the open
66  *    we dup it upwards.
67  *
68  * 2) Set the close on exec flags so descriptor remains available
69  *    to child processes.
70  *
71  * 3) Verify that the door is still the same one we had before
72  *    by using door_info on the client side.
73  *
74  *	Note that we never close the file descriptor if it isn't one
75  *	we allocated; we check this with door info.  The rather tricky
76  *	logic is designed to be fast in the normal case (fd is already
77  *	allocated and is ok) while handling the case where the application
78  *	closed it underneath us or where the nscd dies or re-execs itself
79  *	and we're a multi-threaded application.  Note that we cannot protect
80  *	the application if it closes the fd and it is multi-threaded.
81  *
82  *  int _nsc_trydoorcall(void *dptr, size_t *bufsize, size_t *actualsize);
83  *
84  *      *dptr           IN: points to arg buffer OUT: points to results buffer
85  *      *bufsize        IN: overall size of buffer OUT: overall size of buffer
86  *      *actualsize     IN: size of call data OUT: size of return data
87  *
88  *  Note that *dptr may change if provided space as defined by *bufsize is
89  *  inadequate.  In this case the door call mmaps more space and places
90  *  the answer there and sets dptr to contain a pointer to the space, which
91  *  should be freed with munmap.
92  *
93  *  Returns 0 if the door call reached the server, -1 if contact was not made.
94  *
95  */
96 
97 /*
98  * Max size for list of db names supported by the private nscd
99  * No implied max here, any size will do, fixed size chosen to
100  * reduce yet another malloc
101  */
102 
103 #define	BD_BUFSIZE	1024
104 #define	BD_SEP		','
105 
106 typedef struct _nsc_door_t {
107 	int 		doorfd;
108 	mutex_t		door_lock;
109 	door_info_t 	doori;
110 } nsc_door_t;
111 
112 static nsc_door_t	nsc_door[2] = {
113 	{ -1, DEFAULTMUTEX, { 0 } },		/* front (fattached) door */
114 	{ -1, DEFAULTMUTEX, { 0 } },		/* back (private) door */
115 };
116 
117 /* assumed to be locked by using nsc_door[1] mutex */
118 static char	*nsc_db_buf = NULL;
119 static char	**nsc_db_list = NULL;
120 
121 /*
122  * Check for a valid and matching db in the list.
123  * assume list is in the locked state.
124  */
125 
126 static int
127 _nsc_use_backdoor(char *db)
128 {
129 	char 	**ndb;
130 
131 	if (db && nsc_db_buf != NULL && nsc_db_list != NULL) {
132 		for (ndb = nsc_db_list; *ndb; ndb++) {
133 			if (strcmp(db, *ndb) == 0)
134 				return (1);
135 		}
136 	}
137 	return (0);
138 }
139 
140 /*
141  * flush private db lists
142  */
143 static void
144 _nsc_flush_private_db()
145 {
146 	if (nsc_db_buf != NULL) {
147 		libc_free((void *)nsc_db_buf);
148 		nsc_db_buf = NULL;
149 	}
150 	if (nsc_db_list != NULL) {
151 		libc_free((void *)nsc_db_list);
152 		nsc_db_list = NULL;
153 	}
154 }
155 
156 /*
157  * init/update nsc_db_buf given buff containing list of
158  * db's to be processed by a private nscd.
159  * This function assumes it has a well formed string from nscd.
160  */
161 
162 static int
163 _nsc_init_private_db(char *dblist)
164 {
165 	char	*cp, **lp;
166 	int	buflen = 0;
167 	int	arrlen = 0;
168 
169 	if (dblist == NULL)
170 		return (0);
171 
172 	/* reset db list */
173 	_nsc_flush_private_db();
174 
175 	/* rebuild fresh list */
176 	buflen = strlen(dblist) + 1;
177 	for (cp = dblist; *cp; cp++)
178 		if (*cp == BD_SEP)
179 			arrlen++;
180 	if (cp == dblist)
181 		return (0);
182 	arrlen += 2;
183 	nsc_db_buf = (char *)libc_malloc(buflen);
184 	if (nsc_db_buf == (char *)NULL)
185 		return (0);
186 	nsc_db_list = (char **)libc_malloc(arrlen * sizeof (char *));
187 	if (nsc_db_list == (char **)NULL) {
188 		libc_free((void *)nsc_db_buf);
189 		nsc_db_buf = NULL;
190 		return (0);
191 	}
192 	(void) memcpy(nsc_db_buf, dblist, buflen);
193 	lp = nsc_db_list;
194 	*lp++ = nsc_db_buf;
195 	for (cp = nsc_db_buf; *cp; ) {
196 		if (*cp == BD_SEP) {
197 			*cp++ = '\0';
198 			*lp++ = cp;
199 		} else
200 			cp++;
201 	}
202 	*lp = NULL;
203 	return (1);
204 }
205 
206 /*
207  * _nsc_initdoor_fp attempts to validate the given door and
208  * confirm that it is still available for use.  The options are:
209  *	Front door:
210  *		If it's not open, attempt to open or error
211  *		If it's open attempt to validate.
212  *		If it's not validatable, reset fd and try again.
213  *		Other wise it open and validated, return success
214  *	Per user (back) door:
215  *		This door is passed to the client through th front door
216  *		attempt to validate it.  If it can't be validated, it
217  *		must be reset. Then send a NSS_ALTRESET error, so nscd can
218  *		forward another fd if desired.
219  */
220 
221 static nss_status_t
222 _nsc_initdoor_fp(nsc_door_t *dp)
223 {
224 
225 	door_info_t 		my_door;
226 
227 	if (dp == NULL) {
228 		errno = ENOTCONN;
229 		return (NSS_ERROR);
230 	}
231 
232 	/*
233 	 * the first time in we try and open and validate the front door.
234 	 * A front door request may return an alternate private back door
235 	 * that the client should use instead.
236 	 *
237 	 * To validate a door the door must have been created with
238 	 * the name service door cookie. The front door is file
239 	 * attached, owned by root and readonly by user, group and
240 	 * other.  If any of these validations fail we refuse to use
241 	 * the door.  A back door is delivered from the front door
242 	 * via a door_desc_t, and have the same cooke notification.
243 	 */
244 
245 	lmutex_lock(&dp->door_lock);
246 
247 try_again:
248 
249 	if (dp->doorfd == -1 && dp == &nsc_door[0]) {	/* open front door */
250 		int		tbc[3];
251 		int		i;
252 
253 		dp->doorfd = open64(NAME_SERVICE_DOOR, O_RDONLY, 0);
254 		if (dp->doorfd == -1) {
255 			lmutex_unlock(&dp->door_lock);
256 			return (NSS_ERROR);
257 		}
258 
259 		/*
260 		 * dup up the file descriptor if we have 0 - 2
261 		 * to avoid problems with shells stdin/out/err
262 		 */
263 		i = 0;
264 
265 		while (dp->doorfd < 3) { /* we have a reserved fd */
266 			tbc[i++] = dp->doorfd;
267 			if ((dp->doorfd = dup(dp->doorfd)) < 0) {
268 				while (i--)
269 					(void) close(tbc[i]);
270 				dp->doorfd = -1;
271 				lmutex_unlock(&dp->door_lock);
272 				return (NSS_ERROR);
273 			}
274 		}
275 
276 		while (i--)
277 			(void) close(tbc[i]);
278 
279 		/*
280 		 * mark this door descriptor as close on exec
281 		 */
282 		(void) fcntl(dp->doorfd, F_SETFD, FD_CLOEXEC);
283 		if (__door_info(dp->doorfd, &dp->doori) < 0 ||
284 		    (dp->doori.di_attributes & DOOR_REVOKED) ||
285 		    dp->doori.di_data != (uintptr_t)NAME_SERVICE_DOOR_COOKIE) {
286 			/*
287 			 * we should close doorfd because we just opened it
288 			 */
289 			(void) close(dp->doorfd);
290 			dp->doorfd = -1;
291 			(void) memset((void *)&dp->doori,
292 			    '\0', sizeof (door_info_t));
293 			lmutex_unlock(&dp->door_lock);
294 			errno = ECONNREFUSED;
295 			return (NSS_ERROR);
296 		}
297 	} else {
298 		if (__door_info(dp->doorfd, &my_door) < 0 ||
299 		    my_door.di_data != (uintptr_t)NAME_SERVICE_DOOR_COOKIE ||
300 		    my_door.di_uniquifier != dp->doori.di_uniquifier) {
301 			/*
302 			 * don't close it -
303 			 * someone else has clobbered fd
304 			 */
305 			dp->doorfd = -1;
306 			(void) memset((void *)&dp->doori,
307 			    '\0', sizeof (door_info_t));
308 			if (dp == &nsc_door[1]) {	/* reset back door */
309 				/* flush invalid db list */
310 				_nsc_flush_private_db();
311 				lmutex_unlock(&dp->door_lock);
312 				return (NSS_ALTRESET);
313 			}
314 			goto try_again;
315 		}
316 
317 		if (my_door.di_attributes & DOOR_REVOKED) {
318 			(void) close(dp->doorfd);	/* nscd exited .... */
319 			dp->doorfd = -1;	/* try and restart connection */
320 			(void) memset((void *)&dp->doori,
321 			    '\0', sizeof (door_info_t));
322 			if (dp == &nsc_door[1]) {	/* back door reset */
323 				/* flush invalid db list */
324 				_nsc_flush_private_db();
325 				lmutex_unlock(&dp->door_lock);
326 				return (NSS_ALTRESET);
327 			}
328 			goto try_again;
329 		}
330 	}
331 
332 	lmutex_unlock(&dp->door_lock);
333 	return (NSS_SUCCESS);
334 }
335 
336 /*
337  * Try the door request once only, to the specified connection.
338  * return the results or error.
339  */
340 
341 static nss_status_t
342 _nsc_try1door(nsc_door_t *dp, void **dptr, size_t *ndata,
343 			size_t *adata, int *pdesc)
344 {
345 	door_arg_t		param;
346 	int			ret;
347 	nss_pheader_t		*rp;
348 
349 	ret = _nsc_initdoor_fp(dp);
350 	if (ret != NSS_SUCCESS)
351 		return (ret);
352 
353 	param.rbuf = (char *)*dptr;
354 	param.rsize = *ndata;
355 	param.data_ptr = (char *)*dptr;
356 	param.data_size = *adata;
357 	param.desc_ptr = NULL;
358 	param.desc_num = 0;
359 	ret = __door_call(dp->doorfd, &param);
360 	if (ret < 0) {
361 		return (NSS_ERROR);
362 	}
363 	*adata = param.data_size;
364 	*ndata = param.rsize;
365 	*dptr = (void *)param.data_ptr;
366 	rp = (nss_pheader_t *)((void *)param.rbuf);
367 	if (pdesc != NULL && rp && rp->p_status == NSS_ALTRETRY &&
368 	    param.desc_ptr != NULL && param.desc_num > 0) {
369 		if ((param.desc_ptr->d_attributes & DOOR_DESCRIPTOR) &&
370 		    param.desc_ptr->d_data.d_desc.d_descriptor >= 0 &&
371 		    param.desc_ptr->d_data.d_desc.d_id != 0) {
372 			/* have an alt descriptor */
373 			*pdesc = param.desc_ptr->d_data.d_desc.d_descriptor;
374 			/* got a NSS_ALTRETRY command */
375 			return (NSS_ALTRETRY);
376 		}
377 		errno = EINVAL;
378 		return (NSS_ERROR);		/* other error? */
379 	}
380 	if (*adata == 0 || *dptr == NULL) {
381 		errno = ENOTCONN;
382 		return (NSS_ERROR);
383 	}
384 
385 	if (rp->p_status == NSS_ALTRESET ||
386 	    rp->p_status == NSS_ALTRETRY ||
387 	    rp->p_status == NSS_TRYLOCAL)
388 		return (rp->p_status);
389 
390 	return (NSS_SUCCESS);
391 }
392 
393 /*
394  * Backwards compatible API
395  */
396 
397 nss_status_t
398 _nsc_trydoorcall(void **dptr, size_t *ndata, size_t *adata)
399 {
400 	return (_nsc_try1door(&nsc_door[0], dptr, ndata, adata, NULL));
401 }
402 
403 /*
404  * Send the request to the designated door, based on the supplied db
405  * Retry on the alternate door fd if possible.
406  */
407 
408 nss_status_t
409 _nsc_trydoorcall_ext(void **dptr, size_t *ndata, size_t *adata)
410 {
411 	int		ret = NSS_ALTRETRY;
412 	nsc_door_t	*frontd = &nsc_door[0];
413 	nsc_door_t	*backd = &nsc_door[1];
414 	int		fd;
415 
416 	nss_pheader_t	*ph, ph_save;
417 	char		*dbl;
418 	char		*db = NULL;
419 	nss_dbd_t	*dbd;
420 	int		fb2frontd = 0;
421 	int		reset_frontd = 0;
422 	size_t		ndata_save = *ndata, adata_save = *adata;
423 	void		*dptr_save = *dptr;
424 
425 	ph = (nss_pheader_t *)*dptr;
426 	dbd = (nss_dbd_t *)((void *)((char *)ph + ph->dbd_off));
427 	if (dbd->o_name != 0)
428 		db = (char *)dbd + dbd->o_name;
429 
430 	/*
431 	 * save away a copy of the header, in case the request needs
432 	 * to be sent to nscd more than once. In that case, this
433 	 * original header can be copied back to the door buffer
434 	 * to replace the possibly changed header
435 	 */
436 	ph_save = *ph;
437 
438 	while (ret == NSS_ALTRETRY || ret == NSS_ALTRESET) {
439 		/* try private (back) door first if it exists and applies */
440 		if (db != NULL && backd->doorfd > 0 && fb2frontd == 0 &&
441 		    _nsc_use_backdoor(db)) {
442 			ret = _nsc_try1door(backd, dptr, ndata, adata, NULL);
443 			if (ret == NSS_ALTRESET) {
444 				/*
445 				 * received NSS_ALTRESET command,
446 				 * retry on front door
447 				 */
448 				lmutex_lock(&backd->door_lock);
449 				backd->doorfd = -1;
450 				(void) memset((void *)&backd->doori,
451 				    '\0', sizeof (door_info_t));
452 				/* flush now invalid db list */
453 				_nsc_flush_private_db();
454 				lmutex_unlock(&backd->door_lock);
455 				continue;
456 			} else if (ret == NSS_ALTRETRY) {
457 				/*
458 				 * received NSS_ALTRETRY command,
459 				 * fall back and retry on front door
460 				 */
461 				fb2frontd = 1;
462 				if (*dptr != dptr_save)
463 					(void) munmap((void *)*dptr, *ndata);
464 
465 				/*
466 				 * restore the buffer size and header
467 				 * data so that the front door will
468 				 * see the original request
469 				 */
470 				*ndata = ndata_save;
471 				*adata = adata_save;
472 				*dptr = dptr_save;
473 				ph =  (nss_pheader_t *)*dptr;
474 				*ph = ph_save;
475 				/*
476 				 * tell the front door server, this is
477 				 * a fallback call
478 				 */
479 				ph->p_status = NSS_ALTRETRY;
480 				continue;
481 			}
482 
483 			/* return the result or error */
484 			break;
485 		}
486 
487 		/* try the front door */
488 		fd = -1;
489 		ret = _nsc_try1door(frontd, dptr, ndata, adata, &fd);
490 
491 		if (ret != NSS_ALTRETRY) {
492 			/*
493 			 * got a success or failure result.
494 			 * but front door should never send NSS_ALTRESET
495 			 */
496 			if (ret == NSS_ALTRESET)
497 				/* reset the front door */
498 				reset_frontd = 1;
499 			else
500 				/*
501 				 * not NSS_ALTRETRY and not NSS_ALTRESET
502 				 * return the result or error
503 				 */
504 				break;
505 		} else if (fb2frontd == 1) {
506 			/*
507 			 * front door should never send NSS_ALTRETRY
508 			 * in a fallback call. Reset the front door.
509 			 */
510 			reset_frontd = 1;
511 		}
512 
513 		if (reset_frontd == 1) {
514 			lmutex_lock(&frontd->door_lock);
515 			frontd->doorfd = -1;
516 			(void) memset((void *)&frontd->doori,
517 			    '\0', sizeof (door_info_t));
518 			lmutex_unlock(&frontd->door_lock);
519 			/* error out */
520 			ret = NSS_ERROR;
521 			break;
522 		}
523 
524 		/* process NSS_ALTRETRY request from front door */
525 		if (fd < 0)
526 			continue;	/* no new door given, try again */
527 
528 		/* update and try alternate door */
529 		lmutex_lock(&backd->door_lock);
530 		if (backd->doorfd >= 0) {
531 			/* unexpected open alt door - clean up, continue */
532 			_nsc_flush_private_db();
533 			(void) close(backd->doorfd);
534 		}
535 
536 		/* set up back door fd */
537 		backd->doorfd = fd;
538 
539 		/* set up back door db list */
540 		ph =  (nss_pheader_t *)*dptr;
541 		dbl = ((char *)ph) + ph->data_off;
542 
543 		if (_nsc_init_private_db(dbl) == 0) {
544 			/* could not init db list, try again */
545 			(void) close(backd->doorfd);
546 			backd->doorfd = -1;
547 			lmutex_unlock(&backd->door_lock);
548 			continue;
549 		}
550 		if (door_info(backd->doorfd, &backd->doori) < 0 ||
551 		    (backd->doori.di_attributes & DOOR_REVOKED) ||
552 		    backd->doori.di_data !=
553 		    (uintptr_t)NAME_SERVICE_DOOR_COOKIE) {
554 			/* doorfd bad, or must not really be open */
555 			(void) close(backd->doorfd);
556 			backd->doorfd = -1;
557 			(void) memset((void *)&backd->doori,
558 			    '\0', sizeof (door_info_t));
559 		}
560 		(void) fcntl(backd->doorfd, F_SETFD, FD_CLOEXEC);
561 		lmutex_unlock(&backd->door_lock);
562 		/* NSS_ALTRETRY new back door */
563 		if (*dptr != dptr_save)
564 			(void) munmap((void *)*dptr, *ndata);
565 
566 		/*
567 		 * restore the buffer size and header
568 		 * data so that the back door will
569 		 * see the original request
570 		 */
571 		*ndata = ndata_save;
572 		*adata = adata_save;
573 		*dptr = dptr_save;
574 		ph =  (nss_pheader_t *)*dptr;
575 		*ph = ph_save;
576 	}
577 	return (ret);
578 }
579 
580 /*
581  * Get the current (but growable) buffer size for a NSS2 packet.
582  * Heuristic algorithm used:
583  *	1) Make sure it's at least NSS_BUFLEN_DOOR in length (16k default)
584  *	2) if an incoming user buffer is > larger than the current size
585  *	   Make the buffer at least NSS_BUFLEN_DOOR/2+user buffer size
586  *	   This should account for any reasonable nss_pheader, keys
587  *	   extended area etc.
588  *	3) keep the prototype/debugging (private)NSS_BUFLEN option
589  *	   to change any preconfigured value if needed(?)
590  */
591 
592 static size_t
593 _nsc_getdoorbsize(size_t min_size)
594 {
595 	if (!door_bsize) {
596 		lmutex_lock(&hints_lock);
597 		if (!door_bsize) {
598 			/* future work - get nscd hint & use hint size */
599 			door_bsize = ROUND_UP(door_bsize, NSS_BUFSIZ);
600 			if (door_bsize < NSS_BUFLEN_DOOR) {
601 				door_bsize = NSS_BUFLEN_DOOR;
602 			}
603 		}
604 		lmutex_unlock(&hints_lock);
605 	}
606 	if (min_size && door_bsize < (min_size + NSS_BUFLEN_DOOR/2)) {
607 		lmutex_lock(&hints_lock);
608 		if (door_bsize < (min_size + NSS_BUFLEN_DOOR/2)) {
609 			min_size += NSS_BUFLEN_DOOR;
610 			door_bsize = ROUND_UP(min_size, NSS_BUFSIZ);
611 		}
612 		lmutex_unlock(&hints_lock);
613 	}
614 	return (door_bsize);
615 }
616 
617 static void
618 _nsc_freedbuf(void *arg)
619 {
620 	nss_XbyY_buf_t *tsdbuf = arg;
621 
622 	if (tsdbuf != NULL && tsdbuf->buffer != NULL) {
623 		lfree(tsdbuf->buffer, (size_t)tsdbuf->buflen);
624 		tsdbuf->result = NULL;
625 		tsdbuf->buffer = NULL;
626 		tsdbuf->buflen = 0;
627 	}
628 }
629 
630 /*
631  * _nsc_getdoorbuf - return the client side per thread door buffer
632  * Elsewhere, it is assumed that the header is 0'd upon return from here.
633  */
634 
635 int
636 _nsc_getdoorbuf(void **doorptr, size_t *bufsize)
637 {
638 	nss_XbyY_buf_t *tsdbuf;
639 	char *bp;
640 	size_t dsize;
641 
642 	if (doorptr == NULL || bufsize == NULL)
643 		return (-1);
644 
645 	/* Get thread specific pointer to door buffer */
646 	tsdbuf = tsdalloc(_T_DOORBUF, sizeof (nss_XbyY_buf_t), _nsc_freedbuf);
647 	if (tsdbuf == NULL)
648 		return (-1);
649 
650 	/* if door buffer does not exist create it */
651 	if (tsdbuf->buffer == NULL) {
652 		dsize = _nsc_getdoorbsize(*bufsize);
653 
654 		/* setup a door buffer with a total length of dsize */
655 		bp = lmalloc(dsize);
656 		if (bp == NULL)
657 			return (-1);
658 		tsdbuf->buffer = bp;
659 		tsdbuf->buflen = dsize;
660 	} else {
661 		/* check old buffer size and resize if needed */
662 		if (*bufsize) {
663 			dsize = _nsc_getdoorbsize(*bufsize);
664 			if (tsdbuf->buflen < dsize) {
665 				lfree(tsdbuf->buffer, (size_t)tsdbuf->buflen);
666 				bp = lmalloc(dsize);
667 				if (bp == NULL)
668 					return (-1);
669 				tsdbuf->buffer = bp;
670 				tsdbuf->buflen = dsize;
671 			}
672 		}
673 		/* freshly malloc'd door bufs are 0'd */
674 		/* 0 header for now.  Zero entire buf(?) TDB */
675 		(void) memset((void *)tsdbuf->buffer, 0,
676 		    (size_t)sizeof (nss_pheader_t));
677 
678 	}
679 	*doorptr = (void *)tsdbuf->buffer;
680 	*bufsize = tsdbuf->buflen;
681 	return (0);
682 }
683 
684 void
685 _nsc_resizedoorbuf(size_t bsize)
686 {
687 	/* signal to update if new door size is desired */
688 	lmutex_lock(&hints_lock);
689 	if (bsize > door_bsize && door_nbsize < bsize)
690 		door_nbsize = bsize;
691 	lmutex_unlock(&hints_lock);
692 }
693 
694 /*
695  * Check uid and /proc/PID/psinfo to see if this process is nscd
696  * If it is set the appropriate flags and allow policy reconfiguration.
697  */
698 int
699 _nsc_proc_is_cache()
700 {
701 	psinfo_t	pinfo;
702 	char		fname[128];
703 	int		ret;
704 	int		fd;
705 
706 	if (proc_is_cache >= 0)
707 		return (proc_is_cache);
708 	lmutex_lock(&hints_lock);
709 	if (proc_is_cache >= 0) {
710 		lmutex_unlock(&hints_lock);
711 		return (proc_is_cache);
712 	}
713 	proc_is_cache = 0;
714 	/* It can't be nscd if it's not running as root... */
715 	if (getuid() != 0) {
716 		lmutex_unlock(&hints_lock);
717 		return (0);
718 	}
719 	ret = snprintf(fname, 128, "/proc/%d/psinfo", getpid());
720 	if (ret > 0 && ret < 128) {
721 		if ((fd = open(fname,  O_RDONLY)) >= 0) {
722 			ret = read(fd, &pinfo, sizeof (psinfo_t));
723 			(void) close(fd);
724 			if (ret == sizeof (psinfo_t) &&
725 			    (strcmp(pinfo.pr_fname, "nscd") == 0)) {
726 				/* process runs as root and is named nscd */
727 				/* that's good enough for now */
728 				proc_is_cache = 1;
729 			}
730 		}
731 	}
732 	lmutex_unlock(&hints_lock);
733 	return (proc_is_cache);
734 }
735