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