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__)
31static 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
40extern caddr_t xdralloc_getdata(XDR *xdrs);
41extern void xdralloc_create(XDR *xdrs, enum xdr_op op);
42
43krb5_principal	    master_princ;
44krb5_db_entry	    master_db;
45
46krb5_principal	    hist_princ;
47krb5_keyblock	    hist_key;
48krb5_db_entry	    hist_db;
49krb5_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
54krb5_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
98done:
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 */
131krb5_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
217done:
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 */
242krb5_error_code
243kdb_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
318krb5_error_code
319kdb_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 */
355krb5_error_code
356kdb_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
400krb5_error_code
401kdb_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
411typedef struct _iter_data {
412    void (*func)(void *, krb5_principal);
413    void *data;
414} iter_data;
415
416static krb5_error_code
417kdb_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
426krb5_error_code
427kdb_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