diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/access.c | 9 | ||||
-rw-r--r-- | server/acl.h | 2 | ||||
-rw-r--r-- | server/acl_files.c | 173 | ||||
-rw-r--r-- | server/dispatch.c | 22 | ||||
-rw-r--r-- | server/subscr.c | 8 | ||||
-rw-r--r-- | server/zserver.h | 2 |
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 */ |