#include "config.h" #include "plugin.h" #include "common/soa.h" #include "common/strset.h" #include #include #include #include #include #include #include /*---------------------------------------------------------------------------------- * DECLARATIONS */ static const char *dnsserial_soa_attribute = "sOARecord"; static const char *dnsserial_soa_base = ""; static Slapi_Mutex *dnsserial_mutex = NULL; static strset *dnsserial_soa_cache = NULL; static int dnsserial_enable = 0; /* --------------------------------------------------------------------------------- * MEMORY ALLOCATION */ void* strset_malloc (size_t length) { return slapi_ch_malloc (length); } void strset_free (void *ptr) { return slapi_ch_free (&ptr); } /* --------------------------------------------------------------------------------- * LDAP OPERATIONS */ static int search_for_dns (const char *base, const char *filter, char ***results) { Slapi_Entry **entries, **e; Slapi_PBlock *pb; LDAPControl *ctrl; char **dns, **d; char *attr, *dn; int rc, num, code; assert (results); assert (filter && filter[0]); assert (base && base[0]); ctrl = NULL; /* No controls */ attr = NULL; /* No attributes */ trace ("performing internal search"); /* Do the actual search */ pb = slapi_search_internal ((char*)base, LDAP_SCOPE_SUBTREE, (char*)filter, &ctrl, &attr, 1); return_val_if_fail (pb, -1); /* Was it successful? */ code = -1; rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &code); return_val_if_fail (rc >= 0, -1); if (code != LDAP_SUCCESS) { log_plugin ("error performing search on %s for %s (code %d)", base, filter, code); slapi_pblock_destroy (pb); trace ("failure"); return -1; } /* Dig out all the entries */ entries = NULL; rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); return_val_if_fail (rc >= 0, -1); /* How many entries? */ num = 0; for (e = entries; entries && *e; ++e) ++num; /* Allocate memory and copy over all dns found */ d = dns = (char**)slapi_ch_calloc (num + 1, sizeof (char*)); for (e = entries; entries && *e; ++e) { dn = slapi_entry_get_dn (*e); return_val_if_fail (dn, -1); dn = slapi_ch_strdup (dn); dn = slapi_dn_normalize_case (dn); *(d++) = dn; } slapi_pblock_destroy (pb); *results = dns; trace ("success"); return 0; } static int populate_soa_cache (void) { char **dns, **d; char *filter; int rc, len, have; assert (dnsserial_mutex); have = 0; slapi_lock_mutex (dnsserial_mutex); /* Already populated? */ have = dnsserial_soa_cache ? 1 : 0; slapi_unlock_mutex (dnsserial_mutex); if (have) return 1; /* Build up the filter */ len = strlen (dnsserial_soa_attribute) + strlen ("(%s=*)"); filter = slapi_ch_malloc (len); strcpy (filter, "("); strcat (filter, dnsserial_soa_attribute); strcat (filter, "=*)"); trace (filter); rc = search_for_dns (dnsserial_soa_base, filter, &dns); slapi_ch_free_string (&filter); if (rc < 0) return rc; /* Normalize all the DNs */ for (d = dns; *d; ++d) slapi_dn_normalize_case (*d); /* Now we're ready to put them in the cache */ assert (dnsserial_mutex); slapi_lock_mutex (dnsserial_mutex); /* Already populated? */ if (!dnsserial_soa_cache) { dnsserial_soa_cache = strset_create (); for (d = dns; *d; ++d) strset_add (dnsserial_soa_cache, *d); } slapi_unlock_mutex (dnsserial_mutex); slapi_ch_array_free (dns); return 1; } static int present_in_soa_cache (const char *dn) { int present; assert (dn && dn[0]); assert (dnsserial_mutex); if (populate_soa_cache () < 0) return 0; slapi_lock_mutex (dnsserial_mutex); present = strset_has (dnsserial_soa_cache, dn); slapi_unlock_mutex (dnsserial_mutex); return present; } static char* ancestor_in_soa_cache (const char *dn) { Slapi_DN *sdn = NULL; Slapi_DN *parent = NULL; char *ancestor; assert (dn && dn[0]); assert (dnsserial_mutex); if (populate_soa_cache () < 0) return 0; sdn = slapi_sdn_new_dn_byval (dn); return_val_if_fail (sdn, 0); ancestor = NULL; slapi_lock_mutex (dnsserial_mutex); for (;;) { /* Get the parent of the dn */ assert (sdn); assert (!parent); parent = slapi_sdn_new (); slapi_sdn_get_parent (sdn, parent); /* Reached the root? */ if (slapi_sdn_isempty (parent)) break; /* Reached the top of our SOA block? */ dn = slapi_sdn_get_dn (parent); if (strcmp (dn, dnsserial_soa_base) == 0) break; /* Found this DN? */ if (strset_has (dnsserial_soa_cache, dn)) { ancestor = slapi_ch_strdup (dn); break; } slapi_sdn_free (&sdn); sdn = parent; parent = NULL; } slapi_unlock_mutex (dnsserial_mutex); if (sdn) slapi_sdn_free (&sdn); if (parent) slapi_sdn_free (&parent); return ancestor; } static int add_to_soa_cache (const char *dn) { assert (dn && dn[0]); assert (dnsserial_mutex); if (populate_soa_cache () < 0) return -1; slapi_lock_mutex (dnsserial_mutex); strset_add (dnsserial_soa_cache, dn); slapi_unlock_mutex (dnsserial_mutex); return 0; } static int remove_from_soa_cache (const char *dn) { assert (dn && dn[0]); if (populate_soa_cache () < 0) return -1; slapi_lock_mutex (dnsserial_mutex); strset_remove (dnsserial_soa_cache, dn); slapi_unlock_mutex (dnsserial_mutex); return 0; } static char* load_soa_attribute (const char *dn) { Slapi_Entry **entries; struct berval **values; Slapi_PBlock *pb; Slapi_Attr *attr; LDAPControl *ctrl; char *attrs[2]; char *soa; int rc, code; trace (dn); assert (dn && dn[0]); assert (dnsserial_soa_attribute); ctrl = NULL; /* No controls */ attrs[0] = (char*)dnsserial_soa_attribute; attrs[1] = NULL; trace ("performing internal search"); /* Do the actual search */ pb = slapi_search_internal ((char*)dn, LDAP_SCOPE_BASE, "(objectClass=*)", &ctrl, attrs, 0); return_val_if_fail (pb, NULL); /* Was it successful? */ code = -1; rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &code); return_val_if_fail (rc >= 0, NULL); if (code != LDAP_SUCCESS) { log_plugin ("error loading attribute %s from %s (code %d)", dnsserial_soa_attribute, dn, code); slapi_pblock_destroy (pb); trace ("failure"); return NULL; } /* Dig out all the entries */ entries = NULL; slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); return_val_if_fail (entries, NULL); soa = NULL; if (entries[0]) { if (slapi_entry_attr_find (entries[0], (char*)dnsserial_soa_attribute, &attr) >= 0 && attr && slapi_attr_get_values (attr, &values) >= 0 && values && values[0]) { /* Convert the berval into a string */ soa = slapi_ch_malloc (values[0]->bv_len + 1); if (values[0]->bv_len) memcpy (soa, values[0]->bv_val, values[0]->bv_len); soa[values[0]->bv_len] = 0; } } slapi_pblock_destroy (pb); trace ("success"); return soa; } static int change_soa_attribute (const char *dn, const char *soa_old, const char *soa_new) { Slapi_PBlock *pb; LDAPMod *mods[3]; char *values_add[2]; char *values_remove[2]; LDAPMod mod_add; LDAPMod mod_remove; int rc, code; trace (dn); assert (dn && dn[0]); assert (soa_old); assert (soa_new); /* Setup the mods to change */ values_add[0] = (char*)soa_new; values_add[1] = NULL; mod_add.mod_op = LDAP_MOD_ADD; mod_add.mod_type = (char*)dnsserial_soa_attribute; mod_add.mod_values = values_add; values_remove[0] = (char*)soa_old; values_remove[1] = NULL; mod_remove.mod_op = LDAP_MOD_DELETE; mod_remove.mod_type = (char*)dnsserial_soa_attribute; mod_remove.mod_values = values_remove; mods[0] = &mod_remove; mods[1] = &mod_add; mods[2] = NULL; trace ("performing modify internal"); pb = slapi_modify_internal ((char*)dn, mods, NULL, 1); return_val_if_fail (pb, -1); code = -1; rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &code); return_val_if_fail (pb, -1); slapi_pblock_destroy (pb); if (code != LDAP_SUCCESS && code != LDAP_TYPE_OR_VALUE_EXISTS && code != LDAP_NO_SUCH_ATTRIBUTE) { log_plugin ("couldn't replace %s on %s with '%s' (code %d)", dnsserial_soa_attribute, dn, soa_new, code); trace ("failure"); return -1; } /* Call the dns notify code, and note that we've changed a sOARecord */ dnsnotify_post_modify (dn, mods); trace ("success"); return 0; } static int increment_soa_dn (const char *dn) { char *soa_new; char *soa_old; int len, ret = -1; assert (dn && dn[0]); soa_old = load_soa_attribute (dn); if (!soa_old) { log_plugin ("couldn't increment SOA record at %s: not found", dn); return -1; } len = strlen (soa_old) + 32; soa_new = slapi_ch_malloc (len); if (soa_serial_increment (soa_old, soa_new, len) < 0) { if (errno == EINVAL) log_plugin ("invalid SOA record at %s: %s", dn, soa_old); else log_plugin ("couldn't increment SOA record at %s: %s", dn, strerror (errno)); ret = -1; } else { ret = change_soa_attribute (dn, soa_old, soa_new); } slapi_ch_free_string (&soa_new); slapi_ch_free_string (&soa_old); return ret; } void autoserial_post_delete (const char *dn) { char *soa_dn; return_if_fail (dn && dn[0]); if (!dnsserial_enable) return; /* A DN that is an SOA? */ if (present_in_soa_cache (dn)) { remove_from_soa_cache (dn); /* A DN underneath an SOA? */ } else { soa_dn = ancestor_in_soa_cache (dn); if (soa_dn) { increment_soa_dn (soa_dn); slapi_ch_free_string (&soa_dn); } } } void autoserial_post_modify (const char *dn, LDAPMod **mods) { char *soa, *soa_dn; int present; return_if_fail (dn && dn[0]); if (!dnsserial_enable) return; /* Add or remove from cache as appropriate */ soa = load_soa_attribute(dn); if (soa) { add_to_soa_cache (dn); slapi_ch_free_string (&soa); present = 1; } else { remove_from_soa_cache (dn); present = 0; } /* A DN underneath an SOA? */ if (!present) { soa_dn = ancestor_in_soa_cache (dn); if (soa_dn) { increment_soa_dn (soa_dn); slapi_ch_free_string (&soa_dn); } } } void autoserial_post_modrdn (const char *odn, const char *ndn) { char *soa_dn; return_if_fail (odn && odn[0]); return_if_fail (ndn && ndn[0]); if (!dnsserial_enable) return; /* A DN that is an SOA? */ if (present_in_soa_cache (odn)) { remove_from_soa_cache (odn); add_to_soa_cache (ndn); /* A DN underneath an SOA? */ } else { soa_dn = ancestor_in_soa_cache (ndn); if (soa_dn) { increment_soa_dn (soa_dn); slapi_ch_free_string (&soa_dn); } } } void autoserial_post_add (const char *dn) { char *soa, *soa_dn; return_if_fail (dn && dn[0]); if (!dnsserial_enable) return; /* A DN that is an SOA? */ soa = load_soa_attribute (dn); if (soa) { add_to_soa_cache (dn); slapi_ch_free_string (&soa); /* A DN underneath an SOA? */ } else { soa_dn = ancestor_in_soa_cache (dn); if (soa_dn) { increment_soa_dn (soa_dn); slapi_ch_free_string (&soa_dn); } } } int autoserial_config (const char *name, const char *value) { return_val_if_fail (name, 0); if (strcmp ("soa-attribute", name) == 0 && value) { dnsserial_soa_attribute = value; return 1; } else if (strcmp ("base-dn", name) == 0 && value) { dnsserial_soa_base = value; return 1; } else if (strcmp ("enable-auto-serial", name) == 0 && !value) { dnsserial_enable = 1; return 1; } return 0; } void autoserial_destroy (void) { assert (dnsserial_mutex); slapi_lock_mutex (dnsserial_mutex); if (dnsserial_soa_cache) strset_destroy (dnsserial_soa_cache); dnsserial_soa_cache = NULL; slapi_unlock_mutex (dnsserial_mutex); slapi_destroy_mutex (dnsserial_mutex); dnsserial_mutex = NULL; } int autoserial_init (void) { if (!dnsserial_soa_base || !dnsserial_soa_base[0]) { log_plugin ("'base-dn' argument not specified"); return -1; } assert (!dnsserial_mutex); dnsserial_mutex = slapi_new_mutex (); return_val_if_fail (dnsserial_mutex, -1); return 0; }