"Fossies" - the Fresh Open Source Software Archive

Member "heimdal-7.7.0/kdc/announce.c" (20 Dec 2016, 12819 Bytes) of package /linux/misc/heimdal-7.7.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "announce.c" see the Fossies "Dox" file reference documentation.

    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 }