2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
9 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
13 * Copyright (C) 1998 by the FundsXpress, INC.
15 * All rights reserved.
17 * Export of this software from the United States of America may require
18 * a specific license from the United States Government. It is the
19 * responsibility of any person or organization contemplating export to
20 * obtain such a license before exporting.
22 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
23 * distribute this software and its documentation for any purpose and
24 * without fee is hereby granted, provided that the above copyright
25 * notice appear in all copies and that both that copyright notice and
26 * this permission notice appear in supporting documentation, and that
27 * the name of FundsXpress. not be used in advertising or publicity pertaining
28 * to distribution of the software without specific, written prior
29 * permission. FundsXpress makes no representations about the suitability of
30 * this software for any purpose. It is provided "as is" without express
31 * or implied warranty.
33 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
34 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
35 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <k5-int.h> /* for KRB5_ADM_DEFAULT_PORT */
56 #include <kadm5/admin.h>
57 #include <kadm5/kadm_rpc.h>
58 #include "client_internal.h"
61 #include <gssapi/gssapi.h>
62 #include <gssapi_krb5.h>
63 #include <gssapiP_krb5.h>
66 #include <iprop_hdr.h>
69 #define ADM_CCACHE "/tmp/ovsec_adm.XXXXXX"
71 static int old_auth_gssapi
= 0;
72 /* connection timeout to kadmind in seconds */
73 #define KADMIND_CONNECT_TIMEOUT 25
75 int _kadm5_check_handle();
77 enum init_type
{ INIT_PASS
, INIT_SKEY
, INIT_CREDS
};
79 static kadm5_ret_t
_kadm5_init_any(char *client_name
,
80 enum init_type init_type
,
82 krb5_ccache ccache_in
,
84 kadm5_config_params
*params
,
85 krb5_ui_4 struct_version
,
86 krb5_ui_4 api_version
,
88 void **server_handle
);
90 kadm5_ret_t
kadm5_init_with_creds(char *client_name
,
93 kadm5_config_params
*params
,
94 krb5_ui_4 struct_version
,
95 krb5_ui_4 api_version
,
99 return _kadm5_init_any(client_name
, INIT_CREDS
, NULL
, ccache
,
100 service_name
, params
,
101 struct_version
, api_version
, db_args
,
106 kadm5_ret_t
kadm5_init_with_password(char *client_name
, char *pass
,
108 kadm5_config_params
*params
,
109 krb5_ui_4 struct_version
,
110 krb5_ui_4 api_version
,
112 void **server_handle
)
114 return _kadm5_init_any(client_name
, INIT_PASS
, pass
, NULL
,
115 service_name
, params
, struct_version
,
116 api_version
, db_args
, server_handle
);
119 kadm5_ret_t
kadm5_init(char *client_name
, char *pass
,
121 kadm5_config_params
*params
,
122 krb5_ui_4 struct_version
,
123 krb5_ui_4 api_version
,
125 void **server_handle
)
127 return _kadm5_init_any(client_name
, INIT_PASS
, pass
, NULL
,
128 service_name
, params
, struct_version
,
129 api_version
, db_args
, server_handle
);
132 kadm5_ret_t
kadm5_init_with_skey(char *client_name
, char *keytab
,
134 kadm5_config_params
*params
,
135 krb5_ui_4 struct_version
,
136 krb5_ui_4 api_version
,
138 void **server_handle
)
140 return _kadm5_init_any(client_name
, INIT_SKEY
, keytab
, NULL
,
141 service_name
, params
, struct_version
,
142 api_version
, db_args
, server_handle
);
145 krb5_error_code
kadm5_free_config_params();
148 display_status_1(m
, code
, type
, mech
)
154 OM_uint32 maj_stat
, min_stat
;
155 gss_buffer_desc msg
= GSS_C_EMPTY_BUFFER
;
159 ADMIN_LOG(LOG_ERR
, "%s\n", m
);
162 maj_stat
= gss_display_status(&min_stat
, code
,
165 if (maj_stat
!= GSS_S_COMPLETE
) {
167 dgettext(TEXT_DOMAIN
,
168 "error in gss_display_status"
169 " called from <%s>\n"), m
);
172 syslog(LOG_ERR
, dgettext(TEXT_DOMAIN
,
173 "GSS-API error : %s\n"),
175 syslog(LOG_ERR
, dgettext(TEXT_DOMAIN
,
176 "GSS-API error : %s\n"),
179 (void) gss_release_buffer(&min_stat
, &msg
);
187 * Function: display_status
189 * Purpose: displays GSS-API messages
193 * msg a string to be displayed with the message
194 * maj_stat the GSS-API major status code
195 * min_stat the GSS-API minor status code
199 * The GSS-API messages associated with maj_stat and min_stat are
200 * displayed on stderr, each preceeded by "GSS-API error <msg>: " and
201 * followed by a newline.
204 display_status(msg
, maj_stat
, min_stat
, mech
)
212 if (!rpc_gss_mech_to_oid(mech
, (rpc_gss_OID
*)&mech_oid
)) {
214 dgettext(TEXT_DOMAIN
,
215 "Invalid mechanism oid <%s>"), mech
);
219 display_status_1(msg
, maj_stat
, GSS_C_GSS_CODE
, mech_oid
);
220 display_status_1(msg
, min_stat
, GSS_C_MECH_CODE
, mech_oid
);
224 * Open an fd for the given address and connect asynchronously. Wait
225 * KADMIND_CONNECT_TIMEOUT seconds or till it succeeds. If it succeeds
226 * change fd to blocking and return it, else return -1.
229 get_connection(struct netconfig
*nconf
, struct netbuf netaddr
)
232 struct t_call sndcall
;
233 struct t_call
*rcvcall
= NULL
;
238 (void) memset(&tinfo
, 0, sizeof (tinfo
));
240 /* we'l open with O_NONBLOCK and avoid an fcntl */
241 fd
= t_open(nconf
->nc_device
, O_RDWR
| O_NONBLOCK
, &tinfo
);
246 if (t_bind(fd
, NULL
, NULL
) == -1) {
251 /* we can't connect unless fd is in IDLE state */
252 if (t_getstate(fd
) != T_IDLE
) {
257 /* setup connect parameters */
258 netaddr
.len
= netaddr
.maxlen
= __rpc_get_a_size(tinfo
.addr
);
259 sndcall
.addr
= netaddr
;
260 sndcall
.opt
.len
= sndcall
.udata
.len
= 0;
262 /* we wait for KADMIND_CONNECT_TIMEOUT seconds from now */
263 connect_time
= time(NULL
) + KADMIND_CONNECT_TIMEOUT
;
264 if (t_connect(fd
, &sndcall
, rcvcall
) != 0) {
265 if (t_errno
!= TNODATA
) {
271 /* loop till success or timeout */
273 if (t_rcvconnect(fd
, rcvcall
) == 0)
276 if (t_errno
!= TNODATA
|| time(NULL
) > connect_time
) {
277 /* we have either timed out or caught an error */
280 t_free((char *)rcvcall
, T_CALL
);
286 /* make the fd blocking (synchronous) */
287 flags
= fcntl(fd
, F_GETFL
, 0);
288 (void) fcntl(fd
, F_SETFL
, flags
& ~O_NONBLOCK
);
290 t_free((char *)rcvcall
, T_CALL
);
295 * Open an RPCSEC_GSS connection and
296 * get a client handle to use for future RPCSEC calls.
298 * This function is only used when changing passwords and
299 * the kpasswd_protocol is RPCSEC_GSS
302 _kadm5_initialize_rpcsec_gss_handle(kadm5_server_handle_t handle
,
306 struct netbuf netaddr
;
309 struct sockaddr_in addr
;
310 struct sockaddr_in
*sin
;
311 struct netconfig
*nconf
;
316 boolean_t iprop_enable
= B_FALSE
;
317 char mech
[] = "kerberos_v5";
319 gss_OID_set_desc oid_set
;
320 gss_name_t gss_client
;
321 gss_buffer_desc input_name
;
322 gss_cred_id_t gss_client_creds
= GSS_C_NO_CREDENTIAL
;
323 rpc_gss_options_req_t options_req
;
324 rpc_gss_options_ret_t options_ret
;
325 rpc_gss_service_t service
= rpc_gss_svc_privacy
;
326 OM_uint32 gssstat
, minor_stat
;
328 enum clnt_stat rpc_err_code
;
329 char *server
= handle
->params
.admin_server
;
332 * Try to find the kpasswd_server first if this is for the changepw
333 * service. If defined then it should be resolvable else return error.
335 if (strncmp(service_name
, KADM5_CHANGEPW_HOST_SERVICE
,
336 strlen(KADM5_CHANGEPW_HOST_SERVICE
)) == 0) {
337 if (handle
->params
.kpasswd_server
!= NULL
)
338 server
= handle
->params
.kpasswd_server
;
340 hp
= gethostbyname(server
);
342 code
= KADM5_BAD_SERVER_NAME
;
343 ADMIN_LOGO(LOG_ERR
, dgettext(TEXT_DOMAIN
,
344 "bad server name\n"));
348 memset(&addr
, 0, sizeof (addr
));
349 addr
.sin_family
= hp
->h_addrtype
;
350 (void) memcpy((char *)&addr
.sin_addr
, (char *)hp
->h_addr
,
351 sizeof (addr
.sin_addr
));
352 addr
.sin_port
= htons((ushort_t
)handle
->params
.kadmind_port
);
355 printf("kadmin_port %d\n", handle
->params
.kadmind_port
);
356 printf("addr: sin_port: %d, sin_family: %d, sin_zero %s\n",
357 addr
.sin_port
, addr
.sin_family
, addr
.sin_zero
);
358 printf("sin_addr %d:%d\n", addr
.sin_addr
.S_un
.S_un_w
.s_w1
,
359 addr
.sin_addr
.S_un
.S_un_w
.s_w2
);
361 if ((handlep
= setnetconfig()) == NULL
) {
362 (void) syslog(LOG_ERR
,
363 dgettext(TEXT_DOMAIN
,
364 "cannot get any transport information"));
368 while (nconf
= getnetconfig(handlep
)) {
369 if ((nconf
->nc_semantics
== NC_TPI_COTS_ORD
) &&
370 (strcmp(nconf
->nc_protofmly
, NC_INET
) == 0) &&
371 (strcmp(nconf
->nc_proto
, NC_TCP
) == 0))
378 /* Transform addr to netbuf */
379 (void) memset(&netaddr
, 0, sizeof (netaddr
));
380 netaddr
.buf
= (char *)sin
;
382 /* get an fd connected to the given address */
383 fd
= get_connection(nconf
, netaddr
);
385 syslog(LOG_ERR
, dgettext(TEXT_DOMAIN
,
386 "unable to open connection to ADMIN server "
387 "(t_error %i)"), t_errno
);
388 code
= KADM5_RPC_ERROR
;
393 printf("fd: %d, KADM: %d, KADMVERS %d\n", fd
, KADM
, KADMVERS
);
394 printf("nconf: nc_netid: %s, nc_semantics: %d, nc_flag: %d, "
395 "nc_protofmly: %s\n",
396 nconf
->nc_netid
, nconf
->nc_semantics
, nconf
->nc_flag
,
397 nconf
->nc_protofmly
);
398 printf("nc_proto: %s, nc_device: %s, nc_nlookups: %d, nc_used: %d\n",
399 nconf
->nc_proto
, nconf
->nc_device
, nconf
->nc_nlookups
,
401 printf("netaddr: maxlen %d, buf: %s, len: %d\n", netaddr
.maxlen
,
402 netaddr
.buf
, netaddr
.len
);
405 * Tell clnt_tli_create that given fd is already connected
407 * If the service_name and client_name are iprop-centric,
408 * we need to clnt_tli_create to the appropriate RPC prog
410 iprop_svc
= strdup(KIPROP_SVC_NAME
);
411 if (iprop_svc
== NULL
)
414 if ((strstr(service_name
, iprop_svc
) != NULL
) &&
415 (strstr(client_name
, iprop_svc
) != NULL
)) {
416 iprop_enable
= B_TRUE
;
417 handle
->clnt
= clnt_tli_create(fd
, nconf
, NULL
,
418 KRB5_IPROP_PROG
, KRB5_IPROP_VERS
, 0, 0);
421 handle
->clnt
= clnt_tli_create(fd
, nconf
, NULL
,
422 KADM
, KADMVERS
, 0, 0);
426 if (handle
->clnt
== NULL
) {
427 syslog(LOG_ERR
, dgettext(TEXT_DOMAIN
,
428 "clnt_tli_create failed\n"));
429 code
= KADM5_RPC_ERROR
;
434 * The rpc-handle was created on an fd opened and connected
435 * by us, so we have to explicitly tell rpc to close it.
437 if (clnt_control(handle
->clnt
, CLSET_FD_CLOSE
, NULL
) != TRUE
) {
438 clnt_pcreateerror("ERROR:");
439 syslog(LOG_ERR
, dgettext(TEXT_DOMAIN
,
440 "clnt_control failed to set CLSET_FD_CLOSE"));
441 code
= KADM5_RPC_ERROR
;
446 handle
->lhandle
->clnt
= handle
->clnt
;
448 /* now that handle->clnt is set, we can check the handle */
449 if (code
= _kadm5_check_handle((void *) handle
))
453 * The RPC connection is open; establish the GSS-API
454 * authentication context.
456 ADMIN_LOGO(LOG_ERR
, dgettext(TEXT_DOMAIN
,
457 "have an rpc connection open\n"));
458 /* use the kadm5 cache */
459 ccname_orig
= getenv("KRB5CCNAME");
461 ccname_orig
= strdup(ccname_orig
);
463 (void) krb5_setenv("KRB5CCNAME", handle
->cache_name
, 1);
466 dgettext(TEXT_DOMAIN
,
467 "current credential cache: %s"), handle
->cache_name
);
468 input_name
.value
= client_name
;
469 input_name
.length
= strlen((char *)input_name
.value
) + 1;
470 gssstat
= gss_import_name(&minor_stat
, &input_name
,
471 (gss_OID
)gss_nt_krb5_name
, &gss_client
);
472 if (gssstat
!= GSS_S_COMPLETE
) {
473 code
= KADM5_GSS_ERROR
;
475 dgettext(TEXT_DOMAIN
,
476 "gss_import_name failed for client name\n"));
480 if (!rpc_gss_mech_to_oid(mech
, (rpc_gss_OID
*)&mech_oid
)) {
482 dgettext(TEXT_DOMAIN
,
483 "Invalid mechanism oid <%s>"), mech
);
488 oid_set
.elements
= mech_oid
;
490 gssstat
= gss_acquire_cred(&minor_stat
, gss_client
, 0,
491 &oid_set
, GSS_C_INITIATE
,
492 &gss_client_creds
, NULL
, NULL
);
493 (void) gss_release_name(&minor_stat
, &gss_client
);
494 if (gssstat
!= GSS_S_COMPLETE
) {
495 code
= KADM5_GSS_ERROR
;
497 dgettext(TEXT_DOMAIN
,
498 "could not acquire credentials, "
499 "major error code: %d\n"), gssstat
);
502 handle
->my_cred
= gss_client_creds
;
503 options_req
.my_cred
= gss_client_creds
;
504 options_req
.req_flags
= GSS_C_MUTUAL_FLAG
| GSS_C_REPLAY_FLAG
;
505 options_req
.time_req
= 0;
506 options_req
.input_channel_bindings
= NULL
;
508 handle
->clnt
->cl_auth
= rpc_gss_seccreate(handle
->clnt
,
515 #endif /* ! INIT_TEST */
518 (void) krb5_setenv("KRB5CCNAME", ccname_orig
, 1);
521 (void) krb5_unsetenv("KRB5CCNAME");
523 if (handle
->clnt
->cl_auth
== NULL
) {
524 code
= KADM5_GSS_ERROR
;
525 display_status(dgettext(TEXT_DOMAIN
,
526 "rpc_gss_seccreate failed\n"),
527 options_ret
.major_status
,
528 options_ret
.minor_status
,
534 * Bypass the remainder of the code and return straightaway
535 * if the gss service requested is kiprop
537 if (iprop_enable
== B_TRUE
) {
542 r
= init_2(&handle
->api_version
, handle
->clnt
);
543 /* Solaris Kerberos: 163 resync */
545 ADMIN_LOGO(LOG_ERR
, dgettext(TEXT_DOMAIN
,
546 "error during admin api initialization\n"));
547 code
= KADM5_RPC_ERROR
;
554 dgettext(TEXT_DOMAIN
,
555 "error during admin api initialization: %d\n"),
563 (void) endnetconfig(handlep
);
565 * gss_client_creds is freed only when there is an error condition,
566 * given that rpc_gss_seccreate() will assign the cred pointer to the
567 * my_cred member in the auth handle's private data structure.
569 if (code
&& (gss_client_creds
!= GSS_C_NO_CREDENTIAL
))
570 (void) gss_release_cred(&minor_stat
, &gss_client_creds
);
575 static kadm5_ret_t
_kadm5_init_any(char *client_name
,
576 enum init_type init_type
,
578 krb5_ccache ccache_in
,
580 kadm5_config_params
*params_in
,
581 krb5_ui_4 struct_version
,
582 krb5_ui_4 api_version
,
584 void **server_handle
)
588 krb5_ccache ccache
= NULL
;
590 OM_uint32 gssstat
, minor_stat
;
591 kadm5_server_handle_t handle
;
592 kadm5_config_params params_local
;
594 krb5_get_init_creds_opt opt
;
595 gss_buffer_desc input_name
;
596 krb5_error_code kret
;
597 krb5_int32 starttime
;
599 krb5_principal serverp
= NULL
, clientp
= NULL
;
600 krb5_principal saved_server
= NULL
;
603 ADMIN_LOGO(LOG_ERR
, dgettext(TEXT_DOMAIN
,
604 "entering kadm5_init_any\n"));
605 if (! server_handle
) {
609 if (! (handle
= malloc(sizeof(*handle
)))) {
612 if (! (handle
->lhandle
= malloc(sizeof(*handle
)))) {
617 handle
->magic_number
= KADM5_SERVER_HANDLE_MAGIC
;
618 handle
->struct_version
= struct_version
;
619 handle
->api_version
= api_version
;
621 handle
->cache_name
= 0;
622 handle
->destroy_cache
= 0;
623 *handle
->lhandle
= *handle
;
624 handle
->lhandle
->api_version
= KADM5_API_VERSION_2
;
625 handle
->lhandle
->struct_version
= KADM5_STRUCT_VERSION
;
626 handle
->lhandle
->lhandle
= handle
->lhandle
;
628 kret
= krb5_init_context(&handle
->context
);
630 free(handle
->lhandle
);
635 if(service_name
== NULL
|| client_name
== NULL
) {
636 krb5_free_context(handle
->context
);
637 free(handle
->lhandle
);
641 memset((char *) &creds
, 0, sizeof(creds
));
644 * Verify the version numbers before proceeding; we can't use
645 * CHECK_HANDLE because not all fields are set yet.
647 GENERIC_CHECK_HANDLE(handle
, KADM5_OLD_LIB_API_VERSION
,
648 KADM5_NEW_LIB_API_VERSION
);
651 * Acquire relevant profile entries. In version 2, merge values
652 * in params_in with values from profile, based on
655 * In version 1, we've given a realm (which may be NULL) instead
656 * of params_in. So use that realm, make params_in contain an
657 * empty mask, and behave like version 2.
659 memset((char *) ¶ms_local
, 0, sizeof(params_local
));
660 if (api_version
== KADM5_API_VERSION_1
) {
662 params_local
.mask
= KADM5_CONFIG_REALM
;
663 params_in
= ¶ms_local
;
666 #define ILLEGAL_PARAMS ( \
667 KADM5_CONFIG_ACL_FILE | KADM5_CONFIG_ADB_LOCKFILE | \
668 KADM5_CONFIG_DBNAME | KADM5_CONFIG_ADBNAME | \
669 KADM5_CONFIG_DICT_FILE | KADM5_CONFIG_ADMIN_KEYTAB | \
670 KADM5_CONFIG_STASH_FILE | KADM5_CONFIG_MKEY_NAME | \
671 KADM5_CONFIG_ENCTYPE | KADM5_CONFIG_MAX_LIFE | \
672 KADM5_CONFIG_MAX_RLIFE | KADM5_CONFIG_EXPIRATION | \
673 KADM5_CONFIG_FLAGS | KADM5_CONFIG_ENCTYPES | \
674 KADM5_CONFIG_MKEY_FROM_KBD)
676 if (params_in
&& params_in
->mask
& ILLEGAL_PARAMS
) {
677 krb5_free_context(handle
->context
);
678 free(handle
->lhandle
);
680 ADMIN_LOG(LOG_ERR
, dgettext(TEXT_DOMAIN
,
681 "bad client parameters, returning %d"),
682 KADM5_BAD_CLIENT_PARAMS
);
683 return KADM5_BAD_CLIENT_PARAMS
;
686 if ((code
= kadm5_get_config_params(handle
->context
, 0,
687 params_in
, &handle
->params
))) {
688 krb5_free_context(handle
->context
);
689 free(handle
->lhandle
);
691 ADMIN_LOG(LOG_ERR
, dgettext(TEXT_DOMAIN
,
692 "failed to get config_params, return: %d\n"), code
);
696 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \
697 KADM5_CONFIG_ADMIN_SERVER | \
698 KADM5_CONFIG_KADMIND_PORT)
699 #define KPW_REQUIRED_PARAMS (KADM5_CONFIG_REALM | \
700 KADM5_CONFIG_KPASSWD_SERVER | \
701 KADM5_CONFIG_KPASSWD_PORT)
703 if (((handle
->params
.mask
& REQUIRED_PARAMS
) != REQUIRED_PARAMS
) &&
704 ((handle
->params
.mask
& KPW_REQUIRED_PARAMS
) != KPW_REQUIRED_PARAMS
)) {
705 (void) kadm5_free_config_params(handle
->context
,
707 krb5_free_context(handle
->context
);
708 free(handle
->lhandle
);
710 ADMIN_LOGO(LOG_ERR
, dgettext(TEXT_DOMAIN
,
711 "missing config parameters\n"));
712 return KADM5_MISSING_KRB5_CONF_PARAMS
;
716 * Acquire a service ticket for service_name@realm in the name of
717 * client_name, using password pass (which could be NULL), and
718 * create a ccache to store them in. If INIT_CREDS, use the
719 * ccache we were provided instead.
721 if ((code
= krb5_parse_name(handle
->context
, client_name
,
723 ADMIN_LOGO(LOG_ERR
, dgettext(TEXT_DOMAIN
,
724 "could not parse client name\n"));
727 clientp
= creds
.client
;
729 if (strncmp(service_name
, KADM5_CHANGEPW_HOST_SERVICE
,
730 strlen(KADM5_CHANGEPW_HOST_SERVICE
)) == 0)
733 if (init_type
== INIT_PASS
&&
734 handle
->params
.kpasswd_protocol
== KRB5_CHGPWD_CHANGEPW_V2
&&
737 * The 'service_name' is constructed by the caller
738 * but its done before the parameter which determines
739 * the kpasswd_protocol is found. The servers that
740 * support the SET/CHANGE password protocol expect
741 * a slightly different service principal than
742 * the normal SEAM kadmind so construct the correct
743 * name here and then forget it.
745 char *newsvcname
= NULL
;
746 newsvcname
= malloc(strlen(KADM5_CHANGEPW_SERVICE
) +
747 strlen(handle
->params
.realm
) + 2);
748 if (newsvcname
== NULL
) {
749 ADMIN_LOGO(LOG_ERR
, dgettext(TEXT_DOMAIN
,
750 "could not malloc\n"));
754 sprintf(newsvcname
, "%s@%s", KADM5_CHANGEPW_SERVICE
,
755 handle
->params
.realm
);
757 if ((code
= krb5_parse_name(handle
->context
, newsvcname
,
759 ADMIN_LOGO(LOG_ERR
, dgettext(TEXT_DOMAIN
,
760 "could not parse server "
767 input_name
.value
= service_name
;
768 input_name
.length
= strlen((char *)input_name
.value
) + 1;
769 gssstat
= krb5_gss_import_name(&minor_stat
,
771 (gss_OID
)GSS_C_NT_HOSTBASED_SERVICE
,
772 (gss_name_t
*)&creds
.server
);
774 if (gssstat
!= GSS_S_COMPLETE
) {
775 code
= KADM5_GSS_ERROR
;
776 ADMIN_LOGO(LOG_ERR
, dgettext(TEXT_DOMAIN
,
777 "gss_import_name failed for client name\n"));
781 serverp
= creds
.server
;
783 /* XXX temporarily fix a bug in krb5_cc_get_type */
784 #undef krb5_cc_get_type
785 #define krb5_cc_get_type(context, cache) ((cache)->ops->prefix)
788 if (init_type
== INIT_CREDS
) {
790 handle
->cache_name
= (char *)
791 malloc(strlen(krb5_cc_get_type(handle
->context
, ccache
)) +
792 strlen(krb5_cc_get_name(handle
->context
, ccache
)) + 2);
793 if (handle
->cache_name
== NULL
) {
797 sprintf(handle
->cache_name
, "%s:%s",
798 krb5_cc_get_type(handle
->context
, ccache
),
799 krb5_cc_get_name(handle
->context
, ccache
));
803 (char *) malloc(strlen(ADM_CCACHE
)+strlen("FILE:")+1);
804 if (handle
->cache_name
== NULL
) {
808 sprintf(handle
->cache_name
, "FILE:%s", ADM_CCACHE
);
809 mktemp(handle
->cache_name
+ strlen("FILE:"));
812 static int counter
= 0;
813 handle
->cache_name
= malloc(sizeof("MEMORY:kadm5_")
814 + 3*sizeof(counter
));
815 sprintf(handle
->cache_name
, "MEMORY:kadm5_%u", counter
++);
818 if ((code
= krb5_cc_resolve(handle
->context
, handle
->cache_name
,
822 if ((code
= krb5_cc_initialize (handle
->context
, ccache
,
826 handle
->destroy_cache
= 1;
828 handle
->lhandle
->cache_name
= handle
->cache_name
;
829 ADMIN_LOG(LOG_ERR
, dgettext(TEXT_DOMAIN
,
830 "cache created: %s\n"), handle
->cache_name
);
832 if ((code
= krb5_timeofday(handle
->context
, &now
)))
836 * Get a ticket, use the method specified in init_type.
839 creds
.times
.starttime
= 0; /* start timer at KDC */
840 creds
.times
.endtime
= 0; /* endtime will be limited by service */
842 memset(&opt
, 0, sizeof (opt
));
843 krb5_get_init_creds_opt_init(&opt
);
845 if (creds
.times
.endtime
) {
846 if (creds
.times
.starttime
)
847 starttime
= creds
.times
.starttime
;
851 krb5_get_init_creds_opt_set_tkt_life(&opt
,
852 creds
.times
.endtime
- starttime
);
854 code
= krb5_unparse_name(handle
->context
, creds
.server
, &server
);
860 * Save the original creds.server as krb5_get_init_creds*() always
861 * sets the realm of the server to the client realm.
863 code
= krb5_copy_principal(handle
->context
, creds
.server
, &saved_server
);
867 if (init_type
== INIT_PASS
) {
868 code
= krb5_get_init_creds_password(handle
->context
,
869 &creds
, creds
.client
, pass
, NULL
,
870 NULL
, creds
.times
.starttime
,
872 } else if (init_type
== INIT_SKEY
) {
873 krb5_keytab kt
= NULL
;
875 if (!(pass
&& (code
= krb5_kt_resolve(handle
->context
,
877 code
= krb5_get_init_creds_keytab(
879 &creds
, creds
.client
, kt
,
880 creds
.times
.starttime
,
883 if (pass
) krb5_kt_close(handle
->context
, kt
);
887 /* Improved error messages */
888 if (code
== KRB5KRB_AP_ERR_BAD_INTEGRITY
) code
= KADM5_BAD_PASSWORD
;
889 if (code
== KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
)
890 code
= KADM5_SECURE_PRINC_MISSING
;
893 ADMIN_LOGO(LOG_ERR
, dgettext(TEXT_DOMAIN
,
894 "failed to obtain credentials cache\n"));
895 krb5_free_principal(handle
->context
, saved_server
);
901 * If the server principal had an empty realm then store that in
902 * the cred cache and not the server realm as returned by
903 * krb5_get_init_creds_{keytab|password}(). This ensures that rpcsec_gss
904 * will find the credential in the cred cache even if a "fallback"
905 * method is being used to determine the realm.
907 if (init_type
!= INIT_CREDS
) {
908 krb5_free_principal(handle
->context
, creds
.server
);
910 creds
.server
= saved_server
;
913 * If we got this far, save the creds in the cache.
916 code
= krb5_cc_store_cred(handle
->context
, ccache
, &creds
);
919 ADMIN_LOGO(LOG_ERR
, dgettext(TEXT_DOMAIN
, "obtained credentials cache\n"));
923 memset(pass
, 0, strlen(pass
));
926 if (init_type
!= INIT_PASS
||
927 handle
->params
.kpasswd_protocol
== KRB5_CHGPWD_RPCSEC
||
929 code
= _kadm5_initialize_rpcsec_gss_handle(handle
,
930 client_name
, service_name
);
934 * If _kadm5_initialize_rpcsec_gss_handle() fails it will have
935 * called krb5_gss_release_cred(). If the credential cache is a
936 * MEMORY cred cache krb5_gss_release_cred() destroys the
937 * cred cache data. Make sure that the cred-cache is closed
938 * to prevent a double free in the "error" code.
941 if (init_type
!= INIT_CREDS
) {
942 krb5_cc_close(handle
->context
, ccache
);
949 *server_handle
= (void *) handle
;
951 if (init_type
!= INIT_CREDS
)
952 krb5_cc_close(handle
->context
, ccache
);
958 * Note that it is illegal for this code to execute if "handle"
959 * has not been allocated and initialized. I.e., don't use "goto
960 * error" before the block of code at the top of the function
961 * that allocates and initializes "handle".
963 free(handle
->cache_name
);
964 if (handle
->destroy_cache
&& ccache
)
965 krb5_cc_destroy(handle
->context
, ccache
);
966 if(handle
->clnt
&& handle
->clnt
->cl_auth
)
967 AUTH_DESTROY(handle
->clnt
->cl_auth
);
969 clnt_destroy(handle
->clnt
);
970 (void) kadm5_free_config_params(handle
->context
, &handle
->params
);
976 * cred's server and client pointers could have been overwritten
977 * by the krb5_get_init_* functions. If the addresses are different
978 * before and after the calls then we must free the memory that
979 * was allocated before the call.
981 if (clientp
&& clientp
!= creds
.client
)
982 krb5_free_principal(handle
->context
, clientp
);
984 if (serverp
&& serverp
!= creds
.server
)
985 krb5_free_principal(handle
->context
, serverp
);
987 krb5_free_cred_contents(handle
->context
, &creds
);
990 * Dont clean up the handle if the code is OK (code==0)
991 * because it is returned to the caller in the 'server_handle'
995 krb5_free_context(handle
->context
);
996 free(handle
->lhandle
);
1004 kadm5_destroy(void *server_handle
)
1006 krb5_ccache ccache
= NULL
;
1007 int code
= KADM5_OK
;
1008 kadm5_server_handle_t handle
=
1009 (kadm5_server_handle_t
) server_handle
;
1012 CHECK_HANDLE(server_handle
);
1014 * krb5_cc_resolve() will resolve a ccache with the same data that
1015 * handle->my_cred points to. If the ccache is a MEMORY ccache then
1016 * gss_release_cred() will free that data (it doesn't do this when ccache
1017 * is a FILE ccache).
1018 * if'ed out to avoid the double free.
1021 if (handle
->destroy_cache
&& handle
->cache_name
) {
1022 if ((code
= krb5_cc_resolve(handle
->context
,
1023 handle
->cache_name
, &ccache
)) == 0)
1024 code
= krb5_cc_destroy (handle
->context
, ccache
);
1027 free(handle
->cache_name
);
1028 if (handle
->clnt
&& handle
->clnt
->cl_auth
) {
1030 * Since kadm5 doesn't use the default credentials we
1031 * must clean this up manually.
1033 if (handle
->my_cred
!= GSS_C_NO_CREDENTIAL
)
1034 (void) gss_release_cred(&min_stat
, &handle
->my_cred
);
1035 AUTH_DESTROY(handle
->clnt
->cl_auth
);
1038 clnt_destroy(handle
->clnt
);
1039 free(handle
->lhandle
);
1041 kadm5_free_config_params(handle
->context
, &handle
->params
);
1042 krb5_free_context(handle
->context
);
1044 handle
->magic_number
= 0;
1049 /* not supported on client */
1050 kadm5_ret_t
kadm5_lock(void *server_handle
)
1055 /* not supported on client */
1056 kadm5_ret_t
kadm5_unlock(void *server_handle
)
1061 kadm5_ret_t
kadm5_flush(void *server_handle
)
1066 int _kadm5_check_handle(void *handle
)
1068 CHECK_HANDLE(handle
);
1072 krb5_error_code
kadm5_init_krb5_context (krb5_context
*ctx
)
1074 return krb5_init_context(ctx
);
1078 * Stub function for kadmin. It was created to eliminate the dependency on
1079 * libkdb's ulog functions. The srv equivalent makes the actual calls.
1082 kadm5_init_iprop(void *handle
)