9741c616c1fb5dfc8ee9dd570b6a93ab4db8c40c
[strongswan.git] / src / libstrongswan / credentials / ietf_attributes / ietf_attributes.c
1 /*
2 * Copyright (C) 2007-2009 Andreas Steffen
3 *
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include <asn1/oid.h>
18 #include <asn1/asn1.h>
19 #include <asn1/asn1_parser.h>
20 #include <utils/linked_list.h>
21 #include <utils/lexparser.h>
22
23 #include "ietf_attributes.h"
24
25 /**
26 * Private definition of IETF attribute types
27 */
28 typedef enum {
29 IETF_ATTRIBUTE_OCTETS = 0,
30 IETF_ATTRIBUTE_OID = 1,
31 IETF_ATTRIBUTE_STRING = 2
32 } ietf_attribute_type_t;
33
34 typedef struct ietf_attr_t ietf_attr_t;
35
36 /**
37 * Private definition of an IETF attribute
38 */
39 struct ietf_attr_t {
40 /**
41 * IETF attribute type
42 */
43 ietf_attribute_type_t type;
44
45 /**
46 * IETF attribute value
47 */
48 chunk_t value;
49
50 /**
51 * Compares two IETF attributes
52 *
53 * return -1 if this is earlier in the alphabet than other
54 * return 0 if this equals other
55 * return +1 if this is later in the alphabet than other
56 *
57 * @param other other object
58 */
59 int (*compare) (ietf_attr_t *this, ietf_attr_t *other);
60
61 /**
62 * Destroys an ietf_attr_t object.
63 */
64 void (*destroy) (ietf_attr_t *this);
65 };
66
67 /**
68 * Implements ietf_attr_t.compare.
69 */
70 static int ietf_attr_compare(ietf_attr_t *this, ietf_attr_t *other)
71 {
72 int cmp_len, len, cmp_value;
73
74 /* OID attributes are appended after STRING and OCTETS attributes */
75 if (this->type != IETF_ATTRIBUTE_OID && other->type == IETF_ATTRIBUTE_OID)
76 {
77 return -1;
78 }
79 if (this->type == IETF_ATTRIBUTE_OID && other->type != IETF_ATTRIBUTE_OID)
80 {
81 return 1;
82 }
83
84 cmp_len = this->value.len - other->value.len;
85 len = (cmp_len < 0) ? this->value.len : other->value.len;
86 cmp_value = memcmp(this->value.ptr, other->value.ptr, len);
87
88 return (cmp_value == 0) ? cmp_len : cmp_value;
89 }
90
91 /**
92 * Implements ietf_attr_t.destroy.
93 */
94 static void ietf_attr_destroy(ietf_attr_t *this)
95 {
96 free(this->value.ptr);
97 free(this);
98 }
99
100 /**
101 * Creates an ietf_attr_t object.
102 */
103 static ietf_attr_t* ietf_attr_create(ietf_attribute_type_t type, chunk_t value)
104 {
105 ietf_attr_t *this = malloc_thing(ietf_attr_t);
106
107 /* initialize */
108 this->type = type;
109 this->value = chunk_clone(value);
110
111 /* function */
112 this->compare = ietf_attr_compare;
113 this->destroy = ietf_attr_destroy;
114
115 return this;
116 }
117
118 typedef struct private_ietf_attributes_t private_ietf_attributes_t;
119
120 /**
121 * Private data of an ietf_attributes_t object.
122 */
123 struct private_ietf_attributes_t {
124 /**
125 * Public interface.
126 */
127 ietf_attributes_t public;
128
129 /**
130 * Printable representation of the IETF attributes
131 */
132 char *string;
133
134 /**
135 * Linked list of IETF attributes.
136 */
137 linked_list_t *list;
138
139 /**
140 * reference count
141 */
142 refcount_t ref;
143 };
144
145 /**
146 * Implementation of ietf_attributes_t.get_string.
147 */
148 static char* get_string(private_ietf_attributes_t *this)
149 {
150 if (this->string == NULL)
151 {
152 char buf[BUF_LEN];
153 char *pos = buf;
154 int len = BUF_LEN;
155 bool first = TRUE;
156 ietf_attr_t *attr;
157 enumerator_t *enumerator;
158
159 enumerator = this->list->create_enumerator(this->list);
160 while (enumerator->enumerate(enumerator, &attr))
161 {
162 int written;
163
164 if (first)
165 {
166 first = FALSE;
167 }
168 else
169 {
170 written = snprintf(pos, len, ", ");
171 if (written < 0 || written >= len)
172 {
173 break;
174 }
175 pos += written;
176 len -= written;
177 }
178
179 switch (attr->type)
180 {
181 case IETF_ATTRIBUTE_OCTETS:
182 case IETF_ATTRIBUTE_STRING:
183 written = snprintf(pos, len, "%.*s", (int)attr->value.len,
184 attr->value.ptr);
185 break;
186 case IETF_ATTRIBUTE_OID:
187 {
188 int oid = asn1_known_oid(attr->value);
189
190 if (oid == OID_UNKNOWN)
191 {
192 written = snprintf(pos, len, "0x%#B", &attr->value);
193 }
194 else
195 {
196 written = snprintf(pos, len, "%s", oid_names[oid].name);
197 }
198 break;
199 }
200 default:
201 written = 0;
202 break;
203 }
204 if (written < 0 || written >= len)
205 {
206 break;
207 }
208 pos += written;
209 len -= written;
210 }
211 enumerator->destroy(enumerator);
212 if (len < BUF_LEN)
213 {
214 this->string = strdup(buf);
215 }
216 }
217 return this->string;
218 }
219
220 /**
221 * Implementation of ietf_attributes_t.get_encoding.
222 */
223 static chunk_t get_encoding(private_ietf_attributes_t *this)
224 {
225 chunk_t values;
226 size_t size = 0;
227 u_char *pos;
228 ietf_attr_t *attr;
229 enumerator_t *enumerator;
230
231 /* precalculate the total size of all values */
232 enumerator = this->list->create_enumerator(this->list);
233 while (enumerator->enumerate(enumerator, &attr))
234 {
235 size_t len = attr->value.len;
236
237 size += 1 + (len > 0) + (len >= 128) + (len >= 256) + (len >= 65536) + len;
238 }
239 enumerator->destroy(enumerator);
240
241 pos = asn1_build_object(&values, ASN1_SEQUENCE, size);
242
243 enumerator = this->list->create_enumerator(this->list);
244 while (enumerator->enumerate(enumerator, &attr))
245 {
246 chunk_t ietfAttribute;
247 asn1_t type = ASN1_NULL;
248
249 switch (attr->type)
250 {
251 case IETF_ATTRIBUTE_OCTETS:
252 type = ASN1_OCTET_STRING;
253 break;
254 case IETF_ATTRIBUTE_STRING:
255 type = ASN1_UTF8STRING;
256 break;
257 case IETF_ATTRIBUTE_OID:
258 type = ASN1_OID;
259 break;
260 }
261 ietfAttribute = asn1_simple_object(type, attr->value);
262
263 /* copy ietfAttribute into values chunk */
264 memcpy(pos, ietfAttribute.ptr, ietfAttribute.len);
265 pos += ietfAttribute.len;
266 free(ietfAttribute.ptr);
267 }
268 enumerator->destroy(enumerator);
269
270 return asn1_wrap(ASN1_SEQUENCE, "m", values);
271 }
272
273 static bool equals(private_ietf_attributes_t *this, private_ietf_attributes_t *other)
274 {
275 bool result = TRUE;
276
277 /* lists must have the same number of attributes */
278 if (other == NULL ||
279 this->list->get_count(this->list) != other->list->get_count(other->list))
280 {
281 return FALSE;
282 }
283
284 /* compare two alphabetically-sorted lists */
285 {
286 ietf_attr_t *attr_a, *attr_b;
287 enumerator_t *enum_a, *enum_b;
288
289 enum_a = this->list->create_enumerator(this->list);
290 enum_b = other->list->create_enumerator(other->list);
291 while (enum_a->enumerate(enum_a, &attr_a) &&
292 enum_b->enumerate(enum_b, &attr_b))
293 {
294 if (attr_a->compare(attr_a, attr_b) != 0)
295 {
296 /* we have a mismatch */
297 result = FALSE;
298 break;
299 }
300 }
301 enum_a->destroy(enum_a);
302 enum_b->destroy(enum_b);
303 }
304 return result;
305 }
306
307 static bool matches(private_ietf_attributes_t *this, private_ietf_attributes_t *other)
308 {
309 bool result = FALSE;
310 ietf_attr_t *attr_a, *attr_b;
311 enumerator_t *enum_a, *enum_b;
312
313 /* always match if this->list does not contain any attributes */
314 if (this->list->get_count(this->list) == 0)
315 {
316 return TRUE;
317 }
318
319 /* never match if other->list does not contain any attributes */
320 if (other == NULL || other->list->get_count(other->list) == 0)
321 {
322 return FALSE;
323 }
324
325 /* get first attribute from both lists */
326 enum_a = this->list->create_enumerator(this->list);
327 enum_a->enumerate(enum_a, &attr_a);
328 enum_b = other->list->create_enumerator(other->list);
329 enum_b->enumerate(enum_b, &attr_b);
330
331 /* look for at least one common attribute */
332 while (TRUE)
333 {
334 int cmp = attr_a->compare(attr_a, attr_b);
335
336 if (cmp == 0)
337 {
338 /* we have a match */
339 result = TRUE;
340 break;
341 }
342 if (cmp == -1)
343 {
344 /* attr_a is earlier in the alphabet, get next attr_a */
345 if (!enum_a->enumerate(enum_a, &attr_a))
346 {
347 /* we have reached the end of enum_a */
348 break;
349 }
350 }
351 else
352 {
353 /* attr_a is later in the alphabet, get next attr_b */
354 if (!enum_b->enumerate(enum_b, &attr_b))
355 {
356 /* we have reached the end of enum_b */
357 break;
358 }
359 }
360 }
361 enum_a->destroy(enum_a);
362 enum_b->destroy(enum_b);
363
364 return result;
365 }
366
367 /**
368 * Implementation of ietf_attributes_t.get_ref
369 */
370 static private_ietf_attributes_t* get_ref(private_ietf_attributes_t *this)
371 {
372 ref_get(&this->ref);
373 return this;
374 }
375
376 /**
377 * Implementation of ietf_attributes_t.destroy.
378 */
379 static void destroy(private_ietf_attributes_t *this)
380 {
381 if (ref_put(&this->ref))
382 {
383 this->list->destroy_offset(this->list, offsetof(ietf_attr_t, destroy));
384 free(this->string);
385 free(this);
386 }
387 }
388
389 static private_ietf_attributes_t* create_empty(void)
390 {
391 private_ietf_attributes_t *this = malloc_thing(private_ietf_attributes_t);
392
393 this->public.get_string = (char* (*)(ietf_attributes_t*))get_string;
394 this->public.get_encoding = (chunk_t (*)(ietf_attributes_t*))get_encoding;
395 this->public.equals = (bool (*)(ietf_attributes_t*,ietf_attributes_t*))equals;
396 this->public.matches = (bool (*)(ietf_attributes_t*,ietf_attributes_t*))matches;
397 this->public.get_ref = (ietf_attributes_t* (*)(ietf_attributes_t*))get_ref;
398 this->public.destroy = (void (*)(ietf_attributes_t*))destroy;
399
400 this->list = linked_list_create();
401 this->string = NULL;
402 this->ref = 1;
403 return this;
404 }
405
406 /**
407 * Adds an ietf_attr_t object to a sorted linked list
408 */
409 static void ietf_attributes_add(private_ietf_attributes_t *this,
410 ietf_attr_t *attr)
411 {
412 ietf_attr_t *current_attr;
413 enumerator_t *enumerator;
414 int cmp = -1;
415
416 enumerator = this->list->create_enumerator(this->list);
417 while (enumerator->enumerate(enumerator, (void **)&current_attr) &&
418 (cmp = attr->compare(attr, current_attr)) > 0)
419 {
420 continue;
421 }
422 if (cmp == 0)
423 {
424 attr->destroy(attr);
425 }
426 else
427 { /* the enumerator either points to the end or to the attribute > attr */
428 this->list->insert_before(this->list, enumerator, attr);
429 }
430 enumerator->destroy(enumerator);
431 }
432
433 /*
434 * Described in header.
435 */
436 ietf_attributes_t *ietf_attributes_create_from_string(char *string)
437 {
438 private_ietf_attributes_t *this = create_empty();
439
440 chunk_t line = { string, strlen(string) };
441
442 while (eat_whitespace(&line))
443 {
444 chunk_t group;
445
446 /* extract the next comma-separated group attribute */
447 if (!extract_token(&group, ',', &line))
448 {
449 group = line;
450 line.len = 0;
451 }
452
453 /* remove any trailing spaces */
454 while (group.len > 0 && *(group.ptr + group.len - 1) == ' ')
455 {
456 group.len--;
457 }
458
459 /* add the group attribute to the list */
460 if (group.len > 0)
461 {
462 ietf_attr_t *attr = ietf_attr_create(IETF_ATTRIBUTE_STRING, group);
463
464 ietf_attributes_add(this, attr);
465 }
466 }
467
468 return &(this->public);
469 }
470
471 /**
472 * ASN.1 definition of ietfAttrSyntax
473 */
474 static const asn1Object_t ietfAttrSyntaxObjects[] =
475 {
476 { 0, "ietfAttrSyntax", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
477 { 1, "policyAuthority", ASN1_CONTEXT_C_0, ASN1_OPT |
478 ASN1_BODY }, /* 1 */
479 { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */
480 { 1, "values", ASN1_SEQUENCE, ASN1_LOOP }, /* 3 */
481 { 2, "octets", ASN1_OCTET_STRING, ASN1_OPT |
482 ASN1_BODY }, /* 4 */
483 { 2, "end choice", ASN1_EOC, ASN1_END }, /* 5 */
484 { 2, "oid", ASN1_OID, ASN1_OPT |
485 ASN1_BODY }, /* 6 */
486 { 2, "end choice", ASN1_EOC, ASN1_END }, /* 7 */
487 { 2, "string", ASN1_UTF8STRING, ASN1_OPT |
488 ASN1_BODY }, /* 8 */
489 { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */
490 { 1, "end loop", ASN1_EOC, ASN1_END }, /* 10 */
491 { 0, "exit", ASN1_EOC, ASN1_EXIT }
492 };
493 #define IETF_ATTR_OCTETS 4
494 #define IETF_ATTR_OID 6
495 #define IETF_ATTR_STRING 8
496
497 /*
498 * Described in header.
499 */
500 ietf_attributes_t *ietf_attributes_create_from_encoding(chunk_t encoded)
501 {
502 private_ietf_attributes_t *this = create_empty();
503 asn1_parser_t *parser;
504 chunk_t object;
505 int objectID;
506
507 parser = asn1_parser_create(ietfAttrSyntaxObjects, encoded);
508 while (parser->iterate(parser, &objectID, &object))
509 {
510 switch (objectID)
511 {
512 case IETF_ATTR_OCTETS:
513 case IETF_ATTR_OID:
514 case IETF_ATTR_STRING:
515 {
516 ietf_attribute_type_t type;
517 ietf_attr_t *attr;
518
519 type = (objectID - IETF_ATTR_OCTETS) / 2;
520 attr = ietf_attr_create(type, object);
521 ietf_attributes_add(this, attr);
522 }
523 break;
524 default:
525 break;
526 }
527 }
528 parser->destroy(parser);
529
530 return &(this->public);
531 }
532