1 | /*
|
---|
2 | * Copyright (c) 2004 - 2009 Kungliga Tekniska Högskolan
|
---|
3 | * (Royal Institute of Technology, Stockholm, Sweden).
|
---|
4 | * All rights reserved.
|
---|
5 | *
|
---|
6 | * Redistribution and use in source and binary forms, with or without
|
---|
7 | * modification, are permitted provided that the following conditions
|
---|
8 | * are met:
|
---|
9 | *
|
---|
10 | * 1. Redistributions of source code must retain the above copyright
|
---|
11 | * notice, this list of conditions and the following disclaimer.
|
---|
12 | *
|
---|
13 | * 2. Redistributions in binary form must reproduce the above copyright
|
---|
14 | * notice, this list of conditions and the following disclaimer in the
|
---|
15 | * documentation and/or other materials provided with the distribution.
|
---|
16 | *
|
---|
17 | * 3. Neither the name of the Institute nor the names of its contributors
|
---|
18 | * may be used to endorse or promote products derived from this software
|
---|
19 | * without specific prior written permission.
|
---|
20 | *
|
---|
21 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
---|
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
---|
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
---|
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
---|
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
---|
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
---|
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
---|
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
---|
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
---|
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
---|
31 | * SUCH DAMAGE.
|
---|
32 | */
|
---|
33 |
|
---|
34 | #include "hx_locl.h"
|
---|
35 | #include <wind.h>
|
---|
36 | #include "char_map.h"
|
---|
37 |
|
---|
38 | /**
|
---|
39 | * @page page_name PKIX/X.509 Names
|
---|
40 | *
|
---|
41 | * There are several names in PKIX/X.509, GeneralName and Name.
|
---|
42 | *
|
---|
43 | * A Name consists of an ordered list of Relative Distinguished Names
|
---|
44 | * (RDN). Each RDN consists of an unordered list of typed strings. The
|
---|
45 | * types are defined by OID and have long and short description. For
|
---|
46 | * example id-at-commonName (2.5.4.3) have the long name CommonName
|
---|
47 | * and short name CN. The string itself can be of several encoding,
|
---|
48 | * UTF8, UTF16, Teltex string, etc. The type limit what encoding
|
---|
49 | * should be used.
|
---|
50 | *
|
---|
51 | * GeneralName is a broader nametype that can contains al kind of
|
---|
52 | * stuff like Name, IP addresses, partial Name, etc.
|
---|
53 | *
|
---|
54 | * Name is mapped into a hx509_name object.
|
---|
55 | *
|
---|
56 | * Parse and string name into a hx509_name object with hx509_parse_name(),
|
---|
57 | * make it back into string representation with hx509_name_to_string().
|
---|
58 | *
|
---|
59 | * Name string are defined rfc2253, rfc1779 and X.501.
|
---|
60 | *
|
---|
61 | * See the library functions here: @ref hx509_name
|
---|
62 | */
|
---|
63 |
|
---|
64 | static const struct {
|
---|
65 | const char *n;
|
---|
66 | const heim_oid *o;
|
---|
67 | wind_profile_flags flags;
|
---|
68 | } no[] = {
|
---|
69 | { "C", &asn1_oid_id_at_countryName },
|
---|
70 | { "CN", &asn1_oid_id_at_commonName },
|
---|
71 | { "DC", &asn1_oid_id_domainComponent },
|
---|
72 | { "L", &asn1_oid_id_at_localityName },
|
---|
73 | { "O", &asn1_oid_id_at_organizationName },
|
---|
74 | { "OU", &asn1_oid_id_at_organizationalUnitName },
|
---|
75 | { "S", &asn1_oid_id_at_stateOrProvinceName },
|
---|
76 | { "STREET", &asn1_oid_id_at_streetAddress },
|
---|
77 | { "UID", &asn1_oid_id_Userid },
|
---|
78 | { "emailAddress", &asn1_oid_id_pkcs9_emailAddress },
|
---|
79 | { "serialNumber", &asn1_oid_id_at_serialNumber }
|
---|
80 | };
|
---|
81 |
|
---|
82 | static char *
|
---|
83 | quote_string(const char *f, size_t len, int flags, size_t *rlen)
|
---|
84 | {
|
---|
85 | size_t i, j, tolen;
|
---|
86 | const unsigned char *from = (const unsigned char *)f;
|
---|
87 | unsigned char *to;
|
---|
88 |
|
---|
89 | tolen = len * 3 + 1;
|
---|
90 | to = malloc(tolen);
|
---|
91 | if (to == NULL)
|
---|
92 | return NULL;
|
---|
93 |
|
---|
94 | for (i = 0, j = 0; i < len; i++) {
|
---|
95 | unsigned char map = char_map[from[i]] & flags;
|
---|
96 | if (i == 0 && (map & Q_RFC2253_QUOTE_FIRST)) {
|
---|
97 | to[j++] = '\\';
|
---|
98 | to[j++] = from[i];
|
---|
99 | } else if ((i + 1) == len && (map & Q_RFC2253_QUOTE_LAST)) {
|
---|
100 |
|
---|
101 | to[j++] = '\\';
|
---|
102 | to[j++] = from[i];
|
---|
103 | } else if (map & Q_RFC2253_QUOTE) {
|
---|
104 | to[j++] = '\\';
|
---|
105 | to[j++] = from[i];
|
---|
106 | } else if (map & Q_RFC2253_HEX) {
|
---|
107 | int l = snprintf((char *)&to[j], tolen - j - 1,
|
---|
108 | "#%02x", (unsigned char)from[i]);
|
---|
109 | j += l;
|
---|
110 | } else {
|
---|
111 | to[j++] = from[i];
|
---|
112 | }
|
---|
113 | }
|
---|
114 | to[j] = '\0';
|
---|
115 | assert(j < tolen);
|
---|
116 | *rlen = j;
|
---|
117 | return (char *)to;
|
---|
118 | }
|
---|
119 |
|
---|
120 |
|
---|
121 | static int
|
---|
122 | append_string(char **str, size_t *total_len, const char *ss,
|
---|
123 | size_t len, int quote)
|
---|
124 | {
|
---|
125 | char *s, *qs;
|
---|
126 |
|
---|
127 | if (quote)
|
---|
128 | qs = quote_string(ss, len, Q_RFC2253, &len);
|
---|
129 | else
|
---|
130 | qs = rk_UNCONST(ss);
|
---|
131 |
|
---|
132 | s = realloc(*str, len + *total_len + 1);
|
---|
133 | if (s == NULL)
|
---|
134 | _hx509_abort("allocation failure"); /* XXX */
|
---|
135 | memcpy(s + *total_len, qs, len);
|
---|
136 | if (qs != ss)
|
---|
137 | free(qs);
|
---|
138 | s[*total_len + len] = '\0';
|
---|
139 | *str = s;
|
---|
140 | *total_len += len;
|
---|
141 | return 0;
|
---|
142 | }
|
---|
143 |
|
---|
144 | static char *
|
---|
145 | oidtostring(const heim_oid *type)
|
---|
146 | {
|
---|
147 | char *s;
|
---|
148 | size_t i;
|
---|
149 |
|
---|
150 | for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
|
---|
151 | if (der_heim_oid_cmp(no[i].o, type) == 0)
|
---|
152 | return strdup(no[i].n);
|
---|
153 | }
|
---|
154 | if (der_print_heim_oid(type, '.', &s) != 0)
|
---|
155 | return NULL;
|
---|
156 | return s;
|
---|
157 | }
|
---|
158 |
|
---|
159 | static int
|
---|
160 | stringtooid(const char *name, size_t len, heim_oid *oid)
|
---|
161 | {
|
---|
162 | int i, ret;
|
---|
163 | char *s;
|
---|
164 |
|
---|
165 | memset(oid, 0, sizeof(*oid));
|
---|
166 |
|
---|
167 | for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
|
---|
168 | if (strncasecmp(no[i].n, name, len) == 0)
|
---|
169 | return der_copy_oid(no[i].o, oid);
|
---|
170 | }
|
---|
171 | s = malloc(len + 1);
|
---|
172 | if (s == NULL)
|
---|
173 | return ENOMEM;
|
---|
174 | memcpy(s, name, len);
|
---|
175 | s[len] = '\0';
|
---|
176 | ret = der_parse_heim_oid(s, ".", oid);
|
---|
177 | free(s);
|
---|
178 | return ret;
|
---|
179 | }
|
---|
180 |
|
---|
181 | /**
|
---|
182 | * Convert the hx509 name object into a printable string.
|
---|
183 | * The resulting string should be freed with free().
|
---|
184 | *
|
---|
185 | * @param name name to print
|
---|
186 | * @param str the string to return
|
---|
187 | *
|
---|
188 | * @return An hx509 error code, see hx509_get_error_string().
|
---|
189 | *
|
---|
190 | * @ingroup hx509_name
|
---|
191 | */
|
---|
192 |
|
---|
193 | int
|
---|
194 | hx509_name_to_string(const hx509_name name, char **str)
|
---|
195 | {
|
---|
196 | return _hx509_Name_to_string(&name->der_name, str);
|
---|
197 | }
|
---|
198 |
|
---|
199 | int
|
---|
200 | _hx509_Name_to_string(const Name *n, char **str)
|
---|
201 | {
|
---|
202 | size_t total_len = 0;
|
---|
203 | int i, j, ret;
|
---|
204 |
|
---|
205 | *str = strdup("");
|
---|
206 | if (*str == NULL)
|
---|
207 | return ENOMEM;
|
---|
208 |
|
---|
209 | for (i = n->u.rdnSequence.len - 1 ; i >= 0 ; i--) {
|
---|
210 | size_t len;
|
---|
211 |
|
---|
212 | for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
|
---|
213 | DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
|
---|
214 | char *oidname;
|
---|
215 | char *ss;
|
---|
216 |
|
---|
217 | oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type);
|
---|
218 |
|
---|
219 | switch(ds->element) {
|
---|
220 | case choice_DirectoryString_ia5String:
|
---|
221 | ss = ds->u.ia5String.data;
|
---|
222 | len = ds->u.ia5String.length;
|
---|
223 | break;
|
---|
224 | case choice_DirectoryString_printableString:
|
---|
225 | ss = ds->u.printableString.data;
|
---|
226 | len = ds->u.printableString.length;
|
---|
227 | break;
|
---|
228 | case choice_DirectoryString_utf8String:
|
---|
229 | ss = ds->u.utf8String;
|
---|
230 | len = strlen(ss);
|
---|
231 | break;
|
---|
232 | case choice_DirectoryString_bmpString: {
|
---|
233 | const uint16_t *bmp = ds->u.bmpString.data;
|
---|
234 | size_t bmplen = ds->u.bmpString.length;
|
---|
235 | size_t k;
|
---|
236 |
|
---|
237 | ret = wind_ucs2utf8_length(bmp, bmplen, &k);
|
---|
238 | if (ret)
|
---|
239 | return ret;
|
---|
240 |
|
---|
241 | ss = malloc(k + 1);
|
---|
242 | if (ss == NULL)
|
---|
243 | _hx509_abort("allocation failure"); /* XXX */
|
---|
244 | ret = wind_ucs2utf8(bmp, bmplen, ss, NULL);
|
---|
245 | if (ret) {
|
---|
246 | free(ss);
|
---|
247 | return ret;
|
---|
248 | }
|
---|
249 | ss[k] = '\0';
|
---|
250 | len = k;
|
---|
251 | break;
|
---|
252 | }
|
---|
253 | case choice_DirectoryString_teletexString:
|
---|
254 | ss = ds->u.teletexString;
|
---|
255 | len = strlen(ss);
|
---|
256 | break;
|
---|
257 | case choice_DirectoryString_universalString: {
|
---|
258 | const uint32_t *uni = ds->u.universalString.data;
|
---|
259 | size_t unilen = ds->u.universalString.length;
|
---|
260 | size_t k;
|
---|
261 |
|
---|
262 | ret = wind_ucs4utf8_length(uni, unilen, &k);
|
---|
263 | if (ret)
|
---|
264 | return ret;
|
---|
265 |
|
---|
266 | ss = malloc(k + 1);
|
---|
267 | if (ss == NULL)
|
---|
268 | _hx509_abort("allocation failure"); /* XXX */
|
---|
269 | ret = wind_ucs4utf8(uni, unilen, ss, NULL);
|
---|
270 | if (ret) {
|
---|
271 | free(ss);
|
---|
272 | return ret;
|
---|
273 | }
|
---|
274 | ss[k] = '\0';
|
---|
275 | len = k;
|
---|
276 | break;
|
---|
277 | }
|
---|
278 | default:
|
---|
279 | _hx509_abort("unknown directory type: %d", ds->element);
|
---|
280 | exit(1);
|
---|
281 | }
|
---|
282 | append_string(str, &total_len, oidname, strlen(oidname), 0);
|
---|
283 | free(oidname);
|
---|
284 | append_string(str, &total_len, "=", 1, 0);
|
---|
285 | append_string(str, &total_len, ss, len, 1);
|
---|
286 | if (ds->element == choice_DirectoryString_bmpString ||
|
---|
287 | ds->element == choice_DirectoryString_universalString)
|
---|
288 | {
|
---|
289 | free(ss);
|
---|
290 | }
|
---|
291 | if (j + 1 < n->u.rdnSequence.val[i].len)
|
---|
292 | append_string(str, &total_len, "+", 1, 0);
|
---|
293 | }
|
---|
294 |
|
---|
295 | if (i > 0)
|
---|
296 | append_string(str, &total_len, ",", 1, 0);
|
---|
297 | }
|
---|
298 | return 0;
|
---|
299 | }
|
---|
300 |
|
---|
301 | #define COPYCHARARRAY(_ds,_el,_l,_n) \
|
---|
302 | (_l) = strlen(_ds->u._el); \
|
---|
303 | (_n) = malloc((_l) * sizeof((_n)[0])); \
|
---|
304 | if ((_n) == NULL) \
|
---|
305 | return ENOMEM; \
|
---|
306 | for (i = 0; i < (_l); i++) \
|
---|
307 | (_n)[i] = _ds->u._el[i]
|
---|
308 |
|
---|
309 |
|
---|
310 | #define COPYVALARRAY(_ds,_el,_l,_n) \
|
---|
311 | (_l) = _ds->u._el.length; \
|
---|
312 | (_n) = malloc((_l) * sizeof((_n)[0])); \
|
---|
313 | if ((_n) == NULL) \
|
---|
314 | return ENOMEM; \
|
---|
315 | for (i = 0; i < (_l); i++) \
|
---|
316 | (_n)[i] = _ds->u._el.data[i]
|
---|
317 |
|
---|
318 | #define COPYVOIDARRAY(_ds,_el,_l,_n) \
|
---|
319 | (_l) = _ds->u._el.length; \
|
---|
320 | (_n) = malloc((_l) * sizeof((_n)[0])); \
|
---|
321 | if ((_n) == NULL) \
|
---|
322 | return ENOMEM; \
|
---|
323 | for (i = 0; i < (_l); i++) \
|
---|
324 | (_n)[i] = ((unsigned char *)_ds->u._el.data)[i]
|
---|
325 |
|
---|
326 |
|
---|
327 |
|
---|
328 | static int
|
---|
329 | dsstringprep(const DirectoryString *ds, uint32_t **rname, size_t *rlen)
|
---|
330 | {
|
---|
331 | wind_profile_flags flags;
|
---|
332 | size_t i, len;
|
---|
333 | int ret;
|
---|
334 | uint32_t *name;
|
---|
335 |
|
---|
336 | *rname = NULL;
|
---|
337 | *rlen = 0;
|
---|
338 |
|
---|
339 | switch(ds->element) {
|
---|
340 | case choice_DirectoryString_ia5String:
|
---|
341 | flags = WIND_PROFILE_LDAP;
|
---|
342 | COPYVOIDARRAY(ds, ia5String, len, name);
|
---|
343 | break;
|
---|
344 | case choice_DirectoryString_printableString:
|
---|
345 | flags = WIND_PROFILE_LDAP;
|
---|
346 | flags |= WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE;
|
---|
347 | COPYVOIDARRAY(ds, printableString, len, name);
|
---|
348 | break;
|
---|
349 | case choice_DirectoryString_teletexString:
|
---|
350 | flags = WIND_PROFILE_LDAP_CASE;
|
---|
351 | COPYCHARARRAY(ds, teletexString, len, name);
|
---|
352 | break;
|
---|
353 | case choice_DirectoryString_bmpString:
|
---|
354 | flags = WIND_PROFILE_LDAP;
|
---|
355 | COPYVALARRAY(ds, bmpString, len, name);
|
---|
356 | break;
|
---|
357 | case choice_DirectoryString_universalString:
|
---|
358 | flags = WIND_PROFILE_LDAP;
|
---|
359 | COPYVALARRAY(ds, universalString, len, name);
|
---|
360 | break;
|
---|
361 | case choice_DirectoryString_utf8String:
|
---|
362 | flags = WIND_PROFILE_LDAP;
|
---|
363 | ret = wind_utf8ucs4_length(ds->u.utf8String, &len);
|
---|
364 | if (ret)
|
---|
365 | return ret;
|
---|
366 | name = malloc(len * sizeof(name[0]));
|
---|
367 | if (name == NULL)
|
---|
368 | return ENOMEM;
|
---|
369 | ret = wind_utf8ucs4(ds->u.utf8String, name, &len);
|
---|
370 | if (ret) {
|
---|
371 | free(name);
|
---|
372 | return ret;
|
---|
373 | }
|
---|
374 | break;
|
---|
375 | default:
|
---|
376 | _hx509_abort("unknown directory type: %d", ds->element);
|
---|
377 | }
|
---|
378 |
|
---|
379 | *rlen = len;
|
---|
380 | /* try a couple of times to get the length right, XXX gross */
|
---|
381 | for (i = 0; i < 4; i++) {
|
---|
382 | *rlen = *rlen * 2;
|
---|
383 | *rname = malloc(*rlen * sizeof((*rname)[0]));
|
---|
384 |
|
---|
385 | ret = wind_stringprep(name, len, *rname, rlen, flags);
|
---|
386 | if (ret == WIND_ERR_OVERRUN) {
|
---|
387 | free(*rname);
|
---|
388 | *rname = NULL;
|
---|
389 | continue;
|
---|
390 | } else
|
---|
391 | break;
|
---|
392 | }
|
---|
393 | free(name);
|
---|
394 | if (ret) {
|
---|
395 | if (*rname)
|
---|
396 | free(*rname);
|
---|
397 | *rname = NULL;
|
---|
398 | *rlen = 0;
|
---|
399 | return ret;
|
---|
400 | }
|
---|
401 |
|
---|
402 | return 0;
|
---|
403 | }
|
---|
404 |
|
---|
405 | int
|
---|
406 | _hx509_name_ds_cmp(const DirectoryString *ds1,
|
---|
407 | const DirectoryString *ds2,
|
---|
408 | int *diff)
|
---|
409 | {
|
---|
410 | uint32_t *ds1lp, *ds2lp;
|
---|
411 | size_t ds1len, ds2len, i;
|
---|
412 | int ret;
|
---|
413 |
|
---|
414 | ret = dsstringprep(ds1, &ds1lp, &ds1len);
|
---|
415 | if (ret)
|
---|
416 | return ret;
|
---|
417 | ret = dsstringprep(ds2, &ds2lp, &ds2len);
|
---|
418 | if (ret) {
|
---|
419 | free(ds1lp);
|
---|
420 | return ret;
|
---|
421 | }
|
---|
422 |
|
---|
423 | if (ds1len != ds2len)
|
---|
424 | *diff = ds1len - ds2len;
|
---|
425 | else {
|
---|
426 | for (i = 0; i < ds1len; i++) {
|
---|
427 | *diff = ds1lp[i] - ds2lp[i];
|
---|
428 | if (*diff)
|
---|
429 | break;
|
---|
430 | }
|
---|
431 | }
|
---|
432 | free(ds1lp);
|
---|
433 | free(ds2lp);
|
---|
434 |
|
---|
435 | return 0;
|
---|
436 | }
|
---|
437 |
|
---|
438 | int
|
---|
439 | _hx509_name_cmp(const Name *n1, const Name *n2, int *c)
|
---|
440 | {
|
---|
441 | int ret, i, j;
|
---|
442 |
|
---|
443 | *c = n1->u.rdnSequence.len - n2->u.rdnSequence.len;
|
---|
444 | if (*c)
|
---|
445 | return 0;
|
---|
446 |
|
---|
447 | for (i = 0 ; i < n1->u.rdnSequence.len; i++) {
|
---|
448 | *c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len;
|
---|
449 | if (*c)
|
---|
450 | return 0;
|
---|
451 |
|
---|
452 | for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) {
|
---|
453 | *c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type,
|
---|
454 | &n1->u.rdnSequence.val[i].val[j].type);
|
---|
455 | if (*c)
|
---|
456 | return 0;
|
---|
457 |
|
---|
458 | ret = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value,
|
---|
459 | &n2->u.rdnSequence.val[i].val[j].value,
|
---|
460 | c);
|
---|
461 | if (ret)
|
---|
462 | return ret;
|
---|
463 | if (*c)
|
---|
464 | return 0;
|
---|
465 | }
|
---|
466 | }
|
---|
467 | *c = 0;
|
---|
468 | return 0;
|
---|
469 | }
|
---|
470 |
|
---|
471 | /**
|
---|
472 | * Compare to hx509 name object, useful for sorting.
|
---|
473 | *
|
---|
474 | * @param n1 a hx509 name object.
|
---|
475 | * @param n2 a hx509 name object.
|
---|
476 | *
|
---|
477 | * @return 0 the objects are the same, returns > 0 is n2 is "larger"
|
---|
478 | * then n2, < 0 if n1 is "smaller" then n2.
|
---|
479 | *
|
---|
480 | * @ingroup hx509_name
|
---|
481 | */
|
---|
482 |
|
---|
483 | int
|
---|
484 | hx509_name_cmp(hx509_name n1, hx509_name n2)
|
---|
485 | {
|
---|
486 | int ret, diff;
|
---|
487 | ret = _hx509_name_cmp(&n1->der_name, &n2->der_name, &diff);
|
---|
488 | if (ret)
|
---|
489 | return ret;
|
---|
490 | return diff;
|
---|
491 | }
|
---|
492 |
|
---|
493 |
|
---|
494 | int
|
---|
495 | _hx509_name_from_Name(const Name *n, hx509_name *name)
|
---|
496 | {
|
---|
497 | int ret;
|
---|
498 | *name = calloc(1, sizeof(**name));
|
---|
499 | if (*name == NULL)
|
---|
500 | return ENOMEM;
|
---|
501 | ret = copy_Name(n, &(*name)->der_name);
|
---|
502 | if (ret) {
|
---|
503 | free(*name);
|
---|
504 | *name = NULL;
|
---|
505 | }
|
---|
506 | return ret;
|
---|
507 | }
|
---|
508 |
|
---|
509 | int
|
---|
510 | _hx509_name_modify(hx509_context context,
|
---|
511 | Name *name,
|
---|
512 | int append,
|
---|
513 | const heim_oid *oid,
|
---|
514 | const char *str)
|
---|
515 | {
|
---|
516 | RelativeDistinguishedName *rdn;
|
---|
517 | int ret;
|
---|
518 | void *ptr;
|
---|
519 |
|
---|
520 | ptr = realloc(name->u.rdnSequence.val,
|
---|
521 | sizeof(name->u.rdnSequence.val[0]) *
|
---|
522 | (name->u.rdnSequence.len + 1));
|
---|
523 | if (ptr == NULL) {
|
---|
524 | hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
|
---|
525 | return ENOMEM;
|
---|
526 | }
|
---|
527 | name->u.rdnSequence.val = ptr;
|
---|
528 |
|
---|
529 | if (append) {
|
---|
530 | rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len];
|
---|
531 | } else {
|
---|
532 | memmove(&name->u.rdnSequence.val[1],
|
---|
533 | &name->u.rdnSequence.val[0],
|
---|
534 | name->u.rdnSequence.len *
|
---|
535 | sizeof(name->u.rdnSequence.val[0]));
|
---|
536 |
|
---|
537 | rdn = &name->u.rdnSequence.val[0];
|
---|
538 | }
|
---|
539 | rdn->val = malloc(sizeof(rdn->val[0]));
|
---|
540 | if (rdn->val == NULL)
|
---|
541 | return ENOMEM;
|
---|
542 | rdn->len = 1;
|
---|
543 | ret = der_copy_oid(oid, &rdn->val[0].type);
|
---|
544 | if (ret)
|
---|
545 | return ret;
|
---|
546 | rdn->val[0].value.element = choice_DirectoryString_utf8String;
|
---|
547 | rdn->val[0].value.u.utf8String = strdup(str);
|
---|
548 | if (rdn->val[0].value.u.utf8String == NULL)
|
---|
549 | return ENOMEM;
|
---|
550 | name->u.rdnSequence.len += 1;
|
---|
551 |
|
---|
552 | return 0;
|
---|
553 | }
|
---|
554 |
|
---|
555 | /**
|
---|
556 | * Parse a string into a hx509 name object.
|
---|
557 | *
|
---|
558 | * @param context A hx509 context.
|
---|
559 | * @param str a string to parse.
|
---|
560 | * @param name the resulting object, NULL in case of error.
|
---|
561 | *
|
---|
562 | * @return An hx509 error code, see hx509_get_error_string().
|
---|
563 | *
|
---|
564 | * @ingroup hx509_name
|
---|
565 | */
|
---|
566 |
|
---|
567 | int
|
---|
568 | hx509_parse_name(hx509_context context, const char *str, hx509_name *name)
|
---|
569 | {
|
---|
570 | const char *p, *q;
|
---|
571 | size_t len;
|
---|
572 | hx509_name n;
|
---|
573 | int ret;
|
---|
574 |
|
---|
575 | *name = NULL;
|
---|
576 |
|
---|
577 | n = calloc(1, sizeof(*n));
|
---|
578 | if (n == NULL) {
|
---|
579 | hx509_set_error_string(context, 0, ENOMEM, "out of memory");
|
---|
580 | return ENOMEM;
|
---|
581 | }
|
---|
582 |
|
---|
583 | n->der_name.element = choice_Name_rdnSequence;
|
---|
584 |
|
---|
585 | p = str;
|
---|
586 |
|
---|
587 | while (p != NULL && *p != '\0') {
|
---|
588 | heim_oid oid;
|
---|
589 | int last;
|
---|
590 |
|
---|
591 | q = strchr(p, ',');
|
---|
592 | if (q) {
|
---|
593 | len = (q - p);
|
---|
594 | last = 1;
|
---|
595 | } else {
|
---|
596 | len = strlen(p);
|
---|
597 | last = 0;
|
---|
598 | }
|
---|
599 |
|
---|
600 | q = strchr(p, '=');
|
---|
601 | if (q == NULL) {
|
---|
602 | ret = HX509_PARSING_NAME_FAILED;
|
---|
603 | hx509_set_error_string(context, 0, ret, "missing = in %s", p);
|
---|
604 | goto out;
|
---|
605 | }
|
---|
606 | if (q == p) {
|
---|
607 | ret = HX509_PARSING_NAME_FAILED;
|
---|
608 | hx509_set_error_string(context, 0, ret,
|
---|
609 | "missing name before = in %s", p);
|
---|
610 | goto out;
|
---|
611 | }
|
---|
612 |
|
---|
613 | if ((q - p) > len) {
|
---|
614 | ret = HX509_PARSING_NAME_FAILED;
|
---|
615 | hx509_set_error_string(context, 0, ret, " = after , in %s", p);
|
---|
616 | goto out;
|
---|
617 | }
|
---|
618 |
|
---|
619 | ret = stringtooid(p, q - p, &oid);
|
---|
620 | if (ret) {
|
---|
621 | ret = HX509_PARSING_NAME_FAILED;
|
---|
622 | hx509_set_error_string(context, 0, ret,
|
---|
623 | "unknown type: %.*s", (int)(q - p), p);
|
---|
624 | goto out;
|
---|
625 | }
|
---|
626 |
|
---|
627 | {
|
---|
628 | size_t pstr_len = len - (q - p) - 1;
|
---|
629 | const char *pstr = p + (q - p) + 1;
|
---|
630 | char *r;
|
---|
631 |
|
---|
632 | r = malloc(pstr_len + 1);
|
---|
633 | if (r == NULL) {
|
---|
634 | der_free_oid(&oid);
|
---|
635 | ret = ENOMEM;
|
---|
636 | hx509_set_error_string(context, 0, ret, "out of memory");
|
---|
637 | goto out;
|
---|
638 | }
|
---|
639 | memcpy(r, pstr, pstr_len);
|
---|
640 | r[pstr_len] = '\0';
|
---|
641 |
|
---|
642 | ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r);
|
---|
643 | free(r);
|
---|
644 | der_free_oid(&oid);
|
---|
645 | if(ret)
|
---|
646 | goto out;
|
---|
647 | }
|
---|
648 | p += len + last;
|
---|
649 | }
|
---|
650 |
|
---|
651 | *name = n;
|
---|
652 |
|
---|
653 | return 0;
|
---|
654 | out:
|
---|
655 | hx509_name_free(&n);
|
---|
656 | return HX509_NAME_MALFORMED;
|
---|
657 | }
|
---|
658 |
|
---|
659 | /**
|
---|
660 | * Copy a hx509 name object.
|
---|
661 | *
|
---|
662 | * @param context A hx509 cotext.
|
---|
663 | * @param from the name to copy from
|
---|
664 | * @param to the name to copy to
|
---|
665 | *
|
---|
666 | * @return An hx509 error code, see hx509_get_error_string().
|
---|
667 | *
|
---|
668 | * @ingroup hx509_name
|
---|
669 | */
|
---|
670 |
|
---|
671 | int
|
---|
672 | hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to)
|
---|
673 | {
|
---|
674 | int ret;
|
---|
675 |
|
---|
676 | *to = calloc(1, sizeof(**to));
|
---|
677 | if (*to == NULL)
|
---|
678 | return ENOMEM;
|
---|
679 | ret = copy_Name(&from->der_name, &(*to)->der_name);
|
---|
680 | if (ret) {
|
---|
681 | free(*to);
|
---|
682 | *to = NULL;
|
---|
683 | return ENOMEM;
|
---|
684 | }
|
---|
685 | return 0;
|
---|
686 | }
|
---|
687 |
|
---|
688 | /**
|
---|
689 | * Convert a hx509_name into a Name.
|
---|
690 | *
|
---|
691 | * @param from the name to copy from
|
---|
692 | * @param to the name to copy to
|
---|
693 | *
|
---|
694 | * @return An hx509 error code, see hx509_get_error_string().
|
---|
695 | *
|
---|
696 | * @ingroup hx509_name
|
---|
697 | */
|
---|
698 |
|
---|
699 | int
|
---|
700 | hx509_name_to_Name(const hx509_name from, Name *to)
|
---|
701 | {
|
---|
702 | return copy_Name(&from->der_name, to);
|
---|
703 | }
|
---|
704 |
|
---|
705 | int
|
---|
706 | hx509_name_normalize(hx509_context context, hx509_name name)
|
---|
707 | {
|
---|
708 | return 0;
|
---|
709 | }
|
---|
710 |
|
---|
711 | /**
|
---|
712 | * Expands variables in the name using env. Variables are on the form
|
---|
713 | * ${name}. Useful when dealing with certificate templates.
|
---|
714 | *
|
---|
715 | * @param context A hx509 cotext.
|
---|
716 | * @param name the name to expand.
|
---|
717 | * @param env environment variable to expand.
|
---|
718 | *
|
---|
719 | * @return An hx509 error code, see hx509_get_error_string().
|
---|
720 | *
|
---|
721 | * @ingroup hx509_name
|
---|
722 | */
|
---|
723 |
|
---|
724 | int
|
---|
725 | hx509_name_expand(hx509_context context,
|
---|
726 | hx509_name name,
|
---|
727 | hx509_env env)
|
---|
728 | {
|
---|
729 | Name *n = &name->der_name;
|
---|
730 | int i, j;
|
---|
731 |
|
---|
732 | if (env == NULL)
|
---|
733 | return 0;
|
---|
734 |
|
---|
735 | if (n->element != choice_Name_rdnSequence) {
|
---|
736 | hx509_set_error_string(context, 0, EINVAL, "RDN not of supported type");
|
---|
737 | return EINVAL;
|
---|
738 | }
|
---|
739 |
|
---|
740 | for (i = 0 ; i < n->u.rdnSequence.len; i++) {
|
---|
741 | for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
|
---|
742 | /** Only UTF8String rdnSequence names are allowed */
|
---|
743 | /*
|
---|
744 | THIS SHOULD REALLY BE:
|
---|
745 | COMP = n->u.rdnSequence.val[i].val[j];
|
---|
746 | normalize COMP to utf8
|
---|
747 | check if there are variables
|
---|
748 | expand variables
|
---|
749 | convert back to orignal format, store in COMP
|
---|
750 | free normalized utf8 string
|
---|
751 | */
|
---|
752 | DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
|
---|
753 | char *p, *p2;
|
---|
754 | struct rk_strpool *strpool = NULL;
|
---|
755 |
|
---|
756 | if (ds->element != choice_DirectoryString_utf8String) {
|
---|
757 | hx509_set_error_string(context, 0, EINVAL, "unsupported type");
|
---|
758 | return EINVAL;
|
---|
759 | }
|
---|
760 | p = strstr(ds->u.utf8String, "${");
|
---|
761 | if (p) {
|
---|
762 | strpool = rk_strpoolprintf(strpool, "%.*s",
|
---|
763 | (int)(p - ds->u.utf8String),
|
---|
764 | ds->u.utf8String);
|
---|
765 | if (strpool == NULL) {
|
---|
766 | hx509_set_error_string(context, 0, ENOMEM, "out of memory");
|
---|
767 | return ENOMEM;
|
---|
768 | }
|
---|
769 | }
|
---|
770 | while (p != NULL) {
|
---|
771 | /* expand variables */
|
---|
772 | const char *value;
|
---|
773 | p2 = strchr(p, '}');
|
---|
774 | if (p2 == NULL) {
|
---|
775 | hx509_set_error_string(context, 0, EINVAL, "missing }");
|
---|
776 | rk_strpoolfree(strpool);
|
---|
777 | return EINVAL;
|
---|
778 | }
|
---|
779 | p += 2;
|
---|
780 | value = hx509_env_lfind(context, env, p, p2 - p);
|
---|
781 | if (value == NULL) {
|
---|
782 | hx509_set_error_string(context, 0, EINVAL,
|
---|
783 | "variable %.*s missing",
|
---|
784 | (int)(p2 - p), p);
|
---|
785 | rk_strpoolfree(strpool);
|
---|
786 | return EINVAL;
|
---|
787 | }
|
---|
788 | strpool = rk_strpoolprintf(strpool, "%s", value);
|
---|
789 | if (strpool == NULL) {
|
---|
790 | hx509_set_error_string(context, 0, ENOMEM, "out of memory");
|
---|
791 | return ENOMEM;
|
---|
792 | }
|
---|
793 | p2++;
|
---|
794 |
|
---|
795 | p = strstr(p2, "${");
|
---|
796 | if (p)
|
---|
797 | strpool = rk_strpoolprintf(strpool, "%.*s",
|
---|
798 | (int)(p - p2), p2);
|
---|
799 | else
|
---|
800 | strpool = rk_strpoolprintf(strpool, "%s", p2);
|
---|
801 | if (strpool == NULL) {
|
---|
802 | hx509_set_error_string(context, 0, ENOMEM, "out of memory");
|
---|
803 | return ENOMEM;
|
---|
804 | }
|
---|
805 | }
|
---|
806 | if (strpool) {
|
---|
807 | free(ds->u.utf8String);
|
---|
808 | ds->u.utf8String = rk_strpoolcollect(strpool);
|
---|
809 | if (ds->u.utf8String == NULL) {
|
---|
810 | hx509_set_error_string(context, 0, ENOMEM, "out of memory");
|
---|
811 | return ENOMEM;
|
---|
812 | }
|
---|
813 | }
|
---|
814 | }
|
---|
815 | }
|
---|
816 | return 0;
|
---|
817 | }
|
---|
818 |
|
---|
819 | /**
|
---|
820 | * Free a hx509 name object, upond return *name will be NULL.
|
---|
821 | *
|
---|
822 | * @param name a hx509 name object to be freed.
|
---|
823 | *
|
---|
824 | * @ingroup hx509_name
|
---|
825 | */
|
---|
826 |
|
---|
827 | void
|
---|
828 | hx509_name_free(hx509_name *name)
|
---|
829 | {
|
---|
830 | free_Name(&(*name)->der_name);
|
---|
831 | memset(*name, 0, sizeof(**name));
|
---|
832 | free(*name);
|
---|
833 | *name = NULL;
|
---|
834 | }
|
---|
835 |
|
---|
836 | /**
|
---|
837 | * Convert a DER encoded name info a string.
|
---|
838 | *
|
---|
839 | * @param data data to a DER/BER encoded name
|
---|
840 | * @param length length of data
|
---|
841 | * @param str the resulting string, is NULL on failure.
|
---|
842 | *
|
---|
843 | * @return An hx509 error code, see hx509_get_error_string().
|
---|
844 | *
|
---|
845 | * @ingroup hx509_name
|
---|
846 | */
|
---|
847 |
|
---|
848 | int
|
---|
849 | hx509_unparse_der_name(const void *data, size_t length, char **str)
|
---|
850 | {
|
---|
851 | Name name;
|
---|
852 | int ret;
|
---|
853 |
|
---|
854 | *str = NULL;
|
---|
855 |
|
---|
856 | ret = decode_Name(data, length, &name, NULL);
|
---|
857 | if (ret)
|
---|
858 | return ret;
|
---|
859 | ret = _hx509_Name_to_string(&name, str);
|
---|
860 | free_Name(&name);
|
---|
861 | return ret;
|
---|
862 | }
|
---|
863 |
|
---|
864 | /**
|
---|
865 | * Convert a hx509_name object to DER encoded name.
|
---|
866 | *
|
---|
867 | * @param name name to concert
|
---|
868 | * @param os data to a DER encoded name, free the resulting octet
|
---|
869 | * string with hx509_xfree(os->data).
|
---|
870 | *
|
---|
871 | * @return An hx509 error code, see hx509_get_error_string().
|
---|
872 | *
|
---|
873 | * @ingroup hx509_name
|
---|
874 | */
|
---|
875 |
|
---|
876 | int
|
---|
877 | hx509_name_binary(const hx509_name name, heim_octet_string *os)
|
---|
878 | {
|
---|
879 | size_t size;
|
---|
880 | int ret;
|
---|
881 |
|
---|
882 | ASN1_MALLOC_ENCODE(Name, os->data, os->length, &name->der_name, &size, ret);
|
---|
883 | if (ret)
|
---|
884 | return ret;
|
---|
885 | if (os->length != size)
|
---|
886 | _hx509_abort("internal ASN.1 encoder error");
|
---|
887 |
|
---|
888 | return 0;
|
---|
889 | }
|
---|
890 |
|
---|
891 | int
|
---|
892 | _hx509_unparse_Name(const Name *aname, char **str)
|
---|
893 | {
|
---|
894 | hx509_name name;
|
---|
895 | int ret;
|
---|
896 |
|
---|
897 | ret = _hx509_name_from_Name(aname, &name);
|
---|
898 | if (ret)
|
---|
899 | return ret;
|
---|
900 |
|
---|
901 | ret = hx509_name_to_string(name, str);
|
---|
902 | hx509_name_free(&name);
|
---|
903 | return ret;
|
---|
904 | }
|
---|
905 |
|
---|
906 | /**
|
---|
907 | * Unparse the hx509 name in name into a string.
|
---|
908 | *
|
---|
909 | * @param name the name to check if its empty/null.
|
---|
910 | *
|
---|
911 | * @return non zero if the name is empty/null.
|
---|
912 | *
|
---|
913 | * @ingroup hx509_name
|
---|
914 | */
|
---|
915 |
|
---|
916 | int
|
---|
917 | hx509_name_is_null_p(const hx509_name name)
|
---|
918 | {
|
---|
919 | return name->der_name.u.rdnSequence.len == 0;
|
---|
920 | }
|
---|
921 |
|
---|
922 | /**
|
---|
923 | * Unparse the hx509 name in name into a string.
|
---|
924 | *
|
---|
925 | * @param name the name to print
|
---|
926 | * @param str an allocated string returns the name in string form
|
---|
927 | *
|
---|
928 | * @return An hx509 error code, see hx509_get_error_string().
|
---|
929 | *
|
---|
930 | * @ingroup hx509_name
|
---|
931 | */
|
---|
932 |
|
---|
933 | int
|
---|
934 | hx509_general_name_unparse(GeneralName *name, char **str)
|
---|
935 | {
|
---|
936 | struct rk_strpool *strpool = NULL;
|
---|
937 |
|
---|
938 | *str = NULL;
|
---|
939 |
|
---|
940 | switch (name->element) {
|
---|
941 | case choice_GeneralName_otherName: {
|
---|
942 | char *oid;
|
---|
943 | hx509_oid_sprint(&name->u.otherName.type_id, &oid);
|
---|
944 | if (oid == NULL)
|
---|
945 | return ENOMEM;
|
---|
946 | strpool = rk_strpoolprintf(strpool, "otherName: %s", oid);
|
---|
947 | free(oid);
|
---|
948 | break;
|
---|
949 | }
|
---|
950 | case choice_GeneralName_rfc822Name:
|
---|
951 | strpool = rk_strpoolprintf(strpool, "rfc822Name: %.*s\n",
|
---|
952 | (int)name->u.rfc822Name.length,
|
---|
953 | (char *)name->u.rfc822Name.data);
|
---|
954 | break;
|
---|
955 | case choice_GeneralName_dNSName:
|
---|
956 | strpool = rk_strpoolprintf(strpool, "dNSName: %.*s\n",
|
---|
957 | (int)name->u.dNSName.length,
|
---|
958 | (char *)name->u.dNSName.data);
|
---|
959 | break;
|
---|
960 | case choice_GeneralName_directoryName: {
|
---|
961 | Name dir;
|
---|
962 | char *s;
|
---|
963 | int ret;
|
---|
964 | memset(&dir, 0, sizeof(dir));
|
---|
965 | dir.element = name->u.directoryName.element;
|
---|
966 | dir.u.rdnSequence = name->u.directoryName.u.rdnSequence;
|
---|
967 | ret = _hx509_unparse_Name(&dir, &s);
|
---|
968 | if (ret)
|
---|
969 | return ret;
|
---|
970 | strpool = rk_strpoolprintf(strpool, "directoryName: %s", s);
|
---|
971 | free(s);
|
---|
972 | break;
|
---|
973 | }
|
---|
974 | case choice_GeneralName_uniformResourceIdentifier:
|
---|
975 | strpool = rk_strpoolprintf(strpool, "URI: %.*s",
|
---|
976 | (int)name->u.uniformResourceIdentifier.length,
|
---|
977 | (char *)name->u.uniformResourceIdentifier.data);
|
---|
978 | break;
|
---|
979 | case choice_GeneralName_iPAddress: {
|
---|
980 | unsigned char *a = name->u.iPAddress.data;
|
---|
981 |
|
---|
982 | strpool = rk_strpoolprintf(strpool, "IPAddress: ");
|
---|
983 | if (strpool == NULL)
|
---|
984 | break;
|
---|
985 | if (name->u.iPAddress.length == 4)
|
---|
986 | strpool = rk_strpoolprintf(strpool, "%d.%d.%d.%d",
|
---|
987 | a[0], a[1], a[2], a[3]);
|
---|
988 | else if (name->u.iPAddress.length == 16)
|
---|
989 | strpool = rk_strpoolprintf(strpool,
|
---|
990 | "%02X:%02X:%02X:%02X:"
|
---|
991 | "%02X:%02X:%02X:%02X:"
|
---|
992 | "%02X:%02X:%02X:%02X:"
|
---|
993 | "%02X:%02X:%02X:%02X",
|
---|
994 | a[0], a[1], a[2], a[3],
|
---|
995 | a[4], a[5], a[6], a[7],
|
---|
996 | a[8], a[9], a[10], a[11],
|
---|
997 | a[12], a[13], a[14], a[15]);
|
---|
998 | else
|
---|
999 | strpool = rk_strpoolprintf(strpool,
|
---|
1000 | "unknown IP address of length %lu",
|
---|
1001 | (unsigned long)name->u.iPAddress.length);
|
---|
1002 | break;
|
---|
1003 | }
|
---|
1004 | case choice_GeneralName_registeredID: {
|
---|
1005 | char *oid;
|
---|
1006 | hx509_oid_sprint(&name->u.registeredID, &oid);
|
---|
1007 | if (oid == NULL)
|
---|
1008 | return ENOMEM;
|
---|
1009 | strpool = rk_strpoolprintf(strpool, "registeredID: %s", oid);
|
---|
1010 | free(oid);
|
---|
1011 | break;
|
---|
1012 | }
|
---|
1013 | default:
|
---|
1014 | return EINVAL;
|
---|
1015 | }
|
---|
1016 | if (strpool == NULL)
|
---|
1017 | return ENOMEM;
|
---|
1018 |
|
---|
1019 | *str = rk_strpoolcollect(strpool);
|
---|
1020 |
|
---|
1021 | return 0;
|
---|
1022 | }
|
---|