Merge branch 'naf4' into 'development'

Big refactor of Rhizome author and bundle secret handling.
This commit is contained in:
Andrew Bettison 2013-11-07 00:42:42 +10:30
commit e00c945fd1
21 changed files with 1405 additions and 756 deletions

View File

@ -1375,17 +1375,19 @@ int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_context *co
keyring_free(keyring);
return WHY("Existing manifest is a journal");
}
if (bskhex)
rhizome_apply_bundle_secret(m, &bsk);
if (m->service == NULL)
rhizome_manifest_set_service(m, RHIZOME_SERVICE_FILE);
if (rhizome_fill_manifest(m, filepath, *authorSidHex ? &authorSid : NULL, bskhex ? &bsk : NULL)){
if (rhizome_fill_manifest(m, filepath, *authorSidHex ? &authorSid : NULL)) {
rhizome_manifest_free(m);
keyring_free(keyring);
return -1;
}
if (journal){
if (rhizome_append_journal_file(m, bskhex?&bsk:NULL, 0, filepath)){
if (rhizome_append_journal_file(m, 0, filepath)){
rhizome_manifest_free(m);
keyring_free(keyring);
return -1;
@ -1424,15 +1426,17 @@ int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_context *co
cli_field_name(context, "manifestid", ":");
cli_put_string(context, alloca_tohex_rhizome_bid_t(mout->cryptoSignPublic), "\n");
}
assert(m->haveSecret);
{
char secret[RHIZOME_BUNDLE_KEY_STRLEN + 1];
rhizome_bytes_to_hex_upper(mout->cryptoSignSecret, secret, RHIZOME_BUNDLE_KEY_BYTES);
cli_field_name(context, ".secret", ":");
cli_put_string(context, secret, "\n");
}
if (m->has_author) {
assert(mout->authorship != AUTHOR_LOCAL);
if (mout->authorship == AUTHOR_AUTHENTIC) {
cli_field_name(context, ".author", ":");
cli_put_string(context, alloca_tohex_sid_t(m->author), "\n");
cli_put_string(context, alloca_tohex_sid_t(mout->author), "\n");
}
if (mout->has_bundle_key) {
cli_field_name(context, "BK", ":");
@ -1705,8 +1709,9 @@ int app_rhizome_extract(const struct cli_parsed *parsed, struct cli_context *con
ret = rhizome_retrieve_manifest(&bid, m);
if (ret==0){
// ignore errors
rhizome_extract_privatekey(m, bskhex ? &bsk : NULL);
if (bskhex)
rhizome_apply_bundle_secret(m, &bsk);
rhizome_authenticate_author(m);
if (m->service) {
cli_field_name(context, "service", ":");
@ -1719,6 +1724,9 @@ int app_rhizome_extract(const struct cli_parsed *parsed, struct cli_context *con
rhizome_bytes_to_hex_upper(m->cryptoSignSecret, secret, RHIZOME_BUNDLE_KEY_BYTES);
cli_field_name(context, ".secret", ":");
cli_put_string(context, secret, "\n");
}
assert(m->authorship != AUTHOR_LOCAL);
if (m->authorship == AUTHOR_AUTHENTIC) {
cli_field_name(context, ".author", ":");
cli_put_string(context, alloca_tohex_sid_t(m->author), "\n");
}
@ -1742,8 +1750,7 @@ int app_rhizome_extract(const struct cli_parsed *parsed, struct cli_context *con
if (ret==0 && m->filesize != 0 && filepath && *filepath){
if (extract){
// Save the file, implicitly decrypting if required.
// TODO, this may cause us to search for an author a second time if the above call to rhizome_extract_privatekey failed
retfile = rhizome_extract_file(m, filepath, bskhex?&bsk:NULL);
retfile = rhizome_extract_file(m, filepath);
}else{
// Save the file without attempting to decrypt
int64_t length;

View File

@ -88,5 +88,6 @@ void dump_stack(int log_level);
#define OUT() fd_func_exit(__HERE__, &_this_call)
#define RETURN(X) do { OUT(); return (X); } while (0);
#define RETURNNULL do { OUT(); return (NULL); } while (0);
#define RETURNVOID do { OUT(); return; } while (0);
#endif // __SERVALDNA__FDQUEUE_H

View File

@ -98,6 +98,8 @@ void http_request_init(struct http_request *r, int sockfd)
assert(sockfd != -1);
r->request_header.content_length = CONTENT_LENGTH_UNKNOWN;
r->request_content_remaining = CONTENT_LENGTH_UNKNOWN;
r->response.header.content_length = CONTENT_LENGTH_UNKNOWN;
r->response.header.resource_length = CONTENT_LENGTH_UNKNOWN;
r->alarm.stats = &http_server_stats;
r->alarm.function = http_server_poll;
if (r->idle_timeout == 0)
@ -162,31 +164,85 @@ void http_request_finalise(struct http_request *r)
r->phase = DONE;
}
#define _SEP (1 << 0)
#define _BND (1 << 1)
#define _BASE64 (1 << 6)
#define _MASK64 ((1 << 6) - 1)
#define _SEP (1 << 7)
#define _BND (1 << 8)
uint8_t http_ctype[256] = {
['0'] = _BND, ['1'] = _BND, ['2'] = _BND, ['3'] = _BND, ['4'] = _BND,
['5'] = _BND, ['6'] = _BND, ['7'] = _BND, ['8'] = _BND, ['9'] = _BND,
['A'] = _BND, ['B'] = _BND, ['C'] = _BND, ['D'] = _BND, ['E'] = _BND,
['F'] = _BND, ['G'] = _BND, ['H'] = _BND, ['I'] = _BND, ['J'] = _BND,
['K'] = _BND, ['L'] = _BND, ['M'] = _BND, ['N'] = _BND, ['O'] = _BND,
['P'] = _BND, ['Q'] = _BND, ['R'] = _BND, ['S'] = _BND, ['T'] = _BND,
['U'] = _BND, ['V'] = _BND, ['W'] = _BND, ['X'] = _BND, ['Y'] = _BND,
['Z'] = _BND,
['a'] = _BND, ['b'] = _BND, ['c'] = _BND, ['d'] = _BND, ['e'] = _BND,
['f'] = _BND, ['g'] = _BND, ['h'] = _BND, ['i'] = _BND, ['j'] = _BND,
['k'] = _BND, ['l'] = _BND, ['m'] = _BND, ['n'] = _BND, ['o'] = _BND,
['p'] = _BND, ['q'] = _BND, ['r'] = _BND, ['s'] = _BND, ['t'] = _BND,
['u'] = _BND, ['v'] = _BND, ['w'] = _BND, ['x'] = _BND, ['y'] = _BND,
['z'] = _BND,
['+'] = _BND, ['-'] = _BND, ['.'] = _BND, ['/'] = _BND, [':'] = _BND,
uint16_t http_ctype[256] = {
['A'] = _BND | _BASE64 | 0,
['B'] = _BND | _BASE64 | 1,
['C'] = _BND | _BASE64 | 2,
['D'] = _BND | _BASE64 | 3,
['E'] = _BND | _BASE64 | 4,
['F'] = _BND | _BASE64 | 5,
['G'] = _BND | _BASE64 | 6,
['H'] = _BND | _BASE64 | 7,
['I'] = _BND | _BASE64 | 8,
['J'] = _BND | _BASE64 | 9,
['K'] = _BND | _BASE64 | 10,
['L'] = _BND | _BASE64 | 11,
['M'] = _BND | _BASE64 | 12,
['N'] = _BND | _BASE64 | 13,
['O'] = _BND | _BASE64 | 14,
['P'] = _BND | _BASE64 | 15,
['Q'] = _BND | _BASE64 | 16,
['R'] = _BND | _BASE64 | 17,
['S'] = _BND | _BASE64 | 18,
['T'] = _BND | _BASE64 | 19,
['U'] = _BND | _BASE64 | 20,
['V'] = _BND | _BASE64 | 21,
['W'] = _BND | _BASE64 | 22,
['X'] = _BND | _BASE64 | 23,
['Y'] = _BND | _BASE64 | 24,
['Z'] = _BND | _BASE64 | 25,
['a'] = _BND | _BASE64 | 26,
['b'] = _BND | _BASE64 | 27,
['c'] = _BND | _BASE64 | 28,
['d'] = _BND | _BASE64 | 29,
['e'] = _BND | _BASE64 | 30,
['f'] = _BND | _BASE64 | 31,
['g'] = _BND | _BASE64 | 32,
['h'] = _BND | _BASE64 | 33,
['i'] = _BND | _BASE64 | 34,
['j'] = _BND | _BASE64 | 35,
['k'] = _BND | _BASE64 | 36,
['l'] = _BND | _BASE64 | 37,
['m'] = _BND | _BASE64 | 38,
['n'] = _BND | _BASE64 | 39,
['o'] = _BND | _BASE64 | 40,
['p'] = _BND | _BASE64 | 41,
['q'] = _BND | _BASE64 | 42,
['r'] = _BND | _BASE64 | 43,
['s'] = _BND | _BASE64 | 44,
['t'] = _BND | _BASE64 | 45,
['u'] = _BND | _BASE64 | 46,
['v'] = _BND | _BASE64 | 47,
['w'] = _BND | _BASE64 | 48,
['x'] = _BND | _BASE64 | 49,
['y'] = _BND | _BASE64 | 50,
['z'] = _BND | _BASE64 | 51,
['0'] = _BND | _BASE64 | 52,
['1'] = _BND | _BASE64 | 53,
['2'] = _BND | _BASE64 | 54,
['3'] = _BND | _BASE64 | 55,
['4'] = _BND | _BASE64 | 56,
['5'] = _BND | _BASE64 | 57,
['6'] = _BND | _BASE64 | 58,
['7'] = _BND | _BASE64 | 59,
['8'] = _BND | _BASE64 | 60,
['9'] = _BND | _BASE64 | 61,
['+'] = _BND | _BASE64 | 62,
['/'] = _BND | _BASE64 | 63,
['='] = _SEP | _BND,
['-'] = _BND,
['.'] = _BND,
[':'] = _BND,
['_'] = _BND,
['('] = _SEP | _BND,
[')'] = _SEP | _BND,
[','] = _SEP | _BND,
['?'] = _SEP | _BND,
['='] = _SEP | _BND,
[' '] = _SEP | _BND,
['\t'] = _SEP,
['<'] = _SEP,
@ -213,6 +269,21 @@ inline int is_http_ctl(char c)
return iscntrl(c);
}
inline int is_base64_digit(char c)
{
return (http_ctype[(unsigned char) c] & _BASE64) != 0;
}
inline int is_base64_pad(char c)
{
return c == '=';
}
inline uint8_t base64_digit(char c)
{
return http_ctype[(unsigned char) c] & _MASK64;
}
inline int is_http_separator(char c)
{
return (http_ctype[(unsigned char) c] & _SEP) != 0;
@ -556,6 +627,150 @@ static unsigned _parse_ranges(struct http_request *r, struct http_range *range,
return i;
}
static int _parse_content_type(struct http_request *r, struct mime_content_type *ct)
{
size_t n = _parse_token(r, ct->type, sizeof ct->type);
if (n == 0)
return 0;
if (n >= sizeof ct->type) {
WARNF("HTTP Content-Type type truncated: %s", alloca_str_toprint(ct->type));
return 0;
}
if (!_skip_literal(r, "/"))
return 0;
n = _parse_token(r, ct->subtype, sizeof ct->subtype);
if (n == 0)
return 0;
if (n >= sizeof ct->subtype) {
WARNF("HTTP Content-Type subtype truncated: %s", alloca_str_toprint(ct->subtype));
return 0;
}
while (_skip_optional_space(r) && _skip_literal(r, ";") && _skip_optional_space(r)) {
const char *start = r->cursor;
if (_skip_literal(r, "charset=")) {
size_t n = _parse_token_or_quoted_string(r, ct->charset, sizeof ct->charset);
if (n == 0)
return 0;
if (n >= sizeof ct->charset) {
WARNF("HTTP Content-Type charset truncated: %s", alloca_str_toprint(ct->charset));
return 0;
}
continue;
}
r->cursor = start;
if (_skip_literal(r, "boundary=")) {
size_t n = _parse_token_or_quoted_string(r, ct->multipart_boundary, sizeof ct->multipart_boundary);
if (n == 0)
return 0;
if (n >= sizeof ct->multipart_boundary) {
WARNF("HTTP Content-Type boundary truncated: %s", alloca_str_toprint(ct->multipart_boundary));
return 0;
}
continue;
}
r->cursor = start;
struct substring param;
if (_skip_token(r, &param) && _skip_literal(r, "=") && _parse_token_or_quoted_string(r, NULL, 0)) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping HTTP Content-Type parameter: %s", alloca_substring_toprint(param));
continue;
}
WARNF("Malformed HTTP Content-Type: %s", alloca_toprint(50, r->cursor, r->end - r->cursor));
return 0;
}
return 1;
}
static size_t _parse_base64(struct http_request *r, char *bin, size_t binsize)
{
uint8_t buf = 0;
size_t digits = 0;
size_t bytes = 0;
for (; !_run_out(r) && is_base64_digit(*r->cursor); _skip_optional_space(r), ++r->cursor) {
if (bytes < binsize) {
uint8_t d = base64_digit(*r->cursor);
switch (digits++ & 3) {
case 0:
buf = d << 2;
break;
case 1:
if (bin)
bin[bytes] = buf | (d >> 4);
++bytes;
buf = d << 4;
break;
case 2:
if (bin)
bin[bytes] = buf | (d >> 2);
++bytes;
buf = d << 6;
break;
case 3:
if (bin)
bin[bytes] = buf | d;
++bytes;
break;
}
}
}
if (digits == 0)
return 0;
if (!_run_out(r) && is_base64_pad(*r->cursor))
++r->cursor;
if (!_run_out(r) && is_base64_pad(*r->cursor))
++r->cursor;
return bytes;
}
static int _parse_authorization_credentials_basic(struct http_request *r, struct http_client_credentials_basic *cred, char *buf, size_t bufsz)
{
size_t n = _parse_base64(r, buf, bufsz - 1); // leave room for NUL terminator on password
assert(n < bufsz); // buffer must be big enough
char *pw = (char *) strnchr(buf, n, ':');
if (pw == NULL)
return 0; // malformed
cred->user = buf;
*pw++ = '\0'; // NUL terminate user
cred->password = pw;
buf[n] = '\0'; // NUL terminate password
return 1;
}
static int _parse_authorization(struct http_request *r, struct http_client_authorization *auth, size_t header_bytes)
{
const char *start = r->cursor;
if (_skip_literal(r, "Basic") && _skip_space(r)) {
size_t bufsz = 5 + header_bytes * 3 / 4; // enough for base64 decoding
char buf[bufsz];
if (_parse_authorization_credentials_basic(r, &auth->credentials.basic, buf, bufsz)) {
auth->scheme = BASIC;
if ( (auth->credentials.basic.user = _reserve_str(r, auth->credentials.basic.user)) == NULL
|| (auth->credentials.basic.password = _reserve_str(r, auth->credentials.basic.password)) == NULL
)
return 0; // error
return 1;
}
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP header: Authorization: %s", alloca_toprint(50, start, header_bytes));
return 0;
}
if (_skip_literal(r, "Digest") && _skip_space(r)) {
if (r->debug_flag && *r->debug_flag)
DEBUG("Ignoring unsupported HTTP Authorization scheme: Digest");
r->cursor += header_bytes;
return 1;
}
struct substring scheme;
if (_skip_token(r, &scheme) && _skip_space(r)) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Unrecognised HTTP Authorization scheme: %s", alloca_toprint(-1, scheme.start, scheme.end - scheme.start));
return 0;
}
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP Authorization header: %s", alloca_toprint(50, r->parsed, r->end - r->parsed));
return 0;
}
static int _parse_quoted_rfc822_time(struct http_request *r, time_t *timep)
{
char datestr[40];
@ -743,6 +958,13 @@ static int http_request_parse_header(struct http_request *r)
_rewind(r);
const char *const sol = r->cursor;
if (_skip_literal_nocase(r, "Content-Length:")) {
if (r->request_header.content_length != CONTENT_LENGTH_UNKNOWN) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP header Content-Length: %s", alloca_toprint(50, sol, r->end - sol));
r->cursor = nextline;
_commit(r);
return 0;
}
_skip_optional_space(r);
http_size_t length;
if (_parse_http_size_t(r, &length) && _skip_optional_space(r) && r->cursor == eol) {
@ -757,56 +979,41 @@ static int http_request_parse_header(struct http_request *r)
}
_rewind(r);
if (_skip_literal_nocase(r, "Content-Type:")) {
_skip_optional_space(r);
struct substring type = substring_NULL;
struct substring subtype = substring_NULL;
char boundary[BOUNDARY_STRING_MAXLEN + 1];
boundary[0] = '\0';
if (_skip_token(r, &type) && _skip_literal(r, "/") && _skip_token(r, &subtype)) {
// Parse zero or more content-type parameters.
for (_skip_optional_space(r); r->cursor < eol && _skip_literal(r, ";"); _skip_optional_space(r)) {
_skip_optional_space(r);
const char *startparam = r->cursor;
if (_skip_literal(r, "boundary=")) {
size_t n = _parse_token_or_quoted_string(r, boundary, sizeof boundary);
if (n == 0 || n >= sizeof boundary || !is_valid_http_boundary_string(boundary))
goto malformed;
continue;
}
// Silently ignore unrecognised parameters (eg, charset=) if they are well formed.
r->cursor = startparam; // partial rewind
if (_skip_token(r, NULL) && _skip_literal(r, "=") && _parse_token_or_quoted_string(r, NULL, 0))
continue;
break;
}
if (r->cursor == eol) {
if (r->request_header.content_type.type[0]) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP header Content-Type: %s", alloca_toprint(50, sol, r->end - sol));
r->cursor = nextline;
_commit(r);
if ( (r->request_header.content_type = _reserve(r, type)) == NULL
|| (r->request_header.content_subtype = _reserve(r, subtype)) == NULL
|| (boundary[0] && (r->request_header.boundary = _reserve_str(r, boundary)) == NULL)
)
return 0; // error
if (r->debug_flag && *r->debug_flag)
DEBUGF("Parsed HTTP request Content-type: %s/%s%s%s",
r->request_header.content_type,
r->request_header.content_subtype,
r->request_header.boundary ? "; boundary=" : "",
r->request_header.boundary ? alloca_str_toprint(r->request_header.boundary) : ""
);
return 0;
}
_skip_optional_space(r);
if ( _parse_content_type(r, &r->request_header.content_type)
&& _skip_optional_space(r)
&& r->cursor == eol
) {
r->cursor = nextline;
_commit(r);
if (r->debug_flag && *r->debug_flag)
DEBUGF("Parsed HTTP request Content-type: %s", alloca_mime_content_type(&r->request_header.content_type));
return 0;
}
goto malformed;
}
_rewind(r);
if (_skip_literal_nocase(r, "Range:")) {
if (r->request_header.content_range_count) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP header Range: %s", alloca_toprint(50, sol, r->end - sol));
r->cursor = nextline;
_commit(r);
return 0;
}
_skip_optional_space(r);
unsigned int n;
if ( _skip_literal(r, "bytes=")
&& (n = _parse_ranges(r, r->request_header.content_ranges, NELS(r->request_header.content_ranges)))
&& _skip_optional_space(r)
&& (r->cursor == eol)
&& r->cursor == eol
) {
r->cursor = nextline;
_commit(r);
@ -826,6 +1033,27 @@ static int http_request_parse_header(struct http_request *r)
goto malformed;
}
_rewind(r);
if (_skip_literal_nocase(r, "Authorization:")) {
if (r->request_header.authorization.scheme != NOAUTH) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP header Authorization: %s", alloca_toprint(50, sol, r->end - sol));
r->cursor = nextline;
_commit(r);
return 0;
}
_skip_optional_space(r);
if ( _parse_authorization(r, &r->request_header.authorization, eol - r->cursor)
&& _skip_optional_space(r)
&& r->cursor == eol
) {
assert(r->request_header.authorization.scheme != NOAUTH);
r->cursor = nextline;
_commit(r);
return 0;
}
goto malformed;
}
_rewind(r);
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipped HTTP request header: %s", alloca_toprint(-1, sol, eol - sol));
r->cursor = nextline;
@ -858,7 +1086,7 @@ static int http_request_start_body(struct http_request *r)
DEBUGF("Malformed HTTP %s request: non-zero Content-Length not allowed", r->verb);
return 400;
}
if (r->request_header.content_type) {
if (r->request_header.content_type.type) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP %s request: Content-Type not allowed", r->verb);
return 400;
@ -871,26 +1099,28 @@ static int http_request_start_body(struct http_request *r)
DEBUGF("Malformed HTTP %s request: missing Content-Length header", r->verb);
return 411;
}
if (r->request_header.content_type == NULL) {
if (r->request_header.content_type.type == NULL) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP %s request: missing Content-Type header", r->verb);
return 400;
}
if ( strcmp(r->request_header.content_type, "multipart") == 0
&& strcmp(r->request_header.content_subtype, "form-data") == 0
if ( strcmp(r->request_header.content_type.type, "multipart") == 0
&& strcmp(r->request_header.content_type.subtype, "form-data") == 0
) {
if ( r->request_header.content_type.multipart_boundary == NULL
|| r->request_header.content_type.multipart_boundary[0] == '\0'
) {
if (r->request_header.boundary == NULL || r->request_header.boundary[0] == '\0') {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP %s request: Content-Type %s/%s missing boundary parameter",
r->verb, r->request_header.content_type, r->request_header.content_subtype);
r->verb, r->request_header.content_type.type, r->request_header.content_type.subtype);
return 400;
}
r->parser = http_request_parse_body_form_data;
r->form_data_state = START;
} else {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Unsupported HTTP %s request: Content-Type %s/%s not supported",
r->verb, r->request_header.content_type, r->request_header.content_subtype);
DEBUGF("Unsupported HTTP %s request: Content-Type %s not supported",
r->verb, alloca_mime_content_type(&r->request_header.content_type));
return 415;
}
}
@ -909,7 +1139,7 @@ static int http_request_start_body(struct http_request *r)
*/
static int _skip_mime_boundary(struct http_request *r)
{
if (!_skip_literal(r, "--") || !_skip_literal(r, r->request_header.boundary))
if (!_skip_literal(r, "--") || !_skip_literal(r, r->request_header.content_type.multipart_boundary))
return 0;
if (_skip_literal(r, "--") && _skip_crlf(r))
return 2;
@ -988,6 +1218,43 @@ malformed:
return 1;
}
static void http_request_form_data_start_part(struct http_request *r, int b)
{
switch (r->form_data_state) {
case BODY:
if ( r->part_header.content_length != CONTENT_LENGTH_UNKNOWN
&& r->part_body_length != r->part_header.content_length
) {
WARNF("HTTP multipart part body length (%"PRIhttp_size_t") does not match Content-Length header (%"PRIhttp_size_t")",
r->part_body_length,
r->part_header.content_length
);
}
// fall through...
case HEADER:
if (r->form_data.handle_mime_part_end) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_end()");
r->form_data.handle_mime_part_end(r);
}
break;
default:
break;
}
if (b == 1) {
r->form_data_state = HEADER;
bzero(&r->part_header, sizeof r->part_header);
r->part_body_length = 0;
r->part_header.content_length = CONTENT_LENGTH_UNKNOWN;
if (r->form_data.handle_mime_part_start) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_start()");
r->form_data.handle_mime_part_start(r);
}
} else
r->form_data_state = EPILOGUE;
}
/* If parsing completes (ie, parsed to end of epilogue), then sets r->parser to NULL and returns 0,
* so this function will not be called again. If parsing cannot complete due to running out of
* data, returns 100, so this function will not be called again until more data has been read.
@ -1029,15 +1296,7 @@ static int http_request_parse_body_form_data(struct http_request *r)
}
_rewind_crlf(r);
_commit(r);
if (b == 1) {
r->form_data_state = HEADER;
if (r->form_data.handle_mime_part_start) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_start()");
r->form_data.handle_mime_part_start(r);
}
} else
r->form_data_state = EPILOGUE;
http_request_form_data_start_part(r, b);
return 0;
}
}
@ -1074,6 +1333,16 @@ static int http_request_parse_body_form_data(struct http_request *r)
// A blank line finishes the headers. The CRLF does not form part of the body.
if (_skip_crlf(r)) {
_commit(r);
if (r->form_data.handle_mime_part_header) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_header(Content-Length: %"PRIhttp_size_t", Content-Type: %s, Content-Disposition: %s)",
r->part_header.content_length,
alloca_mime_content_type(&r->part_header.content_type),
alloca_mime_content_disposition(&r->part_header.content_disposition)
);
r->form_data.handle_mime_part_header(r, &r->part_header);
}
r->form_data_state = BODY;
return 0;
}
@ -1086,23 +1355,9 @@ static int http_request_parse_body_form_data(struct http_request *r)
if ((b = _skip_mime_boundary(r))) {
_rewind_crlf(r);
_commit(r);
if (r->form_data.handle_mime_part_end) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_end()");
r->form_data.handle_mime_part_end(r);
}
// A boundary in the middle of headers finishes the current part and starts a new part.
// An end boundary terminates the current part and starts the epilogue.
if (b == 1) {
r->form_data_state = HEADER;
if (r->form_data.handle_mime_part_start) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_start()");
r->form_data.handle_mime_part_start(r);
}
}
else
r->form_data_state = EPILOGUE;
http_request_form_data_start_part(r, b);
return 0;
}
if (_run_out(r))
@ -1115,27 +1370,54 @@ static int http_request_parse_body_form_data(struct http_request *r)
strncpy(labelstr, label.start, labellen)[labellen] = '\0';
str_tolower_inplace(labelstr);
const char *value = r->cursor;
if (strcmp(labelstr, "content-disposition") == 0) {
struct mime_content_disposition cd;
bzero(&cd, sizeof cd);
if (_parse_content_disposition(r, &cd) && _skip_optional_space(r) && _skip_crlf(r)) {
if (strcmp(labelstr, "content-length") == 0) {
if (r->part_header.content_length != CONTENT_LENGTH_UNKNOWN) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP multipart header Content-Length: %s", alloca_toprint(50, sol, r->end - sol));
return 400;
}
http_size_t length;
if (_parse_http_size_t(r, &length) && _skip_optional_space(r) && _skip_crlf(r)) {
_rewind_crlf(r);
_commit(r);
if (r->form_data.handle_mime_content_disposition) {
r->part_header.content_length = length;
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_content_disposition(%s)", alloca_mime_content_disposition(&cd));
r->form_data.handle_mime_content_disposition(r, &cd);
DEBUGF("Parsed HTTP multipart header Content-Length: %"PRIhttp_size_t, r->part_header.content_length);
return 0;
}
}
else if (strcmp(labelstr, "content-type") == 0) {
if (r->part_header.content_type.type[0]) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP multipart header Content-Type: %s", alloca_toprint(50, sol, r->end - sol));
return 400;
}
if (_parse_content_type(r, &r->part_header.content_type) && _skip_optional_space(r) && _skip_crlf(r)) {
_rewind_crlf(r);
_commit(r);
if (r->debug_flag && *r->debug_flag)
DEBUGF("Parsed HTTP multipart header Content-Type: %s", alloca_mime_content_type(&r->part_header.content_type));
return 0;
}
}
else if (strcmp(labelstr, "content-disposition") == 0) {
if (r->part_header.content_disposition.type[0]) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP multipart header Content-Disposition: %s", alloca_toprint(50, sol, r->end - sol));
return 400;
}
if (_parse_content_disposition(r, &r->part_header.content_disposition) && _skip_optional_space(r) && _skip_crlf(r)) {
_rewind_crlf(r);
_commit(r);
if (r->debug_flag && *r->debug_flag)
DEBUGF("Parsed HTTP multipart header Content-Disposition: %s", alloca_mime_content_disposition(&r->part_header.content_disposition));
return 0;
}
}
else if (_skip_to_crlf(r)) {
_commit(r);
if (r->form_data.handle_mime_header) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_header(%s, %s)", alloca_str_toprint(labelstr), alloca_toprint(-1, value, value - r->cursor));
r->form_data.handle_mime_header(r, labelstr, value, value - r->cursor); // excluding CRLF at end
}
DEBUGF("Skip HTTP multipart header: %s: %s", alloca_str_toprint(labelstr), alloca_toprint(-1, value, value - r->cursor));
return 0;
}
}
@ -1169,25 +1451,14 @@ static int http_request_parse_body_form_data(struct http_request *r)
if ((b = _skip_mime_boundary(r))) {
_rewind_crlf(r);
_commit(r);
assert(end_body >= start);
r->part_body_length += end_body - start;
if (end_body > start && r->form_data.handle_mime_body) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_body(%s length=%zu)", alloca_toprint(80, start, end_body - start), end_body - start);
r->form_data.handle_mime_body(r, start, end_body - start); // excluding CRLF at end
}
if (r->form_data.handle_mime_part_end) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_end()");
r->form_data.handle_mime_part_end(r);
}
r->form_data_state = EPILOGUE;
if (b == 1) {
r->form_data_state = HEADER;
if (r->form_data.handle_mime_part_start) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_start()");
r->form_data.handle_mime_part_start(r);
}
}
http_request_form_data_start_part(r, b);
return 0;
}
}
@ -1198,6 +1469,8 @@ static int http_request_parse_body_form_data(struct http_request *r)
}
_rewind_optional_cr(r);
_commit(r);
assert(r->parsed >= start);
r->part_body_length += r->parsed - start;
if (r->parsed > start && r->form_data.handle_mime_body) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_body(%s length=%zu)", alloca_toprint(80, start, r->parsed - start), r->parsed - start);
@ -1427,6 +1700,9 @@ static void http_request_send_response(struct http_request *r)
r->response_buffer_sent += (size_t) written;
assert(r->response_sent <= r->response_length);
assert(r->response_buffer_sent <= r->response_buffer_length);
if (r->debug_flag && *r->debug_flag)
DEBUGF("Wrote %zu bytes to HTTP socket, total %"PRIhttp_size_t", remaining=%"PRIhttp_size_t,
(size_t) written, r->response_sent, r->response_length - r->response_sent);
// Reset inactivity timer.
r->alarm.alarm = gettime_ms() + r->idle_timeout;
r->alarm.deadline = r->alarm.alarm + r->idle_timeout;
@ -1538,18 +1814,6 @@ static const char *httpResultString(int response_code)
}
}
static strbuf strbuf_append_quoted_string(strbuf sb, const char *str)
{
strbuf_putc(sb, '"');
for (; *str; ++str) {
if (*str == '"' || *str == '\\')
strbuf_putc(sb, '\\');
strbuf_putc(sb, *str);
}
strbuf_putc(sb, '"');
return sb;
}
/* Render the HTTP response into the current response buffer. Return 1 if it fits, 0 if it does
* not. The buffer response_pointer may be NULL, in which case no response is rendered, but the
* content_length is still computed
@ -1559,24 +1823,32 @@ static strbuf strbuf_append_quoted_string(strbuf sb, const char *str)
static int _render_response(struct http_request *r)
{
struct http_response hr = r->response;
assert(hr.result_code != 0);
assert(hr.header.content_range_start <= hr.header.resource_length);
assert(hr.header.content_length <= hr.header.resource_length);
// To save page handlers having to decide between 200 (OK) and 206 (Partial Content), they can
// just send 200 and the content range fields, and this logic will detect if it should be 206.
if (hr.header.content_length > 0 && hr.header.content_length < hr.header.resource_length && hr.result_code == 200)
hr.result_code = 206; // Partial Content
assert(hr.result_code >= 100);
assert(hr.result_code < 600);
if (hr.result_code == 401)
assert(hr.header.www_authenticate.scheme != NOAUTH);
const char *result_string = httpResultString(hr.result_code);
strbuf sb = strbuf_local(r->response_buffer, r->response_buffer_size);
if (hr.content == NULL && hr.content_generator == NULL) {
assert(hr.header.content_length == CONTENT_LENGTH_UNKNOWN);
assert(hr.header.resource_length == CONTENT_LENGTH_UNKNOWN);
assert(hr.header.content_range_start == 0);
strbuf cb = strbuf_alloca(100 + strlen(result_string));
strbuf_puts(cb, "<html><h1>");
strbuf_puts(cb, result_string);
strbuf_puts(cb, "</h1></html>\r\n");
strbuf_sprintf(cb, "<html><h1>%03u %s</h1></html>", hr.result_code, result_string);
hr.content = strbuf_str(cb);
hr.header.resource_length = hr.header.content_length = strbuf_len(cb);
hr.header.content_type = "text/html";
hr.header.content_range_start = 0;
} else {
assert(hr.header.content_length != CONTENT_LENGTH_UNKNOWN);
assert(hr.header.resource_length != CONTENT_LENGTH_UNKNOWN);
assert(hr.header.content_length <= hr.header.resource_length);
assert(hr.header.content_range_start + hr.header.content_length <= hr.header.resource_length);
// To save page handlers having to decide between 200 (OK) and 206 (Partial Content), they can
// just set the content range fields and pass 200 to http_request_response_static(), and this
// logic will change it to 206 if appropriate.
if (hr.header.content_length > 0 && hr.header.content_length < hr.header.resource_length && hr.result_code == 200)
hr.result_code = 206; // Partial Content
}
assert(hr.header.content_type != NULL);
assert(hr.header.content_type[0]);
@ -1603,6 +1875,17 @@ static int _render_response(struct http_request *r)
);
}
strbuf_sprintf(sb, "Content-Length: %"PRIhttp_size_t"\r\n", hr.header.content_length);
const char *scheme = NULL;
switch (hr.header.www_authenticate.scheme) {
case NOAUTH: break;
case BASIC: scheme = "Basic"; break;
}
if (scheme) {
assert(hr.result_code == 401);
strbuf_sprintf(sb, "WWW-Authenticate: %s realm=", scheme);
strbuf_append_quoted_string(sb, hr.header.www_authenticate.realm);
strbuf_puts(sb, "\r\n");
}
strbuf_puts(sb, "\r\n");
if (strbuf_overrun(sb))
return 0;
@ -1656,7 +1939,6 @@ static size_t http_request_drain(struct http_request *r)
static void http_request_start_response(struct http_request *r)
{
assert(r->phase == RECEIVE);
assert(r->response.result_code != 0);
if (r->response.content || r->response.content_generator) {
assert(r->response.header.content_type != NULL);
assert(r->response.header.content_type[0]);
@ -1674,6 +1956,13 @@ static void http_request_start_response(struct http_request *r)
http_request_drain(r);
if (r->phase != RECEIVE)
return;
// Ensure conformance to HTTP standards.
if (r->response.result_code == 401 && r->response.header.www_authenticate.scheme == NOAUTH) {
WHY("HTTP 401 response missing WWW-Authenticate header, sending 500 Server Error instead");
r->response.result_code = 500;
r->response.content = NULL;
r->response.content_generator = NULL;
}
// If the response cannot be rendered, then render a 500 Server Error instead. If that fails,
// then just close the connection.
http_request_render_response(r);
@ -1681,6 +1970,7 @@ static void http_request_start_response(struct http_request *r)
WARN("Cannot render HTTP response, sending 500 Server Error instead");
r->response.result_code = 500;
r->response.content = NULL;
r->response.content_generator = NULL;
http_request_render_response(r);
if (r->response_buffer == NULL) {
WHY("Cannot render HTTP 500 Server Error response, closing connection");
@ -1706,8 +1996,6 @@ static void http_request_start_response(struct http_request *r)
void http_request_response_static(struct http_request *r, int result, const char *mime_type, const char *body, uint64_t bytes)
{
assert(r->phase == RECEIVE);
assert(result >= 100);
assert(result < 300);
assert(mime_type != NULL);
assert(mime_type[0]);
r->response.result_code = result;
@ -1722,8 +2010,6 @@ void http_request_response_static(struct http_request *r, int result, const char
void http_request_response_generated(struct http_request *r, int result, const char *mime_type, HTTP_CONTENT_GENERATOR generator)
{
assert(r->phase == RECEIVE);
assert(result >= 100);
assert(result < 300);
assert(mime_type != NULL);
assert(mime_type[0]);
r->response.result_code = result;
@ -1744,8 +2030,6 @@ void http_request_response_generated(struct http_request *r, int result, const c
void http_request_simple_response(struct http_request *r, uint16_t result, const char *body)
{
assert(r->phase == RECEIVE);
assert(result >= 200);
assert(result < 600);
strbuf h = NULL;
if (body) {
size_t html_len = strlen(body) + 40;
@ -1755,8 +2039,10 @@ void http_request_simple_response(struct http_request *r, uint16_t result, const
r->response.result_code = result;
r->response.header.content_type = "text/html";
r->response.header.content_range_start = 0;
r->response.header.resource_length = r->response.header.content_length = h ? strbuf_len(h) : 0;
r->response.content = h ? strbuf_str(h) : NULL;
if (h) {
r->response.header.resource_length = r->response.header.content_length = strbuf_len(h);
r->response.content = strbuf_str(h);
}
r->response.content_generator = NULL;
http_request_start_response(r);
}

View File

@ -56,13 +56,35 @@ http_size_t http_range_bytes(const struct http_range *range, unsigned nranges);
#define CONTENT_LENGTH_UNKNOWN UINT64_MAX
struct mime_content_type {
char type[64];
char subtype[64];
char multipart_boundary[71];
char charset[31];
};
struct http_client_authorization {
enum http_authorization_scheme { NOAUTH = 0, BASIC } scheme;
union {
struct http_client_credentials_basic {
const char *user;
const char *password;
} basic;
} credentials;
};
struct http_www_authenticate {
enum http_authorization_scheme scheme;
const char *realm;
};
struct http_request_headers {
http_size_t content_length;
const char *content_type;
const char *content_subtype;
const char *boundary;
struct mime_content_type content_type;
unsigned short content_range_count;
struct http_range content_ranges[5];
struct http_client_authorization authorization;
};
struct http_response_headers {
@ -71,6 +93,7 @@ struct http_response_headers {
http_size_t resource_length; // size of entire resource
const char *content_type; // "type/subtype"
const char *boundary;
struct http_www_authenticate www_authenticate;
};
typedef int (*HTTP_CONTENT_GENERATOR)(struct http_request *);
@ -94,11 +117,16 @@ struct mime_content_disposition {
time_t read_date;
};
struct mime_part_headers {
http_size_t content_length;
struct mime_content_type content_type;
struct mime_content_disposition content_disposition;
};
struct http_mime_handler {
void (*handle_mime_preamble)(struct http_request *, const char *, size_t);
void (*handle_mime_part_start)(struct http_request *);
void (*handle_mime_content_disposition)(struct http_request *, const struct mime_content_disposition *);
void (*handle_mime_header)(struct http_request *, const char *label, const char *, size_t);
void (*handle_mime_part_header)(struct http_request *, const struct mime_part_headers *);
void (*handle_mime_body)(struct http_request *, const char *, size_t);
void (*handle_mime_part_end)(struct http_request *);
void (*handle_mime_epilogue)(struct http_request *, const char *, size_t);
@ -118,31 +146,49 @@ typedef int (*HTTP_REQUEST_PARSER)(struct http_request *);
struct http_request {
struct sched_ent alarm; // MUST BE FIRST ELEMENT
// The following control the lifetime of this struct.
enum http_request_phase { RECEIVE, TRANSMIT, DONE } phase;
bool_t *debug_flag;
bool_t *disable_tx_flag;
time_ms_t initiate_time; // time connection was initiated
time_ms_t idle_timeout; // disconnect if no bytes received for this long
struct sockaddr_in client_sockaddr_in;
HTTP_REQUEST_PARSER parser; // current parser function
HTTP_REQUEST_PARSER handle_first_line; // called after first line is parsed
HTTP_REQUEST_PARSER handle_headers; // called after all headers are parsed
HTTP_REQUEST_PARSER handle_content_end; // called after all content is received
enum mime_state { START, PREAMBLE, HEADER, BODY, EPILOGUE } form_data_state;
struct http_mime_handler form_data; // called to parse multipart/form-data body
void (*finalise)(struct http_request *);
void (*free)(void*);
// These can be set up to point to config flags, to allow debug to be
// enabled indpendently for different instances HTTP server instances
// that use this code.
bool_t *debug_flag;
bool_t *disable_tx_flag;
// The following are used for parsing the HTTP request.
time_ms_t initiate_time; // time connection was initiated
time_ms_t idle_timeout; // disconnect if no bytes received for this long
struct sockaddr_in client_sockaddr_in; // caller may supply this
// The parsed HTTP request is accumulated into the following fields.
const char *verb; // points to nul terminated static string, "GET", "PUT", etc.
const char *path; // points into buffer; nul terminated
uint8_t version_major; // m from from HTTP/m.n
uint8_t version_minor; // n from HTTP/m.n
struct http_request_headers request_header;
// Parsing is done by setting 'parser' to point to a series of parsing
// functions as the parsing state progresses.
HTTP_REQUEST_PARSER parser; // current parser function
// The caller may set these up, and they are invoked by the parser as request
// parsing reaches different stages.
HTTP_REQUEST_PARSER handle_first_line; // called after first line is parsed
HTTP_REQUEST_PARSER handle_headers; // called after all HTTP headers are parsed
HTTP_REQUEST_PARSER handle_content_end; // called after all content is received
// The following are used for managing the buffer during RECEIVE phase.
const char *received; // start of received data in buffer[]
const char *end; // end of received data in buffer[]
const char *parsed; // start of unparsed data in buffer[]
const char *cursor; // for parsing
http_size_t request_content_remaining;
// The following are used for parsing a multipart body.
enum mime_state { START, PREAMBLE, HEADER, BODY, EPILOGUE } form_data_state;
struct http_mime_handler form_data;
struct mime_part_headers part_header;
http_size_t part_body_length;
// The following are used for constructing the response that will be sent in
// TRANSMIT phase.
struct http_response response;
// The following are used during TRANSMIT phase to control buffering and
// sending.
http_size_t response_length; // total response bytes (header + content)
http_size_t response_sent; // for counting up to response_length
char *response_buffer;
@ -150,6 +196,7 @@ struct http_request {
size_t response_buffer_length;
size_t response_buffer_sent;
void (*response_free_buffer)(void*);
// This buffer is used during RECEIVE and TRANSMIT phase.
char buffer[8 * 1024];
};

View File

@ -1506,7 +1506,7 @@ unsigned char *keyring_find_sas_private(keyring_file *k, const sid_t *sidp, unsi
k->contexts[cn]->identities[in]->keypairs[kp]->private_key;
unsigned char *sas_public=
k->contexts[cn]->identities[in]->keypairs[kp]->public_key;
if (rhizome_verify_bundle_privatekey(NULL,sas_private,sas_public))
if (!rhizome_verify_bundle_privatekey(sas_private, sas_public))
{
/* SAS key is invalid (perhaps because it was a pre 0.90 format one),
so replace it */

View File

@ -87,7 +87,7 @@ static int get_my_conversation_bundle(const sid_t *my_sidp, rhizome_manifest *m)
assert(m->haveSecret);
if (m->haveSecret == NEW_BUNDLE_ID) {
rhizome_manifest_set_service(m, RHIZOME_SERVICE_FILE);
if (rhizome_fill_manifest(m, NULL, my_sidp, NULL) == -1)
if (rhizome_fill_manifest(m, NULL, my_sidp) == -1)
return WHY("Invalid manifest");
if (config.debug.meshms) {
char secret[RHIZOME_BUNDLE_KEY_STRLEN + 1];
@ -218,8 +218,9 @@ static int create_ply(const sid_t *my_sid, struct conversations *conv, rhizome_m
rhizome_manifest_set_recipient(m, &conv->them);
rhizome_manifest_set_filesize(m, 0);
rhizome_manifest_set_tail(m, 0);
if (rhizome_fill_manifest(m, NULL, my_sid, NULL))
if (rhizome_fill_manifest(m, NULL, my_sid))
return -1;
assert(m->haveSecret);
assert(m->payloadEncryption == PAYLOAD_ENCRYPTED);
conv->my_ply.bundle_id = m->cryptoSignPublic;
conv->found_my_ply = 1;
@ -239,7 +240,7 @@ static int ply_read_open(struct ply_read *ply, const rhizome_bid_t *bid, rhizome
DEBUGF("Opening ply %s", alloca_tohex_rhizome_bid_t(*bid));
if (rhizome_retrieve_manifest(bid, m))
return -1;
int ret = rhizome_open_decrypt_read(m, NULL, &ply->read);
int ret = rhizome_open_decrypt_read(m, &ply->read);
if (ret == 1)
WARNF("Payload was not found for manifest %s, %"PRId64, alloca_tohex_rhizome_bid_t(m->cryptoSignPublic), m->version);
if (ret != 0)
@ -317,7 +318,8 @@ static int ply_find_next(struct ply_read *ply, char type){
}
}
static int append_meshms_buffer(const sid_t *my_sid, struct conversations *conv, unsigned char *buffer, int len){
static int append_meshms_buffer(const sid_t *my_sid, struct conversations *conv, unsigned char *buffer, int len)
{
int ret=-1;
rhizome_manifest *mout = NULL;
rhizome_manifest *m = rhizome_new_manifest();
@ -327,14 +329,17 @@ static int append_meshms_buffer(const sid_t *my_sid, struct conversations *conv,
if (conv->found_my_ply){
if (rhizome_retrieve_manifest(&conv->my_ply.bundle_id, m))
goto end;
if (rhizome_find_bundle_author(m))
rhizome_authenticate_author(m);
if (!m->haveSecret || m->authorship != AUTHOR_AUTHENTIC)
goto end;
}else{
if (create_ply(my_sid, conv, m))
goto end;
}
assert(m->haveSecret);
assert(m->authorship == AUTHOR_AUTHENTIC);
if (rhizome_append_journal_buffer(m, NULL, 0, buffer, len))
if (rhizome_append_journal_buffer(m, 0, buffer, len))
goto end;
if (rhizome_manifest_finalise(m, &mout, 1))
@ -493,7 +498,7 @@ static int read_known_conversations(rhizome_manifest *m, const sid_t *their_sid,
struct rhizome_read_buffer buff;
bzero(&buff, sizeof(buff));
int ret = rhizome_open_decrypt_read(m, NULL, &read);
int ret = rhizome_open_decrypt_read(m, &read);
if (ret == -1)
goto end;
@ -717,7 +722,8 @@ int app_meshms_conversations(const struct cli_parsed *parsed, struct cli_context
return 0;
}
int app_meshms_send_message(const struct cli_parsed *parsed, struct cli_context *context){
int app_meshms_send_message(const struct cli_parsed *parsed, struct cli_context *context)
{
const char *my_sidhex, *their_sidhex, *message;
if (cli_arg(parsed, "sender_sid", &my_sidhex, str_is_subscriber_id, "") == -1
|| cli_arg(parsed, "recipient_sid", &their_sidhex, str_is_subscriber_id, "") == -1
@ -734,8 +740,10 @@ int app_meshms_send_message(const struct cli_parsed *parsed, struct cli_context
}
sid_t my_sid, their_sid;
fromhex(my_sid.binary, my_sidhex, sizeof(my_sid.binary));
fromhex(their_sid.binary, their_sidhex, sizeof(their_sid.binary));
if (str_to_sid_t(&my_sid, my_sidhex) == -1)
return WHY("invalid sender SID");
if (str_to_sid_t(&their_sid, their_sidhex) == -1)
return WHY("invalid recipient SID");
struct conversations *conv = find_or_create_conv(&my_sid, &their_sid);
if (!conv) {
keyring_free(keyring);

2
os.h
View File

@ -66,7 +66,7 @@ __SERVALDNA_OS_INLINE void bzero(void *buf, size_t len) {
#endif
#ifndef HAVE_BCOPY
__SERVALDNA_OS_INLINE void bcopy(void *src, void *dst, size_t len) {
__SERVALDNA_OS_INLINE void bcopy(const void *src, void *dst, size_t len) {
memcpy(dst, src, len);
}
#endif

View File

@ -184,44 +184,74 @@ int rhizome_manifest_check_sanity(rhizome_manifest *m)
return 0;
}
/*
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.
/* Sets the bundle key "BK" field of a manifest. Returns 1 if the field was set, 0 if not.
*
* This function must not be called unless the bundle secret is known.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rhizome_manifest_bind_id(rhizome_manifest *m)
int rhizome_manifest_add_bundle_key(rhizome_manifest *m)
{
if (rhizome_manifest_createid(m) == -1)
return -1;
/* 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. */
if (m->has_author) {
/* Set the BK using the provided authorship information.
Serval Security Framework defines BK as being:
BK = privateKey XOR sha512(RS##BID), where BID = cryptoSignPublic,
and RS is the rhizome secret for the specified author.
The nice thing about this specification is that:
privateKey = BK XOR sha512(RS##BID), so the same function can be used
to encrypt and decrypt the BK field. */
IN();
assert(m->haveSecret);
switch (m->authorship) {
case ANONYMOUS: // there can be no BK field without an author
case AUTHOR_UNKNOWN: // we already know the author is not in the keyring
case AUTHENTICATION_ERROR: // already tried and failed to get Rhizome Secret
break;
case AUTHOR_NOT_CHECKED:
case AUTHOR_LOCAL:
case AUTHOR_AUTHENTIC:
case AUTHOR_IMPOSTOR: {
/* Set the BK using the provided author. Serval Security Framework defines BK as being:
* BK = privateKey XOR sha512(RS##BID)
* where BID = cryptoSignPublic,
* RS is the rhizome secret for the specified author.
* The nice thing about this specification is that:
* privateKey = BK XOR sha512(RS##BID)
* so the same function can be used to encrypt and decrypt the BK field.
*/
const unsigned char *rs;
int rs_len=0;
if (rhizome_find_secret(&m->author, &rs_len, &rs))
return WHYF("Failed to obtain RS for %s to calculate BK", alloca_tohex_sid_t(m->author));
size_t rs_len = 0;
enum rhizome_secret_disposition d = find_rhizome_secret(&m->author, &rs_len, &rs);
switch (d) {
case FOUND_RHIZOME_SECRET: {
rhizome_bk_t bkey;
if (!rhizome_secret2bk(&m->cryptoSignPublic, rs, rs_len, bkey.binary, m->cryptoSignSecret))
if (rhizome_secret2bk(&m->cryptoSignPublic, rs, rs_len, bkey.binary, m->cryptoSignSecret) == 0) {
rhizome_manifest_set_bundle_key(m, &bkey);
else
return WHY("Failed to set BK");
m->authorship = AUTHOR_AUTHENTIC;
RETURN(1);
} else
m->authorship = AUTHENTICATION_ERROR;
}
return 0;
break;
case IDENTITY_NOT_FOUND:
m->authorship = AUTHOR_UNKNOWN;
break;
case IDENTITY_HAS_NO_RHIZOME_SECRET:
m->authorship = AUTHENTICATION_ERROR;
break;
default:
FATALF("find_rhizome_secret() returned unknown code %d", (int)d);
break;
}
}
break;
default:
FATALF("m->authorship = %d", (int)m->authorship);
}
rhizome_manifest_del_bundle_key(m);
switch (m->authorship) {
case AUTHOR_UNKNOWN:
WHYF("Cannot set BK because author=%s is not in keyring", alloca_tohex_sid_t(m->author));
break;
case AUTHENTICATION_ERROR:
WHY("Cannot set BK due to error");
break;
default:
break;
}
RETURN(0);
}
int rhizome_add_manifest(rhizome_manifest *m, int ttl)

View File

@ -238,9 +238,18 @@ typedef struct rhizome_manifest
bool_t has_sender;
bool_t has_recipient;
/* Set if the 'author' element is valid, ie, a SID has been assigned.
/* Local authorship. Useful for dividing bundle lists between "sent" and
* "inbox" views.
*/
bool_t has_author;
enum rhizome_bundle_authorship {
ANONYMOUS = 0, // 'author' element is not valid
AUTHOR_NOT_CHECKED, // 'author' element is valid but not checked
AUTHENTICATION_ERROR, // author check failed, don't try again
AUTHOR_UNKNOWN, // author is not a local identity
AUTHOR_LOCAL, // author is in keyring (unlocked) but not verified
AUTHOR_IMPOSTOR, // author is a local identity but fails verification
AUTHOR_AUTHENTIC // a local identity is the verified author
} authorship;
/* time-to-live in hops of this manifest. */
int ttl;
@ -283,7 +292,8 @@ typedef struct rhizome_manifest
sid_t recipient;
/* Local data, not encapsulated in the bundle. The system time of the most
* recent INSERT or UPDATE of the manifest into the store.
* recent INSERT or UPDATE of the manifest into the store. Zero if the manifest
* has not been stored yet.
*/
time_ms_t inserttime;
@ -320,13 +330,21 @@ typedef struct rhizome_manifest
#define rhizome_manifest_set_filehash(m,v) _rhizome_manifest_set_filehash(__WHENCE__,(m),(v))
#define rhizome_manifest_set_tail(m,v) _rhizome_manifest_set_tail(__WHENCE__,(m),(v))
#define rhizome_manifest_set_bundle_key(m,v) _rhizome_manifest_set_bundle_key(__WHENCE__,(m),(v))
#define rhizome_manifest_del_bundle_key(m) _rhizome_manifest_del_bundle_key(__WHENCE__,(m))
#define rhizome_manifest_set_service(m,v) _rhizome_manifest_set_service(__WHENCE__,(m),(v))
#define rhizome_manifest_del_service(m) _rhizome_manifest_del_service(__WHENCE__,(m))
#define rhizome_manifest_set_name(m,v) _rhizome_manifest_set_name(__WHENCE__,(m),(v))
#define rhizome_manifest_del_name(m) _rhizome_manifest_del_name(__WHENCE__,(m))
#define rhizome_manifest_set_date(m,v) _rhizome_manifest_set_date(__WHENCE__,(m),(v))
#define rhizome_manifest_del_date(m) _rhizome_manifest_del_date(__WHENCE__,(m))
#define rhizome_manifest_set_sender(m,v) _rhizome_manifest_set_sender(__WHENCE__,(m),(v))
#define rhizome_manifest_del_sender(m) _rhizome_manifest_del_sender(__WHENCE__,(m))
#define rhizome_manifest_set_recipient(m,v) _rhizome_manifest_set_recipient(__WHENCE__,(m),(v))
#define rhizome_manifest_del_recipient(m) _rhizome_manifest_del_recipient(__WHENCE__,(m))
#define rhizome_manifest_set_crypt(m,v) _rhizome_manifest_set_crypt(__WHENCE__,(m),(v))
#define rhizome_manifest_set_inserttime(m,v) _rhizome_manifest_set_inserttime(__WHENCE__,(m),(v))
#define rhizome_manifest_set_author(m,v) _rhizome_manifest_set_author(__WHENCE__,(m),(v))
#define rhizome_manifest_del_author(m) _rhizome_manifest_del_author(__WHENCE__,(m))
void _rhizome_manifest_set_id(struct __sourceloc, rhizome_manifest *, const rhizome_bid_t *);
void _rhizome_manifest_set_version(struct __sourceloc, rhizome_manifest *, int64_t); // TODO change to uint64_t
@ -334,13 +352,21 @@ void _rhizome_manifest_set_filesize(struct __sourceloc, rhizome_manifest *, uint
void _rhizome_manifest_set_filehash(struct __sourceloc, rhizome_manifest *, const rhizome_filehash_t *);
void _rhizome_manifest_set_tail(struct __sourceloc, rhizome_manifest *, uint64_t);
void _rhizome_manifest_set_bundle_key(struct __sourceloc, rhizome_manifest *, const rhizome_bk_t *);
void _rhizome_manifest_del_bundle_key(struct __sourceloc, rhizome_manifest *);
void _rhizome_manifest_set_service(struct __sourceloc, rhizome_manifest *, const char *);
void _rhizome_manifest_del_service(struct __sourceloc, rhizome_manifest *);
void _rhizome_manifest_set_name(struct __sourceloc, rhizome_manifest *, const char *);
void _rhizome_manifest_del_name(struct __sourceloc, rhizome_manifest *);
void _rhizome_manifest_set_date(struct __sourceloc, rhizome_manifest *, time_ms_t);
void _rhizome_manifest_del_date(struct __sourceloc, rhizome_manifest *);
void _rhizome_manifest_set_sender(struct __sourceloc, rhizome_manifest *, const sid_t *);
void _rhizome_manifest_del_sender(struct __sourceloc, rhizome_manifest *);
void _rhizome_manifest_set_recipient(struct __sourceloc, rhizome_manifest *, const sid_t *);
void _rhizome_manifest_del_recipient(struct __sourceloc, rhizome_manifest *);
void _rhizome_manifest_set_crypt(struct __sourceloc, rhizome_manifest *, enum rhizome_manifest_crypt);
void _rhizome_manifest_set_inserttime(struct __sourceloc, rhizome_manifest *, time_ms_t);
void _rhizome_manifest_set_author(struct __sourceloc, rhizome_manifest *, const sid_t *);
void _rhizome_manifest_del_author(struct __sourceloc, rhizome_manifest *);
/* Supported service identifiers. These go in the 'service' field of every
* manifest, and indicate which application must be used to process the bundle
@ -427,16 +453,21 @@ rhizome_manifest *_rhizome_new_manifest(struct __sourceloc __whence);
int rhizome_manifest_pack_variables(rhizome_manifest *m);
int rhizome_store_bundle(rhizome_manifest *m);
int rhizome_remove_file_datainvalid(sqlite_retry_state *retry, const rhizome_filehash_t *hashp);
int rhizome_manifest_add_group(rhizome_manifest *m,char *groupid);
int rhizome_clean_payload(const char *fileidhex);
int rhizome_store_file(rhizome_manifest *m,const unsigned char *key);
int rhizome_bundle_import_files(rhizome_manifest *m, const char *manifest_path, const char *filepath);
int rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t *authorSidp, rhizome_bk_t *bsk);
int rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t *authorSidp);
int rhizome_apply_bundle_secret(rhizome_manifest *, const rhizome_bk_t *);
int rhizome_manifest_add_bundle_key(rhizome_manifest *);
void rhizome_find_bundle_author_and_secret(rhizome_manifest *m);
int rhizome_lookup_author(rhizome_manifest *m);
void rhizome_authenticate_author(rhizome_manifest *m);
int rhizome_manifest_verify(rhizome_manifest *m);
int rhizome_manifest_check_sanity(rhizome_manifest *m_in);
int rhizome_manifest_bind_id(rhizome_manifest *m_in);
int rhizome_manifest_finalise(rhizome_manifest *m, rhizome_manifest **mout, int deduplicate);
int rhizome_add_manifest(rhizome_manifest *m_in,int ttl);
@ -535,7 +566,7 @@ int rhizome_is_bar_interesting(unsigned char *bar);
int rhizome_is_manifest_interesting(rhizome_manifest *m);
int rhizome_list_manifests(struct cli_context *context, const char *service, const char *name,
const char *sender_sid, const char *recipient_sid,
int limit, int offset, char count_rows);
size_t rowlimit, size_t rowoffset, char count_rows);
int rhizome_retrieve_manifest(const rhizome_bid_t *bid, rhizome_manifest *m);
int rhizome_retrieve_manifest_by_prefix(const unsigned char *prefix, unsigned prefix_len, rhizome_manifest *m);
int rhizome_advertise_manifest(struct subscriber *dest, rhizome_manifest *m);
@ -549,7 +580,12 @@ int rhizome_delete_file(const rhizome_filehash_t *hashp);
int rhizome_fetching_get_fds(struct pollfd *fds,int *fdcount,int fdmax);
int monitor_announce_bundle(rhizome_manifest *m);
int rhizome_find_secret(const sid_t *authorSidp, int *rs_len, const unsigned char **rs);
enum rhizome_secret_disposition {
FOUND_RHIZOME_SECRET = 0,
IDENTITY_NOT_FOUND,
IDENTITY_HAS_NO_RHIZOME_SECRET,
};
enum rhizome_secret_disposition find_rhizome_secret(const sid_t *authorSidp, size_t *rs_len, const unsigned char **rs);
int rhizome_bk_xor_stream(
const rhizome_bid_t *bidp,
const unsigned char *rs,
@ -571,13 +607,9 @@ int rhizome_secret2bk(
const unsigned char secret[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES]
);
unsigned char *rhizome_bundle_shared_secret(rhizome_manifest *m);
int rhizome_extract_privatekey(rhizome_manifest *m, const rhizome_bk_t *bsk);
int rhizome_extract_privatekey_required(rhizome_manifest *m, rhizome_bk_t *bsk);
int rhizome_sign_hash_with_key(rhizome_manifest *m,const unsigned char *sk,
const unsigned char *pk,rhizome_signature *out);
int rhizome_verify_bundle_privatekey(rhizome_manifest *m, const unsigned char *sk,
const unsigned char *pk);
int rhizome_find_bundle_author(rhizome_manifest *m);
int rhizome_verify_bundle_privatekey(const unsigned char *sk, const unsigned char *pk);
int rhizome_queue_ignore_manifest(unsigned char *bid_prefix, int prefix_len, int timeout);
int rhizome_ignore_manifest_check(unsigned char *bid_prefix, int prefix_len);
@ -853,11 +885,10 @@ int rhizome_import_file(rhizome_manifest *m, const char *filepath);
int rhizome_import_buffer(rhizome_manifest *m, unsigned char *buffer, size_t length);
int rhizome_stat_file(rhizome_manifest *m, const char *filepath);
int rhizome_add_file(rhizome_manifest *m, const char *filepath);
int rhizome_derive_key(rhizome_manifest *m, rhizome_bk_t *bsk);
int rhizome_derive_payload_key(rhizome_manifest *m);
int rhizome_open_write_journal(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, uint64_t new_size);
int rhizome_append_journal_buffer(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, unsigned char *buffer, size_t len);
int rhizome_append_journal_file(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, const char *filename);
int rhizome_append_journal_buffer(rhizome_manifest *m, uint64_t advance_by, unsigned char *buffer, size_t len);
int rhizome_append_journal_file(rhizome_manifest *m, uint64_t advance_by, const char *filename);
int rhizome_journal_pipe(struct rhizome_write *write, const rhizome_filehash_t *hashp, uint64_t start_offset, uint64_t length);
int rhizome_crypt_xor_block(unsigned char *buffer, size_t buffer_size, uint64_t stream_offset,
@ -866,8 +897,8 @@ int rhizome_open_read(struct rhizome_read *read, const rhizome_filehash_t *hashp
ssize_t rhizome_read(struct rhizome_read *read, unsigned char *buffer, size_t buffer_length);
ssize_t rhizome_read_buffered(struct rhizome_read *read, struct rhizome_read_buffer *buffer, unsigned char *data, size_t len);
int rhizome_read_close(struct rhizome_read *read);
int rhizome_open_decrypt_read(rhizome_manifest *m, rhizome_bk_t *bsk, struct rhizome_read *read_state);
int rhizome_extract_file(rhizome_manifest *m, const char *filepath, rhizome_bk_t *bsk);
int rhizome_open_decrypt_read(rhizome_manifest *m, struct rhizome_read *read_state);
int rhizome_extract_file(rhizome_manifest *m, const char *filepath);
int rhizome_dump_file(const rhizome_filehash_t *hashp, const char *filepath, int64_t *length);
int rhizome_read_cached(const rhizome_bid_t *bid, uint64_t version, time_ms_t timeout,
uint64_t fileOffset, unsigned char *buffer, size_t length);

View File

@ -117,8 +117,21 @@ void _rhizome_manifest_set_id(struct __sourceloc __whence, rhizome_manifest *m,
{
const char *v = rhizome_manifest_set(m, "id", alloca_tohex_rhizome_bid_t(*bidp));
assert(v); // TODO: remove known manifest fields from vars[]
if (bidp != &m->cryptoSignPublic)
if (bidp != &m->cryptoSignPublic && cmp_rhizome_bid_t(&m->cryptoSignPublic, bidp) != 0) {
m->cryptoSignPublic = *bidp;
// The BID just changed, so the secret key and bundle key are no longer valid.
if (m->haveSecret) {
m->haveSecret = SECRET_UNKNOWN;
bzero(m->cryptoSignSecret, sizeof m->cryptoSignSecret); // not strictly necessary but aids debugging
}
if (m->has_bundle_key) {
m->has_bundle_key = 0;
m->bundle_key = RHIZOME_BK_NONE; // not strictly necessary but aids debugging
}
// Any authenticated author is no longer authenticated, but is still known to be in the keyring.
if (m->authorship == AUTHOR_AUTHENTIC)
m->authorship = AUTHOR_LOCAL;
}
}
void _rhizome_manifest_set_version(struct __sourceloc __whence, rhizome_manifest *m, int64_t version)
@ -169,11 +182,21 @@ void _rhizome_manifest_set_bundle_key(struct __sourceloc __whence, rhizome_manif
assert(v); // TODO: remove known manifest fields from vars[]
m->bundle_key = *bkp;
m->has_bundle_key = 1;
} else {
rhizome_manifest_del(m, "BK");
m->bundle_key = RHIZOME_BK_NONE;
m->has_bundle_key = 0;
} else
_rhizome_manifest_del_bundle_key(__whence, m);
}
void _rhizome_manifest_del_bundle_key(struct __sourceloc __whence, rhizome_manifest *m)
{
if (m->has_bundle_key) {
rhizome_manifest_del(m, "BK");
m->has_bundle_key = 0;
m->bundle_key = RHIZOME_BK_NONE; // not strictly necessary, but aids debugging
} else
assert(rhizome_manifest_get(m, "BK") == NULL);
// Once there is no BK field, any authenticated authorship is no longer.
if (m->authorship == AUTHOR_AUTHENTIC)
m->authorship = AUTHOR_LOCAL;
}
void _rhizome_manifest_set_service(struct __sourceloc __whence, rhizome_manifest *m, const char *service)
@ -182,10 +205,17 @@ void _rhizome_manifest_set_service(struct __sourceloc __whence, rhizome_manifest
const char *v = rhizome_manifest_set(m, "service", service);
assert(v); // TODO: remove known manifest fields from vars[]
m->service = v;
} else {
rhizome_manifest_del(m, "service");
m->service = NULL;
} else
_rhizome_manifest_del_service(__whence, m);
}
void _rhizome_manifest_del_service(struct __sourceloc __whence, rhizome_manifest *m)
{
if (m->service) {
m->service = NULL;
rhizome_manifest_del(m, "service");
} else
assert(rhizome_manifest_get(m, "service") == NULL);
}
void _rhizome_manifest_set_name(struct __sourceloc __whence, rhizome_manifest *m, const char *name)
@ -200,6 +230,15 @@ void _rhizome_manifest_set_name(struct __sourceloc __whence, rhizome_manifest *m
}
}
void _rhizome_manifest_del_name(struct __sourceloc __whence, rhizome_manifest *m)
{
if (m->name) {
m->name = NULL;
rhizome_manifest_del(m, "name");
} else
assert(rhizome_manifest_get(m, "name") == NULL);
}
void _rhizome_manifest_set_date(struct __sourceloc __whence, rhizome_manifest *m, time_ms_t date)
{
const char *v = rhizome_manifest_set_ll(m, "date", date);
@ -215,11 +254,18 @@ void _rhizome_manifest_set_sender(struct __sourceloc __whence, rhizome_manifest
assert(v); // TODO: remove known manifest fields from vars[]
m->sender = *sidp;
m->has_sender = 1;
} else {
} else
_rhizome_manifest_del_sender(__whence, m);
}
void _rhizome_manifest_del_sender(struct __sourceloc __whence, rhizome_manifest *m)
{
if (m->has_sender) {
rhizome_manifest_del(m, "sender");
m->sender = SID_ANY;
m->has_sender = 0;
}
} else
assert(rhizome_manifest_get(m, "sender") == NULL);
}
void _rhizome_manifest_set_recipient(struct __sourceloc __whence, rhizome_manifest *m, const sid_t *sidp)
@ -229,11 +275,18 @@ void _rhizome_manifest_set_recipient(struct __sourceloc __whence, rhizome_manife
assert(v); // TODO: remove known manifest fields from vars[]
m->recipient = *sidp;
m->has_recipient = 1;
} else {
} else
_rhizome_manifest_del_recipient(__whence, m);
}
void _rhizome_manifest_del_recipient(struct __sourceloc __whence, rhizome_manifest *m)
{
if (m->has_recipient) {
rhizome_manifest_del(m, "recipient");
m->recipient = SID_ANY;
m->has_recipient = 0;
}
} else
assert(rhizome_manifest_get(m, "recipient") == NULL);
}
void _rhizome_manifest_set_crypt(struct __sourceloc __whence, rhizome_manifest *m, enum rhizome_manifest_crypt flag)
@ -257,14 +310,31 @@ void _rhizome_manifest_set_crypt(struct __sourceloc __whence, rhizome_manifest *
m->payloadEncryption = flag;
}
void _rhizome_manifest_set_inserttime(struct __sourceloc __whence, rhizome_manifest *m, time_ms_t time)
{
m->inserttime = time;
}
void _rhizome_manifest_set_author(struct __sourceloc __whence, rhizome_manifest *m, const sid_t *sidp)
{
if (sidp) {
if (m->authorship == ANONYMOUS || cmp_sid_t(&m->author, sidp) != 0) {
if (config.debug.rhizome_manifest)
DEBUGF("SET manifest[%d] author = %s", m->manifest_record_number, alloca_tohex_sid_t(*sidp));
m->author = *sidp;
m->has_author = 1;
} else {
m->authorship = AUTHOR_NOT_CHECKED;
}
} else
_rhizome_manifest_del_author(__whence, m);
}
void _rhizome_manifest_del_author(struct __sourceloc __whence, rhizome_manifest *m)
{
if (m->authorship != ANONYMOUS) {
if (config.debug.rhizome_manifest)
DEBUGF("DEL manifest[%d] author", m->manifest_record_number);
m->author = SID_ANY;
m->has_author = 0;
m->authorship = ANONYMOUS;
}
}
@ -403,7 +473,11 @@ int rhizome_manifest_parse(rhizome_manifest *m)
} else {
m->vars[m->var_count] = strdup(var);
m->values[m->var_count] = strdup(value);
// if any of these fields are not well formed, the manifest is invalid and cannot be imported
/* The bundle ID is implicit in transit, but we need to store it in the manifest, 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.
*/
if (strcasecmp(var, "id") == 0) {
have_id = 1;
if (str_to_rhizome_bid_t(&m->cryptoSignPublic, value) == -1) {
@ -455,7 +529,7 @@ int rhizome_manifest_parse(rhizome_manifest *m)
if (!str_to_uint64(value, 10, &tail, NULL) || tail == RHIZOME_SIZE_UNSET) {
if (config.debug.rejecteddata)
DEBUGF("Invalid tail: %s", value);
m->warnings++;
m->errors++;
} else {
m->tail = tail;
m->is_journal = 1;
@ -735,22 +809,18 @@ void _rhizome_manifest_free(struct __sourceloc __whence, rhizome_manifest *m)
if (!m) return;
int mid=m->manifest_record_number;
if (m!=&manifests[mid]) {
WHYF("%s(): asked to free manifest %p, which claims to be manifest slot #%d (%p), but isn't",
if (m!=&manifests[mid])
FATALF("%s(): asked to free manifest %p, which claims to be manifest slot #%d (%p), but isn't",
__FUNCTION__, m, mid, &manifests[mid]
);
exit(-1);
}
if (manifest_free[mid]) {
WHYF("%s(): asked to free manifest slot #%d (%p), which was already freed at %s:%d:%s()",
if (manifest_free[mid])
FATALF("%s(): asked to free manifest slot #%d (%p), which was already freed at %s:%d:%s()",
__FUNCTION__, mid, m,
manifest_free_whence[mid].file,
manifest_free_whence[mid].line,
manifest_free_whence[mid].function
);
exit(-1);
}
/* Free variable and signature blocks. */
unsigned i;
@ -857,17 +927,6 @@ int rhizome_write_manifest_file(rhizome_manifest *m, const char *path, char appe
return ret;
}
/*
Adds a group that this bundle should be present in. If we have the means to sign
the bundle as a member of that group, then we create the appropriate signature block.
The group signature blocks, like all signature blocks, will be appended to the
manifest data during the finalisation process.
*/
int rhizome_manifest_add_group(rhizome_manifest *m,char *groupid)
{
return WHY("Not implemented.");
}
int rhizome_manifest_dump(rhizome_manifest *m, const char *msg)
{
unsigned i;
@ -908,42 +967,41 @@ int rhizome_manifest_finalise(rhizome_manifest *m, rhizome_manifest **mout, int
OUT();
}
int rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t *authorSidp, rhizome_bk_t *bsk)
int rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t *authorSidp)
{
/* Fill in a few missing manifest fields, to make it easier to use when adding new files:
- the default service is FILE
- use the current time for "date"
- use the current time for "date" and "version"
- if service is file, then use the payload file's basename for "name"
*/
/* Set version of manifest, either from version variable, or using current time */
/* Set version of manifest from current time if not already set. */
if (m->version == 0)
rhizome_manifest_set_version(m, gettime_ms());
/* Set the manifest's author (not stored). This must be done before binding to a new ID (below).
* If no author was specified, then the manifest's "sender" field is used, if present.
/* Set the manifest's author. This must be done before binding to a new ID (below). If no author
* was specified, then the manifest's "sender" field is used, if present.
*/
if (authorSidp)
rhizome_manifest_set_author(m, authorSidp);
else if (m->has_sender)
rhizome_manifest_set_author(m, &m->sender);
if (!m->haveSecret) {
if (rhizome_bid_t_is_zero(m->cryptoSignPublic)) {
/* Set the bundle ID (public key) and secret key.
*/
if (!m->haveSecret && rhizome_bid_t_is_zero(m->cryptoSignPublic)) {
if (config.debug.rhizome)
DEBUG("creating new bundle");
if (rhizome_manifest_bind_id(m) == -1)
if (rhizome_manifest_createid(m) == -1)
return WHY("Could not bind manifest to an ID");
if (m->authorship != ANONYMOUS)
rhizome_manifest_add_bundle_key(m); // set the BK field
} else {
if (config.debug.rhizome)
DEBUGF("modifying existing bundle bid=%s", alloca_tohex_rhizome_bid_t(m->cryptoSignPublic));
// Modifying an existing bundle. Make sure we can find the bundle secret.
if (rhizome_extract_privatekey_required(m, bsk) == -1)
return -1;
// Modifying an existing bundle. Try to discover the bundle secret key and the author.
rhizome_authenticate_author(m);
// TODO assert that new version > old version?
}
}
assert(m->haveSecret);
if (m->service == NULL)
return WHYF("missing 'service'");
@ -984,3 +1042,50 @@ int rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t
return 0;
}
/* Work out the authorship status of the bundle without performing any cryptographic checks.
* Sets the 'authorship' element and returns 1 if an author was found, 0 if not.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rhizome_lookup_author(rhizome_manifest *m)
{
IN();
int cn, in, kp;
switch (m->authorship) {
case AUTHOR_NOT_CHECKED:
if (config.debug.rhizome)
DEBUGF("manifest[%d] lookup author=%s", m->manifest_record_number, alloca_tohex_sid_t(m->author));
cn = 0, in = 0, kp = 0;
if (keyring_find_sid(keyring, &cn, &in, &kp, &m->author)) {
if (config.debug.rhizome)
DEBUGF("found author");
m->authorship = AUTHOR_LOCAL;
RETURN(1);
}
// fall through
case ANONYMOUS:
if (m->has_sender) {
if (config.debug.rhizome)
DEBUGF("manifest[%d] lookup sender=%s", m->manifest_record_number, alloca_tohex_sid_t(m->sender));
cn = 0, in = 0, kp = 0;
if (keyring_find_sid(keyring, &cn, &in, &kp, &m->sender)) {
if (config.debug.rhizome)
DEBUGF("found sender");
rhizome_manifest_set_author(m, &m->sender);
m->authorship = AUTHOR_LOCAL;
RETURN(1);
}
}
case AUTHENTICATION_ERROR:
case AUTHOR_UNKNOWN:
case AUTHOR_IMPOSTOR:
RETURN(0);
case AUTHOR_LOCAL:
case AUTHOR_AUTHENTIC:
RETURN(1);
}
FATALF("m->authorship = %d", m->authorship);
RETURN(0);
OUT();
}

View File

@ -44,6 +44,8 @@ int rhizome_manifest_createid(rhizome_manifest *m)
return WHY("Failed to create keypair for manifest ID.");
rhizome_manifest_set_id(m, &m->cryptoSignPublic);
m->haveSecret = NEW_BUNDLE_ID;
// A new Bundle ID and secret invalidates any existing BK field.
rhizome_manifest_del_bundle_key(m);
return 0;
}
@ -60,7 +62,7 @@ static int generate_keypair(const char *seed, struct signing_key *key)
// The first 256 bits (32 bytes) of the hash will be used as the private key of the BID.
bcopy(hash, key->Private, sizeof key->Private);
if (crypto_sign_compute_public_key(key->Private, key->Public.binary))
if (crypto_sign_compute_public_key(key->Private, key->Public.binary) == -1)
return WHY("Could not generate public key");
// The last 32 bytes of the private key should be identical to the public key. This is what
// crypto_sign_edwards25519sha512batch_keypair() returns, and there is code that depends on it.
@ -81,13 +83,13 @@ int rhizome_get_bundle_from_seed(rhizome_manifest *m, const char *seed)
return -1;
if (ret == 1) {
// manifest not retrieved
rhizome_manifest_set_id(m, &key.Public);
rhizome_manifest_set_id(m, &key.Public); // zerofills m->cryptoSignSecret
m->haveSecret = NEW_BUNDLE_ID;
} else {
m->haveSecret = EXISTING_BUNDLE_ID;
}
bcopy(key.Private, m->cryptoSignSecret, sizeof m->cryptoSignSecret);
//Disabled for performance, but these asserts should always hold.
// Disabled for performance, these asserts should nevertheless always hold.
//assert(cmp_rhizome_bid_t(&m->cryptoSignPublic, &key.Public) == 0);
//assert(memcmp(m->cryptoSignPublic.binary, m->cryptoSignSecret + RHIZOME_BUNDLE_KEY_BYTES, sizeof m->cryptoSignPublic.binary) == 0);
return ret;
@ -124,10 +126,12 @@ int rhizome_bk_xor_stream(
return 0;
}
/*
CryptoSign Secret Keys in cupercop-20120525 onwards have the public key as the
second half of the secret key. The public key is the BID, so this simplifies
the BK<-->SECRET conversion processes. */
/* CryptoSign Secret Keys in cupercop-20120525 onwards have the public key as the second half of the
* secret key. The public key is the BID, so this simplifies the BK<-->SECRET conversion processes.
*
* Returns 0 if the BK decodes correctly to the bundle secret, 1 if not. Returns -1 if there is an
* error.
*/
int rhizome_bk2secret(rhizome_manifest *m,
const rhizome_bid_t *bidp,
const unsigned char *rs, const size_t rs_len,
@ -140,19 +144,14 @@ int rhizome_bk2secret(rhizome_manifest *m,
unsigned char xor_stream[RHIZOME_BUNDLE_KEY_BYTES];
if (rhizome_bk_xor_stream(bidp, rs, rs_len, xor_stream, RHIZOME_BUNDLE_KEY_BYTES))
RETURN(WHY("rhizome_bk_xor_stream() failed"));
int i;
/* XOR and store secret part of secret key */
for(i = 0; i != RHIZOME_BUNDLE_KEY_BYTES; i++)
unsigned i;
for (i = 0; i != RHIZOME_BUNDLE_KEY_BYTES; ++i)
secret[i] = bkin[i] ^ xor_stream[i];
/* Copy BID as public-key part of secret key */
for(;i!=crypto_sign_edwards25519sha512batch_SECRETKEYBYTES;++i)
secret[i] = bidp->binary[i - RHIZOME_BUNDLE_KEY_BYTES];
bzero(xor_stream, sizeof xor_stream);
RETURN(rhizome_verify_bundle_privatekey(m, secret, bidp->binary));
/* Copy BID as public-key part of secret key */
bcopy(bidp->binary, secret + RHIZOME_BUNDLE_KEY_BYTES, sizeof bidp->binary);
RETURN(rhizome_verify_bundle_privatekey(secret, bidp->binary) ? 0 : 1);
OUT();
}
@ -181,187 +180,208 @@ int rhizome_secret2bk(
}
/* Given the SID of a bundle's author, search for an identity in the keyring and return its
* Rhizome secret if found.
/* Given a SID, search the keyring for an identity with the same SID and return its Rhizome secret
* if found.
*
* Returns -1 if an error occurs.
* Returns 0 if the author's rhizome secret is found; '*rs' is set to point to the secret key in the
* keyring, and '*rs_len' is set to the key length.
* Returns 2 if the author's identity is not in the keyring.
* Returns 3 if the author's identity is in the keyring but has no rhizome secret.
* Returns FOUND_RHIZOME_SECRET if the author's rhizome secret is found; '*rs' is set to point to
* the secret key in the keyring, and '*rs_len' is set to the key length.
*
* Returns IDENTITY_NOT_FOUND if the SID is not in the keyring.
*
* Returns IDENTITY_HAS_NO_RHIZOME_SECRET if the SID is in the keyring but has no Rhizome Secret.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rhizome_find_secret(const sid_t *authorSidp, int *rs_len, const unsigned char **rs)
enum rhizome_secret_disposition find_rhizome_secret(const sid_t *authorSidp, size_t *rs_len, const unsigned char **rs)
{
IN();
int cn=0, in=0, kp=0;
if (!keyring_find_sid(keyring,&cn,&in,&kp, authorSidp)) {
if (config.debug.rhizome)
DEBUGF("identity sid=%s is not in keyring", alloca_tohex_sid_t(*authorSidp));
return 2;
RETURN(IDENTITY_NOT_FOUND);
}
kp = keyring_identity_find_keytype(keyring, cn, in, KEYTYPE_RHIZOME);
if (kp == -1) {
if (config.debug.rhizome)
DEBUGF("identity sid=%s has no Rhizome Secret", alloca_tohex_sid_t(*authorSidp));
return 3;
WARNF("Identity sid=%s has no Rhizome Secret", alloca_tohex_sid_t(*authorSidp));
RETURN(IDENTITY_HAS_NO_RHIZOME_SECRET);
}
int rslen = keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key_len;
if (rslen < 16 || rslen > 1024)
return WHYF("identity sid=%s has invalid Rhizome Secret: length=%d", alloca_tohex_sid_t(*authorSidp), rslen);
assert(rslen >= 16);
assert(rslen <= 1024);
if (rs_len)
*rs_len = rslen;
if (rs)
*rs = keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key;
return 0;
RETURN(FOUND_RHIZOME_SECRET);
}
/* Given the SID of a bundle's author and the bundle ID, XOR a bundle key (private or public) with
* RS##BID where RS is the rhizome secret of the bundle's author, and BID is the bundle's public key
* (aka the Bundle ID).
*
* This will convert a manifest BK field into the bundle's private key, or vice versa.
*
* Returns -1 if an error occurs.
* Returns 0 if the author's private key is located and the XOR is performed successfully.
* Returns 2 if the author's identity is not in the keyring (this return code from
* rhizome_find_secret()).
* Returns 3 if the author's identity is in the keyring but has no rhizome secret (this return code
* from rhizome_find_secret()).
*
* Looks up the SID in the keyring, and if it is present and has a valid-looking RS, calls
* rhizome_bk_xor_rs() to perform the XOR.
/* Attempt to authenticate the authorship of the given bundle, and set the 'authorship' element
* accordingly. If the manifest has nk BK field, then no authentication can be performed.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
/* See if the manifest has a BK entry, and if so, use it to obtain the private key for the BID. The
* manifest's 'author' field must contain the (binary) SID of the purported author of the bundle,
* which is used to look up the author's rhizome secret in the keyring.
*
* Returns 0 if a valid private key was extracted, with the private key in the manifest
* 'cryptoSignSecret' field and the 'haveSecret' field set to EXISTING_BUNDLE_ID.
*
* Returns 1 if the manifest does not have a BK field.
*
* Returns 2 if the author is not found in the keyring (not unlocked?) -- this return code from
* rhizome_bk_xor().
*
* Returns 3 if the author is found in the keyring but has no rhizome secret -- this return code
* from rhizome_bk_xor().
*
* Returns 4 if the author is found in the keyring and has a rhizome secret but the private bundle
* key formed using it does not verify.
*
* Returns -1 on error.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rhizome_extract_privatekey(rhizome_manifest *m, const rhizome_bk_t *bsk)
void rhizome_authenticate_author(rhizome_manifest *m)
{
if (config.debug.rhizome)
DEBUGF("manifest[%d] bsk=%s", m->manifest_record_number, bsk ? alloca_tohex_rhizome_bk_t(*bsk) : "NULL");
IN();
int result;
if (m->has_bundle_key) {
if (!m->has_author) {
result = rhizome_find_bundle_author(m);
} else {
int rs_len;
if (!m->has_bundle_key)
RETURNVOID;
switch (m->authorship) {
case ANONYMOUS:
rhizome_find_bundle_author_and_secret(m);
break;
case AUTHOR_NOT_CHECKED:
case AUTHOR_LOCAL: {
if (config.debug.rhizome)
DEBUGF("manifest[%d] authenticate author=%s", m->manifest_record_number, alloca_tohex_sid_t(m->author));
size_t rs_len;
const unsigned char *rs;
result = rhizome_find_secret(&m->author, &rs_len, &rs);
if (result == 0)
result = rhizome_bk2secret(m, &m->cryptoSignPublic, rs, rs_len, m->bundle_key.binary, m->cryptoSignSecret);
}
if (result == 0 && bsk && !rhizome_is_bk_none(bsk)){
// If a bundle secret key was supplied that does not match the secret key derived from the
// author, then warn but carry on using the author's.
if (memcmp(bsk->binary, m->cryptoSignSecret, sizeof bsk->binary) != 0)
WARNF("Supplied bundle secret key is invalid -- ignoring");
}
}else if (bsk && !rhizome_is_bk_none(bsk)){
bcopy(bsk->binary, m->cryptoSignSecret, sizeof bsk->binary);
bcopy(m->cryptoSignPublic.binary, m->cryptoSignSecret + sizeof bsk->binary, sizeof m->cryptoSignPublic.binary);
if (rhizome_verify_bundle_privatekey(m, m->cryptoSignSecret, m->cryptoSignPublic.binary))
result=5;
else
result=0;
}else{
result=1;
}
if (result == 0)
enum rhizome_secret_disposition d = find_rhizome_secret(&m->author, &rs_len, &rs);
switch (d) {
case FOUND_RHIZOME_SECRET:
if (config.debug.rhizome)
DEBUGF("author has Rhizome secret");
switch (rhizome_bk2secret(m, &m->cryptoSignPublic, rs, rs_len, m->bundle_key.binary, m->cryptoSignSecret)) {
case 0:
if (config.debug.rhizome)
DEBUGF("authentic");
m->authorship = AUTHOR_AUTHENTIC;
if (!m->haveSecret)
m->haveSecret = EXISTING_BUNDLE_ID;
else {
memset(m->cryptoSignSecret, 0, sizeof m->cryptoSignSecret);
m->haveSecret = SECRET_UNKNOWN;
break;
case -1:
if (config.debug.rhizome)
DEBUGF("error");
m->authorship = AUTHENTICATION_ERROR;
break;
default:
if (config.debug.rhizome)
DEBUGF("impostor");
m->authorship = AUTHOR_IMPOSTOR;
break;
}
break;
case IDENTITY_NOT_FOUND:
if (config.debug.rhizome)
DEBUGF("author not found");
m->authorship = AUTHOR_UNKNOWN;
break;
case IDENTITY_HAS_NO_RHIZOME_SECRET:
if (config.debug.rhizome)
DEBUGF("author has no Rhizome secret");
m->authorship = AUTHENTICATION_ERROR;
break;
default:
FATALF("find_rhizome_secret() returned unknown code %d", (int)d);
break;
}
}
break;
case AUTHENTICATION_ERROR:
case AUTHOR_UNKNOWN:
case AUTHOR_IMPOSTOR:
case AUTHOR_AUTHENTIC:
// work has already been done, don't repeat it
break;
default:
FATALF("m->authorship = %d", (int)m->authorship);
break;
}
RETURN(result);
OUT();
}
/* Same as rhizome_extract_privatekey, except warnings become errors and are logged */
int rhizome_extract_privatekey_required(rhizome_manifest *m, rhizome_bk_t *bsk)
/* If the given bundle secret key corresponds to the bundle's ID (public key) then store it in the
* manifest structure and mark the secret key as known. Return 1 if the secret key was assigned,
* 0 if not.
*
* This function should only be called on a manifest that already has a public key (ID) and does
* not have a known secret key.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rhizome_apply_bundle_secret(rhizome_manifest *m, const rhizome_bk_t *bsk)
{
int result = rhizome_extract_privatekey(m, bsk);
switch (result) {
case -1:
case 0:
return result;
case 1:
return WHY("Bundle contains no BK field, and no bundle secret supplied");
case 2:
return WHY("Author unknown");
case 3:
return WHY("Author does not have a Rhizome Secret");
case 4:
return WHY("Author does not have permission to modify manifest");
case 5:
return WHY("Bundle secret is not valid for this manifest");
default:
return WHYF("Unknown result from rhizome_extract_privatekey(): %d", result);
IN();
if (config.debug.rhizome)
DEBUGF("manifest[%d] bsk=%s", m->manifest_record_number, bsk ? alloca_tohex_rhizome_bk_t(*bsk) : "NULL");
assert(m->haveSecret == SECRET_UNKNOWN);
assert(is_all_matching(m->cryptoSignSecret, sizeof m->cryptoSignSecret, 0));
assert(!rhizome_bid_t_is_zero(m->cryptoSignPublic));
assert(bsk != NULL);
assert(!rhizome_is_bk_none(bsk));
if (rhizome_verify_bundle_privatekey(bsk->binary, m->cryptoSignPublic.binary)) {
if (config.debug.rhizome)
DEBUG("bundle secret verifies ok");
bcopy(bsk->binary, m->cryptoSignSecret, sizeof bsk->binary);
bcopy(m->cryptoSignPublic.binary, m->cryptoSignSecret + sizeof bsk->binary, sizeof m->cryptoSignPublic.binary);
m->haveSecret = EXISTING_BUNDLE_ID;
RETURN(1);
}
RETURN(0);
OUT();
}
/* Discover if the given manifest was created (signed) by any unlocked identity currently in the
* keyring.
*
* Returns 0 if an identity is found with permission to alter the bundle, after setting the manifest
* 'author' field to the SID of the identity and the manifest 'cryptoSignSecret' field to the bundle
* secret key and the 'haveSecret' field to EXISTING_BUNDLE_ID.
* This function must only be called if the bundle secret is not known. If it is known, then
* use
*
* Returns 1 if no identity in the keyring is the author of this bundle.
* If the authorship is already known (ie, not ANONYMOUS) then returns without changing anything.
* That means this function can be called several times on the same manifest, but will only perform
* any work the first time.
*
* Returns 4 if the manifest has no BK field.
* If the manifest has no bundle key (BK) field, then it is anonymous, so leaves 'authorship'
* unchanged and returns.
*
* Returns -1 if an error occurs, eg, the manifest contains an invalid BK field.
* If an identity is found in the keyring with permission to alter the bundle, then sets the
* manifest 'authorship' field to AUTHOR_AUTHENTIC, the 'author' field to the SID of the identity,
* the manifest 'cryptoSignSecret' field to the bundle secret key and the 'haveSecret' field to
* EXISTING_BUNDLE_ID.
*
* If no identity is found in the keyring that combines with the bundle key (BK) field to yield
* the bundle's secret key, then leaves the manifest 'authorship' field as ANONYMOUS.
*
* If an error occurs, eg, the keyring contains an invalid Rhizome Secret or a cryptographic
* operation fails, then sets the 'authorship' field to AUTHENTICATION_ERROR and leaves the
* 'author', 'haveSecret' and 'cryptoSignSecret' fields unchanged.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rhizome_find_bundle_author(rhizome_manifest *m)
void rhizome_find_bundle_author_and_secret(rhizome_manifest *m)
{
IN();
if (!m->has_bundle_key) {
if (config.debug.rhizome)
DEBUG("missing BK");
RETURN(4);
}
if (m->authorship != ANONYMOUS)
RETURNVOID;
assert(is_sid_t_any(m->author));
if (!m->has_bundle_key)
RETURNVOID;
int cn = 0, in = 0, kp = 0;
for (; keyring_next_identity(keyring, &cn, &in, &kp); ++kp) {
const sid_t *authorSidp = (const sid_t *) keyring->contexts[cn]->identities[in]->keypairs[kp]->public_key;
//if (config.debug.rhizome) DEBUGF("try author identity sid=%s", alloca_tohex_sid_t(*authorSidp));
int rkp = keyring_identity_find_keytype(keyring, cn, in, KEYTYPE_RHIZOME);
if (rkp != -1) {
int rs_len = keyring->contexts[cn]->identities[in]->keypairs[rkp]->private_key_len;
if (rs_len < 16 || rs_len > 1024)
RETURN(WHYF("invalid Rhizome Secret: length=%d", rs_len));
size_t rs_len = keyring->contexts[cn]->identities[in]->keypairs[rkp]->private_key_len;
if (rs_len < 16 || rs_len > 1024) {
WHYF("invalid Rhizome Secret: length=%zu", rs_len);
m->authorship = AUTHENTICATION_ERROR;
RETURNVOID;
}
const unsigned char *rs = keyring->contexts[cn]->identities[in]->keypairs[rkp]->private_key;
if (rhizome_bk2secret(m, &m->cryptoSignPublic, rs, rs_len, m->bundle_key.binary, m->cryptoSignSecret) == 0) {
unsigned char *secretp = m->cryptoSignSecret;
if (m->haveSecret)
secretp = alloca(sizeof m->cryptoSignSecret);
if (rhizome_bk2secret(m, &m->cryptoSignPublic, rs, rs_len, m->bundle_key.binary, secretp) == 0) {
if (m->haveSecret) {
if (memcmp(secretp, m->cryptoSignSecret, sizeof m->cryptoSignSecret) != 0)
FATALF("Bundle secret does not match derived secret");
} else
m->haveSecret = EXISTING_BUNDLE_ID;
if (!m->has_author || cmp_sid_t(&m->author, authorSidp) != 0){
if (config.debug.rhizome)
DEBUGF("found bundle author sid=%s", alloca_tohex_sid_t(*authorSidp));
rhizome_manifest_set_author(m, authorSidp);
m->authorship = AUTHOR_AUTHENTIC;
// if this bundle is already in the database, update the author.
if (m->inserttime)
sqlite_exec_void_loglevel(LOG_LEVEL_WARN,
@ -369,54 +389,35 @@ int rhizome_find_bundle_author(rhizome_manifest *m)
SID_T, &m->author,
RHIZOME_BID_T, &m->cryptoSignPublic,
END);
}
RETURN(0); // bingo
RETURNVOID; // bingo
}
}
}
assert(m->authorship == ANONYMOUS);
if (config.debug.rhizome)
DEBUG("bundle author not found");
RETURN(1);
OUT();
}
/* Verify the validity of the manifest's secret key, ie, is the given manifest's 'cryptoSignSecret'
* field actually the secret key corresponding to the public key in 'cryptoSignPublic'?
* Return 0 if valid, 1 if not. Return -1 if an error occurs.
/* Verify the validity of a given secret manifest key. Return 1 if valid, 0 if not.
*
* There is no NaCl API to efficiently test this. We use a modified version of
* crypto_sign_keypair() to accomplish this task.
*/
int rhizome_verify_bundle_privatekey(rhizome_manifest *m,
const unsigned char *sk,
const unsigned char *pkin)
int rhizome_verify_bundle_privatekey(const unsigned char *sk, const unsigned char *pkin)
{
IN();
unsigned char pk[32];
int i;
crypto_sign_compute_public_key(sk,pk);
for (i = 0;i < 32;++i)
if (pkin[i] != pk[i]) {
if (m&&sk==m->cryptoSignSecret&&pkin==m->cryptoSignPublic.binary)
m->haveSecret = SECRET_UNKNOWN;
RETURN(-1);
}
if (m&&sk==m->cryptoSignSecret&&pkin==m->cryptoSignPublic.binary) {
if (config.debug.rhizome)
DEBUGF("We have the private key for this bundle.");
m->haveSecret = EXISTING_BUNDLE_ID;
}
rhizome_bid_t pk;
if (crypto_sign_compute_public_key(sk, pk.binary) == -1)
RETURN(0);
OUT();
int ret = bcmp(pkin, pk.binary, sizeof pk.binary) == 0;
RETURN(ret);
}
int rhizome_sign_hash(rhizome_manifest *m,
rhizome_signature *out)
int rhizome_sign_hash(rhizome_manifest *m, rhizome_signature *out)
{
IN();
if (!m->haveSecret && rhizome_extract_privatekey_required(m, NULL))
RETURN(-1);
assert(m->haveSecret);
int ret = rhizome_sign_hash_with_key(m, m->cryptoSignSecret, m->cryptoSignPublic.binary, out);
RETURN(ret);
OUT();
@ -627,7 +628,7 @@ int rhizome_crypt_xor_block(unsigned char *buffer, size_t buffer_size, uint64_t
return 0;
}
int rhizome_derive_key(rhizome_manifest *m, rhizome_bk_t *bsk)
int rhizome_derive_payload_key(rhizome_manifest *m)
{
// don't do anything if the manifest isn't flagged as being encrypted
if (m->payloadEncryption != PAYLOAD_ENCRYPTED)
@ -655,9 +656,8 @@ int rhizome_derive_key(rhizome_manifest *m, rhizome_bk_t *bsk)
bcopy(hash, m->payloadKey, RHIZOME_CRYPT_KEY_BYTES);
}else{
if (!m->haveSecret && rhizome_extract_privatekey_required(m, bsk))
return -1;
assert(m->haveSecret);
if (!m->haveSecret)
return WHY("Cannot derive payload key because bundle secret is unknown");
unsigned char raw_key[9+crypto_sign_edwards25519sha512batch_SECRETKEYBYTES]="sasquatch";
bcopy(m->cryptoSignSecret, &raw_key[9], crypto_sign_edwards25519sha512batch_SECRETKEYBYTES);

View File

@ -167,6 +167,7 @@ void verify_bundles(){
ret=rhizome_store_bundle(m);
}
if (ret!=0){
if (config.debug.rhizome)
DEBUGF("Removing invalid manifest entry @%lld", rowid);
sqlite_exec_void_retry(&retry, "DELETE FROM MANIFESTS WHERE ROWID = ?;", INT64, rowid, END);
}
@ -1278,15 +1279,9 @@ int rhizome_store_bundle(rhizome_manifest *m)
if (!m->finalised)
return WHY("Manifest was not finalised");
if (m->haveSecret) {
/* We used to store the secret in the database, but we don't anymore, as we use
the BK field in the manifest. So nothing to do here. */
} else {
/* We don't have the secret for this manifest, so only allow updates if
the self-signature is valid */
if (!m->selfSigned)
// If we don't have the secret for this manifest, only store it if its self-signature is valid
if (!m->haveSecret && !m->selfSigned)
return WHY("Manifest is not signed, and I don't have the key. Manifest might be forged or corrupt.");
}
/* Bind BAR to data field */
unsigned char bar[RHIZOME_BAR_BYTES];
@ -1301,6 +1296,8 @@ int rhizome_store_bundle(rhizome_manifest *m)
if (sqlite_exec_void_retry(&retry, "BEGIN TRANSACTION;", END) == -1)
return WHY("Failed to begin transaction");
time_ms_t now = gettime_ms();
sqlite3_stmt *stmt;
if ((stmt = sqlite_prepare_bind(&retry,
"INSERT OR REPLACE INTO MANIFESTS("
@ -1323,11 +1320,12 @@ int rhizome_store_bundle(rhizome_manifest *m)
RHIZOME_BID_T, &m->cryptoSignPublic,
STATIC_BLOB, m->manifestdata, m->manifest_bytes,
INT64, m->version,
INT64, (int64_t) gettime_ms(),
INT64, (int64_t) now,
STATIC_BLOB, bar, RHIZOME_BAR_BYTES,
INT64, m->filesize,
RHIZOME_FILEHASH_T|NUL, m->filesize > 0 ? &m->filehash : NULL,
SID_T|NUL, m->has_author ? &m->author : NULL,
// Only store the author if it is known to be authentic.
SID_T|NUL, m->authorship == AUTHOR_AUTHENTIC ? &m->author : NULL,
STATIC_TEXT, m->service,
STATIC_TEXT|NUL, m->name,
SID_T|NUL, m->has_sender ? &m->sender : NULL,
@ -1341,6 +1339,7 @@ int rhizome_store_bundle(rhizome_manifest *m)
goto rollback;
sqlite3_finalize(stmt);
stmt = NULL;
rhizome_manifest_set_inserttime(m, now);
// if (serverMode)
// rhizome_sync_bundle_inserted(bar);
@ -1408,63 +1407,156 @@ rollback:
return -1;
}
int rhizome_list_manifests(struct cli_context *context, const char *service, const char *name,
const char *sender_hex, const char *recipient_hex,
int limit, int offset, char count_rows)
{
IN();
struct rhizome_list_cursor {
// Query parameters that narrow the set of listed bundles.
const char *service;
const char *name;
sid_t sender;
sid_t recipient;
// Set by calling the next() function.
int64_t rowid;
rhizome_manifest *manifest;
size_t rowcount;
// Private state.
sqlite3_stmt *_statement;
unsigned _offset;
};
/* The cursor struct must be zerofilled and the query parameters optionally filled in prior to
* calling this function.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static int rhizome_list_open(sqlite_retry_state *retry, struct rhizome_list_cursor *cursor)
{
IN();
strbuf b = strbuf_alloca(1024);
strbuf_sprintf(b, "SELECT id, manifest, version, inserttime, author, rowid FROM manifests WHERE 1=1");
if (service && *service)
strbuf_sprintf(b, " AND service = ?1");
if (name && *name)
strbuf_sprintf(b, " AND name like ?2");
if (sender_hex && *sender_hex) {
if (str_to_sid_t(&sender, sender_hex) == -1)
RETURN(WHYF("Invalid sender SID: %s", sender_hex));
strbuf_sprintf(b, " AND sender = ?3");
}
if (recipient_hex && *recipient_hex) {
if (str_to_sid_t(&recipient, recipient_hex) == -1)
RETURN(WHYF("Invalid recipient SID: %s", recipient_hex));
strbuf_sprintf(b, " AND recipient = ?4");
}
strbuf_sprintf(b, " ORDER BY inserttime DESC");
if (offset)
strbuf_sprintf(b, " OFFSET %u", offset);
if (cursor->service)
strbuf_puts(b, " AND service = @service");
if (cursor->name)
strbuf_puts(b, " AND name like @name");
if (!is_sid_t_any(cursor->sender))
strbuf_puts(b, " AND sender = @sender");
if (!is_sid_t_any(cursor->recipient))
strbuf_puts(b, " AND recipient = @recipient");
strbuf_puts(b, " ORDER BY inserttime DESC LIMIT -1 OFFSET @offset");
if (strbuf_overrun(b))
RETURN(WHYF("SQL command too long: %s", strbuf_str(b)));
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT;
sqlite3_stmt *statement = sqlite_prepare(&retry, strbuf_str(b));
if (!statement)
cursor->_statement = sqlite_prepare(retry, strbuf_str(b));
if (cursor->_statement == NULL)
RETURN(-1);
int ret = 0;
if (service && *service)
ret = sqlite3_bind_text(statement, 1, service, -1, SQLITE_STATIC);
if (ret==SQLITE_OK && name && *name)
ret = sqlite3_bind_text(statement, 2, name, -1, SQLITE_STATIC);
if (ret==SQLITE_OK && sender_hex && *sender_hex)
ret = sqlite3_bind_text(statement, 3, sender_hex, -1, SQLITE_STATIC);
if (ret==SQLITE_OK && recipient_hex && *recipient_hex)
ret = sqlite3_bind_text(statement, 4, recipient_hex, -1, SQLITE_STATIC);
if (ret!=SQLITE_OK){
ret = WHYF("Failed to bind parameters: %s", sqlite3_errmsg(rhizome_db));
goto cleanup;
if (sqlite_bind(retry, cursor->_statement, NAMED|INT, "@offset", cursor->_offset, END) == -1)
goto failure;
if (cursor->service && sqlite_bind(retry, cursor->_statement, NAMED|STATIC_TEXT, "@service", cursor->service, END) == -1)
goto failure;
if (cursor->name && sqlite_bind(retry, cursor->_statement, NAMED|STATIC_TEXT, "@name", cursor->name, END) == -1)
goto failure;
if (!is_sid_t_any(cursor->sender) && sqlite_bind(retry, cursor->_statement, NAMED|SID_T, "@sender", &cursor->sender, END) == -1)
goto failure;
if (!is_sid_t_any(cursor->recipient) && sqlite_bind(retry, cursor->_statement, NAMED|SID_T, "@recipient", &cursor->recipient, END) == -1)
goto failure;
cursor->manifest = NULL;
RETURN(0);
OUT();
failure:
sqlite3_finalize(cursor->_statement);
cursor->_statement = NULL;
RETURN(-1);
OUT();
}
ret=0;
size_t rows = 0;
static int rhizome_list_next(sqlite_retry_state *retry, struct rhizome_list_cursor *cursor)
{
IN();
if (cursor->_statement == NULL && rhizome_list_open(retry, cursor) == -1)
RETURN(-1);
while (sqlite_step_retry(retry, cursor->_statement) == SQLITE_ROW) {
++cursor->_offset;
if (cursor->manifest) {
rhizome_manifest_free(cursor->manifest);
cursor->manifest = NULL;
}
assert(sqlite3_column_count(cursor->_statement) == 6);
assert(sqlite3_column_type(cursor->_statement, 0) == SQLITE_TEXT);
assert(sqlite3_column_type(cursor->_statement, 1) == SQLITE_BLOB);
assert(sqlite3_column_type(cursor->_statement, 2) == SQLITE_INTEGER);
assert(sqlite3_column_type(cursor->_statement, 3) == SQLITE_INTEGER);
assert(sqlite3_column_type(cursor->_statement, 4) == SQLITE_TEXT || sqlite3_column_type(cursor->_statement, 4) == SQLITE_NULL);
assert(sqlite3_column_type(cursor->_statement, 5) == SQLITE_INTEGER);
const char *q_manifestid = (const char *) sqlite3_column_text(cursor->_statement, 0);
const char *manifestblob = (char *) sqlite3_column_blob(cursor->_statement, 1);
size_t manifestblobsize = sqlite3_column_bytes(cursor->_statement, 1); // must call after sqlite3_column_blob()
int64_t q_version = sqlite3_column_int64(cursor->_statement, 2);
int64_t q_inserttime = sqlite3_column_int64(cursor->_statement, 3);
const char *q_author = (const char *) sqlite3_column_text(cursor->_statement, 4);
cursor->rowid = sqlite3_column_int64(cursor->_statement, 5);
sid_t *author = NULL;
if (q_author) {
author = alloca(sizeof *author);
if (str_to_sid_t(author, q_author) == -1) {
WHYF("MANIFESTS row id=%s has invalid author column %s -- skipped", q_manifestid, alloca_str_toprint(q_author));
continue;
}
}
rhizome_manifest *m = cursor->manifest = rhizome_new_manifest();
if (m == NULL)
RETURN(-1);
if (rhizome_read_manifest_file(m, manifestblob, manifestblobsize) == -1) {
WHYF("MANIFESTS row id=%s has invalid manifest blob -- skipped", q_manifestid);
continue;
}
if (m->version != q_version) {
WHYF("MANIFESTS row id=%s version=%"PRId64" does not match manifest blob version=%"PRId64" -- skipped",
q_manifestid, q_version, m->version);
continue;
}
if (author)
rhizome_manifest_set_author(m, author);
rhizome_manifest_set_inserttime(m, q_inserttime);
if (cursor->service && !(m->service && strcasecmp(cursor->service, m->service) == 0))
continue;
if (!is_sid_t_any(cursor->sender) && !(m->has_sender && cmp_sid_t(&cursor->sender, &m->sender) == 0))
continue;
if (!is_sid_t_any(cursor->recipient) && !(m->has_recipient && cmp_sid_t(&cursor->recipient, &m->recipient) == 0))
continue;
// Don't do rhizome_verify_author(m); too CPU expensive for a listing. Save that for when
// the bundle is extracted or exported.
++cursor->rowcount;
RETURN(1);
}
RETURN(0);
OUT();
}
static void rhizome_list_release(struct rhizome_list_cursor *cursor)
{
if (cursor->manifest) {
rhizome_manifest_free(cursor->manifest);
cursor->manifest = NULL;
}
if (cursor->_statement) {
sqlite3_finalize(cursor->_statement);
cursor->_statement = NULL;
}
}
int rhizome_list_manifests(struct cli_context *context, const char *service, const char *name,
const char *sender_hex, const char *recipient_hex,
size_t rowlimit, size_t rowoffset, char count_rows)
{
IN();
struct rhizome_list_cursor cursor;
bzero(&cursor, sizeof cursor);
cursor.service = service && service[0] ? service : NULL;
cursor.name = name && name[0] ? name : NULL;
if (sender_hex && *sender_hex && str_to_sid_t(&cursor.sender, sender_hex) == -1)
RETURN(WHYF("Invalid sender SID: %s", sender_hex));
if (recipient_hex && *recipient_hex && str_to_sid_t(&cursor.recipient, recipient_hex) == -1)
RETURN(WHYF("Invalid recipient SID: %s", recipient_hex));
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT;
if (rhizome_list_open(&retry, &cursor) == -1)
RETURN(-1);
const char *names[]={
"_id",
"service",
@ -1480,105 +1572,42 @@ int rhizome_list_manifests(struct cli_context *context, const char *service, con
"recipient",
"name"
};
cli_columns(context, 13, names);
while (sqlite_step_retry(&retry, statement) == SQLITE_ROW) {
++rows;
if (limit>0 && rows>limit)
break;
if (!( sqlite3_column_count(statement) == 6
&& 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;
}
rhizome_manifest *m = rhizome_new_manifest();
if (m == NULL) {
ret = WHY("Out of manifests");
break;
}
const char *q_manifestid = (const char *) sqlite3_column_text(statement, 0);
const char *manifestblob = (char *) sqlite3_column_blob(statement, 1);
size_t manifestblobsize = sqlite3_column_bytes(statement, 1); // must call after sqlite3_column_blob()
int64_t q_version = sqlite3_column_int64(statement, 2);
int64_t q_inserttime = sqlite3_column_int64(statement, 3);
const char *q_author = (const char *) sqlite3_column_text(statement, 4);
int64_t rowid = sqlite3_column_int64(statement, 5);
if (rhizome_read_manifest_file(m, manifestblob, manifestblobsize) == -1) {
WARNF("MANIFESTS row id=%s has invalid manifest blob -- skipped", q_manifestid);
} else {
if (m->version != q_version)
WARNF("MANIFESTS row id=%s version=%"PRId64" does not match manifest blob.version=%"PRId64,
q_manifestid, q_version, m->version);
int match = 1;
if (service && service[0] && !(m->service && strcasecmp(m->service, service) == 0))
match = 0;
if (match && sender_hex && sender_hex[0]) {
if (!(m->has_sender && cmp_sid_t(&sender, &m->sender) == 0))
match = 0;
}
if (match && recipient_hex && recipient_hex[0]) {
if (!(m->has_recipient && cmp_sid_t(&recipient, &m->recipient) == 0))
match = 0;
}
if (match) {
int from_here = 0;
if (q_author) {
sid_t author;
if (str_to_sid_t(&author, q_author) == -1) {
WARNF("MANIFESTS row id=%s has invalid author=%s -- ignored", q_manifestid, alloca_str_toprint(q_author));
} else {
rhizome_manifest_set_author(m, &author);
int cn = 0, in = 0, kp = 0;
from_here = keyring_find_sid(keyring, &cn, &in, &kp, &m->author);
}
}
if (!from_here && m->has_sender) {
if (config.debug.rhizome)
DEBUGF("blob.sender=%s", alloca_tohex_sid_t(m->sender));
int cn = 0, in = 0, kp = 0;
from_here = keyring_find_sid(keyring, &cn, &in, &kp, &m->sender);
}
cli_put_long(context, rowid, ":");
cli_columns(context, NELS(names), names);
while (rhizome_list_next(&retry, &cursor) == 1) {
rhizome_manifest *m = cursor.manifest;
assert(m->filesize != RHIZOME_SIZE_UNSET);
if (cursor.rowcount < rowoffset)
continue;
if (rowlimit == 0 || cursor.rowcount <= rowlimit) {
rhizome_lookup_author(m);
cli_put_long(context, cursor.rowid, ":");
cli_put_string(context, m->service, ":");
cli_put_hexvalue(context, m->cryptoSignPublic.binary, sizeof m->cryptoSignPublic.binary, ":");
cli_put_long(context, m->version, ":");
cli_put_long(context, m->has_date ? m->date : 0, ":");
cli_put_long(context, q_inserttime, ":");
cli_put_hexvalue(context, m->has_author ? m->author.binary : NULL, sizeof m->author.binary, ":");
cli_put_long(context, from_here, ":");
assert(m->filesize != RHIZOME_SIZE_UNSET);
cli_put_long(context, m->inserttime, ":");
switch (m->authorship) {
case AUTHOR_LOCAL:
case AUTHOR_AUTHENTIC:
cli_put_hexvalue(context, m->author.binary, sizeof m->author.binary, ":");
cli_put_long(context, 1, ":");
break;
default:
cli_put_string(context, NULL, ":");
cli_put_long(context, 0, ":");
break;
}
cli_put_long(context, m->filesize, ":");
cli_put_hexvalue(context, m->filesize ? m->filehash.binary : NULL, sizeof m->filehash.binary, ":");
cli_put_hexvalue(context, m->has_sender ? m->sender.binary : NULL, sizeof m->sender.binary, ":");
cli_put_hexvalue(context, m->has_recipient ? m->recipient.binary : NULL, sizeof m->recipient.binary, ":");
cli_put_string(context, m->name, "\n");
} else if (!count_rows)
break;
}
}
if (m) rhizome_manifest_free(m);
}
if (ret==0 && count_rows){
while (sqlite_step_retry(&retry, statement) == SQLITE_ROW)
++rows;
}
cli_row_count(context, rows);
cleanup:
sqlite3_finalize(statement);
RETURN(ret);
rhizome_list_release(&cursor);
cli_row_count(context, cursor.rowcount);
RETURN(0);
OUT();
}
@ -1680,9 +1709,9 @@ int rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found)
rhizome_manifest_set_author(blob_m, &author);
}
// check that we can re-author this manifest
if (rhizome_extract_privatekey(blob_m, NULL)){
rhizome_authenticate_author(blob_m);
if (m->authorship != AUTHOR_AUTHENTIC)
goto next;
}
*found = blob_m;
if (config.debug.rhizome)
DEBUGF("Found duplicate payload, %s", q_manifestid);
@ -1715,7 +1744,7 @@ static int unpack_manifest_row(rhizome_manifest *m, sqlite3_stmt *statement)
}
if (m->version != q_version)
WARNF("Version mismatch, manifest is %"PRId64", database is %"PRId64, m->version, q_version);
m->inserttime = q_inserttime;
rhizome_manifest_set_inserttime(m, q_inserttime);
return 0;
}

View File

@ -54,7 +54,7 @@ static void rhizome_direct_clear_temporary_files(rhizome_http_request *r)
}
}
int rhizome_direct_import_end(struct http_request *hr)
static int rhizome_direct_import_end(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
if (!r->received_manifest) {
@ -168,7 +168,7 @@ int rhizome_direct_enquiry_end(struct http_request *hr)
return 0;
}
int rhizome_direct_addfile_end(struct http_request *hr)
static int rhizome_direct_addfile_end(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
// If given a file without a manifest, we should only accept if it we are configured to do so, and
@ -222,13 +222,12 @@ int rhizome_direct_addfile_end(struct http_request *hr)
return 0;
}
// If manifest template did not specify a service field, then by default it is "file".
if (!rhizome_is_bk_none(&config.rhizome.api.addfile.bundle_secret_key))
rhizome_apply_bundle_secret(m, &config.rhizome.api.addfile.bundle_secret_key);
if (m->service == NULL)
rhizome_manifest_set_service(m, RHIZOME_SERVICE_FILE);
sid_t *author = NULL;
if (!is_sid_t_any(config.rhizome.api.addfile.default_author))
author = &config.rhizome.api.addfile.default_author;
rhizome_bk_t bsk = config.rhizome.api.addfile.bundle_secret_key;
if (rhizome_fill_manifest(m, r->data_file_name, author, &bsk)) {
const sid_t *author = is_sid_t_any(config.rhizome.api.addfile.default_author) ? NULL : &config.rhizome.api.addfile.default_author;
if (rhizome_fill_manifest(m, r->data_file_name, author)) {
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
http_request_simple_response(&r->http, 500, "Internal Error: Could not fill manifest");
@ -271,14 +270,14 @@ int rhizome_direct_addfile_end(struct http_request *hr)
}
}
void rhizome_direct_process_mime_start(struct http_request *hr)
static void rhizome_direct_process_mime_start(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
assert(r->current_part == NONE);
assert(r->part_fd == -1);
}
void rhizome_direct_process_mime_end(struct http_request *hr)
static void rhizome_direct_process_mime_end(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
if (r->part_fd != -1) {
@ -302,19 +301,19 @@ void rhizome_direct_process_mime_end(struct http_request *hr)
r->current_part = NONE;
}
void rhizome_direct_process_mime_content_disposition(struct http_request *hr, const struct mime_content_disposition *cd)
static void rhizome_direct_process_mime_part_header(struct http_request *hr, const struct mime_part_headers *h)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
if (strcmp(cd->name, "data") == 0) {
if (strcmp(h->content_disposition.name, "data") == 0) {
r->current_part = DATA;
strncpy(r->data_file_name, cd->filename, sizeof r->data_file_name)[sizeof r->data_file_name - 1] = '\0';
strncpy(r->data_file_name, h->content_disposition.filename, sizeof r->data_file_name)[sizeof r->data_file_name - 1] = '\0';
}
else if (strcmp(cd->name, "manifest") == 0) {
else if (strcmp(h->content_disposition.name, "manifest") == 0) {
r->current_part = MANIFEST;
} else
return;
char path[512];
if (form_temporary_file_path(r, path, cd->name) == -1) {
if (form_temporary_file_path(r, path, h->content_disposition.name) == -1) {
http_request_simple_response(&r->http, 500, "Internal Error: Buffer overrun");
return;
}
@ -325,7 +324,7 @@ void rhizome_direct_process_mime_content_disposition(struct http_request *hr, co
}
}
void rhizome_direct_process_mime_body(struct http_request *hr, const char *buf, size_t len)
static void rhizome_direct_process_mime_body(struct http_request *hr, const char *buf, size_t len)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
if (r->part_fd != -1) {
@ -344,7 +343,7 @@ int rhizome_direct_import(rhizome_http_request *r, const char *remainder)
}
r->http.form_data.handle_mime_part_start = rhizome_direct_process_mime_start;
r->http.form_data.handle_mime_part_end = rhizome_direct_process_mime_end;
r->http.form_data.handle_mime_content_disposition = rhizome_direct_process_mime_content_disposition;
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
r->http.handle_content_end = rhizome_direct_import_end;
r->current_part = NONE;
@ -361,7 +360,7 @@ int rhizome_direct_enquiry(rhizome_http_request *r, const char *remainder)
}
r->http.form_data.handle_mime_part_start = rhizome_direct_process_mime_start;
r->http.form_data.handle_mime_part_end = rhizome_direct_process_mime_end;
r->http.form_data.handle_mime_content_disposition = rhizome_direct_process_mime_content_disposition;
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
r->http.handle_content_end = rhizome_direct_enquiry_end;
r->current_part = NONE;
@ -394,7 +393,7 @@ int rhizome_direct_addfile(rhizome_http_request *r, const char *remainder)
}
r->http.form_data.handle_mime_part_start = rhizome_direct_process_mime_start;
r->http.form_data.handle_mime_part_end = rhizome_direct_process_mime_end;
r->http.form_data.handle_mime_content_disposition = rhizome_direct_process_mime_content_disposition;
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
r->http.handle_content_end = rhizome_direct_addfile_end;
r->current_part = NONE;

View File

@ -114,7 +114,6 @@ struct rhizome_fetch_candidate queue3[4];
struct rhizome_fetch_candidate queue4[2];
struct rhizome_fetch_candidate queue5[2];
#define NELS(a) (sizeof (a) / sizeof *(a))
#define slotno(slot) (int)((struct rhizome_fetch_queue *)(slot) - &rhizome_fetch_queues[0])
/* Static allocation of the queue structures. Must be in order of ascending log_size_threshold.

View File

@ -34,24 +34,29 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define RHIZOME_SERVER_MAX_LIVE_REQUESTS 32
typedef int HTTP_HANDLER(rhizome_http_request *r, const char *remainder);
struct http_handler{
const char *path;
int (*parser)(rhizome_http_request *r, const char *remainder);
HTTP_HANDLER *parser;
};
static int rhizome_status_page(rhizome_http_request *r, const char *remainder);
static int rhizome_file_page(rhizome_http_request *r, const char *remainder);
static int manifest_by_prefix_page(rhizome_http_request *r, const char *remainder);
static int interface_page(rhizome_http_request *r, const char *remainder);
static int neighbour_page(rhizome_http_request *r, const char *remainder);
static int fav_icon_header(rhizome_http_request *r, const char *remainder);
static int root_page(rhizome_http_request *r, const char *remainder);
static HTTP_HANDLER restful_rhizome_bundlelist_json;
extern int rhizome_direct_import(rhizome_http_request *r, const char *remainder);
extern int rhizome_direct_enquiry(rhizome_http_request *r, const char *remainder);
extern int rhizome_direct_dispatch(rhizome_http_request *r, const char *remainder);
static HTTP_HANDLER rhizome_status_page;
static HTTP_HANDLER rhizome_file_page;
static HTTP_HANDLER manifest_by_prefix_page;
static HTTP_HANDLER interface_page;
static HTTP_HANDLER neighbour_page;
static HTTP_HANDLER fav_icon_header;
static HTTP_HANDLER root_page;
extern HTTP_HANDLER rhizome_direct_import;
extern HTTP_HANDLER rhizome_direct_enquiry;
extern HTTP_HANDLER rhizome_direct_dispatch;
struct http_handler paths[]={
{"/restful/rhizome/bundlelist.json", restful_rhizome_bundlelist_json},
{"/rhizome/status", rhizome_status_page},
{"/rhizome/file/", rhizome_file_page},
{"/rhizome/import", rhizome_direct_import},
@ -312,6 +317,43 @@ int is_http_header_complete(const char *buf, size_t len, size_t read_since_last_
OUT();
}
/* Return 1 if the given authorization credentials are acceptable.
* Return 0 if not.
*/
static int is_authorized(struct http_client_authorization *auth)
{
if (auth->scheme != BASIC)
return 0;
unsigned i;
for (i = 0; i != config.rhizome.api.restful.users.ac; ++i) {
if ( strcmp(config.rhizome.api.restful.users.av[i].key, auth->credentials.basic.user) == 0
&& strcmp(config.rhizome.api.restful.users.av[i].value.password, auth->credentials.basic.password) == 0
)
return 1;
}
return 0;
}
static int restful_rhizome_bundlelist_json(rhizome_http_request *r, const char *remainder)
{
if (!is_rhizome_http_enabled())
return 1;
if (*remainder)
return 1;
if (r->http.verb != HTTP_VERB_GET) {
http_request_simple_response(&r->http, 405, NULL);
return 0;
}
if (!is_authorized(&r->http.request_header.authorization)) {
r->http.response.header.www_authenticate.scheme = BASIC;
r->http.response.header.www_authenticate.realm = "Serval Rhizome";
http_request_simple_response(&r->http, 401, NULL);
return 0;
}
http_request_simple_response(&r->http, 200, NULL);
return 0;
}
static int neighbour_page(rhizome_http_request *r, const char *remainder)
{
if (r->http.verb != HTTP_VERB_GET) {

View File

@ -637,13 +637,13 @@ int rhizome_stat_file(rhizome_manifest *m, const char *filepath)
return 0;
}
static int rhizome_write_derive_key(rhizome_manifest *m, rhizome_bk_t *bsk, struct rhizome_write *write)
static int rhizome_write_derive_key(rhizome_manifest *m, struct rhizome_write *write)
{
if (m->payloadEncryption != PAYLOAD_ENCRYPTED)
return 0;
// if the manifest specifies encryption, make sure we can generate the payload key and encrypt the contents as we go
if (rhizome_derive_key(m, bsk))
if (rhizome_derive_payload_key(m))
return -1;
if (config.debug.rhizome)
@ -664,7 +664,7 @@ int rhizome_write_open_manifest(struct rhizome_write *write, rhizome_manifest *m
if (rhizome_open_write(write, NULL, m->filesize, RHIZOME_PRIORITY_DEFAULT))
return -1;
if (rhizome_write_derive_key(m, NULL, write))
if (rhizome_write_derive_key(m, write))
return -1;
return 0;
}
@ -1083,13 +1083,13 @@ static int write_file(struct rhizome_read *read, const char *filepath){
return ret;
}
static int read_derive_key(rhizome_manifest *m, rhizome_bk_t *bsk, struct rhizome_read *read_state)
static int read_derive_key(rhizome_manifest *m, struct rhizome_read *read_state)
{
read_state->crypt = m->payloadEncryption == PAYLOAD_ENCRYPTED;
if (read_state->crypt){
// if the manifest specifies encryption, make sure we can generate the payload key and encrypt
// the contents as we go
if (rhizome_derive_key(m, bsk)) {
if (rhizome_derive_payload_key(m)) {
rhizome_read_close(read_state);
return WHY("Unable to decrypt bundle, valid key not found");
}
@ -1103,11 +1103,11 @@ static int read_derive_key(rhizome_manifest *m, rhizome_bk_t *bsk, struct rhizom
return 0;
}
int rhizome_open_decrypt_read(rhizome_manifest *m, rhizome_bk_t *bsk, struct rhizome_read *read_state)
int rhizome_open_decrypt_read(rhizome_manifest *m, struct rhizome_read *read_state)
{
int ret = rhizome_open_read(read_state, &m->filehash);
if (ret == 0)
ret = read_derive_key(m, bsk, read_state);
ret = read_derive_key(m, read_state);
return ret;
}
@ -1116,11 +1116,11 @@ int rhizome_open_decrypt_read(rhizome_manifest *m, rhizome_bk_t *bsk, struct rhi
*
* Returns -1 on error, 0 if extracted successfully, 1 if not found.
*/
int rhizome_extract_file(rhizome_manifest *m, const char *filepath, rhizome_bk_t *bsk)
int rhizome_extract_file(rhizome_manifest *m, const char *filepath)
{
struct rhizome_read read_state;
bzero(&read_state, sizeof read_state);
int ret = rhizome_open_decrypt_read(m, bsk, &read_state);
int ret = rhizome_open_decrypt_read(m, &read_state);
if (ret == 0)
ret = write_file(&read_state, filepath);
rhizome_read_close(&read_state);
@ -1185,7 +1185,7 @@ int rhizome_journal_pipe(struct rhizome_write *write, const rhizome_filehash_t *
}
// open an existing journal bundle, advance the head pointer, duplicate the existing content and get ready to add more.
int rhizome_write_open_journal(struct rhizome_write *write, rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, uint64_t new_size)
int rhizome_write_open_journal(struct rhizome_write *write, rhizome_manifest *m, uint64_t advance_by, uint64_t new_size)
{
int ret = 0;
@ -1213,7 +1213,7 @@ int rhizome_write_open_journal(struct rhizome_write *write, rhizome_manifest *m,
goto failure;
}
ret = rhizome_write_derive_key(m, bsk, write);
ret = rhizome_write_derive_key(m, write);
if (ret)
goto failure;
@ -1225,12 +1225,12 @@ failure:
return ret;
}
int rhizome_append_journal_buffer(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, unsigned char *buffer, size_t len)
int rhizome_append_journal_buffer(rhizome_manifest *m, uint64_t advance_by, unsigned char *buffer, size_t len)
{
struct rhizome_write write;
bzero(&write, sizeof write);
int ret = rhizome_write_open_journal(&write, m, bsk, advance_by, (uint64_t) len);
int ret = rhizome_write_open_journal(&write, m, advance_by, (uint64_t) len);
if (ret)
return -1;
@ -1253,7 +1253,7 @@ failure:
return ret;
}
int rhizome_append_journal_file(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, const char *filename)
int rhizome_append_journal_file(rhizome_manifest *m, uint64_t advance_by, const char *filename)
{
struct stat stat;
if (lstat(filename,&stat))
@ -1261,7 +1261,7 @@ int rhizome_append_journal_file(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t
struct rhizome_write write;
bzero(&write, sizeof write);
int ret = rhizome_write_open_journal(&write, m, bsk, advance_by, stat.st_size);
int ret = rhizome_write_open_journal(&write, m, advance_by, stat.st_size);
if (ret)
return -1;

View File

@ -402,6 +402,18 @@ strbuf strbuf_append_iovec(strbuf sb, const struct iovec *iov, int iovcnt)
return sb;
}
strbuf strbuf_append_quoted_string(strbuf sb, const char *str)
{
strbuf_putc(sb, '"');
for (; *str; ++str) {
if (*str == '"' || *str == '\\')
strbuf_putc(sb, '\\');
strbuf_putc(sb, *str);
}
strbuf_putc(sb, '"');
return sb;
}
strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, unsigned nels)
{
unsigned i;
@ -427,31 +439,47 @@ strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, uns
return sb;
}
strbuf strbuf_append_mime_content_disposition(strbuf sb, const struct mime_content_disposition *cd)
strbuf strbuf_append_mime_content_type(strbuf sb, const struct mime_content_type *ct)
{
strbuf_puts(sb, "type=");
strbuf_toprint_quoted(sb, "``", cd->type);
strbuf_puts(sb, " name=");
strbuf_toprint_quoted(sb, "``", cd->name);
strbuf_puts(sb, " filename=");
strbuf_toprint_quoted(sb, "``", cd->filename);
strbuf_puts(sb, " size=");
strbuf_sprintf(sb, "%"PRIhttp_size_t, cd->size);
struct tm tm;
strbuf_puts(sb, " creation_date=");
if (cd->creation_date)
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->creation_date, &tm));
else
strbuf_puts(sb, "0");
strbuf_puts(sb, " modification_date=");
if (cd->modification_date)
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->modification_date, &tm));
else
strbuf_puts(sb, "0");
strbuf_puts(sb, " read_date=");
if (cd->read_date)
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->read_date, &tm));
else
strbuf_puts(sb, "0");
strbuf_puts(sb, ct->type);
strbuf_putc(sb, '/');
strbuf_puts(sb, ct->subtype);
if (ct->charset) {
strbuf_puts(sb, "; charset=");
strbuf_append_quoted_string(sb, ct->charset);
}
if (ct->multipart_boundary) {
strbuf_puts(sb, "; boundary=");
strbuf_append_quoted_string(sb, ct->multipart_boundary);
}
return sb;
}
strbuf strbuf_append_mime_content_disposition(strbuf sb, const struct mime_content_disposition *cd)
{
strbuf_puts(sb, cd->type);
if (cd->name) {
strbuf_puts(sb, "; name=");
strbuf_append_quoted_string(sb, cd->name);
}
if (cd->filename) {
strbuf_puts(sb, "; filename=");
strbuf_append_quoted_string(sb, cd->filename);
}
if (cd->size)
strbuf_sprintf(sb, "; size=%"PRIhttp_size_t, cd->size);
struct tm tm;
if (cd->creation_date) {
strbuf_puts(sb, " creation_date=");
strbuf_append_strftime(sb, "\"%a, %d %b %Y %T %z\"", gmtime_r(&cd->creation_date, &tm));
}
if (cd->modification_date) {
strbuf_puts(sb, " modification_date=");
strbuf_append_strftime(sb, "\"%a, %d %b %Y %T %z\"", gmtime_r(&cd->modification_date, &tm));
}
if (cd->read_date) {
strbuf_puts(sb, " read_date=");
strbuf_append_strftime(sb, "\"%a, %d %b %Y %T %z\"", gmtime_r(&cd->read_date, &tm));
}
return sb;
}

View File

@ -145,6 +145,12 @@ struct iovec;
strbuf strbuf_append_iovec(strbuf sb, const struct iovec *iov, int iovcnt);
#define alloca_iovec(iov,cnt) strbuf_str(strbuf_append_iovec(strbuf_alloca(200), (iov), (cnt)))
/* Append a string using HTTP quoted-string format: delimited by double quotes (") and
* internal double quotes and backslash escaped by leading backslash.
* @author Andrew Bettison <andrew@servalproject.com>
*/
strbuf strbuf_append_quoted_string(strbuf sb, const char *str);
/* Append a representation of a struct http_range[] array.
* @author Andrew Bettison <andrew@servalproject.com>
*/
@ -152,7 +158,14 @@ struct http_range;
strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, unsigned nels);
#define alloca_http_ranges(ra) strbuf_str(strbuf_append_http_ranges(strbuf_alloca(25*NELS(ra)), (ra), NELS(ra)))
/* Append a representation of a struct mime_content_disposition struct.
/* Append a representation of a struct mime_content_type in HTTP header format.
* @author Andrew Bettison <andrew@servalproject.com>
*/
struct mime_content_type;
strbuf strbuf_append_mime_content_type(strbuf, const struct mime_content_type *);
#define alloca_mime_content_type(ct) strbuf_str(strbuf_append_mime_content_type(strbuf_alloca(500), (ct)))
/* Append a representation of a struct mime_content_disposition, in HTTP header format.
* @author Andrew Bettison <andrew@servalproject.com>
*/
struct mime_content_disposition;

View File

@ -920,8 +920,8 @@ _tfw_assertExpr() {
_tfw_get_content() {
case "$_tfw_opt_line_sed" in
'') ln -f "$1" "$_tfw_process_tmp/content" || error "ln failed";;
*) $SED -n -e "${_tfw_opt_line_sed}p" "$1" >"$_tfw_process_tmp/content" || error "sed failed";;
'') cat "$1" >|"$_tfw_process_tmp/content" || error "cat failed";;
*) $SED -n -e "${_tfw_opt_line_sed}p" "$1" >|"$_tfw_process_tmp/content" || error "sed failed";;
esac
}

View File

@ -25,6 +25,7 @@ source "${0%/*}/../testdefs_rhizome.sh"
shopt -s extglob
setup() {
CR=' '
setup_curl 7
setup_jq 1.2
setup_servald
@ -41,8 +42,20 @@ setup() {
get_rhizome_server_port PORTA +A
}
finally() {
stop_all_servald_servers
}
teardown() {
kill_all_servald_processes
assert_no_servald_processes
report_all_servald_servers
}
set_rhizome_config() {
executeOk_servald config \
set debug.httpd on \
set debug.rhizome_httpd on \
set debug.rhizome on \
set debug.verbose on \
set log.console.level debug
@ -50,30 +63,40 @@ set_rhizome_config() {
doc_AuthBasicMissing="Basic Authentication credentials are required"
test_AuthBasicMissing() {
execute --exit-status=67 curl \
--silent --fail --show-error \
executeOk curl \
--silent --show-error --write-out '%{http_code}' \
--output http.output \
--dump-header http.headers \
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
assertStdoutIs '401'
assertGrep http.headers "^WWW-Authenticate: Basic realm=\"Serval Rhizome\"$CR$"
}
teardown_AuthBasicMissing() {
tfw_cat http.headers http.output
teardown
}
doc_AuthBasicWrong="Basic Authentication credentials must be correct"
test_AuthBasicWrong() {
execute --exit-status=67 curl \
--silent --fail --show-error \
executeOk curl \
--silent --show-error --write-out '%{http_code}' \
--output http.output \
--dump-header http.headers \
--basic --user fred:nurks \
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
assertStdoutIs '401'
assertGrep http.headers "^WWW-Authenticate: Basic realm=\"Serval Rhizome\"$CR$"
executeOk curl \
--silent --fail --show-error \
--silent --fail --show-error --write-out '%{http_code}' \
--output http.output \
--dump-header http.headers \
--basic --user ron:weasley \
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
assertStdoutIs '200'
}
teardown_AuthBasicWrong() {
tfw_cat http.headers http.output
teardown
}
doc_RhizomeList="Fetch full Rhizome bundle list in JSON format"

View File

@ -651,7 +651,7 @@ test_AddUpdateNoAuthor() {
execute $servald rhizome add file $SIDB1 file1_2 file1_2.manifest
tfw_cat --stderr
assertExitStatus '!=' 0
# Rhizome store contents have old payload.
# Rhizome store contents have old payload, with the original author.
executeOk_servald rhizome list
assert_rhizome_list --fromhere=1 --author=$SIDB1 file1 file2
}
@ -664,9 +664,10 @@ test_AddUpdateNoAuthorWithSecret() {
tfw_cat -v file1_2.manifest
executeOk_servald rhizome add file $SIDB1 file1_2 file1_2.manifest "$file1_secret"
tfw_cat --stderr
# Rhizome store contents have new payload.
# Rhizome store contents have new payload, but it has lost its author (no BK
# field any more).
executeOk_servald rhizome list
assert_rhizome_list --fromhere=1 --author=$SIDB1 file1_2 file2
assert_rhizome_list --fromhere=0 file1_2 --fromhere=1 --author=$SIDB1 file2
}
doc_AddUpdateAutoVersion="Add new payload to existing manifest with automatic version"