1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  *      dh_template.c
  24  *
  25  *      Copyright (c) 1997, by Sun Microsystems, Inc.
  26  *      All rights reserved.
  27  */
  28 
  29 #pragma ident   "%Z%%M% %I%     %E% SMI"
  30 
  31 #include <stdlib.h>
  32 #include <string.h>
  33 #include <syslog.h>
  34 #include <dh_gssapi.h>
  35 #include <dlfcn.h>
  36 #include "../dh_common/dh_common.h"
  37 
  38 extern int key_encryptsession_pk_g();
  39 extern int key_decryptsession_pk_g();
  40 extern int key_gendes_g();
  41 extern int key_secretkey_is_set_g();
  42 
  43 static int __encrypt(const char *remotename, des_block deskeys[], int no_keys);
  44 static int __decrypt(const char *remotename,
  45                     des_block deskeys[], int no_keys, int *key_cached);
  46 static int __gendes(des_block deskeys[], int no_keys);
  47 static int __secret_is_set(void);
  48 static char *__get_principal(void);
  49 
  50 /*
  51  * This module defines the entry point for gss_mech_initialize and the
  52  * key opts for Diffie-Hellman mechanism of type algorithm 0. Each algorithm
  53  * 0 mechanism defines its OID, MODULUS, ROOT, KEYLEN, ALGTYPE (which should
  54  * be zero) and HEX_KEY_BYTES. That module then will #include this file.
  55  */
  56 
  57 /* The keyopts for the per mechanism context */
  58 static dh_keyopts_desc dh_keyopts = {
  59         __encrypt,
  60         __decrypt,
  61         __gendes,
  62         __secret_is_set,
  63         __get_principal
  64 };
  65 
  66 /* The gss_context for this mechanism */
  67 static struct gss_config  dh_mech;
  68 
  69 /*
  70  * gss_mech_initialize: This is the libgss entry point to bring this
  71  * mechanism on line.  It is just a wrap to pass the pointer to its
  72  * gss_config structure, OID, and the above keyopts to the common
  73  * __dh_geneirc_initialize routine. We return null on failure, otherwise
  74  * we return the mechanism's gss_mechanism.
  75  */
  76 gss_mechanism
  77 gss_mech_initialize()
  78 {
  79         gss_mechanism mech;
  80 
  81         mech = __dh_generic_initialize(&dh_mech, OID, &dh_keyopts);
  82 
  83         if (mech == NULL) {
  84                 return (NULL);
  85         }
  86 
  87         return (mech);
  88 }
  89 
  90 /*
  91  * A NIS+ server will define the function __rpcsec_gss_is_server.
  92  * This function will return one when it is appropriate to get public
  93  * keys out of the per process public key cache. Appropriateness here
  94  * is when the name server just put the public key in the cache from a
  95  * received directory object, typically from the cold start file.
  96  */
  97 static int
  98 dh_getpublickey(const char *remote, keylen_t keylen, algtype_t algtype,
  99                 char *pk, size_t pklen)
 100 {
 101         static mutex_t init_nis_pubkey_lock = DEFAULTMUTEX;
 102         static int init_nis_pubkey = 0;
 103         static int (*nis_call)();
 104         static const char NIS_SYMBOL[] = "__rpcsec_gss_is_server";
 105 
 106         if (!init_nis_pubkey) {
 107                 mutex_lock(&init_nis_pubkey_lock);
 108                 if (!init_nis_pubkey) {
 109                         void *dlhandle = dlopen(0, RTLD_NOLOAD);
 110                         if (dlhandle == 0) {
 111                                 syslog(LOG_ERR, "dh: Could not dlopen "
 112                                     "in dh_getpublickey for %s. "
 113                                     "dlopen returned %s", remote, dlerror());
 114                         } else {
 115                                 nis_call = (int (*)())
 116                                         dlsym(dlhandle, NIS_SYMBOL);
 117                         }
 118                         init_nis_pubkey = 1;
 119                 }
 120                 mutex_unlock(&init_nis_pubkey_lock);
 121         }
 122         if (nis_call && (*nis_call)()) {
 123                 int key_cached;
 124                 return (__getpublickey_cached_g(remote, keylen, algtype,
 125                                             pk, pklen, &key_cached));
 126         }
 127 
 128         /*
 129          * If we're not being called by a nis plus server or that
 130          * server does not want to get the keys from the cache we
 131          * get the key in the normal manner.
 132          */
 133 
 134         return (getpublickey_g(remote, keylen, algtype, pk, pklen));
 135 }
 136 
 137 
 138 /*
 139  * Routine to encrypt a set of session keys with keys derived from
 140  * the common key with the caller and the remote principal.
 141  */
 142 static int __encrypt(const char *remotename, des_block deskeys[], int no_keys)
 143 {
 144         char pk[HEX_KEY_BYTES+1];
 145 
 146         /*
 147          * Get the public key out of the cache if this is a NIS+
 148          * server. The reason is that the server may be a root replica
 149          * that has just been created. It will not yet have the
 150          * public key data to talk to its master. When the cold start
 151          * file is read the public keys that are found there are
 152          * cached. We will use the cache to get the public key data so
 153          * the server will not hang or dump core. We call NIS_getpublickey
 154          * to get the appropriate public key from NIS+. If that fails
 155          * we just try to get the public key in the normal manner.
 156          */
 157 
 158         if (!dh_getpublickey(remotename, KEYLEN, 0, pk, sizeof (pk)))
 159                         return (-1);
 160 
 161         if (key_encryptsession_pk_g(remotename, pk,
 162                                     KEYLEN, ALGTYPE, deskeys, no_keys))
 163                 return (-1);
 164 
 165         return (0);
 166 }
 167 
 168 /*
 169  * Routine to decrypt a set of session keys with the common key that
 170  * is held between the caller and the remote principal.
 171  */
 172 static int __decrypt(const char *remotename,
 173                     des_block deskeys[], int no_keys, int *key_cached)
 174 {
 175         int *use_cache = key_cached;
 176         char pk[HEX_KEY_BYTES+1];
 177 
 178         if (key_cached) {
 179                 use_cache = *key_cached ? key_cached : 0;
 180                 *key_cached = 0;
 181         }
 182 
 183 #ifdef DH_DEBUG
 184         syslog(LOG_DEBUG, "dh: __decrypt is %s cache for %s\n",
 185                 use_cache ? "using" : "not using", remotename);
 186 #endif
 187 
 188         /*
 189          * If we are not using the cache, flush the entry for remotename.
 190          * It may be bad. The call to __getpublickey_cached_g below will
 191          * repopulate the cache with the current public key.
 192          */
 193         if (!use_cache)
 194                 __getpublickey_flush_g(remotename, KEYLEN, ALGTYPE);
 195 
 196         /* Get the public key */
 197         if (!__getpublickey_cached_g(remotename, KEYLEN,
 198                                     0, pk, sizeof (pk), use_cache))
 199                 return (-1);
 200 
 201 #if DH_DEBUG
 202         if (use_cache)
 203                 syslog(LOG_DEBUG, "dh: __decrypt cache = %d\n", *key_cached);
 204 #endif
 205 
 206         if (key_decryptsession_pk_g(remotename, pk,
 207                                     KEYLEN, ALGTYPE, deskeys, no_keys)) {
 208 
 209                 return (-1);
 210         }
 211 
 212         return (0);
 213 }
 214 
 215 /*
 216  * Routine to generate a set of random session keys.
 217  */
 218 static int __gendes(des_block deskeys[], int no_keys)
 219 {
 220 
 221         memset(deskeys, 0, no_keys* sizeof (des_block));
 222         if (key_gendes_g(deskeys, no_keys))
 223                         return (-1);
 224 
 225         return (0);
 226 }
 227 
 228 /*
 229  * Routine that will return true if this mechanism corresponding
 230  * private keys has been set.
 231  */
 232 static int __secret_is_set(void)
 233 {
 234         return (key_secretkey_is_set_g(KEYLEN, ALGTYPE));
 235 }
 236 
 237 /*
 238  * Routine to retrieve the callers principal name. Note it is up to
 239  * the caller to free the result.
 240  */
 241 static char * __get_principal(void)
 242 {
 243         char netname[MAXNETNAMELEN+1];
 244 
 245         if (getnetname(netname))
 246                 return (strdup(netname));
 247 
 248         return (NULL);
 249 }