155682Smarkm/*
2178825Sdfr * Copyright (c) 1997 - 2006 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "kadm5_locl.h"
3555682Smarkm#include <sys/types.h>
3655682Smarkm#include <sys/socket.h>
3755682Smarkm#include <netinet/in.h>
3855682Smarkm#include <netdb.h>
3955682Smarkm
40178825SdfrRCSID("$Id: init_c.c 21972 2007-10-18 19:11:15Z lha $");
4155682Smarkm
4255682Smarkmstatic void
4355682Smarkmset_funcs(kadm5_client_context *c)
4455682Smarkm{
4555682Smarkm#define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F
4655682Smarkm    SET(c, chpass_principal);
4772445Sassar    SET(c, chpass_principal_with_key);
4855682Smarkm    SET(c, create_principal);
4955682Smarkm    SET(c, delete_principal);
5055682Smarkm    SET(c, destroy);
5155682Smarkm    SET(c, flush);
5255682Smarkm    SET(c, get_principal);
5355682Smarkm    SET(c, get_principals);
5455682Smarkm    SET(c, get_privs);
5555682Smarkm    SET(c, modify_principal);
5655682Smarkm    SET(c, randkey_principal);
5755682Smarkm    SET(c, rename_principal);
5855682Smarkm}
5955682Smarkm
6055682Smarkmkadm5_ret_t
6155682Smarkm_kadm5_c_init_context(kadm5_client_context **ctx,
6255682Smarkm		      kadm5_config_params *params,
6355682Smarkm		      krb5_context context)
6455682Smarkm{
6555682Smarkm    krb5_error_code ret;
6655682Smarkm    char *colon;
6755682Smarkm
6855682Smarkm    *ctx = malloc(sizeof(**ctx));
6955682Smarkm    if(*ctx == NULL)
7055682Smarkm	return ENOMEM;
7155682Smarkm    memset(*ctx, 0, sizeof(**ctx));
7255682Smarkm    krb5_add_et_list (context, initialize_kadm5_error_table_r);
7355682Smarkm    set_funcs(*ctx);
7455682Smarkm    (*ctx)->context = context;
75127808Snectar    if(params->mask & KADM5_CONFIG_REALM) {
76127808Snectar	ret = 0;
7755682Smarkm	(*ctx)->realm = strdup(params->realm);
78127808Snectar	if ((*ctx)->realm == NULL)
79127808Snectar	    ret = ENOMEM;
80127808Snectar    } else
81127808Snectar	ret = krb5_get_default_realm((*ctx)->context, &(*ctx)->realm);
82127808Snectar    if (ret) {
83127808Snectar	free(*ctx);
84127808Snectar	return ret;
85127808Snectar    }
8655682Smarkm    if(params->mask & KADM5_CONFIG_ADMIN_SERVER)
8755682Smarkm	(*ctx)->admin_server = strdup(params->admin_server);
8855682Smarkm    else {
8955682Smarkm	char **hostlist;
9055682Smarkm
9155682Smarkm	ret = krb5_get_krb_admin_hst (context, &(*ctx)->realm, &hostlist);
92127808Snectar	if (ret) {
93127808Snectar	    free((*ctx)->realm);
94127808Snectar	    free(*ctx);
9555682Smarkm	    return ret;
96127808Snectar	}
9755682Smarkm	(*ctx)->admin_server = strdup(*hostlist);
9855682Smarkm	krb5_free_krbhst (context, hostlist);
9955682Smarkm    }
10055682Smarkm
101127808Snectar    if ((*ctx)->admin_server == NULL) {
102127808Snectar	free((*ctx)->realm);
103127808Snectar	free(*ctx);
104178825Sdfr	return ENOMEM;
105127808Snectar    }
10655682Smarkm    colon = strchr ((*ctx)->admin_server, ':');
10755682Smarkm    if (colon != NULL)
10855682Smarkm	*colon++ = '\0';
10955682Smarkm
11055682Smarkm    (*ctx)->kadmind_port = 0;
11155682Smarkm
11255682Smarkm    if(params->mask & KADM5_CONFIG_KADMIND_PORT)
11355682Smarkm	(*ctx)->kadmind_port = params->kadmind_port;
11455682Smarkm    else if (colon != NULL) {
11555682Smarkm	char *end;
11655682Smarkm
11755682Smarkm	(*ctx)->kadmind_port = htons(strtol (colon, &end, 0));
11855682Smarkm    }
11955682Smarkm    if ((*ctx)->kadmind_port == 0)
12055682Smarkm	(*ctx)->kadmind_port = krb5_getportbyname (context, "kerberos-adm",
12155682Smarkm						   "tcp", 749);
12255682Smarkm    return 0;
12355682Smarkm}
12455682Smarkm
12555682Smarkmstatic krb5_error_code
12655682Smarkmget_kadm_ticket(krb5_context context,
12755682Smarkm		krb5_ccache id,
12855682Smarkm		krb5_principal client,
12955682Smarkm		const char *server_name)
13055682Smarkm{
13155682Smarkm    krb5_error_code ret;
13255682Smarkm    krb5_creds in, *out;
13355682Smarkm
13455682Smarkm    memset(&in, 0, sizeof(in));
13555682Smarkm    in.client = client;
13655682Smarkm    ret = krb5_parse_name(context, server_name, &in.server);
13755682Smarkm    if(ret)
13855682Smarkm	return ret;
13955682Smarkm    ret = krb5_get_credentials(context, 0, id, &in, &out);
14055682Smarkm    if(ret == 0)
14155682Smarkm	krb5_free_creds(context, out);
14255682Smarkm    krb5_free_principal(context, in.server);
14355682Smarkm    return ret;
14455682Smarkm}
14555682Smarkm
14655682Smarkmstatic krb5_error_code
14755682Smarkmget_new_cache(krb5_context context,
14855682Smarkm	      krb5_principal client,
14955682Smarkm	      const char *password,
15055682Smarkm	      krb5_prompter_fct prompter,
15155682Smarkm	      const char *keytab,
15255682Smarkm	      const char *server_name,
15355682Smarkm	      krb5_ccache *ret_cache)
15455682Smarkm{
15555682Smarkm    krb5_error_code ret;
15655682Smarkm    krb5_creds cred;
157178825Sdfr    krb5_get_init_creds_opt *opt;
15855682Smarkm    krb5_ccache id;
15955682Smarkm
160178825Sdfr    ret = krb5_get_init_creds_opt_alloc (context, &opt);
161178825Sdfr    if (ret)
162178825Sdfr	return ret;
16390926Snectar
16490926Snectar    krb5_get_init_creds_opt_set_default_flags(context, "kadmin",
16590926Snectar					      krb5_principal_get_realm(context,
16690926Snectar								       client),
167178825Sdfr					      opt);
16890926Snectar
16990926Snectar
170178825Sdfr    krb5_get_init_creds_opt_set_forwardable (opt, FALSE);
171178825Sdfr    krb5_get_init_creds_opt_set_proxiable (opt, FALSE);
17257416Smarkm
17355682Smarkm    if(password == NULL && prompter == NULL) {
17455682Smarkm	krb5_keytab kt;
17555682Smarkm	if(keytab == NULL)
17655682Smarkm	    ret = krb5_kt_default(context, &kt);
17755682Smarkm	else
17855682Smarkm	    ret = krb5_kt_resolve(context, keytab, &kt);
179178825Sdfr	if(ret) {
180178825Sdfr	    krb5_get_init_creds_opt_free(context, opt);
18155682Smarkm	    return ret;
182178825Sdfr	}
18355682Smarkm	ret = krb5_get_init_creds_keytab (context,
18455682Smarkm					  &cred,
18555682Smarkm					  client,
18655682Smarkm					  kt,
18755682Smarkm					  0,
18855682Smarkm					  server_name,
189178825Sdfr					  opt);
19055682Smarkm	krb5_kt_close(context, kt);
19155682Smarkm    } else {
19255682Smarkm	ret = krb5_get_init_creds_password (context,
19355682Smarkm					    &cred,
19455682Smarkm					    client,
19555682Smarkm					    password,
19655682Smarkm					    prompter,
19755682Smarkm					    NULL,
19855682Smarkm					    0,
19955682Smarkm					    server_name,
200178825Sdfr					    opt);
20155682Smarkm    }
202178825Sdfr    krb5_get_init_creds_opt_free(context, opt);
20355682Smarkm    switch(ret){
20455682Smarkm    case 0:
20555682Smarkm	break;
20655682Smarkm    case KRB5_LIBOS_PWDINTR:	/* don't print anything if it was just C-c:ed */
20755682Smarkm    case KRB5KRB_AP_ERR_BAD_INTEGRITY:
20855682Smarkm    case KRB5KRB_AP_ERR_MODIFIED:
20955682Smarkm	return KADM5_BAD_PASSWORD;
21055682Smarkm    default:
21155682Smarkm	return ret;
21255682Smarkm    }
21355682Smarkm    ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id);
21455682Smarkm    if(ret)
21555682Smarkm	return ret;
21655682Smarkm    ret = krb5_cc_initialize (context, id, cred.client);
21755682Smarkm    if (ret)
21855682Smarkm	return ret;
21955682Smarkm    ret = krb5_cc_store_cred (context, id, &cred);
22055682Smarkm    if (ret)
22155682Smarkm	return ret;
222178825Sdfr    krb5_free_cred_contents (context, &cred);
22355682Smarkm    *ret_cache = id;
22455682Smarkm    return 0;
22555682Smarkm}
22655682Smarkm
227178825Sdfr/*
228178825Sdfr * Check the credential cache `id� to figure out what principal to use
229178825Sdfr * when talking to the kadmind. If there is a initial kadmin/admin@
230178825Sdfr * credential in the cache, use that client principal. Otherwise, use
231178825Sdfr * the client principals first component and add /admin to the
232178825Sdfr * principal.
233178825Sdfr */
234178825Sdfr
23555682Smarkmstatic krb5_error_code
236178825Sdfrget_cache_principal(krb5_context context,
237178825Sdfr		    krb5_ccache *id,
238178825Sdfr		    krb5_principal *client)
23955682Smarkm{
24055682Smarkm    krb5_error_code ret;
241178825Sdfr    const char *name, *inst;
242178825Sdfr    krb5_principal p1, p2;
243178825Sdfr
244178825Sdfr    ret = krb5_cc_default(context, id);
245178825Sdfr    if(ret) {
246178825Sdfr	*id = NULL;
247178825Sdfr	return ret;
248178825Sdfr    }
249178825Sdfr
250178825Sdfr    ret = krb5_cc_get_principal(context, *id, &p1);
251178825Sdfr    if(ret) {
252178825Sdfr	krb5_cc_close(context, *id);
253178825Sdfr	*id = NULL;
254178825Sdfr	return ret;
255178825Sdfr    }
256178825Sdfr
257178825Sdfr    ret = krb5_make_principal(context, &p2, NULL,
258178825Sdfr			      "kadmin", "admin", NULL);
259178825Sdfr    if (ret) {
260178825Sdfr	krb5_cc_close(context, *id);
261178825Sdfr	*id = NULL;
262178825Sdfr	krb5_free_principal(context, p1);
263178825Sdfr	return ret;
264178825Sdfr    }
265178825Sdfr
266178825Sdfr    {
267178825Sdfr	krb5_creds in, *out;
268178825Sdfr	krb5_kdc_flags flags;
269178825Sdfr
270178825Sdfr	flags.i = 0;
271178825Sdfr	memset(&in, 0, sizeof(in));
272178825Sdfr
273178825Sdfr	in.client = p1;
274178825Sdfr	in.server = p2;
275178825Sdfr
276178825Sdfr	/* check for initial ticket kadmin/admin */
277178825Sdfr	ret = krb5_get_credentials_with_flags(context, KRB5_GC_CACHED, flags,
278178825Sdfr					      *id, &in, &out);
279178825Sdfr	krb5_free_principal(context, p2);
280178825Sdfr	if (ret == 0) {
281178825Sdfr	    if (out->flags.b.initial) {
282178825Sdfr		*client = p1;
283178825Sdfr		krb5_free_creds(context, out);
284178825Sdfr		return 0;
285178825Sdfr	    }
286178825Sdfr	    krb5_free_creds(context, out);
287178825Sdfr	}
288178825Sdfr    }
289178825Sdfr    krb5_cc_close(context, *id);
290178825Sdfr    *id = NULL;
291178825Sdfr
292178825Sdfr    name = krb5_principal_get_comp_string(context, p1, 0);
293178825Sdfr    inst = krb5_principal_get_comp_string(context, p1, 1);
294178825Sdfr    if(inst == NULL || strcmp(inst, "admin") != 0) {
295178825Sdfr	ret = krb5_make_principal(context, &p2, NULL, name, "admin", NULL);
296178825Sdfr	krb5_free_principal(context, p1);
297178825Sdfr	if(ret != 0)
298178825Sdfr	    return ret;
299178825Sdfr
300178825Sdfr	*client = p2;
301178825Sdfr	return 0;
302178825Sdfr    }
303178825Sdfr
304178825Sdfr    *client = p1;
305178825Sdfr
306178825Sdfr    return 0;
307178825Sdfr}
308178825Sdfr
309178825Sdfrkrb5_error_code
310178825Sdfr_kadm5_c_get_cred_cache(krb5_context context,
311178825Sdfr			const char *client_name,
312178825Sdfr			const char *server_name,
313178825Sdfr			const char *password,
314178825Sdfr			krb5_prompter_fct prompter,
315178825Sdfr			const char *keytab,
316178825Sdfr			krb5_ccache ccache,
317178825Sdfr			krb5_ccache *ret_cache)
318178825Sdfr{
319178825Sdfr    krb5_error_code ret;
32055682Smarkm    krb5_ccache id = NULL;
32155682Smarkm    krb5_principal default_client = NULL, client = NULL;
32255682Smarkm
32355682Smarkm    /* treat empty password as NULL */
32455682Smarkm    if(password && *password == '\0')
32555682Smarkm	password = NULL;
32655682Smarkm    if(server_name == NULL)
32755682Smarkm	server_name = KADM5_ADMIN_SERVICE;
32855682Smarkm
32955682Smarkm    if(client_name != NULL) {
33055682Smarkm	ret = krb5_parse_name(context, client_name, &client);
33155682Smarkm	if(ret)
33255682Smarkm	    return ret;
33355682Smarkm    }
33455682Smarkm
335178825Sdfr    if(ccache != NULL) {
336178825Sdfr	id = ccache;
337178825Sdfr	ret = krb5_cc_get_principal(context, id, &client);
338178825Sdfr	if(ret)
339178825Sdfr	    return ret;
340178825Sdfr    } else {
34155682Smarkm	/* get principal from default cache, ok if this doesn't work */
342102644Snectar
343178825Sdfr	ret = get_cache_principal(context, &id, &default_client);
344178825Sdfr	if (ret) {
345178825Sdfr	    /*
346178825Sdfr	     * No client was specified by the caller and we cannot
347178825Sdfr	     * determine the client from a credentials cache.
348102644Snectar	     */
34955682Smarkm	    const char *user;
35055682Smarkm
35155682Smarkm	    user = get_default_username ();
35255682Smarkm
353178825Sdfr	    if(user == NULL) {
354178825Sdfr		krb5_set_error_string(context, "Unable to find local user name");
35555682Smarkm		return KADM5_FAILURE;
356178825Sdfr	    }
357178825Sdfr	    ret = krb5_make_principal(context, &default_client,
35855682Smarkm				      NULL, user, "admin", NULL);
35955682Smarkm	    if(ret)
36055682Smarkm		return ret;
36155682Smarkm	}
362178825Sdfr    }
363178825Sdfr
364178825Sdfr
365178825Sdfr    /*
366178825Sdfr     * No client was specified by the caller, but we have a client
367178825Sdfr     * from the default credentials cache.
368178825Sdfr     */
369178825Sdfr    if (client == NULL && default_client != NULL)
370178825Sdfr	client = default_client;
371178825Sdfr
37255682Smarkm
37355682Smarkm    if(id && (default_client == NULL ||
37455682Smarkm	      krb5_principal_compare(context, client, default_client))) {
37555682Smarkm	ret = get_kadm_ticket(context, id, client, server_name);
37655682Smarkm	if(ret == 0) {
37755682Smarkm	    *ret_cache = id;
37855682Smarkm	    krb5_free_principal(context, default_client);
37955682Smarkm	    if (default_client != client)
38055682Smarkm		krb5_free_principal(context, client);
38155682Smarkm	    return 0;
38255682Smarkm	}
38355682Smarkm	if(ccache != NULL)
38455682Smarkm	    /* couldn't get ticket from cache */
38555682Smarkm	    return -1;
38655682Smarkm    }
38755682Smarkm    /* get creds via AS request */
388178825Sdfr    if(id && (id != ccache))
38955682Smarkm	krb5_cc_close(context, id);
39055682Smarkm    if (client != default_client)
39155682Smarkm	krb5_free_principal(context, default_client);
39255682Smarkm
39355682Smarkm    ret = get_new_cache(context, client, password, prompter, keytab,
39455682Smarkm			server_name, ret_cache);
39555682Smarkm    krb5_free_principal(context, client);
39655682Smarkm    return ret;
39755682Smarkm}
39855682Smarkm
39972445Sassarstatic kadm5_ret_t
40072445Sassarkadm_connect(kadm5_client_context *ctx)
40155682Smarkm{
40255682Smarkm    kadm5_ret_t ret;
40355682Smarkm    krb5_principal server;
40455682Smarkm    krb5_ccache cc;
40555682Smarkm    int s;
40655682Smarkm    struct addrinfo *ai, *a;
40755682Smarkm    struct addrinfo hints;
40855682Smarkm    int error;
40955682Smarkm    char portstr[NI_MAXSERV];
41055682Smarkm    char *hostname, *slash;
411120945Snectar    char *service_name;
41272445Sassar    krb5_context context = ctx->context;
41355682Smarkm
41455682Smarkm    memset (&hints, 0, sizeof(hints));
41555682Smarkm    hints.ai_socktype = SOCK_STREAM;
41655682Smarkm    hints.ai_protocol = IPPROTO_TCP;
41772445Sassar
41855682Smarkm    snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port));
41955682Smarkm
42055682Smarkm    hostname = ctx->admin_server;
42155682Smarkm    slash = strchr (hostname, '/');
42255682Smarkm    if (slash != NULL)
42355682Smarkm	hostname = slash + 1;
42455682Smarkm
42555682Smarkm    error = getaddrinfo (hostname, portstr, &hints, &ai);
426178825Sdfr    if (error) {
427178825Sdfr	krb5_clear_error_string(context);
42855682Smarkm	return KADM5_BAD_SERVER_NAME;
429178825Sdfr    }
43055682Smarkm
43155682Smarkm    for (a = ai; a != NULL; a = a->ai_next) {
43255682Smarkm	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
43355682Smarkm	if (s < 0)
43455682Smarkm	    continue;
43555682Smarkm	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
436178825Sdfr	    krb5_clear_error_string(context);
43755682Smarkm	    krb5_warn (context, errno, "connect(%s)", hostname);
43855682Smarkm	    close (s);
43955682Smarkm	    continue;
44055682Smarkm	}
44155682Smarkm	break;
44255682Smarkm    }
44355682Smarkm    if (a == NULL) {
44455682Smarkm	freeaddrinfo (ai);
445178825Sdfr	krb5_clear_error_string(context);
44655682Smarkm	krb5_warnx (context, "failed to contact %s", hostname);
44755682Smarkm	return KADM5_FAILURE;
44855682Smarkm    }
449178825Sdfr    ret = _kadm5_c_get_cred_cache(context,
450178825Sdfr				  ctx->client_name,
451178825Sdfr				  ctx->service_name,
452178825Sdfr				  NULL, ctx->prompter, ctx->keytab,
453178825Sdfr				  ctx->ccache, &cc);
45455682Smarkm
45555682Smarkm    if(ret) {
45655682Smarkm	freeaddrinfo (ai);
45755682Smarkm	close(s);
45855682Smarkm	return ret;
45955682Smarkm    }
460120945Snectar
461120945Snectar    if (ctx->realm)
462120945Snectar	asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, ctx->realm);
463120945Snectar    else
464120945Snectar	asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE);
465120945Snectar
466120945Snectar    if (service_name == NULL) {
467120945Snectar	freeaddrinfo (ai);
468120945Snectar	close(s);
469178825Sdfr	krb5_clear_error_string(context);
470120945Snectar	return ENOMEM;
471120945Snectar    }
472120945Snectar
473120945Snectar    ret = krb5_parse_name(context, service_name, &server);
474120945Snectar    free(service_name);
47555682Smarkm    if(ret) {
47655682Smarkm	freeaddrinfo (ai);
47772445Sassar	if(ctx->ccache == NULL)
47855682Smarkm	    krb5_cc_close(context, cc);
47955682Smarkm	close(s);
48055682Smarkm	return ret;
48155682Smarkm    }
48255682Smarkm    ctx->ac = NULL;
48355682Smarkm
48455682Smarkm    ret = krb5_sendauth(context, &ctx->ac, &s,
48555682Smarkm			KADMIN_APPL_VERSION, NULL,
48655682Smarkm			server, AP_OPTS_MUTUAL_REQUIRED,
48755682Smarkm			NULL, NULL, cc, NULL, NULL, NULL);
48855682Smarkm    if(ret == 0) {
48972445Sassar	krb5_data params;
49090926Snectar	kadm5_config_params p;
49190926Snectar	memset(&p, 0, sizeof(p));
49290926Snectar	if(ctx->realm) {
49390926Snectar	    p.mask |= KADM5_CONFIG_REALM;
49490926Snectar	    p.realm = ctx->realm;
49590926Snectar	}
49690926Snectar	ret = _kadm5_marshal_params(context, &p, &params);
49755682Smarkm
49872445Sassar	ret = krb5_write_priv_message(context, ctx->ac, &s, &params);
49955682Smarkm	krb5_data_free(&params);
50072445Sassar	if(ret) {
50172445Sassar	    freeaddrinfo (ai);
50272445Sassar	    close(s);
50372445Sassar	    if(ctx->ccache == NULL)
50472445Sassar		krb5_cc_close(context, cc);
50572445Sassar	    return ret;
50672445Sassar	}
50755682Smarkm    } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) {
50855682Smarkm	close(s);
50955682Smarkm
51055682Smarkm	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
51155682Smarkm	if (s < 0) {
51255682Smarkm	    freeaddrinfo (ai);
513178825Sdfr	    krb5_clear_error_string(context);
51455682Smarkm	    return errno;
51555682Smarkm	}
51655682Smarkm	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
51755682Smarkm	    close (s);
51855682Smarkm	    freeaddrinfo (ai);
519178825Sdfr	    krb5_clear_error_string(context);
52055682Smarkm	    return errno;
52155682Smarkm	}
52255682Smarkm	ret = krb5_sendauth(context, &ctx->ac, &s,
52355682Smarkm			    KADMIN_OLD_APPL_VERSION, NULL,
52455682Smarkm			    server, AP_OPTS_MUTUAL_REQUIRED,
52555682Smarkm			    NULL, NULL, cc, NULL, NULL, NULL);
52655682Smarkm    }
52755682Smarkm    freeaddrinfo (ai);
52855682Smarkm    if(ret) {
52955682Smarkm	close(s);
53055682Smarkm	return ret;
53155682Smarkm    }
53255682Smarkm
53355682Smarkm    krb5_free_principal(context, server);
53472445Sassar    if(ctx->ccache == NULL)
53555682Smarkm	krb5_cc_close(context, cc);
53655682Smarkm    ctx->sock = s;
53772445Sassar
53872445Sassar    return 0;
53972445Sassar}
54072445Sassar
54172445Sassarkadm5_ret_t
54272445Sassar_kadm5_connect(void *handle)
54372445Sassar{
54472445Sassar    kadm5_client_context *ctx = handle;
54572445Sassar    if(ctx->sock == -1)
54672445Sassar	return kadm_connect(ctx);
54772445Sassar    return 0;
54872445Sassar}
54972445Sassar
55072445Sassarstatic kadm5_ret_t
55172445Sassarkadm5_c_init_with_context(krb5_context context,
55272445Sassar			  const char *client_name,
55372445Sassar			  const char *password,
55472445Sassar			  krb5_prompter_fct prompter,
55572445Sassar			  const char *keytab,
55672445Sassar			  krb5_ccache ccache,
55772445Sassar			  const char *service_name,
55872445Sassar			  kadm5_config_params *realm_params,
55972445Sassar			  unsigned long struct_version,
56072445Sassar			  unsigned long api_version,
56172445Sassar			  void **server_handle)
56272445Sassar{
56372445Sassar    kadm5_ret_t ret;
56472445Sassar    kadm5_client_context *ctx;
56572445Sassar    krb5_ccache cc;
56672445Sassar
56772445Sassar    ret = _kadm5_c_init_context(&ctx, realm_params, context);
56872445Sassar    if(ret)
56972445Sassar	return ret;
57072445Sassar
57172445Sassar    if(password != NULL && *password != '\0') {
572178825Sdfr	ret = _kadm5_c_get_cred_cache(context,
573178825Sdfr				      client_name,
574178825Sdfr				      service_name,
575178825Sdfr				      password, prompter, keytab, ccache, &cc);
57672445Sassar	if(ret)
57772445Sassar	    return ret; /* XXX */
57872445Sassar	ccache = cc;
57972445Sassar    }
58072445Sassar
58172445Sassar
58272445Sassar    if (client_name != NULL)
58372445Sassar	ctx->client_name = strdup(client_name);
58472445Sassar    else
58572445Sassar	ctx->client_name = NULL;
58672445Sassar    if (service_name != NULL)
58772445Sassar	ctx->service_name = strdup(service_name);
58872445Sassar    else
58972445Sassar	ctx->service_name = NULL;
59072445Sassar    ctx->prompter = prompter;
59172445Sassar    ctx->keytab = keytab;
59272445Sassar    ctx->ccache = ccache;
59390926Snectar    /* maybe we should copy the params here */
59472445Sassar    ctx->sock = -1;
59572445Sassar
59655682Smarkm    *server_handle = ctx;
59755682Smarkm    return 0;
59855682Smarkm}
59955682Smarkm
60055682Smarkmstatic kadm5_ret_t
60155682Smarkminit_context(const char *client_name,
60255682Smarkm	     const char *password,
60355682Smarkm	     krb5_prompter_fct prompter,
60455682Smarkm	     const char *keytab,
60555682Smarkm	     krb5_ccache ccache,
60655682Smarkm	     const char *service_name,
60755682Smarkm	     kadm5_config_params *realm_params,
60855682Smarkm	     unsigned long struct_version,
60955682Smarkm	     unsigned long api_version,
61055682Smarkm	     void **server_handle)
61155682Smarkm{
61255682Smarkm    krb5_context context;
61355682Smarkm    kadm5_ret_t ret;
61455682Smarkm    kadm5_server_context *ctx;
61555682Smarkm
61672445Sassar    ret = krb5_init_context(&context);
61772445Sassar    if (ret)
61872445Sassar	return ret;
61955682Smarkm    ret = kadm5_c_init_with_context(context,
62055682Smarkm				    client_name,
62155682Smarkm				    password,
62255682Smarkm				    prompter,
62355682Smarkm				    keytab,
62455682Smarkm				    ccache,
62555682Smarkm				    service_name,
62655682Smarkm				    realm_params,
62755682Smarkm				    struct_version,
62855682Smarkm				    api_version,
62955682Smarkm				    server_handle);
63055682Smarkm    if(ret){
63155682Smarkm	krb5_free_context(context);
63255682Smarkm	return ret;
63355682Smarkm    }
63455682Smarkm    ctx = *server_handle;
63555682Smarkm    ctx->my_context = 1;
63655682Smarkm    return 0;
63755682Smarkm}
63855682Smarkm
63955682Smarkmkadm5_ret_t
64055682Smarkmkadm5_c_init_with_password_ctx(krb5_context context,
64155682Smarkm			       const char *client_name,
64255682Smarkm			       const char *password,
64355682Smarkm			       const char *service_name,
64455682Smarkm			       kadm5_config_params *realm_params,
64555682Smarkm			       unsigned long struct_version,
64655682Smarkm			       unsigned long api_version,
64755682Smarkm			       void **server_handle)
64855682Smarkm{
64955682Smarkm    return kadm5_c_init_with_context(context,
65055682Smarkm				     client_name,
65155682Smarkm				     password,
65255682Smarkm				     krb5_prompter_posix,
65355682Smarkm				     NULL,
65455682Smarkm				     NULL,
65555682Smarkm				     service_name,
65655682Smarkm				     realm_params,
65755682Smarkm				     struct_version,
65855682Smarkm				     api_version,
65955682Smarkm				     server_handle);
66055682Smarkm}
66155682Smarkm
66255682Smarkmkadm5_ret_t
66355682Smarkmkadm5_c_init_with_password(const char *client_name,
66455682Smarkm			   const char *password,
66555682Smarkm			   const char *service_name,
66655682Smarkm			   kadm5_config_params *realm_params,
66755682Smarkm			   unsigned long struct_version,
66855682Smarkm			   unsigned long api_version,
66955682Smarkm			   void **server_handle)
67055682Smarkm{
67155682Smarkm    return init_context(client_name,
67255682Smarkm			password,
67355682Smarkm			krb5_prompter_posix,
67455682Smarkm			NULL,
67555682Smarkm			NULL,
67655682Smarkm			service_name,
67755682Smarkm			realm_params,
67855682Smarkm			struct_version,
67955682Smarkm			api_version,
68055682Smarkm			server_handle);
68155682Smarkm}
68255682Smarkm
68355682Smarkmkadm5_ret_t
68455682Smarkmkadm5_c_init_with_skey_ctx(krb5_context context,
68555682Smarkm			   const char *client_name,
68655682Smarkm			   const char *keytab,
68755682Smarkm			   const char *service_name,
68855682Smarkm			   kadm5_config_params *realm_params,
68955682Smarkm			   unsigned long struct_version,
69055682Smarkm			   unsigned long api_version,
69155682Smarkm			   void **server_handle)
69255682Smarkm{
69355682Smarkm    return kadm5_c_init_with_context(context,
69455682Smarkm				     client_name,
69555682Smarkm				     NULL,
69655682Smarkm				     NULL,
69755682Smarkm				     keytab,
69855682Smarkm				     NULL,
69955682Smarkm				     service_name,
70055682Smarkm				     realm_params,
70155682Smarkm				     struct_version,
70255682Smarkm				     api_version,
70355682Smarkm				     server_handle);
70455682Smarkm}
70555682Smarkm
70655682Smarkm
70755682Smarkmkadm5_ret_t
70855682Smarkmkadm5_c_init_with_skey(const char *client_name,
70955682Smarkm		     const char *keytab,
71055682Smarkm		     const char *service_name,
71155682Smarkm		     kadm5_config_params *realm_params,
71255682Smarkm		     unsigned long struct_version,
71355682Smarkm		     unsigned long api_version,
71455682Smarkm		     void **server_handle)
71555682Smarkm{
71655682Smarkm    return init_context(client_name,
71755682Smarkm			NULL,
71855682Smarkm			NULL,
71955682Smarkm			keytab,
72055682Smarkm			NULL,
72155682Smarkm			service_name,
72255682Smarkm			realm_params,
72355682Smarkm			struct_version,
72455682Smarkm			api_version,
72555682Smarkm			server_handle);
72655682Smarkm}
72755682Smarkm
72855682Smarkmkadm5_ret_t
72955682Smarkmkadm5_c_init_with_creds_ctx(krb5_context context,
73055682Smarkm			    const char *client_name,
73155682Smarkm			    krb5_ccache ccache,
73255682Smarkm			    const char *service_name,
73355682Smarkm			    kadm5_config_params *realm_params,
73455682Smarkm			    unsigned long struct_version,
73555682Smarkm			    unsigned long api_version,
73655682Smarkm			    void **server_handle)
73755682Smarkm{
73855682Smarkm    return kadm5_c_init_with_context(context,
73955682Smarkm				     client_name,
74055682Smarkm				     NULL,
74155682Smarkm				     NULL,
74255682Smarkm				     NULL,
74355682Smarkm				     ccache,
74455682Smarkm				     service_name,
74555682Smarkm				     realm_params,
74655682Smarkm				     struct_version,
74755682Smarkm				     api_version,
74855682Smarkm				     server_handle);
74955682Smarkm}
75055682Smarkm
75155682Smarkmkadm5_ret_t
75255682Smarkmkadm5_c_init_with_creds(const char *client_name,
75355682Smarkm			krb5_ccache ccache,
75455682Smarkm			const char *service_name,
75555682Smarkm			kadm5_config_params *realm_params,
75655682Smarkm			unsigned long struct_version,
75755682Smarkm			unsigned long api_version,
75855682Smarkm			void **server_handle)
75955682Smarkm{
76055682Smarkm    return init_context(client_name,
76155682Smarkm			NULL,
76255682Smarkm			NULL,
76355682Smarkm			NULL,
76455682Smarkm			ccache,
76555682Smarkm			service_name,
76655682Smarkm			realm_params,
76755682Smarkm			struct_version,
76855682Smarkm			api_version,
76955682Smarkm			server_handle);
77055682Smarkm}
77155682Smarkm
77255682Smarkm#if 0
77355682Smarkmkadm5_ret_t
77455682Smarkmkadm5_init(char *client_name, char *pass,
77555682Smarkm	   char *service_name,
77655682Smarkm	   kadm5_config_params *realm_params,
77755682Smarkm	   unsigned long struct_version,
77855682Smarkm	   unsigned long api_version,
77955682Smarkm	   void **server_handle)
78055682Smarkm{
78155682Smarkm}
78255682Smarkm#endif
78355682Smarkm
784