X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fafb-perm.c;fp=src%2Fafb-perm.c;h=b4d2b5e31c1d5ee7274948bc49d3431a6a7d31a1;hb=5200d7022b34c0be646ae92de8c638c9437c7072;hp=0000000000000000000000000000000000000000;hpb=7effe02be236921437fb77d8fbeb21eb62e35939;p=src%2Fapp-framework-binder.git diff --git a/src/afb-perm.c b/src/afb-perm.c new file mode 100644 index 00000000..b4d2b5e3 --- /dev/null +++ b/src/afb-perm.c @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2017 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include + +#include "afb-perm.h" +#include "verbose.h" + +/********************************************************************* +*** SECTION node +*********************************************************************/ + +/** + * types of nodes + */ +enum type +{ + Text, /**< text node */ + And, /**< and node */ + Or, /**< or node */ + Not /**< not node */ +}; + +/** + * structure for nodes + */ +struct node +{ + enum type type; /**< type of the node */ + union { + struct node *children[2]; /**< possible subnodes */ + char text[1]; /**< text of the node */ + }; +}; + +/** + * make a text node and return it + * @param type type of the node (should be Text) + * @param text the text to set for the node + * @param length the length of the text + * @return the allocated node or NULL on memory depletion + */ +static struct node *node_make_text(enum type type, const char *text, size_t length) +{ + struct node *node; + + node = malloc(length + sizeof *node); + if (!node) + errno = ENOMEM; + else { + node->type = type; + memcpy(node->text, text, length); + node->text[length] = 0; + } + return node; +} + +/** + * make a node with sub nodes and return it + * @param type type of the node (should be And, Or or Text) + * @param left the "left" sub node + * @param right the "right" sub node (if any or NULL) + * @return the allocated node or NULL on memory depletion + */ +static struct node *node_make_parent(enum type type, struct node *left, struct node *right) +{ + struct node *node; + + node = malloc(sizeof *node); + if (!node) + errno = ENOMEM; + else { + node->type = type; + node->children[0] = left; + node->children[1] = right; + } + return node; +} + +/** + * Frees the node and its possible subnodes + * @param node the node to free + */ +static void node_free(struct node *node) +{ + struct node *left, *right; + + if (node) { + switch (node->type) { + case Text: + free(node); + break; + case And: + case Or: + left = node->children[0]; + right = node->children[1]; + free(node); + node_free(left); + node_free(right); + break; + case Not: + left = node->children[0]; + free(node); + node_free(left); + break; + } + } +} + +/** + * Checks the permissions for the 'node' using the 'check' function + * and its 'closure'. + * @param node the node to check + * @param check the function that checks if a pernmission of 'name' is granted for 'closure' + * @param closure the context closure for the function 'check' + * @return 1 if the permission is granted or 0 otherwise + */ +static int node_check(struct node *node, int (*check)(void *closure, const char *name), void *closure) +{ + int rc; + + switch (node->type) { + case Text: + rc = check(closure, node->text); + break; + case And: + rc = node_check(node->children[0], check, closure); + if (rc) + rc = node_check(node->children[1], check, closure); + break; + case Or: + rc = node_check(node->children[0], check, closure); + if (!rc) + rc = node_check(node->children[1], check, closure); + break; + case Not: + rc = !node_check(node->children[0], check, closure); + break; + } + return rc; +} + +/********************************************************************* +*** SECTION parse +*********************************************************************/ + +/** + * the symbol types + */ +enum symbol +{ + TEXT, /**< a common text, name of a permission */ + AND, /**< and keyword */ + OR, /**< or keyword */ + NOT, /**< not keyword */ + OBRA, /**< open bracket */ + CBRA, /**< close bracket */ + END /**< end of input */ +}; + +/** + * structure for parsing permission description + */ +struct parse +{ + const char *desc; /**< initial permission description */ + const char *symbol; /**< current symbol parsed */ + size_t symlen; /**< length of the current symbol */ + enum symbol type; /**< type of the current symbol */ +}; + +/** + * updates parse to point to the next symbol if any + * @param parse parser state to update + */ +static void parse_next(struct parse *parse) +{ + const char *scan; + size_t len; + + /* skip current symbol */ + scan = &parse->symbol[parse->symlen]; + + /* skip white spaces */ + while (*scan && isspace(*scan)) + scan++; + + /* scan the symbol */ + switch (*scan) { + case 0: + len = 0; + parse->type = END; + break; + case '(': + len = 1; + parse->type = OBRA; + break; + case ')': + len = 1; + parse->type = CBRA; + break; + default: + /* compute the length */ + len = 0; + while (scan[len] && !isspace(scan[len]) && scan[len] != ')' && scan[len] != '(') + len++; + parse->type = TEXT; + + /* fall to keyword if any */ + switch(len) { + case 2: + if (!strncasecmp(scan, "or", len)) + parse->type = OR; + break; + case 3: + if (!strncasecmp(scan, "and", len)) + parse->type = AND; + else if (!strncasecmp(scan, "not", len)) + parse->type = NOT; + break; + } + break; + } + parse->symbol = scan; + parse->symlen = len; +} + +/** + * Init the parser state 'parse' for the description 'desc' + * @param parse the parser state to initialise + * @param desc the description of the permissions to be parsed + */ +static void parse_init(struct parse *parse, const char *desc) +{ + parse->desc = desc; + parse->symbol = desc; + parse->symlen = 0; + parse_next(parse); +} + +/********************************************************************* +*** SECTION node_parse +*********************************************************************/ + +static struct node *node_parse_or(struct parse *parse); + +/** + * Parse a permission name + * @param parser the parser state + * @return the parsed node or NULL in case of error + * in case of error errno is set to EINVAL or ENOMEM + */ +static struct node *node_parse_text(struct parse *parse) +{ + struct node *node; + + if (parse->type == TEXT) { + node = node_make_text(Text, parse->symbol, parse->symlen); + parse_next(parse); + } else { + errno = EINVAL; + node = NULL; + } + return node; +} + +/** + * Parse a term that is either a name (text) or a sub expression + * enclosed in brackets. + * @param parser the parser state + * @return the parsed node or NULL in case of error + * in case of error errno is set to EINVAL or ENOMEM + */ +static struct node *node_parse_term(struct parse *parse) +{ + struct node *node; + + if (parse->type != OBRA) + node = node_parse_text(parse); + else { + parse_next(parse); + node = node_parse_or(parse); + if (parse->type == CBRA) + parse_next(parse); + else { + errno = EINVAL; + node_free(node); + node = NULL; + } + } + return node; +} + +/** + * Parse a term potentially prefixed by not. + * @param parser the parser state + * @return the parsed node or NULL in case of error + * in case of error errno is set to EINVAL or ENOMEM + */ +static struct node *node_parse_not(struct parse *parse) +{ + struct node *node, *not; + + if (parse->type != NOT) + node = node_parse_term(parse); + else { + parse_next(parse); + node = node_parse_term(parse); + if (node) { + not = node_make_parent(Not, node, NULL); + if (not) + node = not; + else { + node_free(node); + node = NULL; + } + } + } + return node; +} + +/** + * Parse a potential sequence of terms connected with the + * given operator (AND or OR). The function takes care to + * create an evaluation tree that respects the order given + * by the description and that will limit the recursivity + * depth. + * @param parser the parser state + * @param operator the symbol type of the operator scanned + * @param subparse the function for parsing terms of the sequence + * @param type the node type corresponding to the operator + * @return the parsed node or NULL in case of error + * in case of error errno is set to EINVAL or ENOMEM + */ +static struct node *node_parse_infix( + struct parse *parse, + enum symbol operator, + struct node *(*subparse)(struct parse*), + enum type type +) +{ + struct node *root, **up, *right, *node; + + root = subparse(parse); + if (root) { + up = &root; + while (parse->type == operator) { + parse_next(parse); + right = subparse(parse); + if (!right) { + node_free(root); + root = NULL; + break; + } + node = node_make_parent(type, *up, right); + if (!node) { + node_free(right); + node_free(root); + root = NULL; + break; + } + *up = node; + up = &node->children[1]; + } + } + return root; +} + +/** + * Parse a potential sequence of anded terms. + * @param parser the parser state + * @return the parsed node or NULL in case of error + * in case of error errno is set to EINVAL or ENOMEM + */ +static struct node *node_parse_and(struct parse *parse) +{ + return node_parse_infix(parse, AND, node_parse_not, And); +} + +/** + * Parse a potential sequence of ored terms. + * @param parser the parser state + * @return the parsed node or NULL in case of error + * in case of error errno is set to EINVAL or ENOMEM + */ +static struct node *node_parse_or(struct parse *parse) +{ + return node_parse_infix(parse, OR, node_parse_and, Or); +} + +/** + * Parse description of permissions. + * @param desc the description to parse + * @return the parsed node or NULL in case of error + * in case of error errno is set to EINVAL or ENOMEM + */ +static struct node *node_parse(const char *desc) +{ + struct node *node; + struct parse parse; + + parse_init(&parse, desc); + node = node_parse_or(&parse); + if (node && parse.type != END) { + node_free(node); + node = NULL; + } + return node; +} + +/********************************************************************* +*** SECTION perm +*********************************************************************/ + +/** + * structure for storing permissions + */ +struct afb_perm +{ + struct node *root; /**< root node descripbing the permission */ + int refcount; /**< the count of use of the structure */ +}; + +/** + * allocates the structure for the given root + * @param root the root node to keep + * @return the created permission object or NULL + */ +static struct afb_perm *make_perm(struct node *root) +{ + struct afb_perm *perm; + + perm = malloc(sizeof *perm); + if (!perm) + errno = ENOMEM; + else { + perm->root = root; + perm->refcount = 1; + } + return perm; +} + +/** + * Creates the permission for the given description + * @param desc the description of the permission to create + * @return the created permission object or NULL + */ +struct afb_perm *afb_perm_parse(const char *desc) +{ + struct node *root; + struct afb_perm *perm; + + root = node_parse(desc); + if (root) { + perm = make_perm(root); + if (perm) + return perm; + node_free(root); + } + return NULL; +} + +/** + * Adds a reference to the permissions + * @param perm the permission to reference + */ +void afb_perm_addref(struct afb_perm *perm) +{ + assert(perm); + perm->refcount++; +} + +/** + * Removes a reference to the permissions + * @param perm the permission to dereference + */ +void afb_perm_unref(struct afb_perm *perm) +{ + if (perm && !--perm->refcount) { + node_free(perm->root); + free(perm); + } +} + +/** + * Checks permission 'perm' using the 'check' function + * and its 'closure'. + * @param perm the permission to check + * @param check the function that checks if a pernmission of 'name' is granted for 'closure' + * @param closure the context closure for the function 'check' + * @return 1 if the permission is granted or 0 otherwise + */ +int afb_perm_check(struct afb_perm *perm, int (*check)(void *closure, const char *name), void *closure) +{ + return node_check(perm->root, check, closure); +} + +