/src/strongswan/src/libcharon/encoding/payloads/cert_payload.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2008 Tobias Brunner |
3 | | * Copyright (C) 2005-2010 Martin Willi |
4 | | * Copyright (C) 2005 Jan Hutter |
5 | | * |
6 | | * Copyright (C) secunet Security Networks AG |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify it |
9 | | * under the terms of the GNU General Public License as published by the |
10 | | * Free Software Foundation; either version 2 of the License, or (at your |
11 | | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, but |
14 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
15 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
16 | | * for more details. |
17 | | */ |
18 | | |
19 | | #include <stddef.h> |
20 | | #include <ctype.h> |
21 | | |
22 | | #include <daemon.h> |
23 | | |
24 | | #include "cert_payload.h" |
25 | | |
26 | | ENUM(cert_encoding_names, ENC_PKCS7_WRAPPED_X509, ENC_OCSP_CONTENT, |
27 | | "ENC_PKCS7_WRAPPED_X509", |
28 | | "ENC_PGP", |
29 | | "ENC_DNS_SIGNED_KEY", |
30 | | "ENC_X509_SIGNATURE", |
31 | | "ENC_X509_KEY_EXCHANGE", |
32 | | "ENC_KERBEROS_TOKENS", |
33 | | "ENC_CRL", |
34 | | "ENC_ARL", |
35 | | "ENC_SPKI", |
36 | | "ENC_X509_ATTRIBUTE", |
37 | | "ENC_RAW_RSA_KEY", |
38 | | "ENC_X509_HASH_AND_URL", |
39 | | "ENC_X509_HASH_AND_URL_BUNDLE", |
40 | | "ENC_OCSP_CONTENT", |
41 | | ); |
42 | | |
43 | | typedef struct private_cert_payload_t private_cert_payload_t; |
44 | | |
45 | | /** |
46 | | * Private data of an cert_payload_t object. |
47 | | */ |
48 | | struct private_cert_payload_t { |
49 | | |
50 | | /** |
51 | | * Public cert_payload_t interface. |
52 | | */ |
53 | | cert_payload_t public; |
54 | | |
55 | | /** |
56 | | * Next payload type. |
57 | | */ |
58 | | uint8_t next_payload; |
59 | | |
60 | | /** |
61 | | * Critical flag. |
62 | | */ |
63 | | bool critical; |
64 | | |
65 | | /** |
66 | | * reserved bits |
67 | | */ |
68 | | bool reserved[7]; |
69 | | |
70 | | /** |
71 | | * Length of this payload. |
72 | | */ |
73 | | uint16_t payload_length; |
74 | | |
75 | | /** |
76 | | * Encoding of the CERT Data. |
77 | | */ |
78 | | uint8_t encoding; |
79 | | |
80 | | /** |
81 | | * The contained cert data value. |
82 | | */ |
83 | | chunk_t data; |
84 | | |
85 | | /** |
86 | | * TRUE if the "Hash and URL" data is invalid |
87 | | */ |
88 | | bool invalid_hash_and_url; |
89 | | |
90 | | /** |
91 | | * The payload type. |
92 | | */ |
93 | | payload_type_t type; |
94 | | }; |
95 | | |
96 | | /** |
97 | | * Encoding rules to parse or generate a CERT payload |
98 | | * |
99 | | * The defined offsets are the positions in a object of type |
100 | | * private_cert_payload_t. |
101 | | * |
102 | | */ |
103 | | static encoding_rule_t encodings[] = { |
104 | | /* 1 Byte next payload type, stored in the field next_payload */ |
105 | | { U_INT_8, offsetof(private_cert_payload_t, next_payload) }, |
106 | | /* the critical bit */ |
107 | | { FLAG, offsetof(private_cert_payload_t, critical) }, |
108 | | /* 7 Bit reserved bits, nowhere stored */ |
109 | | { RESERVED_BIT, offsetof(private_cert_payload_t, reserved[0]) }, |
110 | | { RESERVED_BIT, offsetof(private_cert_payload_t, reserved[1]) }, |
111 | | { RESERVED_BIT, offsetof(private_cert_payload_t, reserved[2]) }, |
112 | | { RESERVED_BIT, offsetof(private_cert_payload_t, reserved[3]) }, |
113 | | { RESERVED_BIT, offsetof(private_cert_payload_t, reserved[4]) }, |
114 | | { RESERVED_BIT, offsetof(private_cert_payload_t, reserved[5]) }, |
115 | | { RESERVED_BIT, offsetof(private_cert_payload_t, reserved[6]) }, |
116 | | /* Length of the whole payload*/ |
117 | | { PAYLOAD_LENGTH, offsetof(private_cert_payload_t, payload_length)}, |
118 | | /* 1 Byte CERT type*/ |
119 | | { U_INT_8, offsetof(private_cert_payload_t, encoding) }, |
120 | | /* some cert data bytes, length is defined in PAYLOAD_LENGTH */ |
121 | | { CHUNK_DATA, offsetof(private_cert_payload_t, data) } |
122 | | }; |
123 | | |
124 | | /* |
125 | | 1 2 3 |
126 | | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
127 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
128 | | ! Next Payload !C! RESERVED ! Payload Length ! |
129 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
130 | | ! Cert Encoding ! ! |
131 | | +-+-+-+-+-+-+-+-+ ! |
132 | | ~ Certificate Data ~ |
133 | | ! ! |
134 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
135 | | */ |
136 | | |
137 | | METHOD(payload_t, verify, status_t, |
138 | | private_cert_payload_t *this) |
139 | 1.92k | { |
140 | 1.92k | if (this->encoding == ENC_X509_HASH_AND_URL || |
141 | 934 | this->encoding == ENC_X509_HASH_AND_URL_BUNDLE) |
142 | 1.39k | { |
143 | 1.39k | int i; |
144 | | |
145 | | /* coarse verification of "Hash and URL" encoded certificates */ |
146 | 1.39k | if (this->data.len <= 20) |
147 | 721 | { |
148 | 721 | DBG1(DBG_ENC, "invalid payload length for hash-and-url (%d), ignore", |
149 | 721 | this->data.len); |
150 | 721 | this->invalid_hash_and_url = TRUE; |
151 | 721 | return SUCCESS; |
152 | 721 | } |
153 | 1.94k | for (i = 20; i < this->data.len; ++i) |
154 | 1.67k | { |
155 | 1.67k | if (this->data.ptr[i] == '\0') |
156 | 199 | { |
157 | | /* null terminated, fine */ |
158 | 199 | return SUCCESS; |
159 | 199 | } |
160 | 1.47k | else if (!isprint(this->data.ptr[i])) |
161 | 206 | { |
162 | 206 | DBG1(DBG_ENC, "non printable characters in url of hash-and-url" |
163 | 206 | " encoded certificate payload, ignore"); |
164 | 206 | this->invalid_hash_and_url = TRUE; |
165 | 206 | return SUCCESS; |
166 | 206 | } |
167 | 1.67k | } |
168 | | /* URL is not null terminated, correct that */ |
169 | 272 | this->data = chunk_cat("mc", this->data, chunk_from_chars(0)); |
170 | 272 | } |
171 | 800 | return SUCCESS; |
172 | 1.92k | } |
173 | | |
174 | | METHOD(payload_t, get_encoding_rules, int, |
175 | | private_cert_payload_t *this, encoding_rule_t **rules) |
176 | 2.01k | { |
177 | 2.01k | *rules = encodings; |
178 | 2.01k | return countof(encodings); |
179 | 2.01k | } |
180 | | |
181 | | METHOD(payload_t, get_header_length, int, |
182 | | private_cert_payload_t *this) |
183 | 25.4k | { |
184 | 25.4k | return 5; |
185 | 25.4k | } |
186 | | |
187 | | METHOD(payload_t, get_type, payload_type_t, |
188 | | private_cert_payload_t *this) |
189 | 2.17k | { |
190 | 2.17k | return this->type; |
191 | 2.17k | } |
192 | | |
193 | | METHOD(payload_t, get_next_type, payload_type_t, |
194 | | private_cert_payload_t *this) |
195 | 1.92k | { |
196 | 1.92k | return this->next_payload; |
197 | 1.92k | } |
198 | | |
199 | | METHOD(payload_t, set_next_type, void, |
200 | | private_cert_payload_t *this, payload_type_t type) |
201 | 0 | { |
202 | 0 | this->next_payload = type; |
203 | 0 | } |
204 | | |
205 | | METHOD(payload_t, get_length, size_t, |
206 | | private_cert_payload_t *this) |
207 | 0 | { |
208 | 0 | return this->payload_length; |
209 | 0 | } |
210 | | |
211 | | METHOD(cert_payload_t, get_cert_encoding, cert_encoding_t, |
212 | | private_cert_payload_t *this) |
213 | 0 | { |
214 | 0 | return this->encoding; |
215 | 0 | } |
216 | | |
217 | | METHOD(cert_payload_t, get_cert, certificate_t*, |
218 | | private_cert_payload_t *this) |
219 | 0 | { |
220 | 0 | int type; |
221 | |
|
222 | 0 | switch (this->encoding) |
223 | 0 | { |
224 | 0 | case ENC_X509_SIGNATURE: |
225 | 0 | type = CERT_X509; |
226 | 0 | break; |
227 | 0 | case ENC_X509_ATTRIBUTE: |
228 | 0 | type = CERT_X509_AC; |
229 | 0 | break; |
230 | 0 | case ENC_CRL: |
231 | 0 | type = CERT_X509_CRL; |
232 | 0 | break; |
233 | 0 | case ENC_OCSP_CONTENT: |
234 | 0 | type = CERT_X509_OCSP_RESPONSE; |
235 | 0 | break; |
236 | 0 | default: |
237 | 0 | return NULL; |
238 | 0 | } |
239 | 0 | return lib->creds->create(lib->creds, CRED_CERTIFICATE, type, |
240 | 0 | BUILD_BLOB_ASN1_DER, this->data, BUILD_END); |
241 | 0 | } |
242 | | |
243 | | METHOD(cert_payload_t, get_container, container_t*, |
244 | | private_cert_payload_t *this) |
245 | 0 | { |
246 | 0 | int type; |
247 | |
|
248 | 0 | switch (this->encoding) |
249 | 0 | { |
250 | 0 | case ENC_PKCS7_WRAPPED_X509: |
251 | 0 | type = CONTAINER_PKCS7; |
252 | 0 | break; |
253 | 0 | default: |
254 | 0 | return NULL; |
255 | 0 | } |
256 | 0 | return lib->creds->create(lib->creds, CRED_CONTAINER, type, |
257 | 0 | BUILD_BLOB_ASN1_DER, this->data, BUILD_END); |
258 | 0 | } |
259 | | |
260 | | METHOD(cert_payload_t, get_hash, chunk_t, |
261 | | private_cert_payload_t *this) |
262 | 0 | { |
263 | 0 | chunk_t hash = chunk_empty; |
264 | |
|
265 | 0 | if ((this->encoding != ENC_X509_HASH_AND_URL && |
266 | 0 | this->encoding != ENC_X509_HASH_AND_URL_BUNDLE) || |
267 | 0 | this->invalid_hash_and_url) |
268 | 0 | { |
269 | 0 | return hash; |
270 | 0 | } |
271 | 0 | hash.ptr = this->data.ptr; |
272 | 0 | hash.len = 20; |
273 | 0 | return hash; |
274 | 0 | } |
275 | | |
276 | | METHOD(cert_payload_t, get_url, char*, |
277 | | private_cert_payload_t *this) |
278 | 0 | { |
279 | 0 | if ((this->encoding != ENC_X509_HASH_AND_URL && |
280 | 0 | this->encoding != ENC_X509_HASH_AND_URL_BUNDLE) || |
281 | 0 | this->invalid_hash_and_url) |
282 | 0 | { |
283 | 0 | return NULL; |
284 | 0 | } |
285 | 0 | return (char*)this->data.ptr + 20; |
286 | 0 | } |
287 | | |
288 | | METHOD2(payload_t, cert_payload_t, destroy, void, |
289 | | private_cert_payload_t *this) |
290 | 2.01k | { |
291 | 2.01k | free(this->data.ptr); |
292 | 2.01k | free(this); |
293 | 2.01k | } |
294 | | |
295 | | /* |
296 | | * Described in header |
297 | | */ |
298 | | cert_payload_t *cert_payload_create(payload_type_t type) |
299 | 2.01k | { |
300 | 2.01k | private_cert_payload_t *this; |
301 | | |
302 | 2.01k | INIT(this, |
303 | 2.01k | .public = { |
304 | 2.01k | .payload_interface = { |
305 | 2.01k | .verify = _verify, |
306 | 2.01k | .get_encoding_rules = _get_encoding_rules, |
307 | 2.01k | .get_header_length = _get_header_length, |
308 | 2.01k | .get_length = _get_length, |
309 | 2.01k | .get_next_type = _get_next_type, |
310 | 2.01k | .set_next_type = _set_next_type, |
311 | 2.01k | .get_type = _get_type, |
312 | 2.01k | .destroy = _destroy, |
313 | 2.01k | }, |
314 | 2.01k | .get_cert = _get_cert, |
315 | 2.01k | .get_container = _get_container, |
316 | 2.01k | .get_cert_encoding = _get_cert_encoding, |
317 | 2.01k | .get_hash = _get_hash, |
318 | 2.01k | .get_url = _get_url, |
319 | 2.01k | .destroy = _destroy, |
320 | 2.01k | }, |
321 | 2.01k | .next_payload = PL_NONE, |
322 | 2.01k | .payload_length = get_header_length(this), |
323 | 2.01k | .type = type, |
324 | 2.01k | ); |
325 | 2.01k | return &this->public; |
326 | 2.01k | } |
327 | | |
328 | | /* |
329 | | * Described in header |
330 | | */ |
331 | | cert_payload_t *cert_payload_create_from_cert(payload_type_t type, |
332 | | certificate_t *cert) |
333 | 0 | { |
334 | 0 | private_cert_payload_t *this; |
335 | |
|
336 | 0 | this = (private_cert_payload_t*)cert_payload_create(type); |
337 | 0 | switch (cert->get_type(cert)) |
338 | 0 | { |
339 | 0 | case CERT_X509: |
340 | 0 | this->encoding = ENC_X509_SIGNATURE; |
341 | 0 | break; |
342 | 0 | case CERT_X509_AC: |
343 | 0 | this->encoding = ENC_X509_ATTRIBUTE; |
344 | 0 | break; |
345 | 0 | case CERT_X509_OCSP_RESPONSE: |
346 | 0 | this->encoding = ENC_OCSP_CONTENT; |
347 | 0 | break; |
348 | 0 | default: |
349 | 0 | DBG1(DBG_ENC, "embedding %N certificate in payload failed", |
350 | 0 | certificate_type_names, cert->get_type(cert)); |
351 | 0 | free(this); |
352 | 0 | return NULL; |
353 | 0 | } |
354 | 0 | if (!cert->get_encoding(cert, CERT_ASN1_DER, &this->data)) |
355 | 0 | { |
356 | 0 | DBG1(DBG_ENC, "encoding certificate for cert payload failed"); |
357 | 0 | free(this); |
358 | 0 | return NULL; |
359 | 0 | } |
360 | 0 | this->payload_length = get_header_length(this) + this->data.len; |
361 | |
|
362 | 0 | return &this->public; |
363 | 0 | } |
364 | | |
365 | | /* |
366 | | * Described in header |
367 | | */ |
368 | | cert_payload_t *cert_payload_create_from_hash_and_url(chunk_t hash, char *url) |
369 | 0 | { |
370 | 0 | private_cert_payload_t *this; |
371 | |
|
372 | 0 | this = (private_cert_payload_t*)cert_payload_create(PLV2_CERTIFICATE); |
373 | 0 | this->encoding = ENC_X509_HASH_AND_URL; |
374 | 0 | this->data = chunk_cat("cc", hash, chunk_create(url, strlen(url))); |
375 | 0 | this->payload_length = get_header_length(this) + this->data.len; |
376 | |
|
377 | 0 | return &this->public; |
378 | 0 | } |
379 | | |
380 | | /* |
381 | | * Described in header |
382 | | */ |
383 | | cert_payload_t *cert_payload_create_custom(payload_type_t type, |
384 | | cert_encoding_t encoding, chunk_t data) |
385 | 0 | { |
386 | 0 | private_cert_payload_t *this; |
387 | |
|
388 | 0 | this = (private_cert_payload_t*)cert_payload_create(type); |
389 | 0 | this->encoding = encoding; |
390 | 0 | this->data = data; |
391 | 0 | this->payload_length = get_header_length(this) + this->data.len; |
392 | |
|
393 | 0 | return &this->public; |
394 | 0 | } |