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