xref: /onnv-gate/usr/src/lib/krb5/kadm5/srv/svr_principal.c (revision 12253:f3ab3e9cfad1)
1  /*
2   * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
3   */
4  
5  /*
6   * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
7   *
8   *	Openvision retains the copyright to derivative works of
9   *	this source code.  Do *NOT* create a derivative of this
10   *	source code before consulting with your legal department.
11   *	Do *NOT* integrate *ANY* of this source code into another
12   *	product before consulting with your legal department.
13   *
14   *	For further information, read the top-level Openvision
15   *	copyright which is contained in the top-level MIT Kerberos
16   *	copyright.
17   *
18   * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
19   *
20   */
21  
22  /*
23   * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
24   *
25   * $Header$
26   */
27  
28  #if !defined(lint) && !defined(__CODECENTER__)
29  static char *rcsid = "$Header$";
30  #endif
31  
32  #include	<sys/types.h>
33  #include	<sys/time.h>
34  #include	<errno.h>
35  #include	"server_internal.h"
36  #include	<kadm5/admin.h>
37  #include	<kdb.h>
38  #include	<stdio.h>
39  #include	<string.h>
40  #include	<stdarg.h>
41  #include	<stdlib.h>
42  #include	<k5-int.h>
43  #include	<kadm5/server_internal.h>
44  #include	<kadm5/admin.h>
45  #ifdef USE_PASSWORD_SERVER
46  #include	<sys/wait.h>
47  #endif
48  
49  extern	krb5_principal	    master_princ;
50  extern	krb5_principal	    hist_princ;
51  extern	krb5_keyblock	    hist_key;
52  extern	krb5_db_entry	    master_db;
53  extern	krb5_db_entry	    hist_db;
54  extern  krb5_kvno	    hist_kvno;
55  
56  static int decrypt_key_data(krb5_context context,
57  			    krb5_keyblock *, int n_key_data, krb5_key_data *key_data,
58  			    krb5_keyblock **keyblocks, int *n_keys);
59  
60  static krb5_error_code
kadm5_copy_principal(krb5_context context,krb5_const_principal inprinc,krb5_principal * outprinc)61  kadm5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc)
62  {
63      register krb5_principal tempprinc;
64      register int i, nelems;
65  
66      tempprinc = (krb5_principal)krb5_db_alloc(context, NULL, sizeof(krb5_principal_data));
67  
68      if (tempprinc == 0)
69          return ENOMEM;
70  
71      memcpy(tempprinc, inprinc, sizeof(krb5_principal_data));
72  
73      nelems = (int) krb5_princ_size(context, inprinc);
74      tempprinc->data = krb5_db_alloc(context, NULL, nelems * sizeof(krb5_data));
75  
76      if (tempprinc->data == 0) {
77  	krb5_db_free(context, (char *)tempprinc);
78          return ENOMEM;
79      }
80  
81      for (i = 0; i < nelems; i++) {
82          unsigned int len = krb5_princ_component(context, inprinc, i)->length;
83          krb5_princ_component(context, tempprinc, i)->length = len;
84          if (((krb5_princ_component(context, tempprinc, i)->data =
85                krb5_db_alloc(context, NULL, len)) == 0) && len) {
86              while (--i >= 0)
87                  krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
88              krb5_db_free (context, tempprinc->data);
89              krb5_db_free (context, tempprinc);
90              return ENOMEM;
91          }
92          if (len)
93              memcpy(krb5_princ_component(context, tempprinc, i)->data,
94                     krb5_princ_component(context, inprinc, i)->data, len);
95      }
96  
97      tempprinc->realm.data =
98  	krb5_db_alloc(context, NULL, tempprinc->realm.length = inprinc->realm.length);
99      if (!tempprinc->realm.data && tempprinc->realm.length) {
100              for (i = 0; i < nelems; i++)
101  		krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
102              krb5_db_free(context, tempprinc->data);
103              krb5_db_free(context, tempprinc);
104              return ENOMEM;
105      }
106      if (tempprinc->realm.length)
107          memcpy(tempprinc->realm.data, inprinc->realm.data,
108                 inprinc->realm.length);
109  
110      *outprinc = tempprinc;
111      return 0;
112  }
113  
114  static void
kadm5_free_principal(krb5_context context,krb5_principal val)115  kadm5_free_principal(krb5_context context, krb5_principal val)
116  {
117      register krb5_int32 i;
118  
119      if (!val)
120          return;
121  
122      if (val->data) {
123          i = krb5_princ_size(context, val);
124          while(--i >= 0)
125              krb5_db_free(context, krb5_princ_component(context, val, i)->data);
126          krb5_db_free(context, val->data);
127      }
128      if (val->realm.data)
129          krb5_db_free(context, val->realm.data);
130      krb5_db_free(context, val);
131  }
132  
133  /*
134   * XXX Functions that ought to be in libkrb5.a, but aren't.
135   */
krb5_copy_key_data_contents(context,from,to)136  kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
137     krb5_context context;
138     krb5_key_data *from, *to;
139  {
140       int i, idx;
141  
142       *to = *from;
143  
144       idx = (from->key_data_ver == 1 ? 1 : 2);
145  
146       for (i = 0; i < idx; i++) {
147         if ( from->key_data_length[i] ) {
148  	 to->key_data_contents[i] = malloc(from->key_data_length[i]);
149  	 if (to->key_data_contents[i] == NULL) {
150  	   for (i = 0; i < idx; i++) {
151  	     if (to->key_data_contents[i]) {
152  	       memset(to->key_data_contents[i], 0,
153  		      to->key_data_length[i]);
154  	       free(to->key_data_contents[i]);
155  	     }
156  	   }
157  	   return ENOMEM;
158  	 }
159  	 memcpy(to->key_data_contents[i], from->key_data_contents[i],
160  		from->key_data_length[i]);
161         }
162       }
163       return 0;
164  }
165  
dup_tl_data(krb5_tl_data * tl)166  static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
167  {
168       krb5_tl_data *n;
169  
170       n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
171       if (n == NULL)
172  	  return NULL;
173       n->tl_data_contents = malloc(tl->tl_data_length);
174       if (n->tl_data_contents == NULL) {
175  	  free(n);
176  	  return NULL;
177       }
178       memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
179       n->tl_data_type = tl->tl_data_type;
180       n->tl_data_length = tl->tl_data_length;
181       n->tl_data_next = NULL;
182       return n;
183  }
184  
185  /* This is in lib/kdb/kdb_cpw.c, but is static */
cleanup_key_data(context,count,data)186  static void cleanup_key_data(context, count, data)
187     krb5_context	  context;
188     int			  count;
189     krb5_key_data	* data;
190  {
191       int i, j;
192  
193       for (i = 0; i < count; i++)
194  	  for (j = 0; j < data[i].key_data_ver; j++)
195  	       if (data[i].key_data_length[j])
196  		   krb5_db_free(context, data[i].key_data_contents[j]);
197       krb5_db_free(context, data);
198  }
199  
200  kadm5_ret_t
kadm5_create_principal(void * server_handle,kadm5_principal_ent_t entry,long mask,char * password)201  kadm5_create_principal(void *server_handle,
202  			    kadm5_principal_ent_t entry, long mask,
203  			    char *password)
204  {
205      return
206  	kadm5_create_principal_3(server_handle, entry, mask,
207  				 0, NULL, password);
208  }
209  kadm5_ret_t
kadm5_create_principal_3(void * server_handle,kadm5_principal_ent_t entry,long mask,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,char * password)210  kadm5_create_principal_3(void *server_handle,
211  			 kadm5_principal_ent_t entry, long mask,
212  			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
213  			 char *password)
214  {
215      krb5_db_entry		kdb;
216      osa_princ_ent_rec		adb;
217      kadm5_policy_ent_rec	polent;
218      krb5_int32			now;
219      krb5_tl_data		*tl_data_orig, *tl_data_tail;
220      unsigned int		ret;
221      kadm5_server_handle_t handle = server_handle;
222  
223      CHECK_HANDLE(server_handle);
224  
225      krb5_clear_error_message(handle->context);
226  
227      /*
228       * Argument sanity checking, and opening up the DB
229       */
230      if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
231         (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
232         (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
233         (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
234         (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
235         (mask & KADM5_FAIL_AUTH_COUNT))
236  	return KADM5_BAD_MASK;
237      if((mask & ~ALL_PRINC_MASK))
238  	return KADM5_BAD_MASK;
239      if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
240  	return EINVAL;
241  
242      /*
243       * Check to see if the principal exists
244       */
245      ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
246  
247      switch(ret) {
248      case KADM5_UNK_PRINC:
249  	/* Solaris Kerberos */
250  	memset(&kdb, 0, sizeof(krb5_db_entry));
251  	memset(&adb, 0, sizeof(osa_princ_ent_rec));
252  	break;
253      case 0:
254  	/*
255  	 * Solaris Kerberos: this allows an addprinc to be done on a mix-in
256  	 * princ which has no keys initially.
257  	 */
258  	if (kdb.n_key_data != 0) {
259  		/* have a princ with keys, return dupe princ error */
260  		kdb_free_entry(handle, &kdb, &adb);
261  		return KADM5_DUP;
262  	} else {
263  		/*
264  		 * have a princ with no keys, let's replace it.  Note, want to
265  		 * keep the existing kdb tl_data (specifically the LDAP plugin
266  		 * adds the DN to the tl_data which is needed to locate the dir.
267  		 * entry).
268  		 */
269  		kdb_free_entry(handle, NULL, &adb);
270  		memset(&adb, 0, sizeof(osa_princ_ent_rec));
271  	}
272  	break;
273      default:
274  	return ret;
275      }
276  
277      /*
278       * If a policy was specified, load it.
279       * If we can not find the one specified return an error
280       */
281      if ((mask & KADM5_POLICY)) {
282  	 if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
283  				     &polent)) != KADM5_OK) {
284  	    if(ret == EINVAL)
285  		return KADM5_BAD_POLICY;
286  	    else
287  		return ret;
288  	}
289      }
290      if ((ret = passwd_check(handle, password, (mask & KADM5_POLICY),
291  			    &polent, entry->principal))) {
292  	if (mask & KADM5_POLICY)
293  	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
294  	return ret;
295      }
296      /*
297       * Start populating the various DB fields, using the
298       * "defaults" for fields that were not specified by the
299       * mask.
300       */
301      if ((ret = krb5_timeofday(handle->context, &now))) {
302  	 if (mask & KADM5_POLICY)
303  	      (void) kadm5_free_policy_ent(handle->lhandle, &polent);
304  	 return ret;
305      }
306  
307      kdb.magic = KRB5_KDB_MAGIC_NUMBER;
308      kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
309  
310      /*
311       * Solaris Kerberos:
312       * If KADM5_ATTRIBUTES is set, we want to rope in not only
313       * entry->attributes, but also the generic params.flags
314       * obtained previously via kadm5_get_config_params.
315       */
316      if ((mask & KADM5_ATTRIBUTES)) {
317  	kdb.attributes = handle->params.flags;
318  	kdb.attributes |= entry->attributes;
319      } else {
320  	kdb.attributes = handle->params.flags;
321      }
322  
323      if ((mask & KADM5_MAX_LIFE))
324  	kdb.max_life = entry->max_life;
325      else
326  	kdb.max_life = handle->params.max_life;
327  
328      if (mask & KADM5_MAX_RLIFE)
329  	 kdb.max_renewable_life = entry->max_renewable_life;
330      else
331  	 kdb.max_renewable_life = handle->params.max_rlife;
332  
333      if ((mask & KADM5_PRINC_EXPIRE_TIME))
334  	kdb.expiration = entry->princ_expire_time;
335      else
336  	kdb.expiration = handle->params.expiration;
337  
338      kdb.pw_expiration = 0;
339      if ((mask & KADM5_POLICY)) {
340  	if(polent.pw_max_life)
341  	    kdb.pw_expiration = now + polent.pw_max_life;
342  	else
343  	    kdb.pw_expiration = 0;
344      }
345      if ((mask & KADM5_PW_EXPIRATION))
346  	 kdb.pw_expiration = entry->pw_expiration;
347  
348      kdb.last_success = 0;
349      kdb.last_failed = 0;
350      kdb.fail_auth_count = 0;
351  
352      /* this is kind of gross, but in order to free the tl data, I need
353         to free the entire kdb entry, and that will try to free the
354         principal. */
355  
356      if ((ret = kadm5_copy_principal(handle->context,
357  				    entry->principal, &(kdb.princ)))) {
358  	if (mask & KADM5_POLICY)
359  	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
360  	return(ret);
361      }
362  
363      if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))) {
364           krb5_db_free_principal(handle->context, &kdb, 1);
365  	 if (mask & KADM5_POLICY)
366  	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
367  	 return(ret);
368      }
369  
370      if (mask & KADM5_TL_DATA) {
371  	/* splice entry->tl_data onto the front of kdb.tl_data */
372  	tl_data_orig = kdb.tl_data;
373  	for (tl_data_tail = entry->tl_data; tl_data_tail;
374  	     tl_data_tail = tl_data_tail->tl_data_next)
375  	{
376  	    ret = krb5_dbe_update_tl_data(handle->context, &kdb, tl_data_tail);
377  	    if( ret )
378  	    {
379  		krb5_db_free_principal(handle->context, &kdb, 1);
380  		if (mask & KADM5_POLICY)
381  		    (void) kadm5_free_policy_ent(handle->lhandle, &polent);
382  		return ret;
383  	    }
384  	}
385      }
386  
387      /* initialize the keys */
388  
389      if ((ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock,
390  			    n_ks_tuple?ks_tuple:handle->params.keysalts,
391  			    n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
392  			    password,
393  			    (mask & KADM5_KVNO)?entry->kvno:1,
394  			    FALSE, &kdb))) {
395  	krb5_db_free_principal(handle->context, &kdb, 1);
396  	if (mask & KADM5_POLICY)
397  	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
398  	return(ret);
399      }
400  
401      /* populate the admin-server-specific fields.  In the OV server,
402         this used to be in a separate database.  Since there's already
403         marshalling code for the admin fields, to keep things simple,
404         I'm going to keep it, and make all the admin stuff occupy a
405         single tl_data record, */
406  
407      adb.admin_history_kvno = hist_kvno;
408      if ((mask & KADM5_POLICY)) {
409  	adb.aux_attributes = KADM5_POLICY;
410  
411  	/* this does *not* need to be strdup'ed, because adb is xdr */
412  	/* encoded in osa_adb_create_princ, and not ever freed */
413  
414  	adb.policy = entry->policy;
415      }
416  
417      /* increment the policy ref count, if any */
418  
419      if ((mask & KADM5_POLICY)) {
420  	polent.policy_refcnt++;
421  	if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
422  						    KADM5_REF_COUNT))
423  	    != KADM5_OK) {
424  	    krb5_db_free_principal(handle->context, &kdb, 1);
425  	    if (mask & KADM5_POLICY)
426  		 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
427  	    return(ret);
428  	}
429      }
430  
431      /* In all cases key and the principal data is set, let the database provider know */
432      kdb.mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL ;
433  
434      /* store the new db entry */
435      ret = kdb_put_entry(handle, &kdb, &adb);
436  
437      krb5_db_free_principal(handle->context, &kdb, 1);
438  
439      if (ret) {
440  	if ((mask & KADM5_POLICY)) {
441  	    /* decrement the policy ref count */
442  
443  	    polent.policy_refcnt--;
444  	    /*
445  	     * if this fails, there's nothing we can do anyway.  the
446  	     * policy refcount wil be too high.
447  	     */
448  	    (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
449  						     KADM5_REF_COUNT);
450  	}
451  
452  	if (mask & KADM5_POLICY)
453  	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
454  	return(ret);
455      }
456  
457      if (mask & KADM5_POLICY)
458  	 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
459  
460      return KADM5_OK;
461  }
462  
463  
464  kadm5_ret_t
kadm5_delete_principal(void * server_handle,krb5_principal principal)465  kadm5_delete_principal(void *server_handle, krb5_principal principal)
466  {
467      unsigned int		ret;
468      kadm5_policy_ent_rec	polent;
469      krb5_db_entry		kdb;
470      osa_princ_ent_rec		adb;
471      kadm5_server_handle_t handle = server_handle;
472  
473      CHECK_HANDLE(server_handle);
474  
475      krb5_clear_error_message(handle->context);
476  
477      if (principal == NULL)
478  	return EINVAL;
479  
480      if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
481  	return(ret);
482  
483      if ((adb.aux_attributes & KADM5_POLICY)) {
484  	if ((ret = kadm5_get_policy(handle->lhandle,
485  				    adb.policy, &polent))
486  	    == KADM5_OK) {
487  	    polent.policy_refcnt--;
488  	    if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
489  							 KADM5_REF_COUNT))
490  		!= KADM5_OK) {
491  		(void) kadm5_free_policy_ent(handle->lhandle, &polent);
492  		kdb_free_entry(handle, &kdb, &adb);
493  		return(ret);
494  	    }
495  	}
496  	if ((ret = kadm5_free_policy_ent(handle->lhandle, &polent))) {
497  	     kdb_free_entry(handle, &kdb, &adb);
498  	     return ret;
499  	}
500      }
501  
502      ret = kdb_delete_entry(handle, principal);
503  
504      kdb_free_entry(handle, &kdb, &adb);
505  
506      return ret;
507  }
508  
509  kadm5_ret_t
kadm5_modify_principal(void * server_handle,kadm5_principal_ent_t entry,long mask)510  kadm5_modify_principal(void *server_handle,
511  			    kadm5_principal_ent_t entry, long mask)
512  {
513      int			    ret, ret2, i;
514      kadm5_policy_ent_rec    npol, opol;
515      int			    have_npol = 0, have_opol = 0;
516      krb5_db_entry	    kdb;
517      krb5_tl_data	    *tl_data_orig;
518      osa_princ_ent_rec	    adb;
519      kadm5_server_handle_t handle = server_handle;
520  
521      CHECK_HANDLE(server_handle);
522  
523      krb5_clear_error_message(handle->context);
524  
525      if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
526         (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
527         (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
528         (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
529         (mask & KADM5_LAST_FAILED))
530  	return KADM5_BAD_MASK;
531      if((mask & ~ALL_PRINC_MASK))
532  	return KADM5_BAD_MASK;
533      if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
534  	return KADM5_BAD_MASK;
535      if(entry == (kadm5_principal_ent_t) NULL)
536  	return EINVAL;
537      if (mask & KADM5_TL_DATA) {
538  	 tl_data_orig = entry->tl_data;
539  	 while (tl_data_orig) {
540  	      if (tl_data_orig->tl_data_type < 256)
541  		   return KADM5_BAD_TL_TYPE;
542  	      tl_data_orig = tl_data_orig->tl_data_next;
543  	 }
544      }
545  
546      ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
547      if (ret)
548  	return(ret);
549  
550      /*
551       * This is pretty much the same as create ...
552       */
553  
554      if ((mask & KADM5_POLICY)) {
555  	 /* get the new policy */
556  	 ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
557  	 if (ret) {
558  	      switch (ret) {
559  	      case EINVAL:
560  		   ret = KADM5_BAD_POLICY;
561  		   break;
562  	      case KADM5_UNK_POLICY:
563  	      case KADM5_BAD_POLICY:
564  		   ret =  KADM5_UNK_POLICY;
565  		   break;
566  	      }
567  	      goto done;
568  	 }
569  	 have_npol = 1;
570  
571  	 /* if we already have a policy, get it to decrement the refcnt */
572  	 if(adb.aux_attributes & KADM5_POLICY) {
573  	      /* ... but not if the old and new are the same */
574  	      if(strcmp(adb.policy, entry->policy)) {
575  		   ret = kadm5_get_policy(handle->lhandle,
576  					  adb.policy, &opol);
577  		   switch(ret) {
578  		   case EINVAL:
579  		   case KADM5_BAD_POLICY:
580  		   case KADM5_UNK_POLICY:
581  			break;
582  		   case KADM5_OK:
583  			have_opol = 1;
584  			opol.policy_refcnt--;
585  			break;
586  		   default:
587  			goto done;
588  			break;
589  		   }
590  		   npol.policy_refcnt++;
591  	      }
592  	 } else npol.policy_refcnt++;
593  
594  	 /* set us up to use the new policy */
595  	 adb.aux_attributes |= KADM5_POLICY;
596  	 if (adb.policy)
597  	      free(adb.policy);
598  	 adb.policy = strdup(entry->policy);
599  
600  	 /* set pw_max_life based on new policy */
601  	 if (npol.pw_max_life) {
602  	     ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
603  						   &(kdb.pw_expiration));
604  	     if (ret)
605  		 goto done;
606  	     kdb.pw_expiration += npol.pw_max_life;
607  	 } else {
608  	     kdb.pw_expiration = 0;
609  	 }
610      }
611  
612      if ((mask & KADM5_POLICY_CLR) &&
613  	(adb.aux_attributes & KADM5_POLICY)) {
614  	 ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
615  	 switch(ret) {
616  	 case EINVAL:
617  	 case KADM5_BAD_POLICY:
618  	 case KADM5_UNK_POLICY:
619  	      ret = KADM5_BAD_DB;
620  	      goto done;
621  	      break;
622  	 case KADM5_OK:
623  	      have_opol = 1;
624  	      if (adb.policy)
625  		   free(adb.policy);
626  	      adb.policy = NULL;
627  	      adb.aux_attributes &= ~KADM5_POLICY;
628  	      kdb.pw_expiration = 0;
629  	      opol.policy_refcnt--;
630  	      break;
631  	 default:
632  	      goto done;
633  	      break;
634  	 }
635      }
636  
637      if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
638  	(((have_opol) &&
639  	  (ret =
640  	   kadm5_modify_policy_internal(handle->lhandle, &opol,
641  					     KADM5_REF_COUNT))) ||
642  	 ((have_npol) &&
643  	  (ret =
644  	   kadm5_modify_policy_internal(handle->lhandle, &npol,
645  					     KADM5_REF_COUNT)))))
646  	goto done;
647  
648      if ((mask & KADM5_ATTRIBUTES))
649  	kdb.attributes = entry->attributes;
650      if ((mask & KADM5_MAX_LIFE))
651  	kdb.max_life = entry->max_life;
652      if ((mask & KADM5_PRINC_EXPIRE_TIME))
653  	kdb.expiration = entry->princ_expire_time;
654      if (mask & KADM5_PW_EXPIRATION)
655  	 kdb.pw_expiration = entry->pw_expiration;
656      if (mask & KADM5_MAX_RLIFE)
657  	 kdb.max_renewable_life = entry->max_renewable_life;
658      if (mask & KADM5_FAIL_AUTH_COUNT)
659  	 kdb.fail_auth_count = entry->fail_auth_count;
660  
661      if((mask & KADM5_KVNO)) {
662  	 for (i = 0; i < kdb.n_key_data; i++)
663  	      kdb.key_data[i].key_data_kvno = entry->kvno;
664      }
665  
666      if (mask & KADM5_TL_DATA) {
667  	 krb5_tl_data *tl;
668  
669  	 /* may have to change the version number of the API. Updates the list with the given tl_data rather than over-writting */
670  
671  	 for (tl = entry->tl_data; tl;
672  	      tl = tl->tl_data_next)
673  	 {
674  	     ret = krb5_dbe_update_tl_data(handle->context, &kdb, tl);
675  	     if( ret )
676  	     {
677  		 goto done;
678  	     }
679  	 }
680      }
681  
682      /* let the mask propagate to the database provider */
683      kdb.mask = mask;
684  
685      ret = kdb_put_entry(handle, &kdb, &adb);
686      if (ret) goto done;
687  
688      ret = KADM5_OK;
689  done:
690      if (have_opol) {
691  	 ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
692  	 ret = ret ? ret : ret2;
693      }
694      if (have_npol) {
695  	 ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
696  	 ret = ret ? ret : ret2;
697      }
698      kdb_free_entry(handle, &kdb, &adb);
699      return ret;
700  }
701  
702  kadm5_ret_t
kadm5_rename_principal(void * server_handle,krb5_principal source,krb5_principal target)703  kadm5_rename_principal(void *server_handle,
704  			    krb5_principal source, krb5_principal target)
705  {
706      krb5_db_entry	kdb;
707      osa_princ_ent_rec	adb;
708      int			ret, i;
709      kadm5_server_handle_t handle = server_handle;
710  
711      CHECK_HANDLE(server_handle);
712  
713      krb5_clear_error_message(handle->context);
714  
715      if (source == NULL || target == NULL)
716  	return EINVAL;
717  
718      if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
719  	kdb_free_entry(handle, &kdb, &adb);
720  	return(KADM5_DUP);
721      }
722  
723      if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
724  	return ret;
725  
726      /* this is kinda gross, but unavoidable */
727  
728      for (i=0; i<kdb.n_key_data; i++) {
729  	if ((kdb.key_data[i].key_data_ver == 1) ||
730  	    (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
731  	    ret = KADM5_NO_RENAME_SALT;
732  	    goto done;
733  	}
734      }
735  
736      kadm5_free_principal(handle->context, kdb.princ);
737      ret = kadm5_copy_principal(handle->context, target, &kdb.princ);
738      if (ret) {
739  	kdb.princ = NULL; /* so freeing the dbe doesn't lose */
740  	goto done;
741      }
742  
743      if ((ret = kdb_put_entry(handle, &kdb, &adb)))
744  	goto done;
745  
746      ret = kdb_delete_entry(handle, source);
747  
748  done:
749      kdb_free_entry(handle, &kdb, &adb);
750      return ret;
751  }
752  
753  kadm5_ret_t
kadm5_get_principal(void * server_handle,krb5_principal principal,kadm5_principal_ent_t entry,long in_mask)754  kadm5_get_principal(void *server_handle, krb5_principal principal,
755  		    kadm5_principal_ent_t entry,
756  		    long in_mask)
757  {
758      krb5_db_entry		kdb;
759      osa_princ_ent_rec		adb;
760      krb5_error_code		ret = 0;
761      long			mask;
762      int i;
763      kadm5_server_handle_t handle = server_handle;
764      kadm5_principal_ent_rec	entry_local, *entry_orig;
765  
766      CHECK_HANDLE(server_handle);
767  
768      krb5_clear_error_message(handle->context);
769  
770      /*
771       * In version 1, all the defined fields are always returned.
772       * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
773       * filled with allocated memory.
774       */
775      if (handle->api_version == KADM5_API_VERSION_1) {
776  	 mask = KADM5_PRINCIPAL_NORMAL_MASK;
777  	 entry_orig = entry;
778  	 entry = &entry_local;
779      } else {
780  	 mask = in_mask;
781      }
782  
783      memset((char *) entry, 0, sizeof(*entry));
784  
785      if (principal == NULL)
786  	return EINVAL;
787  
788      if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
789  	return ret;
790  
791      if ((mask & KADM5_POLICY) &&
792  	adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
793  	if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
794  	    ret = ENOMEM;
795  	    goto done;
796  	}
797  	strcpy(entry->policy, adb.policy);
798      }
799  
800      if (mask & KADM5_AUX_ATTRIBUTES)
801  	 entry->aux_attributes = adb.aux_attributes;
802  
803      if ((mask & KADM5_PRINCIPAL) &&
804  	(ret = krb5_copy_principal(handle->context, principal,
805  				   &entry->principal))) {
806  	goto done;
807      }
808  
809      if (mask & KADM5_PRINC_EXPIRE_TIME)
810  	 entry->princ_expire_time = kdb.expiration;
811  
812      if ((mask & KADM5_LAST_PWD_CHANGE) &&
813  	(ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
814  					       &(entry->last_pwd_change)))) {
815  	goto done;
816      }
817  
818      if (mask & KADM5_PW_EXPIRATION)
819  	 entry->pw_expiration = kdb.pw_expiration;
820      if (mask & KADM5_MAX_LIFE)
821  	 entry->max_life = kdb.max_life;
822  
823      /* this is a little non-sensical because the function returns two */
824      /* values that must be checked separately against the mask */
825      if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
826  	ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
827  					     &(entry->mod_date),
828  					     &(entry->mod_name));
829  	if (ret) {
830  	    goto done;
831  	}
832  
833  	if (! (mask & KADM5_MOD_TIME))
834  	    entry->mod_date = 0;
835  	if (! (mask & KADM5_MOD_NAME)) {
836  	    krb5_free_principal(handle->context, entry->principal);
837  	    entry->principal = NULL;
838  	}
839      }
840  
841      if (mask & KADM5_ATTRIBUTES)
842  	 entry->attributes = kdb.attributes;
843  
844      if (mask & KADM5_KVNO)
845  	 for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
846  	      if (kdb.key_data[i].key_data_kvno > entry->kvno)
847  		   entry->kvno = kdb.key_data[i].key_data_kvno;
848  
849      if (handle->api_version == KADM5_API_VERSION_2)
850  	 entry->mkvno = 0;
851      else {
852  	 /* XXX I'll be damned if I know how to deal with this one --marc */
853  	 entry->mkvno = 1;
854      }
855  
856      /*
857       * The new fields that only exist in version 2 start here
858       */
859      if (handle->api_version == KADM5_API_VERSION_2) {
860  	 if (mask & KADM5_MAX_RLIFE)
861  	      entry->max_renewable_life = kdb.max_renewable_life;
862  	 if (mask & KADM5_LAST_SUCCESS)
863  	      entry->last_success = kdb.last_success;
864  	 if (mask & KADM5_LAST_FAILED)
865  	      entry->last_failed = kdb.last_failed;
866  	 if (mask & KADM5_FAIL_AUTH_COUNT)
867  	      entry->fail_auth_count = kdb.fail_auth_count;
868  	 if (mask & KADM5_TL_DATA) {
869  	      krb5_tl_data *tl, *tl2;
870  
871  	      entry->tl_data = NULL;
872  
873  	      tl = kdb.tl_data;
874  	      while (tl) {
875  		   if (tl->tl_data_type > 255) {
876  			if ((tl2 = dup_tl_data(tl)) == NULL) {
877  			     ret = ENOMEM;
878  			     goto done;
879  			}
880  			tl2->tl_data_next = entry->tl_data;
881  			entry->tl_data = tl2;
882  			entry->n_tl_data++;
883  		   }
884  
885  		   tl = tl->tl_data_next;
886  	      }
887  	 }
888  	 if (mask & KADM5_KEY_DATA) {
889  	      entry->n_key_data = kdb.n_key_data;
890  	      if(entry->n_key_data) {
891  		      entry->key_data = (krb5_key_data *)
892  			      malloc(entry->n_key_data*sizeof(krb5_key_data));
893  		      if (entry->key_data == NULL) {
894  			      ret = ENOMEM;
895  			      goto done;
896  		      }
897  	      } else
898  		      entry->key_data = NULL;
899  
900  	      for (i = 0; i < entry->n_key_data; i++)
901  		  ret = krb5_copy_key_data_contents(handle->context,
902  						    &kdb.key_data[i],
903  						    &entry->key_data[i]);
904  		   if (ret)
905  			goto done;
906  	 }
907      }
908  
909      /*
910       * If KADM5_API_VERSION_1, we return an allocated structure, and
911       * we need to convert the new structure back into the format the
912       * caller is expecting.
913       */
914      if (handle->api_version == KADM5_API_VERSION_1) {
915  	 kadm5_principal_ent_t_v1 newv1;
916  
917  	 newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
918  	 if (newv1 == NULL) {
919  	      ret = ENOMEM;
920  	      goto done;
921  	 }
922  
923  	 newv1->principal = entry->principal;
924  	 newv1->princ_expire_time = entry->princ_expire_time;
925  	 newv1->last_pwd_change = entry->last_pwd_change;
926  	 newv1->pw_expiration = entry->pw_expiration;
927  	 newv1->max_life = entry->max_life;
928  	 newv1->mod_name = entry->mod_name;
929  	 newv1->mod_date = entry->mod_date;
930  	 newv1->attributes = entry->attributes;
931  	 newv1->kvno = entry->kvno;
932  	 newv1->mkvno = entry->mkvno;
933  	 newv1->policy = entry->policy;
934  	 newv1->aux_attributes = entry->aux_attributes;
935  
936  	 *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
937      }
938  
939      ret = KADM5_OK;
940  
941  done:
942      if (ret && entry->principal)
943  	 krb5_free_principal(handle->context, entry->principal);
944      kdb_free_entry(handle, &kdb, &adb);
945  
946      return ret;
947  }
948  
949  /*
950   * Function: check_pw_reuse
951   *
952   * Purpose: Check if a key appears in a list of keys, in order to
953   * enforce password history.
954   *
955   * Arguments:
956   *
957   *	context			(r) the krb5 context
958   *	hist_keyblock		(r) the key that hist_key_data is
959   *				encrypted in
960   *	n_new_key_data		(r) length of new_key_data
961   *	new_key_data		(r) keys to check against
962   *				pw_hist_data, encrypted in hist_keyblock
963   *	n_pw_hist_data		(r) length of pw_hist_data
964   *	pw_hist_data		(r) passwords to check new_key_data against
965   *
966   * Effects:
967   * For each new_key in new_key_data:
968   * 	decrypt new_key with the master_keyblock
969   * 	for each password in pw_hist_data:
970   *		for each hist_key in password:
971   *			decrypt hist_key with hist_keyblock
972   *			compare the new_key and hist_key
973   *
974   * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
975   * new_key_data is the same as a key in pw_hist_data, or 0.
976   */
977  static kadm5_ret_t
check_pw_reuse(krb5_context context,krb5_keyblock * master_keyblock,krb5_keyblock * hist_keyblock,int n_new_key_data,krb5_key_data * new_key_data,unsigned int n_pw_hist_data,osa_pw_hist_ent * pw_hist_data)978  check_pw_reuse(krb5_context context,
979  	       krb5_keyblock *master_keyblock,
980  	       krb5_keyblock *hist_keyblock,
981  	       int n_new_key_data, krb5_key_data *new_key_data,
982  	       unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
983  {
984      int x, y, z;
985      krb5_keyblock newkey, histkey;
986      krb5_error_code ret;
987  
988      for (x = 0; x < n_new_key_data; x++) {
989  	ret = krb5_dbekd_decrypt_key_data(context,
990  					  master_keyblock,
991  					  &(new_key_data[x]),
992  					  &newkey, NULL);
993  	if (ret)
994  	    return(ret);
995  	for (y = 0; y < n_pw_hist_data; y++) {
996  	     for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
997  		 ret = krb5_dbekd_decrypt_key_data(context,
998  						   hist_keyblock,
999  						   &pw_hist_data[y].key_data[z],
1000  						   &histkey, NULL);
1001  		 if (ret)
1002  		     return(ret);
1003  
1004  		 if ((newkey.length == histkey.length) &&
1005  		     (newkey.enctype == histkey.enctype) &&
1006  		     (memcmp(newkey.contents, histkey.contents,
1007  			     histkey.length) == 0)) {
1008  		     krb5_free_keyblock_contents(context, &histkey);
1009  		     krb5_free_keyblock_contents(context, &newkey);
1010  
1011  		     return(KADM5_PASS_REUSE);
1012  		 }
1013  		 krb5_free_keyblock_contents(context, &histkey);
1014  	     }
1015  	}
1016  	krb5_free_keyblock_contents(context, &newkey);
1017      }
1018  
1019      return(0);
1020  }
1021  
1022  /*
1023   * Function: create_history_entry
1024   *
1025   * Purpose: Creates a password history entry from an array of
1026   * key_data.
1027   *
1028   * Arguments:
1029   *
1030   *	context		(r) krb5_context to use
1031   *      master_keyblcok (r) master key block
1032   *	n_key_data	(r) number of elements in key_data
1033   *	key_data	(r) keys to add to the history entry
1034   *	hist		(w) history entry to fill in
1035   *
1036   * Effects:
1037   *
1038   * hist->key_data is allocated to store n_key_data key_datas.  Each
1039   * element of key_data is decrypted with master_keyblock, re-encrypted
1040   * in hist_key, and added to hist->key_data.  hist->n_key_data is
1041   * set to n_key_data.
1042   */
1043  static
create_history_entry(krb5_context context,krb5_keyblock * master_keyblock,int n_key_data,krb5_key_data * key_data,osa_pw_hist_ent * hist)1044  int create_history_entry(krb5_context context,
1045  	krb5_keyblock *master_keyblock,	int n_key_data,
1046  	krb5_key_data *key_data, osa_pw_hist_ent *hist)
1047  {
1048       int i, ret;
1049       krb5_keyblock key;
1050       krb5_keysalt salt;
1051  
1052       hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
1053       if (hist->key_data == NULL)
1054  	  return ENOMEM;
1055       memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
1056  
1057       for (i = 0; i < n_key_data; i++) {
1058  	 ret = krb5_dbekd_decrypt_key_data(context,
1059  					   master_keyblock,
1060  					   &key_data[i],
1061  					   &key, &salt);
1062  	 if (ret)
1063  	     return ret;
1064  
1065  	 ret = krb5_dbekd_encrypt_key_data(context, &hist_key,
1066  					   &key, &salt,
1067  					   key_data[i].key_data_kvno,
1068  					   &hist->key_data[i]);
1069  	 if (ret)
1070  	     return ret;
1071  
1072  	 krb5_free_keyblock_contents(context, &key);
1073  	 /* krb5_free_keysalt(context, &salt); */
1074       }
1075  
1076       hist->n_key_data = n_key_data;
1077       return 0;
1078  }
1079  
1080  static
free_history_entry(krb5_context context,osa_pw_hist_ent * hist)1081  void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
1082  {
1083       int i;
1084  
1085       for (i = 0; i < hist->n_key_data; i++)
1086  	  krb5_free_key_data_contents(context, &hist->key_data[i]);
1087       free(hist->key_data);
1088  }
1089  
1090  /*
1091   * Function: add_to_history
1092   *
1093   * Purpose: Adds a password to a principal's password history.
1094   *
1095   * Arguments:
1096   *
1097   *	context		(r) krb5_context to use
1098   *	adb		(r/w) admin principal entry to add keys to
1099   *	pol		(r) adb's policy
1100   *	pw		(r) keys for the password to add to adb's key history
1101   *
1102   * Effects:
1103   *
1104   * add_to_history adds a single password to adb's password history.
1105   * pw contains n_key_data keys in its key_data, in storage should be
1106   * allocated but not freed by the caller (XXX blech!).
1107   *
1108   * This function maintains adb->old_keys as a circular queue.  It
1109   * starts empty, and grows each time this function is called until it
1110   * is pol->pw_history_num items long.  adb->old_key_len holds the
1111   * number of allocated entries in the array, and must therefore be [0,
1112   * pol->pw_history_num).  adb->old_key_next is the index into the
1113   * array where the next element should be written, and must be [0,
1114   * adb->old_key_len).
1115   */
1116  #define	KADM_MOD(x)	(x + adb->old_key_next) % adb->old_key_len
add_to_history(krb5_context context,osa_princ_ent_t adb,kadm5_policy_ent_t pol,osa_pw_hist_ent * pw)1117  static kadm5_ret_t add_to_history(krb5_context context,
1118  				  osa_princ_ent_t adb,
1119  				  kadm5_policy_ent_t pol,
1120  				  osa_pw_hist_ent *pw)
1121  {
1122       osa_pw_hist_ent *histp;
1123       uint32_t nhist;
1124       unsigned int i, knext, nkeys;
1125  
1126       nhist = pol->pw_history_num;
1127       /* A history of 1 means just check the current password */
1128       if (nhist <= 1)
1129  	  return 0;
1130  
1131       nkeys = adb->old_key_len;
1132       knext = adb->old_key_next;
1133       /* resize the adb->old_keys array if necessary */
1134       if (nkeys + 1 < nhist) {
1135  	  if (adb->old_keys == NULL) {
1136  	       adb->old_keys = (osa_pw_hist_ent *)
1137  		    malloc((nkeys + 1) * sizeof (osa_pw_hist_ent));
1138  	  } else {
1139  	       adb->old_keys = (osa_pw_hist_ent *)
1140  		    realloc(adb->old_keys,
1141  			    (nkeys + 1) * sizeof (osa_pw_hist_ent));
1142  	  }
1143  	  if (adb->old_keys == NULL)
1144  	       return(ENOMEM);
1145  
1146  	  memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent));
1147       	  nkeys = ++adb->old_key_len;
1148  	  /*
1149  	   * To avoid losing old keys, shift forward each entry after
1150  	   * knext.
1151  	   */
1152  	  for (i = nkeys - 1; i > knext; i--) {
1153  	      adb->old_keys[i] = adb->old_keys[i - 1];
1154  	  }
1155  	  memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent));
1156       } else if (nkeys + 1 > nhist) {
1157  	 /*
1158  	  * The policy must have changed!  Shrink the array.
1159  	  * Can't simply realloc() down, since it might be wrapped.
1160  	  * To understand the arithmetic below, note that we are
1161  	  * copying into new positions 0 .. N-1 from old positions
1162  	  * old_key_next-N .. old_key_next-1, modulo old_key_len,
1163  	  * where N = pw_history_num - 1 is the length of the
1164  	  * shortened list.        Matt Crawford, FNAL
1165  	  */
1166  	 /*
1167  	  * M = adb->old_key_len, N = pol->pw_history_num - 1
1168  	  *
1169  	  * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M]
1170  	  */
1171  	 int j;
1172  	 osa_pw_hist_t tmp;
1173  
1174  	 tmp = (osa_pw_hist_ent *)
1175  	     malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
1176  	 if (tmp == NULL)
1177  	     return ENOMEM;
1178  	 for (i = 0; i < nhist - 1; i++) {
1179  	     /*
1180  	      * Add nkeys once before taking remainder to avoid
1181  	      * negative values.
1182  	      */
1183  	     j = (i + nkeys + knext - (nhist - 1)) % nkeys;
1184  	     tmp[i] = adb->old_keys[j];
1185  	 }
1186  	 /* Now free the ones we don't keep (the oldest ones) */
1187  	 for (i = 0; i < nkeys - (nhist - 1); i++) {
1188  	     j = (i + nkeys + knext) % nkeys;
1189  	     histp = &adb->old_keys[j];
1190  	     for (j = 0; j < histp->n_key_data; j++) {
1191  		 krb5_free_key_data_contents(context, &histp->key_data[j]);
1192  	     }
1193  	     free(histp->key_data);
1194  	 }
1195  	 free((void *)adb->old_keys);
1196  	 adb->old_keys = tmp;
1197  	 nkeys = adb->old_key_len = nhist - 1;
1198  	 knext = adb->old_key_next = 0;
1199       }
1200  
1201       /*
1202        * If nhist decreased since the last password change, and nkeys+1
1203        * is less than the previous nhist, it is possible for knext to
1204        * index into unallocated space.  This condition would not be
1205        * caught by the resizing code above.
1206        */
1207       if (knext + 1 > nkeys)
1208  	 knext = adb->old_key_next = 0;
1209       /* free the old pw history entry if it contains data */
1210       histp = &adb->old_keys[knext];
1211       for (i = 0; i < histp->n_key_data; i++)
1212  	  krb5_free_key_data_contents(context, &histp->key_data[i]);
1213       free(histp->key_data);
1214  
1215       /* store the new entry */
1216       adb->old_keys[knext] = *pw;
1217  
1218       /* update the next pointer */
1219       if (++adb->old_key_next == nhist - 1)
1220  	 adb->old_key_next = 0;
1221  
1222       return(0);
1223  }
1224  #undef KADM_MOD
1225  
1226  #ifdef USE_PASSWORD_SERVER
1227  /* FIXME: don't use global variable for this */
1228  krb5_boolean use_password_server = 0;
1229  
1230  static krb5_boolean
kadm5_use_password_server(void)1231  kadm5_use_password_server (void)
1232  {
1233      return use_password_server;
1234  }
1235  
1236  void
kadm5_set_use_password_server(void)1237  kadm5_set_use_password_server (void)
1238  {
1239      use_password_server = 1;
1240  }
1241  #endif
1242  
1243  #ifdef USE_PASSWORD_SERVER
1244  
1245  /*
1246   * kadm5_launch_task () runs a program (task_path) to synchronize the
1247   * Apple password server with the Kerberos database.  Password server
1248   * programs can receive arguments on the command line (task_argv)
1249   * and a block of data via stdin (data_buffer).
1250   *
1251   * Because a failure to communicate with the tool results in the
1252   * password server falling out of sync with the database,
1253   * kadm5_launch_task() always fails if it can't talk to the tool.
1254   */
1255  
1256  static kadm5_ret_t
kadm5_launch_task(krb5_context context,const char * task_path,char * const task_argv[],const char * data_buffer)1257  kadm5_launch_task (krb5_context context,
1258                     const char *task_path, char * const task_argv[],
1259                     const char *data_buffer)
1260  {
1261      kadm5_ret_t ret = 0;
1262      int data_pipe[2];
1263  
1264      if (data_buffer != NULL) {
1265          ret = pipe (data_pipe);
1266          if (ret) { ret = errno; }
1267      }
1268  
1269      if (!ret) {
1270          pid_t pid = fork ();
1271          if (pid == -1) {
1272              ret = errno;
1273          } else if (pid == 0) {
1274              /* The child: */
1275  
1276              if (data_buffer != NULL) {
1277                  if (dup2 (data_pipe[0], STDIN_FILENO) == -1) {
1278                      _exit (1);
1279                  }
1280              } else {
1281                  close (data_pipe[0]);
1282              }
1283  
1284              close (data_pipe[1]);
1285  
1286              execv (task_path, task_argv);
1287  
1288              _exit (1); /* Fail if execv fails */
1289          } else {
1290              /* The parent: */
1291              int status;
1292  
1293              if (data_buffer != NULL) {
1294                  /* Write out the buffer to the child */
1295                  if (krb5_net_write (context, data_pipe[1],
1296                                      data_buffer, strlen (data_buffer)) < 0) {
1297                      /* kill the child to make sure waitpid() won't hang later */
1298                      ret = errno;
1299                      kill (pid, SIGKILL);
1300                  }
1301              }
1302  
1303              close (data_buffer[0]);
1304              close (data_buffer[1]);
1305  
1306              waitpid (pid, &status, 0);
1307  
1308              if (!ret) {
1309                  if (WIFEXITED (status)) {
1310                      /* child read password and exited.  Check the return value. */
1311                      if ((WEXITSTATUS (status) != 0) && (WEXITSTATUS (status) != 252)) {
1312                         ret = KRB5KDC_ERR_POLICY; /* password change rejected */
1313                      }
1314                  } else {
1315                      /* child read password but crashed or was killed */
1316                      ret = KRB5KRB_ERR_GENERIC; /* FIXME: better error */
1317                  }
1318              }
1319          }
1320      }
1321  
1322      return ret;
1323  }
1324  
1325  #endif
1326  
1327  kadm5_ret_t
kadm5_chpass_principal(void * server_handle,krb5_principal principal,char * password)1328  kadm5_chpass_principal(void *server_handle,
1329  			    krb5_principal principal, char *password)
1330  {
1331      return
1332  	kadm5_chpass_principal_3(server_handle, principal, FALSE,
1333  				 0, NULL, password);
1334  }
1335  
1336  kadm5_ret_t
kadm5_chpass_principal_3(void * server_handle,krb5_principal principal,krb5_boolean keepold,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,char * password)1337  kadm5_chpass_principal_3(void *server_handle,
1338  			 krb5_principal principal, krb5_boolean keepold,
1339  			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1340  			 char *password)
1341  {
1342      krb5_int32			now;
1343      kadm5_policy_ent_rec	pol;
1344      osa_princ_ent_rec		adb;
1345      krb5_db_entry		kdb, kdb_save;
1346      int				ret, ret2, last_pwd, hist_added;
1347      int				have_pol = 0;
1348      kadm5_server_handle_t	handle = server_handle;
1349      osa_pw_hist_ent		hist;
1350  
1351      CHECK_HANDLE(server_handle);
1352  
1353      /* Solaris Kerberos - kadm5_check_min_life checks for null principal. */
1354      ret = kadm5_check_min_life(server_handle,principal,NULL,0);
1355      if (ret)
1356  	return (ret);
1357      krb5_clear_error_message(handle->context);
1358  
1359      hist_added = 0;
1360      memset(&hist, 0, sizeof(hist));
1361  
1362      if (principal == NULL || password == NULL)
1363  	return EINVAL;
1364      if ((krb5_principal_compare(handle->context,
1365  				principal, hist_princ)) == TRUE)
1366  	return KADM5_PROTECT_PRINCIPAL;
1367  
1368      if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1369         return(ret);
1370  
1371      /* we are going to need the current keys after the new keys are set */
1372      if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
1373  	 kdb_free_entry(handle, &kdb, &adb);
1374  	 return(ret);
1375      }
1376  
1377      if ((adb.aux_attributes & KADM5_POLICY)) {
1378  	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1379  	     goto done;
1380  	have_pol = 1;
1381      }
1382  
1383      if ((ret = passwd_check(handle, password, adb.aux_attributes &
1384  			    KADM5_POLICY, &pol, principal)))
1385  	 goto done;
1386  
1387      ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock,
1388  		       n_ks_tuple?ks_tuple:handle->params.keysalts,
1389  		       n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1390  		       password, 0 /* increment kvno */,
1391  		       keepold, &kdb);
1392      if (ret)
1393  	goto done;
1394  
1395      kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1396  
1397      ret = krb5_timeofday(handle->context, &now);
1398      if (ret)
1399  	 goto done;
1400  
1401      if ((adb.aux_attributes & KADM5_POLICY)) {
1402         /* the policy was loaded before */
1403  
1404  	ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1405  					      &kdb, &last_pwd);
1406  	if (ret)
1407  	    goto done;
1408  
1409  #if 0
1410  	 /*
1411  	  * The spec says this check is overridden if the caller has
1412  	  * modify privilege.  The admin server therefore makes this
1413  	  * check itself (in chpass_principal_wrapper, misc.c). A
1414  	  * local caller implicitly has all authorization bits.
1415  	  */
1416  	if ((now - last_pwd) < pol.pw_min_life &&
1417  	    !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1418  	     ret = KADM5_PASS_TOOSOON;
1419  	     goto done;
1420  	}
1421  #endif
1422  
1423  	ret = create_history_entry(handle->context,
1424  				   &handle->master_keyblock, kdb_save.n_key_data,
1425  				   kdb_save.key_data, &hist);
1426  	if (ret)
1427  	    goto done;
1428  
1429  	ret = check_pw_reuse(handle->context,
1430  			     &handle->master_keyblock,
1431  			     &hist_key,
1432  			     kdb.n_key_data, kdb.key_data,
1433  			     1, &hist);
1434  	if (ret)
1435  	    goto done;
1436  
1437  	if (pol.pw_history_num > 1) {
1438  	    if (adb.admin_history_kvno != hist_kvno) {
1439  		ret = KADM5_BAD_HIST_KEY;
1440  		goto done;
1441  	    }
1442  
1443  	    ret = check_pw_reuse(handle->context,
1444  				&handle->master_keyblock,
1445  				     &hist_key,
1446  				 kdb.n_key_data, kdb.key_data,
1447  				 adb.old_key_len, adb.old_keys);
1448  	    if (ret)
1449  		goto done;
1450  
1451  	    ret = add_to_history(handle->context, &adb, &pol, &hist);
1452  	    if (ret)
1453  		goto done;
1454  	    hist_added = 1;
1455         }
1456  
1457  	if (pol.pw_max_life)
1458  	   kdb.pw_expiration = now + pol.pw_max_life;
1459  	else
1460  	   kdb.pw_expiration = 0;
1461      } else {
1462  	kdb.pw_expiration = 0;
1463      }
1464  
1465  #ifdef USE_PASSWORD_SERVER
1466      if (kadm5_use_password_server () &&
1467          (krb5_princ_size (handle->context, principal) == 1)) {
1468          krb5_data *princ = krb5_princ_component (handle->context, principal, 0);
1469          const char *path = "/usr/sbin/mkpassdb";
1470          char *argv[] = { "mkpassdb", "-setpassword", NULL, NULL };
1471          char *pstring = NULL;
1472          char pwbuf[256];
1473          int pwlen = strlen (password);
1474  
1475          if (pwlen > 254) pwlen = 254;
1476          strncpy (pwbuf, password, pwlen);
1477          pwbuf[pwlen] = '\n';
1478          pwbuf[pwlen + 1] = '\0';
1479  
1480          if (!ret) {
1481              pstring = malloc ((princ->length + 1) * sizeof (char));
1482              if (pstring == NULL) { ret = errno; }
1483          }
1484  
1485          if (!ret) {
1486              memcpy (pstring, princ->data, princ->length);
1487              pstring [princ->length] = '\0';
1488              argv[2] = pstring;
1489  
1490              ret = kadm5_launch_task (handle->context, path, argv, pwbuf);
1491          }
1492  
1493          if (pstring != NULL)
1494              free (pstring);
1495  
1496          if (ret)
1497              goto done;
1498      }
1499  #endif
1500  
1501      ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1502      if (ret)
1503  	goto done;
1504  
1505      /* key data and attributes changed, let the database provider know */
1506      /* Solaris Kerberos: adding support for key history in LDAP KDB */
1507      if (hist_added == 1)
1508  	kdb.mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_KEY_HIST
1509  	    /* | KADM5_CPW_FUNCTION */;
1510      else
1511  	kdb.mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES /* | KADM5_CPW_FUNCTION */;
1512  
1513      if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1514  	goto done;
1515  
1516      ret = KADM5_OK;
1517  done:
1518      if (!hist_added && hist.key_data)
1519  	 free_history_entry(handle->context, &hist);
1520      kdb_free_entry(handle, &kdb, &adb);
1521      kdb_free_entry(handle, &kdb_save, NULL);
1522      krb5_db_free_principal(handle->context, &kdb, 1);
1523  
1524      if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1525  	&& !ret)
1526  	 ret = ret2;
1527  
1528      return ret;
1529  }
1530  
1531  kadm5_ret_t
kadm5_randkey_principal(void * server_handle,krb5_principal principal,krb5_keyblock ** keyblocks,int * n_keys)1532  kadm5_randkey_principal(void *server_handle,
1533  			krb5_principal principal,
1534  			krb5_keyblock **keyblocks,
1535  			int *n_keys)
1536  {
1537  	 /* Solaris Kerberos: */
1538  	krb5_key_salt_tuple keysalts[2];
1539  
1540  	/*
1541  	 * Anyone calling this routine is forced to use only DES
1542  	 * enctypes to be compatible with earlier releases that
1543  	 * did not support stronger crypto.
1544  	 *
1545  	 * S10 (and later) kadmin clients will not use this API,
1546  	 * so we can assume the request is from an older version.
1547  	 */
1548  	keysalts[0].ks_enctype = ENCTYPE_DES_CBC_MD5;
1549  	keysalts[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
1550  	keysalts[1].ks_enctype = ENCTYPE_DES_CBC_CRC;
1551  	keysalts[1].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
1552  
1553  	return (kadm5_randkey_principal_3(server_handle, principal,
1554  			FALSE, 2, keysalts, keyblocks, n_keys));
1555  }
1556  kadm5_ret_t
kadm5_randkey_principal_3(void * server_handle,krb5_principal principal,krb5_boolean keepold,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,krb5_keyblock ** keyblocks,int * n_keys)1557  kadm5_randkey_principal_3(void *server_handle,
1558  			krb5_principal principal,
1559  			krb5_boolean keepold,
1560  			int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1561  			krb5_keyblock **keyblocks,
1562  			int *n_keys)
1563  {
1564      krb5_db_entry		kdb;
1565      osa_princ_ent_rec		adb;
1566      krb5_int32			now;
1567      kadm5_policy_ent_rec	pol;
1568      krb5_key_data		*key_data;
1569      int				ret, last_pwd, have_pol = 0;
1570      kadm5_server_handle_t	handle = server_handle;
1571  
1572      if (keyblocks)
1573  	 *keyblocks = NULL;
1574  
1575      CHECK_HANDLE(server_handle);
1576  
1577      krb5_clear_error_message(handle->context);
1578  
1579      if (principal == NULL)
1580  	return EINVAL;
1581      if (hist_princ && /* this will be NULL when initializing the databse */
1582  	((krb5_principal_compare(handle->context,
1583  				 principal, hist_princ)) == TRUE))
1584  	return KADM5_PROTECT_PRINCIPAL;
1585  
1586      if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1587         return(ret);
1588  
1589      ret = krb5_dbe_crk(handle->context, &handle->master_keyblock,
1590  		       n_ks_tuple?ks_tuple:handle->params.keysalts,
1591  		       n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1592  		       keepold,
1593  		       &kdb);
1594      if (ret)
1595  	goto done;
1596  
1597      kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1598  
1599      ret = krb5_timeofday(handle->context, &now);
1600      if (ret)
1601  	goto done;
1602  
1603      if ((adb.aux_attributes & KADM5_POLICY)) {
1604  	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1605  				    &pol)) != KADM5_OK)
1606  	   goto done;
1607  	have_pol = 1;
1608  
1609  	ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1610  					      &kdb, &last_pwd);
1611  	if (ret)
1612  	     goto done;
1613  
1614  #if 0
1615  	 /*
1616  	  * The spec says this check is overridden if the caller has
1617  	  * modify privilege.  The admin server therefore makes this
1618  	  * check itself (in chpass_principal_wrapper, misc.c).  A
1619  	  * local caller implicitly has all authorization bits.
1620  	  */
1621  	if((now - last_pwd) < pol.pw_min_life &&
1622  	   !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1623  	     ret = KADM5_PASS_TOOSOON;
1624  	     goto done;
1625  	}
1626  #endif
1627  
1628  	if(pol.pw_history_num > 1) {
1629  	    if(adb.admin_history_kvno != hist_kvno) {
1630  		ret = KADM5_BAD_HIST_KEY;
1631  		goto done;
1632  	    }
1633  
1634  	    ret = check_pw_reuse(handle->context,
1635  				 &handle->master_keyblock,
1636  				 &hist_key,
1637  				 kdb.n_key_data, kdb.key_data,
1638  				 adb.old_key_len, adb.old_keys);
1639  	    if (ret)
1640  		goto done;
1641  	}
1642  	if (pol.pw_max_life)
1643  	   kdb.pw_expiration = now + pol.pw_max_life;
1644  	else
1645  	   kdb.pw_expiration = 0;
1646      } else {
1647  	kdb.pw_expiration = 0;
1648      }
1649  
1650      ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1651      if (ret)
1652  	 goto done;
1653  
1654      if (keyblocks) {
1655  	 if (handle->api_version == KADM5_API_VERSION_1) {
1656  	      /* Version 1 clients will expect to see a DES_CRC enctype. */
1657  	     ret = krb5_dbe_find_enctype(handle->context, &kdb,
1658  					 ENCTYPE_DES_CBC_CRC,
1659  					 -1, -1, &key_data);
1660  	     if (ret)
1661  		 goto done;
1662  
1663  	     ret = decrypt_key_data(handle->context,
1664  				&handle->master_keyblock, 1, key_data,
1665  				     keyblocks, NULL);
1666  	     if (ret)
1667  		 goto done;
1668  	 } else {
1669  	     ret = decrypt_key_data(handle->context,
1670  				     &handle->master_keyblock,
1671  				     kdb.n_key_data, kdb.key_data,
1672  				     keyblocks, n_keys);
1673  	     if (ret)
1674  		 goto done;
1675  	 }
1676      }
1677  
1678      /* key data changed, let the database provider know */
1679      kdb.mask = KADM5_KEY_DATA /* | KADM5_RANDKEY_USED */;
1680  
1681      if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1682  	goto done;
1683  
1684      ret = KADM5_OK;
1685  done:
1686      kdb_free_entry(handle, &kdb, &adb);
1687      if (have_pol)
1688  	 kadm5_free_policy_ent(handle->lhandle, &pol);
1689  
1690      return ret;
1691  }
1692  
1693  #if 0 /* Solaris Kerberos */
1694  /*
1695   * kadm5_setv4key_principal:
1696   *
1697   * Set only ONE key of the principal, removing all others.  This key
1698   * must have the DES_CBC_CRC enctype and is entered as having the
1699   * krb4 salttype.  This is to enable things like kadmind4 to work.
1700   */
1701  kadm5_ret_t
1702  kadm5_setv4key_principal(void *server_handle,
1703  		       krb5_principal principal,
1704  		       krb5_keyblock *keyblock)
1705  {
1706      krb5_db_entry		kdb;
1707      osa_princ_ent_rec		adb;
1708      krb5_int32			now;
1709      kadm5_policy_ent_rec	pol;
1710      krb5_keysalt		keysalt;
1711      int				i, k, kvno, ret, have_pol = 0;
1712  #if 0
1713      int                         last_pwd;
1714  #endif
1715      kadm5_server_handle_t	handle = server_handle;
1716      krb5_key_data               tmp_key_data;
1717  
1718      memset( &tmp_key_data, 0, sizeof(tmp_key_data));
1719  
1720      CHECK_HANDLE(server_handle);
1721  
1722      krb5_clear_error_message(handle->context);
1723  
1724      if (principal == NULL || keyblock == NULL)
1725  	return EINVAL;
1726      if (hist_princ && /* this will be NULL when initializing the databse */
1727  	((krb5_principal_compare(handle->context,
1728  				 principal, hist_princ)) == TRUE))
1729  	return KADM5_PROTECT_PRINCIPAL;
1730  
1731      if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
1732  	return KADM5_SETV4KEY_INVAL_ENCTYPE;
1733  
1734      if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1735         return(ret);
1736  
1737      for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1738  	 if (kdb.key_data[i].key_data_kvno > kvno)
1739  	      kvno = kdb.key_data[i].key_data_kvno;
1740  
1741      if (kdb.key_data != NULL)
1742  	 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1743  
1744      kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data));
1745      if (kdb.key_data == NULL)
1746  	 return ENOMEM;
1747      memset(kdb.key_data, 0, sizeof(krb5_key_data));
1748      kdb.n_key_data = 1;
1749      keysalt.type = KRB5_KDB_SALTTYPE_V4;
1750      /* XXX data.magic? */
1751      keysalt.data.length = 0;
1752      keysalt.data.data = NULL;
1753  
1754      /* use tmp_key_data as temporary location and reallocate later */
1755      ret = krb5_dbekd_encrypt_key_data(handle->context, &master_keyblock,
1756  				      keyblock, &keysalt, kvno + 1,
1757  				      &tmp_key_data);
1758      if (ret) {
1759  	goto done;
1760      }
1761  
1762      for (k = 0; k < tmp_key_data.key_data_ver; k++) {
1763  	kdb.key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
1764  	kdb.key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
1765  	if (tmp_key_data.key_data_contents[k]) {
1766  	    kdb.key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1767  	    if (kdb.key_data->key_data_contents[k] == NULL) {
1768  		cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1769  		kdb.key_data = NULL;
1770  		kdb.n_key_data = 0;
1771  		ret = ENOMEM;
1772  		goto done;
1773  	    }
1774  	    memcpy (kdb.key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
1775  
1776  	    memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
1777  	    free (tmp_key_data.key_data_contents[k]);
1778  	    tmp_key_data.key_data_contents[k] = NULL;
1779  	}
1780      }
1781  
1782  
1783  
1784      kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1785  
1786      ret = krb5_timeofday(handle->context, &now);
1787      if (ret)
1788  	goto done;
1789  
1790      if ((adb.aux_attributes & KADM5_POLICY)) {
1791  	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1792  				    &pol)) != KADM5_OK)
1793  	   goto done;
1794  	have_pol = 1;
1795  
1796  #if 0
1797  	/*
1798  	  * The spec says this check is overridden if the caller has
1799  	  * modify privilege.  The admin server therefore makes this
1800  	  * check itself (in chpass_principal_wrapper, misc.c).  A
1801  	  * local caller implicitly has all authorization bits.
1802  	  */
1803  	if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1804  						  &kdb, &last_pwd))
1805  	     goto done;
1806  	if((now - last_pwd) < pol.pw_min_life &&
1807  	   !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1808  	     ret = KADM5_PASS_TOOSOON;
1809  	     goto done;
1810  	}
1811  #endif
1812  #if 0
1813  	/*
1814  	 * Should we be checking/updating pw history here?
1815  	 */
1816  	if(pol.pw_history_num > 1) {
1817  	    if(adb.admin_history_kvno != hist_kvno) {
1818  		ret = KADM5_BAD_HIST_KEY;
1819  		goto done;
1820  	    }
1821  
1822  	    if (ret = check_pw_reuse(handle->context,
1823  				     &hist_key,
1824  				     kdb.n_key_data, kdb.key_data,
1825  				     adb.old_key_len, adb.old_keys))
1826  		goto done;
1827  	}
1828  #endif
1829  
1830  	if (pol.pw_max_life)
1831  	   kdb.pw_expiration = now + pol.pw_max_life;
1832  	else
1833  	   kdb.pw_expiration = 0;
1834      } else {
1835  	kdb.pw_expiration = 0;
1836      }
1837  
1838      ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1839      if (ret)
1840  	 goto done;
1841  
1842      if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1843  	goto done;
1844  
1845      ret = KADM5_OK;
1846  done:
1847      for (i = 0; i < tmp_key_data.key_data_ver; i++) {
1848  	if (tmp_key_data.key_data_contents[i]) {
1849  	    memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
1850  	    free (tmp_key_data.key_data_contents[i]);
1851  	}
1852      }
1853  
1854      kdb_free_entry(handle, &kdb, &adb);
1855      if (have_pol)
1856  	 kadm5_free_policy_ent(handle->lhandle, &pol);
1857  
1858      return ret;
1859  }
1860  #endif
1861  
1862  kadm5_ret_t
kadm5_setkey_principal(void * server_handle,krb5_principal principal,krb5_keyblock * keyblocks,int n_keys)1863  kadm5_setkey_principal(void *server_handle,
1864  		       krb5_principal principal,
1865  		       krb5_keyblock *keyblocks,
1866  		       int n_keys)
1867  {
1868      return
1869  	kadm5_setkey_principal_3(server_handle, principal,
1870  				 FALSE, 0, NULL,
1871  				 keyblocks, n_keys);
1872  }
1873  
1874  kadm5_ret_t
kadm5_setkey_principal_3(void * server_handle,krb5_principal principal,krb5_boolean keepold,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,krb5_keyblock * keyblocks,int n_keys)1875  kadm5_setkey_principal_3(void *server_handle,
1876  			 krb5_principal principal,
1877  			 krb5_boolean keepold,
1878  			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1879  			 krb5_keyblock *keyblocks,
1880  			 int n_keys)
1881  {
1882      krb5_db_entry		kdb;
1883      osa_princ_ent_rec		adb;
1884      krb5_int32			now;
1885      kadm5_policy_ent_rec	pol;
1886      krb5_key_data		*old_key_data;
1887      int				n_old_keys;
1888      int				i, j, k, kvno, ret, have_pol = 0;
1889  #if 0
1890      int                         last_pwd;
1891  #endif
1892      kadm5_server_handle_t	handle = server_handle;
1893      krb5_boolean		similar;
1894      krb5_keysalt		keysalt;
1895      krb5_key_data         tmp_key_data;
1896      krb5_key_data        *tptr;
1897  
1898      CHECK_HANDLE(server_handle);
1899  
1900      krb5_clear_error_message(handle->context);
1901  
1902      if (principal == NULL || keyblocks == NULL)
1903  	return EINVAL;
1904      if (hist_princ && /* this will be NULL when initializing the databse */
1905  	((krb5_principal_compare(handle->context,
1906  				 principal, hist_princ)) == TRUE))
1907  	return KADM5_PROTECT_PRINCIPAL;
1908  
1909      for (i = 0; i < n_keys; i++) {
1910  	for (j = i+1; j < n_keys; j++) {
1911  	    if ((ret = krb5_c_enctype_compare(handle->context,
1912  					      keyblocks[i].enctype,
1913  					      keyblocks[j].enctype,
1914  					      &similar)))
1915  		return(ret);
1916  	    if (similar) {
1917  		if (n_ks_tuple) {
1918  		    if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
1919  			return KADM5_SETKEY_DUP_ENCTYPES;
1920  		} else
1921  		    return KADM5_SETKEY_DUP_ENCTYPES;
1922  	    }
1923  	}
1924      }
1925  
1926      if (n_ks_tuple && n_ks_tuple != n_keys)
1927  	return KADM5_SETKEY3_ETYPE_MISMATCH;
1928  
1929      if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1930         return(ret);
1931  
1932      for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1933  	 if (kdb.key_data[i].key_data_kvno > kvno)
1934  	      kvno = kdb.key_data[i].key_data_kvno;
1935  
1936      if (keepold) {
1937  	old_key_data = kdb.key_data;
1938  	n_old_keys = kdb.n_key_data;
1939      } else {
1940  	if (kdb.key_data != NULL)
1941  	    cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1942  	n_old_keys = 0;
1943  	old_key_data = NULL;
1944      }
1945  
1946      kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys)
1947  						 *sizeof(krb5_key_data));
1948      if (kdb.key_data == NULL) {
1949  	ret = ENOMEM;
1950  	goto done;
1951      }
1952  
1953      memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
1954      kdb.n_key_data = 0;
1955  
1956      for (i = 0; i < n_keys; i++) {
1957  	if (n_ks_tuple) {
1958  	    keysalt.type = ks_tuple[i].ks_salttype;
1959  	    keysalt.data.length = 0;
1960  	    keysalt.data.data = NULL;
1961  	    if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
1962  		ret = KADM5_SETKEY3_ETYPE_MISMATCH;
1963  		goto done;
1964  	    }
1965  	}
1966  	memset (&tmp_key_data, 0, sizeof(tmp_key_data));
1967  
1968  	ret = krb5_dbekd_encrypt_key_data(handle->context,
1969  					  &handle->master_keyblock,
1970  					  &keyblocks[i],
1971  					  n_ks_tuple ? &keysalt : NULL,
1972  					  kvno + 1,
1973  					  &tmp_key_data);
1974  	if (ret) {
1975  	    goto done;
1976  	}
1977  	tptr = &kdb.key_data[i];
1978  	for (k = 0; k < tmp_key_data.key_data_ver; k++) {
1979  	    tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
1980  	    tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
1981  	    if (tmp_key_data.key_data_contents[k]) {
1982  		tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1983  		if (tptr->key_data_contents[k] == NULL) {
1984  		    int i1;
1985  		    for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) {
1986  			if (tmp_key_data.key_data_contents[i1]) {
1987  			    memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
1988  			    free (tmp_key_data.key_data_contents[i1]);
1989  			}
1990  		    }
1991  
1992  		    ret =  ENOMEM;
1993  		    goto done;
1994  		}
1995  		memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
1996  
1997  		memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
1998  		free (tmp_key_data.key_data_contents[k]);
1999  		tmp_key_data.key_data_contents[k] = NULL;
2000  	    }
2001  	}
2002  	kdb.n_key_data++;
2003      }
2004  
2005      /* copy old key data if necessary */
2006      for (i = 0; i < n_old_keys; i++) {
2007  	kdb.key_data[i+n_keys] = old_key_data[i];
2008  	memset(&old_key_data[i], 0, sizeof (krb5_key_data));
2009  	kdb.n_key_data++;
2010      }
2011  
2012      if (old_key_data)
2013  	krb5_db_free(handle->context, old_key_data);
2014  
2015      /* assert(kdb.n_key_data == n_keys + n_old_keys) */
2016      kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
2017  
2018      if ((ret = krb5_timeofday(handle->context, &now)))
2019  	goto done;
2020  
2021      if ((adb.aux_attributes & KADM5_POLICY)) {
2022  	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
2023  				    &pol)) != KADM5_OK)
2024  	   goto done;
2025  	have_pol = 1;
2026  
2027  #if 0
2028  	/*
2029  	  * The spec says this check is overridden if the caller has
2030  	  * modify privilege.  The admin server therefore makes this
2031  	  * check itself (in chpass_principal_wrapper, misc.c).  A
2032  	  * local caller implicitly has all authorization bits.
2033  	  */
2034  	if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
2035  						  &kdb, &last_pwd))
2036  	     goto done;
2037  	if((now - last_pwd) < pol.pw_min_life &&
2038  	   !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
2039  	     ret = KADM5_PASS_TOOSOON;
2040  	     goto done;
2041  	}
2042  #endif
2043  #if 0
2044  	/*
2045  	 * Should we be checking/updating pw history here?
2046  	 */
2047  	if (pol.pw_history_num > 1) {
2048  	    if(adb.admin_history_kvno != hist_kvno) {
2049  		ret = KADM5_BAD_HIST_KEY;
2050  		goto done;
2051  	    }
2052  
2053  	    if (ret = check_pw_reuse(handle->context,
2054  				&handle->master_keyblock,
2055  				     &hist_key,
2056  				     kdb.n_key_data, kdb.key_data,
2057  				     adb.old_key_len, adb.old_keys))
2058  		goto done;
2059  	}
2060  #endif
2061  
2062  	if (pol.pw_max_life)
2063  	   kdb.pw_expiration = now + pol.pw_max_life;
2064  	else
2065  	   kdb.pw_expiration = 0;
2066      } else {
2067  	kdb.pw_expiration = 0;
2068      }
2069  
2070      if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)))
2071          goto done;
2072  
2073      if ((ret = kdb_put_entry(handle, &kdb, &adb)))
2074  	goto done;
2075  
2076      ret = KADM5_OK;
2077  done:
2078      kdb_free_entry(handle, &kdb, &adb);
2079      if (have_pol)
2080  	 kadm5_free_policy_ent(handle->lhandle, &pol);
2081  
2082      return ret;
2083  }
2084  
2085  /*
2086   * Allocate an array of n_key_data krb5_keyblocks, fill in each
2087   * element with the results of decrypting the nth key in key_data with
2088   * master_keyblock, and if n_keys is not NULL fill it in with the
2089   * number of keys decrypted.
2090   */
decrypt_key_data(krb5_context context,krb5_keyblock * master_keyblock,int n_key_data,krb5_key_data * key_data,krb5_keyblock ** keyblocks,int * n_keys)2091  static int decrypt_key_data(krb5_context context,
2092  			    krb5_keyblock *master_keyblock,
2093  			    int n_key_data, krb5_key_data *key_data,
2094  			    krb5_keyblock **keyblocks, int *n_keys)
2095  {
2096       krb5_keyblock *keys;
2097       int ret, i;
2098  
2099       keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
2100       if (keys == NULL)
2101  	  return ENOMEM;
2102       memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
2103  
2104       for (i = 0; i < n_key_data; i++) {
2105            ret = krb5_dbekd_decrypt_key_data(context,
2106  					    master_keyblock,
2107  					    &key_data[i],
2108  					    &keys[i], NULL);
2109  	  if (ret) {
2110  	       for (; i >= 0; i--) {
2111  		   if (keys[i].contents) {
2112  		       memset (keys[i].contents, 0, keys[i].length);
2113  		       free( keys[i].contents );
2114  		   }
2115  	       }
2116  
2117  	       memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
2118  	       free(keys);
2119  	       return ret;
2120  	  }
2121       }
2122  
2123       *keyblocks = keys;
2124       if (n_keys)
2125  	  *n_keys = n_key_data;
2126  
2127       return 0;
2128  }
2129  
2130  /*
2131   * Function: kadm5_decrypt_key
2132   *
2133   * Purpose: Retrieves and decrypts a principal key.
2134   *
2135   * Arguments:
2136   *
2137   *	server_handle	(r) kadm5 handle
2138   *	entry		(r) principal retrieved with kadm5_get_principal
2139   *	ktype		(r) enctype to search for, or -1 to ignore
2140   *	stype		(r) salt type to search for, or -1 to ignore
2141   *	kvno		(r) kvno to search for, -1 for max, 0 for max
2142   *			only if it also matches ktype and stype
2143   *	keyblock	(w) keyblock to fill in
2144   *	keysalt		(w) keysalt to fill in, or NULL
2145   *	kvnop		(w) kvno to fill in, or NULL
2146   *
2147   * Effects: Searches the key_data array of entry, which must have been
2148   * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
2149   * find a key with a specified enctype, salt type, and kvno in a
2150   * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
2151   * it with the master key, and return the key in keyblock, the salt
2152   * in salttype, and the key version number in kvno.
2153   *
2154   * If ktype or stype is -1, it is ignored for the search.  If kvno is
2155   * -1, ktype and stype are ignored and the key with the max kvno is
2156   * returned.  If kvno is 0, only the key with the max kvno is returned
2157   * and only if it matches the ktype and stype; otherwise, ENOENT is
2158   * returned.
2159   */
kadm5_decrypt_key(void * server_handle,kadm5_principal_ent_t entry,krb5_int32 ktype,krb5_int32 stype,krb5_int32 kvno,krb5_keyblock * keyblock,krb5_keysalt * keysalt,int * kvnop)2160  kadm5_ret_t kadm5_decrypt_key(void *server_handle,
2161  			      kadm5_principal_ent_t entry, krb5_int32
2162  			      ktype, krb5_int32 stype, krb5_int32
2163  			      kvno, krb5_keyblock *keyblock,
2164  			      krb5_keysalt *keysalt, int *kvnop)
2165  {
2166      kadm5_server_handle_t handle = server_handle;
2167      krb5_db_entry dbent;
2168      krb5_key_data *key_data;
2169      int ret;
2170  
2171      CHECK_HANDLE(server_handle);
2172  
2173      if (entry->n_key_data == 0 || entry->key_data == NULL)
2174  	 return EINVAL;
2175  
2176      /* find_enctype only uses these two fields */
2177      dbent.n_key_data = entry->n_key_data;
2178      dbent.key_data = entry->key_data;
2179      if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
2180  				    stype, kvno, &key_data)))
2181  	 return ret;
2182  
2183      if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
2184  					   &handle->master_keyblock, key_data,
2185  					   keyblock, keysalt)))
2186  	 return ret;
2187  
2188      /*
2189       * Coerce the enctype of the output keyblock in case we got an
2190       * inexact match on the enctype; this behavior will go away when
2191       * the key storage architecture gets redesigned for 1.3.
2192       */
2193      keyblock->enctype = ktype;
2194  
2195      if (kvnop)
2196  	 *kvnop = key_data->key_data_kvno;
2197  
2198      return KADM5_OK;
2199  }
2200  
2201  /* Solaris Kerberos */
2202  kadm5_ret_t
kadm5_check_min_life(void * server_handle,krb5_principal principal,char * msg_ret,unsigned int msg_len)2203  kadm5_check_min_life(void *server_handle, krb5_principal principal,
2204  	       char *msg_ret, unsigned int msg_len)
2205  {
2206      krb5_int32			now;
2207      kadm5_ret_t			ret;
2208      kadm5_policy_ent_rec	pol;
2209      kadm5_principal_ent_rec	princ;
2210      kadm5_server_handle_t	handle = server_handle;
2211  
2212      if (msg_ret != NULL)
2213  	*msg_ret = '\0';
2214  
2215      ret = krb5_timeofday(handle->context, &now);
2216      if (ret)
2217  	return ret;
2218  
2219      ret = kadm5_get_principal(handle->lhandle, principal,
2220  			      &princ, KADM5_PRINCIPAL_NORMAL_MASK);
2221      if(ret)
2222  	 return ret;
2223      if(princ.aux_attributes & KADM5_POLICY) {
2224  	if((ret=kadm5_get_policy(handle->lhandle,
2225  				 princ.policy, &pol)) != KADM5_OK) {
2226  	    (void) kadm5_free_principal_ent(handle->lhandle, &princ);
2227  	    return ret;
2228  	}
2229  	if((now - princ.last_pwd_change) < pol.pw_min_life &&
2230  	   !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
2231  	    if (msg_ret != NULL) {
2232  		time_t until;
2233  		char *time_string, *ptr, *errstr;
2234  
2235  		until = princ.last_pwd_change + pol.pw_min_life;
2236  
2237  		time_string = ctime(&until);
2238  		errstr = (char *)error_message(CHPASS_UTIL_PASSWORD_TOO_SOON);
2239  
2240  		if (strlen(errstr) + strlen(time_string) >= msg_len) {
2241  		    *errstr = '\0';
2242  		} else {
2243  		    if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
2244  			*ptr = '\0';
2245  		    sprintf(msg_ret, errstr, time_string);
2246  		}
2247  	    }
2248  
2249  	    (void) kadm5_free_policy_ent(handle->lhandle, &pol);
2250  	    (void) kadm5_free_principal_ent(handle->lhandle, &princ);
2251  	    return KADM5_PASS_TOOSOON;
2252  	}
2253  
2254  	ret = kadm5_free_policy_ent(handle->lhandle, &pol);
2255  	if (ret) {
2256  	    (void) kadm5_free_principal_ent(handle->lhandle, &princ);
2257  	    return ret;
2258          }
2259      }
2260  
2261      return kadm5_free_principal_ent(handle->lhandle, &princ);
2262  }
2263