Fixed segfaulting access control, allowed change to acl via control socket.

This commit is contained in:
Matthew Bloch
2012-05-19 12:48:03 +01:00
parent 580b821f61
commit 8a38cf48eb
8 changed files with 257 additions and 104 deletions

View File

@@ -1,6 +1,6 @@
DEBUG = true DEBUG = true
SOURCES = %w( flexnbd ioutil readwrite serve util ) SOURCES = %w( flexnbd ioutil readwrite serve util parse )
OBJECTS = SOURCES.map { |s| "#{s}.o" } OBJECTS = SOURCES.map { |s| "#{s}.o" }
LIBS = %w( pthread ) LIBS = %w( pthread )
CCFLAGS = %w( -Wall ) CCFLAGS = %w( -Wall )

View File

@@ -17,89 +17,16 @@ void syntax()
"Syntax: flexnbd serve <listen IP address> <port> <file> \\\n" "Syntax: flexnbd serve <listen IP address> <port> <file> \\\n"
" [full path to control socket] \\\n" " [full path to control socket] \\\n"
" [allowed connection addresses ...]\n" " [allowed connection addresses ...]\n"
" flexnbd mirror <control socket> <dst IP address> <dst port>\n"
" flexnbd status <control socket>\n"
" flexnbd read <IP address> <port> <offset> <length> > data\n" " flexnbd read <IP address> <port> <offset> <length> > data\n"
" flexnbd write <IP address> <port> <offset> <length> < data\n" " flexnbd write <IP address> <port> <offset> <length> < data\n"
" flexnbd write <IP address> <port> <offset> <file to write>\n" " flexnbd write <IP address> <port> <offset> <file to write>\n"
" flexnbd acl <control socket> [allowed connection addresses ...]\n"
" flexnbd mirror <control socket> <dst IP address> <dst port>\n"
" flexnbd status <control socket>\n"
); );
exit(1); exit(1);
} }
#define IS_IP_VALID_CHAR(x) ( ((x) >= '0' && (x) <= '9' ) || \
((x) >= 'a' && (x) <= 'f') || \
((x) >= 'A' && (x) <= 'F' ) || \
(x) == ':' || (x) == '.' \
)
int parse_ip_to_sockaddr(struct sockaddr* out, char* src)
{
char temp[64];
struct sockaddr_in *v4 = (struct sockaddr_in *) out;
struct sockaddr_in6 *v6 = (struct sockaddr_in6 *) out;
/* allow user to start with [ and end with any other invalid char */
{
int i=0, j=0;
if (src[i] == '[')
i++;
for (; i<64 && IS_IP_VALID_CHAR(src[i]); i++)
temp[j++] = src[i];
temp[j] = 0;
}
if (temp[0] == '0' && temp[1] == '\0') {
v4->sin_family = AF_INET;
v4->sin_addr.s_addr = INADDR_ANY;
return 1;
}
if (inet_pton(AF_INET, temp, &v4->sin_addr) == 1) {
out->sa_family = AF_INET;
return 1;
}
if (inet_pton(AF_INET6, temp, &v6->sin6_addr) == 1) {
out->sa_family = AF_INET6;
return 1;
}
return 0;
}
int parse_acl(struct ip_and_mask*** out, int max, char **entries)
{
int i;
if (max == 0) {
*out = NULL;
return 0;
}
for (i = 0; i < max; i++) {
# define MAX_MASK_BITS (outentry->ip.family == AF_INET ? 32 : 128)
int j;
struct ip_and_mask* outentry = (*out)[i];
if (parse_ip_to_sockaddr(&outentry->ip.generic, entries[i]) == 0)
return i;
for (j=0; entries[i][j] && entries[i][j] != '/'; j++)
;
if (entries[i][j] == '/') {
outentry->mask = atoi(entries[i]+j+1);
if (outentry->mask < 1 || outentry->mask > MAX_MASK_BITS)
return i;
}
else
outentry->mask = MAX_MASK_BITS;
# undef MAX_MASK_BITS
debug("acl entry %d has mask %d", i, outentry->mask);
}
return max;
}
void params_serve( void params_serve(
struct mode_serve_params* out, struct mode_serve_params* out,
char* s_ip_address, char* s_ip_address,

View File

@@ -137,4 +137,19 @@ int splice_via_pipe_loop(int fd_in, int fd_out, size_t len)
return spliced < len ? -1 : 0; return spliced < len ? -1 : 0;
} }
int read_until_newline(int fd, char* buf, int bufsize)
{
int cur;
bufsize -=1;
for (cur=0; cur < bufsize; cur++) {
int result = read(fd, buf+cur, 1);
if (result < 0)
return -1;
if (buf[cur] == 10 || buf[cur] == 13)
break;
}
buf[cur++] = 0;
return cur;
}

