Refactor rhizome_add_manifest()

This commit is contained in:
Andrew Bettison 2012-04-11 18:40:10 +09:30
parent f10e9c15ca
commit a87a78536c
2 changed files with 133 additions and 73 deletions

View File

@ -678,7 +678,7 @@ int app_rhizome_list(int argc, char **argv, struct command_line_option *o)
return -1;
rhizome_datastore_path = serval_instancepath();
rhizome_opendb();
rhizome_list_manifests(0, 0);
return rhizome_list_manifests(0, 0);
}
/* NULL marks ends of command structure.

204
rhizome.c
View File

@ -21,20 +21,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "rhizome.h"
#include <stdlib.h>
/* Import a bundle from the inbox folder.
Check that the manifest prototype is valid, and if so, complete it, and sign it if required and possible.
/* Import a bundle from the inbox folder. The bundle is contained a pair of files, one containing
the manifest and the optional other containing the payload.
Note that bundles can either be an ordinary bundle, or a group description.
Group specifications are simply manifests that have the "isagroup" variable set.
Groups get stored in the manifests table AND a reference included in the
grouplist table.
Groups are allowed to be listed as being members of other groups.
This allows a nested, i.e., multi-level group heirarchy where sub-groups will only
typically be discovered by joining the parent group. Probably not a bad way to do
things.
The file should be included in the specified rhizome groups, if possible.
(some groups may be closed groups that we do not have the private key for.)
The logic is all in rhizome_add_manifest(). This function just wraps that function and manages
file and object buffers and lifetimes.
*/
int rhizome_bundle_import(rhizome_manifest *m_in,char *bundle,char *groups[], int ttl,
@ -71,92 +62,160 @@ int rhizome_bundle_import(rhizome_manifest *m_in,char *bundle,char *groups[], in
ret = WHY("Could not write manifest file.");
}
/* If the manifest was allocated in this function, then this function is responsible for freeing
* it */
/* If the manifest structure was allocated in this function, then this function is responsible for
freeing it */
if (!m_in)
rhizome_manifest_free(m);
return ret;
}
int rhizome_add_manifest(rhizome_manifest *m, const char *filename, char *groups[], int ttl, int verifyP, int checkFileP, int signP)
{
char *buffer;
char hexhash[SHA512_DIGEST_STRING_LENGTH];
/* Add a manifest/payload pair ("bundle") to the rhizome data store.
/* Keep associated file name handy for later */
Fills in any missing manifest fields (generating a new, random manifest ID if necessary),
optionally performs consistency checks (see below), adds the manifest to the given groups (for
which private keys must be held), optionally signs it, and inserts the manifest and payload into
the rhizome store unless the store already contains a manifest with the same ID and a higher
version number or an identical manifest (in which case the stored version number is bumped to the
maximum of the two).
This function is called in three different situations:
(a) when the user injects a file (with or without a complete manifest) into rhizome,
(b) when a manifest is received via the mesh and the file is already present in the rhizome store,
(c) when a file is received via the mesh for a manifest that was received previously.
The following arguments distinguish these situations:
ttl
- In case (a) ttl will be typically set to the initial (maximum?) TTL for a manifest. In
cases (b) and (c) ttl will be the TTL of the received manifest decremented by one. This
function clamps the supplied value into the legal range 0..255, so callers need not perform
range checking after decrement.
verifyP
- If set, then checks that no signature verifications failed when the manifest was loaded.
(If checkFileP is given, then also checks that the payload and manifest are consistent.)
This is used in case (a) if the user provided a manifest file, and always for cases (b) and
(c). It ensures the integrity of the received/provided manifest, and ensures that the
received/provided payload is actually the one that the manifest belongs to. If verifyP is
false, then the new manifest will be overwritten with the correct values for the payload.
checkFileP
- If set then checks that the payload file is readable, and will cause verifyP to also check
that the payload matches the values in the manifest, specifically length and content hash.
This is always used in cases (a) and (c), but not in case (b) because in that case, the
file's contents with the given file hash are known to be already in the database.
signP
- If set, then signs the manifest after all its fields have been filled in. Only used in case
(a), because in cases (b) and (c) the manifest has already been signed, since it is already
on the air.
A bundle can either be an ordinary manifest-payload pair, or a group description.
- Group descriptions are manifests with no payload that have the "isagroup" variable set. They
get stored in the manifests table AND a reference is added to the grouplist table. Any
manifest, including any group manifest, may be a member of zero or one group. This allows a
nested, i.e., multi-level group hierarchy where sub-groups will only typically be discovered
by joining the parent group.
*/
int rhizome_add_manifest(rhizome_manifest *m,
const char *filename,
char *groups[],
int ttl,
int verifyP, // verify that file's hash is consistent with manifest
int checkFileP,
int signP
)
{
char *id = NULL;
char hexhash[SHA512_DIGEST_STRING_LENGTH];
int verifyErrors = 0;
/* Keep payload file name handy for later */
m->dataFileName = strdup(filename);
/* Store time to live */
/* Store time to live, clamped to within legal range */
m->ttl = ttl < 0 ? 0 : ttl > 254 ? 254 : ttl;
/* Check file is accessible and discover its length */
if (checkFileP || verifyP) {
/* Check payload file is accessible and discover its length, then check that it matches
the file size stored in the manifest */
if (checkFileP) {
struct stat stat;
if (lstat(filename,&stat))
return WHY("Could not stat() associated file");
return WHY("Could not stat() payload file");
m->fileLength = stat.st_size;
long long mfilesize = rhizome_manifest_get_ll(m, "filesize");
if (mfilesize != -1 && mfilesize != m->fileLength)
++verifyErrors;
}
if (checkFileP || signP) {
/* Compute hash of payload unless we know verification has already failed */
if (checkFileP ? !(verifyP && (m->errors || verifyErrors)) : signP) {
if (rhizome_hash_file(filename, hexhash))
return WHY("Could not hash file.");
memcpy(&m->fileHexHash[0], &hexhash[0], SHA512_DIGEST_STRING_LENGTH);
m->fileHashedP = 1;
}
/* Check that paylod hash matches manifest */
if (checkFileP) {
const char *mhexhash = rhizome_manifest_get(m, "filehash", NULL, 0);
if (mhexhash && strcmp(hexhash, mhexhash))
++verifyErrors;
}
/* If any signature errors were encountered on loading, or manifest is inconsistent with payload,
then bail out now. */
if (verifyP) {
/* Make sure hashes match.
Make sure that no signature verification errors were spotted on loading. */
int verifyErrors=0;
char *mhexhash;
if (checkFileP) {
if ((mhexhash=rhizome_manifest_get(m,"filehash",NULL,0))!=NULL)
if (strcmp(hexhash,mhexhash))
verifyErrors++;
}
if (m->errors)
verifyErrors+=m->errors;
if (verifyErrors)
if (verifyErrors || m->errors)
return WHY("Errors encountered verifying bundle manifest");
}
else {
if (!(buffer = rhizome_manifest_get(m, "id", NULL, 0))) {
/* No bundle id (256 bit random string being a public key in the NaCl CryptoSign crypto system),
so create one, and keep the private key handy. */
printf("manifest does not have an id\n");
rhizome_manifest_createid(m);
/* The ID is implicit in transit, but we need to store it in the file,
so that reimporting manifests on receiver nodes works easily.
We might implement something that strips the id variable out of the
manifest when sending it, or some other scheme to avoid sending all
the extra bytes. */
rhizome_manifest_set(m,"id",rhizome_bytes_to_hex(m->cryptoSignPublic,crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES));
} else {
/* An ID was specified, so remember it, and look for the private key if
we have it stowed away */
rhizome_hex_to_bytes(buffer,m->cryptoSignPublic,
crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES*2);
if (!rhizome_find_keypair_bytes(m->cryptoSignPublic,m->cryptoSignSecret))
m->haveSecret=1;
}
rhizome_manifest_set(m,"filehash",hexhash);
if (rhizome_manifest_get(m,"version",NULL,0)==NULL)
/* Version not set, so set one */
rhizome_manifest_set_ll(m,"version", overlay_gettime_ms());
rhizome_manifest_set_ll(m,"first_byte", 0);
rhizome_manifest_set_ll(m,"last_byte", m->fileLength);
/* Fill in the manifest to avoid redundant work by rhizome_manifest_finalise() below */
if (checkFileP) {
rhizome_manifest_set(m, "filehash", hexhash);
rhizome_manifest_set_ll(m, "first_byte", 0);
rhizome_manifest_set_ll(m, "last_byte", m->fileLength);
}
/* Discard if it is older than the most recent known version */
long long storedversion = sqlite_exec_int64(
"SELECT version from manifests where id='%s';",
rhizome_bytes_to_hex(m->cryptoSignPublic, crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES)
);
if (storedversion > rhizome_manifest_get_ll(m, "version"))
return WHY("Newer version exists");
/* Supply manifest version number if missing, so we can do the version check below */
if (rhizome_manifest_get(m, "version", NULL, 0) == NULL) {
rhizome_manifest_set_ll(m, "version", overlay_gettime_ms());
}
/* Check if a manifest is already stored for the same file with the same details, except version
number. This catches the case of "dna rhizome add file <filename>" on the same file more than
once. (Debounce!) */
/* If the manifest already has an ID, look to see if we possess its private key */
if ((id = rhizome_manifest_get(m, "id", NULL, 0))) {
rhizome_hex_to_bytes(id, m->cryptoSignPublic, crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES*2);
if (!rhizome_find_keypair_bytes(m->cryptoSignPublic, m->cryptoSignSecret))
m->haveSecret=1;
}
/* Discard the new manifest it is older than the most recent known version with the same ID */
if (id) {
long long storedversion = sqlite_exec_int64("SELECT version from manifests where id='%s';", id);
if (storedversion > rhizome_manifest_get_ll(m, "version")) {
return WHY("Newer version exists");
}
} else {
/* The manifest had no ID (256 bit random string being a public key in the NaCl CryptoSign
crypto system), so create one. */
printf("manifest does not have an id\n");
rhizome_manifest_createid(m);
/* The ID is implicit in transit, but we need to store it in the file, so that reimporting
manifests on receiver nodes works easily. We might implement something that strips the id
variable out of the manifest when sending it, or some other scheme to avoid sending all the
extra bytes. */
id = rhizome_bytes_to_hex(m->cryptoSignPublic, crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
rhizome_manifest_set(m, "id", id);
}
/* Add group memberships */
if (groups) {
int i;
@ -164,6 +223,7 @@ int rhizome_add_manifest(rhizome_manifest *m, const char *filename, char *groups
rhizome_manifest_add_group(m, groups[i]);
}
/* Finish completing the manifest */
if (rhizome_manifest_finalise(m,signP))
return WHY("Failed to finalise manifest.\n");