Pull ACLs into their own struct

This commit is contained in:
Alex Young
2012-06-07 17:47:43 +01:00
parent 601e5b475a
commit 2d9d00b636
8 changed files with 291 additions and 97 deletions

99
src/acl.c Normal file
View File

@@ -0,0 +1,99 @@
#include <stdlib.h>
#include "util.h"
#include "parse.h"
#include "acl.h"
struct acl * acl_create( int len, char ** lines, int default_deny )
{
struct acl * acl;
acl = (struct acl *)xmalloc( sizeof( struct acl ) );
acl->len = parse_acl( &acl->entries, len, lines );
acl->default_deny = default_deny;
return acl;
}
static int testmasks[9] = { 0,128,192,224,240,248,252,254,255 };
/** Test whether AF_INET or AF_INET6 sockaddr is included in the given access
* control list, returning 1 if it is, and 0 if not.
*/
static int is_included_in_acl(int list_length, struct ip_and_mask (*list)[], union mysockaddr* test)
{
NULLCHECK( test );
int i;
for (i=0; i < list_length; i++) {
struct ip_and_mask *entry = &(*list)[i];
int testbits;
unsigned char *raw_address1, *raw_address2;
debug("checking acl entry %d (%d/%d)", i, test->generic.sa_family, entry->ip.family);
if (test->generic.sa_family != entry->ip.family) {
continue;
}
if (test->generic.sa_family == AF_INET) {
debug("it's an AF_INET");
raw_address1 = (unsigned char*) &test->v4.sin_addr;
raw_address2 = (unsigned char*) &entry->ip.v4.sin_addr;
}
else if (test->generic.sa_family == AF_INET6) {
debug("it's an AF_INET6");
raw_address1 = (unsigned char*) &test->v6.sin6_addr;
raw_address2 = (unsigned char*) &entry->ip.v6.sin6_addr;
}
debug("testbits=%d", entry->mask);
for (testbits = entry->mask; testbits > 0; testbits -= 8) {
debug("testbits=%d, c1=%02x, c2=%02x", testbits, raw_address1[0], raw_address2[0]);
if (testbits >= 8) {
if (raw_address1[0] != raw_address2[0])
goto no_match;
}
else {
if ((raw_address1[0] & testmasks[testbits%8]) !=
(raw_address2[0] & testmasks[testbits%8]) )
goto no_match;
}
raw_address1++;
raw_address2++;
}
return 1;
no_match: ;
debug("no match");
}
return 0;
}
int acl_includes( struct acl * acl, union mysockaddr * addr )
{
NULLCHECK( acl );
if ( 0 == acl->len ) {
return !( acl->default_deny );
}
else {
return is_included_in_acl( acl->len, acl->entries, addr );
}
}
void acl_destroy( struct acl * acl )
{
free( acl->entries );
acl->len = 0;
acl->entries = NULL;
free( acl );
}

34
src/acl.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef ACL_H
#define ACL_H
#include "parse.h"
struct acl {
int len;
int default_deny;
struct ip_and_mask (*entries)[];
};
/** Allocate a new acl structure, parsing the given lines to sockaddr
* structures in the process. After allocation, acl->len might not
* equal len. In that case, there was an error in parsing and acl->len
* will be the index of the failed entry in lines.
*
* default_deny controls the behaviour of an empty list: if true, all
* requests will be denied. If true, all requests will be accepted.
*/
struct acl * acl_create( int len, char **lines, int default_deny );
/** Check to see whether an address is allowed by an acl.
* See acl_create for how the default_deny setting affects this.
*/
int acl_includes( struct acl *, union mysockaddr *);
/** Free the acl structure and the internal acl entries table.
*/
void acl_destroy( struct acl * );
#endif

View File

