garyttierney / secsp

SELinux C-style Policy Language
MIT License
4 stars 1 forks source link

Grammar design: class permissions and mappings #75

Open garyttierney opened 5 years ago

garyttierney commented 5 years ago

Tracking issue for the implementation of classmap and associated CIL statements in the secsp grammar.

Current syntax used for security class definitions:

common class file_common {
    read,
    write
}

class file extends file_common { ... }

class dir extends file_common {
    search,
    ...
}

A rough draft of a potential approach:

class_permission cps_zygote = zygote(~specifyids);
class_map android_classes {
    set1 { binder(all) & property_service(set) & zygote(~specifycapabilities) }
    set2 { binder(impersonate call set_context_mgr transfer) & zygote(specifyids specifyrlimits specifycapabilities specifyinvokewith) },
    set3 { cps_zygote & binder(impersonate, call, set_context_mgr) }
}

block map_example {
    type type_1;
    type type_2;
    type type_3;

    allow type_1 self : android_classes(set_1);
    allow type_2 self : android_classes(set_2);
    allow type_3 self : android_classes(set_3);
}

The syntax isn't great here. The choice of & as a separator is arbitrary, and may as well be + or |.

garyttierney commented 5 years ago

The implementation could be a 1-1 mapping to the CIL representation, however, I feel that it'd be nicer if a class map and all of it's associated permissions could be consolidated to a single syntactical unit.

Does that have any impact on how class maps are used? It may make definitions of large class maps (i.e. a map containing all socket security classes) awkward without some other language support.

garyttierney commented 5 years ago

An alternative 1-1 mapping example:

class_permission cps_zygote = zygote(~specifyids);
class_map android_classes {
   set_1,
   set_2,
   set_3
}

android_classes.set_1 |= binder(all);
android_classes.set_1 |= property_service(set);
android_classes.set_1 |= zygote(~specifycapabilities);

android_classes.set_2 |= binder(impersonate, call, set_context_mgr, transfer);
android_classes.set_2 |= zygote(specifyids, specifyrlimits, specifycapabilities, specifyinvokewith);

android_classes.set_3 |= cps_zygote;
android_classes.set_3 |= binder(impersonate, call, set_context_mgr);

Again, the operator chosen is arbitrary, but would conflict with the operator used for attributeset statements.

ghost commented 5 years ago

My personal preference is a language that is 1. as identical to CIL as possible 2. that is as simple as possible syntax-wise, and that it only changes the lua style into c equivalents. where possible wthout compromise. With that said, i think the latter alternative better fits those goals

Its easy for me to want that but i dont know the limitations you have to deal with. I see a lot of things that i am hoping can be omitted.

if i look for example at this:

class_permission cps_zygote = zygote(~specifyids);

Then i notice for example that you are collapsing two CIL statements into a single one (why?) but i also wonder if and why the = and the () is needed here.

(classpermission ops_zygote_minus_spefifyids)
(classpermissionset ops_zygote_minus_specifyids (zygote (not (specifyids))))

i would probably have limited it to:

classpermission ops_zygote_minus_specifyids;
classpermissionset ops_zygote_minus_specifyids zygote ~specifyids;

and:

(classpermission ops_zygote_minus_spefifyids_and_specifylimits)
(classpermissionset ops_zygote_minus_specifyids_and_specifylimits (zygote (and (all) (not (specifyids specifylimits))))

to

class_permission ops_zygote_minus_speficyids_and_specifylimits;
classpermissionset ops_zygote_minus_speficyids_and_specifylimits zygote { specifyids specifyrlimits specifycapabilities specifyinvokewith specifyseinfo -specifyids -specifylimits };

or to

class_permission ops_zygote_minus_speficyids_and_specifylimits;
classpermissionset ops_zygote_minus_speficyids_and_specifylimits zygote { * -specifyids -specifylimits };

Why can't this:

class_map android_classes {
   set_1,
   set_2,
   set_3
}

just be this:

class_map android_classes {
   set_1
   set_2
   set_3
}

or:

classmap android_classes { set1 set2 set3 }

why arent you using the classmapping statement here:

android_classes.set_1 |= binder(all);

like:

classmapping android_classes set1 binder *;

Again, i might not understand the limitations you have to deal with, also i might not have the same vision as to what a ideal HLL would look like.

To me CIL is ideal, however the parens and the other s-expression-ism could be improved by using old selinux policy c-style syntax

But other than that i would (atleast initially) not deviate too much from the cil language (like collapsing stuff etc) You can always "extend" it latter to "add" some collapsing on top of what you already have.

Also I would try to keep it as simple as possible, if you can possibly omit a , = | () {} or what have you then i would try that.

garyttierney commented 5 years ago

Its easy for me to want that but i dont know the limitations you have to deal with. I see a lot of things that i am hoping can be omitted.

To be up front about this, there are no real limitations beyond keeping the grammar of the language reasonable. All of your points here could be implemented in the parser (which is still very much a WIP).

Then i notice for example that you are collapsing two CIL statements into a single one (why?)

This is about reducing the verbosity of some places where a statement only exists to be used once (i.e. typealiasactual and similar aliasing statements). The intention is to make the name declaration and the set of associated values in the same place.

It's purely syntactic sugar, e.g.,

classpermission id = initializer;
type t;
type_alias my_alias = t;

->

(classpermission id)
(classpermissionset id initializer)
(type t)
(typealias my_alias)
(typealiasactual my_alias t)

The first example states the same as the second with less noise.

but i also wonder if and why the = and the () is needed here.

Hmm. I'll think about this some more. Permission expressions in CIL are much more flexible than in the kernel policy language, so only supporting variants of:

security_class { perm -perm };
security_class { * };

Wouldn't allow me to keep feature parity. For example, how should a CIL statement like so look:

(classpermissionset zygote_4 (zygote (xor (specifyids specifyrlimits specifycapabilities specifyinvokewith specifyseinfo) (specifyids specifyrlimits specifycapabilities specifyinvokewith specifyseinfo))))

A basic translation might be something like:

classpermissionset zygote_4 zygote { specifyids specifyrlimits specifycapabilities specifyinvokewith specifyseinfo ^ specifyids specifyrlimits specifycapabilities specifyinvokewith specifyseinfo };

Does that look reasonable? I'm not sure. Keep in mind you can start to nest those expressions arbitrarily (((specifyids specifyrlimits ^ specifycapabilities) | specifyinvokewith)). I'll see what I can do to bring this more inline with kernel policy language without sacrificing any of the CIL flexibility.

Why can't this:

No reason, and good idea. I've dropped the commas from the latest version of the grammar

why arent you using the classmapping statement here:

Mostly, this point of the CIL design philosophy

The statements are unambiguous and overlap in very well defined ways. This is in contrast to the current language where a statement, such as a role statement, might be a declaration, a further definition, or both depending on context.

I'd like to strip back some of the verbosity of CIL (i.e. needing 2 statements to define a typealias) without making the resulting language ambiguous .

This isn't the case of the classmapping example, since classmapping is always a modifier -- but I do wonder if it's worthwhile to have a syntactical delineation between modifying statements and declarations/definitions.

classmapping android_classes set1 binder *;

I think there's some key things for me to look at:

garyttierney commented 5 years ago

Will implement this with a direct CIL mapping for now. As for class permission expressions, it seems like 2 variants might be what we need:

// variant 1, same as legacy kernel policy
classmapping android_classes set1 binder { perm1 -perm2 };

// variant 2, using classpermissionsets
classpermission perms1 = binder { perm1 };
classpermission perms2 = binder { -perm2 };
classmapping android_classes set1 perms1 & perms2;  // all CIL operators available here