1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
8  *
9  * $Id: kadm5_create.c,v 1.6 1998/10/30 02:52:37 marc Exp $
10  * $Source: /cvs/krbdev/krb5/src/kadmin/dbutil/kadm5_create.c,v $
11  */
12 
13 /*
14  * Copyright (C) 1998 by the FundsXpress, INC.
15  *
16  * All rights reserved.
17  *
18  * Export of this software from the United States of America may require
19  * a specific license from the United States Government.  It is the
20  * responsibility of any person or organization contemplating export to
21  * obtain such a license before exporting.
22  *
23  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
24  * distribute this software and its documentation for any purpose and
25  * without fee is hereby granted, provided that the above copyright
26  * notice appear in all copies and that both that copyright notice and
27  * this permission notice appear in supporting documentation, and that
28  * the name of FundsXpress. not be used in advertising or publicity pertaining
29  * to distribution of the software without specific, written prior
30  * permission.  FundsXpress makes no representations about the suitability of
31  * this software for any purpose.  It is provided "as is" without express
32  * or implied warranty.
33  *
34  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
35  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
36  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
37  */
38 
39 #include "string_table.h"
40 
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <k5-int.h>
45 #include <kdb.h>
46 #include <kadm5/admin.h>
47 #include <krb5/adm_proto.h>
48 
49 #include <krb5.h>
50 #include <krb5/kdb.h>
51 #include "kdb5_util.h"
52 #include <libintl.h>
53 
54 int
55 add_admin_old_princ(void *handle, krb5_context context,
56 		    char *name, char *realm, int attrs, int lifetime);
57 int
58 add_admin_sname_princ(void *handle, krb5_context context,
59     char *sname, int attrs, int lifetime);
60 static int
61 add_admin_princ(void *handle, krb5_context context,
62     krb5_principal principal, int attrs, int lifetime);
63 
64 static int add_admin_princs(void *handle, krb5_context context, char *realm);
65 
66 #define ERR 1
67 #define OK 0
68 
69 #define ADMIN_LIFETIME 60*60*3 /* 3 hours */
70 #define CHANGEPW_LIFETIME 60*5 /* 5 minutes */
71 
72 extern char *progname;
73 
74 /*
75  * Function: kadm5_create
76  *
77  * Purpose: create admin principals in KDC database
78  *
79  * Arguments:	params	(r) configuration parameters to use
80  *
81  * Effects:  Creates KADM5_ADMIN_SERVICE and KADM5_CHANGEPW_SERVICE
82  * principals in the KDC database and sets their attributes
83  * appropriately.
84  */
85 int kadm5_create(kadm5_config_params *params)
86 {
87      int retval;
88      krb5_context context;
89 
90      kadm5_config_params lparams;
91 
92      if ((retval = kadm5_init_krb5_context(&context)))
93 	  exit(ERR);
94 
95      (void) memset(&lparams, 0, sizeof (kadm5_config_params));
96 
97      /*
98       * The lock file has to exist before calling kadm5_init, but
99       * params->admin_lockfile may not be set yet...
100       */
101      if ((retval = kadm5_get_config_params(context, 1,
102 					   params, &lparams))) {
103 	com_err(progname, retval, gettext("while looking up the Kerberos configuration"));
104 	  return 1;
105      }
106 
107      retval = kadm5_create_magic_princs(&lparams, context);
108 
109      kadm5_free_config_params(context, &lparams);
110      krb5_free_context(context);
111 
112      return retval;
113 }
114 
115 int kadm5_create_magic_princs(kadm5_config_params *params,
116 			      krb5_context context)
117 {
118      int retval;
119      void *handle;
120 
121      retval = krb5_klog_init(context, "admin_server", progname, 0);
122      if (retval)
123 	  return retval;
124      if ((retval = kadm5_init(progname, NULL, NULL, params,
125 			      KADM5_STRUCT_VERSION,
126 			      KADM5_API_VERSION_2,
127 			      db5util_db_args,
128 			      &handle))) {
129 	com_err(progname, retval,  gettext("while initializing the Kerberos admin interface"));
130 	  return retval;
131      }
132 
133      retval = add_admin_princs(handle, context, params->realm);
134 
135      kadm5_destroy(handle);
136 
137      krb5_klog_close(context);
138 
139      return retval;
140 }
141 
142 /*
143  * Function: build_name_with_realm
144  *
145  * Purpose: concatenate a name and a realm to form a krb5 name
146  *
147  * Arguments:
148  *
149  * 	name	(input) the name
150  * 	realm	(input) the realm
151  *
152  * Returns:
153  *
154  * 	pointer to name@realm, in allocated memory, or NULL if it
155  * 	cannot be allocated
156  *
157  * Requires: both strings are null-terminated
158  */
159 static char *build_name_with_realm(char *name, char *realm)
160 {
161      char *n;
162 
163      n = (char *) malloc(strlen(name) + strlen(realm) + 2);
164      sprintf(n, "%s@%s", name, realm);
165      return n;
166 }
167 
168 /*
169  * Function: add_admin_princs
170  *
171  * Purpose: create admin principals
172  *
173  * Arguments:
174  *
175  * 	rseed		(input) random seed
176  * 	realm		(input) realm, or NULL for default realm
177  *      <return value>  (output) status, 0 for success, 1 for serious error
178  *
179  * Requires:
180  *
181  * Effects:
182  *
183  * add_admin_princs creates KADM5_ADMIN_SERVICE,
184  * KADM5_CHANGEPW_SERVICE.  If any of these exist a message is
185  * printed.  If any of these existing principal do not have the proper
186  * attributes, a warning message is printed.
187  */
188 static int add_admin_princs(void *handle, krb5_context context, char *realm)
189 {
190   krb5_error_code ret = 0;
191 
192 /*
193  * Solaris Kerberos:
194  * The kadmin/admin principal is unused on Solaris. This principal is used
195  * in AUTH_GSSAPI but Solaris doesn't support AUTH_GSSAPI. RPCSEC_GSS can only
196  * be used with host-based principals.
197  *
198  */
199 
200 #if 0
201   if ((ret = add_admin_old_princ(handle, context,
202   		     KADM5_ADMIN_SERVICE, realm,
203   		     KRB5_KDB_DISALLOW_TGT_BASED,
204   		     ADMIN_LIFETIME)))
205      goto clean_and_exit;
206 #endif
207 
208 	if ((ret = add_admin_old_princ(handle, context,
209 			     KADM5_CHANGEPW_SERVICE, realm,
210 			     KRB5_KDB_DISALLOW_TGT_BASED |
211 			     KRB5_KDB_PWCHANGE_SERVICE,
212 			     CHANGEPW_LIFETIME)))
213        goto clean_and_exit;
214 
215 	if ((ret = add_admin_sname_princ(handle, context,
216 		    KADM5_ADMIN_HOST_SERVICE,
217 		    KRB5_KDB_DISALLOW_TGT_BASED,
218 		    ADMIN_LIFETIME)))
219 		goto clean_and_exit;
220 
221 	if ((ret = add_admin_sname_princ(handle, context,
222 		    KADM5_CHANGEPW_HOST_SERVICE,
223 		    KRB5_KDB_DISALLOW_TGT_BASED |
224 		    KRB5_KDB_PWCHANGE_SERVICE,
225 		    ADMIN_LIFETIME)))
226 		goto clean_and_exit;
227 
228 	if ((ret = add_admin_sname_princ(handle, context,
229 		    KADM5_KIPROP_HOST_SERVICE,
230 		    KRB5_KDB_DISALLOW_TGT_BASED,
231 		    ADMIN_LIFETIME)))
232 		goto clean_and_exit;
233 
234 clean_and_exit:
235 
236   return ret;
237 }
238 
239 /*
240  * Function: add_admin_princ
241  *
242  * Arguments:
243  *
244  * 	creator		(r) principal to use as "mod_by"
245  * 	rseed		(r) seed for random key generator
246  *	principal	(r) kerberos principal to add
247  * 	attrs		(r) principal's attributes
248  * 	lifetime	(r) principal's max life, or 0
249  * 	not_unique	(r) error message for multiple entries, never used
250  * 	exists		(r) warning message for principal exists
251  * 	wrong_attrs	(r) warning message for wrong attributes
252  *
253  * Returns:
254  *
255  * 	OK on success
256  * 	ERR on serious errors
257  *
258  * Effects:
259  *
260  * If the principal is not unique, not_unique is printed (but this
261  * never happens).  If the principal exists, then exists is printed
262  * and if the principals attributes != attrs, wrong_attrs is printed.
263  * Otherwise, the principal is created with mod_by creator and
264  * attributes attrs and max life of lifetime (if not zero).
265  */
266 
267 static int add_admin_princ(void *handle, krb5_context context,
268     krb5_principal principal, int attrs, int lifetime)
269 {
270      char *fullname;
271      krb5_error_code ret;
272      kadm5_principal_ent_rec ent;
273 
274      memset(&ent, 0, sizeof(ent));
275 
276 	if (krb5_unparse_name(context, principal, &fullname))
277 		return ERR;
278 
279      ent.principal = principal;
280      ent.max_life = lifetime;
281      ent.attributes = attrs | KRB5_KDB_DISALLOW_ALL_TIX;
282 
283      ret = kadm5_create_principal(handle, &ent,
284 				  (KADM5_PRINCIPAL | KADM5_MAX_LIFE |
285 				   KADM5_ATTRIBUTES),
286 				  "to-be-random");
287      if (ret) {
288 	  if (ret != KADM5_DUP) {
289 	       com_err(progname, ret,
290 			gettext(str_PUT_PRINC), fullname);
291 	       krb5_free_principal(context, ent.principal);
292 	       free(fullname);
293 	       return ERR;
294 	  }
295      } else {
296 	  /* only randomize key if we created the principal */
297 
298 	  /*
299 	   * Solaris Kerberos:
300 	   * Create kadmind principals with keys for all supported encryption types.
301 	   * Follows a similar pattern to add_principal() in keytab.c.
302 	   */
303 	  krb5_enctype *tmpenc, *enctype = NULL;
304 	  krb5_key_salt_tuple *keysalt;
305 	  int num_ks, i;
306 	  krb5_int32 normalsalttype;
307 
308 	  ret = krb5_get_permitted_enctypes(context, &enctype);
309 	  if (ret || *enctype == 0) {
310 	       com_err(progname, ret,
311 		   gettext("while getting list of permitted encryption types"));
312 	       krb5_free_principal(context, ent.principal);
313 	       free(fullname);
314 	       return ERR;
315 	  }
316 
317 	  /* Count the number of enc types */
318 	  for (tmpenc = enctype, num_ks = 0; *tmpenc; tmpenc++)
319 		num_ks++;
320 
321 	  keysalt = malloc (sizeof (krb5_key_salt_tuple) * num_ks);
322 	  if (keysalt == NULL) {
323 	       com_err(progname, ENOMEM,
324 		   gettext("while generating list of key salt tuples"));
325 	       krb5_free_ktypes(context, enctype);
326 	       krb5_free_principal(context, ent.principal);
327 	       free(fullname);
328 	       return ERR;
329 	  }
330 
331 	  ret = krb5_string_to_salttype("normal", &normalsalttype);
332 	  if (ret) {
333 	  	com_err(progname, ret,
334 	  	 	gettext("while converting \"normal\" to a salttype"));
335 		free(keysalt);
336 		krb5_free_ktypes(context, enctype);
337 	  	krb5_free_principal(context, ent.principal);
338 	  	free(fullname);
339 	  	return ERR;
340 	  }
341 
342 	  /* Only create keys with "normal" salttype */
343 	  for (i = 0; i < num_ks; i++) {
344 		keysalt[i].ks_enctype = enctype[i];
345 		keysalt[i].ks_salttype = normalsalttype;
346 	  }
347 
348 	  ret = kadm5_randkey_principal_3(handle, ent.principal, FALSE, num_ks,
349 	      keysalt, NULL, NULL);
350 	  free(keysalt);
351           krb5_free_ktypes (context, enctype);
352 
353 
354 	  if (ret) {
355 	       com_err(progname, ret,
356 			gettext(str_RANDOM_KEY), fullname);
357 	       krb5_free_principal(context, ent.principal);
358 	       free(fullname);
359 	       return ERR;
360 	  }
361 
362 	  ent.attributes = attrs;
363 	  ret = kadm5_modify_principal(handle, &ent, KADM5_ATTRIBUTES);
364 	  if (ret) {
365 	      com_err(progname, ret,
366 	       gettext(str_PUT_PRINC), fullname);
367 	       krb5_free_principal(context, ent.principal);
368 	       free(fullname);
369 	       return ERR;
370 	  }
371      }
372 
373      krb5_free_principal(context, ent.principal);
374      free(fullname);
375 
376      return OK;
377 }
378 
379 int
380 add_admin_old_princ(void *handle, krb5_context context,
381     char *name, char *realm, int attrs, int lifetime)
382 {
383 	char *fullname;
384 	krb5_error_code ret;
385 	krb5_principal principal;
386 
387 	fullname = build_name_with_realm(name, realm);
388 	if (ret = krb5_parse_name(context, fullname, &principal)) {
389 		com_err(progname, ret, gettext(str_PARSE_NAME));
390 		return (ERR);
391 	}
392 
393 	return (add_admin_princ(handle, context, principal, attrs, lifetime));
394 }
395 
396 int
397 add_admin_sname_princ(void *handle, krb5_context context,
398 	     char *sname, int attrs, int lifetime)
399 {
400 	krb5_error_code ret;
401 	krb5_principal principal;
402 
403 	if (ret = krb5_sname_to_principal(context, NULL, sname,
404 					  KRB5_NT_SRV_HST, &principal)) {
405 		com_err(progname, ret,
406 			gettext("Could not get host based "
407 				"service name for %s principal\n"), sname);
408 		return (ERR);
409 	}
410 	return (add_admin_princ(handle, context, principal, attrs, lifetime));
411 }
412 
413 
414 
415