mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-03-22 03:45:22 +00:00
Refactor rhizome extract manifest into more useful methods
This commit is contained in:
parent
a492c05d64
commit
6c7ba438a3
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user