--- /dev/null
+/*
+ * Copyright (c) 2003 SkyRope, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Skyrope, LLC nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission from Skyrope, LLC.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this code are derived from portions of the MIT
+ * Leash Ticket Manager and LoadFuncs utilities. For these portions the
+ * following copyright applies.
+ *
+ * Copyright (c) 2003,2004 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+
+#define USE_MS2MIT
+#define USE_KRB4
+#include "afskfw-int.h"
+#include "afskfw.h"
+#include "creds.h"
+
+#include <osilog.h>
+
+/*
+ * TIMING _____________________________________________________________________
+ *
+ */
+
+#define cminREMIND_TEST 1 // test every minute for expired creds
+#define cminREMIND_WARN 15 // warn if creds expire in 15 minutes
+#define cminRENEW 20 // renew creds when there are 20 minutes remaining
+#define cminMINLIFE 30 // minimum life of Kerberos creds
+
+#define c100ns1SECOND (LONGLONG)10000000
+#define cmsec1SECOND 1000
+#define cmsec1MINUTE 60000
+#define csec1MINUTE 60
+
+/* Function Pointer Declarations for Delayed Loading */
+// CCAPI
+DECL_FUNC_PTR(cc_initialize);
+DECL_FUNC_PTR(cc_shutdown);
+DECL_FUNC_PTR(cc_get_NC_info);
+DECL_FUNC_PTR(cc_free_NC_info);
+
+// leash functions
+DECL_FUNC_PTR(Leash_get_default_lifetime);
+DECL_FUNC_PTR(Leash_get_default_forwardable);
+DECL_FUNC_PTR(Leash_get_default_renew_till);
+DECL_FUNC_PTR(Leash_get_default_noaddresses);
+DECL_FUNC_PTR(Leash_get_default_proxiable);
+DECL_FUNC_PTR(Leash_get_default_publicip);
+DECL_FUNC_PTR(Leash_get_default_use_krb4);
+DECL_FUNC_PTR(Leash_get_default_life_min);
+DECL_FUNC_PTR(Leash_get_default_life_max);
+DECL_FUNC_PTR(Leash_get_default_renew_min);
+DECL_FUNC_PTR(Leash_get_default_renew_max);
+DECL_FUNC_PTR(Leash_get_default_renewable);
+
+// krb5 functions
+DECL_FUNC_PTR(krb5_change_password);
+DECL_FUNC_PTR(krb5_get_init_creds_opt_init);
+DECL_FUNC_PTR(krb5_get_init_creds_opt_set_tkt_life);
+DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life);
+DECL_FUNC_PTR(krb5_get_init_creds_opt_set_forwardable);
+DECL_FUNC_PTR(krb5_get_init_creds_opt_set_proxiable);
+DECL_FUNC_PTR(krb5_get_init_creds_opt_set_address_list);
+DECL_FUNC_PTR(krb5_get_init_creds_password);
+DECL_FUNC_PTR(krb5_build_principal_ext);
+DECL_FUNC_PTR(krb5_cc_get_name);
+DECL_FUNC_PTR(krb5_cc_resolve);
+DECL_FUNC_PTR(krb5_cc_default);
+DECL_FUNC_PTR(krb5_cc_default_name);
+DECL_FUNC_PTR(krb5_cc_set_default_name);
+DECL_FUNC_PTR(krb5_cc_initialize);
+DECL_FUNC_PTR(krb5_cc_destroy);
+DECL_FUNC_PTR(krb5_cc_close);
+DECL_FUNC_PTR(krb5_cc_store_cred);
+DECL_FUNC_PTR(krb5_cc_copy_creds);
+DECL_FUNC_PTR(krb5_cc_retrieve_cred);
+DECL_FUNC_PTR(krb5_cc_get_principal);
+DECL_FUNC_PTR(krb5_cc_start_seq_get);
+DECL_FUNC_PTR(krb5_cc_next_cred);
+DECL_FUNC_PTR(krb5_cc_end_seq_get);
+DECL_FUNC_PTR(krb5_cc_remove_cred);
+DECL_FUNC_PTR(krb5_cc_set_flags);
+DECL_FUNC_PTR(krb5_cc_get_type);
+DECL_FUNC_PTR(krb5_free_context);
+DECL_FUNC_PTR(krb5_free_cred_contents);
+DECL_FUNC_PTR(krb5_free_principal);
+DECL_FUNC_PTR(krb5_get_in_tkt_with_password);
+DECL_FUNC_PTR(krb5_init_context);
+DECL_FUNC_PTR(krb5_parse_name);
+DECL_FUNC_PTR(krb5_timeofday);
+DECL_FUNC_PTR(krb5_timestamp_to_sfstring);
+DECL_FUNC_PTR(krb5_unparse_name);
+DECL_FUNC_PTR(krb5_get_credentials);
+DECL_FUNC_PTR(krb5_mk_req);
+DECL_FUNC_PTR(krb5_sname_to_principal);
+DECL_FUNC_PTR(krb5_get_credentials_renew);
+DECL_FUNC_PTR(krb5_free_data);
+DECL_FUNC_PTR(krb5_free_data_contents);
+DECL_FUNC_PTR(krb5_free_unparsed_name);
+DECL_FUNC_PTR(krb5_os_localaddr);
+DECL_FUNC_PTR(krb5_copy_keyblock_contents);
+DECL_FUNC_PTR(krb5_copy_data);
+DECL_FUNC_PTR(krb5_free_creds);
+DECL_FUNC_PTR(krb5_build_principal);
+DECL_FUNC_PTR(krb5_get_renewed_creds);
+DECL_FUNC_PTR(krb5_get_default_config_files);
+DECL_FUNC_PTR(krb5_free_config_files);
+DECL_FUNC_PTR(krb5_get_default_realm);
+DECL_FUNC_PTR(krb5_free_ticket);
+DECL_FUNC_PTR(krb5_decode_ticket);
+DECL_FUNC_PTR(krb5_get_host_realm);
+DECL_FUNC_PTR(krb5_free_host_realm);
+DECL_FUNC_PTR(krb5_free_addresses);
+DECL_FUNC_PTR(krb5_c_random_make_octets);
+
+// Krb524 functions
+DECL_FUNC_PTR(krb524_init_ets);
+DECL_FUNC_PTR(krb524_convert_creds_kdc);
+
+// krb4 functions
+DECL_FUNC_PTR(krb_get_cred);
+DECL_FUNC_PTR(tkt_string);
+DECL_FUNC_PTR(krb_get_tf_realm);
+DECL_FUNC_PTR(krb_mk_req);
+
+// ComErr functions
+DECL_FUNC_PTR(com_err);
+DECL_FUNC_PTR(error_message);
+
+// Profile functions
+DECL_FUNC_PTR(profile_init);
+DECL_FUNC_PTR(profile_release);
+DECL_FUNC_PTR(profile_get_subsection_names);
+DECL_FUNC_PTR(profile_free_list);
+DECL_FUNC_PTR(profile_get_string);
+DECL_FUNC_PTR(profile_release_string);
+
+// Service functions
+DECL_FUNC_PTR(OpenSCManagerA);
+DECL_FUNC_PTR(OpenServiceA);
+DECL_FUNC_PTR(QueryServiceStatus);
+DECL_FUNC_PTR(CloseServiceHandle);
+#ifdef USE_MS2MIT
+DECL_FUNC_PTR(LsaNtStatusToWinError);
+#endif /* USE_MS2MIT */
+
+#ifdef USE_MS2MIT
+// LSA Functions
+DECL_FUNC_PTR(LsaConnectUntrusted);
+DECL_FUNC_PTR(LsaLookupAuthenticationPackage);
+DECL_FUNC_PTR(LsaCallAuthenticationPackage);
+DECL_FUNC_PTR(LsaFreeReturnBuffer);
+DECL_FUNC_PTR(LsaGetLogonSessionData);
+#endif /* USE_MS2MIT */
+
+// AFS36 Token Functions
+DECL_FUNC_PTR(ktc_ListTokens);
+DECL_FUNC_PTR(ktc_GetToken);
+DECL_FUNC_PTR(ktc_SetToken);
+DECL_FUNC_PTR(ktc_ForgetAllTokens);
+
+// AFS36 Config Functions
+DECL_FUNC_PTR(cm_SearchCellFile);
+DECL_FUNC_PTR(cm_GetRootCellName);
+
+// CCAPI
+FUNC_INFO ccapi_fi[] = {
+ MAKE_FUNC_INFO(cc_initialize),
+ MAKE_FUNC_INFO(cc_shutdown),
+ MAKE_FUNC_INFO(cc_get_NC_info),
+ MAKE_FUNC_INFO(cc_free_NC_info),
+ END_FUNC_INFO
+};
+
+FUNC_INFO leash_fi[] = {
+ MAKE_FUNC_INFO(Leash_get_default_lifetime),
+ MAKE_FUNC_INFO(Leash_get_default_renew_till),
+ MAKE_FUNC_INFO(Leash_get_default_forwardable),
+ MAKE_FUNC_INFO(Leash_get_default_noaddresses),
+ MAKE_FUNC_INFO(Leash_get_default_proxiable),
+ MAKE_FUNC_INFO(Leash_get_default_publicip),
+ MAKE_FUNC_INFO(Leash_get_default_use_krb4),
+ MAKE_FUNC_INFO(Leash_get_default_life_min),
+ MAKE_FUNC_INFO(Leash_get_default_life_max),
+ MAKE_FUNC_INFO(Leash_get_default_renew_min),
+ MAKE_FUNC_INFO(Leash_get_default_renew_max),
+ MAKE_FUNC_INFO(Leash_get_default_renewable),
+ END_FUNC_INFO
+};
+
+FUNC_INFO k5_fi[] = {
+ MAKE_FUNC_INFO(krb5_change_password),
+ MAKE_FUNC_INFO(krb5_get_init_creds_opt_init),
+ MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_tkt_life),
+ MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_renew_life),
+ MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_forwardable),
+ MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_proxiable),
+ MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_address_list),
+ MAKE_FUNC_INFO(krb5_get_init_creds_password),
+ MAKE_FUNC_INFO(krb5_build_principal_ext),
+ MAKE_FUNC_INFO(krb5_cc_get_name),
+ MAKE_FUNC_INFO(krb5_cc_resolve),
+ MAKE_FUNC_INFO(krb5_cc_default),
+ MAKE_FUNC_INFO(krb5_cc_default_name),
+ MAKE_FUNC_INFO(krb5_cc_set_default_name),
+ MAKE_FUNC_INFO(krb5_cc_initialize),
+ MAKE_FUNC_INFO(krb5_cc_destroy),
+ MAKE_FUNC_INFO(krb5_cc_close),
+ MAKE_FUNC_INFO(krb5_cc_copy_creds),
+ MAKE_FUNC_INFO(krb5_cc_store_cred),
+ MAKE_FUNC_INFO(krb5_cc_retrieve_cred),
+ MAKE_FUNC_INFO(krb5_cc_get_principal),
+ MAKE_FUNC_INFO(krb5_cc_start_seq_get),
+ MAKE_FUNC_INFO(krb5_cc_next_cred),
+ MAKE_FUNC_INFO(krb5_cc_end_seq_get),
+ MAKE_FUNC_INFO(krb5_cc_remove_cred),
+ MAKE_FUNC_INFO(krb5_cc_set_flags),
+ MAKE_FUNC_INFO(krb5_cc_get_type),
+ MAKE_FUNC_INFO(krb5_free_context),
+ MAKE_FUNC_INFO(krb5_free_cred_contents),
+ MAKE_FUNC_INFO(krb5_free_principal),
+ MAKE_FUNC_INFO(krb5_get_in_tkt_with_password),
+ MAKE_FUNC_INFO(krb5_init_context),
+ MAKE_FUNC_INFO(krb5_parse_name),
+ MAKE_FUNC_INFO(krb5_timeofday),
+ MAKE_FUNC_INFO(krb5_timestamp_to_sfstring),
+ MAKE_FUNC_INFO(krb5_unparse_name),
+ MAKE_FUNC_INFO(krb5_get_credentials),
+ MAKE_FUNC_INFO(krb5_mk_req),
+ MAKE_FUNC_INFO(krb5_sname_to_principal),
+ MAKE_FUNC_INFO(krb5_get_credentials_renew),
+ MAKE_FUNC_INFO(krb5_free_data),
+ MAKE_FUNC_INFO(krb5_free_data_contents),
+ MAKE_FUNC_INFO(krb5_free_unparsed_name),
+ MAKE_FUNC_INFO(krb5_os_localaddr),
+ MAKE_FUNC_INFO(krb5_copy_keyblock_contents),
+ MAKE_FUNC_INFO(krb5_copy_data),
+ MAKE_FUNC_INFO(krb5_free_creds),
+ MAKE_FUNC_INFO(krb5_build_principal),
+ MAKE_FUNC_INFO(krb5_get_renewed_creds),
+ MAKE_FUNC_INFO(krb5_free_addresses),
+ MAKE_FUNC_INFO(krb5_get_default_config_files),
+ MAKE_FUNC_INFO(krb5_free_config_files),
+ MAKE_FUNC_INFO(krb5_get_default_realm),
+ MAKE_FUNC_INFO(krb5_free_ticket),
+ MAKE_FUNC_INFO(krb5_decode_ticket),
+ MAKE_FUNC_INFO(krb5_get_host_realm),
+ MAKE_FUNC_INFO(krb5_free_host_realm),
+ MAKE_FUNC_INFO(krb5_free_addresses),
+ MAKE_FUNC_INFO(krb5_c_random_make_octets),
+ END_FUNC_INFO
+};
+
+FUNC_INFO k4_fi[] = {
+ MAKE_FUNC_INFO(krb_get_cred),
+ MAKE_FUNC_INFO(krb_get_tf_realm),
+ MAKE_FUNC_INFO(krb_mk_req),
+ MAKE_FUNC_INFO(tkt_string),
+ END_FUNC_INFO
+};
+
+FUNC_INFO k524_fi[] = {
+ MAKE_FUNC_INFO(krb524_init_ets),
+ MAKE_FUNC_INFO(krb524_convert_creds_kdc),
+ END_FUNC_INFO
+};
+
+FUNC_INFO profile_fi[] = {
+ MAKE_FUNC_INFO(profile_init),
+ MAKE_FUNC_INFO(profile_release),
+ MAKE_FUNC_INFO(profile_get_subsection_names),
+ MAKE_FUNC_INFO(profile_free_list),
+ MAKE_FUNC_INFO(profile_get_string),
+ MAKE_FUNC_INFO(profile_release_string),
+ END_FUNC_INFO
+};
+
+FUNC_INFO ce_fi[] = {
+ MAKE_FUNC_INFO(com_err),
+ MAKE_FUNC_INFO(error_message),
+ END_FUNC_INFO
+};
+
+FUNC_INFO service_fi[] = {
+ MAKE_FUNC_INFO(OpenSCManagerA),
+ MAKE_FUNC_INFO(OpenServiceA),
+ MAKE_FUNC_INFO(QueryServiceStatus),
+ MAKE_FUNC_INFO(CloseServiceHandle),
+#ifdef USE_MS2MIT
+ MAKE_FUNC_INFO(LsaNtStatusToWinError),
+#endif /* USE_MS2MIT */
+ END_FUNC_INFO
+};
+
+#ifdef USE_MS2MIT
+FUNC_INFO lsa_fi[] = {
+ MAKE_FUNC_INFO(LsaConnectUntrusted),
+ MAKE_FUNC_INFO(LsaLookupAuthenticationPackage),
+ MAKE_FUNC_INFO(LsaCallAuthenticationPackage),
+ MAKE_FUNC_INFO(LsaFreeReturnBuffer),
+ MAKE_FUNC_INFO(LsaGetLogonSessionData),
+ END_FUNC_INFO
+};
+#endif /* USE_MS2MIT */
+
+FUNC_INFO afst_fi[] = {
+ MAKE_FUNC_INFO(ktc_ListTokens),
+ MAKE_FUNC_INFO(ktc_GetToken),
+ MAKE_FUNC_INFO(ktc_SetToken),
+ MAKE_FUNC_INFO(ktc_ForgetAllTokens),
+ END_FUNC_INFO
+};
+
+FUNC_INFO afsc_fi[] = {
+ MAKE_FUNC_INFO(cm_SearchCellFile),
+ MAKE_FUNC_INFO(cm_GetRootCellName),
+ END_FUNC_INFO
+};
+
+/* Static Prototypes */
+static char *afs_realm_of_cell(afsconf_cell *);
+static long get_cellconfig_callback(void *, struct sockaddr_in *, char *);
+static int get_cellconfig(char *, afsconf_cell *, char *);
+static krb5_error_code KRB5_CALLCONV KRB5_prompter( krb5_context context,
+ void *data, const char *name, const char *banner, int num_prompts,
+ krb5_prompt prompts[]);
+
+
+/* Static Declarations */
+static int inited = 0;
+static int mid_cnt = 0;
+static struct textField * mid_tb = NULL;
+static HINSTANCE hKrb5 = 0;
+static HINSTANCE hKrb4 = 0;
+static HINSTANCE hKrb524 = 0;
+#ifdef USE_MS2MIT
+static HINSTANCE hSecur32 = 0;
+#endif /* USE_MS2MIT */
+static HINSTANCE hAdvApi32 = 0;
+static HINSTANCE hAfsTokens = 0;
+static HINSTANCE hAfsConf = 0;
+static HINSTANCE hComErr = 0;
+static HINSTANCE hService = 0;
+static HINSTANCE hProfile = 0;
+static HINSTANCE hLeash = 0;
+static HINSTANCE hCCAPI = 0;
+static struct principal_ccache_data * princ_cc_data = NULL;
+static struct cell_principal_map * cell_princ_map = NULL;
+
+void
+KFW_initialize(void)
+{
+ static int inited = 0;
+ if ( !inited ) {
+ inited = 1;
+ LoadFuncs(KRB5_DLL, k5_fi, &hKrb5, 0, 1, 0, 0);
+ LoadFuncs(KRB4_DLL, k4_fi, &hKrb5, 0, 1, 0, 0);
+ LoadFuncs(COMERR_DLL, ce_fi, &hComErr, 0, 0, 1, 0);
+ LoadFuncs(SERVICE_DLL, service_fi, &hService, 0, 1, 0, 0);
+#ifdef USE_MS2MIT
+ LoadFuncs(SECUR32_DLL, lsa_fi, &hSecur32, 0, 1, 1, 1);
+#endif /* USE_MS2MIT */
+ LoadFuncs(KRB524_DLL, k524_fi, &hKrb524, 0, 1, 1, 1);
+ LoadFuncs(PROFILE_DLL, profile_fi, &hProfile, 0, 1, 0, 0);
+ LoadFuncs(AFSTOKENS_DLL, afst_fi, &hAfsTokens, 0, 1, 0, 0);
+ LoadFuncs(AFSCONF_DLL, afsc_fi, &hAfsConf, 0, 1, 0, 0);
+ LoadFuncs(LEASH_DLL, leash_fi, &hLeash, 0, 1, 0, 0);
+ LoadFuncs(CCAPI_DLL, ccapi_fi, &hCCAPI, 0, 1, 0, 0);
+
+ if ( KFW_is_available() ) {
+ char rootcell[MAXCELLCHARS+1];
+#ifdef USE_MS2MIT
+ KFW_import_windows_lsa();
+#endif /* USE_MS2MIT */
+ KFW_import_ccache_data();
+ KFW_AFS_renew_expiring_credentials();
+
+ /* WIN32 NOTE: no way to get max chars */
+ if (!pcm_GetRootCellName(rootcell))
+ KFW_AFS_renew_token_for_cell(rootcell);
+ }
+ }
+}
+
+void
+KFW_cleanup(void)
+{
+ if (hKrb5)
+ FreeLibrary(hKrb5);
+ if (hKrb4)
+ FreeLibrary(hKrb4);
+ if (hProfile)
+ FreeLibrary(hProfile);
+ if (hAfsTokens)
+ FreeLibrary(hAfsTokens);
+ if (hAfsConf)
+ FreeLibrary(hAfsConf);
+ if (hComErr)
+ FreeLibrary(hComErr);
+ if (hService)
+ FreeLibrary(hService);
+#ifdef USE_MS2MIT
+ if (hSecur32)
+ FreeLibrary(hSecur32);
+#endif /* USE_MS2MIT */
+ if (hKrb524)
+ FreeLibrary(hKrb524);
+ if (hLeash)
+ FreeLibrary(hLeash);
+ if (hCCAPI)
+ FreeLibrary(hCCAPI);
+}
+
+int
+KFW_is_available(void)
+{
+ KFW_initialize();
+ if ( hKrb5 && hComErr && hService &&
+#ifdef USE_MS2MIT
+ hSecur32 &&
+#endif /* USE_MS2MIT */
+ hKrb524 &&
+ hProfile && hAfsTokens && hAfsConf )
+ return TRUE;
+ return FALSE;
+}
+
+int
+KRB5_error(krb5_error_code rc, LPCSTR FailedFunctionName,
+ int FreeContextFlag, krb5_context * ctx,
+ krb5_ccache * cache)
+{
+ char message[256];
+ const char *errText;
+ int krb5Error = ((int)(rc & 255));
+
+ /*
+ switch (krb5Error)
+ {
+ // Wrong password
+ case 31:
+ case 8:
+ return;
+ }
+ */
+
+ errText = perror_message(rc);
+ _snprintf(message, sizeof(message),
+ "%s\n(Kerberos error %ld)\n\n%s failed",
+ errText,
+ krb5Error,
+ FailedFunctionName);
+
+ if ( IsDebuggerPresent() )
+ OutputDebugString(message);
+
+ MessageBox(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR |
+ MB_TASKMODAL |
+ MB_SETFOREGROUND);
+ if (FreeContextFlag == 1)
+ {
+ if (ctx && *ctx != NULL)
+ {
+ if (cache && *cache != NULL) {
+ pkrb5_cc_close(*ctx, *cache);
+ *cache = NULL;
+ }
+
+ pkrb5_free_context(*ctx);
+ *ctx = NULL;
+ }
+ }
+
+ return rc;
+}
+
+void
+KFW_AFS_update_princ_ccache_data(krb5_context ctx, krb5_ccache cc, int lsa)
+{
+ struct principal_ccache_data * next = princ_cc_data;
+ krb5_principal principal = 0;
+ char * pname = NULL;
+ const char * ccname = NULL;
+ krb5_error_code code = 0;
+ krb5_error_code cc_code = 0;
+ krb5_cc_cursor cur;
+ krb5_creds creds;
+ krb5_flags flags=0;
+ krb5_timestamp now;
+
+ if (ctx == 0 || cc == 0)
+ return;
+
+ code = pkrb5_cc_get_principal(ctx, cc, &principal);
+ if ( code ) return;
+
+ code = pkrb5_unparse_name(ctx, principal, &pname);
+ if ( code ) goto cleanup;
+
+ ccname = pkrb5_cc_get_name(ctx, cc);
+ if (!ccname) goto cleanup;
+
+ // Search the existing list to see if we have a match
+ if ( next ) {
+ for ( ; next ; next = next->next ) {
+ if ( !strcmp(next->principal,pname) && !strcmp(next->ccache_name, ccname) )
+ break;
+ }
+ }
+
+ // If not, match add a new node to the beginning of the list and assign init it
+ if ( !next ) {
+ next = (struct principal_ccache_data *) malloc(sizeof(struct principal_ccache_data));
+ next->next = princ_cc_data;
+ princ_cc_data = next;
+ next->principal = _strdup(pname);
+ next->ccache_name = _strdup(ccname);
+ next->from_lsa = lsa;
+ next->expired = 1;
+ next->expiration_time = 0;
+ next->renew = 0;
+ }
+
+ flags = 0; // turn off OPENCLOSE mode
+ code = pkrb5_cc_set_flags(ctx, cc, flags);
+ if ( code ) goto cleanup;
+
+ code = pkrb5_timeofday(ctx, &now);
+
+ cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur);
+
+ while (!(cc_code = pkrb5_cc_next_cred(ctx, cc, &cur, &creds))) {
+ if ( creds.ticket_flags & TKT_FLG_INITIAL ) {
+ int valid;
+ // we found the ticket we are looking for
+ // check validity of timestamp
+ // We add a 5 minutes fudge factor to compensate for potential
+ // clock skew errors between the KDC and client OS
+
+ valid = ((creds.times.starttime > 0) &&
+ now >= (creds.times.starttime - 300) &&
+ now < (creds.times.endtime + 300) &&
+ !(creds.ticket_flags & TKT_FLG_INVALID));
+
+ if ( next->from_lsa) {
+ next->expired = 0;
+ next->expiration_time = creds.times.endtime;
+ next->renew = 1;
+ } else if ( valid ) {
+ next->expired = 0;
+ next->expiration_time = creds.times.endtime;
+ next->renew = (creds.times.renew_till > creds.times.endtime) &&
+ (creds.ticket_flags & TKT_FLG_RENEWABLE);
+ } else {
+ next->expired = 1;
+ next->expiration_time = 0;
+ next->renew = 0;
+ }
+
+ pkrb5_free_cred_contents(ctx, &creds);
+ cc_code = KRB5_CC_END;
+ break;
+ }
+ pkrb5_free_cred_contents(ctx, &creds);
+ }
+
+ if (cc_code == KRB5_CC_END) {
+ code = pkrb5_cc_end_seq_get(ctx, cc, &cur);
+ if (code) goto cleanup;
+ }
+
+ cleanup:
+ flags = KRB5_TC_OPENCLOSE; //turn on OPENCLOSE
+ code = pkrb5_cc_set_flags(ctx, cc, flags);
+
+ if ( pname )
+ pkrb5_free_unparsed_name(ctx,pname);
+ if ( principal )
+ pkrb5_free_principal(ctx,principal);
+}
+
+int
+KFW_AFS_find_ccache_for_principal(krb5_context ctx, char * principal, char **ccache, int valid_only)
+{
+ struct principal_ccache_data * next = princ_cc_data;
+ char * response = NULL;
+
+ if ( !principal || !ccache )
+ return 0;
+
+ while ( next ) {
+ if ( (!valid_only || !next->expired) && !strcmp(next->principal,principal) ) {
+ if (response) {
+ // we always want to prefer the MS Kerberos LSA cache or
+ // the cache afscreds created specifically for the principal
+ // if the current entry is either one, drop the previous find
+ if ( next->from_lsa || !strcmp(next->ccache_name,principal) )
+ free(response);
+ }
+ response = _strdup(next->ccache_name);
+ // MS Kerberos LSA is our best option so use it and quit
+ if ( next->from_lsa )
+ break;
+ }
+ next = next->next;
+ }
+
+ if ( response ) {
+ *ccache = response;
+ return 1;
+ }
+ return 0;
+}
+
+void
+KFW_AFS_delete_princ_ccache_data(krb5_context ctx, char * pname, char * ccname)
+{
+ struct principal_ccache_data ** next = &princ_cc_data;
+
+ if ( !pname && !ccname )
+ return;
+
+ while ( (*next) ) {
+ if ( !strcmp((*next)->principal,pname) ||
+ !strcmp((*next)->ccache_name,ccname) ) {
+ void * temp;
+ free((*next)->principal);
+ free((*next)->ccache_name);
+ temp = (*next);
+ (*next) = (*next)->next;
+ free(temp);
+ }
+ }
+}
+
+void
+KFW_AFS_update_cell_princ_map(krb5_context ctx, char * cell, char *pname, int active)
+{
+ struct cell_principal_map * next = cell_princ_map;
+
+ // Search the existing list to see if we have a match
+ if ( next ) {
+ for ( ; next ; next = next->next ) {
+ if ( !strcmp(next->cell, cell) ) {
+ if ( !strcmp(next->principal,pname) ) {
+ next->active = active;
+ break;
+ } else {
+ // OpenAFS currently has a restriction of one active token per cell
+ // Therefore, whenever we update the table with a new active cell we
+ // must mark all of the other principal to cell entries as inactive.
+ if (active)
+ next->active = 0;
+ }
+ }
+ }
+ }
+
+ // If not, match add a new node to the beginning of the list and assign init it
+ if ( !next ) {
+ next = (struct cell_principal_map *) malloc(sizeof(struct cell_principal_map));
+ next->next = cell_princ_map;
+ cell_princ_map = next;
+ next->principal = _strdup(pname);
+ next->cell = _strdup(cell);
+ next->active = active;
+ }
+}
+
+void
+KFW_AFS_delete_cell_princ_maps(krb5_context ctx, char * pname, char * cell)
+{
+ struct cell_principal_map ** next = &cell_princ_map;
+
+ if ( !pname && !cell )
+ return;
+
+ while ( (*next) ) {
+ if ( !strcmp((*next)->principal,pname) ||
+ !strcmp((*next)->cell,cell) ) {
+ void * temp;
+ free((*next)->principal);
+ free((*next)->cell);
+ temp = (*next);
+ (*next) = (*next)->next;
+ free(temp);
+ }
+ }
+}
+
+// Returns (if possible) a principal which has been known in
+// the past to have been used to obtain tokens for the specified
+// cell.
+// TODO: Attempt to return one which has not yet expired by checking
+// the principal/ccache data
+int
+KFW_AFS_find_principals_for_cell(krb5_context ctx, char * cell, char **principals[], int active_only)
+{
+ struct cell_principal_map * next_map = cell_princ_map;
+ const char * princ = NULL;
+ int count = 0, i;
+
+ if ( !cell )
+ return 0;
+
+ while ( next_map ) {
+ if ( (!active_only || next_map->active) && !strcmp(next_map->cell,cell) ) {
+ count++;
+ }
+ next_map = next_map->next;
+ }
+
+ if ( !principals )
+ return count;
+
+ *principals = (char **) malloc(sizeof(char *) * count);
+ for ( next_map = cell_princ_map, i=0 ; next_map && i<count; next_map = next_map->next )
+ {
+ if ( (!active_only || next_map->active) && !strcmp(next_map->cell,cell) ) {
+ (*principals)[i++] = _strdup(next_map->principal);
+ }
+ }
+ return count;
+}
+
+int
+KFW_AFS_find_cells_for_princ(krb5_context ctx, char * pname, char **cells[], int active_only)
+{
+ int count = 0, i;
+ struct cell_principal_map * next_map = cell_princ_map;
+ const char * princ = NULL;
+
+ if ( !pname )
+ return 0;
+
+ while ( next_map ) {
+ if ( (!active_only || next_map->active) && !strcmp(next_map->principal,pname) ) {
+ count++;
+ }
+ next_map = next_map->next;
+ }
+
+ if ( !cells )
+ return count;
+
+ *cells = (char **) malloc(sizeof(char *) * count);
+ for ( next_map = cell_princ_map, i=0 ; next_map && i<count; next_map = next_map->next )
+ {
+ if ( (!active_only || next_map->active) && !strcmp(next_map->principal,pname) ) {
+ (*cells)[i++] = _strdup(next_map->cell);
+ }
+ }
+ return count;
+}
+
+/* Given a principal return an existing ccache or create one and return */
+int
+KFW_get_ccache(krb5_context alt_ctx, krb5_principal principal, krb5_ccache * cc)
+{
+ krb5_context ctx;
+ char * pname = 0;
+ char * ccname = 0;
+ krb5_error_code code;
+
+ if ( alt_ctx ) {
+ ctx = alt_ctx;
+ } else {
+ code = pkrb5_init_context(&ctx);
+ if (code) goto cleanup;
+ }
+
+ if ( principal ) {
+ code = pkrb5_unparse_name(ctx, principal, &pname);
+ if (code) goto cleanup;
+
+ if ( !KFW_AFS_find_ccache_for_principal(ctx,pname,&ccname,TRUE) &&
+ !KFW_AFS_find_ccache_for_principal(ctx,pname,&ccname,FALSE)) {
+ ccname = (char *)malloc(strlen(pname) + 5);
+ sprintf(ccname,"API:%s",pname);
+ }
+ code = pkrb5_cc_resolve(ctx, ccname, cc);
+ } else {
+ code = pkrb5_cc_default(ctx, cc);
+ if (code) goto cleanup;
+ }
+
+ cleanup:
+ if (ccname)
+ free(ccname);
+ if (pname)
+ pkrb5_free_unparsed_name(ctx,pname);
+ if (ctx && (ctx != alt_ctx))
+ pkrb5_free_context(ctx);
+ return(code);
+}
+
+#ifdef USE_MS2MIT
+// Import Microsoft Credentials into a new MIT ccache
+void
+KFW_import_windows_lsa(void)
+{
+ krb5_context ctx = 0;
+ krb5_ccache cc = 0;
+ krb5_principal princ = 0;
+ char * pname = NULL;
+ krb5_data * realm;
+ krb5_error_code code;
+ char cell[128]="";
+ int i;
+
+ if ( !MSLSA_IsKerberosLogon() )
+ return;
+
+ code = pkrb5_init_context(&ctx);
+ if (code) goto cleanup;
+
+ code = pkrb5_cc_resolve(ctx, LSA_CCNAME, &cc);
+ if (code) goto cleanup;
+
+ KFW_AFS_update_princ_ccache_data(ctx, cc, TRUE);
+
+ code = pkrb5_cc_get_principal(ctx, cc, &princ);
+ if ( code ) goto cleanup;
+
+ code = pkrb5_unparse_name(ctx,princ,&pname);
+ if ( code ) goto cleanup;
+
+ realm = krb5_princ_realm(ctx, princ);
+ for ( i=0; i<realm->length; i++ ) {
+ cell[i] = tolower(realm->data[i]);
+ }
+ cell[i] = '\0';
+
+ code = KFW_AFS_klog(ctx, cc, "afs", cell, realm->data, pLeash_get_default_lifetime());
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
+ OutputDebugString(message);
+ }
+ if ( code ) goto cleanup;
+
+ KFW_AFS_update_cell_princ_map(ctx, cell, pname, TRUE);
+
+ cleanup:
+ if (pname)
+ pkrb5_free_unparsed_name(ctx,pname);
+ if (princ)
+ pkrb5_free_principal(ctx,princ);
+ if (cc)
+ pkrb5_cc_close(ctx,cc);
+ if (ctx)
+ pkrb5_free_context(ctx);
+}
+#endif /* USE_MS2MIT */
+
+// If there are existing MIT credentials, copy them to a new
+// ccache named after the principal
+
+// Enumerate all existing MIT ccaches and construct entries
+// in the principal_ccache table
+
+// Enumerate all existing AFS Tokens and construct entries
+// in the cell_principal table
+void
+KFW_import_ccache_data(void)
+{
+ krb5_context ctx = 0;
+ krb5_ccache cc = 0;
+ krb5_principal principal = 0;
+ krb5_creds creds;
+ krb5_error_code code;
+ krb5_error_code cc_code;
+ krb5_cc_cursor cur;
+ apiCB * cc_ctx = 0;
+ struct _infoNC ** pNCi = NULL;
+ int i, j, flags;
+
+ if ( !pcc_initialize )
+ return;
+
+ if ( IsDebuggerPresent() )
+ OutputDebugString("KFW_import_ccache_data()\n");
+
+ code = pcc_initialize(&cc_ctx, CC_API_VER_2, NULL, NULL);
+ if (code) goto cleanup;
+
+ code = pcc_get_NC_info(cc_ctx, &pNCi);
+ if (code) goto cleanup;
+
+ code = pkrb5_init_context(&ctx);
+ if (code) goto cleanup;
+
+ for ( i=0; pNCi[i]; i++ ) {
+ if ( pNCi[i]->vers != CC_CRED_V5 )
+ continue;
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("Principal: ");
+ OutputDebugString(pNCi[i]->principal);
+ OutputDebugString(" in ccache ");
+ OutputDebugString(pNCi[i]->name);
+ OutputDebugString("\n");
+ }
+ if ( strcmp(pNCi[i]->name,pNCi[i]->principal)
+ && strcmp(pNCi[i]->name,LSA_CCNAME)
+ ) {
+ int found = 0;
+ krb5_ccache oldcc = 0;
+ for ( j=0; pNCi[j]; j++ ) {
+ if (!strcmp(pNCi[j]->name,pNCi[i]->principal)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ continue;
+
+ if ( IsDebuggerPresent() )
+ OutputDebugString("copying ccache data to new ccache\n");
+
+ code = pkrb5_cc_resolve(ctx, pNCi[i]->principal, &cc);
+ if (code) goto loop_cleanup;
+ code = pkrb5_parse_name(ctx, pNCi[i]->principal, &principal);
+ if (code) goto loop_cleanup;
+ code = pkrb5_cc_initialize(ctx, cc, principal);
+ if (code) goto loop_cleanup;
+ code = pkrb5_cc_resolve(ctx, pNCi[i]->name, &oldcc);
+ if (code) goto loop_cleanup;
+ code = pkrb5_cc_copy_creds(ctx,oldcc,cc);
+ if (code) {
+ code = pkrb5_cc_close(ctx,cc);
+ cc = 0;
+ code = pkrb5_cc_close(ctx,oldcc);
+ cc = 0;
+ KRB5_error(code, "krb5_cc_copy_creds", 0, NULL, NULL);
+ continue;
+ }
+ code = pkrb5_cc_close(ctx,oldcc);
+ } else {
+ code = pkrb5_cc_resolve(ctx, pNCi[i]->name, &cc);
+ if (code) goto loop_cleanup;
+ }
+
+ flags = 0; // turn off OPENCLOSE mode
+ code = pkrb5_cc_set_flags(ctx, cc, flags);
+ if ( code ) goto cleanup;
+
+ KFW_AFS_update_princ_ccache_data(ctx, cc, !strcmp(pNCi[i]->name,LSA_CCNAME));
+
+ cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur);
+
+ while (!(cc_code = pkrb5_cc_next_cred(ctx, cc, &cur, &creds))) {
+ krb5_data * sname = krb5_princ_name(ctx, creds.server);
+ krb5_data * cell = krb5_princ_component(ctx, creds.server, 1);
+ krb5_data * realm = krb5_princ_realm(ctx, creds.server);
+ if ( sname && cell && !strcmp("afs",sname->data) ) {
+ struct ktc_principal aserver;
+ struct ktc_principal aclient;
+ struct ktc_token atoken;
+ int active = TRUE;
+
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("Found AFS ticket: ");
+ OutputDebugString(sname->data);
+ if ( cell->data ) {
+ OutputDebugString("/");
+ OutputDebugString(cell->data);
+ }
+ OutputDebugString("@");
+ OutputDebugString(realm->data);
+ OutputDebugString("\n");
+ }
+
+ memset(&aserver, '\0', sizeof(aserver));
+ strcpy(aserver.name, sname->data);
+ strcpy(aserver.cell, cell->data);
+
+ code = pktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
+ if (!code) {
+ // Found a token in AFS Client Server which matches
+ char pname[128], *p, *q;
+ for ( p=pname, q=aclient.name; *q; p++, q++)
+ *p = *q;
+ for ( *p++ = '@', q=aclient.cell; *q; p++, q++)
+ *p = toupper(*q);
+ *p = '\0';
+
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("Found AFS token: ");
+ OutputDebugString(pname);
+ OutputDebugString("\n");
+ }
+
+ if ( strcmp(pname,pNCi[i]->principal) )
+ active = FALSE;
+ KFW_AFS_update_cell_princ_map(ctx, cell->data, pNCi[i]->principal, active);
+ } else {
+ // Attempt to import it
+ KFW_AFS_update_cell_princ_map(ctx, cell->data, pNCi[i]->principal, active);
+
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("Calling KFW_AFS_klog() to obtain token\n");
+ }
+
+ code = KFW_AFS_klog(ctx, cc, "afs", cell->data, realm->data, pLeash_get_default_lifetime());
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
+ OutputDebugString(message);
+ }
+ }
+ } else if ( IsDebuggerPresent() ) {
+ OutputDebugString("Found ticket: ");
+ OutputDebugString(sname->data);
+ if ( cell && cell->data ) {
+ OutputDebugString("/");
+ OutputDebugString(cell->data);
+ }
+ OutputDebugString("@");
+ OutputDebugString(realm->data);
+ OutputDebugString("\n");
+ }
+ pkrb5_free_cred_contents(ctx, &creds);
+ }
+
+ if (cc_code == KRB5_CC_END) {
+ cc_code = pkrb5_cc_end_seq_get(ctx, cc, &cur);
+ if (cc_code) goto loop_cleanup;
+ }
+
+ loop_cleanup:
+ flags = KRB5_TC_OPENCLOSE; //turn on OPENCLOSE
+ code = pkrb5_cc_set_flags(ctx, cc, flags);
+ if (cc) {
+ pkrb5_cc_close(ctx,cc);
+ cc = 0;
+ }
+ }
+
+ cleanup:
+ if (principal)
+ pkrb5_free_principal(ctx,principal);
+ if (ctx)
+ pkrb5_free_context(ctx);
+ if (pNCi)
+ pcc_free_NC_info(cc_ctx, &pNCi);
+ if (cc_ctx)
+ pcc_shutdown(&cc_ctx);
+}
+
+
+int
+KFW_AFS_get_cred(char * username,
+ char * instance,
+ char * cell,
+ char * password,
+ int lifetime,
+ char ** reasonP )
+{
+ krb5_context ctx = 0;
+ krb5_ccache cc = 0;
+ char * realm = 0;
+ char ** realmlist = 0;
+ krb5_principal principal = 0;
+ char * pname = 0;
+ krb5_error_code code;
+ char local_cell[MAXCELLCHARS+1];
+ char **cells = NULL;
+ int cell_count=0;
+ afsconf_cell cellconfig;
+
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("KFW_AFS_get_cred for token ");
+ OutputDebugString(username);
+ if ( instance ) {
+ OutputDebugString("/");
+ OutputDebugString(instance);
+ }
+ OutputDebugString("@");
+ OutputDebugString(cell);
+ OutputDebugString("\n");
+ }
+
+ code = pkrb5_init_context(&ctx);
+ if ( code ) goto cleanup;
+
+ code = get_cellconfig( cell, (void*)&cellconfig, local_cell);
+ if ( code ) goto cleanup;
+
+ realm = afs_realm_of_cell(&cellconfig); // do not free
+
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("Realm: ");
+ OutputDebugString(realm);
+ OutputDebugString("\n");
+ }
+
+ code = pkrb5_build_principal(ctx, &principal, strlen(realm),
+ realm, username,
+ (instance && instance[0]) ? instance : NULL,
+ NULL);
+
+ code = KFW_get_ccache(ctx, principal, &cc);
+ if ( code ) goto cleanup;
+
+ code = pkrb5_unparse_name(ctx, principal, &pname);
+ if ( code ) goto cleanup;
+
+ if ( lifetime == 0 )
+ lifetime = pLeash_get_default_lifetime();
+
+ code = KFW_kinit(ctx, cc, HWND_DESKTOP,
+ pname,
+ password,
+ lifetime,
+ pLeash_get_default_forwardable(),
+ pLeash_get_default_proxiable(),
+ pLeash_get_default_renewable() ? pLeash_get_default_renew_till() : 0,
+ pLeash_get_default_noaddresses(),
+ pLeash_get_default_publicip());
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"KFW_kinit() returns: %d\n",code);
+ OutputDebugString(message);
+ }
+ if ( code ) goto cleanup;
+
+ KFW_AFS_update_princ_ccache_data(ctx, cc, FALSE);
+
+ code = KFW_AFS_klog(ctx, cc, "afs", cell, realm, lifetime);
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
+ OutputDebugString(message);
+ }
+ if ( code ) goto cleanup;
+
+ KFW_AFS_update_cell_princ_map(ctx, cell, pname, TRUE);
+
+ // Attempt to obtain new tokens for other cells supported by the same
+ // principal
+ cell_count = KFW_AFS_find_cells_for_princ(ctx, pname, &cells, TRUE);
+ if ( cell_count > 1 ) {
+ while ( cell_count-- ) {
+ if ( strcmp(cells[cell_count],cell) ) {
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"found another cell for the same principal: %s\n",cell);
+ OutputDebugString(message);
+ }
+ code = get_cellconfig( cells[cell_count], (void*)&cellconfig, local_cell);
+ if ( code ) continue;
+
+ realm = afs_realm_of_cell(&cellconfig); // do not free
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("Realm: ");
+ OutputDebugString(realm);
+ OutputDebugString("\n");
+ }
+
+ code = KFW_AFS_klog(ctx, cc, "afs", cells[cell_count], realm, lifetime);
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
+ OutputDebugString(message);
+ }
+ }
+ free(cells[cell_count]);
+ }
+ free(cells);
+ } else if ( cell_count == 1 ) {
+ free(cells[0]);
+ free(cells);
+ }
+
+ cleanup:
+ if ( pname )
+ pkrb5_free_unparsed_name(ctx,pname);
+ if ( cc )
+ pkrb5_cc_close(ctx, cc);
+
+ if ( code && reasonP ) {
+ *reasonP = (char *)perror_message(code);
+ }
+ return(code);
+}
+
+int
+KFW_AFS_destroy_tickets_for_cell(char * cell)
+{
+ krb5_context ctx = 0;
+ krb5_error_code code;
+ int count;
+ char ** principals = NULL;
+
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("KFW_AFS_destroy_ticets_for_cell: ");
+ OutputDebugString(cell);
+ OutputDebugString("\n");
+ }
+
+ code = pkrb5_init_context(&ctx);
+ if (code) ctx = 0;
+
+ count = KFW_AFS_find_principals_for_cell(ctx, cell, &principals, FALSE);
+ if ( count > 0 ) {
+ krb5_principal princ = 0;
+ krb5_ccache cc = 0;
+
+ while ( count-- ) {
+ int cell_count = KFW_AFS_find_cells_for_princ(ctx, principals[count], NULL, TRUE);
+ if ( cell_count > 1 ) {
+ // TODO - What we really should do here is verify whether or not any of the
+ // other cells which use this principal to obtain its credentials actually
+ // have valid tokens or not. If they are currently using these credentials
+ // we will skip them. For the time being we assume that if there is an active
+ // map in the table that they are actively being used.
+ goto loop_cleanup;
+ }
+
+ code = pkrb5_parse_name(ctx, principals[count], &princ);
+ if (code) goto loop_cleanup;
+
+ code = KFW_get_ccache(ctx, princ, &cc);
+ if (code) goto loop_cleanup;
+
+ code = pkrb5_cc_destroy(ctx, cc);
+ if (!code) cc = 0;
+
+ loop_cleanup:
+ if ( cc ) {
+ pkrb5_cc_close(ctx, cc);
+ cc = 0;
+ }
+ if ( princ ) {
+ pkrb5_free_principal(ctx, princ);
+ princ = 0;
+ }
+
+ KFW_AFS_update_cell_princ_map(ctx, cell, principals[count], FALSE);
+ free(principals[count]);
+ }
+ free(principals);
+ }
+ pkrb5_free_context(ctx);
+ return 0;
+}
+
+int
+KFW_AFS_renew_expiring_credentials(void)
+{
+ krb5_error_code code = 0;
+ krb5_context ctx = 0;
+ krb5_ccache cc = 0;
+ krb5_timestamp now;
+ struct principal_ccache_data * pcc_next = princ_cc_data;
+ int cell_count;
+ char ** cells=NULL;
+ const char * realm = NULL;
+ char local_cell[MAXCELLCHARS+1]="";
+ afsconf_cell cellconfig;
+
+ if ( pcc_next == NULL ) // nothing to do
+ return 0;
+
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("KFW_AFS_renew_expiring_credentials\n");
+ }
+
+ code = pkrb5_init_context(&ctx);
+ if (code) goto cleanup;
+
+ code = pkrb5_timeofday(ctx, &now);
+ if (code) goto cleanup;
+
+ for ( ; pcc_next ; pcc_next = pcc_next->next ) {
+ if ( pcc_next->expired )
+ continue;
+
+ if ( now >= (pcc_next->expiration_time) ) {
+ if ( !pcc_next->from_lsa ) {
+ pcc_next->expired = 1;
+ continue;
+ }
+ }
+
+ if ( pcc_next->renew && now >= (pcc_next->expiration_time - cminRENEW * csec1MINUTE) ) {
+ code = pkrb5_cc_resolve(ctx, pcc_next->ccache_name, &cc);
+ if ( code )
+ goto loop_cleanup;
+ code = KFW_renew(ctx,cc);
+#ifdef USE_MS2MIT
+ if ( code && pcc_next->from_lsa)
+ goto loop_cleanup;
+#endif /* USE_MS2MIT */
+
+
+ KFW_AFS_update_princ_ccache_data(ctx, cc, pcc_next->from_lsa);
+ if (code) goto loop_cleanup;
+
+ // Attempt to obtain new tokens for other cells supported by the same
+ // principal
+ cell_count = KFW_AFS_find_cells_for_princ(ctx, pcc_next->principal, &cells, TRUE);
+ if ( cell_count > 0 ) {
+ while ( cell_count-- ) {
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("Cell: ");
+ OutputDebugString(cells[cell_count]);
+ OutputDebugString("\n");
+ }
+ code = get_cellconfig( cells[cell_count], (void*)&cellconfig, local_cell);
+ if ( code ) continue;
+ realm = afs_realm_of_cell(&cellconfig); // do not free
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("Realm: ");
+ OutputDebugString(realm);
+ OutputDebugString("\n");
+ }
+ code = KFW_AFS_klog(ctx, cc, "afs", cells[cell_count], (char *)realm, pLeash_get_default_lifetime());
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
+ OutputDebugString(message);
+ }
+ free(cells[cell_count]);
+ }
+ free(cells);
+ }
+ }
+
+ loop_cleanup:
+ if ( cc ) {
+ pkrb5_cc_close(ctx,cc);
+ cc = 0;
+ }
+ }
+
+ cleanup:
+ if ( cc )
+ pkrb5_cc_close(ctx,cc);
+ if ( ctx )
+ pkrb5_free_context(ctx);
+
+ return 0;
+}
+
+
+BOOL
+KFW_AFS_renew_token_for_cell(char * cell)
+{
+ krb5_error_code code = 0;
+ krb5_context ctx = 0;
+ int count;
+ char ** principals = NULL;
+
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("KFW_AFS_renew_token_for_cell:");
+ OutputDebugString(cell);
+ OutputDebugString("\n");
+ }
+
+ code = pkrb5_init_context(&ctx);
+ if (code) goto cleanup;
+
+ count = KFW_AFS_find_principals_for_cell(ctx, cell, &principals, TRUE);
+ if ( count > 0 ) {
+ krb5_principal princ = 0;
+ krb5_principal service = 0;
+ krb5_creds mcreds, creds;
+ krb5_ccache cc = 0;
+ const char * realm = NULL;
+ afsconf_cell cellconfig;
+ char local_cell[MAXCELLCHARS+1];
+
+ while ( count-- ) {
+ code = pkrb5_parse_name(ctx, principals[count], &princ);
+ if (code) goto loop_cleanup;
+
+ code = KFW_get_ccache(ctx, princ, &cc);
+ if (code) goto loop_cleanup;
+
+ code = get_cellconfig( cell, (void*)&cellconfig, local_cell);
+ if ( code ) goto loop_cleanup;
+
+ realm = afs_realm_of_cell(&cellconfig); // do not free
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString("Realm: ");
+ OutputDebugString(realm);
+ OutputDebugString("\n");
+ }
+
+ code = pkrb5_build_principal(ctx, &service, strlen(realm),
+ realm, "afs", cell, NULL);
+ if (!code) {
+ memset(&mcreds, 0, sizeof(krb5_creds));
+ mcreds.client = princ;
+ mcreds.server = service;
+
+ code = pkrb5_cc_retrieve_cred(ctx, cc, 0, &mcreds, &creds);
+ if (!code) {
+ if ( IsDebuggerPresent() ) {
+ char * cname, *sname;
+ pkrb5_unparse_name(ctx, creds.client, &cname);
+ pkrb5_unparse_name(ctx, creds.server, &sname);
+ OutputDebugString("Removing credential for client \"");
+ OutputDebugString(cname);
+ OutputDebugString("\" and service \"");
+ OutputDebugString(sname);
+ OutputDebugString("\"\n");
+ pkrb5_free_unparsed_name(ctx,cname);
+ pkrb5_free_unparsed_name(ctx,sname);
+ }
+
+ code = pkrb5_cc_remove_cred(ctx, cc, 0, &creds);
+ pkrb5_free_principal(ctx, creds.client);
+ pkrb5_free_principal(ctx, creds.server);
+ }
+ }
+ code = KFW_AFS_klog(ctx, cc, "afs", cell, (char *)realm, pLeash_get_default_lifetime());
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
+ OutputDebugString(message);
+ }
+
+ loop_cleanup:
+ if (cc) {
+ pkrb5_cc_close(ctx, cc);
+ cc = 0;
+ }
+ if (princ) {
+ pkrb5_free_principal(ctx, princ);
+ princ = 0;
+ }
+ if (service) {
+ pkrb5_free_principal(ctx, service);
+ princ = 0;
+ }
+
+ KFW_AFS_update_cell_princ_map(ctx, cell, principals[count], code ? FALSE : TRUE);
+ free(principals[count]);
+ }
+ free(principals);
+ } else
+ code = -1; // we did not renew the tokens
+
+ cleanup:
+ pkrb5_free_context(ctx);
+ return (code ? FALSE : TRUE);
+
+}
+
+int
+KFW_AFS_renew_tokens_for_all_cells(void)
+{
+ struct cell_principal_map * next = cell_princ_map;
+
+ if ( IsDebuggerPresent() )
+ OutputDebugString("KFW_AFS_renew_tokens_for_all()\n");
+
+ if ( !next )
+ return 0;
+
+ for ( ; next ; next = next->next ) {
+ if ( next->active )
+ KFW_AFS_renew_token_for_cell(next->cell);
+ }
+ return 0;
+}
+
+int
+KFW_renew(krb5_context alt_ctx, krb5_ccache alt_cc)
+{
+ krb5_error_code code = 0;
+ krb5_context ctx = 0;
+ krb5_ccache cc = 0;
+ krb5_principal me = 0;
+ krb5_principal server = 0;
+ krb5_creds my_creds;
+ krb5_data *realm = 0;
+
+ memset(&my_creds, 0, sizeof(krb5_creds));
+
+ if ( alt_ctx ) {
+ ctx = alt_ctx;
+ } else {
+ code = pkrb5_init_context(&ctx);
+ if (code) goto cleanup;
+ }
+
+ if ( alt_cc ) {
+ cc = alt_cc;
+ } else {
+ code = pkrb5_cc_default(ctx, &cc);
+ if (code) goto cleanup;
+ }
+
+ code = pkrb5_cc_get_principal(ctx, cc, &me);
+ if (code) goto cleanup;
+
+ realm = krb5_princ_realm(ctx, me);
+
+ code = pkrb5_build_principal_ext(ctx, &server,
+ realm->length,realm->data,
+ KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
+ realm->length,realm->data,
+ 0);
+ if ( code )
+ goto cleanup;
+
+ if ( IsDebuggerPresent() ) {
+ char * cname, *sname;
+ pkrb5_unparse_name(ctx, me, &cname);
+ pkrb5_unparse_name(ctx, server, &sname);
+ OutputDebugString("Renewing credential for client \"");
+ OutputDebugString(cname);
+ OutputDebugString("\" and service \"");
+ OutputDebugString(sname);
+ OutputDebugString("\"\n");
+ pkrb5_free_unparsed_name(ctx,cname);
+ pkrb5_free_unparsed_name(ctx,sname);
+ }
+
+ my_creds.client = me;
+ my_creds.server = server;
+
+ code = pkrb5_get_renewed_creds(ctx, &my_creds, me, cc, NULL);
+ if (code) {
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"krb5_get_renewed_creds() failed: %d\n",code);
+ OutputDebugString(message);
+ }
+ goto cleanup;
+ }
+
+ code = pkrb5_cc_initialize(ctx, cc, me);
+ if (code) {
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"krb5_cc_initialize() failed: %d\n",code);
+ OutputDebugString(message);
+ }
+ goto cleanup;
+ }
+
+ code = pkrb5_cc_store_cred(ctx, cc, &my_creds);
+ if (code) {
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"krb5_cc_store_cred() failed: %d\n",code);
+ OutputDebugString(message);
+ }
+ goto cleanup;
+ }
+
+ cleanup:
+ if (my_creds.client == me)
+ my_creds.client = 0;
+ if (my_creds.server == server)
+ my_creds.server = 0;
+ pkrb5_free_cred_contents(ctx, &my_creds);
+ if (me)
+ pkrb5_free_principal(ctx, me);
+ if (server)
+ pkrb5_free_principal(ctx, server);
+ if (cc && (cc != alt_cc))
+ pkrb5_cc_close(ctx, cc);
+ if (ctx && (ctx != alt_ctx))
+ pkrb5_free_context(ctx);
+ return(code);
+}
+
+int
+KFW_kinit( krb5_context alt_ctx,
+ krb5_ccache alt_cc,
+ HWND hParent,
+ char *principal_name,
+ char *password,
+ krb5_deltat lifetime,
+ DWORD forwardable,
+ DWORD proxiable,
+ krb5_deltat renew_life,
+ DWORD addressless,
+ DWORD publicIP
+ )
+{
+ krb5_error_code code = 0;
+ krb5_context ctx = 0;
+ krb5_ccache cc = 0;
+ krb5_principal me = 0;
+ char* name = 0;
+ krb5_creds my_creds;
+ krb5_get_init_creds_opt options;
+ krb5_address ** addrs = NULL;
+ int i = 0, addr_count = 0;
+
+ if (!pkrb5_init_context)
+ return 0;
+
+ pkrb5_get_init_creds_opt_init(&options);
+ memset(&my_creds, 0, sizeof(my_creds));
+
+ if (alt_ctx)
+ {
+ ctx = alt_ctx;
+ }
+ else
+ {
+ code = pkrb5_init_context(&ctx);
+ if (code) goto cleanup;
+ }
+
+ if ( alt_cc ) {
+ cc = alt_cc;
+ } else {
+ code = pkrb5_cc_default(ctx, &cc);
+ if (code) goto cleanup;
+ }
+
+ code = pkrb5_parse_name(ctx, principal_name, &me);
+ if (code)
+ goto cleanup;
+
+ code = pkrb5_unparse_name(ctx, me, &name);
+ if (code)
+ goto cleanup;
+
+ if (lifetime == 0)
+ lifetime = pLeash_get_default_lifetime();
+ else
+ lifetime *= 5*60;
+
+ if (renew_life > 0)
+ renew_life *= 5*60;
+
+ if (lifetime)
+ pkrb5_get_init_creds_opt_set_tkt_life(&options, lifetime);
+ pkrb5_get_init_creds_opt_set_forwardable(&options,
+ forwardable ? 1 : 0);
+ pkrb5_get_init_creds_opt_set_proxiable(&options,
+ proxiable ? 1 : 0);
+ pkrb5_get_init_creds_opt_set_renew_life(&options,
+ renew_life);
+ if (addressless)
+ pkrb5_get_init_creds_opt_set_address_list(&options,NULL);
+ else {
+ if (publicIP)
+ {
+ // we are going to add the public IP address specified by the user
+ // to the list provided by the operating system
+ krb5_address ** local_addrs=NULL;
+ DWORD netIPAddr;
+
+ pkrb5_os_localaddr(ctx, &local_addrs);
+ while ( local_addrs[i++] );
+ addr_count = i + 1;
+
+ addrs = (krb5_address **) malloc((addr_count+1) * sizeof(krb5_address *));
+ if ( !addrs ) {
+ pkrb5_free_addresses(ctx, local_addrs);
+ goto cleanup;
+ }
+ memset(addrs, 0, sizeof(krb5_address *) * (addr_count+1));
+ i = 0;
+ while ( local_addrs[i] ) {
+ addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));
+ if (addrs[i] == NULL) {
+ pkrb5_free_addresses(ctx, local_addrs);
+ goto cleanup;
+ }
+
+ addrs[i]->magic = local_addrs[i]->magic;
+ addrs[i]->addrtype = local_addrs[i]->addrtype;
+ addrs[i]->length = local_addrs[i]->length;
+ addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);
+ if (!addrs[i]->contents) {
+ pkrb5_free_addresses(ctx, local_addrs);
+ goto cleanup;
+ }
+
+ memcpy(addrs[i]->contents,local_addrs[i]->contents,
+ local_addrs[i]->length); /* safe */
+ i++;
+ }
+ pkrb5_free_addresses(ctx, local_addrs);
+
+ addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));
+ if (addrs[i] == NULL)
+ goto cleanup;
+
+ addrs[i]->magic = KV5M_ADDRESS;
+ addrs[i]->addrtype = AF_INET;
+ addrs[i]->length = 4;
+ addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);
+ if (!addrs[i]->contents)
+ goto cleanup;
+
+ netIPAddr = htonl(publicIP);
+ memcpy(addrs[i]->contents,&netIPAddr,4);
+
+ pkrb5_get_init_creds_opt_set_address_list(&options,addrs);
+
+ }
+ }
+
+ code = pkrb5_get_init_creds_password(ctx,
+ &my_creds,
+ me,
+ password, // password
+ KRB5_prompter, // prompter
+ hParent, // prompter data
+ 0, // start time
+ 0, // service name
+ &options);
+ if (code)
+ goto cleanup;
+
+ code = pkrb5_cc_initialize(ctx, cc, me);
+ if (code)
+ goto cleanup;
+
+ code = pkrb5_cc_store_cred(ctx, cc, &my_creds);
+ if (code)
+ goto cleanup;
+
+ cleanup:
+ if ( addrs ) {
+ for ( i=0;i<addr_count;i++ ) {
+ if ( addrs[i] ) {
+ if ( addrs[i]->contents )
+ free(addrs[i]->contents);
+ free(addrs[i]);
+ }
+ }
+ }
+ if (my_creds.client == me)
+ my_creds.client = 0;
+ pkrb5_free_cred_contents(ctx, &my_creds);
+ if (name)
+ pkrb5_free_unparsed_name(ctx, name);
+ if (me)
+ pkrb5_free_principal(ctx, me);
+ if (cc && (cc != alt_cc))
+ pkrb5_cc_close(ctx, cc);
+ if (ctx && (ctx != alt_ctx))
+ pkrb5_free_context(ctx);
+ return(code);
+}
+
+
+int
+KFW_kdestroy(krb5_context alt_ctx, krb5_ccache alt_cc)
+{
+ krb5_context ctx;
+ krb5_ccache cc;
+ krb5_error_code code;
+
+ if (alt_ctx)
+ {
+ ctx = alt_ctx;
+ }
+ else
+ {
+ code = pkrb5_init_context(&ctx);
+ if (code) goto cleanup;
+ }
+
+ if ( alt_cc ) {
+ cc = alt_cc;
+ } else {
+ code = pkrb5_cc_default(ctx, &cc);
+ if (code) goto cleanup;
+ }
+
+ code = pkrb5_cc_destroy(ctx, cc);
+ if ( !code ) cc = 0;
+
+ cleanup:
+ if (cc && (cc != alt_cc))
+ pkrb5_cc_close(ctx, cc);
+ if (ctx && (ctx != alt_ctx))
+ pkrb5_free_context(ctx);
+
+ return(code);
+}
+
+
+#ifdef USE_MS2MIT
+static BOOL
+GetSecurityLogonSessionData(PSECURITY_LOGON_SESSION_DATA * ppSessionData)
+{
+ NTSTATUS Status = 0;
+ HANDLE TokenHandle;
+ TOKEN_STATISTICS Stats;
+ DWORD ReqLen;
+ BOOL Success;
+
+ if (!ppSessionData)
+ return FALSE;
+ *ppSessionData = NULL;
+
+ Success = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &TokenHandle );
+ if ( !Success )
+ return FALSE;
+
+ Success = GetTokenInformation( TokenHandle, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );
+ CloseHandle( TokenHandle );
+ if ( !Success )
+ return FALSE;
+
+ Status = pLsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );
+ if ( FAILED(Status) || !ppSessionData )
+ return FALSE;
+
+ return TRUE;
+}
+
+//
+// MSLSA_IsKerberosLogon() does not validate whether or not there are valid tickets in the
+// cache. It validates whether or not it is reasonable to assume that if we
+// attempted to retrieve valid tickets we could do so. Microsoft does not
+// automatically renew expired tickets. Therefore, the cache could contain
+// expired or invalid tickets. Microsoft also caches the user's password
+// and will use it to retrieve new TGTs if the cache is empty and tickets
+// are requested.
+
+static BOOL
+MSLSA_IsKerberosLogon(VOID)
+{
+ PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
+ BOOL Success = FALSE;
+
+ if ( GetSecurityLogonSessionData(&pSessionData) ) {
+ if ( pSessionData->AuthenticationPackage.Buffer ) {
+ WCHAR buffer[256];
+ WCHAR *usBuffer;
+ int usLength;
+
+ Success = FALSE;
+ usBuffer = (pSessionData->AuthenticationPackage).Buffer;
+ usLength = (pSessionData->AuthenticationPackage).Length;
+ if (usLength < 256)
+ {
+ lstrcpynW (buffer, usBuffer, usLength);
+ lstrcatW (buffer,L"");
+ if ( !lstrcmpW(L"Kerberos",buffer) )
+ Success = TRUE;
+ }
+ }
+ pLsaFreeReturnBuffer(pSessionData);
+ }
+ return Success;
+}
+#endif /* USE_MS2MIT */
+
+static BOOL CALLBACK
+MultiInputDialogProc( HWND hDialog, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ int i;
+
+ switch ( message ) {
+ case WM_INITDIALOG:
+ if ( GetDlgCtrlID((HWND) wParam) != ID_MID_TEXT )
+ {
+ SetFocus(GetDlgItem( hDialog, ID_MID_TEXT));
+ return FALSE;
+ }
+ for ( i=0; i < mid_cnt ; i++ ) {
+ if (mid_tb[i].echo == 0)
+ SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, 32, 0);
+ else if (mid_tb[i].echo == 2)
+ SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, '*', 0);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch ( LOWORD(wParam) ) {
+ case IDOK:
+ for ( i=0; i < mid_cnt ; i++ ) {
+ if ( !GetDlgItemText(hDialog, ID_MID_TEXT+i, mid_tb[i].buf, mid_tb[i].len) )
+ *mid_tb[i].buf = '\0';
+ }
+ /* fallthrough */
+ case IDCANCEL:
+ EndDialog(hDialog, LOWORD(wParam));
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static LPWORD
+lpwAlign( LPWORD lpIn )
+{
+ ULONG ul;
+
+ ul = (ULONG) lpIn;
+ ul += 3;
+ ul >>=2;
+ ul <<=2;
+ return (LPWORD) ul;;
+}
+
+/*
+ * dialog widths are measured in 1/4 character widths
+ * dialog height are measured in 1/8 character heights
+ */
+
+static LRESULT
+MultiInputDialog( HINSTANCE hinst, HWND hwndOwner,
+ char * ptext[], int numlines, int width,
+ int tb_cnt, struct textField * tb)
+{
+ HGLOBAL hgbl;
+ LPDLGTEMPLATE lpdt;
+ LPDLGITEMTEMPLATE lpdit;
+ LPWORD lpw;
+ LPWSTR lpwsz;
+ LRESULT ret;
+ int nchar, i, pwid;
+
+ hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
+ if (!hgbl)
+ return -1;
+
+ mid_cnt = tb_cnt;
+ mid_tb = tb;
+
+ lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
+
+ // Define a dialog box.
+
+ lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU
+ | DS_MODALFRAME | WS_CAPTION | DS_CENTER
+ | DS_SETFOREGROUND | DS_3DLOOK
+ | DS_SETFONT | DS_FIXEDSYS | DS_NOFAILCREATE;
+ lpdt->cdit = numlines + (2 * tb_cnt) + 2; // number of controls
+ lpdt->x = 10;
+ lpdt->y = 10;
+ lpdt->cx = 20 + width * 4;
+ lpdt->cy = 20 + (numlines + tb_cnt + 4) * 14;
+
+ lpw = (LPWORD) (lpdt + 1);
+ *lpw++ = 0; // no menu
+ *lpw++ = 0; // predefined dialog box class (by default)
+
+ lpwsz = (LPWSTR) lpw;
+ nchar = MultiByteToWideChar (CP_ACP, 0, "", -1, lpwsz, 128);
+ lpw += nchar;
+ *lpw++ = 8; // font size (points)
+ lpwsz = (LPWSTR) lpw;
+ nchar = MultiByteToWideChar (CP_ACP, 0, "MS Shell Dlg",
+ -1, lpwsz, 128);
+ lpw += nchar;
+
+ //-----------------------
+ // Define an OK button.
+ //-----------------------
+ lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
+ lpdit = (LPDLGITEMTEMPLATE) lpw;
+ lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP | WS_BORDER;
+ lpdit->dwExtendedStyle = 0;
+ lpdit->x = (lpdt->cx - 14)/4 - 20;
+ lpdit->y = 10 + (numlines + tb_cnt + 2) * 14;
+ lpdit->cx = 40;
+ lpdit->cy = 14;
+ lpdit->id = IDOK; // OK button identifier
+
+ lpw = (LPWORD) (lpdit + 1);
+ *lpw++ = 0xFFFF;
+ *lpw++ = 0x0080; // button class
+
+ lpwsz = (LPWSTR) lpw;
+ nchar = MultiByteToWideChar (CP_ACP, 0, "OK", -1, lpwsz, 50);
+ lpw += nchar;
+ *lpw++ = 0; // no creation data
+
+ //-----------------------
+ // Define an Cancel button.
+ //-----------------------
+ lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
+ lpdit = (LPDLGITEMTEMPLATE) lpw;
+ lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP | WS_BORDER;
+ lpdit->dwExtendedStyle = 0;
+ lpdit->x = (lpdt->cx - 14)*3/4 - 20;
+ lpdit->y = 10 + (numlines + tb_cnt + 2) * 14;
+ lpdit->cx = 40;
+ lpdit->cy = 14;
+ lpdit->id = IDCANCEL; // CANCEL button identifier
+
+ lpw = (LPWORD) (lpdit + 1);
+ *lpw++ = 0xFFFF;
+ *lpw++ = 0x0080; // button class
+
+ lpwsz = (LPWSTR) lpw;
+ nchar = MultiByteToWideChar (CP_ACP, 0, "Cancel", -1, lpwsz, 50);
+ lpw += nchar;
+ *lpw++ = 0; // no creation data
+
+ /* Add controls for preface data */
+ for ( i=0; i<numlines; i++) {
+ /*-----------------------
+ * Define a static text control.
+ *-----------------------*/
+ lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
+ lpdit = (LPDLGITEMTEMPLATE) lpw;
+ lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
+ lpdit->dwExtendedStyle = 0;
+ lpdit->x = 10;
+ lpdit->y = 10 + i * 14;
+ lpdit->cx = strlen(ptext[i]) * 4 + 10;
+ lpdit->cy = 14;
+ lpdit->id = ID_TEXT + i; // text identifier
+
+ lpw = (LPWORD) (lpdit + 1);
+ *lpw++ = 0xFFFF;
+ *lpw++ = 0x0082; // static class
+
+ lpwsz = (LPWSTR) lpw;
+ nchar = MultiByteToWideChar (CP_ACP, 0, ptext[i],
+ -1, lpwsz, 2*width);
+ lpw += nchar;
+ *lpw++ = 0; // no creation data
+ }
+
+ for ( i=0, pwid = 0; i<tb_cnt; i++) {
+ if ( pwid < strlen(tb[i].label) )
+ pwid = strlen(tb[i].label);
+ }
+
+ for ( i=0; i<tb_cnt; i++) {
+ /* Prompt */
+ /*-----------------------
+ * Define a static text control.
+ *-----------------------*/
+ lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
+ lpdit = (LPDLGITEMTEMPLATE) lpw;
+ lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
+ lpdit->dwExtendedStyle = 0;
+ lpdit->x = 10;
+ lpdit->y = 10 + (numlines + i + 1) * 14;
+ lpdit->cx = pwid * 4;
+ lpdit->cy = 14;
+ lpdit->id = ID_TEXT + numlines + i; // text identifier
+
+ lpw = (LPWORD) (lpdit + 1);
+ *lpw++ = 0xFFFF;
+ *lpw++ = 0x0082; // static class
+
+ lpwsz = (LPWSTR) lpw;
+ nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].label ? tb[i].label : "",
+ -1, lpwsz, 128);
+ lpw += nchar;
+ *lpw++ = 0; // no creation data
+
+ /*-----------------------
+ * Define an edit control.
+ *-----------------------*/
+ lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
+ lpdit = (LPDLGITEMTEMPLATE) lpw;
+ lpdit->style = WS_CHILD | WS_VISIBLE | ES_LEFT | WS_TABSTOP | WS_BORDER | (tb[i].echo == 1 ? 0L : ES_PASSWORD);
+ lpdit->dwExtendedStyle = 0;
+ lpdit->x = 10 + (pwid + 1) * 4;
+ lpdit->y = 10 + (numlines + i + 1) * 14;
+ lpdit->cx = (width - (pwid + 1)) * 4;
+ lpdit->cy = 14;
+ lpdit->id = ID_MID_TEXT + i; // identifier
+
+ lpw = (LPWORD) (lpdit + 1);
+ *lpw++ = 0xFFFF;
+ *lpw++ = 0x0081; // edit class
+
+ lpwsz = (LPWSTR) lpw;
+ nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].def ? tb[i].def : "",
+ -1, lpwsz, 128);
+ lpw += nchar;
+ *lpw++ = 0; // no creation data
+ }
+
+ GlobalUnlock(hgbl);
+ ret = DialogBoxIndirect(hinst, (LPDLGTEMPLATE) hgbl,
+ hwndOwner, (DLGPROC) MultiInputDialogProc);
+ GlobalFree(hgbl);
+
+ switch ( ret ) {
+ case 0: /* Timeout */
+ return -1;
+ case IDOK:
+ return 1;
+ case IDCANCEL:
+ return 0;
+ default: {
+ char buf[256];
+ sprintf(buf,"DialogBoxIndirect() failed: %d",GetLastError());
+ MessageBox(hwndOwner,
+ buf,
+ "GetLastError()",
+ MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
+ return -1;
+ }
+ }
+}
+
+static int
+multi_field_dialog(HWND hParent, char * preface, int n, struct textField tb[])
+{
+ HINSTANCE hInst = 0;
+ int maxwidth = 0;
+ int numlines = 0;
+ int len;
+ char * plines[16], *p = preface ? preface : "";
+ int i;
+
+ for ( i=0; i<16; i++ )
+ plines[i] = NULL;
+
+ while (*p && numlines < 16) {
+ plines[numlines++] = p;
+ for ( ;*p && *p != '\r' && *p != '\n'; p++ );
+ if ( *p == '\r' && *(p+1) == '\n' ) {
+ *p++ = '\0';
+ p++;
+ } else if ( *p == '\n' ) {
+ *p++ = '\0';
+ }
+ if ( strlen(plines[numlines-1]) > maxwidth )
+ maxwidth = strlen(plines[numlines-1]);
+ }
+
+ for ( i=0;i<n;i++ ) {
+ len = strlen(tb[i].label) + 1 + (tb[i].len > 40 ? 40 : tb[i].len);
+ if ( maxwidth < len )
+ maxwidth = len;
+ }
+
+ return(MultiInputDialog(hInst, hParent, plines, numlines, maxwidth, n, tb));
+}
+
+static krb5_error_code KRB5_CALLCONV
+KRB5_prompter( krb5_context context,
+ void *data,
+ const char *name,
+ const char *banner,
+ int num_prompts,
+ krb5_prompt prompts[])
+{
+ krb5_error_code errcode = 0;
+ int i;
+ struct textField * tb = NULL;
+ int len = 0, blen=0, nlen=0;
+ HWND hParent = (HWND)data;
+
+ if (name)
+ nlen = strlen(name)+2;
+
+ if (banner)
+ blen = strlen(banner)+2;
+
+ tb = (struct textField *) malloc(sizeof(struct textField) * num_prompts);
+ if ( tb != NULL ) {
+ int ok;
+ memset(tb,0,sizeof(struct textField) * num_prompts);
+ for ( i=0; i < num_prompts; i++ ) {
+ tb[i].buf = prompts[i].reply->data;
+ tb[i].len = prompts[i].reply->length;
+ tb[i].label = prompts[i].prompt;
+ tb[i].def = NULL;
+ tb[i].echo = (prompts[i].hidden ? 2 : 1);
+ }
+
+ ok = multi_field_dialog(hParent,(char *)banner,num_prompts,tb);
+ if ( ok ) {
+ for ( i=0; i < num_prompts; i++ )
+ prompts[i].reply->length = strlen(prompts[i].reply->data);
+ } else
+ errcode = -2;
+ }
+
+ if ( tb )
+ free(tb);
+ if (errcode) {
+ for (i = 0; i < num_prompts; i++) {
+ memset(prompts[i].reply->data, 0, prompts[i].reply->length);
+ }
+ }
+ return errcode;
+}
+
+BOOL
+KFW_AFS_wait_for_service_start(void)
+{
+ char HostName[64];
+ DWORD CurrentState;
+
+ CurrentState = SERVICE_START_PENDING;
+ memset(HostName, '\0', sizeof(HostName));
+ gethostname(HostName, sizeof(HostName));
+
+ while (CurrentState != SERVICE_RUNNING || CurrentState != SERVICE_STOPPED)
+ {
+ if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
+ return(0);
+ if ( IsDebuggerPresent() ) {
+ switch ( CurrentState ) {
+ case SERVICE_STOPPED:
+ OutputDebugString("SERVICE_STOPPED\n");
+ break;
+ case SERVICE_START_PENDING:
+ OutputDebugString("SERVICE_START_PENDING\n");
+ break;
+ case SERVICE_STOP_PENDING:
+ OutputDebugString("SERVICE_STOP_PENDING\n");
+ break;
+ case SERVICE_RUNNING:
+ OutputDebugString("SERVICE_RUNNING\n");
+ break;
+ case SERVICE_CONTINUE_PENDING:
+ OutputDebugString("SERVICE_CONTINUE_PENDING\n");
+ break;
+ case SERVICE_PAUSE_PENDING:
+ OutputDebugString("SERVICE_PAUSE_PENDING\n");
+ break;
+ case SERVICE_PAUSED:
+ OutputDebugString("SERVICE_PAUSED\n");
+ break;
+ default:
+ OutputDebugString("UNKNOWN Service State\n");
+ }
+ }
+ if (CurrentState == SERVICE_STOPPED)
+ return(0);
+ if (CurrentState == SERVICE_RUNNING)
+ return(1);
+ Sleep(500);
+ }
+ return(0);
+}
+
+
+int
+KFW_AFS_unlog(void)
+{
+ long rc;
+ char HostName[64];
+ DWORD CurrentState;
+
+ CurrentState = 0;
+ memset(HostName, '\0', sizeof(HostName));
+ gethostname(HostName, sizeof(HostName));
+ if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
+ return(0);
+ if (CurrentState != SERVICE_RUNNING)
+ return(0);
+
+ rc = pktc_ForgetAllTokens();
+
+ return(0);
+}
+
+int
+KFW_AFS_klog(
+ krb5_context alt_ctx,
+ krb5_ccache alt_cc,
+ char *service,
+ char *cell,
+ char *realm,
+ int LifeTime
+ )
+{
+ long rc = 0;
+ CREDENTIALS creds;
+ KTEXT_ST ticket;
+ struct ktc_principal aserver;
+ struct ktc_principal aclient;
+ char username[BUFSIZ]; /* To hold client username structure */
+ char realm_of_user[REALM_SZ]; /* Kerberos realm of user */
+ char realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */
+ char local_cell[MAXCELLCHARS+1];
+ char Dmycell[MAXCELLCHARS+1];
+ struct ktc_token atoken;
+ struct ktc_token btoken;
+ afsconf_cell ak_cellconfig; /* General information about the cell */
+ char RealmName[128];
+ char CellName[128];
+ char ServiceName[128];
+ DWORD CurrentState;
+ char HostName[64];
+ BOOL try_krb5 = 0;
+ krb5_context ctx = 0;
+ krb5_ccache cc = 0;
+ krb5_creds increds;
+ krb5_creds * k5creds = 0;
+ krb5_error_code code;
+ krb5_principal client_principal = 0;
+ int i, retry = 0;
+
+ CurrentState = 0;
+ memset(HostName, '\0', sizeof(HostName));
+ gethostname(HostName, sizeof(HostName));
+ if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR) {
+ if ( IsDebuggerPresent() )
+ OutputDebugString("Unable to retrieve AFSD Service Status\n");
+ return(-1);
+ }
+ if (CurrentState != SERVICE_RUNNING) {
+ if ( IsDebuggerPresent() )
+ OutputDebugString("AFSD Service NOT RUNNING\n");
+ return(-2);
+ }
+
+ memset(RealmName, '\0', sizeof(RealmName));
+ memset(CellName, '\0', sizeof(CellName));
+ memset(ServiceName, '\0', sizeof(ServiceName));
+ memset(realm_of_user, '\0', sizeof(realm_of_user));
+ memset(realm_of_cell, '\0', sizeof(realm_of_cell));
+ if (cell && cell[0])
+ strcpy(Dmycell, cell);
+ else
+ memset(Dmycell, '\0', sizeof(Dmycell));
+
+ // NULL or empty cell returns information on local cell
+ if (rc = get_cellconfig(Dmycell, &ak_cellconfig, local_cell))
+ {
+ KFW_AFS_error(rc, "get_cellconfig()");
+ return(rc);
+ }
+
+ if ( alt_ctx ) {
+ ctx = alt_ctx;
+ } else {
+ code = pkrb5_init_context(&ctx);
+ if (code) goto cleanup;
+ }
+
+ if ( alt_cc ) {
+ cc = alt_cc;
+ } else {
+ code = pkrb5_cc_default(ctx, &cc);
+ if (code) goto skip_krb5_init;
+ }
+
+ memset((char *)&increds, 0, sizeof(increds));
+
+ code = pkrb5_cc_get_principal(ctx, cc, &client_principal);
+ if (code) {
+ if ( code == KRB5_CC_NOTFOUND && IsDebuggerPresent() )
+ {
+ OutputDebugString("Principal Not Found for ccache\n");
+ }
+ goto skip_krb5_init;
+ }
+ i = krb5_princ_realm(ctx, client_principal)->length;
+ if (i > REALM_SZ-1)
+ i = REALM_SZ-1;
+ strncpy(realm_of_user,krb5_princ_realm(ctx, client_principal)->data,i);
+ realm_of_user[i] = 0;
+ try_krb5 = 1;
+
+ skip_krb5_init:
+#ifdef USE_KRB4
+ if ( !try_krb5 || !realm_of_user[0] ) {
+ if ((rc = (*pkrb_get_tf_realm)((*ptkt_string)(), realm_of_user)) != KSUCCESS)
+ {
+ goto cleanup;
+ }
+ }
+#else
+ goto cleanup;
+#endif
+ strcpy(realm_of_cell, afs_realm_of_cell(&ak_cellconfig));
+
+ if (strlen(service) == 0)
+ strcpy(ServiceName, "afs");
+ else
+ strcpy(ServiceName, service);
+
+ if (strlen(cell) == 0)
+ strcpy(CellName, local_cell);
+ else
+ strcpy(CellName, cell);
+
+ if (strlen(realm) == 0)
+ strcpy(RealmName, realm_of_cell);
+ else
+ strcpy(RealmName, realm);
+
+ memset(&creds, '\0', sizeof(creds));
+
+ if ( try_krb5 ) {
+ /* First try service/cell@REALM */
+ if (code = pkrb5_build_principal(ctx, &increds.server,
+ strlen(RealmName),
+ RealmName,
+ ServiceName,
+ CellName,
+ 0))
+ {
+ goto cleanup;
+ }
+
+ increds.client = client_principal;
+ increds.times.endtime = 0;
+ /* Ask for DES since that is what V4 understands */
+ increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
+
+ if ( IsDebuggerPresent() ) {
+ char * cname, *sname;
+ pkrb5_unparse_name(ctx, increds.client, &cname);
+ pkrb5_unparse_name(ctx, increds.server, &sname);
+ OutputDebugString("Getting credentials for \"");
+ OutputDebugString(cname);
+ OutputDebugString("\" and service \"");
+ OutputDebugString(sname);
+ OutputDebugString("\"\n");
+ pkrb5_free_unparsed_name(ctx,cname);
+ pkrb5_free_unparsed_name(ctx,sname);
+ }
+
+ code = pkrb5_get_credentials(ctx, 0, cc, &increds, &k5creds);
+ if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
+ code == KRB5KRB_ERR_GENERIC /* heimdal */) {
+ /* Or service@REALM */
+ pkrb5_free_principal(ctx,increds.server);
+ increds.server = 0;
+ code = pkrb5_build_principal(ctx, &increds.server,
+ strlen(RealmName),
+ RealmName,
+ ServiceName,
+ 0);
+
+ if ( IsDebuggerPresent() ) {
+ char * cname, *sname;
+ pkrb5_unparse_name(ctx, increds.client, &cname);
+ pkrb5_unparse_name(ctx, increds.server, &sname);
+ OutputDebugString("krb5_get_credentials() returned Service Principal Unknown\n");
+ OutputDebugString("Trying again: getting credentials for \"");
+ OutputDebugString(cname);
+ OutputDebugString("\" and service \"");
+ OutputDebugString(sname);
+ OutputDebugString("\"\n");
+ pkrb5_free_unparsed_name(ctx,cname);
+ pkrb5_free_unparsed_name(ctx,sname);
+ }
+
+ if (!code)
+ code = pkrb5_get_credentials(ctx, 0, cc, &increds, &k5creds);
+ }
+
+ if (code) {
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"krb5_get_credentials returns: %d\n",code);
+ OutputDebugString(message);
+ }
+ try_krb5 = 0;
+ goto use_krb4;
+ }
+ /* This requires krb524d to be running with the KDC */
+ code = pkrb524_convert_creds_kdc(ctx, k5creds, &creds);
+ pkrb5_free_creds(ctx, k5creds);
+ if (code) {
+ if ( IsDebuggerPresent() ) {
+ char message[256];
+ sprintf(message,"krb524_convert_creds_kdc returns: %d\n",code);
+ OutputDebugString(message);
+ }
+ try_krb5 = 0;
+ goto use_krb4;
+ }
+ } else {
+ use_krb4:
+#ifdef USE_KRB4
+ rc = (*pkrb_get_cred)(ServiceName, CellName, RealmName, &creds);
+ if (rc == NO_TKT_FIL) {
+ // if the problem is that we have no krb4 tickets
+ // do not attempt to continue
+ goto cleanup;
+ }
+ if (rc != KSUCCESS)
+ rc = (*pkrb_get_cred)(ServiceName, "", RealmName, &creds);
+
+ if (rc != KSUCCESS)
+ {
+ if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, CellName, RealmName, 0)) == KSUCCESS)
+ {
+ if ((rc = (*pkrb_get_cred)(ServiceName, CellName, RealmName, &creds)) != KSUCCESS)
+ {
+ goto cleanup;
+ }
+ }
+ else if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, "", RealmName, 0)) == KSUCCESS)
+ {
+ if ((rc = (*pkrb_get_cred)(ServiceName, "", RealmName, &creds)) != KSUCCESS)
+ {
+ goto cleanup;
+ }
+ }
+ else
+ {
+ goto cleanup;
+ }
+ }
+#else
+ goto cleanup;
+#endif
+ }
+
+ memset(&aserver, '\0', sizeof(aserver));
+ strncpy(aserver.name, ServiceName, MAXKTCNAMELEN - 1);
+ strncpy(aserver.cell, CellName, MAXKTCREALMLEN - 1);
+
+ strcpy(username, creds.pname);
+ if (creds.pinst[0])
+ {
+ strcat(username, ".");
+ strcat(username, creds.pinst);
+ }
+
+ memset(&atoken, '\0', sizeof(atoken));
+ atoken.kvno = creds.kvno;
+ atoken.startTime = creds.issue_date;
+ atoken.endTime = creds.issue_date + (creds.lifetime * 300);
+ memcpy(&atoken.sessionKey, creds.session, 8);
+ atoken.ticketLen = creds.ticket_st.length;
+ memcpy(atoken.ticket, creds.ticket_st.dat, atoken.ticketLen);
+
+ retry_gettoken:
+ rc = pktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient);
+ if (rc != 0 && rc != KTC_NOENT && rc != KTC_NOCELL) {
+ if ( rc == KTC_NOCM && retry < 20 ) {
+ Sleep(500);
+ retry++;
+ goto retry_gettoken;
+ }
+ KFW_AFS_error(rc, "ktc_GetToken()");
+ code = rc;
+ goto cleanup;
+ }
+
+ if (atoken.kvno == btoken.kvno &&
+ atoken.ticketLen == btoken.ticketLen &&
+ !memcmp(&atoken.sessionKey, &btoken.sessionKey, sizeof(atoken.sessionKey)) &&
+ !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen))
+ {
+ goto cleanup;
+ }
+
+ // * Reset the "aclient" structure before we call ktc_SetToken.
+ // * This structure was first set by the ktc_GetToken call when
+ // * we were comparing whether identical tokens already existed.
+
+ strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
+ strcpy(aclient.instance, "");
+ strncpy(aclient.cell, creds.realm, MAXKTCREALMLEN - 1);
+
+ if (rc = pktc_SetToken(&aserver, &atoken, &aclient, 0))
+ {
+ KFW_AFS_error(rc, "ktc_SetToken()");
+ code = rc;
+ goto cleanup;
+ }
+
+ cleanup:
+ if (client_principal)
+ pkrb5_free_principal(ctx,client_principal);
+ /* increds.client == client_principal */
+ if (increds.server)
+ pkrb5_free_principal(ctx,increds.server);
+ if (cc && (cc != alt_cc))
+ pkrb5_cc_close(ctx, cc);
+ if (ctx && (ctx != alt_ctx))
+ pkrb5_free_context(ctx);
+
+ return(code);
+}
+
+/**************************************/
+/* afs_realm_of_cell(): */
+/**************************************/
+static char *
+afs_realm_of_cell(afsconf_cell *cellconfig)
+{
+ static char krbrlm[REALM_SZ+1]="";
+ krb5_context ctx = 0;
+ char ** realmlist=NULL;
+ krb5_error_code r;
+
+ if (!cellconfig)
+ return 0;
+
+ r = pkrb5_init_context(&ctx);
+ if ( !r )
+ r = pkrb5_get_host_realm(ctx, cellconfig->hostName[0], &realmlist);
+ if ( !r && realmlist && realmlist[0] ) {
+ strcpy(krbrlm, realmlist[0]);
+ pkrb5_free_host_realm(ctx, realmlist);
+ }
+ if (ctx)
+ pkrb5_free_context(ctx);
+
+ if ( !krbrlm[0] )
+ {
+ char *s = krbrlm;
+ char *t = cellconfig->name;
+ int c;
+
+ while (c = *t++)
+ {
+ if (islower(c)) c=toupper(c);
+ *s++ = c;
+ }
+ *s++ = 0;
+ }
+ return(krbrlm);
+}
+
+/**************************************/
+/* get_cellconfig(): */
+/**************************************/
+static int
+get_cellconfig(char *cell, afsconf_cell *cellconfig, char *local_cell)
+{
+ int rc;
+ char newcell[MAXCELLCHARS+1];
+
+ local_cell[0] = (char)0;
+ memset(cellconfig, 0, sizeof(*cellconfig));
+
+ /* WIN32: cm_GetRootCellName(local_cell) - NOTE: no way to get max chars */
+ if (rc = pcm_GetRootCellName(local_cell))
+ {
+ return(rc);
+ }
+
+ if (strlen(cell) == 0)
+ strcpy(cell, local_cell);
+
+ /* WIN32: cm_SearchCellFile(cell, pcallback, pdata) */
+ strcpy(cellconfig->name, cell);
+
+ return pcm_SearchCellFile(cell, newcell, get_cellconfig_callback, (void*)cellconfig);
+}
+
+/**************************************/
+/* get_cellconfig_callback(): */
+/**************************************/
+static long
+get_cellconfig_callback(void *cellconfig, struct sockaddr_in *addrp, char *namep)
+{
+ afsconf_cell *cc = (afsconf_cell *)cellconfig;
+
+ cc->hostAddr[cc->numServers] = *addrp;
+ strcpy(cc->hostName[cc->numServers], namep);
+ cc->numServers++;
+ return(0);
+}
+
+
+/**************************************/
+/* KFW_AFS_error(): */
+/**************************************/
+void
+KFW_AFS_error(LONG rc, LPCSTR FailedFunctionName)
+{
+ char message[256];
+ const char *errText;
+
+ // Using AFS defines as error messages for now, until Transarc
+ // gets back to me with "string" translations of each of these
+ // const. defines.
+ if (rc == KTC_ERROR)
+ errText = "KTC_ERROR";
+ else if (rc == KTC_TOOBIG)
+ errText = "KTC_TOOBIG";
+ else if (rc == KTC_INVAL)
+ errText = "KTC_INVAL";
+ else if (rc == KTC_NOENT)
+ errText = "KTC_NOENT";
+ else if (rc == KTC_PIOCTLFAIL)
+ errText = "KTC_PIOCTLFAIL";
+ else if (rc == KTC_NOPIOCTL)
+ errText = "KTC_NOPIOCTL";
+ else if (rc == KTC_NOCELL)
+ errText = "KTC_NOCELL";
+ else if (rc == KTC_NOCM)
+ errText = "KTC_NOCM: The service, Transarc AFS Daemon, most likely is not started!";
+ else
+ errText = "Unknown error!";
+
+ sprintf(message, "%s\n(%s failed)", errText, FailedFunctionName);
+
+ if ( IsDebuggerPresent() ) {
+ OutputDebugString(message);
+ OutputDebugString("\n");
+ }
+ MessageBox(NULL, message, "AFS", MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND);
+ return;
+}
+
+static DWORD
+GetServiceStatus(
+ LPSTR lpszMachineName,
+ LPSTR lpszServiceName,
+ DWORD *lpdwCurrentState)
+{
+ DWORD hr = NOERROR;
+ SC_HANDLE schSCManager = NULL;
+ SC_HANDLE schService = NULL;
+ DWORD fdwDesiredAccess = 0;
+ SERVICE_STATUS ssServiceStatus = {0};
+ BOOL fRet = FALSE;
+
+ *lpdwCurrentState = 0;
+
+ fdwDesiredAccess = GENERIC_READ;
+
+ schSCManager = OpenSCManager(lpszMachineName,
+ NULL,
+ fdwDesiredAccess);
+
+ if(schSCManager == NULL)
+ {
+ hr = GetLastError();
+ goto cleanup;
+ }
+
+ schService = OpenService(schSCManager,
+ lpszServiceName,
+ fdwDesiredAccess);
+
+ if(schService == NULL)
+ {
+ hr = GetLastError();
+ goto cleanup;
+ }
+
+ fRet = QueryServiceStatus(schService,
+ &ssServiceStatus);
+
+ if(fRet == FALSE)
+ {
+ hr = GetLastError();
+ goto cleanup;
+ }
+
+ *lpdwCurrentState = ssServiceStatus.dwCurrentState;
+
+cleanup:
+
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
+
+ return(hr);
+}
+
+void
+UnloadFuncs(
+ FUNC_INFO fi[],
+ HINSTANCE h
+ )
+{
+ int n;
+ if (fi)
+ for (n = 0; fi[n].func_ptr_var; n++)
+ *(fi[n].func_ptr_var) = 0;
+ if (h) FreeLibrary(h);
+}
+
+int
+LoadFuncs(
+ const char* dll_name,
+ FUNC_INFO fi[],
+ HINSTANCE* ph, // [out, optional] - DLL handle
+ int* pindex, // [out, optional] - index of last func loaded (-1 if none)
+ int cleanup, // cleanup function pointers and unload on error
+ int go_on, // continue loading even if some functions cannot be loaded
+ int silent // do not pop-up a system dialog if DLL cannot be loaded
+ )
+{
+ HINSTANCE h;
+ int i, n, last_i;
+ int error = 0;
+ UINT em;
+
+ if (ph) *ph = 0;
+ if (pindex) *pindex = -1;
+
+ for (n = 0; fi[n].func_ptr_var; n++)
+ *(fi[n].func_ptr_var) = 0;
+
+ if (silent)
+ em = SetErrorMode(SEM_FAILCRITICALERRORS);
+ h = LoadLibrary(dll_name);
+ if (silent)
+ SetErrorMode(em);
+
+ if (!h)
+ return 0;
+
+ last_i = -1;
+ for (i = 0; (go_on || !error) && (i < n); i++)
+ {
+ void* p = (void*)GetProcAddress(h, fi[i].func_name);
+ if (!p)
+ error = 1;
+ else
+ {
+ last_i = i;
+ *(fi[i].func_ptr_var) = p;
+ }
+ }
+ if (pindex) *pindex = last_i;
+ if (error && cleanup && !go_on) {
+ for (i = 0; i < n; i++) {
+ *(fi[i].func_ptr_var) = 0;
+ }
+ FreeLibrary(h);
+ return 0;
+ }
+ if (ph) *ph = h;
+ if (error) return 0;
+ return 1;
+}
+
+#ifdef USE_FSPROBE
+// Cell Accessibility Functions
+// based on work originally submitted to the CMU Computer Club
+// by Jeffrey Hutzelman
+//
+// These would work great if the fsProbe interface had been
+// ported to Windows
+
+static
+void probeComplete()
+{
+ fsprobe_Cleanup(1);
+ rx_Finalize();
+}
+
+struct ping_params {
+ unsigned short port; // in
+ int retry_delay; // in seconds
+ int verbose; // in
+ struct {
+ int wait; // in seconds
+ int retry; // in attempts
+ } host;
+ int max_hosts; // in
+ int hosts_attempted; // out
+}
+
+// the fsHandler is where we receive the answer to the probe
+static
+int fsHandler(void)
+{
+ ping_count = fsprobe_Results.probeNum;
+ if (!*fsprobe_Results.probeOK)
+ {
+ ok_count++;
+ if (waiting) complete();
+ }
+ if (ping_count == retry)
+ complete();
+ return 0;
+}
+
+// ping_fs is a callback routine meant to be called from within
+// cm_SearchCellFile() or cm_SearchCellDNS()
+static long
+pingFS(void *ping_params, struct sockaddr_in *addrp, char *namep)
+{
+ int rc;
+ struct ping_params * pp = (struct ping_params *) ping_params;
+
+ if ( pp->max_hosts && pp->hosts_attempted >= pp->max_hosts )
+ return 0;
+
+ pp->hosts_attempted++;
+
+ if (pp->port && addrp->sin_port != htons(pp->port))
+ addrp->sin_port = htons(pp->port);
+
+ rc = fsprobe_Init(1, addrp, pp->retry_delay, fsHandler, pp->verbose);
+ if (rc)
+ {
+ fprintf(stderr, "fsprobe_Init failed (%d)\n", rc);
+ fsprobe_Cleanup(1);
+ return 0;
+ }
+
+ for (;;)
+ {
+ tv.tv_sec = pp->host.wait;
+ tv.tv_usec = 0;
+ if (IOMGR_Select(0, 0, 0, 0, &tv))
+ break;
+ }
+ probeComplete();
+ return(0);
+}
+
+
+static BOOL
+pingCell(char *cell)
+{
+ int rc;
+ char rootcell[MAXCELLCHARS+1];
+ char newcell[MAXCELLCHARS+1];
+ struct ping_params pp;
+
+ memset(&pp, 0, sizeof(struct ping_params));
+
+ if (!cell || strlen(cell) == 0) {
+ /* WIN32 NOTE: no way to get max chars */
+ if (rc = pcm_GetRootCellName(rootcell))
+ return(FALSE);
+ cell = rootcell;
+ }
+
+ pp.port = 7000; // AFS FileServer
+ pp.retry_delay = 10;
+ pp.max_hosts = 3;
+ pp.host.wait = 30;
+ pp.host.retry = 0;
+ pp.verbose = 1;
+
+ /* WIN32: cm_SearchCellFile(cell, pcallback, pdata) */
+ rc = pcm_SearchCellFile(cell, newcell, pingFS, (void *)&pp);
+}
+#endif /* USE_FSPROBE */
+
+// These two items are imported from afscreds.h
+// but it cannot be included without causing conflicts
+#define c100ns1SECOND (LONGLONG)10000000
+static void
+TimeToSystemTime (SYSTEMTIME *pst, time_t TimeT)
+{
+ struct tm *pTime;
+ memset (pst, 0x00, sizeof(SYSTEMTIME));
+
+ if ((pTime = localtime (&TimeT)) != NULL)
+ {
+ pst->wYear = pTime->tm_year + 1900;
+ pst->wMonth = pTime->tm_mon + 1;
+ pst->wDayOfWeek = pTime->tm_wday;
+ pst->wDay = pTime->tm_mday;
+ pst->wHour = pTime->tm_hour;
+ pst->wMinute = pTime->tm_min;
+ pst->wSecond = pTime->tm_sec;
+ pst->wMilliseconds = 0;
+ }
+}
+
+void
+ObtainTokensFromUserIfNeeded(HWND hWnd)
+{
+ char * rootcell = NULL;
+ char cell[MAXCELLCHARS+1] = "";
+ char password[PROBE_PASSWORD_LEN+1];
+ krb5_data pwdata;
+ afsconf_cell cellconfig;
+ struct ktc_principal aserver;
+ struct ktc_principal aclient;
+ struct ktc_token atoken;
+ krb5_context ctx;
+ krb5_timestamp now = 0;
+ krb5_error_code code;
+ int serverReachable = 0;
+ int rc;
+#ifndef USE_FSPROBE
+ krb5_ccache cc = 0;
+ const char * realm = 0;
+ krb5_principal principal = 0;
+ char * pname = 0;
+#endif /* USE_FSPROBE */
+ DWORD CurrentState;
+ char HostName[64];
+
+ CurrentState = 0;
+ memset(HostName, '\0', sizeof(HostName));
+ gethostname(HostName, sizeof(HostName));
+ if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
+ return;
+ if (CurrentState != SERVICE_RUNNING) {
+ SendMessage(hWnd, WM_START_SERVICE, FALSE, 0L);
+ return;
+ }
+
+ if ( KFW_is_available() ) {
+ code = pkrb5_init_context(&ctx);
+ if ( code ) goto cleanup;
+ }
+
+ rootcell = (char *)GlobalAlloc(GPTR,MAXCELLCHARS+1);
+ if ( !rootcell ) goto cleanup;
+
+ code = get_cellconfig(cell, (void*)&cellconfig, rootcell);
+ if ( code ) goto cleanup;
+
+ memset(&aserver, '\0', sizeof(aserver));
+ strcpy(aserver.name, "afs");
+ strcpy(aserver.cell, rootcell);
+
+ rc = pktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
+
+ if ( KFW_is_available() ) {
+ code = pkrb5_timeofday(ctx, &now);
+ if ( code )
+ now = 0;
+
+ if (!rc && (now < atoken.endTime))
+ goto cleanup;
+ } else {
+ SYSTEMTIME stNow;
+ FILETIME ftNow;
+ FILETIME ftExpires;
+ LONGLONG llNow;
+ LONGLONG llExpires;
+ SYSTEMTIME stExpires;
+
+ TimeToSystemTime (&stExpires, atoken.endTime);
+ GetLocalTime (&stNow);
+ SystemTimeToFileTime (&stNow, &ftNow);
+ SystemTimeToFileTime (&stExpires, &ftExpires);
+
+ llNow = (((LONGLONG)ftNow.dwHighDateTime) << 32) + (LONGLONG)(ftNow.dwLowDateTime);
+ llExpires = (((LONGLONG)ftExpires.dwHighDateTime) << 32) + (LONGLONG)(ftExpires.dwLowDateTime);
+
+ llNow /= c100ns1SECOND;
+ llExpires /= c100ns1SECOND;
+
+ if (!rc && (llNow < llExpires))
+ goto cleanup;
+ }
+
+
+#ifdef USE_FSPROBE
+ serverReachable = cellPing(NULL);
+#else
+ if ( KFW_is_available() ) {
+ // If we can't use the FSProbe interface we can attempt to forge
+ // a kinit and if we can back an invalid user error we know the
+ // kdc is at least reachable
+ realm = afs_realm_of_cell(&cellconfig); // do not free
+
+ code = pkrb5_build_principal(ctx, &principal, strlen(realm),
+ realm, PROBE_USERNAME, NULL, NULL);
+ if ( code ) goto cleanup;
+
+ code = KFW_get_ccache(ctx, principal, &cc);
+ if ( code ) goto cleanup;
+
+ code = pkrb5_unparse_name(ctx, principal, &pname);
+ if ( code ) goto cleanup;
+
+ pwdata.data = password;
+ pwdata.length = PROBE_PASSWORD_LEN;
+ code = pkrb5_c_random_make_octets(ctx, &pwdata);
+ if (code) {
+ int i;
+ for ( i=0 ; i<PROBE_PASSWORD_LEN ; i )
+ password[i] = 'x';
+ }
+ password[PROBE_PASSWORD_LEN] = '\0';
+
+ code = KFW_kinit(NULL, NULL, HWND_DESKTOP,
+ pname,
+ password,
+ 5,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0);
+ switch ( code ) {
+ case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
+ case KRB5KDC_ERR_CLIENT_REVOKED:
+ case KRB5KDC_ERR_CLIENT_NOTYET:
+ case KRB5KDC_ERR_PREAUTH_FAILED:
+ case KRB5KDC_ERR_PREAUTH_REQUIRED:
+ case KRB5KDC_ERR_PADATA_TYPE_NOSUPP:
+ serverReachable = TRUE;
+ break;
+ default:
+ serverReachable = FALSE;
+ }
+ } else {
+ int i;
+ for ( i=0 ; i<PROBE_PASSWORD_LEN ; i )
+ password[i] = 'x';
+
+ code = ObtainNewCredentials(rootcell, PROBE_USERNAME, password);
+ serverReachable = 1;
+ }
+#endif
+ if ( !serverReachable )
+ goto cleanup;
+
+ if ( KFW_is_available() ) {
+#ifdef USE_MS2MIT
+ KFW_import_windows_lsa();
+#endif /* USE_MS2MIT */
+ KFW_AFS_renew_expiring_credentials();
+ KFW_AFS_renew_token_for_cell(rootcell);
+
+ rc = pktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
+ if (!rc && (now < atoken.endTime))
+ goto cleanup;
+ }
+
+ SendMessage(hWnd, WM_OBTAIN_TOKENS, FALSE, (long)rootcell);
+ rootcell = NULL; // rootcell freed by message receiver
+
+ cleanup:
+ if (rootcell)
+ GlobalFree(rootcell);
+
+#ifndef USE_FSPROBE
+ if ( pname )
+ pkrb5_free_unparsed_name(ctx,pname);
+ if ( principal )
+ pkrb5_free_principal(ctx,principal);
+ if (cc)
+ pkrb5_cc_close(ctx,cc);
+#endif /* USE_FSPROBE */
+ if (ctx)
+ pkrb5_free_context(ctx);
+ return;
+}
+
+// IP Change Monitoring Functions
+#include <Iphlpapi.h>
+
+DWORD
+GetNumOfIpAddrs(void)
+{
+ PMIB_IPADDRTABLE pIpAddrTable = NULL;
+ ULONG dwSize;
+ DWORD code;
+ DWORD index;
+ DWORD validAddrs = 0;
+
+ dwSize = 0;
+ code = GetIpAddrTable(NULL, &dwSize, 0);
+ if (code == ERROR_INSUFFICIENT_BUFFER) {
+ pIpAddrTable = malloc(dwSize);
+ code = GetIpAddrTable(pIpAddrTable, &dwSize, 0);
+ for ( index=0; index < pIpAddrTable->dwNumEntries; index++ ) {
+ if (pIpAddrTable->table[index].dwAddr != 0)
+ validAddrs++;
+ }
+ free(pIpAddrTable);
+ }
+ return validAddrs;
+}
+
+void
+IpAddrChangeMonitor(void * hWnd)
+{
+#ifdef USE_OVERLAPPED
+ HANDLE Handle = INVALID_HANDLE_VALUE;
+ OVERLAPPED Ovlap;
+#endif /* USE_OVERLAPPED */
+ DWORD Result;
+ DWORD prevNumOfAddrs = GetNumOfIpAddrs();
+ DWORD NumOfAddrs;
+
+ if ( !hWnd )
+ return;
+
+ while ( TRUE ) {
+#ifdef USE_OVERLAPPED
+ ZeroMemory(&Ovlap, sizeof(OVERLAPPED));
+
+ Result = NotifyAddrChange(&Handle,&Ovlap);
+ if (Result != ERROR_IO_PENDING)
+ {
+ printf("NotifyAddrChange() failed with error %d \n", Result);
+ break;
+ }
+
+ if ((Result = WaitForSingleObject(Handle,INFINITE)) == WAIT_FAILED)
+ {
+ printf("WaitForSingleObject() failed with error %d\n",
+ GetLastError());
+ continue;
+ }
+
+ if (GetOverlappedResult(Handle, &Ovlap,
+ &DataTransfered, TRUE) == 0)
+ {
+ printf("GetOverlapped result failed %d \n",
+ GetLastError());
+ break;
+ }
+
+#else
+ Result = NotifyAddrChange(NULL,NULL);
+#endif
+
+ NumOfAddrs = GetNumOfIpAddrs();
+ if ( NumOfAddrs != prevNumOfAddrs ) {
+ // Give AFS Client Service a chance to notice and die
+ // Or for network services to startup
+ Sleep(2000);
+ // this call should probably be mutex protected
+ ObtainTokensFromUserIfNeeded(hWnd);
+ }
+ prevNumOfAddrs = NumOfAddrs;
+ }
+
+#ifdef USE_OVERLAPPED
+ if (Handle != INVALID_HANDLE_VALUE)
+ CloseHandle(Handle);
+#endif
+}
+
+
+DWORD
+IpAddrChangeMonitorInit(HWND hWnd)
+{
+ DWORD status = ERROR_SUCCESS;
+ HANDLE thread;
+ ULONG threadID = 0;
+
+ thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)IpAddrChangeMonitor,
+ hWnd, 0, &threadID);
+
+ if (thread == NULL) {
+ status = GetLastError();
+ }
+ CloseHandle(thread);
+ return status;
+}
+