root/usr/src/cmd/nscd/nscd_door.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright 2012 Milan Jurik. All rights reserved.
 */

#include <sys/param.h>
#include <string.h>
#include <door.h>
#include <sys/mman.h>
#include "nscd_door.h"
#include "nscd_log.h"
#include <getxby_door.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>

static void
initdoor(void *buf, int *doorfd)
{
        nss_pheader_t   *phdr = (nss_pheader_t *)buf;
        door_info_t     doori;
        char            *me = "initdoor";

        *doorfd = open64(NAME_SERVICE_DOOR, O_RDONLY, 0);

        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "door is %s (fd is %d)\n", NAME_SERVICE_DOOR,
                    *doorfd);

        if (*doorfd == -1) {
                NSCD_SET_STATUS(phdr, NSS_ERROR, errno);
                return;
        }

        if (door_info(*doorfd, &doori) < 0 ||
            (doori.di_attributes & DOOR_REVOKED) ||
            doori.di_data != (uintptr_t)NAME_SERVICE_DOOR_COOKIE) {

                /*
                 * we should close doorfd because we just opened it
                 */
                (void) close(*doorfd);

                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "door %d not valid\n", *doorfd);

                NSCD_SET_STATUS(phdr, NSS_ERROR, ECONNREFUSED);
                return;
        }

        NSCD_SET_STATUS_SUCCESS(phdr);
}

/* general door call functions used by nscd */

static nss_status_t
copy_output(void *outdata, int outdlen,
        nss_pheader_t *phdr, nss_pheader_t *outphdr)
{
        void            *dp;
        nss_status_t    ret = NSS_SUCCESS;
        char            *me = "copy_output";

        if (outdata != NULL && phdr->data_off > 0 && phdr->data_len > 0) {
                if (phdr->data_len <= outdlen) {
                        dp = (char *)phdr + phdr->data_off;
                        (void) memmove(outdata, dp, phdr->data_len);
                } else {

                        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                        (me, "output buffer not large enough "
                            " should be > %d but is %d\n",
                            phdr->data_len, outdlen);

                        if (outphdr != NULL) {
                                NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV,
                                    0, NSCD_INVALID_ARGUMENT);
                                NSCD_COPY_STATUS(outphdr, phdr);
                        }
                        ret = NSS_NSCD_PRIV;
                }
        }

        return (ret);
}

nss_status_t
_nscd_doorcall(int callnum)
{
        size_t          buflen;
        nss_pheader_t   *phdr;
        void            *dptr;
        size_t          ndata;
        size_t          adata;
        int             ret;
        char            *me = "_nscd_doorcall";

        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
        (me, "processing door call %d ...\n", callnum);

        /* allocate door buffer from the stack */
        NSCD_ALLOC_DOORBUF(callnum, 0, dptr, buflen);
        ndata = buflen;
        adata = buflen;

        ret = _nsc_trydoorcall(&dptr, &ndata, &adata);

        if (ret != NSS_SUCCESS) {
                phdr = (nss_pheader_t *)dptr;
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "door call (%d) failed (status = %d, error = %s)\n",
                    callnum, ret, strerror(NSCD_GET_ERRNO(phdr)));
        }

        return (ret);
}


nss_status_t
_nscd_doorcall_data(int callnum, void *indata, int indlen,
        void *outdata, int outdlen, nss_pheader_t *phdr)
{
        void            *uptr;
        size_t          buflen;
        void            *dptr;
        void            *datap;
        size_t          ndata;
        size_t          adata;
        nss_pheader_t   *phdr_d;
        int             ret;
        char            *me = "_nscd_doorcall_data";

        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
        (me, "processing door call %d ...\n", callnum);

        /* allocate door buffer from the stack */
        NSCD_ALLOC_DOORBUF(callnum, indlen, uptr, buflen);
        dptr = uptr;
        ndata = buflen;
        adata = buflen;
        datap = NSCD_N2N_DOOR_DATA(void, dptr);
        if (indata != NULL)
                (void) memmove(datap, indata, indlen);

        ret = _nsc_trydoorcall(&dptr, &ndata, &adata);

        phdr_d = (nss_pheader_t *)dptr;
        if (ret != NSS_SUCCESS) {
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "door call (%d) failed (status = %d, error = %s)\n",
                    callnum, ret, strerror(NSCD_GET_ERRNO(phdr_d)));
        } else {
                if (phdr != NULL) {
                        NSCD_COPY_STATUS(phdr, phdr_d);
                }
                ret = copy_output(outdata, outdlen, phdr_d, phdr);
        }

        /* if new buffer allocated for this door call, free it */
        if (dptr != uptr)
                (void) munmap(dptr, ndata);

        return (ret);
}

