92e9e6f974f75690950e097391f7f30956bbc9d1
[tprouty/samba.git] / source4 / heimdal / lib / hx509 / name.c
1 /*
2  * Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright 
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #include "hx_locl.h"
35 RCSID("$Id: name.c,v 1.33 2006/12/30 23:04:11 lha Exp $");
36
37 /* 
38  * name parsing from rfc2253
39  * fix so parsing rfc1779 works too 
40  * rfc3280
41  */
42
43 static const struct {
44     char *n;
45     const heim_oid *(*o)(void);
46 } no[] = {
47     { "C", oid_id_at_countryName },
48     { "CN", oid_id_at_commonName },
49     { "DC", oid_id_domainComponent },
50     { "L", oid_id_at_localityName },
51     { "O", oid_id_at_organizationName },
52     { "OU", oid_id_at_organizationalUnitName },
53     { "S", oid_id_at_stateOrProvinceName },
54     { "UID", oid_id_Userid },
55     { "emailAddress", oid_id_pkcs9_emailAddress },
56     { "serialNumber", oid_id_at_serialNumber }
57 };
58
59 static char *
60 quote_string(const char *f, size_t len, size_t *rlen)
61 {
62     size_t i, j, tolen;
63     const char *from = f;
64     char *to;
65
66     tolen = len * 3 + 1;
67     to = malloc(tolen);
68     if (to == NULL)
69         return NULL;
70
71     for (i = 0, j = 0; i < len; i++) {
72         if (from[i] == ' ' && i + 1 < len)
73             to[j++] = from[i];
74         else if (from[i] == ',' || from[i] == '=' || from[i] == '+' ||
75                  from[i] == '<' || from[i] == '>' || from[i] == '#' ||
76                  from[i] == ';' || from[i] == ' ')
77         {
78             to[j++] = '\\';
79             to[j++] = from[i];
80         } else if (((unsigned char)from[i]) >= 32 && ((unsigned char)from[i]) <= 127) {
81             to[j++] = from[i];
82         } else {
83             int l = snprintf(&to[j], tolen - j - 1,
84                              "#%02x", (unsigned int)from[i]);
85             j += l;
86         }
87     }
88     to[j] = '\0';
89     *rlen = j;
90     return to;
91 }
92
93
94 static int
95 append_string(char **str, size_t *total_len, char *ss, size_t len, int quote)
96 {
97     char *s, *qs;
98
99     if (quote)
100         qs = quote_string(ss, len, &len);
101     else
102         qs = ss;
103
104     s = realloc(*str, len + *total_len + 1);
105     if (s == NULL)
106         _hx509_abort("allocation failure"); /* XXX */
107     memcpy(s + *total_len, qs, len);
108     if (qs != ss)
109         free(qs);
110     s[*total_len + len] = '\0';
111     *str = s;
112     *total_len += len;
113     return 0;
114 }
115
116 static char *
117 oidtostring(const heim_oid *type)
118 {
119     char *s;
120     size_t i;
121     
122     for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
123         if (der_heim_oid_cmp((*no[i].o)(), type) == 0)
124             return strdup(no[i].n);
125     }
126     if (der_print_heim_oid(type, '.', &s) != 0)
127         return NULL;
128     return s;
129 }
130
131 static int
132 stringtooid(const char *name, size_t len, heim_oid *oid)
133 {
134     int i, ret;
135     char *s;
136     
137     memset(oid, 0, sizeof(*oid));
138
139     for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
140         if (strncasecmp(no[i].n, name, len) == 0)
141             return der_copy_oid((*no[i].o)(), oid);
142     }
143     s = malloc(len + 1);
144     if (s == NULL)
145         return ENOMEM;
146     memcpy(s, name, len);
147     s[len] = '\0';
148     ret = der_parse_heim_oid(s, ".", oid);
149     free(s);
150     return ret;
151 }
152
153 int
154 hx509_name_to_string(const hx509_name name, char **str)
155 {
156     return _hx509_Name_to_string(&name->der_name, str);
157 }
158
159 int
160 _hx509_Name_to_string(const Name *n, char **str)
161 {
162     size_t total_len = 0;
163     int i, j;
164
165     *str = strdup("");
166     if (*str == NULL)
167         return ENOMEM;
168
169     for (i = n->u.rdnSequence.len - 1 ; i >= 0 ; i--) {
170         int len;
171
172         for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
173             DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
174             char *oidname;
175             char *ss;
176             
177             oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type);
178
179             switch(ds->element) {
180             case choice_DirectoryString_ia5String:
181                 ss = ds->u.ia5String;
182                 break;
183             case choice_DirectoryString_printableString:
184                 ss = ds->u.ia5String;
185                 break;
186             case choice_DirectoryString_utf8String:
187                 ss = ds->u.ia5String;
188                 break;
189             case choice_DirectoryString_bmpString: {
190                 uint16_t *bmp = ds->u.bmpString.data;
191                 size_t bmplen = ds->u.bmpString.length;
192                 size_t k;
193
194                 ss = malloc(bmplen + 1);
195                 if (ss == NULL)
196                     _hx509_abort("allocation failure"); /* XXX */
197                 for (k = 0; k < bmplen; k++)
198                     ss[k] = bmp[k] & 0xff; /* XXX */
199                 ss[k] = '\0';
200                 break;
201             }
202             case choice_DirectoryString_teletexString:
203                 ss = "teletex-string"; /* XXX */
204                 break;
205             case choice_DirectoryString_universalString:
206                 ss = "universalString"; /* XXX */
207                 break;
208             default:
209                 _hx509_abort("unknown directory type: %d", ds->element);
210                 exit(1);
211             }
212             append_string(str, &total_len, oidname, strlen(oidname), 0);
213             free(oidname);
214             append_string(str, &total_len, "=", 1, 0);
215             len = strlen(ss);
216             append_string(str, &total_len, ss, len, 1);
217             if (ds->element == choice_DirectoryString_bmpString)
218                 free(ss);
219             if (j + 1 < n->u.rdnSequence.val[i].len)
220                 append_string(str, &total_len, "+", 1, 0);
221         }
222
223         if (i > 0)
224             append_string(str, &total_len, ",", 1, 0);
225     }
226     return 0;
227 }
228
229 /*
230  * XXX this function is broken, it needs to compare code points, not
231  * bytes.
232  */
233
234 int
235 _hx509_name_ds_cmp(const DirectoryString *ds1, const DirectoryString *ds2)
236 {
237     int c;
238
239     c = ds1->element - ds2->element;
240     if (c)
241         return c;
242
243     switch(ds1->element) {
244     case choice_DirectoryString_ia5String:
245         c = strcmp(ds1->u.ia5String, ds2->u.ia5String);
246         break;
247     case choice_DirectoryString_teletexString:
248         c = der_heim_octet_string_cmp(&ds1->u.teletexString,
249                                   &ds2->u.teletexString);
250         break;
251     case choice_DirectoryString_printableString:
252         c = strcasecmp(ds1->u.printableString, ds2->u.printableString);
253         break;
254     case choice_DirectoryString_utf8String:
255         c = strcmp(ds1->u.utf8String, ds2->u.utf8String);
256         break;
257     case choice_DirectoryString_universalString:
258         c = der_heim_universal_string_cmp(&ds1->u.universalString,
259                                           &ds2->u.universalString);
260         break;
261     case choice_DirectoryString_bmpString:
262         c = der_heim_bmp_string_cmp(&ds1->u.bmpString,
263                                     &ds2->u.bmpString);
264         break;
265     default:
266         c = 1;
267         break;
268     }
269     return c;
270 }
271
272 int
273 _hx509_name_cmp(const Name *n1, const Name *n2)
274 {
275     int i, j, c;
276
277     c = n1->u.rdnSequence.len - n2->u.rdnSequence.len;
278     if (c)
279         return c;
280
281     for (i = 0 ; i < n1->u.rdnSequence.len; i++) {
282         c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len;
283         if (c)
284             return c;
285
286         for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) {
287             c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type,
288                                  &n1->u.rdnSequence.val[i].val[j].type);
289             if (c)
290                 return c;
291                              
292             c = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value,
293                                    &n2->u.rdnSequence.val[i].val[j].value);
294             if (c)
295                 return c;
296         }
297     }
298     return 0;
299 }
300
301 int
302 _hx509_name_from_Name(const Name *n, hx509_name *name)
303 {
304     int ret;
305     *name = calloc(1, sizeof(**name));
306     if (*name == NULL)
307         return ENOMEM;
308     ret = copy_Name(n, &(*name)->der_name);
309     if (ret) {
310         free(*name);
311         *name = NULL;
312     }
313     return ret;
314 }
315
316 static int
317 hx509_der_parse_name(const void *data, size_t length, hx509_name *name)
318 {
319     int ret;
320     Name n;
321
322     *name = NULL;
323     ret = decode_Name(data, length, &n, NULL);
324     if (ret)
325         return ret;
326     return _hx509_name_from_Name(&n, name);
327 }
328
329 int
330 _hx509_name_modify(hx509_context context,
331                    Name *name, 
332                    int append,
333                    const heim_oid *oid, 
334                    const char *str)
335 {
336     RelativeDistinguishedName *rdn;
337     int ret;
338     void *ptr;
339
340     ptr = realloc(name->u.rdnSequence.val, 
341                   sizeof(name->u.rdnSequence.val[0]) * 
342                   (name->u.rdnSequence.len + 1));
343     if (ptr == NULL) {
344         hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
345         return ENOMEM;
346     }
347     name->u.rdnSequence.val = ptr;
348
349     if (append) {
350         rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len];
351     } else {
352         memmove(&name->u.rdnSequence.val[1],
353                 &name->u.rdnSequence.val[0],
354                 name->u.rdnSequence.len * 
355                 sizeof(name->u.rdnSequence.val[0]));
356         
357         rdn = &name->u.rdnSequence.val[0];
358     }
359     rdn->val = malloc(sizeof(rdn->val[0]));
360     if (rdn->val == NULL)
361         return ENOMEM;
362     rdn->len = 1;
363     ret = der_copy_oid(oid, &rdn->val[0].type);
364     if (ret)
365         return ret;
366     rdn->val[0].value.element = choice_DirectoryString_utf8String;
367     rdn->val[0].value.u.utf8String = strdup(str);
368     if (rdn->val[0].value.u.utf8String == NULL)
369         return ENOMEM;
370     name->u.rdnSequence.len += 1;
371
372     return 0;
373 }
374
375 int
376 hx509_parse_name(hx509_context context, const char *str, hx509_name *name)
377 {
378     const char *p, *q;
379     size_t len;
380     hx509_name n;
381     int ret;
382
383     *name = NULL;
384
385     n = calloc(1, sizeof(*n));
386     if (n == NULL) {
387         hx509_set_error_string(context, 0, ENOMEM, "out of memory");
388         return ENOMEM;
389     }
390
391     n->der_name.element = choice_Name_rdnSequence;
392
393     p = str;
394
395     while (p != NULL && *p != '\0') {
396         heim_oid oid;
397         int last;
398
399         q = strchr(p, ',');
400         if (q) {
401             len = (q - p);
402             last = 1;
403         } else {
404             len = strlen(p);
405             last = 0;
406         }
407
408         q = strchr(p, '=');
409         if (q == NULL) {
410             ret = HX509_PARSING_NAME_FAILED;
411             hx509_set_error_string(context, 0, ret, "missing = in %s", p);
412             goto out;
413         }
414         if (q == p) {
415             ret = HX509_PARSING_NAME_FAILED;
416             hx509_set_error_string(context, 0, ret, 
417                                    "missing name before = in %s", p);
418             goto out;
419         }
420         
421         if ((q - p) > len) {
422             ret = HX509_PARSING_NAME_FAILED;
423             hx509_set_error_string(context, 0, ret, " = after , in %s", p);
424             goto out;
425         }
426
427         ret = stringtooid(p, q - p, &oid);
428         if (ret) {
429             ret = HX509_PARSING_NAME_FAILED;
430             hx509_set_error_string(context, 0, ret, 
431                                    "unknown type: %.*s", (int)(q - p), p);
432             goto out;
433         }
434         
435         {
436             size_t pstr_len = len - (q - p) - 1;
437             const char *pstr = p + (q - p) + 1;
438             char *r;
439             
440             r = malloc(pstr_len + 1);
441             if (r == NULL) {
442                 der_free_oid(&oid);
443                 ret = ENOMEM;
444                 hx509_set_error_string(context, 0, ret, "out of memory");
445                 goto out;
446             }
447             memcpy(r, pstr, pstr_len);
448             r[pstr_len] = '\0';
449
450             ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r);
451             free(r);
452             der_free_oid(&oid);
453             if(ret)
454                 goto out;
455         }
456         p += len + last;
457     }
458
459     *name = n;
460
461     return 0;
462 out:
463     hx509_name_free(&n);
464     return HX509_NAME_MALFORMED;
465 }
466
467 int
468 hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to)
469 {
470     int ret;
471
472     *to = calloc(1, sizeof(**to));
473     if (*to == NULL)
474         return ENOMEM;
475     ret = copy_Name(&from->der_name, &(*to)->der_name);
476     if (ret) {
477         free(*to);
478         *to = NULL;
479         return ENOMEM;
480     }
481     return 0;
482 }
483
484 int
485 hx509_name_to_Name(const hx509_name from, Name *to)
486 {
487     return copy_Name(&from->der_name, to);
488 }
489
490
491 void
492 hx509_name_free(hx509_name *name)
493 {
494     free_Name(&(*name)->der_name);
495     memset(*name, 0, sizeof(**name));
496     free(*name);
497     *name = NULL;
498 }
499
500 int
501 hx509_unparse_der_name(const void *data, size_t length, char **str)
502 {
503     hx509_name name;
504     int ret;
505
506     ret = hx509_der_parse_name(data, length, &name);
507     if (ret)
508         return ret;
509     
510     ret = hx509_name_to_string(name, str);
511     hx509_name_free(&name);
512     return ret;
513 }
514
515 int
516 hx509_name_to_der_name(const hx509_name name, void **data, size_t *length)
517 {
518     size_t size;
519     int ret;
520
521     ASN1_MALLOC_ENCODE(Name, *data, *length, &name->der_name, &size, ret);
522     if (ret)
523         return ret;
524     if (*length != size)
525         _hx509_abort("internal ASN.1 encoder error");
526
527     return 0;
528 }
529
530
531 int
532 _hx509_unparse_Name(const Name *aname, char **str)
533 {
534     hx509_name name;
535     int ret;
536
537     ret = _hx509_name_from_Name(aname, &name);
538     if (ret)
539         return ret;
540
541     ret = hx509_name_to_string(name, str);
542     hx509_name_free(&name);
543     return ret;
544 }
545
546 int
547 hx509_name_is_null_p(const hx509_name name)
548 {
549     return name->der_name.u.rdnSequence.len == 0;
550 }