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 }