nss_status_t
_nscd_doorcall_fd(int fd, int callnum, void *indata, int indlen,
        void *outdata, int outdlen, nss_pheader_t *phdr)
{
        void            *uptr;
        void            *dptr;
        void            *datap;
        size_t          ndata;
        size_t          adata;
        size_t          buflen;
        door_arg_t      param;
        int             ret, errnum;
        nss_pheader_t   *phdr_d;
        char            *me = "_nscd_doorcall_fd";

        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
        (me, "processing door call %d (fd = %d)...\n", callnum, fd);

        /* allocate door buffer from the stack */
        NSCD_ALLOC_DOORBUF(callnum, indlen, uptr, buflen);
        dptr = uptr;
        ndata = buflen;
        adata = buflen;
        datap = NSCD_N2N_DOOR_DATA(void, dptr);
        if (indata != NULL)
                (void) memmove(datap, indata, indlen);

        param.rbuf = (char *)dptr;
        param.rsize = ndata;
        param.data_ptr = (char *)dptr;
        param.data_size = adata;
        param.desc_ptr = NULL;
        param.desc_num = 0;
        ret = door_call(fd, &param);
        if (ret < 0) {
                errnum = errno;
                /*
                 * door call did not get through, return errno
                 * if requested
                 */
                if (phdr != NULL) {
                        NSCD_SET_STATUS(phdr, NSS_ERROR, errnum);
                }

                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "door call (%d to %d) did not get through (%s)\n",
                    callnum, fd, strerror(errnum));

                return (NSS_ERROR);
        }
        ndata = param.rsize;
        dptr = (void *)param.data_ptr;

        /*
         * door call got through, check if operation failed.
         * if so, return error info if requested
         */
        phdr_d = (nss_pheader_t *)dptr;
        ret = NSCD_GET_STATUS(phdr_d);
        if (ret != NSS_SUCCESS) {
                if (phdr != NULL) {
                        NSCD_COPY_STATUS(phdr, phdr_d);
                }

                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "door call (%d to %d) failed: p_status = %d, "
                    "p_errno = %s, nscd status = %d\n", callnum, fd,
                    ret, strerror(NSCD_GET_ERRNO(phdr_d)),
                    NSCD_GET_NSCD_STATUS(phdr_d));
        } else
                ret = copy_output(outdata, outdlen, phdr_d, phdr);

        /* if new buffer allocated for this door call, free it */
        if (dptr != uptr)
                (void) munmap(dptr, param.rsize);


        return (ret);
}

static void
send_doorfd(void **dptr, size_t *ndata, size_t *adata,
        door_desc_t *pdesc)
{
        nss_pheader_t   *phdr = (nss_pheader_t *)*dptr;
        door_arg_t      param;
        int             ret;
        int             doorfd;
        int             errnum;
        char            *me = "send_doorfd";

        initdoor(*dptr, &doorfd);
        if (NSCD_STATUS_IS_NOT_OK(phdr))
                return;

        param.rbuf = (char *)*dptr;
        param.rsize = *ndata;
        param.data_ptr = (char *)*dptr;
        param.data_size = *adata;
        param.desc_ptr = pdesc;
        param.desc_num = 1;
        ret = door_call(doorfd, &param);
        if (ret < 0) {
                errnum = errno;

                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "door call (to fd %d) failed (%s)\n",
                    doorfd, strerror(errnum));
                (void) close(doorfd);
                NSCD_SET_STATUS(phdr, NSS_ERROR, errnum);
                return;
        }
        *adata = param.data_size;
        *ndata = param.rsize;
        *dptr = (void *)param.data_ptr;

        if (*adata == 0 || *dptr == NULL) {
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "no data\n");

                NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTCONN);
        }

        (void) close(doorfd);
}

nss_status_t
_nscd_doorcall_sendfd(int fd, int callnum, void *indata, int indlen,
        nss_pheader_t *phdr)
{
        void            *uptr;
        void            *dptr;
        void            *datap;
        size_t          ndata;
        size_t          adata;
        size_t          buflen;
        nss_pheader_t   *phdr_d;
        door_desc_t     desc;
        char            *me = "_nscd_doorcall_sendfd";

        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
        (me, "processing door call %d (fd = %d)...\n", callnum, fd);

        /* allocate door buffer from the stack */
        NSCD_ALLOC_DOORBUF(callnum, indlen, uptr, buflen);
        dptr = uptr;
        ndata = buflen;
        adata = buflen;
        datap = NSCD_N2N_DOOR_DATA(void, dptr);
        if (indata != NULL)
                (void) memmove(datap, indata, indlen);
        desc.d_attributes = DOOR_DESCRIPTOR;
        desc.d_data.d_desc.d_descriptor = fd;

        send_doorfd(&dptr, &ndata, &adata, &desc);

        phdr_d = (nss_pheader_t *)dptr;
        if (NSCD_STATUS_IS_NOT_OK(phdr_d)) {
                if (phdr != NULL)
                        *phdr = *phdr_d;

                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "door call (%d) failed (status = %d, error = %s)\n",
                    callnum, NSCD_GET_STATUS(phdr_d),
                    strerror(NSCD_GET_ERRNO(phdr_d)));
        }

        return (NSCD_GET_STATUS(phdr_d));
}