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