ACL refactoring.

master
Nicolas Favre-Felix 14 years ago
parent 17669d9768
commit a7bf6f76e9

@ -2,7 +2,7 @@ OUT=webdis
HIREDIS_OBJ=hiredis/hiredis.o hiredis/sds.o hiredis/net.o hiredis/async.o hiredis/dict.o
JANSSON_OBJ=jansson/src/dump.o jansson/src/error.o jansson/src/hashtable.o jansson/src/load.o jansson/src/strbuffer.o jansson/src/utf.o jansson/src/value.o jansson/src/variadic.o
FORMAT_OBJS=formats/json.o formats/raw.o formats/common.o
OBJS=webdis.o conf.o $(FORMAT_OBJS) cmd.o server.o $(HIREDIS_OBJ) $(JANSSON_OBJ) libb64/cencode.o
OBJS=webdis.o conf.o $(FORMAT_OBJS) cmd.o server.o $(HIREDIS_OBJ) $(JANSSON_OBJ) libb64/cencode.o acl.o
CFLAGS=-O3 -Wall -Wextra -I. -Ijansson/src
LDFLAGS=-levent

82
acl.c

@ -0,0 +1,82 @@
#include "acl.h"
#include "cmd.h"
#include "conf.h"
#include <string.h>
#include <evhttp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int
acl_match_client(struct acl *a, struct evhttp_request *rq, in_addr_t *ip) {
/* check HTTP Basic Auth */
const char *auth;
auth = evhttp_find_header(rq->input_headers, "Authorization");
if(auth && a->http_basic_auth && strncasecmp(auth, "Basic ", 6) == 0) { /* sent auth */
if(strcmp(auth + 6, a->http_basic_auth) != 0) { /* wrong */
return 0;
}
}
/* CIDR check. */
if(a->cidr.enabled == 0) { /* none given, all match */
return 1;
}
if(((*ip) & a->cidr.mask) == (a->cidr.subnet & a->cidr.mask)) {
return 1;
}
return 0;
}
int
acl_allow_command(struct cmd *cmd, struct conf *cfg, struct evhttp_request *rq) {
char *always_off[] = {"MULTI", "EXEC", "WATCH", "DISCARD"};
unsigned int i;
int authorized = 1;
struct acl *a;
char *client_ip;
u_short client_port;
in_addr_t client_addr;
const char *cmd_name = cmd->argv[0];
size_t cmd_len = cmd->argv_len[0];
/* some commands are always disabled, regardless of the config file. */
for(i = 0; i < sizeof(always_off) / sizeof(always_off[0]); ++i) {
if(strncasecmp(always_off[i], cmd_name, cmd_len) == 0) {
return 0;
}
}
/* find client's address */
evhttp_connection_get_peer(rq->evcon, &client_ip, &client_port);
client_addr = ntohl(inet_addr(client_ip));
/* go through permissions */
for(a = cfg->perms; a; a = a->next) {
if(!acl_match_client(a, rq, &client_addr)) continue; /* match client */
/* go through authorized commands */
for(i = 0; i < a->enabled.count; ++i) {
if(strncasecmp(a->enabled.commands[i], cmd_name, cmd_len) == 0) {
authorized = 1;
}
}
/* go through unauthorized commands */
for(i = 0; i < a->disabled.count; ++i) {
if(strncasecmp(a->disabled.commands[i], cmd_name, cmd_len) == 0) {
authorized = 0;
}
}
}
return authorized;
}

39
acl.h

@ -0,0 +1,39 @@
#ifndef ACL_H
#define ACL_H
#include <netinet/in.h>
struct evhttp_request;
struct cmd;
struct conf;
struct acl_commands {
unsigned int count;
char **commands;
};
struct acl {
/* CIDR subnet + mask */
struct {
int enabled;
in_addr_t subnet;
in_addr_t mask;
} cidr;
char *http_basic_auth;
/* commands that have been enabled or disabled */
struct acl_commands enabled;
struct acl_commands disabled;
struct acl *next;
};
int
acl_match_client(struct acl *a, struct evhttp_request *rq, in_addr_t *ip);
int
acl_allow_command(struct cmd *cmd, struct conf *cfg, struct evhttp_request *rq);
#endif

55
cmd.c

