#include "KerberosHelper.h"
#include "KerberosHelperContext.h"
#include <Kerberos/KerberosLogin.h>
#include <Kerberos/pkinit_cert_store.h>
#include "LKDCHelper.h"
#include <Carbon/Carbon.h>
#include <Security/Security.h>
#include <Security/SecCertificatePriv.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <asl.h>
#define DEBUG 0
static krb5_error_code
_k5_check_err(krb5_error_code error, const char *function, const char *file, int line)
{
if (error)
asl_log(NULL, NULL, ASL_LEVEL_DEBUG, " %s: krb5 call got %d (%s) on %s:%d", function, error, error_message(error), file, line);
return error;
}
#define k5_ok(x) _k5_check_err (x, __func__, __FILE__, __LINE__)
#define KHLog(FMT, ...) asl_log(NULL, NULL, ASL_LEVEL_DEBUG, FMT, __VA_ARGS__)
static const char lkdc_prefix[] = "LKDC:";
static int
has_common_subrealm(const char *a, const char *b)
{
const char *ap = &a[strlen(a)];
const char *bp = &b[strlen(b)];
unsigned int n = 0;
if (ap == a || bp == b)
return 0;
for (--ap, --bp; ap >= a && bp >= b && *ap == *bp; --ap, --bp)
if ((ap == a && bp == b) ||
(ap == a && '.' == bp[-1]) ||
(bp == b && '.' == ap[-1]) ||
('.' == ap[-1] && '.' == bp[-1]))
++n;
return n;
}
static const char *
principal_realm(const char *princ)
{
const char *p = &princ[strlen(princ)];
while (p > princ) {
if ('@' == p[0] && '\\' != p[-1])
break;
--p;
}
if (p == princ)
return NULL;
else
return &p[1];
}
static int
realm_for_host(krb5_context ctx, const char *hostname, const char *hintrealm,
char **realm)
{
char **realmlist = NULL;
int err = 0;
KHLog ("[[[ %s: hostname=%s hintrealm=%s", __func__, hostname, NULL == hintrealm ? "(null)" : hintrealm);
*realm = NULL;
if (0 == k5_ok( krb5_get_host_realm (ctx, hostname, &realmlist))) {
if (NULL != realmlist && NULL != realmlist[0] && '\0' != *realmlist[0] &&
NULL != (*realm = strdup(*realmlist))) {
KHLog (" %s: krb5_get_host_realm success", __func__);
goto fin;
} else
KHLog (" %s: krb5_get_host_realm returned unusable realm!", __func__);
}
if ((NULL == hintrealm || 0 == strncmp(hintrealm, lkdc_prefix, sizeof(lkdc_prefix)-1)) &&
0 == LKDCDiscoverRealm(hostname, realm)) {
KHLog (" %s: LKDCDiscoverRealm success", __func__);
goto fin;
}
err = -1;
fin:
if (NULL != realmlist)
krb5_free_host_realm (ctx, realmlist);
if (0 == err)
KHLog("]]] %s: returning realm=%s", __func__, *realm);
else
KHLog("]]] %s: failed to determine realm", __func__);
return err;
}
static int
host_matches_hint(const char *hostname, const char *realm, const char *hinthost,
const char *hintrealm)
{
if (NULL == hinthost || NULL == hintrealm)
return 0;
else if (0 == strncmp(hinthost, lkdc_prefix, sizeof(lkdc_prefix)-1) &&
0 == strcmp(realm, hinthost))
return 1;
else if (0 == strcasecmp(hostname, hinthost) && 0 == strcmp(realm, hintrealm))
return 1;
else
return 0;
}
struct host_realm_mapping {
char *hostname;
char *realm;
};
static int
set_host_realm_mapping(struct host_realm_mapping *p, const char *hostname, const char *realm)
{
struct host_realm_mapping tmp = { strdup (hostname), strdup (realm) };
if (NULL == tmp.hostname || NULL == tmp.realm) {
free (tmp.hostname);
free (tmp.realm);
return -1;
} else {
free (p->hostname);
free (p->realm);
*p = tmp;
return 0;
}
}
static inline void
free_host_realm_mapping(struct host_realm_mapping *p)
{
free (p->hostname);
p->hostname = NULL;
free (p->realm);
p->realm = NULL;
}
static int
parse_principal_name(__unused krb5_context ctx, const char *princname, char **namep, char **instancep, char **realmp)
{
KLPrincipal principal = NULL;
char *name = NULL, *instance = NULL, *realm = NULL;
int err = 0;
KHLog ("[[[ %s () decomposing %s", __func__, princname);
*namep = *instancep = *realmp = NULL;
if (klNoErr != KLCreatePrincipalFromString (princname, kerberosVersion_V5, &principal)) {
err = -1;
goto fin;
}
if (klNoErr != KLGetTripletFromPrincipal (principal, &name, &instance, &realm)) {
err = -2;
goto fin;
}
if (NULL == (*namep = strdup(name)) || NULL == (*instancep = strdup(instance)) || NULL == (*realmp = strdup(realm))) {
err = -3;
goto fin;
}
fin:
if (NULL != principal)
KLDisposePrincipal (principal);
if (NULL != name)
KLDisposeString (name);
if (NULL != instance)
KLDisposeString (instance);
if (NULL != realm)
KLDisposeString (realm);
KHLog ("]]] %s () - %d", __func__, err);
return err;
}
OSStatus
KRBCreateSession(CFStringRef inHostName, CFStringRef inAdvertisedPrincipal,
void **outKerberosSession)
{
char hbuf[NI_MAXHOST];
struct host_realm_mapping primaryMatch = { NULL, NULL }, secondaryMatch = { NULL, NULL }, *matchp = NULL;
struct addrinfo hints, *res = NULL, *aip = NULL;
KRBhelperContext *hCtx = NULL;
char *tmp = NULL;
char *hintname = NULL, *hinthost = NULL, *hintrealm = NULL;
char *localname = NULL, *hostname = NULL, *realm = NULL;
OSStatus err = noErr;
int avoidDNSCanonicalizationBug = 0;
if (NULL == outKerberosSession) {
err = paramErr;
goto fin;
}
KHLog ("[[[ %s () - required parameters okay", __func__);
if (NULL == (hCtx = calloc(1, sizeof(*hCtx)))) {
err = memFullErr;
goto fin;
}
if (NULL == inHostName) {
err = DSCopyLocalKDC (&hCtx->realm);
KHLog (" %s: LocalKDC realm lookup only", __func__);
goto fin;
}
if (0 != k5_ok( krb5_init_context (&hCtx->krb5_ctx) )) {
err = memFullErr;
goto fin;
}
if (0 == k5_ok( krb5_get_default_realm (hCtx->krb5_ctx, &tmp) ) && NULL != tmp) {
if (NULL == (hCtx->defaultRealm = strdup (tmp))) {
err = memFullErr;
goto fin;
}
krb5_free_default_realm (hCtx->krb5_ctx, tmp);
}
if (NULL != inHostName)
hCtx->inHostName = CFRetain (inHostName);
if (NULL != inAdvertisedPrincipal) {
char *s = NULL;
hCtx->inAdvertisedPrincipal = CFRetain (inAdvertisedPrincipal);
if (0 != __KRBCreateUTF8StringFromCFString (inAdvertisedPrincipal, &s))
KHLog (" %s: __KRBCreateUTF8StringFromCFString failed", __func__);
else {
(void)parse_principal_name (hCtx->krb5_ctx, s, &hintname, &hinthost, &hintrealm);
__KRBReleaseUTF8String (s);
}
}
#if DEBUG
__KRBCreateUTF8StringFromCFString (inHostName, &tmp);
if (NULL != tmp) {
KHLog (" %s: raw host name = %s", __func__, tmp);
free (tmp);
tmp = NULL;
}
#endif
if (! _CFNetServiceDeconstructServiceName (&inHostName, &hostname))
__KRBCreateUTF8StringFromCFString (inHostName, &hostname);
else
avoidDNSCanonicalizationBug = 1;
if (inHostName != hCtx->inHostName) {
CFRelease (hCtx->inHostName);
hCtx->inHostName = inHostName;
}
KHLog (" %s: processed host name = %s", __func__, hostname);
if (NULL == strchr(hostname, '.') &&
(0 > asprintf(&localname, "%s.local", hostname) || NULL == localname)) {
err = memFullErr;
goto fin;
}
char lastChar = hostname[strlen(hostname)-1];
KHLog (" %s: last char of host name = 0x%02x", __func__, lastChar);
if (avoidDNSCanonicalizationBug == 0 && '.' != lastChar) {
memset (&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
err = getaddrinfo (hostname, NULL, &hints, &res);
KHLog (" %s: getaddrinfo = %s (%d)", __func__, 0 == err ? "success" : gai_strerror (err), err);
if (0 == err && NULL != res->ai_canonname && NULL != (tmp = strdup (res->ai_canonname))) {
free (hostname);
hostname = tmp;
KHLog (" %s: canonical host name = %s", __func__, hostname);
}
}
if (0 == realm_for_host (hCtx->krb5_ctx, hostname, hintrealm, &realm)) {
if (0 != set_host_realm_mapping (&secondaryMatch, hostname, realm)) {
err = memFullErr;
goto fin;
}
free (realm);
realm = NULL;
KHLog (" %s: secondary match = %s", __func__, secondaryMatch.hostname);
}
if (NULL != secondaryMatch.hostname &&
host_matches_hint(secondaryMatch.hostname, secondaryMatch.realm, hinthost, hintrealm)) {
primaryMatch = secondaryMatch;
secondaryMatch.hostname = secondaryMatch.realm = NULL;
KHLog (" %s: primary match = %s", __func__, primaryMatch.hostname);
}
free (hostname);
hostname = NULL;
for (aip = res; NULL != aip &&
NULL == primaryMatch.hostname && (NULL != hinthost || NULL == secondaryMatch.hostname);
aip = aip->ai_next) {
err = getnameinfo (aip->ai_addr, aip->ai_addr->sa_len, hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD);
#if DEBUG
KHLog (" %s: getnameinfo result %d %s", __func__, err, 0 == err ? "success" : gai_strerror (err));
#endif
if (0 != err)
continue;
#if DEBUG
char ipbuf[NI_MAXHOST];
const void *addr;
switch (aip->ai_family) {
case AF_INET:
addr = &((struct sockaddr_in *)aip->ai_addr)->sin_addr;
break;
case AF_INET6:
addr = &((struct sockaddr_in6 *)aip->ai_addr)->sin6_addr;
break;
default:
addr = NULL;
break;
}
if (NULL == addr || NULL == inet_ntop (aip->ai_family, addr, ipbuf, sizeof(ipbuf)))
KHLog (" %s: inet_ntop failed %s", __func__, NULL == addr ? "unknown address family" : strerror (errno));
else
KHLog (" %s: %s -> %s", __func__, ipbuf, hbuf);
#endif
if (0 == realm_for_host (hCtx->krb5_ctx, hbuf, hintrealm, &realm)) {
if (NULL == secondaryMatch.hostname) {
if (0 != set_host_realm_mapping (&secondaryMatch, hbuf, realm)) {
err = memFullErr;
goto fin;
}
KHLog (" %s: secondary match = %s", __func__, secondaryMatch.hostname);
}
if (NULL == primaryMatch.hostname && host_matches_hint(hbuf, realm, hinthost, hintrealm)) {
if (0 != set_host_realm_mapping (&primaryMatch, hbuf, realm)) {
err = memFullErr;
goto fin;
}
KHLog (" %s: primary match = %s", __func__, primaryMatch.hostname);
}
free (realm);
realm = NULL;
}
}
if (NULL == primaryMatch.hostname && NULL != localname &&
0 == realm_for_host (hCtx->krb5_ctx, localname, hintrealm, &realm)) {
if (0 != set_host_realm_mapping (&primaryMatch, localname, realm)) {
err = memFullErr;
goto fin;
}
KHLog (" %s: primary match = %s", __func__, primaryMatch.hostname);
free (realm);
realm = NULL;
}
if (NULL != primaryMatch.hostname)
matchp = &primaryMatch;
else if (NULL != secondaryMatch.hostname)
matchp = &secondaryMatch;
else {
KHLog (" %s: could not find a suitable host/realm mapping", __func__);
err = paramErr;
goto fin;
}
KHLog (" %s: Using host name = %s, realm = %s", __func__, matchp->hostname, matchp->realm);
CFRelease (hCtx->inHostName);
if (NULL == (hCtx->inHostName =
CFStringCreateWithCString (kCFAllocatorDefault, matchp->hostname, kCFStringEncodingASCII)) ||
NULL == (hCtx->realm = CFStringCreateWithCString (kCFAllocatorDefault, matchp->realm, kCFStringEncodingASCII))) {
err = memFullErr;
goto fin;
}
fin:
free (hintname);
free (hinthost);
free (hintrealm);
free (hostname);
free (localname);
free (realm);
if (NULL != res)
freeaddrinfo (res);
free_host_realm_mapping (&primaryMatch);
free_host_realm_mapping (&secondaryMatch);
if (noErr != err) {
free (hCtx->defaultRealm);
if (NULL != hCtx->realm)
CFRelease (hCtx->realm);
if (NULL != hCtx->inAdvertisedPrincipal)
CFRelease (hCtx->inAdvertisedPrincipal);
if (NULL != hCtx->inHostName)
CFRelease (hCtx->inHostName);
if (NULL != hCtx->krb5_ctx)
krb5_free_context (hCtx->krb5_ctx);
free (hCtx);
} else
*outKerberosSession = hCtx;
KHLog ("]]] %s () = %d", __func__, err);
return err;
}
OSStatus KRBCopyRealm(void *inKerberosSession, CFStringRef *outRealm)
{
OSStatus err = noErr;
KRBhelperContext *hCtx = (KRBhelperContext *)inKerberosSession;
if (NULL == hCtx) {
err = paramErr;
goto Done;
}
KHLog ("%s", "[[[ KRBCopyRealm () - required parameters okay");
if (NULL == hCtx->realm) {
err = paramErr;
goto Done;
}
*outRealm = CFRetain (hCtx->realm);
Done:
KHLog ("]]] KRBCopyRealm () = %d", err);
return err;
}
OSStatus KRBCopyKeychainLookupInfo (void *inKerberosSession, CFStringRef inUsername, CFDictionaryRef *outKeychainLookupInfo)
{
OSStatus err = noErr;
KRBhelperContext *hCtx = (KRBhelperContext *)inKerberosSession;
CFMutableDictionaryRef outInfo = NULL;
CFStringRef accountName = NULL;
if (NULL == inKerberosSession || NULL == outKeychainLookupInfo) { err = paramErr; goto Error; }
*outKeychainLookupInfo = NULL;
outInfo = CFDictionaryCreateMutable (kCFAllocatorDefault, 3, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (NULL == outInfo) { err = memFullErr; goto Error; }
KHLog ("%s", "[[[ KRBCopyKeychainLookupInfo () - required parameters okay");
if (NULL != inUsername) {
CFDictionarySetValue (outInfo, kKRBUsernameKey, inUsername);
accountName = hCtx->realm;
} else {
accountName = hCtx->realm;
}
if (NULL != accountName) {
CFDictionarySetValue (outInfo, kKRBKeychainAccountName, accountName);
}
CFDictionarySetValue (outInfo, kKRBDisableSaveToKeychainKey, kCFBooleanFalse);
CFPropertyListRef savePasswordDisabled = CFPreferencesCopyAppValue(CFSTR("SavePasswordDisabled"), kKRBAgentBundleIdentifier);
if (savePasswordDisabled != NULL) {
if (CFGetTypeID(savePasswordDisabled) == CFBooleanGetTypeID() && CFBooleanGetValue(savePasswordDisabled)) {
CFDictionarySetValue (outInfo, kKRBDisableSaveToKeychainKey, kCFBooleanTrue);
KHLog ("%s", " KRBCopyKeychainLookupInfo: DisableSaveToKeychainKey = TRUE");
}
CFRelease(savePasswordDisabled);
} else {
KHLog ("%s", " KRBCopyKeychainLookupInfo: CFPreferencesCopyAppValue == NULL");
}
*outKeychainLookupInfo = outInfo;
Error:
KHLog ("]]] KRBCopyKeychainLookupInfo () = %d", err);
return err;
}
OSStatus KRBCopyServicePrincipal (void *inKerberosSession, CFStringRef inServiceName, CFStringRef *outServicePrincipal)
{
OSStatus err = noErr;
KRBhelperContext *hCtx = (KRBhelperContext *)inKerberosSession;
KLPrincipal svcPrincipal = NULL;
char *advertisedPrincipalString = NULL, *inServiceString = NULL;
char *svcName = NULL, *svcInstance = NULL, *svcRealm = NULL;
char *useName = NULL, *useInstance = NULL, *useRealm = NULL;
char *inHostNameString = NULL, *realmString = NULL;
CFStringRef outString;
if (NULL == hCtx || NULL == outServicePrincipal) { err = paramErr; goto Done; }
*outServicePrincipal = NULL;
KHLog ("%s", "[[[ KRBCopyServicePrincipal () - required parameters okay");
if (NULL != inServiceName) {
__KRBCreateUTF8StringFromCFString (inServiceName, &inServiceString);
}
if (NULL != hCtx->realm) {
__KRBCreateUTF8StringFromCFString (hCtx->realm, &realmString);
}
#if DEBUG
if (NULL != inServiceString) KHLog(" KRBCopyServicePrincipal: inServiceNameString=%s", inServiceString);
if (NULL != realmString) KHLog(" KRBCopyServicePrincipal: realmString=%s", realmString);
#endif
if (NULL != hCtx->inAdvertisedPrincipal) {
__KRBCreateUTF8StringFromCFString (hCtx->inAdvertisedPrincipal, &advertisedPrincipalString);
#if DEBUG
if (NULL != advertisedPrincipalString) KHLog(" KRBCopyServicePrincipal: advertisedPrincipalString=%s", advertisedPrincipalString);
#endif
KLCreatePrincipalFromString (advertisedPrincipalString, kerberosVersion_V5, &svcPrincipal);
if (KLGetTripletFromPrincipal(svcPrincipal, &svcName, &svcInstance, &svcRealm) == klNoErr) {
useName = svcName;
if (inServiceString) { useName = inServiceString; }
if (NULL != inServiceString && 0 != strcmp (svcName, inServiceString)) {
KHLog (" KRBCopyServicePrincipal: svcName mismatch inService = \"%s\", svcName = \"%s\"", inServiceString, svcName);
}
KHLog (" KRBCopyServicePrincipal: useName = \"%s\"", useName);
if (0 == strncmp (svcRealm, "LKDC:", 5)) {
if (0 != strcmp (svcRealm, realmString)) {
}
KHLog (" KRBCopyServicePrincipal: realm is Local KDC.", NULL);
useRealm = useInstance = realmString;
} else {
useRealm = svcRealm;
if (NULL != hCtx->inHostName) {
__KRBCreateUTF8StringFromCFString (hCtx->inHostName, &inHostNameString);
#if DEBUG
KHLog (" KRBCopyServicePrincipal: inHostNameString = %s", inHostNameString);
#endif
} else {
KHLog (" KRBCopyServicePrincipal: Bad inHostName, using svcInstance = \"%s\"", svcInstance);
inHostNameString = strdup (svcInstance);
}
useInstance = inHostNameString;
}
KHLog (" KRBCopyServicePrincipal: useInstance = \"%s\"", useInstance);
}
if (svcPrincipal) { KLDisposePrincipal (svcPrincipal); }
} else {
if (realmString) {
useRealm = realmString;
useName = inServiceString;
if (0 == strncmp (useRealm, "LKDC:", 5)) {
useInstance = useRealm;
} else {
if (NULL != hCtx->inHostName) {
__KRBCreateUTF8StringFromCFString (hCtx->inHostName, &inHostNameString);
useInstance = inHostNameString;
} else {
KHLog ("%s", " KRBCopyServicePrincipal: Fatal - Bad inHostName & no inAdvertisedPrincipal");
useInstance = NULL;
}
}
}
}
if (NULL != useName && NULL != useInstance && NULL != useRealm) {
outString = CFStringCreateWithFormat (NULL, NULL, CFSTR("%s/%s@%s"), useName, useInstance, useRealm);
hCtx->useName = strdup (useName);
hCtx->useInstance = strdup (useInstance);
hCtx->useRealm = strdup (useRealm);
KHLog (" KRBCopyServicePrincipal: principal = \"%s/%s@%s\"", useName, useInstance, useRealm);
} else {
err = paramErr;
}
*outServicePrincipal = outString;
if (svcInstance) { KLDisposeString (svcInstance); }
if (svcRealm) { KLDisposeString (svcRealm); }
if (svcName) { KLDisposeString (svcName); }
if (advertisedPrincipalString) { __KRBReleaseUTF8String (advertisedPrincipalString); }
if (inHostNameString) { __KRBReleaseUTF8String (inHostNameString); }
if (inServiceString) { __KRBReleaseUTF8String (inServiceString); }
if (realmString) { __KRBReleaseUTF8String (realmString); }
Done:
KHLog ("]]] KRBCopyServicePrincipal () = %d", err);
return err;
}
OSStatus KRBCopyClientPrincipalInfo (void *inKerberosSession, CFDictionaryRef inOptions, CFDictionaryRef *outClientPrincipalInfo)
{
OSStatus err = noErr;
KRBhelperContext *hCtx = (KRBhelperContext *)inKerberosSession;
CFIndex newCount;
CFMutableDictionaryRef outInfo = NULL;
CFStringRef clientPrincipal = NULL;
char *clientPrincipalString = NULL;
char *useRealm = NULL;
CFStringRef useClientName = NULL;
CFStringRef inferredLabel = NULL;
SecCertificateRef certRef = NULL;
CFStringRef certificateHash = NULL;
char *cert_hash = NULL;
int usingCertificate = 0;
int clientNameProvided = 0;
if (NULL == hCtx || NULL == outClientPrincipalInfo) { err = paramErr; goto Error; }
*outClientPrincipalInfo = NULL;
KHLog ("%s", "[[[ KRBCopyClientPrincipalInfo () - required parameters okay");
if (NULL != inOptions) {
newCount = CFDictionaryGetCount (inOptions) + 3;
outInfo = CFDictionaryCreateMutableCopy (kCFAllocatorDefault, newCount, inOptions);
CFDictionaryGetValueIfPresent (inOptions, kKRBCertificateKey, (const void **)&certRef);
if (NULL != certRef) {
KHLog ("%s", " KRBCopyClientPrincipalInfo: Certificate information in dictionary");
} else {
KHLog ("%s", " KRBCopyClientPrincipalInfo: Certificate not present in dictionary");
}
} else {
outInfo = CFDictionaryCreateMutable (kCFAllocatorDefault, 3, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
if (NULL != certRef && SecCertificateGetTypeID() == CFGetTypeID (certRef)) {
CSSM_DATA certData;
krb5_data kcert;
CFStringRef description = NULL;
err = SecCertificateGetData(certRef, &certData);
if (0 != err) { goto Error; }
kcert.magic = 0;
kcert.length = certData.Length;
kcert.data = (char *)certData.Data;
cert_hash = krb5_pkinit_cert_hash_str(&kcert);
if (NULL == cert_hash) { goto Error; }
useClientName = CFStringCreateWithCString (NULL, cert_hash, kCFStringEncodingASCII);
usingCertificate = 1;
if (NULL != useClientName) {
certificateHash = CFRetain (useClientName);
}
SecCertificateCopySubjectComponent (certRef, &CSSMOID_Description, &description);
if (NULL != description && kCFCompareEqualTo == CFStringCompare(description, CFSTR (".Mac Sharing Certificate"), 0)) {
CFStringRef commonName = NULL, organizationalUnit = NULL;
SecCertificateCopyCommonName (certRef, &commonName);
SecCertificateCopySubjectComponent (certRef, &CSSMOID_OrganizationalUnitName, &organizationalUnit);
if (NULL != commonName && NULL != organizationalUnit) {
inferredLabel = CFStringCreateWithFormat (NULL, NULL, CFSTR("%@@%@"), commonName, organizationalUnit);
}
if (NULL != commonName) { CFRelease (commonName); }
if (NULL != organizationalUnit) { CFRelease (organizationalUnit); }
} else {
err = SecCertificateInferLabel (certRef, &inferredLabel);
}
if (NULL != description) { CFRelease (description); }
if (0 != err) { goto Error; }
} else if (NULL != inOptions) {
CFDictionaryGetValueIfPresent (inOptions, kKRBUsernameKey, (const void **)&useClientName);
if (NULL != useClientName) {
CFRetain(useClientName);
clientNameProvided = 1;
}
}
if (NULL == useClientName) {
char *clientName = getlogin ();
if (NULL != clientName) {
useClientName = CFStringCreateWithCString (NULL, clientName, kCFStringEncodingUTF8);
KHLog (" KRBCopyClientPrincipalInfo: Using login name = \"%s\"", clientName);
}
}
if (NULL != hCtx->useRealm && NULL != hCtx->defaultRealm) {
size_t useRealmLength = strlen (hCtx->useRealm);
size_t defaultRealmLength = strlen (hCtx->defaultRealm);
if (defaultRealmLength < useRealmLength) {
char *subRealm = hCtx->useRealm + (useRealmLength - defaultRealmLength);
if (0 == strcmp (subRealm, hCtx->defaultRealm)) {
useRealm = hCtx->defaultRealm;
}
}
}
if (NULL != useRealm) {
clientPrincipal = CFStringCreateWithFormat (NULL, NULL, CFSTR("%@@%s"), useClientName, useRealm);
} else {
clientPrincipal = CFStringCreateWithFormat (NULL, NULL, CFSTR("%@@%@"), useClientName, hCtx->realm);
}
if (NULL != clientPrincipal) {
__KRBCreateUTF8StringFromCFString (clientPrincipal, &clientPrincipalString);
KHLog (" KRBCopyClientPrincipalInfo: principal guess = \"%s\"", clientPrincipalString);
}
if (NULL != clientPrincipalString) {
krb5_context kcontext;
krb5_error_code krb_err = 0;
cc_context_t cc_context = NULL;
cc_ccache_iterator_t iterator = NULL;
const char *alternateRealm = NULL;
const char *clientRealm = principal_realm(clientPrincipalString);
char *alternateClientPrincipal = NULL;
char *bestClientPrincipal = NULL;
int commonSubrealms = 1, tmp;
krb_err = k5_ok (krb5_init_context(&kcontext));
if (!krb_err) {
krb_err = k5_ok (cc_initialize (&cc_context, ccapi_version_4, NULL, NULL));
}
if (!krb_err) {
krb_err = k5_ok (cc_context_new_ccache_iterator (cc_context, &iterator));
}
while (!krb_err) {
cc_ccache_t cc_ccache = NULL;
cc_string_t ccacheName = NULL;
krb5_ccache ccache = NULL;
krb5_principal ccachePrinc = NULL;
krb_err = k5_ok (cc_ccache_iterator_next (iterator, &cc_ccache));
if (!krb_err) { krb_err = k5_ok (cc_ccache_get_name (cc_ccache, &ccacheName)); }
if (!krb_err) { krb_err = k5_ok (krb5_cc_resolve (kcontext, ccacheName->data, &ccache)); }
if (!krb_err) { krb_err = k5_ok (krb5_cc_get_principal (kcontext, ccache, &ccachePrinc)); }
if (!krb_err) { krb_err = k5_ok (krb5_unparse_name (kcontext, ccachePrinc, &alternateClientPrincipal)); }
if (!krb_err) { alternateRealm = principal_realm(alternateClientPrincipal); }
if (!krb_err && (0 == (tmp = strcmp(clientRealm, alternateRealm)) ||
commonSubrealms < (tmp = has_common_subrealm(clientRealm, alternateRealm)))) {
if (NULL != bestClientPrincipal) { free(bestClientPrincipal); }
bestClientPrincipal = strdup(alternateClientPrincipal);
if (0 == tmp) {
break;
} else {
commonSubrealms = tmp;
}
}
if (NULL != alternateClientPrincipal) {
krb5_free_unparsed_name(kcontext, alternateClientPrincipal);
alternateClientPrincipal = NULL;
}
if (NULL != ccache) { krb5_cc_close (kcontext, ccache); }
if (NULL != ccacheName) { cc_string_release (ccacheName); }
if (NULL != cc_ccache) { cc_ccache_release (cc_ccache); }
if (NULL != ccachePrinc) { krb5_free_principal (kcontext, ccachePrinc); }
}
if (NULL != iterator) { cc_ccache_iterator_release (iterator); }
if (NULL != cc_context) { cc_context_release (cc_context); }
if (NULL != bestClientPrincipal) {
KHLog (" KRBCopyClientPrincipalInfo: ccache principal match = \"%s\"", bestClientPrincipal);
}
if (NULL != bestClientPrincipal && !clientNameProvided &&
0 != strcmp(clientPrincipalString, bestClientPrincipal)) {
char *useClientNameString = NULL, *startOfRealm;
KHLog ("%s", " KRBCopyClientPrincipalInfo: found a single ticket for realm, replacing principal & username");
usingCertificate = 0;
if (NULL != clientPrincipal) { CFRelease (clientPrincipal); }
if (NULL != clientPrincipalString) { __KRBReleaseUTF8String (clientPrincipalString); }
clientPrincipal = CFStringCreateWithCString (NULL, bestClientPrincipal, kCFStringEncodingASCII);
clientPrincipalString = strdup (bestClientPrincipal);
useClientNameString = strdup (bestClientPrincipal);
do {
startOfRealm = strrchr (&useClientNameString[1], '@');
if (NULL != startOfRealm) {
if ('\\' == *(startOfRealm - 1)) {
*(startOfRealm - 1) = '\0';
} else {
*startOfRealm = '\0';
break;
}
}
} while (NULL != startOfRealm);
if (NULL != useClientName) { CFRelease (useClientName); }
useClientName = CFStringCreateWithCString (NULL, useClientNameString, kCFStringEncodingUTF8);
KHLog (" KRBCopyClientPrincipalInfo: Setting found Username to = \"%s\"", useClientNameString);
if (NULL != useClientNameString) { free (useClientNameString); }
}
if (NULL != bestClientPrincipal) { free (bestClientPrincipal); }
if (NULL != alternateClientPrincipal) { krb5_free_unparsed_name (kcontext, alternateClientPrincipal); }
if (NULL != kcontext) { krb5_free_context (kcontext); }
}
KHLog (" KRBCopyClientPrincipalInfo: using principal = \"%s\"", clientPrincipalString);
CFDictionarySetValue (outInfo, kKRBClientPrincipalKey, clientPrincipal);
CFDictionarySetValue (outInfo, kKRBUsernameKey, useClientName);
KHLog (" KRBCopyClientPrincipalInfo: usingCertificate == %d", usingCertificate);
if (usingCertificate && NULL != certRef) {
CFDictionarySetValue (outInfo, kKRBUsingCertificateKey, certRef);
if (NULL != certificateHash)
CFDictionarySetValue (outInfo, kKRBCertificateHashKey, certificateHash);
if (NULL != inferredLabel) {
CFDictionarySetValue (outInfo, kKRBCertificateInferredLabelKey, inferredLabel);
char *inferredLabelString = NULL;
__KRBCreateUTF8StringFromCFString (inferredLabel, &inferredLabelString);
KHLog (" KRBCopyClientPrincipalInfo: InferredLabel = \"%s\"", inferredLabelString);
if (NULL != inferredLabelString) { __KRBReleaseUTF8String (inferredLabelString); }
}
}
*outClientPrincipalInfo = outInfo;
Error:
if (NULL != useClientName) { CFRelease (useClientName); }
if (NULL != certificateHash) { CFRelease (certificateHash); }
if (NULL != inferredLabel) { CFRelease (inferredLabel); }
if (NULL != cert_hash) { free (cert_hash); }
if (NULL != clientPrincipal) { CFRelease (clientPrincipal); }
if (NULL != clientPrincipalString) { __KRBReleaseUTF8String (clientPrincipalString); }
KHLog ("]]] KRBCopyClientPrincipalInfo () = %d", err);
return err;
}
OSStatus KRBTestForExistingTicket (void *inKerberosSession, CFDictionaryRef inClientPrincipalInfo)
{
OSStatus err = noErr;
KRBhelperContext *hCtx = (KRBhelperContext *)inKerberosSession;
CFStringRef clientPrincipal = NULL;
char *principalString = NULL;
if (NULL == hCtx || NULL == inClientPrincipalInfo) { err = paramErr; goto Done; }
KHLog ("%s", "[[[ KRBTestForExistingTicket () - required parameters okay");
CFDictionaryGetValueIfPresent (inClientPrincipalInfo, kKRBClientPrincipalKey, (const void **)&clientPrincipal);
if (NULL != clientPrincipal) {
KLStatus krb_err = klNoErr;
KLLoginOptions loginOptions = NULL;
KLPrincipal klPrincipal = NULL;
KLBoolean outFoundValidTickets = FALSE;
KLPrincipal outPrincipal = NULL;
char *ccacheName = NULL;
__KRBCreateUTF8StringFromCFString (clientPrincipal, &principalString);
KHLog (" KRBTestForExistingTicket: principal = \"%s\"", principalString);
krb_err = KLCreateLoginOptions (&loginOptions);
KLCreatePrincipalFromString (principalString, kerberosVersion_V5, &klPrincipal);
krb_err = KLCacheHasValidTickets (klPrincipal, kerberosVersion_V5, &outFoundValidTickets, &outPrincipal, &ccacheName);
if (TRUE == outFoundValidTickets) {
KHLog (" KRBTestForExistingTicket: Valid Ticket, ccacheName = \"%s\"", ccacheName);
err = 0;
} else {
err = krb_err;
}
if (NULL != loginOptions) { KLDisposeLoginOptions (loginOptions); }
if (NULL != principalString) { __KRBReleaseUTF8String (principalString); }
if (NULL != klPrincipal) { KLDisposePrincipal (klPrincipal); }
if (NULL != outPrincipal) { KLDisposePrincipal (outPrincipal); }
if (NULL != ccacheName) { free (ccacheName); }
}
Done:
KHLog ("]]] KRBTestForExistingTicket () = %d", err);
return err;
}
OSStatus KRBAcquireTicket(void *inKerberosSession, CFDictionaryRef inClientPrincipalInfo)
{
OSStatus err = noErr;
KRBhelperContext *hCtx = (KRBhelperContext *)inKerberosSession;
KLStatus krb_err = klNoErr;
KLLoginOptions loginOptions = NULL;
KLPrincipal clientPrincipal = NULL;
char *ccacheName = NULL;
CFStringRef principal = NULL, password = NULL;
char *principalString = NULL, *passwordString = NULL;
SecCertificateRef usingCertificate = NULL;
if (NULL == hCtx) { err = paramErr; goto Error; }
KHLog ("%s", "[[[ KRBAcquireTicket () - required parameters okay");
krb_err = KLCreateLoginOptions (&loginOptions);
principal = CFDictionaryGetValue (inClientPrincipalInfo, kKRBClientPrincipalKey);
if (NULL == principal) { err = paramErr; goto Error; }
__KRBCreateUTF8StringFromCFString (principal, &principalString);
KLCreatePrincipalFromString (principalString, kerberosVersion_V5, &clientPrincipal);
CFDictionaryGetValueIfPresent (inClientPrincipalInfo, kKRBUsingCertificateKey, (const void **)&usingCertificate);
if (NULL != usingCertificate) {
krb_err = k5_ok(krb5_pkinit_set_client_cert(principalString, (krb5_pkinit_cert_t)usingCertificate));
if (0 == krb_err) {
KHLog ("%s", " KRBAcquireTicket: Using a certificate");
passwordString = strdup (" ");
} else
usingCertificate = NULL;
}
if (NULL == usingCertificate) {
password = CFDictionaryGetValue (inClientPrincipalInfo, kKRBClientPasswordKey);
if (NULL == password) { err = paramErr; goto Error; }
__KRBCreateUTF8StringFromCFString (password, &passwordString);
}
krb_err = KLAcquireInitialTicketsWithPassword (clientPrincipal, loginOptions, passwordString, &ccacheName);
err = krb_err;
Error:
if (NULL != loginOptions) { KLDisposeLoginOptions (loginOptions); }
if (NULL != principalString) { __KRBReleaseUTF8String (principalString); }
if (NULL != passwordString) { __KRBReleaseUTF8String (passwordString); }
if (NULL != clientPrincipal) { KLDisposePrincipal (clientPrincipal); }
if (NULL != ccacheName) { free (ccacheName); }
KHLog ("]]] KRBAcquireTicket () = %d", err);
return err;
}
OSStatus KRBCloseSession(void *inKerberosSession)
{
OSStatus err = noErr;
KRBhelperContext *hCtx = (KRBhelperContext *)inKerberosSession;
if (NULL == hCtx) { err = paramErr; goto Error; }
KHLog ("%s", "[[[ KRBCloseSession () - required parameters okay");
if (NULL != hCtx->inAdvertisedPrincipal) { CFRelease (hCtx->inAdvertisedPrincipal); }
if (NULL != hCtx->realm) { CFRelease (hCtx->realm); }
if (NULL != hCtx->useName) { free (hCtx->useName); }
if (NULL != hCtx->useInstance) { free (hCtx->useInstance); }
if (NULL != hCtx->useRealm) { free (hCtx->useRealm); }
free(hCtx);
Error:
KHLog ("]]] KRBCloseSession () = %d", err);
return err;
}