/* Support of OpenPGP certificates * Copyright (C) 2002-2009 Andreas Steffen * * HSR - Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include #include #include #include #include #include #include "constants.h" #include "defs.h" #include "log.h" #include "id.h" #include "pgpcert.h" #include "certs.h" #include "whack.h" #include "keys.h" /** * Chained list of OpenPGP end certificates */ static pgpcert_t *pgpcerts = NULL; /** * Size of PGP Key ID */ #define PGP_KEYID_SIZE 8 const pgpcert_t pgpcert_empty = { NULL , /* next */ 0 , /* version */ 0 , /* installed */ 0 , /* count */ { NULL, 0 }, /* certificate */ 0 , /* created */ 0 , /* until */ NULL , /* public key */ NULL /* fingerprint */ }; /** * Extracts the length of a PGP packet */ static size_t pgp_old_packet_length(chunk_t *blob) { /* bits 0 and 1 define the packet length type */ int len_type = 0x03 & *blob->ptr++; blob->len--; /* len_type: 0 -> 1 byte, 1 -> 2 bytes, 2 -> 4 bytes */ return pgp_length(blob, (len_type == 0)? 1: len_type << 1); } /** * Extracts PGP packet version (V3 or V4) */ static u_char pgp_version(chunk_t *blob) { u_char version = *blob->ptr++; blob->len--; DBG(DBG_PARSING, DBG_log("L3 - version:"); DBG_log(" V%d", version) ) return version; } /** * Parse OpenPGP signature packet defined in section 5.2.2 of RFC 2440 */ static bool parse_pgp_signature_packet(chunk_t *packet, pgpcert_t *cert) { time_t created; chunk_t keyid; u_char sig_type; u_char version = pgp_version(packet); /* we parse only V3 signature packets */ if (version != 3) { return TRUE; } /* size byte must have the value 5 */ if (pgp_length(packet, 1) != 5) { plog(" size must be 5"); return FALSE; } /* signature type - 1 byte */ sig_type = (u_char)pgp_length(packet, 1); DBG(DBG_PARSING, DBG_log("L3 - signature type: 0x%2x", sig_type) ) /* creation date - 4 bytes */ created = (time_t)pgp_length(packet, 4); DBG(DBG_PARSING, DBG_log("L3 - created:"); DBG_log(" %T", &cert->created, TRUE) ) /* key ID of signer - 8 bytes */ keyid.ptr = packet->ptr; keyid.len = PGP_KEYID_SIZE; DBG_cond_dump_chunk(DBG_PARSING, "L3 - key ID of signer", keyid); return TRUE; } /** * Parses the version and validity of an OpenPGP public key packet */ static bool parse_pgp_pubkey_version_validity(chunk_t *packet, pgpcert_t *cert) { cert->version = pgp_version(packet); if (cert->version < 3 || cert->version > 4) { plog("OpenPGP packet version V%d not supported", cert->version); return FALSE; } /* creation date - 4 bytes */ cert->created = (time_t)pgp_length(packet, 4); DBG(DBG_PARSING, DBG_log("L3 - created:"); DBG_log(" %T", &cert->created, TRUE) ) if (cert->version == 3) { /* validity in days - 2 bytes */ cert->until = (time_t)pgp_length(packet, 2); /* validity of 0 days means that the key never expires */ if (cert->until > 0) { cert->until = cert->created + 24*3600*cert->until; } DBG(DBG_PARSING, DBG_log("L3 - until:"); DBG_log(" %T", &cert->until, TRUE); ) } return TRUE; } /** * Parse OpenPGP public key packet defined in section 5.5.2 of RFC 4880 */ static bool parse_pgp_pubkey_packet(chunk_t *packet, pgpcert_t *cert) { pgp_pubkey_alg_t pubkey_alg; public_key_t *key; if (!parse_pgp_pubkey_version_validity(packet, cert)) { return FALSE; } /* public key algorithm - 1 byte */ pubkey_alg = pgp_length(packet, 1); DBG(DBG_PARSING, DBG_log("L3 - public key algorithm:"); DBG_log(" %N", pgp_pubkey_alg_names, pubkey_alg) ) switch (pubkey_alg) { case PGP_PUBKEY_ALG_RSA: case PGP_PUBKEY_ALG_RSA_SIGN_ONLY: key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_BLOB_PGP, *packet, BUILD_END); if (key == NULL) { return FALSE; } cert->public_key = key; if (cert->version == 3) { cert->fingerprint = key->get_id(key, ID_KEY_ID); if (cert->fingerprint == NULL) { return FALSE; } } else { plog(" computation of V4 key ID not implemented yet"); return FALSE; } break; default: plog(" non RSA public keys not supported"); return FALSE; } return TRUE; } /* * Parse OpenPGP secret key packet defined in section 5.5.3 of RFC 4880 */ static bool parse_pgp_secretkey_packet(chunk_t *packet, private_key_t **key) { pgp_pubkey_alg_t pubkey_alg; pgpcert_t cert = pgpcert_empty; if (!parse_pgp_pubkey_version_validity(packet, &cert)) { return FALSE; } /* public key algorithm - 1 byte */ pubkey_alg = pgp_length(packet, 1); DBG(DBG_PARSING, DBG_log("L3 - public key algorithm:"); DBG_log(" %N", pgp_pubkey_alg_names, pubkey_alg) ) switch (pubkey_alg) { case PGP_PUBKEY_ALG_RSA: case PGP_PUBKEY_ALG_RSA_SIGN_ONLY: *key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, BUILD_BLOB_PGP, *packet, BUILD_END); break; default: plog(" non RSA private keys not supported"); return FALSE; } return (*key != NULL); } bool parse_pgp(chunk_t blob, pgpcert_t *cert, private_key_t **key) { DBG(DBG_PARSING, DBG_log("L0 - PGP file:") ) DBG_cond_dump_chunk(DBG_RAW, "", blob); if (cert != NULL) { /* parse a PGP certificate file */ cert->certificate = blob; time(&cert->installed); } else if (key == NULL) { /* should not occur, nothing to parse */ return FALSE; } while (blob.len > 0) { chunk_t packet = chunk_empty; u_char packet_tag = *blob.ptr; DBG(DBG_PARSING, DBG_log("L1 - PGP packet: tag= 0x%2x", packet_tag) ) /* bit 7 must be set */ if (!(packet_tag & 0x80)) { plog(" incorrect Packet Tag"); return FALSE; } /* bit 6 set defines new packet format */ if (packet_tag & 0x40) { plog(" new PGP packet format not supported"); return FALSE; } else { int packet_type = (packet_tag & 0x3C) >> 2; packet.len = pgp_old_packet_length(&blob); packet.ptr = blob.ptr; blob.ptr += packet.len; blob.len -= packet.len; DBG(DBG_PARSING, DBG_log(" %N (%d), old format, %u bytes", pgp_packet_tag_names, packet_type, packet_type, packet.len); DBG_log("L2 - body:") ) DBG_cond_dump_chunk(DBG_RAW, "", packet); if (cert != NULL) { /* parse a PGP certificate */ switch (packet_type) { case PGP_PKT_PUBLIC_KEY: if (!parse_pgp_pubkey_packet(&packet, cert)) { return FALSE; } break; case PGP_PKT_SIGNATURE: if (!parse_pgp_signature_packet(&packet, cert)) { return FALSE; } break; case PGP_PKT_USER_ID: DBG(DBG_PARSING, DBG_log("L3 - user ID:"); DBG_log(" '%.*s'", (int)packet.len, packet.ptr) ) break; default: break; } } else { /* parse a PGP private key file */ switch (packet_type) { case PGP_PKT_SECRET_KEY: if (!parse_pgp_secretkey_packet(&packet, key)) { return FALSE; } break; case PGP_PKT_USER_ID: DBG(DBG_PARSING, DBG_log("L3 - user ID:"); DBG_log(" '%.*s'", (int)packet.len, packet.ptr) ) break; default: break; } } } } return TRUE; } /** * Compare two OpenPGP certificates */ static bool same_pgpcert(pgpcert_t *a, pgpcert_t *b) { return a->certificate.len == b->certificate.len && memeq(a->certificate.ptr, b->certificate.ptr, b->certificate.len); } /** * For each link pointing to the certificate increase the count by one */ void share_pgpcert(pgpcert_t *cert) { if (cert != NULL) { cert->count++; } } /** * Select the OpenPGP keyid as ID */ void select_pgpcert_id(pgpcert_t *cert, struct id *end_id) { end_id->kind = ID_KEY_ID; end_id->name = cert->fingerprint->get_encoding(cert->fingerprint); } /** * Add an OpenPGP user/host certificate to the chained list */ pgpcert_t* add_pgpcert(pgpcert_t *cert) { pgpcert_t *c = pgpcerts; while (c != NULL) { if (same_pgpcert(c, cert)) /* already in chain, free cert */ { free_pgpcert(cert); return c; } c = c->next; } /* insert new cert at the root of the chain */ cert->next = pgpcerts; pgpcerts = cert; DBG(DBG_CONTROL | DBG_PARSING, DBG_log(" pgp cert inserted") ) return cert; } /** * Release of a certificate decreases the count by one. * The certificate is freed when the counter reaches zero */ void release_pgpcert(pgpcert_t *cert) { if (cert != NULL && --cert->count == 0) { pgpcert_t **pp = &pgpcerts; while (*pp != cert) { pp = &(*pp)->next; } *pp = cert->next; free_pgpcert(cert); } } /** * Free a PGP certificate */ void free_pgpcert(pgpcert_t *cert) { if (cert != NULL) { DESTROY_IF(cert->public_key); DESTROY_IF(cert->fingerprint); free(cert->certificate.ptr); free(cert); } } /** * List all PGP end certificates in a chained list */ void list_pgp_end_certs(bool utc) { pgpcert_t *cert = pgpcerts; time_t now; /* determine the current time */ time(&now); if (cert != NULL) { whack_log(RC_COMMENT, " "); whack_log(RC_COMMENT, "List of PGP End certificates:"); whack_log(RC_COMMENT, " "); } while (cert != NULL) { public_key_t *key = cert->public_key; cert_t c; c.type = CERT_PGP; c.u.pgp = cert; whack_log(RC_COMMENT, "%T, count: %d", &cert->installed, utc, cert->count); whack_log(RC_COMMENT, " digest: %Y", cert->fingerprint); whack_log(RC_COMMENT, " created: %T", &cert->created, utc); whack_log(RC_COMMENT, " until: %T %s", &cert->until, utc, check_expiry(cert->until, CA_CERT_WARNING_INTERVAL, TRUE)); whack_log(RC_COMMENT, " pubkey: %N %4d bits%s", key_type_names, key->get_type(key), key->get_keysize(key) * BITS_PER_BYTE, has_private_key(c)? ", has private key" : ""); whack_log(RC_COMMENT, " keyid: %Y", key->get_id(key, ID_PUBKEY_INFO_SHA1)); cert = cert->next; } }