root/usr/src/lib/libgss/g_utils.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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <ctype.h>
#include <errno.h>
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_ext.h>
#include <synch.h>

#define Q_DEFAULT               "default"
#define BUFLEN                  256

static int qop_num_pair_cnt;
static const char    QOP_NUM_FILE[] = "/etc/gss/qop";
static qop_num  qop_num_pairs[MAX_QOP_NUM_PAIRS+1];
static mutex_t qopfile_lock = DEFAULTMUTEX;

static OM_uint32 __gss_read_qop_file(void);

/*
 * This routine fetches qop and num from "/etc/gss/qop".
 * There is a memory leak associated with rereading this file,
 * because we can't free the qop_num_pairs array when we reread
 * the file (some callers may have been given these pointers).
 * In general, this memory leak should be a small one, because
 * we don't expect the qop file to be changed and reread often.
 */
static OM_uint32
__gss_read_qop_file(void)
{
        char    buf[BUFLEN];    /* one line from the file */
        char    *name, *next;
        char    *qopname, *num_str;
        char    *line;
        FILE    *fp;
        static int last = 0;
        struct stat stbuf;
        OM_uint32 major = GSS_S_COMPLETE;

        (void) mutex_lock(&qopfile_lock);
        if (stat(QOP_NUM_FILE, &stbuf) != 0 || stbuf.st_mtime < last) {
                if (!qop_num_pairs[0].qop) {
                        major = GSS_S_FAILURE;
                }
                goto done;
        }
        last = stbuf.st_mtime;

        fp = fopen(QOP_NUM_FILE, "rF");
        if (fp == (FILE *)0) {
                major = GSS_S_FAILURE;
                goto done;
        }

        /*
         * For each line in the file parse it appropriately.
         * File format : qopname        num(int)
         * Note that we silently ignore corrupt entries.
         */
        qop_num_pair_cnt = 0;
        while (!feof(fp)) {
                line = fgets(buf, BUFLEN, fp);
                if (line == NULL)
                        break;

                /* Skip comments and blank lines */
                if ((*line == '#') || (*line == '\n'))
                        continue;

                /* Skip trailing comments */
                next = strchr(line, '#');
                if (next)
                        *next = '\0';

                name = &(buf[0]);
                while (isspace(*name))
                        name++;
                if (*name == '\0')      /* blank line */
                        continue;

                qopname = name; /* will contain qop name */
                while (!isspace(*qopname))
                        qopname++;
                if (*qopname == '\0') {
                        continue;
                }
                next = qopname+1;
                *qopname = '\0';        /* null terminate qopname */
                qop_num_pairs[qop_num_pair_cnt].qop = strdup(name);
                if (qop_num_pairs[qop_num_pair_cnt].qop == NULL)
                        continue;

                name = next;
                while (isspace(*name))
                        name++;
                if (*name == '\0') {    /* end of line, no num */
                        free(qop_num_pairs[qop_num_pair_cnt].qop);
                        continue;
                }
                num_str = name; /* will contain num (n) */
                while (!isspace(*num_str))
                        num_str++;
                next = num_str+1;
                *num_str++ = '\0';      /* null terminate num_str */

                qop_num_pairs[qop_num_pair_cnt].num = (OM_uint32)atoi(name);
                name = next;
                while (isspace(*name))
                        name++;
                if (*name == '\0') {    /* end of line, no mechanism */
                        free(qop_num_pairs[qop_num_pair_cnt].qop);
                        continue;
                }
                num_str = name; /* will contain mech */
                while (!isspace(*num_str))
                        num_str++;
                *num_str = '\0';

                qop_num_pairs[qop_num_pair_cnt].mech = strdup(name);
                if (qop_num_pairs[qop_num_pair_cnt].mech == NULL) {
                        free(qop_num_pairs[qop_num_pair_cnt].qop);
                        continue;
                }

                if (qop_num_pair_cnt++ >= MAX_QOP_NUM_PAIRS)
                        break;
        }
        (void) fclose(fp);
done:
        (void) mutex_unlock(&qopfile_lock);
        return (major);
}

