]>
Commit | Line | Data |
---|---|---|
d45ec1de MW |
1 | /** |
2 | * @file identification.c | |
3 | * | |
4 | * @brief Implementation of identification_t. | |
5 | * | |
6 | */ | |
7 | ||
8 | /* | |
9 | * Copyright (C) 2005 Jan Hutter, Martin Willi | |
10 | * Hochschule fuer Technik Rapperswil | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify it | |
13 | * under the terms of the GNU General Public License as published by the | |
14 | * Free Software Foundation; either version 2 of the License, or (at your | |
15 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, but | |
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
19 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | * for more details. | |
21 | */ | |
22 | ||
a8c09d8c | 23 | #define _GNU_SOURCE |
d45ec1de MW |
24 | #include <sys/socket.h> |
25 | #include <netinet/in.h> | |
26 | #include <arpa/inet.h> | |
5113680f | 27 | #include <string.h> |
a8c09d8c MW |
28 | #include <stdio.h> |
29 | #include <ctype.h> | |
d45ec1de MW |
30 | |
31 | #include "identification.h" | |
32 | ||
a8c09d8c MW |
33 | #include <asn1/asn1.h> |
34 | ||
1b3f92d2 JH |
35 | /** |
36 | * String mappings for id_type_t. | |
37 | */ | |
38 | mapping_t id_type_m[] = { | |
9151843f MW |
39 | {ID_IPV4_ADDR, "ID_IPV4_ADDR"}, |
40 | {ID_FQDN, "ID_FQDN"}, | |
41 | {ID_RFC822_ADDR, "ID_RFC822_ADDR"}, | |
42 | {ID_IPV6_ADDR, "ID_IPV6_ADDR"}, | |
43 | {ID_DER_ASN1_DN, "ID_DER_ASN1_DN"}, | |
44 | {ID_DER_ASN1_GN, "ID_DER_ASN1_GN"}, | |
45 | {ID_KEY_ID, "ID_KEY_ID"}, | |
a8c09d8c | 46 | {ID_ANY, "ID_ANY"}, |
9151843f | 47 | {MAPPING_END, NULL} |
1b3f92d2 JH |
48 | }; |
49 | ||
50 | ||
a8c09d8c MW |
51 | /** |
52 | * X.501 acronyms for well known object identifiers (OIDs) | |
53 | */ | |
54 | static u_char oid_ND[] = { | |
55 | 0x02, 0x82, 0x06, 0x01, | |
56 | 0x0A, 0x07, 0x14 | |
57 | }; | |
58 | static u_char oid_UID[] = { | |
59 | 0x09, 0x92, 0x26, 0x89, 0x93, | |
60 | 0xF2, 0x2C, 0x64, 0x01, 0x01 | |
61 | }; | |
62 | static u_char oid_DC[] = { | |
63 | 0x09, 0x92, 0x26, 0x89, 0x93, | |
64 | 0xF2, 0x2C, 0x64, 0x01, 0x19 | |
65 | }; | |
66 | static u_char oid_CN[] = { | |
67 | 0x55, 0x04, 0x03 | |
68 | }; | |
69 | static u_char oid_S[] = { | |
70 | 0x55, 0x04, 0x04 | |
71 | }; | |
72 | static u_char oid_SN[] = { | |
73 | 0x55, 0x04, 0x05 | |
74 | }; | |
75 | static u_char oid_C[] = { | |
76 | 0x55, 0x04, 0x06 | |
77 | }; | |
78 | static u_char oid_L[] = { | |
79 | 0x55, 0x04, 0x07 | |
80 | }; | |
81 | static u_char oid_ST[] = { | |
82 | 0x55, 0x04, 0x08 | |
83 | }; | |
84 | static u_char oid_O[] = { | |
85 | 0x55, 0x04, 0x0A | |
86 | }; | |
87 | static u_char oid_OU[] = { | |
88 | 0x55, 0x04, 0x0B | |
89 | }; | |
90 | static u_char oid_T[] = { | |
91 | 0x55, 0x04, 0x0C | |
92 | }; | |
93 | static u_char oid_D[] = { | |
94 | 0x55, 0x04, 0x0D | |
95 | }; | |
96 | static u_char oid_N[] = { | |
97 | 0x55, 0x04, 0x29 | |
98 | }; | |
99 | static u_char oid_G[] = { | |
100 | 0x55, 0x04, 0x2A | |
101 | }; | |
102 | static u_char oid_I[] = { | |
103 | 0x55, 0x04, 0x2B | |
104 | }; | |
105 | static u_char oid_ID[] = { | |
106 | 0x55, 0x04, 0x2D | |
107 | }; | |
108 | static u_char oid_EN[] = { | |
109 | 0x60, 0x86, 0x48, 0x01, 0x86, | |
110 | 0xF8, 0x42, 0x03, 0x01, 0x03 | |
111 | }; | |
112 | static u_char oid_E[] = { | |
113 | 0x2A, 0x86, 0x48, 0x86, 0xF7, | |
114 | 0x0D, 0x01, 0x09, 0x01 | |
115 | }; | |
116 | static u_char oid_UN[] = { | |
117 | 0x2A, 0x86, 0x48, 0x86, 0xF7, | |
118 | 0x0D, 0x01, 0x09, 0x02 | |
119 | }; | |
120 | static u_char oid_TCGID[] = { | |
121 | 0x2B, 0x06, 0x01, 0x04, 0x01, 0x89, | |
122 | 0x31, 0x01, 0x01, 0x02, 0x02, 0x4B | |
123 | }; | |
124 | ||
125 | /** | |
126 | * coding of X.501 distinguished name | |
127 | */ | |
128 | typedef struct { | |
129 | const u_char *name; | |
130 | chunk_t oid; | |
131 | u_char type; | |
132 | } x501rdn_t; | |
133 | ||
134 | static const x501rdn_t x501rdns[] = { | |
135 | {"ND", {oid_ND, 7}, ASN1_PRINTABLESTRING}, | |
136 | {"UID", {oid_UID, 10}, ASN1_PRINTABLESTRING}, | |
137 | {"DC", {oid_DC, 10}, ASN1_PRINTABLESTRING}, | |
138 | {"CN", {oid_CN, 3}, ASN1_PRINTABLESTRING}, | |
139 | {"S", {oid_S, 3}, ASN1_PRINTABLESTRING}, | |
140 | {"SN", {oid_SN, 3}, ASN1_PRINTABLESTRING}, | |
141 | {"serialNumber", {oid_SN, 3}, ASN1_PRINTABLESTRING}, | |
142 | {"C", {oid_C, 3}, ASN1_PRINTABLESTRING}, | |
143 | {"L", {oid_L, 3}, ASN1_PRINTABLESTRING}, | |
144 | {"ST", {oid_ST, 3}, ASN1_PRINTABLESTRING}, | |
145 | {"O", {oid_O, 3}, ASN1_PRINTABLESTRING}, | |
146 | {"OU", {oid_OU, 3}, ASN1_PRINTABLESTRING}, | |
147 | {"T", {oid_T, 3}, ASN1_PRINTABLESTRING}, | |
148 | {"D", {oid_D, 3}, ASN1_PRINTABLESTRING}, | |
149 | {"N", {oid_N, 3}, ASN1_PRINTABLESTRING}, | |
150 | {"G", {oid_G, 3}, ASN1_PRINTABLESTRING}, | |
151 | {"I", {oid_I, 3}, ASN1_PRINTABLESTRING}, | |
152 | {"ID", {oid_ID, 3}, ASN1_PRINTABLESTRING}, | |
153 | {"EN", {oid_EN, 10}, ASN1_PRINTABLESTRING}, | |
154 | {"employeeNumber", {oid_EN, 10}, ASN1_PRINTABLESTRING}, | |
155 | {"E", {oid_E, 9}, ASN1_IA5STRING}, | |
156 | {"Email", {oid_E, 9}, ASN1_IA5STRING}, | |
157 | {"emailAddress", {oid_E, 9}, ASN1_IA5STRING}, | |
158 | {"UN", {oid_UN, 9}, ASN1_IA5STRING}, | |
159 | {"unstructuredName",{oid_UN, 9}, ASN1_IA5STRING}, | |
160 | {"TCGID", {oid_TCGID, 12}, ASN1_PRINTABLESTRING} | |
161 | }; | |
162 | #define X501_RDN_ROOF 26 | |
163 | ||
164 | /** | |
165 | * Different kinds of generalNames | |
166 | */ | |
167 | enum generalNames_t { | |
168 | GN_OTHER_NAME = 0, | |
169 | GN_RFC822_NAME = 1, | |
170 | GN_DNS_NAME = 2, | |
171 | GN_X400_ADDRESS = 3, | |
172 | GN_DIRECTORY_NAME = 4, | |
173 | GN_EDI_PARTY_NAME = 5, | |
174 | GN_URI = 6, | |
175 | GN_IP_ADDRESS = 7, | |
176 | GN_REGISTERED_ID = 8, | |
177 | }; | |
178 | ||
179 | /** | |
180 | * ASN.1 definition of generalName | |
181 | */ | |
182 | static const asn1Object_t generalNameObjects[] = { | |
183 | { 0, "otherName", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_BODY }, /* 0 */ | |
184 | { 0, "end choice", ASN1_EOC, ASN1_END }, /* 1 */ | |
185 | { 0, "rfc822Name", ASN1_CONTEXT_S_1, ASN1_OPT|ASN1_BODY }, /* 2 */ | |
186 | { 0, "end choice", ASN1_EOC, ASN1_END }, /* 3 */ | |
187 | { 0, "dnsName", ASN1_CONTEXT_S_2, ASN1_OPT|ASN1_BODY }, /* 4 */ | |
188 | { 0, "end choice", ASN1_EOC, ASN1_END }, /* 5 */ | |
189 | { 0, "x400Address", ASN1_CONTEXT_S_3, ASN1_OPT|ASN1_BODY }, /* 6 */ | |
190 | { 0, "end choice", ASN1_EOC, ASN1_END }, /* 7 */ | |
191 | { 0, "directoryName", ASN1_CONTEXT_C_4, ASN1_OPT|ASN1_BODY }, /* 8 */ | |
192 | { 0, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ | |
193 | { 0, "ediPartyName", ASN1_CONTEXT_C_5, ASN1_OPT|ASN1_BODY }, /* 10 */ | |
194 | { 0, "end choice", ASN1_EOC, ASN1_END }, /* 11 */ | |
195 | { 0, "URI", ASN1_CONTEXT_S_6, ASN1_OPT|ASN1_BODY }, /* 12 */ | |
196 | { 0, "end choice", ASN1_EOC, ASN1_END }, /* 13 */ | |
197 | { 0, "ipAddress", ASN1_CONTEXT_S_7, ASN1_OPT|ASN1_BODY }, /* 14 */ | |
198 | { 0, "end choice", ASN1_EOC, ASN1_END }, /* 15 */ | |
199 | { 0, "registeredID", ASN1_CONTEXT_S_8, ASN1_OPT|ASN1_BODY }, /* 16 */ | |
200 | { 0, "end choice", ASN1_EOC, ASN1_END } /* 17 */ | |
201 | }; | |
202 | #define GN_OBJ_OTHER_NAME 0 | |
203 | #define GN_OBJ_RFC822_NAME 2 | |
204 | #define GN_OBJ_DNS_NAME 4 | |
205 | #define GN_OBJ_X400_ADDRESS 6 | |
206 | #define GN_OBJ_DIRECTORY_NAME 8 | |
207 | #define GN_OBJ_EDI_PARTY_NAME 10 | |
208 | #define GN_OBJ_URI 12 | |
209 | #define GN_OBJ_IP_ADDRESS 14 | |
210 | #define GN_OBJ_REGISTERED_ID 16 | |
211 | #define GN_OBJ_ROOF 18 | |
212 | ||
213 | /** | |
214 | * ASN.1 definition of otherName | |
215 | */ | |
216 | static const asn1Object_t otherNameObjects[] = { | |
217 | {0, "type-id", ASN1_OID, ASN1_BODY }, /* 0 */ | |
218 | {0, "value", ASN1_CONTEXT_C_0, ASN1_BODY } /* 1 */ | |
219 | }; | |
220 | #define ON_OBJ_ID_TYPE 0 | |
221 | #define ON_OBJ_VALUE 1 | |
222 | #define ON_OBJ_ROOF 2 | |
223 | ||
d45ec1de MW |
224 | typedef struct private_identification_t private_identification_t; |
225 | ||
226 | /** | |
227 | * Private data of an identification_t object. | |
228 | */ | |
229 | struct private_identification_t { | |
230 | /** | |
231 | * Public interface. | |
232 | */ | |
233 | identification_t public; | |
234 | ||
235 | /** | |
d794bcdb | 236 | * String representation of this ID. |
d45ec1de MW |
237 | */ |
238 | char *string; | |
239 | ||
240 | /** | |
d794bcdb | 241 | * Encoded representation of this ID. |
d45ec1de MW |
242 | */ |
243 | chunk_t encoded; | |
244 | ||
245 | /** | |
d794bcdb | 246 | * Type of this ID. |
d45ec1de MW |
247 | */ |
248 | id_type_t type; | |
249 | }; | |
250 | ||
16b9a73c MW |
251 | static private_identification_t *identification_create(); |
252 | ||
a8c09d8c MW |
253 | |
254 | /** | |
255 | * updates a chunk (!????) | |
256 | * TODO: We should reconsider this stuff, its not really clear | |
257 | */ | |
258 | static void update_chunk(chunk_t *ch, int n) | |
259 | { | |
260 | n = (n > -1 && n < (int)ch->len)? n : (int)ch->len-1; | |
261 | ch->ptr += n; ch->len -= n; | |
262 | } | |
263 | ||
264 | /** | |
265 | * Prints a binary string in hexadecimal form | |
266 | */ | |
267 | void hex_str(chunk_t bin, chunk_t *str) | |
268 | { | |
269 | u_int i; | |
270 | update_chunk(str, snprintf(str->ptr,str->len,"0x")); | |
271 | for (i=0; i < bin.len; i++) | |
272 | { | |
273 | update_chunk(str, snprintf(str->ptr,str->len,"%02X",*bin.ptr++)); | |
274 | } | |
275 | } | |
276 | ||
277 | /** | |
278 | * Pointer is set to the first RDN in a DN | |
279 | */ | |
280 | static status_t init_rdn(chunk_t dn, chunk_t *rdn, chunk_t *attribute, bool *next) | |
281 | { | |
282 | *rdn = CHUNK_INITIALIZER; | |
283 | *attribute = CHUNK_INITIALIZER; | |
284 | ||
285 | /* a DN is a SEQUENCE OF RDNs */ | |
286 | if (*dn.ptr != ASN1_SEQUENCE) | |
287 | { | |
288 | /* DN is not a SEQUENCE */ | |
289 | return FAILED; | |
290 | } | |
291 | ||
292 | rdn->len = asn1_length(&dn); | |
293 | ||
294 | if (rdn->len == ASN1_INVALID_LENGTH) | |
295 | { | |
296 | /* Invalid RDN length */ | |
297 | return FAILED; | |
298 | } | |
299 | ||
300 | rdn->ptr = dn.ptr; | |
301 | ||
302 | /* are there any RDNs ? */ | |
303 | *next = rdn->len > 0; | |
304 | ||
305 | return SUCCESS; | |
306 | } | |
307 | ||
308 | /** | |
309 | * Fetches the next RDN in a DN | |
310 | */ | |
311 | static status_t get_next_rdn(chunk_t *rdn, chunk_t * attribute, chunk_t *oid, chunk_t *value, asn1_t *type, bool *next) | |
312 | { | |
313 | chunk_t body; | |
314 | ||
315 | /* initialize return values */ | |
316 | *oid = CHUNK_INITIALIZER; | |
317 | *value = CHUNK_INITIALIZER; | |
318 | ||
319 | /* if all attributes have been parsed, get next rdn */ | |
320 | if (attribute->len <= 0) | |
321 | { | |
322 | /* an RDN is a SET OF attributeTypeAndValue */ | |
323 | if (*rdn->ptr != ASN1_SET) | |
324 | { | |
325 | /* RDN is not a SET */ | |
326 | return FAILED; | |
327 | } | |
328 | attribute->len = asn1_length(rdn); | |
329 | if (attribute->len == ASN1_INVALID_LENGTH) | |
330 | { | |
331 | /* Invalid attribute length */ | |
332 | return FAILED; | |
333 | } | |
334 | attribute->ptr = rdn->ptr; | |
335 | /* advance to start of next RDN */ | |
336 | rdn->ptr += attribute->len; | |
337 | rdn->len -= attribute->len; | |
338 | } | |
339 | ||
340 | /* an attributeTypeAndValue is a SEQUENCE */ | |
341 | if (*attribute->ptr != ASN1_SEQUENCE) | |
342 | { | |
343 | /* attributeTypeAndValue is not a SEQUENCE */ | |
344 | return FAILED; | |
345 | } | |
346 | ||
347 | /* extract the attribute body */ | |
348 | body.len = asn1_length(attribute); | |
349 | ||
350 | if (body.len == ASN1_INVALID_LENGTH) | |
351 | { | |
352 | /* Invalid attribute body length */ | |
353 | return FAILED; | |
354 | } | |
355 | ||
356 | body.ptr = attribute->ptr; | |
357 | ||
358 | /* advance to start of next attribute */ | |
359 | attribute->ptr += body.len; | |
360 | attribute->len -= body.len; | |
361 | ||
362 | /* attribute type is an OID */ | |
363 | if (*body.ptr != ASN1_OID) | |
364 | { | |
365 | /* attributeType is not an OID */ | |
366 | return FAILED; | |
367 | } | |
368 | /* extract OID */ | |
369 | oid->len = asn1_length(&body); | |
370 | ||
371 | if (oid->len == ASN1_INVALID_LENGTH) | |
372 | { | |
373 | /* Invalid attribute OID length */ | |
374 | return FAILED; | |
375 | } | |
376 | oid->ptr = body.ptr; | |
377 | ||
378 | /* advance to the attribute value */ | |
379 | body.ptr += oid->len; | |
380 | body.len -= oid->len; | |
381 | ||
382 | /* extract string type */ | |
383 | *type = *body.ptr; | |
384 | ||
385 | /* extract string value */ | |
386 | value->len = asn1_length(&body); | |
387 | ||
388 | if (value->len == ASN1_INVALID_LENGTH) | |
389 | { | |
390 | /* Invalid attribute string length */ | |
391 | return FAILED; | |
392 | } | |
393 | value->ptr = body.ptr; | |
394 | ||
395 | /* are there any RDNs left? */ | |
396 | *next = rdn->len > 0 || attribute->len > 0; | |
397 | return SUCCESS; | |
398 | } | |
399 | ||
400 | /** | |
401 | * Parses an ASN.1 distinguished name int its OID/value pairs | |
402 | */ | |
403 | static status_t dntoa(chunk_t dn, chunk_t *str) | |
404 | { | |
405 | chunk_t rdn, oid, attribute, value; | |
406 | asn1_t type; | |
407 | int oid_code; | |
408 | bool next; | |
409 | bool first = TRUE; | |
410 | ||
411 | status_t status = init_rdn(dn, &rdn, &attribute, &next); | |
412 | ||
413 | if (status != SUCCESS) | |
414 | {/* a parsing error has occured */ | |
415 | return status; | |
416 | } | |
417 | ||
418 | while (next) | |
419 | { | |
420 | status = get_next_rdn(&rdn, &attribute, &oid, &value, &type, &next); | |
421 | ||
422 | if (status != SUCCESS) | |
423 | {/* a parsing error has occured */ | |
424 | return status; | |
425 | } | |
426 | ||
427 | if (first) | |
428 | { /* first OID/value pair */ | |
429 | first = FALSE; | |
430 | } | |
431 | else | |
432 | { /* separate OID/value pair by a comma */ | |
433 | update_chunk(str, snprintf(str->ptr,str->len,", ")); | |
434 | } | |
435 | ||
436 | /* print OID */ | |
437 | oid_code = known_oid(oid); | |
438 | if (oid_code == OID_UNKNOWN) | |
439 | { /* OID not found in list */ | |
440 | hex_str(oid, str); | |
441 | } | |
442 | else | |
443 | { | |
444 | update_chunk(str, snprintf(str->ptr,str->len,"%s", oid_names[oid_code].name)); | |
445 | } | |
446 | /* print value */ | |
447 | update_chunk(str, snprintf(str->ptr,str->len,"=%.*s", (int)value.len,value.ptr)); | |
448 | } | |
449 | return SUCCESS; | |
450 | } | |
451 | ||
452 | /** | |
453 | * compare two distinguished names by | |
454 | * comparing the individual RDNs | |
455 | */ | |
456 | static bool same_dn(chunk_t a, chunk_t b) | |
457 | { | |
458 | chunk_t rdn_a, rdn_b, attribute_a, attribute_b; | |
459 | chunk_t oid_a, oid_b, value_a, value_b; | |
460 | asn1_t type_a, type_b; | |
461 | bool next_a, next_b; | |
462 | ||
463 | /* same lengths for the DNs */ | |
464 | if (a.len != b.len) | |
465 | { | |
466 | return FALSE; | |
467 | } | |
468 | /* try a binary comparison first */ | |
469 | if (memcmp(a.ptr, b.ptr, b.len) == 0) | |
470 | { | |
471 | return TRUE; | |
472 | } | |
473 | ||
474 | /* initialize DN parsing */ | |
475 | if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS || | |
476 | init_rdn(b, &rdn_b, &attribute_b, &next_b) != SUCCESS) | |
477 | { | |
478 | return FALSE; | |
479 | } | |
480 | ||
481 | /* fetch next RDN pair */ | |
482 | while (next_a && next_b) | |
483 | { | |
484 | /* parse next RDNs and check for errors */ | |
485 | if (get_next_rdn(&rdn_a, &attribute_a, &oid_a, &value_a, &type_a, &next_a) != SUCCESS || | |
486 | get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != SUCCESS) | |
487 | { | |
488 | return FALSE; | |
489 | } | |
490 | /* OIDs must agree */ | |
491 | if (oid_a.len != oid_b.len || memcmp(oid_a.ptr, oid_b.ptr, oid_b.len) != 0) | |
492 | { | |
493 | return FALSE; | |
494 | } | |
495 | /* same lengths for values */ | |
496 | if (value_a.len != value_b.len) | |
497 | { | |
498 | return FALSE; | |
499 | } | |
500 | /* printableStrings and email RDNs require uppercase comparison */ | |
501 | if (type_a == type_b && (type_a == ASN1_PRINTABLESTRING || | |
502 | (type_a == ASN1_IA5STRING && known_oid(oid_a) == OID_PKCS9_EMAIL))) | |
503 | { | |
504 | if (strncasecmp(value_a.ptr, value_b.ptr, value_b.len) != 0) | |
505 | { | |
506 | return FALSE; | |
507 | } | |
508 | } | |
509 | else | |
510 | { | |
511 | if (strncmp(value_a.ptr, value_b.ptr, value_b.len) != 0) | |
512 | { | |
513 | return FALSE; | |
514 | } | |
515 | } | |
516 | } | |
517 | /* both DNs must have same number of RDNs */ | |
518 | if (next_a || next_b) | |
519 | return FALSE; | |
520 | ||
521 | /* the two DNs are equal! */ | |
522 | return TRUE; | |
523 | } | |
524 | ||
525 | ||
526 | /** | |
527 | * compare two distinguished names by comparing the individual RDNs. | |
528 | * A single'*' character designates a wildcard RDN in DN b. | |
529 | * TODO: Add support for different RDN order in DN !! | |
530 | */ | |
531 | bool match_dn(chunk_t a, chunk_t b, int *wildcards) | |
532 | { | |
533 | chunk_t rdn_a, rdn_b, attribute_a, attribute_b; | |
534 | chunk_t oid_a, oid_b, value_a, value_b; | |
535 | asn1_t type_a, type_b; | |
536 | bool next_a, next_b; | |
537 | ||
538 | /* initialize wildcard counter */ | |
539 | *wildcards = 0; | |
540 | ||
541 | /* initialize DN parsing */ | |
542 | if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS || | |
543 | init_rdn(b, &rdn_b, &attribute_b, &next_b) != SUCCESS) | |
544 | { | |
545 | return FALSE; | |
546 | } | |
547 | /* fetch next RDN pair */ | |
548 | while (next_a && next_b) | |
549 | { | |
550 | /* parse next RDNs and check for errors */ | |
551 | if (get_next_rdn(&rdn_a, &attribute_a, &oid_a, &value_a, &type_a, &next_a) != SUCCESS || | |
552 | get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != SUCCESS) | |
553 | { | |
554 | return FALSE; | |
555 | } | |
556 | /* OIDs must agree */ | |
557 | if (oid_a.len != oid_b.len || memcmp(oid_a.ptr, oid_b.ptr, oid_b.len) != 0) | |
558 | { | |
559 | return FALSE; | |
560 | } | |
561 | /* does rdn_b contain a wildcard? */ | |
562 | if (value_b.len == 1 && *value_b.ptr == '*') | |
563 | { | |
564 | (*wildcards)++; | |
565 | continue; | |
566 | } | |
567 | /* same lengths for values */ | |
568 | if (value_a.len != value_b.len) | |
569 | { | |
570 | return FALSE; | |
571 | } | |
572 | /* printableStrings and email RDNs require uppercase comparison */ | |
573 | if (type_a == type_b && (type_a == ASN1_PRINTABLESTRING || | |
574 | (type_a == ASN1_IA5STRING && known_oid(oid_a) == OID_PKCS9_EMAIL))) | |
575 | { | |
576 | if (strncasecmp(value_a.ptr, value_b.ptr, value_b.len) != 0) | |
577 | { | |
578 | return FALSE; | |
579 | } | |
580 | } | |
581 | else | |
582 | { | |
583 | if (strncmp(value_a.ptr, value_b.ptr, value_b.len) != 0) | |
584 | { | |
585 | return FALSE; | |
586 | } | |
587 | } | |
588 | } | |
589 | /* both DNs must have same number of RDNs */ | |
590 | if (next_a || next_b) | |
591 | { | |
592 | return FALSE; | |
593 | } | |
594 | /* the two DNs match! */ | |
595 | return TRUE; | |
596 | } | |
597 | ||
598 | /** | |
599 | * get string representation of a general name | |
600 | * TODO: Add support for gn types | |
601 | */ | |
602 | static char *gntoa(chunk_t blob) | |
603 | { | |
604 | asn1_ctx_t ctx; | |
605 | chunk_t object; | |
606 | int objectID = 0; | |
607 | u_int level; | |
608 | char buf[128]; | |
609 | ||
610 | asn1_init(&ctx, blob, 0, FALSE); | |
611 | ||
612 | while (objectID < GN_OBJ_ROOF) | |
613 | { | |
614 | if (!extract_object(generalNameObjects, &objectID, &object, &level, &ctx)) | |
615 | { | |
616 | return NULL; | |
617 | } | |
618 | switch (objectID) | |
619 | { | |
620 | case GN_OBJ_RFC822_NAME: | |
621 | case GN_OBJ_DNS_NAME: | |
622 | case GN_OBJ_URI: | |
623 | snprintf(buf, sizeof(buf), "%.*s", object.len, object.ptr); | |
624 | return strdup(buf); | |
625 | case GN_OBJ_IP_ADDRESS: | |
626 | if (object.len == 4 && | |
627 | inet_ntop(AF_INET, object.ptr, buf, sizeof(buf))) | |
628 | { | |
629 | return strdup(buf); | |
630 | } | |
631 | return NULL; | |
632 | break; | |
633 | case GN_OBJ_OTHER_NAME: | |
634 | return strdup("(other name)"); | |
635 | case GN_OBJ_X400_ADDRESS: | |
636 | return strdup("(X400 Address)"); | |
637 | case GN_OBJ_EDI_PARTY_NAME: | |
638 | return strdup("(EDI party name)"); | |
639 | case GN_OBJ_REGISTERED_ID: | |
640 | return strdup("(registered ID)"); | |
641 | case GN_OBJ_DIRECTORY_NAME: | |
642 | return strdup("(directory name)"); | |
643 | default: | |
644 | break; | |
645 | } | |
646 | objectID++; | |
647 | } | |
648 | return NULL; | |
649 | } | |
650 | ||
651 | /** | |
652 | * Converts an LDAP-style human-readable ASCII-encoded | |
653 | * ASN.1 distinguished name into binary DER-encoded format | |
654 | */ | |
655 | static status_t atodn(char *src, chunk_t *dn) | |
656 | { | |
657 | /* finite state machine for atodn */ | |
658 | typedef enum { | |
659 | SEARCH_OID = 0, | |
660 | READ_OID = 1, | |
661 | SEARCH_NAME = 2, | |
662 | READ_NAME = 3, | |
663 | UNKNOWN_OID = 4 | |
664 | } state_t; | |
665 | ||
666 | char *wrap_mode; | |
667 | chunk_t oid = CHUNK_INITIALIZER; | |
668 | chunk_t name = CHUNK_INITIALIZER; | |
669 | chunk_t names[25]; /* max to 25 rdns */ | |
670 | int name_count = 0; | |
671 | int whitespace = 0; | |
672 | int pos = 0; | |
673 | asn1_t rdn_type; | |
674 | state_t state = SEARCH_OID; | |
675 | status_t status = SUCCESS; | |
676 | ||
677 | do | |
678 | { | |
679 | switch (state) | |
680 | { | |
681 | case SEARCH_OID: | |
682 | if (*src != ' ' && *src != '/' && *src != ',') | |
683 | { | |
684 | oid.ptr = src; | |
685 | oid.len = 1; | |
686 | state = READ_OID; | |
687 | } | |
688 | break; | |
689 | case READ_OID: | |
690 | if (*src != ' ' && *src != '=') | |
691 | { | |
692 | oid.len++; | |
693 | } | |
694 | else | |
695 | { | |
696 | for (pos = 0; pos < X501_RDN_ROOF; pos++) | |
697 | { | |
698 | if (strlen(x501rdns[pos].name) == oid.len && | |
699 | strncasecmp(x501rdns[pos].name, oid.ptr, oid.len) == 0) | |
700 | { | |
701 | break; /* found a valid OID */ | |
702 | } | |
703 | } | |
704 | if (pos == X501_RDN_ROOF) | |
705 | { | |
706 | status = NOT_SUPPORTED; | |
707 | state = UNKNOWN_OID; | |
708 | break; | |
709 | } | |
710 | /* reset oid and change state */ | |
711 | oid = CHUNK_INITIALIZER; | |
712 | state = SEARCH_NAME; | |
713 | } | |
714 | break; | |
715 | case SEARCH_NAME: | |
716 | if (*src != ' ' && *src != '=') | |
717 | { | |
718 | name.ptr = src; | |
719 | name.len = 1; | |
720 | whitespace = 0; | |
721 | state = READ_NAME; | |
722 | } | |
723 | break; | |
724 | case READ_NAME: | |
725 | if (*src != ',' && *src != '/' && *src != '\0') | |
726 | { | |
727 | name.len++; | |
728 | if (*src == ' ') | |
729 | { | |
730 | whitespace++; | |
731 | } | |
732 | else | |
733 | { | |
734 | whitespace = 0; | |
735 | } | |
736 | } | |
737 | else | |
738 | { | |
739 | name.len -= whitespace; | |
740 | rdn_type = (x501rdns[pos].type == ASN1_PRINTABLESTRING | |
741 | && !is_printablestring(name))? ASN1_T61STRING : x501rdns[pos].type; | |
742 | ||
743 | if (name_count < 25) | |
744 | { | |
745 | names[name_count++] = | |
746 | asn1_wrap(ASN1_SET, "m", | |
747 | asn1_wrap(ASN1_SEQUENCE, "mm", | |
748 | asn1_wrap(ASN1_OID, "c", x501rdns[pos].oid), | |
749 | asn1_wrap(rdn_type, "c", name) | |
750 | ) | |
751 | ); | |
752 | } | |
753 | else | |
754 | { | |
755 | status = OUT_OF_RES; | |
756 | } | |
757 | /* reset name and change state */ | |
758 | name = CHUNK_INITIALIZER; | |
759 | state = SEARCH_OID; | |
760 | } | |
761 | break; | |
762 | case UNKNOWN_OID: | |
763 | break; | |
764 | } | |
765 | } while (*src++ != '\0'); | |
766 | ||
767 | ||
768 | /* build the distinguished name sequence */ | |
769 | wrap_mode = alloca(26); | |
770 | memset(wrap_mode, 0, 26); | |
771 | memset(wrap_mode, 'm', name_count); | |
772 | *dn = asn1_wrap(ASN1_SEQUENCE, wrap_mode, | |
773 | names[0], names[1], names[2], names[3], names[4], | |
774 | names[5], names[6], names[7], names[8], names[9], | |
775 | names[10], names[11], names[12], names[13], names[14], | |
776 | names[15], names[16], names[17], names[18], names[19], | |
777 | names[20], names[21], names[22], names[23], names[24]); | |
778 | if (status != SUCCESS) | |
779 | { | |
780 | free(dn->ptr); | |
781 | *dn = CHUNK_INITIALIZER; | |
782 | } | |
783 | return status; | |
784 | } | |
785 | ||
d45ec1de | 786 | /** |
d794bcdb | 787 | * Implementation of identification_t.get_encoding. |
d45ec1de MW |
788 | */ |
789 | static chunk_t get_encoding(private_identification_t *this) | |
790 | { | |
791 | return this->encoded; | |
792 | } | |
793 | ||
794 | /** | |
d794bcdb | 795 | * Implementation of identification_t.get_type. |
d45ec1de MW |
796 | */ |
797 | static id_type_t get_type(private_identification_t *this) | |
798 | { | |
799 | return this->type; | |
800 | } | |
801 | ||
802 | /** | |
d794bcdb | 803 | * Implementation of identification_t.get_string. |
d45ec1de MW |
804 | */ |
805 | static char *get_string(private_identification_t *this) | |
806 | { | |
807 | return this->string; | |
808 | } | |
809 | ||
eea35346 MW |
810 | /** |
811 | * Implementation of identification_t.contains_wildcards. | |
812 | */ | |
813 | static bool contains_wildcards(private_identification_t *this) | |
814 | { | |
815 | if (this->type == ID_ANY || | |
816 | memchr(this->encoded.ptr, '*', this->encoded.len) != NULL) | |
817 | { | |
818 | return TRUE; | |
819 | } | |
820 | return FALSE; | |
821 | } | |
822 | ||
a9428251 | 823 | /** |
a8c09d8c MW |
824 | * Default implementation of identification_t.equals and identification_t.belongs_to. |
825 | * compares encoded chunk for equality. | |
a9428251 | 826 | */ |
a8c09d8c | 827 | static bool equals_binary(private_identification_t *this,private_identification_t *other) |
a9428251 JH |
828 | { |
829 | if (this->type == other->type) | |
830 | { | |
a8c09d8c MW |
831 | if (this->encoded.len == other->encoded.len && |
832 | memcmp(this->encoded.ptr, other->encoded.ptr, this->encoded.len) == 0) | |
a9428251 JH |
833 | { |
834 | return TRUE; | |
835 | } | |
836 | } | |
837 | return FALSE; | |
838 | } | |
839 | ||
87a217f9 | 840 | /** |
a8c09d8c | 841 | * Special implementation of identification_t.equals for ID_DER_ASN1_DN |
87a217f9 | 842 | */ |
a8c09d8c | 843 | static bool equals_dn(private_identification_t *this, private_identification_t *other) |
87a217f9 | 844 | { |
a8c09d8c MW |
845 | return same_dn(this->encoded, other->encoded); |
846 | } | |
847 | ||
848 | /** | |
849 | * Special implementation of identification_t.belongs_to for ID_RFC822_ADDR/ID_FQDN. | |
850 | * checks for a wildcard in other-string, and compares it against this-string. | |
851 | */ | |
852 | static bool belongs_to_wc_string(private_identification_t *this, private_identification_t *other) | |
853 | { | |
854 | char *this_str, *other_str, *pos; | |
855 | ||
eea35346 MW |
856 | if (other->type == ID_ANY) |
857 | { | |
858 | return TRUE; | |
859 | } | |
860 | ||
a8c09d8c | 861 | if (this->type == other->type) |
87a217f9 | 862 | { |
a8c09d8c MW |
863 | /* try a binary comparison first */ |
864 | if (equals_binary(this, other)) | |
865 | { | |
866 | return TRUE; | |
867 | } | |
87a217f9 | 868 | } |
a8c09d8c MW |
869 | if (other->encoded.len > 0 && |
870 | *(other->encoded.ptr) == '*') | |
87a217f9 | 871 | { |
a8c09d8c | 872 | if (other->encoded.len == 1) |
87a217f9 | 873 | { |
a8c09d8c | 874 | /* other contains just a wildcard, and therefore matches anything */ |
87a217f9 MW |
875 | return TRUE; |
876 | } | |
a8c09d8c MW |
877 | /* We strdup chunks, since they are NOT null-terminated */ |
878 | this_str = strndupa(this->encoded.ptr, this->encoded.len); | |
879 | other_str = strndupa(other->encoded.ptr + 1, other->encoded.len - 1); | |
880 | pos = strstr(this_str, other_str); | |
881 | if (pos != NULL) | |
882 | { | |
883 | /* ok, other is contained in this, but there may be more characters, so check it */ | |
884 | if (strlen(pos) == strlen(other_str)) | |
885 | { | |
886 | return TRUE; | |
887 | } | |
888 | } | |
889 | } | |
890 | ||
891 | return FALSE; | |
892 | } | |
893 | ||
894 | /** | |
895 | * Special implementation of identification_t.belongs_to for ID_ANY. | |
eea35346 | 896 | * ANY matches only another ANY, but nothing other |
a8c09d8c MW |
897 | */ |
898 | static bool belongs_to_any(private_identification_t *this, private_identification_t *other) | |
eea35346 MW |
899 | { |
900 | if (other->type == ID_ANY) | |
901 | { | |
902 | return TRUE; | |
903 | } | |
904 | return FALSE; | |
a8c09d8c MW |
905 | } |
906 | ||
907 | /** | |
908 | * Special implementation of identification_t.belongs_to for ID_DER_ASN1_DN. | |
909 | * ANY matches any, even ANY, thats why its there... | |
910 | */ | |
911 | static bool belongs_to_dn(private_identification_t *this, private_identification_t *other) | |
912 | { | |
913 | int wildcards; | |
914 | ||
eea35346 MW |
915 | if (other->type == ID_ANY) |
916 | { | |
917 | return TRUE; | |
918 | } | |
919 | ||
a8c09d8c MW |
920 | if (this->type == other->type) |
921 | { | |
922 | return match_dn(this->encoded, other->encoded, &wildcards); | |
87a217f9 MW |
923 | } |
924 | return FALSE; | |
925 | } | |
926 | ||
16b9a73c MW |
927 | /** |
928 | * Implementation of identification_t.clone. | |
929 | */ | |
930 | static identification_t *clone(private_identification_t *this) | |
931 | { | |
932 | private_identification_t *clone = identification_create(); | |
933 | ||
934 | clone->type = this->type; | |
5113680f MW |
935 | clone->encoded = chunk_clone(this->encoded); |
936 | clone->string = malloc(strlen(this->string) + 1); | |
16b9a73c MW |
937 | strcpy(clone->string, this->string); |
938 | ||
939 | return &clone->public; | |
940 | } | |
941 | ||
d45ec1de | 942 | /** |
d794bcdb | 943 | * Implementation of identification_t.destroy. |
d45ec1de MW |
944 | */ |
945 | static void destroy(private_identification_t *this) | |
946 | { | |
5113680f MW |
947 | free(this->string); |
948 | free(this->encoded.ptr); | |
949 | free(this); | |
d45ec1de MW |
950 | } |
951 | ||
a8c09d8c | 952 | /** |
d794bcdb | 953 | * Generic constructor used for the other constructors. |
d45ec1de MW |
954 | */ |
955 | static private_identification_t *identification_create() | |
956 | { | |
5113680f | 957 | private_identification_t *this = malloc_thing(private_identification_t); |
d45ec1de | 958 | |
d45ec1de MW |
959 | this->public.get_encoding = (chunk_t (*) (identification_t*))get_encoding; |
960 | this->public.get_type = (id_type_t (*) (identification_t*))get_type; | |
961 | this->public.get_string = (char* (*) (identification_t*))get_string; | |
eea35346 | 962 | this->public.contains_wildcards = (bool (*) (identification_t *this))contains_wildcards; |
16b9a73c | 963 | this->public.clone = (identification_t* (*) (identification_t*))clone; |
d45ec1de | 964 | this->public.destroy = (void (*) (identification_t*))destroy; |
a8c09d8c MW |
965 | /* we use these as defaults, the may be overloaded for special ID types */ |
966 | this->public.equals = (bool (*) (identification_t*,identification_t*))equals_binary; | |
967 | this->public.belongs_to = (bool (*) (identification_t*,identification_t*))equals_binary; | |
d45ec1de MW |
968 | |
969 | this->string = NULL; | |
970 | this->encoded = CHUNK_INITIALIZER; | |
971 | ||
972 | return this; | |
973 | } | |
974 | ||
975 | /* | |
976 | * Described in header. | |
977 | */ | |
a8c09d8c | 978 | identification_t *identification_create_from_string(char *string) |
d45ec1de MW |
979 | { |
980 | private_identification_t *this = identification_create(); | |
9151843f | 981 | |
a8c09d8c | 982 | if (strchr(string, '=') != NULL) |
d45ec1de | 983 | { |
a8c09d8c MW |
984 | /* we interpret this as an ASCII X.501 ID_DER_ASN1_DN. |
985 | * convert from LDAP style or openssl x509 -subject style to ASN.1 DN | |
986 | * discard optional @ character in front of DN | |
987 | */ | |
988 | if (atodn((*string == '@') ? string + 1 : string, &this->encoded) != SUCCESS) | |
d45ec1de | 989 | { |
a8c09d8c MW |
990 | free(this); |
991 | return NULL; | |
992 | } | |
993 | this->string = strdup(string); | |
994 | this->type = ID_DER_ASN1_DN; | |
995 | this->public.equals = (bool (*) (identification_t*,identification_t*))equals_dn; | |
996 | this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_dn; | |
997 | return &this->public; | |
998 | } | |
999 | else if (strchr(string, '@') == NULL) | |
1000 | { | |
1001 | if (strcmp(string, "%any") == 0 || | |
1002 | strcmp(string, "0.0.0.0") == 0 || | |
1003 | strcmp(string, "*") == 0 || | |
1004 | strcmp(string, "::") == 0|| | |
1005 | strcmp(string, "0::0") == 0) | |
1006 | { | |
1007 | /* any ID will be accepted */ | |
1008 | this->type = ID_ANY; | |
1009 | this->string = strdup("%any"); | |
1010 | this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_any; | |
1011 | return &this->public; | |
1012 | } | |
1013 | else | |
1014 | { | |
1015 | /* TODO: Pluto resolve domainnames without '@' to IPv4/6 address. Is this really needed? */ | |
1016 | ||
1017 | if (strchr(string, ':') == NULL) | |
1018 | { | |
1019 | /* try IPv4 */ | |
1020 | struct in_addr address; | |
1021 | chunk_t chunk = {(void*)&address, sizeof(address)}; | |
1022 | ||
1023 | if (inet_pton(AF_INET, string, &address) <= 0) | |
1024 | { | |
1025 | free(this); | |
1026 | return NULL; | |
1027 | } | |
1028 | this->encoded = chunk_clone(chunk); | |
1029 | this->string = strdup(string); | |
1030 | this->type = ID_IPV4_ADDR; | |
1031 | return &(this->public); | |
1032 | } | |
1033 | else | |
1034 | { | |
1035 | /* try IPv6 */ | |
1036 | struct in6_addr address; | |
1037 | chunk_t chunk = {(void*)&address, sizeof(address)}; | |
1038 | ||
1039 | if (inet_pton(AF_INET6, string, &address) <= 0) | |
1040 | { | |
1041 | free(this); | |
1042 | return NULL; | |
1043 | } | |
1044 | this->encoded = chunk_clone(chunk); | |
1045 | this->string = strdup(string); | |
1046 | this->type = ID_IPV6_ADDR; | |
1047 | return &(this->public); | |
1048 | } | |
1049 | } | |
1050 | } | |
1051 | else | |
1052 | { | |
1053 | if (*string == '@') | |
1054 | { | |
1055 | if (*(string + 1) == '#') | |
d45ec1de | 1056 | { |
a8c09d8c | 1057 | /* TODO: Pluto handles '#' as hex encoded ASN1/KEY ID. Do we need this, too? */ |
5113680f | 1058 | free(this); |
d45ec1de MW |
1059 | return NULL; |
1060 | } | |
a8c09d8c MW |
1061 | else |
1062 | { | |
1063 | this->type = ID_FQDN; | |
1064 | this->string = strdup(string + 1); /* discard @ */ | |
1065 | this->encoded.ptr = strdup(string + 1); | |
1066 | this->encoded.len = strlen(string + 1); | |
1067 | this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_string; | |
1068 | return &(this->public); | |
1069 | } | |
d45ec1de | 1070 | } |
a8c09d8c | 1071 | else |
d45ec1de | 1072 | { |
a8c09d8c MW |
1073 | this->type = ID_RFC822_ADDR; |
1074 | this->string = strdup(string); | |
1075 | this->encoded.ptr = strdup(string); | |
1076 | this->encoded.len = strlen(string); | |
1077 | this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_string; | |
1078 | return &(this->public); | |
d45ec1de MW |
1079 | } |
1080 | } | |
1081 | } | |
1082 | ||
1083 | /* | |
1084 | * Described in header. | |
1085 | */ | |
1086 | identification_t *identification_create_from_encoding(id_type_t type, chunk_t encoded) | |
1087 | { | |
1088 | private_identification_t *this = identification_create(); | |
a8c09d8c MW |
1089 | char buf[256]; |
1090 | chunk_t buf_chunk = chunk_from_buf(buf); | |
1091 | char *pos; | |
9151843f | 1092 | |
1b3f92d2 | 1093 | this->type = type; |
d45ec1de MW |
1094 | switch (type) |
1095 | { | |
a8c09d8c MW |
1096 | case ID_ANY: |
1097 | this->string = strdup("%any"); | |
1098 | this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_any; | |
1099 | break; | |
d45ec1de | 1100 | case ID_IPV4_ADDR: |
a8c09d8c MW |
1101 | if (encoded.len < sizeof(struct in_addr) || |
1102 | inet_ntop(AF_INET, encoded.ptr, buf, sizeof(buf)) == NULL) | |
1103 | { | |
1104 | this->string = strdup("(invalid ID_IPV4_ADDR)"); | |
1105 | } | |
1106 | else | |
1107 | { | |
1108 | this->string = strdup(buf); | |
1109 | } | |
c7314095 | 1110 | break; |
d45ec1de | 1111 | case ID_IPV6_ADDR: |
a8c09d8c MW |
1112 | if (encoded.len < sizeof(struct in6_addr) || |
1113 | inet_ntop(AF_INET6, encoded.ptr, buf, INET6_ADDRSTRLEN) == NULL) | |
1114 | { | |
1115 | this->string = strdup("(invalid ID_IPV6_ADDR)"); | |
1116 | } | |
1117 | else | |
1118 | { | |
1119 | this->string = strdup(buf); | |
1120 | } | |
9151843f | 1121 | break; |
d45ec1de | 1122 | case ID_FQDN: |
a8c09d8c MW |
1123 | snprintf(buf, sizeof(buf), "@%.*s", encoded.len, encoded.ptr); |
1124 | this->string = strdup(buf); | |
1125 | this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_string; | |
9151843f | 1126 | break; |
d45ec1de | 1127 | case ID_RFC822_ADDR: |
a8c09d8c MW |
1128 | snprintf(buf, sizeof(buf), "%.*s", encoded.len, encoded.ptr); |
1129 | this->string = strdup(buf); | |
1130 | this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_string; | |
9151843f | 1131 | break; |
d45ec1de | 1132 | case ID_DER_ASN1_DN: |
a8c09d8c MW |
1133 | snprintf(buf, sizeof(buf), "%.*s", encoded.len, encoded.ptr); |
1134 | /* TODO: whats returned on failure */ | |
1135 | dntoa(encoded, &buf_chunk); | |
1136 | this->string = strdup(buf); | |
1137 | this->public.equals = (bool (*) (identification_t*,identification_t*))equals_dn; | |
1138 | this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_dn; | |
9151843f | 1139 | break; |
d45ec1de | 1140 | case ID_DER_ASN1_GN: |
a8c09d8c | 1141 | this->string = gntoa(encoded); |
9151843f | 1142 | break; |
d45ec1de | 1143 | case ID_KEY_ID: |
a8c09d8c | 1144 | this->string = strdup("(unparsed KEY_ID)"); |
9151843f | 1145 | break; |
d45ec1de | 1146 | default: |
a8c09d8c MW |
1147 | snprintf(buf, sizeof(buf), "(invalid ID type: %d)", type); |
1148 | this->string = strdup(buf); | |
1149 | break; | |
1150 | } | |
ba425b87 | 1151 | |
a8c09d8c MW |
1152 | /* apply encoded chunk */ |
1153 | if (type != ID_ANY) | |
1154 | { | |
1155 | this->encoded = chunk_clone(encoded); | |
1156 | } | |
9151843f | 1157 | |
a8c09d8c MW |
1158 | /* remove unprintable chars in string */ |
1159 | for (pos = this->string; *pos != '\0'; pos++) | |
1160 | { | |
1161 | if (!isprint(*pos)) | |
1162 | { | |
1163 | *pos = '?'; | |
1164 | } | |
1165 | } | |
9151843f | 1166 | return &(this->public); |
d45ec1de | 1167 | } |