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
== (char *)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((void *)&dp
->doori
,
292 '\0', sizeof (door_info_t
));
293 lmutex_unlock(&dp
->door_lock
);
294 errno
= ECONNREFUSED
;
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
) {
303 * someone else has clobbered fd
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
);
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
);
332 lmutex_unlock(&dp
->door_lock
);
333 return (NSS_SUCCESS
);
337 * Try the door request once only, to the specified connection.
338 * return the results or error.
342 _nsc_try1door(nsc_door_t
*dp
, void **dptr
, size_t *ndata
,
343 size_t *adata
, int *pdesc
)
349 ret
= _nsc_initdoor_fp(dp
);
350 if (ret
!= NSS_SUCCESS
)
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
;
359 ret
= __door_call(dp
->doorfd
, ¶m
);
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
);
378 return (NSS_ERROR
); /* other error? */
380 if (*adata
== 0 || *dptr
== NULL
) {
385 if (rp
->p_status
== NSS_ALTRESET
||
386 rp
->p_status
== NSS_ALTRETRY
||
387 rp
->p_status
== NSS_TRYLOCAL
)
388 return (rp
->p_status
);
390 return (NSS_SUCCESS
);
394 * Backwards compatible API
398 _nsc_trydoorcall(void **dptr
, size_t *ndata
, size_t *adata
)
400 return (_nsc_try1door(&nsc_door
[0], dptr
, ndata
, adata
, NULL
));
404 * Send the request to the designated door, based on the supplied db
405 * Retry on the alternate door fd if possible.
409 _nsc_trydoorcall_ext(void **dptr
, size_t *ndata
, size_t *adata
)
411 int ret
= NSS_ALTRETRY
;
412 nsc_door_t
*frontd
= &nsc_door
[0];
413 nsc_door_t
*backd
= &nsc_door
[1];
416 nss_pheader_t
*ph
, ph_save
;
421 int reset_frontd
= 0;
422 size_t ndata_save
= *ndata
, adata_save
= *adata
;
423 void *dptr_save
= *dptr
;
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
;
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
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
) {
445 * received NSS_ALTRESET command,
446 * retry on front door
448 lmutex_lock(&backd
->door_lock
);
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
);
456 } else if (ret
== NSS_ALTRETRY
) {
458 * received NSS_ALTRETRY command,
459 * fall back and retry on front door
462 if (*dptr
!= dptr_save
)
463 (void) munmap((void *)*dptr
, *ndata
);
466 * restore the buffer size and header
467 * data so that the front door will
468 * see the original request
473 ph
= (nss_pheader_t
*)*dptr
;
476 * tell the front door server, this is
479 ph
->p_status
= NSS_ALTRETRY
;
483 /* return the result or error */
487 /* try the front door */
489 ret
= _nsc_try1door(frontd
, dptr
, ndata
, adata
, &fd
);
491 if (ret
!= NSS_ALTRETRY
) {
493 * got a success or failure result.
494 * but front door should never send NSS_ALTRESET
496 if (ret
== NSS_ALTRESET
)
497 /* reset the front door */
501 * not NSS_ALTRETRY and not NSS_ALTRESET
502 * return the result or error
505 } else if (fb2frontd
== 1) {
507 * front door should never send NSS_ALTRETRY
508 * in a fallback call. Reset the front door.
513 if (reset_frontd
== 1) {
514 lmutex_lock(&frontd
->door_lock
);
516 (void) memset((void *)&frontd
->doori
,
517 '\0', sizeof (door_info_t
));
518 lmutex_unlock(&frontd
->door_lock
);
524 /* process NSS_ALTRETRY request from front door */
526 continue; /* no new door given, try again */
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
);
536 /* set up back door fd */
539 /* set up back door db list */
540 ph
= (nss_pheader_t
*)*dptr
;
541 dbl
= ((char *)ph
) + ph
->data_off
;
543 if (_nsc_init_private_db(dbl
) == 0) {
544 /* could not init db list, try again */
545 (void) close(backd
->doorfd
);
547 lmutex_unlock(&backd
->door_lock
);
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
);
557 (void) memset((void *)&backd
->doori
,
558 '\0', sizeof (door_info_t
));
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
);
567 * restore the buffer size and header
568 * data so that the back door will
569 * see the original request
574 ph
= (nss_pheader_t
*)*dptr
;
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
588 * 3) keep the prototype/debugging (private)NSS_BUFLEN option
589 * to change any preconfigured value if needed(?)
593 _nsc_getdoorbsize(size_t min_size
)
596 lmutex_lock(&hints_lock
);
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
;
604 lmutex_unlock(&hints_lock
);
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
);
612 lmutex_unlock(&hints_lock
);
618 _nsc_freedbuf(void *arg
)
620 nss_XbyY_buf_t
*tsdbuf
= arg
;
622 if (tsdbuf
!= NULL
&& tsdbuf
->buffer
!= NULL
) {
623 lfree(tsdbuf
->buffer
, (size_t)tsdbuf
->buflen
);
624 tsdbuf
->result
= NULL
;
625 tsdbuf
->buffer
= NULL
;
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.
636 _nsc_getdoorbuf(void **doorptr
, size_t *bufsize
)
638 nss_XbyY_buf_t
*tsdbuf
;
642 if (doorptr
== NULL
|| bufsize
== NULL
)
645 /* Get thread specific pointer to door buffer */
646 tsdbuf
= tsdalloc(_T_DOORBUF
, sizeof (nss_XbyY_buf_t
), _nsc_freedbuf
);
650 /* if door buffer does not exist create it */
651 if (tsdbuf
->buffer
== NULL
) {
652 dsize
= _nsc_getdoorbsize(*bufsize
);
654 /* setup a door buffer with a total length of dsize */
659 tsdbuf
->buflen
= dsize
;
661 /* check old buffer size and resize if needed */
663 dsize
= _nsc_getdoorbsize(*bufsize
);
664 if (tsdbuf
->buflen
< dsize
) {
665 lfree(tsdbuf
->buffer
, (size_t)tsdbuf
->buflen
);
670 tsdbuf
->buflen
= dsize
;
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
));
679 *doorptr
= (void *)tsdbuf
->buffer
;
680 *bufsize
= tsdbuf
->buflen
;
685 _nsc_resizedoorbuf(size_t bsize
)
687 /* signal to update if new door size is desired */
688 lmutex_lock(&hints_lock
);
689 if (bsize
> door_bsize
&& door_nbsize
< bsize
)
691 lmutex_unlock(&hints_lock
);
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.
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
);
714 /* It can't be nscd if it's not running as root... */
716 lmutex_unlock(&hints_lock
);
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
));
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 */
732 lmutex_unlock(&hints_lock
);
733 return (proc_is_cache
);