init_c.c revision 90926
1218885Sdim/*
2218885Sdim * Copyright (c) 1997 - 2002 Kungliga Tekniska H�gskolan
3218885Sdim * (Royal Institute of Technology, Stockholm, Sweden).
4218885Sdim * All rights reserved.
5218885Sdim *
6218885Sdim * Redistribution and use in source and binary forms, with or without
7218885Sdim * modification, are permitted provided that the following conditions
8218885Sdim * are met:
9218885Sdim *
10218885Sdim * 1. Redistributions of source code must retain the above copyright
11218885Sdim *    notice, this list of conditions and the following disclaimer.
12218885Sdim *
13218885Sdim * 2. Redistributions in binary form must reproduce the above copyright
14218885Sdim *    notice, this list of conditions and the following disclaimer in the
15218885Sdim *    documentation and/or other materials provided with the distribution.
16218885Sdim *
17249423Sdim * 3. Neither the name of the Institute nor the names of its contributors
18218885Sdim *    may be used to endorse or promote products derived from this software
19249423Sdim *    without specific prior written permission.
20218885Sdim *
21239462Sdim * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22249423Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23249423Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24249423Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25218885Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26218885Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27218885Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28218885Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29218885Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30218885Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31223017Sdim * SUCH DAMAGE.
32249423Sdim */
33249423Sdim
34249423Sdim#include "kadm5_locl.h"
35249423Sdim#include <sys/types.h>
36249423Sdim#include <sys/socket.h>
37249423Sdim#include <netinet/in.h>
38218885Sdim#include <netdb.h>
39249423Sdim
40249423SdimRCSID("$Id: init_c.c,v 1.42 2002/02/08 18:31:49 joda Exp $");
41249423Sdim
42249423Sdimstatic void
43249423Sdimset_funcs(kadm5_client_context *c)
44249423Sdim{
45249423Sdim#define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F
46249423Sdim    SET(c, chpass_principal);
47249423Sdim    SET(c, chpass_principal_with_key);
48249423Sdim    SET(c, create_principal);
49249423Sdim    SET(c, delete_principal);
50249423Sdim    SET(c, destroy);
51249423Sdim    SET(c, flush);
52218885Sdim    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
60kadm5_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	(*ctx)->realm = strdup(params->realm);
77    else
78	krb5_get_default_realm((*ctx)->context, &(*ctx)->realm);
79    if(params->mask & KADM5_CONFIG_ADMIN_SERVER)
80	(*ctx)->admin_server = strdup(params->admin_server);
81    else {
82	char **hostlist;
83
84	ret = krb5_get_krb_admin_hst (context, &(*ctx)->realm, &hostlist);
85	if (ret)
86	    return ret;
87	(*ctx)->admin_server = strdup(*hostlist);
88	krb5_free_krbhst (context, hostlist);
89    }
90
91    if ((*ctx)->admin_server == NULL)
92	return ENOMEM;
93    colon = strchr ((*ctx)->admin_server, ':');
94    if (colon != NULL)
95	*colon++ = '\0';
96
97    (*ctx)->kadmind_port = 0;
98
99    if(params->mask & KADM5_CONFIG_KADMIND_PORT)
100	(*ctx)->kadmind_port = params->kadmind_port;
101    else if (colon != NULL) {
102	char *end;
103
104	(*ctx)->kadmind_port = htons(strtol (colon, &end, 0));
105    }
106    if ((*ctx)->kadmind_port == 0)
107	(*ctx)->kadmind_port = krb5_getportbyname (context, "kerberos-adm",
108						   "tcp", 749);
109    return 0;
110}
111
112static krb5_error_code
113get_kadm_ticket(krb5_context context,
114		krb5_ccache id,
115		krb5_principal client,
116		const char *server_name)
117{
118    krb5_error_code ret;
119    krb5_creds in, *out;
120
121    memset(&in, 0, sizeof(in));
122    in.client = client;
123    ret = krb5_parse_name(context, server_name, &in.server);
124    if(ret)
125	return ret;
126    ret = krb5_get_credentials(context, 0, id, &in, &out);
127    if(ret == 0)
128	krb5_free_creds(context, out);
129    krb5_free_principal(context, in.server);
130    return ret;
131}
132
133static krb5_error_code
134get_new_cache(krb5_context context,
135	      krb5_principal client,
136	      const char *password,
137	      krb5_prompter_fct prompter,
138	      const char *keytab,
139	      const char *server_name,
140	      krb5_ccache *ret_cache)
141{
142    krb5_error_code ret;
143    krb5_creds cred;
144    krb5_get_init_creds_opt opt;
145    krb5_ccache id;
146
147    krb5_get_init_creds_opt_init (&opt);
148
149    krb5_get_init_creds_opt_set_default_flags(context, "kadmin",
150					      krb5_principal_get_realm(context,
151								       client),
152					      &opt);
153
154
155    krb5_get_init_creds_opt_set_forwardable (&opt, FALSE);
156    krb5_get_init_creds_opt_set_proxiable (&opt, FALSE);
157
158    if(password == NULL && prompter == NULL) {
159	krb5_keytab kt;
160	if(keytab == NULL)
161	    ret = krb5_kt_default(context, &kt);
162	else
163	    ret = krb5_kt_resolve(context, keytab, &kt);
164	if(ret)
165	    return ret;
166	ret = krb5_get_init_creds_keytab (context,
167					  &cred,
168					  client,
169					  kt,
170					  0,
171					  server_name,
172					  &opt);
173	krb5_kt_close(context, kt);
174    } else {
175	ret = krb5_get_init_creds_password (context,
176					    &cred,
177					    client,
178					    password,
179					    prompter,
180					    NULL,
181					    0,
182					    server_name,
183					    &opt);
184    }
185    switch(ret){
186    case 0:
187	break;
188    case KRB5_LIBOS_PWDINTR:	/* don't print anything if it was just C-c:ed */
189    case KRB5KRB_AP_ERR_BAD_INTEGRITY:
190    case KRB5KRB_AP_ERR_MODIFIED:
191	return KADM5_BAD_PASSWORD;
192    default:
193	return ret;
194    }
195    ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id);
196    if(ret)
197	return ret;
198    ret = krb5_cc_initialize (context, id, cred.client);
199    if (ret)
200	return ret;
201    ret = krb5_cc_store_cred (context, id, &cred);
202    if (ret)
203	return ret;
204    krb5_free_creds_contents (context, &cred);
205    *ret_cache = id;
206    return 0;
207}
208
209static krb5_error_code
210get_cred_cache(krb5_context context,
211	       const char *client_name,
212	       const char *server_name,
213	       const char *password,
214	       krb5_prompter_fct prompter,
215	       const char *keytab,
216	       krb5_ccache ccache,
217	       krb5_ccache *ret_cache)
218{
219    krb5_error_code ret;
220    krb5_ccache id = NULL;
221    krb5_principal default_client = NULL, client = NULL;
222
223    /* treat empty password as NULL */
224    if(password && *password == '\0')
225	password = NULL;
226    if(server_name == NULL)
227	server_name = KADM5_ADMIN_SERVICE;
228
229    if(client_name != NULL) {
230	ret = krb5_parse_name(context, client_name, &client);
231	if(ret)
232	    return ret;
233    }
234
235    if(password != NULL || prompter != NULL) {
236	/* get principal from default cache, ok if this doesn't work */
237	ret = krb5_cc_default(context, &id);
238	if(ret == 0) {
239	    ret = krb5_cc_get_principal(context, id, &default_client);
240	    if(ret) {
241		krb5_cc_close(context, id);
242		id = NULL;
243	    }
244	}
245
246	if(client == NULL)
247	    client = default_client;
248	if(client == NULL) {
249	    const char *user;
250
251	    user = get_default_username ();
252
253	    if(user == NULL)
254		return KADM5_FAILURE;
255	    ret = krb5_make_principal(context, &client,
256				      NULL, user, "admin", NULL);
257	    if(ret)
258		return ret;
259	}
260	if(client != default_client) {
261	    krb5_free_principal(context, default_client);
262	    default_client = NULL;
263	    if (id != NULL) {
264		krb5_cc_close(context, id);
265		id = NULL;
266	    }
267	}
268    } else if(ccache != NULL)
269	id = ccache;
270
271
272    if(id && (default_client == NULL ||
273	      krb5_principal_compare(context, client, default_client))) {
274	ret = get_kadm_ticket(context, id, client, server_name);
275	if(ret == 0) {
276	    *ret_cache = id;
277	    krb5_free_principal(context, default_client);
278	    if (default_client != client)
279		krb5_free_principal(context, client);
280	    return 0;
281	}
282	if(ccache != NULL)
283	    /* couldn't get ticket from cache */
284	    return -1;
285    }
286    /* get creds via AS request */
287    if(id)
288	krb5_cc_close(context, id);
289    if (client != default_client)
290	krb5_free_principal(context, default_client);
291
292    ret = get_new_cache(context, client, password, prompter, keytab,
293			server_name, ret_cache);
294    krb5_free_principal(context, client);
295    return ret;
296}
297
298static kadm5_ret_t
299kadm_connect(kadm5_client_context *ctx)
300{
301    kadm5_ret_t ret;
302    krb5_principal server;
303    krb5_ccache cc;
304    int s;
305    struct addrinfo *ai, *a;
306    struct addrinfo hints;
307    int error;
308    char portstr[NI_MAXSERV];
309    char *hostname, *slash;
310    krb5_context context = ctx->context;
311
312    memset (&hints, 0, sizeof(hints));
313    hints.ai_socktype = SOCK_STREAM;
314    hints.ai_protocol = IPPROTO_TCP;
315
316    snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port));
317
318    hostname = ctx->admin_server;
319    slash = strchr (hostname, '/');
320    if (slash != NULL)
321	hostname = slash + 1;
322
323    error = getaddrinfo (hostname, portstr, &hints, &ai);
324    if (error)
325	return KADM5_BAD_SERVER_NAME;
326
327    for (a = ai; a != NULL; a = a->ai_next) {
328	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
329	if (s < 0)
330	    continue;
331	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
332	    krb5_warn (context, errno, "connect(%s)", hostname);
333	    close (s);
334	    continue;
335	}
336	break;
337    }
338    if (a == NULL) {
339	freeaddrinfo (ai);
340	krb5_warnx (context, "failed to contact %s", hostname);
341	return KADM5_FAILURE;
342    }
343    ret = get_cred_cache(context, ctx->client_name, ctx->service_name,
344			 NULL, ctx->prompter, ctx->keytab,
345			 ctx->ccache, &cc);
346
347    if(ret) {
348	freeaddrinfo (ai);
349	close(s);
350	return ret;
351    }
352    ret = krb5_parse_name(context, KADM5_ADMIN_SERVICE, &server);
353    if(ret) {
354	freeaddrinfo (ai);
355	if(ctx->ccache == NULL)
356	    krb5_cc_close(context, cc);
357	close(s);
358	return ret;
359    }
360    ctx->ac = NULL;
361
362    ret = krb5_sendauth(context, &ctx->ac, &s,
363			KADMIN_APPL_VERSION, NULL,
364			server, AP_OPTS_MUTUAL_REQUIRED,
365			NULL, NULL, cc, NULL, NULL, NULL);
366    if(ret == 0) {
367	krb5_data params;
368	kadm5_config_params p;
369	memset(&p, 0, sizeof(p));
370	if(ctx->realm) {
371	    p.mask |= KADM5_CONFIG_REALM;
372	    p.realm = ctx->realm;
373	}
374	ret = _kadm5_marshal_params(context, &p, &params);
375
376	ret = krb5_write_priv_message(context, ctx->ac, &s, &params);
377	krb5_data_free(&params);
378	if(ret) {
379	    freeaddrinfo (ai);
380	    close(s);
381	    if(ctx->ccache == NULL)
382		krb5_cc_close(context, cc);
383	    return ret;
384	}
385    } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) {
386	close(s);
387
388	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
389	if (s < 0) {
390	    freeaddrinfo (ai);
391	    return errno;
392	}
393	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
394	    close (s);
395	    freeaddrinfo (ai);
396	    return errno;
397	}
398	ret = krb5_sendauth(context, &ctx->ac, &s,
399			    KADMIN_OLD_APPL_VERSION, NULL,
400			    server, AP_OPTS_MUTUAL_REQUIRED,
401			    NULL, NULL, cc, NULL, NULL, NULL);
402    }
403    freeaddrinfo (ai);
404    if(ret) {
405	close(s);
406	return ret;
407    }
408
409    krb5_free_principal(context, server);
410    if(ctx->ccache == NULL)
411	krb5_cc_close(context, cc);
412    if(ret) {
413	close(s);
414	return ret;
415    }
416    ctx->sock = s;
417
418    return 0;
419}
420
421kadm5_ret_t
422_kadm5_connect(void *handle)
423{
424    kadm5_client_context *ctx = handle;
425    if(ctx->sock == -1)
426	return kadm_connect(ctx);
427    return 0;
428}
429
430static kadm5_ret_t
431kadm5_c_init_with_context(krb5_context context,
432			  const char *client_name,
433			  const char *password,
434			  krb5_prompter_fct prompter,
435			  const char *keytab,
436			  krb5_ccache ccache,
437			  const char *service_name,
438			  kadm5_config_params *realm_params,
439			  unsigned long struct_version,
440			  unsigned long api_version,
441			  void **server_handle)
442{
443    kadm5_ret_t ret;
444    kadm5_client_context *ctx;
445    krb5_ccache cc;
446
447    ret = _kadm5_c_init_context(&ctx, realm_params, context);
448    if(ret)
449	return ret;
450
451    if(password != NULL && *password != '\0') {
452	ret = get_cred_cache(context, client_name, service_name,
453			     password, prompter, keytab, ccache, &cc);
454	if(ret)
455	    return ret; /* XXX */
456	ccache = cc;
457    }
458
459
460    if (client_name != NULL)
461	ctx->client_name = strdup(client_name);
462    else
463	ctx->client_name = NULL;
464    if (service_name != NULL)
465	ctx->service_name = strdup(service_name);
466    else
467	ctx->service_name = NULL;
468    ctx->prompter = prompter;
469    ctx->keytab = keytab;
470    ctx->ccache = ccache;
471    /* maybe we should copy the params here */
472    ctx->sock = -1;
473
474    *server_handle = ctx;
475    return 0;
476}
477
478static kadm5_ret_t
479init_context(const char *client_name,
480	     const char *password,
481	     krb5_prompter_fct prompter,
482	     const char *keytab,
483	     krb5_ccache ccache,
484	     const char *service_name,
485	     kadm5_config_params *realm_params,
486	     unsigned long struct_version,
487	     unsigned long api_version,
488	     void **server_handle)
489{
490    krb5_context context;
491    kadm5_ret_t ret;
492    kadm5_server_context *ctx;
493
494    ret = krb5_init_context(&context);
495    if (ret)
496	return ret;
497    ret = kadm5_c_init_with_context(context,
498				    client_name,
499				    password,
500				    prompter,
501				    keytab,
502				    ccache,
503				    service_name,
504				    realm_params,
505				    struct_version,
506				    api_version,
507				    server_handle);
508    if(ret){
509	krb5_free_context(context);
510	return ret;
511    }
512    ctx = *server_handle;
513    ctx->my_context = 1;
514    return 0;
515}
516
517kadm5_ret_t
518kadm5_c_init_with_password_ctx(krb5_context context,
519			       const char *client_name,
520			       const char *password,
521			       const char *service_name,
522			       kadm5_config_params *realm_params,
523			       unsigned long struct_version,
524			       unsigned long api_version,
525			       void **server_handle)
526{
527    return kadm5_c_init_with_context(context,
528				     client_name,
529				     password,
530				     krb5_prompter_posix,
531				     NULL,
532				     NULL,
533				     service_name,
534				     realm_params,
535				     struct_version,
536				     api_version,
537				     server_handle);
538}
539
540kadm5_ret_t
541kadm5_c_init_with_password(const char *client_name,
542			   const char *password,
543			   const char *service_name,
544			   kadm5_config_params *realm_params,
545			   unsigned long struct_version,
546			   unsigned long api_version,
547			   void **server_handle)
548{
549    return init_context(client_name,
550			password,
551			krb5_prompter_posix,
552			NULL,
553			NULL,
554			service_name,
555			realm_params,
556			struct_version,
557			api_version,
558			server_handle);
559}
560
561kadm5_ret_t
562kadm5_c_init_with_skey_ctx(krb5_context context,
563			   const char *client_name,
564			   const char *keytab,
565			   const char *service_name,
566			   kadm5_config_params *realm_params,
567			   unsigned long struct_version,
568			   unsigned long api_version,
569			   void **server_handle)
570{
571    return kadm5_c_init_with_context(context,
572				     client_name,
573				     NULL,
574				     NULL,
575				     keytab,
576				     NULL,
577				     service_name,
578				     realm_params,
579				     struct_version,
580				     api_version,
581				     server_handle);
582}
583
584
585kadm5_ret_t
586kadm5_c_init_with_skey(const char *client_name,
587		     const char *keytab,
588		     const char *service_name,
589		     kadm5_config_params *realm_params,
590		     unsigned long struct_version,
591		     unsigned long api_version,
592		     void **server_handle)
593{
594    return init_context(client_name,
595			NULL,
596			NULL,
597			keytab,
598			NULL,
599			service_name,
600			realm_params,
601			struct_version,
602			api_version,
603			server_handle);
604}
605
606kadm5_ret_t
607kadm5_c_init_with_creds_ctx(krb5_context context,
608			    const char *client_name,
609			    krb5_ccache ccache,
610			    const char *service_name,
611			    kadm5_config_params *realm_params,
612			    unsigned long struct_version,
613			    unsigned long api_version,
614			    void **server_handle)
615{
616    return kadm5_c_init_with_context(context,
617				     client_name,
618				     NULL,
619				     NULL,
620				     NULL,
621				     ccache,
622				     service_name,
623				     realm_params,
624				     struct_version,
625				     api_version,
626				     server_handle);
627}
628
629kadm5_ret_t
630kadm5_c_init_with_creds(const char *client_name,
631			krb5_ccache ccache,
632			const char *service_name,
633			kadm5_config_params *realm_params,
634			unsigned long struct_version,
635			unsigned long api_version,
636			void **server_handle)
637{
638    return init_context(client_name,
639			NULL,
640			NULL,
641			NULL,
642			ccache,
643			service_name,
644			realm_params,
645			struct_version,
646			api_version,
647			server_handle);
648}
649
650#if 0
651kadm5_ret_t
652kadm5_init(char *client_name, char *pass,
653	   char *service_name,
654	   kadm5_config_params *realm_params,
655	   unsigned long struct_version,
656	   unsigned long api_version,
657	   void **server_handle)
658{
659}
660#endif
661
662