diff options
author | Timo Teräs <timo.teras@iki.fi> | 2010-08-22 17:54:51 +0300 |
---|---|---|
committer | Timo Teräs <timo.teras@iki.fi> | 2010-08-22 17:54:51 +0300 |
commit | 29331f1c5e940499f282aea8155b89fae69f3fb8 (patch) | |
tree | 08e2b1ba23017ab72fb02d6eea9df43989258162 /squark-auth.c | |
parent | 15d76c3469dc6a98db1dab022aa2254bb8f5fbdb (diff) | |
download | squark-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.c | 1151 |
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; -} |