xref: /illumos-gate/usr/src/lib/krb5/kadm5/srv/server_kdb.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * Copyright 2004 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.2 1998/10/30 02:54:39 marc 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.2 1998/10/30 02:54:39 marc 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_keyblock  tmk;
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 
76     if (ret = krb5_db_fetch_mkey(handle->context, master_princ,
77 				 handle->params.enctype,
78 				from_keyboard,
79 				 FALSE /* only prompt once */,
80 				 handle->params.stash_file,
81 				 NULL /* I'm not sure about this,
82 					 but it's what the kdc does --marc */,
83 				 &handle->master_keyblock))
84 	goto done;
85 
86     if ((ret = krb5_db_init(handle->context)) != KSUCCESS)
87 	goto done;
88 
89     if ((ret = krb5_db_verify_master_key(handle->context, master_princ,
90 					 &handle->master_keyblock))) {
91 	  krb5_db_fini(handle->context);
92 	  return ret;
93     }
94 
95 done:
96     if (r == NULL)
97 	free(realm);
98 
99     return(ret);
100 }
101 
102 /*
103  * Function: kdb_init_hist
104  *
105  * Purpose: Initializes the global history variables.
106  *
107  * Arguments:
108  *
109  *	handle		(r) kadm5 api server handle
110  *	r		(r) realm of history principal to use, or NULL
111  *
112  * Effects: This function sets the value of the following global
113  * variables:
114  *
115  *	hist_princ	krb5_principal holding the history principal
116  *	hist_db		krb5_db_entry of the history principal
117  *	hist_key	krb5_keyblock holding the history principal's key
118  *	hist_encblock	krb5_encrypt_block holding the procssed hist_key
119  *	hist_kvno	the version number of the history key
120  *
121  * If the history principal does not already exist, this function
122  * attempts to create it with kadm5_create_principal.  WARNING!
123  * If the history principal is deleted and this function is executed
124  * (by kadmind, or kadmin.local, or anything else with permission),
125  * the principal will be assigned a new random key and all existing
126  * password history information will become useless.
127  */
128 krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r)
129 {
130     int	    ret = 0;
131     char    *realm, *hist_name;
132     krb5_key_data *key_data;
133     krb5_key_salt_tuple ks[1];
134 
135     if (r == NULL)  {
136 	if ((ret = krb5_get_default_realm(handle->context, &realm)))
137 	    return ret;
138     } else {
139 	realm = r;
140     }
141 
142     if ((hist_name = (char *) malloc(strlen(KADM5_HIST_PRINCIPAL) +
143 				     strlen(realm) + 2)) == NULL)
144 	goto done;
145 
146     (void) sprintf(hist_name, "%s@%s", KADM5_HIST_PRINCIPAL, realm);
147 
148     if ((ret = krb5_parse_name(handle->context, hist_name, &hist_princ)))
149 	goto done;
150 
151     if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL))) {
152 	kadm5_principal_ent_rec ent;
153 
154 	if (ret != KADM5_UNK_PRINC)
155 	    goto done;
156 
157 	/* try to create the principal */
158 
159 	memset(&ent, 0, sizeof(ent));
160 
161 	ent.principal = hist_princ;
162 	ent.max_life = KRB5_KDB_DISALLOW_ALL_TIX;
163 	ent.attributes = 0;
164 
165 	/* this uses hist_kvno.  So we set it to 2, which will be the
166 	   correct value once the principal is created and randomized.
167 	   Of course, it doesn't make sense to keep a history for the
168 	   history principal, anyway. */
169 
170 	hist_kvno = 2;
171 	ks[0].ks_enctype = handle->params.enctype;
172 	ks[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
173 	ret = kadm5_create_principal_3(handle, &ent,
174 				     (KADM5_PRINCIPAL |
175 				       KADM5_MAX_LIFE |
176 				       KADM5_ATTRIBUTES),
177 				       1, ks,
178 				      "to-be-random");
179 	if (ret)
180 	    goto done;
181 
182 	/* this won't let us randomize the hist_princ.  So we cheat. */
183 
184 	hist_princ = NULL;
185 
186 	ret = kadm5_randkey_principal_3(handle, ent.principal, 0, 1, ks,
187 					NULL, NULL);
188 
189 	hist_princ = ent.principal;
190 
191 	if (ret)
192 	    goto done;
193 
194 	/* now read the newly-created kdb record out of the
195 	   database. */
196 
197 	if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL)))
198 	    goto done;
199 
200     }
201 
202     ret = krb5_dbe_find_enctype(handle->context, &hist_db,
203 			    handle->params.enctype, -1, -1, &key_data);
204     if (ret)
205 	goto done;
206 
207     ret = krb5_dbekd_decrypt_key_data(handle->context,
208 		&handle->master_keyblock, key_data, &hist_key, NULL);
209     if (ret)
210 	goto done;
211 
212     hist_kvno = key_data->key_data_kvno;
213 
214 done:
215     free(hist_name);
216     if (r == NULL)
217 	free(realm);
218     return ret;
219 }
220 
221 /*
222  * Function: kdb_get_entry
223  *
224  * Purpose: Gets an entry from the kerberos database and breaks
225  * it out into a krb5_db_entry and an osa_princ_ent_t.
226  *
227  * Arguments:
228  *
229  *		handle		(r) the server_handle
230  * 		principal	(r) the principal to get
231  * 		kdb		(w) krb5_db_entry to fill in
232  * 		adb		(w) osa_princ_ent_rec to fill in
233  *
234  * when the caller is done with kdb and adb, kdb_free_entry must be
235  * called to release them.  The adb record is filled in with the
236  * contents of the KRB5_TL_KADM_DATA record; if that record doesn't
237  * exist, an empty but valid adb record is returned.
238  */
239 krb5_error_code
240 kdb_get_entry(kadm5_server_handle_t handle,
241 	      krb5_principal principal, krb5_db_entry *kdb,
242 	      osa_princ_ent_rec *adb)
243 {
244     krb5_error_code ret;
245     int nprincs;
246     krb5_boolean more;
247     krb5_tl_data tl_data;
248     XDR xdrs;
249 
250     if (ret = krb5_db_get_principal(handle->context, principal, kdb, &nprincs,
251 				    &more))
252 	return(ret);
253 
254     if (more) {
255 	krb5_db_free_principal(handle->context, kdb, nprincs);
256 	return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
257     } else if (nprincs != 1) {
258 	krb5_db_free_principal(handle->context, kdb, nprincs);
259 	return(KADM5_UNK_PRINC);
260     }
261 
262     if (adb) {
263 	memset(adb, 0, sizeof(*adb));
264 
265 	tl_data.tl_data_type = KRB5_TL_KADM_DATA;
266 	/*
267 	 * XXX Currently, lookup_tl_data always returns zero; it sets
268 	 * tl_data->tl_data_length to zero if the type isn't found.
269 	 * This should be fixed...
270 	 */
271 	if ((ret = krb5_dbe_lookup_tl_data(handle->context, kdb, &tl_data))
272 	    || (tl_data.tl_data_length == 0)) {
273 	    /* there's no admin data.  this can happen, if the admin
274 	       server is put into production after some principals
275 	       are created.  In this case, return valid admin
276 	       data (which is all zeros with the hist_kvno filled
277 	       in), and when the entry is written, the admin
278 	       data will get stored correctly. */
279 
280 	    adb->admin_history_kvno = hist_kvno;
281 
282 	    return(ret);
283 	}
284 
285 	xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents,
286 		      tl_data.tl_data_length, XDR_DECODE);
287 	if (! xdr_osa_princ_ent_rec(&xdrs, adb)) {
288 	   xdr_destroy(&xdrs);
289 	   krb5_db_free_principal(handle->context, kdb, 1);
290 	   return(OSA_ADB_XDR_FAILURE);
291 	}
292 	xdr_destroy(&xdrs);
293     }
294 
295     return(0);
296 }
297 
298 /*
299  * Function: kdb_free_entry
300  *
301  * Purpose: frees the resources allocated by kdb_get_entry
302  *
303  * Arguments:
304  *
305  *		handle		(r) the server_handle
306  * 		kdb		(w) krb5_db_entry to fill in
307  * 		adb		(w) osa_princ_ent_rec to fill in
308  *
309  * when the caller is done with kdb and adb, kdb_free_entry must be
310  * called to release them.
311  */
312 
313 krb5_error_code
314 kdb_free_entry(kadm5_server_handle_t handle,
315 	       krb5_db_entry *kdb, osa_princ_ent_rec *adb)
316 {
317     XDR xdrs;
318 
319 
320     if (kdb)
321 	krb5_db_free_principal(handle->context, kdb, 1);
322 
323     if (adb) {
324 	xdrmem_create(&xdrs, NULL, 0, XDR_FREE);
325 	xdr_osa_princ_ent_rec(&xdrs, adb);
326 	xdr_destroy(&xdrs);
327     }
328 
329     return(0);
330 }
331 
332 /*
333  * Function: kdb_put_entry
334  *
335  * Purpose: Stores the osa_princ_ent_t and krb5_db_entry into to
336  * database.
337  *
338  * Arguments:
339  *
340  *		handle	(r) the server_handle
341  * 		kdb	(r/w) the krb5_db_entry to store
342  * 		adb	(r) the osa_princ_db_ent to store
343  *
344  * Effects:
345  *
346  * The last modifier field of the kdb is set to the caller at now.
347  * adb is encoded with xdr_osa_princ_ent_ret and stored in kbd as
348  * KRB5_TL_KADM_DATA.  kdb is then written to the database.
349  */
350 krb5_error_code
351 kdb_put_entry(kadm5_server_handle_t handle,
352 	      krb5_db_entry *kdb, osa_princ_ent_rec *adb)
353 {
354     krb5_error_code ret;
355     krb5_int32 now;
356     XDR xdrs;
357     krb5_tl_data tl_data;
358     int one;
359 
360     if (ret = krb5_timeofday(handle->context, &now))
361 	return(ret);
362 
363     if (ret = krb5_dbe_update_mod_princ_data(handle->context, kdb, now,
364 					     handle->current_caller))
365 	return(ret);
366 
367     xdralloc_create(&xdrs, XDR_ENCODE);
368     if(! xdr_osa_princ_ent_rec(&xdrs, adb)) {
369 	xdr_destroy(&xdrs);
370 	return(OSA_ADB_XDR_FAILURE);
371     }
372     tl_data.tl_data_type = KRB5_TL_KADM_DATA;
373     tl_data.tl_data_length = xdr_getpos(&xdrs);
374     tl_data.tl_data_contents = (unsigned char *) xdralloc_getdata(&xdrs);
375 
376     ret = krb5_dbe_update_tl_data(handle->context, kdb, &tl_data);
377 
378     xdr_destroy(&xdrs);
379 
380     if (ret)
381 	return(ret);
382 
383     one = 1;
384 
385     if (ret = krb5_db_put_principal(handle->context, kdb, &one))
386 	return(ret);
387 
388     return(0);
389 }
390 
391 krb5_error_code
392 kdb_delete_entry(kadm5_server_handle_t handle, krb5_principal name)
393 {
394     int one = 1;
395     krb5_error_code ret;
396 
397     ret = krb5_db_delete_principal(handle->context, name, &one);
398 
399     return ret;
400 }
401 
402 typedef struct _iter_data {
403     void (*func)(void *, krb5_principal);
404     void *data;
405 } iter_data;
406 
407 static krb5_error_code
408 kdb_iter_func(krb5_pointer data, krb5_db_entry *kdb)
409 {
410     iter_data *id = (iter_data *) data;
411 
412     (*(id->func))(id->data, kdb->princ);
413 
414     return(0);
415 }
416 
417 krb5_error_code
418 kdb_iter_entry(kadm5_server_handle_t handle,
419 	       void (*iter_fct)(void *, krb5_principal), void *data)
420 {
421     iter_data id;
422     krb5_error_code ret;
423 
424     id.func = iter_fct;
425     id.data = data;
426 
427     if (ret = krb5_db_iterate(handle->context, kdb_iter_func, &id))
428 	return(ret);
429 
430     return(0);
431 }
432 
433