mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-01-18 02:39:44 +00:00
Merge branch 'naf4' into 'development'
Big refactor of Rhizome author and bundle secret handling.
This commit is contained in:
commit
e00c945fd1
@ -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;
|
||||
|
@ -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
|
||||
|
574
http_server.c
574
http_server.c
@ -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, ¶m) && _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);
|
||||
}
|
||||
|
@ -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];
|
||||
};
|
||||
|
||||
|
@ -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 */
|
||||
|
28
meshms.c
28
meshms.c
@ -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
2
os.h
@ -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
|
||||
|
94
rhizome.c
94
rhizome.c
@ -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)
|
||||
|
71
rhizome.h
71
rhizome.h
@ -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);
|
||||
|
205
rhizome_bundle.c
205
rhizome_bundle.c
@ -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();
|
||||
}
|
||||
|
348
rhizome_crypto.c
348
rhizome_crypto.c
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user