2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <syslog.h>
2N/A#include <dh_gssapi.h>
2N/A#include <dlfcn.h>
2N/A#include "../dh_common/dh_common.h"
2N/A
2N/Aextern int key_encryptsession_pk_g();
2N/Aextern int key_decryptsession_pk_g();
2N/Aextern int key_gendes_g();
2N/Aextern int key_secretkey_is_set_g();
2N/A
2N/Astatic int __encrypt(const char *remotename, des_block deskeys[], int no_keys);
2N/Astatic int __decrypt(const char *remotename,
2N/A des_block deskeys[], int no_keys, int *key_cached);
2N/Astatic int __gendes(des_block deskeys[], int no_keys);
2N/Astatic int __secret_is_set(void);
2N/Astatic char *__get_principal(void);
2N/A
2N/A/*
2N/A * This module defines the entry point for gss_mech_initialize and the
2N/A * key opts for Diffie-Hellman mechanism of type algorithm 0. Each algorithm
2N/A * 0 mechanism defines its OID, MODULUS, ROOT, KEYLEN, ALGTYPE (which should
2N/A * be zero) and HEX_KEY_BYTES. That module then will #include this file.
2N/A */
2N/A
2N/A/* The keyopts for the per mechanism context */
2N/Adh_keyopts_desc dh_keyopts = {
2N/A __encrypt,
2N/A __decrypt,
2N/A __gendes,
2N/A __secret_is_set,
2N/A __get_principal
2N/A};
2N/A
2N/A/* The gss_context for this mechanism */
2N/Astatic struct gss_config dh_mech;
2N/A
2N/A/*
2N/A * gss_mech_initialize: This is the libgss entry point to bring this
2N/A * mechanism on line. It is just a wrap to pass the pointer to its
2N/A * gss_config structure, OID, and the above keyopts to the common
2N/A * __dh_geneirc_initialize routine. We return null on failure, otherwise
2N/A * we return the mechanism's gss_mechanism.
2N/A */
2N/Agss_mechanism
2N/Agss_mech_initialize()
2N/A{
2N/A gss_mechanism mech;
2N/A
2N/A mech = __dh_generic_initialize(&dh_mech, OID, &dh_keyopts);
2N/A
2N/A if (mech == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A return (mech);
2N/A}
2N/A
2N/A/*
2N/A * A NIS+ server will define the function __rpcsec_gss_is_server.
2N/A * This function will return one when it is appropriate to get public
2N/A * keys out of the per process public key cache. Appropriateness here
2N/A * is when the name server just put the public key in the cache from a
2N/A * received directory object, typically from the cold start file.
2N/A */
2N/Astatic int
2N/Adh_getpublickey(const char *remote, keylen_t keylen, algtype_t algtype,
2N/A char *pk, size_t pklen)
2N/A{
2N/A static mutex_t init_nis_pubkey_lock = DEFAULTMUTEX;
2N/A static int init_nis_pubkey = 0;
2N/A static int (*nis_call)();
2N/A static const char NIS_SYMBOL[] = "__rpcsec_gss_is_server";
2N/A
2N/A if (!init_nis_pubkey) {
2N/A mutex_lock(&init_nis_pubkey_lock);
2N/A if (!init_nis_pubkey) {
2N/A void *dlhandle = dlopen(0, RTLD_NOLOAD);
2N/A if (dlhandle == 0) {
2N/A syslog(LOG_ERR, "dh: Could not dlopen "
2N/A "in dh_getpublickey for %s. "
2N/A "dlopen returned %s", remote, dlerror());
2N/A } else {
2N/A nis_call = (int (*)())
2N/A dlsym(dlhandle, NIS_SYMBOL);
2N/A }
2N/A init_nis_pubkey = 1;
2N/A }
2N/A mutex_unlock(&init_nis_pubkey_lock);
2N/A }
2N/A if (nis_call && (*nis_call)()) {
2N/A int key_cached;
2N/A return (__getpublickey_cached_g(remote, keylen, algtype,
2N/A pk, pklen, &key_cached));
2N/A }
2N/A
2N/A /*
2N/A * If we're not being called by a nis plus server or that
2N/A * server does not want to get the keys from the cache we
2N/A * get the key in the normal manner.
2N/A */
2N/A
2N/A return (getpublickey_g(remote, keylen, algtype, pk, pklen));
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Routine to encrypt a set of session keys with keys derived from
2N/A * the common key with the caller and the remote principal.
2N/A */
2N/Astatic int __encrypt(const char *remotename, des_block deskeys[], int no_keys)
2N/A{
2N/A char pk[HEX_KEY_BYTES+1];
2N/A
2N/A /*
2N/A * Get the public key out of the cache if this is a NIS+
2N/A * server. The reason is that the server may be a root replica
2N/A * that has just been created. It will not yet have the
2N/A * public key data to talk to its master. When the cold start
2N/A * file is read the public keys that are found there are
2N/A * cached. We will use the cache to get the public key data so
2N/A * the server will not hang or dump core. We call NIS_getpublickey
2N/A * to get the appropriate public key from NIS+. If that fails
2N/A * we just try to get the public key in the normal manner.
2N/A */
2N/A
2N/A if (!dh_getpublickey(remotename, KEYLEN, 0, pk, sizeof (pk)))
2N/A return (-1);
2N/A
2N/A if (key_encryptsession_pk_g(remotename, pk,
2N/A KEYLEN, ALGTYPE, deskeys, no_keys))
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Routine to decrypt a set of session keys with the common key that
2N/A * is held between the caller and the remote principal.
2N/A */
2N/Astatic int __decrypt(const char *remotename,
2N/A des_block deskeys[], int no_keys, int *key_cached)
2N/A{
2N/A int *use_cache = key_cached;
2N/A char pk[HEX_KEY_BYTES+1];
2N/A
2N/A if (key_cached) {
2N/A use_cache = *key_cached ? key_cached : 0;
2N/A *key_cached = 0;
2N/A }
2N/A
2N/A#ifdef DH_DEBUG
2N/A syslog(LOG_DEBUG, "dh: __decrypt is %s cache for %s\n",
2N/A use_cache ? "using" : "not using", remotename);
2N/A#endif
2N/A
2N/A /*
2N/A * If we are not using the cache, flush the entry for remotename.
2N/A * It may be bad. The call to __getpublickey_cached_g below will
2N/A * repopulate the cache with the current public key.
2N/A */
2N/A if (!use_cache)
2N/A __getpublickey_flush_g(remotename, KEYLEN, ALGTYPE);
2N/A
2N/A /* Get the public key */
2N/A if (!__getpublickey_cached_g(remotename, KEYLEN,
2N/A 0, pk, sizeof (pk), use_cache))
2N/A return (-1);
2N/A
2N/A#if DH_DEBUG
2N/A if (use_cache)
2N/A syslog(LOG_DEBUG, "dh: __decrypt cache = %d\n", *key_cached);
2N/A#endif
2N/A
2N/A if (key_decryptsession_pk_g(remotename, pk,
2N/A KEYLEN, ALGTYPE, deskeys, no_keys)) {
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Routine to generate a set of random session keys.
2N/A */
2N/Astatic int __gendes(des_block deskeys[], int no_keys)
2N/A{
2N/A
2N/A memset(deskeys, 0, no_keys* sizeof (des_block));
2N/A if (key_gendes_g(deskeys, no_keys))
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Routine that will return true if this mechanism corresponding
2N/A * private keys has been set.
2N/A */
2N/Astatic int __secret_is_set(void)
2N/A{
2N/A return (key_secretkey_is_set_g(KEYLEN, ALGTYPE));
2N/A}
2N/A
2N/A/*
2N/A * Routine to retrieve the callers principal name. Note it is up to
2N/A * the caller to free the result.
2N/A */
2N/Astatic char *__get_principal(void)
2N/A{
2N/A char netname[MAXNETNAMELEN+1];
2N/A
2N/A if (getnetname(netname))
2N/A return (strdup(netname));
2N/A
2N/A return (NULL);
2N/A}