Line data Source code
1 : /* dn.c - Distinguished Name helper functions
2 : * Copyright (C) 2001, 2006, 2012 g10 Code GmbH
3 : *
4 : * This file is part of KSBA.
5 : *
6 : * KSBA is free software; you can redistribute it and/or modify
7 : * it under the terms of either
8 : *
9 : * - the GNU Lesser General Public License as published by the Free
10 : * Software Foundation; either version 3 of the License, or (at
11 : * your option) any later version.
12 : *
13 : * or
14 : *
15 : * - the GNU General Public License as published by the Free
16 : * Software Foundation; either version 2 of the License, or (at
17 : * your option) any later version.
18 : *
19 : * or both in parallel, as here.
20 : *
21 : * KSBA is distributed in the hope that it will be useful, but WITHOUT
22 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 : * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
24 : * License for more details.
25 : *
26 : * You should have received a copies of the GNU General Public License
27 : * and the GNU Lesser General Public License along with this program;
28 : * if not, see <http://www.gnu.org/licenses/>.
29 : */
30 :
31 : /* Reference is RFC-2253 */
32 :
33 : #include <config.h>
34 : #include <stdio.h>
35 : #include <stdlib.h>
36 : #include <string.h>
37 : #include <assert.h>
38 :
39 : #include "util.h"
40 : #include "asn1-func.h"
41 : #include "ber-help.h"
42 : #include "ber-decoder.h"
43 :
44 : static const struct {
45 : const char *name;
46 : int source; /* 0 = unknown
47 : 1 = rfc2253
48 : 2 = David Chadwick, July 2003
49 : <draft-ietf-pkix-dnstrings-02.txt>
50 : 3 = Peter Gutmann
51 : */
52 : const char *description;
53 : size_t oidlen;
54 : const unsigned char *oid; /* DER encoded OID. */
55 : const char *oidstr; /* OID as dotted string. */
56 : } oid_name_tbl[] = {
57 : {"CN", 1, "CommonName", 3, "\x55\x04\x03", "2.5.4.3" },
58 : {"SN", 2, "Surname", 3, "\x55\x04\x04", "2.5.4.4" },
59 : {"SERIALNUMBER", 2, "SerialNumber",3, "\x55\x04\x05", "2.5.4.5" },
60 : {"C", 1, "CountryName", 3, "\x55\x04\x06", "2.5.4.6" },
61 : {"L" , 1, "LocalityName", 3, "\x55\x04\x07", "2.5.4.7" },
62 : {"ST", 1, "StateOrProvince", 3, "\x55\x04\x08", "2.5.4.8" },
63 : {"STREET", 1, "StreetAddress", 3, "\x55\x04\x09", "2.5.4.9" },
64 : {"O", 1, "OrganizationName", 3, "\x55\x04\x0a", "2.5.4.10" },
65 : {"OU", 1, "OrganizationalUnit", 3, "\x55\x04\x0b", "2.5.4.11" },
66 : {"T", 2, "Title", 3, "\x55\x04\x0c", "2.5.4.12" },
67 : {"D", 3, "Description", 3, "\x55\x04\x0d", "2.5.4.13" },
68 : {"BC", 3, "BusinessCategory", 3, "\x55\x04\x0f", "2.5.4.15" },
69 : {"ADDR", 2, "PostalAddress", 3, "\x55\x04\x11", "2.5.4.16" },
70 : {"POSTALCODE" , 0, "PostalCode", 3, "\x55\x04\x11", "2.5.4.17" },
71 : {"GN", 2, "GivenName", 3, "\x55\x04\x2a", "2.5.4.42" },
72 : {"PSEUDO", 2, "Pseudonym", 3, "\x55\x04\x41", "2.5.4.65" },
73 : {"DC", 1, "domainComponent", 10,
74 : "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x19", "0.9.2342.19200300.100.1.25" },
75 : {"UID", 1, "userid", 10,
76 : "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x01", "0.9.2342.19200300.100.1.1 " },
77 : {"EMAIL", 3, "emailAddress", 9,
78 : "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01", "1.2.840.113549.1.9.1" },
79 : { NULL }
80 : };
81 :
82 :
83 : #define N 0x00
84 : #define P 0x01
85 : static unsigned char charclasses[128] = {
86 : N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
87 : N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
88 : P, N, N, N, N, N, N, P, P, P, N, P, P, P, P, P,
89 : P, P, P, P, P, P, P, P, P, P, P, N, N, P, N, P,
90 : N, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P,
91 : P, P, P, P, P, P, P, P, P, P, P, N, N, N, N, N,
92 : N, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P,
93 : P, P, P, P, P, P, P, P, P, P, P, N, N, N, N, N
94 : };
95 : #undef N
96 : #undef P
97 :
98 : struct stringbuf {
99 : size_t len;
100 : size_t size;
101 : char *buf;
102 : int out_of_core;
103 : };
104 :
105 :
106 :
107 : static void
108 9 : init_stringbuf (struct stringbuf *sb, int initiallen)
109 : {
110 9 : sb->len = 0;
111 9 : sb->size = initiallen;
112 9 : sb->out_of_core = 0;
113 : /* allocate one more, so that get_stringbuf can append a nul */
114 9 : sb->buf = xtrymalloc (initiallen+1);
115 9 : if (!sb->buf)
116 0 : sb->out_of_core = 1;
117 9 : }
118 :
119 : static void
120 9 : deinit_stringbuf (struct stringbuf *sb)
121 : {
122 9 : xfree (sb->buf);
123 9 : sb->buf = NULL;
124 9 : sb->out_of_core = 1; /* make sure the caller does an init before reuse */
125 9 : }
126 :
127 :
128 : static void
129 190 : put_stringbuf (struct stringbuf *sb, const char *text)
130 : {
131 190 : size_t n = strlen (text);
132 :
133 190 : if (sb->out_of_core)
134 0 : return;
135 :
136 190 : if (sb->len + n >= sb->size)
137 : {
138 : char *p;
139 :
140 4 : sb->size += n + 100;
141 4 : p = xtryrealloc (sb->buf, sb->size);
142 4 : if ( !p)
143 : {
144 0 : sb->out_of_core = 1;
145 0 : return;
146 : }
147 4 : sb->buf = p;
148 : }
149 190 : memcpy (sb->buf+sb->len, text, n);
150 190 : sb->len += n;
151 : }
152 :
153 : static void
154 43 : put_stringbuf_mem (struct stringbuf *sb, const char *text, size_t n)
155 : {
156 43 : if (sb->out_of_core)
157 0 : return;
158 :
159 43 : if (sb->len + n >= sb->size)
160 : {
161 : char *p;
162 :
163 0 : sb->size += n + 100;
164 0 : p = xtryrealloc (sb->buf, sb->size);
165 0 : if ( !p)
166 : {
167 0 : sb->out_of_core = 1;
168 0 : return;
169 : }
170 0 : sb->buf = p;
171 : }
172 43 : memcpy (sb->buf+sb->len, text, n);
173 43 : sb->len += n;
174 : }
175 :
176 : static void
177 40 : put_stringbuf_mem_skip (struct stringbuf *sb, const char *text, size_t n,
178 : int skip)
179 : {
180 : char *p;
181 :
182 40 : if (!skip)
183 : {
184 40 : put_stringbuf_mem (sb, text, n);
185 40 : return;
186 : }
187 0 : if (sb->out_of_core)
188 0 : return;
189 :
190 0 : if (sb->len + n >= sb->size)
191 : {
192 : /* Note: we allocate too much here, but we don't care. */
193 0 : sb->size += n + 100;
194 0 : p = xtryrealloc (sb->buf, sb->size);
195 0 : if ( !p)
196 : {
197 0 : sb->out_of_core = 1;
198 0 : return;
199 : }
200 0 : sb->buf = p;
201 : }
202 0 : p = sb->buf+sb->len;
203 0 : while (n > skip)
204 : {
205 0 : text += skip;
206 0 : n -= skip;
207 0 : *p++ = *text++;
208 0 : n--;
209 0 : sb->len++;
210 : }
211 : }
212 :
213 : static char *
214 9 : get_stringbuf (struct stringbuf *sb)
215 : {
216 : char *p;
217 :
218 9 : if (sb->out_of_core)
219 : {
220 0 : xfree (sb->buf); sb->buf = NULL;
221 0 : return NULL;
222 : }
223 :
224 9 : sb->buf[sb->len] = 0;
225 9 : p = sb->buf;
226 9 : sb->buf = NULL;
227 9 : sb->out_of_core = 1; /* make sure the caller does an init before reuse */
228 9 : return p;
229 : }
230 :
231 :
232 : /* This function is used for 1 byte encodings to insert any required
233 : quoting. It does not do the quoting for a space or hash mark at
234 : the beginning of a string or a space as the last character of a
235 : string. It will do steps of SKIP+1 characters, assuming that these
236 : SKIP characters are null octets. */
237 : static void
238 40 : append_quoted (struct stringbuf *sb, const unsigned char *value, size_t length,
239 : int skip)
240 : {
241 : unsigned char tmp[4];
242 40 : const unsigned char *s = value;
243 40 : size_t n = 0;
244 :
245 : for (;;)
246 : {
247 506 : for (value = s; n+skip < length; n++, s++)
248 : {
249 466 : s += skip;
250 466 : n += skip;
251 466 : if (*s < ' ' || *s > 126 || strchr (",+\"\\<>;", *s) )
252 : break;
253 : }
254 :
255 40 : if (s != value)
256 40 : put_stringbuf_mem_skip (sb, value, s-value, skip);
257 40 : if (n+skip >= length)
258 80 : return; /* ready */
259 0 : s += skip;
260 0 : n += skip;
261 0 : if ( *s < ' ' || *s > 126 )
262 : {
263 0 : snprintf (tmp, sizeof tmp, "\\%02X", *s);
264 0 : put_stringbuf_mem (sb, tmp, 3);
265 : }
266 : else
267 : {
268 0 : tmp[0] = '\\';
269 0 : tmp[1] = *s;
270 0 : put_stringbuf_mem (sb, tmp, 2);
271 : }
272 0 : n++; s++;
273 : }
274 : }
275 :
276 :
277 : /* Append VALUE of LENGTH and TYPE to SB. Do the required quoting. */
278 : static void
279 0 : append_utf8_value (const unsigned char *value, size_t length,
280 : struct stringbuf *sb)
281 : {
282 : unsigned char tmp[6];
283 : const unsigned char *s;
284 : size_t n;
285 : int i, nmore;
286 :
287 0 : if (length && (*value == ' ' || *value == '#'))
288 : {
289 0 : tmp[0] = '\\';
290 0 : tmp[1] = *value;
291 0 : put_stringbuf_mem (sb, tmp, 2);
292 0 : value++;
293 0 : length--;
294 : }
295 0 : if (length && value[length-1] == ' ')
296 : {
297 0 : tmp[0] = '\\';
298 0 : tmp[1] = ' ';
299 0 : put_stringbuf_mem (sb, tmp, 2);
300 0 : length--;
301 : }
302 :
303 0 : for (s=value, n=0;;)
304 : {
305 0 : for (value = s; n < length && !(*s & 0x80); n++, s++)
306 : ;
307 0 : if (s != value)
308 0 : append_quoted (sb, value, s-value, 0);
309 0 : if (n==length)
310 0 : return; /* ready */
311 0 : if (!(*s & 0x80))
312 0 : nmore = 0; /* Not expected here: high bit not set. */
313 0 : else if ( (*s & 0xe0) == 0xc0 ) /* 110x xxxx */
314 0 : nmore = 1;
315 0 : else if ( (*s & 0xf0) == 0xe0 ) /* 1110 xxxx */
316 0 : nmore = 2;
317 0 : else if ( (*s & 0xf8) == 0xf0 ) /* 1111 0xxx */
318 0 : nmore = 3;
319 0 : else if ( (*s & 0xfc) == 0xf8 ) /* 1111 10xx */
320 0 : nmore = 4;
321 0 : else if ( (*s & 0xfe) == 0xfc ) /* 1111 110x */
322 0 : nmore = 5;
323 : else /* Invalid encoding */
324 0 : nmore = 0;
325 :
326 0 : if (!nmore)
327 : {
328 : /* Encoding error: We quote the bad byte. */
329 0 : snprintf (tmp, sizeof tmp, "\\%02X", *s);
330 0 : put_stringbuf_mem (sb, tmp, 3);
331 0 : s++; n++;
332 : }
333 : else
334 : {
335 0 : tmp[0] = *s++; n++;
336 0 : for (i=1; n < length && i <= nmore; i++)
337 : {
338 0 : if ( (*s & 0xc0) != 0x80)
339 0 : break; /* Invalid encoding - let the next cycle detect this. */
340 0 : tmp[i] = *s++;
341 0 : n++;
342 : }
343 0 : put_stringbuf_mem (sb, tmp, i);
344 : }
345 : }
346 : }
347 :
348 : /* Append VALUE of LENGTH and TYPE to SB. Do character set conversion
349 : and quoting */
350 : static void
351 37 : append_latin1_value (const unsigned char *value, size_t length,
352 : struct stringbuf *sb)
353 : {
354 : unsigned char tmp[2];
355 : const unsigned char *s;
356 : size_t n;
357 :
358 37 : if (length && (*value == ' ' || *value == '#'))
359 : {
360 0 : tmp[0] = '\\';
361 0 : tmp[1] = *value;
362 0 : put_stringbuf_mem (sb, tmp, 2);
363 0 : value++;
364 0 : length--;
365 : }
366 37 : if (length && value[length-1] == ' ')
367 : {
368 0 : tmp[0] = '\\';
369 0 : tmp[1] = ' ';
370 0 : put_stringbuf_mem (sb, tmp, 2);
371 0 : length--;
372 : }
373 :
374 37 : for (s=value, n=0;;)
375 : {
376 43 : for (value = s; n < length && !(*s & 0x80); n++, s++)
377 : ;
378 40 : if (s != value)
379 40 : append_quoted (sb, value, s-value, 0);
380 40 : if (n==length)
381 74 : return; /* ready */
382 3 : assert ((*s & 0x80));
383 3 : tmp[0] = 0xc0 | ((*s >> 6) & 3);
384 3 : tmp[1] = 0x80 | ( *s & 0x3f );
385 3 : put_stringbuf_mem (sb, tmp, 2);
386 3 : n++; s++;
387 : }
388 : }
389 :
390 : /* Append VALUE of LENGTH and TYPE to SB. Do UCS-4 to utf conversion
391 : and and quoting */
392 : static void
393 0 : append_ucs4_value (const unsigned char *value, size_t length,
394 : struct stringbuf *sb)
395 : {
396 : unsigned char tmp[7];
397 : const unsigned char *s;
398 : size_t n;
399 : unsigned int c;
400 : int i;
401 :
402 0 : if (length>3 && !value[0] && !value[1] && !value[2]
403 0 : && (value[3] == ' ' || value[3] == '#'))
404 : {
405 0 : tmp[0] = '\\';
406 0 : tmp[1] = *value;
407 0 : put_stringbuf_mem (sb, tmp, 2);
408 0 : value += 4;
409 0 : length -= 4;
410 : }
411 0 : if (length>3 && !value[0] && !value[1] && !value[2] && value[3] == ' ')
412 : {
413 0 : tmp[0] = '\\';
414 0 : tmp[1] = ' ';
415 0 : put_stringbuf_mem (sb, tmp, 2);
416 0 : length -= 4;
417 : }
418 :
419 0 : for (s=value, n=0;;)
420 : {
421 0 : for (value = s; n+3 < length
422 0 : && !s[0] && !s[1] && !s[2] && !(s[3] & 0x80); n += 4, s += 4)
423 : ;
424 0 : if (s != value)
425 0 : append_quoted (sb, value, s-value, 3);
426 0 : if (n>=length)
427 0 : return; /* ready */
428 0 : if (n < 4)
429 : { /* This is an invalid encoding - better stop after adding
430 : one impossible characater */
431 0 : put_stringbuf_mem (sb, "\xff", 1);
432 0 : return;
433 : }
434 0 : c = *s++ << 24;
435 0 : c |= *s++ << 16;
436 0 : c |= *s++ << 8;
437 0 : c |= *s++;
438 0 : n += 4;
439 0 : i=0;
440 0 : if (c < (1<<11))
441 : {
442 0 : tmp[i++] = 0xc0 | ( c >> 6);
443 0 : tmp[i++] = 0x80 | ( c & 0x3f);
444 : }
445 0 : else if (c < (1<<16))
446 : {
447 0 : tmp[i++] = 0xe0 | ( c >> 12);
448 0 : tmp[i++] = 0x80 | ((c >> 6) & 0x3f);
449 0 : tmp[i++] = 0x80 | ( c & 0x3f);
450 : }
451 0 : else if (c < (1<<21))
452 : {
453 0 : tmp[i++] = 0xf0 | ( c >> 18);
454 0 : tmp[i++] = 0x80 | ((c >> 12) & 0x3f);
455 0 : tmp[i++] = 0x80 | ((c >> 6) & 0x3f);
456 0 : tmp[i++] = 0x80 | ( c & 0x3f);
457 : }
458 0 : else if (c < (1<<26))
459 : {
460 0 : tmp[i++] = 0xf8 | ( c >> 24);
461 0 : tmp[i++] = 0x80 | ((c >> 18) & 0x3f);
462 0 : tmp[i++] = 0x80 | ((c >> 12) & 0x3f);
463 0 : tmp[i++] = 0x80 | ((c >> 6) & 0x3f);
464 0 : tmp[i++] = 0x80 | ( c & 0x3f);
465 : }
466 : else
467 : {
468 0 : tmp[i++] = 0xfc | ( c >> 30);
469 0 : tmp[i++] = 0x80 | ((c >> 24) & 0x3f);
470 0 : tmp[i++] = 0x80 | ((c >> 18) & 0x3f);
471 0 : tmp[i++] = 0x80 | ((c >> 12) & 0x3f);
472 0 : tmp[i++] = 0x80 | ((c >> 6) & 0x3f);
473 0 : tmp[i++] = 0x80 | ( c & 0x3f);
474 : }
475 0 : put_stringbuf_mem (sb, tmp, i);
476 : }
477 : }
478 :
479 : /* Append VALUE of LENGTH and TYPE to SB. Do UCS-2 to utf conversion
480 : and and quoting */
481 : static void
482 0 : append_ucs2_value (const unsigned char *value, size_t length,
483 : struct stringbuf *sb)
484 : {
485 : unsigned char tmp[3];
486 : const unsigned char *s;
487 : size_t n;
488 : unsigned int c;
489 : int i;
490 :
491 0 : if (length>1 && !value[0] && (value[1] == ' ' || value[1] == '#'))
492 : {
493 0 : tmp[0] = '\\';
494 0 : tmp[1] = *value;
495 0 : put_stringbuf_mem (sb, tmp, 2);
496 0 : value += 2;
497 0 : length -= 2;
498 : }
499 0 : if (length>1 && !value[0] && value[1] == ' ')
500 : {
501 0 : tmp[0] = '\\';
502 0 : tmp[1] = ' ';
503 0 : put_stringbuf_mem (sb, tmp, 2);
504 0 : length -=2;
505 : }
506 :
507 0 : for (s=value, n=0;;)
508 : {
509 0 : for (value = s; n+1 < length && !s[0] && !(s[1] & 0x80); n += 2, s += 2)
510 : ;
511 0 : if (s != value)
512 0 : append_quoted (sb, value, s-value, 1);
513 0 : if (n>=length)
514 0 : return; /* ready */
515 0 : if (n < 2)
516 : { /* This is an invalid encoding - better stop after adding
517 : one impossible characater */
518 0 : put_stringbuf_mem (sb, "\xff", 1);
519 0 : return;
520 : }
521 0 : c = *s++ << 8;
522 0 : c |= *s++;
523 0 : n += 2;
524 0 : i=0;
525 0 : if (c < (1<<11))
526 : {
527 0 : tmp[i++] = 0xc0 | ( c >> 6);
528 0 : tmp[i++] = 0x80 | ( c & 0x3f);
529 : }
530 : else
531 : {
532 0 : tmp[i++] = 0xe0 | ( c >> 12);
533 0 : tmp[i++] = 0x80 | ((c >> 6) & 0x3f);
534 0 : tmp[i++] = 0x80 | ( c & 0x3f);
535 : }
536 0 : put_stringbuf_mem (sb, tmp, i);
537 : }
538 : }
539 :
540 :
541 : /* Append attribute and value. ROOT is the sequence */
542 : static gpg_error_t
543 41 : append_atv (const unsigned char *image, AsnNode root, struct stringbuf *sb)
544 : {
545 41 : AsnNode node = root->down;
546 : const char *name;
547 41 : int use_hex = 0;
548 : int i;
549 :
550 41 : if (!node || node->type != TYPE_OBJECT_ID)
551 0 : return gpg_error (GPG_ERR_UNEXPECTED_TAG);
552 41 : if (node->off == -1)
553 0 : return gpg_error (GPG_ERR_NO_VALUE); /* Hmmm, this might lead to misunderstandings */
554 :
555 41 : name = NULL;
556 275 : for (i=0; oid_name_tbl[i].name; i++)
557 : {
558 271 : if (oid_name_tbl[i].source == 1
559 175 : && node->len == oid_name_tbl[i].oidlen
560 278 : && !memcmp (image+node->off+node->nhdr,
561 278 : oid_name_tbl[i].oid, node->len))
562 : {
563 37 : name = oid_name_tbl[i].name;
564 37 : break;
565 : }
566 : }
567 41 : if (name)
568 37 : put_stringbuf (sb, name);
569 : else
570 : { /* No name for the OID in the table; at least not DER encoded.
571 : Now convert the OID to a string, try to find it in the table
572 : again and use the string as last resort. */
573 : char *p;
574 :
575 4 : p = ksba_oid_to_str (image+node->off+node->nhdr, node->len);
576 4 : if (!p)
577 0 : return gpg_error (GPG_ERR_ENOMEM);
578 :
579 80 : for (i=0; *p && oid_name_tbl[i].name; i++)
580 : {
581 76 : if (oid_name_tbl[i].source == 1
582 36 : && !strcmp (p, oid_name_tbl[i].oidstr))
583 : {
584 0 : name = oid_name_tbl[i].name;
585 0 : break;
586 : }
587 : }
588 4 : if (name)
589 0 : put_stringbuf (sb, name);
590 : else
591 : {
592 4 : put_stringbuf (sb, p);
593 4 : use_hex = 1;
594 : }
595 4 : xfree (p);
596 : }
597 41 : put_stringbuf (sb, "=");
598 41 : node = node->right;
599 41 : if (!node || node->off == -1)
600 0 : return gpg_error (GPG_ERR_NO_VALUE);
601 :
602 41 : switch (use_hex? 0 : node->type)
603 : {
604 : case TYPE_UTF8_STRING:
605 0 : append_utf8_value (image+node->off+node->nhdr, node->len, sb);
606 0 : break;
607 : case TYPE_PRINTABLE_STRING:
608 : case TYPE_IA5_STRING:
609 : /* we assume that wrong encodings are latin-1 */
610 : case TYPE_TELETEX_STRING: /* Not correct, but mostly used as latin-1 */
611 37 : append_latin1_value (image+node->off+node->nhdr, node->len, sb);
612 37 : break;
613 :
614 : case TYPE_UNIVERSAL_STRING:
615 0 : append_ucs4_value (image+node->off+node->nhdr, node->len, sb);
616 0 : break;
617 :
618 : case TYPE_BMP_STRING:
619 0 : append_ucs2_value (image+node->off+node->nhdr, node->len, sb);
620 0 : break;
621 :
622 : case 0: /* forced usage of hex */
623 : default:
624 4 : put_stringbuf (sb, "#");
625 76 : for (i=0; i < node->len; i++)
626 : {
627 : char tmp[3];
628 72 : snprintf (tmp, sizeof tmp, "%02X", image[node->off+node->nhdr+i]);
629 72 : put_stringbuf (sb, tmp);
630 : }
631 4 : break;
632 : }
633 :
634 41 : return 0;
635 : }
636 :
637 : static gpg_error_t
638 9 : dn_to_str (const unsigned char *image, AsnNode root, struct stringbuf *sb)
639 : {
640 : gpg_error_t err;
641 : AsnNode nset;
642 :
643 9 : if (!root )
644 0 : return 0; /* empty DN */
645 9 : nset = root->down;
646 9 : if (!nset)
647 0 : return 0; /* consider this as empty */
648 9 : if (nset->type != TYPE_SET_OF)
649 0 : return gpg_error (GPG_ERR_UNEXPECTED_TAG);
650 :
651 : /* output in reverse order */
652 50 : while (nset->right)
653 32 : nset = nset->right;
654 :
655 : for (;;)
656 32 : {
657 : AsnNode nseq;
658 :
659 41 : if (nset->type != TYPE_SET_OF)
660 0 : return gpg_error (GPG_ERR_UNEXPECTED_TAG);
661 82 : for (nseq = nset->down; nseq; nseq = nseq->right)
662 : {
663 41 : if (nseq->type != TYPE_SEQUENCE)
664 0 : return gpg_error (GPG_ERR_UNEXPECTED_TAG);
665 41 : if (nseq != nset->down)
666 0 : put_stringbuf (sb, "+");
667 41 : err = append_atv (image, nseq, sb);
668 41 : if (err)
669 0 : return err;
670 : }
671 41 : if (nset == root->down)
672 9 : break;
673 32 : put_stringbuf (sb, ",");
674 32 : nset = nset->left;
675 : }
676 :
677 9 : return 0;
678 : }
679 :
680 :
681 : gpg_error_t
682 9 : _ksba_dn_to_str (const unsigned char *image, AsnNode node, char **r_string)
683 : {
684 : gpg_error_t err;
685 : struct stringbuf sb;
686 :
687 9 : *r_string = NULL;
688 9 : if (!node || node->type != TYPE_SEQUENCE_OF)
689 0 : return gpg_error (GPG_ERR_INV_VALUE);
690 :
691 9 : init_stringbuf (&sb, 100);
692 9 : err = dn_to_str (image, node, &sb);
693 9 : if (!err)
694 : {
695 9 : *r_string = get_stringbuf (&sb);
696 9 : if (!*r_string)
697 0 : err = gpg_error (GPG_ERR_ENOMEM);
698 : }
699 9 : deinit_stringbuf (&sb);
700 :
701 9 : return err;
702 : }
703 :
704 :
705 : /* Create a new decoder and run it for the given element */
706 : /* Fixme: this code is duplicated from cms-parser.c */
707 : static gpg_error_t
708 2 : create_and_run_decoder (ksba_reader_t reader, const char *elem_name,
709 : AsnNode *r_root,
710 : unsigned char **r_image, size_t *r_imagelen)
711 : {
712 : gpg_error_t err;
713 : ksba_asn_tree_t crl_tree;
714 : BerDecoder decoder;
715 :
716 2 : err = ksba_asn_create_tree ("tmttv2", &crl_tree);
717 2 : if (err)
718 0 : return err;
719 :
720 2 : decoder = _ksba_ber_decoder_new ();
721 2 : if (!decoder)
722 : {
723 0 : ksba_asn_tree_release (crl_tree);
724 0 : return gpg_error (GPG_ERR_ENOMEM);
725 : }
726 :
727 2 : err = _ksba_ber_decoder_set_reader (decoder, reader);
728 2 : if (err)
729 : {
730 0 : ksba_asn_tree_release (crl_tree);
731 0 : _ksba_ber_decoder_release (decoder);
732 0 : return err;
733 : }
734 :
735 2 : err = _ksba_ber_decoder_set_module (decoder, crl_tree);
736 2 : if (err)
737 : {
738 0 : ksba_asn_tree_release (crl_tree);
739 0 : _ksba_ber_decoder_release (decoder);
740 0 : return err;
741 : }
742 :
743 2 : err = _ksba_ber_decoder_decode (decoder, elem_name, 0,
744 : r_root, r_image, r_imagelen);
745 :
746 2 : _ksba_ber_decoder_release (decoder);
747 2 : ksba_asn_tree_release (crl_tree);
748 2 : return err;
749 : }
750 :
751 :
752 : gpg_error_t
753 2 : _ksba_derdn_to_str (const unsigned char *der, size_t derlen, char **r_string)
754 : {
755 : gpg_error_t err;
756 : AsnNode root;
757 : unsigned char *image;
758 : size_t imagelen;
759 : ksba_reader_t reader;
760 :
761 2 : err = ksba_reader_new (&reader);
762 2 : if (err)
763 0 : return err;
764 2 : err = ksba_reader_set_mem (reader, der, derlen);
765 2 : if (err)
766 : {
767 0 : ksba_reader_release (reader);
768 0 : return err;
769 : }
770 2 : err = create_and_run_decoder (reader,
771 : "TMTTv2.CertificateList.tbsCertList.issuer",
772 : &root, &image, &imagelen);
773 2 : ksba_reader_release (reader);
774 2 : if (!err)
775 : {
776 2 : err = _ksba_dn_to_str (image, root->down, r_string);
777 2 : _ksba_asn_release_nodes (root);
778 2 : xfree (image);
779 : }
780 2 : return err;
781 : }
782 :
783 :
784 : /*
785 : Convert a string back to DN
786 : */
787 :
788 : /* Count the number of bytes in a quoted string, return a pointer to
789 : the character after the string or NULL in case of an paring error.
790 : The number of bytes needed to store the string verbatim will be
791 : return as RESULT. With V2COMAP true, the string is assumed to be
792 : in v2 quoting (but w/o the leading quote character)
793 : */
794 : static const char *
795 74 : count_quoted_string (const char *string, size_t *result,
796 : int v2compat, int *stringtype)
797 : {
798 : const unsigned char *s;
799 74 : int nbytes = 0;
800 74 : int highbit = 0;
801 74 : int nonprint = 0;
802 74 : int atsign = 0;
803 :
804 74 : *stringtype = 0;
805 497 : for (s=string; *s; s++)
806 : {
807 482 : if (*s == '\\')
808 : { /* pair */
809 0 : s++;
810 0 : if (*s == ',' || *s == '=' || *s == '+'
811 0 : || *s == '<' || *s == '>' || *s == '#' || *s == ';'
812 0 : || *s == '\\' || *s == '\"' || *s == ' ')
813 : {
814 0 : if (!charclasses[*s])
815 0 : nonprint = 1;
816 0 : nbytes++;
817 : }
818 0 : else if (hexdigitp (s) && hexdigitp (s+1))
819 0 : {
820 0 : int c = xtoi_2 (s);
821 0 : if ((c & 0x80))
822 0 : highbit = 1;
823 0 : else if (c == '@')
824 0 : atsign = 1;
825 0 : else if (!charclasses[c])
826 0 : nonprint = 1;
827 :
828 0 : s++;
829 0 : nbytes++;
830 : }
831 : else
832 0 : return NULL; /* invalid escape sequence */
833 : }
834 482 : else if (*s == '\"')
835 : {
836 2 : if (v2compat)
837 2 : break;
838 0 : return NULL; /* invalid encoding */
839 : }
840 480 : else if (!v2compat
841 480 : && (*s == ',' || *s == '=' || *s == '+'
842 423 : || *s == '<' || *s == '>' || *s == '#' || *s == ';') )
843 : {
844 : break;
845 : }
846 : else
847 : {
848 423 : nbytes++;
849 423 : if ((*s & 0x80))
850 24 : highbit = 1;
851 399 : else if (*s == '@')
852 3 : atsign = 1;
853 396 : else if (!charclasses[*s])
854 0 : nonprint = 1;
855 : }
856 : }
857 :
858 : /* Fixme: Should be remove spaces or white spces from the end unless
859 : they are not escaped or we are in v2compat mode? See TODO */
860 :
861 74 : if (highbit || nonprint)
862 6 : *stringtype = TYPE_UTF8_STRING;
863 68 : else if (atsign)
864 3 : *stringtype = TYPE_IA5_STRING;
865 : else
866 65 : *stringtype = TYPE_PRINTABLE_STRING;
867 :
868 74 : *result = nbytes;
869 74 : return s;
870 : }
871 :
872 :
873 : /* Write out the data to W and do the required escaping. Note that
874 : NBYTES is the number of bytes actually to be written, i.e. it is
875 : the result from count_quoted_string */
876 : static gpg_error_t
877 14 : write_escaped (ksba_writer_t w, const unsigned char *buffer, size_t nbytes)
878 : {
879 : const unsigned char *s;
880 : gpg_error_t err;
881 :
882 137 : for (s=buffer; nbytes; s++)
883 : {
884 123 : if (*s == '\\')
885 : {
886 0 : s++;
887 0 : if (hexdigitp (s) && hexdigitp (s+1))
888 0 : {
889 0 : unsigned char buf = xtoi_2 (s);
890 0 : err = ksba_writer_write (w, &buf, 1);
891 0 : if (err)
892 0 : return err;
893 0 : s++;
894 0 : nbytes--;
895 : }
896 : else
897 : {
898 0 : err = ksba_writer_write (w, s, 1);
899 0 : if (err)
900 0 : return err;
901 0 : nbytes--;
902 : }
903 : }
904 : else
905 : {
906 123 : err = ksba_writer_write (w, s, 1);
907 123 : if (err)
908 0 : return err;
909 123 : nbytes--;
910 : }
911 : }
912 :
913 14 : return 0;
914 : }
915 :
916 :
917 : /* Parse one RDN, and write it to WRITER. Returns a pointer to the
918 : next RDN part where the comma has already been skipped or NULL in
919 : case of an error. When NULL is passed as WRITER, the function does
920 : not allocate any memory but just parses the string and returns the
921 : ENDP. If ROFF or RLEN are not NULL, they will receive informaion
922 : useful for error reporting. */
923 : static gpg_error_t
924 84 : parse_rdn (const unsigned char *string, const char **endp,
925 : ksba_writer_t writer, size_t *roff, size_t *rlen)
926 : {
927 84 : const unsigned char *orig_string = string;
928 : const unsigned char *s, *s1;
929 : size_t n, n1;
930 : int i;
931 : unsigned char *p;
932 84 : unsigned char *oidbuf = NULL;
933 84 : unsigned char *valuebuf = NULL;
934 84 : const unsigned char *oid = NULL;
935 : size_t oidlen;
936 84 : const unsigned char *value = NULL;
937 : int valuelen;
938 : int valuetype;
939 84 : int need_escaping = 0;
940 84 : gpg_error_t err = 0;
941 : size_t dummy_roff, dummy_rlen;
942 :
943 84 : if (!roff)
944 49 : roff = &dummy_roff;
945 84 : if (!rlen)
946 49 : rlen = &dummy_rlen;
947 :
948 84 : *roff = *rlen = 0;
949 :
950 84 : if (!string)
951 0 : return gpg_error (GPG_ERR_INV_VALUE);
952 192 : while (*string == ' ')
953 24 : string++;
954 84 : *roff = string - orig_string;
955 84 : if (!*string)
956 0 : return gpg_error (GPG_ERR_SYNTAX); /* empty elements are not allowed */
957 84 : s = string;
958 :
959 84 : if ( ((*s == 'o' && s[1] == 'i' && s[2] == 'd')
960 84 : ||(*s == 'O' && s[1] == 'I' && s[2] == 'D'))
961 0 : && s[3] == '.' && digitp (s+4))
962 0 : s += 3; /* skip a prefixed oid */
963 :
964 : /* parse attributeType */
965 84 : string = s;
966 84 : *roff = string - orig_string;
967 84 : if (digitp (s))
968 : { /* oid */
969 0 : for (s++; digitp (s) || (*s == '.' && s[1] != '.'); s++)
970 : ;
971 0 : n = s - string;
972 0 : while (*s == ' ')
973 0 : s++;
974 0 : if (*s != '=')
975 0 : return gpg_error (GPG_ERR_SYNTAX);
976 :
977 0 : if (writer)
978 : {
979 0 : p = xtrymalloc (n+1);
980 0 : if (!p)
981 0 : return gpg_error (GPG_ERR_ENOMEM);
982 0 : memcpy (p, string, n);
983 0 : p[n] = 0;
984 0 : err = ksba_oid_from_str (p, &oidbuf, &oidlen);
985 0 : xfree (p);
986 0 : if (err)
987 0 : return err;
988 0 : oid = oidbuf;
989 : }
990 : }
991 84 : else if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z') )
992 : { /* name */
993 215 : for (s++; ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')
994 131 : || digitp (s) || *s == '-'); s++)
995 : ;
996 84 : n = s - string;
997 170 : while (*s == ' ')
998 2 : s++;
999 84 : if (*s != '=')
1000 0 : return gpg_error (GPG_ERR_SYNTAX);
1001 :
1002 547 : for (i=0; oid_name_tbl[i].name; i++)
1003 : {
1004 543 : if ( n == strlen (oid_name_tbl[i].name)
1005 175 : && !ascii_memcasecmp (string, oid_name_tbl[i].name, n))
1006 80 : break;
1007 : }
1008 84 : if (!oid_name_tbl[i].name)
1009 : {
1010 4 : *roff = string - orig_string;
1011 4 : *rlen = n;
1012 4 : return gpg_error (GPG_ERR_UNKNOWN_NAME);
1013 : }
1014 80 : oid = oid_name_tbl[i].oid;
1015 80 : oidlen = oid_name_tbl[i].oidlen;
1016 : }
1017 : else
1018 0 : return gpg_error (GPG_ERR_INV_NAME);
1019 :
1020 80 : s++;
1021 169 : while (*s == ' ')
1022 9 : s++;
1023 80 : string = s;
1024 :
1025 80 : *roff = string - orig_string;
1026 :
1027 : /* parse attributeValue */
1028 80 : if (!*s)
1029 : {
1030 6 : err = gpg_error (GPG_ERR_SYNTAX); /* missing value */
1031 6 : goto leave;
1032 : }
1033 :
1034 74 : if (*s == '#')
1035 : { /* hexstring */
1036 0 : int need_utf8 = 0;
1037 0 : int need_ia5 = 0;
1038 :
1039 0 : string = ++s;
1040 0 : for (; hexdigitp (s); s++)
1041 0 : s++;
1042 0 : n = s - string;
1043 0 : if (!n || (n & 1))
1044 : {
1045 0 : *rlen = n;
1046 0 : err = gpg_error (GPG_ERR_SYNTAX); /* no hex digits or odd number */
1047 0 : goto leave;
1048 : }
1049 0 : while (*s == ' ')
1050 0 : s++;
1051 0 : n /= 2;
1052 0 : valuelen = n;
1053 0 : if (writer)
1054 : {
1055 0 : valuebuf = xtrymalloc (valuelen);
1056 0 : if (!valuebuf)
1057 : {
1058 0 : err = gpg_error (GPG_ERR_ENOMEM);
1059 0 : goto leave;
1060 : }
1061 0 : for (p=valuebuf, s1=string; n; p++, s1 += 2, n--)
1062 : {
1063 0 : *p = xtoi_2 (s1);
1064 0 : if (*p == '@')
1065 0 : need_ia5 = 1;
1066 0 : else if ((*p & 0x80) || !charclasses[*p])
1067 0 : need_utf8 = 1;
1068 : }
1069 0 : value = valuebuf;
1070 : }
1071 : else
1072 : {
1073 0 : for (s1=string; n; s1 += 2, n--)
1074 : {
1075 : unsigned int c;
1076 :
1077 0 : c = xtoi_2 (s1);
1078 0 : if (c == '@')
1079 0 : need_ia5 = 1;
1080 0 : else if ((c & 0x80) || !charclasses[c])
1081 0 : need_utf8 = 1;
1082 : }
1083 : }
1084 0 : valuetype = need_utf8? TYPE_UTF8_STRING :
1085 0 : need_ia5 ? TYPE_IA5_STRING : TYPE_PRINTABLE_STRING;
1086 : }
1087 74 : else if (*s == '\"')
1088 : { /* old style quotation */
1089 2 : string = s+1;
1090 2 : s = count_quoted_string (string, &n, 1, &valuetype);
1091 2 : if (!s || *s != '\"')
1092 : {
1093 0 : *rlen = s - orig_string;
1094 0 : err = gpg_error (GPG_ERR_SYNTAX); /* error or quote not closed */
1095 0 : goto leave;
1096 : }
1097 2 : s++;
1098 6 : while (*s == ' ')
1099 2 : s++;
1100 2 : value = string;
1101 2 : valuelen = n;
1102 2 : need_escaping = 1;
1103 : }
1104 : else
1105 : { /* regular v3 quoted string */
1106 72 : s = count_quoted_string (string, &n, 0, &valuetype);
1107 72 : if (!s)
1108 : {
1109 0 : err = gpg_error (GPG_ERR_SYNTAX); /* error */
1110 0 : goto leave;
1111 : }
1112 144 : while (*s == ' ')
1113 0 : s++;
1114 72 : value = string;
1115 72 : valuelen = n;
1116 72 : need_escaping = 1;
1117 : }
1118 :
1119 74 : if (!valuelen)
1120 : {
1121 10 : err = gpg_error (GPG_ERR_SYNTAX); /* empty elements are not allowed */
1122 10 : goto leave;
1123 : }
1124 64 : if ( *s && *s != ',' && *s != ';' && *s != '+')
1125 : {
1126 0 : *roff = s - orig_string;
1127 0 : err = gpg_error (GPG_ERR_SYNTAX); /* invalid delimiter */
1128 0 : goto leave;
1129 : }
1130 64 : if (*s == '+') /* fixme: implement this */
1131 : {
1132 0 : *roff = s - orig_string;
1133 0 : *rlen = 1;
1134 0 : err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1135 0 : goto leave;
1136 : }
1137 64 : *endp = *s? (s+1):s;
1138 :
1139 64 : if (writer)
1140 : { /* write out the data */
1141 :
1142 : /* need to calculate the length in advance */
1143 14 : n1 = _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, oidlen);
1144 14 : n1 += oidlen;
1145 14 : n1 += _ksba_ber_count_tl (valuetype, CLASS_UNIVERSAL, 0, valuelen);
1146 14 : n1 += valuelen;
1147 :
1148 : /* The SET tag */
1149 14 : n = _ksba_ber_count_tl (TYPE_SET, CLASS_UNIVERSAL, 1, n);
1150 14 : n += n1;
1151 14 : err = _ksba_ber_write_tl (writer, TYPE_SET, CLASS_UNIVERSAL, 1, n);
1152 :
1153 : /* The sequence tag */
1154 14 : n = n1;
1155 14 : err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n);
1156 :
1157 : /* the OBJECT ID */
1158 14 : err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL,
1159 : 0, oidlen);
1160 14 : if (!err)
1161 14 : err = ksba_writer_write (writer, oid, oidlen);
1162 14 : if (err)
1163 0 : goto leave;
1164 :
1165 : /* the value. Note that we don't need any conversion to the target
1166 : characters set because the input is expected to be utf8 and the
1167 : target type is either utf8, IA5 or printable string where the last
1168 : two are subsets of utf8 */
1169 14 : err = _ksba_ber_write_tl (writer, valuetype,
1170 : CLASS_UNIVERSAL, 0, valuelen);
1171 14 : if (!err)
1172 28 : err = need_escaping? write_escaped (writer, value, valuelen)
1173 28 : : ksba_writer_write (writer, value, valuelen);
1174 : }
1175 :
1176 : leave:
1177 80 : xfree (oidbuf);
1178 80 : xfree (valuebuf);
1179 80 : return err;
1180 : }
1181 :
1182 :
1183 :
1184 : gpg_error_t
1185 15 : _ksba_dn_from_str (const char *string, char **rbuf, size_t *rlength)
1186 : {
1187 : gpg_error_t err;
1188 : ksba_writer_t writer;
1189 : const char *s, *endp;
1190 15 : void *buf = NULL;
1191 : size_t buflen;
1192 15 : char const **part_array = NULL;
1193 : int part_array_size, nparts;
1194 :
1195 15 : *rbuf = NULL; *rlength = 0;
1196 : /* We are going to build the object using a writer object. */
1197 15 : err = ksba_writer_new (&writer);
1198 15 : if (!err)
1199 15 : err = ksba_writer_set_mem (writer, 1024);
1200 15 : if (err)
1201 0 : return err;
1202 :
1203 : /* We must assign it in reverse order so we do it in 2 passes. */
1204 15 : part_array_size = 0;
1205 55 : for (nparts=0, s=string; s && *s;)
1206 : {
1207 35 : err = parse_rdn (s, &endp, NULL, NULL, NULL);
1208 35 : if (err)
1209 10 : goto leave;
1210 25 : if (nparts >= part_array_size)
1211 : {
1212 : char const **tmp;
1213 :
1214 14 : part_array_size += 2;
1215 14 : tmp = part_array_size? xtryrealloc (part_array,
1216 : part_array_size * sizeof *tmp)
1217 0 : : xtrymalloc (part_array_size * sizeof *tmp);
1218 14 : if (!tmp)
1219 : {
1220 0 : err = gpg_error (GPG_ERR_ENOMEM);
1221 0 : goto leave;
1222 : }
1223 14 : part_array = tmp;
1224 : }
1225 25 : part_array[nparts++] = s;
1226 25 : s = endp;
1227 : }
1228 5 : if (!nparts)
1229 : {
1230 0 : err = gpg_error (GPG_ERR_SYNTAX);
1231 0 : goto leave;
1232 : }
1233 :
1234 24 : while (--nparts >= 0)
1235 : {
1236 14 : err = parse_rdn (part_array[nparts], &endp, writer, NULL, NULL);
1237 14 : if (err)
1238 0 : goto leave;
1239 : }
1240 :
1241 : /* Now get the memory. */
1242 5 : buf = ksba_writer_snatch_mem (writer, &buflen);
1243 5 : if (!buf)
1244 : {
1245 0 : err = gpg_error (GPG_ERR_ENOMEM);
1246 0 : goto leave;
1247 : }
1248 : /* Reinitialize the buffer to create the outer sequence. */
1249 5 : err = ksba_writer_set_mem (writer, buflen + 10);
1250 5 : if (err)
1251 0 : goto leave;
1252 :
1253 : /* write the outer sequence */
1254 5 : err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE,
1255 : CLASS_UNIVERSAL, 1, buflen);
1256 5 : if (err)
1257 0 : goto leave;
1258 : /* write the collected sets */
1259 5 : err = ksba_writer_write (writer, buf, buflen);
1260 5 : if (err)
1261 0 : goto leave;
1262 :
1263 : /* and get the result */
1264 5 : *rbuf = ksba_writer_snatch_mem (writer, rlength);
1265 5 : if (!*rbuf)
1266 : {
1267 0 : err = gpg_error (GPG_ERR_ENOMEM);
1268 0 : goto leave;
1269 : }
1270 :
1271 : leave:
1272 15 : xfree (part_array);
1273 15 : ksba_writer_release (writer);
1274 15 : xfree (buf);
1275 15 : return err;
1276 : }
1277 :
1278 :
1279 :
1280 : gpg_error_t
1281 0 : ksba_dn_der2str (const void *der, size_t derlen, char **rstring)
1282 : {
1283 0 : return _ksba_derdn_to_str (der, derlen, rstring);
1284 : }
1285 :
1286 :
1287 : gpg_error_t
1288 15 : ksba_dn_str2der (const char *string, unsigned char **rder, size_t *rderlen)
1289 : {
1290 15 : return _ksba_dn_from_str (string, (char**)rder, rderlen);
1291 : }
1292 :
1293 :
1294 :
1295 : /* Assuming that STRING contains an rfc2253 encoded string, test
1296 : whether this string may be passed as a valid DN to libksba. On
1297 : success the functions returns 0. On error the function returns an
1298 : error code and stores the offset within STRING of the erroneous
1299 : part at RERROFF. RERRLEN will then receive the length of the
1300 : erroneous part. This function is most useful to test whether a
1301 : symbolic name (like SN) is supported. SEQ should be passed as 0 for
1302 : now. RERROFF and RERRLEN may be passed as NULL if the caller is
1303 : not interested at this value. */
1304 : gpg_error_t
1305 15 : ksba_dn_teststr (const char *string, int seq,
1306 : size_t *rerroff, size_t *rerrlen)
1307 : {
1308 : size_t dummy_erroff, dummy_errlen;
1309 : gpg_error_t err;
1310 : int nparts;
1311 : const char *s, *endp;
1312 : size_t off, len;
1313 :
1314 15 : if (!rerroff)
1315 0 : rerroff = &dummy_erroff;
1316 15 : if (!rerrlen)
1317 0 : rerrlen = &dummy_errlen;
1318 :
1319 15 : *rerrlen = *rerroff = 0;
1320 :
1321 40 : for (nparts=0, s=string; s && *s; nparts++)
1322 : {
1323 35 : err = parse_rdn (s, &endp, NULL, &off, &len);
1324 35 : if (err && !seq--)
1325 : {
1326 10 : *rerroff = s - string + off;
1327 10 : *rerrlen = len? len : strlen (s);
1328 10 : return err;
1329 : }
1330 25 : s = endp;
1331 : }
1332 5 : if (!nparts)
1333 0 : return gpg_error (GPG_ERR_SYNTAX);
1334 5 : return 0;
1335 : }
|