Novell Database Abstraction Layer merge.
[krb5.git] / src / lib / kadm5 / clnt / client_init.c
1 /*
2  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
3  */
4
5 /*
6  * Copyright (C) 1998 by the FundsXpress, INC.
7  * 
8  * All rights reserved.
9  * 
10  * Export of this software from the United States of America may require
11  * a specific license from the United States Government.  It is the
12  * responsibility of any person or organization contemplating export to
13  * obtain such a license before exporting.
14  * 
15  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
16  * distribute this software and its documentation for any purpose and
17  * without fee is hereby granted, provided that the above copyright
18  * notice appear in all copies and that both that copyright notice and
19  * this permission notice appear in supporting documentation, and that
20  * the name of FundsXpress. not be used in advertising or publicity pertaining
21  * to distribution of the software without specific, written prior
22  * permission.  FundsXpress makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  * 
26  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
27  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
28  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  */
30
31 #include <stdio.h>
32 #include <netdb.h>
33 #ifdef HAVE_MEMORY_H
34 #include <memory.h>
35 #endif
36 #include <string.h>
37 #include <com_err.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <krb5.h>
42 #include <k5-int.h> /* for KRB5_ADM_DEFAULT_PORT */
43 #ifdef __STDC__
44 #include <stdlib.h>
45 #endif
46
47 #include <kadm5/admin.h>
48 #include <kadm5/kadm_rpc.h>
49 #include "client_internal.h"
50
51 #include <gssrpc/rpc.h>
52 #include <gssapi/gssapi.h>
53 #include <gssapi/gssapi_krb5.h>
54 #include <gssrpc/auth_gssapi.h>
55
56 #define ADM_CCACHE  "/tmp/ovsec_adm.XXXXXX"
57
58 enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS };
59
60 static kadm5_ret_t _kadm5_init_any(char *client_name,
61                                    enum init_type init_type,
62                                    char *pass,
63                                    krb5_ccache ccache_in,
64                                    char *service_name,
65                                    kadm5_config_params *params,
66                                    krb5_ui_4 struct_version,
67                                    krb5_ui_4 api_version,
68                                    char **db_args,
69                                    void **server_handle);
70
71 static kadm5_ret_t
72 kadm5_get_init_creds(kadm5_server_handle_t handle,
73                      char *client_name, enum init_type init_type,
74                      char *pass, krb5_ccache ccache_in,
75                      char *svcname_in, char *realm,
76                      char *full_svcname, unsigned int full_svcname_len);
77
78 static kadm5_ret_t
79 kadm5_gic_iter(kadm5_server_handle_t handle,
80                enum init_type init_type,
81                krb5_ccache ccache,
82                krb5_principal client, char *pass,
83                char *svcname, char *realm,
84                char *full_svcname, unsigned int full_svcname_len);
85
86 static kadm5_ret_t
87 kadm5_setup_gss(kadm5_server_handle_t handle,
88                 kadm5_config_params *params_in,
89                 char *client_name, char *full_svcname);
90
91 static void
92 kadm5_rpc_auth(kadm5_server_handle_t handle,
93                kadm5_config_params *params_in,
94                gss_cred_id_t gss_client_creds,
95                gss_name_t gss_target);
96
97 kadm5_ret_t kadm5_init_with_creds(char *client_name,
98                                   krb5_ccache ccache,
99                                   char *service_name,
100                                   kadm5_config_params *params,
101                                   krb5_ui_4 struct_version,
102                                   krb5_ui_4 api_version,
103                                   char **db_args,
104                                   void **server_handle)
105 {
106      return _kadm5_init_any(client_name, INIT_CREDS, NULL, ccache,
107                             service_name, params,
108                             struct_version, api_version, db_args,
109                             server_handle);
110 }
111
112
113 kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass,
114                                      char *service_name,
115                                      kadm5_config_params *params,
116                                      krb5_ui_4 struct_version,
117                                      krb5_ui_4 api_version,
118                                      char **db_args,
119                                      void **server_handle)
120 {
121      return _kadm5_init_any(client_name, INIT_PASS, pass, NULL,
122                             service_name, params, struct_version,
123                             api_version, db_args, server_handle);
124 }
125
126 kadm5_ret_t kadm5_init(char *client_name, char *pass,
127                        char *service_name, 
128                        kadm5_config_params *params,
129                        krb5_ui_4 struct_version,
130                        krb5_ui_4 api_version,
131                        char **db_args,
132                        void **server_handle)
133 {
134      return _kadm5_init_any(client_name, INIT_PASS, pass, NULL,
135                             service_name, params, struct_version,
136                             api_version, db_args, server_handle);
137 }
138
139 kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab,
140                                  char *service_name,
141                                  kadm5_config_params *params,
142                                  krb5_ui_4 struct_version,
143                                  krb5_ui_4 api_version,
144                                  char **db_args,
145                                  void **server_handle)
146 {
147      return _kadm5_init_any(client_name, INIT_SKEY, keytab, NULL,
148                             service_name, params, struct_version,
149                             api_version, db_args, server_handle);
150 }
151
152 static kadm5_ret_t _kadm5_init_any(char *client_name,
153                                    enum init_type init_type,
154                                    char *pass,
155                                    krb5_ccache ccache_in,
156                                    char *service_name,
157                                    kadm5_config_params *params_in,
158                                    krb5_ui_4 struct_version,
159                                    krb5_ui_4 api_version,
160                                    char **db_args,
161                                    void **server_handle)
162 {
163      struct sockaddr_in addr;
164      struct hostent *hp;
165      int fd;
166
167      char full_svcname[BUFSIZ];
168      char *realm;
169      
170      kadm5_server_handle_t handle;
171      kadm5_config_params params_local;
172
173      int code = 0;
174      generic_ret *r;
175
176      initialize_ovk_error_table();
177 /*      initialize_adb_error_table(); */
178      initialize_ovku_error_table();
179      
180      if (! server_handle) {
181          return EINVAL;
182      }
183
184      if (! (handle = malloc(sizeof(*handle)))) {
185           return ENOMEM;
186      }
187      if (! (handle->lhandle = malloc(sizeof(*handle)))) {
188           free(handle);
189           return ENOMEM;
190      }
191
192      handle->magic_number = KADM5_SERVER_HANDLE_MAGIC;
193      handle->struct_version = struct_version;
194      handle->api_version = api_version;
195      handle->clnt = 0;
196      handle->cache_name = 0;
197      handle->destroy_cache = 0;
198      *handle->lhandle = *handle;
199      handle->lhandle->api_version = KADM5_API_VERSION_2;
200      handle->lhandle->struct_version = KADM5_STRUCT_VERSION;
201      handle->lhandle->lhandle = handle->lhandle;
202
203      krb5_init_context(&handle->context);
204
205      if(client_name == NULL) {
206         free(handle);
207         return EINVAL;
208      }
209
210      /*
211       * Verify the version numbers before proceeding; we can't use
212       * CHECK_HANDLE because not all fields are set yet.
213       */
214      GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION,
215                           KADM5_NEW_LIB_API_VERSION);
216      
217      /*
218       * Acquire relevant profile entries.  In version 2, merge values
219       * in params_in with values from profile, based on
220       * params_in->mask.
221       *
222       * In version 1, we've given a realm (which may be NULL) instead
223       * of params_in.  So use that realm, make params_in contain an
224       * empty mask, and behave like version 2.
225       */
226      memset((char *) &params_local, 0, sizeof(params_local));
227      if (api_version == KADM5_API_VERSION_1) {
228           realm = params_local.realm = (char *) params_in;
229           if (params_in)
230                params_local.mask = KADM5_CONFIG_REALM;
231
232           /* Use old AUTH_GSSAPI for version 1 protocol. */
233           params_local.mask |= KADM5_CONFIG_OLD_AUTH_GSSAPI;
234           params_in = &params_local;
235      } else {
236           if (params_in && (params_in->mask & KADM5_CONFIG_REALM))
237                realm = params_in->realm;
238           else
239                realm = NULL;
240      }
241
242 #define ILLEGAL_PARAMS (KADM5_CONFIG_DBNAME | KADM5_CONFIG_ADBNAME | \
243                         KADM5_CONFIG_ADB_LOCKFILE | \
244                         KADM5_CONFIG_ACL_FILE | KADM5_CONFIG_DICT_FILE \
245                         | KADM5_CONFIG_ADMIN_KEYTAB | \
246                         KADM5_CONFIG_STASH_FILE | \
247                         KADM5_CONFIG_MKEY_NAME | KADM5_CONFIG_ENCTYPE \
248                         | KADM5_CONFIG_MAX_LIFE | \
249                         KADM5_CONFIG_MAX_RLIFE | \
250                         KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_FLAGS | \
251                         KADM5_CONFIG_ENCTYPES | KADM5_CONFIG_MKEY_FROM_KBD)
252
253      if (params_in && params_in->mask & ILLEGAL_PARAMS) {
254           free(handle);
255           return KADM5_BAD_CLIENT_PARAMS;
256      }
257                         
258      if ((code = kadm5_get_config_params(handle->context,
259                                         DEFAULT_PROFILE_PATH,
260                                         "KRB5_CONFIG",
261                                         params_in,
262                                         &handle->params))) {
263           krb5_free_context(handle->context);
264           free(handle);
265           return(code);
266      }
267
268 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \
269                          KADM5_CONFIG_ADMIN_SERVER | \
270                          KADM5_CONFIG_KADMIND_PORT) 
271
272      if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
273           krb5_free_context(handle->context);
274           free(handle);
275           return KADM5_MISSING_KRB5_CONF_PARAMS;
276      }
277
278      /*
279       * Get credentials.  Also does some fallbacks in case kadmin/fqdn
280       * principal doesn't exist.
281       */
282      code = kadm5_get_init_creds(handle, client_name, init_type, pass,
283                                  ccache_in, service_name, realm,
284                                  full_svcname, sizeof(full_svcname));
285      if (code)
286           goto error;
287      /*
288       * We have ticket; open the RPC connection.
289       */
290
291      hp = gethostbyname(handle->params.admin_server);
292      if (hp == (struct hostent *) NULL) {
293           code = KADM5_BAD_SERVER_NAME;
294           goto cleanup;
295      }
296
297      memset(&addr, 0, sizeof(addr));
298      addr.sin_family = hp->h_addrtype;
299      (void) memcpy((char *) &addr.sin_addr, (char *) hp->h_addr,
300                    sizeof(addr.sin_addr));
301      addr.sin_port = htons((u_short) handle->params.kadmind_port);
302      
303      fd = RPC_ANYSOCK;
304      
305      handle->clnt = clnttcp_create(&addr, KADM, KADMVERS, &fd, 0, 0);
306      if (handle->clnt == NULL) {
307           code = KADM5_RPC_ERROR;
308 #ifdef DEBUG
309           clnt_pcreateerror("clnttcp_create");
310 #endif
311           goto error;
312      }
313      handle->lhandle->clnt = handle->clnt;
314
315      /* now that handle->clnt is set, we can check the handle */
316      if ((code = _kadm5_check_handle((void *) handle)))
317           goto error;
318
319      /*
320       * The RPC connection is open; establish the GSS-API
321       * authentication context.
322       */
323      code = kadm5_setup_gss(handle, params_in, client_name, full_svcname);
324      if (code)
325           goto error;
326
327      r = init_1(&handle->api_version, handle->clnt);
328      if (r == NULL) {
329           code = KADM5_RPC_ERROR;
330 #ifdef DEBUG
331           clnt_perror(handle->clnt, "init_1 null resp");
332 #endif
333           goto error;
334      }
335      if (r->code) {
336           code = r->code;
337           goto error;
338      }
339
340      *server_handle = (void *) handle;
341
342      goto cleanup;
343
344 error:
345      /*
346       * Note that it is illegal for this code to execute if "handle"
347       * has not been allocated and initialized.  I.e., don't use "goto
348       * error" before the block of code at the top of the function
349       * that allocates and initializes "handle".
350       */
351      if (handle->cache_name)
352          free(handle->cache_name);
353      if(handle->clnt && handle->clnt->cl_auth)
354           AUTH_DESTROY(handle->clnt->cl_auth);
355      if(handle->clnt)
356           clnt_destroy(handle->clnt);
357
358 cleanup:
359      if (code)
360           free(handle);
361
362      return code;
363 }
364
365 /*
366  * kadm5_get_init_creds
367  *
368  * Get initial credentials for authenticating to server.  Perform
369  * fallback from kadmin/fqdn to kadmin/admin if svcname_in is NULL.
370  */
371 static kadm5_ret_t
372 kadm5_get_init_creds(kadm5_server_handle_t handle,
373                      char *client_name, enum init_type init_type,
374                      char *pass, krb5_ccache ccache_in,
375                      char *svcname_in, char *realm,
376                      char *full_svcname, unsigned int full_svcname_len)
377 {
378      kadm5_ret_t code;
379      krb5_principal client;
380      krb5_ccache ccache;
381      char svcname[BUFSIZ];
382
383      client = NULL;
384      ccache = NULL;
385      /* NULL svcname means use host-based. */
386      if (svcname_in == NULL) {
387           code = kadm5_get_admin_service_name(handle->context,
388                                               handle->params.realm,
389                                               svcname, sizeof(svcname));
390           if (code) {
391                code = KADM5_MISSING_KRB5_CONF_PARAMS;
392                goto error;
393           }
394      } else {
395           strncpy(svcname, svcname_in, sizeof(svcname));
396           svcname[sizeof(svcname)-1] = '\0';
397      }
398      /*
399       * Acquire a service ticket for svcname@realm in the name of
400       * client_name, using password pass (which could be NULL), and
401       * create a ccache to store them in.  If INIT_CREDS, use the
402       * ccache we were provided instead.
403       */
404      code = krb5_parse_name(handle->context, client_name, &client);
405      if (code)
406           goto error;
407
408      if (init_type == INIT_CREDS) {
409           ccache = ccache_in;
410           handle->cache_name = (char *)
411                malloc(strlen(krb5_cc_get_type(handle->context, ccache)) +
412                       strlen(krb5_cc_get_name(handle->context, ccache)) + 2);
413           if (handle->cache_name == NULL) {
414                code = ENOMEM;
415                goto error;
416           }
417           sprintf(handle->cache_name, "%s:%s",
418                   krb5_cc_get_type(handle->context, ccache),
419                   krb5_cc_get_name(handle->context, ccache));
420      } else {
421           static int counter = 0;
422
423           handle->cache_name = malloc(sizeof("MEMORY:kadm5_")
424                                       + 3*sizeof(counter));
425           sprintf(handle->cache_name, "MEMORY:kadm5_%u", counter++);
426
427           code = krb5_cc_resolve(handle->context, handle->cache_name,
428                                  &ccache);
429           if (code) 
430                goto error;
431
432           code = krb5_cc_initialize (handle->context, ccache, client);
433           if (code) 
434                goto error;
435
436           handle->destroy_cache = 1;
437      }
438      handle->lhandle->cache_name = handle->cache_name;
439
440      code = kadm5_gic_iter(handle, init_type, ccache,
441                            client, pass, svcname, realm,
442                            full_svcname, full_svcname_len);
443      if ((code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
444           || code == KRB5_CC_NOTFOUND) && svcname_in == NULL) {
445           /* Retry with old host-independent service princpal. */
446           code = kadm5_gic_iter(handle, init_type, ccache,
447                                 client, pass,
448                                 KADM5_ADMIN_SERVICE, realm,
449                                 full_svcname, full_svcname_len);
450      }
451      /* Improved error messages */
452      if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD;
453      if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
454           code = KADM5_SECURE_PRINC_MISSING;
455
456 error:
457      if (ccache != NULL && init_type != INIT_CREDS)
458           krb5_cc_close(handle->context, ccache);
459      return code;
460 }
461
462 /*
463  * kadm5_gic_iter
464  *
465  * Perform one iteration of attempting to get credentials.  This
466  * includes searching existing ccache for requested service if
467  * INIT_CREDS.
468  */
469 static kadm5_ret_t
470 kadm5_gic_iter(kadm5_server_handle_t handle,
471                enum init_type init_type,
472                krb5_ccache ccache,
473                krb5_principal client, char *pass,
474                char *svcname, char *realm,
475                char *full_svcname, unsigned int full_svcname_len)
476 {
477      kadm5_ret_t code;
478      krb5_context ctx;
479      krb5_keytab kt;
480      krb5_get_init_creds_opt opt;
481      krb5_creds mcreds, outcreds;
482
483      ctx = handle->context;
484      kt = NULL;
485      memset(full_svcname, 0, full_svcname_len);
486      memset(&opt, 0, sizeof(opt));
487      memset(&mcreds, 0, sizeof(mcreds));
488      memset(&outcreds, 0, sizeof(outcreds));
489
490      code = ENOMEM;
491      if (realm) {
492           if ((strlen(svcname) + strlen(realm) + 1) >= full_svcname_len)
493                goto error;
494           sprintf(full_svcname, "%s@%s", svcname, realm);
495      } else {
496           /* krb5_princ_realm(client) is not null terminated */
497           if ((strlen(svcname) + krb5_princ_realm(ctx, client)->length + 1)
498               >= full_svcname_len)
499                goto error;
500
501           strcpy(full_svcname, svcname);
502           strcat(full_svcname, "@");
503           strncat(full_svcname,
504                   krb5_princ_realm(ctx, client)->data,
505                   krb5_princ_realm(ctx, client)->length);
506      }
507
508      if (init_type != INIT_CREDS)
509           krb5_get_init_creds_opt_init(&opt);
510
511      if (init_type == INIT_PASS) {
512           code = krb5_get_init_creds_password(ctx, &outcreds, client, pass,
513                                               krb5_prompter_posix,
514                                               NULL, 0,
515                                               full_svcname, &opt);
516           if (code)
517                goto error;
518      } else if (init_type == INIT_SKEY) {
519           if (pass) {
520                code = krb5_kt_resolve(ctx, pass, &kt);
521                if (code)
522                    goto error;
523           }
524           code = krb5_get_init_creds_keytab(ctx, &outcreds, client, kt,
525                                             0, full_svcname, &opt);
526           if (pass)
527                krb5_kt_close(ctx, kt);
528           if (code)
529                goto error;
530      } else if (init_type == INIT_CREDS) {
531           mcreds.client = client;
532           code = krb5_parse_name(ctx, full_svcname, &mcreds.server);
533           if (code)
534                goto error;
535           code = krb5_cc_retrieve_cred(ctx, ccache, 0,
536                                        &mcreds, &outcreds);
537           krb5_free_principal(ctx, mcreds.server);
538           if (code)
539                goto error;
540      }
541      if (init_type != INIT_CREDS) {
542           /* Caller has initialized ccache. */
543           code = krb5_cc_store_cred(ctx, ccache, &outcreds);
544           if (code)
545                goto error;
546      }
547 error:
548      krb5_free_cred_contents(ctx, &outcreds);
549      return code;
550 }
551
552 /*
553  * kadm5_setup_gss
554  *
555  * Acquire GSSAPI credentials and set up RPC auth flavor.
556  */
557 static kadm5_ret_t
558 kadm5_setup_gss(kadm5_server_handle_t handle,
559                 kadm5_config_params *params_in,
560                 char *client_name, char *full_svcname)
561 {
562      kadm5_ret_t code;
563      OM_uint32 gssstat, minor_stat;
564      gss_buffer_desc buf;
565      gss_name_t gss_client;
566      gss_name_t gss_target;
567      gss_cred_id_t gss_client_creds;
568      const char *c_ccname_orig;
569      char *ccname_orig;
570
571      code = KADM5_GSS_ERROR;
572      gss_client_creds = GSS_C_NO_CREDENTIAL;
573      ccname_orig = NULL;
574
575      /* Temporarily use the kadm5 cache. */
576      gssstat = gss_krb5_ccache_name(&minor_stat, handle->cache_name,
577                                     &c_ccname_orig);
578      if (gssstat != GSS_S_COMPLETE) {
579           code = KADM5_GSS_ERROR;
580           goto error;
581      }
582      if (c_ccname_orig)
583           ccname_orig = strdup(c_ccname_orig);
584      else
585           ccname_orig = 0;
586
587      buf.value = full_svcname;
588      buf.length = strlen((char *)buf.value) + 1;
589      gssstat = gss_import_name(&minor_stat, &buf,
590                                (gss_OID) gss_nt_krb5_name, &gss_target);
591      if (gssstat != GSS_S_COMPLETE) {
592           code = KADM5_GSS_ERROR;
593           goto error;
594      }
595
596      buf.value = client_name;
597      buf.length = strlen((char *)buf.value) + 1;
598      gssstat = gss_import_name(&minor_stat, &buf,
599                                (gss_OID) gss_nt_krb5_name, &gss_client);
600      if (gssstat != GSS_S_COMPLETE) {
601           code = KADM5_GSS_ERROR;
602           goto error;
603      }
604
605      gssstat = gss_acquire_cred(&minor_stat, gss_client, 0,
606                                 GSS_C_NULL_OID_SET, GSS_C_INITIATE,
607                                 &gss_client_creds, NULL, NULL);
608      if (gssstat != GSS_S_COMPLETE) {
609           code = KADM5_GSS_ERROR;
610           goto error;
611      }
612
613      /*
614       * Do actual creation of RPC auth handle.  Implements auth flavor
615       * fallback.
616       */
617      kadm5_rpc_auth(handle, params_in, gss_client_creds, gss_target);
618
619 error:
620      if (gss_client_creds != GSS_C_NO_CREDENTIAL)
621           (void) gss_release_cred(&minor_stat, &gss_client_creds);
622
623      if (gss_client)
624           gss_release_name(&minor_stat, &gss_client);
625      if (gss_target)
626           gss_release_name(&minor_stat, &gss_target);
627
628      /* Revert to prior gss_krb5 ccache. */
629      if (ccname_orig) {
630          gssstat = gss_krb5_ccache_name(&minor_stat, ccname_orig, NULL);
631          if (gssstat) {
632              return KADM5_GSS_ERROR;
633          }
634          free(ccname_orig);
635      } else {
636          gssstat = gss_krb5_ccache_name(&minor_stat, NULL, NULL);
637          if (gssstat) {
638              return KADM5_GSS_ERROR;
639          }
640      }
641
642      if (handle->clnt->cl_auth == NULL) {
643           return KADM5_GSS_ERROR;
644      }
645      return 0;
646 }
647
648 /*
649  * kadm5_rpc_auth
650  *
651  * Create RPC auth handle.  Do auth flavor fallback if needed.
652  */
653 static void
654 kadm5_rpc_auth(kadm5_server_handle_t handle,
655                kadm5_config_params *params_in,
656                gss_cred_id_t gss_client_creds,
657                gss_name_t gss_target)
658 {
659      OM_uint32 gssstat, minor_stat;
660      struct rpc_gss_sec sec;
661
662      /* Allow unauthenticated option for testing. */
663      if (params_in != NULL && (params_in->mask & KADM5_CONFIG_NO_AUTH))
664           return;
665
666      /* Use RPCSEC_GSS by default. */
667      if (params_in == NULL ||
668          !(params_in->mask & KADM5_CONFIG_OLD_AUTH_GSSAPI)) {
669           sec.mech = gss_mech_krb5;
670           sec.qop = GSS_C_QOP_DEFAULT;
671           sec.svc = RPCSEC_GSS_SVC_PRIVACY;
672           sec.cred = gss_client_creds;
673           sec.req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
674
675           handle->clnt->cl_auth = authgss_create(handle->clnt,
676                                                  gss_target, &sec);
677           if (handle->clnt->cl_auth != NULL)
678                return;
679      }
680
681      if (params_in != NULL && (params_in->mask & KADM5_CONFIG_AUTH_NOFALLBACK))
682           return;
683
684      /* Fall back to old AUTH_GSSAPI. */
685      handle->clnt->cl_auth = auth_gssapi_create(handle->clnt,
686                                                 &gssstat,
687                                                 &minor_stat,
688                                                 gss_client_creds,
689                                                 gss_target,
690                                                 (gss_OID) gss_mech_krb5,
691                                                 GSS_C_MUTUAL_FLAG
692                                                 | GSS_C_REPLAY_FLAG,
693                                                 0, NULL, NULL, NULL);
694 }
695
696 kadm5_ret_t
697 kadm5_destroy(void *server_handle)
698 {
699      krb5_ccache            ccache = NULL;
700      int                    code = KADM5_OK;
701      kadm5_server_handle_t      handle =
702           (kadm5_server_handle_t) server_handle;
703
704      CHECK_HANDLE(server_handle);
705
706      if (handle->destroy_cache && handle->cache_name) {
707          if ((code = krb5_cc_resolve(handle->context,
708                                      handle->cache_name, &ccache)) == 0) 
709              code = krb5_cc_destroy (handle->context, ccache);
710      }
711      if (handle->cache_name)
712          free(handle->cache_name);
713      if (handle->clnt && handle->clnt->cl_auth)
714           AUTH_DESTROY(handle->clnt->cl_auth);
715      if (handle->clnt)
716           clnt_destroy(handle->clnt);
717      if (handle->lhandle)
718           free (handle->lhandle);
719
720      kadm5_free_config_params(handle->context, &handle->params);
721      krb5_free_context(handle->context);
722
723      handle->magic_number = 0;
724      free(handle);
725
726      return code;
727 }
728 /* not supported on client */
729 kadm5_ret_t kadm5_lock(void *server_handle)
730 {
731     return EINVAL;
732 }
733
734 /* not supported on client */
735 kadm5_ret_t kadm5_unlock(void *server_handle)
736 {
737     return EINVAL;
738 }
739
740 kadm5_ret_t kadm5_flush(void *server_handle)
741 {
742      return KADM5_OK;
743 }
744
745 int _kadm5_check_handle(void *handle)
746 {
747      CHECK_HANDLE(handle);
748      return 0;
749 }