]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
unbound-anchor work
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 27 Sep 2010 14:54:22 +0000 (14:54 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 27 Sep 2010 14:54:22 +0000 (14:54 +0000)
git-svn-id: file:///svn/unbound/trunk@2251 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
smallapp/unbound-anchor.c

index e9e36da17556f694a3f524443eb3ad1c08291490..3583c375ba98aff550948f30bfb23ddf223df2e7 100644 (file)
@@ -1,3 +1,6 @@
+27 September 2010: Wouter
+       - iana portlist updated.
+
 24 September 2010: Wouter
        - bug#329: in example.conf show correct ipv4 link-local 169.254/16.
 
index 2426edcfa7dfd1a3ad72e41299b08b396c2666ad..19f2d257d62d3229217b35524a1432100e87afd1 100644 (file)
@@ -1068,6 +1068,258 @@ free_file_bio(BIO* bio)
        BIO_free(bio);
 }
 
+/** XML parse private data during the parse */
+struct xml_data {
+       /** the parser, reference */
+       XML_Parser parser;
+       /** the current tag; malloced; or NULL outside of tags */
+       char* tag;
+       /** current date to use during the parse */
+       time_t date;
+       /** do we want to use this anchor? */
+       int use_key;
+       /** number of keys usefully read in */
+       int num_keys;
+       /** the compiled anchors as DS records */
+       BIO* ds;
+};
+
+/**
+ * XML handle character data, the data inside an element.
+ * @param userData: xml_data structure
+ * @param s: the character data.  May not all be in one callback.
+ *     NOT zero terminated.
+ * @param len: length of this part of the data.
+ */
+void
+xml_charhandle(void *userData, const XML_Char *s, int len)
+{
+       struct xml_data* data = (struct xml_data*)userData;
+       /* skip characters outside of elements */
+       if(!data->tag)
+               return;
+       if(verb>=4) {
+               int i;
+               printf("%s%s charhandle: '",
+                       data->use_key?"use ":"",
+                       data->tag?data->tag:"none");
+               for(i=0; i<len; i++)
+                       printf("%c", s[i]);
+               printf("'\n");
+       }
+       /* only store if key is used */
+       if(!data->use_key)
+               return;
+       if(strcasecmp(data->tag, "KeyTag") == 0 ||
+          strcasecmp(data->tag, "Algorithm") == 0 ||
+          strcasecmp(data->tag, "DigestType") == 0 ||
+          strcasecmp(data->tag, "Digest") == 0) {
+               if(BIO_write(data->ds, s, len) <= 0) {
+                       if(verb) printf("out of memory in BIO_write\n");
+                       exit(0);
+               }
+       }
+}
+
+/**
+ * XML fetch value of particular attribute(by name) or NULL if not present.
+ * @param atts: attribute array (from xml_startelem).
+ * @param name: name of attribute to look for.
+ * @return the value or NULL. (ptr into atts).
+ */
+static const XML_Char*
+find_att(const XML_Char **atts, XML_Char* name)
+{
+       int i;
+       for(i=0; atts[i]; i+=2) {
+               if(strcasecmp(atts[i], name) == 0)
+                       return atts[i+1];
+       }
+       return NULL;
+}
+
+/**
+ * XML handle the KeyDigest start tag, check validity periods.
+ */
+static void
+handle_keydigest(struct xml_data* data, const XML_Char **atts)
+{
+       const char* s = ". IN DS";
+       data->use_key = 0;
+       if(find_att(atts, "validFrom")) {
+               /* TODO */
+       }
+       if(find_att(atts, "validUntil")) {
+               /* TODO */
+       }
+       /* yes we want to use this key */
+       data->use_key = 1;
+       data->num_keys++;
+       if(BIO_write(data->ds, s, (int)strlen(s)) <= 0) {
+               if(verb) printf("out of memory in BIO_write\n");
+               exit(0);
+       }
+}
+
+/** 
+ * XML start of element. This callback is called whenever an XML tag starts.
+ * XML_Char is UTF8.
+ * @param userData: the xml_data structure.
+ * @param name: the tag that starts.
+ * @param atts: array of strings, pairs of attr = value, ends with NULL.
+ *     i.e. att[0]="att[1]" att[2]="att[3]" att[4]isNull
+ */
+static void
+xml_startelem(void *userData, const XML_Char *name, const XML_Char **atts)
+{
+       struct xml_data* data = (struct xml_data*)userData;
+       if(verb>=4) printf("xml tag start '%s'\n", name);
+       free(data->tag);
+       data->tag = strdup(name);
+       if(!data->tag) {
+               if(verb) printf("out of memory\n");
+               exit(0);
+       }
+       if(verb>=4) {
+               int i;
+               for(i=0; atts[i]; i+=2) {
+                       printf("  %s='%s'\n", atts[i], atts[i+1]);
+               }
+       }
+       /* handle attributes to particular types */
+       if(strcasecmp(name, "KeyDigest") == 0) {
+               handle_keydigest(data, atts);
+       }
+
+       /* write whitespace separators to outputBIO here */
+       if(!data->use_key)
+               return;
+       if(strcasecmp(data->tag, "KeyTag") == 0 ||
+          strcasecmp(data->tag, "Algorithm") == 0 ||
+          strcasecmp(data->tag, "DigestType") == 0 ||
+          strcasecmp(data->tag, "Digest") == 0) {
+               if(BIO_write(data->ds, " ", 1) <= 0) {
+                       if(verb) printf("out of memory in BIO_write\n");
+                       exit(0);
+               }
+       }
+}
+
+/**
+ * XML end of element. This callback is called whenever an XML tag ends.
+ * XML_Char is UTF8.
+ * @param userData: the xml_data structure
+ * @param name: the tag that ends.
+ */
+static void
+xml_endelem(void *userData, const XML_Char *name)
+{
+       struct xml_data* data = (struct xml_data*)userData;
+       if(verb>=4) printf("xml tag end   '%s'\n", name);
+       free(data->tag);
+       data->tag = NULL;
+       if(strcasecmp(name, "KeyDigest") == 0) {
+               data->use_key = 0;
+               if(BIO_write(data->ds, "\n", 1) <= 0) {
+                       if(verb) printf("out of memory in BIO_write\n");
+                       exit(0);
+               }
+       }
+}
+
+/**
+ * XML parser setup of the callbacks for the tags
+ */
+static void
+xml_parse_setup(XML_Parser parser, struct xml_data* data, time_t now)
+{
+       char buf[1024];
+       memset(data, 0, sizeof(*data));
+       XML_SetUserData(parser, data);
+       data->parser = parser;
+       data->date = now;
+       data->ds = BIO_new(BIO_s_mem());
+       if(!data->ds) {
+               if(verb) printf("out of memory\n");
+               exit(0);
+       }
+       snprintf(buf, sizeof(buf), "; created by unbound-anchor on %s",
+               ctime(&now));
+       if(BIO_write(data->ds, buf, (int)strlen(buf)) <= 0) {
+               if(verb) printf("out of memory\n");
+               exit(0);
+       }
+       XML_SetElementHandler(parser, xml_startelem, xml_endelem);
+       XML_SetCharacterDataHandler(parser, xml_charhandle);
+}
+
+/**
+ * Perform XML parsing of the root-anchors file
+ * Its format description can be read here
+ * https://data.iana.org/root-anchors/draft-icann-dnssec-trust-anchor.txt
+ * It uses libexpat.
+ * @param xml: BIO with xml data.
+ * @param now: the current time for checking DS validity periods.
+ * @return memoryBIO with the DS data in zone format.
+ *     or NULL if the zone is insecure.
+ *     (It exit()s on error)
+ */
+static BIO*
+xml_parse(BIO* xml, time_t now)
+{
+       char* pp;
+       int len;
+       XML_Parser parser;
+       struct xml_data data;
+
+       parser = XML_ParserCreate(NULL);
+       if(!parser) {
+               if(verb) printf("could not XML_ParserCreate\n");
+               exit(0);
+       }
+
+       /* setup callbacks */
+       xml_parse_setup(parser, &data, now);
+
+       /* parse it */
+       BIO_reset(xml);
+       len = (int)BIO_get_mem_data(xml, &pp);
+       if(!len || !pp) {
+               if(verb) printf("out of memory\n");
+               exit(0);
+       }
+       if(!XML_Parse(parser, pp, len, 1 /*isfinal*/ )) {
+               const char *e = XML_ErrorString(XML_GetErrorCode(parser));
+               if(verb) printf("XML_Parse failure %s\n",
+                       e?e:"");
+               exit(0);
+       }
+
+       /* parsed */
+       if(verb) printf("XML was parsed successfully, %d keys\n",
+                       data.num_keys);
+       free(data.tag);
+       XML_ParserFree(parser);
+
+       if(verb >= 4) {
+               char* pp = NULL;
+               int len;
+               BIO_seek(data.ds, 0);
+               len = BIO_get_mem_data(data.ds, &pp);
+               printf("got DS bio %d: '", len);
+               (void)fwrite(pp, (size_t)len, 1, stdout);
+               printf("'\n");
+       }
+
+       if(data.num_keys == 0) {
+               /* the root zone seems to have gone insecure */
+               BIO_free(data.ds);
+               return NULL;
+       } else {
+               return data.ds;
+       }
+}
+
 /** verify a PKCS7 signature, false on failure */
 static int
 verify_p7sig(BIO* data, BIO* p7s, BIO* pem, STACK_OF(X509)* trust, time_t now)
