Home | History | Annotate | Download | only in srv
      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 
     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  */
    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
    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
    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
    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
    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
    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
    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