123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- #include "platform.h"
- #include <sys/types.h>
- #include <assert.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include "chunks.h"
- #include "crypto.h"
- #include "crypto_verify_bytes.h"
- #include "storage.h"
- #include "sysendian.h"
- #include "warnp.h"
- #include "multitape_internal.h"
- /**
- * Metadata format:
- * <NUL-terminated name>
- * <64-bit little-endian creation time>
- * <32-bit little-endian argc>
- * argc * <NUL-terminated argv entry>
- * SHA256(metaindex)
- * <64-bit metaindex length>
- * RSA_SIGN(all the metadata before this signature)
- */
- static int multitape_metadata_enc(const struct tapemetadata *, uint8_t **,
- size_t *);
- static int multitape_metadata_dec(struct tapemetadata *, uint8_t *, size_t);
- static int multitape_metadata_get(STORAGE_R *, CHUNKS_S *,
- struct tapemetadata *, const uint8_t[32], const char *, int);
- /**
- * multitape_metadata_ispresent(S, tapename):
- * Return 1 if there is already a metadata file for the specified archive
- * name, 0 if not, or -1 on error.
- */
- int
- multitape_metadata_ispresent(STORAGE_W * S, const char * tapename)
- {
- uint8_t hbuf[32]; /* HMAC of tapename. */
- /* Compute the hash of the tape name. */
- if (crypto_hash_data(CRYPTO_KEY_HMAC_NAME,
- (const uint8_t *)tapename, strlen(tapename), hbuf))
- goto err0;
- /* Ask the storage layer if the metadata file exists. */
- return (storage_write_fexist(S, 'm', hbuf));
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * multitape_metadata_enc(mdat, bufp, buflenp):
- * Encode a struct tapemetadata into a buffer. Return the buffer and its
- * length via ${bufp} and ${buflenp} respectively.
- */
- static int
- multitape_metadata_enc(const struct tapemetadata * mdat, uint8_t ** bufp,
- size_t * buflenp)
- {
- uint8_t * buf; /* Encoded metadata. */
- size_t buflen; /* Encoded metadata size. */
- uint8_t * p;
- int i;
- /* Sanity check. */
- assert((mdat->argc >= 0) && ((uintmax_t)mdat->argc <= UINT32_MAX));
- /* Add up the lengths of various pieces of metadata. */
- buflen = strlen(mdat->name) + 1; /* name */
- buflen += 8; /* ctime */
- buflen += 4; /* argc */
- for (i = 0; i < mdat->argc; i++) /* argv */
- buflen += strlen(mdat->argv[i]) + 1;
- buflen += 32; /* indexhash */
- buflen += 8; /* index length */
- buflen += 256; /* 2048-bit RSA signature */
- /* Allocate memory. */
- if ((p = buf = malloc(buflen)) == NULL)
- goto err0;
- /* Copy name. */
- memcpy(p, mdat->name, strlen(mdat->name) + 1);
- p += strlen(mdat->name) + 1;
- /* Encode ctime and argc. */
- le64enc(p, (uint64_t)mdat->ctime);
- p += 8;
- le32enc(p, (uint32_t)mdat->argc);
- p += 4;
- /* Copy argv. */
- for (i = 0; i < mdat->argc; i++) {
- memcpy(p, mdat->argv[i], strlen(mdat->argv[i]) + 1);
- p += strlen(mdat->argv[i]) + 1;
- }
- /* Copy index hash. */
- memcpy(p, mdat->indexhash, 32);
- p += 32;
- /* Encode index length. */
- le64enc(p, mdat->indexlen);
- p += 8;
- /* Generate signature. */
- if (crypto_rsa_sign(CRYPTO_KEY_SIGN_PRIV, buf, (size_t)(p - buf), p,
- 256))
- goto err1;
- /* Return buffer and length. */
- *bufp = buf;
- *buflenp = buflen;
- /* Success! */
- return (0);
- err1:
- free(buf);
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * multitape_metadata_put(S, C, mdat):
- * Store archive metadata. Call chunks_write_extrastats on ${C} and the
- * metadata file length.
- */
- int
- multitape_metadata_put(STORAGE_W * S, CHUNKS_W * C,
- struct tapemetadata * mdat)
- {
- uint8_t hbuf[32]; /* HMAC of tape name. */
- uint8_t * buf; /* Encoded metadata. */
- size_t buflen; /* Encoded metadata size. */
- /* Construct metadata file. */
- if (multitape_metadata_enc(mdat, &buf, &buflen))
- goto err0;
- /* Compute hash of tape name. */
- if (crypto_hash_data(CRYPTO_KEY_HMAC_NAME,
- (uint8_t *)mdat->name, strlen(mdat->name), hbuf))
- goto err1;
- /* Store the archive metadata. */
- if (storage_write_file(S, buf, buflen, 'm', hbuf))
- goto err1;
- chunks_write_extrastats(C, buflen);
- /* Free metadata buffer. */
- free(buf);
- /* Success! */
- return (0);
- err1:
- free(buf);
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * multitape_metadata_dec(mdat, buf, buflen):
- * Parse a buffer into a struct tapemetadata. Return 0 on success, 1 if the
- * metadata is corrupt, or -1 on error.
- */
- static int
- multitape_metadata_dec(struct tapemetadata * mdat, uint8_t * buf,
- size_t buflen)
- {
- uint8_t * p;
- size_t i;
- int arg;
- /* Start at the beginning... */
- p = buf;
- /* Make sure the archive name is NUL-terminated. */
- for (i = 0; i < buflen; i++)
- if (p[i] == '\0')
- break;
- if (i == buflen)
- goto bad0;
- /* Copy the archive name and move on to next field. */
- if ((mdat->name = strdup((char *)p)) == NULL)
- goto err0;
- buflen -= strlen((char *)p) + 1;
- p += strlen((char *)p) + 1;
- /* Parse ctime and argc. */
- if (buflen < 8)
- goto bad1;
- mdat->ctime = (time_t)le64dec(p);
- buflen -= 8;
- p += 8;
- if (buflen < 4)
- goto bad1;
- mdat->argc = (int)le32dec(p);
- buflen -= 4;
- p += 4;
- /* Sanity-check argc. */
- if ((mdat->argc < 0) || ((uintmax_t)mdat->argc > SIZE_MAX) ||
- ((size_t)(mdat->argc) > buflen))
- goto bad1;
- /* Allocate space for argv. */
- if ((mdat->argv = malloc((size_t)mdat->argc * sizeof(char *))) == NULL)
- goto err1;
- /* Parse argv. */
- for (arg = 0; arg < mdat->argc; arg++)
- mdat->argv[arg] = NULL;
- for (arg = 0; arg < mdat->argc; arg++) {
- /* Make sure argument is NUL-terminated. */
- for (i = 0; i < buflen; i++)
- if (p[i] == '\0')
- break;
- if (i == buflen)
- goto bad2;
- /* Copy argument and move on to next field. */
- if ((mdat->argv[arg] = strdup((char *)p)) == NULL)
- goto err2;
- buflen -= strlen((char *)p) + 1;
- p += strlen((char *)p) + 1;
- }
- /* Copy indexhash. */
- if (buflen < 32)
- goto bad2;
- memcpy(mdat->indexhash, p, 32);
- buflen -= 32;
- p += 32;
- /* Parse index length. */
- if (buflen < 8)
- goto bad2;
- mdat->indexlen = le64dec(p);
- buflen -= 8;
- p += 8;
- /* Validate signature. */
- if (buflen < 256)
- goto bad2;
- switch (crypto_rsa_verify(CRYPTO_KEY_SIGN_PUB,
- buf, (size_t)(p - buf), p, 256)) {
- case -1:
- /* Error in crypto_rsa_verify. */
- goto err2;
- case 1:
- /* Bad signature. */
- goto bad2;
- case 0:
- /* Signature is good. */
- break;
- }
- buflen -= 256;
- p += 256;
- (void)p; /* not used beyond this point. */
- /* We should be at the end of the metadata now. */
- if (buflen != 0)
- goto bad2;
- /* Success! */
- return (0);
- bad2:
- for (arg = 0; arg < mdat->argc; arg++)
- free(mdat->argv[arg]);
- free(mdat->argv);
- bad1:
- free(mdat->name);
- bad0:
- /* Metadata is corrupt. */
- return (1);
- err2:
- for (arg = 0; arg < mdat->argc; arg++)
- free(mdat->argv[arg]);
- free(mdat->argv);
- err1:
- free(mdat->name);
- err0:
- /* Failure! */
- return (-1);
- }
- static int
- multitape_metadata_get(STORAGE_R * S, CHUNKS_S * C,
- struct tapemetadata * mdat,
- const uint8_t tapehash[32], const char * tapename, int quiet)
- {
- uint8_t hbuf[32];
- uint8_t * mbuf;
- size_t mdlen;
- /* Read the tape metadata. */
- switch (storage_read_file_alloc(S, &mbuf, &mdlen, 'm', tapehash)) {
- case -1:
- /* Internal error. */
- goto err1;
- case 1:
- /* ENOENT. */
- goto notpresent;
- case 2:
- /* Corrupt metadata file. */
- goto corrupt;
- }
- /* Adjust chunk statistics. */
- if (C != NULL)
- chunks_stats_extrastats(C, mdlen);
- /* Parse the tape metadata. */
- switch (multitape_metadata_dec(mdat, mbuf, mdlen)) {
- case 1:
- /* Metadata is corrupt. */
- goto corrupt1;
- case -1:
- /* Error. */
- goto err2;
- }
- /* Store metadata length. */
- mdat->metadatalen = mdlen;
- /* Free tape metadata. */
- free(mbuf);
- /*
- * Make sure the name stored in the archive metadata matches the
- * name of the metadata file.
- */
- if (crypto_hash_data(CRYPTO_KEY_HMAC_NAME,
- (uint8_t *)mdat->name, strlen(mdat->name), hbuf))
- goto err0;
- if (crypto_verify_bytes(tapehash, hbuf, 32))
- goto corrupt;
- /* Success! */
- return (0);
- corrupt1:
- free(mbuf);
- corrupt:
- if (quiet == 0) {
- if (tapename)
- warn0("Archive metadata is corrupt: %s", tapename);
- else
- warn0("Archive metadata file is corrupt");
- }
- /* File is corrupt. */
- return (2);
- notpresent:
- if (quiet == 0) {
- if (tapename)
- warn0("Archive does not exist: %s", tapename);
- else
- warn0("Cannot read archive metadata file");
- }
- /* ENOENT. */
- return (1);
- err2:
- free(mbuf);
- err1:
- warnp("Error reading archive metadata");
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * multitape_metadata_get_byhash(S, C, mdat, tapehash, quiet):
- * Read and parse metadata for the archive for which the metadata file is
- * named ${tapehash}. If ${C} is non-NULL, call chunks_stats_extrastats on
- * ${C} and the length of the metadata file. If ${quiet}, don't print any
- * warnings about corrupt or missing files. Return 0 on success, 1 if the
- * metadata file does not exist, 2 if the metadata file is corrupt, or -1 on
- * error.
- */
- int
- multitape_metadata_get_byhash(STORAGE_R * S, CHUNKS_S * C,
- struct tapemetadata * mdat, const uint8_t tapehash[32], int quiet)
- {
- /* Let multitape_metadata_get do the work. */
- return (multitape_metadata_get(S, C, mdat, tapehash, NULL, quiet));
- }
- /**
- * multitape_metadata_get_byname(S, C, mdat, tapename, quiet):
- * Read and parse metadata for the archive named ${tapename}. If ${C} is
- * non-NULL, call chunks_stats_extrastats on ${C} and the length of the
- * metadata file. If ${quiet}, don't print any warnings about corrupt or
- * missing files. Return 0 on success, 1 if the metadata file does not
- * exist, 2 if the metadata file is corrupt, or -1 on error.
- */
- int
- multitape_metadata_get_byname(STORAGE_R * S, CHUNKS_S * C,
- struct tapemetadata * mdat, const char * tapename, int quiet)
- {
- uint8_t hbuf[32];
- /* Compute the hash of the tape name. */
- if (crypto_hash_data(CRYPTO_KEY_HMAC_NAME,
- (const uint8_t *)tapename, strlen(tapename), hbuf))
- goto err0;
- /* Let multitape_metadata_get do the work. */
- return (multitape_metadata_get(S, C, mdat, hbuf, tapename, quiet));
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * multitape_metadata_free(mdat):
- * Free pointers within ${mdat} (but not ${mdat} itself).
- */
- void
- multitape_metadata_free(struct tapemetadata * mdat)
- {
- int arg;
- /* Behave consistently with free(NULL). */
- if (mdat == NULL)
- return;
- /* Free arguments. */
- for (arg = 0; arg < mdat->argc; arg++)
- free(mdat->argv[arg]);
- free(mdat->argv);
- /* Free archive name. */
- free(mdat->name);
- }
- /**
- * multitape_metadata_recrypt(obuf, obuflen, nbuf, nbuflen):
- * Decrypt and re-encrypt the provided metadata file.
- */
- int
- multitape_metadata_recrypt(uint8_t * obuf, size_t obuflen, uint8_t ** nbuf,
- size_t * nbuflen)
- {
- struct tapemetadata mdat;
- /* Parse the metadata file. */
- switch (multitape_metadata_dec(&mdat, obuf, obuflen)) {
- case 1:
- warn0("Metadata file is corrupt");
- goto err0;
- case -1:
- warnp("Error parsing metadata file");
- goto err0;
- }
- /* Construct a new metadata file. */
- if (multitape_metadata_enc(&mdat, nbuf, nbuflen)) {
- warnp("Error constructing metadata file");
- goto err1;
- }
- /* Free the metadata we parsed. */
- multitape_metadata_free(&mdat);
- /* Success! */
- return (0);
- err1:
- multitape_metadata_free(&mdat);
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * multitape_metadata_delete(S, C, mdat):
- * Delete specified metadata file; ${mdat} must have been initialized by a
- * call to multitape_metadata_get_by(hash|name). Call
- * chunks_delete_extrastats on ${C} and the metadata file length.
- */
- int
- multitape_metadata_delete(STORAGE_D * S, CHUNKS_D * C,
- struct tapemetadata * mdat)
- {
- uint8_t hbuf[32];
- if (crypto_hash_data(CRYPTO_KEY_HMAC_NAME,
- (uint8_t *)mdat->name, strlen(mdat->name), hbuf))
- goto err0;
- if (storage_delete_file(S, 'm', hbuf))
- goto err0;
- chunks_delete_extrastats(C, mdat->metadatalen);
- /* Success! */
- return (0);
- err0:
- /* Failure! */
- return (-1);
- }
|