Migrated ietf_attributes to INIT/METHOD macros
[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;
106
107 INIT(this,
108 .compare = ietf_attr_compare,
109 .destroy = ietf_attr_destroy,
110 .type = type,
111 .value = chunk_clone(value),
112 );
113
114 return this;
115 }
116
117 typedef struct private_ietf_attributes_t private_ietf_attributes_t;
118
119 /**
120 * Private data of an ietf_attributes_t object.
121 */
122 struct private_ietf_attributes_t {
123 /**
124 * Public interface.
125 */
126 ietf_attributes_t public;
127
128 /**
129 * Printable representation of the IETF attributes
130 */
131 char *string;
132
133 /**
134 * Linked list of IETF attributes.
135 */
136 linked_list_t *list;
137
138 /**
139 * reference count
140 */
141 refcount_t ref;
142 };
143
144 METHOD(ietf_attributes_t, get_string, char*,
145 private_ietf_attributes_t *this)
146 {
147 if (this->string == NULL)
148 {
149 char buf[BUF_LEN];
150 char *pos = buf;
151 int len = BUF_LEN;
152 bool first = TRUE;
153 ietf_attr_t *attr;
154 enumerator_t *enumerator;
155
156 enumerator = this->list->create_enumerator(this->list);
157 while (enumerator->enumerate(enumerator, &attr))
158 {
159 int written;
160
161 if (first)
162 {
163 first = FALSE;
164 }
165 else
166 {
167 written = snprintf(pos, len, ", ");
168 if (written < 0 || written >= len)
169 {
170 break;
171 }
172 pos += written;
173 len -= written;
174 }
175
176 switch (attr->type)
177 {
178 case IETF_ATTRIBUTE_OCTETS:
179 case IETF_ATTRIBUTE_STRING:
180 written = snprintf(pos, len, "%.*s", (int)attr->value.len,
181 attr->value.ptr);
182 break;
183 case IETF_ATTRIBUTE_OID:
184 {
185 int oid = asn1_known_oid(attr->value);
186
187 if (oid == OID_UNKNOWN)
188 {
189 written = snprintf(pos, len, "0x%#B", &attr->value);
190 }
191 else
192 {
193 written = snprintf(pos, len, "%s", oid_names[oid].name);
194 }
195 break;
196 }
197 default:
198 written = 0;
199 break;
200 }
201 if (written < 0 || written >= len)
202 {
203 break;
204 }
205 pos += written;
206 len -= written;
207 }
208 enumerator->destroy(enumerator);
209 if (len < BUF_LEN)
210 {
211 this->string = strdup(buf);
212 }
213 }
214 return this->string;
215 }
216
217 METHOD(ietf_attributes_t, get_encoding, chunk_t,
218 private_ietf_attributes_t *this)
219 {
220 chunk_t values;
221 size_t size = 0;
222 u_char *pos;
223 ietf_attr_t *attr;
224 enumerator_t *enumerator;
225
226 /* precalculate the total size of all values */
227 enumerator = this->list->create_enumerator(this->list);
228 while (enumerator->enumerate(enumerator, &attr))
229 {
230 size_t len = attr->value.len;
231
232 size += 1 + (len > 0) + (len >= 128) + (len >= 256) + (len >= 65536) + len;
233 }
234 enumerator->destroy(enumerator);
235
236 pos = asn1_build_object(&values, ASN1_SEQUENCE, size);
237
238 enumerator = this->list->create_enumerator(this->list);
239 while (enumerator->enumerate(enumerator, &attr))
240 {
241 chunk_t ietfAttribute;
242 asn1_t type = ASN1_NULL;
243
244 switch (attr->type)
245 {
246 case IETF_ATTRIBUTE_OCTETS:
247 type = ASN1_OCTET_STRING;
248 break;
249 case IETF_ATTRIBUTE_STRING:
250 type = ASN1_UTF8STRING;
251 break;
252 case IETF_ATTRIBUTE_OID:
253 type = ASN1_OID;
254 break;
255 }
256 ietfAttribute = asn1_simple_object(type, attr->value);
257
258 /* copy ietfAttribute into values chunk */
259 memcpy(pos, ietfAttribute.ptr, ietfAttribute.len);
260 pos += ietfAttribute.len;
261 free(ietfAttribute.ptr);
262 }
263 enumerator->destroy(enumerator);
264
265 return asn1_wrap(ASN1_SEQUENCE, "m", values);
266 }
267
268 /**
269 * Implementation of ietf_attributes_t.equals.
270 */
271 static bool equals(private_ietf_attributes_t *this,
272 private_ietf_attributes_t *other)
273 {
274 bool result = TRUE;
275
276 /* lists must have the same number of attributes */
277 if (other == NULL ||
278 this->list->get_count(this->list) != other->list->get_count(other->list))
279 {
280 return FALSE;
281 }
282
283 /* compare two alphabetically-sorted lists */
284 {
285 ietf_attr_t *attr_a, *attr_b;
286 enumerator_t *enum_a, *enum_b;
287
288 enum_a = this->list->create_enumerator(this->list);
289 enum_b = other->list->create_enumerator(other->list);
290 while (enum_a->enumerate(enum_a, &attr_a) &&
291 enum_b->enumerate(enum_b, &attr_b))
292 {
293 if (attr_a->compare(attr_a, attr_b) != 0)
294 {
295 /* we have a mismatch */
296 result = FALSE;
297 break;
298 }
299 }
300 enum_a->destroy(enum_a);
301 enum_b->destroy(enum_b);
302 }
303 return result;
304 }
305
306 /**
307 * Implementation of ietf_attributes_t.matches.
308 */
309 static bool matches(private_ietf_attributes_t *this,
310 private_ietf_attributes_t *other)
311 {
312 bool result = FALSE;
313 ietf_attr_t *attr_a, *attr_b;
314 enumerator_t *enum_a, *enum_b;
315
316 /* always match if this->list does not contain any attributes */
317 if (this->list->get_count(this->list) == 0)
318 {
319 return TRUE;
320 }
321
322 /* never match if other->list does not contain any attributes */
323 if (other == NULL || other->list->get_count(other->list) == 0)
324 {
325 return FALSE;
326 }
327
328 /* get first attribute from both lists */
329 enum_a = this->list->create_enumerator(this->list);
330 enum_a->enumerate(enum_a, &attr_a);
331 enum_b = other->list->create_enumerator(other->list);
332 enum_b->enumerate(enum_b, &attr_b);
333
334 /* look for at least one common attribute */
335 while (TRUE)
336 {
337 int cmp = attr_a->compare(attr_a, attr_b);
338
339 if (cmp == 0)
340 {
341 /* we have a match */
342 result = TRUE;
343 break;
344 }
345 if (cmp == -1)
346 {
347 /* attr_a is earlier in the alphabet, get next attr_a */
348 if (!enum_a->enumerate(enum_a, &attr_a))
349 {
350 /* we have reached the end of enum_a */
351 break;
352 }
353 }
354 else
355 {
356 /* attr_a is later in the alphabet, get next attr_b */
357 if (!enum_b->enumerate(enum_b, &attr_b))
358 {
359 /* we have reached the end of enum_b */
360 break;
361 }
362 }
363 }
364 enum_a->destroy(enum_a);
365 enum_b->destroy(enum_b);
366
367 return result;
368 }
369
370 METHOD(ietf_attributes_t, get_ref, ietf_attributes_t*,
371 private_ietf_attributes_t *this)
372 {
373 ref_get(&this->ref);
374 return &this->public;
375 }
376
377 METHOD(ietf_attributes_t, destroy, void,
378 private_ietf_attributes_t *this)
379 {
380 if (ref_put(&this->ref))
381 {
382 this->list->destroy_offset(this->list, offsetof(ietf_attr_t, destroy));
383 free(this->string);
384 free(this);
385 }
386 }
387
388 static private_ietf_attributes_t* create_empty(void)
389 {
390 private_ietf_attributes_t *this;
391
392 INIT(this,
393 .public = {
394 .get_string = _get_string,
395 .get_encoding = _get_encoding,
396 .equals = (bool (*)(ietf_attributes_t*,ietf_attributes_t*))equals,
397 .matches = (bool (*)(ietf_attributes_t*,ietf_attributes_t*))matches,
398 .get_ref = _get_ref,
399 .destroy = _destroy,
400 },
401 .list = linked_list_create(),
402 .ref = 1,
403 );
404
405 return this;
406 }
407
408 /**
409 * Adds an ietf_attr_t object to a sorted linked list
410 */
411 static void ietf_attributes_add(private_ietf_attributes_t *this,
412 ietf_attr_t *attr)
413 {
414 ietf_attr_t *current_attr;
415 enumerator_t *enumerator;
416 int cmp = -1;
417
418 enumerator = this->list->create_enumerator(this->list);
419 while (enumerator->enumerate(enumerator, (void **)&current_attr) &&
420 (cmp = attr->compare(attr, current_attr)) > 0)
421 {
422 continue;
423 }
424 if (cmp == 0)
425 {
426 attr->destroy(attr);
427 }
428 else
429 { /* the enumerator either points to the end or to the attribute > attr */
430 this->list->insert_before(this->list, enumerator, attr);
431 }
432 enumerator->destroy(enumerator);
433 }
434
435 /*
436 * Described in header.
437 */
438 ietf_attributes_t *ietf_attributes_create_from_string(char *string)
439 {
440 private_ietf_attributes_t *this = create_empty();
441
442 chunk_t line = { string, strlen(string) };
443
444 while (eat_whitespace(&line))
445 {
446 chunk_t group;
447
448 /* extract the next comma-separated group attribute */
449 if (!extract_token(&group, ',', &line))
450 {
451 group = line;
452 line.len = 0;
453 }
454
455 /* remove any trailing spaces */
456 while (group.len > 0 && *(group.ptr + group.len - 1) == ' ')
457 {
458 group.len--;
459 }
460
461 /* add the group attribute to the list */
462 if (group.len > 0)
463 {
464 ietf_attr_t *attr = ietf_attr_create(IETF_ATTRIBUTE_STRING, group);
465
466 ietf_attributes_add(this, attr);
467 }
468 }
469
470 return &(this->public);
471 }
472
473 /**
474 * ASN.1 definition of ietfAttrSyntax
475 */
476 static const asn1Object_t ietfAttrSyntaxObjects[] =
477 {
478 { 0, "ietfAttrSyntax", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
479 { 1, "policyAuthority", ASN1_CONTEXT_C_0, ASN1_OPT |
480 ASN1_BODY }, /* 1 */
481 { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */
482 { 1, "values", ASN1_SEQUENCE, ASN1_LOOP }, /* 3 */
483 { 2, "octets", ASN1_OCTET_STRING, ASN1_OPT |
484 ASN1_BODY }, /* 4 */
485 { 2, "end choice", ASN1_EOC, ASN1_END }, /* 5 */
486 { 2, "oid", ASN1_OID, ASN1_OPT |
487 ASN1_BODY }, /* 6 */
488 { 2, "end choice", ASN1_EOC, ASN1_END }, /* 7 */
489 { 2, "string", ASN1_UTF8STRING, ASN1_OPT |
490 ASN1_BODY }, /* 8 */
491 { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */
492 { 1, "end loop", ASN1_EOC, ASN1_END }, /* 10 */
493 { 0, "exit", ASN1_EOC, ASN1_EXIT }
494 };
495 #define IETF_ATTR_OCTETS 4
496 #define IETF_ATTR_OID 6
497 #define IETF_ATTR_STRING 8
498
499 /*
500 * Described in header.
501 */
502 ietf_attributes_t *ietf_attributes_create_from_encoding(chunk_t encoded)
503 {
504 private_ietf_attributes_t *this = create_empty();
505 asn1_parser_t *parser;
506 chunk_t object;
507 int objectID;
508
509 parser = asn1_parser_create(ietfAttrSyntaxObjects, encoded);
510 while (parser->iterate(parser, &objectID, &object))
511 {
512 switch (objectID)
513 {
514 case IETF_ATTR_OCTETS:
515 case IETF_ATTR_OID:
516 case IETF_ATTR_STRING:
517 {
518 ietf_attribute_type_t type;
519 ietf_attr_t *attr;
520
521 type = (objectID - IETF_ATTR_OCTETS) / 2;
522 attr = ietf_attr_create(type, object);
523 ietf_attributes_add(this, attr);
524 }
525 break;
526 default:
527 break;
528 }
529 }
530 parser->destroy(parser);
531
532 return &(this->public);
533 }
534