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