summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jeffrey Hutzelman <jhutz@cmu.edu>2000-06-13 17:44:19 -0400
committerGravatar Karl Ramm <kcr@1ts.org>2013-01-19 15:38:24 -0500
commit625c4d3660c09808a926e44bfe859f049449be7c (patch)
tree5c3e37dd2c440adb949c184bdcb5fe5b1b883eef
parent5465266334ece0ed4137053eef3a7c06453df71e (diff)
Add IP-address and negative ACL entries
This allows ACLs to grant access based on the IP address of a client instead of its principal name. This is done using ACL entries with the syntax "@a.b.c.d". Currently, only IPv4 addresses are supported. A single entry may match all hosts on a particular subnet by using CIDR notation, written as @a.b.c.d/nn. If no length is given, 32 is assumed. Host and principal entries can be freely mixed within the same ACL; the ACL matches if any entry matches the client. Note that this means that ACLs can now match unauthenticated clients (however, this does not lift the general constraint that only authenticated clients can subscribe at all). Additionally, support for negative ACL entries is added. These entries are indicated by a leading '!', which may be applied to both principal and host entries. Negative entries are applied in the style of AFS ACLs; that is, a matching negative entry overrides any positive entry and thus guarantees that matching clients will be denied access. (edited slightly for style by kcr@1TS.ORG)
-rw-r--r--server/access.c9
-rw-r--r--server/acl.h2
-rw-r--r--server/acl_files.c173
-rw-r--r--server/dispatch.c22
-rw-r--r--server/subscr.c8
-rw-r--r--server/zserver.h2
6 files changed, 165 insertions, 51 deletions
diff --git a/server/access.c b/server/access.c
index 8aa2060..918d6e2 100644
--- a/server/access.c
+++ b/server/access.c
@@ -22,8 +22,9 @@ static const char rcsid_access_c[] =
*
* External routines:
*
- * int access_check(notice, acl, accesstype)
+ * int access_check(notice, who, acl, accesstype)
* ZNotice_t *notice;
+ * struct sockaddr_in *who;
* Acl *acl;
* Access accesstype;
*
@@ -58,6 +59,7 @@ static void access_setup(int first);
int
access_check(char *sender,
+ struct sockaddr_in *who,
Acl *acl,
Access accesstype)
{
@@ -96,10 +98,11 @@ access_check(char *sender,
*/
retval = acl_load(buf);
if (retval < 0) {
- syslog(LOG_DEBUG, "Error in acl_load of %s for %s", buf, sender);
+ syslog(LOG_DEBUG, "Error in acl_load of %s for %s",
+ buf, sender ? sender : "unauth client");
return 0;
}
- return acl_check(buf, sender);
+ return acl_check(buf, sender, who);
}
static void
diff --git a/server/acl.h b/server/acl.h
index 3e5984f..84c5a70 100644
--- a/server/acl.h
+++ b/server/acl.h
@@ -16,7 +16,7 @@
#define __ACL__
int acl_add(char *, char *);
-int acl_check(char *, char *);
+int acl_check(char *, char *, struct sockaddr_in *);
int acl_delete(char *, char *);
int acl_initialize(char *, int);
void acl_cache_reset(void);
diff --git a/server/acl_files.c b/server/acl_files.c
index be15f75..e62451a 100644
--- a/server/acl_files.c
+++ b/server/acl_files.c
@@ -229,9 +229,63 @@ check_hash(struct hashtbl *h,
return 0;
}
+struct host_ace {
+ struct host_ace *next;
+ unsigned long addr, mask;
+};
+
+static void
+add_host(struct host_ace **list,
+ char *buf)
+{
+ struct host_ace *e;
+ struct in_addr addr;
+ unsigned long mask = 0, i;
+ char *m, *x;
+
+ m = strchr(buf, '/');
+ if (m) {
+ *(m++) = 0;
+ if (!*m)
+ return;
+ i = strtol(m, &x, 10);
+ if (*x || i < 0 || i > 32)
+ return;
+ while (i--)
+ mask = (mask >> 1) | 0x80000000;
+ } else {
+ mask = 0xffffffff;
+ }
+
+ if (!inet_aton(buf, &addr))
+ return;
+
+ e = (struct host_ace *)malloc(sizeof(struct host_ace));
+ memset(e, 0, sizeof(struct host_ace));
+ e->addr = addr.s_addr;
+ e->mask = htonl(mask);
+ e->next = *list;
+ *list = e;
+}
+
+
+static void
+destroy_hosts(struct host_ace **list)
+{
+ struct host_ace *e;
+
+ while ((e = *list)) {
+ *list = e->next;
+ free(e);
+ }
+}
+
struct acl {
char filename[LINESIZE]; /* Name of acl file */
- struct hashtbl *acl; /* Acl entries */
+ struct hashtbl *acl; /* Positive acl entries */
+ struct hashtbl *negacl; /* Negative acl entries */
+ struct host_ace *hosts; /* Positive host entries */
+ struct host_ace *neghosts; /* Negative host entries */
};
static struct acl acl_cache[CACHED_ACLS];
@@ -273,23 +327,39 @@ int acl_load(char *name)
strcpy(acl_cache[i].filename, name);
/* Force reload */
acl_cache[i].acl = (struct hashtbl *) 0;
+ acl_cache[i].negacl = (struct hashtbl *)0;
+ acl_cache[i].hosts = (struct host_ace *)0;
+ acl_cache[i].neghosts = (struct host_ace *)0;
got_it:
/*
* See if we need to reload the ACL
*/
- if (acl_cache[i].acl == (struct hashtbl *) 0) {
+ if (acl_cache[i].acl == (struct hashtbl *)0
+ || acl_cache[i].negacl == (struct hashtbl *)0) {
/* Gotta reload */
if ((f = fopen(name, "r")) == NULL) {
syslog(LOG_ERR, "Error loading acl file %s: %m", name);
return -1;
}
if (acl_cache[i].acl) destroy_hash(acl_cache[i].acl);
+ if (acl_cache[i].negacl)
+ destroy_hash(acl_cache[i].negacl);
acl_cache[i].acl = make_hash(ACL_LEN);
+ acl_cache[i].negacl = make_hash(ACL_LEN);
while(fgets(buf, sizeof(buf), f) != NULL) {
nuke_whitespace(buf);
- acl_canonicalize_principal(buf, canon);
- add_hash(acl_cache[i].acl, canon);
+ if (buf[0] == '!' && buf[1] == '@') {
+ add_host(&acl_cache[i].neghosts, buf + 2);
+ } else if (buf[0] == '@') {
+ add_host(&acl_cache[i].hosts, buf + 1);
+ } else if (buf[0] == '!') {
+ acl_canonicalize_principal(buf + 1, canon);
+ add_hash(acl_cache[i].negacl, canon);
+ } else {
+ acl_canonicalize_principal(buf, canon);
+ add_hash(acl_cache[i].acl, canon);
+ }
}
fclose(f);
}
@@ -309,7 +379,13 @@ acl_cache_reset(void)
for (i = 0; i < acl_cache_count; i++)
if (acl_cache[i].acl) {
destroy_hash(acl_cache[i].acl);
+ destroy_hash(acl_cache[i].negacl);
+ destroy_hosts(&acl_cache[i].hosts);
+ destroy_hosts(&acl_cache[i].neghosts);
acl_cache[i].acl = (struct hashtbl *) 0;
+ acl_cache[i].negacl = (struct hashtbl *) 0;
+ acl_cache[i].hosts = (struct host_ace *) 0;
+ acl_cache[i].neghosts = (struct host_ace *) 0;
}
acl_cache_count = 0;
acl_cache_next = 0;
@@ -318,44 +394,85 @@ acl_cache_reset(void)
/* Returns nonzero if it can be determined that acl contains principal */
/* Principal is not canonicalized, and no wildcarding is done */
+/* If neg is nonzero, we look for negative entries */
int
acl_exact_match(char *acl,
- char *principal)
+ char *principal,
+ int neg)
{
int idx;
- return((idx = acl_load(acl)) >= 0
- && check_hash(acl_cache[idx].acl, principal));
+ if ((idx = acl_load(acl)) < 0)
+ return 0;
+ if (neg)
+ return check_hash(acl_cache[idx].negacl, principal);
+ else
+ return check_hash(acl_cache[idx].acl, principal);
+}
+
+/* Returns nonzero if it can be determined that acl contains who */
+/* If neg is nonzero, we look for negative entries */
+int
+acl_host_match(char *acl,
+ unsigned long who,
+ int neg)
+{
+ int idx;
+ struct host_ace *e;
+
+ if ((idx = acl_load(acl)) < 0)
+ return 0;
+ e = neg ? acl_cache[idx].neghosts : acl_cache[idx].hosts;
+ while (e) {
+ if ((e->addr & e->mask) == (who & e->mask))
+ return 1;
+ e = e->next;
+ }
+ return 0;
}
/* Returns nonzero if it can be determined that acl contains principal */
/* Recognizes wildcards in acl. */
+/* Also checks for IP address entries and applies negative ACL's */
int
acl_check(char *acl,
- char *principal)
+ char *principal,
+ struct sockaddr_in *who)
{
char buf[MAX_PRINCIPAL_SIZE];
char canon[MAX_PRINCIPAL_SIZE];
char *instance, *realm;
- int p, i, r;
-
- /* Parse into principal, instance, and realm. */
- acl_canonicalize_principal(principal, canon);
- instance = (char *) strchr(canon, INST_SEP);
- *instance++ = 0;
- realm = (char *) strchr(instance, REALM_SEP);
- *realm++ = 0;
-
- for (p = 0; p <= 1; p++) {
- for (i = 0; i <= 1; i++) {
- for (r = 0; r <= 1; r++) {
- sprintf(buf, "%s%c%s%c%s", (p) ? canon : "*", INST_SEP,
- (i) ? instance : "*", REALM_SEP, (r) ? realm : "*");
- if (acl_exact_match(acl, buf))
- return 1;
- }
- }
+ unsigned long mask;
+ int p, i, r, result = 0;
+
+ if (principal) {
+ /* Parse into principal, instance, and realm. */
+ acl_canonicalize_principal(principal, canon);
+ instance = (char *)strchr(canon, INST_SEP);
+ *instance++ = 0;
+ realm = (char *)strchr(instance, REALM_SEP);
+ *realm++ = 0;
+
+ for (p = 0; p <= 1; p++) {
+ for (i = 0; i <= 1; i++) {
+ for (r = 0; r <= 1; r++) {
+ sprintf(buf, "%s%c%s%c%s", (p) ? canon : "*", INST_SEP,
+ (i) ? instance : "*", REALM_SEP, (r) ? realm : "*");
+ if (acl_exact_match(acl, buf, 1))
+ return 0;
+ if (acl_exact_match(acl, buf, 0))
+ result = 1;
+ }
+ }
+ }
}
-
- return(0);
+
+ if (who) {
+ if (acl_host_match(acl, who->sin_addr.s_addr, 1))
+ return 0;
+ if (acl_host_match(acl, who->sin_addr.s_addr, 0))
+ result = 1;
+ }
+
+ return result;
}
diff --git a/server/dispatch.c b/server/dispatch.c
index b591ec6..9f8ce40 100644
--- a/server/dispatch.c
+++ b/server/dispatch.c
@@ -311,7 +311,7 @@ sendit(ZNotice_t *notice,
int external)
{
static int send_counter = 0;
- char recipbuf[MAX_PRINCIPAL_SIZE], *recipp;
+ char recipbuf[MAX_PRINCIPAL_SIZE], *recipp, *acl_sender;
int any = 0;
Acl *acl;
Destination dest;
@@ -323,14 +323,7 @@ sendit(ZNotice_t *notice,
acl = class_get_acl(class);
if (acl != NULL) {
- /* if controlled and not auth, fail */
- if (!auth) {
- syslog(LOG_WARNING, "sendit unauthentic %s from %s",
- notice->z_class, notice->z_sender);
- clt_ack(notice, who, AUTH_FAILED);
- free_string(class);
- return;
- }
+ acl_sender = auth ? notice->z_sender : 0;
/* if from foreign realm server, disallow if not realm of sender */
rlm = realm_which_realm(who);
if (rlm) {
@@ -343,17 +336,18 @@ sendit(ZNotice_t *notice,
}
}
/* if not auth to transmit, fail */
- if (!access_check(notice->z_sender, acl, TRANSMIT)) {
- syslog(LOG_WARNING, "sendit unauthorized %s from %s",
- notice->z_class, notice->z_sender);
+ if (!access_check(acl_sender, who, acl, TRANSMIT)) {
+ syslog(LOG_WARNING, "sendit unauthorized %s from %s%s",
+ notice->z_class, notice->z_sender, auth ? "" : " (unauth)");
clt_ack(notice, who, AUTH_FAILED);
free_string(class);
return;
}
/* sender != inst and not auth to send to others --> fail */
if (strcmp(notice->z_sender, notice->z_class_inst) != 0 &&
- !access_check(notice->z_sender, acl, INSTUID)) {
- syslog(LOG_WARNING, "sendit unauth uid %s %s.%s", notice->z_sender,
+ !access_check(acl_sender, who, acl, INSTUID)) {
+ syslog(LOG_WARNING, "sendit unauth uid %s%s %s.%s",
+ notice->z_sender, auth ? "" : " (unauth)",
notice->z_class, notice->z_class_inst);
clt_ack(notice, who, AUTH_FAILED);
free_string(class);
diff --git a/server/subscr.c b/server/subscr.c
index 8a06813..d276958 100644
--- a/server/subscr.c
+++ b/server/subscr.c
@@ -142,14 +142,14 @@ add_subscriptions(Client *who,
}
acl = class_get_acl(subs->dest.classname);
if (acl && !realm) {
- if (!access_check(sender->string, acl, SUBSCRIBE)) {
+ if (!access_check(sender->string, &who->addr, acl, SUBSCRIBE)) {
syslog(LOG_WARNING, "subscr unauth %s class %s",
sender->string, subs->dest.classname->string);
free_subscription(subs); /* free this one - denied */
continue; /* the for loop */
}
if (wildcard_instance == subs->dest.inst) {
- if (!access_check(sender->string, acl, INSTWILD)) {
+ if (!access_check(sender->string, &who->addr, acl, INSTWILD)) {
syslog(LOG_WARNING,
"subscr unauth %s class %s wild inst",
sender->string, subs->dest.classname->string);
@@ -1156,13 +1156,13 @@ subscr_check_foreign_subs(ZNotice_t *notice,
return ZSRV_CLASSRESTRICTED;
}
}
- if (!access_check(sender->string, acl, SUBSCRIBE)) {
+ if (!access_check(sender->string, who, acl, SUBSCRIBE)) {
syslog(LOG_WARNING, "subscr unauth %s class %s",
sender->string, subs->dest.classname->string);
continue; /* the for loop */
}
if (wildcard_instance == subs->dest.inst) {
- if (!access_check(sender->string, acl, INSTWILD)) {
+ if (!access_check(sender->string, who, acl, INSTWILD)) {
syslog(LOG_WARNING,
"subscr unauth %s class %s wild inst",
sender->string, subs->dest.classname->string);
diff --git a/server/zserver.h b/server/zserver.h
index 767d853..874b11a 100644
--- a/server/zserver.h
+++ b/server/zserver.h
@@ -378,7 +378,7 @@ void realm_dump_realms(FILE *);
char *get_version(void);
/* found in access.c */
-int access_check(char *, Acl *, Access);
+int access_check(char *, struct sockaddr_in *, Acl *, Access);
/* global identifiers */