$Id$ -*- Text -*- OpenSSL hacked to add support for the RFC 3779 X.509 v3 extensions. Current status: - Not (yet) for distribution outside the RIRs. - Reads and writes RFC 3779 extensions but does not (yet) perform the additional validation described in RFC 3779 2.3 & 3.3. - Not (yet) tested extensively. Please report any problems to me (sra) or the rescert mailing list. This is what the current openssl.conf syntax looks like for the RFC 3779 certificate extensions. Syntax is admittedly wretched, because it has to work with the existing OpenSSL code. Within that restriction, I've attempted to make this look as much as practical like the existing OpenSSL support for "multi-valued" extensions. RFC 3779 ASN.1 provided for easy reference. Notes: * Ranges are denoted with a hyphen, prefix lengths with a slash. I could tag ranges differently from the atomic types, but this seemed easier for the user to understand. * The "@" syntax indicating indirection through a separate section is lifted from the stock OpenSSL multi-valued extension support. * I didn't attempt to guess which addresses are IPv4 and which are IPv6 from the syntax, since the opensssl.conf multi-value syntax needs tags anyway. * SAFI support is present but minimal. If you want a SAFI, you have to specify its numeric value. It would be trivial to add additional keywords for specific SAFIs if there were a reason to do so. * The "sbgp-" names were already present in OpenSSL's table of known extension OIDs. We can talk to the folks at the OpenSSL project about changing the names if there's a reason to do so. ### # An address extension, all specified on one line sbgp-ipAddrBlock = critical, IPv4:10.1.1.1/32, IPv4:10.2.0.0-10.3.255.255 # An address extension, all specified on one line, with inheritance sbgp-ipAddrBlock = critical, IPv4:inherit, IPv6:2002::/16 # An address extension using SAFIs sbgp-ipAddrBlock = critical, IPv4-SAFI:1:10.1.1.1/32, IPv6-SAFI:1:2002::/16 # Address extension using an indirect section sbgp-ipAddrBlock = critical, @addr-section [addr-section] IPv4.0 = 10.0.0.1 IPv4.1 = 10.0.1.0/24 IPv4.2 = 10.2.0.0 - 10.3.255.255 IPv6.0 = 2002:1::/64 IPv6.1 = 2002:2:: - 2002:8::ffff:ffff:ffff:ffff:ffff ### # An ASID extension, all specified on one line: sbgp-autonomousSysNum = critical, AS:44, RDI:33-45 # ASID extension on one line using inheritance sbgp-autonomousSysNum = critical, AS:55, RDI:inherit # ASID extension using an indirect section sbgp-autonomousSysNum = critical, @asid-section [asid-section] AS.0 = 44 AS.1 = 55 - 77 RDI.0 = 33 Some notes on OpenSSL internals. O'Reilly "Network Security with OpenSSL" is a bit dated (four years old, corresponds roughly to OpenSSL 0.9.7), but still appears to be the closest thing there is to coherent documentation. Some updates and machine readable copies of examples are available at http://www.opensslbook.com/. In spite of its age, the book is useful as it gives a readable overview of bit and pieces of OpenSSL's internal programming environment which one would otherwise have to absorb from the code via osmosis. Chapter 10 is particularly useful, as are the sections on error handling and abstract I/O (the ERR and BIO packages, respectively) in chapter 4. OpenSSL's own doc is very patchy, although fairly extensive in places. Most of it eventually comes down to "Use the Source, Luke" with pointers on which bit of source serves as an interesting example. For x509v3 extentions, the place to start is doc/openssl.txt, which, oddly, turns out to be mostly about certificate extensions. It gives an overview of the mechanisms, in particular of the method routine interface for certificate extensions. For something like the RFC 3779 extensions, it's pretty clear that we need to use a "raw" extension (which, as far as I can tell, just means that the RFC 3779 stuff is complicated enough that the extension handler has to do a lot of work to deal with a complex ASN.1 structure that the rest of the code doesn't know much about). General note on global symbols in OpenSSL: always look at the header file for any global symbol you're using. In fact, it's probably best to do a global search (m-x tags-search if you're an emacs user) for all instances of a global symbol before attempting to use it, as there are a lot of things that one just has to know about how all the global stuff hangs together. There are header files full of magic definitions that one just has to one need to be extended. There are magic pre-sorted lists of handlers that one just needs to know about. Little or none of this is documented. Use the Source, Luke. In some cases -portions- of files are automatically generated by Perl scripts (eg, the per-type stack definitions in safestack.h). Ouch. If you see a large block of very repetitive stuff, check for comments indicating that it's automatically generated. Oh, and the indentation style is demented. Header files you definitely need to read if you're going to touch this stuff: crypto/x509v3/x509v3.h crypto/x509v3/ext_dat.h crypto/stack/safestack.h crypto/asn1/asn1t.h crypto/asn1/asn1.h crypto/objects/objects.h Automatically generated header files you'll need to skim, then go read the input files listed in the header comments and perhaps the generating Perl code: crypto/objects/obj_mac.h crypto/objects/obj_dat.h Much of the code shows a heavy Perl influence, presumably dating back clear to Eric Young. Some of the internal data structure operators have names that only make sense to a Perl programmer. Stacks are really lists, and may be sorted. Where code is automatically generated, it's done by Perl scripts. The configuration language for the whole package is a Perl script. Assembly code is all wrapped up inside perl scripts in a moderately clever attempt at being able to write the assembly language only once and use it with various assemblers with nontrivially different syntax. Much of the documentation markup (including manual pages) for the C code is .pod. I have not yet figured out where to hook in the extra goop that RFC 3779 will need for verification. Making extensions critical is easy enough, but the validation stuff in RFC 3779 2.3 and 3.3 needs to go somewhere. A lot of the missing documentation is buried in ssleay.txt, which the other documentation says not to read because it's so old. But it's where Eric explains all the basic data structures and expected usage as of the dawn of time, so most of the stuff that's so old that it's undocumented is really documented there. xxx_new() functions set pointers of sub-structures to NULL or allocate the substructures (one can leak memory if one doesn't know or check which a particular xxx_new() function has done...), and the xxx_free() functions clean up complex structures. So be sure to set unused pointers to NULL if one has been fiddling. Make sure that memory leak detection (CRYPTO_MDEBUG) is turned on when debugging. "make update" in the top level runs all the magic perl code that grovels through the code generating error codes, safestacks, etc. util/mkstack.pl finds DECLARE_STACK_OF() declarations and generates safestack definitions automatically if you run "make update". Be afraid. Be very very afraid. My initial test configuration was: ./Configure debug -DDEBUG_SAFESTACK which tried to pull in -lefence (/usr/ports/devel/ElectricFence), so I installed that. Sadly, ElectricFence is not kidding when it says it is very very very slow, but it was the bignum debugging printouts that were driving me nuts, so I ended up creating my own "debug-sra" configuration for the options I want. Random reminders and notes to myself: - The new stuff in crypto/x509v3/x509v3.h is not organized properly yet, it's all lumped in one place rather than being interleved with the other supported extensions. This was to make it easier for me to debug, but will proably need to be cleaned up eventually. - I've seen occasional printouts of what look like empty address extensions. So far the ones I've investigated really have been empty; they were created by the APNIC perl code, so I haven't tried to figure out whether that was deliberate or a bug. - It might be possible to get rid of inet_pton(): see ipv4_from_asc() and ipv6_from_asc() in v3_utl.c. Similarly, it might not really be necessary to use inet_ntop(), as we're mostly dealing with prefixes here and thus can probably get away with a simplified IPv6 printout routine that doesn't bother with "::" except at the end of a prefix. - Right now the library code silently merges duplicates and overlaps. It might useful to emit warnings when we do this. Merging all takes place when we're whacking the extension into canonical form, so it should be easy to do this; the only hard part is figuring out whether the application wants it, and, if so, where the application wants it sent. This probably requires a global variable, either a pointer to a BIO stream or a callback (probably the latter as it's less likely to cause surprising results running multi-threaded). - The reference for RDIs in RFC 3779 is incorrect. I've been told that the authoritative definition of RDIs is ISO 10747, available as http://www.acm.org/sigs/sigcomm/standards/iso_stds/IDRP/10747.TXT. - RobL tells me that the place to start on the validation code is the function x509_verify(), and that X509_VERIFY_PARAM is an interesting data structure. Hmm. x509_verify() is a macro. x509_vrfy.c:check_chain_extensions() might be interesting. crypto/x509/x509_vrfy.[ch] in general are interesting.