home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.ee.lbl.gov
/
2014.05.ftp.ee.lbl.gov.tar
/
ftp.ee.lbl.gov
/
acld-1.11.tar.gz
/
acld-1.11.tar
/
acld-1.11
/
cforce.c
< prev
next >
Wrap
C/C++ Source or Header
|
2012-02-08
|
16KB
|
692 lines
/*
* Copyright (c) 2009, 2010, 2011, 2012
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Id: cforce.c 807 2012-02-09 00:57:40Z leres $ (LBL)";
#endif
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <syslog.h>
#include <unistd.h>
#include <cforce.h>
#include "acld.h"
#include "cforcep.h"
#include "util.h"
/* Locals */
struct s2v cforceerr2str[] = {
{ "GENERAL", ERROR_GENERAL },
{ "COMMUNICATION", ERROR_COMMUNICATION },
{ "INVALID_IP_ADDRESS", ERROR_INVALID_IP_ADDRESS },
{ "ADDRESS_ALREADY_PROVISIONED", ERROR_ADDRESS_ALREADY_PROVISIONED },
{ "HOST_ALREADY_BLOCKED", ERROR_HOST_ALREADY_BLOCKED },
{ "INVALID_ID", ERROR_INVALID_ID },
{ "CAPACITY_EXCEEDED", ERROR_CAPACITY_EXCEEDED },
{ "INVALID_RULE", ERROR_INVALID_RULE },
{ "BOTH_NOT_SUPPORTED", ERROR_BOTH_NOT_SUPPORTED },
{ "CONVERSATION_ALREADY_BLOCKED", ERROR_CONVERSATION_ALREADY_BLOCKED },
{ "NOT_INITIALIZED", ERROR_NOT_INITIALIZED },
{ "INVALID_ARGUMENTS", ERROR_INVALID_ARGUMENTS },
{ "INVALID_CONNECTION_PARAMETERS",
ERROR_INVALID_CONNECTION_PARAMETERS },
{ "ALL_RULE_PROVISIONED", ERROR_ALL_RULE_PROVISIONED },
{ "RULE_CONFLICT", ERROR_RULE_CONFLICT },
{ "INVALID_COMBINATION", ERROR_INVALID_COMBINATION },
{ "COULD_NOT_OPEN_FILE", ERROR_COULD_NOT_OPEN_FILE },
{ "COULD_NOT_WRITE_TO_FD", ERROR_COULD_NOT_WRITE_TO_FD },
{ "DATA_STRUCTURE_INCONSISTENCY", ERROR_DATA_STRUCTURE_INCONSISTENCY },
};
struct cforceseq {
enum acltype type;
int dir;
struct addr addr1;
struct addr addr2;
};
static int md = -1;
struct cforceseq *cforceseq;
/* XXX doesn't include hosthost or IPv6 rules */
#define NUMRULES 64000
size_t cforceseqsize = sizeof(*cforceseq) * NUMRULES;
/* Forwards */
static int acl2dir(struct acllist *);
static int cforce_drophost(int, struct addr *);
static int cforce_drophosthost(int, struct addr *, struct addr *);
static void cforcemapfile(const char *fn);
static void cforcemsync(void);
static void cforceunmapfile(void);
static int
acl2dir(struct acllist *ap)
{
const char *interface;
interface = ap->intlist->i_name;
if (strcasecmp(interface, "outside") == 0)
return (OUTSIDE);
if (strcasecmp(interface, "inside") == 0)
return (INSIDE);
return (-1);
}
static int
cforce_drophost(int dir, struct addr *addr)
{
int (*drop_host_func)(block_direction_enum_t, char *);
if (addr->family == AF_INET)
drop_host_func = drop_host;
else
drop_host_func = drop_ipv6_host;
return ((drop_host_func)(dir, (char *)addr2str(addr)));
}
static int
cforce_drophosthost(int dir, struct addr *addr1, struct addr *addr2)
{
char saddr1[128];
int (*drop_hosthost_func)(rule_port_enum_t, char *, char *);
if (addr1->family == AF_INET)
drop_hosthost_func = drop_conversation;
else
drop_hosthost_func = drop_ipv6_conversation;
strncpy(saddr1, addr2str(addr1), sizeof(saddr1));
saddr1[sizeof(saddr1) - 1] = '\0';
return ((drop_hosthost_func)(dir, saddr1, (char *)addr2str(addr2)));
}
/* Assume if we can get the appliance counters that it's alive */
void
cforceayt(struct state *sp)
{
int rc;
cforce_counters_t counters;
rc = get_counters(&counters);
if (rc < 0) {
if (sp->state != ASTATE_NOTCONNECTED) {
lg(LOG_DEBUG, "cforceayt: not connected");
sp->state = ASTATE_NOTCONNECTED;
}
}
}
void
cforcecompact(struct state *sp)
{
lg(LOG_DEBUG, "cforcecompact: noop");
}
void
cforcedroprestore(struct state *sp, struct client *cl, struct req *rp)
{
int rc, dir, seq;
const char *p, *what;
struct acllist *ap;
struct acl *al;
struct addr *wp, *tp;
struct cf *cf;
struct cforceseq *cs;
char buf[132];
/* Find ACL list */
cf = sp->cf;
switch (rp->acl.type) {
case ATYPE_BLOCKHOST:
case ATYPE_BLOCKHOSTHOST:
/* By address */
ap = aclfindlistbyaddr(cf, &rp->acl.addr1);
if (ap == NULL) {
clientsenderr(cl, rp, "can't find acllist for %s"
" (is the default ACL missing?)",
addr2str(&rp->acl.addr1));
return;
}
rp->acllist = ap;
break;
default:
clientsenderr(cl, rp, "ATYPE %s not implemented",
val2str(str2acl, rp->acl.type));
return;
}
/* Check if ACL should or should not already exist */
al = aclfindacl(ap, &rp->acl, 0);
switch (rp->type) {
case REQ_DROP:
stats_setrate(&ap->stats);
tp = &rp->acl.addr1;
wp = whitelist(cf, tp);
if (wp != NULL) {
snprintf(buf, sizeof(buf), "%s", addr2str(tp));
clientsenderr(cl, rp, "%s is on the whitelist (%s)",
buf, addr2str(wp));
return;
}
if (al != NULL) {
rp->flags |= RFLAG_IGNORE;
clientsendfmt(cl, rp,
"Note: %s is already blocked in ACL %s",
addr2str(tp), ap->name);
/* Log for NETS */
nets_log(sp, rp);
return;
}
break;
case REQ_BLOCKHOSTHOST:
stats_setrate(&ap->stats);
tp = &rp->acl.addr1;
wp = whitelist(cf, tp);
if (wp == NULL) {
/* Check second address too */
tp = &rp->acl.addr2;
wp = whitelist(cf, tp);
}
if (wp != NULL) {
snprintf(buf, sizeof(buf), "%s", addr2str(tp));
clientsenderr(cl, rp, "%s is on the whitelist (%s)",
buf, addr2str(wp));
return;
}
if (al != NULL) {
rp->flags |= RFLAG_IGNORE;
snprintf(buf, sizeof(buf), "%s", addr2str(tp));
clientsendfmt(cl, rp,
"Note: %s/%s is already blocked in ACL %s",
buf, addr2str(&rp->acl.addr2), ap->name);
/* Log for NETS */
nets_log(sp, rp);
return;
}
break;
case REQ_RESTORE:
case REQ_RESTOREHOSTHOST:
if (al == NULL) {
rp->flags |= RFLAG_IGNORE;
clientsenderr(cl, rp, "%s not found in ACL %s",
addr2str(&rp->acl.addr1), ap->name);
/* Log for NETS */
nets_log(sp, rp);
return;
}
break;
default:
lg(LOG_ERR, "cforcedroprestore: bad type %d (1)", rp->type);
exit(EX_SOFTWARE);
}
/* Add or remove the ACL */
switch (rp->type) {
case REQ_DROP:
case REQ_BLOCKHOSTHOST:
/* Check cap and limits */
p = checklimits(cf, ap);
if (p != NULL) {
clientsenderr(cl, rp, "%s", p);
syslog(LOG_INFO, "%s", p);
return;
}
/* Add the rule */
if (rp->type == REQ_DROP) {
/* Determine cForce direction */
dir = acl2dir(ap);
if (dir < 0) {
(void)snprintf(buf, sizeof(buf),
"Direction for ACL %s undefined", ap->name);
clientsenderr(cl, rp, "%s", buf);
syslog(LOG_INFO, "%s", buf);
return;
}
rc = cforce_drophost(dir, &rp->acl.addr1);
what = "cforce_drophost";
} else {
dir = CFORCE_BOTH_PORTS;
rc = cforce_drophosthost(dir, &rp->acl.addr1,
&rp->acl.addr2);
what = "cforce_drophosthost";
}
if (rc < 0) {
(void)snprintf(buf, sizeof(buf), "%s failed: %s",
what, val2str(cforceerr2str, rc));
lg(LOG_ERR, "cforcedroprestore: %s", buf);
if (rc == ERROR_CAPACITY_EXCEEDED) {
clientsenderr(cl, rp, "%s", buf);
return;
}
exit(EX_SOFTWARE);
}
/* Assign sequence number */
rp->acl.seq = rc;
/* Sanity */
cs = cforceseq + rp->acl.seq;
if (cs->type != ATYPE_UNKNOWN) {
lg(LOG_ERR, "cforcedroprestore: %s failed:"
" cforceseq slot %d already in use",
what, rp->acl.seq);
exit(EX_SOFTWARE);
}
/* Update our data structure */
acladdacl(ap, &rp->acl);
/* Update mmap file */
cs->dir = dir;
if (rp->type == REQ_DROP) {
cs->type = ATYPE_BLOCKHOST;
cs->addr1 = rp->acl.addr1;
} else {
cs->type = ATYPE_BLOCKHOSTHOST;
cs->addr1 = rp->acl.addr1;
cs->addr2 = rp->acl.addr2;
}
/* Always sort after assigning new sequence numbers */
aclsortacls(cf);
break;
case REQ_RESTORE:
case REQ_RESTOREHOSTHOST:
/* Save a copy of the sequence number before it gets zeroed */
seq = al->seq;
/* Delete the rule */
rc = delete_rule(seq);
if (rc < 0) {
lg(LOG_ERR,
"cforcedroprestore: delete_rule(%d) failed: %s",
seq, val2str(cforceerr2str, rc));
exit(EX_SOFTWARE);
}
/* Remove from our data structure */
if (!acldeleteacl(ap, &rp->acl)) {
lg(LOG_ERR, "cforcedroprestore: acldeleteacl failed");
exit(EX_SOFTWARE);
}
/* Update mmap file */
cs = cforceseq + seq;
memset(cs, 0, sizeof(*cs));
break;
default:
lg(LOG_ERR, "cforcedroprestore: bad type %d (2)", rp->type);
exit(EX_SOFTWARE);
}
/* Update mmap file */
cforcemsync();
/* Log for NETS */
nets_log(sp, rp);
clientsend(cl, rp);
}
void
cforceinit(struct state *sp)
{
sp->f_ayt = cforceayt;
sp->f_compact = cforcecompact;
sp->f_droprestore = cforcedroprestore;
sp->f_kill = cforcekill;
sp->f_listacl = cforcelistacl;
sp->f_listroute = cforcelistroute;
sp->f_login = cforcelogin;
sp->f_nullzero = cforcenullzero;
sp->f_send = cforcesend;
sp->f_sendattr = cforcesendattr;
sp->f_sync = cforcesync;
}
int
cforceinput(struct state *sp, struct client *cl)
{
/* XXX Shouldn't be able to get here */
abort();
}
void
cforcekill(struct state *sp)
{
sp->state = ASTATE_NOTCONNECTED;
timerset(&sp->t_ayt, 0);
timerset(&sp->t_login, 0);
timerset(&sp->t_sync, 0);
}
void
cforcelistacl(struct state *sp)
{
aclsortacls(sp->cf);
++sp->listedallacls;
}
void
cforcelistroute(struct state *sp)
{
++sp->listedroutes;
}
void
cforcelogin(struct state *sp)
{
int rc, i;
struct cf *cf;
const char *what;
struct acllist *ap;
struct cforceseq *cforceseq2, *cs, *cs2;
struct acl acl;
static int init = 0;
cf = sp->cf;
if (!init) {
rc = cforce_initialize(sp->cf->c_cforceaddr,
sp->cf->c_portcforce);
if (rc < 0) {
lg(LOG_ERR, "cforcelogin: cforce_initialize failed: %s",
val2str(cforceerr2str, rc));
sp->state = ASTATE_NOTCONNECTED;
return;
}
++init;
} else {
lg(LOG_INFO, "cforcelogin: unmapping cforce data file");
cforceunmapfile();
}
/* Zero rules in appliance */
rc = clear_rules();
if (rc < 0) {
lg(LOG_ERR, "cforcelogin: clear_rules failed: %s",
val2str(cforceerr2str, rc));
return;
}
/* Reinstall saved rules */
cforcemapfile(cf->c_cforcedata);
/* Reblock hosts */
cs = cforceseq;
cforceseq2 = malloc(cforceseqsize);
if (cforceseq2 == NULL) {
lg(LOG_ERR, "cforcelogin: malloc");
exit(EX_OSERR);
}
memmove(cforceseq2, cforceseq, cforceseqsize);
memset(cforceseq, 0, cforceseqsize);
for (i = 0, cs2 = cforceseq2; i < NUMRULES; ++i, ++cs2) {
switch (cs2->type) {
case ATYPE_UNKNOWN:
/* Unused slot */
break;
case ATYPE_BLOCKHOST:
/* Install rule in appliance */
what = "cforce_drophost";
rc = cforce_drophost(cs2->dir, &cs2->addr1);
if (rc < 0) {
lg(LOG_ERR, "cforcelogin: drop_host failed: %s",
val2str(cforceerr2str, rc));
exit(EX_SOFTWARE);
}
/* Sanity */
if (rc >= NUMRULES) {
lg(LOG_ERR, "cforcelogin: drop_host"
" seq number too boku %d", rc);
exit(EX_SOFTWARE);
}
cs = cforceseq + rc;
if (cs->type != ATYPE_UNKNOWN) {
lg(LOG_ERR, "cforcelogin: %s failed:"
" cforceseq seq %d already in use",
what, rc);
exit(EX_SOFTWARE);
}
/* Update our data structure */
ap = aclfindlistbyaddr(cf, &cs2->addr1);
if (ap == NULL) {
lg(LOG_ERR, "can't find acllist for %s",
addr2str(&cs2->addr1));
exit(EX_SOFTWARE);
}
memset(&acl, 0, sizeof(acl));
acl.seq = rc;
acl.type = cs2->type;
memmove(&acl.addr1, &cs2->addr1, sizeof(acl.addr1));
acladdacl(ap, &acl);
/* Copy rule */
*cs = *cs2;
++cs;
break;
case ATYPE_BLOCKHOSTHOST:
/* Install rule in appliance */
what = "cforce_drophosthost";
rc = cforce_drophosthost(cs2->dir, &cs2->addr1,
&cs2->addr2);
if (rc < 0) {
lg(LOG_ERR, "cforcelogin: cforce_drophosthost"
" failed: %s", val2str(cforceerr2str, rc));
exit(EX_SOFTWARE);
}
/* Sanity */
if (rc >= NUMRULES) {
lg(LOG_ERR, "cforcelogin: drop_hosthosthost"
" seq number too boku %d", rc);
exit(EX_SOFTWARE);
}
cs = cforceseq + rc;
if (cs->type != ATYPE_UNKNOWN) {
lg(LOG_ERR, "cforcelogin: %s failed:"
" cforceseq seq %d already in use",
what, rc);
exit(EX_SOFTWARE);
}
/* Update our data structure */
ap = aclfindlistbyaddr(cf, &cs2->addr1);
if (ap == NULL) {
lg(LOG_ERR, "can't find acllist for %s",
addr2str(&cs2->addr1));
exit(EX_SOFTWARE);
}
memset(&acl, 0, sizeof(acl));
acl.seq = rc;
acl.type = cs2->type;
memmove(&acl.addr1, &cs2->addr1, sizeof(acl.addr1));
memmove(&acl.addr2, &cs2->addr2, sizeof(acl.addr2));
acladdacl(ap, &acl);
/* Copy rule */
*cs = *cs2;
++cs;
break;
default:
lg(LOG_ERR, "unhandled type #%d", cs2->type);
break;
}
}
free(cforceseq2);
/* Need to sort since the cForce appliance picks the sequence numbers */
aclsortacls(sp->cf);
sp->state = ASTATE_LOGGEDIN;
}
static void
cforcemapfile(const char *fn)
{
char ch;
struct stat sbuf;
if (fn == NULL) {
lg(LOG_ERR, "cforcemapfile: missing cforce filename");
exit(EX_SOFTWARE);
}
md = open(fn, O_RDWR | O_CREAT, 0600);
if (md < 0) {
lg(LOG_ERR, "cforcemapfile(%s): open: %s",
fn, strerror(errno));
exit(EX_SOFTWARE);
}
memset(&sbuf, 0, sizeof(sbuf));
if (fstat(md, &sbuf) < 0) {
lg(LOG_ERR, "cforcemapfile(%s): fstat: %s",
fn, strerror(errno));
exit(EX_SOFTWARE);
}
if (sbuf.st_size == 0) {
lg(LOG_ERR,
"cforcemapfile(%s): initialize empty mmap file", fn);
if (lseek(md, cforceseqsize - 1, SEEK_SET) < 0) {
lg(LOG_ERR, "cforcemapfile(%s): lseek: %s",
fn, strerror(errno));
exit(EX_SOFTWARE);
}
ch = 0;
if (write(md, &ch, 1) < 0) {
lg(LOG_ERR, "cforcemapfile(%s): write: %s",
fn, strerror(errno));
exit(EX_OSERR);
}
if (fsync(md) < 0) {
lg(LOG_ERR, "cforcemapfile(%s): fsync: %s",
fn, strerror(errno));
exit(EX_OSERR);
}
} else if (sbuf.st_size != cforceseqsize) {
lg(LOG_ERR, "cforcemapfile(%s): bad size: %ld != %ld",
fn, (u_long)sbuf.st_size, (u_long)cforceseqsize);
exit(EX_SOFTWARE);
}
cforceseq = mmap(0, cforceseqsize, PROT_READ | PROT_WRITE, MAP_SHARED,
md, 0);
if ((int)cforceseq == -1) {
lg(LOG_ERR, "cforcemapfile(%s): mmap: %s",
fn, strerror(errno));
exit(EX_OSERR);
}
if (close(md) < 0) {
lg(LOG_ERR, "cforcemapfile(%s): close: %s",
fn, strerror(errno));
exit(EX_OSERR);
}
cforcemsync();
}
static void
cforcemsync()
{
if (msync(cforceseq, cforceseqsize, MS_SYNC) < 0) {
lg(LOG_ERR, "cforcemsync(): msync: %s", strerror(errno));
exit(EX_OSERR);
}
}
void
cforcenullzero(struct state *sp, struct client *cl, struct req *rp)
{
clientsenderr(cl, rp, "cForce doesn't support null zero routes");
}
void
cforcesend(struct state *sp, const char *fmt, ...)
{
abort();
}
void
cforcesendattr(struct state *sp)
{
++sp->sentattrs;
}
void
cforcesync(struct state *sp)
{
lg(LOG_DEBUG, "cforcesync: noop");
}
static void
cforceunmapfile()
{
if (cforceseq == NULL)
return;
if (munmap(cforceseq, cforceseqsize) < 0) {
lg(LOG_ERR, "cforceunmapfile(): munmap: %s", strerror(errno));
exit(EX_SOFTWARE);
}
cforceseq = NULL;
}