diff options
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | addr.c | 53 | ||||
| -rw-r--r-- | addr.h | 31 | ||||
| -rw-r--r-- | authdb.c | 223 | ||||
| -rw-r--r-- | authdb.h | 50 | ||||
| -rw-r--r-- | filterdb.h | 4 | ||||
| -rw-r--r-- | squark-auth-snmp.c | 69 | ||||
| -rw-r--r-- | squark-filter.c | 67 | 
8 files changed, 420 insertions, 83 deletions
@@ -12,11 +12,11 @@ CFLAGS=-g -I. $(NETSNMP_CFLAGS) $(LUA_CFLAGS) $(CMPH_CFLAGS) -std=gnu99 -D_GNU_S  all: $(TARGETS) -squark-auth-snmp: squark-auth-snmp.o blob.o +squark-auth-snmp: squark-auth-snmp.o blob.o addr.o  	$(CC) -o $@ $^ $(NETSNMP_LIBS) -squark-filter: squark-filter.o filterdb.o blob.o -	$(CC) -o $@ $^ $(CMPH_LIBS) +squark-filter: squark-filter.o filterdb.o authdb.o blob.o addr.o +	$(CC) -o $@ $^ $(CMPH_LIBS) -lrt  squarkdb.so: lua-squarkdb.o filterdb.o blob.o  	$(CC) -shared -o $@ $^ $(LUA_LIBS) $(CMPH_LIBS) @@ -0,0 +1,53 @@ +#include <string.h> + +#include "addr.h" + +int addr_len(const sockaddr_any *addr) +{ +	switch (addr->any.sa_family) { +	case AF_INET: +		return sizeof(struct sockaddr_in); +	default: +		return 0; +	} +} + +sockaddr_any *addr_parse(blob_t b, sockaddr_any *addr) +{ +	memset(addr, 0, sizeof(*addr)); +	addr->ipv4.sin_family = AF_INET; +	addr->ipv4.sin_addr.s_addr = blob_inet_addr(b); +	if (addr->ipv4.sin_addr.s_addr == -1) +		return NULL; +	return addr; +} + +unsigned long addr_hash(const sockaddr_any *addr) +{ +	switch (addr->any.sa_family) { +	case AF_INET: +		return htonl(addr->ipv4.sin_addr.s_addr); +	default: +		return 0; +	} +} + +const char *addr_print(const sockaddr_any *addr) +{ +	switch (addr->any.sa_family) { +	case AF_INET: +		return inet_ntoa(addr->ipv4.sin_addr); +	default: +		return "unknown"; +	} +} + +blob_t addr_get_hostaddr_blob(const sockaddr_any *addr) +{ +	switch (addr->any.sa_family) { +	case AF_INET: +		return BLOB_BUF(&addr->ipv4.sin_addr); +	default: +		return BLOB_NULL; +	} +} @@ -0,0 +1,31 @@ +#ifndef ADDR_H +#define ADDR_H + +#include <arpa/inet.h> +#include "blob.h" + +typedef union { +	struct sockaddr		any; +	struct sockaddr_in	ipv4; +} sockaddr_any; + +int addr_len(const sockaddr_any *addr); +sockaddr_any *addr_parse(blob_t text, sockaddr_any *addr); +unsigned long addr_hash(const sockaddr_any *addr); +const char *addr_print(const sockaddr_any *addr); +blob_t addr_get_hostaddr_blob(const sockaddr_any *addr); + +static inline void addr_copy(sockaddr_any *dst, const sockaddr_any *src) +{ +	memcpy(dst, src, addr_len(src)); +} + +static inline int addr_cmp(const sockaddr_any *a, const sockaddr_any *b) +{ +	if (a->any.sa_family != b->any.sa_family) +		return -1; +	return memcmp(a, b, addr_len(a)); +} + +#endif + diff --git a/authdb.c b/authdb.c new file mode 100644 index 0000000..452c94e --- /dev/null +++ b/authdb.c @@ -0,0 +1,223 @@ +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> +#include <malloc.h> +#include <sched.h> +#include <fcntl.h> + +#include "authdb.h" +#include "addr.h" +#include "blob.h" + +#define AUTHDB_IP_PER_ME		256 +#define AUTHDB_LOGOFF_PERIOD		(15*60)		/* 15 mins */ + +static struct authdb_map_entry *authdb_me_open(sockaddr_any *addr, int create) +{ +	int oflag, fd; +	char name[64]; +	blob_t b = BLOB_BUF(name); +	void *base; +	struct authdb_map_entry *me; + +	blob_push(&b, BLOB_STR("squark-auth-")); +	blob_push_hexdump(&b, addr_get_hostaddr_blob(addr)); +	blob_push_byte(&b, 0); + +	oflag = O_RDWR; +	if (create) +		oflag |= O_CREAT; + +	fd = shm_open(name, oflag, 0600); +	if (fd < 0) +		return NULL; + +	if (create && +	    ftruncate(fd, sizeof(struct authdb_entry[AUTHDB_IP_PER_ME])) < 0) { +		close(fd); +		return NULL; +	} + +	base = mmap(NULL, sizeof(struct authdb_entry[AUTHDB_IP_PER_ME]), +		    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); +	close(fd); + +	if (base == MAP_FAILED) +		return NULL; + +	me = malloc(sizeof(*me)); +	if (me == NULL) { +		munmap(base, sizeof(struct authdb_entry[AUTHDB_IP_PER_ME])); +		return NULL; +	} + +	me->next = NULL; +	me->baseaddr = *addr; +	me->entries = base; + +	return me; +} + +static void authdb_me_free(struct authdb_map_entry *me) +{ +	munmap(me->entries, sizeof(struct authdb_entry[AUTHDB_IP_PER_ME])); +	free(me); +} + +int authdb_open(struct authdb *adb) +{ +	memset(adb, 0, sizeof(*adb)); +	return 0; +} + +void authdb_close(struct authdb *adb) +{ +	struct authdb_map_entry *c, *n; +	int i; + +	for (i = 0; i < ARRAY_SIZE(adb->hash_bucket); i++) { +		for (c = adb->hash_bucket[i]; c != NULL; c = n) { +			n = c->next; +			authdb_me_free(n); +		} +	} +} + +static unsigned int MurmurHash2(const void * key, int len, unsigned int seed) +{ +	// 'm' and 'r' are mixing constants generated offline. +	// They're not really 'magic', they just happen to work well. +	const unsigned int m = 0x5bd1e995; +	const int r = 24; +	unsigned int h = seed ^ len; +	const unsigned char * data = (const unsigned char *)key; + +	while(len >= 4) +	{ +		unsigned int k = *(unsigned int *)data; + +		k *= m; +		k ^= k >> r; +		k *= m; + +		h *= m; +		h ^= k; + +		data += 4; +		len -= 4; +	} + +	switch(len) +	{ +	case 3:	h ^= data[2] << 16; +	case 2:	h ^= data[1] << 8; +	case 1:	h ^= data[0]; +		h *= m; +	} + +	h ^= h >> 13; +	h *= m; +	h ^= h >> 15; + +	return h; +} + +static uint32_t authdb_entry_checksum(struct authdb_entry *entry) +{ +	return MurmurHash2(&entry->p, sizeof(entry->p), 0); +} + +void *authdb_get(struct authdb *adb, sockaddr_any *addr, struct authdb_entry *entry, int create) +{ +	struct authdb_map_entry *me; +	int hash, e, i; +	sockaddr_any baseaddr; +	blob_t b; + +	baseaddr = *addr; +	b = addr_get_hostaddr_blob(&baseaddr); +	if (b.len < 4) +		return NULL; + +	e = b.ptr[0]; +	b.ptr[0] = 0x00; +	hash = b.ptr[1] + b.ptr[2] + b.ptr[3]; + +	for (me = adb->hash_bucket[hash]; me != NULL; me = me->next) { +		if (addr_cmp(&baseaddr, &me->baseaddr) == 0) +			break; +	} +	if (me == NULL) { +		me = authdb_me_open(&baseaddr, create); +		if (me == NULL) +			return NULL; +		me->next = adb->hash_bucket[hash]; +		adb->hash_bucket[hash] = me; +	} + +	for (i = 0; i < 3; i++) { +		memcpy(&me->entries[e], entry, sizeof(struct authdb_entry)); +		if (entry->u.checksum == 0 && entry->u.login_time == 0) +			return &me->entries[e]; +		if (entry->u.checksum == authdb_entry_checksum(entry)) +			return &me->entries[e]; +		sched_yield(); +	} + +	authdb_clear_entry(entry); + +	return &me->entries[e]; +} + +int authdb_set(void *token, struct authdb_entry *entry) +{ +	struct authdb_entry *mme = token; +	uint32_t checksum = entry->u.checksum; + +	entry->u.checksum = authdb_entry_checksum(entry); +	if (mme->u.checksum != checksum) +		return 0; + +	mme->u.checksum = entry->u.checksum; +	memcpy(mme, entry, sizeof(*entry)); + +	return 1; +} + +int authdb_check_login(void *token, struct authdb_entry *e, blob_t username, time_t now) +{ +	struct authdb_entry *mme = token; + +	/* check username */ +	if (!blob_is_null(username) && +	    blob_cmp(username, BLOB_STRLEN(e->p.login_name)) != 0) +		return 0; + +	/* and dates */ +	if (now > e->u.login_time + AUTHDB_LOGOFF_PERIOD) +		return 0; + +	/* and that no one clobbered the entry */ +	if (mme->u.checksum != e->u.checksum) +		return 0; + +	/* refresh last activity */ +	mme->u.login_time = now; + +	return 1; +} + +void authdb_clear_entry(struct authdb_entry *entry) +{ +	memset(&entry->p, 0, sizeof(entry->p)); +	entry->u.login_time = 0; +	entry->u.override_time = 0; +} + +void authdb_commit_login(void *token, struct authdb_entry *e, time_t now) +{ +	/* fixme read stuff from config files */ +	e->u.login_time = now; + +	authdb_set(token, e); +} diff --git a/authdb.h b/authdb.h new file mode 100644 index 0000000..5a3f3c4 --- /dev/null +++ b/authdb.h @@ -0,0 +1,50 @@ +#ifndef AUTHDB_H +#define AUTHDB_H + +#include <stddef.h> +#include <stdint.h> +#include "blob.h" +#include "addr.h" + +#define AUTHDB_IP_HASH_SIZE		64 + +struct authdb_map_entry; + +struct authdb { +	struct authdb_map_entry	*hash_bucket[AUTHDB_IP_HASH_SIZE]; +}; + +struct authdb_entry { +	struct { +		char		login_name[44]; +		char		mac_address[6]; +		uint16_t	switch_port; +		sockaddr_any	switch_ip; +		uint64_t	block_categories; +		uint64_t	hard_block_categories; +	} p; + +	struct { +		uint32_t	login_time; +		uint32_t	override_time; +		uint32_t	checksum; +	} u; +}; + +struct authdb_map_entry { +	struct authdb_map_entry	*next; +	sockaddr_any		baseaddr; +	struct authdb_entry *	entries; +}; + +int authdb_open(struct authdb *adb); +void authdb_close(struct authdb *adb); + +void *authdb_get(struct authdb *adb, sockaddr_any *addr, struct authdb_entry *entry, int create); + +void authdb_clear_entry(struct authdb_entry *entry); +int authdb_set(void *token, struct authdb_entry *entry); +int authdb_check_login(void *token, struct authdb_entry *e, blob_t username, time_t now); +void authdb_commit_login(void *token, struct authdb_entry *e, time_t now); + +#endif @@ -1,5 +1,5 @@ -#ifndef SQUARKDB_H -#define SQUARKDB_H +#ifndef FILTERDB_H +#define FILTERDB_H  #include <stddef.h>  #include <stdint.h> diff --git a/squark-auth-snmp.c b/squark-auth-snmp.c index 4b88913..f386ab3 100644 --- a/squark-auth-snmp.c +++ b/squark-auth-snmp.c @@ -22,12 +22,12 @@  #include <stdio.h>  #include <string.h>  #include <unistd.h> -#include <arpa/inet.h>  #include <net-snmp/net-snmp-config.h>  #include <net-snmp/net-snmp-includes.h>  #include "blob.h" +#include "addr.h"  /* Compile time configurables */  #define SWITCH_HASH_SIZE		128 @@ -167,43 +167,6 @@ static inline void blob_pull_oid_dump(blob_t *b, blob_t d)  /* ----------------------------------------------------------------- */ -typedef union { -	struct sockaddr		any; -	struct sockaddr_in	ipv4; -} sockaddr_any; - -int addr_len(const sockaddr_any *addr) -{ -	switch (addr->any.sa_family) { -	case AF_INET: -		return sizeof(struct sockaddr_in); -	default: -		return 0; -	} -} - -void addr_copy(sockaddr_any *dst, const sockaddr_any *src) -{ -	memcpy(dst, src, addr_len(src)); -} - -int addr_cmp(const sockaddr_any *a, const sockaddr_any *b) -{ -	if (a->any.sa_family != b->any.sa_family) -		return -1; -	return memcmp(a, b, addr_len(a)); -} - -sockaddr_any *addr_parse(const char *str, sockaddr_any *addr) -{ -	memset(addr, 0, sizeof(*addr)); -	addr->ipv4.sin_family = AF_INET; -	addr->ipv4.sin_addr.s_addr = inet_addr(str); -	if (addr->ipv4.sin_addr.s_addr == -1) -		return NULL; -	return addr; -} -  void blob_push_iana_afn(blob_t *b, sockaddr_any *addr)  {  	unsigned char *ptr; @@ -245,26 +208,6 @@ sockaddr_any *blob_pull_iana_afn(blob_t *b, sockaddr_any *addr)  	return addr;  } -unsigned long addr_hash(const sockaddr_any *addr) -{ -	switch (addr->any.sa_family) { -	case AF_INET: -		return htonl(addr->ipv4.sin_addr.s_addr); -	default: -		return 0; -	} -} - -const char *addr_print(const sockaddr_any *addr) -{ -	switch (addr->any.sa_family) { -	case AF_INET: -		return inet_ntoa(addr->ipv4.sin_addr); -	default: -		return "unknown"; -	} -} -  /* ----------------------------------------------------------------- */  static void safe_free(void *ptr) @@ -435,10 +378,10 @@ void link_switch(const char *a, int ap, const char *b, int bp)  	struct switch_port_info *spia, *spib;  	sockaddr_any addr; -	sia = get_switch(addr_parse(a, &addr)); +	sia = get_switch(addr_parse(BLOB_STRLEN(a), &addr));  	spia = get_switch_port(sia, ap); -	sib = get_switch(addr_parse(b, &addr)); +	sib = get_switch(addr_parse(BLOB_STRLEN(b), &addr));  	spib = get_switch_port(sib, bp);  	addr_copy(&spia->link_partner, &sib->addr); @@ -994,7 +937,7 @@ void start_authentication(const char *token, const char *ip)  	auth = calloc(1, sizeof(*auth));  	auth->token = strdup(token); -	if (addr_parse(ip, &auth->addr) == NULL) { +	if (addr_parse(BLOB_STRLEN(ip), &auth->addr) == NULL) {  		auth_completed(auth);  		return;  	} @@ -1112,9 +1055,9 @@ int main(int argc, char **argv)  	if (l2_root == NULL)  		l2_root = l3_root; -	l3_root_dev = get_switch(addr_parse(l3_root, &addr)); +	l3_root_dev = get_switch(addr_parse(BLOB_STRLEN(l3_root), &addr));  	l3_if_ndx   = resolve_ifName2ifIndex(l3_root_dev, BLOB_STRLEN((char *) l3_ifname)); -	l2_root_dev = get_switch(addr_parse(l2_root, &addr)); +	l2_root_dev = get_switch(addr_parse(BLOB_STRLEN(l2_root), &addr));  	l2_vlan_ndx = atoi(l2_vlan);  	username_format_flags = parse_format(username_format); diff --git a/squark-filter.c b/squark-filter.c index 588eb73..7a005e1 100644 --- a/squark-filter.c +++ b/squark-filter.c @@ -10,6 +10,7 @@   * by the Free Software Foundation. See http://www.gnu.org/ for details.   */ +#include <time.h>  #include <stdio.h>  #include <ctype.h>  #include <string.h> @@ -17,14 +18,20 @@  #include <cmph.h> -#include "filterdb.h"  #include "blob.h" +#include "addr.h" +#include "filterdb.h" +#include "authdb.h"  static int running = 1;  static uint64_t banned_categories = 0; +static const blob_t dash = BLOB_STR_INIT("-");  static const blob_t space = BLOB_STR_INIT(" "); +static const blob_t slash = BLOB_STR_INIT("/");  static const blob_t lf = BLOB_STR_INIT("\n"); -static blob_t redirect_page; +static blob_t redirect_banned_page, redirect_login_page; +static struct authdb adb; +static time_t now;  struct url_info {  	blob_t protocol; @@ -321,7 +328,7 @@ static void send_ok(blob_t tag)  	write(STDOUT_FILENO, b.ptr, b.len);  } -static void send_redirect(struct sqdb *db, blob_t tag, blob_t url, int categ, blob_t username) +static void send_redirect(blob_t redirect_page, blob_t tag, blob_t url, blob_t categ, blob_t username)  {  	static char buffer[8*1024];  	blob_t b = BLOB_BUF(buffer); @@ -330,7 +337,7 @@ static void send_redirect(struct sqdb *db, blob_t tag, blob_t url, int categ, bl  	blob_push(&b, BLOB_STR(" 302:"));  	blob_push(&b, redirect_page);  	blob_push(&b, BLOB_STR("?REASON=")); -	blob_push_urlencode(&b, get_category_name(db, categ)); +	blob_push_urlencode(&b, categ);  	blob_push(&b, BLOB_STR("&USER="));  	blob_push_urlencode(&b, username);  	blob_push(&b, BLOB_STR("&DENIEDURL=")); @@ -346,9 +353,12 @@ static void read_input(struct sqdb *db)  	static char buffer[8 * 1024];  	static blob_t left; -	blob_t b, line, id, url, username; +	blob_t b, line, id, ipaddr, url, username;  	struct url_info nfo; -	int r, category; +	int r, category, auth_ok; +	sockaddr_any addr; +	struct authdb_entry entry; +	void *token;  	if (blob_is_null(left))  		left = BLOB_BUF(buffer); @@ -363,6 +373,8 @@ static void read_input(struct sqdb *db)  	left.ptr += r;  	left.len -= r; +	now = time(NULL); +  	b = blob_pushed(BLOB_BUF(buffer), left);  	do {  		line = blob_pull_cspn(&b, lf); @@ -373,16 +385,16 @@ static void read_input(struct sqdb *db)  		blob_pull_spn(&line, space);  		url = blob_pull_cspn(&line, space);  		blob_pull_spn(&line, space); -		blob_pull_cspn(&line, space); /* client addr / fqdn */ +		ipaddr = blob_pull_cspn(&line, slash); /* client addr */ +		blob_pull_cspn(&line, space); /* fqdn */  		blob_pull_spn(&line, space);  		username = blob_pull_cspn(&line, space);  		/* http method */  		/* urlgroup */  		/* myaddr=xxx myport=xxx etc */ -		if (!blob_is_null(url)) { -			if (blob_is_null(username)) -				username = BLOB_STR("-"); +		if (!blob_is_null(url) && +		    addr_parse(ipaddr, &addr)) {  			/* valid request, handle it */  			if (url_parse(url, &nfo)) {  				url_print(&nfo); @@ -390,9 +402,29 @@ static void read_input(struct sqdb *db)  			} else  				category = 0; -			if ((1ULL << category) & banned_categories) -				send_redirect(db, id, url, category, username); -			else +			token = authdb_get(&adb, &addr, &entry, 1); +			if (authdb_check_login(token, &entry, username, now)) { +				auth_ok = 1; +				username = BLOB_STRLEN(entry.p.login_name); +			} else if (blob_cmp(username, dash) != 0 || +				   blob_is_null(redirect_login_page)) { +				auth_ok = 1; +				authdb_clear_entry(&entry); +				entry.p.block_categories = banned_categories; +				memcpy(entry.p.login_name, username.ptr, username.len); +				authdb_commit_login(token, &entry, now); +			} else { +				auth_ok = 0; +			} + +			if (!auth_ok) { +				send_redirect(redirect_login_page, id, url, BLOB_STR("auth"), username); +			} else if (((1ULL << category) & entry.p.block_categories) && +				   (entry.u.override_time < now || +				    entry.u.override_time + 15*60 > now || +				   ((1ULL << category) & entry.p.hard_block_categories))) { +				send_redirect(redirect_banned_page, id, url, get_category_name(db, category), username); +			} else  				send_ok(id);  		} @@ -421,12 +453,16 @@ int main(int argc, char **argv)  	struct sqdb db;  	int opt; +	authdb_open(&adb);  	sqdb_open(&db, "/var/lib/squark/squark.db"); -	while ((opt = getopt(argc, argv, "r:b:")) != -1) { +	while ((opt = getopt(argc, argv, "r:b:c:")) != -1) {  		switch (opt) {  		case 'r': -			redirect_page = BLOB_STRLEN(optarg); +			redirect_banned_page = BLOB_STRLEN(optarg); +			break; +		case 'c': +			redirect_login_page = BLOB_STRLEN(optarg);  			break;  		case 'b':  			ban_category(&db, BLOB_STRLEN(optarg)); @@ -438,4 +474,5 @@ int main(int argc, char **argv)  		read_input(&db);  	sqdb_close(&db); +	authdb_close(&adb);  }  | 
