summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 */