init_c.c revision 72445
1/*
2 * Copyright (c) 1997 - 2000 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
40RCSID("$Id: init_c.c,v 1.40 2000/12/31 08:00:23 assar Exp $");
41
42static void
43set_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
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    krb5_get_init_creds_opt_set_forwardable (&opt, FALSE);
149    krb5_get_init_creds_opt_set_proxiable (&opt, FALSE);
150
151    if(password == NULL && prompter == NULL) {
152	krb5_keytab kt;
153	if(keytab == NULL)
154	    ret = krb5_kt_default(context, &kt);
155	else
156	    ret = krb5_kt_resolve(context, keytab, &kt);
157	if(ret)
158	    return ret;
159	ret = krb5_get_init_creds_keytab (context,
160					  &cred,
161					  client,
162					  kt,
163					  0,
164					  server_name,
165					  &opt);
166	krb5_kt_close(context, kt);
167    } else {
168	ret = krb5_get_init_creds_password (context,
169					    &cred,
170					    client,
171					    password,
172					    prompter,
173					    NULL,
174					    0,
175					    server_name,
176					    &opt);
177    }
178    switch(ret){
179    case 0:
180	break;
181    case KRB5_LIBOS_PWDINTR:	/* don't print anything if it was just C-c:ed */
182    case KRB5KRB_AP_ERR_BAD_INTEGRITY:
183    case KRB5KRB_AP_ERR_MODIFIED:
184	return KADM5_BAD_PASSWORD;
185    default:
186	return ret;
187    }
188    ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id);
189    if(ret)
190	return ret;
191    ret = krb5_cc_initialize (context, id, cred.client);
192    if (ret)
193	return ret;
194    ret = krb5_cc_store_cred (context, id, &cred);
195    if (ret)
196	return ret;
197    krb5_free_creds_contents (context, &cred);
198    *ret_cache = id;
199    return 0;
200}
201
202static krb5_error_code
203get_cred_cache(krb5_context context,
204	       const char *client_name,
205	       const char *server_name,
206	       const char *password,
207	       krb5_prompter_fct prompter,
208	       const char *keytab,
209	       krb5_ccache ccache,
210	       krb5_ccache *ret_cache)
211{
212    krb5_error_code ret;
213    krb5_ccache id = NULL;
214    krb5_principal default_client = NULL, client = NULL;
215
216    /* treat empty password as NULL */
217    if(password && *password == '\0')
218	password = NULL;
219    if(server_name == NULL)
220	server_name = KADM5_ADMIN_SERVICE;
221
222    if(client_name != NULL) {
223	ret = krb5_parse_name(context, client_name, &client);
224	if(ret)
225	    return ret;
226    }
227
228    if(password != NULL || prompter != NULL) {
229	/* get principal from default cache, ok if this doesn't work */
230	ret = krb5_cc_default(context, &id);
231	if(ret == 0) {
232	    ret = krb5_cc_get_principal(context, id, &default_client);
233	    if(ret) {
234		krb5_cc_close(context, id);
235		id = NULL;
236	    }
237	}
238
239	if(client == NULL)
240	    client = default_client;
241	if(client == NULL) {
242	    const char *user;
243
244	    user = get_default_username ();
245
246	    if(user == NULL)
247		return KADM5_FAILURE;
248	    ret = krb5_make_principal(context, &client,
249				      NULL, user, "admin", NULL);
250	    if(ret)
251		return ret;
252	}
253	if(client != default_client) {
254	    krb5_free_principal(context, default_client);
255	    default_client = NULL;
256	    if (id != NULL) {
257		krb5_cc_close(context, id);
258		id = NULL;
259	    }
260	}
261    } else if(ccache != NULL)
262	id = ccache;
263
264
265    if(id && (default_client == NULL ||
266	      krb5_principal_compare(context, client, default_client))) {
267	ret = get_kadm_ticket(context, id, client, server_name);
268	if(ret == 0) {
269	    *ret_cache = id;
270	    krb5_free_principal(context, default_client);
271	    if (default_client != client)
272		krb5_free_principal(context, client);
273	    return 0;
274	}
275	if(ccache != NULL)
276	    /* couldn't get ticket from cache */
277	    return -1;
278    }
279    /* get creds via AS request */
280    if(id)
281	krb5_cc_close(context, id);
282    if (client != default_client)
283	krb5_free_principal(context, default_client);
284
285    ret = get_new_cache(context, client, password, prompter, keytab,
286			server_name, ret_cache);
287    krb5_free_principal(context, client);
288    return ret;
289}
290
291static kadm5_ret_t
292kadm_connect(kadm5_client_context *ctx)
293{
294    kadm5_ret_t ret;
295    krb5_principal server;
296    krb5_ccache cc;
297    int s;
298    struct addrinfo *ai, *a;
299    struct addrinfo hints;
300    int error;
301    char portstr[NI_MAXSERV];
302    char *hostname, *slash;
303    krb5_context context = ctx->context;
304
305    memset (&hints, 0, sizeof(hints));
306    hints.ai_socktype = SOCK_STREAM;
307    hints.ai_protocol = IPPROTO_TCP;
308
309    snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port));
310
311    hostname = ctx->admin_server;
312    slash = strchr (hostname, '/');
313    if (slash != NULL)
314	hostname = slash + 1;
315
316    error = getaddrinfo (hostname, portstr, &hints, &ai);
317    if (error)
318	return KADM5_BAD_SERVER_NAME;
319
320    for (a = ai; a != NULL; a = a->ai_next) {
321	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
322	if (s < 0)
323	    continue;
324	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
325	    krb5_warn (context, errno, "connect(%s)", hostname);
326	    close (s);
327	    continue;
328	}
329	break;
330    }
331    if (a == NULL) {
332	freeaddrinfo (ai);
333	krb5_warnx (context, "failed to contact %s", hostname);
334	return KADM5_FAILURE;
335    }
336    ret = get_cred_cache(context, ctx->client_name, ctx->service_name,
337			 NULL, ctx->prompter, ctx->keytab,
338			 ctx->ccache, &cc);
339
340    if(ret) {
341	freeaddrinfo (ai);
342	close(s);
343	return ret;
344    }
345    ret = krb5_parse_name(context, KADM5_ADMIN_SERVICE, &server);
346    if(ret) {
347	freeaddrinfo (ai);
348	if(ctx->ccache == NULL)
349	    krb5_cc_close(context, cc);
350	close(s);
351	return ret;
352    }
353    ctx->ac = NULL;
354
355    ret = krb5_sendauth(context, &ctx->ac, &s,
356			KADMIN_APPL_VERSION, NULL,
357			server, AP_OPTS_MUTUAL_REQUIRED,
358			NULL, NULL, cc, NULL, NULL, NULL);
359    if(ret == 0) {
360	krb5_data params;
361	ret = _kadm5_marshal_params(context, ctx->realm_params, &params);
362
363	ret = krb5_write_priv_message(context, ctx->ac, &s, &params);
364	krb5_data_free(&params);
365	if(ret) {
366	    freeaddrinfo (ai);
367	    close(s);
368	    if(ctx->ccache == NULL)
369		krb5_cc_close(context, cc);
370	    return ret;
371	}
372    } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) {
373	close(s);
374
375	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
376	if (s < 0) {
377	    freeaddrinfo (ai);
378	    return errno;
379	}
380	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
381	    close (s);
382	    freeaddrinfo (ai);
383	    return errno;
384	}
385	ret = krb5_sendauth(context, &ctx->ac, &s,
386			    KADMIN_OLD_APPL_VERSION, NULL,
387			    server, AP_OPTS_MUTUAL_REQUIRED,
388			    NULL, NULL, cc, NULL, NULL, NULL);
389    }
390    freeaddrinfo (ai);
391    if(ret) {
392	close(s);
393	return ret;
394    }
395
396    krb5_free_principal(context, server);
397    if(ctx->ccache == NULL)
398	krb5_cc_close(context, cc);
399    if(ret) {
400	close(s);
401	return ret;
402    }
403    ctx->sock = s;
404
405    return 0;
406}
407
408kadm5_ret_t
409_kadm5_connect(void *handle)
410{
411    kadm5_client_context *ctx = handle;
412    if(ctx->sock == -1)
413	return kadm_connect(ctx);
414    return 0;
415}
416
417static kadm5_ret_t
418kadm5_c_init_with_context(krb5_context context,
419			  const char *client_name,
420			  const char *password,
421			  krb5_prompter_fct prompter,
422			  const char *keytab,
423			  krb5_ccache ccache,
424			  const char *service_name,
425			  kadm5_config_params *realm_params,
426			  unsigned long struct_version,
427			  unsigned long api_version,
428			  void **server_handle)
429{
430    kadm5_ret_t ret;
431    kadm5_client_context *ctx;
432    krb5_ccache cc;
433
434    ret = _kadm5_c_init_context(&ctx, realm_params, context);
435    if(ret)
436	return ret;
437
438    if(password != NULL && *password != '\0') {
439	ret = get_cred_cache(context, client_name, service_name,
440			     password, prompter, keytab, ccache, &cc);
441	if(ret)
442	    return ret; /* XXX */
443	ccache = cc;
444    }
445
446
447    if (client_name != NULL)
448	ctx->client_name = strdup(client_name);
449    else
450	ctx->client_name = NULL;
451    if (service_name != NULL)
452	ctx->service_name = strdup(service_name);
453    else
454	ctx->service_name = NULL;
455    ctx->prompter = prompter;
456    ctx->keytab = keytab;
457    ctx->ccache = ccache;
458    ctx->realm_params = realm_params;
459    ctx->sock = -1;
460
461    *server_handle = ctx;
462    return 0;
463}
464
465static kadm5_ret_t
466init_context(const char *client_name,
467	     const char *password,
468	     krb5_prompter_fct prompter,
469	     const char *keytab,
470	     krb5_ccache ccache,
471	     const char *service_name,
472	     kadm5_config_params *realm_params,
473	     unsigned long struct_version,
474	     unsigned long api_version,
475	     void **server_handle)
476{
477    krb5_context context;
478    kadm5_ret_t ret;
479    kadm5_server_context *ctx;
480
481    ret = krb5_init_context(&context);
482    if (ret)
483	return ret;
484    ret = kadm5_c_init_with_context(context,
485				    client_name,
486				    password,
487				    prompter,
488				    keytab,
489				    ccache,
490				    service_name,
491				    realm_params,
492				    struct_version,
493				    api_version,
494				    server_handle);
495    if(ret){
496	krb5_free_context(context);
497	return ret;
498    }
499    ctx = *server_handle;
500    ctx->my_context = 1;
501    return 0;
502}
503
504kadm5_ret_t
505kadm5_c_init_with_password_ctx(krb5_context context,
506			       const char *client_name,
507			       const char *password,
508			       const char *service_name,
509			       kadm5_config_params *realm_params,
510			       unsigned long struct_version,
511			       unsigned long api_version,
512			       void **server_handle)
513{
514    return kadm5_c_init_with_context(context,
515				     client_name,
516				     password,
517				     krb5_prompter_posix,
518				     NULL,
519				     NULL,
520				     service_name,
521				     realm_params,
522				     struct_version,
523				     api_version,
524				     server_handle);
525}
526
527kadm5_ret_t
528kadm5_c_init_with_password(const char *client_name,
529			   const char *password,
530			   const char *service_name,
531			   kadm5_config_params *realm_params,
532			   unsigned long struct_version,
533			   unsigned long api_version,
534			   void **server_handle)
535{
536    return init_context(client_name,
537			password,
538			krb5_prompter_posix,
539			NULL,
540			NULL,
541			service_name,
542			realm_params,
543			struct_version,
544			api_version,
545			server_handle);
546}
547
548kadm5_ret_t
549kadm5_c_init_with_skey_ctx(krb5_context context,
550			   const char *client_name,
551			   const char *keytab,
552			   const char *service_name,
553			   kadm5_config_params *realm_params,
554			   unsigned long struct_version,
555			   unsigned long api_version,
556			   void **server_handle)
557{
558    return kadm5_c_init_with_context(context,
559				     client_name,
560				     NULL,
561				     NULL,
562				     keytab,
563				     NULL,
564				     service_name,
565				     realm_params,
566				     struct_version,
567				     api_version,
568				     server_handle);
569}
570
571
572kadm5_ret_t
573kadm5_c_init_with_skey(const char *client_name,
574		     const char *keytab,
575		     const char *service_name,
576		     kadm5_config_params *realm_params,
577		     unsigned long struct_version,
578		     unsigned long api_version,
579		     void **server_handle)
580{
581    return init_context(client_name,
582			NULL,
583			NULL,
584			keytab,
585			NULL,
586			service_name,
587			realm_params,
588			struct_version,
589			api_version,
590			server_handle);
591}
592
593kadm5_ret_t
594kadm5_c_init_with_creds_ctx(krb5_context context,
595			    const char *client_name,
596			    krb5_ccache ccache,
597			    const char *service_name,
598			    kadm5_config_params *realm_params,
599			    unsigned long struct_version,
600			    unsigned long api_version,
601			    void **server_handle)
602{
603    return kadm5_c_init_with_context(context,
604				     client_name,
605				     NULL,
606				     NULL,
607				     NULL,
608				     ccache,
609				     service_name,
610				     realm_params,
611				     struct_version,
612				     api_version,
613				     server_handle);
614}
615
616kadm5_ret_t
617kadm5_c_init_with_creds(const char *client_name,
618			krb5_ccache ccache,
619			const char *service_name,
620			kadm5_config_params *realm_params,
621			unsigned long struct_version,
622			unsigned long api_version,
623			void **server_handle)
624{
625    return init_context(client_name,
626			NULL,
627			NULL,
628			NULL,
629			ccache,
630			service_name,
631			realm_params,
632			struct_version,
633			api_version,
634			server_handle);
635}
636
637#if 0
638kadm5_ret_t
639kadm5_init(char *client_name, char *pass,
640	   char *service_name,
641	   kadm5_config_params *realm_params,
642	   unsigned long struct_version,
643	   unsigned long api_version,
644	   void **server_handle)
645{
646}
647#endif
648
649