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