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.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
31 #include <sys/types.h>
34 #include <nss_dbdefs.h>
38 #include <sys/param.h>
42 #include <getxby_door.h>
49 #include "base_conversion.h"
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;
57 /* library<->nscd door interaction apis */
61 * Routine that actually performs the door call.
62 * Note that we cache a file descriptor. We do
63 * the following to prevent disasters:
65 * 1) Never use 0,1 or 2; if we get this from the open
68 * 2) Set the close on exec flags so descriptor remains available
71 * 3) Verify that the door is still the same one we had before
72 * by using door_info on the client side.
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.
82 * int _nsc_trydoorcall(void *dptr, size_t *bufsize, size_t *actualsize);
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
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.
93 * Returns 0 if the door call reached the server, -1 if contact was not made.
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
103 #define BD_BUFSIZE 1024
106 typedef struct _nsc_door_t
{
112 static nsc_door_t nsc_door
[2] = {
113 { -1, DEFAULTMUTEX
, { 0 } }, /* front (fattached) door */
114 { -1, DEFAULTMUTEX
, { 0 } }, /* back (private) door */
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
;
122 * Check for a valid and matching db in the list.
123 * assume list is in the locked state.
127 _nsc_use_backdoor(char *db
)
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)
141 * flush private db lists
144 _nsc_flush_private_db()
146 if (nsc_db_buf
!= NULL
) {
147 libc_free((void *)nsc_db_buf
);
150 if (nsc_db_list
!= NULL
) {
151 libc_free((void *)nsc_db_list
);
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.
163 _nsc_init_private_db(char *dblist
)
173 _nsc_flush_private_db();
175 /* rebuild fresh list */
176 buflen
= strlen(dblist
) + 1;
177 for (cp
= dblist
; *cp
; cp
++)
183 nsc_db_buf
= (char *)libc_malloc(buflen
);
184 if (nsc_db_buf
== NULL
)
186 nsc_db_list
= (char **)libc_malloc(arrlen
* sizeof (char *));
187 if (nsc_db_list
== (char **)NULL
) {
188 libc_free((void *)nsc_db_buf
);
192 (void) memcpy(nsc_db_buf
, dblist
, buflen
);
195 for (cp
= nsc_db_buf
; *cp
; ) {
207 * _nsc_initdoor_fp attempts to validate the given door and
208 * confirm that it is still available for use. The options are:
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.
222 _nsc_initdoor_fp(nsc_door_t
*dp
)
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.
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.
245 lmutex_lock(&dp
->door_lock
);
249 if (dp
->doorfd
== -1 && dp
== &nsc_door
[0]) { /* open front door */
253 dp
->doorfd
= open64(NAME_SERVICE_DOOR
, O_RDONLY
, 0);
254 if (dp
->doorfd
== -1) {
255 lmutex_unlock(&dp
->door_lock
);
260 * dup up the file descriptor if we have 0 - 2
261 * to avoid problems with shells stdin/out/err
265 while (dp
->doorfd
< 3) { /* we have a reserved fd */
266 tbc
[i
++] = dp
->doorfd
;
267 if ((dp
->doorfd
= dup(dp
->doorfd
)) < 0) {
269 (void) close(tbc
[i
]);
271 lmutex_unlock(&dp
->door_lock
);
277 (void) close(tbc
[i
]);
280 * mark this door descriptor as close on exec
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
) {
287 * we should close doorfd because we just opened it
289 (void) close(dp
->doorfd
);
291 (void) memset(&dp
->doori
, 0, sizeof (door_info_t
));
292 lmutex_unlock(&dp
->door_lock
);
293 errno
= ECONNREFUSED
;
297 if (__door_info(dp
->doorfd
, &my_door
) < 0 ||
298 my_door
.di_data
!= (uintptr_t)NAME_SERVICE_DOOR_COOKIE
||
299 my_door
.di_uniquifier
!= dp
->doori
.di_uniquifier
) {
302 * someone else has clobbered fd
305 (void) memset(&dp
->doori
, 0, sizeof (door_info_t
));
306 if (dp
== &nsc_door
[1]) { /* reset back door */
307 /* flush invalid db list */
308 _nsc_flush_private_db();
309 lmutex_unlock(&dp
->door_lock
);
310 return (NSS_ALTRESET
);
315 if (my_door
.di_attributes
& DOOR_REVOKED
) {
316 (void) close(dp
->doorfd
); /* nscd exited .... */
317 dp
->doorfd
= -1; /* try and restart connection */
318 (void) memset(&dp
->doori
, 0, sizeof (door_info_t
));
319 if (dp
== &nsc_door
[1]) { /* back door reset */
320 /* flush invalid db list */
321 _nsc_flush_private_db();
322 lmutex_unlock(&dp
->door_lock
);
323 return (NSS_ALTRESET
);
329 lmutex_unlock(&dp
->door_lock
);
330 return (NSS_SUCCESS
);
334 * Try the door request once only, to the specified connection.
335 * return the results or error.
339 _nsc_try1door(nsc_door_t
*dp
, void **dptr
, size_t *ndata
,
340 size_t *adata
, int *pdesc
)
346 ret
= _nsc_initdoor_fp(dp
);
347 if (ret
!= NSS_SUCCESS
)
350 param
.rbuf
= (char *)*dptr
;
351 param
.rsize
= *ndata
;
352 param
.data_ptr
= (char *)*dptr
;
353 param
.data_size
= *adata
;
354 param
.desc_ptr
= NULL
;
356 ret
= __door_call(dp
->doorfd
, ¶m
);
360 *adata
= param
.data_size
;
361 *ndata
= param
.rsize
;
362 *dptr
= (void *)param
.data_ptr
;
363 rp
= (nss_pheader_t
*)((void *)param
.rbuf
);
364 if (pdesc
!= NULL
&& rp
&& rp
->p_status
== NSS_ALTRETRY
&&
365 param
.desc_ptr
!= NULL
&& param
.desc_num
> 0) {
366 if ((param
.desc_ptr
->d_attributes
& DOOR_DESCRIPTOR
) &&
367 param
.desc_ptr
->d_data
.d_desc
.d_descriptor
>= 0 &&
368 param
.desc_ptr
->d_data
.d_desc
.d_id
!= 0) {
369 /* have an alt descriptor */
370 *pdesc
= param
.desc_ptr
->d_data
.d_desc
.d_descriptor
;
371 /* got a NSS_ALTRETRY command */
372 return (NSS_ALTRETRY
);
375 return (NSS_ERROR
); /* other error? */
377 if (*adata
== 0 || *dptr
== NULL
) {
382 if (rp
->p_status
== NSS_ALTRESET
||
383 rp
->p_status
== NSS_ALTRETRY
||
384 rp
->p_status
== NSS_TRYLOCAL
)
385 return (rp
->p_status
);
387 return (NSS_SUCCESS
);
391 * Backwards compatible API
395 _nsc_trydoorcall(void **dptr
, size_t *ndata
, size_t *adata
)
397 return (_nsc_try1door(&nsc_door
[0], dptr
, ndata
, adata
, NULL
));
401 * Send the request to the designated door, based on the supplied db
402 * Retry on the alternate door fd if possible.
406 _nsc_trydoorcall_ext(void **dptr
, size_t *ndata
, size_t *adata
)
408 int ret
= NSS_ALTRETRY
;
409 nsc_door_t
*frontd
= &nsc_door
[0];
410 nsc_door_t
*backd
= &nsc_door
[1];
413 nss_pheader_t
*ph
, ph_save
;
418 int reset_frontd
= 0;
419 size_t ndata_save
= *ndata
, adata_save
= *adata
;
420 void *dptr_save
= *dptr
;
422 ph
= (nss_pheader_t
*)*dptr
;
423 dbd
= (nss_dbd_t
*)((void *)((char *)ph
+ ph
->dbd_off
));
424 if (dbd
->o_name
!= 0)
425 db
= (char *)dbd
+ dbd
->o_name
;
428 * save away a copy of the header, in case the request needs
429 * to be sent to nscd more than once. In that case, this
430 * original header can be copied back to the door buffer
431 * to replace the possibly changed header
435 while (ret
== NSS_ALTRETRY
|| ret
== NSS_ALTRESET
) {
436 /* try private (back) door first if it exists and applies */
437 if (db
!= NULL
&& backd
->doorfd
> 0 && fb2frontd
== 0 &&
438 _nsc_use_backdoor(db
)) {
439 ret
= _nsc_try1door(backd
, dptr
, ndata
, adata
, NULL
);
440 if (ret
== NSS_ALTRESET
) {
442 * received NSS_ALTRESET command,
443 * retry on front door
445 lmutex_lock(&backd
->door_lock
);
447 (void) memset(&backd
->doori
,
448 0, sizeof (door_info_t
));
449 /* flush now invalid db list */
450 _nsc_flush_private_db();
451 lmutex_unlock(&backd
->door_lock
);
453 } else if (ret
== NSS_ALTRETRY
) {
455 * received NSS_ALTRETRY command,
456 * fall back and retry on front door
459 if (*dptr
!= dptr_save
)
460 (void) munmap((void *)*dptr
, *ndata
);
463 * restore the buffer size and header
464 * data so that the front door will
465 * see the original request
470 ph
= (nss_pheader_t
*)*dptr
;
473 * tell the front door server, this is
476 ph
->p_status
= NSS_ALTRETRY
;
480 /* return the result or error */
484 /* try the front door */
486 ret
= _nsc_try1door(frontd
, dptr
, ndata
, adata
, &fd
);
488 if (ret
!= NSS_ALTRETRY
) {
490 * got a success or failure result.
491 * but front door should never send NSS_ALTRESET
493 if (ret
== NSS_ALTRESET
)
494 /* reset the front door */
498 * not NSS_ALTRETRY and not NSS_ALTRESET
499 * return the result or error
502 } else if (fb2frontd
== 1) {
504 * front door should never send NSS_ALTRETRY
505 * in a fallback call. Reset the front door.
510 if (reset_frontd
== 1) {
511 lmutex_lock(&frontd
->door_lock
);
513 (void) memset(&frontd
->doori
, 0, sizeof (door_info_t
));
514 lmutex_unlock(&frontd
->door_lock
);
520 /* process NSS_ALTRETRY request from front door */
522 continue; /* no new door given, try again */
524 /* update and try alternate door */
525 lmutex_lock(&backd
->door_lock
);
526 if (backd
->doorfd
>= 0) {
527 /* unexpected open alt door - clean up, continue */
528 _nsc_flush_private_db();
529 (void) close(backd
->doorfd
);
532 /* set up back door fd */
535 /* set up back door db list */
536 ph
= (nss_pheader_t
*)*dptr
;
537 dbl
= ((char *)ph
) + ph
->data_off
;
539 if (_nsc_init_private_db(dbl
) == 0) {
540 /* could not init db list, try again */
541 (void) close(backd
->doorfd
);
543 lmutex_unlock(&backd
->door_lock
);
546 if (door_info(backd
->doorfd
, &backd
->doori
) < 0 ||
547 (backd
->doori
.di_attributes
& DOOR_REVOKED
) ||
548 backd
->doori
.di_data
!=
549 (uintptr_t)NAME_SERVICE_DOOR_COOKIE
) {
550 /* doorfd bad, or must not really be open */
551 (void) close(backd
->doorfd
);
553 (void) memset(&backd
->doori
, 0, sizeof (door_info_t
));
555 (void) fcntl(backd
->doorfd
, F_SETFD
, FD_CLOEXEC
);
556 lmutex_unlock(&backd
->door_lock
);
557 /* NSS_ALTRETRY new back door */
558 if (*dptr
!= dptr_save
)
559 (void) munmap((void *)*dptr
, *ndata
);
562 * restore the buffer size and header
563 * data so that the back door will
564 * see the original request
569 ph
= (nss_pheader_t
*)*dptr
;
576 * Get the current (but growable) buffer size for a NSS2 packet.
577 * Heuristic algorithm used:
578 * 1) Make sure it's at least NSS_BUFLEN_DOOR in length (16k default)
579 * 2) if an incoming user buffer is > larger than the current size
580 * Make the buffer at least NSS_BUFLEN_DOOR/2+user buffer size
581 * This should account for any reasonable nss_pheader, keys
583 * 3) keep the prototype/debugging (private)NSS_BUFLEN option
584 * to change any preconfigured value if needed(?)
588 _nsc_getdoorbsize(size_t min_size
)
591 lmutex_lock(&hints_lock
);
593 /* future work - get nscd hint & use hint size */
594 door_bsize
= ROUND_UP(door_bsize
, NSS_BUFSIZ
);
595 if (door_bsize
< NSS_BUFLEN_DOOR
) {
596 door_bsize
= NSS_BUFLEN_DOOR
;
599 lmutex_unlock(&hints_lock
);
601 if (min_size
&& door_bsize
< (min_size
+ NSS_BUFLEN_DOOR
/2)) {
602 lmutex_lock(&hints_lock
);
603 if (door_bsize
< (min_size
+ NSS_BUFLEN_DOOR
/2)) {
604 min_size
+= NSS_BUFLEN_DOOR
;
605 door_bsize
= ROUND_UP(min_size
, NSS_BUFSIZ
);
607 lmutex_unlock(&hints_lock
);
613 _nsc_freedbuf(void *arg
)
615 nss_XbyY_buf_t
*tsdbuf
= arg
;
617 if (tsdbuf
!= NULL
&& tsdbuf
->buffer
!= NULL
) {
618 lfree(tsdbuf
->buffer
, (size_t)tsdbuf
->buflen
);
619 tsdbuf
->result
= NULL
;
620 tsdbuf
->buffer
= NULL
;
626 * _nsc_getdoorbuf - return the client side per thread door buffer
627 * Elsewhere, it is assumed that the header is 0'd upon return from here.
631 _nsc_getdoorbuf(void **doorptr
, size_t *bufsize
)
633 nss_XbyY_buf_t
*tsdbuf
;
637 if (doorptr
== NULL
|| bufsize
== NULL
)
640 /* Get thread specific pointer to door buffer */
641 tsdbuf
= tsdalloc(_T_DOORBUF
, sizeof (nss_XbyY_buf_t
), _nsc_freedbuf
);
645 /* if door buffer does not exist create it */
646 if (tsdbuf
->buffer
== NULL
) {
647 dsize
= _nsc_getdoorbsize(*bufsize
);
649 /* setup a door buffer with a total length of dsize */
654 tsdbuf
->buflen
= dsize
;
656 /* check old buffer size and resize if needed */
658 dsize
= _nsc_getdoorbsize(*bufsize
);
659 if (tsdbuf
->buflen
< dsize
) {
660 lfree(tsdbuf
->buffer
, (size_t)tsdbuf
->buflen
);
665 tsdbuf
->buflen
= dsize
;
668 /* freshly malloc'd door bufs are 0'd */
669 /* 0 header for now. Zero entire buf(?) TDB */
670 (void) memset(tsdbuf
->buffer
, 0,
671 (size_t)sizeof (nss_pheader_t
));
674 *doorptr
= (void *)tsdbuf
->buffer
;
675 *bufsize
= tsdbuf
->buflen
;
680 _nsc_resizedoorbuf(size_t bsize
)
682 /* signal to update if new door size is desired */
683 lmutex_lock(&hints_lock
);
684 if (bsize
> door_bsize
&& door_nbsize
< bsize
)
686 lmutex_unlock(&hints_lock
);
690 * Check uid and /proc/PID/psinfo to see if this process is nscd
691 * If it is set the appropriate flags and allow policy reconfiguration.
701 if (proc_is_cache
>= 0)
702 return (proc_is_cache
);
703 lmutex_lock(&hints_lock
);
704 if (proc_is_cache
>= 0) {
705 lmutex_unlock(&hints_lock
);
706 return (proc_is_cache
);
709 /* It can't be nscd if it's not running as root... */
711 lmutex_unlock(&hints_lock
);
714 ret
= snprintf(fname
, 128, "/proc/%d/psinfo", getpid());
715 if (ret
> 0 && ret
< 128) {
716 if ((fd
= open(fname
, O_RDONLY
)) >= 0) {
717 ret
= read(fd
, &pinfo
, sizeof (psinfo_t
));
719 if (ret
== sizeof (psinfo_t
) &&
720 (strcmp(pinfo
.pr_fname
, "nscd") == 0)) {
721 /* process runs as root and is named nscd */
722 /* that's good enough for now */
727 lmutex_unlock(&hints_lock
);
728 return (proc_is_cache
);