xref: /freebsd/crypto/heimdal/lib/kadm5/init_c.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
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 
40 RCSID("$Id: init_c.c,v 1.40 2000/12/31 08:00:23 assar Exp $");
41 
42 static void
43 set_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 
60 kadm5_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 
112 static krb5_error_code
113 get_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 
133 static krb5_error_code
134 get_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 
202 static krb5_error_code
203 get_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 
291 static kadm5_ret_t
292 kadm_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 
408 kadm5_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 
417 static kadm5_ret_t
418 kadm5_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 
465 static kadm5_ret_t
466 init_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 
504 kadm5_ret_t
505 kadm5_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 
527 kadm5_ret_t
528 kadm5_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 
548 kadm5_ret_t
549 kadm5_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 
572 kadm5_ret_t
573 kadm5_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 
593 kadm5_ret_t
594 kadm5_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 
616 kadm5_ret_t
617 kadm5_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
638 kadm5_ret_t
639 kadm5_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