init_c.c revision 57416
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.35 2000/01/28 03:20:18 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);
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
292kadm5_c_init_with_context(krb5_context context,
293			  const char *client_name,
294			  const char *password,
295			  krb5_prompter_fct prompter,
296			  const char *keytab,
297			  krb5_ccache ccache,
298			  const char *service_name,
299			  kadm5_config_params *realm_params,
300			  unsigned long struct_version,
301			  unsigned long api_version,
302			  void **server_handle)
303{
304    kadm5_ret_t ret;
305    kadm5_client_context *ctx;
306    krb5_principal server;
307    krb5_ccache cc;
308    int s;
309    struct addrinfo *ai, *a;
310    struct addrinfo hints;
311    int error;
312    char portstr[NI_MAXSERV];
313    char *hostname, *slash;
314
315    memset (&hints, 0, sizeof(hints));
316    hints.ai_socktype = SOCK_STREAM;
317    hints.ai_protocol = IPPROTO_TCP;
318
319    ret = _kadm5_c_init_context(&ctx, realm_params, context);
320    if(ret)
321	return ret;
322
323    snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port));
324
325    hostname = ctx->admin_server;
326    slash = strchr (hostname, '/');
327    if (slash != NULL)
328	hostname = slash + 1;
329
330    error = getaddrinfo (hostname, portstr, &hints, &ai);
331    if (error)
332	return KADM5_BAD_SERVER_NAME;
333
334    for (a = ai; a != NULL; a = a->ai_next) {
335	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
336	if (s < 0)
337	    continue;
338	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
339	    krb5_warn (context, errno, "connect(%s)", hostname);
340	    close (s);
341	    continue;
342	}
343	break;
344    }
345    if (a == NULL) {
346	freeaddrinfo (ai);
347	krb5_warnx (context, "failed to contact %s", hostname);
348	return KADM5_FAILURE;
349    }
350    ret = get_cred_cache(context, client_name, service_name,
351			 password, prompter, keytab, ccache, &cc);
352
353    if(ret) {
354	freeaddrinfo (ai);
355	close(s);
356	return ret;
357    }
358    ret = krb5_parse_name(context, KADM5_ADMIN_SERVICE, &server);
359    if(ret) {
360	freeaddrinfo (ai);
361	if(ccache == NULL)
362	    krb5_cc_close(context, cc);
363	close(s);
364	return ret;
365    }
366    ctx->ac = NULL;
367
368    ret = krb5_sendauth(context, &ctx->ac, &s,
369			KADMIN_APPL_VERSION, NULL,
370			server, AP_OPTS_MUTUAL_REQUIRED,
371			NULL, NULL, cc, NULL, NULL, NULL);
372    if(ret == 0) {
373	krb5_data params, enc_data;
374	ret = _kadm5_marshal_params(context, realm_params, &params);
375
376	ret = krb5_mk_priv(context,
377			   ctx->ac,
378			   &params,
379			   &enc_data,
380			   NULL);
381
382	ret = krb5_write_message(context, &s, &enc_data);
383
384	krb5_data_free(&params);
385	krb5_data_free(&enc_data);
386    } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) {
387	close(s);
388
389	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
390	if (s < 0) {
391	    freeaddrinfo (ai);
392	    return errno;
393	}
394	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
395	    close (s);
396	    freeaddrinfo (ai);
397	    return errno;
398	}
399	freeaddrinfo (ai);
400
401	ret = krb5_sendauth(context, &ctx->ac, &s,
402			    KADMIN_OLD_APPL_VERSION, NULL,
403			    server, AP_OPTS_MUTUAL_REQUIRED,
404			    NULL, NULL, cc, NULL, NULL, NULL);
405    }
406    freeaddrinfo (ai);
407    if(ret) {
408	close(s);
409	return ret;
410    }
411
412    krb5_free_principal(context, server);
413    if(ccache == NULL)
414	krb5_cc_close(context, cc);
415    if(ret) {
416	close(s);
417	return ret;
418    }
419    ctx->sock = s;
420    *server_handle = ctx;
421    return 0;
422}
423
424static kadm5_ret_t
425init_context(const char *client_name,
426	     const char *password,
427	     krb5_prompter_fct prompter,
428	     const char *keytab,
429	     krb5_ccache ccache,
430	     const char *service_name,
431	     kadm5_config_params *realm_params,
432	     unsigned long struct_version,
433	     unsigned long api_version,
434	     void **server_handle)
435{
436    krb5_context context;
437    kadm5_ret_t ret;
438    kadm5_server_context *ctx;
439
440    krb5_init_context(&context);
441    ret = kadm5_c_init_with_context(context,
442				    client_name,
443				    password,
444				    prompter,
445				    keytab,
446				    ccache,
447				    service_name,
448				    realm_params,
449				    struct_version,
450				    api_version,
451				    server_handle);
452    if(ret){
453	krb5_free_context(context);
454	return ret;
455    }
456    ctx = *server_handle;
457    ctx->my_context = 1;
458    return 0;
459}
460
461kadm5_ret_t
462kadm5_c_init_with_password_ctx(krb5_context context,
463			       const char *client_name,
464			       const char *password,
465			       const char *service_name,
466			       kadm5_config_params *realm_params,
467			       unsigned long struct_version,
468			       unsigned long api_version,
469			       void **server_handle)
470{
471    return kadm5_c_init_with_context(context,
472				     client_name,
473				     password,
474				     krb5_prompter_posix,
475				     NULL,
476				     NULL,
477				     service_name,
478				     realm_params,
479				     struct_version,
480				     api_version,
481				     server_handle);
482}
483
484kadm5_ret_t
485kadm5_c_init_with_password(const char *client_name,
486			   const char *password,
487			   const char *service_name,
488			   kadm5_config_params *realm_params,
489			   unsigned long struct_version,
490			   unsigned long api_version,
491			   void **server_handle)
492{
493    return init_context(client_name,
494			password,
495			krb5_prompter_posix,
496			NULL,
497			NULL,
498			service_name,
499			realm_params,
500			struct_version,
501			api_version,
502			server_handle);
503}
504
505kadm5_ret_t
506kadm5_c_init_with_skey_ctx(krb5_context context,
507			   const char *client_name,
508			   const char *keytab,
509			   const char *service_name,
510			   kadm5_config_params *realm_params,
511			   unsigned long struct_version,
512			   unsigned long api_version,
513			   void **server_handle)
514{
515    return kadm5_c_init_with_context(context,
516				     client_name,
517				     NULL,
518				     NULL,
519				     keytab,
520				     NULL,
521				     service_name,
522				     realm_params,
523				     struct_version,
524				     api_version,
525				     server_handle);
526}
527
528
529kadm5_ret_t
530kadm5_c_init_with_skey(const char *client_name,
531		     const char *keytab,
532		     const char *service_name,
533		     kadm5_config_params *realm_params,
534		     unsigned long struct_version,
535		     unsigned long api_version,
536		     void **server_handle)
537{
538    return init_context(client_name,
539			NULL,
540			NULL,
541			keytab,
542			NULL,
543			service_name,
544			realm_params,
545			struct_version,
546			api_version,
547			server_handle);
548}
549
550kadm5_ret_t
551kadm5_c_init_with_creds_ctx(krb5_context context,
552			    const char *client_name,
553			    krb5_ccache ccache,
554			    const char *service_name,
555			    kadm5_config_params *realm_params,
556			    unsigned long struct_version,
557			    unsigned long api_version,
558			    void **server_handle)
559{
560    return kadm5_c_init_with_context(context,
561				     client_name,
562				     NULL,
563				     NULL,
564				     NULL,
565				     ccache,
566				     service_name,
567				     realm_params,
568				     struct_version,
569				     api_version,
570				     server_handle);
571}
572
573kadm5_ret_t
574kadm5_c_init_with_creds(const char *client_name,
575			krb5_ccache ccache,
576			const char *service_name,
577			kadm5_config_params *realm_params,
578			unsigned long struct_version,
579			unsigned long api_version,
580			void **server_handle)
581{
582    return init_context(client_name,
583			NULL,
584			NULL,
585			NULL,
586			ccache,
587			service_name,
588			realm_params,
589			struct_version,
590			api_version,
591			server_handle);
592}
593
594#if 0
595kadm5_ret_t
596kadm5_init(char *client_name, char *pass,
597	   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}
604#endif
605
606