LKDC
The local KDC in Leopard is pretty rad. In the absence of any real documentation, this page exists as a jumble of observations and theories.
Contents
Positioning and Use in Leoaprd
The Local KDC (LKDC) is a Kerberos implementation that extends "single sign-on" capabilities into ad-hoc networks. The LKDC supports the AFP, CIFS, and VNC services included in Mac OS X and Mac OS X Server. At the surface, the LKDC looks pretty much just like a regular Kerberos setup... you can log into one of the above named services and get Kerberos tickets, use standard tools like klist to manage tickets, use kadmin to administer the KDC, etc, etc. Some examples:
{3} andre@donk [~] % sudo klist -k
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
3 afpserver/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
3 afpserver/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
3 afpserver/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
3 cifs/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
3 cifs/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
3 cifs/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
3 vnc/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
3 vnc/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
3 vnc/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
{10} andre@donk [~] % sudo kadmin.local -q listprincs
Authenticating as principal andre/admin@LKDC:SHA1.C81B8D1A890D4D4DD079059A54594AA53B9A1A2B with password.
K/M@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
afpserver/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
andre@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
cifs/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
kadmin/admin@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
kadmin/changepw@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
kadmin/donk.local@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
kadmin/history@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
krbtgt/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
vnc/LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
{13} andre@donk [~] % sudo kadmin.local -q 'get_principal andre'
Authenticating as principal andre/admin@LKDC:SHA1.C81B8D1A890D4D4DD079059A54594AA53B9A1A2B with password.
Principal: andre@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
Expiration date: [never]
Last password change: Sat May 10 21:43:57 PDT 2008
Password expiration date: [none]
Maximum ticket life: 0 days 10:00:00
Maximum renewable life: 7 days 00:00:00
Last modified: Sat May 10 21:43:57 PDT 2008 (root/admin@LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD)
Last successful authentication: [never]
Last failed authentication: [never]
Failed password attempts: 0
Number of keys: 4
Key: vno 1, Triple DES cbc mode with HMAC/sha1, no salt
Key: vno 1, ArcFour with HMAC/md5, no salt
Key: vno 1, DES cbc mode with CRC-32, no salt
Key: vno 1, DES cbc mode with CRC-32, Version 4
Attributes: REQUIRES_PRE_AUTH
Policy: [none]
{106} andre@donk [~] % klist
Kerberos 5 ticket cache: 'API:Initial default ccache'
Default principal: andre@LKDC:SHA1.C81B8D1A890D4D4DD079059A54594AA53B9A1A2B
Valid Starting Expires Service Principal
05/13/08 00:35:00 05/13/08 10:34:58 krbtgt/LKDC:SHA1.C81B8D1A890D4D4DD079059A54594AA53B9A1A2B@LKDC:SHA1.C81B8D1A890D4D4DD079059A54594AA53B9A1A2B
renew until 05/20/08 00:34:58
05/13/08 00:35:00 05/13/08 10:34:58 vnc/LKDC:SHA1.C81B8D1A890D4D4DD079059A54594AA53B9A1A2B@LKDC:SHA1.C81B8D1A890D4D4DD079059A54594AA53B9A1A2B
renew until 05/20/08 00:34:58
The crux of the LKDC implementation is the use of a SHA1 hash in place of the server name portion of a kerberos principal. This effectively insulates Kerberos from dynamic network conditions, but also adds the requirement of using a discovery protocol to learn the SHA1 hash of a remote host. This significant re-definition of a kerberos principal is really cool, but also not standard. The standard kerberos libraries pretty much assume and require that the server name portion of a kerberos principal be just that: a server name (or IP address). Accordingly, there is some glue provided by apple that handles the advertisement and discovery of LKDC data, and that harnesses the kerberos authentication for the supported service clients. This glue is provided in the form of a private framework called KerberosHelper, a Kerberos framework plugin called LKDCLocate, and a... 'core service' called NetAuthAgent.
KerberosHelper
The discovery protocol used to advertise and discovery LKDC info is multicast DNS, and examples of performing lookups to obtain the SHA1 hash of a remote LKDC given the machine name are shown below.
Discovery
The LKDC realm name of a remote Mac OS X machine may be discovered as follows:
dns-sd -Q "_kerberos.donk.local" txt
Replace "donk" with the bonjour name of a Leopard machine on your local network (can test on yourself if needed).
The result is a string of hex characters. This command does not terminate, so control-C to stop it. Now take the hex and run it through xxd -r -c 256.
xxd -r -c 256
<paste in the hex string, press return>
The result is the LKDC realm name, such as LKDC:SHA1.8B0FBACC08E3152A473A68D303E297A13CAA3AFD
Interesting Files
NetAuthAgent
This appears to be a service client helper that manages the authentication process at a high level on behalf of various clients included with Mac OS X. A string dump reveals the following chunk of kerberos service names:
vncserver webdaveserver ftpsserver ftpserver cifs afpserver
vncserver, cifs, and afpserver are the only 3 services that are kerberized in the LKDC by default, though NetAuthAgent appears to support others as well. Looking at all strings matching 'kerb', we see:
{5} andre@donk [~] % strings /System/Library/CoreServices/NetAuthAgent.app/Contents/MacOS/NetAuthAgent | grep -i kerb
KerberosSession
BypassKerberos
kerberosClientPrincipalCredentials
kerberosRelease
kerberosClientPrincipal
kerberosKeychainRealm
kerberosPrincipalInfo
MountedByKerberos
SupportsKerberos
/System/Library/CoreServices/Kerberos.app
mInvalidKerberosUserName
checkForKerberosUserName:
isValidKerberosUserName:
useKerberos
kerberosServiceName
kerberosServicePrincipalHint
kerberosSession
kerberosServicePrincipal
kerberosAcquireTicket
Kerberos
AllowKerberosUI
KerberosInfo
kerberosUIOption
kerberosHostDisplayName
kerberosHostAddress
kerberosAlreadyHasTicket
NetAuthAgent.app also includes NetAuthSysAgent, which appears to be a superset of NetAuthAgent. It contains lots of filesystem semantics, and also appears to deal with certificates.
{14} andre@donk [~] % otool -L /System/Library/CoreServices/NetAuthAgent.app/Contents/MacOS/NetAuthAgent | cut -d ' ' -f1
/System/Library/CoreServices/NetAuthAgent.app/Contents/MacOS/NetAuthAgent:
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa
/System/Library/Frameworks/Security.framework/Versions/A/Security
/System/Library/PrivateFrameworks/URLMount.framework/Versions/A/URLMount
/System/Library/Frameworks/SecurityInterface.framework/Versions/A/SecurityInterface
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices
/System/Library/PrivateFrameworks/KerberosHelper.framework/Versions/A/KerberosHelper
/usr/lib/libgcc_s.1.dylib
/usr/lib/libSystem.B.dylib
/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices
/usr/lib/libobjc.A.dylib
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
LKDCLocate
This (non-private?) Kerberos framework plugin looks like it's able to perform DNS queries to retrieve LKDC info. It also seems to interact with a mach service created by LKDCHelper called com.apple.KerberosHelper.LKDCHelper.
{14} andre@donk [~] % strings /System/Library/KerberosPlugins/KerberosFrameworkPlugins/LKDCLocate.bundle/Contents/MacOS/LKDCLocate
%s:
LKDCLookup
Declined to handle address family %d
svc = %d, realm = %s, family= %d, socktype = %d
KDC|MasterKDC
LKDC:
getaddrinfo () == %d
0x%08p: family = %d, socktype = %d, protocol = %d
Running callback 0x%08p
Unexpected address family %d
Callback done 0x%08p, err=%d
inet_ntop failed: %s
addr = %s, port = %d
failed %d
LKDCGetHelperPort
com.apple.KerberosHelper.LKDCHelper
%s: cannot contact helper
LKDCHelperExit
Mach communication failed: %s
LKDCDumpStatus
LKDCSetLogLevel
LKDCGetLocalRealm
[[[ %s
Local realm = %s
]]] %s = %d (%s)
LKDCDiscoverRealm
No place to store discovered realm.
realm = %s
LKDCFindKDCForRealm
No place to store discovered KDC hostname.
KDC Hostname = %s:%u
Communication to the helper failed
Not authorized
Input parameter error
Serializing object failed
Unserializing object failed
Object passed is not a dictionary
A Local KDC was not found
Lookup of the KDC for the requested realm failed
%s:
Success
<unknown error>
[...]
KerberosHelper
This private framework appears to provide API that can be used on behalf of service clients (or their agents) to discover the LKDC information of a remote realm.
{6} root@donk [~] # strings /System/Library/PrivateFrameworks/KerberosHelper.framework/Versions/A/KerberosHelper
LKDCGetHelperPort
com.apple.KerberosHelper.LKDCHelper
%s: cannot contact helper
LKDCHelperExit
Mach communication failed: %s
LKDCDumpStatus
LKDCSetLogLevel
LKDCGetLocalRealm
[[[ %s
Local realm = %s
]]] %s = %d (%s)
LKDCDiscoverRealm
No place to store discovered realm.
realm = %s
LKDCFindKDCForRealm
No place to store discovered KDC hostname.
KDC Hostname = %s:%u
LKDC:
%s: krb5 call got %d (%s) on %s:%d
realm_for_host
(null)
[[[ %s: hostname=%s hintrealm=%s
/SourceCache/KerberosHelper/KerberosHelper-31/Source/KerberosHelper.c
%s: krb5_get_host_realm success
%s: krb5_get_host_realm returned unusable realm!
%s: LKDCDiscoverRealm success
]]] %s: returning realm=%s
]]] %s: failed to determine realm
[[[ KRBCopyRealm () - required parameters okay
]]] KRBCopyRealm () = %d
[[[ KRBCopyKeychainLookupInfo () - required parameters okay
Username
KeychainAccountName
DisableSaveToKeychain
edu.mit.Kerberos.KerberosAgent
SavePasswordDisabled
KRBCopyKeychainLookupInfo: DisableSaveToKeychainKey = TRUE
KRBCopyKeychainLookupInfo: CFPreferencesCopyAppValue == NULL
]]] KRBCopyKeychainLookupInfo () = %d
[[[ KRBCopyServicePrincipal () - required parameters okay
KRBCopyServicePrincipal: svcName mismatch inService = "%s", svcName = "%s"
KRBCopyServicePrincipal: useName = "%s"
LKDC:
KRBCopyServicePrincipal: realm is Local KDC.
KRBCopyServicePrincipal: Bad inHostName, using svcInstance = "%s"
KRBCopyServicePrincipal: useInstance = "%s"
KRBCopyServicePrincipal: Fatal - Bad inHostName & no inAdvertisedPrincipal
%s/%s@%s
KRBCopyServicePrincipal: principal = "%s/%s@%s"
]]] KRBCopyServicePrincipal () = %d
KRBCopyClientPrincipalInfo
[[[ KRBCopyClientPrincipalInfo () - required parameters okay
Certificate
KRBCopyClientPrincipalInfo: Certificate information in dictionary
KRBCopyClientPrincipalInfo: Certificate not present in dictionary
.Mac Sharing Certificate
%@@%@
KRBCopyClientPrincipalInfo: Using login name = "%s"
KRBCopyClientPrincipalInfo: principal guess = "%s"
KRBCopyClientPrincipalInfo: ccache principal match = "%s"
KRBCopyClientPrincipalInfo: found a single ticket for realm, replacing principal & username
KRBCopyClientPrincipalInfo: Setting found Username to = "%s"
KRBCopyClientPrincipalInfo: using principal = "%s"
ClientPrincipal
KRBCopyClientPrincipalInfo: usingCertificate == %d
UsingCertificate
CetificateHash
CertificateInferredLabel
KRBCopyClientPrincipalInfo: InferredLabel = "%s"
]]] KRBCopyClientPrincipalInfo () = %d
%@@%s
[[[ KRBTestForExistingTicket () - required parameters okay
KRBTestForExistingTicket: principal = "%s"
KRBTestForExistingTicket: Valid Ticket, ccacheName = "%s"
]]] KRBTestForExistingTicket () = %d
KRBAcquireTicket
[[[ KRBAcquireTicket () - required parameters okay
KRBAcquireTicket: Using a certificate
Password
]]] KRBAcquireTicket () = %d
[[[ KRBCloseSession () - required parameters okay
]]] KRBCloseSession () = %d
parse_principal_name
KRBCreateSession
[[[ %s () - required parameters okay
%s: LocalKDC realm lookup only
%s: __KRBCreateUTF8StringFromCFString failed
[[[ %s () decomposing %s
]]] %s () - %d
%s: processed host name = %s
%s.local
%s: last char of host name = 0x%02x
success
%s: getaddrinfo = %s (%d)
%s: canonical host name = %s
%s: secondary match = %s
%s: primary match = %s
%s: could not find a suitable host/realm mapping
%s: Using host name = %s, realm = %s
]]] %s () = %d
KerberosKDC
dsRecTypeStandard:Config
realname
Communication to the helper failed
Not authorized
Input parameter error
Serializing object failed
Unserializing object failed
Object passed is not a dictionary
A Local KDC was not found
Lookup of the KDC for the requested realm failed
%s:
Success
<unknown error>
LKDCHelper
This executable is part of the KerberosHelper framework and is a launch agent that runs in the user's namespace. This appears to fire up a mach service called com.apple.KerberosHelper.LKDCHelper that is used for IPC with other interested parties (see: elsewhere on this page).
{34} andre@donk [~] % strings /System/Library/PrivateFrameworks/KerberosHelper.framework/Versions/A/Resources/LKDCHelper
Idle exit
do_LKDCDumpStatus
[[[ %s
do_LKDCSetLogLevel
do_LKDCGetLocalRealm
Cached lookup
LocalKDCRealm = %s
do_LKDCDiscoverRealm
Looking up realm for %s
do_LKDCFindKDCForRealm
Looking up host for %s
%s:
Unauthorized access by euid=%lu pid=%lu
update_idle_timer
0 == gettimeofday(&last_message, NULL)
/SourceCache/KerberosHelper/KerberosHelper-31/Source/LKDCHelper-main.c
idletimer_main
0 == gettimeofday(&now, NULL)
Invalid idle timeout: %s
Usage: [-d] [-t maxidle]
Could not initialize ASL logging.
Starting (uid=%ul)
mach_port_allocate: %s
mach_port_insert_right: %s
com.apple.KerberosHelper.LKDCHelper
bootstrap_register2 failed: %s
CheckIn
Could not create checkin message for launchd.
Could not message launchd.
Launchd checkin failed: %s.
MachServices
Launchd reply does not contain %s dictionary.
Launchd reply does not contain %s Mach port.
Launchd gave me a null Mach port.
Failed to start idletimer thread: %s
mach_msg_server: %s
KerberosKDC
dsRecTypeStandard:Config
realname
_kerberos
LookupRealmCallBack
mDNSError = %d
More than one record, last one wins!!!
LKDCAddLocatorDetails
New entry for (realm=%s host=%s)
Replacing existing entry (realm=%s host=%s) with (realm=%s host=%s)
]]] %s = %d (%s)
LKDCHostnameForRealm
Cache hit
Cache miss
HandleEvents
LKDCLookupRealm
LKDCRealmForHostname
%s.%s
mDNSResult
CallbackError = %d
Timeout!
LKDCDumpCacheStatus
Cache root node = %08p
node = %08p {
realmName = (%08p) %s
serviceHost = (%08p) %s
servicePort = %u
TTL = %u
}
Communication to the helper failed
Not authorized
Input parameter error
Serializing object failed
Unserializing object failed
Object passed is not a dictionary
A Local KDC was not found
Lookup of the KDC for the requested realm failed
%s:
Success
<unknown error>
- /usr/libexec/configureLocalKDC - this is a perl script that creates the LKDC at installation time, including creating service principals, editing service config files, and installing a kerberos certificate into the system keychain.
Resources
- http://www.felipe-alfaro.org/blog/2007/12/07/kerberizing-leopards-ssh/ This documents how to kerberize ssh / sshd, in Leopard, but uses a static configuration instead of dynamically discovering the remote realm name, as is done by the services kerberized in the LKDC by default.
- http://www.mactech.com/articles/mactech/Vol.23/23.11/ExploringLeopardwithDTrace/index.html Exploring Leopard with DTrace - was useful in determining how some of the service clients interact with Kerberos.
- http://developer.apple.com/technotes/tn2005/tn2083.html Daemons and Agents. Fantastic technote by Quinn. This helped me understand a bit more about the mach service that is provided by LKDCHelper.
- http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/129443 Multicast DNS libraries for Ruby
- http://www.ldap.com/1/commentary/wahl/20070511_01.shtml Discovering local identity services. Outlines several methods for dynamic discovery of how to bootstrap authentication sessions with a remote system.
- http://www.cybersafe.ltd.uk/docs_standards/draft-ietf-krb-wg-krb-dns-locate-03.txt This expired draft documents how kerberos realm information might be discovered using multicast dns.
- http://www.dns-sd.org/ServiceTypes.html multicast DNS service types
- http://files.multicastdns.org/draft-cheshire-dnsext-multicastdns.txt This expired draft documents multicast DNS in general
- http://web.mit.edu/kerberos/www/krb5-1.2/krb5-1.2.6/doc/install.html#SEC9 Kerberos install document; this section documents how Kerberos finds realm information.