/* * Copyright (C) 2007, 2012 David Shaw * * 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. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "restore.h" #include #include #include #include #include #include "GFModuleCommonUtils.hpp" #include "GFSDKBasic.h" #include "output.h" #include "packets.h" #include "parse.h" struct key { unsigned char fpr[20]; struct packet *packet; struct key *next; }; static auto extract_keys(struct packet *packet) -> struct key * { struct key *key = NULL; size_t idx = 1; /* Check the version */ if (packet->len && packet->buf[0] != 0) { FLOG_ERROR("Cannot handle secrets file version %d", packet->buf[0]); return NULL; } while (idx < packet->len) { /* 1+20+2 == version + fingerprint + length */ if (idx + 1 + 20 + 2 <= packet->len) { if (packet->buf[idx] == 4) { unsigned int len; struct key *newkey; newkey = static_cast(GFAllocateMemory(sizeof(struct key))); newkey->next = NULL; idx++; memcpy(newkey->fpr, &packet->buf[idx], 20); idx += 20; len = packet->buf[idx++] << 8; len |= packet->buf[idx++]; if (idx + len <= packet->len) { newkey->packet = append_packet(NULL, &packet->buf[idx], len); idx += len; } else { LOG_WARN("Short data in secret image"); free(newkey); break; } newkey->next = key; key = newkey; } else { LOG_WARN("Corrupt data in secret image"); break; } } else { LOG_WARN("Short header in secret image"); break; } } return key; } static void free_keys(struct key *key) { while (key) { struct key *keytmp = key; free_packet(key->packet); key = key->next; free(keytmp); } } auto restore(FILE *pubring, FILE *secrets, enum data_type input_type, FILE *output) -> int { struct packet *secret; if (input_type == AUTO) { int test = fgetc(secrets); if (test == EOF) { LOG_ERROR("Unable to check type of secrets file"); return 1; } else if (isascii(test) && isprint(test)) input_type = BASE16; else input_type = RAW; ungetc(test, secrets); } secret = read_secrets_file(secrets, input_type); if (secret) { struct packet *pubkey; struct key *keys; int did_pubkey = 0; /* Build a list of all keys. We need to do this since the public key we are transforming might have the subkeys in a different order than (or not match subkeys at all with) our secret data. */ keys = extract_keys(secret); if (keys) { output_start(output, RAW, NULL); while ((pubkey = parse(pubring, 0, 0))) { unsigned char ptag; if (pubkey->type == 6 || pubkey->type == 14) { /* Public key or subkey */ unsigned char fpr[20]; struct key *keyidx; if (pubkey->type == 6 && did_pubkey) break; calculate_fingerprint(pubkey, pubkey->len, fpr); /* Do we have a secret key that matches? */ for (keyidx = keys; keyidx; keyidx = keyidx->next) { if (memcmp(fpr, keyidx->fpr, 20) == 0) { if (pubkey->type == 6) { ptag = 5; did_pubkey = 1; } else ptag = 7; /* Match, so create a secret key. */ output_openpgp_header(ptag, pubkey->len + keyidx->packet->len); output_packet(pubkey); output_packet(keyidx->packet); } } } else if (did_pubkey) { /* Copy the usual user ID, sigs, etc, so the key is well-formed. */ output_openpgp_header(pubkey->type, pubkey->len); output_packet(pubkey); } free_packet(pubkey); } free_keys(keys); output_end(); } else { LOG_ERROR("Unable to parse secret data"); return 1; } } else { LOG_ERROR("Unable to read secrets file"); return 1; } return 0; }