1/* 2 * Copyright (c) 2006-2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25#include <mach/mach.h> 26#include <dns_sd.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <unistd.h> 30#include <string.h> 31#include <errno.h> 32 33#include "LKDCHelper.h" 34#include "LKDCHelper-main.h" 35#include "LKDCHelper-lookup.h" 36 37typedef struct _lookupContext { 38 LKDCLocator *realm; 39 volatile int terminateLoop; 40 volatile DNSServiceErrorType errorCode; 41} lookupContext; 42 43// Lookup timeout for LKDC. The following constant is certain to be "wrong" 44#define LKDC_DNS_TIMEOUT 11 45 46static LKDCLocator localRealm = { 47 .next = NULL, 48 .realmName = NULL, 49 .serviceHost = "localhost", 50 .servicePort = 88, 51 .ttl = 0, 52 .absoluteTTL = 0 53}; 54 55 56static LKDCHelperErrorType HandleEvents(DNSServiceRef serviceRef, lookupContext *context) 57{ 58 LKDCHelperErrorType err = kLKDCHelperSuccess; 59 int dns_sd_fd, nfds, result; 60 fd_set readfds; 61 struct timeval tv; 62 63 LKDCLogEnter (); 64 65 dns_sd_fd = DNSServiceRefSockFD(serviceRef); 66 nfds = dns_sd_fd + 1; 67 context->terminateLoop = 0; 68 context->errorCode = 0; 69 70 while (!context->terminateLoop) { 71 FD_ZERO(&readfds); 72 FD_SET(dns_sd_fd, &readfds); 73 74 tv.tv_sec = LKDC_DNS_TIMEOUT; 75 tv.tv_usec = 0; 76 77 result = select(nfds, &readfds, NULL, NULL, &tv); 78 if (result > 0) { 79 if (FD_ISSET(dns_sd_fd, &readfds)) { 80 LKDCLog ("mDNSResult"); 81 err = DNSServiceProcessResult(serviceRef); 82 if (kDNSServiceErr_NoSuchRecord == context->errorCode) { 83 err = kLKDCHelperRealmNotFound; 84 } 85 } 86 if (err || context->errorCode) { 87 LKDCLog ("mDNSError = %d", err); 88 LKDCLog ("CallbackError = %d", context->errorCode); 89 context->terminateLoop = 1; 90 } 91 } else if (result < 0 && errno == EINTR) { 92 /* Try again */ 93 94 } else { 95 /* Timeout or other error */ 96 LKDCLog ("Timeout!"); 97 err = kLKDCHelperRealmNotFound; 98 context->terminateLoop = 1; 99 } 100 } 101 102 LKDCLogExit (err); 103 return err; 104} 105 106static const char *kerberosService = "_kerberos"; 107/* static const char *kerberosServiceName = "kerberos"; */ 108/* static const char *kerberosServiceType = "_kerberos._udp."; */ 109static const uint16_t kerberosServicePort = 88; 110/* static const char *kerberosServicePortString = "88"; */ 111 112static void LookupRealmCallBack( 113 DNSServiceRef serviceRef, 114 DNSServiceFlags flags, 115 uint32_t interface, 116 DNSServiceErrorType errorCode, 117 const char *fullname, 118 uint16_t rrType, 119 uint16_t rrClass, 120 uint16_t rdlen, 121 const void *rdata, 122 uint32_t ttl, 123 void *ctx 124 ) 125{ 126 char *realm = NULL; 127 uint32_t size = 0; 128 lookupContext *context = (lookupContext *)ctx; 129 130 context->errorCode = errorCode; 131 if (kDNSServiceErr_NoSuchRecord == errorCode) { 132 context->terminateLoop = 1; 133 } else if (errorCode != kDNSServiceErr_NoError) { 134 /* We'll try again */ 135 LKDCLog ("mDNSError = %d", errorCode); 136 } else { 137 if (rdlen > 1 && rdlen < 1024 /* max realm name? */) { 138 size = *(const unsigned char *)rdata; 139 if (size >= rdlen) 140 size = rdlen - 1; 141 realm = malloc (size + 1); 142 143 memcpy (realm, rdata + 1, size); 144 realm[size] = '\0'; 145 146 context->realm->realmName = realm; 147 148 if (NULL != fullname) { context->realm->serviceHost = fullname; } 149 150 context->realm->servicePort = kerberosServicePort; 151 152 context->realm->ttl = ttl; 153 context->realm->absoluteTTL = time(NULL) + ttl; 154 155 if (flags & kDNSServiceFlagsMoreComing) { 156 LKDCLog ("More than one record, last one wins!!!"); 157 } else { 158 context->terminateLoop = 1; 159 } 160 } 161 } 162} 163 164 165static LKDCHelperErrorType LKDCLookupRealm (const char *hostname, LKDCLocator *l) 166{ 167 // DNSServiceErrorType error = kDNSServiceErr_NoError; 168 LKDCHelperErrorType error = kLKDCHelperSuccess; 169 DNSServiceRef serviceRef = NULL; 170 lookupContext context; 171 char *lookupName = NULL; 172 173 LKDCLogEnter(); 174 175 if (NULL == hostname || NULL == l) { goto Done; } 176 177 context.realm = l; 178 179 asprintf (&lookupName, "%s.%s", kerberosService, hostname); 180 181 if (NULL == lookupName) { 182 error = kDNSServiceErr_NoMemory; 183 goto Done; 184 } 185 186 error = DNSServiceQueryRecord (&serviceRef, 187 kDNSServiceFlagsReturnIntermediates, 188 0, // All network interfaces 189 lookupName, 190 kDNSServiceType_TXT, 191 kDNSServiceClass_IN, 192 &LookupRealmCallBack, 193 &context); 194 195 if (kDNSServiceErr_NoError != error) { goto Done; } 196 197 error = HandleEvents(serviceRef, &context); 198 DNSServiceRefDeallocate(serviceRef); 199 200 /* 201 * "kdcmond" does not register SRV records, so we fake 202 * the serviceName and servicePort for now 203 */ 204 205 l->serviceHost = strdup (hostname); 206 l->servicePort = kerberosServicePort; 207 208Done: 209 if (lookupName) { free (lookupName); } 210 211 LKDCLogExit(error); 212 return error; 213} 214 215 216LKDCHelperErrorType LKDCCreateLocator (LKDCLocator **locator) 217{ 218 LKDCLocator *l; 219 LKDCHelperErrorType error = kLKDCHelperSuccess; 220 221 if (NULL == locator) { error = kLKDCHelperParameterError; goto Done; } 222 223 l = (LKDCLocator *) malloc (sizeof (LKDCLocator)); 224 225 if (NULL == l) { error = kLKDCHelperParameterError; goto Done; } 226 227 memset (l, 0, sizeof (*l)); 228 229 *locator = l; 230 231Done: 232 return error; 233} 234 235LKDCHelperErrorType LKDCReleaseLocator (LKDCLocator **locator) 236{ 237 LKDCLocator *l = NULL; 238 LKDCHelperErrorType error = kLKDCHelperSuccess; 239 240 if (NULL == locator || NULL == *locator) { error = kLKDCHelperParameterError; goto Done; } 241 242 l = *locator; 243 244 if (l->realmName) { free ((char *)l->realmName); } 245 if (l->serviceHost) { free ((char *)l->serviceHost); } 246 247 free (l); 248 249 *locator = NULL; 250 251Done: 252 return error; 253} 254 255static LKDCLocator *root = NULL; 256 257LKDCHelperErrorType LKDCAddLocatorDetails (LKDCLocator *l) 258{ 259 LKDCHelperErrorType error = kLKDCHelperSuccess; 260 LKDCLocator **lp; 261 262 LKDCLogEnter(); 263 264 if (NULL == l) { error = kLKDCHelperParameterError; goto Done; } 265 266 /* If the realm is already in the cache, update it. 267 * Otherwise, push it on the list. 268 */ 269 for (lp = &root; *lp != NULL; lp = &((*lp)->next)) 270 if (0 == strcmp(l->realmName, (*lp)->realmName)) 271 break; 272 if (NULL == *lp) { 273 LKDCLog ("New entry for (realm=%s host=%s)", l->realmName, l->serviceHost); 274 l->next = root; 275 root = l; 276 } else { 277 LKDCLog ("Replacing existing entry (realm=%s host=%s) with (realm=%s host=%s)", 278 (*lp)->realmName, (*lp)->serviceHost, l->realmName, l->serviceHost); 279 l->next = (*lp)->next; 280 LKDCReleaseLocator(lp); 281 *lp = l; 282 } 283 284Done: 285 LKDCLogExit(error); 286 return error; 287} 288 289 290LKDCHelperErrorType LKDCHostnameForRealm (const char *realm, LKDCLocator **l) 291{ 292 LKDCLocator *p; 293 LKDCHelperErrorType error = kLKDCHelperSuccess; 294 time_t now = time(NULL); 295 296 LKDCLogEnter(); 297 298 if (NULL == l || NULL == realm) { error = kLKDCHelperParameterError; goto Done; } 299 300 if (LocalLKDCRealm && strcmp(LocalLKDCRealm, realm) == 0) { 301 localRealm.realmName = LocalLKDCRealm; 302 p = &localRealm; 303 goto Found; 304 } 305 306 for (p = root; p != NULL; p = p->next) { 307 if (strcmp (p->realmName, realm) == 0 && p->absoluteTTL > now) { 308 LKDCLog ("Cache hit: %lus left", (unsigned long)(p->absoluteTTL - now)); 309 goto Found; 310 } 311 } 312 313 LKDCLog ("Cache miss"); 314 315 p = NULL; 316 317 /* There isn't anything we can do to map an arbitrary LocalKDC realm to a hostname */ 318 error = kLKDCHelperNoKDCForRealm; 319 goto Done; 320 321Found: 322 *l = p; 323 p = NULL; 324 325Done: 326 LKDCLogExit(error); 327 return error; 328} 329 330LKDCHelperErrorType LKDCRealmForHostname (const char *hostname, LKDCLocator **l) 331{ 332 LKDCLocator *p = NULL; 333 LKDCHelperErrorType error = kLKDCHelperSuccess; 334 time_t now = time(NULL); 335 336 LKDCLogEnter(); 337 338 if (NULL == l || NULL == hostname) { error = kLKDCHelperParameterError; goto Done; } 339 340 for (p = root; p != NULL; p = p->next) { 341 if (strcasecmp (p->serviceHost, hostname) == 0 && p->absoluteTTL > now) { 342 LKDCLog ("Cache hit: %lus left", (unsigned long)(p->absoluteTTL - now)); 343 goto Found; 344 } 345 } 346 347 LKDCLog ("Cache miss"); 348 349 p = NULL; 350 351 error = LKDCCreateLocator (&p); 352 if (kLKDCHelperSuccess != error) { goto Done; } 353 354 error = LKDCLookupRealm (hostname, p); 355 if (kLKDCHelperSuccess != error) { error = kLKDCHelperRealmNotFound; goto Done; } 356 357 error = LKDCAddLocatorDetails (p); 358 359Found: 360 *l = p; 361 p = NULL; 362 363Done: 364 if (NULL != p) { LKDCReleaseLocator (&p); } 365 366 LKDCLogExit(error); 367 return error; 368} 369 370LKDCHelperErrorType LKDCDumpCacheStatus () 371{ 372 LKDCLocator *p = NULL; 373 LKDCHelperErrorType error = kLKDCHelperSuccess; 374 375 LKDCLog ("Cache root node = %p", root); 376 377 for (p = root; p != NULL; p = p->next) { 378 LKDCLog ("node = %p {", p); 379 LKDCLog (" realmName = (%p) %s", p->realmName, p->realmName); 380 LKDCLog (" serviceHost = (%p) %s", p->serviceHost, p->serviceHost); 381 LKDCLog (" servicePort = %u", p->servicePort); 382 LKDCLog (" TTL = %u", p->ttl); 383 LKDCLog (" }"); 384 } 385 386 return error; 387} 388 389