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 }