etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / kdc / announce.c
blobd341f2ddbb9e13d1a2e77ca22eeb0621c4129124
1 /* $NetBSD: announce.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */
3 /*
4 * Copyright (c) 2008 Apple Inc. All Rights Reserved.
6 * Export of this software from the United States of America may require
7 * a specific license from the United States Government. It is the
8 * responsibility of any person or organization contemplating export to
9 * obtain such a license before exporting.
11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12 * distribute this software and its documentation for any purpose and
13 * without fee is hereby granted, provided that the above copyright
14 * notice appear in all copies and that both that copyright notice and
15 * this permission notice appear in supporting documentation, and that
16 * the name of Apple Inc. not be used in advertising or publicity pertaining
17 * to distribution of the software without specific, written prior
18 * permission. Apple Inc. makes no representations about the suitability of
19 * this software for any purpose. It is provided "as is" without express
20 * or implied warranty.
22 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
24 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
28 #include "kdc_locl.h"
30 #if defined(__APPLE__) && defined(HAVE_GCD)
32 #include <CoreFoundation/CoreFoundation.h>
33 #include <SystemConfiguration/SCDynamicStore.h>
34 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
35 #include <SystemConfiguration/SCDynamicStoreKey.h>
37 #include <dispatch/dispatch.h>
39 #include <asl.h>
40 #include <resolv.h>
42 #include <dns_sd.h>
43 #include <err.h>
45 static krb5_kdc_configuration *announce_config;
46 static krb5_context announce_context;
48 struct entry {
49 DNSRecordRef recordRef;
50 char *domain;
51 char *realm;
52 #define F_EXISTS 1
53 #define F_PUSH 2
54 int flags;
55 struct entry *next;
58 /* #define REGISTER_SRV_RR */
60 static struct entry *g_entries = NULL;
61 static CFStringRef g_hostname = NULL;
62 static DNSServiceRef g_dnsRef = NULL;
63 static SCDynamicStoreRef g_store = NULL;
64 static dispatch_queue_t g_queue = NULL;
66 #define LOG(...) asl_log(NULL, NULL, ASL_LEVEL_INFO, __VA_ARGS__)
68 static void create_dns_sd(void);
69 static void destroy_dns_sd(void);
70 static void update_all(SCDynamicStoreRef, CFArrayRef, void *);
73 /* parameters */
74 static CFStringRef NetworkChangedKey_BackToMyMac = CFSTR("Setup:/Network/BackToMyMac");
77 static char *
78 CFString2utf8(CFStringRef string)
80 size_t size;
81 char *str;
83 size = 1 + CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8);
84 str = malloc(size);
85 if (str == NULL)
86 return NULL;
88 if (CFStringGetCString(string, str, size, kCFStringEncodingUTF8) == false) {
89 free(str);
90 return NULL;
92 return str;
99 static void
100 retry_timer(void)
102 dispatch_source_t s;
103 dispatch_time_t t;
105 s = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
106 0, 0, g_queue);
107 t = dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC);
108 dispatch_source_set_timer(s, t, 0, NSEC_PER_SEC);
109 dispatch_source_set_event_handler(s, ^{
110 create_dns_sd();
111 dispatch_release(s);
113 dispatch_resume(s);
120 static void
121 create_dns_sd(void)
123 DNSServiceErrorType error;
124 dispatch_source_t s;
126 error = DNSServiceCreateConnection(&g_dnsRef);
127 if (error) {
128 retry_timer();
129 return;
132 dispatch_suspend(g_queue);
134 s = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
135 DNSServiceRefSockFD(g_dnsRef),
136 0, g_queue);
138 dispatch_source_set_event_handler(s, ^{
139 DNSServiceErrorType ret = DNSServiceProcessResult(g_dnsRef);
140 /* on error tear down and set timer to recreate */
141 if (ret != kDNSServiceErr_NoError && ret != kDNSServiceErr_Transient) {
142 dispatch_source_cancel(s);
146 dispatch_source_set_cancel_handler(s, ^{
147 destroy_dns_sd();
148 retry_timer();
149 dispatch_release(s);
152 dispatch_resume(s);
154 /* Do the first update ourself */
155 update_all(g_store, NULL, NULL);
156 dispatch_resume(g_queue);
159 static void
160 domain_add(const char *domain, const char *realm, int flag)
162 struct entry *e;
164 for (e = g_entries; e != NULL; e = e->next) {
165 if (strcmp(domain, e->domain) == 0 && strcmp(realm, e->realm) == 0) {
166 e->flags |= flag;
167 return;
171 LOG("Adding realm %s to domain %s", realm, domain);
173 e = calloc(1, sizeof(*e));
174 if (e == NULL)
175 return;
176 e->domain = strdup(domain);
177 e->realm = strdup(realm);
178 if (e->domain == NULL || e->realm == NULL) {
179 free(e->domain);
180 free(e->realm);
181 free(e);
182 return;
184 e->flags = flag | F_PUSH; /* if we allocate, we push */
185 e->next = g_entries;
186 g_entries = e;
189 struct addctx {
190 int flags;
191 const char *realm;
194 static void
195 domains_add(const void *key, const void *value, void *context)
197 char *str = CFString2utf8((CFStringRef)value);
198 struct addctx *ctx = context;
200 if (str == NULL)
201 return;
202 if (str[0] != '\0')
203 domain_add(str, ctx->realm, F_EXISTS | ctx->flags);
204 free(str);
208 static void
209 dnsCallback(DNSServiceRef sdRef __attribute__((unused)),
210 DNSRecordRef RecordRef __attribute__((unused)),
211 DNSServiceFlags flags __attribute__((unused)),
212 DNSServiceErrorType errorCode __attribute__((unused)),
213 void *context __attribute__((unused)))
217 #ifdef REGISTER_SRV_RR
220 * Register DNS SRV rr for the realm.
223 static const char *register_names[2] = {
224 "_kerberos._tcp",
225 "_kerberos._udp"
228 static struct {
229 DNSRecordRef *val;
230 size_t len;
231 } srvRefs = { NULL, 0 };
233 static void
234 register_srv(const char *realm, const char *hostname, int port)
236 unsigned char target[1024];
237 int i;
238 int size;
240 /* skip registering LKDC realms */
241 if (strncmp(realm, "LKDC:", 5) == 0)
242 return;
244 /* encode SRV-RR */
245 target[0] = 0; /* priority */
246 target[1] = 0; /* priority */
247 target[2] = 0; /* weight */
248 target[3] = 0; /* weigth */
249 target[4] = (port >> 8) & 0xff; /* port */
250 target[5] = (port >> 0) & 0xff; /* port */
252 size = dn_comp(hostname, target + 6, sizeof(target) - 6, NULL, NULL);
253 if (size < 0)
254 return;
256 size += 6;
258 LOG("register SRV rr for realm %s hostname %s:%d", realm, hostname, port);
260 for (i = 0; i < sizeof(register_names)/sizeof(register_names[0]); i++) {
261 char name[kDNSServiceMaxDomainName];
262 DNSServiceErrorType error;
263 void *ptr;
265 ptr = realloc(srvRefs.val, sizeof(srvRefs.val[0]) * (srvRefs.len + 1));
266 if (ptr == NULL)
267 errx(1, "malloc: out of memory");
268 srvRefs.val = ptr;
270 DNSServiceConstructFullName(name, NULL, register_names[i], realm);
272 error = DNSServiceRegisterRecord(g_dnsRef,
273 &srvRefs.val[srvRefs.len],
274 kDNSServiceFlagsUnique | kDNSServiceFlagsShareConnection,
276 name,
277 kDNSServiceType_SRV,
278 kDNSServiceClass_IN,
279 size,
280 target,
282 dnsCallback,
283 NULL);
284 if (error) {
285 LOG("Failed to register SRV rr for realm %s: %d", realm, error);
286 } else
287 srvRefs.len++;
291 static void
292 unregister_srv_realms(void)
294 if (g_dnsRef) {
295 for (i = 0; i < srvRefs.len; i++)
296 DNSServiceRemoveRecord(g_dnsRef, srvRefs.val[i], 0);
298 free(srvRefs.val);
299 srvRefs.len = 0;
300 srvRefs.val = NULL;
303 static void
304 register_srv_realms(CFStringRef host)
306 krb5_error_code ret;
307 char *hostname;
308 size_t i;
310 /* first unregister old names */
312 hostname = CFString2utf8(host);
313 if (hostname == NULL)
314 return;
316 for(i = 0; i < announce_config->num_db; i++) {
317 char **realms, **r;
319 if (announce_config->db[i]->hdb_get_realms == NULL)
320 continue;
322 ret = (announce_config->db[i]->hdb_get_realms)(announce_context, &realms);
323 if (ret == 0) {
324 for (r = realms; r && *r; r++)
325 register_srv(*r, hostname, 88);
326 krb5_free_host_realm(announce_context, realms);
330 free(hostname);
332 #endif /* REGISTER_SRV_RR */
334 static void
335 update_dns(void)
337 DNSServiceErrorType error;
338 struct entry **e = &g_entries;
339 char *hostname;
341 hostname = CFString2utf8(g_hostname);
342 if (hostname == NULL)
343 return;
345 while (*e != NULL) {
346 /* remove if this wasn't updated */
347 if (((*e)->flags & F_EXISTS) == 0) {
348 struct entry *drop = *e;
349 *e = (*e)->next;
351 LOG("Deleting realm %s from domain %s",
352 drop->realm, drop->domain);
354 if (drop->recordRef && g_dnsRef)
355 DNSServiceRemoveRecord(g_dnsRef, drop->recordRef, 0);
356 free(drop->domain);
357 free(drop->realm);
358 free(drop);
359 continue;
361 if ((*e)->flags & F_PUSH) {
362 struct entry *update = *e;
363 char *dnsdata, *name;
364 size_t len;
366 len = strlen(update->realm);
367 asprintf(&dnsdata, "%c%s", (int)len, update->realm);
368 if (dnsdata == NULL)
369 errx(1, "malloc");
371 asprintf(&name, "_kerberos.%s.%s", hostname, update->domain);
372 if (name == NULL)
373 errx(1, "malloc");
375 if (update->recordRef)
376 DNSServiceRemoveRecord(g_dnsRef, update->recordRef, 0);
378 error = DNSServiceRegisterRecord(g_dnsRef,
379 &update->recordRef,
380 kDNSServiceFlagsShared | kDNSServiceFlagsAllowRemoteQuery,
382 name,
383 kDNSServiceType_TXT,
384 kDNSServiceClass_IN,
385 len+1,
386 dnsdata,
388 dnsCallback,
389 NULL);
390 free(name);
391 free(dnsdata);
392 if (error)
393 errx(1, "failure to update entry for %s/%s",
394 update->domain, update->realm);
396 e = &(*e)->next;
398 free(hostname);
401 static void
402 update_entries(SCDynamicStoreRef store, const char *realm, int flags)
404 CFDictionaryRef btmm;
406 /* we always announce in the local domain */
407 domain_add("local", realm, F_EXISTS | flags);
409 /* announce btmm */
410 btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac);
411 if (btmm) {
412 struct addctx addctx;
414 addctx.flags = flags;
415 addctx.realm = realm;
417 CFDictionaryApplyFunction(btmm, domains_add, &addctx);
418 CFRelease(btmm);
422 static void
423 update_all(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
425 struct entry *e;
426 CFStringRef host;
427 int i, flags = 0;
429 LOG("something changed, running update");
431 host = SCDynamicStoreCopyLocalHostName(store);
432 if (host == NULL)
433 return;
435 if (g_hostname == NULL || CFStringCompare(host, g_hostname, 0) != kCFCompareEqualTo) {
436 if (g_hostname)
437 CFRelease(g_hostname);
438 g_hostname = CFRetain(host);
439 flags = F_PUSH; /* if hostname has changed, force push */
441 #ifdef REGISTER_SRV_RR
442 register_srv_realms(g_hostname);
443 #endif
446 for (e = g_entries; e != NULL; e = e->next)
447 e->flags &= ~(F_EXISTS|F_PUSH);
449 for(i = 0; i < announce_config->num_db; i++) {
450 krb5_error_code ret;
451 char **realms, **r;
453 if (announce_config->db[i]->hdb_get_realms == NULL)
454 continue;
456 ret = (announce_config->db[i]->hdb_get_realms)(announce_context, announce_config->db[i], &realms);
457 if (ret == 0) {
458 for (r = realms; r && *r; r++)
459 update_entries(store, *r, flags);
460 krb5_free_host_realm(announce_context, realms);
464 update_dns();
466 CFRelease(host);
469 static void
470 delete_all(void)
472 struct entry *e;
474 for (e = g_entries; e != NULL; e = e->next)
475 e->flags &= ~(F_EXISTS|F_PUSH);
477 update_dns();
478 if (g_entries != NULL)
479 errx(1, "Failed to remove all bonjour entries");
482 static void
483 destroy_dns_sd(void)
485 if (g_dnsRef == NULL)
486 return;
488 delete_all();
489 #ifdef REGISTER_SRV_RR
490 unregister_srv_realms();
491 #endif
493 DNSServiceRefDeallocate(g_dnsRef);
494 g_dnsRef = NULL;
498 static SCDynamicStoreRef
499 register_notification(void)
501 SCDynamicStoreRef store;
502 CFStringRef computerNameKey;
503 CFMutableArrayRef keys;
505 computerNameKey = SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault);
507 store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Network watcher"),
508 update_all, NULL);
509 if (store == NULL)
510 errx(1, "SCDynamicStoreCreate");
512 keys = CFArrayCreateMutable(kCFAllocatorDefault, 2, &kCFTypeArrayCallBacks);
513 if (keys == NULL)
514 errx(1, "CFArrayCreateMutable");
516 CFArrayAppendValue(keys, computerNameKey);
517 CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac);
519 if (SCDynamicStoreSetNotificationKeys(store, keys, NULL) == false)
520 errx(1, "SCDynamicStoreSetNotificationKeys");
522 CFRelease(computerNameKey);
523 CFRelease(keys);
525 if (!SCDynamicStoreSetDispatchQueue(store, g_queue))
526 errx(1, "SCDynamicStoreSetDispatchQueue");
528 return store;
530 #endif
532 void
533 bonjour_announce(krb5_context context, krb5_kdc_configuration *config)
535 #if defined(__APPLE__) && defined(HAVE_GCD)
536 g_queue = dispatch_queue_create("com.apple.kdc_announce", NULL);
537 if (!g_queue)
538 errx(1, "dispatch_queue_create");
540 g_store = register_notification();
541 announce_config = config;
542 announce_context = context;
544 create_dns_sd();
545 #endif