6811333 Remove prom_printf() message in emlxs driver
[opensolaris.git] / usr / src / lib / libc / port / gen / getxby_door.c
blobe98bf317efbd5c7498ce42b36647840647245913
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
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"
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
66 * we dup it upwards.
68 * 2) Set the close on exec flags so descriptor remains available
69 * to child processes.
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
104 #define BD_SEP ','
106 typedef struct _nsc_door_t {
107 int doorfd;
108 mutex_t door_lock;
109 door_info_t doori;
110 } 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.
126 static int
127 _nsc_use_backdoor(char *db)
129 char **ndb;
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);
137 return (0);
141 * flush private db lists
143 static void
144 _nsc_flush_private_db()
146 if (nsc_db_buf != NULL) {
147 libc_free((void *)nsc_db_buf);
148 nsc_db_buf = NULL;
150 if (nsc_db_list != NULL) {
151 libc_free((void *)nsc_db_list);
152 nsc_db_list = NULL;
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.
162 static int
163 _nsc_init_private_db(char *dblist)
165 char *cp, **lp;
166 int buflen = 0;
167 int arrlen = 0;
169 if (dblist == NULL)
170 return (0);
172 /* reset db list */
173 _nsc_flush_private_db();
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);
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++;
202 *lp = NULL;
203 return (1);
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.
221 static nss_status_t
222 _nsc_initdoor_fp(nsc_door_t *dp)
225 door_info_t my_door;
227 if (dp == NULL) {
228 errno = ENOTCONN;
229 return (NSS_ERROR);
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);
247 try_again:
249 if (dp->doorfd == -1 && dp == &nsc_door[0]) { /* open front door */
250 int tbc[3];
251 int i;
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);
260 * dup up the file descriptor if we have 0 - 2
261 * to avoid problems with shells stdin/out/err
263 i = 0;
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);
276 while (i--)
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);
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);
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) {
302 * don't close it -
303 * someone else has clobbered fd
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);
314 goto try_again;
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);
328 goto try_again;
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.
341 static nss_status_t
342 _nsc_try1door(nsc_door_t *dp, void **dptr, size_t *ndata,
343 size_t *adata, int *pdesc)
345 door_arg_t param;
346 int ret;
347 nss_pheader_t *rp;
349 ret = _nsc_initdoor_fp(dp);
350 if (ret != NSS_SUCCESS)
351 return (ret);
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);
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);
377 errno = EINVAL;
378 return (NSS_ERROR); /* other error? */
380 if (*adata == 0 || *dptr == NULL) {
381 errno = ENOTCONN;
382 return (NSS_ERROR);
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
397 nss_status_t
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.
408 nss_status_t
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];
414 int fd;
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;
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
436 ph_save = *ph;
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);
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) {
458 * received NSS_ALTRETRY command,
459 * fall back and retry on front door
461 fb2frontd = 1;
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
470 *ndata = ndata_save;
471 *adata = adata_save;
472 *dptr = dptr_save;
473 ph = (nss_pheader_t *)*dptr;
474 *ph = ph_save;
476 * tell the front door server, this is
477 * a fallback call
479 ph->p_status = NSS_ALTRETRY;
480 continue;
483 /* return the result or error */
484 break;
487 /* try the front door */
488 fd = -1;
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 */
498 reset_frontd = 1;
499 else
501 * not NSS_ALTRETRY and not NSS_ALTRESET
502 * return the result or error
504 break;
505 } else if (fb2frontd == 1) {
507 * front door should never send NSS_ALTRETRY
508 * in a fallback call. Reset the front door.
510 reset_frontd = 1;
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;
524 /* process NSS_ALTRETRY request from front door */
525 if (fd < 0)
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 */
537 backd->doorfd = 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);
546 backd->doorfd = -1;
547 lmutex_unlock(&backd->door_lock);
548 continue;
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));
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
571 *ndata = ndata_save;
572 *adata = adata_save;
573 *dptr = dptr_save;
574 ph = (nss_pheader_t *)*dptr;
575 *ph = ph_save;
577 return (ret);
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(?)
592 static size_t
593 _nsc_getdoorbsize(size_t min_size)
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;
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);
614 return (door_bsize);
617 static void
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;
626 tsdbuf->buflen = 0;
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;
639 char *bp;
640 size_t dsize;
642 if (doorptr == NULL || bufsize == NULL)
643 return (-1);
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);
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 */
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;
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;
681 return (0);
684 void
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)
690 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.
699 _nsc_proc_is_cache()
701 psinfo_t pinfo;
702 char fname[128];
703 int ret;
704 int fd;
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);
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);
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;
732 lmutex_unlock(&hints_lock);
733 return (proc_is_cache);