aboutsummaryrefslogtreecommitdiff
path: root/openssl/README
blob: ec85d3cf3841d3c3549e5fa1e5d65a520ba0d9cb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
$Id$

OpenSSL hacked to add support for the RFC 3779 X.509 v3 extensions.



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).

openssl.txt refers the reader to the certificate policy extension
handler as an example of how to write a raw extension. The relevant
file appears to be

    crypto/x509v3/v3_cpols.c

This is indeed a complicated mess, but it's a surprisingly good
tutorial, on about the tenth reading.  Note that this depends on
extension-specific typedefs defined in crypto/x509v3/x509v3.h.

Aside from the method interface and routines described in openssl.txt,
v3_cpols.c also includes a bunch of template junk for the ASN.1
encoder and decoder.  According to README.ASN1 (q.v.), the ASN.1 code
is in transition.  I haven't attempted to understand the "old" ASN.1
code; the new stuff is template driven.  README.ASN.1 mumbles about
this a bit, but the real doc on the template stuff appears to be the
definitions in crypto/asn1/asn1t.h.  Given that and a reading
knowledge of ASN.1, the template definitions in v3_cpols.c are fairly
obviously a direct hand translation from ASN.1 into C.

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

Nice example of "new" style ASN.1 (if it compiles, which the README
suggets it won't, sigh):

    demos/asn1/ocsp.c

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.  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.

At this point I think I understand enough that I could write the RFC
3779 extension I/O routines, although no doubt some of it would be
take a while since I would be learning my way through some of the
library routines as I go.

I have not yet written up a syntax description for how we would encode
the RFC 3779 extensions in an openssl.conf file.  That should be
fairly straightforward, if a bit tedious.

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, I'm not quite sure where yet.  To the extent that I
understand how OpenSSL does such things (not very well, yet), the
"-purpose" flags to the CLI's x509 and verify commands (q.v.) look
like the right hooks.  I haven't (yet) investigated the code in back
of the "-purpose" mechanism.

At the moment the consensus appears to be that we should be using
OpenSSL 0.9.8b, in order to get SHA-256 support.  My research to date
has been on OpenSSL 0.9.8a, but I doubt that any of the stuff I've
been looking at has changed enough to matter.



This is a first cut at what the openssl.conf syntax might look 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 long form needed tags anyway.

* For the moment I'm assuming that the only addresses we really care
  about making (relatively) easy are unicast v4 and v6 addresses; the
  "afisafi" syntax is there for completeness, but I'm not currently
  worrying about making it easy to do, eg, multicast addresses.
  Adding support for other specific AFI/SAFI pairs should be
  straightforward.

* I'm particularly looking for feedback about things I've made
  impossible (syntax not general enough) or common things I've made
  harder than necessary.

###

   IPAddressFamily     ::= SEQUENCE {    -- AFI & optional SAFI --
      addressFamily        OCTET STRING (SIZE (2..3)),
      ipAddressChoice      IPAddressChoice }

   IPAddressChoice     ::= CHOICE {
      inherit              NULL, -- inherit from issuer --
      addressesOrRanges    SEQUENCE OF IPAddressOrRange }

   IPAddressOrRange    ::= CHOICE {
      addressPrefix        IPAddress,
      addressRange         IPAddressRange }

   IPAddressRange      ::= SEQUENCE {
      min                  IPAddress,
      max                  IPAddress }

   IPAddress           ::= BIT STRING


# An address extension all specified on one line

rfc3779-addr = 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

rfc3779-addr = critical, ipv4:inherit, ipv6:2002::/16

# An address extension using raw AFI/SAFI syntax.
#
# This doesn't really work, we can't do prefixes without knowing how long
# a particular kind of address is supposed to be.
#rfc3779-addr = critical, afisafi:000101:001122334455-aabbccddee

# An address extension using SAFIs

rfc3779-addr = critical, ipv4-safi:1:10.1.1.1/32, ipv6-safi:1:2002::/16

# Address extension using an indirect section

rfc3779-addr = 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

###

   ASIdentifiers       ::= SEQUENCE {
       asnum               [0] EXPLICIT ASIdentifierChoice OPTIONAL,
       rdi                 [1] EXPLICIT ASIdentifierChoice OPTIONAL}

   ASIdentifierChoice  ::= CHOICE {
      inherit              NULL, -- inherit from issuer --
      asIdsOrRanges        SEQUENCE OF ASIdOrRange }

   ASIdOrRange         ::= CHOICE {
       id                  ASId,
       range               ASRange }

   ASRange             ::= SEQUENCE {
       min                 ASId,
       max                 ASId }

   ASId                ::= INTEGER

# ASID extension all specified on one line:

rfc3779-asid = critical, as:44, rdi:33-45

# ASID extension on one line using inheritance

rfc3779-asid = critical, as:55, rdi:inherit

# ASID extension using an indirect section

rfc3779-asid = critical, @asid-section

[asid-section]

as.0 = 44
as.1 = 55 - 77
rdi.0 = 33



Problem: how to do the top level ASN.1 template thingie for a SEQUENCE
(not SEQUENCE OF)?

Possible example: crypto/krb5/krb5_asn.c, KRB5_TKTBODY and KRB5_TICKET.

I -think- the flag combination I should be using is ASN1_TFLG_EXPLICIT
(context-specific explicit tags, which, according to mtr and my vague
recollection of ASN.1 tagging, is what RFC 3779 uses).

No, that's not right, kerberos tags the SEQUENCE itself, which we
don't.  So either leave the flags field empty or use
ASN1_TFLG_IMPLICIT.

But this is all very spooky, I can't find a good example, and it seems
wrong somehow that I'm ending up with template that points to a
sequence instead of being a sequence.

Hmm, maybe that means I need to call lower-level macros that do
specific parts of the sequence constructor, then build a template with
the SEQUENCE flags?

Well, try what we have and see if it works, then figure out why not.

Ok, one more try: crypto/x509v3/v3_{pci,pcia}.c implement RFC 3820 and
show a SEQUENCE used as the top level template.  Try it that way.

Hmm, maybe openssl.txt really meant v3_{pci,pcia}.c as an example?



Oooh, crypto/stack/ includes stack sorting functions.  With the right
structure definition and comparision function, this might solve a
whole mess of our canonicalization problems.  General plan:

1) Define an internal stack type for each of the extensions, with
   fields organized to allow us to handle everything with a single
   sort operation.  Eg, major key is afi/safi, expand prefixes to
   ranges or some such, etc.

