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