@ -1,6 +1,7 @@
#include "cmd.h"
#include "server.h"
#include "conf.h"
#include "acl.h"
#include "formats/json.h"
#include "formats/raw.h"
@ -8,8 +9,6 @@
#include <stdlib.h>
#include <string.h>
#include <hiredis/hiredis.h>
#include <netinet/in.h>
#include <arpa/inet.h>
struct cmd *
cmd_new(struct evhttp_request *rq, int count) {
@ -54,56 +53,6 @@ void on_http_disconnect(struct evhttp_connection *evcon, void *ctx) {
free(ps);
}
int
cmd_authorized(struct cmd *cmd, struct conf *cfg, struct evhttp_request *rq) {
char *always_off[] = {"MULTI", "EXEC", "WATCH", "DISCARD"};
unsigned int i;
int authorized = 1;
struct acl *a;
char *client_ip;
u_short client_port;
in_addr_t client_addr;
const char *cmd_name = cmd->argv[0];
size_t cmd_len = cmd->argv_len[0];
/* some commands are always disabled, regardless of the config file. */
for(i = 0; i < sizeof(always_off) / sizeof(always_off[0]); ++i) {
if(strncasecmp(always_off[i], cmd_name, cmd_len) == 0) {
return 0;
}
}
/* find client's address */
evhttp_connection_get_peer(rq->evcon, &client_ip, &client_port);
client_addr = ntohl(inet_addr(client_ip));
/* go through permissions */
for(a = cfg->perms; a; a = a->next) {
if(!acl_match(a, rq, &client_addr)) continue; /* match client */
/* go through authorized commands */
for(i = 0; i < a->enabled.count; ++i) {
if(strncasecmp(a->enabled.commands[i], cmd_name, cmd_len) == 0) {
authorized = 1;
}
}
/* go through unauthorized commands */
for(i = 0; i < a->disabled.count; ++i) {
if(strncasecmp(a->disabled.commands[i], cmd_name, cmd_len) == 0) {
authorized = 0;
}
}
}
return authorized;
}
int
cmd_run(struct server *s, struct evhttp_request *rq,
const char *uri, size_t uri_len) {
@ -145,7 +94,7 @@ cmd_run(struct server *s, struct evhttp_request *rq,
/* check that the client is able to run this command */
if(!cmd_authorized(cmd, s->cfg, rq)) {
if(!acl_allow_command(cmd, s->cfg, rq)) {
return -1;
}

@ -8,6 +8,7 @@
#include <evhttp.h>
#include <libb64/cencode.h>
#include "conf.h"
#include "acl.h"
static struct acl *
conf_parse_acls(json_t *jtab);
@ -174,30 +175,6 @@ conf_parse_acls(json_t *jtab) {
return head;
}
int
acl_match(struct acl *a, struct evhttp_request *rq, in_addr_t *ip) {
/* check HTTP Basic Auth */
const char *auth;
auth = evhttp_find_header(rq->input_headers, "Authorization");
if(auth && a->http_basic_auth && strncasecmp(auth, "Basic ", 6) == 0) { /* sent auth */
if(strcmp(auth + 6, a->http_basic_auth) != 0) { /* wrong */
return 0;
}
}
/* CIDR check. */
if(a->cidr.enabled == 0) { /* none given, all match */
return 1;
}
if(((*ip) & a->cidr.mask) == (a->cidr.subnet & a->cidr.mask)) {
return 1;
}
return 0;
}
void
conf_free(struct conf *conf) {

@ -1,33 +1,6 @@
#ifndef CONF_H
#define CONF_H
#include <netinet/in.h>
struct evhttp_request;
struct acl_commands {
unsigned int count;
char **commands;
};
struct acl {
/* CIDR subnet + mask */
struct {
int enabled;
in_addr_t subnet;
in_addr_t mask;
} cidr;
char *http_basic_auth;
/* commands that have been enabled or disabled */
struct acl_commands enabled;
struct acl_commands disabled;
struct acl *next;
};
struct conf {
char *redis_host;
@ -46,7 +19,4 @@ conf_read(const char *filename);
void
conf_free(struct conf *conf);
int
acl_match(struct acl *a, struct evhttp_request *rq, in_addr_t *ip);
#endif /* CONF_H */

Loading…
Cancel
Save