root/usr/src/lib/gss_mechs/mech_dh/backend/mech/cred.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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
 */
/*
 *      cred.c
 *
 * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 */

#include <unistd.h>
#include <sys/note.h>
#include "dh_gssapi.h"

/*
 * This module supports the GSS credential family of routines for
 * Diffie-Hellman mechanism.
 */

/*
 * __dh_gss_acquire_cred: Get the credential associated with principal
 * with the requested expire time and usage. Return the credential with
 * the optional set of supported mechs and actual time left on the credential.
 *
 * Note in Diffie-Hellman the supplied principal name must be that of
 * the caller. There is no way to delegate credentials.
 *
 * Libgss alwas sets desired_mechs to GSS_C_NO_OID_SET and set the return
 * set of mechs to NULL.
 */

OM_uint32
__dh_gss_acquire_cred(void *ctx, /* Per mechanism context */
                    OM_uint32 *minor, /* Mechanism status */
                    gss_name_t principal, /* Requested principal */
                    OM_uint32  expire_req, /* Requested Expire time */
                    gss_OID_set desired_mechs, /* Set of desired mechs */
                    gss_cred_usage_t usage, /* Usage: init, accept, both */
                    gss_cred_id_t *cred, /* The return credential */
                    gss_OID_set *mechs, /* The return set of mechs */
                    OM_uint32 *expire_rec /* The expire time received*/)
{
        /* Diffie-Hellman mechanism context is ctx */
        dh_context_t cntx = (dh_context_t)ctx;
        dh_principal netname;
        dh_cred_id_t dh_cred;

        /* Need to write to these */
        if (minor == 0 || cred == 0)
                return (GSS_S_CALL_INACCESSIBLE_WRITE);

        /* Set sane outputs */
        *minor = 0;
        if (mechs)
                *mechs = GSS_C_NO_OID_SET;
        if (expire_rec)
                *expire_rec = 0;
        *cred = GSS_C_NO_CREDENTIAL;

        /*
         * If not GSS_C_NO_OID_SET then the set must contain the
         * Diffie-Hellman mechanism
         */
        if (desired_mechs != GSS_C_NO_OID_SET &&
            !__OID_is_member(desired_mechs, cntx->mech))
                return (GSS_S_BAD_MECH);

        /* See if the callers secretkey is available */
        if (!cntx->keyopts->key_secretkey_is_set())
                return (GSS_S_NO_CRED);

        /* Get the principal name of the caller */
        if ((netname = cntx->keyopts->get_principal()) == NULL)
                return (GSS_S_NO_CRED);

        /*
         * Diffie-Hellman requires the principal to be the principal
         * of the caller
         */

        if (principal &&
            strncmp(netname, (char *)principal, MAXNETNAMELEN) != 0) {
                Free(netname);
                return (GSS_S_NO_CRED);
        }

        /* Allocate the credential */
        dh_cred = New(dh_cred_id_desc, 1);
        if (dh_cred == NULL) {
                Free(netname);
                *minor = DH_NOMEM_FAILURE;
                return (GSS_S_FAILURE);
        }

        /* Set credential state */
        dh_cred->uid = geteuid();
        dh_cred->usage = usage;
        dh_cred->principal = netname;
        dh_cred->expire = expire_req ? time(0) + expire_req : GSS_C_INDEFINITE;

        /*
         * If mechs set it to the set that contains the appropriate
         * Diffie-Hellman mechanism
         */
        if (mechs && (*minor = __OID_to_OID_set(mechs, cntx->mech))) {
                Free(dh_cred);
                Free(netname);
                return (GSS_S_FAILURE);
        }

        /* Register the credential */
        if ((*minor = __dh_install_cred(dh_cred)) != DH_SUCCESS) {
                Free(dh_cred);
                Free(netname);
                return (GSS_S_FAILURE);
        }

        if (expire_rec)
                *expire_rec = expire_req ? expire_req : GSS_C_INDEFINITE;

        /* Return the Diffie-Hellman credential through cred */
        *cred  = (gss_cred_id_t)dh_cred;

        return (GSS_S_COMPLETE);
}


/*
 * __dh_gss_add_cred is currently a no-op. All the work is done at the
 * libgss layer. That layer will invoke the mechanism specific gss_acquire_cred
 * routine. This entry point should never be called. The entry point for
 * this routine is set to NULL in dhmech.c.
 */

/*
 * OM_uint32
 * __dh_gss_add_cred(void * ctx, OM_uint32 *minor, gss_cred_id_t cred_in,
 *    gss_name_t name, gss_OID mech, gss_cred_usage_t usage,
 *   OM_uint32 init_time_req, OM_uint32 accep_time_req,
 *   gss_cred_id_t *cred_out, gss_OID_set *mechs,
 *   OM_uint32 *init_time_rec, OM_uint32 *accep_time_rec)
 * {
 *      return (GSS_S_UNAVAILABLE);
 * }
 */

/*
 * __dh_gss_inquire_cred: Return tracked state of the supplied credential.
 */
