From 7ca84567fe657bae756224895d4e6a7f9cc375e0 Mon Sep 17 00:00:00 2001 From: Travis McArthur Date: Sun, 31 May 2015 17:15:23 -0400 Subject: [PATCH] Add oper ACL evaluation system Still requires module and core hooks to be added, config test to be added, and to require these for perm validation - this enables core parser and querying of system though --- include/h.h | 3 +- include/struct.h | 9 +++ src/Makefile | 5 +- src/operclass.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++ src/s_conf.c | 1 + 5 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 src/operclass.c diff --git a/include/h.h b/include/h.h index d411412c6..b991ddee4 100644 --- a/include/h.h +++ b/include/h.h @@ -119,8 +119,7 @@ ConfigItem_deny_channel *Find_channel_allowed(aClient *cptr, char *name); ConfigItem_alias *Find_alias(char *name); ConfigItem_help *Find_Help(char *command); - - +OperPermission OperClass_evaluateACLPath(char* operClass, char* path, OperClassCheckParams* params); int AllowClient(aClient *cptr, struct hostent *hp, char *sockhost, char *username); int parse_netmask(const char *text, struct irc_netmask *netmask); diff --git a/include/struct.h b/include/struct.h index 5454063c8..3db138aea 100644 --- a/include/struct.h +++ b/include/struct.h @@ -153,12 +153,15 @@ typedef enum OperClassEntryType { OPERCLASSENTRY_ALLOW=1, OPERCLASSENTRY_DENY=2} typedef enum OperPermission { OPER_ALLOW=1, OPER_DENY=2} OperPermission; +typedef struct _operClassACLPath OperClassACLPath; typedef struct _operClass OperClass; typedef struct _operClassACL OperClassACL; typedef struct _operClassACLEntry OperClassACLEntry; typedef struct _operClassACLEntryVar OperClassACLEntryVar; typedef struct _operClassCheckParams OperClassCheckParams; +typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* variables,OperClassCheckParams* params); + #ifndef VMSP #include "class.h" #include "dbuf.h" /* THIS REALLY SHOULDN'T BE HERE!!! --msa */ @@ -1192,6 +1195,12 @@ struct _configitem_allow { #endif /* INET6 */ }; +struct _operClassACLPath +{ + OperClassACLPath *prev,*next; + char* identifier; +}; + struct _operClassACLEntryVar { OperClassACLEntryVar *prev,*next; diff --git a/src/Makefile b/src/Makefile index 32b1cad59..aace67dbc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,7 +23,7 @@ CC = do not run make from this directory OBJS=timesynch.o res.o s_bsd.o auth.o aln.o channel.o cloak.o crule.o dbuf.o \ events.o fdlist.o hash.o help.o ircd.o ircsprintf.o list.o \ - match.o modules.o packet.o parse.o s_auth.o mempool.o \ + match.o modules.o packet.o parse.o s_auth.o mempool.o operclass.o \ s_conf.o s_debug.o s_dispatch.o s_err.o s_extra.o s_kline.o \ s_misc.o s_numeric.o s_serv.o s_svs.o $(STRTOUL) socket.o \ ssl.o s_user.o charsys.o scache.o send.o support.o umodes.o \ @@ -236,6 +236,9 @@ random.o: random.c $(INCLUDES) extcmodes.o: extcmodes.c $(INCLUDES) $(CC) $(CFLAGS) -c extcmodes.c +operclass.o: operclass.c $(INCLUDES) + $(CC) $(CFLAGS) -c operclass.c + moddata.o: moddata.c $(INCLUDES) $(CC) $(CFLAGS) -c moddata.c diff --git a/src/operclass.c b/src/operclass.c new file mode 100644 index 000000000..7f3081fc7 --- /dev/null +++ b/src/operclass.c @@ -0,0 +1,197 @@ +#include "struct.h" +#include "common.h" +#include "sys.h" +#include "numeric.h" +#include "msg.h" +#include "proto.h" +#include "channel.h" +#include "version.h" +#include +#ifdef _WIN32 +#include +#endif +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif +#include +#include "h.h" + +typedef struct _operClass_PathNode OperClass_PathNode; +typedef struct _operClass_CallbackNode OperClass_CallbackNode; + +struct _operClass_PathNode +{ + OperClass_PathNode *prev,*next; + OperClass_PathNode *children; + char* identifier; + OperClass_CallbackNode* callbacks; +}; + +struct _operClass_CallbackNode +{ + OperClass_CallbackNode *prev, *next; + OperClassEntryEvalCallback callback; +}; + +OperClass_PathNode* rootEvalNode = NULL; + +OperClassACLPath* OperClass_parsePath(char* path) +{ + OperClassACLPath* pathHead = NULL; + OperClassACLPath* tmpPath; + char *tmp = strdup(path); + char *str = strtok(tmp,":"); + while (str) + { + tmpPath = MyMallocEx(sizeof(OperClassACLPath)); + tmpPath->identifier = str; + AddListItem(tmpPath,pathHead); + } + + return pathHead; +} + +OperClassACL* OperClass_FindACL(OperClassACL* acl, char* name) +{ + for (;acl;acl = acl->next) + { + if (!strcmp(acl->name,name)) + { + return acl; + } + } + return NULL; +} + +OperClass_PathNode* OperClass_findPathNodeForIdentifier(char* identifier, OperClass_PathNode *head) +{ + for (; head; head = head->next) + { + if (!strcmp(head->identifier,identifier)) + { + return head; + } + } + return NULL; +} + +unsigned char OperClass_evaluateACLEntry(OperClassACLEntry* entry, OperClassACLPath* path, OperClassCheckParams* params) +{ + OperClass_PathNode *node = rootEvalNode; + OperClass_CallbackNode *callbackNode = NULL; + unsigned char eval = 0; + + /* Go as deep as possible */ + while (path->next && node) + { + node = OperClass_findPathNodeForIdentifier(path->identifier,node); + /* If we can't find a node we need, no match */ + if (!node) + { + return 0; + } + node = node->children; + } + + /* If no evals for full path, no match */ + if (path->next) + { + return 0; + } + + /* We have a valid node, execute all callback nodes */ + for (callbackNode = node->callbacks; callbackNode; callbackNode = callbackNode->next) + { + eval = callbackNode->callback(entry->variables,params); + } + + return 0; +} + +OperPermission OperClass_evaluateACLPathEx(OperClassACL* acl, OperClassACLPath* path, OperClassCheckParams* params) +{ + /** Evaluate into ACL struct as deep as possible **/ + OperClassACLPath *basePath = path; + path = path->next; /* Avoid first level since we have resolved it */ + OperClassACL* tmp; + OperClassACLEntry* entry; + unsigned char allow = 0; + unsigned char deny = 0; + while (path->next && acl->acls) + { + tmp = OperClass_FindACL(acl->acls,path->identifier); + if (!tmp) + { + break; + } + path = path->next; + acl = tmp; + } + + /** If node exists for this but has no ACL entries, allow **/ + if (!acl->entries) + { + return OPER_ALLOW; + } + + /** Process entries **/ + for (entry = acl->entries; entry; entry = entry->next) + { + /* Short circuit if we already have valid block */ + if (entry->type == OPERCLASSENTRY_ALLOW && allow) + continue; + if (entry->type == OPERCLASSENTRY_DENY && deny) + continue; + + unsigned char result = OperClass_evaluateACLEntry(entry,basePath,params); + if (entry->type == OPERCLASSENTRY_ALLOW) + { + allow = result; + } + deny = result; + } + + /** We only permit if an allow matched AND no deny matched **/ + if (allow && !deny) + { + return OPER_ALLOW; + } + + return OPER_DENY; +} + +OperPermission OperClass_evaluateACLPath(char* operClass, char* path, OperClassCheckParams* params) +{ + ConfigItem_operclass *ce_operClass = Find_operclass(operClass); + OperClass *oc = NULL; + OperClassACLPath* operPath = OperClass_parsePath(path); + OperClassACL* acl; + if (ce_operClass) + { + oc = ce_operClass->classStruct; + } + + while (oc && operPath) + { + OperClassACL* acl = OperClass_FindACL(oc->acls,operPath->identifier); + if (acl) + { + return OperClass_evaluateACLPathEx(oc->acls, operPath, params); + } + if (!oc->ISA) + { + break; + } + ce_operClass = Find_operclass(oc->ISA); + if (ce_operClass) + { + oc = ce_operClass->classStruct; + } + } + + return OPER_DENY; +} diff --git a/src/s_conf.c b/src/s_conf.c index c2b4335f0..e61eacf3a 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -2501,6 +2501,7 @@ ConfigItem_class *Find_class(char *name) return NULL; } + ConfigItem_oper *Find_oper(char *name) { ConfigItem_oper *p;