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