summaryrefslogtreecommitdiffstats
path: root/squark-auth.c
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2010-08-22 17:54:51 +0300
committerTimo Teräs <timo.teras@iki.fi>2010-08-22 17:54:51 +0300
commit29331f1c5e940499f282aea8155b89fae69f3fb8 (patch)
tree08e2b1ba23017ab72fb02d6eea9df43989258162 /squark-auth.c
parent15d76c3469dc6a98db1dab022aa2254bb8f5fbdb (diff)
downloadsquark-29331f1c5e940499f282aea8155b89fae69f3fb8.tar.bz2
squark-29331f1c5e940499f282aea8155b89fae69f3fb8.tar.xz
auth: rename to squark-auth-snmp
Will implement 'captive portal' style authentication with separate DB later.
Diffstat (limited to 'squark-auth.c')
-rw-r--r--squark-auth.c1151
1 files changed, 0 insertions, 1151 deletions
diff --git a/squark-auth.c b/squark-auth.c
deleted file mode 100644
index 4b88913..0000000
--- a/squark-auth.c
+++ /dev/null
@@ -1,1151 +0,0 @@
-/* squark-auth.c - Squid User Authentication and Rating Kit
- * An external acl helper for Squid which collects authentication
- * information about an IP-address from switches via SNMP.
- *
- * Copyright (C) 2010 Timo Teräs <timo.teras@iki.fi>
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation. See http://www.gnu.org/ for details.
- */
-
-/* TODO:
- * - implement Q-BRIDGE-MIB query
- * - map vlan names to vlan index
- * - print some usage information
- * - poll lldpStatsRemTablesLastChangeTime when doing switch update
- * to figure out if lldp info is valid or not
- */
-
-#include <fcntl.h>
-#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"
-
-/* Compile time configurables */
-#define SWITCH_HASH_SIZE 128
-#define PORT_HASH_SIZE 128
-#define CACHE_TIME 120 /* seconds */
-
-/* Some helpers */
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-#define MAC_LEN 6
-
-#define oid_const(oid) (oid), ARRAY_SIZE(oid)
-#define oid_blob(b) ((oid *) (b).ptr), ((b).len / sizeof(oid))
-
-/* Format specifiers for username type */
-#define FORMAT_CLIENT_IP 0x01 /* %I */
-#define FORMAT_CLIENT_MAC 0x02 /* %M */
-#define FORMAT_SWITCH_NAME 0x04 /* %N */
-#define FORMAT_SWITCH_LOCATION 0x08 /* %L */
-#define FORMAT_PORT_INDEX 0x10 /* %i */
-#define FORMAT_PORT_NAME 0x20 /* %n */
-#define FORMAT_PORT_DESCR 0x40 /* %d */
-#define FORMAT_PORT_WEBAUTH 0x80 /* %w */
-
-/* Some info about the switch which we need */
-#define SWITCHF_NO_LLDP 0x01
-#define SWITCHF_BRIDGE_MIB_HAS_VLAN 0x02
-
-/* IANA-AddressFamilyNumbers */
-#define IANA_AFN_OTHER 0
-#define IANA_AFN_IPV4 1
-#define IANA_AFN_IPV6 2
-
-/* OIDs used by the program */
-static const oid SNMPv2_MIB_sysObjectID[] =
- { SNMP_OID_MIB2, 1, 2, 0 };
-static const oid SNMPv2_MIB_sysName[] =
- { SNMP_OID_MIB2, 1, 5, 0 };
-static const oid SNMPv2_MIB_sysLocation[] =
- { SNMP_OID_MIB2, 1, 6, 0 };
-static const oid IF_MIB_ifDescr[] =
- { SNMP_OID_MIB2, 2, 2, 1, 2 };
-static const oid IF_MIB_ifName[] =
- { SNMP_OID_MIB2, 31, 1, 1, 1, 1 };
-static const oid IF_MIB_ifStackStatus[] =
- { SNMP_OID_MIB2, 31, 1, 2, 1, 3 };
-static const oid IP_MIB_ipNetToPhysicalPhysAddress[] =
- { SNMP_OID_MIB2, 4, 35, 1, 4 };
-static const oid BRIDGE_MIB_dot1dTpFdbPort[] =
- { SNMP_OID_MIB2, 17, 4, 3, 1, 2 };
-static const oid LLDP_lldpLocSysName[] =
- { 1, 0, 8802, 1, 1, 2, 1, 3, 3, 0 };
-static const oid LLDP_lldpRemManAddrIfSubtype[] =
- { 1, 0, 8802, 1, 1, 2, 1, 4, 2, 1, 3 };
-static const oid HP_hpicfUsrAuthWebAuthSessionName[] =
- { SNMP_OID_ENTERPRISES, 11, 2, 14, 11, 5, 1, 19, 5, 1, 1, 2 };
-static const oid HP_hpicfUsrAuthPortReauthenticate[] =
- { SNMP_OID_ENTERPRISES, 11, 2, 14, 11, 5, 1, 19, 2, 1, 1, 4 };
-static const oid SEMI_MIB_hpHttpMgVersion[] =
- { SNMP_OID_ENTERPRISES, 11, 2, 36, 1, 1, 2, 6, 0 };
-
-/* ----------------------------------------------------------------- */
-
-struct switch_info;
-
-static int num_queries = 0;
-static int running = TRUE;
-static int kick_out = FALSE;
-
-static const char *snmp_community = NULL;
-static const char *username_format = "%w";
-static struct switch_info *all_switches[SWITCH_HASH_SIZE];
-static struct switch_info *l3_root_dev, *l2_root_dev;
-static int l3_if_ndx, l2_vlan_ndx;
-static time_t current_time;
-static int username_format_flags;
-
-/* ----------------------------------------------------------------- */
-
-#define BLOB_OID(objid) BLOB_BUF(objid)
-#define BLOB_OID_DYN(objid,len) BLOB_PTR_LEN(objid, (len) * sizeof(oid))
-
-static inline void blob_push_oid(blob_t *b, oid objid)
-{
- if (b->len >= sizeof(objid)) {
- *((oid*) b->ptr) = objid;
- b->ptr += sizeof(oid);
- b->len -= sizeof(oid);
- } else {
- *b = BLOB_NULL;
- }
-}
-
-static inline oid blob_pull_oid(blob_t *b)
-{
- oid objid;
-
- if (b->len >= sizeof(objid)) {
- objid = *((oid*) b->ptr);
- b->ptr += sizeof(oid);
- b->len -= sizeof(oid);
- } else {
- *b = BLOB_NULL;
- objid = -1;
- }
- return objid;
-}
-
-static inline void blob_push_oid_dump(blob_t *b, blob_t d)
-{
- int i;
-
- if (b->len >= d.len * sizeof(oid)) {
- for (i = 0; i < d.len; i++) {
- *((oid*) b->ptr) = (unsigned char) d.ptr[i];
- b->ptr += sizeof(oid);
- b->len -= sizeof(oid);
- }
- } else {
- *b = BLOB_NULL;
- }
-}
-
-static inline void blob_pull_oid_dump(blob_t *b, blob_t d)
-{
- int i;
-
- if (b->len >= d.len * sizeof(oid)) {
- for (i = 0; i < d.len; i++) {
- d.ptr[i] = (unsigned char) *((oid*) b->ptr);
- b->ptr += sizeof(oid);
- b->len -= sizeof(oid);
- }
- } else {
- *b = BLOB_NULL;
- }
-}
-
-/* ----------------------------------------------------------------- */
-
-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;
- int type=0, len=0;
-
- switch (addr->any.sa_family) {
- case AF_INET:
- type = IANA_AFN_IPV4;
- len = 4;
- ptr = (unsigned char*) &addr->ipv4.sin_addr;
- break;
- }
- if (type == 0 || b->len < len) {
- *b = BLOB_NULL;
- return;
- }
- blob_push_oid(b, type);
- blob_push_oid(b, len);
- blob_push_oid_dump(b, BLOB_PTR_LEN(ptr, len));
-}
-
-sockaddr_any *blob_pull_iana_afn(blob_t *b, sockaddr_any *addr)
-{
- unsigned char *ptr = NULL;
- int type, len;
-
- memset(addr, 0, sizeof(*addr));
- type = blob_pull_oid(b);
- len = blob_pull_oid(b);
- if (type == IANA_AFN_IPV4 && len == 4) {
- addr->ipv4.sin_family = AF_INET;
- ptr = (unsigned char*) &addr->ipv4.sin_addr;
- }
- if (ptr == NULL) {
- blob_pull_skip(b, len);
- return NULL;
- }
- blob_pull_oid_dump(b, BLOB_PTR_LEN(ptr, len));
- 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)
-{
- void **pptr = ptr;
- if (*pptr != NULL) {
- free(*pptr);
- *pptr = NULL;
- }
-}
-
-struct cache_control {
- time_t update_time;
- struct auth_context * sleepers;
-};
-
-struct switch_port_info {
- struct switch_port_info * next;
- int port;
- struct cache_control cache_control;
- sockaddr_any link_partner;
-};
-
-struct switch_info {
- struct switch_info * next;
- sockaddr_any addr;
- netsnmp_session * session;
-
- struct cache_control cache_control;
- int flags;
- int info_available;
- char * system_name;
- char * system_location;
- char * system_version;
- blob_t system_oid;
-
- struct switch_port_info * all_ports[PORT_HASH_SIZE];
-};
-
-struct auth_context {
- char * token;
- sockaddr_any addr;
- unsigned char mac[MAC_LEN];
- int info_available;
- struct switch_info * current_switch;
- struct switch_port_info *spi;
- int local_port;
- int lldp_port[8];
- int num_lldp_ports;
- char * port_name;
- char * port_descr;
- char * webauth_name;
-
- void (*pending_operation)(struct auth_context *);
- struct auth_context * next_sleeper;
-};
-
-static void cache_update_time(void)
-{
- current_time = time(NULL);
-}
-
-static int cache_refresh(
- struct cache_control *cc, struct auth_context *auth,
- void (*callback)(struct auth_context *auth))
-{
- int ret;
-
- if (cc->update_time == -1 ||
- cc->update_time + CACHE_TIME >= current_time) {
- callback(auth);
- return 0;
- }
-
- auth->pending_operation = callback;
-
- ret = (cc->sleepers == NULL);
- auth->next_sleeper = cc->sleepers;
- cc->sleepers = auth;
-
- return ret;
-}
-
-static void cache_update(struct cache_control *cc)
-{
- struct auth_context *auth, *next;
-
- cc->update_time = current_time;
- auth = cc->sleepers;
- cc->sleepers = NULL;
- for (; auth; auth = next) {
- next = auth->next_sleeper;
- auth->pending_operation(auth);
- }
-}
-
-static void cache_update_manual(struct cache_control *cc)
-{
- cache_update(cc);
- cc->update_time = -1;
-}
-
-static void switch_info_free(struct switch_info *si)
-{
- safe_free(&si->system_name);
- safe_free(&si->system_location);
- safe_free(&si->system_version);
- safe_free(&si->system_oid.ptr);
-}
-
-struct switch_info *get_switch(sockaddr_any *addr)
-{
- struct snmp_session config;
- struct switch_info *si;
- unsigned int bucket = addr_hash(addr) % ARRAY_SIZE(all_switches);
-
- for (si = all_switches[bucket]; si != NULL; si = si->next)
- if (addr_cmp(&si->addr, addr) == 0)
- return si;
-
- si = calloc(1, sizeof(*si));
- if (si == NULL)
- return NULL;
-
- addr_copy(&si->addr, addr);
-
- snmp_sess_init(&config);
- if (snmp_community != NULL) {
- config.version = SNMP_VERSION_2c;
- config.community = (unsigned char *) snmp_community;
- config.community_len = strlen(snmp_community);
- }
- config.peername = (char *) addr_print(addr);
- si->session = snmp_open(&config);
-
- si->next = all_switches[bucket];
- all_switches[bucket] = si;
-
- return si;
-}
-
-struct switch_port_info *get_switch_port(struct switch_info *si, int port)
-{
- unsigned int bucket = port % ARRAY_SIZE(si->all_ports);
- struct switch_port_info *spi;
-
- if (si == NULL)
- return NULL;
-
- for (spi = si->all_ports[bucket]; spi != NULL; spi = spi->next)
- if (spi->port == port)
- return spi;
-
- spi = calloc(1, sizeof(*spi));
- if (spi == NULL)
- return NULL;
-
- spi->port = port;
- spi->next = si->all_ports[bucket];
- si->all_ports[bucket] = spi;
-
- return spi;
-}
-
-void link_switch(const char *a, int ap, const char *b, int bp)
-{
- struct switch_info *sia, *sib;
- struct switch_port_info *spia, *spib;
- sockaddr_any addr;
-
- sia = get_switch(addr_parse(a, &addr));
- spia = get_switch_port(sia, ap);
-
- sib = get_switch(addr_parse(b, &addr));
- spib = get_switch_port(sib, bp);
-
- addr_copy(&spia->link_partner, &sib->addr);
- addr_copy(&spib->link_partner, &sia->addr);
-
- cache_update_manual(&spia->cache_control);
- cache_update_manual(&spib->cache_control);
-}
-
-static void auth_query_switch_info(struct auth_context *auth);
-static void auth_query_lldp(struct auth_context *auth, int root_query);
-
-static void auth_free(struct auth_context *auth)
-{
- safe_free(&auth->token);
- safe_free(&auth->port_name);
- safe_free(&auth->port_descr);
- safe_free(&auth->webauth_name);
- free(auth);
-}
-
-int resolve_ifName2ifIndex(struct switch_info *si, blob_t ifName)
-{
- netsnmp_pdu *pdu, *response = NULL;
- netsnmp_variable_list *vars, *lastvar = NULL;
- int rc = -1;
-
- pdu = snmp_pdu_create(SNMP_MSG_GETBULK);
- pdu->non_repeaters = 0;
- pdu->max_repetitions = 10;
- snmp_add_null_var(pdu, oid_const(IF_MIB_ifName));
-
- do {
- if (snmp_synch_response(si->session, pdu, &response) != 0)
- return -1;
- if (response->errstat != SNMP_ERR_NOERROR)
- goto done;
-
- for (vars = response->variables; vars; vars = vars->next_variable) {
- lastvar = vars;
-
- if (vars->name_length < ARRAY_SIZE(IF_MIB_ifName) ||
- memcmp(vars->name, IF_MIB_ifName, sizeof(IF_MIB_ifName)) != 0)
- goto done;
-
- if (vars->type != ASN_OCTET_STR)
- continue;
-
- if (blob_cmp(ifName, BLOB_PTR_LEN(vars->val.string, vars->val_len)) != 0)
- continue;
-
- rc = vars->name[vars->name_length - 1];
- goto done;
- }
-
- pdu = snmp_pdu_create(SNMP_MSG_GETBULK);
- pdu->non_repeaters = 0;
- pdu->max_repetitions = 10;
- snmp_add_null_var(pdu, lastvar->name, lastvar->name_length);
-
- snmp_free_pdu(response);
- response = NULL;
- } while (1);
-
-done:
- if (response)
- snmp_free_pdu(response);
- return rc;
-}
-
-
-static int parse_format(const char *fmt)
-{
- int flags = 0;
- const char *p = fmt;
-
- while ((p = strchr(p, '%')) != NULL) {
- switch (p[1]) {
- case 'I':
- flags |= FORMAT_CLIENT_IP;
- break;
- case 'M':
- flags |= FORMAT_CLIENT_MAC;
- break;
- case 'N':
- flags |= FORMAT_SWITCH_NAME;
- break;
- case 'L':
- flags |= FORMAT_SWITCH_LOCATION;
- break;
- case 'i':
- flags |= FORMAT_PORT_INDEX;
- break;
- case 'n':
- flags |= FORMAT_PORT_NAME;
- break;
- case 'd':
- flags |= FORMAT_PORT_DESCR;
- break;
- case 'w':
- flags |= FORMAT_PORT_WEBAUTH;
- break;
- }
- p++;
- }
- return flags;
-}
-
-static void blob_push_formatted_username(
- blob_t *b, const char *fmt, struct auth_context *auth)
-{
- const char *o = fmt, *p = fmt;
- struct switch_info *si = auth->current_switch;
-
- while ((p = strchr(p, '%')) != NULL) {
- blob_push(b, BLOB_PTR_LEN(o, p - o));
- switch (p[1]) {
- case 'I':
- blob_push(b, BLOB_STRLEN((char*) addr_print(&auth->addr)));
- break;
- case 'M':
- blob_push_hexdump(b, BLOB_BUF(auth->mac));
- break;
- case 'N':
- blob_push(b, BLOB_STRLEN(si->system_name));
- break;
- case 'L':
- blob_push(b, BLOB_STRLEN(si->system_location));
- break;
- case 'i':
- blob_push_uint(b, auth->local_port, 10);
- break;
- case 'n':
- blob_push(b, BLOB_STRLEN(auth->port_name));
- break;
- case 'd':
- blob_push(b, BLOB_STRLEN(auth->port_descr));
- break;
- case 'w':
- blob_push(b, BLOB_STRLEN(auth->webauth_name));
- break;
- default:
- o = p;
- p++;
- continue;
- }
- p += 2;
- o = p;
- }
- blob_push(b, BLOB_STRLEN((char*) o));
-}
-
-static int auth_ok(struct auth_context *auth)
-{
- return (auth->info_available & username_format_flags) == username_format_flags;
-}
-
-static void auth_completed(struct auth_context *auth)
-{
- char tmp[256];
- blob_t b = BLOB_BUF(tmp);
-
- blob_push(&b, BLOB_STRLEN(auth->token));
- if (auth_ok(auth)) {
- blob_push(&b, BLOB_STR(" OK user="));
- blob_push_formatted_username(&b, username_format, auth);
- blob_push(&b, BLOB_PTR_LEN("\n", 1));
- } else {
- blob_push(&b, BLOB_STR(" ERR\n"));
- }
- b = blob_pushed(BLOB_BUF(tmp), b);
- write(STDOUT_FILENO, b.ptr, b.len);
-
- auth_free(auth);
- num_queries--;
-}
-
-static void auth_talk_snmp(struct auth_context *auth, netsnmp_session *s, netsnmp_pdu *pdu, netsnmp_callback callback)
-{
- if (snmp_async_send(s, pdu, callback, auth) == 0) {
- snmp_free_pdu(pdu);
- auth_completed(auth);
- }
-}
-
-static void cache_talk_snmp(struct cache_control *cc, netsnmp_session *s, netsnmp_pdu *pdu, netsnmp_callback callback, struct auth_context *auth)
-{
- if (snmp_async_send(s, pdu, callback, auth) == 0) {
- snmp_free_pdu(pdu);
- cache_update(cc);
- }
-}
-
-static blob_t var_parse_type(netsnmp_variable_list **varptr, int asn_tag)
-{
- netsnmp_variable_list *var = *varptr;
- if (var == NULL)
- return BLOB_NULL;
-
- *varptr = var->next_variable;
- if (var->type != asn_tag)
- return BLOB_NULL;
-
- return BLOB_PTR_LEN(var->val.string, var->val_len);
-}
-
-static void auth_force_reauthentication(struct auth_context *auth)
-{
- struct switch_info *si = auth->current_switch;
- netsnmp_pdu *pdu;
- oid query_oids[ARRAY_SIZE(HP_hpicfUsrAuthPortReauthenticate)+1];
- blob_t b = BLOB_BUF(query_oids);
- long one = 1;
-
- pdu = snmp_pdu_create(SNMP_MSG_SET);
- blob_push(&b, BLOB_OID(HP_hpicfUsrAuthPortReauthenticate));
- blob_push_oid(&b, auth->local_port);
- b = blob_pushed(BLOB_OID(query_oids), b);
-
- snmp_pdu_add_variable(pdu, oid_blob(b), ASN_INTEGER,
- (u_char *) &one, sizeof(one));
-
- /* Send asynchornously - ignore response */
- if (snmp_send(si->session, pdu) == 0)
- snmp_free_pdu(pdu);
-}
-
-static int auth_handle_portinfo_reply(int oper, netsnmp_session *s, int reqid, netsnmp_pdu *resp, void *data)
-{
- struct auth_context *auth = data;
- netsnmp_variable_list *var;
-
- if (oper != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE)
- goto done;
-
- var = resp->variables;
- if (username_format_flags & FORMAT_PORT_NAME)
- auth->port_name = blob_cstr_dup(var_parse_type(&var, ASN_OCTET_STR));
- if (auth->port_name)
- auth->info_available |= FORMAT_PORT_NAME;
- if (username_format_flags & FORMAT_PORT_DESCR)
- auth->port_descr = blob_cstr_dup(var_parse_type(&var, ASN_OCTET_STR));
- if (auth->port_descr)
- auth->info_available |= FORMAT_PORT_DESCR;
- if (username_format_flags & FORMAT_PORT_WEBAUTH)
- auth->webauth_name = blob_cstr_dup(var_parse_type(&var, ASN_OCTET_STR));
- if (auth->webauth_name)
- auth->info_available |= FORMAT_PORT_WEBAUTH;
-
-done:
- if (kick_out && auth_ok(auth))
- auth_force_reauthentication(auth);
-
- auth_completed(auth);
- return 1;
-}
-
-static void auth_query_port_info(struct auth_context *auth)
-{
- struct switch_info *si = auth->current_switch;
- netsnmp_pdu *pdu;
- oid query_oids[MAX_OID_LEN];
- blob_t query;
-
- if (auth_ok(auth)) {
- auth_completed(auth);
- return;
- }
-
- pdu = snmp_pdu_create(SNMP_MSG_GET);
- if (username_format_flags & FORMAT_PORT_NAME) {
- query = BLOB_OID(query_oids);
- blob_push(&query, BLOB_OID(IF_MIB_ifName));
- blob_push_oid(&query, auth->local_port);
- query = blob_pushed(BLOB_OID(query_oids), query);
- snmp_add_null_var(pdu, oid_blob(query));
- }
- if (username_format_flags & FORMAT_PORT_DESCR) {
- query = BLOB_OID(query_oids);
- blob_push(&query, BLOB_OID(IF_MIB_ifDescr));
- blob_push_oid(&query, auth->local_port);
- query = blob_pushed(BLOB_OID(query_oids), query);
- snmp_add_null_var(pdu, oid_blob(query));
- }
- if (username_format_flags & FORMAT_PORT_WEBAUTH) {
- query = BLOB_OID(query_oids);
- blob_push(&query, BLOB_OID(HP_hpicfUsrAuthWebAuthSessionName));
- blob_push_oid(&query, auth->local_port);
- blob_push_oid_dump(&query, BLOB_BUF(auth->mac));
- query = blob_pushed(BLOB_OID(query_oids), query);
- snmp_add_null_var(pdu, oid_blob(query));
- }
- auth_talk_snmp(auth, si->session, pdu, auth_handle_portinfo_reply);
-}
-
-static int auth_handle_lldp_reply(int oper, netsnmp_session *s, int reqid, netsnmp_pdu *resp, void *data)
-{
- struct auth_context *auth = data;
- struct switch_port_info *spi = auth->spi;
- netsnmp_variable_list *var = resp->variables;
- blob_t res;
- int i;
-
- if (oper != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE)
- goto fail;
-
- /* print_variable(var->name, var->name_length, var); */
-
- for (i = 0; i < auth->num_lldp_ports; i++) {
- if (var == NULL)
- goto fail;
- if (var->type != ASN_INTEGER)
- continue;
- /* INDEX: TimeFilter, Port, Idx, Family, Addr */
- res = BLOB_OID_DYN(var->name, var->name_length);
- if (blob_pull_matching(&res, BLOB_OID(LLDP_lldpRemManAddrIfSubtype)) &&
- blob_pull_oid(&res) == 0 &&
- blob_pull_oid(&res) == auth->lldp_port[i]) {
- /* We have mathing LLDP neighbor */
- blob_pull_oid(&res);
- blob_pull_iana_afn(&res, &spi->link_partner);
- cache_update(&spi->cache_control);
- return 1;
- }
-
- var = var->next_variable;
- }
- auth->num_lldp_ports = 0;
- for (; var; var = var->next_variable) {
- if (var->type != ASN_INTEGER)
- break;
- /* print_variable(var->name, var->name_length, var); */
- res = BLOB_OID_DYN(var->name, var->name_length);
- if (!blob_pull_matching(&res, BLOB_OID(IF_MIB_ifStackStatus)))
- break;
- if (blob_pull_oid(&res) != auth->local_port)
- break;
- auth->lldp_port[auth->num_lldp_ports++] = blob_pull_oid(&res);
- if (auth->num_lldp_ports >= ARRAY_SIZE(auth->lldp_port))
- break;
- }
- if (auth->num_lldp_ports) {
- auth_query_lldp(auth, FALSE);
- return 1;
- }
-fail:
- cache_update(&spi->cache_control);
- return 1;
-}
-
-static void auth_query_lldp(struct auth_context *auth, int root_query)
-{
- struct switch_info *si = auth->current_switch;
- struct switch_port_info *spi = auth->spi;
- netsnmp_pdu *pdu;
- oid query_oids[MAX_OID_LEN];
- blob_t query;
- int i;
-
- /* printf("Query LLDP info for %s:%d\n", addr_print(&si->addr), spi->port); */
-
- if (si->flags & SWITCHF_NO_LLDP) {
- memset(&spi->link_partner, 0, sizeof(spi->link_partner));
- cache_update(&spi->cache_control);
- return;
- }
-
- if (root_query) {
- auth->num_lldp_ports = 1;
- auth->lldp_port[0] = auth->local_port;
- }
-
- pdu = snmp_pdu_create(SNMP_MSG_GETBULK);
- pdu->non_repeaters = auth->num_lldp_ports;
- pdu->max_repetitions = 8;
-
- for (i = 0; i < auth->num_lldp_ports; i++) {
- /* Query LLDP neighbor. lldpRemManAddrTable is INDEXed with
- * [TimeFilter, LocalPort, Index, AddrSubType, Addr] */
- query = BLOB_OID(query_oids);
- blob_push(&query, BLOB_OID(LLDP_lldpRemManAddrIfSubtype));
- blob_push_oid(&query, 0);
- blob_push_oid(&query, auth->lldp_port[i]);
- query = blob_pushed(BLOB_OID(query_oids), query);
- snmp_add_null_var(pdu, oid_blob(query));
- }
-
- if (root_query) {
- /* Query interface stacking in case this is aggregated trunk:
- * IF-MIB::ifStackStatus.<ifUpperIndex>.<ifLowerIndex> */
- query = BLOB_OID(query_oids);
- blob_push(&query, BLOB_OID(IF_MIB_ifStackStatus));
- blob_push_oid(&query, auth->local_port);
- query = blob_pushed(BLOB_OID(query_oids), query);
- snmp_add_null_var(pdu, oid_blob(query));
- }
-
- cache_talk_snmp(&spi->cache_control, si->session, pdu, auth_handle_lldp_reply, auth);
-}
-
-static void auth_check_spi(struct auth_context *auth)
-{
- struct switch_port_info *spi = auth->spi;
-
- if (addr_len(&spi->link_partner) != 0) {
- auth->current_switch = get_switch(&spi->link_partner);
- auth_query_switch_info(auth);
- } else {
- auth_query_port_info(auth);
- }
-}
-
-static int auth_handle_fib_reply(int oper, netsnmp_session *s, int reqid, netsnmp_pdu *resp, void *data)
-{
- struct auth_context *auth = data;
- struct switch_info *si = auth->current_switch;
- struct switch_port_info *spi;
- netsnmp_variable_list *var;
-
- if (oper != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE)
- goto failed;
-
- var = resp->variables;
- /* print_variable(var->name, var->name_length, var); */
-
- if (var->type != ASN_INTEGER)
- goto failed;
-
- auth->local_port = *var->val.integer;
- auth->info_available |= FORMAT_PORT_INDEX;
- auth->spi = spi = get_switch_port(si, auth->local_port);
- if (cache_refresh(&spi->cache_control, auth, auth_check_spi))
- auth_query_lldp(auth, TRUE);
- return 1;
-
- /* No further info available */
-failed:
- auth_completed(auth);
- return 1;
-}
-
-static void auth_query_fib(struct auth_context *auth)
-{
- oid query_oids[MAX_OID_LEN];
- blob_t query;
- struct switch_info *si = auth->current_switch;
- netsnmp_pdu *pdu;
-
- auth->info_available |= si->info_available;
-
- /* printf("Probing switch %s\n", addr_print(&si->addr)); */
-
- pdu = snmp_pdu_create(SNMP_MSG_GET);
-
- /* FIXME: Implement Q-BRIDGE-MIB query too. */
-
- /* BRIDGE-MIB::dot1dTpFdbPort.<MAC> = INTEGER: port */
- query = BLOB_OID(query_oids);
- blob_push(&query, BLOB_OID(BRIDGE_MIB_dot1dTpFdbPort));
- if (si->flags & SWITCHF_BRIDGE_MIB_HAS_VLAN)
- blob_push_oid(&query, l2_vlan_ndx);
- blob_push_oid_dump(&query, BLOB_BUF(auth->mac));
- query = blob_pushed(BLOB_OID(query_oids), query);
- snmp_add_null_var(pdu, oid_blob(query));
-
- auth_talk_snmp(auth, si->session, pdu, auth_handle_fib_reply);
-}
-
-static int auth_handle_switch_info_reply(int oper, netsnmp_session *s, int reqid, netsnmp_pdu *resp, void *data)
-{
- static const oid HP_ICF_OID_hpEtherSwitch[] =
- { SNMP_OID_ENTERPRISES, 11, 2, 3, 7, 11 };
- struct auth_context *auth = data;
- struct switch_info *si = auth->current_switch;
- netsnmp_variable_list *var;
- blob_t b;
-
- switch_info_free(si);
- var = resp->variables;
- si->system_name = blob_cstr_dup(var_parse_type(&var, ASN_OCTET_STR));
- si->system_location = blob_cstr_dup(var_parse_type(&var, ASN_OCTET_STR));
- si->system_oid = blob_dup(var_parse_type(&var, ASN_OBJECT_ID));
- si->system_version = blob_cstr_dup(var_parse_type(&var, ASN_OCTET_STR));
- if (blob_is_null(var_parse_type(&var, ASN_OCTET_STR)))
- si->flags |= SWITCHF_NO_LLDP;
- if (si->system_name)
- si->info_available |= FORMAT_SWITCH_NAME;
- if (si->system_location)
- si->info_available |= FORMAT_SWITCH_LOCATION;
- b = si->system_oid;
- if (blob_pull_matching(&b, BLOB_OID(HP_ICF_OID_hpEtherSwitch))) {
- /* Hewlett-Packard ProCurve Switches */
- switch (blob_pull_oid(&b)) {
- case 104: /* 1810G-24 */
- si->flags |= SWITCHF_BRIDGE_MIB_HAS_VLAN;
- break;
- }
- }
- cache_update(&si->cache_control);
- return 1;
-}
-
-static void auth_query_switch_info(struct auth_context *auth)
-{
- struct switch_info *si = auth->current_switch;
- netsnmp_pdu *pdu;
-
- auth->info_available &=
- ~(FORMAT_SWITCH_NAME | FORMAT_SWITCH_LOCATION |
- FORMAT_PORT_INDEX);
-
- if (!cache_refresh(&si->cache_control, auth, auth_query_fib))
- return;
-
- pdu = snmp_pdu_create(SNMP_MSG_GET);
- snmp_add_null_var(pdu, oid_const(SNMPv2_MIB_sysName));
- snmp_add_null_var(pdu, oid_const(SNMPv2_MIB_sysLocation));
- snmp_add_null_var(pdu, oid_const(SNMPv2_MIB_sysObjectID));
- snmp_add_null_var(pdu, oid_const(SEMI_MIB_hpHttpMgVersion));
- snmp_add_null_var(pdu, oid_const(LLDP_lldpLocSysName));
- cache_talk_snmp(&si->cache_control, si->session, pdu, auth_handle_switch_info_reply, auth);
-}
-
-static int auth_handle_arp_reply(int oper, netsnmp_session *s, int reqid, netsnmp_pdu *resp, void *data)
-{
- struct auth_context *auth = data;
- netsnmp_variable_list *var = resp->variables;
-
- if (oper == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE &&
- var->type == ASN_OCTET_STR &&
- var->val_len == MAC_LEN) {
- memcpy(auth->mac, var->val.string, MAC_LEN);
- auth->info_available |= FORMAT_CLIENT_MAC;
- if (!auth_ok(auth)) {
- auth->current_switch = l2_root_dev;
- auth_query_switch_info(auth);
- return 1;
- }
- }
-
- auth_completed(auth);
- return 1;
-}
-
-void start_authentication(const char *token, const char *ip)
-{
- struct auth_context *auth;
- oid query_oids[MAX_OID_LEN];
- blob_t query;
- netsnmp_pdu *pdu;
-
- num_queries++;
-
- auth = calloc(1, sizeof(*auth));
- auth->token = strdup(token);
- if (addr_parse(ip, &auth->addr) == NULL) {
- auth_completed(auth);
- return;
- }
- auth->info_available = FORMAT_CLIENT_IP;
-
- /* IP-MIB::ipNetToPhysicalPhysAddress.<ifIndex>.ipv4."1.2.3.4"
- * = STRING: 01:12:34:56:78:9a */
- pdu = snmp_pdu_create(SNMP_MSG_GET);
-
- query = BLOB_OID(query_oids);
- blob_push(&query, BLOB_OID(IP_MIB_ipNetToPhysicalPhysAddress));
- blob_push_oid(&query, l3_if_ndx);
- blob_push_iana_afn(&query, &auth->addr);
- query = blob_pushed(BLOB_OID(query_oids), query);
- snmp_add_null_var(pdu, oid_blob(query));
-
- auth_talk_snmp(auth, l3_root_dev->session, pdu, auth_handle_arp_reply);
-}
-
-void read_input(void)
-{
- static char buffer[256];
- static int len = 0;
- char token[32], ip[256], *p;
- int r;
-
- r = read(STDIN_FILENO, &buffer[len], sizeof(buffer) - len);
- if (r < 0)
- return;
- if (r == 0) {
- running = FALSE;
- return;
- }
-
- len += r;
- do {
- p = strchr(buffer, '\n');
- if (p == NULL)
- return;
-
- *p = 0;
- if (sscanf(buffer, "%s %s", token, ip) == 2)
- start_authentication(token, ip);
- len -= (p - buffer) + 1;
- memcpy(buffer, p + 1, len);
- } while (len);
-}
-
-void load_topology(const char *file)
-{
- char a_ip[64], b_ip[64];
- int a_port, b_port;
- FILE *in;
-
- in = fopen(file, "r");
- if (in == NULL)
- return;
-
- while (!feof(in)) {
- if (fscanf(in, "%s %d %s %d\n",
- a_ip, &a_port, b_ip, &b_port) == 4)
- link_switch(a_ip, a_port, b_ip, b_port);
- }
- fclose(in);
-}
-
-int main(int argc, char **argv)
-{
- const char *l3_root = NULL, *l3_ifname = NULL;
- const char *l2_root = NULL, *l2_vlan = NULL;
- struct timeval timeout;
- sockaddr_any addr;
- fd_set fdset;
- int opt, fds, block, i;
-
- setenv("MIBS", "", 1);
- init_snmp("squark-auth");
-
- while ((opt = getopt(argc, argv, "c:r:i:R:v:f:T:K")) != -1) {
- switch (opt) {
- case 'c':
- snmp_community = optarg;
- break;
- case 'r':
- l3_root = optarg;
- break;
- case 'i':
- l3_ifname = optarg;
- break;
- case 'R':
- l2_root = optarg;
- break;
- case 'v':
- l2_vlan = optarg;
- break;
- case 'f':
- username_format = optarg;
- break;
- case 'T':
- load_topology(optarg);
- break;
- case 'K':
- kick_out = TRUE;
- break;
- }
- }
- argc -= optind;
- argv += optind;
-
- if (l3_root == NULL || l3_ifname == NULL || l2_vlan == NULL) {
- printf("Mandatory information missing\n");
- return 1;
- }
-
- if (l2_root == NULL)
- l2_root = l3_root;
-
- l3_root_dev = get_switch(addr_parse(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_vlan_ndx = atoi(l2_vlan);
- username_format_flags = parse_format(username_format);
-
- if (kick_out)
- username_format_flags |= FORMAT_PORT_WEBAUTH;
-
- for (i = 0; i < argc; i++) {
- start_authentication(argv[i], argv[i]);
- running = FALSE;
- }
-
- fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
- while (num_queries || running) {
- fds = 0;
- block = 1;
-
- FD_ZERO(&fdset);
- if (running) {
- FD_SET(STDIN_FILENO, &fdset);
- fds = STDIN_FILENO + 1;
- }
- snmp_select_info(&fds, &fdset, &timeout, &block);
- fds = select(fds, &fdset, NULL, NULL, block ? NULL : &timeout);
- cache_update_time();
- if (fds) {
- if (FD_ISSET(STDIN_FILENO, &fdset))
- read_input();
- snmp_read(&fdset);
- } else
- snmp_timeout();
- }
-
- return 0;
-}