Rename context handle lifetime to endtime
[heimdal.git] / lib / kadm5 / init_c.c
blobf6fd6d3dce7d77dc23c131b6d77c6720800f55d0
1 /*
2 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "kadm5_locl.h"
35 #include <sys/types.h>
36 #ifdef HAVE_SYS_SOCKET_H
37 #include <sys/socket.h>
38 #endif
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42 #ifdef HAVE_NETDB_H
43 #include <netdb.h>
44 #endif
46 RCSID("$Id$");
48 static kadm5_ret_t
49 kadm5_c_lock(void *server_handle)
51 return ENOTSUP;
54 static kadm5_ret_t
55 kadm5_c_unlock(void *server_handle)
57 return ENOTSUP;
60 static void
61 set_funcs(kadm5_client_context *c)
63 #define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F
64 SET(c, chpass_principal);
65 SET(c, chpass_principal_with_key);
66 SET(c, create_principal);
67 SET(c, delete_principal);
68 SET(c, destroy);
69 SET(c, flush);
70 SET(c, get_principal);
71 SET(c, get_principals);
72 SET(c, get_privs);
73 SET(c, modify_principal);
74 SET(c, randkey_principal);
75 SET(c, rename_principal);
76 SET(c, lock);
77 SET(c, unlock);
80 kadm5_ret_t
81 _kadm5_c_init_context(kadm5_client_context **ctx,
82 kadm5_config_params *params,
83 krb5_context context)
85 krb5_error_code ret;
86 char *colon;
88 *ctx = malloc(sizeof(**ctx));
89 if(*ctx == NULL)
90 return ENOMEM;
91 memset(*ctx, 0, sizeof(**ctx));
92 krb5_add_et_list (context, initialize_kadm5_error_table_r);
93 set_funcs(*ctx);
94 (*ctx)->context = context;
95 if(params->mask & KADM5_CONFIG_REALM) {
96 ret = 0;
97 (*ctx)->realm = strdup(params->realm);
98 if ((*ctx)->realm == NULL)
99 ret = ENOMEM;
100 } else
101 ret = krb5_get_default_realm((*ctx)->context, &(*ctx)->realm);
102 if (ret) {
103 free(*ctx);
104 return ret;
106 if(params->mask & KADM5_CONFIG_ADMIN_SERVER)
107 (*ctx)->admin_server = strdup(params->admin_server);
108 else {
109 char **hostlist;
111 ret = krb5_get_krb_admin_hst (context, &(*ctx)->realm, &hostlist);
112 if (ret) {
113 free((*ctx)->realm);
114 free(*ctx);
115 return ret;
117 (*ctx)->admin_server = strdup(*hostlist);
118 krb5_free_krbhst (context, hostlist);
121 if ((*ctx)->admin_server == NULL) {
122 free((*ctx)->realm);
123 free(*ctx);
124 return ENOMEM;
126 colon = strchr ((*ctx)->admin_server, ':');
127 if (colon != NULL)
128 *colon++ = '\0';
130 (*ctx)->kadmind_port = 0;
132 if(params->mask & KADM5_CONFIG_KADMIND_PORT)
133 (*ctx)->kadmind_port = params->kadmind_port;
134 else if (colon != NULL) {
135 char *end;
137 (*ctx)->kadmind_port = htons(strtol (colon, &end, 0));
139 if ((*ctx)->kadmind_port == 0)
140 (*ctx)->kadmind_port = krb5_getportbyname (context, "kerberos-adm",
141 "tcp", 749);
142 return 0;
145 static krb5_error_code
146 get_kadm_ticket(krb5_context context,
147 krb5_ccache id,
148 krb5_principal client,
149 const char *server_name)
151 krb5_error_code ret;
152 krb5_creds in, *out;
154 memset(&in, 0, sizeof(in));
155 in.client = client;
156 ret = krb5_parse_name(context, server_name, &in.server);
157 if(ret)
158 return ret;
159 ret = krb5_get_credentials(context, 0, id, &in, &out);
160 if(ret == 0)
161 krb5_free_creds(context, out);
162 krb5_free_principal(context, in.server);
163 return ret;
166 static krb5_error_code
167 get_new_cache(krb5_context context,
168 krb5_principal client,
169 const char *password,
170 krb5_prompter_fct prompter,
171 const char *keytab,
172 const char *server_name,
173 krb5_ccache *ret_cache)
175 krb5_error_code ret;
176 krb5_creds cred;
177 krb5_get_init_creds_opt *opt;
178 krb5_ccache id;
180 ret = krb5_get_init_creds_opt_alloc (context, &opt);
181 if (ret)
182 return ret;
184 krb5_get_init_creds_opt_set_default_flags(context, "kadmin",
185 krb5_principal_get_realm(context,
186 client),
187 opt);
190 krb5_get_init_creds_opt_set_forwardable (opt, FALSE);
191 krb5_get_init_creds_opt_set_proxiable (opt, FALSE);
193 if(password == NULL && prompter == NULL) {
194 krb5_keytab kt;
195 if(keytab == NULL)
196 ret = krb5_kt_default(context, &kt);
197 else
198 ret = krb5_kt_resolve(context, keytab, &kt);
199 if(ret) {
200 krb5_get_init_creds_opt_free(context, opt);
201 return ret;
203 ret = krb5_get_init_creds_keytab (context,
204 &cred,
205 client,
208 server_name,
209 opt);
210 krb5_kt_close(context, kt);
211 } else {
212 ret = krb5_get_init_creds_password (context,
213 &cred,
214 client,
215 password,
216 prompter,
217 NULL,
219 server_name,
220 opt);
222 krb5_get_init_creds_opt_free(context, opt);
223 switch(ret){
224 case 0:
225 break;
226 case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */
227 case KRB5KRB_AP_ERR_BAD_INTEGRITY:
228 case KRB5KRB_AP_ERR_MODIFIED:
229 return KADM5_BAD_PASSWORD;
230 default:
231 return ret;
233 ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);
234 if(ret)
235 return ret;
236 ret = krb5_cc_initialize (context, id, cred.client);
237 if (ret)
238 return ret;
239 ret = krb5_cc_store_cred (context, id, &cred);
240 if (ret)
241 return ret;
242 krb5_free_cred_contents (context, &cred);
243 *ret_cache = id;
244 return 0;
248 * Check the credential cache `id´ to figure out what principal to use
249 * when talking to the kadmind. If there is a initial kadmin/admin@
250 * credential in the cache, use that client principal. Otherwise, use
251 * the client principals first component and add /admin to the
252 * principal.
255 static krb5_error_code
256 get_cache_principal(krb5_context context,
257 krb5_ccache *id,
258 krb5_principal *client)
260 krb5_error_code ret;
261 const char *name, *inst;
262 krb5_principal p1, p2;
264 ret = krb5_cc_default(context, id);
265 if(ret) {
266 *id = NULL;
267 return ret;
270 ret = krb5_cc_get_principal(context, *id, &p1);
271 if(ret) {
272 krb5_cc_close(context, *id);
273 *id = NULL;
274 return ret;
277 ret = krb5_make_principal(context, &p2, NULL,
278 "kadmin", "admin", NULL);
279 if (ret) {
280 krb5_cc_close(context, *id);
281 *id = NULL;
282 krb5_free_principal(context, p1);
283 return ret;
287 krb5_creds in, *out;
288 krb5_kdc_flags flags;
290 flags.i = 0;
291 memset(&in, 0, sizeof(in));
293 in.client = p1;
294 in.server = p2;
296 /* check for initial ticket kadmin/admin */
297 ret = krb5_get_credentials_with_flags(context, KRB5_GC_CACHED, flags,
298 *id, &in, &out);
299 krb5_free_principal(context, p2);
300 if (ret == 0) {
301 if (out->flags.b.initial) {
302 *client = p1;
303 krb5_free_creds(context, out);
304 return 0;
306 krb5_free_creds(context, out);
309 krb5_cc_close(context, *id);
310 *id = NULL;
312 name = krb5_principal_get_comp_string(context, p1, 0);
313 inst = krb5_principal_get_comp_string(context, p1, 1);
314 if(inst == NULL || strcmp(inst, "admin") != 0) {
315 ret = krb5_make_principal(context, &p2, NULL, name, "admin", NULL);
316 krb5_free_principal(context, p1);
317 if(ret != 0)
318 return ret;
320 *client = p2;
321 return 0;
324 *client = p1;
326 return 0;
329 krb5_error_code
330 _kadm5_c_get_cred_cache(krb5_context context,
331 const char *client_name,
332 const char *server_name,
333 const char *password,
334 krb5_prompter_fct prompter,
335 const char *keytab,
336 krb5_ccache ccache,
337 krb5_ccache *ret_cache)
339 krb5_error_code ret;
340 krb5_ccache id = NULL;
341 krb5_principal default_client = NULL, client = NULL;
343 /* treat empty password as NULL */
344 if(password && *password == '\0')
345 password = NULL;
346 if(server_name == NULL)
347 server_name = KADM5_ADMIN_SERVICE;
349 if(client_name != NULL) {
350 ret = krb5_parse_name(context, client_name, &client);
351 if(ret)
352 return ret;
355 if(ccache != NULL) {
356 id = ccache;
357 ret = krb5_cc_get_principal(context, id, &client);
358 if(ret)
359 return ret;
360 } else {
361 /* get principal from default cache, ok if this doesn't work */
363 ret = get_cache_principal(context, &id, &default_client);
364 if (ret) {
366 * No client was specified by the caller and we cannot
367 * determine the client from a credentials cache.
369 const char *user;
371 user = get_default_username ();
373 if(user == NULL) {
374 krb5_set_error_message(context, KADM5_FAILURE, "Unable to find local user name");
375 return KADM5_FAILURE;
377 ret = krb5_make_principal(context, &default_client,
378 NULL, user, "admin", NULL);
379 if(ret)
380 return ret;
386 * No client was specified by the caller, but we have a client
387 * from the default credentials cache.
389 if (client == NULL && default_client != NULL)
390 client = default_client;
393 if(id && client && (default_client == NULL ||
394 krb5_principal_compare(context, client, default_client) != 0)) {
395 ret = get_kadm_ticket(context, id, client, server_name);
396 if(ret == 0) {
397 *ret_cache = id;
398 krb5_free_principal(context, default_client);
399 if (default_client != client)
400 krb5_free_principal(context, client);
401 return 0;
403 if(ccache != NULL)
404 /* couldn't get ticket from cache */
405 return -1;
407 /* get creds via AS request */
408 if(id && (id != ccache))
409 krb5_cc_close(context, id);
410 if (client != default_client)
411 krb5_free_principal(context, default_client);
413 ret = get_new_cache(context, client, password, prompter, keytab,
414 server_name, ret_cache);
415 krb5_free_principal(context, client);
416 return ret;
419 static kadm5_ret_t
420 kadm_connect(kadm5_client_context *ctx)
422 kadm5_ret_t ret;
423 krb5_principal server;
424 krb5_ccache cc;
425 rk_socket_t s = rk_INVALID_SOCKET;
426 struct addrinfo *ai, *a;
427 struct addrinfo hints;
428 int error;
429 char portstr[NI_MAXSERV];
430 char *hostname, *slash;
431 char *service_name;
432 krb5_context context = ctx->context;
434 memset (&hints, 0, sizeof(hints));
435 hints.ai_socktype = SOCK_STREAM;
436 hints.ai_protocol = IPPROTO_TCP;
438 snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port));
440 hostname = ctx->admin_server;
441 slash = strchr (hostname, '/');
442 if (slash != NULL)
443 hostname = slash + 1;
445 error = getaddrinfo (hostname, portstr, &hints, &ai);
446 if (error) {
447 krb5_clear_error_message(context);
448 return KADM5_BAD_SERVER_NAME;
451 for (a = ai; a != NULL; a = a->ai_next) {
452 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
453 if (s < 0)
454 continue;
455 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
456 krb5_clear_error_message(context);
457 krb5_warn (context, errno, "connect(%s)", hostname);
458 rk_closesocket (s);
459 continue;
461 break;
463 if (a == NULL) {
464 freeaddrinfo (ai);
465 krb5_clear_error_message(context);
466 krb5_warnx (context, "failed to contact %s", hostname);
467 return KADM5_FAILURE;
469 ret = _kadm5_c_get_cred_cache(context,
470 ctx->client_name,
471 ctx->service_name,
472 NULL, ctx->prompter, ctx->keytab,
473 ctx->ccache, &cc);
475 if(ret) {
476 freeaddrinfo (ai);
477 rk_closesocket(s);
478 return ret;
481 if (ctx->realm)
482 error = asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE,
483 ctx->realm);
484 else
485 error = asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE);
487 if (error == -1 || service_name == NULL) {
488 freeaddrinfo (ai);
489 rk_closesocket(s);
490 krb5_clear_error_message(context);
491 return ENOMEM;
494 ret = krb5_parse_name(context, service_name, &server);
495 free(service_name);
496 if(ret) {
497 freeaddrinfo (ai);
498 if(ctx->ccache == NULL)
499 krb5_cc_close(context, cc);
500 rk_closesocket(s);
501 return ret;
503 ctx->ac = NULL;
505 ret = krb5_sendauth(context, &ctx->ac, &s,
506 KADMIN_APPL_VERSION, NULL,
507 server, AP_OPTS_MUTUAL_REQUIRED,
508 NULL, NULL, cc, NULL, NULL, NULL);
509 if(ret == 0) {
510 krb5_data params;
511 kadm5_config_params p;
512 memset(&p, 0, sizeof(p));
513 if(ctx->realm) {
514 p.mask |= KADM5_CONFIG_REALM;
515 p.realm = ctx->realm;
517 ret = _kadm5_marshal_params(context, &p, &params);
519 ret = krb5_write_priv_message(context, ctx->ac, &s, &params);
520 krb5_data_free(&params);
521 if(ret) {
522 freeaddrinfo (ai);
523 rk_closesocket(s);
524 if(ctx->ccache == NULL)
525 krb5_cc_close(context, cc);
526 return ret;
528 } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) {
529 rk_closesocket(s);
531 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
532 if (s < 0) {
533 freeaddrinfo (ai);
534 krb5_clear_error_message(context);
535 return errno;
537 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
538 rk_closesocket (s);
539 freeaddrinfo (ai);
540 krb5_clear_error_message(context);
541 return errno;
543 ret = krb5_sendauth(context, &ctx->ac, &s,
544 KADMIN_OLD_APPL_VERSION, NULL,
545 server, AP_OPTS_MUTUAL_REQUIRED,
546 NULL, NULL, cc, NULL, NULL, NULL);
548 freeaddrinfo (ai);
549 if(ret) {
550 rk_closesocket(s);
551 return ret;
554 krb5_free_principal(context, server);
555 if(ctx->ccache == NULL)
556 krb5_cc_close(context, cc);
557 ctx->sock = s;
559 return 0;
562 kadm5_ret_t
563 _kadm5_connect(void *handle)
565 kadm5_client_context *ctx = handle;
566 if(ctx->sock == -1)
567 return kadm_connect(ctx);
568 return 0;
571 static kadm5_ret_t
572 kadm5_c_init_with_context(krb5_context context,
573 const char *client_name,
574 const char *password,
575 krb5_prompter_fct prompter,
576 const char *keytab,
577 krb5_ccache ccache,
578 const char *service_name,
579 kadm5_config_params *realm_params,
580 unsigned long struct_version,
581 unsigned long api_version,
582 void **server_handle)
584 kadm5_ret_t ret;
585 kadm5_client_context *ctx;
586 krb5_ccache cc;
588 ret = _kadm5_c_init_context(&ctx, realm_params, context);
589 if(ret)
590 return ret;
592 if(password != NULL && *password != '\0') {
593 ret = _kadm5_c_get_cred_cache(context,
594 client_name,
595 service_name,
596 password, prompter, keytab, ccache, &cc);
597 if(ret)
598 return ret; /* XXX */
599 ccache = cc;
603 if (client_name != NULL)
604 ctx->client_name = strdup(client_name);
605 else
606 ctx->client_name = NULL;
607 if (service_name != NULL)
608 ctx->service_name = strdup(service_name);
609 else
610 ctx->service_name = NULL;
611 ctx->prompter = prompter;
612 ctx->keytab = keytab;
613 ctx->ccache = ccache;
614 /* maybe we should copy the params here */
615 ctx->sock = -1;
617 *server_handle = ctx;
618 return 0;
621 static kadm5_ret_t
622 init_context(const char *client_name,
623 const char *password,
624 krb5_prompter_fct prompter,
625 const char *keytab,
626 krb5_ccache ccache,
627 const char *service_name,
628 kadm5_config_params *realm_params,
629 unsigned long struct_version,
630 unsigned long api_version,
631 void **server_handle)
633 krb5_context context;
634 kadm5_ret_t ret;
635 kadm5_server_context *ctx;
637 ret = krb5_init_context(&context);
638 if (ret)
639 return ret;
640 ret = kadm5_c_init_with_context(context,
641 client_name,
642 password,
643 prompter,
644 keytab,
645 ccache,
646 service_name,
647 realm_params,
648 struct_version,
649 api_version,
650 server_handle);
651 if(ret){
652 krb5_free_context(context);
653 return ret;
655 ctx = *server_handle;
656 ctx->my_context = 1;
657 return 0;
660 kadm5_ret_t
661 kadm5_c_init_with_password_ctx(krb5_context context,
662 const char *client_name,
663 const char *password,
664 const char *service_name,
665 kadm5_config_params *realm_params,
666 unsigned long struct_version,
667 unsigned long api_version,
668 void **server_handle)
670 return kadm5_c_init_with_context(context,
671 client_name,
672 password,
673 krb5_prompter_posix,
674 NULL,
675 NULL,
676 service_name,
677 realm_params,
678 struct_version,
679 api_version,
680 server_handle);
683 kadm5_ret_t
684 kadm5_c_init_with_password(const char *client_name,
685 const char *password,
686 const char *service_name,
687 kadm5_config_params *realm_params,
688 unsigned long struct_version,
689 unsigned long api_version,
690 void **server_handle)
692 return init_context(client_name,
693 password,
694 krb5_prompter_posix,
695 NULL,
696 NULL,
697 service_name,
698 realm_params,
699 struct_version,
700 api_version,
701 server_handle);
704 kadm5_ret_t
705 kadm5_c_init_with_skey_ctx(krb5_context context,
706 const char *client_name,
707 const char *keytab,
708 const char *service_name,
709 kadm5_config_params *realm_params,
710 unsigned long struct_version,
711 unsigned long api_version,
712 void **server_handle)
714 return kadm5_c_init_with_context(context,
715 client_name,
716 NULL,
717 NULL,
718 keytab,
719 NULL,
720 service_name,
721 realm_params,
722 struct_version,
723 api_version,
724 server_handle);
728 kadm5_ret_t
729 kadm5_c_init_with_skey(const char *client_name,
730 const char *keytab,
731 const char *service_name,
732 kadm5_config_params *realm_params,
733 unsigned long struct_version,
734 unsigned long api_version,
735 void **server_handle)
737 return init_context(client_name,
738 NULL,
739 NULL,
740 keytab,
741 NULL,
742 service_name,
743 realm_params,
744 struct_version,
745 api_version,
746 server_handle);
749 kadm5_ret_t
750 kadm5_c_init_with_creds_ctx(krb5_context context,
751 const char *client_name,
752 krb5_ccache ccache,
753 const char *service_name,
754 kadm5_config_params *realm_params,
755 unsigned long struct_version,
756 unsigned long api_version,
757 void **server_handle)
759 return kadm5_c_init_with_context(context,
760 client_name,
761 NULL,
762 NULL,
763 NULL,
764 ccache,
765 service_name,
766 realm_params,
767 struct_version,
768 api_version,
769 server_handle);
772 kadm5_ret_t
773 kadm5_c_init_with_creds(const char *client_name,
774 krb5_ccache ccache,
775 const char *service_name,
776 kadm5_config_params *realm_params,
777 unsigned long struct_version,
778 unsigned long api_version,
779 void **server_handle)
781 return init_context(client_name,
782 NULL,
783 NULL,
784 NULL,
785 ccache,
786 service_name,
787 realm_params,
788 struct_version,
789 api_version,
790 server_handle);
793 #if 0
794 kadm5_ret_t
795 kadm5_init(char *client_name, char *pass,
796 char *service_name,
797 kadm5_config_params *realm_params,
798 unsigned long struct_version,
799 unsigned long api_version,
800 void **server_handle)
803 #endif