]>
Commit | Line | Data |
---|---|---|
4cb0e1bb MW |
1 | /* |
2 | * Copyright (C) 2009 Martin Willi | |
3 | * Hochschule fuer Technik Rapperswil | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the | |
7 | * Free Software Foundation; either version 2 of the License, or (at your | |
8 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
13 | * for more details. | |
14 | */ | |
15 | ||
16 | #include "pgp_cert.h" | |
17 | #include "pgp_utils.h" | |
18 | ||
19 | #include <time.h> | |
20 | ||
21 | #include <debug.h> | |
22 | ||
23 | typedef struct private_pgp_cert_t private_pgp_cert_t; | |
24 | ||
25 | /** | |
26 | * Private data of an pgp_cert_t object. | |
27 | */ | |
28 | struct private_pgp_cert_t { | |
29 | ||
30 | /** | |
31 | * Implements pgp_cert_t interface. | |
32 | */ | |
33 | pgp_cert_t public; | |
34 | ||
35 | /** | |
36 | * Public key of the certificate | |
37 | */ | |
38 | public_key_t *key; | |
39 | ||
40 | /** | |
41 | * version of the public key | |
42 | */ | |
43 | u_int32_t version; | |
44 | ||
45 | /** | |
46 | * creation time | |
47 | */ | |
48 | u_int32_t created; | |
49 | ||
50 | /** | |
51 | * days the certificate is valid | |
52 | */ | |
53 | u_int32_t valid; | |
54 | ||
55 | /** | |
56 | * userid of the certificate | |
57 | */ | |
58 | identification_t *user_id; | |
59 | ||
ab5762e3 AS |
60 | /** |
61 | * v3 or v4 fingerprint of the PGP public key | |
62 | */ | |
63 | chunk_t fingerprint; | |
64 | ||
4cb0e1bb MW |
65 | /** |
66 | * full PGP encoding | |
67 | */ | |
68 | chunk_t encoding; | |
69 | ||
70 | /** | |
71 | * reference counter | |
72 | */ | |
73 | refcount_t ref; | |
74 | }; | |
75 | ||
76 | ||
4a679468 AS |
77 | METHOD(certificate_t, get_type, certificate_type_t, |
78 | private_pgp_cert_t *this) | |
4cb0e1bb MW |
79 | { |
80 | return CERT_GPG; | |
81 | } | |
82 | ||
4a679468 AS |
83 | METHOD(certificate_t, get_subject,identification_t*, |
84 | private_pgp_cert_t *this) | |
4cb0e1bb MW |
85 | { |
86 | return this->user_id; | |
87 | } | |
88 | ||
4a679468 AS |
89 | METHOD(certificate_t, get_issuer, identification_t*, |
90 | private_pgp_cert_t *this) | |
4cb0e1bb MW |
91 | { |
92 | return this->user_id; | |
93 | } | |
94 | ||
4a679468 AS |
95 | METHOD(certificate_t, has_subject, id_match_t, |
96 | private_pgp_cert_t *this, identification_t *subject) | |
4cb0e1bb | 97 | { |
262af161 AS |
98 | id_match_t match_user_id; |
99 | ||
100 | match_user_id = this->user_id->matches(this->user_id, subject); | |
101 | if (match_user_id == ID_MATCH_NONE && | |
102 | subject->get_type(subject) == ID_KEY_ID && | |
103 | chunk_equals(this->fingerprint, subject->get_encoding(subject))) | |
104 | { | |
105 | return ID_MATCH_PERFECT; | |
106 | } | |
107 | return match_user_id; | |
4cb0e1bb MW |
108 | } |
109 | ||
4a679468 AS |
110 | METHOD(certificate_t, has_issuer, id_match_t, |
111 | private_pgp_cert_t *this, identification_t *issuer) | |
4cb0e1bb MW |
112 | { |
113 | return ID_MATCH_NONE; | |
114 | } | |
115 | ||
4a679468 | 116 | METHOD(certificate_t, issued_by,bool, |
a37f2d20 | 117 | private_pgp_cert_t *this, certificate_t *issuer, signature_scheme_t *scheme) |
4cb0e1bb MW |
118 | { |
119 | /* TODO: check signature blobs for a valid signature */ | |
120 | return FALSE; | |
121 | } | |
122 | ||
4a679468 AS |
123 | METHOD(certificate_t, get_public_key, public_key_t*, |
124 | private_pgp_cert_t *this) | |
4cb0e1bb MW |
125 | { |
126 | this->key->get_ref(this->key); | |
127 | return this->key; | |
128 | } | |
129 | ||
4a679468 AS |
130 | METHOD(certificate_t, get_ref, certificate_t*, |
131 | private_pgp_cert_t *this) | |
4cb0e1bb MW |
132 | { |
133 | ref_get(&this->ref); | |
4a679468 | 134 | return &this->public.interface.interface; |
4cb0e1bb MW |
135 | } |
136 | ||
4a679468 AS |
137 | METHOD(certificate_t, get_validity, bool, |
138 | private_pgp_cert_t *this, time_t *when, time_t *not_before, | |
139 | time_t *not_after) | |
4cb0e1bb MW |
140 | { |
141 | time_t t, until; | |
142 | ||
143 | if (when) | |
144 | { | |
145 | t = *when; | |
146 | } | |
147 | else | |
148 | { | |
149 | t = time(NULL); | |
150 | } | |
151 | if (not_before) | |
152 | { | |
153 | *not_before = this->created; | |
154 | } | |
155 | if (this->valid) | |
156 | { | |
157 | until = this->valid + this->created * 24 * 60 * 60; | |
158 | } | |
159 | else | |
160 | { | |
9f0327e6 AS |
161 | /* Jan 19 03:14:07 UTC 2038 */ |
162 | until = TIME_32_BIT_SIGNED_MAX; | |
4cb0e1bb MW |
163 | } |
164 | if (not_after) | |
165 | { | |
166 | *not_after = until; | |
167 | } | |
168 | return (t >= this->valid && t <= until); | |
169 | } | |
170 | ||
4a679468 AS |
171 | METHOD(certificate_t, get_encoding, bool, |
172 | private_pgp_cert_t *this, cred_encoding_type_t type, chunk_t *encoding) | |
4cb0e1bb | 173 | { |
0406eeaa MW |
174 | if (type == CERT_PGP_PKT) |
175 | { | |
176 | *encoding = chunk_clone(this->encoding); | |
177 | return TRUE; | |
178 | } | |
179 | return lib->encoding->encode(lib->encoding, type, NULL, encoding, | |
180 | CRED_PART_PGP_CERT, this->encoding, CRED_PART_END); | |
4cb0e1bb MW |
181 | } |
182 | ||
4a679468 AS |
183 | METHOD(certificate_t, equals, bool, |
184 | private_pgp_cert_t *this, certificate_t *other) | |
4cb0e1bb MW |
185 | { |
186 | chunk_t encoding; | |
187 | bool equal; | |
188 | ||
189 | if (this == (private_pgp_cert_t*)other) | |
190 | { | |
191 | return TRUE; | |
192 | } | |
193 | if (other->get_type(other) != CERT_X509) | |
194 | { | |
195 | return FALSE; | |
196 | } | |
197 | if (other->equals == (void*)equals) | |
198 | { /* skip allocation if we have the same implementation */ | |
199 | return chunk_equals(this->encoding, ((private_pgp_cert_t*)other)->encoding); | |
200 | } | |
0406eeaa MW |
201 | if (!other->get_encoding(other, CERT_PGP_PKT, &encoding)) |
202 | { | |
203 | return FALSE; | |
204 | } | |
4cb0e1bb MW |
205 | equal = chunk_equals(this->encoding, encoding); |
206 | free(encoding.ptr); | |
207 | return equal; | |
208 | } | |
209 | ||
4a679468 AS |
210 | METHOD(certificate_t, destroy, void, |
211 | private_pgp_cert_t *this) | |
4cb0e1bb MW |
212 | { |
213 | if (ref_put(&this->ref)) | |
214 | { | |
215 | DESTROY_IF(this->key); | |
216 | DESTROY_IF(this->user_id); | |
ab5762e3 | 217 | free(this->fingerprint.ptr); |
4cb0e1bb MW |
218 | free(this->encoding.ptr); |
219 | free(this); | |
220 | } | |
221 | } | |
222 | ||
4a679468 AS |
223 | METHOD(pgp_certificate_t, get_fingerprint, chunk_t, |
224 | private_pgp_cert_t *this) | |
ab5762e3 AS |
225 | { |
226 | return this->fingerprint; | |
227 | } | |
228 | ||
4cb0e1bb MW |
229 | /** |
230 | * See header | |
231 | */ | |
232 | private_pgp_cert_t *create_empty() | |
233 | { | |
4a679468 AS |
234 | private_pgp_cert_t *this; |
235 | ||
236 | INIT(this, | |
237 | .public = { | |
238 | .interface = { | |
239 | .interface = { | |
240 | .get_type = _get_type, | |
241 | .get_subject = _get_subject, | |
242 | .get_issuer = _get_issuer, | |
243 | .has_subject = _has_subject, | |
244 | .has_issuer = _has_issuer, | |
245 | .issued_by = _issued_by, | |
246 | .get_public_key = _get_public_key, | |
247 | .get_validity = _get_validity, | |
248 | .get_encoding = _get_encoding, | |
249 | .equals = _equals, | |
250 | .get_ref = _get_ref, | |
251 | .destroy = _destroy, | |
252 | }, | |
253 | .get_fingerprint = _get_fingerprint, | |
254 | }, | |
255 | }, | |
256 | .ref = 1, | |
257 | ); | |
4cb0e1bb MW |
258 | |
259 | return this; | |
260 | } | |
261 | ||
262 | /** | |
263 | * Parse the public key packet of a PGP certificate | |
264 | */ | |
265 | static bool parse_public_key(private_pgp_cert_t *this, chunk_t packet) | |
266 | { | |
ab5762e3 AS |
267 | chunk_t pubkey_packet = packet; |
268 | ||
4cb0e1bb MW |
269 | if (!pgp_read_scalar(&packet, 1, &this->version)) |
270 | { | |
271 | return FALSE; | |
272 | } | |
273 | switch (this->version) | |
274 | { | |
275 | case 3: | |
276 | if (!pgp_read_scalar(&packet, 4, &this->created) || | |
277 | !pgp_read_scalar(&packet, 2, &this->valid)) | |
278 | { | |
279 | return FALSE; | |
280 | } | |
281 | break; | |
282 | case 4: | |
283 | if (!pgp_read_scalar(&packet, 4, &this->created)) | |
284 | { | |
285 | return FALSE; | |
286 | } | |
287 | break; | |
288 | default: | |
cc4b48e8 | 289 | DBG1(DBG_ASN, "PGP packet version V%d not supported", |
8b0e0910 | 290 | this->version); |
4cb0e1bb MW |
291 | return FALSE; |
292 | } | |
bb7ae382 AS |
293 | if (this->valid) |
294 | { | |
cc4b48e8 | 295 | DBG2(DBG_ASN, "L2 - created %T, valid %d days", &this->created, FALSE, |
8b0e0910 | 296 | this->valid); |
bb7ae382 AS |
297 | } |
298 | else | |
299 | { | |
cc4b48e8 | 300 | DBG2(DBG_ASN, "L2 - created %T, never expires", &this->created, FALSE); |
bb7ae382 | 301 | } |
4cb0e1bb MW |
302 | DESTROY_IF(this->key); |
303 | this->key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY, | |
304 | BUILD_BLOB_PGP, packet, BUILD_END); | |
ab5762e3 AS |
305 | if (this->key == NULL) |
306 | { | |
307 | return FALSE; | |
308 | } | |
309 | ||
310 | /* compute V4 or V3 fingerprint according to section 12.2 of RFC 4880 */ | |
311 | if (this->version == 4) | |
312 | { | |
313 | chunk_t pubkey_packet_header = chunk_from_chars( | |
314 | 0x99, pubkey_packet.len / 256, pubkey_packet.len % 256 | |
315 | ); | |
316 | hasher_t *hasher; | |
317 | ||
318 | hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); | |
319 | if (hasher == NULL) | |
320 | { | |
cc4b48e8 | 321 | DBG1(DBG_ASN, "no SHA-1 hasher available"); |
ab5762e3 AS |
322 | return FALSE; |
323 | } | |
87dd205b MW |
324 | if (!hasher->allocate_hash(hasher, pubkey_packet_header, NULL) || |
325 | !hasher->allocate_hash(hasher, pubkey_packet, &this->fingerprint)) | |
326 | { | |
327 | hasher->destroy(hasher); | |
328 | return FALSE; | |
329 | } | |
ab5762e3 | 330 | hasher->destroy(hasher); |
cc4b48e8 | 331 | DBG2(DBG_ASN, "L2 - v4 fingerprint %#B", &this->fingerprint); |
ab5762e3 AS |
332 | } |
333 | else | |
334 | { | |
335 | /* V3 fingerprint is computed by public_key_t class */ | |
da9724e6 | 336 | if (!this->key->get_fingerprint(this->key, KEYID_PGPV3, |
ab5762e3 AS |
337 | &this->fingerprint)) |
338 | { | |
339 | return FALSE; | |
340 | } | |
341 | this->fingerprint = chunk_clone(this->fingerprint); | |
cc4b48e8 | 342 | DBG2(DBG_ASN, "L2 - v3 fingerprint %#B", &this->fingerprint); |
ab5762e3 AS |
343 | } |
344 | return TRUE; | |
4cb0e1bb MW |
345 | } |
346 | ||
347 | /** | |
348 | * Parse the signature packet of a PGP certificate | |
349 | */ | |
350 | static bool parse_signature(private_pgp_cert_t *this, chunk_t packet) | |
351 | { | |
352 | u_int32_t version, len, type, created; | |
353 | ||
354 | if (!pgp_read_scalar(&packet, 1, &version)) | |
355 | { | |
356 | return FALSE; | |
357 | } | |
bb7ae382 AS |
358 | |
359 | /* we parse only v3 or v4 signature packets */ | |
360 | if (version != 3 && version != 4) | |
4cb0e1bb | 361 | { |
cc4b48e8 | 362 | DBG2(DBG_ASN, "L2 - v%d signature ignored", version); |
4cb0e1bb MW |
363 | return TRUE; |
364 | } | |
bb7ae382 | 365 | if (version == 4) |
4cb0e1bb | 366 | { |
bb7ae382 AS |
367 | if (!pgp_read_scalar(&packet, 1, &type)) |
368 | { | |
369 | return FALSE; | |
370 | } | |
cc4b48e8 | 371 | DBG2(DBG_ASN, "L2 - v%d signature of type 0x%02x", version, type); |
4cb0e1bb | 372 | } |
bb7ae382 | 373 | else |
4cb0e1bb | 374 | { |
bb7ae382 AS |
375 | if (!pgp_read_scalar(&packet, 1, &len) || len != 5) |
376 | { | |
377 | return FALSE; | |
378 | } | |
379 | if (!pgp_read_scalar(&packet, 1, &type) || | |
380 | !pgp_read_scalar(&packet, 4, &created)) | |
381 | { | |
382 | return FALSE; | |
383 | } | |
cc4b48e8 | 384 | DBG2(DBG_ASN, "L2 - v3 signature of type 0x%02x, created %T", type, |
8b0e0910 | 385 | &created, FALSE); |
4cb0e1bb MW |
386 | } |
387 | /* TODO: parse and save signature to a list */ | |
388 | return TRUE; | |
389 | } | |
390 | ||
391 | /** | |
392 | * Parse the userid packet of a PGP certificate | |
393 | */ | |
394 | static bool parse_user_id(private_pgp_cert_t *this, chunk_t packet) | |
395 | { | |
396 | DESTROY_IF(this->user_id); | |
9f45b19f | 397 | this->user_id = identification_create_from_encoding(ID_KEY_ID, packet); |
cc4b48e8 | 398 | DBG2(DBG_ASN, "L2 - '%Y'", this->user_id); |
4cb0e1bb MW |
399 | return TRUE; |
400 | } | |
401 | ||
402 | /** | |
403 | * See header. | |
404 | */ | |
79c6f162 | 405 | pgp_cert_t *pgp_cert_load(certificate_type_t type, va_list args) |
4cb0e1bb MW |
406 | { |
407 | chunk_t packet, blob = chunk_empty; | |
408 | pgp_packet_tag_t tag; | |
409 | private_pgp_cert_t *this; | |
410 | ||
411 | while (TRUE) | |
412 | { | |
413 | switch (va_arg(args, builder_part_t)) | |
414 | { | |
415 | case BUILD_BLOB_PGP: | |
416 | blob = va_arg(args, chunk_t); | |
417 | continue; | |
418 | case BUILD_END: | |
419 | break; | |
420 | default: | |
421 | return NULL; | |
422 | } | |
423 | break; | |
424 | } | |
425 | ||
426 | this = create_empty(); | |
427 | this->encoding = chunk_clone(blob); | |
428 | while (blob.len) | |
429 | { | |
430 | if (!pgp_read_packet(&blob, &packet, &tag)) | |
431 | { | |
432 | destroy(this); | |
433 | return NULL; | |
434 | } | |
435 | switch (tag) | |
436 | { | |
437 | case PGP_PKT_PUBLIC_KEY: | |
438 | if (!parse_public_key(this, packet)) | |
439 | { | |
440 | destroy(this); | |
441 | return NULL; | |
442 | } | |
443 | break; | |
444 | case PGP_PKT_SIGNATURE: | |
445 | if (!parse_signature(this, packet)) | |
446 | { | |
447 | destroy(this); | |
79d5c4f0 | 448 | return NULL; |
4cb0e1bb MW |
449 | } |
450 | break; | |
451 | case PGP_PKT_USER_ID: | |
452 | if (!parse_user_id(this, packet)) | |
453 | { | |
454 | destroy(this); | |
79d5c4f0 | 455 | return NULL; |
4cb0e1bb MW |
456 | } |
457 | break; | |
458 | default: | |
8b0e0910 | 459 | DBG1(DBG_LIB, "ignoring %N packet in PGP certificate", |
4cb0e1bb MW |
460 | pgp_packet_tag_names, tag); |
461 | break; | |
462 | } | |
463 | } | |
464 | if (this->key) | |
465 | { | |
466 | return &this->public; | |
467 | } | |
468 | destroy(this); | |
469 | return NULL; | |
470 | } | |
471 |