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 }
250