View File

@@ -9,6 +9,7 @@ int writeloop(int filedes, const void *buffer, size_t size);
int readloop(int filedes, void *buffer, size_t size); int readloop(int filedes, void *buffer, size_t size);
int sendfileloop(int out_fd, int in_fd, off64_t *offset, size_t count); int sendfileloop(int out_fd, int in_fd, off64_t *offset, size_t count);
int splice_via_pipe_loop(int fd_in, int fd_out, size_t len); int splice_via_pipe_loop(int fd_in, int fd_out, size_t len);
int read_until_newline(int fd, char* buf, int bufsize);
#endif #endif

View File

@@ -4,27 +4,14 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#define _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE
#include "parse.h"
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
union mysockaddr {
unsigned short family;
struct sockaddr generic;
struct sockaddr_in v4;
struct sockaddr_in6 v6;
};
struct ip_and_mask {
union mysockaddr ip;
int mask;
};
struct mode_serve_params { struct mode_serve_params {
union mysockaddr bind_to; union mysockaddr bind_to;
int acl_entries; int acl_entries;
struct ip_and_mask** acl; struct ip_and_mask *acl[0];
char* filename; char* filename;
int tcp_backlog; int tcp_backlog;
char* control_socket_name; char* control_socket_name;
@@ -54,6 +41,11 @@ struct client_params {
char* block_allocation_map; char* block_allocation_map;
}; };
struct control_params {
int socket;
struct mode_serve_params* serve;
};
union mode_params { union mode_params {
struct mode_serve_params serve; struct mode_serve_params serve;
struct mode_readwrite_params readwrite; struct mode_readwrite_params readwrite;

87
parse.c Normal file
View File

@@ -0,0 +1,87 @@
#include "parse.h"
#include "util.h"
int atoi(const char *nptr);
#define IS_IP_VALID_CHAR(x) ( ((x) >= '0' && (x) <= '9' ) || \
((x) >= 'a' && (x) <= 'f') || \
((x) >= 'A' && (x) <= 'F' ) || \
(x) == ':' || (x) == '.' \
)
int parse_ip_to_sockaddr(struct sockaddr* out, char* src)
{
char temp[64];
struct sockaddr_in *v4 = (struct sockaddr_in *) out;
struct sockaddr_in6 *v6 = (struct sockaddr_in6 *) out;
/* allow user to start with [ and end with any other invalid char */
{
int i=0, j=0;
if (src[i] == '[')
i++;
for (; i<64 && IS_IP_VALID_CHAR(src[i]); i++)
temp[j++] = src[i];
temp[j] = 0;
}
if (temp[0] == '0' && temp[1] == '\0') {
v4->sin_family = AF_INET;
v4->sin_addr.s_addr = INADDR_ANY;
return 1;
}
if (inet_pton(AF_INET, temp, &v4->sin_addr) == 1) {
out->sa_family = AF_INET;
return 1;
}
if (inet_pton(AF_INET6, temp, &v6->sin6_addr) == 1) {
out->sa_family = AF_INET6;
return 1;
}
return 0;
}
int parse_acl(struct ip_and_mask *out[], int max, char **entries)
{
int i;
if (max == 0) {
*out = NULL;
return 0;
}
else {
*out = xmalloc(max * sizeof(struct ip_and_mask));
debug("acl alloc: %p", *out);
}
for (i = 0; i < max; i++) {
# define MAX_MASK_BITS (outentry->ip.family == AF_INET ? 32 : 128)
int j;
struct ip_and_mask* outentry = *out+i;
if (parse_ip_to_sockaddr(&outentry->ip.generic, entries[i]) == 0)
return i;
for (j=0; entries[i][j] && entries[i][j] != '/'; j++)
;
if (entries[i][j] == '/') {
outentry->mask = atoi(entries[i]+j+1);
if (outentry->mask < 1 || outentry->mask > MAX_MASK_BITS)
return i;
}
else
outentry->mask = MAX_MASK_BITS;
# undef MAX_MASK_BITS
debug("acl ptr[%d]: %p %d",i, outentry, outentry->mask);
}
for (i=0; i < max; i++) {
struct ip_and_mask* entry = *out+i;
debug("acl entry %d @ %p has mask %d", i, entry, entry->mask);
}
return max;
}

24
parse.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef __PARSE_H
#define __PARSE_H
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
union mysockaddr {
unsigned short family;
struct sockaddr generic;
struct sockaddr_in v4;
struct sockaddr_in6 v6;
};
struct ip_and_mask {
union mysockaddr ip;
int mask;
};
int parse_ip_to_sockaddr(struct sockaddr* out, char* src);
int parse_acl(struct ip_and_mask *out[], int max, char **entries);
#endif

127
serve.c
View File

@@ -3,6 +3,7 @@
#include "ioutil.h" #include "ioutil.h"
#include "util.h" #include "util.h"
#include "bitset.h" #include "bitset.h"
#include "parse.h"
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@@ -264,6 +265,89 @@ void* client_serve(void* client_uncast)
return NULL; return NULL;
} }
void control_acl(struct control_params* client)
{
int acl_entries = 0, parsed;
char** s_acl_entry = NULL;
struct ip_and_mask** acl, **old_acl;
while (1) {
char entry[64];
int result = read_until_newline(client->socket, entry, 64);
if (result == -1 || result == 64)
goto done;
if (result == 1) /* blank line terminates */
break;
s_acl_entry = xrealloc(
s_acl_entry,
++acl_entries * sizeof(struct s_acl_entry*)
);
s_acl_entry[acl_entries-1] = strdup(entry);
debug("acl_entry = '%s'", s_acl_entry[acl_entries-1]);
}
parsed = parse_acl(&acl, acl_entries, s_acl_entry);
if (parsed != acl_entries) {
write(client->socket, "error: ", 7);
write(client->socket, s_acl_entry[parsed],
strlen(s_acl_entry[parsed]));
write(client->socket, "\n", 1);
free(acl);
}
else {
old_acl = client->serve->acl;
client->serve->acl = acl;
client->serve->acl_entries = acl_entries;
free(old_acl);
write(client->socket, "ok\n", 3);
}
done: if (acl_entries > 0) {
int i;
for (i=0; i<acl_entries; i++)
free(s_acl_entry[i]);
free(s_acl_entry);
}
return;
}
void control_mirror(struct control_params* client)
{
}
void control_status(struct control_params* client)
{
}
void* control_serve(void* client_uncast)
{
const int max = 256;
char command[max];
struct control_params* client = (struct control_params*) client_uncast;
while (1) {
CLIENT_ERROR_ON_FAILURE(
read_until_newline(client->socket, command, max),
"Error reading command"
);
if (strcmp(command, "acl") == 0)
control_acl(client);
else if (strcmp(command, "mirror") == 0)
control_mirror(client);
else if (strcmp(command, "status") == 0)
control_status(client);
else {
write(client->socket, "error: unknown command\n", 23);
break;
}
}
close(client->socket);
free(client);
return NULL;
}
static int testmasks[9] = { 0,128,192,224,240,248,252,254,255 }; static int testmasks[9] = { 0,128,192,224,240,248,252,254,255 };
int is_included_in_acl(int list_length, struct ip_and_mask** list, struct sockaddr* test) int is_included_in_acl(int list_length, struct ip_and_mask** list, struct sockaddr* test)
@@ -271,26 +355,30 @@ int is_included_in_acl(int list_length, struct ip_and_mask** list, struct sockad
int i; int i;
for (i=0; i < list_length; i++) { for (i=0; i < list_length; i++) {
struct ip_and_mask *entry = list[i]; struct ip_and_mask *entry = list+i;
int testbits; int testbits;
char *raw_address1, *raw_address2; char *raw_address1, *raw_address2;
debug("checking acl entry %d", i); debug("checking acl entry %d (%d/%d)", i, test->sa_family, entry->ip.family);
if (test->sa_family != entry->ip.family) if (test->sa_family != entry->ip.family)
continue; continue;
if (test->sa_family == AF_INET) { if (test->sa_family == AF_INET) {
debug("it's an AF_INET");
raw_address1 = (char*) raw_address1 = (char*)
&((struct sockaddr_in*) test)->sin_addr; &((struct sockaddr_in*) test)->sin_addr;
raw_address2 = (char*) &entry->ip.v4.sin_addr; raw_address2 = (char*) &entry->ip.v4.sin_addr;
} }
else if (test->sa_family == AF_INET6) { else if (test->sa_family == AF_INET6) {
debug("it's an AF_INET6");
raw_address1 = (char*) raw_address1 = (char*)
&((struct sockaddr_in6*) test)->sin6_addr; &((struct sockaddr_in6*) test)->sin6_addr;
raw_address2 = (char*) &entry->ip.v6.sin6_addr; raw_address2 = (char*) &entry->ip.v6.sin6_addr;
} }
debug("testbits=%d", entry->mask);
for (testbits = entry->mask; testbits > 0; testbits -= 8) { for (testbits = entry->mask; testbits > 0; testbits -= 8) {
debug("testbits=%d, c1=%d, c2=%d", testbits, raw_address1[0], raw_address2[0]); debug("testbits=%d, c1=%d, c2=%d", testbits, raw_address1[0], raw_address2[0]);
if (testbits >= 8) { if (testbits >= 8) {
@@ -366,7 +454,7 @@ void serve_open_control_socket(struct mode_serve_params* params)
void accept_nbd_client(struct mode_serve_params* params, int client_fd, struct sockaddr* client_address) void accept_nbd_client(struct mode_serve_params* params, int client_fd, struct sockaddr* client_address)
{ {
pthread_t client_thread; pthread_t client_thread;
struct client_params* client_params; struct client_params* client_params;
if (params->acl && if (params->acl &&
@@ -382,18 +470,37 @@ void accept_nbd_client(struct mode_serve_params* params, int client_fd, struct s
client_params->block_allocation_map = client_params->block_allocation_map =
params->block_allocation_map; params->block_allocation_map;
client_thread = pthread_create(&client_thread, NULL, SERVER_ERROR_ON_FAILURE(
client_serve, client_params); pthread_create(
SERVER_ERROR_ON_FAILURE(client_thread, &client_thread,
"Failed to create client thread"); NULL,
client_serve,
client_params
),
"Failed to create client thread"
);
/* FIXME: keep track of them? */ /* FIXME: keep track of them? */
/* FIXME: maybe shouldn't be fatal? */ /* FIXME: maybe shouldn't be fatal? */
} }
void accept_control_connection(struct mode_serve_params* params, int client_fd, struct sockaddr* client_address) void accept_control_connection(struct mode_serve_params* params, int client_fd, struct sockaddr* client_address)
{ {
write(client_fd, "hello", 5); pthread_t control_thread;
close(client_fd); struct control_params* control_params;
control_params = xmalloc(sizeof(struct control_params));
control_params->socket = client_fd;
control_params->serve = params;
SERVER_ERROR_ON_FAILURE(
pthread_create(
&control_thread,
NULL,
control_serve,
control_params
),
"Failed to create client thread"
);
} }
void serve_accept_loop(struct mode_serve_params* params) void serve_accept_loop(struct mode_serve_params* params)
@@ -402,7 +509,7 @@ void serve_accept_loop(struct mode_serve_params* params)
int activity_fd, client_fd; int activity_fd, client_fd;
struct sockaddr client_address; struct sockaddr client_address;
fd_set fds; fd_set fds;
socklen_t socklen=0; socklen_t socklen=sizeof(client_address);
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(params->server, &fds); FD_SET(params->server, &fds);