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(¶ms_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