735ddfd317c008bccfc2900d630a6d69b0c1b7d2
[krb5.git] / src / lib / kadm5 / clnt / client_init.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
4  */
5
6 /*
7  * Copyright (C) 1998 by the FundsXpress, INC.
8  *
9  * All rights reserved.
10  *
11  * Export of this software from the United States of America may require
12  * a specific license from the United States Government.  It is the
13  * responsibility of any person or organization contemplating export to
14  * obtain such a license before exporting.
15  *
16  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
17  * distribute this software and its documentation for any purpose and
18  * without fee is hereby granted, provided that the above copyright
19  * notice appear in all copies and that both that copyright notice and
20  * this permission notice appear in supporting documentation, and that
21  * the name of FundsXpress. not be used in advertising or publicity pertaining
22  * to distribution of the software without specific, written prior
23  * permission.  FundsXpress makes no representations about the suitability of
24  * this software for any purpose.  It is provided "as is" without express
25  * or implied warranty.
26  *
27  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
28  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
29  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  */
31
32 #include <stdio.h>
33 #include <netdb.h>
34 #include "autoconf.h"
35 #ifdef HAVE_MEMORY_H
36 #include <memory.h>
37 #endif
38 #include <string.h>
39 #include <com_err.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <fake-addrinfo.h>
44 #include <k5-int.h> /* for KRB5_ADM_DEFAULT_PORT */
45 #include <krb5.h>
46 #ifdef __STDC__
47 #include <stdlib.h>
48 #endif
49
50 #include <kadm5/admin.h>
51 #include <kadm5/kadm_rpc.h>
52 #include "client_internal.h"
53 #include <iprop_hdr.h>
54 #include "iprop.h"
55
56 #include <gssrpc/rpc.h>
57 #include <gssapi/gssapi.h>
58 #include <gssapi/gssapi_krb5.h>
59 #include <gssrpc/auth_gssapi.h>
60
61 #define ADM_CCACHE  "/tmp/ovsec_adm.XXXXXX"
62
63 enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS, INIT_ANONYMOUS };
64
65 static kadm5_ret_t
66 init_any(krb5_context context, char *client_name, enum init_type init_type,
67          char *pass, krb5_ccache ccache_in, char *service_name,
68          kadm5_config_params *params, krb5_ui_4 struct_version,
69          krb5_ui_4 api_version, char **db_args, void **server_handle);
70
71 static kadm5_ret_t
72 get_init_creds(kadm5_server_handle_t handle, char *client_name,
73                enum init_type init_type, char *pass, krb5_ccache ccache_in,
74                char *svcname_in, char *realm, char *full_svcname,
75                unsigned int full_svcname_len);
76
77 static kadm5_ret_t
78 gic_iter(kadm5_server_handle_t handle, enum init_type init_type,
79          krb5_ccache ccache, krb5_principal client, char *pass,
80          char *svcname, char *realm, char *full_svcname,
81          unsigned int full_svcname_len);
82
83 static kadm5_ret_t
84 connect_to_server(const char *hostname, int port, int *fd);
85
86 static kadm5_ret_t
87 setup_gss(kadm5_server_handle_t handle, kadm5_config_params *params_in,
88           char *client_name, char *full_svcname);
89
90 static void
91 rpc_auth(kadm5_server_handle_t handle, kadm5_config_params *params_in,
92          gss_cred_id_t gss_client_creds, gss_name_t gss_target);
93
94 kadm5_ret_t
95 kadm5_init_with_creds(krb5_context context, char *client_name,
96                       krb5_ccache ccache, char *service_name,
97                       kadm5_config_params *params, krb5_ui_4 struct_version,
98                       krb5_ui_4 api_version, char **db_args,
99                       void **server_handle)
100 {
101     return init_any(context, client_name, INIT_CREDS, NULL, ccache,
102                     service_name, params, struct_version, api_version, db_args,
103                     server_handle);
104 }
105
106 kadm5_ret_t
107 kadm5_init_with_password(krb5_context context, char *client_name,
108                          char *pass, char *service_name,
109                          kadm5_config_params *params, krb5_ui_4 struct_version,
110                          krb5_ui_4 api_version, char **db_args,
111                          void **server_handle)
112 {
113     return init_any(context, client_name, INIT_PASS, pass, NULL, service_name,
114                     params, struct_version, api_version, db_args,
115                     server_handle);
116 }
117
118 kadm5_ret_t
119 kadm5_init_anonymous(krb5_context context, char *client_name,
120                      char *service_name, kadm5_config_params *params,
121                      krb5_ui_4 struct_version, krb5_ui_4 api_version,
122                      char **db_args, void **server_handle)
123 {
124     return init_any(context, client_name, INIT_ANONYMOUS, NULL, NULL,
125                     service_name, params, struct_version, api_version,
126                     db_args, server_handle);
127 }
128
129 kadm5_ret_t
130 kadm5_init(krb5_context context, char *client_name, char *pass,
131            char *service_name, kadm5_config_params *params,
132            krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args,
133            void **server_handle)
134 {
135     return init_any(context, client_name, INIT_PASS, pass, NULL, service_name,
136                     params, struct_version, api_version, db_args,
137                     server_handle);
138 }
139
140 kadm5_ret_t
141 kadm5_init_with_skey(krb5_context context, char *client_name,
142                      char *keytab, char *service_name,
143                      kadm5_config_params *params, krb5_ui_4 struct_version,
144                      krb5_ui_4 api_version, char **db_args,
145                      void **server_handle)
146 {
147     return init_any(context, client_name, INIT_SKEY, keytab, NULL,
148                     service_name, params, struct_version, api_version, db_args,
149                     server_handle);
150 }
151
152 static kadm5_ret_t
153 init_any(krb5_context context, char *client_name, enum init_type init_type,
154          char *pass, krb5_ccache ccache_in, char *service_name,
155          kadm5_config_params *params_in, krb5_ui_4 struct_version,
156          krb5_ui_4 api_version, char **db_args, void **server_handle)
157 {
158     int fd;
159
160     krb5_boolean iprop_enable;
161     int port;
162     rpcprog_t rpc_prog;
163     rpcvers_t rpc_vers;
164     char full_svcname[BUFSIZ];
165     char *realm;
166     krb5_ccache ccache;
167
168     kadm5_server_handle_t handle;
169     kadm5_config_params params_local;
170
171     int code = 0;
172     generic_ret *r;
173
174     initialize_ovk_error_table();
175 /*      initialize_adb_error_table(); */
176     initialize_ovku_error_table();
177
178     if (! server_handle) {
179         return EINVAL;
180     }
181
182     if (! (handle = malloc(sizeof(*handle)))) {
183         return ENOMEM;
184     }
185     memset(handle, 0, sizeof(*handle));
186     if (! (handle->lhandle = malloc(sizeof(*handle)))) {
187         free(handle);
188         return ENOMEM;
189     }
190
191     handle->magic_number = KADM5_SERVER_HANDLE_MAGIC;
192     handle->struct_version = struct_version;
193     handle->api_version = api_version;
194     handle->clnt = 0;
195     handle->cache_name = 0;
196     handle->destroy_cache = 0;
197     handle->context = 0;
198     *handle->lhandle = *handle;
199     handle->lhandle->api_version = KADM5_API_VERSION_3;
200     handle->lhandle->struct_version = KADM5_STRUCT_VERSION;
201     handle->lhandle->lhandle = handle->lhandle;
202
203     handle->context = 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(&params_local, 0, sizeof(params_local));
227     if (params_in && (params_in->mask & KADM5_CONFIG_REALM))
228         realm = params_in->realm;
229     else
230         realm = NULL;
231
232 #if 0 /* Since KDC config params can now be put in krb5.conf, these
233          could show up even when you're just using the remote kadmin
234          client.  */
235 #define ILLEGAL_PARAMS (KADM5_CONFIG_DBNAME | KADM5_CONFIG_ADBNAME |    \
236                         KADM5_CONFIG_ADB_LOCKFILE |                     \
237                         KADM5_CONFIG_ACL_FILE | KADM5_CONFIG_DICT_FILE  \
238                         | KADM5_CONFIG_ADMIN_KEYTAB |                   \
239                         KADM5_CONFIG_STASH_FILE |                       \
240                         KADM5_CONFIG_MKEY_NAME | KADM5_CONFIG_ENCTYPE   \
241                         | KADM5_CONFIG_MAX_LIFE |                       \
242                         KADM5_CONFIG_MAX_RLIFE |                        \
243                         KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_FLAGS |  \
244                         KADM5_CONFIG_ENCTYPES | KADM5_CONFIG_MKEY_FROM_KBD)
245
246     if (params_in && params_in->mask & ILLEGAL_PARAMS) {
247         free(handle);
248         return KADM5_BAD_CLIENT_PARAMS;
249     }
250 #endif
251
252     if ((code = kadm5_get_config_params(handle->context, 0,
253                                         params_in, &handle->params))) {
254         free(handle);
255         return(code);
256     }
257
258 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM |           \
259                          KADM5_CONFIG_ADMIN_SERVER |    \
260                          KADM5_CONFIG_KADMIND_PORT)
261
262     if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
263         free(handle);
264         return KADM5_MISSING_KRB5_CONF_PARAMS;
265     }
266
267     /*
268      * Get credentials.  Also does some fallbacks in case kadmin/fqdn
269      * principal doesn't exist.
270      */
271     code = get_init_creds(handle, client_name, init_type, pass, ccache_in,
272                           service_name, realm, full_svcname,
273                           sizeof(full_svcname));
274     if (code)
275         goto error;
276
277     /* If the service_name and client_name are iprop-centric, use the iprop
278      * port and RPC identifiers. */
279     iprop_enable = (service_name != NULL &&
280                     strstr(service_name, KIPROP_SVC_NAME) != NULL &&
281                     strstr(client_name, KIPROP_SVC_NAME) != NULL);
282     if (iprop_enable) {
283         port = handle->params.iprop_port;
284         rpc_prog = KRB5_IPROP_PROG;
285         rpc_vers = KRB5_IPROP_VERS;
286     } else {
287         port = handle->params.kadmind_port;
288         rpc_prog = KADM;
289         rpc_vers = KADMVERS;
290     }
291
292     code = connect_to_server(handle->params.admin_server, port, &fd);
293     if (code)
294         goto error;
295
296     handle->clnt = clnttcp_create(NULL, rpc_prog, rpc_vers, &fd, 0, 0);
297     if (handle->clnt == NULL) {
298         code = KADM5_RPC_ERROR;
299 #ifdef DEBUG
300         clnt_pcreateerror("clnttcp_create");
301 #endif
302         goto error;
303     }
304     handle->lhandle->clnt = handle->clnt;
305
306     /* now that handle->clnt is set, we can check the handle */
307     if ((code = _kadm5_check_handle((void *) handle)))
308         goto error;
309
310     /*
311      * The RPC connection is open; establish the GSS-API
312      * authentication context.
313      */
314     code = setup_gss(handle, params_in,
315                      (init_type == INIT_CREDS) ? client_name : NULL,
316                      full_svcname);
317     if (code)
318         goto error;
319
320     /*
321      * Bypass the remainder of the code and return straightaway
322      * if the gss service requested is kiprop
323      */
324     if (iprop_enable) {
325         code = 0;
326         *server_handle = (void *) handle;
327         goto cleanup;
328     }
329
330     r = init_2(&handle->api_version, handle->clnt);
331     if (r == NULL) {
332         code = KADM5_RPC_ERROR;
333 #ifdef DEBUG
334         clnt_perror(handle->clnt, "init_2 null resp");
335 #endif
336         goto error;
337     }
338     /* Drop down to v2 wire protocol if server does not support v3 */
339     if (r->code == KADM5_NEW_SERVER_API_VERSION &&
340         handle->api_version == KADM5_API_VERSION_3) {
341         handle->api_version = KADM5_API_VERSION_2;
342         r = init_2(&handle->api_version, handle->clnt);
343         if (r == NULL) {
344             code = KADM5_RPC_ERROR;
345             goto error;
346         }
347     }
348     if (r->code) {
349         code = r->code;
350         goto error;
351     }
352
353     *server_handle = (void *) handle;
354
355     goto cleanup;
356
357 error:
358     /*
359      * Note that it is illegal for this code to execute if "handle"
360      * has not been allocated and initialized.  I.e., don't use "goto
361      * error" before the block of code at the top of the function
362      * that allocates and initializes "handle".
363      */
364     if (handle->destroy_cache && handle->cache_name) {
365         if (krb5_cc_resolve(handle->context,
366                             handle->cache_name, &ccache) == 0)
367             (void) krb5_cc_destroy (handle->context, ccache);
368     }
369     if (handle->cache_name)
370         free(handle->cache_name);
371     if(handle->clnt && handle->clnt->cl_auth)
372         AUTH_DESTROY(handle->clnt->cl_auth);
373     if(handle->clnt)
374         clnt_destroy(handle->clnt);
375
376     kadm5_free_config_params(handle->context, &handle->params);
377
378 cleanup:
379     if (code)
380         free(handle);
381
382     return code;
383 }
384
385 /* Get initial credentials for authenticating to server.  Perform fallback from
386  * kadmin/fqdn to kadmin/admin if svcname_in is NULL. */
387 static kadm5_ret_t
388 get_init_creds(kadm5_server_handle_t handle, char *client_name,
389                enum init_type init_type, char *pass, krb5_ccache ccache_in,
390                char *svcname_in, char *realm, char *full_svcname,
391                unsigned int full_svcname_len)
392 {
393     kadm5_ret_t code;
394     krb5_principal client = NULL;
395     krb5_ccache ccache = NULL;
396     char svcname[BUFSIZ];
397
398     /* NULL svcname means use host-based. */
399     if (svcname_in == NULL) {
400         code = kadm5_get_admin_service_name(handle->context,
401                                             handle->params.realm,
402                                             svcname, sizeof(svcname));
403         if (code)
404             goto error;
405     } else {
406         strncpy(svcname, svcname_in, sizeof(svcname));
407         svcname[sizeof(svcname)-1] = '\0';
408     }
409     /*
410      * Acquire a service ticket for svcname@realm in the name of
411      * client_name, using password pass (which could be NULL), and
412      * create a ccache to store them in.  If INIT_CREDS, use the
413      * ccache we were provided instead.
414      */
415     code = krb5_parse_name(handle->context, client_name, &client);
416     if (code)
417         goto error;
418
419     if (init_type == INIT_CREDS) {
420         ccache = ccache_in;
421         if (asprintf(&handle->cache_name, "%s:%s",
422                      krb5_cc_get_type(handle->context, ccache),
423                      krb5_cc_get_name(handle->context, ccache)) < 0) {
424             handle->cache_name = NULL;
425             code = ENOMEM;
426             goto error;
427         }
428     } else {
429         static int counter = 0;
430
431         if (asprintf(&handle->cache_name, "MEMORY:kadm5_%u", counter++) < 0) {
432             handle->cache_name = NULL;
433             code = ENOMEM;
434             goto error;
435         }
436         code = krb5_cc_resolve(handle->context, handle->cache_name,
437                                &ccache);
438         if (code)
439             goto error;
440
441         code = krb5_cc_initialize (handle->context, ccache, client);
442         if (code)
443             goto error;
444
445         handle->destroy_cache = 1;
446     }
447     handle->lhandle->cache_name = handle->cache_name;
448
449     code = gic_iter(handle, init_type, ccache, client, pass, svcname, realm,
450                     full_svcname, full_svcname_len);
451     if ((code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
452          || code == KRB5_CC_NOTFOUND) && svcname_in == NULL) {
453         /* Retry with old host-independent service principal. */
454         code = gic_iter(handle, init_type, ccache, client, pass,
455                         KADM5_ADMIN_SERVICE, realm, full_svcname,
456                         full_svcname_len);
457     }
458     /* Improved error messages */
459     if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD;
460     if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
461         code = KADM5_SECURE_PRINC_MISSING;
462
463 error:
464     krb5_free_principal(handle->context, client);
465     if (ccache != NULL && init_type != INIT_CREDS)
466         krb5_cc_close(handle->context, ccache);
467     return code;
468 }
469
470 /* Perform one iteration of attempting to get credentials.  This includes
471  * searching existing ccache for requested service if INIT_CREDS. */
472 static kadm5_ret_t
473 gic_iter(kadm5_server_handle_t handle, enum init_type init_type,
474          krb5_ccache ccache, krb5_principal client, char *pass, char *svcname,
475          char *realm, 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 = NULL;
481     krb5_creds mcreds, outcreds;
482     int n;
483
484     ctx = handle->context;
485     kt = NULL;
486     memset(full_svcname, 0, full_svcname_len);
487     memset(&opt, 0, sizeof(opt));
488     memset(&mcreds, 0, sizeof(mcreds));
489     memset(&outcreds, 0, sizeof(outcreds));
490
491     code = ENOMEM;
492     if (realm) {
493         n = snprintf(full_svcname, full_svcname_len, "%s@%s",
494                      svcname, realm);
495         if (n < 0 || n >= (int) full_svcname_len)
496             goto error;
497     } else {
498         /* krb5_princ_realm(client) is not null terminated */
499         n = snprintf(full_svcname, full_svcname_len, "%s@%.*s",
500                      svcname, krb5_princ_realm(ctx, client)->length,
501                      krb5_princ_realm(ctx, client)->data);
502         if (n < 0 || n >= (int) full_svcname_len)
503             goto error;
504     }
505
506     /* Credentials for kadmin don't need to be forwardable or proxiable. */
507     if (init_type != INIT_CREDS) {
508         code = krb5_get_init_creds_opt_alloc(ctx, &opt);
509         krb5_get_init_creds_opt_set_forwardable(opt, 0);
510         krb5_get_init_creds_opt_set_proxiable(opt, 0);
511         krb5_get_init_creds_opt_set_out_ccache(ctx, opt, ccache);
512         if (init_type == INIT_ANONYMOUS)
513             krb5_get_init_creds_opt_set_anonymous(opt, 1);
514     }
515
516     if (init_type == INIT_PASS || init_type == INIT_ANONYMOUS) {
517         code = krb5_get_init_creds_password(ctx, &outcreds, client, pass,
518                                             krb5_prompter_posix,
519                                             NULL, 0,
520                                             full_svcname, opt);
521         if (code)
522             goto error;
523     } else if (init_type == INIT_SKEY) {
524         if (pass) {
525             code = krb5_kt_resolve(ctx, pass, &kt);
526             if (code)
527                 goto error;
528         }
529         code = krb5_get_init_creds_keytab(ctx, &outcreds, client, kt,
530                                           0, full_svcname, opt);
531         if (pass)
532             krb5_kt_close(ctx, kt);
533         if (code)
534             goto error;
535     } else if (init_type == INIT_CREDS) {
536         mcreds.client = client;
537         code = krb5_parse_name(ctx, full_svcname, &mcreds.server);
538         if (code)
539             goto error;
540         code = krb5_cc_retrieve_cred(ctx, ccache, 0,
541                                      &mcreds, &outcreds);
542         krb5_free_principal(ctx, mcreds.server);
543         if (code)
544             goto error;
545     }
546 error:
547     krb5_free_cred_contents(ctx, &outcreds);
548     if (opt)
549         krb5_get_init_creds_opt_free(ctx, opt);
550     return code;
551 }
552
553 /* Set *fd to a socket connected to hostname and port. */
554 static kadm5_ret_t
555 connect_to_server(const char *hostname, int port, int *fd)
556 {
557     struct addrinfo hint, *addrs, *a;
558     char portbuf[32];
559     int err, s;
560     kadm5_ret_t code;
561
562     /* Look up the server's addresses. */
563     (void) snprintf(portbuf, sizeof(portbuf), "%d", port);
564     memset(&hint, 0, sizeof(hint));
565     hint.ai_socktype = SOCK_STREAM;
566 #ifdef AI_NUMERICSERV
567     hint.ai_flags = AI_NUMERICSERV;
568 #endif
569     err = getaddrinfo(hostname, portbuf, &hint, &addrs);
570     if (err != 0)
571         return KADM5_CANT_RESOLVE;
572
573     /* Try to connect to each address until we succeed. */
574     for (a = addrs; a != NULL; a = a->ai_next) {
575         s = socket(a->ai_family, a->ai_socktype, 0);
576         if (s == -1) {
577             code = KADM5_FAILURE;
578             goto cleanup;
579         }
580         err = connect(s, a->ai_addr, a->ai_addrlen);
581         if (err == 0) {
582             *fd = s;
583             code = 0;
584             goto cleanup;
585         }
586         close(s);
587     }
588
589     /* We didn't succeed on any address. */
590     code = KADM5_RPC_ERROR;
591 cleanup:
592     freeaddrinfo(addrs);
593     return code;
594 }
595
596 /* Acquire GSSAPI credentials and set up RPC auth flavor. */
597 static kadm5_ret_t
598 setup_gss(kadm5_server_handle_t handle, kadm5_config_params *params_in,
599           char *client_name, char *full_svcname)
600 {
601     kadm5_ret_t code;
602     OM_uint32 gssstat, minor_stat;
603     gss_buffer_desc buf;
604     gss_name_t gss_client;
605     gss_name_t gss_target;
606     gss_cred_id_t gss_client_creds;
607     const char *c_ccname_orig;
608     char *ccname_orig;
609
610     code = KADM5_GSS_ERROR;
611     gss_client_creds = GSS_C_NO_CREDENTIAL;
612     ccname_orig = NULL;
613     gss_client = gss_target = GSS_C_NO_NAME;
614
615     /* Temporarily use the kadm5 cache. */
616     gssstat = gss_krb5_ccache_name(&minor_stat, handle->cache_name,
617                                    &c_ccname_orig);
618     if (gssstat != GSS_S_COMPLETE) {
619         code = KADM5_GSS_ERROR;
620         goto error;
621     }
622     if (c_ccname_orig)
623         ccname_orig = strdup(c_ccname_orig);
624     else
625         ccname_orig = 0;
626
627     buf.value = full_svcname;
628     buf.length = strlen((char *)buf.value) + 1;
629     gssstat = gss_import_name(&minor_stat, &buf,
630                               (gss_OID) gss_nt_krb5_name, &gss_target);
631     if (gssstat != GSS_S_COMPLETE) {
632         code = KADM5_GSS_ERROR;
633         goto error;
634     }
635
636     if (client_name) {
637         buf.value = client_name;
638         buf.length = strlen((char *)buf.value) + 1;
639         gssstat = gss_import_name(&minor_stat, &buf,
640                                   (gss_OID) gss_nt_krb5_name, &gss_client);
641     } else gss_client = GSS_C_NO_NAME;
642
643     if (gssstat != GSS_S_COMPLETE) {
644         code = KADM5_GSS_ERROR;
645         goto error;
646     }
647
648     gssstat = gss_acquire_cred(&minor_stat, gss_client, 0,
649                                GSS_C_NULL_OID_SET, GSS_C_INITIATE,
650                                &gss_client_creds, NULL, NULL);
651     if (gssstat != GSS_S_COMPLETE) {
652         code = KADM5_GSS_ERROR;
653 #if 0 /* for debugging only */
654         {
655             OM_uint32 maj_status, min_status, message_context = 0;
656             gss_buffer_desc status_string;
657             do {
658                 maj_status = gss_display_status(&min_status,
659                                                 gssstat,
660                                                 GSS_C_GSS_CODE,
661                                                 GSS_C_NO_OID,
662                                                 &message_context,
663                                                 &status_string);
664                 if (maj_status == GSS_S_COMPLETE) {
665                     fprintf(stderr, "MAJ: %.*s\n",
666                             (int) status_string.length,
667                             (char *)status_string.value);
668                     gss_release_buffer(&min_status, &status_string);
669                 } else {
670                     fprintf(stderr,
671                             "MAJ? gss_display_status returns 0x%lx?!\n",
672                             (unsigned long) maj_status);
673                     message_context = 0;
674                 }
675             } while (message_context != 0);
676             do {
677                 maj_status = gss_display_status(&min_status,
678                                                 minor_stat,
679                                                 GSS_C_MECH_CODE,
680                                                 GSS_C_NO_OID,
681                                                 &message_context,
682                                                 &status_string);
683                 if (maj_status == GSS_S_COMPLETE) {
684                     fprintf(stderr, "MIN: %.*s\n",
685                             (int) status_string.length,
686                             (char *)status_string.value);
687                     gss_release_buffer(&min_status, &status_string);
688                 } else {
689                     fprintf(stderr,
690                             "MIN? gss_display_status returns 0x%lx?!\n",
691                             (unsigned long) maj_status);
692                     message_context = 0;
693                 }
694             } while (message_context != 0);
695         }
696 #endif
697         goto error;
698     }
699
700     /*
701      * Do actual creation of RPC auth handle.  Implements auth flavor
702      * fallback.
703      */
704     rpc_auth(handle, params_in, gss_client_creds, gss_target);
705
706 error:
707     if (gss_client_creds != GSS_C_NO_CREDENTIAL)
708         (void) gss_release_cred(&minor_stat, &gss_client_creds);
709
710     if (gss_client)
711         gss_release_name(&minor_stat, &gss_client);
712     if (gss_target)
713         gss_release_name(&minor_stat, &gss_target);
714
715     /* Revert to prior gss_krb5 ccache. */
716     if (ccname_orig) {
717         gssstat = gss_krb5_ccache_name(&minor_stat, ccname_orig, NULL);
718         if (gssstat) {
719             return KADM5_GSS_ERROR;
720         }
721         free(ccname_orig);
722     } else {
723         gssstat = gss_krb5_ccache_name(&minor_stat, NULL, NULL);
724         if (gssstat) {
725             return KADM5_GSS_ERROR;
726         }
727     }
728
729     if (handle->clnt->cl_auth == NULL) {
730         return KADM5_GSS_ERROR;
731     }
732     return 0;
733 }
734
735 /* Create RPC auth handle.  Do auth flavor fallback if needed. */
736 static void
737 rpc_auth(kadm5_server_handle_t handle, kadm5_config_params *params_in,
738          gss_cred_id_t gss_client_creds, gss_name_t gss_target)
739 {
740     OM_uint32 gssstat, minor_stat;
741     struct rpc_gss_sec sec;
742
743     /* Allow unauthenticated option for testing. */
744     if (params_in != NULL && (params_in->mask & KADM5_CONFIG_NO_AUTH))
745         return;
746
747     /* Use RPCSEC_GSS by default. */
748     if (params_in == NULL ||
749         !(params_in->mask & KADM5_CONFIG_OLD_AUTH_GSSAPI)) {
750         sec.mech = gss_mech_krb5;
751         sec.qop = GSS_C_QOP_DEFAULT;
752         sec.svc = RPCSEC_GSS_SVC_PRIVACY;
753         sec.cred = gss_client_creds;
754         sec.req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
755
756         handle->clnt->cl_auth = authgss_create(handle->clnt,
757                                                gss_target, &sec);
758         if (handle->clnt->cl_auth != NULL)
759             return;
760     }
761
762     if (params_in != NULL && (params_in->mask & KADM5_CONFIG_AUTH_NOFALLBACK))
763         return;
764
765     /* Fall back to old AUTH_GSSAPI. */
766     handle->clnt->cl_auth = auth_gssapi_create(handle->clnt,
767                                                &gssstat,
768                                                &minor_stat,
769                                                gss_client_creds,
770                                                gss_target,
771                                                (gss_OID) gss_mech_krb5,
772                                                GSS_C_MUTUAL_FLAG
773                                                | GSS_C_REPLAY_FLAG,
774                                                0, NULL, NULL, NULL);
775 }
776
777 kadm5_ret_t
778 kadm5_destroy(void *server_handle)
779 {
780     krb5_ccache            ccache = NULL;
781     int                    code = KADM5_OK;
782     kadm5_server_handle_t      handle =
783         (kadm5_server_handle_t) server_handle;
784
785     CHECK_HANDLE(server_handle);
786
787     if (handle->destroy_cache && handle->cache_name) {
788         if ((code = krb5_cc_resolve(handle->context,
789                                     handle->cache_name, &ccache)) == 0)
790             code = krb5_cc_destroy (handle->context, ccache);
791     }
792     if (handle->cache_name)
793         free(handle->cache_name);
794     if (handle->clnt && handle->clnt->cl_auth)
795         AUTH_DESTROY(handle->clnt->cl_auth);
796     if (handle->clnt)
797         clnt_destroy(handle->clnt);
798     if (handle->lhandle)
799         free (handle->lhandle);
800
801     kadm5_free_config_params(handle->context, &handle->params);
802
803     handle->magic_number = 0;
804     free(handle);
805
806     return code;
807 }
808 /* not supported on client */
809 kadm5_ret_t kadm5_lock(void *server_handle)
810 {
811     return EINVAL;
812 }
813
814 /* not supported on client */
815 kadm5_ret_t kadm5_unlock(void *server_handle)
816 {
817     return EINVAL;
818 }
819
820 kadm5_ret_t kadm5_flush(void *server_handle)
821 {
822     return KADM5_OK;
823 }
824
825 int _kadm5_check_handle(void *handle)
826 {
827     CHECK_HANDLE(handle);
828     return 0;
829 }
830
831 krb5_error_code kadm5_init_krb5_context (krb5_context *ctx)
832 {
833     return krb5_init_context(ctx);
834 }
835
836 /*
837  * Stub function for kadmin.  It was created to eliminate the dependency on
838  * libkdb's ulog functions.  The srv equivalent makes the actual calls.
839  */
840 krb5_error_code
841 kadm5_init_iprop(void *handle, char **db_args)
842 {
843     return (0);
844 }