diff options
Diffstat (limited to 'src/pluto/pgp.c')
-rw-r--r-- | src/pluto/pgp.c | 647 |
1 files changed, 647 insertions, 0 deletions
diff --git a/src/pluto/pgp.c b/src/pluto/pgp.c new file mode 100644 index 000000000..307303f6b --- /dev/null +++ b/src/pluto/pgp.c @@ -0,0 +1,647 @@ +/* Support of OpenPGP certificates + * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCSID $Id: pgp.c,v 1.7 2006/01/04 21:00:43 as Exp $ + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <freeswan.h> +#include <ipsec_policy.h> + +#include "constants.h" +#include "defs.h" +#include "mp_defs.h" +#include "log.h" +#include "id.h" +#include "pgp.h" +#include "certs.h" +#include "md5.h" +#include "whack.h" +#include "pkcs1.h" +#include "keys.h" + +/* + * chained list of OpenPGP end certificates + */ +static pgpcert_t *pgpcerts = NULL; + +/* + * OpenPGP packet tags defined in section 4.3 of RFC 2440 + */ +#define PGP_PKT_RESERVED 0 +#define PGP_PKT_PUBKEY_ENC_SESSION_KEY 1 +#define PGP_PKT_SIGNATURE 2 +#define PGP_PKT_SYMKEY_ENC_SESSION_KEY 3 +#define PGP_PKT_ONE_PASS_SIGNATURE_PKT 4 +#define PGP_PKT_SECRET_KEY 5 +#define PGP_PKT_PUBLIC_KEY 6 +#define PGP_PKT_SECRET_SUBKEY 7 +#define PGP_PKT_COMPRESSED_DATA 8 +#define PGP_PKT_SYMKEY_ENC_DATA 9 +#define PGP_PKT_MARKER 10 +#define PGP_PKT_LITERAL_DATA 11 +#define PGP_PKT_TRUST 12 +#define PGP_PKT_USER_ID 13 +#define PGP_PKT_PUBLIC_SUBKEY 14 +#define PGP_PKT_ROOF 15 + +static const char *const pgp_packet_type_name[] = { + "Reserved", + "Public-Key Encrypted Session Key Packet", + "Signature Packet", + "Symmetric-Key Encrypted Session Key Packet", + "One-Pass Signature Packet", + "Secret Key Packet", + "Public Key Packet", + "Secret Subkey Packet", + "Compressed Data Packet", + "Symmetrically Encrypted Data Packet", + "Marker Packet", + "Literal Data Packet", + "Trust Packet", + "User ID Packet", + "Public Subkey Packet" +}; + +/* + * OpenPGP public key algorithms defined in section 9.1 of RFC 2440 + */ +#define PGP_PUBKEY_ALG_RSA 1 +#define PGP_PUBKEY_ALG_RSA_ENC_ONLY 2 +#define PGP_PUBKEY_ALG_RSA_SIGN_ONLY 3 +#define PGP_PUBKEY_ALG_ELGAMAL_ENC_ONLY 16 +#define PGP_PUBKEY_ALG_DSA 17 +#define PGP_PUBKEY_ALG_ECC 18 +#define PGP_PUBKEY_ALG_ECDSA 19 +#define PGP_PUBKEY_ALG_ELGAMAL 20 + +/* + * OpenPGP symmetric key algorithms defined in section 9.2 of RFC 2440 + */ +#define PGP_SYM_ALG_PLAIN 0 +#define PGP_SYM_ALG_IDEA 1 +#define PGP_SYM_ALG_3DES 2 +#define PGP_SYM_ALG_CAST5 3 +#define PGP_SYM_ALG_BLOWFISH 4 +#define PGP_SYM_ALG_SAFER 5 +#define PGP_SYM_ALG_DES 6 +#define PGP_SYM_ALG_AES 7 +#define PGP_SYM_ALG_AES_192 8 +#define PGP_SYM_ALG_AES_256 9 +#define PGP_SYM_ALG_TWOFISH 10 +#define PGP_SYM_ALG_ROOF 11 + +static const char *const pgp_sym_alg_name[] = { + "Plaintext", + "IDEA", + "3DES", + "CAST5", + "Blowfish", + "SAFER", + "DES", + "AES", + "AES-192", + "AES-256", + "Twofish" +}; + +/* + * Size of PGP Key ID + */ +#define PGP_KEYID_SIZE 8 + +const pgpcert_t empty_pgpcert = { + NULL , /* *next */ + 0 , /* installed */ + 0 , /* count */ + { NULL, 0 }, /* certificate */ + 0 , /* created */ + 0 , /* until */ + 0 , /* pubkeyAlgorithm */ + { NULL, 0 }, /* modulus */ + { NULL, 0 }, /* publicExponent */ + "" /* fingerprint */ +}; + +static size_t +pgp_size(chunk_t *blob, int len) +{ + size_t size = 0; + + blob->len -= len; + while (len-- > 0) + size = 256*size + *blob->ptr++; + return size; +} + +/* + * 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_size(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 public key packet defined in section 5.5.2 of RFC 2440 + */ +static bool +parse_pgp_pubkey_packet(chunk_t *packet, pgpcert_t *cert) +{ + u_char version = pgp_version(packet); + + if (version < 3 || version > 4) + { + plog("PGP packet version V%d not supported", version); + return FALSE; + } + + /* creation date - 4 bytes */ + cert->created = (time_t)pgp_size(packet, 4); + DBG(DBG_PARSING, + DBG_log("L3 - created:"); + DBG_log(" %s", timetoa(&cert->created, TRUE)) + ) + + if (version == 3) + { + /* validity in days - 2 bytes */ + cert->until = (time_t)pgp_size(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(" %s", timetoa(&cert->until, TRUE)); + ) + } + + /* public key algorithm - 1 byte */ + DBG(DBG_PARSING, + DBG_log("L3 - public key algorithm:") + ) + + switch (pgp_size(packet, 1)) + { + case PGP_PUBKEY_ALG_RSA: + case PGP_PUBKEY_ALG_RSA_SIGN_ONLY: + cert->pubkeyAlg = PUBKEY_ALG_RSA; + DBG(DBG_PARSING, + DBG_log(" RSA") + ) + /* modulus n */ + cert->modulus.len = (pgp_size(packet, 2)+7) / BITS_PER_BYTE; + cert->modulus.ptr = packet->ptr; + packet->ptr += cert->modulus.len; + packet->len -= cert->modulus.len; + DBG(DBG_PARSING, + DBG_log("L3 - modulus:") + ) + DBG_cond_dump_chunk(DBG_RAW, "", cert->modulus); + + /* public exponent e */ + cert->publicExponent.len = (pgp_size(packet, 2)+7) / BITS_PER_BYTE; + cert->publicExponent.ptr = packet->ptr; + packet->ptr += cert->publicExponent.len; + packet->len -= cert->publicExponent.len; + DBG(DBG_PARSING, + DBG_log("L3 - public exponent:") + ) + DBG_cond_dump_chunk(DBG_RAW, "", cert->publicExponent); + + if (version == 3) + { + /* a V3 fingerprint is the MD5 hash of modulus and public exponent */ + MD5_CTX context; + MD5Init(&context); + MD5Update(&context, cert->modulus.ptr, cert->modulus.len); + MD5Update(&context, cert->publicExponent.ptr, cert->publicExponent.len); + MD5Final(cert->fingerprint, &context); + } + else + { + plog(" computation of V4 key ID not implemented yet"); + } + break; + case PGP_PUBKEY_ALG_DSA: + cert->pubkeyAlg = PUBKEY_ALG_DSA; + DBG(DBG_PARSING, + DBG_log(" DSA") + ) + plog(" DSA public keys not supported"); + return FALSE; + default: + cert->pubkeyAlg = 0; + DBG(DBG_PARSING, + DBG_log(" other") + ) + plog(" exotic not RSA public keys not supported"); + return FALSE; + } + return TRUE; +} + +/* + * Parse OpenPGP secret key packet defined in section 5.5.3 of RFC 2440 + */ +static bool +parse_pgp_secretkey_packet(chunk_t *packet, RSA_private_key_t *key) +{ + int i, s2k; + pgpcert_t cert = empty_pgpcert; + + if (!parse_pgp_pubkey_packet(packet, &cert)) + return FALSE; + + init_RSA_public_key((RSA_public_key_t *)key, cert.publicExponent + , cert.modulus); + + /* string-to-key usage */ + s2k = pgp_size(packet, 1); + + DBG(DBG_PARSING, + DBG_log("L3 - string-to-key: %d", s2k) + ) + + if (s2k == 255) + { + plog(" string-to-key specifiers not supported"); + return FALSE; + } + + if (s2k >= PGP_SYM_ALG_ROOF) + { + plog(" undefined symmetric key algorithm"); + return FALSE; + } + + /* a known symmetric key algorithm is specified*/ + DBG(DBG_PARSING, + DBG_log(" %s", pgp_sym_alg_name[s2k]) + ) + + /* private key is unencrypted */ + if (s2k == PGP_SYM_ALG_PLAIN) + { + for (i = 2; i < RSA_PRIVATE_FIELD_ELEMENTS; i++) + { + mpz_t u; /* auxiliary variable */ + + /* compute offset to private key component i*/ + MP_INT *n = (MP_INT*)((char *)key + RSA_private_field[i].offset); + + switch (i) + { + case 2: + case 3: + case 4: + { + size_t len = (pgp_size(packet, 2)+7) / BITS_PER_BYTE; + + n_to_mpz(n, packet->ptr, len); + DBG(DBG_PARSING, + DBG_log("L3 - %s:", RSA_private_field[i].name) + ) + DBG_cond_dump(DBG_PRIVATE, "", packet->ptr, len); + packet->ptr += len; + packet->len -= len; + } + break; + case 5: /* dP = d mod (p-1) */ + mpz_init(u); + mpz_sub_ui(u, &key->p, 1); + mpz_mod(n, &key->d, u); + mpz_clear(u); + break; + case 6: /* dQ = d mod (q-1) */ + mpz_init(u); + mpz_sub_ui(u, &key->q, 1); + mpz_mod(n, &key->d, u); + mpz_clear(u); + break; + case 7: /* qInv = (q^-1) mod p */ + mpz_invert(n, &key->q, &key->p); + if (mpz_cmp_ui(n, 0) < 0) + mpz_add(n, n, &key->p); + passert(mpz_cmp(n, &key->p) < 0); + break; + } + } + return TRUE; + } + + plog(" %s encryption not supported", pgp_sym_alg_name[s2k]); + return FALSE; +} + +/* + * 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_size(packet, 1) != 5) + { + plog(" size must be 5"); + return FALSE; + } + + /* signature type - 1 byte */ + sig_type = (u_char)pgp_size(packet, 1); + DBG(DBG_PARSING, + DBG_log("L3 - signature type: 0x%2x", sig_type) + ) + + /* creation date - 4 bytes */ + created = (time_t)pgp_size(packet, 4); + DBG(DBG_PARSING, + DBG_log("L3 - created:"); + DBG_log(" %s", timetoa(&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; +} + +bool +parse_pgp(chunk_t blob, pgpcert_t *cert, RSA_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 = empty_chunk; + 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(" %s (%d), old format, %d bytes", + (packet_type < PGP_PKT_ROOF) ? + pgp_packet_type_name[packet_type] : + "Undefined Packet Type", packet_type, (int)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; + 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 && + memcmp(a->certificate.ptr, b->certificate.ptr, b->certificate.len) == 0; +} + +/* + * 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.len = PGP_FINGERPRINT_SIZE; + end_id->name.ptr = cert->fingerprint; + end_id->name.ptr = temporary_cyclic_buffer(); + memcpy(end_id->name.ptr, cert->fingerprint, PGP_FINGERPRINT_SIZE); +} + +/* + * 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) + { + if (cert->certificate.ptr != NULL) + pfree(cert->certificate.ptr); + pfree(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) + { + unsigned keysize; + char buf[BUF_LEN]; + cert_t c; + + c.type = CERT_PGP; + c.u.pgp = cert; + + whack_log(RC_COMMENT, "%s, count: %d", timetoa(&cert->installed, utc), cert->count); + datatot(cert->fingerprint, PGP_FINGERPRINT_SIZE, 'x', buf, BUF_LEN); + whack_log(RC_COMMENT, " fingerprint: %s", buf); + form_keyid(cert->publicExponent, cert->modulus, buf, &keysize); + whack_log(RC_COMMENT, " pubkey: %4d RSA Key %s%s", 8*keysize, buf, + (has_private_key(c))? ", has private key" : ""); + whack_log(RC_COMMENT, " created: %s", timetoa(&cert->created, utc)); + whack_log(RC_COMMENT, " until: %s %s", timetoa(&cert->until, utc), + check_expiry(cert->until, CA_CERT_WARNING_INTERVAL, TRUE)); + cert = cert->next; + } +} + |