unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

operclass.c (7954B)

      1 /** Oper classes code.
      2  * (C) Copyright 2015-present tmcarthur and the UnrealIRCd team
      3  * License: GPLv2 or later
      4  */
      5 
      6 #include "unrealircd.h"
      7 
      8 typedef struct OperClassPathNode OperClassPathNode;
      9 typedef struct OperClassCallbackNode OperClassCallbackNode;
     10 
     11 struct OperClassPathNode
     12 {
     13 	OperClassPathNode *prev,*next;
     14 	OperClassPathNode *children;
     15 	char *identifier;
     16 	OperClassCallbackNode *callbacks;
     17 };
     18 
     19 struct OperClassCallbackNode
     20 {
     21 	OperClassCallbackNode *prev, *next;
     22 	OperClassPathNode *parent;
     23 	OperClassEntryEvalCallback callback;
     24 };
     25 
     26 struct OperClassValidator
     27 {
     28 	Module *owner;
     29 	OperClassCallbackNode *node;
     30 };
     31 
     32 OperClassACLPath *OperClass_parsePath(const char *path);
     33 void OperClass_freePath(OperClassACLPath *path);
     34 OperClassPathNode *OperClass_findPathNodeForIdentifier(char *identifier, OperClassPathNode *head);
     35 
     36 OperClassPathNode *rootEvalNode = NULL;
     37 
     38 OperClassValidator *OperClassAddValidator(Module *module, char *pathStr, OperClassEntryEvalCallback callback)
     39 {
     40 	OperClassPathNode *node,*nextNode;
     41 	OperClassCallbackNode *callbackNode;
     42 	OperClassValidator *validator; 
     43 	OperClassACLPath *path = OperClass_parsePath(pathStr);
     44 
     45 	if (!rootEvalNode)
     46 	{
     47 		rootEvalNode = safe_alloc(sizeof(OperClassPathNode));
     48 	}
     49 
     50 	node = rootEvalNode;
     51 
     52 	while (path)
     53 	{
     54 		nextNode = OperClass_findPathNodeForIdentifier(path->identifier,node->children);
     55 		if (!nextNode)
     56 		{
     57 			nextNode = safe_alloc(sizeof(OperClassPathNode));
     58 			safe_strdup(nextNode->identifier, path->identifier);
     59 			AddListItem(nextNode,node->children);
     60 		}
     61 		node = nextNode;
     62 		path = path->next;
     63 	}
     64 
     65 	callbackNode = safe_alloc(sizeof(OperClassCallbackNode));
     66 	callbackNode->callback = callback;
     67 	callbackNode->parent = node;	
     68 	AddListItem(callbackNode,node->callbacks);
     69 
     70 	validator = safe_alloc(sizeof(OperClassValidator));
     71 	validator->node = callbackNode;	
     72 	validator->owner = module;
     73 
     74 	if (module)
     75 	{
     76 		ModuleObject *mobj = safe_alloc(sizeof(ModuleObject));
     77 		mobj->object.validator = validator;
     78 		mobj->type = MOBJ_VALIDATOR;
     79 		AddListItem(mobj, module->objects);
     80 		module->errorcode = MODERR_NOERROR;
     81 	}
     82 
     83 	OperClass_freePath(path);
     84 
     85 	return validator;
     86 }
     87 
     88 void OperClassValidatorDel(OperClassValidator *validator)
     89 {
     90 	if (validator->owner)
     91 	{
     92 		ModuleObject *mdobj;
     93 		for (mdobj = validator->owner->objects; mdobj; mdobj = mdobj->next)
     94 		{
     95 			if ((mdobj->type == MOBJ_VALIDATOR) && (mdobj->object.validator == validator))
     96 			{
     97 				DelListItem(mdobj, validator->owner->objects);
     98 				safe_free(mdobj);
     99 				break;
    100 			}
    101 		}
    102 		validator->owner = NULL;
    103 	}
    104 	
    105 	/* Technically, the below leaks memory if you don't re-register
    106 	 * another validator at same path, but it is cheaper than walking
    107 	 * back up and doing cleanup in practice, since this tree is very small
    108 	 */
    109 	DelListItem(validator->node,validator->node->parent->callbacks);
    110 	safe_free(validator->node);
    111 	safe_free(validator);	
    112 }
    113 
    114 OperClassACLPath *OperClass_parsePath(const char *path)
    115 {
    116 	char *pathCopy = raw_strdup(path);
    117 	OperClassACLPath *pathHead = NULL;
    118 	OperClassACLPath *tmpPath;
    119 	char *str = strtok(pathCopy,":");
    120 	while (str)
    121 	{
    122 		tmpPath = safe_alloc(sizeof(OperClassACLPath));
    123 		safe_strdup(tmpPath->identifier, str);
    124 		AddListItem(tmpPath,pathHead);
    125 		str = strtok(NULL,":");
    126 	}
    127 
    128 	while (pathHead->next)
    129 	{
    130 		tmpPath = pathHead->next;
    131 		pathHead->next = pathHead->prev;
    132 		pathHead->prev = tmpPath;
    133 		pathHead = tmpPath;
    134 	}
    135 	pathHead->next = pathHead->prev;
    136 	pathHead->prev = NULL;	
    137 
    138 	safe_free(pathCopy);
    139 	return pathHead;
    140 }
    141 
    142 void OperClass_freePath(OperClassACLPath *path)
    143 {
    144 	OperClassACLPath *next;
    145 	while (path)
    146 	{
    147 		next = path->next;
    148 		safe_free(path->identifier);
    149 		safe_free(path);
    150 		path = next;
    151 	}	
    152 }
    153 
    154 OperClassACL *OperClass_FindACL(OperClassACL *acl, char *name)
    155 {
    156 	for (;acl;acl = acl->next)
    157 	{
    158 		if (!strcmp(acl->name,name))
    159 		{ 
    160 			return acl;
    161 		}
    162 	}
    163 	return NULL;
    164 }
    165 
    166 OperClassPathNode *OperClass_findPathNodeForIdentifier(char *identifier, OperClassPathNode *head)
    167 {
    168 	for (; head; head = head->next)
    169 	{
    170 		if (!strcmp(head->identifier,identifier))
    171 		{
    172 			return head;
    173 		}
    174 	}
    175 	return NULL;
    176 }
    177 
    178 unsigned char OperClass_evaluateACLEntry(OperClassACLEntry *entry, OperClassACLPath *path, OperClassCheckParams *params)
    179 {
    180 	OperClassPathNode *node = rootEvalNode;	
    181 	OperClassCallbackNode *callbackNode = NULL;
    182 	unsigned char eval = 0;
    183 
    184 	/* If no variables, always match */
    185 	if (!entry->variables)
    186 	{
    187 		return 1;
    188 	}
    189 
    190 	/* Go as deep as possible */
    191 	while (path->next && node)
    192 	{
    193 		node = OperClass_findPathNodeForIdentifier(path->identifier,node);	
    194 		/* If we can't find a node we need, and we have vars, no match */
    195 		if (!node)
    196 		{
    197 			return 0;
    198 		}
    199 		node = node->children;
    200 		path = path->next;
    201 	}
    202 
    203 	/* If no evals for full path, no match */
    204 	if (path->next)
    205 	{
    206 		return 0;
    207 	}
    208 
    209 
    210 	/* We have a valid node, execute all callback nodes */
    211 	for (callbackNode = node->callbacks; callbackNode; callbackNode = callbackNode->next)
    212 	{
    213 		eval = callbackNode->callback(entry->variables,params);
    214 	}
    215 
    216 	return eval;	
    217 }
    218 
    219 OperPermission ValidatePermissionsForPathEx(OperClassACL *acl, OperClassACLPath *path, OperClassCheckParams *params)
    220 {
    221 	/** Evaluate into ACL struct as deep as possible **/
    222 	OperClassACLPath *basePath = path;
    223 	OperClassACL *tmp;
    224 	OperClassACLEntry *entry;
    225 	unsigned char allow = 0;
    226 	unsigned char deny = 0;
    227 	unsigned char aclNotFound = 0;
    228 
    229 	path = path->next; /* Avoid first level since we have resolved it */
    230 	while (path && acl->acls)
    231 	{
    232 		tmp = OperClass_FindACL(acl->acls,path->identifier);
    233 		if (!tmp)
    234 		{
    235 			aclNotFound = 1;
    236 			break;
    237 		}
    238 		path = path->next;
    239 		acl = tmp;
    240 	}
    241 	/** If node does not exist, but most specific one has other ACLs, deny **/
    242 	if (acl->acls && aclNotFound)
    243 	{
    244 		return OPER_DENY;
    245 	}
    246 
    247 	/** If node exists for this but has no ACL entries, allow **/
    248 	if (!acl->entries)
    249 	{
    250 		return OPER_ALLOW;
    251 	}
    252 	/** Process entries **/
    253 	for (entry = acl->entries; entry; entry = entry->next)
    254 	{
    255 		unsigned char result;
    256 		/* Short circuit if we already have valid block */
    257 		if (entry->type == OPERCLASSENTRY_ALLOW && allow)
    258 			continue;
    259 		if (entry->type == OPERCLASSENTRY_DENY && deny)
    260 			continue;
    261 
    262 		result = OperClass_evaluateACLEntry(entry,basePath,params);
    263 		if (entry->type == OPERCLASSENTRY_ALLOW)
    264 		{
    265 			allow = result;
    266 		}
    267 		else
    268 		{
    269 			deny = result;
    270 		}
    271 	}
    272 
    273 	/** We only permit if an allow matched AND no deny matched **/
    274 	if (allow && !deny)
    275 	{
    276 		return OPER_ALLOW;
    277 	}
    278 
    279 	return OPER_DENY;
    280 }
    281 
    282 OperPermission ValidatePermissionsForPath(const char *path, Client *client, Client *victim, Channel *channel, const void *extra)
    283 {
    284 	ConfigItem_oper *ce_oper;
    285 	const char *operclass;
    286 	ConfigItem_operclass *ce_operClass;
    287 	OperClass *oc = NULL;
    288 	OperClassACLPath *operPath;
    289 
    290 	if (!client)
    291 		return OPER_DENY;
    292 
    293 	/* Trust Servers, U-Lines and remote opers */
    294 	if (IsServer(client) || IsMe(client) || IsULine(client) || (IsOper(client) && !MyUser(client)))
    295 		return OPER_ALLOW;
    296 
    297 	if (!IsOper(client))
    298 		return OPER_DENY;
    299 
    300 	ce_oper = find_oper(client->user->operlogin);
    301 	if (!ce_oper)
    302 	{
    303 		operclass = moddata_client_get(client, "operclass");
    304 		if (!operclass)
    305 			return OPER_DENY;
    306 	} else
    307 	{
    308 		operclass = ce_oper->operclass;
    309 	}
    310 
    311 	ce_operClass = find_operclass(operclass);
    312 	if (!ce_operClass)
    313 		return OPER_DENY;
    314 
    315 	oc = ce_operClass->classStruct;
    316 	operPath = OperClass_parsePath(path);
    317 	while (oc && operPath)
    318 	{
    319 		OperClassACL *acl = OperClass_FindACL(oc->acls,operPath->identifier);
    320 		if (acl)
    321 		{
    322 			OperPermission perm;
    323 			OperClassCheckParams *params = safe_alloc(sizeof(OperClassCheckParams));
    324 			params->client = client;
    325 			params->victim = victim;
    326 			params->channel = channel;
    327 			params->extra = extra;
    328 			
    329 			perm = ValidatePermissionsForPathEx(acl, operPath, params);
    330 			OperClass_freePath(operPath);
    331 			safe_free(params);
    332 			return perm;
    333 		}
    334 		if (!oc->ISA)
    335 		{
    336 			break;
    337 		}
    338 		ce_operClass = find_operclass(oc->ISA);
    339 		if (ce_operClass)
    340 		{
    341 			oc = ce_operClass->classStruct;
    342 		} else {
    343 			break; /* parent not found */
    344 		}
    345 	}
    346 	OperClass_freePath(operPath);
    347 	return OPER_DENY;
    348 }