/src/strongswan/src/libstrongswan/plugins/pkcs7/pkcs7_attributes.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2012 Tobias Brunner |
3 | | * Copyright (C) 2008 Andreas Steffen |
4 | | * |
5 | | * Copyright (C) secunet Security Networks AG |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or modify it |
8 | | * under the terms of the GNU General Public License as published by the |
9 | | * Free Software Foundation; either version 2 of the License, or (at your |
10 | | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, but |
13 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
14 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
15 | | * for more details. |
16 | | */ |
17 | | |
18 | | #include <library.h> |
19 | | #include <utils/debug.h> |
20 | | |
21 | | #include <asn1/oid.h> |
22 | | #include <asn1/asn1.h> |
23 | | #include <asn1/asn1_parser.h> |
24 | | #include <collections/array.h> |
25 | | #include <collections/linked_list.h> |
26 | | |
27 | | #include "pkcs7_attributes.h" |
28 | | |
29 | | typedef struct private_pkcs7_attributes_t private_pkcs7_attributes_t; |
30 | | typedef struct attribute_t attribute_t; |
31 | | |
32 | | /** |
33 | | * Private data of a pkcs7_attributes_t attribute list. |
34 | | */ |
35 | | struct private_pkcs7_attributes_t { |
36 | | /** |
37 | | * Public interface |
38 | | */ |
39 | | pkcs7_attributes_t public; |
40 | | |
41 | | /** |
42 | | * DER encoding of PKCS#9 attributes |
43 | | */ |
44 | | chunk_t encoding; |
45 | | |
46 | | /** |
47 | | * Linked list of PKCS#9 attributes |
48 | | */ |
49 | | linked_list_t *attributes; |
50 | | }; |
51 | | |
52 | | /** |
53 | | * Definition of an attribute_t object. |
54 | | */ |
55 | | struct attribute_t { |
56 | | |
57 | | /** |
58 | | * Object Identifier (OID) |
59 | | */ |
60 | | int oid; |
61 | | |
62 | | /** |
63 | | * Attribute value |
64 | | */ |
65 | | chunk_t value; |
66 | | |
67 | | /** |
68 | | * ASN.1 encoding |
69 | | */ |
70 | | chunk_t encoding; |
71 | | }; |
72 | | |
73 | | /** |
74 | | * Destroy an attribute_t object. |
75 | | */ |
76 | | static void attribute_destroy(attribute_t *this) |
77 | 0 | { |
78 | 0 | free(this->value.ptr); |
79 | 0 | free(this); |
80 | 0 | } |
81 | | |
82 | | /** |
83 | | * Create an attribute_t object. |
84 | | */ |
85 | | static attribute_t *attribute_create(int oid, chunk_t value) |
86 | 0 | { |
87 | 0 | attribute_t *this; |
88 | |
|
89 | 0 | INIT(this, |
90 | 0 | .oid = oid, |
91 | 0 | .value = chunk_clone(value), |
92 | 0 | ); |
93 | |
|
94 | 0 | return this; |
95 | 0 | } |
96 | | |
97 | | /** |
98 | | * Compare two encoded attributes |
99 | | */ |
100 | | static int cmp_attributes(const chunk_t *a, const chunk_t *b, void *unused) |
101 | 0 | { |
102 | 0 | return chunk_compare(*a, *b); |
103 | 0 | } |
104 | | |
105 | | /** |
106 | | * Build encoding of the attribute list |
107 | | */ |
108 | | static void build_encoding(private_pkcs7_attributes_t *this) |
109 | 0 | { |
110 | 0 | enumerator_t *enumerator; |
111 | 0 | attribute_t *attribute; |
112 | 0 | u_int len = 0, count, i = 0; |
113 | 0 | array_t *chunks; |
114 | 0 | chunk_t chunk; |
115 | 0 | u_char *pos; |
116 | |
|
117 | 0 | count = this->attributes->get_count(this->attributes); |
118 | 0 | chunks = array_create(sizeof(chunk_t), count); |
119 | |
|
120 | 0 | enumerator = this->attributes->create_enumerator(this->attributes); |
121 | 0 | while (enumerator->enumerate(enumerator, &attribute)) |
122 | 0 | { |
123 | 0 | chunk = asn1_wrap(ASN1_SEQUENCE, "mm", |
124 | 0 | asn1_build_known_oid(attribute->oid), |
125 | 0 | asn1_wrap(ASN1_SET, "c", attribute->value)); |
126 | 0 | array_insert(chunks, ARRAY_TAIL, &chunk); |
127 | 0 | len += chunk.len; |
128 | 0 | } |
129 | 0 | enumerator->destroy(enumerator); |
130 | |
|
131 | 0 | array_sort(chunks, (void*)cmp_attributes, NULL); |
132 | |
|
133 | 0 | pos = asn1_build_object(&this->encoding, ASN1_SET, len); |
134 | 0 | for (i = 0; i < count; i++) |
135 | 0 | { |
136 | 0 | array_get(chunks, i, &chunk); |
137 | 0 | memcpy(pos, chunk.ptr, chunk.len); |
138 | 0 | pos += chunk.len; |
139 | 0 | free(chunk.ptr); |
140 | 0 | } |
141 | 0 | array_destroy(chunks); |
142 | 0 | } |
143 | | |
144 | | METHOD(pkcs7_attributes_t, get_encoding, chunk_t, |
145 | | private_pkcs7_attributes_t *this) |
146 | 0 | { |
147 | 0 | if (!this->encoding.len) |
148 | 0 | { |
149 | 0 | build_encoding(this); |
150 | 0 | } |
151 | 0 | return this->encoding; |
152 | 0 | } |
153 | | |
154 | | METHOD(pkcs7_attributes_t, get_attribute, chunk_t, |
155 | | private_pkcs7_attributes_t *this, int oid) |
156 | 0 | { |
157 | 0 | enumerator_t *enumerator; |
158 | 0 | chunk_t value = chunk_empty; |
159 | 0 | attribute_t *attribute; |
160 | |
|
161 | 0 | enumerator = this->attributes->create_enumerator(this->attributes); |
162 | 0 | while (enumerator->enumerate(enumerator, &attribute)) |
163 | 0 | { |
164 | 0 | if (attribute->oid == oid) |
165 | 0 | { |
166 | 0 | value = attribute->value; |
167 | 0 | break; |
168 | 0 | } |
169 | 0 | } |
170 | 0 | enumerator->destroy(enumerator); |
171 | 0 | if (value.len && asn1_unwrap(&value, &value) != ASN1_INVALID) |
172 | 0 | { |
173 | 0 | return value; |
174 | 0 | } |
175 | 0 | return chunk_empty; |
176 | 0 | } |
177 | | |
178 | | METHOD(pkcs7_attributes_t, add_attribute, void, |
179 | | private_pkcs7_attributes_t *this, int oid, chunk_t value) |
180 | 0 | { |
181 | 0 | this->attributes->insert_last(this->attributes, |
182 | 0 | attribute_create(oid, value)); |
183 | 0 | chunk_free(&value); |
184 | | |
185 | | /* rebuild encoding when adding attributes */ |
186 | 0 | chunk_free(&this->encoding); |
187 | 0 | } |
188 | | |
189 | | METHOD(pkcs7_attributes_t, destroy, void, |
190 | | private_pkcs7_attributes_t *this) |
191 | 0 | { |
192 | 0 | this->attributes->destroy_function(this->attributes, |
193 | 0 | (void*)attribute_destroy); |
194 | 0 | free(this->encoding.ptr); |
195 | 0 | free(this); |
196 | 0 | } |
197 | | |
198 | | /* |
199 | | * Described in header. |
200 | | */ |
201 | | pkcs7_attributes_t *pkcs7_attributes_create(void) |
202 | 0 | { |
203 | 0 | private_pkcs7_attributes_t *this; |
204 | |
|
205 | 0 | INIT(this, |
206 | 0 | .public = { |
207 | 0 | .get_encoding = _get_encoding, |
208 | 0 | .get_attribute = _get_attribute, |
209 | 0 | .add_attribute = _add_attribute, |
210 | 0 | .destroy = _destroy, |
211 | 0 | }, |
212 | 0 | .attributes = linked_list_create(), |
213 | 0 | ); |
214 | |
|
215 | 0 | return &this->public; |
216 | 0 | } |
217 | | |
218 | | /** |
219 | | * ASN.1 definition of the X.501 attribute type |
220 | | */ |
221 | | static const asn1Object_t attributesObjects[] = { |
222 | | { 0, "attributes", ASN1_SET, ASN1_LOOP }, /* 0 */ |
223 | | { 1, "attribute", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ |
224 | | { 2, "type", ASN1_OID, ASN1_BODY }, /* 2 */ |
225 | | { 2, "values", ASN1_SET, ASN1_LOOP }, /* 3 */ |
226 | | { 3, "value", ASN1_EOC, ASN1_RAW }, /* 4 */ |
227 | | { 2, "end loop", ASN1_EOC, ASN1_END }, /* 5 */ |
228 | | { 0, "end loop", ASN1_EOC, ASN1_END }, /* 6 */ |
229 | | { 0, "exit", ASN1_EOC, ASN1_EXIT } |
230 | | }; |
231 | 0 | #define ATTRIBUTE_OBJ_TYPE 2 |
232 | 0 | #define ATTRIBUTE_OBJ_VALUE 4 |
233 | | |
234 | | /** |
235 | | * Parse a PKCS#9 attribute list |
236 | | */ |
237 | | static bool parse_attributes(chunk_t chunk, int level0, |
238 | | private_pkcs7_attributes_t* this) |
239 | 0 | { |
240 | 0 | asn1_parser_t *parser; |
241 | 0 | chunk_t object; |
242 | 0 | int objectID; |
243 | 0 | int oid = OID_UNKNOWN; |
244 | 0 | bool success = FALSE; |
245 | |
|
246 | 0 | parser = asn1_parser_create(attributesObjects, chunk); |
247 | 0 | parser->set_top_level(parser, level0); |
248 | |
|
249 | 0 | while (parser->iterate(parser, &objectID, &object)) |
250 | 0 | { |
251 | 0 | switch (objectID) |
252 | 0 | { |
253 | 0 | case ATTRIBUTE_OBJ_TYPE: |
254 | 0 | oid = asn1_known_oid(object); |
255 | 0 | break; |
256 | 0 | case ATTRIBUTE_OBJ_VALUE: |
257 | 0 | if (oid != OID_UNKNOWN) |
258 | 0 | { |
259 | 0 | this->attributes->insert_last(this->attributes, |
260 | 0 | attribute_create(oid, object)); |
261 | 0 | } |
262 | 0 | break; |
263 | 0 | } |
264 | 0 | } |
265 | 0 | success = parser->success(parser); |
266 | |
|
267 | 0 | parser->destroy(parser); |
268 | 0 | return success; |
269 | 0 | } |
270 | | |
271 | | /* |
272 | | * Described in header. |
273 | | */ |
274 | | pkcs7_attributes_t *pkcs7_attributes_create_from_chunk(chunk_t chunk, |
275 | | u_int level) |
276 | 0 | { |
277 | 0 | private_pkcs7_attributes_t *this; |
278 | |
|
279 | 0 | this = (private_pkcs7_attributes_t*)pkcs7_attributes_create(); |
280 | 0 | this->encoding = chunk_clone(chunk); |
281 | 0 | if (!parse_attributes(chunk, level, this)) |
282 | 0 | { |
283 | 0 | destroy(this); |
284 | 0 | return NULL; |
285 | 0 | } |
286 | 0 | return &this->public; |
287 | 0 | } |