2014-07-23 08:01:47 +00:00
|
|
|
/*
|
|
|
|
Serval DNA HTTP RESTful interface
|
2015-10-13 08:23:23 +00:00
|
|
|
Copyright (C) 2013-2015 Serval Project Inc.
|
2014-07-23 08:01:47 +00:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
2017-09-11 03:13:38 +00:00
|
|
|
#include "lang.h" // for FALLTHROUGH
|
2014-07-23 08:01:47 +00:00
|
|
|
#include "serval.h"
|
|
|
|
#include "conf.h"
|
|
|
|
#include "httpd.h"
|
|
|
|
#include "server.h"
|
|
|
|
#include "keyring.h"
|
|
|
|
#include "strbuf_helpers.h"
|
2017-12-19 02:09:29 +00:00
|
|
|
#include "dataformats.h"
|
2014-07-23 08:01:47 +00:00
|
|
|
|
Switch to feature-driven linking
This introduces a new way of linking Serval executables and dynamic
libraries from static libraries like libservald.a -- called
"feature-driven" linking.
The Makefile now links servald and serval-tests from libservald.a,
rather than from an explicit list of object (.o) files. Thanks to the
section-based method for registering functions such as HTTP handlers,
CLI commands and MDP handlers, these object files had become
"stand-alone" and hence were no longer included in the link because
there was no unresolved reference that required them to be linked in.
The new "feature.h" provides the DECLARE_FEATURE(name) macro that each
stand-alone source file uses to declare the named feature(s) it
provides. Each executable can call the USE_FEATURE(name) macro in any
of its explicitly-linked source files to cause the corresponding
object(s) to be included in the link, eg, servald_features.c.
The DEFINE_BINDING() macro has been extended so that every individual
MDP binding is given a feature name based on its port number macro, eg,
"mdp_binding_MDP_PORT_ECHO".
Some features have been factored into their own separate source files so
they can be omitted or included in a build independently of each other:
- the MDP bindings for MDP_PORT_DNALOOKUP, MDP_PORT_ECHO,
MDP_PORT_TRACE, MDP_PORT_KEYMAPREQUEST, MDP_PORT_RHIZOME_xxx,
MDP_PORT_PROBE, MDP_PORT_STUN, MDP_PORT_STUNREQ
- the CLI "log" and "echo" commands
- the CLI "rhizome direct" command
The JNI source files are only compiled if the <jni.h> header is present,
otherwise they are omitted from libservald.so.
2016-10-13 02:58:23 +00:00
|
|
|
DEFINE_FEATURE(http_rest_keyring);
|
|
|
|
|
2014-07-23 08:01:47 +00:00
|
|
|
#define keyring_TOKEN_STRLEN (BASE64_ENCODED_LEN(sizeof(rhizome_bid_t) + sizeof(uint64_t)))
|
|
|
|
#define alloca_keyring_token(bid, offset) keyring_ token_to_str(alloca(keyring_TOKEN_STRLEN + 1), (bid), (offset))
|
|
|
|
|
2015-08-24 03:18:00 +00:00
|
|
|
DECLARE_HANDLER("/restful/keyring/", restful_keyring_);
|
|
|
|
|
2014-07-23 08:01:47 +00:00
|
|
|
static HTTP_HANDLER restful_keyring_identitylist_json;
|
2015-08-10 08:38:42 +00:00
|
|
|
static HTTP_HANDLER restful_keyring_add;
|
2016-11-04 06:30:40 +00:00
|
|
|
static HTTP_HANDLER restful_keyring_remove;
|
2015-08-24 13:10:16 +00:00
|
|
|
static HTTP_HANDLER restful_keyring_set;
|
2014-07-23 08:01:47 +00:00
|
|
|
|
2015-08-24 03:18:00 +00:00
|
|
|
static int restful_keyring_(httpd_request *r, const char *remainder)
|
2014-07-23 08:01:47 +00:00
|
|
|
{
|
2017-10-19 23:25:21 +00:00
|
|
|
r->http.response.header.content_type = &CONTENT_TYPE_JSON;
|
2014-07-23 08:01:47 +00:00
|
|
|
int ret = authorize_restful(&r->http);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
const char *verb = HTTP_VERB_GET;
|
|
|
|
HTTP_HANDLER *handler = NULL;
|
2015-08-24 13:10:16 +00:00
|
|
|
const char *end;
|
2014-07-23 08:01:47 +00:00
|
|
|
if (strcmp(remainder, "identities.json") == 0) {
|
|
|
|
handler = restful_keyring_identitylist_json;
|
|
|
|
verb = HTTP_VERB_GET;
|
|
|
|
remainder = "";
|
|
|
|
}
|
2015-08-10 08:38:42 +00:00
|
|
|
else if (strcmp(remainder, "add") == 0) {
|
|
|
|
handler = restful_keyring_add;
|
|
|
|
verb = HTTP_VERB_GET;
|
|
|
|
remainder = "";
|
|
|
|
}
|
2015-08-24 13:10:16 +00:00
|
|
|
else if (parse_sid_t(&r->sid1, remainder, -1, &end) != -1) {
|
|
|
|
remainder = end;
|
2016-11-04 06:30:40 +00:00
|
|
|
if (strcmp(remainder, "/remove") == 0) {
|
|
|
|
handler = restful_keyring_remove;
|
|
|
|
remainder = "";
|
|
|
|
}
|
|
|
|
else if (strcmp(remainder, "/set") == 0) {
|
2015-08-24 13:10:16 +00:00
|
|
|
handler = restful_keyring_set;
|
|
|
|
remainder = "";
|
|
|
|
}
|
|
|
|
}
|
2014-07-23 08:01:47 +00:00
|
|
|
if (handler == NULL)
|
|
|
|
return 404;
|
|
|
|
if (r->http.verb != verb)
|
|
|
|
return 405;
|
|
|
|
return handler(r, remainder);
|
|
|
|
}
|
|
|
|
|
2015-08-10 08:38:42 +00:00
|
|
|
static int http_request_keyring_response(struct httpd_request *r, uint16_t result, const char *message)
|
|
|
|
{
|
|
|
|
http_request_simple_response(&r->http, result, message);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-11-04 01:19:24 +00:00
|
|
|
static int http_request_keyring_response_identity(struct httpd_request *r, uint16_t result, const keyring_identity *id)
|
2015-08-24 13:09:58 +00:00
|
|
|
{
|
|
|
|
const char *did = NULL;
|
|
|
|
const char *name = NULL;
|
2016-06-28 04:52:16 +00:00
|
|
|
keyring_identity_extract(id, &did, &name);
|
2015-08-31 09:41:08 +00:00
|
|
|
struct json_atom json_id;
|
2016-06-28 04:52:16 +00:00
|
|
|
struct json_key_value json_id_kv[4];
|
2015-08-31 09:41:08 +00:00
|
|
|
struct json_atom json_sid;
|
2016-06-28 04:52:16 +00:00
|
|
|
struct json_atom json_sas;
|
2015-08-31 09:41:08 +00:00
|
|
|
struct json_atom json_did;
|
|
|
|
struct json_atom json_name;
|
|
|
|
json_id.type = JSON_OBJECT;
|
2016-06-28 04:52:16 +00:00
|
|
|
json_id.u.object.itemc = 2;
|
2015-08-31 09:41:08 +00:00
|
|
|
json_id.u.object.itemv = json_id_kv;
|
|
|
|
json_id_kv[0].key = "sid";
|
|
|
|
json_id_kv[0].value = &json_sid;
|
|
|
|
json_sid.type = JSON_STRING_NULTERM;
|
2016-06-28 04:52:16 +00:00
|
|
|
json_sid.u.string.content = alloca_tohex_sid_t(*id->box_pk);
|
|
|
|
|
2016-08-15 03:43:26 +00:00
|
|
|
json_id_kv[1].key = "identity";
|
2016-06-28 04:52:16 +00:00
|
|
|
json_id_kv[1].value = &json_sas;
|
|
|
|
json_sas.type = JSON_STRING_NULTERM;
|
2016-09-27 06:21:23 +00:00
|
|
|
json_sas.u.string.content = alloca_tohex_identity_t(&id->sign_keypair->public_key);
|
2016-06-28 04:52:16 +00:00
|
|
|
|
2015-08-24 13:09:58 +00:00
|
|
|
if (did) {
|
2015-08-31 09:41:08 +00:00
|
|
|
json_id_kv[json_id.u.object.itemc].key = "did";
|
|
|
|
json_id_kv[json_id.u.object.itemc].value = &json_did;
|
|
|
|
++json_id.u.object.itemc;
|
|
|
|
json_did.type = JSON_STRING_NULTERM;
|
|
|
|
json_did.u.string.content = did;
|
2015-08-24 13:09:58 +00:00
|
|
|
}
|
|
|
|
if (name) {
|
2015-08-31 09:41:08 +00:00
|
|
|
json_id_kv[json_id.u.object.itemc].key = "name";
|
|
|
|
json_id_kv[json_id.u.object.itemc].value = &json_name;
|
|
|
|
++json_id.u.object.itemc;
|
|
|
|
json_name.type = JSON_STRING_NULTERM;
|
|
|
|
json_name.u.string.content = name;
|
2015-08-24 13:09:58 +00:00
|
|
|
}
|
2015-08-31 09:41:08 +00:00
|
|
|
r->http.response.result_extra[0].label = "identity";
|
|
|
|
r->http.response.result_extra[0].value = json_id;
|
2016-11-04 01:19:24 +00:00
|
|
|
return http_request_keyring_response(r, result, NULL);
|
2015-08-24 13:09:58 +00:00
|
|
|
}
|
|
|
|
|
2014-07-23 08:01:47 +00:00
|
|
|
static HTTP_CONTENT_GENERATOR restful_keyring_identitylist_json_content;
|
|
|
|
|
|
|
|
static int restful_keyring_identitylist_json(httpd_request *r, const char *remainder)
|
|
|
|
{
|
|
|
|
if (*remainder)
|
|
|
|
return 404;
|
2015-08-24 13:07:23 +00:00
|
|
|
const char *pin = http_request_get_query_param(&r->http, "pin");
|
|
|
|
if (pin)
|
|
|
|
keyring_enter_pin(keyring, pin);
|
2014-07-23 08:01:47 +00:00
|
|
|
r->u.sidlist.phase = LIST_HEADER;
|
2014-10-31 03:13:23 +00:00
|
|
|
keyring_iterator_start(keyring, &r->u.sidlist.it);
|
2017-10-19 23:25:21 +00:00
|
|
|
http_request_response_generated(&r->http, 200, &CONTENT_TYPE_JSON, restful_keyring_identitylist_json_content);
|
2014-07-23 08:01:47 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HTTP_CONTENT_GENERATOR_STRBUF_CHUNKER restful_keyring_identitylist_json_content_chunk;
|
|
|
|
|
|
|
|
static int restful_keyring_identitylist_json_content(struct http_request *hr, unsigned char *buf, size_t bufsz, struct http_content_generator_result *result)
|
|
|
|
{
|
|
|
|
return generate_http_content_from_strbuf_chunks(hr, (char *)buf, bufsz, result, restful_keyring_identitylist_json_content_chunk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int restful_keyring_identitylist_json_content_chunk(struct http_request *hr, strbuf b)
|
|
|
|
{
|
|
|
|
httpd_request *r = (httpd_request *) hr;
|
|
|
|
// The "my_sid" and "their_sid" per-conversation fields allow the same JSON structure to be used
|
|
|
|
// in a future, non-SID-specific request, eg, to list all conversations for all currently open
|
|
|
|
// identities.
|
|
|
|
const char *headers[] = {
|
|
|
|
"sid",
|
2016-08-15 03:43:26 +00:00
|
|
|
"identity",
|
2014-07-23 08:01:47 +00:00
|
|
|
"did",
|
|
|
|
"name"
|
|
|
|
};
|
|
|
|
switch (r->u.sidlist.phase) {
|
|
|
|
case LIST_HEADER:
|
|
|
|
strbuf_puts(b, "{\n\"header\":[");
|
|
|
|
unsigned i;
|
|
|
|
for (i = 0; i != NELS(headers); ++i) {
|
2014-10-30 03:30:18 +00:00
|
|
|
if (i)
|
|
|
|
strbuf_putc(b, ',');
|
|
|
|
strbuf_json_string(b, headers[i]);
|
2014-07-23 08:01:47 +00:00
|
|
|
}
|
|
|
|
strbuf_puts(b, "],\n\"rows\":[");
|
2014-10-31 03:13:23 +00:00
|
|
|
if (!strbuf_overrun(b)){
|
|
|
|
r->u.sidlist.phase = LIST_FIRST;
|
|
|
|
if (!keyring_next_identity(&r->u.sidlist.it))
|
|
|
|
r->u.sidlist.phase = LIST_END;
|
|
|
|
}
|
2014-07-23 08:01:47 +00:00
|
|
|
return 1;
|
2014-10-30 03:30:18 +00:00
|
|
|
|
2014-07-23 08:01:47 +00:00
|
|
|
case LIST_ROWS:
|
2014-10-31 03:13:23 +00:00
|
|
|
strbuf_putc(b, ',');
|
2017-09-11 03:13:38 +00:00
|
|
|
FALLTHROUGH;
|
2014-10-31 03:13:23 +00:00
|
|
|
case LIST_FIRST:
|
|
|
|
r->u.sidlist.phase = LIST_ROWS;
|
2014-10-30 03:30:18 +00:00
|
|
|
const char *did = NULL;
|
|
|
|
const char *name = NULL;
|
2016-06-28 04:52:16 +00:00
|
|
|
keyring_identity_extract(r->u.sidlist.it.identity, &did, &name);
|
|
|
|
strbuf_puts(b, "\n[");
|
|
|
|
strbuf_json_string(b, alloca_tohex_sid_t(*r->u.sidlist.it.identity->box_pk));
|
|
|
|
strbuf_puts(b, ",");
|
2016-09-27 06:21:23 +00:00
|
|
|
strbuf_json_string(b, alloca_tohex_identity_t(&r->u.sidlist.it.identity->sign_keypair->public_key));
|
2016-06-28 04:52:16 +00:00
|
|
|
strbuf_puts(b, ",");
|
|
|
|
strbuf_json_string(b, did);
|
|
|
|
strbuf_puts(b, ",");
|
|
|
|
strbuf_json_string(b, name);
|
|
|
|
strbuf_puts(b, "]");
|
2014-07-23 08:01:47 +00:00
|
|
|
|
2014-10-30 03:30:18 +00:00
|
|
|
if (!strbuf_overrun(b)) {
|
2014-10-31 03:13:23 +00:00
|
|
|
if (!keyring_next_identity(&r->u.sidlist.it))
|
|
|
|
r->u.sidlist.phase = LIST_END;
|
2014-07-23 08:01:47 +00:00
|
|
|
}
|
2014-10-30 03:30:18 +00:00
|
|
|
return 1;
|
|
|
|
|
2014-07-23 08:01:47 +00:00
|
|
|
case LIST_END:
|
|
|
|
strbuf_puts(b, "\n]\n}\n");
|
2014-10-30 03:30:18 +00:00
|
|
|
if (strbuf_overrun(b))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
r->u.sidlist.phase = LIST_DONE;
|
2014-07-23 08:01:47 +00:00
|
|
|
// fall through...
|
|
|
|
case LIST_DONE:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
abort();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-10 08:38:42 +00:00
|
|
|
static int restful_keyring_add(httpd_request *r, const char *remainder)
|
|
|
|
{
|
|
|
|
if (*remainder)
|
|
|
|
return 404;
|
2015-08-24 08:12:26 +00:00
|
|
|
const char *pin = http_request_get_query_param(&r->http, "pin");
|
2016-06-07 02:10:43 +00:00
|
|
|
const char *did = http_request_get_query_param(&r->http, "did");
|
|
|
|
const char *name = http_request_get_query_param(&r->http, "name");
|
2017-12-19 02:09:29 +00:00
|
|
|
if (did && did[0] && !str_is_did(did))
|
|
|
|
return http_request_keyring_response(r, 400, "Invalid DID");
|
|
|
|
if (name && name[0] && !str_is_identity_name(name))
|
|
|
|
return http_request_keyring_response(r, 400, "Invalid Name");
|
2016-06-07 02:10:43 +00:00
|
|
|
keyring_identity *id = keyring_create_identity(keyring, pin ? pin : "");
|
2015-08-10 08:38:42 +00:00
|
|
|
if (id == NULL)
|
2015-10-13 08:23:23 +00:00
|
|
|
return http_request_keyring_response(r, 500, "Could not create identity");
|
2017-12-19 02:09:29 +00:00
|
|
|
if ((did || name) && keyring_set_did_name(id, did ? did : "", name ? name : "") == -1) {
|
|
|
|
keyring_free_identity(id);
|
|
|
|
return http_request_keyring_response(r, 500, "Could not set identity DID/Name");
|
2016-06-07 02:10:43 +00:00
|
|
|
}
|
2017-12-19 02:09:29 +00:00
|
|
|
if (keyring_commit(keyring) == -1) {
|
|
|
|
keyring_free_identity(id);
|
2015-10-13 08:23:23 +00:00
|
|
|
return http_request_keyring_response(r, 500, "Could not store new identity");
|
2017-12-19 02:09:29 +00:00
|
|
|
}
|
2016-11-04 06:30:40 +00:00
|
|
|
return http_request_keyring_response_identity(r, 201, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int restful_keyring_remove(httpd_request *r, const char *remainder)
|
|
|
|
{
|
|
|
|
if (*remainder)
|
|
|
|
return 404;
|
|
|
|
const char *pin = http_request_get_query_param(&r->http, "pin");
|
|
|
|
if (pin)
|
|
|
|
keyring_enter_pin(keyring, pin);
|
|
|
|
keyring_identity *id = keyring_find_identity_sid(keyring, &r->sid1);
|
|
|
|
if (!id)
|
|
|
|
return http_request_keyring_response(r, 404, "Identity not found");
|
|
|
|
keyring_destroy_identity(keyring, id);
|
|
|
|
if (keyring_commit(keyring) == -1)
|
|
|
|
return http_request_keyring_response(r, 500, "Could not erase removed identity");
|
|
|
|
int ret = http_request_keyring_response_identity(r, 200, id);
|
|
|
|
keyring_free_identity(id);
|
|
|
|
return ret;
|
2015-08-10 08:38:42 +00:00
|
|
|
}
|
2015-08-24 13:10:16 +00:00
|
|
|
|
|
|
|
static int restful_keyring_set(httpd_request *r, const char *remainder)
|
|
|
|
{
|
|
|
|
if (*remainder)
|
|
|
|
return 404;
|
|
|
|
const char *pin = http_request_get_query_param(&r->http, "pin");
|
|
|
|
const char *did = http_request_get_query_param(&r->http, "did");
|
|
|
|
const char *name = http_request_get_query_param(&r->http, "name");
|
2017-12-19 02:09:29 +00:00
|
|
|
if (did && did[0] && !str_is_did(did))
|
|
|
|
return http_request_keyring_response(r, 400, "Invalid DID");
|
|
|
|
if (name && name[0] && !str_is_identity_name(name))
|
|
|
|
return http_request_keyring_response(r, 400, "Invalid Name");
|
2015-08-24 13:10:16 +00:00
|
|
|
if (pin)
|
|
|
|
keyring_enter_pin(keyring, pin);
|
2016-08-15 03:43:26 +00:00
|
|
|
keyring_identity *id = keyring_find_identity_sid(keyring, &r->sid1);
|
|
|
|
if (!id)
|
2015-10-13 08:23:23 +00:00
|
|
|
return http_request_keyring_response(r, 404, "Identity not found");
|
2016-10-25 05:46:08 +00:00
|
|
|
if (keyring_set_did_name(id, did, name) == -1)
|
2015-10-13 08:23:23 +00:00
|
|
|
return http_request_keyring_response(r, 500, "Could not set identity DID/Name");
|
2015-08-24 13:10:16 +00:00
|
|
|
if (keyring_commit(keyring) == -1)
|
2015-10-13 08:23:23 +00:00
|
|
|
return http_request_keyring_response(r, 500, "Could not store new identity");
|
2016-11-04 01:19:24 +00:00
|
|
|
return http_request_keyring_response_identity(r, 200, id);
|
2015-08-24 13:10:16 +00:00
|
|
|
}
|