xref: /freebsd/crypto/krb5/src/lib/kadm5/clnt/client_init.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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 <k5-int.h>
33 #include <netdb.h>
34 #include <com_err.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <fake-addrinfo.h>
39 #include <krb5.h>
40 
41 #include <kadm5/admin.h>
42 #include <kadm5/kadm_rpc.h>
43 #include "client_internal.h"
44 #include <iprop_hdr.h>
45 #include "iprop.h"
46 
47 #include <gssrpc/rpc.h>
48 #include <gssapi/gssapi.h>
49 #include <gssapi/gssapi_krb5.h>
50 #include <gssrpc/auth_gssapi.h>
51 
52 #define ADM_CCACHE  "/tmp/ovsec_adm.XXXXXX"
53 
54 enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS, INIT_ANONYMOUS };
55 
56 static kadm5_ret_t
57 init_any(krb5_context context, char *client_name, enum init_type init_type,
58          char *pass, krb5_ccache ccache_in, char *service_name,
59          kadm5_config_params *params, krb5_ui_4 struct_version,
60          krb5_ui_4 api_version, char **db_args, void **server_handle);
61 
62 static kadm5_ret_t
63 get_init_creds(kadm5_server_handle_t handle, krb5_principal client,
64                enum init_type init_type, char *pass, krb5_ccache ccache_in,
65                char *svcname_in, char *realm, krb5_principal *server_out);
66 
67 static kadm5_ret_t
68 gic_iter(kadm5_server_handle_t handle, enum init_type init_type,
69          krb5_ccache ccache, krb5_principal client, char *pass,
70          char *svcname, char *realm, krb5_principal *server_out);
71 
72 static kadm5_ret_t
73 connect_to_server(const char *hostname, int port, int *fd);
74 
75 static kadm5_ret_t
76 setup_gss(kadm5_server_handle_t handle, kadm5_config_params *params_in,
77           krb5_principal client, krb5_principal server);
78 
79 static void
80 rpc_auth(kadm5_server_handle_t handle, kadm5_config_params *params_in,
81          gss_cred_id_t gss_client_creds, gss_name_t gss_target);
82 
83 kadm5_ret_t
kadm5_init_with_creds(krb5_context context,char * client_name,krb5_ccache ccache,char * service_name,kadm5_config_params * params,krb5_ui_4 struct_version,krb5_ui_4 api_version,char ** db_args,void ** server_handle)84 kadm5_init_with_creds(krb5_context context, char *client_name,
85                       krb5_ccache ccache, char *service_name,
86                       kadm5_config_params *params, krb5_ui_4 struct_version,
87                       krb5_ui_4 api_version, char **db_args,
88                       void **server_handle)
89 {
90     return init_any(context, client_name, INIT_CREDS, NULL, ccache,
91                     service_name, params, struct_version, api_version, db_args,
92                     server_handle);
93 }
94 
95 kadm5_ret_t
kadm5_init_with_password(krb5_context context,char * client_name,char * pass,char * service_name,kadm5_config_params * params,krb5_ui_4 struct_version,krb5_ui_4 api_version,char ** db_args,void ** server_handle)96 kadm5_init_with_password(krb5_context context, char *client_name,
97                          char *pass, char *service_name,
98                          kadm5_config_params *params, krb5_ui_4 struct_version,
99                          krb5_ui_4 api_version, char **db_args,
100                          void **server_handle)
101 {
102     return init_any(context, client_name, INIT_PASS, pass, NULL, service_name,
103                     params, struct_version, api_version, db_args,
104                     server_handle);
105 }
106 
107 kadm5_ret_t
kadm5_init_anonymous(krb5_context context,char * client_name,char * service_name,kadm5_config_params * params,krb5_ui_4 struct_version,krb5_ui_4 api_version,char ** db_args,void ** server_handle)108 kadm5_init_anonymous(krb5_context context, char *client_name,
109                      char *service_name, kadm5_config_params *params,
110                      krb5_ui_4 struct_version, krb5_ui_4 api_version,
111                      char **db_args, void **server_handle)
112 {
113     return init_any(context, client_name, INIT_ANONYMOUS, NULL, NULL,
114                     service_name, params, struct_version, api_version,
115                     db_args, server_handle);
116 }
117 
118 kadm5_ret_t
kadm5_init(krb5_context context,char * client_name,char * pass,char * service_name,kadm5_config_params * params,krb5_ui_4 struct_version,krb5_ui_4 api_version,char ** db_args,void ** server_handle)119 kadm5_init(krb5_context context, char *client_name, char *pass,
120            char *service_name, kadm5_config_params *params,
121            krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args,
122            void **server_handle)
123 {
124     return init_any(context, client_name, INIT_PASS, pass, NULL, service_name,
125                     params, struct_version, api_version, db_args,
126                     server_handle);
127 }
128 
129 kadm5_ret_t
kadm5_init_with_skey(krb5_context context,char * client_name,char * keytab,char * service_name,kadm5_config_params * params,krb5_ui_4 struct_version,krb5_ui_4 api_version,char ** db_args,void ** server_handle)130 kadm5_init_with_skey(krb5_context context, char *client_name,
131                      char *keytab, char *service_name,
132                      kadm5_config_params *params, krb5_ui_4 struct_version,
133                      krb5_ui_4 api_version, char **db_args,
134                      void **server_handle)
135 {
136     return init_any(context, client_name, INIT_SKEY, keytab, NULL,
137                     service_name, params, struct_version, api_version, db_args,
138                     server_handle);
139 }
140 
141 static kadm5_ret_t
free_handle(kadm5_server_handle_t handle)142 free_handle(kadm5_server_handle_t handle)
143 {
144     kadm5_ret_t ret = 0;
145     OM_uint32 minor_stat;
146     krb5_ccache ccache;
147 
148     if (handle == NULL)
149         return 0;
150 
151     if (handle->destroy_cache && handle->cache_name != NULL) {
152         ret = krb5_cc_resolve(handle->context, handle->cache_name, &ccache);
153         if (!ret)
154             ret = krb5_cc_destroy(handle->context, ccache);
155     }
156     free(handle->cache_name);
157     (void)gss_release_cred(&minor_stat, &handle->cred);
158     if (handle->clnt != NULL && handle->clnt->cl_auth != NULL)
159         AUTH_DESTROY(handle->clnt->cl_auth);
160     if (handle->clnt != NULL)
161         clnt_destroy(handle->clnt);
162     if (handle->client_socket != -1)
163         close(handle->client_socket);
164     free(handle->lhandle);
165     kadm5_free_config_params(handle->context, &handle->params);
166     free(handle);
167 
168     return ret;
169 }
170 
171 static kadm5_ret_t
init_any(krb5_context context,char * client_name,enum init_type init_type,char * pass,krb5_ccache ccache_in,char * service_name,kadm5_config_params * params_in,krb5_ui_4 struct_version,krb5_ui_4 api_version,char ** db_args,void ** server_handle)172 init_any(krb5_context context, char *client_name, enum init_type init_type,
173          char *pass, krb5_ccache ccache_in, char *service_name,
174          kadm5_config_params *params_in, krb5_ui_4 struct_version,
175          krb5_ui_4 api_version, char **db_args, void **server_handle)
176 {
177     int fd = -1;
178     krb5_boolean iprop_enable;
179     int port;
180     rpcprog_t rpc_prog;
181     rpcvers_t rpc_vers;
182     krb5_principal client = NULL, server = NULL;
183     struct timeval timeout;
184 
185     kadm5_server_handle_t handle = NULL;
186     kadm5_config_params params_local;
187 
188     krb5_error_code code;
189     generic_ret r = { 0, 0 };
190 
191     initialize_ovk_error_table();
192     initialize_ovku_error_table();
193 
194     if (server_handle == NULL || client_name == NULL)
195         return EINVAL;
196 
197     CHECK_VERSIONS(struct_version, api_version, KADM5_OLD_LIB_API_VERSION,
198                    KADM5_NEW_LIB_API_VERSION);
199 
200     handle = k5alloc(sizeof(*handle), &code);
201     if (handle == NULL)
202         goto cleanup;
203     handle->lhandle = k5alloc(sizeof(*handle), &code);
204     if (handle->lhandle == NULL)
205         goto cleanup;
206 
207     handle->magic_number = KADM5_SERVER_HANDLE_MAGIC;
208     handle->struct_version = struct_version;
209     handle->api_version = api_version;
210     handle->clnt = 0;
211     handle->client_socket = -1;
212     handle->cache_name = 0;
213     handle->destroy_cache = 0;
214     handle->context = 0;
215     handle->cred = GSS_C_NO_CREDENTIAL;
216     *handle->lhandle = *handle;
217     handle->lhandle->api_version = KADM5_API_VERSION_4;
218     handle->lhandle->struct_version = KADM5_STRUCT_VERSION;
219     handle->lhandle->lhandle = handle->lhandle;
220 
221     handle->context = context;
222 
223     memset(&params_local, 0, sizeof(params_local));
224 
225     code = kadm5_get_config_params(handle->context, 0, params_in,
226                                    &handle->params);
227     if (code)
228         goto cleanup;
229 
230 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM |           \
231                          KADM5_CONFIG_ADMIN_SERVER |    \
232                          KADM5_CONFIG_KADMIND_PORT)
233 
234     if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
235         code = KADM5_MISSING_KRB5_CONF_PARAMS;
236         goto cleanup;
237     }
238 
239     /*
240      * Parse the client name.  If it has an empty realm, it is almost certainly
241      * a host-based principal using DNS fallback processing or the referral
242      * realm, so give it the appropriate name type for canonicalization.  Also
243      * check for iprop client principals as kpropd sets the realm on the
244      * sn2princ result.
245      */
246     code = krb5_parse_name(handle->context, client_name, &client);
247     if (code)
248         goto cleanup;
249     if ((init_type == INIT_SKEY && client->realm.length == 0) ||
250         (client->length == 2 &&
251          data_eq_string(client->data[0], KIPROP_SVC_NAME)))
252         client->type = KRB5_NT_SRV_HST;
253 
254     /*
255      * Get credentials.  Also does some fallbacks in case kadmin/fqdn
256      * principal doesn't exist.
257      */
258     code = get_init_creds(handle, client, init_type, pass, ccache_in,
259                           service_name, handle->params.realm, &server);
260     if (code)
261         goto cleanup;
262 
263     /* If the service_name and client_name are iprop-centric, use the iprop
264      * port and RPC identifiers. */
265     iprop_enable = (service_name != NULL &&
266                     strstr(service_name, KIPROP_SVC_NAME) != NULL &&
267                     strstr(client_name, KIPROP_SVC_NAME) != NULL);
268     if (iprop_enable) {
269         port = handle->params.iprop_port;
270         rpc_prog = KRB5_IPROP_PROG;
271         rpc_vers = KRB5_IPROP_VERS;
272     } else {
273         port = handle->params.kadmind_port;
274         rpc_prog = KADM;
275         rpc_vers = KADMVERS;
276     }
277 
278     code = connect_to_server(handle->params.admin_server, port, &fd);
279     if (code)
280         goto cleanup;
281 
282     handle->clnt = clnttcp_create(NULL, rpc_prog, rpc_vers, &fd, 0, 0);
283     if (handle->clnt == NULL) {
284         code = KADM5_RPC_ERROR;
285 #ifdef DEBUG
286         clnt_pcreateerror("clnttcp_create");
287 #endif
288         goto cleanup;
289     }
290 
291     /* Set a one-hour timeout. */
292     timeout.tv_sec = 3600;
293     timeout.tv_usec = 0;
294     (void)clnt_control(handle->clnt, CLSET_TIMEOUT, &timeout);
295 
296     handle->client_socket = fd;
297     handle->lhandle->clnt = handle->clnt;
298     handle->lhandle->client_socket = fd;
299 
300     /*
301      * The RPC connection is open; establish the GSS-API
302      * authentication context.
303      */
304     code = setup_gss(handle, params_in,
305                      (init_type == INIT_CREDS) ? client : NULL, server);
306     if (code)
307         goto cleanup;
308 
309     /*
310      * Bypass the remainder of the code and return straight away
311      * if the gss service requested is kiprop
312      */
313     if (iprop_enable) {
314         code = 0;
315         *server_handle = handle;
316         handle = NULL;
317         goto cleanup;
318     }
319 
320     if (init_2(&handle->api_version, &r, handle->clnt)) {
321         code = KADM5_RPC_ERROR;
322 #ifdef DEBUG
323         clnt_perror(handle->clnt, "init_2 null resp");
324 #endif
325         goto cleanup;
326     }
327     /* Drop down to v3 wire protocol if server does not support v4 */
328     if (r.code == KADM5_NEW_SERVER_API_VERSION &&
329         handle->api_version == KADM5_API_VERSION_4) {
330         handle->api_version = KADM5_API_VERSION_3;
331         memset(&r, 0, sizeof(generic_ret));
332         if (init_2(&handle->api_version, &r, handle->clnt)) {
333             code = KADM5_RPC_ERROR;
334             goto cleanup;
335         }
336     }
337     /* Drop down to v2 wire protocol if server does not support v3 */
338     if (r.code == KADM5_NEW_SERVER_API_VERSION &&
339         handle->api_version == KADM5_API_VERSION_3) {
340         handle->api_version = KADM5_API_VERSION_2;
341         memset(&r, 0, sizeof(generic_ret));
342         if (init_2(&handle->api_version, &r, handle->clnt)) {
343             code = KADM5_RPC_ERROR;
344             goto cleanup;
345         }
346     }
347     if (r.code) {
348         code = r.code;
349         goto cleanup;
350     }
351 
352     *server_handle = handle;
353     handle = NULL;
354 
355 cleanup:
356     krb5_free_principal(context, client);
357     krb5_free_principal(context, server);
358     (void)free_handle(handle);
359 
360     return code;
361 }
362 
363 /* Get initial credentials for authenticating to server.  Perform fallback from
364  * kadmin/fqdn to kadmin/admin if svcname_in is NULL. */
365 static kadm5_ret_t
get_init_creds(kadm5_server_handle_t handle,krb5_principal client,enum init_type init_type,char * pass,krb5_ccache ccache_in,char * svcname_in,char * realm,krb5_principal * server_out)366 get_init_creds(kadm5_server_handle_t handle, krb5_principal client,
367                enum init_type init_type, char *pass, krb5_ccache ccache_in,
368                char *svcname_in, char *realm, krb5_principal *server_out)
369 {
370     kadm5_ret_t code;
371     krb5_ccache ccache = NULL;
372     char *svcname, svcbuf[BUFSIZ];
373 
374     *server_out = NULL;
375 
376     /*
377      * Acquire a service ticket for svcname@realm for client, using password
378      * pass (which could be NULL), and create a ccache to store them in.  If
379      * INIT_CREDS, use the ccache we were provided instead.
380      */
381     if (init_type == INIT_CREDS) {
382         ccache = ccache_in;
383         if (asprintf(&handle->cache_name, "%s:%s",
384                      krb5_cc_get_type(handle->context, ccache),
385                      krb5_cc_get_name(handle->context, ccache)) < 0) {
386             handle->cache_name = NULL;
387             code = ENOMEM;
388             goto error;
389         }
390     } else {
391         static int counter = 0;
392 
393         if (asprintf(&handle->cache_name, "MEMORY:kadm5_%u", counter++) < 0) {
394             handle->cache_name = NULL;
395             code = ENOMEM;
396             goto error;
397         }
398         code = krb5_cc_resolve(handle->context, handle->cache_name,
399                                &ccache);
400         if (code)
401             goto error;
402 
403         code = krb5_cc_initialize (handle->context, ccache, client);
404         if (code)
405             goto error;
406 
407         handle->destroy_cache = 1;
408     }
409     handle->lhandle->cache_name = handle->cache_name;
410 
411     svcname = (svcname_in != NULL) ? svcname_in : KADM5_ADMIN_SERVICE;
412     code = gic_iter(handle, init_type, ccache, client, pass, svcname, realm,
413                     server_out);
414     if ((code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
415          || code == KRB5_CC_NOTFOUND) && svcname_in == NULL) {
416         /* Retry with host-based service principal. */
417         code = kadm5_get_admin_service_name(handle->context,
418                                             handle->params.realm,
419                                             svcbuf, sizeof(svcbuf));
420         if (code)
421             goto error;
422         code = gic_iter(handle, init_type, ccache, client, pass, svcbuf, realm,
423                         server_out);
424     }
425     /* Improved error messages */
426     if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD;
427     if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
428         code = KADM5_SECURE_PRINC_MISSING;
429 
430 error:
431     if (ccache != NULL && init_type != INIT_CREDS)
432         krb5_cc_close(handle->context, ccache);
433     return code;
434 }
435 
436 /* Perform one iteration of attempting to get credentials.  This includes
437  * searching existing ccache for requested service if INIT_CREDS. */
438 static kadm5_ret_t
gic_iter(kadm5_server_handle_t handle,enum init_type init_type,krb5_ccache ccache,krb5_principal client,char * pass,char * svcname,char * realm,krb5_principal * server_out)439 gic_iter(kadm5_server_handle_t handle, enum init_type init_type,
440          krb5_ccache ccache, krb5_principal client, char *pass, char *svcname,
441          char *realm, krb5_principal *server_out)
442 {
443     kadm5_ret_t code;
444     krb5_context ctx;
445     krb5_keytab kt;
446     krb5_get_init_creds_opt *opt = NULL;
447     krb5_creds mcreds, outcreds;
448 
449     *server_out = NULL;
450     ctx = handle->context;
451     kt = NULL;
452     memset(&opt, 0, sizeof(opt));
453     memset(&mcreds, 0, sizeof(mcreds));
454     memset(&outcreds, 0, sizeof(outcreds));
455 
456     /* Credentials for kadmin don't need to be forwardable or proxiable. */
457     if (init_type != INIT_CREDS) {
458         code = krb5_get_init_creds_opt_alloc(ctx, &opt);
459         if (code)
460             goto error;
461 
462         krb5_get_init_creds_opt_set_forwardable(opt, 0);
463         krb5_get_init_creds_opt_set_proxiable(opt, 0);
464         krb5_get_init_creds_opt_set_out_ccache(ctx, opt, ccache);
465         if (init_type == INIT_ANONYMOUS)
466             krb5_get_init_creds_opt_set_anonymous(opt, 1);
467     }
468 
469     if (init_type == INIT_PASS || init_type == INIT_ANONYMOUS) {
470         code = krb5_get_init_creds_password(ctx, &outcreds, client, pass,
471                                             krb5_prompter_posix,
472                                             NULL, 0, svcname, opt);
473         if (code)
474             goto error;
475     } else if (init_type == INIT_SKEY) {
476         if (pass) {
477             code = krb5_kt_resolve(ctx, pass, &kt);
478             if (code)
479                 goto error;
480         }
481         code = krb5_get_init_creds_keytab(ctx, &outcreds, client, kt,
482                                           0, svcname, opt);
483         if (pass)
484             krb5_kt_close(ctx, kt);
485         if (code)
486             goto error;
487     } else if (init_type == INIT_CREDS) {
488         mcreds.client = client;
489         code = krb5_parse_name_flags(ctx, svcname,
490                                      KRB5_PRINCIPAL_PARSE_IGNORE_REALM,
491                                      &mcreds.server);
492         if (code)
493             goto error;
494         code = krb5_set_principal_realm(ctx, mcreds.server, realm);
495         if (code)
496             goto error;
497         code = krb5_cc_retrieve_cred(ctx, ccache, 0,
498                                      &mcreds, &outcreds);
499         krb5_free_principal(ctx, mcreds.server);
500         if (code)
501             goto error;
502     } else {
503         code = EINVAL;
504         goto error;
505     }
506 
507     /* Steal the server principal of the creds we acquired and return it to the
508      * caller, which needs to knows what service to authenticate to. */
509     *server_out = outcreds.server;
510     outcreds.server = NULL;
511 
512 error:
513     krb5_free_cred_contents(ctx, &outcreds);
514     if (opt)
515         krb5_get_init_creds_opt_free(ctx, opt);
516     return code;
517 }
518 
519 /* Set *fd to a socket connected to hostname and port. */
520 static kadm5_ret_t
connect_to_server(const char * hostname,int port,int * fd)521 connect_to_server(const char *hostname, int port, int *fd)
522 {
523     struct addrinfo hint, *addrs, *a;
524     char portbuf[32];
525     int err, s;
526     kadm5_ret_t code;
527 
528     /* Look up the server's addresses. */
529     (void) snprintf(portbuf, sizeof(portbuf), "%d", port);
530     memset(&hint, 0, sizeof(hint));
531     hint.ai_socktype = SOCK_STREAM;
532     hint.ai_flags = AI_ADDRCONFIG;
533 #ifdef AI_NUMERICSERV
534     hint.ai_flags |= AI_NUMERICSERV;
535 #endif
536     err = getaddrinfo(hostname, portbuf, &hint, &addrs);
537     if (err != 0)
538         return KADM5_CANT_RESOLVE;
539 
540     /* Try to connect to each address until we succeed. */
541     for (a = addrs; a != NULL; a = a->ai_next) {
542         s = socket(a->ai_family, a->ai_socktype, 0);
543         if (s == -1) {
544             code = KADM5_FAILURE;
545             goto cleanup;
546         }
547         err = connect(s, a->ai_addr, a->ai_addrlen);
548         if (err == 0) {
549             *fd = s;
550             code = 0;
551             goto cleanup;
552         }
553         close(s);
554     }
555 
556     /* We didn't succeed on any address. */
557     code = KADM5_RPC_ERROR;
558 cleanup:
559     freeaddrinfo(addrs);
560     return code;
561 }
562 
563 /* Acquire GSSAPI credentials and set up RPC auth flavor. */
564 static kadm5_ret_t
setup_gss(kadm5_server_handle_t handle,kadm5_config_params * params_in,krb5_principal client,krb5_principal server)565 setup_gss(kadm5_server_handle_t handle, kadm5_config_params *params_in,
566           krb5_principal client, krb5_principal server)
567 {
568     OM_uint32 gssstat, minor_stat;
569     gss_buffer_desc buf;
570     gss_name_t gss_client;
571     gss_name_t gss_target;
572     const char *c_ccname_orig;
573     char *ccname_orig;
574 
575     ccname_orig = NULL;
576     gss_client = gss_target = GSS_C_NO_NAME;
577 
578     /* Temporarily use the kadm5 cache. */
579     gssstat = gss_krb5_ccache_name(&minor_stat, handle->cache_name,
580                                    &c_ccname_orig);
581     if (gssstat != GSS_S_COMPLETE)
582         goto error;
583     if (c_ccname_orig)
584         ccname_orig = strdup(c_ccname_orig);
585     else
586         ccname_orig = 0;
587 
588     buf.value = &server;
589     buf.length = sizeof(server);
590     gssstat = gss_import_name(&minor_stat, &buf,
591                               (gss_OID)gss_nt_krb5_principal, &gss_target);
592     if (gssstat != GSS_S_COMPLETE)
593         goto error;
594 
595     if (client != NULL) {
596         buf.value = &client;
597         buf.length = sizeof(client);
598         gssstat = gss_import_name(&minor_stat, &buf,
599                                   (gss_OID)gss_nt_krb5_principal, &gss_client);
600     } else gss_client = GSS_C_NO_NAME;
601 
602     if (gssstat != GSS_S_COMPLETE)
603         goto error;
604 
605     gssstat = gss_acquire_cred(&minor_stat, gss_client, 0,
606                                GSS_C_NULL_OID_SET, GSS_C_INITIATE,
607                                &handle->cred, NULL, NULL);
608     if (gssstat != GSS_S_COMPLETE)
609         goto error;
610 
611     /*
612      * Do actual creation of RPC auth handle.  Implements auth flavor
613      * fallback.
614      */
615     rpc_auth(handle, params_in, handle->cred, gss_target);
616 
617 error:
618     if (gss_client)
619         gss_release_name(&minor_stat, &gss_client);
620     if (gss_target)
621         gss_release_name(&minor_stat, &gss_target);
622 
623     /* Revert to prior gss_krb5 ccache. */
624     if (ccname_orig) {
625         gssstat = gss_krb5_ccache_name(&minor_stat, ccname_orig, NULL);
626         if (gssstat) {
627             return KADM5_GSS_ERROR;
628         }
629         free(ccname_orig);
630     } else {
631         gssstat = gss_krb5_ccache_name(&minor_stat, NULL, NULL);
632         if (gssstat) {
633             return KADM5_GSS_ERROR;
634         }
635     }
636 
637     if (handle->clnt->cl_auth == NULL) {
638         return KADM5_GSS_ERROR;
639     }
640     return 0;
641 }
642 
643 /* Create RPC auth handle.  Do auth flavor fallback if needed. */
644 static void
rpc_auth(kadm5_server_handle_t handle,kadm5_config_params * params_in,gss_cred_id_t gss_client_creds,gss_name_t gss_target)645 rpc_auth(kadm5_server_handle_t handle, kadm5_config_params *params_in,
646          gss_cred_id_t gss_client_creds, gss_name_t gss_target)
647 {
648     OM_uint32 gssstat, minor_stat;
649     struct rpc_gss_sec sec;
650 
651     /* Allow unauthenticated option for testing. */
652     if (params_in != NULL && (params_in->mask & KADM5_CONFIG_NO_AUTH))
653         return;
654 
655     /* Use RPCSEC_GSS by default. */
656     if (params_in == NULL ||
657         !(params_in->mask & KADM5_CONFIG_OLD_AUTH_GSSAPI)) {
658         sec.mech = (gss_OID)gss_mech_krb5;
659         sec.qop = GSS_C_QOP_DEFAULT;
660         sec.svc = RPCSEC_GSS_SVC_PRIVACY;
661         sec.cred = gss_client_creds;
662         sec.req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
663 
664         handle->clnt->cl_auth = authgss_create(handle->clnt,
665                                                gss_target, &sec);
666         if (handle->clnt->cl_auth != NULL)
667             return;
668     }
669 
670     if (params_in != NULL && (params_in->mask & KADM5_CONFIG_AUTH_NOFALLBACK))
671         return;
672 
673     /* Fall back to old AUTH_GSSAPI. */
674     handle->clnt->cl_auth = auth_gssapi_create(handle->clnt,
675                                                &gssstat,
676                                                &minor_stat,
677                                                gss_client_creds,
678                                                gss_target,
679                                                (gss_OID) gss_mech_krb5,
680                                                GSS_C_MUTUAL_FLAG
681                                                | GSS_C_REPLAY_FLAG,
682                                                0, NULL, NULL, NULL);
683 }
684 
685 kadm5_ret_t
kadm5_destroy(void * server_handle)686 kadm5_destroy(void *server_handle)
687 {
688     CHECK_HANDLE(server_handle);
689     return free_handle(server_handle);
690 }
691 /* not supported on client */
kadm5_lock(void * server_handle)692 kadm5_ret_t kadm5_lock(void *server_handle)
693 {
694     return EINVAL;
695 }
696 
697 /* not supported on client */
kadm5_unlock(void * server_handle)698 kadm5_ret_t kadm5_unlock(void *server_handle)
699 {
700     return EINVAL;
701 }
702 
kadm5_flush(void * server_handle)703 kadm5_ret_t kadm5_flush(void *server_handle)
704 {
705     return KADM5_OK;
706 }
707 
_kadm5_check_handle(void * handle)708 int _kadm5_check_handle(void *handle)
709 {
710     CHECK_HANDLE(handle);
711     return 0;
712 }
713 
kadm5_init_krb5_context(krb5_context * ctx)714 krb5_error_code kadm5_init_krb5_context (krb5_context *ctx)
715 {
716     return krb5_init_context(ctx);
717 }
718 
719 /*
720  * Stub function for kadmin.  It was created to eliminate the dependency on
721  * libkdb's ulog functions.  The srv equivalent makes the actual calls.
722  */
723 krb5_error_code
kadm5_init_iprop(void * handle,char ** db_args)724 kadm5_init_iprop(void *handle, char **db_args)
725 {
726     return (0);
727 }
728