From 25593b5e6fea76ed7c08db586924032c0810c27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Sun, 7 Nov 2010 00:47:39 +0200 Subject: squark: reorganize sources to src directory --- src/authdb.c | 364 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 364 insertions(+) create mode 100644 src/authdb.c (limited to 'src/authdb.c') diff --git a/src/authdb.c b/src/authdb.c new file mode 100644 index 0000000..e6e71c4 --- /dev/null +++ b/src/authdb.c @@ -0,0 +1,364 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "authdb.h" +#include "filterdb.h" +#include "addr.h" +#include "blob.h" + +#define ALIGN(s,a) (((s) + a - 1) & ~(a - 1)) + +#define AUTHDB_IP_PER_ME 256 +#define AUTHDB_LOGOFF_PERIOD (15*60) /* 15 mins */ +#define AUTHDB_SHM_SIZE ALIGN(sizeof(struct authdb_entry[AUTHDB_IP_PER_ME]), 4096) + +static struct authdb_map_entry *authdb_me_open(sockaddr_any *addr, int create) +{ + int oflag, fd; + char name[64], buf[256]; + blob_t b = BLOB_BUF(name); + void *base; + struct authdb_map_entry *me; + struct group grp, *res; + + 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, 0660); + if (fd < 0) + return NULL; + + if (ftruncate(fd, AUTHDB_SHM_SIZE) < 0) { + close(fd); + return NULL; + } + + getgrnam_r("squark", &grp, buf, sizeof(buf), &res); + if (res != NULL) { + fchown(fd, -1, res->gr_gid); + fchmod(fd, 0660); + } + + base = mmap(NULL, AUTHDB_SHM_SIZE, + 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, AUTHDB_SHM_SIZE); + 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, AUTHDB_SHM_SIZE); + free(me); +} + +int authdb_open(struct authdb *adb, struct authdb_config *cfg, struct sqdb *db) +{ + memset(adb, 0, sizeof(*adb)); + memset(cfg, 0, sizeof(*cfg)); + cfg->db = db; + return adbc_refresh(cfg, time(NULL)); +} + +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(c); + } + } +} + +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; + unsigned 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 = (unsigned char) b.ptr[0]; + b.ptr[0] = 0x00; + + hash = b.ptr[1] + b.ptr[2] + b.ptr[3]; + hash %= ARRAY_SIZE(adb->hash_bucket); + + 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(entry, &me->entries[e], sizeof(struct authdb_entry)); + if (entry->checksum == 0 && entry->p.login_time == 0) + return &me->entries[e]; + if (entry->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->checksum; + + entry->checksum = authdb_entry_checksum(entry); + if (mme->checksum != checksum) + return 0; + + mme->checksum = ~0; + 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->last_activity_time + AUTHDB_LOGOFF_PERIOD) + return 0; + + /* and that no one clobbered the entry */ + if (mme->checksum != e->checksum) + return 0; + + /* refresh last activity */ + mme->last_activity_time = now; + + return 1; +} + +void authdb_clear_entry(struct authdb_entry *entry) +{ + uint32_t checksum = entry->checksum; + + memset(entry, 0, sizeof(*entry)); + entry->checksum = checksum; +} + +void authdb_commit_login(void *token, struct authdb_entry *e, time_t now, struct authdb_config *cfg) +{ + e->p.block_categories = cfg->block_categories; + e->p.hard_block_categories = cfg->hard_block_categories; + e->p.login_time = now; + e->last_activity_time = now; + e->override_time = 0; + + authdb_set(token, e); +} + +void authdb_commit_logout(void *token) +{ + memset(token, 0, sizeof(struct authdb_entry)); +} + +void authdb_commit_override(void *token, struct authdb_entry *e, time_t now) +{ + struct authdb_entry *mme = token; + + mme->override_time = now; +} + +static blob_t read_word(FILE *in, int *lineno, blob_t b) +{ + int ch, i, comment = 0; + blob_t r; + + ch = fgetc(in); + while (1) { + if (ch == EOF) + return BLOB_NULL; + if (ch == '#') + comment = 1; + if (!comment && !isspace(ch)) + break; + if (ch == '\n') { + (*lineno)++; + comment = 0; + } + ch = fgetc(in); + } + + r.ptr = b.ptr; + r.len = 0; + for (i = 0; i < b.len-1 && !isspace(ch); i++, r.len++) { + r.ptr[i] = ch; + ch = fgetc(in); + if (ch == EOF) + break; + if (ch == '\n') + (*lineno)++; + } + + return r; +} + +static int find_category_id(struct sqdb *db, blob_t cat) +{ + uint32_t size, *ptr; + int i; + + ptr = sqdb_section_get(db, SQDB_SECTION_CATEGORIES, &size); + if (ptr == NULL) + return -1; + + size /= sizeof(uint32_t); + for (i = 0; i < size; i++) + if (blob_cmp(cat, sqdb_get_string_literal(db, ptr[i])) == 0) + return i; + + return -1; +} + +static inline uint64_t to_category(struct sqdb *db, blob_t c) +{ + int category; + + category = find_category_id(db, c); + if (category >= 0) + return 1ULL << category; + + fprintf(stderr, "WARNING: unknown category '%.*s'\n", + c.len, c.ptr); + return 0; +} + +int adbc_refresh(struct authdb_config *cfg, time_t now) +{ + FILE *in; + int lineno = 1; + char word1[64], word2[64]; + blob_t b, p; + struct stat st; + + if (cfg->last_check != 0 && cfg->last_check + 2*60 > now) + return 0; + + if (stat("/etc/squark/filter.conf", &st) != 0) + return -1; + + if (cfg->last_change == st.st_ctime) + return 0; + + /* check timestamp */ + + in = fopen("/etc/squark/filter.conf", "r"); + if (in == NULL) + return -1; + + cfg->block_categories = 0; + cfg->hard_block_categories = 0; + while (1) { + b = read_word(in, &lineno, BLOB_BUF(word1)); + if (blob_is_null(b)) + break; + + p = read_word(in, &lineno, BLOB_BUF(word2)); + if (blob_cmp(b, BLOB_STR("redirect_path")) == 0) { + cfg->redirect_url_base = blob_dup(p); + } else if (blob_cmp(b, BLOB_STR("forbid")) == 0) { + cfg->hard_block_categories |= to_category(cfg->db, p); + } else if (blob_cmp(b, BLOB_STR("warn")) == 0) { + cfg->block_categories |= to_category(cfg->db, p); + } + } + cfg->block_categories |= cfg->hard_block_categories; + + fclose(in); +} -- cgit v1.2.3