2) Set an appropiate comparison function (see sk_set_cmp_func())

3) Parse config data into internal stack.

4) Call sk_sort().

5) Loop calling sk_shift() (or sk_pop()) to pull items off the sorted
   stack so we can insert them into our real structure in the right
   canonical form.

Hmm.  What about other ways of building up the real structure?  Would
it be better to have a "canoicalize structure" function for each
extension type?  What about allowing a caller to say "no thanks it's
already in canonical form please leave it alone"?

util/mkstack.pl finds DECLARE_STACK_OF() declarations and generates
safestack definitions automatically.  Be afraid.  Be very very afraid.



Upon closer examination of RFC 3779, it's not possible to do a
completely general handler for unknown AFIs, because it's the AFI that
tells one that maximum length of an address (which in turn is required
for certain operations, eg, canonization).

So I guess we end up with:

a) IPv4:foo, IPv4-SAFI:foo, IPv6:foo, IPv6-SAFI:foo (or something like
   that -- perhaps encode SAFI names too, eg IPv4-Unicast:foo), and

b) A table of known AFIs telling us the length of an address for that
   AFI.  For the moment I'm probably not going to bother filling that
   in for anything but IPv4 and IPv6, but having the mechanism in
   place for other AFIs seems harmless.



Current plan for addr_canonize(): sort function for outer sequence
should be straightforward.  One could in theory write a sort function
for the inner sequence, but doing so would require expanding addresses
both in the sort function and in the subsequent cleanup loop in
addr_canonize().  It would probably be simpler (and faster) to use a
temporary stack containing pointers to the IPAddressOrRange elements
and expanded versions of the addresses.

Hmm, the alternative would be to write a comparision routine which
expands a pair of bitstrings as addresses, and write the rest of the
code in terms of that.  Might be a useful function to have anyway.

int addr_cmp(int afi, ASN1_BIT_STRING *a, unsigned char fill_a,
                      ASN1_BIT_STRING *b, unsigned char fill_b);

Problem is, this requires data not available in a sk_sort() handler.
So I think we're stuck with the temporary stack approach. 

No, we can use a closure.