Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/kdc/announce.c
34869 views
1
/*
2
* Copyright (c) 2008 Apple Inc. All Rights Reserved.
3
*
4
* Export of this software from the United States of America may require
5
* a specific license from the United States Government. It is the
6
* responsibility of any person or organization contemplating export to
7
* obtain such a license before exporting.
8
*
9
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10
* distribute this software and its documentation for any purpose and
11
* without fee is hereby granted, provided that the above copyright
12
* notice appear in all copies and that both that copyright notice and
13
* this permission notice appear in supporting documentation, and that
14
* the name of Apple Inc. not be used in advertising or publicity pertaining
15
* to distribution of the software without specific, written prior
16
* permission. Apple Inc. makes no representations about the suitability of
17
* this software for any purpose. It is provided "as is" without express
18
* or implied warranty.
19
*
20
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
21
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
22
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23
*
24
*/
25
26
#include "kdc_locl.h"
27
28
#if defined(__APPLE__) && defined(HAVE_GCD)
29
30
#include <CoreFoundation/CoreFoundation.h>
31
#include <SystemConfiguration/SCDynamicStore.h>
32
#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
33
#include <SystemConfiguration/SCDynamicStoreKey.h>
34
35
#include <dispatch/dispatch.h>
36
37
#include <asl.h>
38
#include <resolv.h>
39
40
#include <dns_sd.h>
41
#include <err.h>
42
43
static krb5_kdc_configuration *announce_config;
44
static krb5_context announce_context;
45
46
struct entry {
47
DNSRecordRef recordRef;
48
char *domain;
49
char *realm;
50
#define F_EXISTS 1
51
#define F_PUSH 2
52
int flags;
53
struct entry *next;
54
};
55
56
/* #define REGISTER_SRV_RR */
57
58
static struct entry *g_entries = NULL;
59
static CFStringRef g_hostname = NULL;
60
static DNSServiceRef g_dnsRef = NULL;
61
static SCDynamicStoreRef g_store = NULL;
62
static dispatch_queue_t g_queue = NULL;
63
64
#define LOG(...) asl_log(NULL, NULL, ASL_LEVEL_INFO, __VA_ARGS__)
65
66
static void create_dns_sd(void);
67
static void destroy_dns_sd(void);
68
static void update_all(SCDynamicStoreRef, CFArrayRef, void *);
69
70
71
/* parameters */
72
static CFStringRef NetworkChangedKey_BackToMyMac = CFSTR("Setup:/Network/BackToMyMac");
73
74
75
static char *
76
CFString2utf8(CFStringRef string)
77
{
78
size_t size;
79
char *str;
80
81
size = 1 + CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8);
82
str = malloc(size);
83
if (str == NULL)
84
return NULL;
85
86
if (CFStringGetCString(string, str, size, kCFStringEncodingUTF8) == false) {
87
free(str);
88
return NULL;
89
}
90
return str;
91
}
92
93
/*
94
*
95
*/
96
97
static void
98
retry_timer(void)
99
{
100
dispatch_source_t s;
101
dispatch_time_t t;
102
103
s = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
104
0, 0, g_queue);
105
t = dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC);
106
dispatch_source_set_timer(s, t, 0, NSEC_PER_SEC);
107
dispatch_source_set_event_handler(s, ^{
108
create_dns_sd();
109
dispatch_release(s);
110
});
111
dispatch_resume(s);
112
}
113
114
/*
115
*
116
*/
117
118
static void
119
create_dns_sd(void)
120
{
121
DNSServiceErrorType error;
122
dispatch_source_t s;
123
124
error = DNSServiceCreateConnection(&g_dnsRef);
125
if (error) {
126
retry_timer();
127
return;
128
}
129
130
dispatch_suspend(g_queue);
131
132
s = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
133
DNSServiceRefSockFD(g_dnsRef),
134
0, g_queue);
135
136
dispatch_source_set_event_handler(s, ^{
137
DNSServiceErrorType ret = DNSServiceProcessResult(g_dnsRef);
138
/* on error tear down and set timer to recreate */
139
if (ret != kDNSServiceErr_NoError && ret != kDNSServiceErr_Transient) {
140
dispatch_source_cancel(s);
141
}
142
});
143
144
dispatch_source_set_cancel_handler(s, ^{
145
destroy_dns_sd();
146
retry_timer();
147
dispatch_release(s);
148
});
149
150
dispatch_resume(s);
151
152
/* Do the first update ourself */
153
update_all(g_store, NULL, NULL);
154
dispatch_resume(g_queue);
155
}
156
157
static void
158
domain_add(const char *domain, const char *realm, int flag)
159
{
160
struct entry *e;
161
162
for (e = g_entries; e != NULL; e = e->next) {
163
if (strcmp(domain, e->domain) == 0 && strcmp(realm, e->realm) == 0) {
164
e->flags |= flag;
165
return;
166
}
167
}
168
169
LOG("Adding realm %s to domain %s", realm, domain);
170
171
e = calloc(1, sizeof(*e));
172
if (e == NULL)
173
return;
174
e->domain = strdup(domain);
175
e->realm = strdup(realm);
176
if (e->domain == NULL || e->realm == NULL) {
177
free(e->domain);
178
free(e->realm);
179
free(e);
180
return;
181
}
182
e->flags = flag | F_PUSH; /* if we allocate, we push */
183
e->next = g_entries;
184
g_entries = e;
185
}
186
187
struct addctx {
188
int flags;
189
const char *realm;
190
};
191
192
static void
193
domains_add(const void *key, const void *value, void *context)
194
{
195
char *str = CFString2utf8((CFStringRef)value);
196
struct addctx *ctx = context;
197
198
if (str == NULL)
199
return;
200
if (str[0] != '\0')
201
domain_add(str, ctx->realm, F_EXISTS | ctx->flags);
202
free(str);
203
}
204
205
206
static void
207
dnsCallback(DNSServiceRef sdRef __attribute__((unused)),
208
DNSRecordRef RecordRef __attribute__((unused)),
209
DNSServiceFlags flags __attribute__((unused)),
210
DNSServiceErrorType errorCode __attribute__((unused)),
211
void *context __attribute__((unused)))
212
{
213
}
214
215
#ifdef REGISTER_SRV_RR
216
217
/*
218
* Register DNS SRV rr for the realm.
219
*/
220
221
static const char *register_names[2] = {
222
"_kerberos._tcp",
223
"_kerberos._udp"
224
};
225
226
static struct {
227
DNSRecordRef *val;
228
size_t len;
229
} srvRefs = { NULL, 0 };
230
231
static void
232
register_srv(const char *realm, const char *hostname, int port)
233
{
234
unsigned char target[1024];
235
int i;
236
int size;
237
238
/* skip registering LKDC realms */
239
if (strncmp(realm, "LKDC:", 5) == 0)
240
return;
241
242
/* encode SRV-RR */
243
target[0] = 0; /* priority */
244
target[1] = 0; /* priority */
245
target[2] = 0; /* weight */
246
target[3] = 0; /* weigth */
247
target[4] = (port >> 8) & 0xff; /* port */
248
target[5] = (port >> 0) & 0xff; /* port */
249
250
size = dn_comp(hostname, target + 6, sizeof(target) - 6, NULL, NULL);
251
if (size < 0)
252
return;
253
254
size += 6;
255
256
LOG("register SRV rr for realm %s hostname %s:%d", realm, hostname, port);
257
258
for (i = 0; i < sizeof(register_names)/sizeof(register_names[0]); i++) {
259
char name[kDNSServiceMaxDomainName];
260
DNSServiceErrorType error;
261
void *ptr;
262
263
ptr = realloc(srvRefs.val, sizeof(srvRefs.val[0]) * (srvRefs.len + 1));
264
if (ptr == NULL)
265
errx(1, "malloc: out of memory");
266
srvRefs.val = ptr;
267
268
DNSServiceConstructFullName(name, NULL, register_names[i], realm);
269
270
error = DNSServiceRegisterRecord(g_dnsRef,
271
&srvRefs.val[srvRefs.len],
272
kDNSServiceFlagsUnique | kDNSServiceFlagsShareConnection,
273
0,
274
name,
275
kDNSServiceType_SRV,
276
kDNSServiceClass_IN,
277
size,
278
target,
279
0,
280
dnsCallback,
281
NULL);
282
if (error) {
283
LOG("Failed to register SRV rr for realm %s: %d", realm, error);
284
} else
285
srvRefs.len++;
286
}
287
}
288
289
static void
290
unregister_srv_realms(void)
291
{
292
if (g_dnsRef) {
293
for (i = 0; i < srvRefs.len; i++)
294
DNSServiceRemoveRecord(g_dnsRef, srvRefs.val[i], 0);
295
}
296
free(srvRefs.val);
297
srvRefs.len = 0;
298
srvRefs.val = NULL;
299
}
300
301
static void
302
register_srv_realms(CFStringRef host)
303
{
304
krb5_error_code ret;
305
char *hostname;
306
size_t i;
307
308
/* first unregister old names */
309
310
hostname = CFString2utf8(host);
311
if (hostname == NULL)
312
return;
313
314
for(i = 0; i < announce_config->num_db; i++) {
315
char **realms, **r;
316
317
if (announce_config->db[i]->hdb_get_realms == NULL)
318
continue;
319
320
ret = (announce_config->db[i]->hdb_get_realms)(announce_context, &realms);
321
if (ret == 0) {
322
for (r = realms; r && *r; r++)
323
register_srv(*r, hostname, 88);
324
krb5_free_host_realm(announce_context, realms);
325
}
326
}
327
328
free(hostname);
329
}
330
#endif /* REGISTER_SRV_RR */
331
332
static void
333
update_dns(void)
334
{
335
DNSServiceErrorType error;
336
struct entry **e = &g_entries;
337
char *hostname;
338
339
hostname = CFString2utf8(g_hostname);
340
if (hostname == NULL)
341
return;
342
343
while (*e != NULL) {
344
/* remove if this wasn't updated */
345
if (((*e)->flags & F_EXISTS) == 0) {
346
struct entry *drop = *e;
347
*e = (*e)->next;
348
349
LOG("Deleting realm %s from domain %s",
350
drop->realm, drop->domain);
351
352
if (drop->recordRef && g_dnsRef)
353
DNSServiceRemoveRecord(g_dnsRef, drop->recordRef, 0);
354
free(drop->domain);
355
free(drop->realm);
356
free(drop);
357
continue;
358
}
359
if ((*e)->flags & F_PUSH) {
360
struct entry *update = *e;
361
char *dnsdata, *name;
362
size_t len;
363
364
len = strlen(update->realm);
365
asprintf(&dnsdata, "%c%s", (int)len, update->realm);
366
if (dnsdata == NULL)
367
errx(1, "malloc");
368
369
asprintf(&name, "_kerberos.%s.%s", hostname, update->domain);
370
if (name == NULL)
371
errx(1, "malloc");
372
373
if (update->recordRef)
374
DNSServiceRemoveRecord(g_dnsRef, update->recordRef, 0);
375
376
error = DNSServiceRegisterRecord(g_dnsRef,
377
&update->recordRef,
378
kDNSServiceFlagsShared | kDNSServiceFlagsAllowRemoteQuery,
379
0,
380
name,
381
kDNSServiceType_TXT,
382
kDNSServiceClass_IN,
383
len+1,
384
dnsdata,
385
0,
386
dnsCallback,
387
NULL);
388
free(name);
389
free(dnsdata);
390
if (error)
391
errx(1, "failure to update entry for %s/%s",
392
update->domain, update->realm);
393
}
394
e = &(*e)->next;
395
}
396
free(hostname);
397
}
398
399
static void
400
update_entries(SCDynamicStoreRef store, const char *realm, int flags)
401
{
402
CFDictionaryRef btmm;
403
404
/* we always announce in the local domain */
405
domain_add("local", realm, F_EXISTS | flags);
406
407
/* announce btmm */
408
btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac);
409
if (btmm) {
410
struct addctx addctx;
411
412
addctx.flags = flags;
413
addctx.realm = realm;
414
415
CFDictionaryApplyFunction(btmm, domains_add, &addctx);
416
CFRelease(btmm);
417
}
418
}
419
420
static void
421
update_all(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
422
{
423
struct entry *e;
424
CFStringRef host;
425
int i, flags = 0;
426
427
LOG("something changed, running update");
428
429
host = SCDynamicStoreCopyLocalHostName(store);
430
if (host == NULL)
431
return;
432
433
if (g_hostname == NULL || CFStringCompare(host, g_hostname, 0) != kCFCompareEqualTo) {
434
if (g_hostname)
435
CFRelease(g_hostname);
436
g_hostname = CFRetain(host);
437
flags = F_PUSH; /* if hostname has changed, force push */
438
439
#ifdef REGISTER_SRV_RR
440
register_srv_realms(g_hostname);
441
#endif
442
}
443
444
for (e = g_entries; e != NULL; e = e->next)
445
e->flags &= ~(F_EXISTS|F_PUSH);
446
447
for(i = 0; i < announce_config->num_db; i++) {
448
krb5_error_code ret;
449
char **realms, **r;
450
451
if (announce_config->db[i]->hdb_get_realms == NULL)
452
continue;
453
454
ret = (announce_config->db[i]->hdb_get_realms)(announce_context, announce_config->db[i], &realms);
455
if (ret == 0) {
456
for (r = realms; r && *r; r++)
457
update_entries(store, *r, flags);
458
krb5_free_host_realm(announce_context, realms);
459
}
460
}
461
462
update_dns();
463
464
CFRelease(host);
465
}
466
467
static void
468
delete_all(void)
469
{
470
struct entry *e;
471
472
for (e = g_entries; e != NULL; e = e->next)
473
e->flags &= ~(F_EXISTS|F_PUSH);
474
475
update_dns();
476
if (g_entries != NULL)
477
errx(1, "Failed to remove all bonjour entries");
478
}
479
480
static void
481
destroy_dns_sd(void)
482
{
483
if (g_dnsRef == NULL)
484
return;
485
486
delete_all();
487
#ifdef REGISTER_SRV_RR
488
unregister_srv_realms();
489
#endif
490
491
DNSServiceRefDeallocate(g_dnsRef);
492
g_dnsRef = NULL;
493
}
494
495
496
static SCDynamicStoreRef
497
register_notification(void)
498
{
499
SCDynamicStoreRef store;
500
CFStringRef computerNameKey;
501
CFMutableArrayRef keys;
502
503
computerNameKey = SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault);
504
505
store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Network watcher"),
506
update_all, NULL);
507
if (store == NULL)
508
errx(1, "SCDynamicStoreCreate");
509
510
keys = CFArrayCreateMutable(kCFAllocatorDefault, 2, &kCFTypeArrayCallBacks);
511
if (keys == NULL)
512
errx(1, "CFArrayCreateMutable");
513
514
CFArrayAppendValue(keys, computerNameKey);
515
CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac);
516
517
if (SCDynamicStoreSetNotificationKeys(store, keys, NULL) == false)
518
errx(1, "SCDynamicStoreSetNotificationKeys");
519
520
CFRelease(computerNameKey);
521
CFRelease(keys);
522
523
if (!SCDynamicStoreSetDispatchQueue(store, g_queue))
524
errx(1, "SCDynamicStoreSetDispatchQueue");
525
526
return store;
527
}
528
#endif
529
530
void
531
bonjour_announce(krb5_context context, krb5_kdc_configuration *config)
532
{
533
#if defined(__APPLE__) && defined(HAVE_GCD)
534
g_queue = dispatch_queue_create("com.apple.kdc_announce", NULL);
535
if (!g_queue)
536
errx(1, "dispatch_queue_create");
537
538
g_store = register_notification();
539
announce_config = config;
540
announce_context = context;
541
542
create_dns_sd();
543
#endif
544
}
545
546