From 32c24c247e71dc8c68760d898fc4bc69caf23a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Mon, 11 Nov 2024 14:51:04 +0100 Subject: [PATCH] checkpolicy: add support for xperms in conditional policies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for extended permission rules in conditional policies. Signed-off-by: Christian Göttsche Acked-by: James Carter --- checkpolicy/policy_define.c | 108 +++++++++++++++--- checkpolicy/policy_define.h | 1 + checkpolicy/policy_parse.y | 20 +++- checkpolicy/tests/policy_allonce.conf | 7 +- .../tests/policy_allonce.expected.conf | 10 ++ .../tests/policy_allonce.expected_opt.conf | 10 ++ 6 files changed, 141 insertions(+), 15 deletions(-) diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c index 9ae8c4d4..4b0eca6b 100644 --- a/checkpolicy/policy_define.c +++ b/checkpolicy/policy_define.c @@ -1882,6 +1882,8 @@ int define_bool_tunable(int is_tunable) avrule_t *define_cond_pol_list(avrule_t * avlist, avrule_t * sl) { + avrule_t *last; + if (pass == 1) { /* return something so we get through pass 1 */ return (avrule_t *) 1; @@ -1892,8 +1894,12 @@ avrule_t *define_cond_pol_list(avrule_t * avlist, avrule_t * sl) return avlist; } - /* prepend the new avlist to the pre-existing one */ - sl->next = avlist; + /* Prepend the new avlist to the pre-existing one. + * An extended permission statement might consist of multiple av + * rules. */ + for (last = sl; last->next; last = last->next) + ; + last->next = avlist; return sl; } @@ -1995,7 +2001,7 @@ static int avrule_read_xperm_ranges(struct av_xperm_range_list **rangehead) id = queue_remove(id_queue); r->range.high = (uint16_t) strtoul(id,NULL,0); if (r->range.high < r->range.low) { - yyerror2("Ioctl range %d-%d must be in ascending order.", + yyerror2("extended permission range %#x-%#x must be in ascending order.", r->range.low, r->range.high); return -1; } @@ -2477,9 +2483,9 @@ static int avrule_cpy(avrule_t *dest, const avrule_t *src) return 0; } -static int define_te_avtab_ioctl(const avrule_t *avrule_template) +static int define_te_avtab_ioctl(const avrule_t *avrule_template, avrule_t **ret_avrules) { - avrule_t *avrule; + avrule_t *avrule, *ret = NULL, **last = &ret; struct av_xperm_range_list *rangelist, *r; av_extended_perms_t *complete_driver, *partial_driver, *xperms; unsigned int i; @@ -2501,7 +2507,13 @@ static int define_te_avtab_ioctl(const avrule_t *avrule_template) if (avrule_cpy(avrule, avrule_template)) return -1; avrule->xperms = complete_driver; - append_avrule(avrule); + + if (ret_avrules) { + *last = avrule; + last = &(avrule->next); + } else { + append_avrule(avrule); + } } /* flag ioctl driver codes that are partially enabled */ @@ -2530,7 +2542,13 @@ static int define_te_avtab_ioctl(const avrule_t *avrule_template) if (avrule_cpy(avrule, avrule_template)) return -1; avrule->xperms = xperms; - append_avrule(avrule); + + if (ret_avrules) { + *last = avrule; + last = &(avrule->next); + } else { + append_avrule(avrule); + } } } @@ -2544,12 +2562,15 @@ done: free(r); } + if (ret_avrules) + *ret_avrules = ret; + return 0; } -static int define_te_avtab_netlink(const avrule_t *avrule_template) +static int define_te_avtab_netlink(const avrule_t *avrule_template, avrule_t **ret_avrules) { - avrule_t *avrule; + avrule_t *avrule, *ret = NULL, **last = &ret; struct av_xperm_range_list *rangelist, *r; av_extended_perms_t *partial_driver, *xperms; unsigned int i; @@ -2584,7 +2605,13 @@ static int define_te_avtab_netlink(const avrule_t *avrule_template) if (avrule_cpy(avrule, avrule_template)) return -1; avrule->xperms = xperms; - append_avrule(avrule); + + if (ret_avrules) { + *last = avrule; + last = &(avrule->next); + } else { + append_avrule(avrule); + } } } @@ -2598,9 +2625,64 @@ done: free(r); } + if (ret_avrules) + *ret_avrules = ret; + return 0; } +avrule_t *define_cond_te_avtab_extended_perms(int which) +{ + char *id; + unsigned int i; + avrule_t *avrule_template, *rules = NULL; + int rc = 0; + + if (policydbp->policy_type == POLICY_KERN && policydbp->policyvers < POLICYDB_VERSION_COND_XPERMS) { + yyerror2("extended permissions in conditional policies are only supported since policy version %d, found policy version %d", + POLICYDB_VERSION_COND_XPERMS, policydbp->policyvers); + return COND_ERR; + } + if (policydbp->policy_type != POLICY_KERN && policydbp->policyvers < MOD_POLICYDB_VERSION_COND_XPERMS) { + yyerror2("extended permissions in conditional policies are only supported since module policy version %d, found module policy version %d", + MOD_POLICYDB_VERSION_COND_XPERMS, policydbp->policyvers); + return COND_ERR; + } + + if (pass == 1) { + for (i = 0; i < 4; i++) { + while ((id = queue_remove(id_queue))) + free(id); + } + return (avrule_t *) 1; /* any non-NULL value */ + } + + /* populate avrule template with source/target/tclass */ + if (define_te_avtab_xperms_helper(which, &avrule_template)) + return COND_ERR; + + id = queue_remove(id_queue); + if (strcmp(id, "ioctl") == 0) { + rc = define_te_avtab_ioctl(avrule_template, &rules); + } else if (strcmp(id, "nlmsg") == 0) { + rc = define_te_avtab_netlink(avrule_template, &rules); + } else { + yyerror2("only ioctl and nlmsg extended permissions are supported, found %s", id); + rc = -1; + } + + free(id); + avrule_destroy(avrule_template); + free(avrule_template); + + if (rc) { + avrule_destroy(rules); + return NULL; + } + + return rules; +} + int define_te_avtab_extended_perms(int which) { char *id; @@ -2622,11 +2704,11 @@ int define_te_avtab_extended_perms(int which) id = queue_remove(id_queue); if (strcmp(id,"ioctl") == 0) { - rc = define_te_avtab_ioctl(avrule_template); + rc = define_te_avtab_ioctl(avrule_template, NULL); } else if (strcmp(id,"nlmsg") == 0) { - rc = define_te_avtab_netlink(avrule_template); + rc = define_te_avtab_netlink(avrule_template, NULL); } else { - yyerror2("only ioctl extended permissions are supported, found %s", id); + yyerror2("only ioctl and nlmsg extended permissions are supported, found %s", id); rc = -1; } diff --git a/checkpolicy/policy_define.h b/checkpolicy/policy_define.h index ef74f616..216da3ad 100644 --- a/checkpolicy/policy_define.h +++ b/checkpolicy/policy_define.h @@ -15,6 +15,7 @@ avrule_t *define_cond_compute_type(int which); avrule_t *define_cond_pol_list(avrule_t *avlist, avrule_t *sl); avrule_t *define_cond_te_avtab(int which); +avrule_t *define_cond_te_avtab_extended_perms(int which); avrule_t *define_cond_filename_trans(void); cond_expr_t *define_cond_expr(uint32_t expr_type, void *arg1, void* arg2); int define_attrib(void); diff --git a/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y index ed1786d8..7e117222 100644 --- a/checkpolicy/policy_parse.y +++ b/checkpolicy/policy_parse.y @@ -74,6 +74,7 @@ typedef int (* require_func_t)(int pass); %type cond_expr cond_expr_prim cond_pol_list cond_else %type cond_allow_def cond_auditallow_def cond_auditdeny_def cond_dontaudit_def +%type cond_xperm_allow_def cond_xperm_auditallow_def cond_xperm_dontaudit_def %type cond_transition_def cond_te_avtab_def cond_rule_def %type cexpr cexpr_prim op role_mls_op %type ipv4_addr_def number @@ -432,6 +433,12 @@ cond_te_avtab_def : cond_allow_def { $$ = $1; } | cond_dontaudit_def { $$ = $1; } + | cond_xperm_allow_def + { $$ = $1; } + | cond_xperm_auditallow_def + { $$ = $1; } + | cond_xperm_dontaudit_def + { $$ = $1; } ; cond_allow_def : ALLOW names names ':' names names ';' { $$ = define_cond_te_avtab(AVRULE_ALLOWED) ; @@ -449,7 +456,18 @@ cond_dontaudit_def : DONTAUDIT names names ':' names names ';' { $$ = define_cond_te_avtab(AVRULE_DONTAUDIT); if ($$ == COND_ERR) YYABORT; } ; - ; +cond_xperm_allow_def : ALLOWXPERM names names ':' names identifier xperms ';' + { $$ = define_cond_te_avtab_extended_perms(AVRULE_XPERMS_ALLOWED) ; + if ($$ == COND_ERR) YYABORT; } + ; +cond_xperm_auditallow_def : AUDITALLOWXPERM names names ':' names identifier xperms ';' + { $$ = define_cond_te_avtab_extended_perms(AVRULE_XPERMS_AUDITALLOW) ; + if ($$ == COND_ERR) YYABORT; } + ; +cond_xperm_dontaudit_def : DONTAUDITXPERM names names ':' names identifier xperms ';' + { $$ = define_cond_te_avtab_extended_perms(AVRULE_XPERMS_DONTAUDIT) ; + if ($$ == COND_ERR) YYABORT; } + ; transition_def : TYPE_TRANSITION names names ':' names identifier filename ';' {if (define_filename_trans()) YYABORT; } | TYPE_TRANSITION names names ':' names identifier ';' diff --git a/checkpolicy/tests/policy_allonce.conf b/checkpolicy/tests/policy_allonce.conf index 2cfbb772..51a8c40a 100644 --- a/checkpolicy/tests/policy_allonce.conf +++ b/checkpolicy/tests/policy_allonce.conf @@ -2,6 +2,7 @@ class CLASS1 class CLASS2 class CLASS3 +class CLASS4 class dir class file class process @@ -10,6 +11,7 @@ common COMMON1 { CPERM1 } class CLASS1 { PERM1 ioctl } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } +class CLASS4 { nlmsg } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; @@ -26,6 +28,7 @@ typealias TYPE1 alias TYPEALIAS1; typeattribute TYPE1 ATTR1; typebounds TYPE4 TYPE3; bool BOOL1 true; +bool BOOL2 false; tunable TUNABLE1 false; tunable TUNABLE2 true; type_transition TYPE1 TYPE2 : CLASS1 TYPE3; @@ -37,6 +40,7 @@ auditallow { TYPE1 TYPE2 } TYPE3 : CLASS1 { PERM1 }; dontaudit TYPE1 { TYPE2 TYPE3 } : CLASS3 { PERM1 CPERM1 }; neverallow TYPE1 TYPE2 : { CLASS2 CLASS3 } { CPERM1 }; allowxperm TYPE1 TYPE2 : CLASS1 ioctl { 0x456-0x5678 }; +allowxperm TYPE2 TYPE1 : CLASS4 nlmsg { 0x1 0x12 }; auditallowxperm TYPE1 TYPE2 : CLASS1 ioctl 0x2; dontauditxperm TYPE1 TYPE2 : CLASS1 ioctl 0x3; neverallowxperm TYPE1 TYPE2 : CLASS1 ioctl 0x4; @@ -50,7 +54,8 @@ role_transition ROLE1 TYPE1 : CLASS1 ROLE2; allow ROLE1 ROLE2; roleattribute ROLE3 ROLE_ATTR1; role ROLE1 types { TYPE1 }; -if ! BOOL1 { allow TYPE1 self: CLASS1 *; } +if ! BOOL1 { allow TYPE1 self: CLASS1 *; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x6789 - 0x9876 }; } +if BOOL2 { allowxperm TYPE2 TYPE1:CLASS4 nlmsg { 0x1 0x2 }; } if TUNABLE1 xor TUNABLE2 { allow TYPE1 self: CLASS2 *; } else { allow TYPE1 self: CLASS3 *; } optional { require { class CLASS2 { CPERM1 }; } allow TYPE1 self: CLASS2 *; } user USER1 roles ROLE1; diff --git a/checkpolicy/tests/policy_allonce.expected.conf b/checkpolicy/tests/policy_allonce.expected.conf index 26d56438..355d9991 100644 --- a/checkpolicy/tests/policy_allonce.expected.conf +++ b/checkpolicy/tests/policy_allonce.expected.conf @@ -2,6 +2,7 @@ class CLASS1 class CLASS2 class CLASS3 +class CLASS4 class dir class file class process @@ -10,6 +11,7 @@ common COMMON1 { CPERM1 } class CLASS1 { PERM1 ioctl } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } +class CLASS4 { nlmsg } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; @@ -17,6 +19,7 @@ policycap open_perms; attribute ATTR1; attribute ATTR2; bool BOOL1 true; +bool BOOL2 false; type TYPE1; type TYPE2; type TYPE3; @@ -37,6 +40,7 @@ dontaudit TYPE1 TYPE3:CLASS3 { CPERM1 PERM1 }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x456-0x4ff }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x500-0x55ff }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x5600-0x5678 }; +allowxperm TYPE2 TYPE1:CLASS4 nlmsg { 0x1 0x12 }; auditallowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x2 }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; @@ -49,6 +53,12 @@ type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; if (BOOL1) { } else { allow TYPE1 self:CLASS1 { PERM1 ioctl }; + dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x6789-0x67ff }; + dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x6800-0x97ff }; + dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x9800-0x9876 }; +} +if (BOOL2) { + allowxperm TYPE2 TYPE1:CLASS4 nlmsg { 0x1-0x2 }; } role ROLE1; role ROLE2; diff --git a/checkpolicy/tests/policy_allonce.expected_opt.conf b/checkpolicy/tests/policy_allonce.expected_opt.conf index 769be2b3..74eec4ba 100644 --- a/checkpolicy/tests/policy_allonce.expected_opt.conf +++ b/checkpolicy/tests/policy_allonce.expected_opt.conf @@ -2,6 +2,7 @@ class CLASS1 class CLASS2 class CLASS3 +class CLASS4 class dir class file class process @@ -10,6 +11,7 @@ common COMMON1 { CPERM1 } class CLASS1 { PERM1 ioctl } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } +class CLASS4 { nlmsg } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; @@ -17,6 +19,7 @@ policycap open_perms; attribute ATTR1; attribute ATTR2; bool BOOL1 true; +bool BOOL2 false; type TYPE1; type TYPE2; type TYPE3; @@ -37,6 +40,7 @@ dontaudit TYPE1 TYPE3:CLASS3 { CPERM1 PERM1 }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x456-0x4ff }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x500-0x55ff }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x5600-0x5678 }; +allowxperm TYPE2 TYPE1:CLASS4 nlmsg { 0x1 0x12 }; auditallowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x2 }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; @@ -49,6 +53,12 @@ type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; if (BOOL1) { } else { allow TYPE1 self:CLASS1 { ioctl }; + dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x6789-0x67ff }; + dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x6800-0x97ff }; + dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x9800-0x9876 }; +} +if (BOOL2) { + allowxperm TYPE2 TYPE1:CLASS4 nlmsg { 0x2 }; } role ROLE1; role ROLE2;