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