1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
8  *
9  *	Openvision retains the copyright to derivative works of
10  *	this source code.  Do *NOT* create a derivative of this
11  *	source code before consulting with your legal department.
12  *	Do *NOT* integrate *ANY* of this source code into another
13  *	product before consulting with your legal department.
14  *
15  *	For further information, read the top-level Openvision
16  *	copyright which is contained in the top-level MIT Kerberos
17  *	copyright.
18  *
19  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
20  *
21  */
22 
23 
24 /*
25  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
26  *
27  * $Header$
28  */
29 
30 #if !defined(lint) && !defined(__CODECENTER__)
31 static char *rcsid = "$Header$";
32 #endif
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include "k5-int.h"
37 #include <kadm5/admin.h>
38 #include "server_internal.h"
39 
40 extern caddr_t xdralloc_getdata(XDR *xdrs);
41 extern void xdralloc_create(XDR *xdrs, enum xdr_op op);
42 
43 krb5_principal	    master_princ;
44 krb5_db_entry	    master_db;
45 
46 krb5_principal	    hist_princ;
47 krb5_keyblock	    hist_key;
48 krb5_db_entry	    hist_db;
49 krb5_kvno	    hist_kvno;
50 
51 /* much of this code is stolen from the kdc.  there should be some
52    library code to deal with this. */
53 
kdb_init_master(kadm5_server_handle_t handle,char * r,int from_keyboard)54 krb5_error_code kdb_init_master(kadm5_server_handle_t handle,
55 				char *r, int from_keyboard)
56 {
57     int		   ret = 0;
58     char	   *realm;
59     krb5_boolean   from_kbd = FALSE;
60 
61     if (from_keyboard)
62       from_kbd = TRUE;
63 
64     if (r == NULL)  {
65 	if ((ret = krb5_get_default_realm(handle->context, &realm)))
66 	    return ret;
67     } else {
68 	realm = r;
69     }
70 
71     if ((ret = krb5_db_setup_mkey_name(handle->context,
72 				       handle->params.mkey_name,
73 				       realm, NULL, &master_princ)))
74 	goto done;
75 /* Solaris Kerberos */
76 #if 0
77     master_keyblock.enctype = handle->params.enctype;
78 #endif
79 
80     /* Solaris Kerberos */
81     ret = krb5_db_fetch_mkey(handle->context, master_princ,
82 			     handle->params.enctype, from_kbd,
83 			     FALSE /* only prompt once */,
84 			     handle->params.stash_file,
85 			     NULL /* I'm not sure about this,
86 				     but it's what the kdc does --marc */,
87 			     &handle->master_keyblock);
88     if (ret)
89 	goto done;
90 
91     /* Solaris Kerberos */
92     if ((ret = krb5_db_verify_master_key(handle->context, master_princ,
93 					 &handle->master_keyblock))) {
94 	  krb5_db_fini(handle->context);
95 	  return ret;
96     }
97 
98 done:
99     if (r == NULL)
100 	free(realm);
101 
102     return(ret);
103 }
104 
105 /*
106  * Function: kdb_init_hist
107  *
108  * Purpose: Initializes the global history variables.
109  *
110  * Arguments:
111  *
112  *	handle		(r) kadm5 api server handle
113  *	r		(r) realm of history principal to use, or NULL
114  *
115  * Effects: This function sets the value of the following global
116  * variables:
117  *
118  *	hist_princ	krb5_principal holding the history principal
119  *	hist_db		krb5_db_entry of the history principal
120  *	hist_key	krb5_keyblock holding the history principal's key
121  *	hist_encblock	krb5_encrypt_block holding the procssed hist_key
122  *	hist_kvno	the version number of the history key
123  *
124  * If the history principal does not already exist, this function
125  * attempts to create it with kadm5_create_principal.  WARNING!
126  * If the history principal is deleted and this function is executed
127  * (by kadmind, or kadmin.local, or anything else with permission),
128  * the principal will be assigned a new random key and all existing
129  * password history information will become useless.
130  */
kdb_init_hist(kadm5_server_handle_t handle,char * r)131 krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r)
132 {
133     int	    ret = 0;
134     char    *realm, *hist_name;
135     krb5_key_data *key_data;
136     krb5_key_salt_tuple ks[1];
137 
138     if (r == NULL)  {
139 	if ((ret = krb5_get_default_realm(handle->context, &realm)))
140 	    return ret;
141     } else {
142 	realm = r;
143     }
144 
145     if ((hist_name = (char *) malloc(strlen(KADM5_HIST_PRINCIPAL) +
146 				     strlen(realm) + 2)) == NULL)
147 	goto done;
148 
149     (void) sprintf(hist_name, "%s@%s", KADM5_HIST_PRINCIPAL, realm);
150 
151     if ((ret = krb5_parse_name(handle->context, hist_name, &hist_princ)))
152 	goto done;
153 
154     if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL))) {
155 	kadm5_principal_ent_rec ent;
156 
157 	if (ret != KADM5_UNK_PRINC)
158 	    goto done;
159 
160 	/* try to create the principal */
161 
162 	memset(&ent, 0, sizeof(ent));
163 
164 	ent.principal = hist_princ;
165 	ent.max_life = KRB5_KDB_DISALLOW_ALL_TIX;
166 	ent.attributes = 0;
167 
168 	/* this uses hist_kvno.  So we set it to 2, which will be the
169 	   correct value once the principal is created and randomized.
170 	   Of course, it doesn't make sense to keep a history for the
171 	   history principal, anyway. */
172 
173 	hist_kvno = 2;
174 	ks[0].ks_enctype = handle->params.enctype;
175 	ks[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
176 	ret = kadm5_create_principal_3(handle, &ent,
177 				       (KADM5_PRINCIPAL | KADM5_MAX_LIFE |
178 					KADM5_ATTRIBUTES),
179 				       1, ks,
180 				       "to-be-random");
181 	if (ret)
182 	    goto done;
183 
184 	/* this won't let us randomize the hist_princ.  So we cheat. */
185 
186 	hist_princ = NULL;
187 
188 	ret = kadm5_randkey_principal_3(handle, ent.principal, 0, 1, ks,
189 					NULL, NULL);
190 
191 	hist_princ = ent.principal;
192 
193 	if (ret)
194 	    goto done;
195 
196 	/* now read the newly-created kdb record out of the
197 	   database. */
198 
199 	if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL)))
200 	    goto done;
201 
202     }
203 
204     ret = krb5_dbe_find_enctype(handle->context, &hist_db,
205 				handle->params.enctype, -1, -1, &key_data);
206     if (ret)
207 	goto done;
208 
209     /* Solaris Kerberos */
210     ret = krb5_dbekd_decrypt_key_data(handle->context,
211 				 &handle->master_keyblock, key_data, &hist_key, NULL);
212     if (ret)
213 	goto done;
214 
215     hist_kvno = key_data->key_data_kvno;
216 
217 done:
218     free(hist_name);
219     if (r == NULL)
220 	free(realm);
221     return ret;
222 }
223 
224 /*
225  * Function: kdb_get_entry
226  *
227  * Purpose: Gets an entry from the kerberos database and breaks
228  * it out into a krb5_db_entry and an osa_princ_ent_t.
229  *
230  * Arguments:
231  *
232  *		handle		(r) the server_handle
233  * 		principal	(r) the principal to get
234  * 		kdb		(w) krb5_db_entry to fill in
235  * 		adb		(w) osa_princ_ent_rec to fill in
236  *
237  * when the caller is done with kdb and adb, kdb_free_entry must be
238  * called to release them.  The adb record is filled in with the
239  * contents of the KRB5_TL_KADM_DATA record; if that record doesn't
240  * exist, an empty but valid adb record is returned.
241  */
242 krb5_error_code
kdb_get_entry(kadm5_server_handle_t handle,krb5_principal principal,krb5_db_entry * kdb,osa_princ_ent_rec * adb)243 kdb_get_entry(kadm5_server_handle_t handle,
244 	      krb5_principal principal, krb5_db_entry *kdb,
245 	      osa_princ_ent_rec *adb)
246 {
247     krb5_error_code ret;
248     int nprincs;
249     krb5_boolean more;
250     krb5_tl_data tl_data;
251     XDR xdrs;
252 
253     ret = krb5_db_get_principal(handle->context, principal, kdb, &nprincs,
254 				&more);
255     if (ret)
256 	return(ret);
257 
258     if (more) {
259 	krb5_db_free_principal(handle->context, kdb, nprincs);
260 	return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
261     } else if (nprincs != 1) {
262 	krb5_db_free_principal(handle->context, kdb, nprincs);
263 	return(KADM5_UNK_PRINC);
264     }
265 
266     if (adb) {
267 	memset(adb, 0, sizeof(*adb));
268 
269 	tl_data.tl_data_type = KRB5_TL_KADM_DATA;
270 	/*
271 	 * XXX Currently, lookup_tl_data always returns zero; it sets
272 	 * tl_data->tl_data_length to zero if the type isn't found.
273 	 * This should be fixed...
274 	 */
275 	if ((ret = krb5_dbe_lookup_tl_data(handle->context, kdb, &tl_data))
276 	    || (tl_data.tl_data_length == 0)) {
277 	    /* there's no admin data.  this can happen, if the admin
278 	       server is put into production after some principals
279 	       are created.  In this case, return valid admin
280 	       data (which is all zeros with the hist_kvno filled
281 	       in), and when the entry is written, the admin
282 	       data will get stored correctly. */
283 
284 	    adb->admin_history_kvno = hist_kvno;
285 
286 	    return(ret);
287 	}
288 
289 	/* Solaris Kerberos */
290 	xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents,
291 		      tl_data.tl_data_length, XDR_DECODE);
292 	if (! xdr_osa_princ_ent_rec(&xdrs, adb)) {
293 	   xdr_destroy(&xdrs);
294 	   krb5_db_free_principal(handle->context, kdb, 1);
295 	   return(KADM5_XDR_FAILURE);
296 	}
297 	xdr_destroy(&xdrs);
298     }
299 
300     return(0);
301 }
302 
303 /*
304  * Function: kdb_free_entry
305  *
306  * Purpose: frees the resources allocated by kdb_get_entry
307  *
308  * Arguments:
309  *
310  *		handle		(r) the server_handle
311  * 		kdb		(w) krb5_db_entry to fill in
312  * 		adb		(w) osa_princ_ent_rec to fill in
313  *
314  * when the caller is done with kdb and adb, kdb_free_entry must be
315  * called to release them.
316  */
317 
318 krb5_error_code
kdb_free_entry(kadm5_server_handle_t handle,krb5_db_entry * kdb,osa_princ_ent_rec * adb)319 kdb_free_entry(kadm5_server_handle_t handle,
320 	       krb5_db_entry *kdb, osa_princ_ent_rec *adb)
321 {
322     XDR xdrs;
323 
324 
325     if (kdb)
326 	krb5_db_free_principal(handle->context, kdb, 1);
327 
328     if (adb) {
329 	xdrmem_create(&xdrs, NULL, 0, XDR_FREE);
330 	xdr_osa_princ_ent_rec(&xdrs, adb);
331 	xdr_destroy(&xdrs);
332     }
333 
334     return(0);
335 }
336 
337 /*
338  * Function: kdb_put_entry
339  *
340  * Purpose: Stores the osa_princ_ent_t and krb5_db_entry into to
341  * database.
342  *
343  * Arguments:
344  *
345  *		handle	(r) the server_handle
346  * 		kdb	(r/w) the krb5_db_entry to store
347  * 		adb	(r) the osa_princ_db_ent to store
348  *
349  * Effects:
350  *
351  * The last modifier field of the kdb is set to the caller at now.
352  * adb is encoded with xdr_osa_princ_ent_ret and stored in kbd as
353  * KRB5_TL_KADM_DATA.  kdb is then written to the database.
354  */
355 krb5_error_code
kdb_put_entry(kadm5_server_handle_t handle,krb5_db_entry * kdb,osa_princ_ent_rec * adb)356 kdb_put_entry(kadm5_server_handle_t handle,
357 	      krb5_db_entry *kdb, osa_princ_ent_rec *adb)
358 {
359     krb5_error_code ret;
360     krb5_int32 now;
361     XDR xdrs;
362     krb5_tl_data tl_data;
363     int one;
364 
365     ret = krb5_timeofday(handle->context, &now);
366     if (ret)
367 	return(ret);
368 
369     ret = krb5_dbe_update_mod_princ_data(handle->context, kdb, now,
370 					 handle->current_caller);
371     if (ret)
372 	return(ret);
373 
374     xdralloc_create(&xdrs, XDR_ENCODE);
375     if(! xdr_osa_princ_ent_rec(&xdrs, adb)) {
376 	xdr_destroy(&xdrs);
377 	return(KADM5_XDR_FAILURE);
378     }
379     tl_data.tl_data_type = KRB5_TL_KADM_DATA;
380     tl_data.tl_data_length = xdr_getpos(&xdrs);
381     /* Solaris Kerberos */
382     tl_data.tl_data_contents = (unsigned char *) xdralloc_getdata(&xdrs);
383 
384     ret = krb5_dbe_update_tl_data(handle->context, kdb, &tl_data);
385 
386     xdr_destroy(&xdrs);
387 
388     if (ret)
389 	return(ret);
390 
391     one = 1;
392 
393     ret = krb5_db_put_principal(handle->context, kdb, &one);
394     if (ret)
395 	return(ret);
396 
397     return(0);
398 }
399 
400 krb5_error_code
kdb_delete_entry(kadm5_server_handle_t handle,krb5_principal name)401 kdb_delete_entry(kadm5_server_handle_t handle, krb5_principal name)
402 {
403     int one = 1;
404     krb5_error_code ret;
405 
406     ret = krb5_db_delete_principal(handle->context, name, &one);
407 
408     return ret;
409 }
410 
411 typedef struct _iter_data {
412     void (*func)(void *, krb5_principal);
413     void *data;
414 } iter_data;
415 
416 static krb5_error_code
kdb_iter_func(krb5_pointer data,krb5_db_entry * kdb)417 kdb_iter_func(krb5_pointer data, krb5_db_entry *kdb)
418 {
419     iter_data *id = (iter_data *) data;
420 
421     (*(id->func))(id->data, kdb->princ);
422 
423     return(0);
424 }
425 
426 krb5_error_code
kdb_iter_entry(kadm5_server_handle_t handle,char * match_entry,void (* iter_fct)(void *,krb5_principal),void * data)427 kdb_iter_entry(kadm5_server_handle_t handle, char *match_entry,
428 	       void (*iter_fct)(void *, krb5_principal), void *data)
429 {
430     iter_data id;
431     krb5_error_code ret;
432 
433     id.func = iter_fct;
434     id.data = data;
435 
436     /* Solaris Kerberos: added support for db_args */
437     ret = krb5_db_iterate(handle->context, match_entry, kdb_iter_func, &id, NULL);
438     if (ret)
439 	return(ret);
440 
441     return(0);
442 }
443 
444