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
gss_mech_initialize()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
dh_getpublickey(const char * remote,keylen_t keylen,algtype_t algtype,char * pk,size_t pklen)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  */
__encrypt(const char * remotename,des_block deskeys[],int no_keys)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  */
__decrypt(const char * remotename,des_block deskeys[],int no_keys,int * key_cached)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  */
__gendes(des_block deskeys[],int no_keys)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  */
__secret_is_set(void)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  */
__get_principal(void)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 }
252