@@ -1132,6 +1384,58 @@ verify_p7sig(BIO* data, BIO* p7s, BIO* pem, STACK_OF(X509)* trust, time_t now)
        return secure;
 }
 
+/** write unsigned root anchor file, a 5011 revoked tp */
+static void
+write_unsigned_root(char* root_anchor_file)
+{
+       FILE* out;
+       time_t now = time(NULL);
+       out = fopen(root_anchor_file, "w");
+       if(!out) {
+               if(verb) printf("%s: %s\n", root_anchor_file, strerror(errno));
+               return;
+       }
+       if(fprintf(out, "; autotrust trust anchor file\n"
+               ";;REVOKED\n"
+               ";;id: . 1\n"
+               "; This file was written by unbound-anchor on %s"
+               "; It indicates that the root does not use DNSSEC\n"
+               "; to restart DNSSEC overwrite this file with a\n"
+               "; valid trustanchor or (empty-it and run unbound-anchor)\n"
+               , ctime(&now)) < 0) {
+               if(verb) printf("failed to write 'unsigned' to %s\n",
+                       root_anchor_file);
+               if(verb && errno != 0) printf("%s\n", strerror(errno));
+       }
+       fclose(out);
+}
+
+/** write root anchor file */
+static void
+write_root_anchor(char* root_anchor_file, BIO* ds)
+{
+       char* pp = NULL;
+       int len;
+       FILE* out;
+       BIO_seek(ds, 0);
+       len = BIO_get_mem_data(ds, &pp);
+       if(!len || !pp) {
+               if(verb) printf("out of memory\n");
+               return;
+       }
+       out = fopen(root_anchor_file, "w");
+       if(!out) {
+               if(verb) printf("%s: %s\n", root_anchor_file, strerror(errno));
+               return;
+       }
+       if(fwrite(pp, (size_t)len, 1, out) != 1) {
+               if(verb) printf("failed to write all data to %s\n",
+                       root_anchor_file);
+               if(verb && errno != 0) printf("%s\n", strerror(errno));
+       }
+       fclose(out);
+}
+
 /** Perform the verification and update of the trustanchor file */
 static void
 verify_and_update_anchor(char* root_anchor_file, char* debugconf,
@@ -1139,6 +1443,7 @@ verify_and_update_anchor(char* root_anchor_file, char* debugconf,
        STACK_OF(X509)* cert)
 {
        time_t now = get_time_now(debugconf);
+       BIO* ds;
 
        /* verify xml file */
        if(!verify_p7sig(xml, p7s, pem, cert, now)) {
@@ -1146,10 +1451,19 @@ verify_and_update_anchor(char* root_anchor_file, char* debugconf,
                exit(0);
        }
 
-       /* see if xml file verifies the dnskey that was probed */
-
-       /* reinstate 5011 tracking */
+       /* parse the xml file into DS records */
+       ds = xml_parse(xml, now);
+       if(!ds) {
+               /* the root zone is unsigned now */
+               write_unsigned_root(root_anchor_file);
+       } else {
+               /* see if xml file verifies the dnskey that was probed */
+               /* TODO */
 
+               /* reinstate 5011 tracking */
+               write_root_anchor(root_anchor_file, ds);
+       }
+       BIO_free(ds);
 }
 
 /** perform actual certupdate work */
@@ -1176,7 +1490,7 @@ do_certupdate(char* root_anchor_file, char* root_cert_file,
        pem = https(ip_list, pemname, urlname);
 
        /* update the pem file (optional) */
-       /*do_pem_update(cert, pem, &write_cert);*/
+       /*do_pem_update(cert, pem, &write_cert); TODO */
        if(write_cert) {
                (void)write_cert_file(root_cert_file, cert);
                if(verb) printf("wrote cert to %s\n", root_cert_file);
@@ -1519,10 +1833,12 @@ int main(int argc, char* argv[])
        argv += optind;
        if(argc != 0)
                usage();
+
        ERR_load_crypto_strings();
        ERR_load_SSL_strings();
        OpenSSL_add_all_algorithms();
        (void)SSL_library_init();
+
        return do_root_update_work(root_anchor_file, root_cert_file,
                urlname, xmlname, p7sname, pemname,
                res_conf, root_hints, debugconf, ip4only, ip6only, force);