155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2006 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "kadm5_locl.h"
3555682Smarkm#include <sys/types.h>
36233294Sstas#ifdef HAVE_SYS_SOCKET_H
3755682Smarkm#include <sys/socket.h>
38233294Sstas#endif
39233294Sstas#ifdef HAVE_NETINET_IN_H
4055682Smarkm#include <netinet/in.h>
41233294Sstas#endif
42233294Sstas#ifdef HAVE_NETDB_H
4355682Smarkm#include <netdb.h>
44233294Sstas#endif
4555682Smarkm
46233294SstasRCSID("$Id$");
4755682Smarkm
4855682Smarkmstatic void
4955682Smarkmset_funcs(kadm5_client_context *c)
5055682Smarkm{
5155682Smarkm#define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F
5255682Smarkm    SET(c, chpass_principal);
5372445Sassar    SET(c, chpass_principal_with_key);
5455682Smarkm    SET(c, create_principal);
5555682Smarkm    SET(c, delete_principal);
5655682Smarkm    SET(c, destroy);
5755682Smarkm    SET(c, flush);
5855682Smarkm    SET(c, get_principal);
5955682Smarkm    SET(c, get_principals);
6055682Smarkm    SET(c, get_privs);
6155682Smarkm    SET(c, modify_principal);
6255682Smarkm    SET(c, randkey_principal);
6355682Smarkm    SET(c, rename_principal);
6455682Smarkm}
6555682Smarkm
6655682Smarkmkadm5_ret_t
67233294Sstas_kadm5_c_init_context(kadm5_client_context **ctx,
6855682Smarkm		      kadm5_config_params *params,
6955682Smarkm		      krb5_context context)
7055682Smarkm{
7155682Smarkm    krb5_error_code ret;
7255682Smarkm    char *colon;
7355682Smarkm
7455682Smarkm    *ctx = malloc(sizeof(**ctx));
7555682Smarkm    if(*ctx == NULL)
7655682Smarkm	return ENOMEM;
7755682Smarkm    memset(*ctx, 0, sizeof(**ctx));
7855682Smarkm    krb5_add_et_list (context, initialize_kadm5_error_table_r);
7955682Smarkm    set_funcs(*ctx);
8055682Smarkm    (*ctx)->context = context;
81127808Snectar    if(params->mask & KADM5_CONFIG_REALM) {
82127808Snectar	ret = 0;
8355682Smarkm	(*ctx)->realm = strdup(params->realm);
84127808Snectar	if ((*ctx)->realm == NULL)
85127808Snectar	    ret = ENOMEM;
86127808Snectar    } else
87127808Snectar	ret = krb5_get_default_realm((*ctx)->context, &(*ctx)->realm);
88127808Snectar    if (ret) {
89127808Snectar	free(*ctx);
90127808Snectar	return ret;
91127808Snectar    }
9255682Smarkm    if(params->mask & KADM5_CONFIG_ADMIN_SERVER)
9355682Smarkm	(*ctx)->admin_server = strdup(params->admin_server);
9455682Smarkm    else {
9555682Smarkm	char **hostlist;
9655682Smarkm
9755682Smarkm	ret = krb5_get_krb_admin_hst (context, &(*ctx)->realm, &hostlist);
98127808Snectar	if (ret) {
99127808Snectar	    free((*ctx)->realm);
100127808Snectar	    free(*ctx);
10155682Smarkm	    return ret;
102127808Snectar	}
10355682Smarkm	(*ctx)->admin_server = strdup(*hostlist);
10455682Smarkm	krb5_free_krbhst (context, hostlist);
10555682Smarkm    }
10655682Smarkm
107127808Snectar    if ((*ctx)->admin_server == NULL) {
108127808Snectar	free((*ctx)->realm);
109127808Snectar	free(*ctx);
110178825Sdfr	return ENOMEM;
111127808Snectar    }
11255682Smarkm    colon = strchr ((*ctx)->admin_server, ':');
11355682Smarkm    if (colon != NULL)
11455682Smarkm	*colon++ = '\0';
11555682Smarkm
11655682Smarkm    (*ctx)->kadmind_port = 0;
11755682Smarkm
11855682Smarkm    if(params->mask & KADM5_CONFIG_KADMIND_PORT)
11955682Smarkm	(*ctx)->kadmind_port = params->kadmind_port;
12055682Smarkm    else if (colon != NULL) {
12155682Smarkm	char *end;
12255682Smarkm
12355682Smarkm	(*ctx)->kadmind_port = htons(strtol (colon, &end, 0));
12455682Smarkm    }
12555682Smarkm    if ((*ctx)->kadmind_port == 0)
126233294Sstas	(*ctx)->kadmind_port = krb5_getportbyname (context, "kerberos-adm",
12755682Smarkm						   "tcp", 749);
12855682Smarkm    return 0;
12955682Smarkm}
13055682Smarkm
13155682Smarkmstatic krb5_error_code
13255682Smarkmget_kadm_ticket(krb5_context context,
13355682Smarkm		krb5_ccache id,
13455682Smarkm		krb5_principal client,
13555682Smarkm		const char *server_name)
13655682Smarkm{
13755682Smarkm    krb5_error_code ret;
13855682Smarkm    krb5_creds in, *out;
139233294Sstas
14055682Smarkm    memset(&in, 0, sizeof(in));
14155682Smarkm    in.client = client;
14255682Smarkm    ret = krb5_parse_name(context, server_name, &in.server);
143233294Sstas    if(ret)
14455682Smarkm	return ret;
14555682Smarkm    ret = krb5_get_credentials(context, 0, id, &in, &out);
14655682Smarkm    if(ret == 0)
14755682Smarkm	krb5_free_creds(context, out);
14855682Smarkm    krb5_free_principal(context, in.server);
14955682Smarkm    return ret;
15055682Smarkm}
15155682Smarkm
15255682Smarkmstatic krb5_error_code
15355682Smarkmget_new_cache(krb5_context context,
15455682Smarkm	      krb5_principal client,
15555682Smarkm	      const char *password,
15655682Smarkm	      krb5_prompter_fct prompter,
15755682Smarkm	      const char *keytab,
15855682Smarkm	      const char *server_name,
15955682Smarkm	      krb5_ccache *ret_cache)
16055682Smarkm{
16155682Smarkm    krb5_error_code ret;
16255682Smarkm    krb5_creds cred;
163178825Sdfr    krb5_get_init_creds_opt *opt;
16455682Smarkm    krb5_ccache id;
165233294Sstas
166178825Sdfr    ret = krb5_get_init_creds_opt_alloc (context, &opt);
167178825Sdfr    if (ret)
168178825Sdfr	return ret;
16990926Snectar
170233294Sstas    krb5_get_init_creds_opt_set_default_flags(context, "kadmin",
171233294Sstas					      krb5_principal_get_realm(context,
172233294Sstas								       client),
173178825Sdfr					      opt);
17490926Snectar
17590926Snectar
176178825Sdfr    krb5_get_init_creds_opt_set_forwardable (opt, FALSE);
177178825Sdfr    krb5_get_init_creds_opt_set_proxiable (opt, FALSE);
17857416Smarkm
17955682Smarkm    if(password == NULL && prompter == NULL) {
18055682Smarkm	krb5_keytab kt;
18155682Smarkm	if(keytab == NULL)
18255682Smarkm	    ret = krb5_kt_default(context, &kt);
18355682Smarkm	else
18455682Smarkm	    ret = krb5_kt_resolve(context, keytab, &kt);
185178825Sdfr	if(ret) {
186178825Sdfr	    krb5_get_init_creds_opt_free(context, opt);
18755682Smarkm	    return ret;
188178825Sdfr	}
18955682Smarkm	ret = krb5_get_init_creds_keytab (context,
19055682Smarkm					  &cred,
19155682Smarkm					  client,
19255682Smarkm					  kt,
19355682Smarkm					  0,
19455682Smarkm					  server_name,
195178825Sdfr					  opt);
19655682Smarkm	krb5_kt_close(context, kt);
19755682Smarkm    } else {
19855682Smarkm	ret = krb5_get_init_creds_password (context,
19955682Smarkm					    &cred,
20055682Smarkm					    client,
20155682Smarkm					    password,
20255682Smarkm					    prompter,
20355682Smarkm					    NULL,
20455682Smarkm					    0,
20555682Smarkm					    server_name,
206178825Sdfr					    opt);
20755682Smarkm    }
208178825Sdfr    krb5_get_init_creds_opt_free(context, opt);
20955682Smarkm    switch(ret){
21055682Smarkm    case 0:
21155682Smarkm	break;
21255682Smarkm    case KRB5_LIBOS_PWDINTR:	/* don't print anything if it was just C-c:ed */
21355682Smarkm    case KRB5KRB_AP_ERR_BAD_INTEGRITY:
21455682Smarkm    case KRB5KRB_AP_ERR_MODIFIED:
21555682Smarkm	return KADM5_BAD_PASSWORD;
21655682Smarkm    default:
21755682Smarkm	return ret;
21855682Smarkm    }
219233294Sstas    ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);
22055682Smarkm    if(ret)
22155682Smarkm	return ret;
22255682Smarkm    ret = krb5_cc_initialize (context, id, cred.client);
22355682Smarkm    if (ret)
22455682Smarkm	return ret;
22555682Smarkm    ret = krb5_cc_store_cred (context, id, &cred);
22655682Smarkm    if (ret)
22755682Smarkm	return ret;
228178825Sdfr    krb5_free_cred_contents (context, &cred);
22955682Smarkm    *ret_cache = id;
23055682Smarkm    return 0;
23155682Smarkm}
23255682Smarkm
233178825Sdfr/*
234233294Sstas * Check the credential cache `id�� to figure out what principal to use
235178825Sdfr * when talking to the kadmind. If there is a initial kadmin/admin@
236178825Sdfr * credential in the cache, use that client principal. Otherwise, use
237178825Sdfr * the client principals first component and add /admin to the
238178825Sdfr * principal.
239178825Sdfr */
240178825Sdfr
24155682Smarkmstatic krb5_error_code
242178825Sdfrget_cache_principal(krb5_context context,
243178825Sdfr		    krb5_ccache *id,
244178825Sdfr		    krb5_principal *client)
24555682Smarkm{
24655682Smarkm    krb5_error_code ret;
247178825Sdfr    const char *name, *inst;
248178825Sdfr    krb5_principal p1, p2;
249178825Sdfr
250178825Sdfr    ret = krb5_cc_default(context, id);
251178825Sdfr    if(ret) {
252178825Sdfr	*id = NULL;
253178825Sdfr	return ret;
254178825Sdfr    }
255233294Sstas
256178825Sdfr    ret = krb5_cc_get_principal(context, *id, &p1);
257178825Sdfr    if(ret) {
258178825Sdfr	krb5_cc_close(context, *id);
259178825Sdfr	*id = NULL;
260178825Sdfr	return ret;
261178825Sdfr    }
262178825Sdfr
263233294Sstas    ret = krb5_make_principal(context, &p2, NULL,
264178825Sdfr			      "kadmin", "admin", NULL);
265178825Sdfr    if (ret) {
266178825Sdfr	krb5_cc_close(context, *id);
267178825Sdfr	*id = NULL;
268178825Sdfr	krb5_free_principal(context, p1);
269178825Sdfr	return ret;
270178825Sdfr    }
271178825Sdfr
272178825Sdfr    {
273178825Sdfr	krb5_creds in, *out;
274178825Sdfr	krb5_kdc_flags flags;
275178825Sdfr
276178825Sdfr	flags.i = 0;
277178825Sdfr	memset(&in, 0, sizeof(in));
278178825Sdfr
279178825Sdfr	in.client = p1;
280178825Sdfr	in.server = p2;
281178825Sdfr
282178825Sdfr	/* check for initial ticket kadmin/admin */
283178825Sdfr	ret = krb5_get_credentials_with_flags(context, KRB5_GC_CACHED, flags,
284178825Sdfr					      *id, &in, &out);
285178825Sdfr	krb5_free_principal(context, p2);
286178825Sdfr	if (ret == 0) {
287178825Sdfr	    if (out->flags.b.initial) {
288178825Sdfr		*client = p1;
289178825Sdfr		krb5_free_creds(context, out);
290178825Sdfr		return 0;
291178825Sdfr	    }
292178825Sdfr	    krb5_free_creds(context, out);
293178825Sdfr	}
294178825Sdfr    }
295178825Sdfr    krb5_cc_close(context, *id);
296178825Sdfr    *id = NULL;
297178825Sdfr
298178825Sdfr    name = krb5_principal_get_comp_string(context, p1, 0);
299178825Sdfr    inst = krb5_principal_get_comp_string(context, p1, 1);
300178825Sdfr    if(inst == NULL || strcmp(inst, "admin") != 0) {
301178825Sdfr	ret = krb5_make_principal(context, &p2, NULL, name, "admin", NULL);
302178825Sdfr	krb5_free_principal(context, p1);
303178825Sdfr	if(ret != 0)
304178825Sdfr	    return ret;
305178825Sdfr
306178825Sdfr	*client = p2;
307178825Sdfr	return 0;
308178825Sdfr    }
309178825Sdfr
310178825Sdfr    *client = p1;
311178825Sdfr
312178825Sdfr    return 0;
313178825Sdfr}
314178825Sdfr
315178825Sdfrkrb5_error_code
316178825Sdfr_kadm5_c_get_cred_cache(krb5_context context,
317178825Sdfr			const char *client_name,
318178825Sdfr			const char *server_name,
319178825Sdfr			const char *password,
320178825Sdfr			krb5_prompter_fct prompter,
321178825Sdfr			const char *keytab,
322178825Sdfr			krb5_ccache ccache,
323178825Sdfr			krb5_ccache *ret_cache)
324178825Sdfr{
325178825Sdfr    krb5_error_code ret;
32655682Smarkm    krb5_ccache id = NULL;
32755682Smarkm    krb5_principal default_client = NULL, client = NULL;
328233294Sstas
32955682Smarkm    /* treat empty password as NULL */
33055682Smarkm    if(password && *password == '\0')
33155682Smarkm	password = NULL;
33255682Smarkm    if(server_name == NULL)
33355682Smarkm	server_name = KADM5_ADMIN_SERVICE;
334233294Sstas
33555682Smarkm    if(client_name != NULL) {
33655682Smarkm	ret = krb5_parse_name(context, client_name, &client);
337233294Sstas	if(ret)
33855682Smarkm	    return ret;
33955682Smarkm    }
34055682Smarkm
341178825Sdfr    if(ccache != NULL) {
342178825Sdfr	id = ccache;
343178825Sdfr	ret = krb5_cc_get_principal(context, id, &client);
344178825Sdfr	if(ret)
345178825Sdfr	    return ret;
346178825Sdfr    } else {
34755682Smarkm	/* get principal from default cache, ok if this doesn't work */
348102644Snectar
349178825Sdfr	ret = get_cache_principal(context, &id, &default_client);
350178825Sdfr	if (ret) {
351233294Sstas	    /*
352178825Sdfr	     * No client was specified by the caller and we cannot
353178825Sdfr	     * determine the client from a credentials cache.
354102644Snectar	     */
35555682Smarkm	    const char *user;
35655682Smarkm
35755682Smarkm	    user = get_default_username ();
35855682Smarkm
359178825Sdfr	    if(user == NULL) {
360233294Sstas		krb5_set_error_message(context, KADM5_FAILURE, "Unable to find local user name");
36155682Smarkm		return KADM5_FAILURE;
362178825Sdfr	    }
363233294Sstas	    ret = krb5_make_principal(context, &default_client,
36455682Smarkm				      NULL, user, "admin", NULL);
36555682Smarkm	    if(ret)
36655682Smarkm		return ret;
36755682Smarkm	}
368178825Sdfr    }
369178825Sdfr
370178825Sdfr
371178825Sdfr    /*
372178825Sdfr     * No client was specified by the caller, but we have a client
373178825Sdfr     * from the default credentials cache.
374178825Sdfr     */
375178825Sdfr    if (client == NULL && default_client != NULL)
376178825Sdfr	client = default_client;
377178825Sdfr
378233294Sstas
379233294Sstas    if(id && client && (default_client == NULL ||
380233294Sstas	      krb5_principal_compare(context, client, default_client) != 0)) {
38155682Smarkm	ret = get_kadm_ticket(context, id, client, server_name);
38255682Smarkm	if(ret == 0) {
38355682Smarkm	    *ret_cache = id;
38455682Smarkm	    krb5_free_principal(context, default_client);
38555682Smarkm	    if (default_client != client)
38655682Smarkm		krb5_free_principal(context, client);
38755682Smarkm	    return 0;
38855682Smarkm	}
38955682Smarkm	if(ccache != NULL)
39055682Smarkm	    /* couldn't get ticket from cache */
39155682Smarkm	    return -1;
39255682Smarkm    }
39355682Smarkm    /* get creds via AS request */
394178825Sdfr    if(id && (id != ccache))
39555682Smarkm	krb5_cc_close(context, id);
39655682Smarkm    if (client != default_client)
39755682Smarkm	krb5_free_principal(context, default_client);
39855682Smarkm
399233294Sstas    ret = get_new_cache(context, client, password, prompter, keytab,
40055682Smarkm			server_name, ret_cache);
40155682Smarkm    krb5_free_principal(context, client);
40255682Smarkm    return ret;
40355682Smarkm}
40455682Smarkm
40572445Sassarstatic kadm5_ret_t
40672445Sassarkadm_connect(kadm5_client_context *ctx)
40755682Smarkm{
40855682Smarkm    kadm5_ret_t ret;
40955682Smarkm    krb5_principal server;
41055682Smarkm    krb5_ccache cc;
411233294Sstas    rk_socket_t s = rk_INVALID_SOCKET;
41255682Smarkm    struct addrinfo *ai, *a;
41355682Smarkm    struct addrinfo hints;
41455682Smarkm    int error;
41555682Smarkm    char portstr[NI_MAXSERV];
41655682Smarkm    char *hostname, *slash;
417120945Snectar    char *service_name;
41872445Sassar    krb5_context context = ctx->context;
41955682Smarkm
42055682Smarkm    memset (&hints, 0, sizeof(hints));
42155682Smarkm    hints.ai_socktype = SOCK_STREAM;
42255682Smarkm    hints.ai_protocol = IPPROTO_TCP;
423233294Sstas
42455682Smarkm    snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port));
42555682Smarkm
42655682Smarkm    hostname = ctx->admin_server;
42755682Smarkm    slash = strchr (hostname, '/');
42855682Smarkm    if (slash != NULL)
42955682Smarkm	hostname = slash + 1;
43055682Smarkm
43155682Smarkm    error = getaddrinfo (hostname, portstr, &hints, &ai);
432178825Sdfr    if (error) {
433233294Sstas	krb5_clear_error_message(context);
43455682Smarkm	return KADM5_BAD_SERVER_NAME;
435178825Sdfr    }
436233294Sstas
43755682Smarkm    for (a = ai; a != NULL; a = a->ai_next) {
43855682Smarkm	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
43955682Smarkm	if (s < 0)
44055682Smarkm	    continue;
44155682Smarkm	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
442233294Sstas	    krb5_clear_error_message(context);
44355682Smarkm	    krb5_warn (context, errno, "connect(%s)", hostname);
444233294Sstas	    rk_closesocket (s);
44555682Smarkm	    continue;
44655682Smarkm	}
44755682Smarkm	break;
44855682Smarkm    }
44955682Smarkm    if (a == NULL) {
45055682Smarkm	freeaddrinfo (ai);
451233294Sstas	krb5_clear_error_message(context);
45255682Smarkm	krb5_warnx (context, "failed to contact %s", hostname);
45355682Smarkm	return KADM5_FAILURE;
45455682Smarkm    }
455178825Sdfr    ret = _kadm5_c_get_cred_cache(context,
456233294Sstas				  ctx->client_name,
457233294Sstas				  ctx->service_name,
458233294Sstas				  NULL, ctx->prompter, ctx->keytab,
459178825Sdfr				  ctx->ccache, &cc);
460233294Sstas
46155682Smarkm    if(ret) {
46255682Smarkm	freeaddrinfo (ai);
463233294Sstas	rk_closesocket(s);
46455682Smarkm	return ret;
46555682Smarkm    }
466120945Snectar
467120945Snectar    if (ctx->realm)
468120945Snectar	asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, ctx->realm);
469120945Snectar    else
470120945Snectar	asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE);
471120945Snectar
472120945Snectar    if (service_name == NULL) {
473120945Snectar	freeaddrinfo (ai);
474233294Sstas	rk_closesocket(s);
475233294Sstas	krb5_clear_error_message(context);
476120945Snectar	return ENOMEM;
477120945Snectar    }
478120945Snectar
479120945Snectar    ret = krb5_parse_name(context, service_name, &server);
480120945Snectar    free(service_name);
48155682Smarkm    if(ret) {
48255682Smarkm	freeaddrinfo (ai);
48372445Sassar	if(ctx->ccache == NULL)
48455682Smarkm	    krb5_cc_close(context, cc);
485233294Sstas	rk_closesocket(s);
48655682Smarkm	return ret;
48755682Smarkm    }
48855682Smarkm    ctx->ac = NULL;
48955682Smarkm
490233294Sstas    ret = krb5_sendauth(context, &ctx->ac, &s,
491233294Sstas			KADMIN_APPL_VERSION, NULL,
492233294Sstas			server, AP_OPTS_MUTUAL_REQUIRED,
49355682Smarkm			NULL, NULL, cc, NULL, NULL, NULL);
49455682Smarkm    if(ret == 0) {
49572445Sassar	krb5_data params;
49690926Snectar	kadm5_config_params p;
49790926Snectar	memset(&p, 0, sizeof(p));
49890926Snectar	if(ctx->realm) {
49990926Snectar	    p.mask |= KADM5_CONFIG_REALM;
50090926Snectar	    p.realm = ctx->realm;
50190926Snectar	}
50290926Snectar	ret = _kadm5_marshal_params(context, &p, &params);
503233294Sstas
50472445Sassar	ret = krb5_write_priv_message(context, ctx->ac, &s, &params);
50555682Smarkm	krb5_data_free(&params);
50672445Sassar	if(ret) {
50772445Sassar	    freeaddrinfo (ai);
508233294Sstas	    rk_closesocket(s);
50972445Sassar	    if(ctx->ccache == NULL)
51072445Sassar		krb5_cc_close(context, cc);
51172445Sassar	    return ret;
51272445Sassar	}
51355682Smarkm    } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) {
514233294Sstas	rk_closesocket(s);
51555682Smarkm
51655682Smarkm	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
51755682Smarkm	if (s < 0) {
51855682Smarkm	    freeaddrinfo (ai);
519233294Sstas	    krb5_clear_error_message(context);
52055682Smarkm	    return errno;
52155682Smarkm	}
52255682Smarkm	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
523233294Sstas	    rk_closesocket (s);
52455682Smarkm	    freeaddrinfo (ai);
525233294Sstas	    krb5_clear_error_message(context);
52655682Smarkm	    return errno;
52755682Smarkm	}
528233294Sstas	ret = krb5_sendauth(context, &ctx->ac, &s,
529233294Sstas			    KADMIN_OLD_APPL_VERSION, NULL,
530233294Sstas			    server, AP_OPTS_MUTUAL_REQUIRED,
53155682Smarkm			    NULL, NULL, cc, NULL, NULL, NULL);
53255682Smarkm    }
53355682Smarkm    freeaddrinfo (ai);
53455682Smarkm    if(ret) {
535233294Sstas	rk_closesocket(s);
53655682Smarkm	return ret;
53755682Smarkm    }
538233294Sstas
53955682Smarkm    krb5_free_principal(context, server);
54072445Sassar    if(ctx->ccache == NULL)
54155682Smarkm	krb5_cc_close(context, cc);
54255682Smarkm    ctx->sock = s;
543233294Sstas
54472445Sassar    return 0;
54572445Sassar}
54672445Sassar
54772445Sassarkadm5_ret_t
54872445Sassar_kadm5_connect(void *handle)
54972445Sassar{
55072445Sassar    kadm5_client_context *ctx = handle;
55172445Sassar    if(ctx->sock == -1)
55272445Sassar	return kadm_connect(ctx);
55372445Sassar    return 0;
55472445Sassar}
55572445Sassar
556233294Sstasstatic kadm5_ret_t
55772445Sassarkadm5_c_init_with_context(krb5_context context,
558233294Sstas			  const char *client_name,
55972445Sassar			  const char *password,
56072445Sassar			  krb5_prompter_fct prompter,
56172445Sassar			  const char *keytab,
56272445Sassar			  krb5_ccache ccache,
56372445Sassar			  const char *service_name,
56472445Sassar			  kadm5_config_params *realm_params,
56572445Sassar			  unsigned long struct_version,
56672445Sassar			  unsigned long api_version,
56772445Sassar			  void **server_handle)
56872445Sassar{
56972445Sassar    kadm5_ret_t ret;
57072445Sassar    kadm5_client_context *ctx;
57172445Sassar    krb5_ccache cc;
57272445Sassar
57372445Sassar    ret = _kadm5_c_init_context(&ctx, realm_params, context);
57472445Sassar    if(ret)
57572445Sassar	return ret;
57672445Sassar
57772445Sassar    if(password != NULL && *password != '\0') {
578233294Sstas	ret = _kadm5_c_get_cred_cache(context,
579178825Sdfr				      client_name,
580233294Sstas				      service_name,
581178825Sdfr				      password, prompter, keytab, ccache, &cc);
58272445Sassar	if(ret)
58372445Sassar	    return ret; /* XXX */
58472445Sassar	ccache = cc;
58572445Sassar    }
58672445Sassar
587233294Sstas
58872445Sassar    if (client_name != NULL)
58972445Sassar	ctx->client_name = strdup(client_name);
59072445Sassar    else
59172445Sassar	ctx->client_name = NULL;
59272445Sassar    if (service_name != NULL)
59372445Sassar	ctx->service_name = strdup(service_name);
59472445Sassar    else
59572445Sassar	ctx->service_name = NULL;
59672445Sassar    ctx->prompter = prompter;
59772445Sassar    ctx->keytab = keytab;
59872445Sassar    ctx->ccache = ccache;
59990926Snectar    /* maybe we should copy the params here */
60072445Sassar    ctx->sock = -1;
601233294Sstas
60255682Smarkm    *server_handle = ctx;
60355682Smarkm    return 0;
60455682Smarkm}
60555682Smarkm
606233294Sstasstatic kadm5_ret_t
607233294Sstasinit_context(const char *client_name,
60855682Smarkm	     const char *password,
60955682Smarkm	     krb5_prompter_fct prompter,
61055682Smarkm	     const char *keytab,
61155682Smarkm	     krb5_ccache ccache,
61255682Smarkm	     const char *service_name,
61355682Smarkm	     kadm5_config_params *realm_params,
61455682Smarkm	     unsigned long struct_version,
61555682Smarkm	     unsigned long api_version,
61655682Smarkm	     void **server_handle)
61755682Smarkm{
61855682Smarkm    krb5_context context;
61955682Smarkm    kadm5_ret_t ret;
62055682Smarkm    kadm5_server_context *ctx;
621233294Sstas
62272445Sassar    ret = krb5_init_context(&context);
62372445Sassar    if (ret)
62472445Sassar	return ret;
62555682Smarkm    ret = kadm5_c_init_with_context(context,
62655682Smarkm				    client_name,
62755682Smarkm				    password,
62855682Smarkm				    prompter,
62955682Smarkm				    keytab,
63055682Smarkm				    ccache,
63155682Smarkm				    service_name,
63255682Smarkm				    realm_params,
63355682Smarkm				    struct_version,
63455682Smarkm				    api_version,
63555682Smarkm				    server_handle);
63655682Smarkm    if(ret){
63755682Smarkm	krb5_free_context(context);
63855682Smarkm	return ret;
63955682Smarkm    }
64055682Smarkm    ctx = *server_handle;
64155682Smarkm    ctx->my_context = 1;
64255682Smarkm    return 0;
64355682Smarkm}
64455682Smarkm
645233294Sstaskadm5_ret_t
64655682Smarkmkadm5_c_init_with_password_ctx(krb5_context context,
647233294Sstas			       const char *client_name,
64855682Smarkm			       const char *password,
64955682Smarkm			       const char *service_name,
65055682Smarkm			       kadm5_config_params *realm_params,
65155682Smarkm			       unsigned long struct_version,
65255682Smarkm			       unsigned long api_version,
65355682Smarkm			       void **server_handle)
65455682Smarkm{
65555682Smarkm    return kadm5_c_init_with_context(context,
65655682Smarkm				     client_name,
65755682Smarkm				     password,
65855682Smarkm				     krb5_prompter_posix,
65955682Smarkm				     NULL,
66055682Smarkm				     NULL,
66155682Smarkm				     service_name,
66255682Smarkm				     realm_params,
66355682Smarkm				     struct_version,
66455682Smarkm				     api_version,
66555682Smarkm				     server_handle);
66655682Smarkm}
66755682Smarkm
668233294Sstaskadm5_ret_t
669233294Sstaskadm5_c_init_with_password(const char *client_name,
67055682Smarkm			   const char *password,
67155682Smarkm			   const char *service_name,
67255682Smarkm			   kadm5_config_params *realm_params,
67355682Smarkm			   unsigned long struct_version,
67455682Smarkm			   unsigned long api_version,
67555682Smarkm			   void **server_handle)
67655682Smarkm{
677233294Sstas    return init_context(client_name,
678233294Sstas			password,
67955682Smarkm			krb5_prompter_posix,
68055682Smarkm			NULL,
68155682Smarkm			NULL,
682233294Sstas			service_name,
683233294Sstas			realm_params,
684233294Sstas			struct_version,
685233294Sstas			api_version,
68655682Smarkm			server_handle);
68755682Smarkm}
68855682Smarkm
689233294Sstaskadm5_ret_t
69055682Smarkmkadm5_c_init_with_skey_ctx(krb5_context context,
691233294Sstas			   const char *client_name,
69255682Smarkm			   const char *keytab,
69355682Smarkm			   const char *service_name,
69455682Smarkm			   kadm5_config_params *realm_params,
69555682Smarkm			   unsigned long struct_version,
69655682Smarkm			   unsigned long api_version,
69755682Smarkm			   void **server_handle)
69855682Smarkm{
69955682Smarkm    return kadm5_c_init_with_context(context,
70055682Smarkm				     client_name,
70155682Smarkm				     NULL,
70255682Smarkm				     NULL,
70355682Smarkm				     keytab,
70455682Smarkm				     NULL,
70555682Smarkm				     service_name,
70655682Smarkm				     realm_params,
70755682Smarkm				     struct_version,
70855682Smarkm				     api_version,
70955682Smarkm				     server_handle);
71055682Smarkm}
71155682Smarkm
71255682Smarkm
713233294Sstaskadm5_ret_t
714233294Sstaskadm5_c_init_with_skey(const char *client_name,
71555682Smarkm		     const char *keytab,
71655682Smarkm		     const char *service_name,
71755682Smarkm		     kadm5_config_params *realm_params,
71855682Smarkm		     unsigned long struct_version,
71955682Smarkm		     unsigned long api_version,
72055682Smarkm		     void **server_handle)
72155682Smarkm{
722233294Sstas    return init_context(client_name,
72355682Smarkm			NULL,
72455682Smarkm			NULL,
72555682Smarkm			keytab,
72655682Smarkm			NULL,
727233294Sstas			service_name,
728233294Sstas			realm_params,
729233294Sstas			struct_version,
730233294Sstas			api_version,
73155682Smarkm			server_handle);
73255682Smarkm}
73355682Smarkm
734233294Sstaskadm5_ret_t
73555682Smarkmkadm5_c_init_with_creds_ctx(krb5_context context,
73655682Smarkm			    const char *client_name,
73755682Smarkm			    krb5_ccache ccache,
73855682Smarkm			    const char *service_name,
73955682Smarkm			    kadm5_config_params *realm_params,
74055682Smarkm			    unsigned long struct_version,
74155682Smarkm			    unsigned long api_version,
74255682Smarkm			    void **server_handle)
74355682Smarkm{
74455682Smarkm    return kadm5_c_init_with_context(context,
74555682Smarkm				     client_name,
74655682Smarkm				     NULL,
74755682Smarkm				     NULL,
74855682Smarkm				     NULL,
74955682Smarkm				     ccache,
75055682Smarkm				     service_name,
75155682Smarkm				     realm_params,
75255682Smarkm				     struct_version,
75355682Smarkm				     api_version,
75455682Smarkm				     server_handle);
75555682Smarkm}
75655682Smarkm
757233294Sstaskadm5_ret_t
75855682Smarkmkadm5_c_init_with_creds(const char *client_name,
75955682Smarkm			krb5_ccache ccache,
76055682Smarkm			const char *service_name,
76155682Smarkm			kadm5_config_params *realm_params,
76255682Smarkm			unsigned long struct_version,
76355682Smarkm			unsigned long api_version,
76455682Smarkm			void **server_handle)
76555682Smarkm{
766233294Sstas    return init_context(client_name,
76755682Smarkm			NULL,
76855682Smarkm			NULL,
76955682Smarkm			NULL,
77055682Smarkm			ccache,
771233294Sstas			service_name,
772233294Sstas			realm_params,
773233294Sstas			struct_version,
774233294Sstas			api_version,
77555682Smarkm			server_handle);
77655682Smarkm}
77755682Smarkm
77855682Smarkm#if 0
779233294Sstaskadm5_ret_t
78055682Smarkmkadm5_init(char *client_name, char *pass,
78155682Smarkm	   char *service_name,
78255682Smarkm	   kadm5_config_params *realm_params,
78355682Smarkm	   unsigned long struct_version,
78455682Smarkm	   unsigned long api_version,
78555682Smarkm	   void **server_handle)
78655682Smarkm{
78755682Smarkm}
78855682Smarkm#endif
78955682Smarkm
790