@@ -32,6 +32,7 @@
#include "readwrite.h"
#include "bitset.h"
#include "self_pipe.h"
#include "acl.h"
#include <stdlib.h>
#include <string.h>
@@ -259,22 +260,21 @@ int control_mirror(struct control_params* client, int linesc, char** lines)
/** Command parser to alter access control list from socket input */
int control_acl(struct control_params* client, int linesc, char** lines)
{
int parsedc;
struct ip_and_mask (*acl)[], (*old_acl)[];
NULLCHECK( client );
struct acl * old_acl = client->serve->acl;
struct acl * new_acl = acl_create( linesc, lines, old_acl ? old_acl->default_deny : 0 );
parsedc = parse_acl(&acl, linesc, lines);
if (parsedc != linesc) {
if (new_acl->len != linesc) {
write(client->socket, "1: bad spec: ", 13);
write(client->socket, lines[parsedc],
strlen(lines[parsedc]));
write(client->socket, lines[new_acl->len],
strlen(lines[new_acl->len]));
write(client->socket, "\n", 1);
free(acl);
acl_destroy( new_acl );
}
else {
old_acl = client->serve->acl;
client->serve->acl = acl;
client->serve->acl_entries = linesc;
free(old_acl);
client->serve->acl = new_acl;
acl_destroy( old_acl );
write_socket("0: updated");
}

View File

@@ -19,6 +19,7 @@
* elsewhere in the program.
*/
#include "serve.h"
#include "util.h"
@@ -33,7 +34,9 @@
#include <signal.h>
#include <getopt.h>
#include "options.h"
#include "acl.h"
void exit_err( char *msg )
@@ -50,11 +53,8 @@ void params_serve(
char *s_ctrl_sock,
int default_deny,
int acl_entries,
char** s_acl_entries /* first may actually be path to control socket */
)
char** s_acl_entries )
{
int parsed;
out->tcp_backlog = 10; /* does this need to be settable? */
if (s_ip_address == NULL)
@@ -72,14 +72,9 @@ void params_serve(
* we pass NULL. */
out->control_socket_name = s_ctrl_sock;
/* If this is true then an empty ACL means "nobody is allowed to connect",
* rather than "anybody is allowed to connect" */
out->default_deny = default_deny;
out->acl_entries = acl_entries;
parsed = parse_acl(&out->acl, acl_entries, s_acl_entries);
if (parsed != acl_entries)
SERVER_ERROR("Bad ACL entry '%s'", s_acl_entries[parsed]);
out->acl = acl_create( acl_entries, s_acl_entries, default_deny );
if (out->acl && out->acl->len != acl_entries)
SERVER_ERROR("Bad ACL entry '%s'", s_acl_entries[out->acl->len]);
out->bind_to.v4.sin_port = atoi(s_port);
if (out->bind_to.v4.sin_port < 0 || out->bind_to.v4.sin_port > 65535)

View File

@@ -65,65 +65,6 @@ void server_unlock_io( struct server* serve )
);
}
static int testmasks[9] = { 0,128,192,224,240,248,252,254,255 };
/** Test whether AF_INET or AF_INET6 sockaddr is included in the given access
* control list, returning 1 if it is, and 0 if not.
*/
int is_included_in_acl(int list_length, struct ip_and_mask (*list)[], union mysockaddr* test)
{
NULLCHECK( test );
int i;
for (i=0; i < list_length; i++) {
struct ip_and_mask *entry = &(*list)[i];
int testbits;
unsigned char *raw_address1, *raw_address2;
debug("checking acl entry %d (%d/%d)", i, test->generic.sa_family, entry->ip.family);
if (test->generic.sa_family != entry->ip.family)
continue;
if (test->generic.sa_family == AF_INET) {
debug("it's an AF_INET");
raw_address1 = (unsigned char*) &test->v4.sin_addr;
raw_address2 = (unsigned char*) &entry->ip.v4.sin_addr;
}
else if (test->generic.sa_family == AF_INET6) {
debug("it's an AF_INET6");
raw_address1 = (unsigned char*) &test->v6.sin6_addr;
raw_address2 = (unsigned char*) &entry->ip.v6.sin6_addr;
}
debug("testbits=%d", entry->mask);
for (testbits = entry->mask; testbits > 0; testbits -= 8) {
debug("testbits=%d, c1=%02x, c2=%02x", testbits, raw_address1[0], raw_address2[0]);
if (testbits >= 8) {
if (raw_address1[0] != raw_address2[0])
goto no_match;
}
else {
if ((raw_address1[0] & testmasks[testbits%8]) !=
(raw_address2[0] & testmasks[testbits%8]) )
goto no_match;
}
raw_address1++;
raw_address2++;
}
return 1;
no_match: ;
debug("no match");
}
return 0;
}
/** Prepares a listening socket for the NBD server, binding etc. */
void serve_open_server_socket(struct server* params)
{
@@ -259,13 +200,10 @@ int server_acl_accepts( struct server *params, union mysockaddr * client_address
NULLCHECK( client_address );
if (params->acl) {
if (is_included_in_acl(params->acl_entries, params->acl, client_address))
return 1;
} else {
if (!params->default_deny)
return 1;
return acl_includes( params->acl, client_address );
}
return 0;
return 1;
}
@@ -405,11 +343,11 @@ void serve_accept_loop(struct server* params)
client_fd = accept(activity_fd, &client_address.generic, &socklen);
if (activity_fd == params->server_fd) {
debug("Accepted nbd client");
debug("Accepted nbd client socket");
accept_nbd_client(params, client_fd, &client_address);
}
if (activity_fd == params->control_fd) {
debug("Accepted control client");
debug("Accepted control client socket");
accept_control_connection(params, client_fd, &client_address);
}

View File

@@ -8,6 +8,7 @@
#endif
#include "parse.h"
#include "acl.h"
#include <sys/types.h>
@@ -46,12 +47,8 @@ struct client_tbl_entry {
struct server {
/** address/port to bind to */
union mysockaddr bind_to;
/** does an empty ACL mean "deny all"? */
int default_deny;
/** number of entries in current access control list*/
int acl_entries;
/** pointer to access control list entries*/
struct ip_and_mask (*acl)[0];
/** access control list */
struct acl * acl;
/** (static) file name to serve */
char* filename;
/** file name of INCOMPLETE flag */