Refactor rhizome extract manifest into more useful methods

This commit is contained in:
Jeremy Lakeman 2013-01-02 11:12:15 +10:30
parent a492c05d64
commit 6c7ba438a3
5 changed files with 115 additions and 161 deletions

View File

@ -1232,32 +1232,58 @@ int app_rhizome_extract_manifest(int argc, const char *const *argv, const struct
return -1;
if (rhizome_opendb() == -1)
return -1;
/* Extract the manifest from the database */
rhizome_manifest *m = NULL;
int ret = rhizome_retrieve_manifest(manifestid, &m);
switch (ret) {
case 0: ret = 1; break;
case 1: ret = 0;
if (manifestpath && strcmp(manifestpath, "-") == 0) {
cli_puts("manifest");
cli_delim(":");
cli_write(m->manifestdata, m->manifest_all_bytes);
cli_delim("\n");
} else if (manifestpath) {
/* If the manifest has been read in from database, the blob is there,
and we can lie and say we are finalised and just want to write it
out. TODO: really should have a dirty/clean flag, so that write
works if clean but not finalised. */
m->finalised=1;
if (rhizome_write_manifest_file(m, manifestpath) == -1)
ret = -1;
}
break;
case -1: break;
default: ret = WHYF("Unsupported return value %d", ret); break;
unsigned char manifest_id[RHIZOME_MANIFEST_ID_BYTES];
if (fromhexstr(manifest_id, manifestid, RHIZOME_MANIFEST_ID_BYTES) == -1)
return WHY("Invalid manifest ID");
char manifestIdUpper[RHIZOME_MANIFEST_ID_STRLEN + 1];
tohex(manifestIdUpper, manifest_id, RHIZOME_MANIFEST_ID_BYTES);
rhizome_manifest *m = rhizome_new_manifest();
if (m == NULL)
return WHY("Out of manifests");
int ret = rhizome_retrieve_manifest(manifestIdUpper, m);
if (ret==0){
if (m->errors){
// fail if the manifest is invalid?
}
rhizome_find_manifest_secret(m);
const char *blob_service = rhizome_manifest_get(m, "service", NULL, 0);
cli_puts("service"); cli_delim(":"); cli_puts(blob_service); cli_delim("\n");
cli_puts("manifestid"); cli_delim(":"); cli_puts(manifestIdUpper); cli_delim("\n");
cli_puts("version"); cli_delim(":"); cli_printf("%lld", m->version); cli_delim("\n");
cli_puts("inserttime"); cli_delim(":"); cli_printf("%lld", m->inserttime); cli_delim("\n");
if (m->haveSecret) {
cli_puts(".author"); cli_delim(":"); cli_puts(alloca_tohex_sid(m->author)); cli_delim("\n");
}
cli_puts(".readonly"); cli_delim(":"); cli_printf("%d", m->haveSecret?0:1); cli_delim("\n");
cli_puts("filesize"); cli_delim(":"); cli_printf("%lld", (long long) m->fileLength); cli_delim("\n");
if (m->fileLength != 0) {
cli_puts("filehash"); cli_delim(":"); cli_puts(m->fileHexHash); cli_delim("\n");
}
if (manifestpath && strcmp(manifestpath, "-") == 0) {
cli_puts("manifest");
cli_delim(":");
cli_write(m->manifestdata, m->manifest_all_bytes);
cli_delim("\n");
} else if (manifestpath) {
/* If the manifest has been read in from database, the blob is there,
and we can lie and say we are finalised and just want to write it
out. TODO: really should have a dirty/clean flag, so that write
works if clean but not finalised. */
m->finalised=1;
if (rhizome_write_manifest_file(m, manifestpath) == -1)
ret = -1;
}
}
if (m)
rhizome_manifest_free(m);
rhizome_manifest_free(m);
return ret;
}

View File

@ -117,6 +117,7 @@ typedef struct rhizome_manifest {
unsigned char signatureTypes[MAX_MANIFEST_VARS];
int errors; /* if non-zero, then manifest should not be trusted */
time_ms_t inserttime;
/* Set non-zero after variables have been packed and
signature blocks appended.
@ -303,9 +304,10 @@ int rhizome_manifest_to_bar(rhizome_manifest *m,unsigned char *bar);
long long rhizome_bar_version(unsigned char *bar);
unsigned long long rhizome_bar_bidprefix_ll(unsigned char *bar);
int rhizome_list_manifests(const char *service, const char *sender_sid, const char *recipient_sid, int limit, int offset);
int rhizome_retrieve_manifest(const char *manifestid, rhizome_manifest **mp);
int rhizome_retrieve_manifest(const char *manifestid, rhizome_manifest *m);
int rhizome_retrieve_file(const char *fileid, const char *filepath,
const unsigned char *key);
int rhizome_find_manifest_secret(rhizome_manifest *m);
#define RHIZOME_DONTVERIFY 0
#define RHIZOME_VERIFY 1

View File

@ -292,6 +292,23 @@ int rhizome_read_manifest_file(rhizome_manifest *m, const char *filename, int bu
RETURN(0);
}
/* Find the author and bundle secret */
int rhizome_find_manifest_secret(rhizome_manifest *m)
{
int ret;
if (is_sid_any(m->author)) {
ret=rhizome_find_bundle_author(m);
}else{
// should we try this first, then fall back to searching the keyring?
ret=rhizome_extract_privatekey(m);
if (ret){
INFOF("bundle author=%s is not in keyring -- ignored", alloca_tohex_sid(m->author));
memset(m->author, 0, sizeof m->author);
}
}
return ret;
}
int rhizome_hash_file(rhizome_manifest *m,const char *filename,char *hash_out)
{
/* Gnarf! NaCl's crypto_hash() function needs the whole file passed in in one

View File

@ -278,6 +278,14 @@ int rhizome_find_bundle_author(rhizome_manifest *m)
m->haveSecret=1;
if (config.debug.rhizome)
DEBUGF("found bundle author sid=%s", alloca_tohex_sid(m->author));
// if this bundle is already in the database, update the author.
if (m->inserttime){
const char *id = rhizome_manifest_get(m, "id", NULL, 0);
if (sqlite_exec_void("UPDATE MANIFESTS SET author='%s' WHERE id='%s';", alloca_tohex_sid(m->author), id) == -1)
WARN("Error updating MANIFESTS author column");
}
RETURN(0); // bingo
}
}

View File

@ -1205,151 +1205,52 @@ int rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found,
/* Retrieve a manifest from the database, given its manifest ID.
*
* Returns 1 if manifest is found (if mp != NULL then a new manifest struct is allocated, made
* finalisable and * assigned to *mp, caller is responsible for freeing).
* Returns 0 if manifest is not found (*mp is unchanged).
* Returns -1 on error (*mp is unchanged).
* Returns 0 if manifest is found
* Returns 1 if manifest is not found
* Returns -1 on error
* Caller is responsible for allocating and freeing rhizome_manifest
*/
int rhizome_retrieve_manifest(const char *manifestid, rhizome_manifest **mp)
{
unsigned char manifest_id[RHIZOME_MANIFEST_ID_BYTES];
if (fromhexstr(manifest_id, manifestid, RHIZOME_MANIFEST_ID_BYTES) == -1)
return WHY("Invalid manifest ID");
int rhizome_retrieve_manifest(const char *manifestid, rhizome_manifest *m){
int ret=0;
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT;
sqlite3_stmt *statement = sqlite_prepare(&retry, "SELECT id, manifest, version, inserttime, author FROM manifests WHERE id = ?");
sqlite3_stmt *statement = sqlite_prepare(&retry, "SELECT manifest, version, inserttime, author FROM manifests WHERE id = ?");
if (!statement)
return -1;
char manifestIdUpper[RHIZOME_MANIFEST_ID_STRLEN + 1];
tohex(manifestIdUpper, manifest_id, RHIZOME_MANIFEST_ID_BYTES);
sqlite3_bind_text(statement, 1, manifestIdUpper, -1, SQLITE_STATIC);
int ret = 0;
rhizome_manifest *m = NULL;
while (sqlite_step_retry(&retry, statement) == SQLITE_ROW) {
if (!( sqlite3_column_count(statement) == 5
&& sqlite3_column_type(statement, 0) == SQLITE_TEXT
&& sqlite3_column_type(statement, 1) == SQLITE_BLOB
&& sqlite3_column_type(statement, 2) == SQLITE_INTEGER
&& sqlite3_column_type(statement, 3) == SQLITE_INTEGER
&& ( sqlite3_column_type(statement, 4) == SQLITE_TEXT
|| sqlite3_column_type(statement, 4) == SQLITE_NULL
)
)) {
ret = WHY("Incorrect statement column");
break;
sqlite3_bind_text(statement, 1, manifestid, -1, SQLITE_STATIC);
if (sqlite_step_retry(&retry, statement) == SQLITE_ROW){
const char *manifestblob = (char *) sqlite3_column_blob(statement, 0);
long long q_version = (long long) sqlite3_column_int64(statement, 1);
long long q_inserttime = (long long) sqlite3_column_int64(statement, 2);
const char *q_author = (const char *) sqlite3_column_text(statement, 3);
size_t manifestblobsize = sqlite3_column_bytes(statement, 0); // must call after sqlite3_column_blob()
if (rhizome_read_manifest_file(m, manifestblob, manifestblobsize)){
ret=WHYF("Manifest %s exists but is invalid", manifestid);
goto done;
}
const char *q_manifestid = (const char *) sqlite3_column_text(statement, 0);
const char *manifestblob = (char *) sqlite3_column_blob(statement, 1);
long long q_version = (long long) sqlite3_column_int64(statement, 2);
long long q_inserttime = (long long) sqlite3_column_int64(statement, 3);
const char *q_author = (const char *) sqlite3_column_text(statement, 4);
size_t manifestblobsize = sqlite3_column_bytes(statement, 1); // must call after sqlite3_column_blob()
if (mp) {
m = rhizome_new_manifest();
if (m == NULL) {
WARNF("MANIFESTS row id=%s has invalid manifest blob -- skipped", q_manifestid);
ret = WHY("Out of manifests");
} else if (rhizome_read_manifest_file(m, manifestblob, manifestblobsize) == -1) {
WARNF("MANIFESTS row id=%s has invalid manifest blob -- skipped", q_manifestid);
ret = WHY("Invalid manifest blob from database");
} else {
ret = 1;
memcpy(m->cryptoSignPublic, manifest_id, RHIZOME_MANIFEST_ID_BYTES);
const char *blob_service = rhizome_manifest_get(m, "service", NULL, 0);
if (blob_service == NULL)
ret = WHY("Manifest is missing 'service' field");
long long filesizeq = rhizome_manifest_get_ll(m, "filesize");
if (filesizeq == -1)
ret = WHY("Manifest is missing 'filesize' field");
else
m->fileLength = filesizeq;
const char *blob_filehash = rhizome_manifest_get(m, "filehash", NULL, 0);
if (m->fileLength != 0) {
if (blob_filehash == NULL)
ret = WHY("Manifest is missing 'filehash' field");
else {
memcpy(m->fileHexHash, blob_filehash, RHIZOME_FILEHASH_STRLEN + 1);
}
} else {
if (blob_filehash != NULL)
WARN("Manifest contains spurious 'filehash' field -- ignored");
m->fileHexHash[0] = '\0';
}
long long blob_version = rhizome_manifest_get_ll(m, "version");
if (blob_version == -1)
ret = WHY("Manifest is missing 'version' field");
else
m->version = blob_version;
int read_only = 1;
if (q_author == NULL) {
// Search for the author in the keyring.
// TODO optimise: if manifest 'sender' is set, try that identity first.
int result = rhizome_find_bundle_author(m);
switch (result) {
case -1:
ret = WHY("Error searching keyring for bundle author");
break;
case 0:
read_only = 0;
if (sqlite_exec_void("UPDATE MANIFESTS SET author='%s' WHERE id='%s';", alloca_tohex_sid(m->author), manifestIdUpper) == -1)
WHY("Error updating MANIFESTS author column");
break;
}
} else if (stowSid(m->author, 0, q_author) == -1) {
WARNF("MANIFESTS row id=%s contains invalid author=%s -- ignored", q_manifestid, alloca_str_toprint(q_author));
} else {
// If the AUTHOR column contains a valid SID, then it means that author verification has
// already been done (either implicitly when the bundle was added locally, or explicitly
// when rhizome_find_bundle_author() was called in the case above), so we represent this
// bundle as writable if the author is present in the keyring and possesses a Rhizome
// Secret.
int result = rhizome_find_secret(m->author, NULL, NULL);
switch (result) {
case -1:
ret = WHY("Error extracting manifest private key");
break;
case 0:
read_only = 0;
break;
default:
INFOF("bundle author=%s is not in keyring -- ignored", q_author);
memset(m->author, 0, sizeof m->author); // do not output ".author" field
break;
}
}
if (ret == 1) {
cli_puts("service"); cli_delim(":");
cli_puts(blob_service); cli_delim("\n");
cli_puts("manifestid"); cli_delim(":");
cli_puts(q_manifestid); cli_delim("\n");
cli_puts("version"); cli_delim(":");
cli_printf("%lld", q_version); cli_delim("\n");
cli_puts("inserttime"); cli_delim(":");
cli_printf("%lld", q_inserttime); cli_delim("\n");
if (!is_sid_any(m->author)) {
cli_puts(".author"); cli_delim(":");
cli_puts(alloca_tohex_sid(m->author)); cli_delim("\n");
}
cli_puts(".readonly"); cli_delim(":");
cli_printf("%d", read_only); cli_delim("\n");
cli_puts("filesize"); cli_delim(":");
cli_printf("%lld", (long long) m->fileLength); cli_delim("\n");
if (m->fileLength != 0) {
cli_puts("filehash"); cli_delim(":");
cli_puts(m->fileHexHash); cli_delim("\n");
}
// Could write the manifest blob to the CLI output here, but that would require the output to
// support byte[] fields as well as String fields.
}
}
if (q_author){
if (stowSid(m->author, 0, q_author) == -1)
WARNF("Manifest %s contains invalid author=%s -- ignored", manifestid, alloca_str_toprint(q_author));
}
break;
if (m->version!=q_version)
WARNF("Version mismatch, manifest is %lld, database is %lld", m->version, q_version);
m->inserttime = q_inserttime;
}else{
ret=1;
}
done:
sqlite3_finalize(statement);
if (mp && ret == 1)
*mp = m;
return ret;
return ret;
}
/* Retrieve a file from the database, given its file hash.
*
* Returns 0 if file is valid, contents are written to filepath if given.