aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-02-29 13:47:43 +0200
committerSergey Poznyakoff <gray@gnu.org>2020-02-29 13:47:43 +0200
commitec86e9d545049b0dc83cc6c5b9c7c66f74915f7f (patch)
treef41451d824f2bdc25010324164def9e5a43b391d
parent4771762b12e16954f94a1da6f49250413686811f (diff)
downloadping903-ec86e9d545049b0dc83cc6c5b9c7c66f74915f7f.tar.gz
ping903-ec86e9d545049b0dc83cc6c5b9c7c66f74915f7f.tar.bz2
Improve authorization support.
* examples/inspect: Special handling for "auth" objects. Print the rest of arrays as here documents. * src/json.c (json_object_filter): new function. * src/json.h (json_object_filter): new proto. * src/ping903.c (try_auth): Don't format HTTP responses if ret_val is NULL. (ept_ident, ept_config): Check individual object attributes using try_auth. Remove those not allowed by auth ACL. (cf_auth): Support for CF_SERIALIZE.
-rw-r--r--README8
-rwxr-xr-xexamples/inspect35
-rw-r--r--src/json.c38
-rw-r--r--src/json.h3
-rw-r--r--src/ping903.c153
5 files changed, 203 insertions, 34 deletions
diff --git a/README b/README
index 15b6b33..2e9486f 100644
--- a/README
+++ b/README
@@ -364,10 +364,10 @@ without reloading the server. For the purpose of updating the
IP list is sectioned in two parts:
1. Immutable IP addresses
- These are IP addresses obtained from files supplied using the
- "ip-list" keyword in the configuration file. These addresses
- cannot be modified using the API described in this section.
- An attempt to do so will return an error status.
+ These are IP addresses specified in the configuration file using
+ the "ip-list: statement. These addresses cannot be modified using
+ the API described in this section. An attempt to do so will return
+ an error status.
2. Mutable IP addresses.
These are additional IP addresses configured via this API.
diff --git a/examples/inspect b/examples/inspect
index f9e21f2..7b10184 100755
--- a/examples/inspect
+++ b/examples/inspect
@@ -116,25 +116,32 @@ unless ($response->is_success) {
my $resp = JSON->new->decode($response->decoded_content);
foreach my $kw (grep { $_ ne 'ip-list' } sort keys %$resp) {
my $val = $resp->{$kw} or next;
- if (ref($val) eq 'ARRAY') {
- foreach my $sv (@$val) {
- print "$kw $sv\n";
+ if ($kw eq 'auth') {
+ my $delim = '';
+ foreach my $acl (@$val) {
+ print "${delim}auth $acl->{type} $acl->{method} $acl->{url}";
+ if (exists($acl->{'passwd-file'})) {
+ print " $acl->{'passwd-file'}";
+ if (exists($acl->{realm})) {
+ print " $acl->{realm}";
+ }
+ }
+ $delim = "\n";
}
} else {
print "$kw ";
- if (JSON::is_bool($val)) {
+
+ if (ref($val) eq 'ARRAY') {
+ print "<<EOF\n";
+ foreach my $ip (@$val) {
+ print " $ip\n";
+ }
+ print "EOF";
+ } elsif (JSON::is_bool($val)) {
print $val ? "on" : "off";
- } else {
+ } else {
print $val
}
- print "\n";
- }
-}
-
-if (@{$resp->{'ip-list'}}) {
- print "ip-list <<EOF\n";
- foreach my $ip (@{$resp->{'ip-list'}}) {
- print " $ip\n";
}
- print "EOF\n";
+ print "\n";
}
diff --git a/src/json.c b/src/json.c
index 510c7ce..2b143ee 100644
--- a/src/json.c
+++ b/src/json.c
@@ -697,6 +697,44 @@ json_format_object(struct json_format *fmt, struct json_value *obj,
}
json_format_writec(fmt, '}');
}
+
+/* Removes from OBJ all pairs for which the predicate function PRED
+ returns 1.
+ */
+int
+json_object_filter(struct json_value *obj,
+ int (*pred)(char const *, struct json_value *, void *),
+ void *data)
+{
+ struct json_object *op;
+ struct json_pair *p, *prev;
+
+ if (obj->type != json_object) {
+ errno = EINVAL;
+ return -1;
+ }
+ op = obj->v.o;
+ if (!op->head)
+ return 0;
+
+ prev = NULL;
+ for (p = op->head; p; ) {
+ struct json_pair *next = p->next;
+ if (pred(p->k, p->v, data)) {
+ if (prev)
+ prev->next = next;
+ else
+ op->head = next;
+ if (!next)
+ op->tail = prev;
+ free(p->k);
+ json_value_free(p->v);
+ } else
+ prev = p;
+ p = next;
+ }
+ return 0;
+}
/* Parser */
#define ISSPACE(c) ((c)==' '||(c)=='\t'||(c)=='\n'||(c)=='\r')
diff --git a/src/json.h b/src/json.h
index 5e320a1..b5fa9d3 100644
--- a/src/json.h
+++ b/src/json.h
@@ -84,6 +84,9 @@ int json_object_set(struct json_value *obj, char const *name,
struct json_value *val);
int json_object_get(struct json_value *obj, char const *name,
struct json_value **retval);
+int json_object_filter(struct json_value *obj,
+ int (*pred)(char const *, struct json_value *, void *),
+ void *data);
struct json_value *json_new_array(void);
static inline size_t json_array_length(struct json_value *j) {
diff --git a/src/ping903.c b/src/ping903.c
index 3de8e97..72c121d 100644
--- a/src/ping903.c
+++ b/src/ping903.c
@@ -294,6 +294,35 @@ httpd_json_response(struct MHD_Connection *conn,
return ret;
}
+static int try_auth(struct MHD_Connection *conn, const char *url,
+ const char *method, int *ret_val);
+
+struct auth_flt_data
+{
+ struct MHD_Connection *conn;
+ const char *url;
+ const char *method;
+ int err;
+};
+
+static int
+auth_flt(char const *kw, struct json_value *val, void *data)
+{
+ int rc;
+ struct auth_flt_data *adata = data;
+ char *url = malloc(strlen(adata->url) + strlen(kw) + 1);
+ if (!url) {
+ adata->err = 1;
+ return 0;
+ }
+ strcpy(url, adata->url);
+ strcat(url, "/");
+ strcat(url, kw);
+ rc = try_auth(adata->conn, url, adata->method, NULL);
+ free(url);
+ return rc;
+}
+
static struct json_value *
ident_to_json(void)
{
@@ -351,7 +380,20 @@ ept_ident(struct MHD_Connection *conn,
}
json_value_free(obj);
obj = cp;
- }
+ } else {
+ struct auth_flt_data adata = {
+ .conn = conn,
+ .url = url,
+ .method = method,
+ .err = 0
+ };
+ if (json_object_filter(obj, auth_flt, &adata) || adata.err) {
+ int ret = http_response(conn, method, url,
+ MHD_HTTP_INTERNAL_SERVER_ERROR);
+ json_value_free(obj);
+ return ret;
+ }
+ }
return httpd_json_response(conn, url, method, MHD_HTTP_OK, obj);
}
@@ -391,7 +433,20 @@ ept_config(struct MHD_Connection *conn,
}
json_value_free(val);
val = cp;
- }
+ } else {
+ struct auth_flt_data adata = {
+ .conn = conn,
+ .url = url,
+ .method = method,
+ .err = 0
+ };
+ if (json_object_filter(val, auth_flt, &adata) || adata.err) {
+ int ret = http_response(conn, method, url,
+ MHD_HTTP_INTERNAL_SERVER_ERROR);
+ json_value_free(val);
+ return ret;
+ }
+ }
return httpd_json_response(conn, url, method, MHD_HTTP_OK, val);
}
@@ -607,8 +662,8 @@ struct auth_location {
static struct auth_location *auth_head, *auth_tail;
-int
-cf_auth(int mode, union cf_callback_arg *arg, void *data)
+static int
+cf_auth_parse(union cf_callback_arg *arg, void *data)
{
int ac;
char **av;
@@ -618,10 +673,7 @@ cf_auth(int mode, union cf_callback_arg *arg, void *data)
char *passwd_file = NULL;
char *realm = NULL;
- if (mode != CF_PARSE)
- return CF_RET_IGNORE;
-
- // auth basic METHOD URL [FILE [REALM]]
+ // auth TYPE METHOD URL [FILE [REALM]]
enum { i_type, i_method, i_url, i_file, i_realm };
switch (strsplit(arg->input.val, 5, &ac, &av, &endp)) {
@@ -695,6 +747,65 @@ cf_auth(int mode, union cf_callback_arg *arg, void *data)
return CF_RET_OK;
}
+static int
+cf_auth_serialize(union cf_callback_arg *arg, void *data)
+{
+ struct json_value *ar, *obj = NULL, *jv = NULL;
+ struct auth_location *auth;
+ static char const *auth_type_str[] = {
+ [AUTH_NONE] = "none",
+ [AUTH_BASIC] = "basic"
+ };
+
+ if (!(ar = json_new_array()))
+ return CF_RET_FAIL;
+
+ for (auth = auth_head; auth; auth = auth->next) {
+ if (!(obj = json_new_object()))
+ goto err;
+ if (!(jv = json_new_string(auth_type_str[auth->type]))
+ || json_object_set(obj, "type", jv))
+ goto err;
+ if (!(jv = json_new_string(auth->url))
+ || json_object_set(obj, "url", jv))
+ goto err;
+ if (!(jv = json_new_string(auth->method))
+ || json_object_set(obj, "method", jv))
+ goto err;
+ if (auth->type == AUTH_BASIC) {
+ if (auth->passwd_file
+ && (!(jv = json_new_string(auth->passwd_file))
+ || json_object_set(obj, "passwd-file", jv)))
+ goto err;
+ if (auth->realm
+ && (!(jv = json_new_string(auth->realm))
+ || json_object_set(obj, "realm", jv)))
+ goto err;
+ }
+ if (json_array_append(ar, obj))
+ goto err;
+ }
+ arg->output = ar;
+ return CF_RET_OK;
+err:
+ json_value_free(jv);
+ json_value_free(obj);
+ json_value_free(ar);
+ return CF_RET_FAIL;
+}
+
+int
+cf_auth(int mode, union cf_callback_arg *arg, void *data)
+{
+ switch (mode) {
+ case CF_PARSE:
+ return cf_auth_parse(arg, data);
+ case CF_SERIALIZE:
+ return cf_auth_serialize(arg, data);
+ }
+ abort();
+}
+
#define WWW_AUTH_PFX "Basic realm=\""
#define WWW_AUTH_SFX "\""
@@ -765,8 +876,9 @@ try_auth(struct MHD_Connection *conn, const char *url, const char *method,
--url_len;
url_buf = malloc(url_len + 1);
if (!url_buf) {
- *ret_val = http_response(conn, method, url,
- MHD_HTTP_INTERNAL_SERVER_ERROR);
+ if (ret_val)
+ *ret_val = http_response(conn, method, url,
+ MHD_HTTP_INTERNAL_SERVER_ERROR);
return 1;
}
memcpy(url_buf, url, url_len);
@@ -791,15 +903,21 @@ try_auth(struct MHD_Connection *conn, const char *url, const char *method,
MHD_HEADER_KIND,
MHD_HTTP_HEADER_AUTHORIZATION);
if (!auth) {
- *ret_val = http_unauthorized(conn, method, url,
- loc->realm);
+ if (ret_val)
+ *ret_val = http_unauthorized(conn,
+ method,
+ url,
+ loc->realm);
return 1;
}
switch (basicauth(loc->passwd_file, auth)) {
case BASICAUTH_DENY:
- case BASICAUTH_BAD_INPUT:
- *ret_val = http_unauthorized(conn, method, url,
- loc->realm);
+ case BASICAUTH_BAD_INPUT:
+ if (ret_val)
+ *ret_val = http_unauthorized(conn,
+ method,
+ url,
+ loc->realm);
return 1;
case BASICAUTH_ALLOW:
return 0;
@@ -808,7 +926,10 @@ try_auth(struct MHD_Connection *conn, const char *url, const char *method,
loc->passwd_file, strerror(errno));
/* fall through */
default:
- *ret_val = http_response(conn, method, url,
+ if (ret_val)
+ *ret_val = http_response(conn,
+ method,
+ url,
MHD_HTTP_INTERNAL_SERVER_ERROR);
return 1;
}

Return to:

Send suggestions and report system problems to the System administrator.