436 lines
8.4 KiB
C++
436 lines
8.4 KiB
C++
/*
|
|
* Copyright 1991 Silicon Graphics, Inc. All rights reserved.
|
|
*
|
|
* Community-based SNMP Authorization
|
|
*
|
|
* $Revision: 1.2 $
|
|
*
|
|
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
|
|
* the contents of this file may not be disclosed to third parties, copied or
|
|
* duplicated in any form, in whole or in part, without the prior written
|
|
* permission of Silicon Graphics, Inc.
|
|
*
|
|
* RESTRICTED RIGHTS LEGEND:
|
|
* Use, duplication or disclosure by the Government is subject to restrictions
|
|
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
|
|
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
|
|
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
|
|
* rights reserved under the Copyright Laws of the United States.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <syslog.h>
|
|
#include <string.h>
|
|
#include <bstring.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include "oid.h"
|
|
#include "asn1.h"
|
|
#include "snmp.h"
|
|
#include "packet.h"
|
|
#include "message.h"
|
|
#include "sat.h"
|
|
#include "table.h"
|
|
#include "subagent.h"
|
|
#include "agent.h"
|
|
#include "commauth.h"
|
|
extern "C" {
|
|
#include "exception.h"
|
|
int gethostname(char *, int);
|
|
}
|
|
|
|
static char *wildcard = "*";
|
|
|
|
/*
|
|
* Hosts
|
|
*/
|
|
commAuthHost::commAuthHost(void)
|
|
{
|
|
next = 0;
|
|
addr.s_addr = 0;
|
|
}
|
|
|
|
unsigned int
|
|
commAuthHost::set(const char *s)
|
|
{
|
|
// Try the wildcard
|
|
if (strcmp(s, wildcard) == 0) {
|
|
addr.s_addr = INADDR_NONE;
|
|
return 1;
|
|
}
|
|
|
|
// Try an Internet Address
|
|
addr.s_addr = inet_addr(s);
|
|
if (addr.s_addr == INADDR_NONE) {
|
|
// Try a host name
|
|
struct hostent *he = gethostbyname(s);
|
|
if (he == 0)
|
|
return 0;
|
|
bcopy(he->h_addr, &addr, sizeof addr);
|
|
}
|
|
|
|
// Change localhost into this host's address
|
|
if (addr.s_addr == 0x7f000001) {
|
|
char buf[64];
|
|
if (gethostname(buf, 64) < 0)
|
|
return 0;
|
|
struct hostent *he = gethostbyname(buf);
|
|
if (he == 0)
|
|
return 0;
|
|
bcopy(he->h_addr, &addr, sizeof addr);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Communities
|
|
*/
|
|
commAuthCommunity::commAuthCommunity(void)
|
|
{
|
|
next = 0;
|
|
community = 0;
|
|
}
|
|
|
|
commAuthCommunity::~commAuthCommunity(void)
|
|
{
|
|
if (community != 0)
|
|
delete community;
|
|
}
|
|
|
|
unsigned int
|
|
commAuthCommunity::set(const char *s)
|
|
{
|
|
if (strcmp(s, wildcard) == 0)
|
|
community = 0;
|
|
else {
|
|
length = strlen(s);
|
|
community = new char[length];
|
|
bcopy(s, community, length);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Operations
|
|
*/
|
|
const unsigned int opSet = 2;
|
|
const unsigned int opGet = 1;
|
|
|
|
commAuthOperation::commAuthOperation(void)
|
|
{
|
|
op = 0;
|
|
}
|
|
|
|
unsigned int
|
|
commAuthOperation::set(const char *s)
|
|
{
|
|
if (strcasecmp(s, "set") == 0)
|
|
op |= opSet;
|
|
else if (strcasecmp(s, "get") == 0)
|
|
op |= opGet;
|
|
else
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Rules
|
|
*/
|
|
commAuthRule::commAuthRule(void)
|
|
{
|
|
next = 0;
|
|
hosts = 0;
|
|
communities = 0;
|
|
}
|
|
|
|
commAuthRule::~commAuthRule(void)
|
|
{
|
|
for (commAuthHost *h; hosts != 0; hosts = h) {
|
|
h = hosts->next;
|
|
delete hosts;
|
|
}
|
|
for (commAuthCommunity *c; communities != 0; communities = c) {
|
|
c = communities->next;
|
|
delete communities;
|
|
}
|
|
}
|
|
|
|
int
|
|
commAuthRule::parse(char *s)
|
|
{
|
|
char *p, *next;
|
|
|
|
// Separate out hosts
|
|
next = strchr(s, ':');
|
|
if (next == 0)
|
|
return 0;
|
|
*next++ = '\0';
|
|
|
|
// Parse hosts
|
|
commAuthHost **h;
|
|
for (h = &hosts; (p = strtok(s, ",")) != 0; h = &((*h)->next)) {
|
|
*h = new commAuthHost;
|
|
if (!(*h)->set(p))
|
|
return 0;
|
|
s = 0;
|
|
}
|
|
if (hosts == 0)
|
|
return 0;
|
|
|
|
// Separate out communities
|
|
s = next;
|
|
next = strchr(s, '/');
|
|
if (next != 0)
|
|
*next++ = '\0';
|
|
|
|
// Parse communities
|
|
commAuthCommunity **c;
|
|
for (c = &communities; (p = strtok(s, ",")) != 0; c = &((*c)->next)) {
|
|
*c = new commAuthCommunity;
|
|
if (!(*c)->set(p))
|
|
return 0;
|
|
s = 0;
|
|
}
|
|
if (communities == 0)
|
|
return 0;
|
|
|
|
// Parse operations
|
|
s = next;
|
|
while ((p = strtok(s, ",")) != 0) {
|
|
if (!operations.set(p))
|
|
return 0;
|
|
s = 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
commAuthRule::match(struct in_addr *a, asnOctetString *o, int pdutype)
|
|
{
|
|
int total = 0;
|
|
int points = 0;
|
|
const int wild = 0x1;
|
|
const int exactHost = 0x10;
|
|
const int exactCommunity = 0x100;
|
|
const int exactOperation = 0x1000;
|
|
|
|
// Match operation first because it's fast
|
|
if (operations.op != 0) {
|
|
switch (pdutype) {
|
|
case SNMP_GetRequest:
|
|
case SNMP_GetNextRequest:
|
|
if (operations.op & opGet)
|
|
points = exactOperation;
|
|
break;
|
|
case SNMP_SetRequest:
|
|
if (operations.op & opSet)
|
|
points = exactOperation;
|
|
break;
|
|
}
|
|
if (points == 0)
|
|
return 0;
|
|
total += points;
|
|
points = 0;
|
|
}
|
|
|
|
// Match host
|
|
for (commAuthHost *h = hosts; h != 0; h = h->next) {
|
|
if (h->addr.s_addr == INADDR_NONE)
|
|
points = wild;
|
|
else if (h->addr.s_addr == a->s_addr) {
|
|
points = exactHost;
|
|
break;
|
|
}
|
|
}
|
|
if (points == 0)
|
|
return 0;
|
|
total += points;
|
|
points = 0;
|
|
|
|
// Match community
|
|
char *c = o->getValue();
|
|
unsigned int l = o->getLength();
|
|
for (commAuthCommunity *co = communities; co != 0; co = co->next) {
|
|
if (co->community == 0)
|
|
points = wild;
|
|
else if (co->length == l && bcmp(co->community, c, l) == 0) {
|
|
points = exactCommunity;
|
|
break;
|
|
}
|
|
}
|
|
if (points == 0)
|
|
return 0;
|
|
|
|
return total + points;
|
|
}
|
|
|
|
/*
|
|
* Authorization
|
|
*/
|
|
communityAuth::communityAuth(const char *f)
|
|
{
|
|
// Clear lists
|
|
accept = reject = 0;
|
|
|
|
// Store the file
|
|
int l = strlen(f);
|
|
file = new char[l + 1];
|
|
strcpy(file, f);
|
|
|
|
// Stat the file
|
|
struct stat st;
|
|
if (stat(file, &st) < 0) {
|
|
st.st_ctime = 0;
|
|
exc_errlog(LOG_WARNING, errno,
|
|
"communityAuth: unable to stat %s", file);
|
|
}
|
|
readTime = st.st_ctime;
|
|
|
|
// Read the file
|
|
readFile();
|
|
}
|
|
|
|
communityAuth::~communityAuth(void)
|
|
{
|
|
clearRules();
|
|
delete file;
|
|
}
|
|
|
|
void
|
|
communityAuth::clearRules(void)
|
|
{
|
|
struct commAuthRule *ar, *next;
|
|
|
|
for (ar = accept; ar != 0; ar = next) {
|
|
next = ar->next;
|
|
delete ar;
|
|
}
|
|
for (ar = reject; ar != 0; ar = next) {
|
|
next = ar->next;
|
|
delete ar;
|
|
}
|
|
|
|
accept = reject = 0;
|
|
}
|
|
|
|
void
|
|
communityAuth::readFile(void)
|
|
{
|
|
FILE *fp = fopen(file, "r");
|
|
if (fp == 0) {
|
|
exc_errlog(LOG_ERR, errno,
|
|
"communityAuth: unable to open authorization file %s", file);
|
|
return;
|
|
}
|
|
|
|
int line = 0;
|
|
char buf[256], tmpbuf[256], *s, *next;
|
|
commAuthRule **acceptp = &accept;
|
|
commAuthRule **rejectp = &reject;
|
|
commAuthRule ***p, *ar;
|
|
|
|
while ((s = fgets(buf, sizeof buf, fp)) != 0) {
|
|
// Increment line number
|
|
line++;
|
|
|
|
// Get the action
|
|
s = getword(s, &next);
|
|
if (s == 0 || *s == '#')
|
|
continue;
|
|
|
|
// Parse the action
|
|
if (strcasecmp(s, "accept") == 0)
|
|
p = &acceptp;
|
|
else if (strcasecmp(s, "reject") == 0)
|
|
p = &rejectp;
|
|
else {
|
|
exc_errlog(LOG_ERR, 0, "%s: line %d: bad action \"%s\"",
|
|
file, line, s);
|
|
// fclose(fp);
|
|
// return;
|
|
continue; /* go to next line (while loop) --vaz */
|
|
}
|
|
|
|
// Now read any number of rules
|
|
for (s = next; s != 0; s = next) {
|
|
s = getword(s, &next);
|
|
if (s == 0)
|
|
break;
|
|
|
|
strcpy(tmpbuf, s);
|
|
ar = new commAuthRule;
|
|
if (!ar->parse(tmpbuf)) {
|
|
delete ar;
|
|
exc_errlog(LOG_ERR, 0, "%s: line %d: bad entry \"%s\"",
|
|
file, line, s);
|
|
// fclose(fp);
|
|
// return;
|
|
continue; // go to next rule (for loop) --vaz
|
|
}
|
|
|
|
// Link new entry into appropriate list
|
|
**p = ar;
|
|
*p = &ar->next;
|
|
}
|
|
}
|
|
|
|
exc_errlog(LOG_DEBUG, 0, "%s: read %d line%s", file, line,
|
|
line == 1 ? "" : "s");
|
|
fclose(fp);
|
|
}
|
|
|
|
int
|
|
communityAuth::authorize(struct in_addr *a, asnOctetString *o, int tag)
|
|
{
|
|
struct stat st;
|
|
struct commAuthRule *ar;
|
|
int acc, rej, score;
|
|
|
|
// See if the authorization file changed
|
|
if (stat(file, &st) < 0) {
|
|
st.st_ctime = 0;
|
|
exc_errlog(LOG_WARNING, errno, "unable to stat %s", file);
|
|
}
|
|
if (readTime < st.st_ctime) {
|
|
clearRules();
|
|
readTime = st.st_ctime;
|
|
readFile();
|
|
}
|
|
|
|
// Tabulate scores for accept and reject rules
|
|
for (acc = 0, ar = accept; ar != 0; ar = ar->next) {
|
|
score = ar->match(a, o, tag);
|
|
acc = MAX(acc, score);
|
|
}
|
|
for (rej = 0, ar = reject; ar != 0; ar = ar->next) {
|
|
score = ar->match(a, o, tag);
|
|
rej = MAX(rej, score);
|
|
}
|
|
|
|
return acc - rej;
|
|
}
|
|
|
|
char *
|
|
communityAuth::getword(char *s, char** nextp)
|
|
{
|
|
while (isspace(*s))
|
|
s++;
|
|
if (*s == '\0')
|
|
return 0;
|
|
|
|
*nextp = strpbrk(s, " \t\n");
|
|
if (*nextp != NULL)
|
|
*(*nextp)++ = '\0';
|
|
|
|
return s;
|
|
}
|