OM_uint32
__gss_qop_to_num(
        char            *qop,
        char            *mech,
        OM_uint32       *num
)
{
        int i;
        OM_uint32 major = GSS_S_FAILURE;

        if (!num)
                return (GSS_S_CALL_INACCESSIBLE_WRITE);

        if (qop == NULL || strlen(qop) == 0 ||
                        strcasecmp(qop, Q_DEFAULT) == 0) {
                *num = GSS_C_QOP_DEFAULT;
                return (GSS_S_COMPLETE);
        }

        if ((major = __gss_read_qop_file()) != GSS_S_COMPLETE)
                return (major);

        for (i = 0; i < qop_num_pair_cnt; i++) {
                if ((strcasecmp(mech, qop_num_pairs[i].mech) == 0) &&
                    (strcasecmp(qop, qop_num_pairs[i].qop) == 0)) {
                        *num = qop_num_pairs[i].num;
                        return (GSS_S_COMPLETE);
                }
        }

        return (GSS_S_FAILURE);
}

OM_uint32
__gss_num_to_qop(
        char            *mech,
        OM_uint32       num,
        char            **qop
)
{
        int i;
        OM_uint32 major;

        if (!qop)
                return (GSS_S_CALL_INACCESSIBLE_WRITE);
        *qop = NULL;

        if (num == GSS_C_QOP_DEFAULT) {
                *qop = Q_DEFAULT;
                return (GSS_S_COMPLETE);
        }

        if (mech == NULL)
                return (GSS_S_CALL_INACCESSIBLE_READ);

        if ((major = __gss_read_qop_file()) != GSS_S_COMPLETE)
                return (major);

        for (i = 0; i < qop_num_pair_cnt; i++) {
                if ((strcasecmp(mech, qop_num_pairs[i].mech) == 0) &&
                    (num == qop_num_pairs[i].num)) {
                        *qop = qop_num_pairs[i].qop;
                        return (GSS_S_COMPLETE);
                }
        }
        return (GSS_S_FAILURE);
}

/*
 * For a given mechanism pass back qop information about it in a buffer
 * of size MAX_QOPS_PER_MECH+1.
 */
OM_uint32
__gss_get_mech_info(
        char            *mech,
        char            **qops
)
{
        int i, cnt = 0;
        OM_uint32 major = GSS_S_COMPLETE;

        if (!qops)
                return (GSS_S_CALL_INACCESSIBLE_WRITE);
        *qops = NULL;

        if (!mech)
                return (GSS_S_CALL_INACCESSIBLE_READ);

        if ((major = __gss_read_qop_file()) != GSS_S_COMPLETE)
                return (major);

        for (i = 0; i < qop_num_pair_cnt; i++) {
                if (strcmp(mech, qop_num_pairs[i].mech) == 0) {
                    if (cnt >= MAX_QOPS_PER_MECH) {
                        return (GSS_S_FAILURE);
                    }
                    qops[cnt++] = qop_num_pairs[i].qop;
                }
        }
        qops[cnt] = NULL;
        return (GSS_S_COMPLETE);
}

/*
 * Copy the qop values and names for the mechanism back in a qop_num
 * buffer of size MAX_QOPS_PER_MECH provided by the caller.
 */
OM_uint32
__gss_mech_qops(
        char *mech,
        qop_num *mechqops,
        int *numqop
)
{
        int i;
        OM_uint32 major;
        int cnt = 0;

        if (!mechqops || !numqop)
                return (GSS_S_CALL_INACCESSIBLE_WRITE);
        *numqop = 0;

        if (!mech)
                return (GSS_S_CALL_INACCESSIBLE_READ);

        if ((major = __gss_read_qop_file()) != GSS_S_COMPLETE)
                return (major);

        for (i = 0; i < qop_num_pair_cnt; i++) {
            if (strcasecmp(mech, qop_num_pairs[i].mech) == 0) {
                if (cnt >= MAX_QOPS_PER_MECH) {
                        return (GSS_S_FAILURE);
                }
                mechqops[cnt++] = qop_num_pairs[i];
            }
        }
        *numqop = cnt;
        return (GSS_S_COMPLETE);
}