From: Tom Yu Date: Wed, 24 Jul 1996 22:22:49 +0000 (+0000) Subject: * Makefile.in, configure.in: break out client lib into a X-Git-Tag: krb5-1.0-beta7~277 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=0e159dd62f2da74c561803a72921a77d9c9f05ff;p=krb5.git * Makefile.in, configure.in: break out client lib into a subdirectory git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@8805 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/lib/kadm5/clnt/ChangeLog b/src/lib/kadm5/clnt/ChangeLog new file mode 100644 index 000000000..cc4e44b09 --- /dev/null +++ b/src/lib/kadm5/clnt/ChangeLog @@ -0,0 +1,5 @@ +Wed Jul 24 18:20:47 1996 Tom Yu + + * Makefile.in, configure.in: break out client lib into a + subdirectory + diff --git a/src/lib/kadm5/clnt/Makefile.in b/src/lib/kadm5/clnt/Makefile.in new file mode 100644 index 000000000..743ca39d4 --- /dev/null +++ b/src/lib/kadm5/clnt/Makefile.in @@ -0,0 +1,79 @@ +CFLAGS = $(CCOPTS) $(DEFS) -I$(BUILDTOP)/include/kadm5 + +##DOSBUILDTOP = ..\..\.. +##DOSLIBNAME = libkadm5clnt.lib + +.c.o: + $(CC) $(CFLAGS) -c $(srcdir)/$*.c +@SHARED_RULE@ + +SRCS = $(srcdir)/clnt_policy.c \ + $(srcdir)/client_rpc.c \ + $(srcdir)/client_principal.c \ + $(srcdir)/client_init.c \ + $(srcdir)/clnt_privs.c \ + $(srcdir)/clnt_chpass_util.c + +OBJS = @LIBOBJS@ \ + clnt_policy.$(OBJEXT) \ + client_rpc.$(OBJEXT) \ + client_principal.$(OBJEXT) \ + client_init.$(OBJEXT) \ + clnt_privs.$(OBJEXT) \ + clnt_chpass_util.$(OBJEXT) + +LIBUPDATE=$(BUILDTOP)/util/libupdate + +# +# Depends on libgssrpc, libgssapi_krb5, libkdb5, libkrb5, libcrypto, +# libcom_err, libdyn +# +GSSRPC_VER=@GSSRPC_SH_VERS@ +GSSAPI_KRB5_VER=@GSSAPI_KRB5_SH_VERS@ +KDB5_VER=@KDB5_SH_VERS@ +KRB5_VER=@KRB5_SH_VERS@ +CRYPTO_VER=@CRYPTO_SH_VERS@ +COMERR_VER=@COMERR_SH_VERS@ +DYN_VER=@DYN_SH_VERS@ +DEPLIBS=$(TOPLIBD)/libgssrpc.$(SHEXT).$(GSSRPC_VER) \ + $(TOPLIBD)/libgssapi_krb5.$(SHEXT).$(GSSAPI_KRB5_VER) \ + $(TOPLIBD)/libkdb5.$(SHEXT).$(KDB5_VER) \ + $(TOPLIBD)/libkrb5.$(SHEXT).$(KRB5_VER) \ + $(TOPLIBD)/libcrypto.$(SHEXT).$(CRYPTO_VER) \ + $(TOPLIBD)/libcom_err.$(SHEXT).$(COMERR_VER) \ + $(TOPLIBD)/libdyn.$(SHEXT).$(DYN_VER) + +SHLIB_LIBS=-lgssrpc -lgssapi_krb5 -lkdb5 -lkrb5 -lcrypto -lcom_err -ldyn +SHLIB_LDFLAGS= $(LDFLAGS) @SHLIB_RPATH_DIRS@ +SHLIB_LIBDIRS= @SHLIB_LIBDIRS@ + +all-unix:: shared includes $(OBJS) +all-mac:: $(OBJS) +all-windows:: $(OBJS) + +LIBDONE=../DONE DONE +LIB_SUBDIRS=.. . + +shared: + mkdir shared + +libkadm5clnt.$(STEXT): $(LIBDONE) + @if test -f $@ ; then \ + (set -x; $(LIBUPDATE) $@ DONE $(LIB_SUBDIRS)) \ + else \ + (set -x; $(LIBUPDATE) --force $@ DONE $(LIB_SUBDIRS)) \ + fi + $(RANLIB) $@ + touch libkadm5clnt.stamp + +check-windows:: + +clean-mac:: +clean-windows:: + +clean-unix:: + $(RM) libkadm5clnt.$(STEXT) libkadm5clnt.stamp + +install:: libkadm5clnt.a + $(INSTALL_DATA) libkadm5clnt.a $(DESTDIR)$(KRB5_LIBDIR)/libkadm5clnt.a + $(RANLIB) $(DESTDIR)$(KRB5_LIBDIR)/libkadm5clnt.a diff --git a/src/lib/kadm5/clnt/client_handle.c b/src/lib/kadm5/clnt/client_handle.c new file mode 100644 index 000000000..895777a6e --- /dev/null +++ b/src/lib/kadm5/clnt/client_handle.c @@ -0,0 +1,9 @@ +#include +#include +#include "client_internal.h" + +int _kadm5_check_handle(void *handle) +{ + CHECK_HANDLE(handle); + return 0; +} diff --git a/src/lib/kadm5/clnt/client_init.c b/src/lib/kadm5/clnt/client_init.c new file mode 100644 index 000000000..bfc1c3a3c --- /dev/null +++ b/src/lib/kadm5/clnt/client_init.c @@ -0,0 +1,554 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for KRB5_ADM_DEFAULT_PORT */ +#ifdef __STDC__ +#include +#endif + +#include +#include +#include "client_internal.h" + +#include +#include +#include +#include + +#define ADM_CCACHE "/tmp/ovsec_adm.XXXXXX" + +#ifndef _POSIX_SOURCE /* Perhaps this should actually say __STDC__ */ +int setenv(const char *var, const char *val, int flag); +void unsetenv(const char *var); +#endif + +enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS }; + +static kadm5_ret_t _kadm5_init_any(char *client_name, + enum init_type init_type, + char *pass, + krb5_ccache ccache_in, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle); + +kadm5_ret_t kadm5_init_with_creds(char *client_name, + krb5_ccache ccache, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return _kadm5_init_any(client_name, INIT_CREDS, NULL, ccache, + service_name, params, + struct_version, api_version, + server_handle); +} + + +kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return _kadm5_init_any(client_name, INIT_PASS, pass, NULL, + service_name, params, struct_version, + api_version, server_handle); +} + +kadm5_ret_t kadm5_init(char *client_name, char *pass, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return _kadm5_init_any(client_name, INIT_PASS, pass, NULL, + service_name, params, struct_version, + api_version, server_handle); +} + +kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return _kadm5_init_any(client_name, INIT_SKEY, keytab, NULL, + service_name, params, struct_version, + api_version, server_handle); +} + +/* + * Try no preauthentication first; then try the encrypted timestamp + * (stolen from krb5 kinit.c) + */ +static int preauth_search_list[] = { + 0, + KRB5_PADATA_ENC_UNIX_TIME, + -1 +}; + +static kadm5_ret_t _kadm5_init_any(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, + void **server_handle) +{ + struct sockaddr_in addr; + struct hostent *hp; + struct servent *srv; + int fd; + int i; + + char full_service_name[BUFSIZ], host[MAXHOSTNAMELEN], *ccname_orig; + char *realm; + krb5_creds creds; + krb5_ccache ccache = NULL; + krb5_timestamp now; + + OM_uint32 gssstat, minor_stat; + gss_buffer_desc input_name; + gss_name_t gss_target, gss_client; + gss_cred_id_t gss_client_creds = GSS_C_NO_CREDENTIAL; + + kadm5_server_handle_t handle; + kadm5_config_params params_local; + + int code = 0; + generic_ret *r; + + initialize_ovk_error_table(); + initialize_adb_error_table(); + initialize_ovku_error_table(); + + if (! server_handle) { + return EINVAL; + } + + if (! (handle = malloc(sizeof(*handle)))) { + return ENOMEM; + } + if (! (handle->lhandle = malloc(sizeof(*handle)))) { + free(handle); + return ENOMEM; + } + + handle->magic_number = KADM5_SERVER_HANDLE_MAGIC; + handle->struct_version = struct_version; + handle->api_version = api_version; + handle->clnt = 0; + handle->cache_name = 0; + handle->destroy_cache = 0; + *handle->lhandle = *handle; + handle->lhandle->api_version = KADM5_API_VERSION_2; + handle->lhandle->struct_version = KADM5_STRUCT_VERSION; + handle->lhandle->lhandle = handle->lhandle; + + krb5_init_context(&handle->context); + krb5_init_ets(handle->context); + + if(service_name == NULL || client_name == NULL) { + free(handle); + return EINVAL; + } + memset((char *) &creds, 0, sizeof(creds)); + + /* + * Verify the version numbers before proceeding; we can't use + * CHECK_HANDLE because not all fields are set yet. + */ + GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION, + KADM5_NEW_LIB_API_VERSION); + + /* + * Acquire relevant profile entries. In version 2, merge values + * in params_in with values from profile, based on + * params_in->mask. + * + * In version 1, we've given a realm (which may be NULL) instead + * of params_in. So use that realm, make params_in contain an + * empty mask, and behave like version 2. + */ + memset((char *) ¶ms_local, 0, sizeof(params_local)); + if (api_version == KADM5_API_VERSION_1) { + realm = params_local.realm = (char *) params_in; + if (params_in) + params_local.mask = KADM5_CONFIG_REALM; + params_in = ¶ms_local; + } else { + if (params_in && (params_in->mask & KADM5_CONFIG_REALM)) + realm = params_in->realm; + else + realm = NULL; + } + +#define ILLEGAL_PARAMS (KADM5_CONFIG_DBNAME | KADM5_CONFIG_ADBNAME | \ + KADM5_CONFIG_ADB_LOCKFILE | \ + KADM5_CONFIG_ACL_FILE | KADM5_CONFIG_DICT_FILE \ + | KADM5_CONFIG_ADMIN_KEYTAB | \ + KADM5_CONFIG_STASH_FILE | \ + KADM5_CONFIG_MKEY_NAME | KADM5_CONFIG_ENCTYPE \ + | KADM5_CONFIG_MAX_LIFE | \ + KADM5_CONFIG_MAX_RLIFE | \ + KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_FLAGS | \ + KADM5_CONFIG_ENCTYPES | KADM5_CONFIG_MKEY_FROM_KBD) + + if (params_in && params_in->mask & ILLEGAL_PARAMS) { + free(handle); + return KADM5_BAD_CLIENT_PARAMS; + } + + if (code = kadm5_get_config_params(handle->context, + "/etc/krb5.conf", + "KRB5_CONFIG", + params_in, + &handle->params)) { + krb5_free_context(handle->context); + free(handle); + return(code); + } + +#define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ + KADM5_CONFIG_ADMIN_SERVER | \ + KADM5_CONFIG_KADMIND_PORT) + + if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { + krb5_free_context(handle->context); + free(handle); + return KRB5_CONFIG_BADFORMAT; + } + + /* + * Acquire a service ticket for service_name@realm in the name of + * client_name, using password pass (which could be NULL), and + * create a ccache to store them in. If INIT_CREDS, use the + * ccache we were provided instead. + */ + + if ((code = krb5_parse_name(handle->context, client_name, &creds.client))) + goto error; + + if (realm) { + sprintf(full_service_name, "%s@%s", service_name, realm); + } else { + /* krb5_princ_realm(creds.client) is not null terminated */ + strcpy(full_service_name, service_name); + strcat(full_service_name, "@"); + strncat(full_service_name, krb5_princ_realm(handle->context, + creds.client)->data, + krb5_princ_realm(handle->context, creds.client)->length); + } + + if ((code = krb5_parse_name(handle->context, full_service_name, + &creds.server))) + goto error; + + /* XXX temporarily fix a bug in krb5_cc_get_type */ +#undef krb5_cc_get_type +#define krb5_cc_get_type(context, cache) ((cache)->ops->prefix) + + + if (init_type == INIT_CREDS) { + ccache = ccache_in; + handle->cache_name = (char *) + malloc(strlen(krb5_cc_get_type(handle->context, ccache)) + + strlen(krb5_cc_get_name(handle->context, ccache)) + 2); + if (handle->cache_name == NULL) { + code = ENOMEM; + goto error; + } + sprintf(handle->cache_name, "%s:%s", + krb5_cc_get_type(handle->context, ccache), + krb5_cc_get_name(handle->context, ccache)); + } else { + handle->cache_name = + (char *) malloc(strlen(ADM_CCACHE)+strlen("FILE:")+1); + if (handle->cache_name == NULL) { + code = ENOMEM; + goto error; + } + sprintf(handle->cache_name, "FILE:%s", ADM_CCACHE); + mktemp(handle->cache_name + strlen("FILE:")); + + if ((code = krb5_cc_resolve(handle->context, handle->cache_name, + &ccache))) + goto error; + + if ((code = krb5_cc_initialize (handle->context, ccache, + creds.client))) + goto error; + + handle->destroy_cache = 1; + } + handle->lhandle->cache_name = handle->cache_name; + + if ((code = krb5_timeofday(handle->context, &now))) + goto error; + + /* + * Get a ticket, use the method specified in init_type. + */ + + creds.times.starttime = 0; /* start timer at KDC */ + creds.times.endtime = 0; /* endtime will be limited by service */ + + if (init_type == INIT_PASS) { + for (i=0; preauth_search_list[i] >= 0; i++) { + code = krb5_get_in_tkt_with_password(handle->context, + 0, /* no options */ + 0, /* default addresses */ + NULL, + NULL, /* XXX preauth */ + pass, + ccache, + &creds, + NULL); + if (code != KRB5KDC_ERR_PREAUTH_FAILED && + code != KRB5KDC_ERR_PREAUTH_REQUIRED && + code != KRB5KRB_ERR_GENERIC) + break; + } + } else if (init_type == INIT_SKEY) { + krb5_keytab kt = NULL; + + if (pass && (code = krb5_kt_resolve(handle->context, pass, &kt))) + ; + else { + for (i=0; preauth_search_list[i] >= 0; i++) { + code = krb5_get_in_tkt_with_keytab(handle->context, + 0, /* no options */ + 0, /* default addrs */ + NULL, + NULL, /* XXX preauth */ + kt, + ccache, + &creds, + NULL); + if (code != KRB5KDC_ERR_PREAUTH_FAILED && + code != KRB5KDC_ERR_PREAUTH_REQUIRED && + code != KRB5KRB_ERR_GENERIC) + break; + } + + if (pass) krb5_kt_close(handle->context, kt); + } + } + + /* Improved error messages */ + if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD; + if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) + code = KADM5_SECURE_PRINC_MISSING; + + if (code != 0) goto error; + +#ifdef ZEROPASSWD + if (pass != NULL) + memset(pass, 0, strlen(pass)); +#endif + + /* + * We have ticket; open the RPC connection. + */ + + hp = gethostbyname(handle->params.admin_server); + if (hp == (struct hostent *) NULL) { + code = KRB5_CONFIG_BADFORMAT; + goto cleanup; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = hp->h_addrtype; + (void) memcpy((char *) &addr.sin_addr, (char *) hp->h_addr, + sizeof(addr.sin_addr)); + addr.sin_port = htons((u_short) handle->params.kadmind_port); + + fd = RPC_ANYSOCK; + + handle->clnt = clnttcp_create(&addr, KADM, KADMVERS, &fd, 0, 0); + if (handle->clnt == NULL) { + code = KADM5_RPC_ERROR; + goto error; + } + handle->lhandle->clnt = handle->clnt; + + /* now that handle->clnt is set, we can check the handle */ + if (code = _kadm5_check_handle((void *) handle)) + goto error; + + /* + * The RPC connection is open; establish the GSS-API + * authentication context. + */ + + /* use the kadm5 cache */ + ccname_orig = getenv("KRB5CCNAME"); + if (ccname_orig) + ccname_orig = strdup(ccname_orig); + + (void) setenv("KRB5CCNAME", handle->cache_name, 1); + +#ifndef INIT_TEST + input_name.value = full_service_name; + input_name.length = strlen((char *)input_name.value) + 1; + gssstat = gss_import_name(&minor_stat, &input_name, + gss_nt_krb5_name, &gss_target); + if (gssstat != GSS_S_COMPLETE) { + code = KADM5_GSS_ERROR; + goto error; + } +#endif /* ! INIT_TEST */ + + input_name.value = client_name; + input_name.length = strlen((char *)input_name.value) + 1; + gssstat = gss_import_name(&minor_stat, &input_name, + gss_nt_krb5_name, &gss_client); + if (gssstat != GSS_S_COMPLETE) { + code = KADM5_GSS_ERROR; + goto error; + } + + gssstat = gss_acquire_cred(&minor_stat, gss_client, 0, + GSS_C_NULL_OID_SET, GSS_C_INITIATE, + &gss_client_creds, NULL, NULL); + (void) gss_release_name(&minor_stat, &gss_client); + if (gssstat != GSS_S_COMPLETE) { + code = KADM5_GSS_ERROR; + goto error; + } + +#ifndef INIT_TEST + handle->clnt->cl_auth = auth_gssapi_create(handle->clnt, + &gssstat, + &minor_stat, + gss_client_creds, + gss_target, + GSS_C_NULL_OID, + GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, + 0, + NULL, + NULL, + NULL); + (void) gss_release_name(&minor_stat, &gss_target); +#endif /* ! INIT_TEST */ + + if (ccname_orig) { + (void) setenv("KRB5CCNAME", ccname_orig, 1); + free(ccname_orig); + } else + (void) unsetenv("KRB5CCNAME"); + + + if (handle->clnt->cl_auth == NULL) { + code = KADM5_GSS_ERROR; + goto error; + } + + r = init_1(&handle->api_version, handle->clnt); + if (r == NULL) { + code = KADM5_RPC_ERROR; + goto error; + } + if (r->code) { + code = r->code; + goto error; + } + + *server_handle = (void *) handle; + + if (init_type != INIT_CREDS) + krb5_cc_close(handle->context, ccache); + + goto cleanup; + +error: + /* + * Note that it is illegal for this code to execute if "handle" + * has not been allocated and initialized. I.e., don't use "goto + * error" before the block of code at the top of the function + * that allocates and initializes "handle". + */ + if (handle->cache_name) + free(handle->cache_name); + if (handle->destroy_cache && ccache) + krb5_cc_destroy(handle->context, ccache); + if(handle->clnt && handle->clnt->cl_auth) + AUTH_DESTROY(handle->clnt->cl_auth); + if(handle->clnt) + clnt_destroy(handle->clnt); + +cleanup: + krb5_free_cred_contents(handle->context, &creds); + if (gss_client_creds != GSS_C_NO_CREDENTIAL) + (void) gss_release_cred(&minor_stat, &gss_client_creds); + + if (code) + free(handle); + + return code; +} + +kadm5_ret_t +kadm5_destroy(void *server_handle) +{ + krb5_ccache ccache = NULL; + int code = KADM5_OK; + kadm5_server_handle_t handle = + (kadm5_server_handle_t) server_handle; + + CHECK_HANDLE(server_handle); + + if (handle->destroy_cache && handle->cache_name) { + if ((code = krb5_cc_resolve(handle->context, + handle->cache_name, &ccache)) == 0) + code = krb5_cc_destroy (handle->context, ccache); + } + if (handle->cache_name) + free(handle->cache_name); + if (handle->clnt && handle->clnt->cl_auth) + AUTH_DESTROY(handle->clnt->cl_auth); + if (handle->clnt) + clnt_destroy(handle->clnt); + + handle->magic_number = 0; + free(handle); + + return code; +} + +kadm5_ret_t kadm5_flush(void *server_handle) +{ + return KADM5_OK; +} + +int _kadm5_check_handle(void *handle) +{ + CHECK_HANDLE(handle); + return 0; +} diff --git a/src/lib/kadm5/clnt/client_internal.h b/src/lib/kadm5/clnt/client_internal.h new file mode 100644 index 000000000..c5ebfec77 --- /dev/null +++ b/src/lib/kadm5/clnt/client_internal.h @@ -0,0 +1,97 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.1 1996/07/24 22:22:43 tlyu + * * Makefile.in, configure.in: break out client lib into a + * subdirectory + * + * Revision 1.11 1996/07/22 20:35:46 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.10.4.1 1996/07/18 03:08:37 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.10.2.1 1996/06/20 02:16:46 marc + * File added to the repository on a branch + * + * Revision 1.10 1996/06/06 20:09:16 bjaspan + * add destroy_cache, for kadm5_init_with_creds + * + * Revision 1.9 1996/05/30 21:04:42 bjaspan + * add lhandle to handle + * + * Revision 1.8 1996/05/28 20:33:49 bjaspan + * rework kadm5_config + * + * Revision 1.7 1996/05/17 21:36:59 bjaspan + * rename to kadm5, begin implementing version 2 + * + * Revision 1.6 1996/05/16 21:45:07 bjaspan + * add context + * + * Revision 1.5 1996/05/08 21:10:23 bjaspan + * marc's changes + * + * Revision 1.4 1996/01/16 20:54:30 grier + * secure/3570 use krb5_ui_4 not unsigned int + * + * Revision 1.3 1995/11/14 17:48:57 grier + * long to int + * + * Revision 1.2 1994/08/16 18:53:47 jik + * Versioning stuff. + * + * Revision 1.1 1994/08/09 21:14:38 jik + * Initial revision + * + */ + +/* + * This header file is used internally by the Admin API client + * libraries. IF YOU THINK YOU NEED TO USE THIS FILE FOR ANYTHING, + * YOU'RE ALMOST CERTAINLY WRONG. + */ + +#ifndef __KADM5_CLIENT_INTERNAL_H__ +#define __KADM5_CLIENT_INTERNAL_H__ + +#include "admin_internal.h" + +typedef struct _kadm5_server_handle_t { + krb5_ui_4 magic_number; + krb5_ui_4 struct_version; + krb5_ui_4 api_version; + char * cache_name; + int destroy_cache; + CLIENT * clnt; + krb5_context context; + kadm5_config_params params; + struct _kadm5_server_handle_t *lhandle; +} kadm5_server_handle_rec, *kadm5_server_handle_t; + +#define CLIENT_CHECK_HANDLE(handle) \ +{ \ + kadm5_server_handle_t srvr = \ + (kadm5_server_handle_t) handle; \ + \ + if (! srvr->clnt) \ + return KADM5_BAD_SERVER_HANDLE; \ + if (! srvr->cache_name) \ + return KADM5_BAD_SERVER_HANDLE; \ + if (! srvr->lhandle) \ + return KADM5_BAD_SERVER_HANDLE; \ +} + +#define CHECK_HANDLE(handle) \ + GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION, \ + KADM5_NEW_LIB_API_VERSION) \ + CLIENT_CHECK_HANDLE(handle) + +#endif /* __KADM5_CLIENT_INTERNAL_H__ */ diff --git a/src/lib/kadm5/clnt/client_principal.c b/src/lib/kadm5/clnt/client_principal.c new file mode 100644 index 000000000..c41922722 --- /dev/null +++ b/src/lib/kadm5/clnt/client_principal.c @@ -0,0 +1,307 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#include "client_internal.h" + +kadm5_ret_t +kadm5_create_principal(void *server_handle, + kadm5_principal_ent_t princ, long mask, + char *pw) +{ + generic_ret *r; + cprinc_arg arg; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + arg.mask = mask; + arg.passwd = pw; + arg.api_version = handle->api_version; + + if(princ == NULL) + return EINVAL; + memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec)); + if (handle->api_version == KADM5_API_VERSION_1) { + /* + * hack hack cough cough. + * krb5_unparse name dumps core if we pass it in garbage + * or null. So, since the client is not allowed to set mod_name + * anyway, we just fill it in with a dummy principal. The server of + * course ignores this. + */ + krb5_parse_name(handle->context, "bogus/bogus", &arg.rec.mod_name); + } else + arg.rec.mod_name = NULL; + + if(!(mask & KADM5_POLICY)) + arg.rec.policy = NULL; + if (! (mask & KADM5_KEY_DATA)) { + arg.rec.n_key_data = 0; + arg.rec.key_data = NULL; + } + if (! (mask & KADM5_TL_DATA)) { + arg.rec.n_tl_data = 0; + arg.rec.tl_data = NULL; + } + + r = create_principal_1(&arg, handle->clnt); + + if (handle->api_version == KADM5_API_VERSION_1) + krb5_free_principal(handle->context, arg.rec.mod_name); + + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_delete_principal(void *server_handle, krb5_principal principal) +{ + dprinc_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if(principal == NULL) + return EINVAL; + arg.princ = principal; + arg.api_version = handle->api_version; + r = delete_principal_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_modify_principal(void *server_handle, + kadm5_principal_ent_t princ, long mask) +{ + mprinc_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + arg.mask = mask; + arg.api_version = handle->api_version; + /* + * cough cough gag gag + * see comment in create_principal. + */ + if(princ == NULL) + return EINVAL; + memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec)); + if(!(mask & KADM5_POLICY)) + arg.rec.policy = NULL; + if (! (mask & KADM5_KEY_DATA)) { + arg.rec.n_key_data = 0; + arg.rec.key_data = NULL; + } + if (! (mask & KADM5_TL_DATA)) { + arg.rec.n_tl_data = 0; + arg.rec.tl_data = NULL; + } + + if (handle->api_version == KADM5_API_VERSION_1) { + /* + * See comment in create_principal + */ + krb5_parse_name(handle->context, "bogus/bogus", &arg.rec.mod_name); + } else + arg.rec.mod_name = NULL; + + r = modify_principal_1(&arg, handle->clnt); + + if (handle->api_version == KADM5_API_VERSION_1) + krb5_free_principal(handle->context, arg.rec.mod_name); + + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_get_principal(void *server_handle, + krb5_principal princ, kadm5_principal_ent_t ent, + long mask) +{ + gprinc_arg arg; + gprinc_ret *r; + kadm5_server_handle_t handle = server_handle; + krb5_error_code retval; + + CHECK_HANDLE(server_handle); + + if(princ == NULL) + return EINVAL; + arg.princ = princ; + if (handle->api_version == KADM5_API_VERSION_1) + arg.mask = KADM5_PRINCIPAL_NORMAL_MASK; + else + arg.mask = mask; + arg.api_version = handle->api_version; + r = get_principal_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + if (handle->api_version == KADM5_API_VERSION_1) { + kadm5_principal_ent_t_v1 *entp; + + entp = (kadm5_principal_ent_t_v1 *) ent; + if (r->code == 0) { + if (!(*entp = (kadm5_principal_ent_t_v1) + malloc(sizeof(kadm5_principal_ent_rec_v1)))) + return ENOMEM; + /* this memcpy works because the v1 structure is an initial + subset of the v2 struct. C guarantees that this will + result in the same layout in memory */ + memcpy(*entp, &r->rec, sizeof(**entp)); + } else { + *entp = NULL; + } + } else { + if (r->code == 0) + memcpy(ent, &r->rec, sizeof(r->rec)); + } + + return r->code; +} + +kadm5_ret_t +kadm5_get_principals(void *server_handle, + char *exp, char ***princs, int *count) +{ + gprincs_arg arg; + gprincs_ret *r; + kadm5_server_handle_t handle = server_handle; + krb5_error_code retval; + + CHECK_HANDLE(server_handle); + + if(princs == NULL || count == NULL) + return EINVAL; + arg.exp = exp; + arg.api_version = handle->api_version; + r = get_princs_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + if(r->code == 0) { + *count = r->count; + *princs = r->princs; + } else { + *count = 0; + *princs = NULL; + } + + return r->code; +} + +kadm5_ret_t +kadm5_rename_principal(void *server_handle, + krb5_principal source, krb5_principal dest) +{ + rprinc_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + arg.src = source; + arg.dest = dest; + arg.api_version = handle->api_version; + if (source == NULL || dest == NULL) + return EINVAL; + r = rename_principal_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_chpass_principal(void *server_handle, + krb5_principal princ, char *password) +{ + chpass_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + arg.princ = princ; + arg.pass = password; + arg.api_version = handle->api_version; + + if(princ == NULL) + return EINVAL; + r = chpass_principal_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_randkey_principal(void *server_handle, + krb5_principal princ, + krb5_keyblock **key, int *n_keys) +{ + chrand_arg arg; + chrand_ret *r; + krb5_keyblock new; + kadm5_server_handle_t handle = server_handle; + int i, ret; + + CHECK_HANDLE(server_handle); + + arg.princ = princ; + arg.api_version = handle->api_version; + + if(princ == NULL) + return EINVAL; + r = chrand_principal_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + if (handle->api_version == KADM5_API_VERSION_1) { + if (key) + krb5_copy_keyblock(handle->context, &r->key, key); + } else { + if (n_keys) + *n_keys = r->n_keys; + if (key) { + *key = (krb5_keyblock *) malloc(r->n_keys*sizeof(krb5_keyblock)); + if (*key == NULL) + return ENOMEM; + for (i = 0; i < r->n_keys; i++) { + ret = krb5_copy_keyblock_contents(handle->context, + &r->keys[i], + &(*key)[i]); + if (ret) { + free(*key); + return ENOMEM; + } + } + } + } + + return r->code; +} + +/* not supported on client side */ +kadm5_ret_t kadm5_decrypt_key(void *server_handle, + kadm5_principal_ent_t entry, krb5_int32 + ktype, krb5_int32 stype, krb5_int32 + kvno, krb5_keyblock *keyblock, + krb5_keysalt *keysalt, int *kvnop) +{ + return EINVAL; +} diff --git a/src/lib/kadm5/clnt/client_rpc.c b/src/lib/kadm5/clnt/client_rpc.c new file mode 100644 index 000000000..547844a2c --- /dev/null +++ b/src/lib/kadm5/clnt/client_rpc.c @@ -0,0 +1,221 @@ +#include +#include +#include +#include +#include + +/* Default timeout can be changed using clnt_control() */ +static struct timeval TIMEOUT = { 25, 0 }; + +generic_ret * +create_principal_1(argp, clnt) + cprinc_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, CREATE_PRINCIPAL, xdr_cprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +delete_principal_1(argp, clnt) + dprinc_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, DELETE_PRINCIPAL, xdr_dprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +modify_principal_1(argp, clnt) + mprinc_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, MODIFY_PRINCIPAL, xdr_mprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +rename_principal_1(argp, clnt) + rprinc_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, RENAME_PRINCIPAL, xdr_rprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +gprinc_ret * +get_principal_1(argp, clnt) + gprinc_arg *argp; + CLIENT *clnt; +{ + static gprinc_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, GET_PRINCIPAL, xdr_gprinc_arg, argp, xdr_gprinc_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +gprincs_ret * +get_princs_1(argp, clnt) + gprinc_arg *argp; + CLIENT *clnt; +{ + static gprincs_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, GET_PRINCS, xdr_gprincs_arg, argp, + xdr_gprincs_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +chpass_principal_1(argp, clnt) + chpass_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, CHPASS_PRINCIPAL, xdr_chpass_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +chrand_ret * +chrand_principal_1(argp, clnt) + chrand_arg *argp; + CLIENT *clnt; +{ + static chrand_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, CHRAND_PRINCIPAL, xdr_chrand_arg, argp, xdr_chrand_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +create_policy_1(argp, clnt) + cpol_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, CREATE_POLICY, xdr_cpol_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +delete_policy_1(argp, clnt) + dpol_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, DELETE_POLICY, xdr_dpol_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +modify_policy_1(argp, clnt) + mpol_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, MODIFY_POLICY, xdr_mpol_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +gpol_ret * +get_policy_1(argp, clnt) + gpol_arg *argp; + CLIENT *clnt; +{ + static gpol_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, GET_POLICY, xdr_gpol_arg, argp, xdr_gpol_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +gpols_ret * +get_pols_1(argp, clnt) + gprinc_arg *argp; + CLIENT *clnt; +{ + static gpols_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, GET_POLS, xdr_gpols_arg, argp, + xdr_gpols_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +getprivs_ret *get_privs_1(argp, clnt) + void *argp; + CLIENT *clnt; +{ + static getprivs_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, GET_PRIVS, xdr_u_int32, argp, + xdr_getprivs_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +init_1(argp, clnt) + void *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, INIT, xdr_u_int32, argp, + xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} diff --git a/src/lib/kadm5/clnt/clnt_chpass_util.c b/src/lib/kadm5/clnt/clnt_chpass_util.c new file mode 100644 index 000000000..d6c7f0bfb --- /dev/null +++ b/src/lib/kadm5/clnt/clnt_chpass_util.c @@ -0,0 +1,15 @@ +#include +#include "client_internal.h" + +kadm5_ret_t kadm5_chpass_principal_util(void *server_handle, + krb5_principal princ, + char *new_pw, + char **ret_pw, + char *msg_ret) +{ + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + return _kadm5_chpass_principal_util(handle, handle->lhandle, princ, + new_pw, ret_pw, msg_ret); +} diff --git a/src/lib/kadm5/clnt/clnt_policy.c b/src/lib/kadm5/clnt/clnt_policy.c new file mode 100644 index 000000000..f81cf7471 --- /dev/null +++ b/src/lib/kadm5/clnt/clnt_policy.c @@ -0,0 +1,151 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include "client_internal.h" +#include +#include + +kadm5_ret_t +kadm5_create_policy(void *server_handle, + kadm5_policy_ent_t policy, long mask) +{ + cpol_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if(policy == (kadm5_policy_ent_t) NULL) + return EINVAL; + + arg.mask = mask; + arg.api_version = handle->api_version; + memcpy(&arg.rec, policy, sizeof(kadm5_policy_ent_rec)); + r = create_policy_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_delete_policy(void *server_handle, char *name) +{ + dpol_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if(name == NULL) + return EINVAL; + + arg.name = name; + arg.api_version = handle->api_version; + + r = delete_policy_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_modify_policy(void *server_handle, + kadm5_policy_ent_t policy, long mask) + +{ + mpol_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if(policy == (kadm5_policy_ent_t) NULL) + return EINVAL; + + arg.mask = mask; + arg.api_version = handle->api_version; + + memcpy(&arg.rec, policy, sizeof(kadm5_policy_ent_rec)); + r = modify_policy_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_get_policy(void *server_handle, char *name, kadm5_policy_ent_t ent) + +{ + gpol_arg arg; + gpol_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + arg.name = name; + arg.api_version = handle->api_version; + + if(name == NULL) + return EINVAL; + + r = get_policy_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + if (handle->api_version == KADM5_API_VERSION_1) { + kadm5_policy_ent_t *entp; + + entp = (kadm5_policy_ent_t *) ent; + if(r->code == 0) { + if (!(*entp = (kadm5_policy_ent_t) + malloc(sizeof(kadm5_policy_ent_rec)))) + return ENOMEM; + memcpy(*entp, &r->rec, sizeof(**entp)); + } else { + *entp = NULL; + } + } else { + if (r->code == 0) + memcpy(ent, &r->rec, sizeof(r->rec)); + } + + return r->code; +} + +kadm5_ret_t +kadm5_get_policies(void *server_handle, + char *exp, char ***pols, int *count) +{ + gpols_arg arg; + gpols_ret *r; + kadm5_server_handle_t handle = server_handle; + krb5_error_code retval; + + CHECK_HANDLE(server_handle); + + if(pols == NULL || count == NULL) + return EINVAL; + arg.exp = exp; + arg.api_version = handle->api_version; + r = get_pols_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + if(r->code == 0) { + *count = r->count; + *pols = r->pols; + } else { + *count = 0; + *pols = NULL; + } + + return r->code; +} diff --git a/src/lib/kadm5/clnt/clnt_privs.c b/src/lib/kadm5/clnt/clnt_privs.c new file mode 100644 index 000000000..7eab2f80e --- /dev/null +++ b/src/lib/kadm5/clnt/clnt_privs.c @@ -0,0 +1,70 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.1 1996/07/24 22:22:48 tlyu + * * Makefile.in, configure.in: break out client lib into a + * subdirectory + * + * Revision 1.6 1996/07/22 20:35:57 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.5.4.1 1996/07/18 03:08:45 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.5.2.1 1996/06/20 02:16:53 marc + * File added to the repository on a branch + * + * Revision 1.5 1996/05/17 21:36:50 bjaspan + * rename to kadm5, begin implementing version 2 + * + * Revision 1.4 1996/05/16 21:45:51 bjaspan + * u_int32 -> long, add krb5_context + * + * Revision 1.3 1994/09/20 16:25:05 bjaspan + * [secure-admin/2436: API versioning fixes to various admin files] + * [secure-releng/2502: audit secure-admin/2436: random API versioning fixes] + * + * Sandbox: + * + * Unnecessary variable initialization removed. + * + * Revision 1.3 1994/09/12 20:26:39 jik + * Unnecessary variable initialization removed. + * + * Revision 1.2 1994/08/16 18:52:02 jik + * Versioning changes. + * + * Revision 1.1 1993/11/10 23:10:39 bjaspan + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include "client_internal.h" + +kadm5_ret_t kadm5_get_privs(void *server_handle, long *privs) +{ + getprivs_ret *r; + kadm5_server_handle_t handle = server_handle; + + r = get_privs_1(&handle->api_version, handle->clnt); + if (r == NULL) + return KADM5_RPC_ERROR; + else if (r->code == KADM5_OK) + *privs = r->privs; + return r->code; +} diff --git a/src/lib/kadm5/clnt/configure.in b/src/lib/kadm5/clnt/configure.in new file mode 100644 index 000000000..6eb3627e4 --- /dev/null +++ b/src/lib/kadm5/clnt/configure.in @@ -0,0 +1,29 @@ +AC_INIT(client_rpc.c) +CONFIG_RULES +AC_PROG_ARCHIVE +AC_PROG_ARCHIVE_ADD +AC_PROG_RANLIB +AC_PROG_INSTALL +AC_REPLACE_FUNCS(setenv) +V5_SHARED_LIB_OBJS +V5_MAKE_SHARED_LIB(libkadm5clnt, 1.0, ../.., ./kadm5/clnt) + +GSSRPC_SH_VERS=$krb5_cv_shlib_version_libgssrpc +AC_SUBST(GSSRPC_SH_VERS) +GSSAPI_KRB5_SH_VERS=$krb5_cv_shlib_version_libgssapi_krb5 +AC_SUBST(GSSAPI_KRB5_SH_VERS) +KDB5_SH_VERS=$krb5_cv_shlib_version_libkdb5 +AC_SUBST(KDB5_SH_VERS) +KRB5_SH_VERS=$krb5_cv_shlib_version_libkrb5 +AC_SUBST(KRB5_SH_VERS) +CRYPTO_SH_VERS=$krb5_cv_shlib_version_libcrypto +AC_SUBST(CRYPTO_SH_VERS) +COMERR_SH_VERS=$krb5_cv_shlib_version_libcom_err +AC_SUBST(COMERR_SH_VERS) +DYN_SH_VERS=$krb5_cv_shlib_version_libdyn +AC_SUBST(DYN_SH_VERS) + +SubdirLibraryRule([$(OBJS)]) + +CopySrcHeader(client_internal.h,[$](BUILDTOP)/include/kadm5) +V5_AC_OUTPUT_MAKEFILE