1 | /* v3_cpols.c */ |
2 | /* |
3 | * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project |
4 | * 1999. |
5 | */ |
6 | /* ==================================================================== |
7 | * Copyright (c) 1999-2004 The OpenSSL Project. All rights reserved. |
8 | * |
9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions |
11 | * are met: |
12 | * |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in |
18 | * the documentation and/or other materials provided with the |
19 | * distribution. |
20 | * |
21 | * 3. All advertising materials mentioning features or use of this |
22 | * software must display the following acknowledgment: |
23 | * "This product includes software developed by the OpenSSL Project |
24 | * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" |
25 | * |
26 | * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to |
27 | * endorse or promote products derived from this software without |
28 | * prior written permission. For written permission, please contact |
29 | * licensing@OpenSSL.org. |
30 | * |
31 | * 5. Products derived from this software may not be called "OpenSSL" |
32 | * nor may "OpenSSL" appear in their names without prior written |
33 | * permission of the OpenSSL Project. |
34 | * |
35 | * 6. Redistributions of any form whatsoever must retain the following |
36 | * acknowledgment: |
37 | * "This product includes software developed by the OpenSSL Project |
38 | * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" |
39 | * |
40 | * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY |
41 | * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
42 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
43 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR |
44 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
45 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
46 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
47 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
48 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
49 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
50 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
51 | * OF THE POSSIBILITY OF SUCH DAMAGE. |
52 | * ==================================================================== |
53 | * |
54 | * This product includes cryptographic software written by Eric Young |
55 | * (eay@cryptsoft.com). This product includes software written by Tim |
56 | * Hudson (tjh@cryptsoft.com). |
57 | * |
58 | */ |
59 | |
60 | #include <stdio.h> |
61 | #include <string.h> |
62 | |
63 | #include <openssl/asn1.h> |
64 | #include <openssl/asn1t.h> |
65 | #include <openssl/conf.h> |
66 | #include <openssl/err.h> |
67 | #include <openssl/mem.h> |
68 | #include <openssl/obj.h> |
69 | #include <openssl/stack.h> |
70 | #include <openssl/x509v3.h> |
71 | |
72 | #include "internal.h" |
73 | #include "pcy_int.h" |
74 | |
75 | /* Certificate policies extension support: this one is a bit complex... */ |
76 | |
77 | static int i2r_certpol(X509V3_EXT_METHOD *method, STACK_OF(POLICYINFO) *pol, |
78 | BIO *out, int indent); |
79 | static STACK_OF(POLICYINFO) *r2i_certpol(X509V3_EXT_METHOD *method, |
80 | X509V3_CTX *ctx, char *value); |
81 | static void print_qualifiers(BIO *out, STACK_OF(POLICYQUALINFO) *quals, |
82 | int indent); |
83 | static void print_notice(BIO *out, USERNOTICE *notice, int indent); |
84 | static POLICYINFO *policy_section(X509V3_CTX *ctx, |
85 | STACK_OF(CONF_VALUE) *polstrs, int ia5org); |
86 | static POLICYQUALINFO *notice_section(X509V3_CTX *ctx, |
87 | STACK_OF(CONF_VALUE) *unot, int ia5org); |
88 | static int nref_nos(STACK_OF(ASN1_INTEGER) *nnums, STACK_OF(CONF_VALUE) *nos); |
89 | |
90 | const X509V3_EXT_METHOD v3_cpols = { |
91 | NID_certificate_policies, 0, ASN1_ITEM_ref(CERTIFICATEPOLICIES), |
92 | 0, 0, 0, 0, |
93 | 0, 0, |
94 | 0, 0, |
95 | (X509V3_EXT_I2R)i2r_certpol, |
96 | (X509V3_EXT_R2I)r2i_certpol, |
97 | NULL |
98 | }; |
99 | |
100 | ASN1_ITEM_TEMPLATE(CERTIFICATEPOLICIES) = |
101 | ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, CERTIFICATEPOLICIES, POLICYINFO) |
102 | ASN1_ITEM_TEMPLATE_END(CERTIFICATEPOLICIES) |
103 | |
104 | IMPLEMENT_ASN1_FUNCTIONS(CERTIFICATEPOLICIES) |
105 | |
106 | ASN1_SEQUENCE(POLICYINFO) = { |
107 | ASN1_SIMPLE(POLICYINFO, policyid, ASN1_OBJECT), |
108 | ASN1_SEQUENCE_OF_OPT(POLICYINFO, qualifiers, POLICYQUALINFO) |
109 | } ASN1_SEQUENCE_END(POLICYINFO) |
110 | |
111 | IMPLEMENT_ASN1_FUNCTIONS(POLICYINFO) |
112 | |
113 | ASN1_ADB_TEMPLATE(policydefault) = ASN1_SIMPLE(POLICYQUALINFO, d.other, ASN1_ANY); |
114 | |
115 | ASN1_ADB(POLICYQUALINFO) = { |
116 | ADB_ENTRY(NID_id_qt_cps, ASN1_SIMPLE(POLICYQUALINFO, d.cpsuri, ASN1_IA5STRING)), |
117 | ADB_ENTRY(NID_id_qt_unotice, ASN1_SIMPLE(POLICYQUALINFO, d.usernotice, USERNOTICE)) |
118 | } ASN1_ADB_END(POLICYQUALINFO, 0, pqualid, 0, &policydefault_tt, NULL); |
119 | |
120 | ASN1_SEQUENCE(POLICYQUALINFO) = { |
121 | ASN1_SIMPLE(POLICYQUALINFO, pqualid, ASN1_OBJECT), |
122 | ASN1_ADB_OBJECT(POLICYQUALINFO) |
123 | } ASN1_SEQUENCE_END(POLICYQUALINFO) |
124 | |
125 | IMPLEMENT_ASN1_FUNCTIONS(POLICYQUALINFO) |
126 | |
127 | ASN1_SEQUENCE(USERNOTICE) = { |
128 | ASN1_OPT(USERNOTICE, noticeref, NOTICEREF), |
129 | ASN1_OPT(USERNOTICE, exptext, DISPLAYTEXT) |
130 | } ASN1_SEQUENCE_END(USERNOTICE) |
131 | |
132 | IMPLEMENT_ASN1_FUNCTIONS(USERNOTICE) |
133 | |
134 | ASN1_SEQUENCE(NOTICEREF) = { |
135 | ASN1_SIMPLE(NOTICEREF, organization, DISPLAYTEXT), |
136 | ASN1_SEQUENCE_OF(NOTICEREF, noticenos, ASN1_INTEGER) |
137 | } ASN1_SEQUENCE_END(NOTICEREF) |
138 | |
139 | IMPLEMENT_ASN1_FUNCTIONS(NOTICEREF) |
140 | |
141 | static STACK_OF(POLICYINFO) *r2i_certpol(X509V3_EXT_METHOD *method, |
142 | X509V3_CTX *ctx, char *value) |
143 | { |
144 | STACK_OF(POLICYINFO) *pols = NULL; |
145 | char *pstr; |
146 | POLICYINFO *pol; |
147 | ASN1_OBJECT *pobj; |
148 | STACK_OF(CONF_VALUE) *vals; |
149 | CONF_VALUE *cnf; |
150 | size_t i; |
151 | int ia5org; |
152 | pols = sk_POLICYINFO_new_null(); |
153 | if (pols == NULL) { |
154 | OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE); |
155 | return NULL; |
156 | } |
157 | vals = X509V3_parse_list(value); |
158 | if (vals == NULL) { |
159 | OPENSSL_PUT_ERROR(X509V3, ERR_R_X509V3_LIB); |
160 | goto err; |
161 | } |
162 | ia5org = 0; |
163 | for (i = 0; i < sk_CONF_VALUE_num(vals); i++) { |
164 | cnf = sk_CONF_VALUE_value(vals, i); |
165 | if (cnf->value || !cnf->name) { |
166 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_POLICY_IDENTIFIER); |
167 | X509V3_conf_err(cnf); |
168 | goto err; |
169 | } |
170 | pstr = cnf->name; |
171 | if (!strcmp(pstr, "ia5org" )) { |
172 | ia5org = 1; |
173 | continue; |
174 | } else if (*pstr == '@') { |
175 | STACK_OF(CONF_VALUE) *polsect; |
176 | polsect = X509V3_get_section(ctx, pstr + 1); |
177 | if (!polsect) { |
178 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_SECTION); |
179 | |
180 | X509V3_conf_err(cnf); |
181 | goto err; |
182 | } |
183 | pol = policy_section(ctx, polsect, ia5org); |
184 | X509V3_section_free(ctx, polsect); |
185 | if (!pol) |
186 | goto err; |
187 | } else { |
188 | if (!(pobj = OBJ_txt2obj(cnf->name, 0))) { |
189 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_OBJECT_IDENTIFIER); |
190 | X509V3_conf_err(cnf); |
191 | goto err; |
192 | } |
193 | pol = POLICYINFO_new(); |
194 | if (pol == NULL) { |
195 | OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE); |
196 | ASN1_OBJECT_free(pobj); |
197 | goto err; |
198 | } |
199 | pol->policyid = pobj; |
200 | } |
201 | if (!sk_POLICYINFO_push(pols, pol)) { |
202 | POLICYINFO_free(pol); |
203 | OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE); |
204 | goto err; |
205 | } |
206 | } |
207 | sk_CONF_VALUE_pop_free(vals, X509V3_conf_free); |
208 | return pols; |
209 | err: |
210 | sk_CONF_VALUE_pop_free(vals, X509V3_conf_free); |
211 | sk_POLICYINFO_pop_free(pols, POLICYINFO_free); |
212 | return NULL; |
213 | } |
214 | |
215 | static POLICYINFO *policy_section(X509V3_CTX *ctx, |
216 | STACK_OF(CONF_VALUE) *polstrs, int ia5org) |
217 | { |
218 | size_t i; |
219 | CONF_VALUE *cnf; |
220 | POLICYINFO *pol; |
221 | POLICYQUALINFO *qual; |
222 | if (!(pol = POLICYINFO_new())) |
223 | goto merr; |
224 | for (i = 0; i < sk_CONF_VALUE_num(polstrs); i++) { |
225 | cnf = sk_CONF_VALUE_value(polstrs, i); |
226 | if (!strcmp(cnf->name, "policyIdentifier" )) { |
227 | ASN1_OBJECT *pobj; |
228 | if (!(pobj = OBJ_txt2obj(cnf->value, 0))) { |
229 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_OBJECT_IDENTIFIER); |
230 | X509V3_conf_err(cnf); |
231 | goto err; |
232 | } |
233 | pol->policyid = pobj; |
234 | |
235 | } else if (!x509v3_name_cmp(cnf->name, "CPS" )) { |
236 | if (!pol->qualifiers) |
237 | pol->qualifiers = sk_POLICYQUALINFO_new_null(); |
238 | if (!(qual = POLICYQUALINFO_new())) |
239 | goto merr; |
240 | if (!sk_POLICYQUALINFO_push(pol->qualifiers, qual)) |
241 | goto merr; |
242 | /* TODO(fork): const correctness */ |
243 | qual->pqualid = (ASN1_OBJECT *)OBJ_nid2obj(NID_id_qt_cps); |
244 | if (qual->pqualid == NULL) { |
245 | OPENSSL_PUT_ERROR(X509V3, ERR_R_INTERNAL_ERROR); |
246 | goto err; |
247 | } |
248 | qual->d.cpsuri = M_ASN1_IA5STRING_new(); |
249 | if (qual->d.cpsuri == NULL) { |
250 | goto err; |
251 | } |
252 | if (!ASN1_STRING_set(qual->d.cpsuri, cnf->value, |
253 | strlen(cnf->value))) |
254 | goto merr; |
255 | } else if (!x509v3_name_cmp(cnf->name, "userNotice" )) { |
256 | STACK_OF(CONF_VALUE) *unot; |
257 | if (*cnf->value != '@') { |
258 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_EXPECTED_A_SECTION_NAME); |
259 | X509V3_conf_err(cnf); |
260 | goto err; |
261 | } |
262 | unot = X509V3_get_section(ctx, cnf->value + 1); |
263 | if (!unot) { |
264 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_SECTION); |
265 | |
266 | X509V3_conf_err(cnf); |
267 | goto err; |
268 | } |
269 | qual = notice_section(ctx, unot, ia5org); |
270 | X509V3_section_free(ctx, unot); |
271 | if (!qual) |
272 | goto err; |
273 | if (!pol->qualifiers) |
274 | pol->qualifiers = sk_POLICYQUALINFO_new_null(); |
275 | if (!sk_POLICYQUALINFO_push(pol->qualifiers, qual)) |
276 | goto merr; |
277 | } else { |
278 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_OPTION); |
279 | |
280 | X509V3_conf_err(cnf); |
281 | goto err; |
282 | } |
283 | } |
284 | if (!pol->policyid) { |
285 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_NO_POLICY_IDENTIFIER); |
286 | goto err; |
287 | } |
288 | |
289 | return pol; |
290 | |
291 | merr: |
292 | OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE); |
293 | |
294 | err: |
295 | POLICYINFO_free(pol); |
296 | return NULL; |
297 | |
298 | } |
299 | |
300 | static POLICYQUALINFO *notice_section(X509V3_CTX *ctx, |
301 | STACK_OF(CONF_VALUE) *unot, int ia5org) |
302 | { |
303 | size_t i; |
304 | int ret; |
305 | CONF_VALUE *cnf; |
306 | USERNOTICE *not; |
307 | POLICYQUALINFO *qual; |
308 | if (!(qual = POLICYQUALINFO_new())) |
309 | goto merr; |
310 | /* TODO(fork): const correctness */ |
311 | qual->pqualid = (ASN1_OBJECT *)OBJ_nid2obj(NID_id_qt_unotice); |
312 | if (qual->pqualid == NULL) { |
313 | OPENSSL_PUT_ERROR(X509V3, ERR_R_INTERNAL_ERROR); |
314 | goto err; |
315 | } |
316 | if (!(not = USERNOTICE_new())) |
317 | goto merr; |
318 | qual->d.usernotice = not; |
319 | for (i = 0; i < sk_CONF_VALUE_num(unot); i++) { |
320 | cnf = sk_CONF_VALUE_value(unot, i); |
321 | if (!strcmp(cnf->name, "explicitText" )) { |
322 | not->exptext = M_ASN1_VISIBLESTRING_new(); |
323 | if (not->exptext == NULL) |
324 | goto merr; |
325 | if (!ASN1_STRING_set(not->exptext, cnf->value, |
326 | strlen(cnf->value))) |
327 | goto merr; |
328 | } else if (!strcmp(cnf->name, "organization" )) { |
329 | NOTICEREF *nref; |
330 | if (!not->noticeref) { |
331 | if (!(nref = NOTICEREF_new())) |
332 | goto merr; |
333 | not->noticeref = nref; |
334 | } else |
335 | nref = not->noticeref; |
336 | if (ia5org) |
337 | nref->organization->type = V_ASN1_IA5STRING; |
338 | else |
339 | nref->organization->type = V_ASN1_VISIBLESTRING; |
340 | if (!ASN1_STRING_set(nref->organization, cnf->value, |
341 | strlen(cnf->value))) |
342 | goto merr; |
343 | } else if (!strcmp(cnf->name, "noticeNumbers" )) { |
344 | NOTICEREF *nref; |
345 | STACK_OF(CONF_VALUE) *nos; |
346 | if (!not->noticeref) { |
347 | if (!(nref = NOTICEREF_new())) |
348 | goto merr; |
349 | not->noticeref = nref; |
350 | } else |
351 | nref = not->noticeref; |
352 | nos = X509V3_parse_list(cnf->value); |
353 | if (!nos || !sk_CONF_VALUE_num(nos)) { |
354 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_NUMBERS); |
355 | X509V3_conf_err(cnf); |
356 | goto err; |
357 | } |
358 | ret = nref_nos(nref->noticenos, nos); |
359 | sk_CONF_VALUE_pop_free(nos, X509V3_conf_free); |
360 | if (!ret) |
361 | goto err; |
362 | } else { |
363 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_OPTION); |
364 | X509V3_conf_err(cnf); |
365 | goto err; |
366 | } |
367 | } |
368 | |
369 | if (not->noticeref && |
370 | (!not->noticeref->noticenos || !not->noticeref->organization)) { |
371 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_NEED_ORGANIZATION_AND_NUMBERS); |
372 | goto err; |
373 | } |
374 | |
375 | return qual; |
376 | |
377 | merr: |
378 | OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE); |
379 | |
380 | err: |
381 | POLICYQUALINFO_free(qual); |
382 | return NULL; |
383 | } |
384 | |
385 | static int nref_nos(STACK_OF(ASN1_INTEGER) *nnums, STACK_OF(CONF_VALUE) *nos) |
386 | { |
387 | CONF_VALUE *cnf; |
388 | ASN1_INTEGER *aint; |
389 | |
390 | size_t i; |
391 | |
392 | for (i = 0; i < sk_CONF_VALUE_num(nos); i++) { |
393 | cnf = sk_CONF_VALUE_value(nos, i); |
394 | if (!(aint = s2i_ASN1_INTEGER(NULL, cnf->name))) { |
395 | OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_NUMBER); |
396 | goto err; |
397 | } |
398 | if (!sk_ASN1_INTEGER_push(nnums, aint)) |
399 | goto merr; |
400 | } |
401 | return 1; |
402 | |
403 | merr: |
404 | ASN1_INTEGER_free(aint); |
405 | OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE); |
406 | |
407 | err: |
408 | return 0; |
409 | } |
410 | |
411 | static int i2r_certpol(X509V3_EXT_METHOD *method, STACK_OF(POLICYINFO) *pol, |
412 | BIO *out, int indent) |
413 | { |
414 | size_t i; |
415 | POLICYINFO *pinfo; |
416 | /* First print out the policy OIDs */ |
417 | for (i = 0; i < sk_POLICYINFO_num(pol); i++) { |
418 | pinfo = sk_POLICYINFO_value(pol, i); |
419 | BIO_printf(out, "%*sPolicy: " , indent, "" ); |
420 | i2a_ASN1_OBJECT(out, pinfo->policyid); |
421 | BIO_puts(out, "\n" ); |
422 | if (pinfo->qualifiers) |
423 | print_qualifiers(out, pinfo->qualifiers, indent + 2); |
424 | } |
425 | return 1; |
426 | } |
427 | |
428 | static void print_qualifiers(BIO *out, STACK_OF(POLICYQUALINFO) *quals, |
429 | int indent) |
430 | { |
431 | POLICYQUALINFO *qualinfo; |
432 | size_t i; |
433 | for (i = 0; i < sk_POLICYQUALINFO_num(quals); i++) { |
434 | qualinfo = sk_POLICYQUALINFO_value(quals, i); |
435 | switch (OBJ_obj2nid(qualinfo->pqualid)) { |
436 | case NID_id_qt_cps: |
437 | BIO_printf(out, "%*sCPS: %s\n" , indent, "" , |
438 | qualinfo->d.cpsuri->data); |
439 | break; |
440 | |
441 | case NID_id_qt_unotice: |
442 | BIO_printf(out, "%*sUser Notice:\n" , indent, "" ); |
443 | print_notice(out, qualinfo->d.usernotice, indent + 2); |
444 | break; |
445 | |
446 | default: |
447 | BIO_printf(out, "%*sUnknown Qualifier: " , indent + 2, "" ); |
448 | |
449 | i2a_ASN1_OBJECT(out, qualinfo->pqualid); |
450 | BIO_puts(out, "\n" ); |
451 | break; |
452 | } |
453 | } |
454 | } |
455 | |
456 | static void print_notice(BIO *out, USERNOTICE *notice, int indent) |
457 | { |
458 | size_t i; |
459 | if (notice->noticeref) { |
460 | NOTICEREF *ref; |
461 | ref = notice->noticeref; |
462 | BIO_printf(out, "%*sOrganization: %s\n" , indent, "" , |
463 | ref->organization->data); |
464 | BIO_printf(out, "%*sNumber%s: " , indent, "" , |
465 | sk_ASN1_INTEGER_num(ref->noticenos) > 1 ? "s" : "" ); |
466 | for (i = 0; i < sk_ASN1_INTEGER_num(ref->noticenos); i++) { |
467 | ASN1_INTEGER *num; |
468 | char *tmp; |
469 | num = sk_ASN1_INTEGER_value(ref->noticenos, i); |
470 | if (i) |
471 | BIO_puts(out, ", " ); |
472 | if (num == NULL) |
473 | BIO_puts(out, "(null)" ); |
474 | else { |
475 | tmp = i2s_ASN1_INTEGER(NULL, num); |
476 | if (tmp == NULL) |
477 | return; |
478 | BIO_puts(out, tmp); |
479 | OPENSSL_free(tmp); |
480 | } |
481 | } |
482 | BIO_puts(out, "\n" ); |
483 | } |
484 | if (notice->exptext) |
485 | BIO_printf(out, "%*sExplicit Text: %s\n" , indent, "" , |
486 | notice->exptext->data); |
487 | } |
488 | |
489 | void X509_POLICY_NODE_print(BIO *out, X509_POLICY_NODE *node, int indent) |
490 | { |
491 | const X509_POLICY_DATA *dat = node->data; |
492 | |
493 | BIO_printf(out, "%*sPolicy: " , indent, "" ); |
494 | |
495 | i2a_ASN1_OBJECT(out, dat->valid_policy); |
496 | BIO_puts(out, "\n" ); |
497 | BIO_printf(out, "%*s%s\n" , indent + 2, "" , |
498 | node_data_critical(dat) ? "Critical" : "Non Critical" ); |
499 | if (dat->qualifier_set) |
500 | print_qualifiers(out, dat->qualifier_set, indent + 2); |
501 | else |
502 | BIO_printf(out, "%*sNo Qualifiers\n" , indent + 2, "" ); |
503 | } |
504 | |