xref: /freebsd/crypto/heimdal/lib/kadm5/init_c.c (revision f7c4bd95ba735bd6a5454b4953945a99cefbb80c)
1 /*
2  * Copyright (c) 1997 - 2006 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "kadm5_locl.h"
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <netdb.h>
39 
40 RCSID("$Id: init_c.c 21972 2007-10-18 19:11:15Z lha $");
41 
42 static void
43 set_funcs(kadm5_client_context *c)
44 {
45 #define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F
46     SET(c, chpass_principal);
47     SET(c, chpass_principal_with_key);
48     SET(c, create_principal);
49     SET(c, delete_principal);
50     SET(c, destroy);
51     SET(c, flush);
52     SET(c, get_principal);
53     SET(c, get_principals);
54     SET(c, get_privs);
55     SET(c, modify_principal);
56     SET(c, randkey_principal);
57     SET(c, rename_principal);
58 }
59 
60 kadm5_ret_t
61 _kadm5_c_init_context(kadm5_client_context **ctx,
62 		      kadm5_config_params *params,
63 		      krb5_context context)
64 {
65     krb5_error_code ret;
66     char *colon;
67 
68     *ctx = malloc(sizeof(**ctx));
69     if(*ctx == NULL)
70 	return ENOMEM;
71     memset(*ctx, 0, sizeof(**ctx));
72     krb5_add_et_list (context, initialize_kadm5_error_table_r);
73     set_funcs(*ctx);
74     (*ctx)->context = context;
75     if(params->mask & KADM5_CONFIG_REALM) {
76 	ret = 0;
77 	(*ctx)->realm = strdup(params->realm);
78 	if ((*ctx)->realm == NULL)
79 	    ret = ENOMEM;
80     } else
81 	ret = krb5_get_default_realm((*ctx)->context, &(*ctx)->realm);
82     if (ret) {
83 	free(*ctx);
84 	return ret;
85     }
86     if(params->mask & KADM5_CONFIG_ADMIN_SERVER)
87 	(*ctx)->admin_server = strdup(params->admin_server);
88     else {
89 	char **hostlist;
90 
91 	ret = krb5_get_krb_admin_hst (context, &(*ctx)->realm, &hostlist);
92 	if (ret) {
93 	    free((*ctx)->realm);
94 	    free(*ctx);
95 	    return ret;
96 	}
97 	(*ctx)->admin_server = strdup(*hostlist);
98 	krb5_free_krbhst (context, hostlist);
99     }
100 
101     if ((*ctx)->admin_server == NULL) {
102 	free((*ctx)->realm);
103 	free(*ctx);
104 	return ENOMEM;
105     }
106     colon = strchr ((*ctx)->admin_server, ':');
107     if (colon != NULL)
108 	*colon++ = '\0';
109 
110     (*ctx)->kadmind_port = 0;
111 
112     if(params->mask & KADM5_CONFIG_KADMIND_PORT)
113 	(*ctx)->kadmind_port = params->kadmind_port;
114     else if (colon != NULL) {
115 	char *end;
116 
117 	(*ctx)->kadmind_port = htons(strtol (colon, &end, 0));
118     }
119     if ((*ctx)->kadmind_port == 0)
120 	(*ctx)->kadmind_port = krb5_getportbyname (context, "kerberos-adm",
121 						   "tcp", 749);
122     return 0;
123 }
124 
125 static krb5_error_code
126 get_kadm_ticket(krb5_context context,
127 		krb5_ccache id,
128 		krb5_principal client,
129 		const char *server_name)
130 {
131     krb5_error_code ret;
132     krb5_creds in, *out;
133 
134     memset(&in, 0, sizeof(in));
135     in.client = client;
136     ret = krb5_parse_name(context, server_name, &in.server);
137     if(ret)
138 	return ret;
139     ret = krb5_get_credentials(context, 0, id, &in, &out);
140     if(ret == 0)
141 	krb5_free_creds(context, out);
142     krb5_free_principal(context, in.server);
143     return ret;
144 }
145 
146 static krb5_error_code
147 get_new_cache(krb5_context context,
148 	      krb5_principal client,
149 	      const char *password,
150 	      krb5_prompter_fct prompter,
151 	      const char *keytab,
152 	      const char *server_name,
153 	      krb5_ccache *ret_cache)
154 {
155     krb5_error_code ret;
156     krb5_creds cred;
157     krb5_get_init_creds_opt *opt;
158     krb5_ccache id;
159 
160     ret = krb5_get_init_creds_opt_alloc (context, &opt);
161     if (ret)
162 	return ret;
163 
164     krb5_get_init_creds_opt_set_default_flags(context, "kadmin",
165 					      krb5_principal_get_realm(context,
166 								       client),
167 					      opt);
168 
169 
170     krb5_get_init_creds_opt_set_forwardable (opt, FALSE);
171     krb5_get_init_creds_opt_set_proxiable (opt, FALSE);
172 
173     if(password == NULL && prompter == NULL) {
174 	krb5_keytab kt;
175 	if(keytab == NULL)
176 	    ret = krb5_kt_default(context, &kt);
177 	else
178 	    ret = krb5_kt_resolve(context, keytab, &kt);
179 	if(ret) {
180 	    krb5_get_init_creds_opt_free(context, opt);
181 	    return ret;
182 	}
183 	ret = krb5_get_init_creds_keytab (context,
184 					  &cred,
185 					  client,
186 					  kt,
187 					  0,
188 					  server_name,
189 					  opt);
190 	krb5_kt_close(context, kt);
191     } else {
192 	ret = krb5_get_init_creds_password (context,
193 					    &cred,
194 					    client,
195 					    password,
196 					    prompter,
197 					    NULL,
198 					    0,
199 					    server_name,
200 					    opt);
201     }
202     krb5_get_init_creds_opt_free(context, opt);
203     switch(ret){
204     case 0:
205 	break;
206     case KRB5_LIBOS_PWDINTR:	/* don't print anything if it was just C-c:ed */
207     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
208     case KRB5KRB_AP_ERR_MODIFIED:
209 	return KADM5_BAD_PASSWORD;
210     default:
211 	return ret;
212     }
213     ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id);
214     if(ret)
215 	return ret;
216     ret = krb5_cc_initialize (context, id, cred.client);
217     if (ret)
218 	return ret;
219     ret = krb5_cc_store_cred (context, id, &cred);
220     if (ret)
221 	return ret;
222     krb5_free_cred_contents (context, &cred);
223     *ret_cache = id;
224     return 0;
225 }
226 
227 /*
228  * Check the credential cache `id� to figure out what principal to use
229  * when talking to the kadmind. If there is a initial kadmin/admin@
230  * credential in the cache, use that client principal. Otherwise, use
231  * the client principals first component and add /admin to the
232  * principal.
233  */
234 
235 static krb5_error_code
236 get_cache_principal(krb5_context context,
237 		    krb5_ccache *id,
238 		    krb5_principal *client)
239 {
240     krb5_error_code ret;
241     const char *name, *inst;
242     krb5_principal p1, p2;
243 
244     ret = krb5_cc_default(context, id);
245     if(ret) {
246 	*id = NULL;
247 	return ret;
248     }
249 
250     ret = krb5_cc_get_principal(context, *id, &p1);
251     if(ret) {
252 	krb5_cc_close(context, *id);
253 	*id = NULL;
254 	return ret;
255     }
256 
257     ret = krb5_make_principal(context, &p2, NULL,
258 			      "kadmin", "admin", NULL);
259     if (ret) {
260 	krb5_cc_close(context, *id);
261 	*id = NULL;
262 	krb5_free_principal(context, p1);
263 	return ret;
264     }
265 
266     {
267 	krb5_creds in, *out;
268 	krb5_kdc_flags flags;
269 
270 	flags.i = 0;
271 	memset(&in, 0, sizeof(in));
272 
273 	in.client = p1;
274 	in.server = p2;
275 
276 	/* check for initial ticket kadmin/admin */
277 	ret = krb5_get_credentials_with_flags(context, KRB5_GC_CACHED, flags,
278 					      *id, &in, &out);
279 	krb5_free_principal(context, p2);
280 	if (ret == 0) {
281 	    if (out->flags.b.initial) {
282 		*client = p1;
283 		krb5_free_creds(context, out);
284 		return 0;
285 	    }
286 	    krb5_free_creds(context, out);
287 	}
288     }
289     krb5_cc_close(context, *id);
290     *id = NULL;
291 
292     name = krb5_principal_get_comp_string(context, p1, 0);
293     inst = krb5_principal_get_comp_string(context, p1, 1);
294     if(inst == NULL || strcmp(inst, "admin") != 0) {
295 	ret = krb5_make_principal(context, &p2, NULL, name, "admin", NULL);
296 	krb5_free_principal(context, p1);
297 	if(ret != 0)
298 	    return ret;
299 
300 	*client = p2;
301 	return 0;
302     }
303 
304     *client = p1;
305 
306     return 0;
307 }
308 
309 krb5_error_code
310 _kadm5_c_get_cred_cache(krb5_context context,
311 			const char *client_name,
312 			const char *server_name,
313 			const char *password,
314 			krb5_prompter_fct prompter,
315 			const char *keytab,
316 			krb5_ccache ccache,
317 			krb5_ccache *ret_cache)
318 {
319     krb5_error_code ret;
320     krb5_ccache id = NULL;
321     krb5_principal default_client = NULL, client = NULL;
322 
323     /* treat empty password as NULL */
324     if(password && *password == '\0')
325 	password = NULL;
326     if(server_name == NULL)
327 	server_name = KADM5_ADMIN_SERVICE;
328 
329     if(client_name != NULL) {
330 	ret = krb5_parse_name(context, client_name, &client);
331 	if(ret)
332 	    return ret;
333     }
334 
335     if(ccache != NULL) {
336 	id = ccache;
337 	ret = krb5_cc_get_principal(context, id, &client);
338 	if(ret)
339 	    return ret;
340     } else {
341 	/* get principal from default cache, ok if this doesn't work */
342 
343 	ret = get_cache_principal(context, &id, &default_client);
344 	if (ret) {
345 	    /*
346 	     * No client was specified by the caller and we cannot
347 	     * determine the client from a credentials cache.
348 	     */
349 	    const char *user;
350 
351 	    user = get_default_username ();
352 
353 	    if(user == NULL) {
354 		krb5_set_error_string(context, "Unable to find local user name");
355 		return KADM5_FAILURE;
356 	    }
357 	    ret = krb5_make_principal(context, &default_client,
358 				      NULL, user, "admin", NULL);
359 	    if(ret)
360 		return ret;
361 	}
362     }
363 
364 
365     /*
366      * No client was specified by the caller, but we have a client
367      * from the default credentials cache.
368      */
369     if (client == NULL && default_client != NULL)
370 	client = default_client;
371 
372 
373     if(id && (default_client == NULL ||
374 	      krb5_principal_compare(context, client, default_client))) {
375 	ret = get_kadm_ticket(context, id, client, server_name);
376 	if(ret == 0) {
377 	    *ret_cache = id;
378 	    krb5_free_principal(context, default_client);
379 	    if (default_client != client)
380 		krb5_free_principal(context, client);
381 	    return 0;
382 	}
383 	if(ccache != NULL)
384 	    /* couldn't get ticket from cache */
385 	    return -1;
386     }
387     /* get creds via AS request */
388     if(id && (id != ccache))
389 	krb5_cc_close(context, id);
390     if (client != default_client)
391 	krb5_free_principal(context, default_client);
392 
393     ret = get_new_cache(context, client, password, prompter, keytab,
394 			server_name, ret_cache);
395     krb5_free_principal(context, client);
396     return ret;
397 }
398 
399 static kadm5_ret_t
400 kadm_connect(kadm5_client_context *ctx)
401 {
402     kadm5_ret_t ret;
403     krb5_principal server;
404     krb5_ccache cc;
405     int s;
406     struct addrinfo *ai, *a;
407     struct addrinfo hints;
408     int error;
409     char portstr[NI_MAXSERV];
410     char *hostname, *slash;
411     char *service_name;
412     krb5_context context = ctx->context;
413 
414     memset (&hints, 0, sizeof(hints));
415     hints.ai_socktype = SOCK_STREAM;
416     hints.ai_protocol = IPPROTO_TCP;
417 
418     snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port));
419 
420     hostname = ctx->admin_server;
421     slash = strchr (hostname, '/');
422     if (slash != NULL)
423 	hostname = slash + 1;
424 
425     error = getaddrinfo (hostname, portstr, &hints, &ai);
426     if (error) {
427 	krb5_clear_error_string(context);
428 	return KADM5_BAD_SERVER_NAME;
429     }
430 
431     for (a = ai; a != NULL; a = a->ai_next) {
432 	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
433 	if (s < 0)
434 	    continue;
435 	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
436 	    krb5_clear_error_string(context);
437 	    krb5_warn (context, errno, "connect(%s)", hostname);
438 	    close (s);
439 	    continue;
440 	}
441 	break;
442     }
443     if (a == NULL) {
444 	freeaddrinfo (ai);
445 	krb5_clear_error_string(context);
446 	krb5_warnx (context, "failed to contact %s", hostname);
447 	return KADM5_FAILURE;
448     }
449     ret = _kadm5_c_get_cred_cache(context,
450 				  ctx->client_name,
451 				  ctx->service_name,
452 				  NULL, ctx->prompter, ctx->keytab,
453 				  ctx->ccache, &cc);
454 
455     if(ret) {
456 	freeaddrinfo (ai);
457 	close(s);
458 	return ret;
459     }
460 
461     if (ctx->realm)
462 	asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, ctx->realm);
463     else
464 	asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE);
465 
466     if (service_name == NULL) {
467 	freeaddrinfo (ai);
468 	close(s);
469 	krb5_clear_error_string(context);
470 	return ENOMEM;
471     }
472 
473     ret = krb5_parse_name(context, service_name, &server);
474     free(service_name);
475     if(ret) {
476 	freeaddrinfo (ai);
477 	if(ctx->ccache == NULL)
478 	    krb5_cc_close(context, cc);
479 	close(s);
480 	return ret;
481     }
482     ctx->ac = NULL;
483 
484     ret = krb5_sendauth(context, &ctx->ac, &s,
485 			KADMIN_APPL_VERSION, NULL,
486 			server, AP_OPTS_MUTUAL_REQUIRED,
487 			NULL, NULL, cc, NULL, NULL, NULL);
488     if(ret == 0) {
489 	krb5_data params;
490 	kadm5_config_params p;
491 	memset(&p, 0, sizeof(p));
492 	if(ctx->realm) {
493 	    p.mask |= KADM5_CONFIG_REALM;
494 	    p.realm = ctx->realm;
495 	}
496 	ret = _kadm5_marshal_params(context, &p, &params);
497 
498 	ret = krb5_write_priv_message(context, ctx->ac, &s, &params);
499 	krb5_data_free(&params);
500 	if(ret) {
501 	    freeaddrinfo (ai);
502 	    close(s);
503 	    if(ctx->ccache == NULL)
504 		krb5_cc_close(context, cc);
505 	    return ret;
506 	}
507     } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) {
508 	close(s);
509 
510 	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
511 	if (s < 0) {
512 	    freeaddrinfo (ai);
513 	    krb5_clear_error_string(context);
514 	    return errno;
515 	}
516 	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
517 	    close (s);
518 	    freeaddrinfo (ai);
519 	    krb5_clear_error_string(context);
520 	    return errno;
521 	}
522 	ret = krb5_sendauth(context, &ctx->ac, &s,
523 			    KADMIN_OLD_APPL_VERSION, NULL,
524 			    server, AP_OPTS_MUTUAL_REQUIRED,
525 			    NULL, NULL, cc, NULL, NULL, NULL);
526     }
527     freeaddrinfo (ai);
528     if(ret) {
529 	close(s);
530 	return ret;
531     }
532 
533     krb5_free_principal(context, server);
534     if(ctx->ccache == NULL)
535 	krb5_cc_close(context, cc);
536     ctx->sock = s;
537 
538     return 0;
539 }
540 
541 kadm5_ret_t
542 _kadm5_connect(void *handle)
543 {
544     kadm5_client_context *ctx = handle;
545     if(ctx->sock == -1)
546 	return kadm_connect(ctx);
547     return 0;
548 }
549 
550 static kadm5_ret_t
551 kadm5_c_init_with_context(krb5_context context,
552 			  const char *client_name,
553 			  const char *password,
554 			  krb5_prompter_fct prompter,
555 			  const char *keytab,
556 			  krb5_ccache ccache,
557 			  const char *service_name,
558 			  kadm5_config_params *realm_params,
559 			  unsigned long struct_version,
560 			  unsigned long api_version,
561 			  void **server_handle)
562 {
563     kadm5_ret_t ret;
564     kadm5_client_context *ctx;
565     krb5_ccache cc;
566 
567     ret = _kadm5_c_init_context(&ctx, realm_params, context);
568     if(ret)
569 	return ret;
570 
571     if(password != NULL && *password != '\0') {
572 	ret = _kadm5_c_get_cred_cache(context,
573 				      client_name,
574 				      service_name,
575 				      password, prompter, keytab, ccache, &cc);
576 	if(ret)
577 	    return ret; /* XXX */
578 	ccache = cc;
579     }
580 
581 
582     if (client_name != NULL)
583 	ctx->client_name = strdup(client_name);
584     else
585 	ctx->client_name = NULL;
586     if (service_name != NULL)
587 	ctx->service_name = strdup(service_name);
588     else
589 	ctx->service_name = NULL;
590     ctx->prompter = prompter;
591     ctx->keytab = keytab;
592     ctx->ccache = ccache;
593     /* maybe we should copy the params here */
594     ctx->sock = -1;
595 
596     *server_handle = ctx;
597     return 0;
598 }
599 
600 static kadm5_ret_t
601 init_context(const char *client_name,
602 	     const char *password,
603 	     krb5_prompter_fct prompter,
604 	     const char *keytab,
605 	     krb5_ccache ccache,
606 	     const char *service_name,
607 	     kadm5_config_params *realm_params,
608 	     unsigned long struct_version,
609 	     unsigned long api_version,
610 	     void **server_handle)
611 {
612     krb5_context context;
613     kadm5_ret_t ret;
614     kadm5_server_context *ctx;
615 
616     ret = krb5_init_context(&context);
617     if (ret)
618 	return ret;
619     ret = kadm5_c_init_with_context(context,
620 				    client_name,
621 				    password,
622 				    prompter,
623 				    keytab,
624 				    ccache,
625 				    service_name,
626 				    realm_params,
627 				    struct_version,
628 				    api_version,
629 				    server_handle);
630     if(ret){
631 	krb5_free_context(context);
632 	return ret;
633     }
634     ctx = *server_handle;
635     ctx->my_context = 1;
636     return 0;
637 }
638 
639 kadm5_ret_t
640 kadm5_c_init_with_password_ctx(krb5_context context,
641 			       const char *client_name,
642 			       const char *password,
643 			       const char *service_name,
644 			       kadm5_config_params *realm_params,
645 			       unsigned long struct_version,
646 			       unsigned long api_version,
647 			       void **server_handle)
648 {
649     return kadm5_c_init_with_context(context,
650 				     client_name,
651 				     password,
652 				     krb5_prompter_posix,
653 				     NULL,
654 				     NULL,
655 				     service_name,
656 				     realm_params,
657 				     struct_version,
658 				     api_version,
659 				     server_handle);
660 }
661 
662 kadm5_ret_t
663 kadm5_c_init_with_password(const char *client_name,
664 			   const char *password,
665 			   const char *service_name,
666 			   kadm5_config_params *realm_params,
667 			   unsigned long struct_version,
668 			   unsigned long api_version,
669 			   void **server_handle)
670 {
671     return init_context(client_name,
672 			password,
673 			krb5_prompter_posix,
674 			NULL,
675 			NULL,
676 			service_name,
677 			realm_params,
678 			struct_version,
679 			api_version,
680 			server_handle);
681 }
682 
683 kadm5_ret_t
684 kadm5_c_init_with_skey_ctx(krb5_context context,
685 			   const char *client_name,
686 			   const char *keytab,
687 			   const char *service_name,
688 			   kadm5_config_params *realm_params,
689 			   unsigned long struct_version,
690 			   unsigned long api_version,
691 			   void **server_handle)
692 {
693     return kadm5_c_init_with_context(context,
694 				     client_name,
695 				     NULL,
696 				     NULL,
697 				     keytab,
698 				     NULL,
699 				     service_name,
700 				     realm_params,
701 				     struct_version,
702 				     api_version,
703 				     server_handle);
704 }
705 
706 
707 kadm5_ret_t
708 kadm5_c_init_with_skey(const char *client_name,
709 		     const char *keytab,
710 		     const char *service_name,
711 		     kadm5_config_params *realm_params,
712 		     unsigned long struct_version,
713 		     unsigned long api_version,
714 		     void **server_handle)
715 {
716     return init_context(client_name,
717 			NULL,
718 			NULL,
719 			keytab,
720 			NULL,
721 			service_name,
722 			realm_params,
723 			struct_version,
724 			api_version,
725 			server_handle);
726 }
727 
728 kadm5_ret_t
729 kadm5_c_init_with_creds_ctx(krb5_context context,
730 			    const char *client_name,
731 			    krb5_ccache ccache,
732 			    const char *service_name,
733 			    kadm5_config_params *realm_params,
734 			    unsigned long struct_version,
735 			    unsigned long api_version,
736 			    void **server_handle)
737 {
738     return kadm5_c_init_with_context(context,
739 				     client_name,
740 				     NULL,
741 				     NULL,
742 				     NULL,
743 				     ccache,
744 				     service_name,
745 				     realm_params,
746 				     struct_version,
747 				     api_version,
748 				     server_handle);
749 }
750 
751 kadm5_ret_t
752 kadm5_c_init_with_creds(const char *client_name,
753 			krb5_ccache ccache,
754 			const char *service_name,
755 			kadm5_config_params *realm_params,
756 			unsigned long struct_version,
757 			unsigned long api_version,
758 			void **server_handle)
759 {
760     return init_context(client_name,
761 			NULL,
762 			NULL,
763 			NULL,
764 			ccache,
765 			service_name,
766 			realm_params,
767 			struct_version,
768 			api_version,
769 			server_handle);
770 }
771 
772 #if 0
773 kadm5_ret_t
774 kadm5_init(char *client_name, char *pass,
775 	   char *service_name,
776 	   kadm5_config_params *realm_params,
777 	   unsigned long struct_version,
778 	   unsigned long api_version,
779 	   void **server_handle)
780 {
781 }
782 #endif
783 
784