/* Copyright 2005,2006 by the Massachusetts Institute of Technology Copyright 2007 by Secure Endpoints Inc. All rights reserved. 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 the Massachusetts Institute of Technology (M.I.T.) not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "kfwlogon.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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_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); DECL_FUNC_PTR(Leash_get_default_mslsa_import); // 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_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); // 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); DECL_FUNC_PTR(LsaNtStatusToWinError); // LSA Functions DECL_FUNC_PTR(LsaConnectUntrusted); DECL_FUNC_PTR(LsaLookupAuthenticationPackage); DECL_FUNC_PTR(LsaCallAuthenticationPackage); DECL_FUNC_PTR(LsaFreeReturnBuffer); DECL_FUNC_PTR(LsaGetLogonSessionData); // 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_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 leash_opt_fi[] = { MAKE_FUNC_INFO(Leash_get_default_mslsa_import), 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_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 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), MAKE_FUNC_INFO(LsaNtStatusToWinError), END_FUNC_INFO }; 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 }; /* Static Declarations */ static int inited = 0; static HINSTANCE hKrb5 = 0; static HINSTANCE hKrb524 = 0; static HINSTANCE hSecur32 = 0; static HINSTANCE hAdvApi32 = 0; static HINSTANCE hComErr = 0; static HINSTANCE hService = 0; static HINSTANCE hProfile = 0; static HINSTANCE hLeash = 0; static HINSTANCE hLeashOpt = 0; static HINSTANCE hCCAPI = 0; static DWORD TraceOption = 0; static HANDLE hDLL; BOOL IsDebugLogging(void) { DWORD LSPsize; HKEY NPKey; DWORD dwDebug = FALSE; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\MIT Kerberos\\NetworkProvider", 0, KEY_QUERY_VALUE, &NPKey) == ERROR_SUCCESS) { LSPsize=sizeof(dwDebug); if (RegQueryValueEx(NPKey, "Debug", NULL, NULL, (LPBYTE)&dwDebug, &LSPsize) != ERROR_SUCCESS) { dwDebug = FALSE; } RegCloseKey (NPKey); } return(dwDebug ? TRUE : FALSE); } void DebugEvent0(char *a) { HANDLE h; char *ptbuf[1]; if (IsDebugLogging()) { h = RegisterEventSource(NULL, KFW_LOGON_EVENT_NAME); if (h) { ptbuf[0] = a; ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL); DeregisterEventSource(h); } } } #define MAXBUF_ 512 void DebugEvent(char *b,...) { HANDLE h; char *ptbuf[1],buf[MAXBUF_+1]; va_list marker; if (IsDebugLogging()) { h = RegisterEventSource(NULL, KFW_LOGON_EVENT_NAME); if (h) { va_start(marker,b); StringCbVPrintf(buf, MAXBUF_+1,b,marker); buf[MAXBUF_] = '\0'; ptbuf[0] = buf; ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL); DeregisterEventSource(h); va_end(marker); } } } static HANDLE hInitMutex = NULL; static BOOL bInit = FALSE; /* KFW_initialize cannot be called from DllEntryPoint */ void KFW_initialize(void) { static int inited = 0; if ( !inited ) { char mutexName[MAX_PATH]; HANDLE hMutex = NULL; sprintf(mutexName, "AFS KFW Init pid=%d", getpid()); hMutex = CreateMutex( NULL, TRUE, mutexName ); if ( GetLastError() == ERROR_ALREADY_EXISTS ) { if ( WaitForSingleObject( hMutex, INFINITE ) != WAIT_OBJECT_0 ) { return; } } if ( !inited ) { inited = 1; LoadFuncs(KRB5_DLL, k5_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); LoadFuncs(SECUR32_DLL, lsa_fi, &hSecur32, 0, 1, 1, 1); LoadFuncs(PROFILE_DLL, profile_fi, &hProfile, 0, 1, 0, 0); LoadFuncs(LEASH_DLL, leash_fi, &hLeash, 0, 1, 0, 0); LoadFuncs(CCAPI_DLL, ccapi_fi, &hCCAPI, 0, 1, 0, 0); LoadFuncs(LEASH_DLL, leash_opt_fi, &hLeashOpt, 0, 1, 0, 0); } ReleaseMutex(hMutex); CloseHandle(hMutex); } } void KFW_cleanup(void) { if (hLeashOpt) FreeLibrary(hLeashOpt); if (hCCAPI) FreeLibrary(hCCAPI); if (hLeash) FreeLibrary(hLeash); if (hKrb524) FreeLibrary(hKrb524); if (hSecur32) FreeLibrary(hSecur32); if (hService) FreeLibrary(hService); if (hComErr) FreeLibrary(hComErr); if (hProfile) FreeLibrary(hProfile); if (hKrb5) FreeLibrary(hKrb5); } int KFW_is_available(void) { KFW_initialize(); if ( hKrb5 && hComErr && hService && #ifdef USE_MS2MIT hSecur32 && #endif /* USE_MS2MIT */ hProfile && hLeash && hCCAPI ) return TRUE; return FALSE; } /* 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 (!pkrb5_init_context) return 0; 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; ccname = (char *)malloc(strlen(pname) + 5); sprintf(ccname,"API:%s",pname); DebugEvent0(ccname); 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); } 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(); lifetime *= 60; if (renew_life > 0) renew_life *= 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 NULL, // no 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;icontents ) 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_get_cred( char * username, char * password, int lifetime, char ** reasonP ) { krb5_context ctx = 0; krb5_ccache cc = 0; char * realm = 0; krb5_principal principal = 0; char * pname = 0; krb5_error_code code; if (!pkrb5_init_context || !username || !password || !password[0]) return 0; DebugEvent0(username); code = pkrb5_init_context(&ctx); if ( code ) goto cleanup; code = pkrb5_get_default_realm(ctx, &realm); if (realm) { pname = malloc(strlen(username) + strlen(realm) + 2); if (!pname) goto cleanup; strcpy(pname, username); strcat(pname, "@"); strcat(pname, realm); } else { goto cleanup; } DebugEvent0(realm); DebugEvent0(pname); code = pkrb5_parse_name(ctx, pname, &principal); if ( code ) goto cleanup; DebugEvent0("parsed name"); code = KFW_get_ccache(ctx, principal, &cc); if ( code ) goto cleanup; DebugEvent0("got ccache"); if ( lifetime == 0 ) lifetime = pLeash_get_default_lifetime(); DebugEvent0("got 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()); DebugEvent0("kinit returned"); if ( code ) goto cleanup; cleanup: if ( pname ) free(pname); if ( realm ) pkrb5_free_default_realm(ctx, realm); if ( cc ) pkrb5_cc_close(ctx, cc); if ( code && reasonP ) { *reasonP = (char *)perror_message(code); } return(code); } int KFW_set_ccache_dacl(char *filename, HANDLE hUserToken) { // SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_SID_AUTHORITY; PSID pSystemSID = NULL; DWORD SystemSIDlength = 0, UserSIDlength = 0; PACL ccacheACL = NULL; DWORD ccacheACLlength = 0; PTOKEN_USER pTokenUser = NULL; DWORD retLen; DWORD gle; int ret = 0; if (!filename) { DebugEvent0("KFW_set_ccache_dacl - invalid parms"); return 1; } DebugEvent0("KFW_set_ccache_dacl"); /* Get System SID */ if (!ConvertStringSidToSid("S-1-5-18", &pSystemSID)) { DebugEvent("KFW_set_ccache_dacl - ConvertStringSidToSid GLE = 0x%x", GetLastError()); ret = 1; goto cleanup; } /* Create ACL */ SystemSIDlength = GetLengthSid(pSystemSID); ccacheACLlength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + SystemSIDlength - sizeof(DWORD); if (hUserToken) { if (!GetTokenInformation(hUserToken, TokenUser, NULL, 0, &retLen)) { if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) { pTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen); if (!GetTokenInformation(hUserToken, TokenUser, pTokenUser, retLen, &retLen)) { DebugEvent("GetTokenInformation failed: GLE = %lX", GetLastError()); } } } if (pTokenUser) { UserSIDlength = GetLengthSid(pTokenUser->User.Sid); ccacheACLlength += sizeof(ACCESS_ALLOWED_ACE) + UserSIDlength - sizeof(DWORD); } } ccacheACL = (PACL) LocalAlloc(LPTR, ccacheACLlength); if (!ccacheACL) { DebugEvent("KFW_set_ccache_dacl - LocalAlloc GLE = 0x%x", GetLastError()); ret = 1; goto cleanup; } InitializeAcl(ccacheACL, ccacheACLlength, ACL_REVISION); AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0, STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL, pSystemSID); if (pTokenUser) { AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0, STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL, pTokenUser->User.Sid); if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, ccacheACL, NULL)) { gle = GetLastError(); DebugEvent("SetNamedSecurityInfo DACL (1) failed: GLE = 0x%lX", gle); if (gle != ERROR_NO_TOKEN) ret = 1; } if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, pTokenUser->User.Sid, NULL, NULL, NULL)) { gle = GetLastError(); DebugEvent("SetNamedSecurityInfo OWNER (2) failed: GLE = 0x%lX", gle); if (gle != ERROR_NO_TOKEN) ret = 1; } } else { if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, ccacheACL, NULL)) { gle = GetLastError(); DebugEvent("SetNamedSecurityInfo DACL (3) failed: GLE = 0x%lX", gle); if (gle != ERROR_NO_TOKEN) ret = 1; } } cleanup: if (pSystemSID) LocalFree(pSystemSID); if (pTokenUser) LocalFree(pTokenUser); if (ccacheACL) LocalFree(ccacheACL); return ret; } int KFW_set_ccache_dacl_with_user_sid(char *filename, PSID pUserSID) { // SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_SID_AUTHORITY; PSID pSystemSID = NULL; DWORD SystemSIDlength = 0, UserSIDlength = 0; PACL ccacheACL = NULL; DWORD ccacheACLlength = 0; DWORD gle; int ret = 0; if (!filename) { DebugEvent0("KFW_set_ccache_dacl_with_user_sid - invalid parms"); return 1; } DebugEvent0("KFW_set_ccache_dacl_with_user_sid"); /* Get System SID */ if (!ConvertStringSidToSid("S-1-5-18", &pSystemSID)) { DebugEvent("KFW_set_ccache_dacl - ConvertStringSidToSid GLE = 0x%x", GetLastError()); ret = 1; goto cleanup; } /* Create ACL */ SystemSIDlength = GetLengthSid(pSystemSID); ccacheACLlength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + SystemSIDlength - sizeof(DWORD); if (pUserSID) { UserSIDlength = GetLengthSid(pUserSID); ccacheACLlength += sizeof(ACCESS_ALLOWED_ACE) + UserSIDlength - sizeof(DWORD); } ccacheACL = (PACL) LocalAlloc(LPTR, ccacheACLlength); if (!ccacheACL) { DebugEvent("KFW_set_ccache_dacl - LocalAlloc GLE = 0x%x", GetLastError()); ret = 1; goto cleanup; } InitializeAcl(ccacheACL, ccacheACLlength, ACL_REVISION); AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0, STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL, pSystemSID); if (pUserSID) { AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0, STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL, pUserSID); if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, ccacheACL, NULL)) { gle = GetLastError(); DebugEvent("SetNamedSecurityInfo DACL (4) failed: GLE = 0x%lX", gle); if (gle != ERROR_NO_TOKEN) ret = 1; } if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, pUserSID, NULL, NULL, NULL)) { gle = GetLastError(); DebugEvent("SetNamedSecurityInfo OWNER (5) failed: GLE = 0x%lX", gle); if (gle != ERROR_NO_TOKEN) ret = 1; } } else { if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, ccacheACL, NULL)) { gle = GetLastError(); DebugEvent("SetNamedSecurityInfo DACL (6) failed: GLE = 0x%lX", gle); if (gle != ERROR_NO_TOKEN) ret = 1; } } cleanup: if (pSystemSID) LocalFree(pSystemSID); if (ccacheACL) LocalFree(ccacheACL); return ret; } int KFW_obtain_user_temp_directory(HANDLE hUserToken, char *newfilename, int size) { int retval = 0; DWORD dwSize = size-1; /* leave room for nul */ DWORD dwLen = 0; if (!hUserToken || !newfilename || size <= 0) return 1; *newfilename = '\0'; dwLen = ExpandEnvironmentStringsForUser(hUserToken, "%TEMP%", newfilename, dwSize); if ( !dwLen || dwLen > dwSize ) dwLen = ExpandEnvironmentStringsForUser(hUserToken, "%TMP%", newfilename, dwSize); if ( !dwLen || dwLen > dwSize ) return 1; newfilename[dwSize] = '\0'; return 0; } void KFW_copy_cache_to_system_file(const char * user, const char * filename) { char cachename[MAX_PATH + 8] = "FILE:"; krb5_context ctx = 0; krb5_error_code code; krb5_principal princ = 0; krb5_ccache cc = 0; krb5_ccache ncc = 0; PSECURITY_ATTRIBUTES pSA = NULL; if (!pkrb5_init_context || !user || !filename) return; strncat(cachename, filename, sizeof(cachename)); cachename[sizeof(cachename)-1] = '\0'; DebugEvent("KFW_Logon_Event - ccache %s", cachename); DeleteFile(filename); code = pkrb5_init_context(&ctx); if (code) goto cleanup; code = pkrb5_parse_name(ctx, user, &princ); if (code) goto cleanup; code = KFW_get_ccache(ctx, princ, &cc); if (code) goto cleanup; code = pkrb5_cc_resolve(ctx, cachename, &ncc); if (code) goto cleanup; code = pkrb5_cc_initialize(ctx, ncc, princ); if (code) goto cleanup; code = KFW_set_ccache_dacl(filename, NULL); if (code) goto cleanup; code = pkrb5_cc_copy_creds(ctx,cc,ncc); cleanup: if ( cc ) { pkrb5_cc_close(ctx, cc); cc = 0; } if ( ncc ) { pkrb5_cc_close(ctx, ncc); ncc = 0; } if ( princ ) { pkrb5_free_principal(ctx, princ); princ = 0; } if (ctx) pkrb5_free_context(ctx); } int KFW_copy_file_cache_to_default_cache(char * filename) { char cachename[MAX_PATH + 8] = "FILE:"; krb5_context ctx = 0; krb5_error_code code; krb5_principal princ = 0; krb5_ccache cc = 0; krb5_ccache ncc = 0; int retval = 1; if (!pkrb5_init_context || !filename) return 1; if ( strlen(filename) + sizeof("FILE:") > sizeof(cachename) ) return 1; code = pkrb5_init_context(&ctx); if (code) return 1; strcat(cachename, filename); code = pkrb5_cc_resolve(ctx, cachename, &cc); if (code) { DebugEvent0("kfwcpcc krb5_cc_resolve failed"); goto cleanup; } code = pkrb5_cc_get_principal(ctx, cc, &princ); if (code) { DebugEvent0("kfwcpcc krb5_cc_get_principal failed"); goto cleanup; } code = pkrb5_cc_default(ctx, &ncc); if (code) { DebugEvent0("kfwcpcc krb5_cc_default failed"); goto cleanup; } if (!code) { code = pkrb5_cc_initialize(ctx, ncc, princ); if (!code) code = pkrb5_cc_copy_creds(ctx,cc,ncc); if (code) { DebugEvent0("kfwcpcc krb5_cc_copy_creds failed"); goto cleanup; } } if ( ncc ) { pkrb5_cc_close(ctx, ncc); ncc = 0; } retval=0; /* success */ cleanup: if ( cc ) { pkrb5_cc_close(ctx, cc); cc = 0; } DeleteFile(filename); if ( princ ) { pkrb5_free_principal(ctx, princ); princ = 0; } if (ctx) pkrb5_free_context(ctx); return 0; } int KFW_copy_file_cache_to_api_cache(char * filename) { char cachename[MAX_PATH + 8] = "FILE:"; krb5_context ctx = 0; krb5_error_code code; krb5_principal princ = 0; krb5_ccache cc = 0; krb5_ccache ncc = 0; char *name = NULL; int retval = 1; if (!pkrb5_init_context || !filename) return 1; if ( strlen(filename) + sizeof("FILE:") > sizeof(cachename) ) return 1; code = pkrb5_init_context(&ctx); if (code) return 1; strcat(cachename, filename); code = pkrb5_cc_resolve(ctx, cachename, &cc); if (code) { DebugEvent0("kfwcpcc krb5_cc_resolve failed"); goto cleanup; } code = pkrb5_cc_get_principal(ctx, cc, &princ); if (code) { DebugEvent0("kfwcpcc krb5_cc_get_principal failed"); goto cleanup; } code = pkrb5_unparse_name(ctx, princ, &name); if (code) { DebugEvent0("kfwcpcc krb5_unparse_name failed"); goto cleanup; } sprintf(cachename, "API:%s", name); code = pkrb5_cc_resolve(ctx, cachename, &ncc); if (code) { DebugEvent0("kfwcpcc krb5_cc_default failed"); goto cleanup; } if (!code) { code = pkrb5_cc_initialize(ctx, ncc, princ); if (!code) code = pkrb5_cc_copy_creds(ctx,cc,ncc); if (code) { DebugEvent0("kfwcpcc krb5_cc_copy_creds failed"); goto cleanup; } } if ( ncc ) { pkrb5_cc_close(ctx, ncc); ncc = 0; } retval=0; /* success */ cleanup: if (name) pkrb5_free_unparsed_name(ctx, name); if ( cc ) { pkrb5_cc_close(ctx, cc); cc = 0; } DeleteFile(filename); if ( princ ) { pkrb5_free_principal(ctx, princ); princ = 0; } if (ctx) pkrb5_free_context(ctx); return 0; } int KFW_destroy_tickets_for_principal(char * user) { krb5_context ctx = 0; krb5_error_code code; krb5_principal princ = 0; krb5_ccache cc = 0; if (!pkrb5_init_context) return 0; code = pkrb5_init_context(&ctx); if (code) return 1; code = pkrb5_parse_name(ctx, user, &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; } pkrb5_free_context(ctx); return 0; } /* There are scenarios in which an interactive logon will not * result in the LogonScript being executed. This will result * in orphaned cache files being left in the Temp directory. * This function will search for cache files in the Temp * directory and delete any that are older than five minutes. */ void KFW_cleanup_orphaned_caches(void) { char * temppath = NULL; char * curdir = NULL; DWORD count, count2; WIN32_FIND_DATA FindFileData; HANDLE hFind = INVALID_HANDLE_VALUE; FILETIME now; ULARGE_INTEGER uli_now; FILETIME expired; count = GetTempPath(0, NULL); if (count <= 0) return; temppath = (char *) malloc(count); if (!temppath) goto cleanup; count2 = GetTempPath(count, temppath); if (count2 <= 0 || count2 > count) goto cleanup; count = GetCurrentDirectory(0, NULL); curdir = (char *)malloc(count); if (!curdir) goto cleanup; count2 = GetCurrentDirectory(count, curdir); if (count2 <= 0 || count2 > count) goto cleanup; if (!SetCurrentDirectory(temppath)) goto cleanup; GetSystemTimeAsFileTime(&now); uli_now.u.LowPart = now.dwLowDateTime; uli_now.u.HighPart = now.dwHighDateTime; uli_now.QuadPart -= 3000000000; /* 5 minutes == 3 billion 100 nano seconds */ expired.dwLowDateTime = uli_now.u.LowPart; expired.dwHighDateTime = uli_now.u.HighPart; hFind = FindFirstFile("kfwlogon-*", &FindFileData); if (hFind != INVALID_HANDLE_VALUE) { do { if (CompareFileTime(&FindFileData.ftCreationTime, &expired) < 0) { DebugEvent("Deleting orphaned cache file: \"%s\"", FindFileData.cFileName); DeleteFile(FindFileData.cFileName); } } while ( FindNextFile(hFind, &FindFileData) ); } SetCurrentDirectory(curdir); cleanup: if (temppath) free(temppath); if (hFind != INVALID_HANDLE_VALUE) FindClose(hFind); if (curdir) free(curdir); }