Fixed segfaulting access control, allowed change to acl via control socket.
This commit is contained in:
2
Rakefile
2
Rakefile
@@ -1,6 +1,6 @@
|
||||
DEBUG = true
|
||||
|
||||
SOURCES = %w( flexnbd ioutil readwrite serve util )
|
||||
SOURCES = %w( flexnbd ioutil readwrite serve util parse )
|
||||
OBJECTS = SOURCES.map { |s| "#{s}.o" }
|
||||
LIBS = %w( pthread )
|
||||
CCFLAGS = %w( -Wall )
|
||||
|
79
flexnbd.c
79
flexnbd.c
@@ -17,89 +17,16 @@ void syntax()
|
||||
"Syntax: flexnbd serve <listen IP address> <port> <file> \\\n"
|
||||
" [full path to control socket] \\\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 write <IP address> <port> <offset> <length> < data\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);
|
||||
}
|
||||
|
||||
#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(
|
||||
struct mode_serve_params* out,
|
||||
char* s_ip_address,
|
||||
|
15
ioutil.c
15
ioutil.c
@@ -137,4 +137,19 @@ int splice_via_pipe_loop(int fd_in, int fd_out, size_t len)
|
||||
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;
|
||||
}
|
||||
|
1
ioutil.h
1
ioutil.h
@@ -9,6 +9,7 @@ int writeloop(int filedes, const 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 splice_via_pipe_loop(int fd_in, int fd_out, size_t len);
|
||||
int read_until_newline(int fd, char* buf, int bufsize);
|
||||
|
||||
#endif
|
||||
|
||||
|
24
params.h
24
params.h
@@ -4,27 +4,14 @@
|
||||
#define _GNU_SOURCE
|
||||
#define _LARGEFILE64_SOURCE
|
||||
|
||||
#include "parse.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 {
|
||||
union mysockaddr bind_to;
|
||||
int acl_entries;
|
||||
struct ip_and_mask** acl;
|
||||
struct ip_and_mask *acl[0];
|
||||
char* filename;
|
||||
int tcp_backlog;
|
||||
char* control_socket_name;
|
||||
@@ -54,6 +41,11 @@ struct client_params {
|
||||
char* block_allocation_map;
|
||||
};
|
||||
|
||||
struct control_params {
|
||||
int socket;
|
||||
struct mode_serve_params* serve;
|
||||
};
|
||||
|
||||
union mode_params {
|
||||
struct mode_serve_params serve;
|
||||
struct mode_readwrite_params readwrite;
|
||||
|
87
parse.c
Normal file
87
parse.c
Normal 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
24
parse.h
Normal 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
127
serve.c
@@ -3,6 +3,7 @@
|
||||
#include "ioutil.h"
|
||||
#include "util.h"
|
||||
#include "bitset.h"
|
||||
#include "parse.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -264,6 +265,89 @@ void* client_serve(void* client_uncast)
|
||||
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 };
|
||||
|
||||
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;
|
||||
|
||||
for (i=0; i < list_length; i++) {
|
||||
struct ip_and_mask *entry = list[i];
|
||||
struct ip_and_mask *entry = list+i;
|
||||
int testbits;
|
||||
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)
|
||||
continue;
|
||||
|
||||
if (test->sa_family == AF_INET) {
|
||||
debug("it's an AF_INET");
|
||||
raw_address1 = (char*)
|
||||
&((struct sockaddr_in*) test)->sin_addr;
|
||||
raw_address2 = (char*) &entry->ip.v4.sin_addr;
|
||||
}
|
||||
else if (test->sa_family == AF_INET6) {
|
||||
debug("it's an AF_INET6");
|
||||
raw_address1 = (char*)
|
||||
&((struct sockaddr_in6*) test)->sin6_addr;
|
||||
raw_address2 = (char*) &entry->ip.v6.sin6_addr;
|
||||
}
|
||||
|
||||
debug("testbits=%d", entry->mask);
|
||||
|
||||
for (testbits = entry->mask; testbits > 0; testbits -= 8) {
|
||||
debug("testbits=%d, c1=%d, c2=%d", testbits, raw_address1[0], raw_address2[0]);
|
||||
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)
|
||||
{
|
||||
pthread_t client_thread;
|
||||
pthread_t client_thread;
|
||||
struct client_params* client_params;
|
||||
|
||||
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 =
|
||||
params->block_allocation_map;
|
||||
|
||||
client_thread = pthread_create(&client_thread, NULL,
|
||||
client_serve, client_params);
|
||||
SERVER_ERROR_ON_FAILURE(client_thread,
|
||||
"Failed to create client thread");
|
||||
SERVER_ERROR_ON_FAILURE(
|
||||
pthread_create(
|
||||
&client_thread,
|
||||
NULL,
|
||||
client_serve,
|
||||
client_params
|
||||
),
|
||||
"Failed to create client thread"
|
||||
);
|
||||
/* FIXME: keep track of them? */
|
||||
/* FIXME: maybe shouldn't be fatal? */
|
||||
}
|
||||
|
||||
void accept_control_connection(struct mode_serve_params* params, int client_fd, struct sockaddr* client_address)
|
||||
{
|
||||
write(client_fd, "hello", 5);
|
||||
close(client_fd);
|
||||
pthread_t control_thread;
|
||||
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)
|
||||
@@ -402,7 +509,7 @@ void serve_accept_loop(struct mode_serve_params* params)
|
||||
int activity_fd, client_fd;
|
||||
struct sockaddr client_address;
|
||||
fd_set fds;
|
||||
socklen_t socklen=0;
|
||||
socklen_t socklen=sizeof(client_address);
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(params->server, &fds);
|
||||
|
Reference in New Issue
Block a user