OM_uint32
__dh_gss_inquire_cred(void *ctx, /* Per mechanism context */
                    OM_uint32 *minor, /* Mechanism status */
                    gss_cred_id_t cred, /* cred of interest */
                    gss_name_t *name, /* name of principal */
                    OM_uint32 *lifetime, /* return the time remainning */
                    gss_cred_usage_t *usage, /* usage: init, accept, both */
                    gss_OID_set *mechs /* Set containing mech_dh */)
{
        /* cred is a Diffie-Hellman credential */
        dh_cred_id_t crid = (dh_cred_id_t)cred;
        /* ctx is a Diffie-Hellman context */
        dh_context_t cntx = (dh_context_t)ctx;
        OM_uint32 t = GSS_C_INDEFINITE;

        if (minor == 0)
                return (GSS_S_CALL_INACCESSIBLE_WRITE);
        if (cntx == 0)
                return (GSS_S_CALL_INACCESSIBLE_READ);

        *minor = DH_SUCCESS;

        /* Default case */
        if (cred == GSS_C_NO_CREDENTIAL) {
                if (!(*cntx->keyopts->key_secretkey_is_set)())
                        return (GSS_S_NO_CRED);
                if (name)
                        *name = (gss_name_t)(*cntx->keyopts->get_principal)();
                if (lifetime)
                        *lifetime = GSS_C_INDEFINITE;
                if (usage)
                        *usage = GSS_C_BOTH;
        } else {
                /* Validate creditial */
                if ((*minor = __dh_validate_cred(crid)) != DH_SUCCESS)
                        return (GSS_S_DEFECTIVE_CREDENTIAL);
                if (name)
                        *name = (gss_name_t)strdup(crid->principal);
                if (lifetime) {
                        if (crid->expire == GSS_C_INDEFINITE)
                                *lifetime = GSS_C_INDEFINITE;
                        else {
                                time_t now = time(0);
                                t = crid->expire > now ? crid->expire-now : 0;
                                *lifetime = t;
                        }
                }
                if (usage)
                        *usage = crid->usage;
        }

        if (name && *name == 0)
                return (GSS_S_FAILURE);


        if (mechs &&
            (*minor = __OID_to_OID_set(mechs, cntx->mech)) != DH_SUCCESS) {
                free(name);
                return (GSS_S_FAILURE);
        }

        /* Check if the credential is still valid */
        return (t ? GSS_S_COMPLETE : GSS_S_CREDENTIALS_EXPIRED);
}


/*
 * __dh_gss_inquire_cred_by_mech: Return the information associated with
 * cred and mech. Since we're a backend, mech must be our mech.
 *
 * We verify that passed in mech is correct and use the above routine
 * to do the work.
 */
OM_uint32
__dh_gss_inquire_cred_by_mech(void *ctx, /* Per mechananism context */
                            OM_uint32 *minor, /* Mechanism status */
                            gss_cred_id_t cred, /* Cred to iquire about */
                            gss_OID mech, /* Along with the mechanism */
                            gss_name_t *name, /* where to return principal */
                            OM_uint32 *init_time, /* Init time left */
                            OM_uint32 *accept_time, /* Accept time left */
                            gss_cred_usage_t *usage /* cred usage */)
{
        /* ctx is them Diffie-Hellman mechanism context */
        dh_context_t context = (dh_context_t)ctx;
        OM_uint32 lifetime;
        OM_uint32 major;
        gss_cred_usage_t use;

        /* This should never happen. It would indicate a libgss failure */
        if (!__OID_equal(mech, context->mech)) {
                *minor = DH_BAD_CONTEXT;
                return (GSS_S_FAILURE);
        }

        /* Fetch cred info */
        major = __dh_gss_inquire_cred(ctx, minor, cred, name,
                                    &lifetime, &use, NULL);

        /* Return option values */
        if (major == GSS_S_COMPLETE) {
                /* set init_time if we can */
                if (init_time)
                        *init_time = (use == GSS_C_BOTH ||
                                    use == GSS_C_INITIATE) ? lifetime : 0;
                /* Ditto for accept time */
                if (accept_time)
                        *accept_time = (use == GSS_C_BOTH ||
                                        use == GSS_C_ACCEPT) ? lifetime : 0;
                if (usage)
                        *usage = use;
        }

        return (major);
}

/*
 * __dh_gss_release_cred: Release the resources associated with cred.
 */
OM_uint32
__dh_gss_release_cred(void *ctx, /* Per mechananism context (not used) */
                    OM_uint32 *minor, /* Mechanism status */
                    gss_cred_id_t *cred /* The cred to free */)
{
_NOTE(ARGUNUSED(ctx))
        dh_cred_id_t dh_cred = (dh_cred_id_t)*cred;

        /* Check that we can read and write required parameters */
        if (minor == 0 || cred == 0)
                return (GSS_S_CALL_INACCESSIBLE_WRITE);

        /* Nothing to do */
        if (*cred == GSS_C_NO_CREDENTIAL)
                return (GSS_S_COMPLETE);

        /* Check if the credential is valid */
        if ((*minor = __dh_validate_cred(dh_cred)) != DH_SUCCESS)
                return (GSS_S_NO_CRED);

        /* Unregister the credential */
        *minor = __dh_remove_cred(dh_cred);

        /* Free the principal and the cred itself */
        Free(dh_cred->principal);
        Free(dh_cred);

        /* Set cred to no credential */
        *cred = GSS_C_NO_CREDENTIAL;

        return (GSS_S_COMPLETE);
}