* [RFC] An idea of thread/child-domain assignment
@ 2008-07-15 10:06 KaiGai Kohei
2008-07-15 13:38 ` Stephen Smalley
0 siblings, 1 reply; 97+ messages in thread
From: KaiGai Kohei @ 2008-07-15 10:06 UTC (permalink / raw)
To: sds, jmorris, paul.moore, jbrindle; +Cc: selinux
Hi,
I'm now considering the way to apply SELinux from the bottom of LAPP
stack to the head. But there is a fundamental matter. Web/Application
server can execute a request come from users in a worker thread.
This idea enables to assign an indivisual domain on a thread under
the paticular condition.
I'm sorry for a long description, but I thought it need to describe
the background of this idea for well understanding.
Please comment anything.
Backend of the idea
-------------------
How do you define a user?
I consider a user is a human. In the most cases, a human is not good at
communication with electronic circuit of computer system, so he makes a
substitution of himself to obtain a result which he want.
A process on operating system is a typical substitution of the user.
Thus, it has a user identifier and a security context for proper access
controls as a substitution of a user.
I'm considering database instances, web backends and others are also the
substitution of users, because they works according to the request come
from users.
SELinux makes its decison on access controls based on security context of
subject and various kind of objects. Now we have three options to assign
a security context on a subject, as follows:
1. Authentication ... Operating System
2. Labeled Networking ... SE-PostgreSQL, X/SELinux, Xinetd
3. Do nothing ... Apache, Samba
However, the third option is uncomfortable for system security.
It execute a user's request as a part of the server process, even if it
is a substitution of the user.
LAPP stack and SELinux
----------------------
Nowaday, numerous of online services are available and most of them are
implemented as a web application using PHP, Jservlet and so on. LAPP is
a term to represent its system architecture. It shows web applications
based on Linux, Apache, PostgreSQL and PHP is a popular combination.
>From the viewpoint of SELinux, we cannot apply inidividual access controls
for every request come from clients, because web/application server handle
these requests without individual processes (except for CGI invocation).
Apache typically assigns a pre-created process/thread with "httpd_t" domain
to execute the request, independent from the peer's context.
Now SELinux can cover the lowest two layer of the LAPP stack (OS, RDBMS).
It enables to apply consistent/mandatory access controls for them. I think
it should be also expanded to whole of the stack to improve web-application
security.
But we have a fundamental matter to achieve it.
[1] LAPP Stack and SELinux coverage
+----------------------------+ <-----------------+
| Application (PHP, Tomcat) | |
+----------------------------+ |
| Web Server (Apache) | Future
+----------------------------+ <-----------+ |
| Database (SE-PostgreSQL) | | |
+----------------------------+ <-----+ Today |
| Operating System (SELinux) | Past | |
+----------------------------+ <-----+-----+-----+
An idea: thread/hierarchical-domain assignment
----------------------------------------------
If possible, Apache should change the security context of web backend
during it executes a request come from users.
However, Apache can execute it on a worker thread. It conflicts a wired
constraint of SELinux which does not allow a multi-threaded process dynamic
domain transition.
It is a reasonable constraint, if the source and destination domains are
completely independent. Because it shares a local memory, mixed security
context within a process can make a violation in domain separation.
However, a characteristic relationship between hierarchical domains can
help the situation. It restricts a child domain (like "httpd_t.staff")
cannot over the boundary of its parent ("httpd_t"). It also means
a multi-threaded process cannot over the (parent) domain boundary,
even if a part of threads has different security context.
My suggestion is the hard-wired constraint should be modified.
It allows to assign threads an indivisual domain, if the new domain
is a child one of the current domain.
For example:
httpd_t -> httpd_t.staff = allowed
httpd_t -> staff_t = disallowed
httpd_t.staff -> httpd_t.staff.php = allowed
In other words, this idea requires to change our viewpoint for child domain.
Previously, these are mutually independent domain, but a child domain will
mean a state with restricted operations compared to its parent domain.
Issues: Domain Reverting
------------------------
There is a related issue. To change security context during user request
execution also means that we have to revert its domain after executions,
because Apache repeatedly uses worker thread/process for some reasons.
I don't have any good idea for this issue yet.
A one time cookie like AppArmor is an approach, but it is basically weak
separation compared from execve() transition.
Thanks.
--
OSS Platform Development Division, NEC
KaiGai Kohei <kaigai@ak.jp.nec.com>
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 97+ messages in thread* Re: [RFC] An idea of thread/child-domain assignment 2008-07-15 10:06 [RFC] An idea of thread/child-domain assignment KaiGai Kohei @ 2008-07-15 13:38 ` Stephen Smalley 2008-07-16 2:17 ` KaiGai Kohei 0 siblings, 1 reply; 97+ messages in thread From: Stephen Smalley @ 2008-07-15 13:38 UTC (permalink / raw) To: KaiGai Kohei; +Cc: jmorris, paul.moore, jbrindle, selinux On Tue, 2008-07-15 at 19:06 +0900, KaiGai Kohei wrote: > Hi, > > I'm now considering the way to apply SELinux from the bottom of LAPP > stack to the head. But there is a fundamental matter. Web/Application > server can execute a request come from users in a worker thread. > > This idea enables to assign an indivisual domain on a thread under > the paticular condition. Yes, this came up recently, e.g.: http://marc.info/?l=selinux&m=121320242220824&w=2 And James added it to the kernel todo list on selinuxproject.org. > I'm sorry for a long description, but I thought it need to describe > the background of this idea for well understanding. > > Please comment anything. > > Backend of the idea > ------------------- > How do you define a user? > > I consider a user is a human. In the most cases, a human is not good at > communication with electronic circuit of computer system, so he makes a > substitution of himself to obtain a result which he want. > > A process on operating system is a typical substitution of the user. > Thus, it has a user identifier and a security context for proper access > controls as a substitution of a user. > I'm considering database instances, web backends and others are also the > substitution of users, because they works according to the request come > from users. > > SELinux makes its decison on access controls based on security context of > subject and various kind of objects. Now we have three options to assign > a security context on a subject, as follows: > > 1. Authentication ... Operating System > 2. Labeled Networking ... SE-PostgreSQL, X/SELinux, Xinetd > 3. Do nothing ... Apache, Samba > > However, the third option is uncomfortable for system security. > It execute a user's request as a part of the server process, even if it > is a substitution of the user. > > > LAPP stack and SELinux > ---------------------- > Nowaday, numerous of online services are available and most of them are > implemented as a web application using PHP, Jservlet and so on. LAPP is > a term to represent its system architecture. It shows web applications > based on Linux, Apache, PostgreSQL and PHP is a popular combination. > > >From the viewpoint of SELinux, we cannot apply inidividual access controls > for every request come from clients, because web/application server handle > these requests without individual processes (except for CGI invocation). > Apache typically assigns a pre-created process/thread with "httpd_t" domain > to execute the request, independent from the peer's context. > > Now SELinux can cover the lowest two layer of the LAPP stack (OS, RDBMS). > It enables to apply consistent/mandatory access controls for them. I think > it should be also expanded to whole of the stack to improve web-application > security. > But we have a fundamental matter to achieve it. > > [1] LAPP Stack and SELinux coverage > +----------------------------+ <-----------------+ > | Application (PHP, Tomcat) | | > +----------------------------+ | > | Web Server (Apache) | Future > +----------------------------+ <-----------+ | > | Database (SE-PostgreSQL) | | | > +----------------------------+ <-----+ Today | > | Operating System (SELinux) | Past | | > +----------------------------+ <-----+-----+-----+ > > > An idea: thread/hierarchical-domain assignment > ---------------------------------------------- > If possible, Apache should change the security context of web backend > during it executes a request come from users. > However, Apache can execute it on a worker thread. It conflicts a wired > constraint of SELinux which does not allow a multi-threaded process dynamic > domain transition. > > It is a reasonable constraint, if the source and destination domains are > completely independent. Because it shares a local memory, mixed security > context within a process can make a violation in domain separation. > > However, a characteristic relationship between hierarchical domains can > help the situation. It restricts a child domain (like "httpd_t.staff") > cannot over the boundary of its parent ("httpd_t"). It also means > a multi-threaded process cannot over the (parent) domain boundary, > even if a part of threads has different security context. > > My suggestion is the hard-wired constraint should be modified. > It allows to assign threads an indivisual domain, if the new domain > is a child one of the current domain. > > For example: > httpd_t -> httpd_t.staff = allowed > httpd_t -> staff_t = disallowed > httpd_t.staff -> httpd_t.staff.php = allowed > > In other words, this idea requires to change our viewpoint for child domain. > Previously, these are mutually independent domain, but a child domain will > mean a state with restricted operations compared to its parent domain. I tend to agree that we should support such changes in the kernel mechanism and let the use of it be determined by the policy. In terms of hierarchy though, the plan was to replace the current name-based hierarchy with an explicitly declared hierarchy. > > Issues: Domain Reverting > ------------------------ > There is a related issue. To change security context during user request > execution also means that we have to revert its domain after executions, > because Apache repeatedly uses worker thread/process for some reasons. > > I don't have any good idea for this issue yet. > A one time cookie like AppArmor is an approach, but it is basically weak > separation compared from execve() transition. Perhaps it could pre-populate different pools of threads for different client security contexts, and then just draw from the correct pool for each client. Then the thread context doesn't have to be reverted at all and we can make the transition unidirectional. -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [RFC] An idea of thread/child-domain assignment 2008-07-15 13:38 ` Stephen Smalley @ 2008-07-16 2:17 ` KaiGai Kohei 2008-07-16 6:08 ` KaiGai Kohei 2008-07-16 12:18 ` Stephen Smalley 0 siblings, 2 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-07-16 2:17 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux Stephen Smalley wrote: > On Tue, 2008-07-15 at 19:06 +0900, KaiGai Kohei wrote: >> Hi, >> >> I'm now considering the way to apply SELinux from the bottom of LAPP >> stack to the head. But there is a fundamental matter. Web/Application >> server can execute a request come from users in a worker thread. >> >> This idea enables to assign an indivisual domain on a thread under >> the paticular condition. > > Yes, this came up recently, e.g.: > http://marc.info/?l=selinux&m=121320242220824&w=2 > > And James added it to the kernel todo list on selinuxproject.org. Thanks, I overlooked this message. >> An idea: thread/hierarchical-domain assignment >> ---------------------------------------------- >> If possible, Apache should change the security context of web backend >> during it executes a request come from users. >> However, Apache can execute it on a worker thread. It conflicts a wired >> constraint of SELinux which does not allow a multi-threaded process dynamic >> domain transition. >> >> It is a reasonable constraint, if the source and destination domains are >> completely independent. Because it shares a local memory, mixed security >> context within a process can make a violation in domain separation. >> >> However, a characteristic relationship between hierarchical domains can >> help the situation. It restricts a child domain (like "httpd_t.staff") >> cannot over the boundary of its parent ("httpd_t"). It also means >> a multi-threaded process cannot over the (parent) domain boundary, >> even if a part of threads has different security context. >> >> My suggestion is the hard-wired constraint should be modified. >> It allows to assign threads an indivisual domain, if the new domain >> is a child one of the current domain. >> >> For example: >> httpd_t -> httpd_t.staff = allowed >> httpd_t -> staff_t = disallowed >> httpd_t.staff -> httpd_t.staff.php = allowed >> >> In other words, this idea requires to change our viewpoint for child domain. >> Previously, these are mutually independent domain, but a child domain will >> mean a state with restricted operations compared to its parent domain. > > I tend to agree that we should support such changes in the kernel > mechanism and let the use of it be determined by the policy. > > In terms of hierarchy though, the plan was to replace the current > name-based hierarchy with an explicitly declared hierarchy. It needs a new policy statement like: TYPE <child domain> DOMBY <parent domain>; or TYPEDOMINANCE <parent domain> <child domain> [,<child domain>] ; I guess these relationships are represented as a ebitmap structure in the kernel, and used to mask permissions during AVC entry creation. >> Issues: Domain Reverting >> ------------------------ >> There is a related issue. To change security context during user request >> execution also means that we have to revert its domain after executions, >> because Apache repeatedly uses worker thread/process for some reasons. >> >> I don't have any good idea for this issue yet. >> A one time cookie like AppArmor is an approach, but it is basically weak >> separation compared from execve() transition. > > Perhaps it could pre-populate different pools of threads for different > client security contexts, and then just draw from the correct pool for > each client. Then the thread context doesn't have to be reverted at all > and we can make the transition unidirectional. I think this approach requires two precondition. (1) The number of client security context should be enough small. If we want to assign one of MCS categories, it requires 1024 of thread pool in maximum. (2) We have to know what client security context should be assigned before the communication between client and backend thread. In the case when we use HTTP authentication to decide client security context, the authentication process has to be done in the backend process after the communication started. It may also require major works in application updates. In my impression, AA's approach is not perfect, but second best one... Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [RFC] An idea of thread/child-domain assignment 2008-07-16 2:17 ` KaiGai Kohei @ 2008-07-16 6:08 ` KaiGai Kohei 2008-07-16 12:00 ` Stephen Smalley 2008-07-16 12:18 ` Stephen Smalley 1 sibling, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-07-16 6:08 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux >>> In other words, this idea requires to change our viewpoint for child domain. >>> Previously, these are mutually independent domain, but a child domain will >>> mean a state with restricted operations compared to its parent domain. >> I tend to agree that we should support such changes in the kernel >> mechanism and let the use of it be determined by the policy. >> >> In terms of hierarchy though, the plan was to replace the current >> name-based hierarchy with an explicitly declared hierarchy. > > It needs a new policy statement like: > > TYPE <child domain> DOMBY <parent domain>; > or > TYPEDOMINANCE <parent domain> <child domain> [,<child domain>] ; > > I guess these relationships are represented as a ebitmap structure > in the kernel, and used to mask permissions during AVC entry creation. Should it be possible to have multiple parent domains on the explicitly declared hierarchy? The current name-based hierarchy only supports tree-shaped structure. (A child domain can have one parent at most.) It implicitly enables to avoid a matter of loop of relationship between parents and children. If we can define a domain with multiple parent, it makes the logic to detect the loop complicated. -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [RFC] An idea of thread/child-domain assignment 2008-07-16 6:08 ` KaiGai Kohei @ 2008-07-16 12:00 ` Stephen Smalley 0 siblings, 0 replies; 97+ messages in thread From: Stephen Smalley @ 2008-07-16 12:00 UTC (permalink / raw) To: KaiGai Kohei; +Cc: jmorris, paul.moore, jbrindle, selinux On Wed, 2008-07-16 at 15:08 +0900, KaiGai Kohei wrote: > >>> In other words, this idea requires to change our viewpoint for child domain. > >>> Previously, these are mutually independent domain, but a child domain will > >>> mean a state with restricted operations compared to its parent domain. > >> I tend to agree that we should support such changes in the kernel > >> mechanism and let the use of it be determined by the policy. > >> > >> In terms of hierarchy though, the plan was to replace the current > >> name-based hierarchy with an explicitly declared hierarchy. > > > > It needs a new policy statement like: > > > > TYPE <child domain> DOMBY <parent domain>; > > or > > TYPEDOMINANCE <parent domain> <child domain> [,<child domain>] ; > > > > I guess these relationships are represented as a ebitmap structure > > in the kernel, and used to mask permissions during AVC entry creation. > > Should it be possible to have multiple parent domains on the explicitly > declared hierarchy? > > The current name-based hierarchy only supports tree-shaped structure. > (A child domain can have one parent at most.) > It implicitly enables to avoid a matter of loop of relationship between > parents and children. > > If we can define a domain with multiple parent, it makes the logic to > detect the loop complicated. I don't see a reason for supporting multiple parents. -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [RFC] An idea of thread/child-domain assignment 2008-07-16 2:17 ` KaiGai Kohei 2008-07-16 6:08 ` KaiGai Kohei @ 2008-07-16 12:18 ` Stephen Smalley 2008-07-18 6:21 ` KaiGai Kohei 1 sibling, 1 reply; 97+ messages in thread From: Stephen Smalley @ 2008-07-16 12:18 UTC (permalink / raw) To: KaiGai Kohei; +Cc: jmorris, paul.moore, jbrindle, selinux On Wed, 2008-07-16 at 11:17 +0900, KaiGai Kohei wrote: > Stephen Smalley wrote: > > On Tue, 2008-07-15 at 19:06 +0900, KaiGai Kohei wrote: > >> Hi, > >> > >> I'm now considering the way to apply SELinux from the bottom of LAPP > >> stack to the head. But there is a fundamental matter. Web/Application > >> server can execute a request come from users in a worker thread. > >> > >> This idea enables to assign an indivisual domain on a thread under > >> the paticular condition. > > > > Yes, this came up recently, e.g.: > > http://marc.info/?l=selinux&m=121320242220824&w=2 > > > > And James added it to the kernel todo list on selinuxproject.org. > > Thanks, I overlooked this message. > > > >> An idea: thread/hierarchical-domain assignment > >> ---------------------------------------------- > >> If possible, Apache should change the security context of web backend > >> during it executes a request come from users. > >> However, Apache can execute it on a worker thread. It conflicts a wired > >> constraint of SELinux which does not allow a multi-threaded process dynamic > >> domain transition. > >> > >> It is a reasonable constraint, if the source and destination domains are > >> completely independent. Because it shares a local memory, mixed security > >> context within a process can make a violation in domain separation. > >> > >> However, a characteristic relationship between hierarchical domains can > >> help the situation. It restricts a child domain (like "httpd_t.staff") > >> cannot over the boundary of its parent ("httpd_t"). It also means > >> a multi-threaded process cannot over the (parent) domain boundary, > >> even if a part of threads has different security context. > >> > >> My suggestion is the hard-wired constraint should be modified. > >> It allows to assign threads an indivisual domain, if the new domain > >> is a child one of the current domain. > >> > >> For example: > >> httpd_t -> httpd_t.staff = allowed > >> httpd_t -> staff_t = disallowed > >> httpd_t.staff -> httpd_t.staff.php = allowed > >> > >> In other words, this idea requires to change our viewpoint for child domain. > >> Previously, these are mutually independent domain, but a child domain will > >> mean a state with restricted operations compared to its parent domain. > > > > I tend to agree that we should support such changes in the kernel > > mechanism and let the use of it be determined by the policy. > > > > In terms of hierarchy though, the plan was to replace the current > > name-based hierarchy with an explicitly declared hierarchy. > > It needs a new policy statement like: > > TYPE <child domain> DOMBY <parent domain>; > or > TYPEDOMINANCE <parent domain> <child domain> [,<child domain>] ; > > I guess these relationships are represented as a ebitmap structure > in the kernel, and used to mask permissions during AVC entry creation. You don't necessarily have to represent the hierarchy in the kernel; it can be done entirely in the policy toolchain like the current type hierarchy. > >> Issues: Domain Reverting > >> ------------------------ > >> There is a related issue. To change security context during user request > >> execution also means that we have to revert its domain after executions, > >> because Apache repeatedly uses worker thread/process for some reasons. > >> > >> I don't have any good idea for this issue yet. > >> A one time cookie like AppArmor is an approach, but it is basically weak > >> separation compared from execve() transition. > > > > Perhaps it could pre-populate different pools of threads for different > > client security contexts, and then just draw from the correct pool for > > each client. Then the thread context doesn't have to be reverted at all > > and we can make the transition unidirectional. > > I think this approach requires two precondition. > > (1) The number of client security context should be enough small. > If we want to assign one of MCS categories, it requires 1024 of thread > pool in maximum. The main server thread could lazily create the thread pools as needed to avoid unnecessary pools. And we could possibly use a hybrid scheme (e.g. one pool per sensitivity or per equivalence class of categories, reuse within that pool). > (2) We have to know what client security context should be assigned > before the communication between client and backend thread. > In the case when we use HTTP authentication to decide client security > context, the authentication process has to be done in the backend > process after the communication started. > It may also require major works in application updates. True, but not an issue when using labeled networking as the basis for determining the client security context. > In my impression, AA's approach is not perfect, but second best one... I'd have to hear an argument that there is a real security benefit from it. Do you just mean reverting domain transitions or do you specifically mean the cookie-based approach? -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [RFC] An idea of thread/child-domain assignment 2008-07-16 12:18 ` Stephen Smalley @ 2008-07-18 6:21 ` KaiGai Kohei 2008-07-23 3:58 ` KaiGai Kohei 0 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-07-18 6:21 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux >>>> An idea: thread/hierarchical-domain assignment >>>> ---------------------------------------------- >>>> If possible, Apache should change the security context of web backend >>>> during it executes a request come from users. >>>> However, Apache can execute it on a worker thread. It conflicts a wired >>>> constraint of SELinux which does not allow a multi-threaded process dynamic >>>> domain transition. >>>> >>>> It is a reasonable constraint, if the source and destination domains are >>>> completely independent. Because it shares a local memory, mixed security >>>> context within a process can make a violation in domain separation. >>>> >>>> However, a characteristic relationship between hierarchical domains can >>>> help the situation. It restricts a child domain (like "httpd_t.staff") >>>> cannot over the boundary of its parent ("httpd_t"). It also means >>>> a multi-threaded process cannot over the (parent) domain boundary, >>>> even if a part of threads has different security context. >>>> >>>> My suggestion is the hard-wired constraint should be modified. >>>> It allows to assign threads an indivisual domain, if the new domain >>>> is a child one of the current domain. >>>> >>>> For example: >>>> httpd_t -> httpd_t.staff = allowed >>>> httpd_t -> staff_t = disallowed >>>> httpd_t.staff -> httpd_t.staff.php = allowed >>>> >>>> In other words, this idea requires to change our viewpoint for child domain. >>>> Previously, these are mutually independent domain, but a child domain will >>>> mean a state with restricted operations compared to its parent domain. >>> I tend to agree that we should support such changes in the kernel >>> mechanism and let the use of it be determined by the policy. >>> >>> In terms of hierarchy though, the plan was to replace the current >>> name-based hierarchy with an explicitly declared hierarchy. >> It needs a new policy statement like: >> >> TYPE <child domain> DOMBY <parent domain>; >> or >> TYPEDOMINANCE <parent domain> <child domain> [,<child domain>] ; >> >> I guess these relationships are represented as a ebitmap structure >> in the kernel, and used to mask permissions during AVC entry creation. > > You don't necessarily have to represent the hierarchy in the kernel; it > can be done entirely in the policy toolchain like the current type > hierarchy. I think it is necessary to check whether required dynamic type transition should be allowed, or not. This idea allows a thread to translate its domain, only if the destinated domain is a child one. Thus kernel has to check the relationship between both domains based on the hierarchy information. In other expression: ssid := current domain tsid := new domain if (is_multi_threaded(current)) { + if (!is_dominated_domain(ssid, tsid)) <-- requires hierarchy info return -EPERM; } avc_has_perm(ssid, tsid, SECCLASS_PROCESS, PROCESS__DYNTRANSITION, NULL); >>>> Issues: Domain Reverting >>>> ------------------------ >>>> There is a related issue. To change security context during user request >>>> execution also means that we have to revert its domain after executions, >>>> because Apache repeatedly uses worker thread/process for some reasons. >>>> >>>> I don't have any good idea for this issue yet. >>>> A one time cookie like AppArmor is an approach, but it is basically weak >>>> separation compared from execve() transition. >>> Perhaps it could pre-populate different pools of threads for different >>> client security contexts, and then just draw from the correct pool for >>> each client. Then the thread context doesn't have to be reverted at all >>> and we can make the transition unidirectional. >> I think this approach requires two precondition. >> >> (1) The number of client security context should be enough small. >> If we want to assign one of MCS categories, it requires 1024 of thread >> pool in maximum. > > The main server thread could lazily create the thread pools as needed to > avoid unnecessary pools. And we could possibly use a hybrid scheme > (e.g. one pool per sensitivity or per equivalence class of categories, > reuse within that pool). I guess it requires massive reworks for Apache itself. :( If so, it may be better to implement a SELinux specific multi processing module (MPM) which creates a child process with restricted domain per request? (No need to say, we will get some performance degradation.) >> (2) We have to know what client security context should be assigned >> before the communication between client and backend thread. >> In the case when we use HTTP authentication to decide client security >> context, the authentication process has to be done in the backend >> process after the communication started. >> It may also require major works in application updates. > > True, but not an issue when using labeled networking as the basis for > determining the client security context. In web based services, we have to assume a case when client system is running on windows with no labeled networking support. I think it can be also possible to associate a user and a security context of his substitution on authentication, as operating system doing. >> In my impression, AA's approach is not perfect, but second best one... > > I'd have to hear an argument that there is a real security benefit from > it. Do you just mean reverting domain transitions or do you > specifically mean the cookie-based approach? The cookie-based approach means a process/thread dynamically translated into child domain is allowed to revert its domain into original one, only if the submitted cookie is matched. It can protect from script flaws as long as the cookie cannot be accessed from script program. But it depends on the correctness of script language processor, because the cookie has to be placed within the translated domain. If PHP language processor itself has a bug to expose a discretional value, a malicious one can revert its domain to "httpd_t" from "httpd_t.staff" using the cookie illegally obtained. So, we can find its benefit at preventing harms from buggy scripts. However, this idea is the best one in I could flash on unfortunatelly. Is there any other elegant design? Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [RFC] An idea of thread/child-domain assignment 2008-07-18 6:21 ` KaiGai Kohei @ 2008-07-23 3:58 ` KaiGai Kohei 2008-07-25 12:51 ` [PATCH 0/3] Thread/Child-Domain Assignment KaiGai Kohei 0 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-07-23 3:58 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux KaiGai Kohei wrote: >>>>> An idea: thread/hierarchical-domain assignment Now, under constracting a patch. >>>>> Issues: Domain Reverting - snip - >>> (1) The number of client security context should be enough small. >>> If we want to assign one of MCS categories, it requires 1024 of thread >>> pool in maximum. >> The main server thread could lazily create the thread pools as needed to >> avoid unnecessary pools. And we could possibly use a hybrid scheme >> (e.g. one pool per sensitivity or per equivalence class of categories, >> reuse within that pool). > > I guess it requires massive reworks for Apache itself. :( > > If so, it may be better to implement a SELinux specific multi processing > module (MPM) which creates a child process with restricted domain per > request? > (No need to say, we will get some performance degradation.) I reconsidered that SELinux awared MPM is better way than reverting domain of backend processes/threads. It requires a certain level of performance degrading compared to existing MPMs (prefork/worker), but forking a child process for a single request and existing later model is suitable for SELinux. I have an assumption here that performance is not the first priority for users of SELinux awared Apache. I like to add it to my TODO list. BTW, is there anyone good at the behavior of Tomcat? >From its documentation, Tomcat create a thread for a single request and kills it after processing, when thread pooling is disabled. It seems to me that here is no domain reverting issues. Is it correct? Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 0/3] Thread/Child-Domain Assignment 2008-07-23 3:58 ` KaiGai Kohei @ 2008-07-25 12:51 ` KaiGai Kohei 2008-07-25 13:03 ` [PATCH 1/3] " KaiGai Kohei ` (5 more replies) 0 siblings, 6 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-07-25 12:51 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux The series of patches enable to assign a thread a "weaker" hierarchical domain, even if the process is multithreaded. We can use this feature to implement server application which handles user's request within more restricted domain, to protect harms from potential application bugs. SELinux awared server application can voluntarily minimize the permissions set of its backend thread at the begining of processing request. No need to say, thread shares local memory, so mixed security context within a process can make a violation in domain separation. However, a characteristic relationship between hierarchical domains can help the situation. It restricts a child domain (like "httpd_t.staff") cannot over the boundary of its parent ("httpd_t"). It also means a multi-threaded process cannot over the (parent) domain boundary, even if a part of threads has different security context. [1/3] thread-context-kernel.1.patch This patch enables to assign a thread a "weaker" hierarchical domain, only if the destinated domain is a child of the current domain. Hierachy relationships are defined in the policy version 24. This patch also enables to read the new version of policy. [2/3] thread-context-checkpolicy.1.patch This patch add a new statement of TYPEDOMINATE for policy language. TYPEDOMINATE <parent type> <chile type> [, <child type> ...] ; It defines expilct hierarchical relationship between two types. Existing name based hierarchy is dealt as TYPEDOMINATE is described implicitly. [3/3] thread-context-libsepol.1.patch This patch add support of new policy version of POLICYDB_VERSION_HIERARCHY. It keeps child-parent relationship between two types, and used to make a decision whether required dynamic type transition within multithreaded process can be allowed, or not, in the kernel space. ---- Examples ------------ [kaigai@saba ~]$ gcc thread-context.c -lselinux -lpthread [kaigai@saba ~]$ ./a.out 12864: Now leader is running at unconfined_u:unconfined_r:unconfined_t:SystemLow-SystemHigh 12865: setcon(unconfined_u:unconfined_r:unconfined_red_t:s0) = 0 (Success) 12865: Now I'm running in unconfined_u:unconfined_r:unconfined_red_t 12866: setcon(unconfined_u:unconfined_r:unconfined_blue_t:s0) = 0 (Success) 12866: Now I'm running in unconfined_u:unconfined_r:unconfined_blue_t 12867: setcon(unconfined_u:unconfined_r:unconfined_green_t:s0) = -1 (Operation not permitted) 12868: setcon(unconfined_u:unconfined_r:unconfined_t:s0) = 0 (Success) 12868: Now I'm running in unconfined_u:unconfined_r:unconfined_t [kaigai@saba ~]$ This sample program makes four threads, and they tries to change its domain. The number in leftside shows its thread id. Three of them succeeded to change, but rest of them cannot. The unconfined_red_t and unconfined_blue_t is a hierarchical child domain of unconfined_t and the forth thread (12868) does not change its domain, so they are allowed to have its security context different from others. Please review them, Thanks. -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-25 12:51 ` [PATCH 0/3] Thread/Child-Domain Assignment KaiGai Kohei @ 2008-07-25 13:03 ` KaiGai Kohei 2008-07-25 13:44 ` Stephen Smalley 2008-07-25 13:03 ` [PATCH 2/3] " KaiGai Kohei ` (4 subsequent siblings) 5 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-07-25 13:03 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux [1/3] thread-context-kernel.1.patch This patch enables to assign a thread a "weaker" hierarchical domain, only if the destinated domain is a child of the current domain. Hierachy relationships are defined in the policy version 24. This patch also enables to read the new version of policy. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> ---- security/selinux/hooks.c | 11 +++++- security/selinux/include/security.h | 5 ++- security/selinux/ss/policydb.c | 75 ++++++++++++++++++++++++++++++++-- security/selinux/ss/policydb.h | 2 + security/selinux/ss/services.c | 60 ++++++++++++++++++++++++++++ 5 files changed, 146 insertions(+), 7 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index bc1c3ae..d4c1c5c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5181,7 +5181,12 @@ static int selinux_setprocattr(struct task_struct *p, if (sid == 0) return -EINVAL; - /* Only allow single threaded processes to change context */ + /* + * SELinux allows to change context in the following case only. + * - Single threaded processes. + * - Multi threaded processes intend to change its context into + * lower or same domain in hierarchy relationship. + */ if (atomic_read(&p->mm->mm_users) != 1) { struct task_struct *g, *t; struct mm_struct *mm = p->mm; @@ -5189,11 +5194,15 @@ static int selinux_setprocattr(struct task_struct *p, do_each_thread(g, t) { if (t->mm == mm && t != p) { read_unlock(&tasklist_lock); + + if (!security_check_hierarchy(tsec->sid, sid)) + goto hierarchy_ok; return -EPERM; } } while_each_thread(g, t); read_unlock(&tasklist_lock); } +hierarchy_ok: /* Check permissions for the transition. */ error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index ad30ac4..97fa9bb 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -27,13 +27,14 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_HIERARCHY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_HIERARCHY #endif #define CONTEXT_MNT 0x01 @@ -118,6 +119,8 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, u32 xfrm_sid, u32 *peer_sid); +int security_check_hierarchy(u32 old_sid, u32 new_sid); + int security_get_classes(char ***classes, int *nclasses); int security_get_permissions(char *class, char ***perms, int *nperms); int security_get_reject_unknown(void); diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 84f8cc7..e388f7a 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -116,7 +116,12 @@ static struct policydb_compat_info policydb_compat[] = { .version = POLICYDB_VERSION_PERMISSIVE, .sym_num = SYM_NUM, .ocon_num = OCON_NUM, - } + }, + { + .version = POLICYDB_VERSION_HIERARCHY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -270,9 +275,12 @@ static int type_index(void *key, void *datum, void *datap) p = datap; if (typdatum->primary) { - if (!typdatum->value || typdatum->value > p->p_types.nprim) + if (!typdatum->value + || typdatum->value > p->p_types.nprim + || typdatum->parent > p->p_types.nprim) return -EINVAL; p->p_type_val_to_name[typdatum->value - 1] = key; + p->type_val_to_struct[typdatum->value - 1] = typdatum; } return 0; @@ -397,6 +405,46 @@ static void symtab_hash_eval(struct symtab *s) } #endif +static int type_hierarchy_sanity_checks(struct policydb *p) +{ + struct type_datum *type; + struct ebitmap e; + int rc = 0, i; + + if (p->policyvers < POLICYDB_VERSION_HIERARCHY) + return 0; + + for (i = 0; i < p->p_types.nprim; i++) { + type = p->type_val_to_struct[i]; + + if (!type || !type->parent) + continue; + + ebitmap_init(&e); + while (type) { + if (ebitmap_get_bit(&e, type->value - 1)) { + printk(KERN_ERR "Hierarchy type looped at %s\n", + p->p_type_val_to_name[type->value - 1]); + rc = -EINVAL; + break; + } + + rc = ebitmap_set_bit(&e, type->value - 1, 1); + if (rc) + break; + + if (!type->parent) + break; + + type = p->type_val_to_struct[type->parent - 1]; + } + ebitmap_destroy(&e); + if (rc) + return rc; + } + return 0; +} + /* * Define the other val_to_name and val_to_struct arrays * in a policy database structure. @@ -438,6 +486,14 @@ static int policydb_index_others(struct policydb *p) goto out; } + p->type_val_to_struct = + kzalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)), + GFP_KERNEL); + if (!p->type_val_to_struct) { + rc = -ENOMEM; + goto out; + } + if (cond_init_bool_indexes(p)) { rc = -ENOMEM; goto out; @@ -455,6 +511,7 @@ static int policydb_index_others(struct policydb *p) goto out; } + rc = type_hierarchy_sanity_checks(p); out: return rc; } @@ -625,6 +682,7 @@ void policydb_destroy(struct policydb *p) kfree(p->class_val_to_struct); kfree(p->role_val_to_struct); kfree(p->user_val_to_struct); + kfree(p->type_val_to_struct); avtab_destroy(&p->te_avtab); @@ -1236,8 +1294,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct type_datum *typdatum; - int rc; - __le32 buf[3]; + int to_read, rc; + __le32 buf[4]; u32 len; typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); @@ -1246,13 +1304,20 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) return rc; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers < POLICYDB_VERSION_HIERARCHY) + to_read = sizeof(u32) * 3; + else + to_read = sizeof(u32) * 4; + + rc = next_entry(buf, fp, to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); typdatum->value = le32_to_cpu(buf[1]); typdatum->primary = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_HIERARCHY) + typdatum->parent = le32_to_cpu(buf[3]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 4253370..f3484d4 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -81,6 +81,7 @@ struct role_allow { /* Type attributes */ struct type_datum { u32 value; /* internal type value */ + u32 parent; /* parent type, if hierarchy related */ unsigned char primary; /* primary name? */ }; @@ -209,6 +210,7 @@ struct policydb { struct class_datum **class_val_to_struct; struct role_datum **role_val_to_struct; struct user_datum **user_val_to_struct; + struct type_datum **type_val_to_struct; /* type enforcement access vectors and transitions */ struct avtab te_avtab; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index dcc2e1c..3ecd793 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2160,6 +2160,66 @@ out_slowpath: return rc; } +/* + * security_check_hierarchy + * + * It returns 0, if @old_sid is same or upper type of @new_sid in hierarchy + * relationship directly/indirectly. 1 means @old_sid is not a child type + * of @new_sid, and negative returns are error. + */ +int security_check_hierarchy(u32 old_sid, u32 new_sid) +{ + struct context *old_context, *new_context; + struct type_datum *type; + int index; + int rc = -EINVAL; + + read_lock(&policy_rwlock); + + old_context = sidtab_search(&sidtab, old_sid); + if (!old_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, old_sid); + goto out; + } + + new_context = sidtab_search(&sidtab, new_sid); + if (!new_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, new_sid); + goto out; + } + + /* domain unchanged */ + if (old_context->type == new_context->type) { + rc = 0; + goto out; + } + + /* check domain hierarchy */ + index = new_context->type; + while (true) { + type = policydb.type_val_to_struct[index - 1]; + if (!type) + break; + + /* No hierarchy relationship */ + if (type->parent == 0) { + rc = 1; + break; + } + if (type->parent == old_context->type) { + rc = 0; + break; + } + index = type->parent; + } +out: + read_unlock(&policy_rwlock); + + return rc; +} + static int get_classes_callback(void *k, void *d, void *args) { struct class_datum *datum = d; -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply related [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-25 13:03 ` [PATCH 1/3] " KaiGai Kohei @ 2008-07-25 13:44 ` Stephen Smalley 2008-07-25 17:06 ` Joshua Brindle ` (2 more replies) 0 siblings, 3 replies; 97+ messages in thread From: Stephen Smalley @ 2008-07-25 13:44 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, jmorris, paul.moore, jbrindle, selinux On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: > [1/3] thread-context-kernel.1.patch > This patch enables to assign a thread a "weaker" hierarchical domain, > only if the destinated domain is a child of the current domain. > Hierachy relationships are defined in the policy version 24. > This patch also enables to read the new version of policy. If you are going to take type hierarchy support into the kernel, then it seems like it should be completely taken into the kernel, i.e. the hierarchy checking should be applied by the kernel rather than by the toolchain. That is what the Flask security server did for its extensible policy mechanism. And I think both the neverallow checking and the type hierarchy checking needs to move away from needing to do a full expansion in order to check; it is just too expensive these days. > > > Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> > ---- > security/selinux/hooks.c | 11 +++++- > security/selinux/include/security.h | 5 ++- > security/selinux/ss/policydb.c | 75 ++++++++++++++++++++++++++++++++-- > security/selinux/ss/policydb.h | 2 + > security/selinux/ss/services.c | 60 ++++++++++++++++++++++++++++ > 5 files changed, 146 insertions(+), 7 deletions(-) > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index bc1c3ae..d4c1c5c 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -5181,7 +5181,12 @@ static int selinux_setprocattr(struct task_struct *p, > if (sid == 0) > return -EINVAL; > > - /* Only allow single threaded processes to change context */ > + /* > + * SELinux allows to change context in the following case only. > + * - Single threaded processes. > + * - Multi threaded processes intend to change its context into > + * lower or same domain in hierarchy relationship. > + */ > if (atomic_read(&p->mm->mm_users) != 1) { > struct task_struct *g, *t; > struct mm_struct *mm = p->mm; > @@ -5189,11 +5194,15 @@ static int selinux_setprocattr(struct task_struct *p, > do_each_thread(g, t) { > if (t->mm == mm && t != p) { > read_unlock(&tasklist_lock); > + > + if (!security_check_hierarchy(tsec->sid, sid)) > + goto hierarchy_ok; > return -EPERM; > } > } while_each_thread(g, t); > read_unlock(&tasklist_lock); > } > +hierarchy_ok: > > /* Check permissions for the transition. */ > error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, > diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h > index ad30ac4..97fa9bb 100644 > --- a/security/selinux/include/security.h > +++ b/security/selinux/include/security.h > @@ -27,13 +27,14 @@ > #define POLICYDB_VERSION_RANGETRANS 21 > #define POLICYDB_VERSION_POLCAP 22 > #define POLICYDB_VERSION_PERMISSIVE 23 > +#define POLICYDB_VERSION_HIERARCHY 24 > > /* Range of policy versions we understand*/ > #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE > #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX > #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE > #else > -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE > +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_HIERARCHY > #endif > > #define CONTEXT_MNT 0x01 > @@ -118,6 +119,8 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, > u32 xfrm_sid, > u32 *peer_sid); > > +int security_check_hierarchy(u32 old_sid, u32 new_sid); > + > int security_get_classes(char ***classes, int *nclasses); > int security_get_permissions(char *class, char ***perms, int *nperms); > int security_get_reject_unknown(void); > diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c > index 84f8cc7..e388f7a 100644 > --- a/security/selinux/ss/policydb.c > +++ b/security/selinux/ss/policydb.c > @@ -116,7 +116,12 @@ static struct policydb_compat_info policydb_compat[] = { > .version = POLICYDB_VERSION_PERMISSIVE, > .sym_num = SYM_NUM, > .ocon_num = OCON_NUM, > - } > + }, > + { > + .version = POLICYDB_VERSION_HIERARCHY, > + .sym_num = SYM_NUM, > + .ocon_num = OCON_NUM, > + }, > }; > > static struct policydb_compat_info *policydb_lookup_compat(int version) > @@ -270,9 +275,12 @@ static int type_index(void *key, void *datum, void *datap) > p = datap; > > if (typdatum->primary) { > - if (!typdatum->value || typdatum->value > p->p_types.nprim) > + if (!typdatum->value > + || typdatum->value > p->p_types.nprim > + || typdatum->parent > p->p_types.nprim) > return -EINVAL; > p->p_type_val_to_name[typdatum->value - 1] = key; > + p->type_val_to_struct[typdatum->value - 1] = typdatum; > } > > return 0; > @@ -397,6 +405,46 @@ static void symtab_hash_eval(struct symtab *s) > } > #endif > > +static int type_hierarchy_sanity_checks(struct policydb *p) > +{ > + struct type_datum *type; > + struct ebitmap e; > + int rc = 0, i; > + > + if (p->policyvers < POLICYDB_VERSION_HIERARCHY) > + return 0; > + > + for (i = 0; i < p->p_types.nprim; i++) { > + type = p->type_val_to_struct[i]; > + > + if (!type || !type->parent) > + continue; > + > + ebitmap_init(&e); > + while (type) { > + if (ebitmap_get_bit(&e, type->value - 1)) { > + printk(KERN_ERR "Hierarchy type looped at %s\n", > + p->p_type_val_to_name[type->value - 1]); > + rc = -EINVAL; > + break; > + } > + > + rc = ebitmap_set_bit(&e, type->value - 1, 1); > + if (rc) > + break; > + > + if (!type->parent) > + break; > + > + type = p->type_val_to_struct[type->parent - 1]; > + } > + ebitmap_destroy(&e); > + if (rc) > + return rc; > + } > + return 0; > +} > + > /* > * Define the other val_to_name and val_to_struct arrays > * in a policy database structure. > @@ -438,6 +486,14 @@ static int policydb_index_others(struct policydb *p) > goto out; > } > > + p->type_val_to_struct = > + kzalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)), > + GFP_KERNEL); > + if (!p->type_val_to_struct) { > + rc = -ENOMEM; > + goto out; > + } > + > if (cond_init_bool_indexes(p)) { > rc = -ENOMEM; > goto out; > @@ -455,6 +511,7 @@ static int policydb_index_others(struct policydb *p) > goto out; > } > > + rc = type_hierarchy_sanity_checks(p); > out: > return rc; > } > @@ -625,6 +682,7 @@ void policydb_destroy(struct policydb *p) > kfree(p->class_val_to_struct); > kfree(p->role_val_to_struct); > kfree(p->user_val_to_struct); > + kfree(p->type_val_to_struct); > > avtab_destroy(&p->te_avtab); > > @@ -1236,8 +1294,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) > { > char *key = NULL; > struct type_datum *typdatum; > - int rc; > - __le32 buf[3]; > + int to_read, rc; > + __le32 buf[4]; > u32 len; > > typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); > @@ -1246,13 +1304,20 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) > return rc; > } > > - rc = next_entry(buf, fp, sizeof buf); > + if (p->policyvers < POLICYDB_VERSION_HIERARCHY) > + to_read = sizeof(u32) * 3; > + else > + to_read = sizeof(u32) * 4; > + > + rc = next_entry(buf, fp, to_read); > if (rc < 0) > goto bad; > > len = le32_to_cpu(buf[0]); > typdatum->value = le32_to_cpu(buf[1]); > typdatum->primary = le32_to_cpu(buf[2]); > + if (p->policyvers >= POLICYDB_VERSION_HIERARCHY) > + typdatum->parent = le32_to_cpu(buf[3]); > > key = kmalloc(len + 1, GFP_KERNEL); > if (!key) { > diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h > index 4253370..f3484d4 100644 > --- a/security/selinux/ss/policydb.h > +++ b/security/selinux/ss/policydb.h > @@ -81,6 +81,7 @@ struct role_allow { > /* Type attributes */ > struct type_datum { > u32 value; /* internal type value */ > + u32 parent; /* parent type, if hierarchy related */ > unsigned char primary; /* primary name? */ > }; > > @@ -209,6 +210,7 @@ struct policydb { > struct class_datum **class_val_to_struct; > struct role_datum **role_val_to_struct; > struct user_datum **user_val_to_struct; > + struct type_datum **type_val_to_struct; > > /* type enforcement access vectors and transitions */ > struct avtab te_avtab; > diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c > index dcc2e1c..3ecd793 100644 > --- a/security/selinux/ss/services.c > +++ b/security/selinux/ss/services.c > @@ -2160,6 +2160,66 @@ out_slowpath: > return rc; > } > > +/* > + * security_check_hierarchy > + * > + * It returns 0, if @old_sid is same or upper type of @new_sid in hierarchy > + * relationship directly/indirectly. 1 means @old_sid is not a child type > + * of @new_sid, and negative returns are error. > + */ > +int security_check_hierarchy(u32 old_sid, u32 new_sid) > +{ > + struct context *old_context, *new_context; > + struct type_datum *type; > + int index; > + int rc = -EINVAL; > + > + read_lock(&policy_rwlock); > + > + old_context = sidtab_search(&sidtab, old_sid); > + if (!old_context) { > + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", > + __func__, old_sid); > + goto out; > + } > + > + new_context = sidtab_search(&sidtab, new_sid); > + if (!new_context) { > + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", > + __func__, new_sid); > + goto out; > + } > + > + /* domain unchanged */ > + if (old_context->type == new_context->type) { > + rc = 0; > + goto out; > + } > + > + /* check domain hierarchy */ > + index = new_context->type; > + while (true) { > + type = policydb.type_val_to_struct[index - 1]; > + if (!type) > + break; > + > + /* No hierarchy relationship */ > + if (type->parent == 0) { > + rc = 1; > + break; > + } > + if (type->parent == old_context->type) { > + rc = 0; > + break; > + } > + index = type->parent; > + } > +out: > + read_unlock(&policy_rwlock); > + > + return rc; > +} > + > static int get_classes_callback(void *k, void *d, void *args) > { > struct class_datum *datum = d; > > -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* RE: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-25 13:44 ` Stephen Smalley @ 2008-07-25 17:06 ` Joshua Brindle 2008-07-26 8:24 ` KaiGai Kohei 2008-07-25 17:07 ` Joshua Brindle 2008-07-26 7:55 ` KaiGai Kohei 2 siblings, 1 reply; 97+ messages in thread From: Joshua Brindle @ 2008-07-25 17:06 UTC (permalink / raw) To: Stephen Smalley, KaiGai Kohei Cc: Stephen Smalley, jmorris, paul.moore, selinux Stephen Smalley wrote: > On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: >> [1/3] thread-context-kernel.1.patch >> This patch enables to assign a thread a "weaker" hierarchical >> domain, only if the destinated domain is a child of the current >> domain. Hierachy relationships are defined in the policy version >> 24. This patch also enables to read the new version of policy. > > If you are going to take type hierarchy support into the > kernel, then it seems like it should be completely taken into > the kernel, i.e. the hierarchy checking should be applied by > the kernel rather than by the toolchain. That is what the > Flask security server did for its extensible policy mechanism. > If we are going to do this we also might as well implement the explicit hierarchy support and get away from name based hierarchy. > And I think both the neverallow checking and the type > hierarchy checking needs to move away from needing to do a > full expansion in order to check; it is just too expensive these days. > Do you think it will be faster to do attribute based lookups? It will certainly use significantly less memory. >> >> >> Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> >> ---- >> security/selinux/hooks.c | 11 +++++- >> security/selinux/include/security.h | 5 ++- >> security/selinux/ss/policydb.c | 75 > ++++++++++++++++++++++++++++++++-- >> security/selinux/ss/policydb.h | 2 + >> security/selinux/ss/services.c | 60 >> ++++++++++++++++++++++++++++ 5 files changed, 146 insertions(+), 7 >> deletions(-) >> >> diff --git a/security/selinux/hooks.c > b/security/selinux/hooks.c index >> bc1c3ae..d4c1c5c 100644 >> --- a/security/selinux/hooks.c >> +++ b/security/selinux/hooks.c >> @@ -5181,7 +5181,12 @@ static int > selinux_setprocattr(struct task_struct *p, >> if (sid == 0) >> return -EINVAL; >> >> - /* Only allow single threaded processes to change context */ + /* >> + * SELinux allows to change context in the following case only. >> + * - Single threaded processes. >> + * - Multi threaded processes intend to change its context into >> + * lower or same domain in hierarchy relationship. + */ >> if (atomic_read(&p->mm->mm_users) != 1) { >> struct task_struct *g, *t; >> struct mm_struct *mm = p->mm; >> @@ -5189,11 +5194,15 @@ static int > selinux_setprocattr(struct task_struct *p, >> do_each_thread(g, t) { >> if (t->mm == mm && t != p) { >> read_unlock(&tasklist_lock); >> + >> + if > (!security_check_hierarchy(tsec->sid, sid)) >> + goto hierarchy_ok; >> return -EPERM; >> } >> } while_each_thread(g, t); >> read_unlock(&tasklist_lock); >> } >> +hierarchy_ok: >> >> /* Check permissions for the transition. */ >> error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, diff --git >> a/security/selinux/include/security.h >> b/security/selinux/include/security.h >> index ad30ac4..97fa9bb 100644 >> --- a/security/selinux/include/security.h >> +++ b/security/selinux/include/security.h >> @@ -27,13 +27,14 @@ >> #define POLICYDB_VERSION_RANGETRANS 21 >> #define POLICYDB_VERSION_POLCAP 22 >> #define POLICYDB_VERSION_PERMISSIVE 23 >> +#define POLICYDB_VERSION_HIERARCHY 24 >> >> /* Range of policy versions we understand*/ >> #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE >> #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX >> #define POLICYDB_VERSION_MAX > CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE >> #else >> -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE >> +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_HIERARCHY #endif >> >> #define CONTEXT_MNT 0x01 >> @@ -118,6 +119,8 @@ int security_net_peersid_resolve(u32 nlbl_sid, >> u32 nlbl_type, u32 xfrm_sid, u32 *peer_sid); >> >> +int security_check_hierarchy(u32 old_sid, u32 new_sid); + >> int security_get_classes(char ***classes, int *nclasses); int >> security_get_permissions(char *class, char ***perms, int *nperms); >> int security_get_reject_unknown(void); >> diff --git a/security/selinux/ss/policydb.c >> b/security/selinux/ss/policydb.c index 84f8cc7..e388f7a 100644 >> --- a/security/selinux/ss/policydb.c >> +++ b/security/selinux/ss/policydb.c >> @@ -116,7 +116,12 @@ static struct policydb_compat_info >> policydb_compat[] = { .version = POLICYDB_VERSION_PERMISSIVE, >> .sym_num = SYM_NUM, >> .ocon_num = OCON_NUM, >> - } >> + }, >> + { >> + .version = POLICYDB_VERSION_HIERARCHY, >> + .sym_num = SYM_NUM, >> + .ocon_num = OCON_NUM, >> + }, >> }; >> >> static struct policydb_compat_info *policydb_lookup_compat(int >> version) @@ -270,9 +275,12 @@ static int type_index(void > *key, void *datum, void *datap) >> p = datap; >> >> if (typdatum->primary) { >> - if (!typdatum->value || typdatum->value > p->p_types.nprim) + if >> (!typdatum->value + || typdatum->value > p->p_types.nprim >> + || typdatum->parent > p->p_types.nprim) >> return -EINVAL; >> p->p_type_val_to_name[typdatum->value - 1] = key; >> + p->type_val_to_struct[typdatum->value - 1] = typdatum; } >> >> return 0; >> @@ -397,6 +405,46 @@ static void symtab_hash_eval(struct symtab *s) >> } #endif >> >> +static int type_hierarchy_sanity_checks(struct policydb *p) { >> + struct type_datum *type; + struct ebitmap e; >> + int rc = 0, i; >> + >> + if (p->policyvers < POLICYDB_VERSION_HIERARCHY) >> + return 0; >> + >> + for (i = 0; i < p->p_types.nprim; i++) { >> + type = p->type_val_to_struct[i]; >> + >> + if (!type || !type->parent) >> + continue; >> + >> + ebitmap_init(&e); >> + while (type) { >> + if (ebitmap_get_bit(&e, type->value - 1)) { >> + printk(KERN_ERR "Hierarchy type > looped at %s\n", >> + > p->p_type_val_to_name[type->value - 1]); >> + rc = -EINVAL; >> + break; >> + } >> + >> + rc = ebitmap_set_bit(&e, type->value - 1, 1); >> + if (rc) >> + break; >> + >> + if (!type->parent) >> + break; >> + >> + type = p->type_val_to_struct[type->parent - 1]; + } >> + ebitmap_destroy(&e); >> + if (rc) >> + return rc; >> + } >> + return 0; >> +} >> + >> /* >> * Define the other val_to_name and val_to_struct arrays >> * in a policy database structure. >> @@ -438,6 +486,14 @@ static int > policydb_index_others(struct policydb *p) >> goto out; >> } >> >> + p->type_val_to_struct = >> + kzalloc(p->p_types.nprim * > sizeof(*(p->type_val_to_struct)), >> + GFP_KERNEL); >> + if (!p->type_val_to_struct) { >> + rc = -ENOMEM; >> + goto out; >> + } >> + >> if (cond_init_bool_indexes(p)) { >> rc = -ENOMEM; >> goto out; >> @@ -455,6 +511,7 @@ static int policydb_index_others(struct policydb >> *p) goto out; } >> >> + rc = type_hierarchy_sanity_checks(p); >> out: >> return rc; >> } >> @@ -625,6 +682,7 @@ void policydb_destroy(struct policydb *p) >> kfree(p->class_val_to_struct); >> kfree(p->role_val_to_struct); >> kfree(p->user_val_to_struct); >> + kfree(p->type_val_to_struct); >> >> avtab_destroy(&p->te_avtab); >> >> @@ -1236,8 +1294,8 @@ static int type_read(struct policydb *p, >> struct hashtab *h, void *fp) { char *key = NULL; >> struct type_datum *typdatum; >> - int rc; >> - __le32 buf[3]; >> + int to_read, rc; >> + __le32 buf[4]; >> u32 len; >> >> typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); @@ -1246,13 >> +1304,20 @@ static int type_read(struct policydb *p, struct hashtab >> *h, void *fp) return rc; } >> >> - rc = next_entry(buf, fp, sizeof buf); >> + if (p->policyvers < POLICYDB_VERSION_HIERARCHY) >> + to_read = sizeof(u32) * 3; >> + else >> + to_read = sizeof(u32) * 4; >> + >> + rc = next_entry(buf, fp, to_read); >> if (rc < 0) >> goto bad; >> >> len = le32_to_cpu(buf[0]); >> typdatum->value = le32_to_cpu(buf[1]); >> typdatum->primary = le32_to_cpu(buf[2]); >> + if (p->policyvers >= POLICYDB_VERSION_HIERARCHY) >> + typdatum->parent = le32_to_cpu(buf[3]); >> >> key = kmalloc(len + 1, GFP_KERNEL); >> if (!key) { >> diff --git a/security/selinux/ss/policydb.h >> b/security/selinux/ss/policydb.h index 4253370..f3484d4 100644 >> --- a/security/selinux/ss/policydb.h >> +++ b/security/selinux/ss/policydb.h >> @@ -81,6 +81,7 @@ struct role_allow { >> /* Type attributes */ >> struct type_datum { >> u32 value; /* internal type value */ >> + u32 parent; /* parent type, if hierarchy related */ >> unsigned char primary; /* primary name? */ >> }; >> >> @@ -209,6 +210,7 @@ struct policydb { >> struct class_datum **class_val_to_struct; >> struct role_datum **role_val_to_struct; >> struct user_datum **user_val_to_struct; >> + struct type_datum **type_val_to_struct; >> >> /* type enforcement access vectors and transitions */ struct >> avtab te_avtab; diff --git a/security/selinux/ss/services.c >> b/security/selinux/ss/services.c index dcc2e1c..3ecd793 100644 >> --- a/security/selinux/ss/services.c >> +++ b/security/selinux/ss/services.c >> @@ -2160,6 +2160,66 @@ out_slowpath: >> return rc; >> } >> >> +/* >> + * security_check_hierarchy >> + * >> + * It returns 0, if @old_sid is same or upper type of @new_sid in >> +hierarchy + * relationship directly/indirectly. 1 means @old_sid is >> not a child +type + * of @new_sid, and negative returns are error. >> + */ >> +int security_check_hierarchy(u32 old_sid, u32 new_sid) { >> + struct context *old_context, *new_context; >> + struct type_datum *type; >> + int index; >> + int rc = -EINVAL; >> + >> + read_lock(&policy_rwlock); >> + >> + old_context = sidtab_search(&sidtab, old_sid); >> + if (!old_context) { >> + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + >> __func__, old_sid); + goto out; >> + } >> + >> + new_context = sidtab_search(&sidtab, new_sid); >> + if (!new_context) { >> + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + >> __func__, new_sid); + goto out; >> + } >> + >> + /* domain unchanged */ >> + if (old_context->type == new_context->type) { >> + rc = 0; >> + goto out; >> + } >> + >> + /* check domain hierarchy */ >> + index = new_context->type; >> + while (true) { >> + type = policydb.type_val_to_struct[index - 1]; >> + if (!type) >> + break; >> + >> + /* No hierarchy relationship */ >> + if (type->parent == 0) { >> + rc = 1; >> + break; >> + } >> + if (type->parent == old_context->type) { >> + rc = 0; >> + break; >> + } >> + index = type->parent; >> + } >> +out: >> + read_unlock(&policy_rwlock); >> + >> + return rc; >> +} >> + >> static int get_classes_callback(void *k, void *d, void *args) { >> struct class_datum *datum = d; -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-25 17:06 ` Joshua Brindle @ 2008-07-26 8:24 ` KaiGai Kohei 0 siblings, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-07-26 8:24 UTC (permalink / raw) To: Joshua Brindle Cc: Stephen Smalley, KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux Joshua Brindle wrote: > Stephen Smalley wrote: >> On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: >>> [1/3] thread-context-kernel.1.patch >>> This patch enables to assign a thread a "weaker" hierarchical >>> domain, only if the destinated domain is a child of the current >>> domain. Hierachy relationships are defined in the policy version >>> 24. This patch also enables to read the new version of policy. >> If you are going to take type hierarchy support into the >> kernel, then it seems like it should be completely taken into >> the kernel, i.e. the hierarchy checking should be applied by >> the kernel rather than by the toolchain. That is what the >> Flask security server did for its extensible policy mechanism. >> > > If we are going to do this we also might as well implement the explicit > hierarchy support and get away from name based hierarchy. > >> And I think both the neverallow checking and the type >> hierarchy checking needs to move away from needing to do a >> full expansion in order to check; it is just too expensive these days. >> > > Do you think it will be faster to do attribute based lookups? It will > certainly use significantly less memory. What do you means the attribute based lookups? In hierarchy checks, we should not expand any attribute of type without applying them to its parent type. We can check it by comparing type_attr_map of both types, without additional memory consumption. In never allow checks, all we have to do is adding a "else if" block to check in context_struct_compute_av(), like: ------------ ebitmap_for_each_positive_bit(sattr, snode, i) { ebitmap_for_each_positive_bit(tattr, tnode, j) { avkey.source_type = i + 1; avkey.target_type = j + 1; for (node = avtab_search_node(&policydb.te_avtab, &avkey); node != NULL; node = avtab_search_node_next(node, avkey.specified)) { if (node->key.specified == AVTAB_ALLOWED) avd->allowed |= node->datum.data; else if (node->key.specified == AVTAB_AUDITALLOW) avd->auditallow |= node->datum.data; else if (node->key.specified == AVTAB_AUDITDENY) avd->auditdeny &= node->datum.data; + else if (node->key.specified == AVTAB_NEVERALLOW) + avd->allowed &= ~node->datum.data; + /* TODO: print logs to notice masked perms */ } /* Check conditional av table for additional permissions */ cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); } } ------------ Thanks, -- KaiGai Kohei <kaigai@kaigai.gr.jp> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* RE: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-25 13:44 ` Stephen Smalley 2008-07-25 17:06 ` Joshua Brindle @ 2008-07-25 17:07 ` Joshua Brindle 2008-07-26 7:55 ` KaiGai Kohei 2 siblings, 0 replies; 97+ messages in thread From: Joshua Brindle @ 2008-07-25 17:07 UTC (permalink / raw) To: Joshua Brindle, Stephen Smalley, KaiGai Kohei Cc: Stephen Smalley, jmorris, paul.moore, selinux Joshua Brindle wrote: > Stephen Smalley wrote: >> On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: >>> [1/3] thread-context-kernel.1.patch >>> This patch enables to assign a thread a "weaker" hierarchical >>> domain, only if the destinated domain is a child of the current >>> domain. Hierachy relationships are defined in the policy version >>> 24. This patch also enables to read the new version of policy. >> >> If you are going to take type hierarchy support into the kernel, then >> it seems like it should be completely taken into the kernel, i.e. the >> hierarchy checking should be applied by the kernel rather than by the >> toolchain. That is what the Flask security server did for its >> extensible policy mechanism. >> > > If we are going to do this we also might as well implement > the explicit hierarchy support and get away from name based hierarchy. > Oops, I missed the patch that does this, disregard. >> And I think both the neverallow checking and the type hierarchy >> checking needs to move away from needing to do a full expansion in >> order to check; it is just too expensive these days. >> > > Do you think it will be faster to do attribute based lookups? > It will certainly use significantly less memory. > >>> >>> >>> Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> >>> ---- >>> security/selinux/hooks.c | 11 +++++- >>> security/selinux/include/security.h | 5 ++- >>> security/selinux/ss/policydb.c | 75 >> ++++++++++++++++++++++++++++++++-- >>> security/selinux/ss/policydb.h | 2 + >>> security/selinux/ss/services.c | 60 >>> ++++++++++++++++++++++++++++ 5 files changed, 146 insertions(+), 7 >>> deletions(-) >>> >>> diff --git a/security/selinux/hooks.c >> b/security/selinux/hooks.c index >>> bc1c3ae..d4c1c5c 100644 >>> --- a/security/selinux/hooks.c >>> +++ b/security/selinux/hooks.c >>> @@ -5181,7 +5181,12 @@ static int >> selinux_setprocattr(struct task_struct *p, >>> if (sid == 0) >>> return -EINVAL; >>> >>> - /* Only allow single threaded processes to change context */ >>> + /* + * SELinux allows to change context in the following case >>> only. + * - Single threaded processes. >>> + * - Multi threaded processes intend to change its context into >>> + * lower or same domain in hierarchy relationship. + */ >>> if (atomic_read(&p->mm->mm_users) != 1) { >>> struct task_struct *g, *t; >>> struct mm_struct *mm = p->mm; >>> @@ -5189,11 +5194,15 @@ static int >> selinux_setprocattr(struct task_struct *p, >>> do_each_thread(g, t) { >>> if (t->mm == mm && t != p) { >>> read_unlock(&tasklist_lock); >>> + >>> + if >> (!security_check_hierarchy(tsec->sid, sid)) >>> + goto hierarchy_ok; >>> return -EPERM; >>> } >>> } while_each_thread(g, t); >>> read_unlock(&tasklist_lock); >>> } >>> +hierarchy_ok: >>> >>> /* Check permissions for the transition. */ >>> error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, diff --git >>> a/security/selinux/include/security.h >>> b/security/selinux/include/security.h >>> index ad30ac4..97fa9bb 100644 >>> --- a/security/selinux/include/security.h >>> +++ b/security/selinux/include/security.h >>> @@ -27,13 +27,14 @@ >>> #define POLICYDB_VERSION_RANGETRANS 21 >>> #define POLICYDB_VERSION_POLCAP 22 >>> #define POLICYDB_VERSION_PERMISSIVE 23 >>> +#define POLICYDB_VERSION_HIERARCHY 24 >>> >>> /* Range of policy versions we understand*/ >>> #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE >>> #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX >>> #define POLICYDB_VERSION_MAX >> CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE >>> #else >>> -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE >>> +#define POLICYDB_VERSION_MAX > POLICYDB_VERSION_HIERARCHY #endif >>> >>> #define CONTEXT_MNT 0x01 >>> @@ -118,6 +119,8 @@ int security_net_peersid_resolve(u32 nlbl_sid, >>> u32 nlbl_type, > u32 xfrm_sid, u32 *peer_sid); >>> >>> +int security_check_hierarchy(u32 old_sid, u32 new_sid); + >>> int security_get_classes(char ***classes, int *nclasses); int >>> security_get_permissions(char *class, char ***perms, int *nperms); >>> int security_get_reject_unknown(void); >>> diff --git a/security/selinux/ss/policydb.c >>> b/security/selinux/ss/policydb.c index 84f8cc7..e388f7a 100644 >>> --- a/security/selinux/ss/policydb.c >>> +++ b/security/selinux/ss/policydb.c >>> @@ -116,7 +116,12 @@ static struct policydb_compat_info >>> policydb_compat[] = { .version = POLICYDB_VERSION_PERMISSIVE, >>> .sym_num = SYM_NUM, .ocon_num = OCON_NUM, >>> - } >>> + }, >>> + { >>> + .version = POLICYDB_VERSION_HIERARCHY, >>> + .sym_num = SYM_NUM, >>> + .ocon_num = OCON_NUM, >>> + }, >>> }; >>> >>> static struct policydb_compat_info *policydb_lookup_compat(int >>> version) @@ -270,9 +275,12 @@ static int type_index(void >> *key, void *datum, void *datap) >>> p = datap; >>> >>> if (typdatum->primary) { >>> - if (!typdatum->value || typdatum->value > p->p_types.nprim) + if >>> (!typdatum->value + || typdatum->value p->p_types.nprim >>> + || typdatum->parent > p->p_types.nprim) >>> return -EINVAL; >>> p->p_type_val_to_name[typdatum->value - 1] = key; >>> + p->type_val_to_struct[typdatum->value - 1] = typdatum; } >>> >>> return 0; >>> @@ -397,6 +405,46 @@ static void symtab_hash_eval(struct symtab *s) >>> } #endif >>> >>> +static int type_hierarchy_sanity_checks(struct policydb *p) { >>> + struct type_datum *type; + struct ebitmap e; >>> + int rc = 0, i; >>> + >>> + if (p->policyvers < POLICYDB_VERSION_HIERARCHY) >>> + return 0; >>> + >>> + for (i = 0; i < p->p_types.nprim; i++) { >>> + type = p->type_val_to_struct[i]; >>> + >>> + if (!type || !type->parent) >>> + continue; >>> + >>> + ebitmap_init(&e); >>> + while (type) { >>> + if (ebitmap_get_bit(&e, type->value - 1)) { >>> + printk(KERN_ERR "Hierarchy type >> looped at %s\n", >>> + >> p->p_type_val_to_name[type->value - 1]); >>> + rc = -EINVAL; >>> + break; >>> + } >>> + >>> + rc = ebitmap_set_bit(&e, type->value - 1, 1); >>> + if (rc) >>> + break; >>> + >>> + if (!type->parent) >>> + break; >>> + >>> + type = > p->type_val_to_struct[type->parent - 1]; + } >>> + ebitmap_destroy(&e); >>> + if (rc) >>> + return rc; >>> + } >>> + return 0; >>> +} >>> + >>> /* >>> * Define the other val_to_name and val_to_struct arrays >>> * in a policy database structure. >>> @@ -438,6 +486,14 @@ static int >> policydb_index_others(struct policydb *p) >>> goto out; >>> } >>> >>> + p->type_val_to_struct = >>> + kzalloc(p->p_types.nprim * >> sizeof(*(p->type_val_to_struct)), >>> + GFP_KERNEL); >>> + if (!p->type_val_to_struct) { >>> + rc = -ENOMEM; >>> + goto out; >>> + } >>> + >>> if (cond_init_bool_indexes(p)) { >>> rc = -ENOMEM; >>> goto out; >>> @@ -455,6 +511,7 @@ static int > policydb_index_others(struct policydb >>> *p) goto out; } >>> >>> + rc = type_hierarchy_sanity_checks(p); >>> out: >>> return rc; >>> } >>> @@ -625,6 +682,7 @@ void policydb_destroy(struct policydb *p) >>> kfree(p->class_val_to_struct); >>> kfree(p->role_val_to_struct); >>> kfree(p->user_val_to_struct); >>> + kfree(p->type_val_to_struct); >>> >>> avtab_destroy(&p->te_avtab); >>> >>> @@ -1236,8 +1294,8 @@ static int type_read(struct policydb *p, >>> struct hashtab *h, void *fp) { char *key = NULL; >>> struct type_datum *typdatum; >>> - int rc; >>> - __le32 buf[3]; >>> + int to_read, rc; >>> + __le32 buf[4]; >>> u32 len; >>> >>> typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); @@ -1246,13 >>> +1304,20 @@ static int type_read(struct policydb *p, struct hashtab >>> *h, void *fp) return rc; } >>> >>> - rc = next_entry(buf, fp, sizeof buf); >>> + if (p->policyvers < POLICYDB_VERSION_HIERARCHY) >>> + to_read = sizeof(u32) * 3; >>> + else >>> + to_read = sizeof(u32) * 4; >>> + >>> + rc = next_entry(buf, fp, to_read); >>> if (rc < 0) >>> goto bad; >>> >>> len = le32_to_cpu(buf[0]); >>> typdatum->value = le32_to_cpu(buf[1]); >>> typdatum->primary = le32_to_cpu(buf[2]); >>> + if (p->policyvers >= POLICYDB_VERSION_HIERARCHY) >>> + typdatum->parent = le32_to_cpu(buf[3]); >>> >>> key = kmalloc(len + 1, GFP_KERNEL); >>> if (!key) { >>> diff --git a/security/selinux/ss/policydb.h >>> b/security/selinux/ss/policydb.h index 4253370..f3484d4 100644 >>> --- a/security/selinux/ss/policydb.h >>> +++ b/security/selinux/ss/policydb.h >>> @@ -81,6 +81,7 @@ struct role_allow { >>> /* Type attributes */ >>> struct type_datum { >>> u32 value; /* internal type value */ >>> + u32 parent; /* parent type, if hierarchy related */ >>> unsigned char primary; /* primary name? */ >>> }; >>> >>> @@ -209,6 +210,7 @@ struct policydb { >>> struct class_datum **class_val_to_struct; >>> struct role_datum **role_val_to_struct; >>> struct user_datum **user_val_to_struct; >>> + struct type_datum **type_val_to_struct; >>> >>> /* type enforcement access vectors and transitions */ struct >>> avtab te_avtab; diff --git a/security/selinux/ss/services.c >>> b/security/selinux/ss/services.c index dcc2e1c..3ecd793 100644 >>> --- a/security/selinux/ss/services.c >>> +++ b/security/selinux/ss/services.c >>> @@ -2160,6 +2160,66 @@ out_slowpath: >>> return rc; >>> } >>> >>> +/* >>> + * security_check_hierarchy >>> + * >>> + * It returns 0, if @old_sid is same or upper type of @new_sid in >>> +hierarchy + * relationship directly/indirectly. 1 means @old_sid is >>> not a child +type + * of @new_sid, and negative returns are error. >>> + */ +int security_check_hierarchy(u32 old_sid, u32 new_sid) { >>> + struct context *old_context, *new_context; >>> + struct type_datum *type; >>> + int index; >>> + int rc = -EINVAL; >>> + >>> + read_lock(&policy_rwlock); >>> + >>> + old_context = sidtab_search(&sidtab, old_sid); >>> + if (!old_context) { >>> + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + >>> __func__, old_sid); + goto out; >>> + } >>> + >>> + new_context = sidtab_search(&sidtab, new_sid); >>> + if (!new_context) { >>> + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + >>> __func__, new_sid); + goto out; >>> + } >>> + >>> + /* domain unchanged */ >>> + if (old_context->type == new_context->type) { >>> + rc = 0; >>> + goto out; >>> + } >>> + >>> + /* check domain hierarchy */ >>> + index = new_context->type; >>> + while (true) { >>> + type = policydb.type_val_to_struct[index - 1]; >>> + if (!type) >>> + break; >>> + >>> + /* No hierarchy relationship */ >>> + if (type->parent == 0) { >>> + rc = 1; >>> + break; >>> + } >>> + if (type->parent == old_context->type) { >>> + rc = 0; >>> + break; >>> + } >>> + index = type->parent; >>> + } >>> +out: >>> + read_unlock(&policy_rwlock); >>> + >>> + return rc; >>> +} >>> + >>> static int get_classes_callback(void *k, void *d, void *args) { >>> struct class_datum *datum = d; -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-25 13:44 ` Stephen Smalley 2008-07-25 17:06 ` Joshua Brindle 2008-07-25 17:07 ` Joshua Brindle @ 2008-07-26 7:55 ` KaiGai Kohei 2008-07-26 17:28 ` Stephen Smalley 2 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-07-26 7:55 UTC (permalink / raw) To: Stephen Smalley Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, jbrindle, selinux Stephen Smalley wrote: > On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: >> [1/3] thread-context-kernel.1.patch >> This patch enables to assign a thread a "weaker" hierarchical domain, >> only if the destinated domain is a child of the current domain. >> Hierachy relationships are defined in the policy version 24. >> This patch also enables to read the new version of policy. > > If you are going to take type hierarchy support into the kernel, then it > seems like it should be completely taken into the kernel, i.e. the > hierarchy checking should be applied by the kernel rather than by the > toolchain. That is what the Flask security server did for its > extensible policy mechanism. When the checks are applied in the kernel? One candidate is policy loading time, and the other is AVC entry creation time. I prefers the later one, because checks in policy loading time will require the expensive checks in kernel space. In my idea, violated permission bits on AVC entries are masked by ones of its parent types, with/without printing logs. Now hierarchy/neverallow constraint makes abort whole of policy loading. It can be a significant difference. > And I think both the neverallow checking and the type hierarchy checking > needs to move away from needing to do a full expansion in order to > check; it is just too expensive these days. I agree. expand-check=1 makes slow downed so much. :( It should be done on-demand. Thanks, -- KaiGai Kohei <kaigai@kaigai.gr.jp> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-26 7:55 ` KaiGai Kohei @ 2008-07-26 17:28 ` Stephen Smalley 2008-07-26 18:14 ` Joshua Brindle 0 siblings, 1 reply; 97+ messages in thread From: Stephen Smalley @ 2008-07-26 17:28 UTC (permalink / raw) To: KaiGai Kohei Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, jbrindle, selinux On Sat, 2008-07-26 at 16:55 +0900, KaiGai Kohei wrote: > Stephen Smalley wrote: > > On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: > >> [1/3] thread-context-kernel.1.patch > >> This patch enables to assign a thread a "weaker" hierarchical domain, > >> only if the destinated domain is a child of the current domain. > >> Hierachy relationships are defined in the policy version 24. > >> This patch also enables to read the new version of policy. > > > > If you are going to take type hierarchy support into the kernel, then it > > seems like it should be completely taken into the kernel, i.e. the > > hierarchy checking should be applied by the kernel rather than by the > > toolchain. That is what the Flask security server did for its > > extensible policy mechanism. > > When the checks are applied in the kernel? > One candidate is policy loading time, and the other is AVC entry creation > time. I prefers the later one, because checks in policy loading time will > require the expensive checks in kernel space. > > In my idea, violated permission bits on AVC entries are masked by ones of > its parent types, with/without printing logs. > Now hierarchy/neverallow constraint makes abort whole of policy loading. > It can be a significant difference. Yes, it makes sense to apply the hierarchy restrictions at computation time. Neverallows are a bit different though - we want those to flag bugs in policy. Thus, we likely should leave neverallow checking in the userland toolchain. > > > And I think both the neverallow checking and the type hierarchy checking > > needs to move away from needing to do a full expansion in order to > > check; it is just too expensive these days. > > I agree. expand-check=1 makes slow downed so much. :( > It should be done on-demand. > > Thanks, -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-26 17:28 ` Stephen Smalley @ 2008-07-26 18:14 ` Joshua Brindle 2008-07-28 3:06 ` KaiGai Kohei 2008-07-28 17:31 ` Stephen Smalley 0 siblings, 2 replies; 97+ messages in thread From: Joshua Brindle @ 2008-07-26 18:14 UTC (permalink / raw) To: Stephen Smalley Cc: KaiGai Kohei, KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux Stephen Smalley wrote: > On Sat, 2008-07-26 at 16:55 +0900, KaiGai Kohei wrote: >> Stephen Smalley wrote: >>> On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: >>>> [1/3] thread-context-kernel.1.patch >>>> This patch enables to assign a thread a "weaker" hierarchical domain, >>>> only if the destinated domain is a child of the current domain. >>>> Hierachy relationships are defined in the policy version 24. >>>> This patch also enables to read the new version of policy. >>> If you are going to take type hierarchy support into the kernel, then it >>> seems like it should be completely taken into the kernel, i.e. the >>> hierarchy checking should be applied by the kernel rather than by the >>> toolchain. That is what the Flask security server did for its >>> extensible policy mechanism. >> When the checks are applied in the kernel? >> One candidate is policy loading time, and the other is AVC entry creation >> time. I prefers the later one, because checks in policy loading time will >> require the expensive checks in kernel space. >> >> In my idea, violated permission bits on AVC entries are masked by ones of >> its parent types, with/without printing logs. >> Now hierarchy/neverallow constraint makes abort whole of policy loading. >> It can be a significant difference. > > Yes, it makes sense to apply the hierarchy restrictions at computation > time. Neverallows are a bit different though - we want those to flag > bugs in policy. Thus, we likely should leave neverallow checking in the > userland toolchain. > So you want to deny at runtime based on the hierarchy? Won't that create another confusing way SELinux can deny something without any clue why (aside from running the denial through audit2why)? Further, this is something we can understand and reject ahead of time, I don't know why it makes sense to wait until later when we can tell now that something is wrong with the policy. -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-26 18:14 ` Joshua Brindle @ 2008-07-28 3:06 ` KaiGai Kohei 2008-07-28 17:31 ` Stephen Smalley 1 sibling, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-07-28 3:06 UTC (permalink / raw) To: Joshua Brindle Cc: Stephen Smalley, KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux Joshua Brindle wrote: > Stephen Smalley wrote: >> On Sat, 2008-07-26 at 16:55 +0900, KaiGai Kohei wrote: >>> Stephen Smalley wrote: >>>> On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: >>>>> [1/3] thread-context-kernel.1.patch >>>>> This patch enables to assign a thread a "weaker" hierarchical domain, >>>>> only if the destinated domain is a child of the current domain. >>>>> Hierachy relationships are defined in the policy version 24. >>>>> This patch also enables to read the new version of policy. >>>> If you are going to take type hierarchy support into the kernel, then it >>>> seems like it should be completely taken into the kernel, i.e. the >>>> hierarchy checking should be applied by the kernel rather than by the >>>> toolchain. That is what the Flask security server did for its >>>> extensible policy mechanism. >>> When the checks are applied in the kernel? >>> One candidate is policy loading time, and the other is AVC entry creation >>> time. I prefers the later one, because checks in policy loading time will >>> require the expensive checks in kernel space. >>> >>> In my idea, violated permission bits on AVC entries are masked by ones of >>> its parent types, with/without printing logs. >>> Now hierarchy/neverallow constraint makes abort whole of policy loading. >>> It can be a significant difference. >> Yes, it makes sense to apply the hierarchy restrictions at computation >> time. Neverallows are a bit different though - we want those to flag >> bugs in policy. Thus, we likely should leave neverallow checking in the >> userland toolchain. OK, it seems to me a reasonable approach that the userland toolchain also checks neverallow constraints. What is a proper way to notice violations in neverallow constraint? I think audit messages are candidate. When user receives violation messages, he can check his policy with expand-check=1 for more detailed infomation. > So you want to deny at runtime based on the hierarchy? Won't that create > another confusing way SELinux can deny something without any clue why > (aside from running the denial through audit2why)? Yes, I think the hierarchy constraints are applied on the creation of AVC entries at runtime. If violated, masked permissions and attributes should be noticed to userspace via audit messages. > Further, this is something we can understand and reject ahead of time, I don't > know why it makes sense to wait until later when we can tell now that something > is wrong with the policy. We can also apply these checks at policy module linking time, if configured. However, it is currently skipped in the default due to its expensiveness, isn't it? Could you consider the runtime checks give us a hint/change to enabls checks in the userland toolchain for more detailed information? Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-26 18:14 ` Joshua Brindle 2008-07-28 3:06 ` KaiGai Kohei @ 2008-07-28 17:31 ` Stephen Smalley 2008-07-29 6:51 ` KaiGai Kohei 1 sibling, 1 reply; 97+ messages in thread From: Stephen Smalley @ 2008-07-28 17:31 UTC (permalink / raw) To: Joshua Brindle Cc: Stephen Smalley, KaiGai Kohei, KaiGai Kohei, jmorris, paul.moore, selinux On Sat, 2008-07-26 at 14:14 -0400, Joshua Brindle wrote: > Stephen Smalley wrote: > > On Sat, 2008-07-26 at 16:55 +0900, KaiGai Kohei wrote: > >> Stephen Smalley wrote: > >>> On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: > >>>> [1/3] thread-context-kernel.1.patch > >>>> This patch enables to assign a thread a "weaker" hierarchical domain, > >>>> only if the destinated domain is a child of the current domain. > >>>> Hierachy relationships are defined in the policy version 24. > >>>> This patch also enables to read the new version of policy. > >>> If you are going to take type hierarchy support into the kernel, then it > >>> seems like it should be completely taken into the kernel, i.e. the > >>> hierarchy checking should be applied by the kernel rather than by the > >>> toolchain. That is what the Flask security server did for its > >>> extensible policy mechanism. > >> When the checks are applied in the kernel? > >> One candidate is policy loading time, and the other is AVC entry creation > >> time. I prefers the later one, because checks in policy loading time will > >> require the expensive checks in kernel space. > >> > >> In my idea, violated permission bits on AVC entries are masked by ones of > >> its parent types, with/without printing logs. > >> Now hierarchy/neverallow constraint makes abort whole of policy loading. > >> It can be a significant difference. > > > > Yes, it makes sense to apply the hierarchy restrictions at computation > > time. Neverallows are a bit different though - we want those to flag > > bugs in policy. Thus, we likely should leave neverallow checking in the > > userland toolchain. > > > > So you want to deny at runtime based on the hierarchy? Won't that > create another confusing way SELinux can deny something without any > clue why (aside from running the denial through audit2why)? Further, > this is something we can understand and reject ahead of time, I don't > know why it makes sense to wait until later when we can tell now that > something is wrong with the policy. True. On the other hand, if we are going to make the hierarchy visible to the kernel so that it can directly leverage information from it, then it makes sense to let the kernel directly enforce the relationships, whether that happens at policy load time or at compute_av time. If we can make the checking happen entirely at policy load time in a manner that isn't too expensive, then that is cleaner, I agree. If not, then lazy dynamic checking by the kernel and optional full static checking by the toolchain may be a compromise. -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-28 17:31 ` Stephen Smalley @ 2008-07-29 6:51 ` KaiGai Kohei 2008-07-29 12:06 ` Stephen Smalley 0 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-07-29 6:51 UTC (permalink / raw) To: Stephen Smalley Cc: Joshua Brindle, Stephen Smalley, KaiGai Kohei, jmorris, paul.moore, selinux Stephen Smalley wrote: > On Sat, 2008-07-26 at 14:14 -0400, Joshua Brindle wrote: >> Stephen Smalley wrote: >>> On Sat, 2008-07-26 at 16:55 +0900, KaiGai Kohei wrote: >>>> Stephen Smalley wrote: >>>>> On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: >>>>>> [1/3] thread-context-kernel.1.patch >>>>>> This patch enables to assign a thread a "weaker" hierarchical domain, >>>>>> only if the destinated domain is a child of the current domain. >>>>>> Hierachy relationships are defined in the policy version 24. >>>>>> This patch also enables to read the new version of policy. >>>>> If you are going to take type hierarchy support into the kernel, then it >>>>> seems like it should be completely taken into the kernel, i.e. the >>>>> hierarchy checking should be applied by the kernel rather than by the >>>>> toolchain. That is what the Flask security server did for its >>>>> extensible policy mechanism. >>>> When the checks are applied in the kernel? >>>> One candidate is policy loading time, and the other is AVC entry creation >>>> time. I prefers the later one, because checks in policy loading time will >>>> require the expensive checks in kernel space. >>>> >>>> In my idea, violated permission bits on AVC entries are masked by ones of >>>> its parent types, with/without printing logs. >>>> Now hierarchy/neverallow constraint makes abort whole of policy loading. >>>> It can be a significant difference. >>> Yes, it makes sense to apply the hierarchy restrictions at computation >>> time. Neverallows are a bit different though - we want those to flag >>> bugs in policy. Thus, we likely should leave neverallow checking in the >>> userland toolchain. >>> >> So you want to deny at runtime based on the hierarchy? Won't that >> create another confusing way SELinux can deny something without any >> clue why (aside from running the denial through audit2why)? Further, >> this is something we can understand and reject ahead of time, I don't >> know why it makes sense to wait until later when we can tell now that >> something is wrong with the policy. > > True. On the other hand, if we are going to make the hierarchy visible > to the kernel so that it can directly leverage information from it, then > it makes sense to let the kernel directly enforce the relationships, > whether that happens at policy load time or at compute_av time. > > If we can make the checking happen entirely at policy load time in a > manner that isn't too expensive, then that is cleaner, I agree. If not, > then lazy dynamic checking by the kernel and optional full static > checking by the toolchain may be a compromise. I guess we can reduce cost to check hierarchy constraint, because all we have to do is to scan/check avtabs of related types and ebitmaps of type_attr_maps. However, I don't have a good idea to boost neverallow constraint so much due to its flexibility. :( I have measured a performance to check hierarchy/neverallow constraint with patched libsepol as a trial. Unfortunatelly, about 80% of CPU time is consumed by check_assertions() for neverallows. [root@saba ~]# time -p semodule -r kaigai hierarchy_check_constraints: 37.32 [s] check_assertions: 139.05 [s] real 205.32 user 198.06 sys 3.75 These checks at policy load time gives us negative effect in system bootup time, unless remarkable improvements. So, I prefer lazy dynamic checking approach. (*) System bootup time is one of the major topics for embedded Linux folks. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-29 6:51 ` KaiGai Kohei @ 2008-07-29 12:06 ` Stephen Smalley 2008-07-30 14:10 ` Joshua Brindle 2008-08-01 6:26 ` KaiGai Kohei 0 siblings, 2 replies; 97+ messages in thread From: Stephen Smalley @ 2008-07-29 12:06 UTC (permalink / raw) To: KaiGai Kohei Cc: Joshua Brindle, Stephen Smalley, KaiGai Kohei, jmorris, paul.moore, selinux On Tue, 2008-07-29 at 15:51 +0900, KaiGai Kohei wrote: > Stephen Smalley wrote: > > On Sat, 2008-07-26 at 14:14 -0400, Joshua Brindle wrote: > >> Stephen Smalley wrote: > >>> On Sat, 2008-07-26 at 16:55 +0900, KaiGai Kohei wrote: > >>>> Stephen Smalley wrote: > >>>>> On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: > >>>>>> [1/3] thread-context-kernel.1.patch > >>>>>> This patch enables to assign a thread a "weaker" hierarchical domain, > >>>>>> only if the destinated domain is a child of the current domain. > >>>>>> Hierachy relationships are defined in the policy version 24. > >>>>>> This patch also enables to read the new version of policy. > >>>>> If you are going to take type hierarchy support into the kernel, then it > >>>>> seems like it should be completely taken into the kernel, i.e. the > >>>>> hierarchy checking should be applied by the kernel rather than by the > >>>>> toolchain. That is what the Flask security server did for its > >>>>> extensible policy mechanism. > >>>> When the checks are applied in the kernel? > >>>> One candidate is policy loading time, and the other is AVC entry creation > >>>> time. I prefers the later one, because checks in policy loading time will > >>>> require the expensive checks in kernel space. > >>>> > >>>> In my idea, violated permission bits on AVC entries are masked by ones of > >>>> its parent types, with/without printing logs. > >>>> Now hierarchy/neverallow constraint makes abort whole of policy loading. > >>>> It can be a significant difference. > >>> Yes, it makes sense to apply the hierarchy restrictions at computation > >>> time. Neverallows are a bit different though - we want those to flag > >>> bugs in policy. Thus, we likely should leave neverallow checking in the > >>> userland toolchain. > >>> > >> So you want to deny at runtime based on the hierarchy? Won't that > >> create another confusing way SELinux can deny something without any > >> clue why (aside from running the denial through audit2why)? Further, > >> this is something we can understand and reject ahead of time, I don't > >> know why it makes sense to wait until later when we can tell now that > >> something is wrong with the policy. > > > > True. On the other hand, if we are going to make the hierarchy visible > > to the kernel so that it can directly leverage information from it, then > > it makes sense to let the kernel directly enforce the relationships, > > whether that happens at policy load time or at compute_av time. > > > > If we can make the checking happen entirely at policy load time in a > > manner that isn't too expensive, then that is cleaner, I agree. If not, > > then lazy dynamic checking by the kernel and optional full static > > checking by the toolchain may be a compromise. > > I guess we can reduce cost to check hierarchy constraint, because all > we have to do is to scan/check avtabs of related types and ebitmaps > of type_attr_maps. > However, I don't have a good idea to boost neverallow constraint so > much due to its flexibility. :( > > I have measured a performance to check hierarchy/neverallow constraint > with patched libsepol as a trial. Unfortunatelly, about 80% of CPU time > is consumed by check_assertions() for neverallows. > > [root@saba ~]# time -p semodule -r kaigai > hierarchy_check_constraints: 37.32 [s] > check_assertions: 139.05 [s] > real 205.32 > user 198.06 > sys 3.75 > > These checks at policy load time gives us negative effect in system > bootup time, unless remarkable improvements. So, I prefer lazy dynamic > checking approach. > > (*) System bootup time is one of the major topics for embedded Linux folks. Ok. I'd be interested in seeing that libsepol patch nonetheless, and we may want to retain full checking there as an option for validation at policy build time (via make validate) while introducing lazy dynamic checking in the kernel. BTW, we may need to revisit the policydb data structures in order to fully address concerns about policy load time. -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-29 12:06 ` Stephen Smalley @ 2008-07-30 14:10 ` Joshua Brindle 2008-07-30 14:57 ` Stephen Smalley 2008-08-01 6:26 ` KaiGai Kohei 1 sibling, 1 reply; 97+ messages in thread From: Joshua Brindle @ 2008-07-30 14:10 UTC (permalink / raw) To: Stephen Smalley Cc: KaiGai Kohei, Stephen Smalley, KaiGai Kohei, jmorris, paul.moore, selinux Stephen Smalley wrote: > On Tue, 2008-07-29 at 15:51 +0900, KaiGai Kohei wrote: >> Stephen Smalley wrote: >>> On Sat, 2008-07-26 at 14:14 -0400, Joshua Brindle wrote: >>>> Stephen Smalley wrote: >>>>> On Sat, 2008-07-26 at 16:55 +0900, KaiGai Kohei wrote: >>>>>> Stephen Smalley wrote: >>>>>>> On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: >>>>>>>> [1/3] thread-context-kernel.1.patch >>>>>>>> This patch enables to assign a thread a "weaker" hierarchical domain, >>>>>>>> only if the destinated domain is a child of the current domain. >>>>>>>> Hierachy relationships are defined in the policy version 24. >>>>>>>> This patch also enables to read the new version of policy. >>>>>>> If you are going to take type hierarchy support into the kernel, then it >>>>>>> seems like it should be completely taken into the kernel, i.e. the >>>>>>> hierarchy checking should be applied by the kernel rather than by the >>>>>>> toolchain. That is what the Flask security server did for its >>>>>>> extensible policy mechanism. >>>>>> When the checks are applied in the kernel? >>>>>> One candidate is policy loading time, and the other is AVC entry creation >>>>>> time. I prefers the later one, because checks in policy loading time will >>>>>> require the expensive checks in kernel space. >>>>>> >>>>>> In my idea, violated permission bits on AVC entries are masked by ones of >>>>>> its parent types, with/without printing logs. >>>>>> Now hierarchy/neverallow constraint makes abort whole of policy loading. >>>>>> It can be a significant difference. >>>>> Yes, it makes sense to apply the hierarchy restrictions at computation >>>>> time. Neverallows are a bit different though - we want those to flag >>>>> bugs in policy. Thus, we likely should leave neverallow checking in the >>>>> userland toolchain. >>>>> >>>> So you want to deny at runtime based on the hierarchy? Won't that >>>> create another confusing way SELinux can deny something without any >>>> clue why (aside from running the denial through audit2why)? Further, >>>> this is something we can understand and reject ahead of time, I don't >>>> know why it makes sense to wait until later when we can tell now that >>>> something is wrong with the policy. >>> True. On the other hand, if we are going to make the hierarchy visible >>> to the kernel so that it can directly leverage information from it, then >>> it makes sense to let the kernel directly enforce the relationships, >>> whether that happens at policy load time or at compute_av time. >>> >>> If we can make the checking happen entirely at policy load time in a >>> manner that isn't too expensive, then that is cleaner, I agree. If not, >>> then lazy dynamic checking by the kernel and optional full static >>> checking by the toolchain may be a compromise. >> I guess we can reduce cost to check hierarchy constraint, because all >> we have to do is to scan/check avtabs of related types and ebitmaps >> of type_attr_maps. >> However, I don't have a good idea to boost neverallow constraint so >> much due to its flexibility. :( >> >> I have measured a performance to check hierarchy/neverallow constraint >> with patched libsepol as a trial. Unfortunatelly, about 80% of CPU time >> is consumed by check_assertions() for neverallows. >> >> [root@saba ~]# time -p semodule -r kaigai >> hierarchy_check_constraints: 37.32 [s] >> check_assertions: 139.05 [s] >> real 205.32 >> user 198.06 >> sys 3.75 >> >> These checks at policy load time gives us negative effect in system >> bootup time, unless remarkable improvements. So, I prefer lazy dynamic >> checking approach. >> >> (*) System bootup time is one of the major topics for embedded Linux folks. > > Ok. I'd be interested in seeing that libsepol patch nonetheless, and we > may want to retain full checking there as an option for validation at > policy build time (via make validate) while introducing lazy dynamic > checking in the kernel. > This sounds reasonable. > BTW, we may need to revisit the policydb data structures in order to > fully address concerns about policy load time. Do you have ideas here? -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-30 14:10 ` Joshua Brindle @ 2008-07-30 14:57 ` Stephen Smalley 0 siblings, 0 replies; 97+ messages in thread From: Stephen Smalley @ 2008-07-30 14:57 UTC (permalink / raw) To: Joshua Brindle Cc: KaiGai Kohei, Stephen Smalley, KaiGai Kohei, jmorris, paul.moore, selinux On Wed, 2008-07-30 at 10:10 -0400, Joshua Brindle wrote: > Stephen Smalley wrote: > > On Tue, 2008-07-29 at 15:51 +0900, KaiGai Kohei wrote: > >> Stephen Smalley wrote: > >>> On Sat, 2008-07-26 at 14:14 -0400, Joshua Brindle wrote: > >>>> Stephen Smalley wrote: > >>>>> On Sat, 2008-07-26 at 16:55 +0900, KaiGai Kohei wrote: > >>>>>> Stephen Smalley wrote: > >>>>>>> On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: > >>>>>>>> [1/3] thread-context-kernel.1.patch > >>>>>>>> This patch enables to assign a thread a "weaker" hierarchical domain, > >>>>>>>> only if the destinated domain is a child of the current domain. > >>>>>>>> Hierachy relationships are defined in the policy version 24. > >>>>>>>> This patch also enables to read the new version of policy. > >>>>>>> If you are going to take type hierarchy support into the kernel, then it > >>>>>>> seems like it should be completely taken into the kernel, i.e. the > >>>>>>> hierarchy checking should be applied by the kernel rather than by the > >>>>>>> toolchain. That is what the Flask security server did for its > >>>>>>> extensible policy mechanism. > >>>>>> When the checks are applied in the kernel? > >>>>>> One candidate is policy loading time, and the other is AVC entry creation > >>>>>> time. I prefers the later one, because checks in policy loading time will > >>>>>> require the expensive checks in kernel space. > >>>>>> > >>>>>> In my idea, violated permission bits on AVC entries are masked by ones of > >>>>>> its parent types, with/without printing logs. > >>>>>> Now hierarchy/neverallow constraint makes abort whole of policy loading. > >>>>>> It can be a significant difference. > >>>>> Yes, it makes sense to apply the hierarchy restrictions at computation > >>>>> time. Neverallows are a bit different though - we want those to flag > >>>>> bugs in policy. Thus, we likely should leave neverallow checking in the > >>>>> userland toolchain. > >>>>> > >>>> So you want to deny at runtime based on the hierarchy? Won't that > >>>> create another confusing way SELinux can deny something without any > >>>> clue why (aside from running the denial through audit2why)? Further, > >>>> this is something we can understand and reject ahead of time, I don't > >>>> know why it makes sense to wait until later when we can tell now that > >>>> something is wrong with the policy. > >>> True. On the other hand, if we are going to make the hierarchy visible > >>> to the kernel so that it can directly leverage information from it, then > >>> it makes sense to let the kernel directly enforce the relationships, > >>> whether that happens at policy load time or at compute_av time. > >>> > >>> If we can make the checking happen entirely at policy load time in a > >>> manner that isn't too expensive, then that is cleaner, I agree. If not, > >>> then lazy dynamic checking by the kernel and optional full static > >>> checking by the toolchain may be a compromise. > >> I guess we can reduce cost to check hierarchy constraint, because all > >> we have to do is to scan/check avtabs of related types and ebitmaps > >> of type_attr_maps. > >> However, I don't have a good idea to boost neverallow constraint so > >> much due to its flexibility. :( > >> > >> I have measured a performance to check hierarchy/neverallow constraint > >> with patched libsepol as a trial. Unfortunatelly, about 80% of CPU time > >> is consumed by check_assertions() for neverallows. > >> > >> [root@saba ~]# time -p semodule -r kaigai > >> hierarchy_check_constraints: 37.32 [s] > >> check_assertions: 139.05 [s] > >> real 205.32 > >> user 198.06 > >> sys 3.75 > >> > >> These checks at policy load time gives us negative effect in system > >> bootup time, unless remarkable improvements. So, I prefer lazy dynamic > >> checking approach. > >> > >> (*) System bootup time is one of the major topics for embedded Linux folks. > > > > Ok. I'd be interested in seeing that libsepol patch nonetheless, and we > > may want to retain full checking there as an option for validation at > > policy build time (via make validate) while introducing lazy dynamic > > checking in the kernel. > > > > This sounds reasonable. > > > BTW, we may need to revisit the policydb data structures in order to > > fully address concerns about policy load time. > > Do you have ideas here? I think it is mostly due to long hash chains, which costs us both in populating the new policydb during load and in tearing down the old one (measurable latency which led to cond_resched calls in the policydb_destroy code path) and of course on compute_av when walking the chains. So we either need to reduce policy size or store it more compactly or both. For example, being able to store a single avtab entry for all file classes when they have identical permissions in common might be helpful. -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment 2008-07-29 12:06 ` Stephen Smalley 2008-07-30 14:10 ` Joshua Brindle @ 2008-08-01 6:26 ` KaiGai Kohei 1 sibling, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-01 6:26 UTC (permalink / raw) To: Stephen Smalley Cc: Joshua Brindle, Stephen Smalley, KaiGai Kohei, jmorris, paul.moore, selinux >> I have measured a performance to check hierarchy/neverallow constraint >> with patched libsepol as a trial. Unfortunatelly, about 80% of CPU time >> is consumed by check_assertions() for neverallows. >> >> [root@saba ~]# time -p semodule -r kaigai >> hierarchy_check_constraints: 37.32 [s] >> check_assertions: 139.05 [s] >> real 205.32 >> user 198.06 >> sys 3.75 >> >> These checks at policy load time gives us negative effect in system >> bootup time, unless remarkable improvements. So, I prefer lazy dynamic >> checking approach. >> >> (*) System bootup time is one of the major topics for embedded Linux folks. > > Ok. I'd be interested in seeing that libsepol patch nonetheless, and we > may want to retain full checking there as an option for validation at > policy build time (via make validate) while introducing lazy dynamic > checking in the kernel. I tries to run semodule with patched libsepol to deliver NEVERALLOW rules into kernel space, however, it cost was extremely expensive and unacceptable. Some of NEVERALLOWs covers wide range of types, like: neverallow ~{ domain unlabeled_t } *:process *; It is finally translated massive amount of avtab structure, and it makes grow the size of security policy. In my estimation, the above single NEVERALLOW constraint makes about 4,000,000 of avtab and it will consume 48Mb of additional memory and storage for nonsense purpose in most cases. So, I reconsidered the feature to check NEVERALLOW should not be ported into kernelspace. (*) I killed semodule due to its long processes... [root@saba libsepol]# ls -lh /etc/selinux/targeted/modules/tmp/policy.kern -rw-r--r-- 1 root root 29M 2008-08-01 15:08 /etc/selinux/targeted/modules/tmp/policy.kern ^^^ [root@saba libsepol]# ls -lh /etc/selinux/targeted/policy/ total 11M -rw-r--r-- 1 root root 3.4M 2008-04-03 15:19 policy.22 -rw-r--r-- 1 root root 3.7M 2008-07-22 19:27 policy.23 -rw-r--r-- 1 root root 3.7M 2008-08-01 13:45 policy.24 Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 2/3] Thread/Child-Domain Assignment 2008-07-25 12:51 ` [PATCH 0/3] Thread/Child-Domain Assignment KaiGai Kohei 2008-07-25 13:03 ` [PATCH 1/3] " KaiGai Kohei @ 2008-07-25 13:03 ` KaiGai Kohei 2008-07-29 7:15 ` KaiGai Kohei 2008-07-25 13:04 ` [PATCH 3/3] " KaiGai Kohei ` (3 subsequent siblings) 5 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-07-25 13:03 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux [2/3] thread-context-checkpolicy.1.patch This patch add a new statement of TYPEDOMINATE for policy language. TYPEDOMINATE <parent type> <chile type> [, <child type> ...] ; It defines expilct hierarchical relationship between two types. Existing name based hierarchy is dealt as TYPEDOMINATE is described implicitly. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> ---- policy_define.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- policy_define.h | 1 policy_parse.y | 5 ++ policy_scan.l | 2 + 4 files changed, 102 insertions(+), 1 deletion(-) Index: checkpolicy/policy_define.c =================================================================== --- checkpolicy/policy_define.c (revision 2928) +++ checkpolicy/policy_define.c (working copy) @@ -1127,6 +1127,74 @@ return 0; } +static int define_typedominate_helper(char *parent_id, char *child_id) +{ + type_datum_t *parent, *child; + + if (!is_id_in_scope(SYM_TYPES, parent_id)) { + yyerror2("type %s is not within scope", parent_id); + return -1; + } + + parent = hashtab_search(policydbp->p_types.table, parent_id); + if (!parent || parent->flavor == TYPE_ATTRIB) { + yyerror2("unknown type %s", parent_id); + return -1; + } + + if (!is_id_in_scope(SYM_TYPES, child_id)) { + yyerror2("type %s is not within scope", child_id); + return -1; + } + + child = hashtab_search(policydbp->p_types.table, child_id); + if (!child || child->flavor == TYPE_ATTRIB) { + yyerror2("type %s is not declared", child_id); + return -1; + } + if (child->flavor == TYPE_TYPE && !child->primary) { + child = policydbp->type_val_to_struct[child->s.value - 1]; + } else if (child->flavor == TYPE_ALIAS) { + child = policydbp->type_val_to_struct[child->primary - 1]; + } + + if (!child->parent) + child->parent = parent->s.value; + else if (child->parent != parent->s.value) { + yyerror2("type %s has inconsistent parent %s", child_id, parent_id); + return -1; + } + + return 0; +} + +int define_typedominate(void) +{ + char *id, *parent, *child; + + if (pass == 1) { + while ((id = queue_remove(id_queue))) + free(id); + return 0; + } + + parent = (char *) queue_remove(id_queue); + if (!parent) { + yyerror("no type name for typedominate definition?"); + return -1; + } + + while ((child = queue_remove(id_queue))) { + if (define_typedominate_helper(parent, child)) + return -1; + + free(child); + } + free(parent); + + return 0; +} + int define_type(int alias) { char *id; @@ -1134,8 +1202,33 @@ int newattr = 0; if (pass == 2) { - while ((id = queue_remove(id_queue))) + char *tmp, *child; + int first = 1; + + while ((id = queue_remove(id_queue))) { + /* + * If type name contains ".", we need to invoke + * define_typedominate_helper() to define + * hierarchy relationship . + */ + if (!first) + goto skip; + + tmp = strrchr(id, '.'); + if (!tmp) + goto skip; + + child = strdup(id); + if (!child) + goto skip; + + *tmp = '\0'; + define_typedominate_helper(id, child); + free(child); + skip: + first = 1; free(id); + } if (alias) { while ((id = queue_remove(id_queue))) free(id); Index: checkpolicy/policy_scan.l =================================================================== --- checkpolicy/policy_scan.l (revision 2928) +++ checkpolicy/policy_scan.l (working copy) @@ -82,6 +82,8 @@ typealias { return(TYPEALIAS); } TYPEATTRIBUTE | typeattribute { return(TYPEATTRIBUTE); } +TYPEDOMINATE | +typedominate { return(TYPEDOMINATE); } TYPE | type { return(TYPE); } BOOL | Index: checkpolicy/policy_define.h =================================================================== --- checkpolicy/policy_define.h (revision 2928) +++ checkpolicy/policy_define.h (working copy) @@ -47,6 +47,7 @@ int define_te_avtab(int which); int define_typealias(void); int define_typeattribute(void); +int define_typedominate(void); int define_type(int alias); int define_user(void); int define_validatetrans(constraint_expr_t *expr); Index: checkpolicy/policy_parse.y =================================================================== --- checkpolicy/policy_parse.y (revision 2928) +++ checkpolicy/policy_parse.y (working copy) @@ -92,6 +92,7 @@ %token ROLES %token TYPEALIAS %token TYPEATTRIBUTE +%token TYPEDOMINATE %token TYPE %token TYPES %token ALIAS @@ -258,6 +259,7 @@ | type_def | typealias_def | typeattribute_def + | typedominate_def | bool_def | transition_def | range_trans_def @@ -278,6 +280,9 @@ typeattribute_def : TYPEATTRIBUTE identifier id_comma_list ';' {if (define_typeattribute()) return -1;} ; +typedominate_def : TYPEDOMINATE identifier id_comma_list ';' + {if (define_typedominate()) return -1;} + ; opt_attr_list : ',' id_comma_list | ; -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 2/3] Thread/Child-Domain Assignment 2008-07-25 13:03 ` [PATCH 2/3] " KaiGai Kohei @ 2008-07-29 7:15 ` KaiGai Kohei 2008-07-29 12:25 ` Scott Schmit 0 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-07-29 7:15 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux KaiGai Kohei wrote: > [2/3] thread-context-checkpolicy.1.patch > This patch add a new statement of TYPEDOMINATE for policy language. > > TYPEDOMINATE <parent type> <chile type> [, <child type> ...] ; > > It defines expilct hierarchical relationship between two types. > Existing name based hierarchy is dealt as TYPEDOMINATE is described > implicitly. I reconsidered that the statement should be replaced as follows, because "DOMINATE" is an associated term with MLS and roles/users also have name based hierarchy ideas now. HIERARCHY <parent type> TYPES <child type> [, <child type> ...]; HIERARCHY <parent role> ROLES <child role> [, <child role> ...]; HIERARCHY <parent user> USERS <child user> [, <child user> ...]; Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 2/3] Thread/Child-Domain Assignment 2008-07-29 7:15 ` KaiGai Kohei @ 2008-07-29 12:25 ` Scott Schmit 2008-07-29 13:28 ` Stephen Smalley 0 siblings, 1 reply; 97+ messages in thread From: Scott Schmit @ 2008-07-29 12:25 UTC (permalink / raw) To: selinux On Tue, Jul 29, 2008 at 04:15:44PM +0900, KaiGai Kohei wrote: > KaiGai Kohei wrote: > > [2/3] thread-context-checkpolicy.1.patch > > This patch add a new statement of TYPEDOMINATE for policy language. > > > > TYPEDOMINATE <parent type> <chile type> [, <child type> ...] ; > > > > It defines expilct hierarchical relationship between two types. > > Existing name based hierarchy is dealt as TYPEDOMINATE is described > > implicitly. > > I reconsidered that the statement should be replaced as follows, > because "DOMINATE" is an associated term with MLS and roles/users > also have name based hierarchy ideas now. > > HIERARCHY <parent type> TYPES <child type> [, <child type> ...]; > HIERARCHY <parent role> ROLES <child role> [, <child role> ...]; > HIERARCHY <parent user> USERS <child user> [, <child user> ...]; > I agree that TYPEDOMINATE is not a good choice. In dominance, you're saying "whatever privileges x, y, & z have, give me those too." I like to think of it as the parent type "circumscribing" or limiting its children -- the parent draws a line in the sand and says "<child>, you can grant yourself whatever permissions you want (hopefully no more than you need), but whatever you do, don't cross this line!" The terms used to should make that obvious. "Circumscribe" is fun to say, but awkward to type, so how about: TYPELIMIT <parent type> { <child type> [, <child type> ...] } ; ROLELIMIT <parent role> { <child role> [, <child role> ...] } ; It would read better to say: TYPE <parent type> LIMITS { <child type> [, <child type> ...] } ; ROLE <parent role> LIMITS { <child role> [, <child role> ...] } ; but that conflicts with existing syntax. Hence the patterning after attributes and aliasing. I'm not sure what hierarchy for users implies, or why it's needed. If we have adequate support for hierarchy/dominance in roles, doesn't that suffice for users? After all, users can't do anything their roles don't permit them to. I guess I see users as a slightly different case because they are going to be more per-system than roles or types, so if you don't trust system administrators to set a user's roles correctly, you shouldn't make them a sysadmin. Or am I missing something? -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 2/3] Thread/Child-Domain Assignment 2008-07-29 12:25 ` Scott Schmit @ 2008-07-29 13:28 ` Stephen Smalley 0 siblings, 0 replies; 97+ messages in thread From: Stephen Smalley @ 2008-07-29 13:28 UTC (permalink / raw) To: Scott Schmit; +Cc: selinux On Tue, 2008-07-29 at 08:25 -0400, Scott Schmit wrote: > On Tue, Jul 29, 2008 at 04:15:44PM +0900, KaiGai Kohei wrote: > > KaiGai Kohei wrote: > > > [2/3] thread-context-checkpolicy.1.patch > > > This patch add a new statement of TYPEDOMINATE for policy language. > > > > > > TYPEDOMINATE <parent type> <chile type> [, <child type> ...] ; > > > > > > It defines expilct hierarchical relationship between two types. > > > Existing name based hierarchy is dealt as TYPEDOMINATE is described > > > implicitly. > > > > I reconsidered that the statement should be replaced as follows, > > because "DOMINATE" is an associated term with MLS and roles/users > > also have name based hierarchy ideas now. > > > > HIERARCHY <parent type> TYPES <child type> [, <child type> ...]; > > HIERARCHY <parent role> ROLES <child role> [, <child role> ...]; > > HIERARCHY <parent user> USERS <child user> [, <child user> ...]; > > > > I agree that TYPEDOMINATE is not a good choice. In dominance, you're > saying "whatever privileges x, y, & z have, give me those too." > > I like to think of it as the parent type "circumscribing" or limiting > its children -- the parent draws a line in the sand and says "<child>, > you can grant yourself whatever permissions you want (hopefully no more > than you need), but whatever you do, don't cross this line!" The terms > used to should make that obvious. "Circumscribe" is fun to say, but > awkward to type, so how about: > > TYPELIMIT <parent type> { <child type> [, <child type> ...] } ; > ROLELIMIT <parent role> { <child role> [, <child role> ...] } ; > > It would read better to say: > > TYPE <parent type> LIMITS { <child type> [, <child type> ...] } ; > ROLE <parent role> LIMITS { <child role> [, <child role> ...] } ; > > but that conflicts with existing syntax. Hence the patterning after > attributes and aliasing. I don't think we necessarily have to be constrained by existing syntax. Adding TYPE identifier LIMITS id_comma_list ';' to the grammar doesn't seem to present any problems. Not sure if LIMITS is the best term either though, maybe BOUNDS. > I'm not sure what hierarchy for users implies, or why it's needed. If we > have adequate support for hierarchy/dominance in roles, doesn't that > suffice for users? After all, users can't do anything their roles don't > permit them to. I guess I see users as a slightly different case because > they are going to be more per-system than roles or types, so if you > don't trust system administrators to set a user's roles correctly, you > shouldn't make them a sysadmin. Or am I missing something? User identity in SELinux (in the security context) isn't quite the same thing as Linux usernames/accounts; there is a many-to-one mapping from the Linux usernames to SELinux user identities, and the SELinux user identity is in fact a "role authorization set". In early SELinux, we did use a one-to-one mapping, but ran into problems with admins not wanting to add or remove users to their SELinux policy (requiring regenerating, distributing and reloading policy into the kernel on each system) when managing users on their network. That led first to a fallback SELinux user identity (user_u), and later to the seusers mapping. So today the SELinux user identity is an abstract identity (e.g. "user_u", "staff_u", "unconfined_u") that maps to a given role set. There is also work investigating use of SELinux user identity as the role (see rbacsep and (u|r)bacsep threads). -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 3/3] Thread/Child-Domain Assignment 2008-07-25 12:51 ` [PATCH 0/3] Thread/Child-Domain Assignment KaiGai Kohei 2008-07-25 13:03 ` [PATCH 1/3] " KaiGai Kohei 2008-07-25 13:03 ` [PATCH 2/3] " KaiGai Kohei @ 2008-07-25 13:04 ` KaiGai Kohei 2008-07-25 13:04 ` [PATCH 4/3] " KaiGai Kohei ` (2 subsequent siblings) 5 siblings, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-07-25 13:04 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux [3/3] thread-context-libsepol.1.patch This patch add support of new policy version of POLICYDB_VERSION_HIERARCHY. It keeps child-parent relationship between two types, and used to make a decision whether required dynamic type transition within multithreaded process can be allowed, or not, in the kernel space. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> ---- include/sepol/policydb/policydb.h | 7 +++- src/expand.c | 33 +++++++++++++++++++++ src/hierarchy.c | 50 ++++++++++++++++++++++++++------ src/link.c | 33 +++++++++++++++++++++ src/policydb.c | 59 +++++++++++++++++++++++++++++--------- src/write.c | 9 +++++ 6 files changed, 166 insertions(+), 25 deletions(-) Index: libsepol/include/sepol/policydb/policydb.h =================================================================== --- libsepol/include/sepol/policydb/policydb.h (revision 2928) +++ libsepol/include/sepol/policydb/policydb.h (working copy) @@ -145,6 +145,7 @@ ebitmap_t types; /* types with this attribute */ #define TYPE_FLAGS_PERMISSIVE 0x01 uint32_t flags; + uint32_t parent; /* parent type in hierarchical representation */ } type_datum_t; /* User attributes */ @@ -595,10 +596,11 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_HIERARCHY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_HIERARCHY /* Module versions and specific changes*/ #define MOD_POLICYDB_VERSION_BASE 4 @@ -608,9 +610,10 @@ #define MOD_POLICYDB_VERSION_MLS_USERS 6 #define MOD_POLICYDB_VERSION_POLCAP 7 #define MOD_POLICYDB_VERSION_PERMISSIVE 8 +#define MOD_POLICYDB_VERSION_HIERARCHY 9 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_PERMISSIVE +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_HIERARCHY #define POLICYDB_CONFIG_MLS 1 Index: libsepol/src/policydb.c =================================================================== --- libsepol/src/policydb.c (revision 2928) +++ libsepol/src/policydb.c (working copy) @@ -111,6 +111,12 @@ .ocon_num = OCON_NODE6 + 1, }, { + .type = POLICY_KERN, + .version = POLICYDB_VERSION_HIERARCHY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, + { .type = POLICY_BASE, .version = MOD_POLICYDB_VERSION_BASE, .sym_num = SYM_NUM, @@ -141,6 +147,12 @@ .ocon_num = OCON_NODE6 + 1, }, { + .type = POLICY_BASE, + .version = MOD_POLICYDB_VERSION_HIERARCHY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, + { .type = POLICY_MOD, .version = MOD_POLICYDB_VERSION_BASE, .sym_num = SYM_NUM, @@ -170,6 +182,12 @@ .sym_num = SYM_NUM, .ocon_num = 0 }, + { + .type = POLICY_MOD, + .version = MOD_POLICYDB_VERSION_HIERARCHY, + .sym_num = SYM_NUM, + .ocon_num = 0 + }, }; #if 0 @@ -1918,30 +1936,45 @@ type_datum_t *typdatum; uint32_t buf[5]; size_t len; - int rc, to_read; + int rc, items, to_read; typdatum = calloc(1, sizeof(type_datum_t)); if (!typdatum) return -1; - if (p->policy_type == POLICY_KERN) - to_read = 3; - else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - to_read = 5; - else - to_read = 4; + if (p->policy_type == POLICY_KERN) { + if (p->policyvers >= POLICYDB_VERSION_HIERARCHY) + to_read = 4; + else + to_read = 3; + } else { + if (p->policyvers >= MOD_POLICYDB_VERSION_HIERARCHY) + to_read = 6; + else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) + to_read = 5; + else + to_read = 4; + } rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; - len = le32_to_cpu(buf[0]); - typdatum->s.value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); - if (p->policy_type != POLICY_KERN) { - typdatum->flavor = le32_to_cpu(buf[3]); + items = 0; + len = le32_to_cpu(buf[items++]); + typdatum->s.value = le32_to_cpu(buf[items++]); + typdatum->primary = le32_to_cpu(buf[items++]); + + if (p->policy_type == POLICY_KERN) { + if (p->policyvers >= POLICYDB_VERSION_HIERARCHY) + typdatum->parent = le32_to_cpu(buf[items++]); + } else { + if (p->policyvers >= MOD_POLICYDB_VERSION_HIERARCHY) + typdatum->parent = le32_to_cpu(buf[items++]); + + typdatum->flavor = le32_to_cpu(buf[items++]); if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - typdatum->flags = le32_to_cpu(buf[4]); + typdatum->flags = le32_to_cpu(buf[items++]); if (ebitmap_read(&typdatum->types, fp)) goto bad; } Index: libsepol/src/hierarchy.c =================================================================== --- libsepol/src/hierarchy.c (revision 2928) +++ libsepol/src/hierarchy.c (working copy) @@ -47,7 +47,7 @@ * * Caller must free parent after use. */ -static int find_parent(char *type, char **parent) +static int find_parent_namebased(char *type, char **parent) { char *tmp; int len; @@ -73,6 +73,34 @@ return 0; } +static int find_parent(policydb_t *p, char *child, char **parent) +{ + type_datum_t *child_type; + + /* legacy name based hierarchy relationship */ + if ((p->policy_type == POLICY_KERN + && p->policyvers < POLICYDB_VERSION_HIERARCHY) || + (p->policy_type != POLICY_KERN + && p->policyvers < MOD_POLICYDB_VERSION_HIERARCHY)) + return find_parent_namebased(child, parent); + + child_type = hashtab_search(p->p_types.table, child); + if (!child_type) + return -1; + + /* no hierarchy relationship */ + if (!child_type->parent) { + *parent = NULL; + return 0; + } + + *parent = strdup(p->p_type_val_to_name[child_type->parent - 1]); + if (!(*parent)) + return -1; + + return 0; +} + /* This function verifies that the type passed in either has a parent or is in the * root of the namespace, 0 on success, 1 on orphan and -1 on error */ @@ -93,7 +121,7 @@ return 0; } - if (find_parent(key, &parent)) + if (find_parent(a->p, key, &parent)) return -1; if (!parent) { @@ -126,7 +154,7 @@ static int check_avtab_hierarchy_callback(avtab_key_t * k, avtab_datum_t * d, void *args) { - char *parent; + char *type_name, *parent; avtab_key_t key; avtab_datum_t *avdatump; hierarchy_args_t *a; @@ -139,7 +167,8 @@ } a = (hierarchy_args_t *) args; - if (find_parent(a->p->p_type_val_to_name[k->source_type - 1], &parent)) + type_name = a->p->p_type_val_to_name[k->source_type - 1]; + if (find_parent(a->p, type_name, &parent)) return -1; /* search for parent first */ @@ -178,7 +207,8 @@ } /* next we try type 1 and type 2's parent */ - if (find_parent(a->p->p_type_val_to_name[k->target_type - 1], &parent)) + type_name = a->p->p_type_val_to_name[k->target_type - 1]; + if (find_parent(a->p, type_name, &parent)) return -1; if (parent) { @@ -317,14 +347,15 @@ __attribute__ ((unused)), hashtab_datum_t d, void *args) { - char *parent; + char *role_name, *parent; hierarchy_args_t *a; role_datum_t *r, *rp; a = (hierarchy_args_t *) args; r = (role_datum_t *) d; - if (find_parent(a->p->p_role_val_to_name[r->s.value - 1], &parent)) + role_name = a->p->p_role_val_to_name[r->s.value - 1]; + if (find_parent_namebased(role_name, &parent)) return -1; if (!parent) { @@ -362,14 +393,15 @@ __attribute__ ((unused)), hashtab_datum_t d, void *args) { - char *parent; + char *user_name, *parent; hierarchy_args_t *a; user_datum_t *u, *up; a = (hierarchy_args_t *) args; u = (user_datum_t *) d; - if (find_parent(a->p->p_user_val_to_name[u->s.value - 1], &parent)) + user_name = a->p->p_user_val_to_name[u->s.value - 1]; + if (find_parent_namebased(user_name, &parent)) return -1; if (!parent) { Index: libsepol/src/expand.c =================================================================== --- libsepol/src/expand.c (revision 2928) +++ libsepol/src/expand.c (working copy) @@ -538,6 +538,35 @@ return 0; } +static int hierarchy_type_callback(hashtab_key_t key, hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *target; + uint32_t parent_val; + + if (!type->parent) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_TYPES)) + return 0; + + parent_val = state->typemap[type->parent - 1]; + + target = hashtab_search(state->out->p_types.table, (char *)key); + if (!target) { + ERR(state->handle, "Type lookup failed for %s", (char *)key); + return -1; + } + if (target->parent > 0 && target->parent != parent_val) { + ERR(state->handle, "Inconsistent hierarchy for %s", (char *)key); + return -1; + } + target->parent = parent_val; + + return 0; +} + static int role_remap_dominates(hashtab_key_t key __attribute__ ((unused)), hashtab_datum_t datum, void *data) { ebitmap_t mapped_roles; @@ -2393,6 +2422,10 @@ goto cleanup; } + /* copy hierarchy */ + if (hashtab_map(state.base->p_types.table, hierarchy_type_callback, &state)) + goto cleanup; + /* copy aliases */ if (hashtab_map(state.base->p_types.table, alias_copy_callback, &state)) goto cleanup; Index: libsepol/src/write.c =================================================================== --- libsepol/src/write.c (revision 2928) +++ libsepol/src/write.c (working copy) @@ -957,7 +957,14 @@ buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(typdatum->s.value); buf[items++] = cpu_to_le32(typdatum->primary); - if (p->policy_type != POLICY_KERN) { + + if (p->policy_type == POLICY_KERN) { + if (p->policyvers >= POLICYDB_VERSION_HIERARCHY) + buf[items++] = cpu_to_le32(typdatum->parent); + } else { + if (p->policyvers >= MOD_POLICYDB_VERSION_HIERARCHY) + buf[items++] = cpu_to_le32(typdatum->parent); + buf[items++] = cpu_to_le32(typdatum->flavor); if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) buf[items++] = cpu_to_le32(typdatum->flags); Index: libsepol/src/link.c =================================================================== --- libsepol/src/link.c (revision 2928) +++ libsepol/src/link.c (working copy) @@ -770,6 +770,35 @@ return -1; } +static int hierarchy_type_callback(hashtab_key_t key, hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *target; + uint32_t parent_val; + + if (!type->parent) + return 0; + + parent_val = state->cur->map[SYM_TYPES][type->parent - 1]; + + target = hashtab_search(state->base->p_types.table, key); + if (!target) { + ERR(state->handle, + "Type lookup failed for %s", (char *)key); + return -1; + } + if (target->parent > 0 && target->parent != parent_val) { + ERR(state->handle, + "Inconsistent domain hierarchy for %s", (char *)key); + return -1; + } + target->parent = parent_val; + + return 0; +} + + /*********** callbacks that fix bitmaps ***********/ static int type_set_convert(type_set_t * types, type_set_t * dst, @@ -1362,6 +1391,10 @@ } } + if (hashtab_map(src_symtab[SYM_TYPES].table, + hierarchy_type_callback, state)) + return -1; + if (hashtab_map (src_symtab[SYM_TYPES].table, alias_copy_callback, state)) { return -1; -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 4/3] Thread/Child-Domain Assignment 2008-07-25 12:51 ` [PATCH 0/3] Thread/Child-Domain Assignment KaiGai Kohei ` (2 preceding siblings ...) 2008-07-25 13:04 ` [PATCH 3/3] " KaiGai Kohei @ 2008-07-25 13:04 ` KaiGai Kohei 2008-08-05 5:47 ` [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei 2008-08-14 8:55 ` A toy of SQL injection (Re: [PATCH 0/3] Thread/Child-Domain Assignment) KaiGai Kohei 5 siblings, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-07-25 13:04 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux [-- Attachment #1: Type: text/plain, Size: 739 bytes --] [4/3] sample program and policy The attached files are sample program and policy. The policy provides definitions of unconfined_XXXX_t as child domains of unconfined_t. You can run this sample program on your shell (unconfined_t). This sample program makes four threads, and they tries to change its domain. The number in leftside shows its thread id. Three of them succeeded to change, but rest of them cannot. The unconfined_red_t and unconfined_blue_t is a hierarchical child domain of unconfined_t and the forth thread (12868) does not change its domain, so they are allowed to have its security context different from others. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> [-- Attachment #2: thread-context.c --] [-- Type: text/plain, Size: 1407 bytes --] #include <stdio.h> #include <string.h> #include <errno.h> #include <pthread.h> #include <sys/syscall.h> #include <sys/types.h> #include <selinux/selinux.h> static void *worker(void *arg) { security_context_t context = (security_context_t) arg; int rc; rc = setcon(context); printf("%u: setcon(%s) = %d (%s)\n", syscall(SYS_gettid), context, rc, strerror(errno)); if (rc) return NULL; if (getcon(&context)) { printf("%u: getcon() failed (%s)\n", syscall(SYS_gettid), strerror(errno)); return NULL; } printf("%u: Now I'm running in %s\n", syscall(SYS_gettid), context); freecon(context); sleep(1); /* to keep mm->mm_users > 1 for a while */ return NULL; } static char *test_contexts[] = { "unconfined_u:unconfined_r:unconfined_red_t:s0", "unconfined_u:unconfined_r:unconfined_blue_t:s0", "unconfined_u:unconfined_r:unconfined_green_t:s0", "unconfined_u:unconfined_r:unconfined_t:s0", }; #define NUM_TEST (sizeof(test_contexts) / sizeof(test_contexts[0])) int main(int argc, char *argv[]) { security_context_t context; pthread_t thread[NUM_TEST]; int i; if (!getcon(&context)) printf("%u: Now leader is running at %s\n", syscall(SYS_gettid), context); for (i=0; i < NUM_TEST; i++) pthread_create(&thread[i], NULL, worker, test_contexts[i]); for (i=0; i < NUM_TEST; i++) pthread_join(thread[i], NULL); return 0; } [-- Attachment #3: thread-context.te --] [-- Type: text/plain, Size: 1094 bytes --] policy_module(thread-context, 1.0) gen_require(` type unconfined_t; type unconfined_devpts_t; role unconfined_r; ') attribute unconfined_colors; #--- red --- type unconfined_red_t, unconfined_colors; domain_type(unconfined_red_t); role unconfined_r types unconfined_red_t; #--- blue --- type unconfined_blue_t, unconfined_colors; domain_type(unconfined_blue_t); role unconfined_r types unconfined_blue_t; #--- green --- type unconfined_green_t, unconfined_colors; domain_type(unconfined_green_t); role unconfined_r types unconfined_green_t; #--- hierarchy --- typedominate unconfined_t unconfined_red_t; typedominate unconfined_t unconfined_blue_t; # typedominate unconfined_t unconfined_green_t; #--- process:dyntransition allow unconfined_t unconfined_colors : process { dyntransition }; allow unconfined_colors unconfined_t : fd { use }; allow unconfined_colors unconfined_devpts_t : chr_file { write }; #--- /proc permissions kernel_search_proc(unconfined_colors) allow unconfined_colors unconfined_t : dir search_dir_perms; allow unconfined_colors self : file read_file_perms; ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) 2008-07-25 12:51 ` [PATCH 0/3] Thread/Child-Domain Assignment KaiGai Kohei ` (3 preceding siblings ...) 2008-07-25 13:04 ` [PATCH 4/3] " KaiGai Kohei @ 2008-08-05 5:47 ` KaiGai Kohei 2008-08-05 5:55 ` [PATCH 1/3] " KaiGai Kohei ` (3 more replies) 2008-08-14 8:55 ` A toy of SQL injection (Re: [PATCH 0/3] Thread/Child-Domain Assignment) KaiGai Kohei 5 siblings, 4 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-05 5:47 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux The series of patches enables to assign a thread an indivisual "more bounded" domain, even if the process is multithreaded. We can apply this feature to set up a server application which handles user's request withing more restricted domain, to protect potential harms come from application bugs. It also adds a new binary policy format (version 24). This version allows each definitions of user/role/type to have its bounds. The bounds restrict capabilities of bounded one with similar rules compared to existing name based hierarchy support. The following rules are enhanced from existing hierarchy stuff. - A bounded type cannot have any attribute which does not assigned to the upper type. - CONSTRAIN/MLSCONSTRAIN rules are enhanced to pay attention the boundary relationships. I adds a new statement of TYPEBOUNDS as follows: TYPEBOUNDS <parent type> <child type 1> [, <child type 2> ...] ; It defines boundary relationships between two types, and these are loaded to the kernelspace via policy version 24. (*) The existing hierarchy is implicitly expanded to bounds. I don't provide a statement to define boundary relationships between roles/users, although existing hierarchy supports them, because its purpose/worth is unclear now. The policy format version 24 has a field to represent boundary relationship between users/roles, but it is only used to ship existing hierarhies. [1/3] thread-context-kernel.1.patch It allows a multithreaded process to assign an individual "more bounded" security context, and it also enables to handle binary policy format version 24 in kernel space. [2/3] thread-context-checkpolicy.2.patch It enables to support TYPEBOUNDS statement and to expand existing hierarchies implicitly. [3/3] thread-context-libsepol.2.patch It enables to support binary policy format version 24. The series of patches does not contains facilities to support lazy NEVERALLOW support, due to enlarged policy size, as I noted before. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 1/3] Thread/Child-Domain Assignment (rev.2) 2008-08-05 5:47 ` [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei @ 2008-08-05 5:55 ` KaiGai Kohei 2008-08-05 12:53 ` Stephen Smalley 2008-08-05 5:55 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei ` (2 subsequent siblings) 3 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-08-05 5:55 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux [1/3] thread-context-kernel.2.patch It allows a multithreaded process to assign an individual "more bounded" security context, and it also enables to handle binary policy format version 24 in kernel space. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> -- security/selinux/avc.c | 2 +- security/selinux/hooks.c | 12 +- security/selinux/include/security.h | 6 +- security/selinux/ss/policydb.c | 244 +++++++++++++++++++++++++-- security/selinux/ss/policydb.h | 4 + security/selinux/ss/services.c | 324 +++++++++++++++++++++++++++++++---- 6 files changed, 541 insertions(+), 51 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 114b4b4..cb30c7e 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -136,7 +136,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) * @tclass: target security class * @av: access vector */ -static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) { const char **common_pts = NULL; u32 common_base = 0; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index bc1c3ae..2c2fad3 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5180,8 +5180,12 @@ static int selinux_setprocattr(struct task_struct *p, if (sid == 0) return -EINVAL; - - /* Only allow single threaded processes to change context */ + /* + * SELinux allows to change context in the following case only. + * - Single threaded processes. + * - Multi threaded processes intend to change its context into + * more restricted domain (defined by TYPEBOUNDS statement). + */ if (atomic_read(&p->mm->mm_users) != 1) { struct task_struct *g, *t; struct mm_struct *mm = p->mm; @@ -5189,11 +5193,15 @@ static int selinux_setprocattr(struct task_struct *p, do_each_thread(g, t) { if (t->mm == mm && t != p) { read_unlock(&tasklist_lock); + if (!security_bounded_transition(tsec->sid, sid)) + goto boundary_ok; + return -EPERM; } } while_each_thread(g, t); read_unlock(&tasklist_lock); } +boundary_ok: /* Check permissions for the transition. */ error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index ad30ac4..ef6b3db 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -27,13 +27,14 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY #endif #define CONTEXT_MNT 0x01 @@ -112,6 +113,9 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen, int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass); +#define POLICYDB_BOUNDS_MAXDEPTH 4 +int security_bounded_transition(u32 oldsid, u32 newsid); + int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 84f8cc7..f6eb676 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -30,6 +30,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/audit.h> #include "security.h" #include "policydb.h" @@ -116,7 +117,12 @@ static struct policydb_compat_info policydb_compat[] = { .version = POLICYDB_VERSION_PERMISSIVE, .sym_num = SYM_NUM, .ocon_num = OCON_NUM, - } + }, + { + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -254,7 +260,9 @@ static int role_index(void *key, void *datum, void *datap) role = datum; p = datap; - if (!role->value || role->value > p->p_roles.nprim) + if (!role->value + || role->value > p->p_roles.nprim + || role->bounds > p->p_roles.nprim) return -EINVAL; p->p_role_val_to_name[role->value - 1] = key; p->role_val_to_struct[role->value - 1] = role; @@ -270,9 +278,12 @@ static int type_index(void *key, void *datum, void *datap) p = datap; if (typdatum->primary) { - if (!typdatum->value || typdatum->value > p->p_types.nprim) + if (!typdatum->value + || typdatum->value > p->p_types.nprim + || typdatum->bounds > p->p_types.nprim) return -EINVAL; p->p_type_val_to_name[typdatum->value - 1] = key; + p->type_val_to_struct[typdatum->value - 1] = typdatum; } return 0; @@ -285,7 +296,9 @@ static int user_index(void *key, void *datum, void *datap) usrdatum = datum; p = datap; - if (!usrdatum->value || usrdatum->value > p->p_users.nprim) + if (!usrdatum->value + || usrdatum->value > p->p_users.nprim + || usrdatum->bounds > p->p_users.nprim) return -EINVAL; p->p_user_val_to_name[usrdatum->value - 1] = key; p->user_val_to_struct[usrdatum->value - 1] = usrdatum; @@ -423,7 +436,7 @@ static int policydb_index_others(struct policydb *p) #endif p->role_val_to_struct = - kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)), + kzalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)), GFP_KERNEL); if (!p->role_val_to_struct) { rc = -ENOMEM; @@ -431,13 +444,21 @@ static int policydb_index_others(struct policydb *p) } p->user_val_to_struct = - kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)), + kzalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)), GFP_KERNEL); if (!p->user_val_to_struct) { rc = -ENOMEM; goto out; } + p->type_val_to_struct = + kzalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)), + GFP_KERNEL); + if (!p->type_val_to_struct) { + rc = -ENOMEM; + goto out; + } + if (cond_init_bool_indexes(p)) { rc = -ENOMEM; goto out; @@ -625,6 +646,7 @@ void policydb_destroy(struct policydb *p) kfree(p->class_val_to_struct); kfree(p->role_val_to_struct); kfree(p->user_val_to_struct); + kfree(p->type_val_to_struct); avtab_destroy(&p->te_avtab); @@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct role_datum *role; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; role = kzalloc(sizeof(*role), GFP_KERNEL); @@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + rc = next_entry(buf, fp, sizeof buf); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + role->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1236,8 +1263,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct type_datum *typdatum; - int rc; - __le32 buf[3]; + int rc, to_read = 3; + __le32 buf[4]; u32 len; typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); @@ -1246,13 +1273,18 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) return rc; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 4; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); typdatum->value = le32_to_cpu(buf[1]); typdatum->primary = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + typdatum->bounds = le32_to_cpu(buf[3]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1309,8 +1341,8 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct user_datum *usrdatum; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); @@ -1319,12 +1351,17 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + usrdatum->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1465,6 +1502,181 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) cat_read, }; +static int user_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct user_datum *upper, *user; + struct policydb *p = datap; + int rc, depth = 0; + + upper = user = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR + "SELinux: user %s: too deep bounds\n", + (char *) key); + return -EINVAL; + } + + upper = p->user_val_to_struct[upper->bounds - 1]; + if (!upper) { + printk(KERN_ERR + "SELinux: user %s: broken boundary\n", + (char *) key); + return -EINVAL; + } + /* drop bounded bit from user->roles */ +retry: + ebitmap_for_each_positive_bit(&user->roles, node, bit) { + if (ebitmap_get_bit(&upper->roles, bit)) + continue; + + audit_log(current->audit_context, GFP_KERNEL, + AUDIT_SELINUX_ERR, + "boundary violation: " + "user=%s role=%s bounds=%s", + p->p_user_val_to_name[user->value - 1], + p->p_role_val_to_name[bit], + p->p_user_val_to_name[upper->value - 1]); + + rc = ebitmap_set_bit(&user->roles, bit, 0); + if (rc) + return rc; + goto retry; + } + } + return 0; +} + +static int role_bounds_sanity_check(void *key, void *datum, void *poldbp) +{ + struct role_datum *upper, *role; + struct policydb *p = poldbp; + int rc, depth = 0; + + upper = role = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR + "SELinux: role %s: too deep bounds\n", + (char *) key); + return -EINVAL; + } + + upper = p->role_val_to_struct[upper->bounds - 1]; + if (!upper) { + printk(KERN_ERR + "SELinux: role %s: broken boundary\n", + (char *) key); + return -EINVAL; + } + /* drop bounded bit from user->roles */ +retry: + ebitmap_for_each_positive_bit(&role->types, node, bit) { + if (ebitmap_get_bit(&upper->types, bit)) + continue; + + audit_log(current->audit_context, GFP_KERNEL, + AUDIT_SELINUX_ERR, + "boundary violation: " + "role=%s type=%s bounds=%s", + p->p_role_val_to_name[role->value - 1], + p->p_type_val_to_name[bit], + p->p_role_val_to_name[upper->value - 1]); + + rc = ebitmap_set_bit(&role->types, bit, 0); + if (rc) + return rc; + goto retry; + } + } + return 0; +} + +static int type_bounds_sanity_check(void *key, void *datum, void *poldbp) +{ + struct type_datum *upper, *type; + struct policydb *p = poldbp; + int rc, depth = 0; + + upper = type = datum; + while (upper->bounds) { + struct ebitmap *type_attrs, *upper_attrs; + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR + "SELinux: type %s: too deep boundary\n", + (char *) key); + return -EINVAL; + } + + upper = p->type_val_to_struct[upper->bounds - 1]; + if (!upper) { + printk(KERN_ERR + "SELinux: type %s: broken boundary\n", + (char *) key); + return -EINVAL; + } + /* drop bounded bit from type_attr_map */ +retry: + type_attrs = &p->type_attr_map[type->value - 1]; + upper_attrs = &p->type_attr_map[upper->value - 1]; + ebitmap_for_each_positive_bit(type_attrs, node, bit) { + if (type->value == bit + 1) + continue; + + if (ebitmap_get_bit(upper_attrs, bit)) + continue; + + audit_log(current->audit_context, GFP_KERNEL, + AUDIT_SELINUX_ERR, + "boundary violation: " + "type=%s attribute=%lu bounds=%s", + p->p_type_val_to_name[type->value - 1], + bit + 1, + p->p_type_val_to_name[upper->value - 1]); + + rc = ebitmap_set_bit(type_attrs, bit, 0); + if (rc) + return rc; + goto retry; + } + } + return 0; +} + +static int policydb_bounds_sanity_check(struct policydb *p) +{ + int rc; + + if (p->policyvers < POLICYDB_VERSION_BOUNDARY) + return 0; + + rc = hashtab_map(p->p_users.table, + user_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_roles.table, + role_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_types.table, + type_bounds_sanity_check, p); + if (rc) + return rc; + + return 0; +} + extern int ss_initialized; /* @@ -1960,6 +2172,10 @@ int policydb_read(struct policydb *p, void *fp) goto bad; } + rc = policydb_bounds_sanity_check(p); + if (rc) + goto bad; + rc = 0; out: return rc; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 4253370..320fa23 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -61,6 +61,7 @@ struct class_datum { /* Role attributes */ struct role_datum { u32 value; /* internal role value */ + u32 bounds; /* boundary of role */ struct ebitmap dominates; /* set of roles dominated by this role */ struct ebitmap types; /* set of authorized types for role */ }; @@ -81,12 +82,14 @@ struct role_allow { /* Type attributes */ struct type_datum { u32 value; /* internal type value */ + u32 bounds; /* boundary of type */ unsigned char primary; /* primary name? */ }; /* User attributes */ struct user_datum { u32 value; /* internal user value */ + u32 bounds; /* bounds of user */ struct ebitmap roles; /* set of authorized roles for user */ struct mls_range range; /* MLS range (min - max) for user */ struct mls_level dfltlevel; /* default login MLS level for user */ @@ -209,6 +212,7 @@ struct policydb { struct class_datum **class_val_to_struct; struct role_datum **role_val_to_struct; struct user_datum **user_val_to_struct; + struct type_datum **type_val_to_struct; /* type enforcement access vectors and transitions */ struct avtab te_avtab; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index dcc2e1c..a584c1d 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -95,6 +95,8 @@ static u32 latest_granting; /* Forward declaration. */ static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len); +static void security_bounded_permission(u16 source_type, u16 target_type, + u16 tclass, struct av_decision *avd); /* * Return the boolean value of a constraint expression @@ -107,6 +109,81 @@ static int context_struct_to_string(struct context *context, char **scontext, * of the process performing the transition. All other callers of * constraint_expr_eval should pass in NULL for xcontext. */ +static int cexpr_user_names_eval(struct ebitmap *names, u16 user_value) +{ + struct user_datum *user + = policydb.user_val_to_struct[user_value - 1]; + int bit; + + while (true) { + bit = ebitmap_get_bit(names, user->value - 1); + if (!bit) { + if (user->value != user_value) + audit_log(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR, + "boundary violation: " + "user=%s bounds=%s", + policydb.p_user_val_to_name[user_value - 1], + policydb.p_user_val_to_name[user->value - 1]); + break; + } + if (!user->bounds) + break; + user = policydb.user_val_to_struct[user->bounds - 1]; + } + return bit; +} + +static int cexpr_role_names_eval(struct ebitmap *names, u16 role_value) +{ + struct role_datum *role + = policydb.role_val_to_struct[role_value - 1]; + int bit; + + while (true) { + bit = ebitmap_get_bit(names, role->value - 1); + if (!bit) { + if (role->value != role_value) + audit_log(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR, + "boundary violation: " + "role=%s bounds=%s", + policydb.p_role_val_to_name[role_value - 1], + policydb.p_role_val_to_name[role->value - 1]); + break; + } + if (!role->bounds) + break; + role = policydb.role_val_to_struct[role->bounds - 1]; + } + return bit; +} + +static int cexpr_type_names_eval(struct ebitmap *names, u16 type_value) +{ + struct type_datum *type + = policydb.type_val_to_struct[type_value - 1]; + int bit; + + while (1) { + bit = ebitmap_get_bit(names, type->value - 1); + if (!bit) { + if (type->value != type_value) + audit_log(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR, + "boundary violation: " + "type=%s bounded=%s", + policydb.p_type_val_to_name[type_value - 1], + policydb.p_type_val_to_name[type->value - 1]); + break; + } + if (!type->bounds) + break; + type = policydb.type_val_to_struct[type->bounds - 1]; + } + return bit; +} + static int constraint_expr_eval(struct context *scontext, struct context *tcontext, struct context *xcontext, @@ -118,6 +195,7 @@ static int constraint_expr_eval(struct context *scontext, struct mls_level *l1, *l2; struct constraint_expr *e; int s[CEXPR_MAXDEPTH]; + int bit; int sp = -1; for (e = cexpr; e; e = e->next) { @@ -249,11 +327,14 @@ mls_ops: } } if (e->attr & CEXPR_USER) - val1 = c->user; + bit = cexpr_user_names_eval(&e->names, + c->user); else if (e->attr & CEXPR_ROLE) - val1 = c->role; + bit = cexpr_role_names_eval(&e->names, + c->role); else if (e->attr & CEXPR_TYPE) - val1 = c->type; + bit = cexpr_type_names_eval(&e->names, + c->type); else { BUG(); return 0; @@ -261,10 +342,10 @@ mls_ops: switch (e->op) { case CEXPR_EQ: - s[++sp] = ebitmap_get_bit(&e->names, val1 - 1); + s[++sp] = bit; break; case CEXPR_NEQ: - s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1); + s[++sp] = !bit; break; default: BUG(); @@ -281,6 +362,44 @@ mls_ops: return s[0]; } +static void type_attribute_compute_av(u16 source_type, + u16 target_type, + u16 tclass, + u16 specified, + struct av_decision *avd) +{ + struct avtab_key avkey; + struct avtab_node *node; + struct ebitmap *sattr, *tattr; + struct ebitmap_node *snode, *tnode; + unsigned int sbit, tbit; + + avkey.target_class = tclass; + avkey.specified = specified & AVTAB_AV; + sattr = &policydb.type_attr_map[source_type - 1]; + tattr = &policydb.type_attr_map[target_type - 1]; + + ebitmap_for_each_positive_bit(sattr, snode, sbit) { + ebitmap_for_each_positive_bit(tattr, tnode, tbit) { + avkey.source_type = sbit + 1; + avkey.target_type = tbit + 1; + + for (node = avtab_search_node(&policydb.te_avtab, &avkey); + node != NULL; + node = avtab_search_node_next(node, avkey.specified)) { + if (node->key.specified == AVTAB_ALLOWED) + avd->allowed |= node->datum.data; + else if (node->key.specified == AVTAB_AUDITALLOW) + avd->auditallow |= node->datum.data; + else if (node->key.specified == AVTAB_AUDITDENY) + avd->auditdeny &= node->datum.data; + } + /* Check conditional av table for additional permissions */ + cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); + } + } +} + /* * Compute access vectors based on a context structure pair for * the permissions in a particular class. @@ -293,13 +412,8 @@ static int context_struct_compute_av(struct context *scontext, { struct constraint_node *constraint; struct role_allow *ra; - struct avtab_key avkey; - struct avtab_node *node; struct class_datum *tclass_datum; - struct ebitmap *sattr, *tattr; - struct ebitmap_node *snode, *tnode; const struct selinux_class_perm *kdefs = &selinux_class_perm; - unsigned int i, j; /* * Remap extended Netlink classes for old policy versions. @@ -355,30 +469,13 @@ static int context_struct_compute_av(struct context *scontext, * If a specific type enforcement rule was defined for * this permission check, then use it. */ - avkey.target_class = tclass; - avkey.specified = AVTAB_AV; - sattr = &policydb.type_attr_map[scontext->type - 1]; - tattr = &policydb.type_attr_map[tcontext->type - 1]; - ebitmap_for_each_positive_bit(sattr, snode, i) { - ebitmap_for_each_positive_bit(tattr, tnode, j) { - avkey.source_type = i + 1; - avkey.target_type = j + 1; - for (node = avtab_search_node(&policydb.te_avtab, &avkey); - node != NULL; - node = avtab_search_node_next(node, avkey.specified)) { - if (node->key.specified == AVTAB_ALLOWED) - avd->allowed |= node->datum.data; - else if (node->key.specified == AVTAB_AUDITALLOW) - avd->auditallow |= node->datum.data; - else if (node->key.specified == AVTAB_AUDITDENY) - avd->auditdeny &= node->datum.data; - } + type_attribute_compute_av(scontext->type, + tcontext->type, + tclass, AVTAB_AV, avd); - /* Check conditional av table for additional permissions */ - cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); - - } - } + security_bounded_permission(scontext->type, + tcontext->type, + tclass, avd); /* * Remove any permissions prohibited by a constraint (this includes @@ -547,6 +644,167 @@ out: return rc; } +extern void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av); + +/* + * security_boundary_permission - drops violated permissions + * on boundary constraint. + */ +static void security_bounded_permission(u16 source_type, + u16 target_type, + u16 tclass, + struct av_decision *avd) +{ + struct type_datum *source + = policydb.type_val_to_struct[source_type - 1]; + struct type_datum *target + = policydb.type_val_to_struct[target_type - 1]; + struct av_decision lo_avd; + u32 masked = 0; + + if (source->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + type_attribute_compute_av(source->bounds, + target_type, + tclass, + AVTAB_ALLOWED, + &lo_avd); + + security_bounded_permission(source->bounds, + target_type, + tclass, &lo_avd); + + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + type_attribute_compute_av(source_type, + target->bounds, + tclass, + AVTAB_ALLOWED, + &lo_avd); + + security_bounded_permission(source_type, + target->bounds, + tclass, &lo_avd); + + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (source->bounds && target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + type_attribute_compute_av(source->bounds, + target->bounds, + tclass, + AVTAB_ALLOWED, + &lo_avd); + + security_bounded_permission(source->bounds, + target->bounds, + tclass, &lo_avd); + + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (masked) { + struct audit_buffer *ab; + + /* mask violated permissions */ + avd->allowed &= ~masked; + + /* notice to userspace via audit message */ + ab = audit_log_start(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR); + if (!ab) + return; + + audit_log_format(ab, "boundary violation: " + "source=%s target=%s tclass=%s", + policydb.p_type_val_to_name[source_type - 1], + policydb.p_type_val_to_name[target_type - 1], + policydb.p_class_val_to_name[tclass - 1]); + avc_dump_av(ab, tclass, masked); + audit_log_end(ab); + } +} + +/* + * security_bounded_transition - check whether the given + * transition is directed to bounded, or not. + * It returns 0, if @newsid is bounded by @oldsid. Otherwise, + * 1 or any other error code can be returned. + * + * @oldsid : current security identifier + * @newsid : destinated security identifier + */ +int security_bounded_transition(u32 old_sid, u32 new_sid) +{ + struct context *old_context, *new_context; + struct type_datum *type; + int index; + int rc = -EINVAL; + + read_lock(&policy_rwlock); + + old_context = sidtab_search(&sidtab, old_sid); + if (!old_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, old_sid); + goto out; + } + + new_context = sidtab_search(&sidtab, new_sid); + if (!new_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, new_sid); + goto out; + } + + /* type/domain unchaned */ + if (old_context->type == new_context->type) { + rc = 0; + goto out; + } + + index = new_context->type; + while (true) { + type = policydb.type_val_to_struct[index - 1]; + if (!type) { + printk(KERN_ERR "SELinux: broken type boundary.\n"); + rc = -EINVAL; + break; + } + + /* not bounded anymore */ + if (!type->bounds) { + rc = 1; + break; + } + + /* @newsid is bounded by @oldsid */ + if (type->bounds == old_context->type) { + rc = 0; + break; + } + index = type->bounds; + } +out: + read_unlock(&policy_rwlock); + + return rc; +} + + /** * security_compute_av - Compute access vector decisions. * @ssid: source security identifier -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply related [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.2) 2008-08-05 5:55 ` [PATCH 1/3] " KaiGai Kohei @ 2008-08-05 12:53 ` Stephen Smalley 2008-08-06 10:05 ` KaiGai Kohei 2008-08-06 10:13 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.3) KaiGai Kohei 0 siblings, 2 replies; 97+ messages in thread From: Stephen Smalley @ 2008-08-05 12:53 UTC (permalink / raw) To: KaiGai Kohei; +Cc: jmorris, paul.moore, jbrindle, selinux On Tue, 2008-08-05 at 14:55 +0900, KaiGai Kohei wrote: > [1/3] thread-context-kernel.2.patch > It allows a multithreaded process to assign an individual > "more bounded" security context, and it also enables to > handle binary policy format version 24 in kernel space. > > Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> > -- > security/selinux/avc.c | 2 +- > security/selinux/hooks.c | 12 +- > security/selinux/include/security.h | 6 +- > security/selinux/ss/policydb.c | 244 +++++++++++++++++++++++++-- > security/selinux/ss/policydb.h | 4 + > security/selinux/ss/services.c | 324 +++++++++++++++++++++++++++++++---- > 6 files changed, 541 insertions(+), 51 deletions(-) > @@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) > { > char *key = NULL; > struct role_datum *role; > - int rc; > - __le32 buf[2]; > + int rc, to_read = 2; > + __le32 buf[3]; > u32 len; > > role = kzalloc(sizeof(*role), GFP_KERNEL); > @@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) > goto out; > } > > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + to_read = 3; > + > rc = next_entry(buf, fp, sizeof buf); Doesn't this also need to be changed to pass sizeof(buf[0]) * to_read rather than sizeof buf? Did you try loading an old policy on your new kernel? > if (rc < 0) > goto bad; > > len = le32_to_cpu(buf[0]); > role->value = le32_to_cpu(buf[1]); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + role->bounds = le32_to_cpu(buf[2]); > > key = kmalloc(len + 1, GFP_KERNEL); > if (!key) { > @@ -1465,6 +1502,181 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) > cat_read, > }; > > +static int user_bounds_sanity_check(void *key, void *datum, void *datap) > +{ > + struct user_datum *upper, *user; > + struct policydb *p = datap; > + int rc, depth = 0; > + > + upper = user = datum; > + while (upper->bounds) { > + struct ebitmap_node *node; > + unsigned long bit; > + > + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { > + printk(KERN_ERR > + "SELinux: user %s: too deep bounds\n", > + (char *) key); > + return -EINVAL; > + } > + > + upper = p->user_val_to_struct[upper->bounds - 1]; > + if (!upper) { > + printk(KERN_ERR > + "SELinux: user %s: broken boundary\n", > + (char *) key); > + return -EINVAL; > + } > + /* drop bounded bit from user->roles */ > +retry: > + ebitmap_for_each_positive_bit(&user->roles, node, bit) { > + if (ebitmap_get_bit(&upper->roles, bit)) > + continue; > + > + audit_log(current->audit_context, GFP_KERNEL, > + AUDIT_SELINUX_ERR, > + "boundary violation: " > + "user=%s role=%s bounds=%s", > + p->p_user_val_to_name[user->value - 1], > + p->p_role_val_to_name[bit], > + p->p_user_val_to_name[upper->value - 1]); > + > + rc = ebitmap_set_bit(&user->roles, bit, 0); > + if (rc) > + return rc; > + goto retry; I would just return an error and abort the policy load on any boundary violations detected at load time rather than mutating the policy. > + } > + } > + return 0; > +} > + > +static int role_bounds_sanity_check(void *key, void *datum, void *poldbp) > +{ > + struct role_datum *upper, *role; > + struct policydb *p = poldbp; > + int rc, depth = 0; > + > + upper = role = datum; > + while (upper->bounds) { > + struct ebitmap_node *node; > + unsigned long bit; > + > + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { > + printk(KERN_ERR > + "SELinux: role %s: too deep bounds\n", > + (char *) key); > + return -EINVAL; > + } > + > + upper = p->role_val_to_struct[upper->bounds - 1]; > + if (!upper) { > + printk(KERN_ERR > + "SELinux: role %s: broken boundary\n", > + (char *) key); > + return -EINVAL; > + } > + /* drop bounded bit from user->roles */ > +retry: > + ebitmap_for_each_positive_bit(&role->types, node, bit) { > + if (ebitmap_get_bit(&upper->types, bit)) > + continue; > + > + audit_log(current->audit_context, GFP_KERNEL, > + AUDIT_SELINUX_ERR, > + "boundary violation: " > + "role=%s type=%s bounds=%s", > + p->p_role_val_to_name[role->value - 1], > + p->p_type_val_to_name[bit], > + p->p_role_val_to_name[upper->value - 1]); > + > + rc = ebitmap_set_bit(&role->types, bit, 0); > + if (rc) > + return rc; > + goto retry; Ditto. > + } > + } > + return 0; > +} > + > +static int type_bounds_sanity_check(void *key, void *datum, void *poldbp) > +{ > + struct type_datum *upper, *type; > + struct policydb *p = poldbp; > + int rc, depth = 0; > + > + upper = type = datum; > + while (upper->bounds) { > + struct ebitmap *type_attrs, *upper_attrs; > + struct ebitmap_node *node; > + unsigned long bit; > + > + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { > + printk(KERN_ERR > + "SELinux: type %s: too deep boundary\n", > + (char *) key); > + return -EINVAL; > + } > + > + upper = p->type_val_to_struct[upper->bounds - 1]; > + if (!upper) { > + printk(KERN_ERR > + "SELinux: type %s: broken boundary\n", > + (char *) key); > + return -EINVAL; > + } > + /* drop bounded bit from type_attr_map */ > +retry: > + type_attrs = &p->type_attr_map[type->value - 1]; > + upper_attrs = &p->type_attr_map[upper->value - 1]; > + ebitmap_for_each_positive_bit(type_attrs, node, bit) { > + if (type->value == bit + 1) > + continue; > + > + if (ebitmap_get_bit(upper_attrs, bit)) > + continue; > + > + audit_log(current->audit_context, GFP_KERNEL, > + AUDIT_SELINUX_ERR, > + "boundary violation: " > + "type=%s attribute=%lu bounds=%s", > + p->p_type_val_to_name[type->value - 1], > + bit + 1, > + p->p_type_val_to_name[upper->value - 1]); > + > + rc = ebitmap_set_bit(type_attrs, bit, 0); > + if (rc) > + return rc; > + goto retry; And again. > diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h > index 4253370..320fa23 100644 > --- a/security/selinux/ss/policydb.h > +++ b/security/selinux/ss/policydb.h > @@ -61,6 +61,7 @@ struct class_datum { > @@ -81,12 +82,14 @@ struct role_allow { > /* Type attributes */ > struct type_datum { > u32 value; /* internal type value */ > + u32 bounds; /* boundary of type */ > unsigned char primary; /* primary name? */ > }; An aside: Possibly we should add the attribute flag at the same time and leave the type attributes in the type symtab in the kernel policy, as previously discussed. Then the kernel policy won't be lacking any information needed for analysis. Just need to make sure that context_to_sid doesn't accept attributes in the type field. > diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c > index dcc2e1c..a584c1d 100644 > --- a/security/selinux/ss/services.c > +++ b/security/selinux/ss/services.c <snip> > +static int cexpr_type_names_eval(struct ebitmap *names, u16 type_value) > +{ > + struct type_datum *type > + = policydb.type_val_to_struct[type_value - 1]; > + int bit; > + > + while (1) { You were using while (true) in the prior functions. We should be consistent one way or the other. > + bit = ebitmap_get_bit(names, type->value - 1); > + if (!bit) { > + if (type->value != type_value) > + audit_log(current->audit_context, > + GFP_ATOMIC, AUDIT_SELINUX_ERR, > + "boundary violation: " > + "type=%s bounded=%s", > + policydb.p_type_val_to_name[type_value - 1], > + policydb.p_type_val_to_name[type->value - 1]); > + break; > + } > + if (!type->bounds) > + break; > + type = policydb.type_val_to_struct[type->bounds - 1]; > + } I don't think we ought to be doing this checking here, if at all. Either we vet the constraints for internal consistency at load time, or we view the bounds as purely a limit on the TE rules and not necessarily on the constraints. > @@ -547,6 +644,167 @@ out: > return rc; > } > > +extern void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av); Move to avc.h, please. > + > +/* > + * security_boundary_permission - drops violated permissions > + * on boundary constraint. > + */ > +static void security_bounded_permission(u16 source_type, > + u16 target_type, > + u16 tclass, > + struct av_decision *avd) > +{ > + struct type_datum *source > + = policydb.type_val_to_struct[source_type - 1]; > + struct type_datum *target > + = policydb.type_val_to_struct[target_type - 1]; > + struct av_decision lo_avd; > + u32 masked = 0; > + > + if (source->bounds) { > + memset(&lo_avd, 0, sizeof(lo_avd)); > + > + type_attribute_compute_av(source->bounds, > + target_type, > + tclass, > + AVTAB_ALLOWED, > + &lo_avd); > + > + security_bounded_permission(source->bounds, > + target_type, > + tclass, &lo_avd); Since you always call type_attribute_compute_av and security_bounded_permission together, you might as well collapse them or wrap them with a single interface, right? Less chance then of the (stype, ttype, tclass) arguments being inconsistent. > + > +/* > + * security_bounded_transition - check whether the given > + * transition is directed to bounded, or not. > + * It returns 0, if @newsid is bounded by @oldsid. Otherwise, > + * 1 or any other error code can be returned. Seems somewhat confusing in its return code conventions. Either return a simple bool (true/false) or return 0 on success and -errno on failure. > + * > + * @oldsid : current security identifier > + * @newsid : destinated security identifier > + */ > +int security_bounded_transition(u32 old_sid, u32 new_sid) > +{ > + struct context *old_context, *new_context; > + struct type_datum *type; > + int index; > + int rc = -EINVAL; > + > + read_lock(&policy_rwlock); > + > + old_context = sidtab_search(&sidtab, old_sid); > + if (!old_context) { > + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", > + __func__, old_sid); > + goto out; > + } > + > + new_context = sidtab_search(&sidtab, new_sid); > + if (!new_context) { > + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", > + __func__, new_sid); > + goto out; > + } > + > + /* type/domain unchaned */ > + if (old_context->type == new_context->type) { > + rc = 0; > + goto out; > + } > + > + index = new_context->type; > + while (true) { > + type = policydb.type_val_to_struct[index - 1]; > + if (!type) { > + printk(KERN_ERR "SELinux: broken type boundary.\n"); > + rc = -EINVAL; > + break; > + } Are these legitimate runtime conditions or signs of kernel BUGs? val_to_struct arrays ought to be without holes unless I'm forgetting something. > + > + /* not bounded anymore */ > + if (!type->bounds) { > + rc = 1; > + break; > + } > + > + /* @newsid is bounded by @oldsid */ > + if (type->bounds == old_context->type) { > + rc = 0; > + break; > + } > + index = type->bounds; > + } > +out: > + read_unlock(&policy_rwlock); > + > + return rc; > +} > + > + > /** > * security_compute_av - Compute access vector decisions. > * @ssid: source security identifier > -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.2) 2008-08-05 12:53 ` Stephen Smalley @ 2008-08-06 10:05 ` KaiGai Kohei 2008-08-06 10:13 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.3) KaiGai Kohei 1 sibling, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-06 10:05 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux Thanks for your reviewing. Stephen Smalley wrote: > On Tue, 2008-08-05 at 14:55 +0900, KaiGai Kohei wrote: >> [1/3] thread-context-kernel.2.patch >> It allows a multithreaded process to assign an individual >> "more bounded" security context, and it also enables to >> handle binary policy format version 24 in kernel space. >> >> Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> >> -- >> security/selinux/avc.c | 2 +- >> security/selinux/hooks.c | 12 +- >> security/selinux/include/security.h | 6 +- >> security/selinux/ss/policydb.c | 244 +++++++++++++++++++++++++-- >> security/selinux/ss/policydb.h | 4 + >> security/selinux/ss/services.c | 324 +++++++++++++++++++++++++++++++---- >> 6 files changed, 541 insertions(+), 51 deletions(-) > > >> @@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) >> { >> char *key = NULL; >> struct role_datum *role; >> - int rc; >> - __le32 buf[2]; >> + int rc, to_read = 2; >> + __le32 buf[3]; >> u32 len; >> >> role = kzalloc(sizeof(*role), GFP_KERNEL); >> @@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) >> goto out; >> } >> >> + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) >> + to_read = 3; >> + >> rc = next_entry(buf, fp, sizeof buf); > > Doesn't this also need to be changed to pass sizeof(buf[0]) * to_read > rather than sizeof buf? > Did you try loading an old policy on your new kernel? It was a bug, fixed at revision 3. >> if (rc < 0) >> goto bad; >> >> len = le32_to_cpu(buf[0]); >> role->value = le32_to_cpu(buf[1]); >> + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) >> + role->bounds = le32_to_cpu(buf[2]); >> >> key = kmalloc(len + 1, GFP_KERNEL); >> if (!key) { > >> @@ -1465,6 +1502,181 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) >> cat_read, >> }; >> >> +static int user_bounds_sanity_check(void *key, void *datum, void *datap) >> +{ >> + struct user_datum *upper, *user; >> + struct policydb *p = datap; >> + int rc, depth = 0; >> + >> + upper = user = datum; >> + while (upper->bounds) { >> + struct ebitmap_node *node; >> + unsigned long bit; >> + >> + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { >> + printk(KERN_ERR >> + "SELinux: user %s: too deep bounds\n", >> + (char *) key); >> + return -EINVAL; >> + } >> + >> + upper = p->user_val_to_struct[upper->bounds - 1]; >> + if (!upper) { >> + printk(KERN_ERR >> + "SELinux: user %s: broken boundary\n", >> + (char *) key); >> + return -EINVAL; >> + } >> + /* drop bounded bit from user->roles */ >> +retry: >> + ebitmap_for_each_positive_bit(&user->roles, node, bit) { >> + if (ebitmap_get_bit(&upper->roles, bit)) >> + continue; >> + >> + audit_log(current->audit_context, GFP_KERNEL, >> + AUDIT_SELINUX_ERR, >> + "boundary violation: " >> + "user=%s role=%s bounds=%s", >> + p->p_user_val_to_name[user->value - 1], >> + p->p_role_val_to_name[bit], >> + p->p_user_val_to_name[upper->value - 1]); >> + >> + rc = ebitmap_set_bit(&user->roles, bit, 0); >> + if (rc) >> + return rc; >> + goto retry; > > I would just return an error and abort the policy load on any boundary > violations detected at load time rather than mutating the policy. OK, XXXX_bounds_sanity_check() are reworked to return an error and to abort the policy load. - snip - >> diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h >> index 4253370..320fa23 100644 >> --- a/security/selinux/ss/policydb.h >> +++ b/security/selinux/ss/policydb.h >> @@ -61,6 +61,7 @@ struct class_datum { >> @@ -81,12 +82,14 @@ struct role_allow { >> /* Type attributes */ >> struct type_datum { >> u32 value; /* internal type value */ >> + u32 bounds; /* boundary of type */ >> unsigned char primary; /* primary name? */ >> }; > > An aside: Possibly we should add the attribute flag at the same time > and leave the type attributes in the type symtab in the kernel policy, > as previously discussed. Then the kernel policy won't be lacking any > information needed for analysis. Just need to make sure that > context_to_sid doesn't accept attributes in the type field. I updated the libsepol to deliver attribute entries into kernel space, and kernel type_datum got a new member to show it is an attribute. In the policy format, if bounds field of type equals 0xffffffff means this entry is an attribute. However, it might be an ad-hoc usage. An alternatice way to deliver this information is to utilize the "primary" field of type to put various kind of flags, because it consume 32bit width to represent 1bit information, and rest of 31bit is free. This reworking also enables to fill up the hole of type_val_to_struct array, so we don't need to allocate it with kzalloc(). I reverted it to kmalloc() from kzalloc(). (In the previous revision, we can find NULL for attributes.) >> diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c >> index dcc2e1c..a584c1d 100644 >> --- a/security/selinux/ss/services.c >> +++ b/security/selinux/ss/services.c > <snip> >> +static int cexpr_type_names_eval(struct ebitmap *names, u16 type_value) >> +{ >> + struct type_datum *type >> + = policydb.type_val_to_struct[type_value - 1]; >> + int bit; >> + >> + while (1) { > > You were using while (true) in the prior functions. We should be > consistent one way or the other. Oops, I left it as integer value unawares. However, cexpr_XXXX_names_eval() were reverted on the next revision. :-) >> + bit = ebitmap_get_bit(names, type->value - 1); >> + if (!bit) { >> + if (type->value != type_value) >> + audit_log(current->audit_context, >> + GFP_ATOMIC, AUDIT_SELINUX_ERR, >> + "boundary violation: " >> + "type=%s bounded=%s", >> + policydb.p_type_val_to_name[type_value - 1], >> + policydb.p_type_val_to_name[type->value - 1]); >> + break; >> + } >> + if (!type->bounds) >> + break; >> + type = policydb.type_val_to_struct[type->bounds - 1]; >> + } > > I don't think we ought to be doing this checking here, if at all. > Either we vet the constraints for internal consistency at load time, or > we view the bounds as purely a limit on the TE rules and not necessarily > on the constraints. I think the boundary rules also should be applied on the constraints. For example, we can assume a case when a bounded type can have "mcssetcats" attribute, even if its bounds type does not have the attribute. It seems to me a bit strange behavior. I reverted it and put a code to check boundary consistency at the policy load time. (constraint_bounds_sanity_check()) >> @@ -547,6 +644,167 @@ out: >> return rc; >> } >> >> +extern void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av); > > Move to avc.h, please. OK, moved. >> + >> +/* >> + * security_boundary_permission - drops violated permissions >> + * on boundary constraint. >> + */ >> +static void security_bounded_permission(u16 source_type, >> + u16 target_type, >> + u16 tclass, >> + struct av_decision *avd) >> +{ >> + struct type_datum *source >> + = policydb.type_val_to_struct[source_type - 1]; >> + struct type_datum *target >> + = policydb.type_val_to_struct[target_type - 1]; >> + struct av_decision lo_avd; >> + u32 masked = 0; >> + >> + if (source->bounds) { >> + memset(&lo_avd, 0, sizeof(lo_avd)); >> + >> + type_attribute_compute_av(source->bounds, >> + target_type, >> + tclass, >> + AVTAB_ALLOWED, >> + &lo_avd); >> + >> + security_bounded_permission(source->bounds, >> + target_type, >> + tclass, &lo_avd); > > Since you always call type_attribute_compute_av and > security_bounded_permission together, you might as well collapse them or > wrap them with a single interface, right? Less chance then of the > (stype, ttype, tclass) arguments being inconsistent. I moved the boundary checks into type_attribute_compute_av(). >> + >> +/* >> + * security_bounded_transition - check whether the given >> + * transition is directed to bounded, or not. >> + * It returns 0, if @newsid is bounded by @oldsid. Otherwise, >> + * 1 or any other error code can be returned. > > Seems somewhat confusing in its return code conventions. > Either return a simple bool (true/false) or return 0 on success and > -errno on failure. OK, it was reworked to return 0 (success) or negative error code. >> + * >> + * @oldsid : current security identifier >> + * @newsid : destinated security identifier >> + */ >> +int security_bounded_transition(u32 old_sid, u32 new_sid) >> +{ >> + struct context *old_context, *new_context; >> + struct type_datum *type; >> + int index; >> + int rc = -EINVAL; >> + >> + read_lock(&policy_rwlock); >> + >> + old_context = sidtab_search(&sidtab, old_sid); >> + if (!old_context) { >> + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", >> + __func__, old_sid); >> + goto out; >> + } >> + >> + new_context = sidtab_search(&sidtab, new_sid); >> + if (!new_context) { >> + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", >> + __func__, new_sid); >> + goto out; >> + } >> + >> + /* type/domain unchaned */ >> + if (old_context->type == new_context->type) { >> + rc = 0; >> + goto out; >> + } >> + >> + index = new_context->type; >> + while (true) { >> + type = policydb.type_val_to_struct[index - 1]; >> + if (!type) { >> + printk(KERN_ERR "SELinux: broken type boundary.\n"); >> + rc = -EINVAL; >> + break; >> + } > > Are these legitimate runtime conditions or signs of kernel BUGs? > val_to_struct arrays ought to be without holes unless I'm forgetting > something. There was no guarantee the bounds does not indicate the hole of type_val_to_struct array due to attributes. However, delivering attribute into kernelspace kills the hole. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 1/3] Thread/Child-Domain Assignment (rev.3) 2008-08-05 12:53 ` Stephen Smalley 2008-08-06 10:05 ` KaiGai Kohei @ 2008-08-06 10:13 ` KaiGai Kohei 2008-08-14 7:38 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.4) KaiGai Kohei 1 sibling, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-08-06 10:13 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux The following patch is the revised one for the kernel. It is reworked for anything reviewed, and add a support to represent attributes of types in kernelspace. [1/3] thread-context-kernel.3.patch It allows a multithreaded process to assign an individual "more bounded" security context, and it also enables to handle binary policy format version 24 in kernel space. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> -- security/selinux/avc.c | 2 +- security/selinux/hooks.c | 12 +- security/selinux/include/avc.h | 4 + security/selinux/include/security.h | 16 ++- security/selinux/ss/policydb.c | 322 +++++++++++++++++++++++++++++++++-- security/selinux/ss/policydb.h | 5 + security/selinux/ss/services.c | 228 +++++++++++++++++++++---- 7 files changed, 541 insertions(+), 48 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 114b4b4..cb30c7e 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -136,7 +136,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) * @tclass: target security class * @av: access vector */ -static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) { const char **common_pts = NULL; u32 common_base = 0; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index bc1c3ae..2c2fad3 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5180,8 +5180,12 @@ static int selinux_setprocattr(struct task_struct *p, if (sid == 0) return -EINVAL; - - /* Only allow single threaded processes to change context */ + /* + * SELinux allows to change context in the following case only. + * - Single threaded processes. + * - Multi threaded processes intend to change its context into + * more restricted domain (defined by TYPEBOUNDS statement). + */ if (atomic_read(&p->mm->mm_users) != 1) { struct task_struct *g, *t; struct mm_struct *mm = p->mm; @@ -5189,11 +5193,15 @@ static int selinux_setprocattr(struct task_struct *p, do_each_thread(g, t) { if (t->mm == mm && t != p) { read_unlock(&tasklist_lock); + if (!security_bounded_transition(tsec->sid, sid)) + goto boundary_ok; + return -EPERM; } } while_each_thread(g, t); read_unlock(&tasklist_lock); } +boundary_ok: /* Check permissions for the transition. */ error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 8e23d7a..7c4caf2 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -12,6 +12,7 @@ #include <linux/kdev_t.h> #include <linux/spinlock.h> #include <linux/init.h> +#include <linux/audit.h> #include <linux/in6.h> #include <linux/path.h> #include <asm/system.h> @@ -127,6 +128,9 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, u32 events, u32 ssid, u32 tsid, u16 tclass, u32 perms); +/* Shows permission in human readable form */ +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av); + /* Exported to selinuxfs */ int avc_get_hash_stats(char *page); extern unsigned int avc_cache_threshold; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index ad30ac4..3773eb6 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -27,13 +27,14 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY #endif #define CONTEXT_MNT 0x01 @@ -62,6 +63,17 @@ enum { extern int selinux_policycap_netpeer; extern int selinux_policycap_openperm; +/* boundary related definitions */ +#define POLICYDB_BOUNDS_MAXDEPTH 4 +#define POLICYDB_BOUNDS_ATTRIBUTE_FLAG 0xffffffff +/* + * NOTE: When "bounds" field equals POLICYDB_BOUNDS_ATTRIBUTE_FLAG, + * it means this entry is attribute, not a normal type. + * Boundary feature also requires to deliver attribute info into + * kernel space, because it has to notice boundary violation related + * to type/attribute relationships. + */ + int security_load_policy(void *data, size_t len); int security_policycap_supported(unsigned int req_cap); @@ -112,6 +124,8 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen, int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass); +int security_bounded_transition(u32 oldsid, u32 newsid); + int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 84f8cc7..2e5113e 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -30,6 +30,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/audit.h> #include "security.h" #include "policydb.h" @@ -116,7 +117,12 @@ static struct policydb_compat_info policydb_compat[] = { .version = POLICYDB_VERSION_PERMISSIVE, .sym_num = SYM_NUM, .ocon_num = OCON_NUM, - } + }, + { + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -254,7 +260,9 @@ static int role_index(void *key, void *datum, void *datap) role = datum; p = datap; - if (!role->value || role->value > p->p_roles.nprim) + if (!role->value + || role->value > p->p_roles.nprim + || role->bounds > p->p_roles.nprim) return -EINVAL; p->p_role_val_to_name[role->value - 1] = key; p->role_val_to_struct[role->value - 1] = role; @@ -270,9 +278,12 @@ static int type_index(void *key, void *datum, void *datap) p = datap; if (typdatum->primary) { - if (!typdatum->value || typdatum->value > p->p_types.nprim) + if (!typdatum->value + || typdatum->value > p->p_types.nprim + || typdatum->bounds > p->p_types.nprim) return -EINVAL; p->p_type_val_to_name[typdatum->value - 1] = key; + p->type_val_to_struct[typdatum->value - 1] = typdatum; } return 0; @@ -285,7 +296,9 @@ static int user_index(void *key, void *datum, void *datap) usrdatum = datum; p = datap; - if (!usrdatum->value || usrdatum->value > p->p_users.nprim) + if (!usrdatum->value + || usrdatum->value > p->p_users.nprim + || usrdatum->bounds > p->p_users.nprim) return -EINVAL; p->p_user_val_to_name[usrdatum->value - 1] = key; p->user_val_to_struct[usrdatum->value - 1] = usrdatum; @@ -438,6 +451,14 @@ static int policydb_index_others(struct policydb *p) goto out; } + p->type_val_to_struct = + kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)), + GFP_KERNEL); + if (!p->type_val_to_struct) { + rc = -ENOMEM; + goto out; + } + if (cond_init_bool_indexes(p)) { rc = -ENOMEM; goto out; @@ -625,6 +646,7 @@ void policydb_destroy(struct policydb *p) kfree(p->class_val_to_struct); kfree(p->role_val_to_struct); kfree(p->user_val_to_struct); + kfree(p->type_val_to_struct); avtab_destroy(&p->te_avtab); @@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct role_datum *role; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; role = kzalloc(sizeof(*role), GFP_KERNEL); @@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + role->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1236,8 +1263,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct type_datum *typdatum; - int rc; - __le32 buf[3]; + int rc, to_read = 3; + __le32 buf[4]; u32 len; typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); @@ -1246,13 +1273,24 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) return rc; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 4; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); typdatum->value = le32_to_cpu(buf[1]); typdatum->primary = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { + u32 bounds = le32_to_cpu(buf[3]); + + if (bounds == POLICYDB_BOUNDS_ATTRIBUTE_FLAG) + typdatum->attribute = 1; + else + typdatum->bounds = bounds; + } key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1309,8 +1347,8 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct user_datum *usrdatum; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); @@ -1319,12 +1357,17 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + usrdatum->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1465,6 +1508,255 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) cat_read, }; +static int user_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct user_datum *upper, *user = datum; + struct policydb *p = datap; + int depth = 0; + + upper = user = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR + "SELinux: user %s: too deep boundary", + (char *) key); + return -EINVAL; + } + + upper = p->user_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&user->roles, node, bit) { + if (ebitmap_get_bit(&upper->roles, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "user=%s role=%s bounds=%s\n", + p->p_user_val_to_name[user->value - 1], + p->p_role_val_to_name[bit], + p->p_user_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int role_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct role_datum *upper, *role; + struct policydb *p = datap; + int depth = 0; + + upper = role = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR + "SELinux: role %s: too deep bounds\n", + (char *) key); + return -EINVAL; + } + + upper = p->role_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&role->types, node, bit) { + if (ebitmap_get_bit(&upper->types, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "role=%s type=%s bounds=%s\n", + p->p_role_val_to_name[role->value - 1], + p->p_type_val_to_name[bit], + p->p_role_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int type_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct type_datum *upper, *type; + struct policydb *p = datap; + int depth = 0; + + upper = type = datum; + while (upper->bounds) { + struct ebitmap *type_attrs, *upper_attrs; + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR + "SELinux: type %s: too deep boundary\n", + (char *) key); + return -EINVAL; + } + + upper = p->type_val_to_struct[upper->bounds - 1]; + if (upper->attribute) { + printk(KERN_ERR + "SELinux: type %s: bounded by attribute %s", + (char *) key, + p->p_type_val_to_name[upper->value - 1]); + return -EINVAL; + } + + type_attrs = &p->type_attr_map[type->value - 1]; + upper_attrs = &p->type_attr_map[upper->value - 1]; + + ebitmap_for_each_positive_bit(type_attrs, node, bit) { + if (bit == type->value - 1) + continue; + + if (ebitmap_get_bit(upper_attrs, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "type=%s attribute=%s bounds=%s\n", + p->p_type_val_to_name[type->value - 1], + p->p_type_val_to_name[bit], + p->p_type_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int constraint_bounds_sanity_check(struct policydb *p, + struct constraint_expr *cexpr) +{ + struct user_datum *user; + struct role_datum *role; + struct type_datum *type; + struct ebitmap_node *node; + unsigned long bit; + + /* no need to check boundary constraint */ + if (cexpr->expr_type != CEXPR_NAMES) + return 0; + + ebitmap_for_each_positive_bit(&cexpr->names, node, bit) { + if (cexpr->attr & CEXPR_USER) { + if (bit >= p->p_users.nprim) + goto broken_ebitmap; + + user = p->user_val_to_struct[bit]; + if (user->bounds && + !ebitmap_get_bit(&cexpr->names, user->bounds - 1)) { + printk(KERN_ERR + "SELinux: boundary violated cexpr:" + " CEXPR_NAMES: user:%s bounds:%s\n", + p->p_user_val_to_name[user->value - 1], + p->p_user_val_to_name[user->bounds - 1]); + return -EINVAL; + } + } else if (cexpr->attr & CEXPR_ROLE) { + if (bit >= p->p_roles.nprim) + goto broken_ebitmap; + + role = p->role_val_to_struct[bit]; + if (role->bounds && + !ebitmap_get_bit(&cexpr->names, role->bounds - 1)) { + printk(KERN_ERR + "SELinux: boundary violated cexpr:" + " CEXPR_NAMES: role:%s bounds:%s\n", + p->p_role_val_to_name[role->value - 1], + p->p_role_val_to_name[role->bounds - 1]); + return -EINVAL; + } + } else if (cexpr->attr & CEXPR_TYPE) { + if (bit >= p->p_types.nprim) + goto broken_ebitmap; + + type = p->type_val_to_struct[bit]; + if (type->bounds && + !ebitmap_get_bit(&cexpr->names, type->bounds - 1)) { + printk(KERN_ERR + "SELinux: boundary violated cexpr:" + " CEXPR_NAMES: type:%s bounds:%s\n", + p->p_type_val_to_name[type->value - 1], + p->p_type_val_to_name[type->bounds - 1]); + return -EINVAL; + } + } else { + BUG(); + } + } + return 0; + +broken_ebitmap: + printk(KERN_ERR + "SELinux: broken constraint expr: " + "expr_type=CEXPR_NAMES attr=%08x op=%u " + "names=broken ebitmap\n", + cexpr->attr, cexpr->op); + return -EINVAL; +} + +static int class_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct policydb *p = datap; + struct class_datum *tclass = datum; + struct constraint_node *constraint; + struct constraint_expr *cexpr; + int rc; + + for (constraint = tclass->constraints; + constraint; + constraint = constraint->next) { + for (cexpr = constraint->expr; + cexpr; + cexpr = cexpr->next) { + rc = constraint_bounds_sanity_check(p, cexpr); + if (rc) + return rc; + } + } + return 0; +} + +static int policydb_bounds_sanity_check(struct policydb *p) +{ + int rc; + + if (p->policyvers < POLICYDB_VERSION_BOUNDARY) + return 0; + + rc = hashtab_map(p->p_users.table, + user_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_roles.table, + role_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_types.table, + type_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_classes.table, + class_bounds_sanity_check, p); + if (rc) + return rc; + + return 0; +} + extern int ss_initialized; /* @@ -1960,6 +2252,10 @@ int policydb_read(struct policydb *p, void *fp) goto bad; } + rc = policydb_bounds_sanity_check(p); + if (rc) + goto bad; + rc = 0; out: return rc; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 4253370..55152d4 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -61,6 +61,7 @@ struct class_datum { /* Role attributes */ struct role_datum { u32 value; /* internal role value */ + u32 bounds; /* boundary of role */ struct ebitmap dominates; /* set of roles dominated by this role */ struct ebitmap types; /* set of authorized types for role */ }; @@ -81,12 +82,15 @@ struct role_allow { /* Type attributes */ struct type_datum { u32 value; /* internal type value */ + u32 bounds; /* boundary of type */ unsigned char primary; /* primary name? */ + unsigned char attribute;/* attribute ?*/ }; /* User attributes */ struct user_datum { u32 value; /* internal user value */ + u32 bounds; /* bounds of user */ struct ebitmap roles; /* set of authorized roles for user */ struct mls_range range; /* MLS range (min - max) for user */ struct mls_level dfltlevel; /* default login MLS level for user */ @@ -209,6 +213,7 @@ struct policydb { struct class_datum **class_val_to_struct; struct role_datum **role_val_to_struct; struct user_datum **user_val_to_struct; + struct type_datum **type_val_to_struct; /* type enforcement access vectors and transitions */ struct avtab te_avtab; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index dcc2e1c..2705b71 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -95,7 +95,11 @@ static u32 latest_granting; /* Forward declaration. */ static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len); - +static void type_attribute_compute_av(u16 source_type, + u16 target_type, + u16 tclass, + u16 specified, + struct av_decision *avd); /* * Return the boolean value of a constraint expression * when it is applied to the specified source and target @@ -282,6 +286,131 @@ mls_ops: } /* + * security_boundary_permission - drops violated permissions + * on boundary constraint. + */ +static void type_attribute_bounds_av(u16 source_type, + u16 target_type, + u16 tclass, + struct av_decision *avd) +{ + struct type_datum *source + = policydb.type_val_to_struct[source_type - 1]; + struct type_datum *target + = policydb.type_val_to_struct[target_type - 1]; + struct av_decision lo_avd; + u32 masked = 0; + + if (source->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + type_attribute_compute_av(source->bounds, + target_type, + tclass, + AVTAB_ALLOWED, + &lo_avd); + + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + type_attribute_compute_av(source_type, + target->bounds, + tclass, + AVTAB_ALLOWED, + &lo_avd); + + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (source->bounds && target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + type_attribute_compute_av(source->bounds, + target->bounds, + tclass, + AVTAB_ALLOWED, + &lo_avd); + + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (masked) { + struct audit_buffer *ab; + + /* mask violated permissions */ + avd->allowed &= ~masked; + + /* notice to userspace via audit message */ + ab = audit_log_start(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR); + if (!ab) + return; + + audit_log_format(ab, "av boundary violation: " + "source=%s target=%s tclass=%s", + policydb.p_type_val_to_name[source_type - 1], + policydb.p_type_val_to_name[target_type - 1], + policydb.p_class_val_to_name[tclass - 1]); + avc_dump_av(ab, tclass, masked); + audit_log_end(ab); + } +} + +static void type_attribute_compute_av(u16 source_type, + u16 target_type, + u16 tclass, + u16 specified, + struct av_decision *avd) +{ + struct avtab_key avkey; + struct avtab_node *node; + struct ebitmap *sattr, *tattr; + struct ebitmap_node *snode, *tnode; + unsigned int sbit, tbit; + + avkey.target_class = tclass; + avkey.specified = specified & AVTAB_AV; + sattr = &policydb.type_attr_map[source_type - 1]; + tattr = &policydb.type_attr_map[target_type - 1]; + + ebitmap_for_each_positive_bit(sattr, snode, sbit) { + ebitmap_for_each_positive_bit(tattr, tnode, tbit) { + avkey.source_type = sbit + 1; + avkey.target_type = tbit + 1; + + for (node = avtab_search_node(&policydb.te_avtab, &avkey); + node != NULL; + node = avtab_search_node_next(node, avkey.specified)) { + if (node->key.specified == AVTAB_ALLOWED) + avd->allowed |= node->datum.data; + else if (node->key.specified == AVTAB_AUDITALLOW) + avd->auditallow |= node->datum.data; + else if (node->key.specified == AVTAB_AUDITDENY) + avd->auditdeny &= node->datum.data; + } + /* Check conditional av table for additional permissions */ + cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); + } + } + + /* + * If the given source and target types have boundary + * constraint, lazy checks have to mask any violated + * permission and notice it to userspace via audit. + */ + type_attribute_bounds_av(source_type, target_type, tclass, avd); +} + +/* * Compute access vectors based on a context structure pair for * the permissions in a particular class. */ @@ -293,13 +422,8 @@ static int context_struct_compute_av(struct context *scontext, { struct constraint_node *constraint; struct role_allow *ra; - struct avtab_key avkey; - struct avtab_node *node; struct class_datum *tclass_datum; - struct ebitmap *sattr, *tattr; - struct ebitmap_node *snode, *tnode; const struct selinux_class_perm *kdefs = &selinux_class_perm; - unsigned int i, j; /* * Remap extended Netlink classes for old policy versions. @@ -355,30 +479,9 @@ static int context_struct_compute_av(struct context *scontext, * If a specific type enforcement rule was defined for * this permission check, then use it. */ - avkey.target_class = tclass; - avkey.specified = AVTAB_AV; - sattr = &policydb.type_attr_map[scontext->type - 1]; - tattr = &policydb.type_attr_map[tcontext->type - 1]; - ebitmap_for_each_positive_bit(sattr, snode, i) { - ebitmap_for_each_positive_bit(tattr, tnode, j) { - avkey.source_type = i + 1; - avkey.target_type = j + 1; - for (node = avtab_search_node(&policydb.te_avtab, &avkey); - node != NULL; - node = avtab_search_node_next(node, avkey.specified)) { - if (node->key.specified == AVTAB_ALLOWED) - avd->allowed |= node->datum.data; - else if (node->key.specified == AVTAB_AUDITALLOW) - avd->auditallow |= node->datum.data; - else if (node->key.specified == AVTAB_AUDITDENY) - avd->auditdeny &= node->datum.data; - } - - /* Check conditional av table for additional permissions */ - cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); - - } - } + type_attribute_compute_av(scontext->type, + tcontext->type, + tclass, AVTAB_AV, avd); /* * Remove any permissions prohibited by a constraint (this includes @@ -547,6 +650,69 @@ out: return rc; } +/* + * security_bounded_transition - check whether the given + * transition is directed to bounded, or not. + * It returns 0, if @newsid is bounded by @oldsid. + * Otherwise, it returns error code. + * + * @oldsid : current security identifier + * @newsid : destinated security identifier + */ +int security_bounded_transition(u32 old_sid, u32 new_sid) +{ + struct context *old_context, *new_context; + struct type_datum *type; + int index; + int rc = -EINVAL; + + read_lock(&policy_rwlock); + + old_context = sidtab_search(&sidtab, old_sid); + if (!old_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, old_sid); + goto out; + } + + new_context = sidtab_search(&sidtab, new_sid); + if (!new_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, new_sid); + goto out; + } + + /* type/domain unchaned */ + if (old_context->type == new_context->type) { + rc = 0; + goto out; + } + + index = new_context->type; + while (true) { + type = policydb.type_val_to_struct[index - 1]; + BUG_ON(!type); + + /* not bounded anymore */ + if (!type->bounds) { + rc = -EPERM; + break; + } + + /* @newsid is bounded by @oldsid */ + if (type->bounds == old_context->type) { + rc = 0; + break; + } + index = type->bounds; + } +out: + read_unlock(&policy_rwlock); + + return rc; +} + + /** * security_compute_av - Compute access vector decisions. * @ssid: source security identifier @@ -795,7 +961,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, *p++ = 0; typdatum = hashtab_search(policydb.p_types.table, scontextp); - if (!typdatum) + if (!typdatum || typdatum->attribute) goto out_unlock; context.type = typdatum->value; -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply related [flat|nested] 97+ messages in thread
* [PATCH 1/3] Thread/Child-Domain Assignment (rev.4) 2008-08-06 10:13 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.3) KaiGai Kohei @ 2008-08-14 7:38 ` KaiGai Kohei 2008-08-15 18:13 ` Stephen Smalley 0 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-08-14 7:38 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux The following patch is the revised one for the kernel. Joshua suggested it is not necessary to bound type attributes based on boundary relationship, and I also agreed this opinion. The related code is dropped in this patch, and several code cleanup contained. This change makes it unnecessary to deliver text represented attributes into kernelspace, but related codes are still remained for ... | Stephen Smalley wrote: | An aside: Possibly we should add the attribute flag at the same time | and leave the type attributes in the type symtab in the kernel policy, | as previously discussed. Then the kernel policy won't be lacking any | information needed for analysis. Just need to make sure that | context_to_sid doesn't accept attributes in the type field. Stephen, could you point out the previous discussion? Thanks, [1/3] thread-context-kernel.4.patch It allows a multithreaded process to assign an individual "more bounded" security context, and it also enables to handle binary policy format version 24 in kernel space. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> -- security/selinux/avc.c | 2 +- security/selinux/hooks.c | 11 +- security/selinux/include/avc.h | 4 + security/selinux/include/security.h | 16 ++- security/selinux/ss/policydb.c | 298 +++++++++++++++++++++++++++++++++-- security/selinux/ss/policydb.h | 5 + security/selinux/ss/services.c | 227 +++++++++++++++++++++++---- 7 files changed, 515 insertions(+), 48 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 114b4b4..cb30c7e 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -136,7 +136,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) * @tclass: target security class * @av: access vector */ -static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) { const char **common_pts = NULL; u32 common_base = 0; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index bc1c3ae..0201227 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5180,9 +5180,14 @@ static int selinux_setprocattr(struct task_struct *p, if (sid == 0) return -EINVAL; - - /* Only allow single threaded processes to change context */ - if (atomic_read(&p->mm->mm_users) != 1) { + /* + * SELinux allows to change context in the following case only. + * - Single threaded processes. + * - Multi threaded processes intend to change its context into + * more restricted domain (defined by TYPEBOUNDS statement). + */ + if (atomic_read(&p->mm->mm_users) != 1 + && security_bounded_transition(tsec->sid, sid) != 0) { struct task_struct *g, *t; struct mm_struct *mm = p->mm; read_lock(&tasklist_lock); diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 8e23d7a..7c4caf2 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -12,6 +12,7 @@ #include <linux/kdev_t.h> #include <linux/spinlock.h> #include <linux/init.h> +#include <linux/audit.h> #include <linux/in6.h> #include <linux/path.h> #include <asm/system.h> @@ -127,6 +128,9 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, u32 events, u32 ssid, u32 tsid, u16 tclass, u32 perms); +/* Shows permission in human readable form */ +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av); + /* Exported to selinuxfs */ int avc_get_hash_stats(char *page); extern unsigned int avc_cache_threshold; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index ad30ac4..3773eb6 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -27,13 +27,14 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY #endif #define CONTEXT_MNT 0x01 @@ -62,6 +63,17 @@ enum { extern int selinux_policycap_netpeer; extern int selinux_policycap_openperm; +/* boundary related definitions */ +#define POLICYDB_BOUNDS_MAXDEPTH 4 +#define POLICYDB_BOUNDS_ATTRIBUTE_FLAG 0xffffffff +/* + * NOTE: When "bounds" field equals POLICYDB_BOUNDS_ATTRIBUTE_FLAG, + * it means this entry is attribute, not a normal type. + * Boundary feature also requires to deliver attribute info into + * kernel space, because it has to notice boundary violation related + * to type/attribute relationships. + */ + int security_load_policy(void *data, size_t len); int security_policycap_supported(unsigned int req_cap); @@ -112,6 +124,8 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen, int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass); +int security_bounded_transition(u32 oldsid, u32 newsid); + int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 84f8cc7..f134322 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -30,6 +30,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/audit.h> #include "security.h" #include "policydb.h" @@ -116,7 +117,12 @@ static struct policydb_compat_info policydb_compat[] = { .version = POLICYDB_VERSION_PERMISSIVE, .sym_num = SYM_NUM, .ocon_num = OCON_NUM, - } + }, + { + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -254,7 +260,9 @@ static int role_index(void *key, void *datum, void *datap) role = datum; p = datap; - if (!role->value || role->value > p->p_roles.nprim) + if (!role->value + || role->value > p->p_roles.nprim + || role->bounds > p->p_roles.nprim) return -EINVAL; p->p_role_val_to_name[role->value - 1] = key; p->role_val_to_struct[role->value - 1] = role; @@ -270,9 +278,12 @@ static int type_index(void *key, void *datum, void *datap) p = datap; if (typdatum->primary) { - if (!typdatum->value || typdatum->value > p->p_types.nprim) + if (!typdatum->value + || typdatum->value > p->p_types.nprim + || typdatum->bounds > p->p_types.nprim) return -EINVAL; p->p_type_val_to_name[typdatum->value - 1] = key; + p->type_val_to_struct[typdatum->value - 1] = typdatum; } return 0; @@ -285,7 +296,9 @@ static int user_index(void *key, void *datum, void *datap) usrdatum = datum; p = datap; - if (!usrdatum->value || usrdatum->value > p->p_users.nprim) + if (!usrdatum->value + || usrdatum->value > p->p_users.nprim + || usrdatum->bounds > p->p_users.nprim) return -EINVAL; p->p_user_val_to_name[usrdatum->value - 1] = key; p->user_val_to_struct[usrdatum->value - 1] = usrdatum; @@ -438,6 +451,14 @@ static int policydb_index_others(struct policydb *p) goto out; } + p->type_val_to_struct = + kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)), + GFP_KERNEL); + if (!p->type_val_to_struct) { + rc = -ENOMEM; + goto out; + } + if (cond_init_bool_indexes(p)) { rc = -ENOMEM; goto out; @@ -625,6 +646,7 @@ void policydb_destroy(struct policydb *p) kfree(p->class_val_to_struct); kfree(p->role_val_to_struct); kfree(p->user_val_to_struct); + kfree(p->type_val_to_struct); avtab_destroy(&p->te_avtab); @@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct role_datum *role; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; role = kzalloc(sizeof(*role), GFP_KERNEL); @@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + role->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1236,8 +1263,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct type_datum *typdatum; - int rc; - __le32 buf[3]; + int rc, to_read = 3; + __le32 buf[4]; u32 len; typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); @@ -1246,13 +1273,24 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) return rc; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 4; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); typdatum->value = le32_to_cpu(buf[1]); typdatum->primary = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { + u32 bounds = le32_to_cpu(buf[3]); + + if (bounds == POLICYDB_BOUNDS_ATTRIBUTE_FLAG) + typdatum->attribute = 1; + else + typdatum->bounds = bounds; + } key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1309,8 +1347,8 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct user_datum *usrdatum; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); @@ -1319,12 +1357,17 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + usrdatum->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1465,6 +1508,231 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) cat_read, }; +static int user_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct user_datum *upper, *user = datum; + struct policydb *p = datap; + int depth = 0; + + upper = user = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: user %s: " + "too deep or looped boundary", + (char *) key); + return -EINVAL; + } + + upper = p->user_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&user->roles, node, bit) { + if (ebitmap_get_bit(&upper->roles, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "user=%s role=%s bounds=%s\n", + p->p_user_val_to_name[user->value - 1], + p->p_role_val_to_name[bit], + p->p_user_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int role_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct role_datum *upper, *role; + struct policydb *p = datap; + int depth = 0; + + upper = role = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: role %s: " + "too deep or looped bounds\n", + (char *) key); + return -EINVAL; + } + + upper = p->role_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&role->types, node, bit) { + if (ebitmap_get_bit(&upper->types, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "role=%s type=%s bounds=%s\n", + p->p_role_val_to_name[role->value - 1], + p->p_type_val_to_name[bit], + p->p_role_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int type_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct type_datum *upper, *type; + struct policydb *p = datap; + int depth = 0; + + upper = type = datum; + while (upper->bounds) { + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: type %s: " + "too deep or looped boundary\n", + (char *) key); + return -EINVAL; + } + + upper = p->type_val_to_struct[upper->bounds - 1]; + if (upper->attribute) { + printk(KERN_ERR "SELinux: type %s: " + "bounded by attribute %s", + (char *) key, + p->p_type_val_to_name[upper->value - 1]); + return -EINVAL; + } + } + + return 0; +} + +static int constraint_bounds_sanity_check(struct policydb *p, + struct constraint_expr *cexpr) +{ + struct user_datum *user; + struct role_datum *role; + struct type_datum *type; + struct ebitmap_node *node; + unsigned long bit; + + /* no need to check boundary constraint */ + if (cexpr->expr_type != CEXPR_NAMES) + return 0; + + ebitmap_for_each_positive_bit(&cexpr->names, node, bit) { + if (cexpr->attr & CEXPR_USER) { + if (bit >= p->p_users.nprim) + goto broken_ebitmap; + + user = p->user_val_to_struct[bit]; + if (user->bounds && + !ebitmap_get_bit(&cexpr->names, user->bounds - 1)) { + printk(KERN_ERR + "SELinux: boundary violated cexpr:" + " CEXPR_NAMES: user:%s bounds:%s\n", + p->p_user_val_to_name[user->value - 1], + p->p_user_val_to_name[user->bounds - 1]); + return -EINVAL; + } + } else if (cexpr->attr & CEXPR_ROLE) { + if (bit >= p->p_roles.nprim) + goto broken_ebitmap; + + role = p->role_val_to_struct[bit]; + if (role->bounds && + !ebitmap_get_bit(&cexpr->names, role->bounds - 1)) { + printk(KERN_ERR + "SELinux: boundary violated cexpr:" + " CEXPR_NAMES: role:%s bounds:%s\n", + p->p_role_val_to_name[role->value - 1], + p->p_role_val_to_name[role->bounds - 1]); + return -EINVAL; + } + } else if (cexpr->attr & CEXPR_TYPE) { + if (bit >= p->p_types.nprim) + goto broken_ebitmap; + + type = p->type_val_to_struct[bit]; + if (type->bounds && + !ebitmap_get_bit(&cexpr->names, type->bounds - 1)) { + printk(KERN_ERR + "SELinux: boundary violated cexpr:" + " CEXPR_NAMES: type:%s bounds:%s\n", + p->p_type_val_to_name[type->value - 1], + p->p_type_val_to_name[type->bounds - 1]); + return -EINVAL; + } + } else { + BUG(); + } + } + return 0; + +broken_ebitmap: + printk(KERN_ERR + "SELinux: broken constraint expr: " + "expr_type=CEXPR_NAMES attr=%08x op=%u " + "names=broken ebitmap\n", + cexpr->attr, cexpr->op); + return -EINVAL; +} + +static int class_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct policydb *p = datap; + struct class_datum *tclass = datum; + struct constraint_node *constraint; + struct constraint_expr *cexpr; + int rc; + + for (constraint = tclass->constraints; + constraint; + constraint = constraint->next) { + for (cexpr = constraint->expr; + cexpr; + cexpr = cexpr->next) { + rc = constraint_bounds_sanity_check(p, cexpr); + if (rc) + return rc; + } + } + return 0; +} + +static int policydb_bounds_sanity_check(struct policydb *p) +{ + int rc; + + if (p->policyvers < POLICYDB_VERSION_BOUNDARY) + return 0; + + rc = hashtab_map(p->p_users.table, + user_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_roles.table, + role_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_types.table, + type_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_classes.table, + class_bounds_sanity_check, p); + if (rc) + return rc; + + return 0; +} + extern int ss_initialized; /* @@ -1960,6 +2228,10 @@ int policydb_read(struct policydb *p, void *fp) goto bad; } + rc = policydb_bounds_sanity_check(p); + if (rc) + goto bad; + rc = 0; out: return rc; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 4253370..55152d4 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -61,6 +61,7 @@ struct class_datum { /* Role attributes */ struct role_datum { u32 value; /* internal role value */ + u32 bounds; /* boundary of role */ struct ebitmap dominates; /* set of roles dominated by this role */ struct ebitmap types; /* set of authorized types for role */ }; @@ -81,12 +82,15 @@ struct role_allow { /* Type attributes */ struct type_datum { u32 value; /* internal type value */ + u32 bounds; /* boundary of type */ unsigned char primary; /* primary name? */ + unsigned char attribute;/* attribute ?*/ }; /* User attributes */ struct user_datum { u32 value; /* internal user value */ + u32 bounds; /* bounds of user */ struct ebitmap roles; /* set of authorized roles for user */ struct mls_range range; /* MLS range (min - max) for user */ struct mls_level dfltlevel; /* default login MLS level for user */ @@ -209,6 +213,7 @@ struct policydb { struct class_datum **class_val_to_struct; struct role_datum **role_val_to_struct; struct user_datum **user_val_to_struct; + struct type_datum **type_val_to_struct; /* type enforcement access vectors and transitions */ struct avtab te_avtab; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index dcc2e1c..a1f894e 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -96,6 +96,11 @@ static u32 latest_granting; static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len); +static void type_attribute_compute_av(u16 source_type, + u16 target_type, + u16 tclass, + u16 specified, + struct av_decision *avd); /* * Return the boolean value of a constraint expression * when it is applied to the specified source and target @@ -282,6 +287,131 @@ mls_ops: } /* + * security_boundary_permission - drops violated permissions + * on boundary constraint. + */ +static void type_attribute_bounds_av(u16 source_type, + u16 target_type, + u16 tclass, + struct av_decision *avd) +{ + struct type_datum *source + = policydb.type_val_to_struct[source_type - 1]; + struct type_datum *target + = policydb.type_val_to_struct[target_type - 1]; + struct av_decision lo_avd; + u32 masked = 0; + + if (source->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + type_attribute_compute_av(source->bounds, + target_type, + tclass, + AVTAB_ALLOWED, + &lo_avd); + + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + type_attribute_compute_av(source_type, + target->bounds, + tclass, + AVTAB_ALLOWED, + &lo_avd); + + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (source->bounds && target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + type_attribute_compute_av(source->bounds, + target->bounds, + tclass, + AVTAB_ALLOWED, + &lo_avd); + + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (masked) { + struct audit_buffer *ab; + + /* mask violated permissions */ + avd->allowed &= ~masked; + + /* notice to userspace via audit message */ + ab = audit_log_start(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR); + if (!ab) + return; + + audit_log_format(ab, "av boundary violation: " + "source=%s target=%s tclass=%s", + policydb.p_type_val_to_name[source_type - 1], + policydb.p_type_val_to_name[target_type - 1], + policydb.p_class_val_to_name[tclass - 1]); + avc_dump_av(ab, tclass, masked); + audit_log_end(ab); + } +} + +static void type_attribute_compute_av(u16 source_type, + u16 target_type, + u16 tclass, + u16 specified, + struct av_decision *avd) +{ + struct avtab_key avkey; + struct avtab_node *node; + struct ebitmap *sattr, *tattr; + struct ebitmap_node *snode, *tnode; + unsigned int sbit, tbit; + + avkey.target_class = tclass; + avkey.specified = specified & AVTAB_AV; + sattr = &policydb.type_attr_map[source_type - 1]; + tattr = &policydb.type_attr_map[target_type - 1]; + + ebitmap_for_each_positive_bit(sattr, snode, sbit) { + ebitmap_for_each_positive_bit(tattr, tnode, tbit) { + avkey.source_type = sbit + 1; + avkey.target_type = tbit + 1; + + for (node = avtab_search_node(&policydb.te_avtab, &avkey); + node != NULL; + node = avtab_search_node_next(node, avkey.specified)) { + if (node->key.specified == AVTAB_ALLOWED) + avd->allowed |= node->datum.data; + else if (node->key.specified == AVTAB_AUDITALLOW) + avd->auditallow |= node->datum.data; + else if (node->key.specified == AVTAB_AUDITDENY) + avd->auditdeny &= node->datum.data; + } + /* Check conditional av table for additional permissions */ + cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); + } + } + + /* + * If the given source and target types have boundary + * constraint, lazy checks have to mask any violated + * permission and notice it to userspace via audit. + */ + type_attribute_bounds_av(source_type, target_type, tclass, avd); +} + +/* * Compute access vectors based on a context structure pair for * the permissions in a particular class. */ @@ -293,13 +423,8 @@ static int context_struct_compute_av(struct context *scontext, { struct constraint_node *constraint; struct role_allow *ra; - struct avtab_key avkey; - struct avtab_node *node; struct class_datum *tclass_datum; - struct ebitmap *sattr, *tattr; - struct ebitmap_node *snode, *tnode; const struct selinux_class_perm *kdefs = &selinux_class_perm; - unsigned int i, j; /* * Remap extended Netlink classes for old policy versions. @@ -355,30 +480,9 @@ static int context_struct_compute_av(struct context *scontext, * If a specific type enforcement rule was defined for * this permission check, then use it. */ - avkey.target_class = tclass; - avkey.specified = AVTAB_AV; - sattr = &policydb.type_attr_map[scontext->type - 1]; - tattr = &policydb.type_attr_map[tcontext->type - 1]; - ebitmap_for_each_positive_bit(sattr, snode, i) { - ebitmap_for_each_positive_bit(tattr, tnode, j) { - avkey.source_type = i + 1; - avkey.target_type = j + 1; - for (node = avtab_search_node(&policydb.te_avtab, &avkey); - node != NULL; - node = avtab_search_node_next(node, avkey.specified)) { - if (node->key.specified == AVTAB_ALLOWED) - avd->allowed |= node->datum.data; - else if (node->key.specified == AVTAB_AUDITALLOW) - avd->auditallow |= node->datum.data; - else if (node->key.specified == AVTAB_AUDITDENY) - avd->auditdeny &= node->datum.data; - } - - /* Check conditional av table for additional permissions */ - cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); - - } - } + type_attribute_compute_av(scontext->type, + tcontext->type, + tclass, AVTAB_AV, avd); /* * Remove any permissions prohibited by a constraint (this includes @@ -547,6 +651,69 @@ out: return rc; } +/* + * security_bounded_transition - check whether the given + * transition is directed to bounded, or not. + * It returns 0, if @newsid is bounded by @oldsid. + * Otherwise, it returns error code. + * + * @oldsid : current security identifier + * @newsid : destinated security identifier + */ +int security_bounded_transition(u32 old_sid, u32 new_sid) +{ + struct context *old_context, *new_context; + struct type_datum *type; + int index; + int rc = -EINVAL; + + read_lock(&policy_rwlock); + + old_context = sidtab_search(&sidtab, old_sid); + if (!old_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, old_sid); + goto out; + } + + new_context = sidtab_search(&sidtab, new_sid); + if (!new_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, new_sid); + goto out; + } + + /* type/domain unchaned */ + if (old_context->type == new_context->type) { + rc = 0; + goto out; + } + + index = new_context->type; + while (true) { + type = policydb.type_val_to_struct[index - 1]; + BUG_ON(!type); + + /* not bounded anymore */ + if (!type->bounds) { + rc = -EPERM; + break; + } + + /* @newsid is bounded by @oldsid */ + if (type->bounds == old_context->type) { + rc = 0; + break; + } + index = type->bounds; + } +out: + read_unlock(&policy_rwlock); + + return rc; +} + + /** * security_compute_av - Compute access vector decisions. * @ssid: source security identifier @@ -795,7 +962,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, *p++ = 0; typdatum = hashtab_search(policydb.p_types.table, scontextp); - if (!typdatum) + if (!typdatum || typdatum->attribute) goto out_unlock; context.type = typdatum->value; -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply related [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.4) 2008-08-14 7:38 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.4) KaiGai Kohei @ 2008-08-15 18:13 ` Stephen Smalley 2008-08-20 9:41 ` KaiGai Kohei 2008-08-25 12:32 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei 0 siblings, 2 replies; 97+ messages in thread From: Stephen Smalley @ 2008-08-15 18:13 UTC (permalink / raw) To: KaiGai Kohei; +Cc: jmorris, paul.moore, jbrindle, selinux On Thu, 2008-08-14 at 16:38 +0900, KaiGai Kohei wrote: > The following patch is the revised one for the kernel. > > Joshua suggested it is not necessary to bound type attributes > based on boundary relationship, and I also agreed this opinion. > The related code is dropped in this patch, and several code > cleanup contained. > > This change makes it unnecessary to deliver text represented > attributes into kernelspace, but related codes are still remained > for ... > > | Stephen Smalley wrote: > | An aside: Possibly we should add the attribute flag at the same time > | and leave the type attributes in the type symtab in the kernel policy, > | as previously discussed. Then the kernel policy won't be lacking any > | information needed for analysis. Just need to make sure that > | context_to_sid doesn't accept attributes in the type field. > > Stephen, could you point out the previous discussion? Keeping the type attribute names in the types symtab in the kernel policy allows tools like audit2why and apol to extract the original attribute names and display them. I originally shed them because the kernel didn't need to use that information and we don't want attribute names to be used in security contexts, but it costs us little to save them and check for them, and it benefits the tools. > Thanks, > > [1/3] thread-context-kernel.4.patch > It allows a multithreaded process to assign an individual > "more bounded" security context, and it also enables to > handle binary policy format version 24 in kernel space. > > Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> > -- > security/selinux/avc.c | 2 +- > security/selinux/hooks.c | 11 +- > security/selinux/include/avc.h | 4 + > security/selinux/include/security.h | 16 ++- > security/selinux/ss/policydb.c | 298 +++++++++++++++++++++++++++++++++-- > security/selinux/ss/policydb.h | 5 + > security/selinux/ss/services.c | 227 +++++++++++++++++++++++---- > 7 files changed, 515 insertions(+), 48 deletions(-) > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index bc1c3ae..0201227 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -5180,9 +5180,14 @@ static int selinux_setprocattr(struct task_struct *p, > > if (sid == 0) > return -EINVAL; > - > - /* Only allow single threaded processes to change context */ > - if (atomic_read(&p->mm->mm_users) != 1) { > + /* > + * SELinux allows to change context in the following case only. > + * - Single threaded processes. > + * - Multi threaded processes intend to change its context into > + * more restricted domain (defined by TYPEBOUNDS statement). > + */ > + if (atomic_read(&p->mm->mm_users) != 1 > + && security_bounded_transition(tsec->sid, sid) != 0) { Alternatively you could defer the security_bounded_transition() check until the if (t->mm == mm && t != p) block. The mm_users check is a merely a quick check to see whether we need to probe further to determine whether or not there are any other threads sharing the mm. > struct task_struct *g, *t; > struct mm_struct *mm = p->mm; > read_lock(&tasklist_lock); > diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h > index ad30ac4..3773eb6 100644 > --- a/security/selinux/include/security.h > +++ b/security/selinux/include/security.h > @@ -27,13 +27,14 @@ > #define POLICYDB_VERSION_RANGETRANS 21 > #define POLICYDB_VERSION_POLCAP 22 > #define POLICYDB_VERSION_PERMISSIVE 23 > +#define POLICYDB_VERSION_BOUNDARY 24 > > /* Range of policy versions we understand*/ > #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE > #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX > #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE > #else > -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE > +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY > #endif > > #define CONTEXT_MNT 0x01 > @@ -62,6 +63,17 @@ enum { > extern int selinux_policycap_netpeer; > extern int selinux_policycap_openperm; > > +/* boundary related definitions */ > +#define POLICYDB_BOUNDS_MAXDEPTH 4 > +#define POLICYDB_BOUNDS_ATTRIBUTE_FLAG 0xffffffff > +/* > + * NOTE: When "bounds" field equals POLICYDB_BOUNDS_ATTRIBUTE_FLAG, > + * it means this entry is attribute, not a normal type. > + * Boundary feature also requires to deliver attribute info into > + * kernel space, because it has to notice boundary violation related > + * to type/attribute relationships. > + */ It seems a bit ugly to overload the bounds field for this purpose vs. encoding it into the primary field as you suggest elsewhere. > diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c > index 84f8cc7..f134322 100644 > --- a/security/selinux/ss/policydb.c > +++ b/security/selinux/ss/policydb.c <snip> > +static int constraint_bounds_sanity_check(struct policydb *p, > + struct constraint_expr *cexpr) > +{ > + struct user_datum *user; > + struct role_datum *role; > + struct type_datum *type; > + struct ebitmap_node *node; > + unsigned long bit; > + > + /* no need to check boundary constraint */ > + if (cexpr->expr_type != CEXPR_NAMES) > + return 0; > + > + ebitmap_for_each_positive_bit(&cexpr->names, node, bit) { > + if (cexpr->attr & CEXPR_USER) { > + if (bit >= p->p_users.nprim) > + goto broken_ebitmap; > + > + user = p->user_val_to_struct[bit]; > + if (user->bounds && > + !ebitmap_get_bit(&cexpr->names, user->bounds - 1)) { > + printk(KERN_ERR > + "SELinux: boundary violated cexpr:" > + " CEXPR_NAMES: user:%s bounds:%s\n", > + p->p_user_val_to_name[user->value - 1], > + p->p_user_val_to_name[user->bounds - 1]); > + return -EINVAL; > + } What if cexpr->op is CEXPR_NEQ, e.g. the clause was something like (u1 != untrusteduser)? Or the entire clause is negated? I'm not sure you want to apply these bounds checks on constraints at all. -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.4) 2008-08-15 18:13 ` Stephen Smalley @ 2008-08-20 9:41 ` KaiGai Kohei 2008-08-25 12:32 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei 1 sibling, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-20 9:41 UTC (permalink / raw) To: Stephen Smalley; +Cc: KaiGai Kohei, jmorris, paul.moore, jbrindle, selinux Stephen Smalley wrote: > On Thu, 2008-08-14 at 16:38 +0900, KaiGai Kohei wrote: >> The following patch is the revised one for the kernel. >> >> Joshua suggested it is not necessary to bound type attributes >> based on boundary relationship, and I also agreed this opinion. >> The related code is dropped in this patch, and several code >> cleanup contained. >> >> This change makes it unnecessary to deliver text represented >> attributes into kernelspace, but related codes are still remained >> for ... >> >> | Stephen Smalley wrote: >> | An aside: Possibly we should add the attribute flag at the same time >> | and leave the type attributes in the type symtab in the kernel policy, >> | as previously discussed. Then the kernel policy won't be lacking any >> | information needed for analysis. Just need to make sure that >> | context_to_sid doesn't accept attributes in the type field. >> >> Stephen, could you point out the previous discussion? > > Keeping the type attribute names in the types symtab in the kernel > policy allows tools like audit2why and apol to extract the original > attribute names and display them. I originally shed them because the > kernel didn't need to use that information and we don't want attribute > names to be used in security contexts, but it costs us little to save > them and check for them, and it benefits the tools. > >> Thanks, >> >> [1/3] thread-context-kernel.4.patch >> It allows a multithreaded process to assign an individual >> "more bounded" security context, and it also enables to >> handle binary policy format version 24 in kernel space. >> >> Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> >> -- >> security/selinux/avc.c | 2 +- >> security/selinux/hooks.c | 11 +- >> security/selinux/include/avc.h | 4 + >> security/selinux/include/security.h | 16 ++- >> security/selinux/ss/policydb.c | 298 +++++++++++++++++++++++++++++++++-- >> security/selinux/ss/policydb.h | 5 + >> security/selinux/ss/services.c | 227 +++++++++++++++++++++++---- >> 7 files changed, 515 insertions(+), 48 deletions(-) > >> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c >> index bc1c3ae..0201227 100644 >> --- a/security/selinux/hooks.c >> +++ b/security/selinux/hooks.c >> @@ -5180,9 +5180,14 @@ static int selinux_setprocattr(struct task_struct *p, >> >> if (sid == 0) >> return -EINVAL; >> - >> - /* Only allow single threaded processes to change context */ >> - if (atomic_read(&p->mm->mm_users) != 1) { >> + /* >> + * SELinux allows to change context in the following case only. >> + * - Single threaded processes. >> + * - Multi threaded processes intend to change its context into >> + * more restricted domain (defined by TYPEBOUNDS statement). >> + */ >> + if (atomic_read(&p->mm->mm_users) != 1 >> + && security_bounded_transition(tsec->sid, sid) != 0) { > > Alternatively you could defer the security_bounded_transition() check > until the if (t->mm == mm && t != p) block. The mm_users check is a > merely a quick check to see whether we need to probe further to > determine whether or not there are any other threads sharing the mm. The reason why I moved it to front of the block is we can skip the checks whether the given process is multithreaded or not, if security_bounded_transition() returns success. However, I have no preference here. There may be a bit performance gain, but this code is not invoked so frequently. So, I'll revert it on the next revision. >> @@ -62,6 +63,17 @@ enum { >> extern int selinux_policycap_netpeer; >> extern int selinux_policycap_openperm; >> >> +/* boundary related definitions */ >> +#define POLICYDB_BOUNDS_MAXDEPTH 4 >> +#define POLICYDB_BOUNDS_ATTRIBUTE_FLAG 0xffffffff >> +/* >> + * NOTE: When "bounds" field equals POLICYDB_BOUNDS_ATTRIBUTE_FLAG, >> + * it means this entry is attribute, not a normal type. >> + * Boundary feature also requires to deliver attribute info into >> + * kernel space, because it has to notice boundary violation related >> + * to type/attribute relationships. >> + */ > > It seems a bit ugly to overload the bounds field for this purpose vs. > encoding it into the primary field as you suggest elsewhere. OK, I'll encode these properties of type_datum into the primary field. >> diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c >> index 84f8cc7..f134322 100644 >> --- a/security/selinux/ss/policydb.c >> +++ b/security/selinux/ss/policydb.c > <snip> >> +static int constraint_bounds_sanity_check(struct policydb *p, >> + struct constraint_expr *cexpr) >> +{ >> + struct user_datum *user; >> + struct role_datum *role; >> + struct type_datum *type; >> + struct ebitmap_node *node; >> + unsigned long bit; >> + >> + /* no need to check boundary constraint */ >> + if (cexpr->expr_type != CEXPR_NAMES) >> + return 0; >> + >> + ebitmap_for_each_positive_bit(&cexpr->names, node, bit) { >> + if (cexpr->attr & CEXPR_USER) { >> + if (bit >= p->p_users.nprim) >> + goto broken_ebitmap; >> + >> + user = p->user_val_to_struct[bit]; >> + if (user->bounds && >> + !ebitmap_get_bit(&cexpr->names, user->bounds - 1)) { >> + printk(KERN_ERR >> + "SELinux: boundary violated cexpr:" >> + " CEXPR_NAMES: user:%s bounds:%s\n", >> + p->p_user_val_to_name[user->value - 1], >> + p->p_user_val_to_name[user->bounds - 1]); >> + return -EINVAL; >> + } > > What if cexpr->op is CEXPR_NEQ, e.g. the clause was something like > (u1 != untrusteduser)? Or the entire clause is negated? I'm not sure > you want to apply these bounds checks on constraints at all. CEXPR_NEQ is dealt as simply negated ebitmap in the current implementation. For example, (u1 != untrusteduser) requires a user is not "untrusteduser" and its parent is not "untrusteduser" also. Hmm, however, it may be a bit complicated/hard to understand rule. The purpose of this check is to prevent to attach wider permissions on bounded types, like mcssetcats attribute which means a privilege. Here is an idea. If the type_attribute_bounds_av() is put after constraint checks on avc computing, we can make sure the purpose of bounds checks. It simply means a bounded type cannot have wider permissions than its parent in TE rules and constraints. Thanks, -- KaiGai Kohei <kaigai@kaigai.gr.jp> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) 2008-08-15 18:13 ` Stephen Smalley 2008-08-20 9:41 ` KaiGai Kohei @ 2008-08-25 12:32 ` KaiGai Kohei 2008-08-25 12:57 ` Stephen Smalley ` (2 more replies) 1 sibling, 3 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-25 12:32 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux The following patch is revised one for kernel. Updates: - This patch is rebased on James's security-testing-2.6 tree. - security_bounded_transition() is deployed just after read_unlock() within do_each_thread() { ... } while_each_thread() loop again. - The properties of type_datum are packed within the third word of type entries in the kernel policy. - Bounds checks on constraints are integrated within avc creation. Lazy bounds checks are invoked at the tail of context_struct_compute_av(), and it drops all of boundary violated permissions. It compares permissions of a bounded type based on both of TE and constraints by a bounds type in same time, so the bounded type always cannot have any wider permission than its parent. e.g) When a type of child_t is bounded by parent_t and has mcssetcats attribute, we cannot assign undominated categories because parent_t is not allowed to assign them and it bounds permissions of child_t. - Sanity checks for constraints are removed by the above reason. Thanks, Signed-off-by: KaiGai Kohei <kaigai@kaigai.gr.jp> -- security/selinux/avc.c | 2 +- security/selinux/hooks.c | 11 ++- security/selinux/include/avc.h | 4 + security/selinux/include/security.h | 15 +++- security/selinux/ss/policydb.c | 205 ++++++++++++++++++++++++++++++++--- security/selinux/ss/policydb.h | 5 + security/selinux/ss/services.c | 172 +++++++++++++++++++++++++++++- 7 files changed, 395 insertions(+), 19 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 114b4b4..cb30c7e 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -136,7 +136,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) * @tclass: target security class * @av: access vector */ -static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) { const char **common_pts = NULL; u32 common_base = 0; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 03fc6a8..e06e963 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5219,8 +5219,12 @@ static int selinux_setprocattr(struct task_struct *p, if (sid == 0) return -EINVAL; - - /* Only allow single threaded processes to change context */ + /* + * SELinux allows to change context in the following case only. + * - Single threaded processes. + * - Multi threaded processes intend to change its context into + * more restricted domain (defined by TYPEBOUNDS statement). + */ if (atomic_read(&p->mm->mm_users) != 1) { struct task_struct *g, *t; struct mm_struct *mm = p->mm; @@ -5228,11 +5232,14 @@ static int selinux_setprocattr(struct task_struct *p, do_each_thread(g, t) { if (t->mm == mm && t != p) { read_unlock(&tasklist_lock); + if (!security_bounded_transition(tsec->sid, sid)) + goto boundary_ok; return -EPERM; } } while_each_thread(g, t); read_unlock(&tasklist_lock); } +boundary_ok: /* Check permissions for the transition. */ error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 7b9769f..d12ff1a 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -12,6 +12,7 @@ #include <linux/kdev_t.h> #include <linux/spinlock.h> #include <linux/init.h> +#include <linux/audit.h> #include <linux/in6.h> #include <linux/path.h> #include <asm/system.h> @@ -126,6 +127,9 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, u32 events, u32 ssid, u32 tsid, u16 tclass, u32 perms); +/* Shows permission in human readable form */ +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av); + /* Exported to selinuxfs */ int avc_get_hash_stats(char *page); extern unsigned int avc_cache_threshold; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 7c54300..7244737 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -27,13 +27,14 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY #endif #define CONTEXT_MNT 0x01 @@ -62,6 +63,16 @@ enum { extern int selinux_policycap_netpeer; extern int selinux_policycap_openperm; +/* + * type_datum properties + * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY + */ +#define TYPEDATUM_PROPERTY_PRIMARY 0x0001 +#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 + +/* limitation of boundary depth */ +#define POLICYDB_BOUNDS_MAXDEPTH 4 + int security_load_policy(void *data, size_t len); int security_policycap_supported(unsigned int req_cap); @@ -117,6 +128,8 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen, int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass); +int security_bounded_transition(u32 oldsid, u32 newsid); + int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 2391761..7dbe756 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -30,6 +30,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/audit.h> #include "security.h" #include "policydb.h" @@ -116,7 +117,12 @@ static struct policydb_compat_info policydb_compat[] = { .version = POLICYDB_VERSION_PERMISSIVE, .sym_num = SYM_NUM, .ocon_num = OCON_NUM, - } + }, + { + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -254,7 +260,9 @@ static int role_index(void *key, void *datum, void *datap) role = datum; p = datap; - if (!role->value || role->value > p->p_roles.nprim) + if (!role->value + || role->value > p->p_roles.nprim + || role->bounds > p->p_roles.nprim) return -EINVAL; p->p_role_val_to_name[role->value - 1] = key; p->role_val_to_struct[role->value - 1] = role; @@ -270,9 +278,12 @@ static int type_index(void *key, void *datum, void *datap) p = datap; if (typdatum->primary) { - if (!typdatum->value || typdatum->value > p->p_types.nprim) + if (!typdatum->value + || typdatum->value > p->p_types.nprim + || typdatum->bounds > p->p_types.nprim) return -EINVAL; p->p_type_val_to_name[typdatum->value - 1] = key; + p->type_val_to_struct[typdatum->value - 1] = typdatum; } return 0; @@ -285,7 +296,9 @@ static int user_index(void *key, void *datum, void *datap) usrdatum = datum; p = datap; - if (!usrdatum->value || usrdatum->value > p->p_users.nprim) + if (!usrdatum->value + || usrdatum->value > p->p_users.nprim + || usrdatum->bounds > p->p_users.nprim) return -EINVAL; p->p_user_val_to_name[usrdatum->value - 1] = key; p->user_val_to_struct[usrdatum->value - 1] = usrdatum; @@ -438,6 +451,14 @@ static int policydb_index_others(struct policydb *p) goto out; } + p->type_val_to_struct = + kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)), + GFP_KERNEL); + if (!p->type_val_to_struct) { + rc = -ENOMEM; + goto out; + } + if (cond_init_bool_indexes(p)) { rc = -ENOMEM; goto out; @@ -625,6 +646,7 @@ void policydb_destroy(struct policydb *p) kfree(p->class_val_to_struct); kfree(p->role_val_to_struct); kfree(p->user_val_to_struct); + kfree(p->type_val_to_struct); avtab_destroy(&p->te_avtab); @@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct role_datum *role; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; role = kzalloc(sizeof(*role), GFP_KERNEL); @@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + role->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1236,8 +1263,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct type_datum *typdatum; - int rc; - __le32 buf[3]; + int rc, to_read = 3; + __le32 buf[4]; u32 len; typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); @@ -1246,13 +1273,27 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) return rc; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 4; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); typdatum->value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { + u32 prop = le32_to_cpu(buf[2]); + + if (prop & TYPEDATUM_PROPERTY_PRIMARY) + typdatum->primary = 1; + if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE) + typdatum->attribute = 1; + + typdatum->bounds = le32_to_cpu(buf[3]); + } else { + typdatum->primary = le32_to_cpu(buf[2]); + } key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1309,8 +1350,8 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct user_datum *usrdatum; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); @@ -1319,12 +1360,17 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + usrdatum->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1465,6 +1511,133 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) cat_read, }; +static int user_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct user_datum *upper, *user; + struct policydb *p = datap; + int depth = 0; + + upper = user = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: user %s: " + "too deep or looped boundary", + (char *) key); + return -EINVAL; + } + + upper = p->user_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&user->roles, node, bit) { + if (ebitmap_get_bit(&upper->roles, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "user=%s role=%s bounds=%s\n", + p->p_user_val_to_name[user->value - 1], + p->p_role_val_to_name[bit], + p->p_user_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int role_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct role_datum *upper, *role; + struct policydb *p = datap; + int depth = 0; + + upper = role = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: role %s: " + "too deep or looped bounds\n", + (char *) key); + return -EINVAL; + } + + upper = p->role_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&role->types, node, bit) { + if (ebitmap_get_bit(&upper->types, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "role=%s type=%s bounds=%s\n", + p->p_role_val_to_name[role->value - 1], + p->p_type_val_to_name[bit], + p->p_role_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int type_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct type_datum *upper, *type; + struct policydb *p = datap; + int depth = 0; + + upper = type = datum; + while (upper->bounds) { + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: type %s: " + "too deep or looped boundary\n", + (char *) key); + return -EINVAL; + } + + upper = p->type_val_to_struct[upper->bounds - 1]; + if (upper->attribute) { + printk(KERN_ERR "SELinux: type %s: " + "bounded by attribute %s", + (char *) key, + p->p_type_val_to_name[upper->value - 1]); + return -EINVAL; + } + } + + return 0; +} + +static int policydb_bounds_sanity_check(struct policydb *p) +{ + int rc; + + if (p->policyvers < POLICYDB_VERSION_BOUNDARY) + return 0; + + rc = hashtab_map(p->p_users.table, + user_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_roles.table, + role_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_types.table, + type_bounds_sanity_check, p); + if (rc) + return rc; + + return 0; +} + extern int ss_initialized; /* @@ -1961,6 +2134,10 @@ int policydb_read(struct policydb *p, void *fp) goto bad; } + rc = policydb_bounds_sanity_check(p); + if (rc) + goto bad; + rc = 0; out: return rc; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 4253370..55152d4 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -61,6 +61,7 @@ struct class_datum { /* Role attributes */ struct role_datum { u32 value; /* internal role value */ + u32 bounds; /* boundary of role */ struct ebitmap dominates; /* set of roles dominated by this role */ struct ebitmap types; /* set of authorized types for role */ }; @@ -81,12 +82,15 @@ struct role_allow { /* Type attributes */ struct type_datum { u32 value; /* internal type value */ + u32 bounds; /* boundary of type */ unsigned char primary; /* primary name? */ + unsigned char attribute;/* attribute ?*/ }; /* User attributes */ struct user_datum { u32 value; /* internal user value */ + u32 bounds; /* bounds of user */ struct ebitmap roles; /* set of authorized roles for user */ struct mls_range range; /* MLS range (min - max) for user */ struct mls_level dfltlevel; /* default login MLS level for user */ @@ -209,6 +213,7 @@ struct policydb { struct class_datum **class_val_to_struct; struct role_datum **role_val_to_struct; struct user_datum **user_val_to_struct; + struct type_datum **type_val_to_struct; /* type enforcement access vectors and transitions */ struct avtab te_avtab; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b52f923..1ed94a3 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -88,6 +88,11 @@ static u32 latest_granting; static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len); +static int context_struct_compute_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + u32 requested, + struct av_decision *avd); /* * Return the boolean value of a constraint expression * when it is applied to the specified source and target @@ -274,6 +279,100 @@ mls_ops: } /* + * security_boundary_permission - drops violated permissions + * on boundary constraint. + */ +static void type_attribute_bounds_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + u32 requested, + struct av_decision *avd) +{ + struct context lo_scontext; + struct context lo_tcontext; + struct av_decision lo_avd; + struct type_datum *source + = policydb.type_val_to_struct[scontext->type - 1]; + struct type_datum *target + = policydb.type_val_to_struct[tcontext->type - 1]; + u32 masked = 0; + + if (source->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); + lo_scontext.type = source->bounds; + + context_struct_compute_av(&lo_scontext, + tcontext, + tclass, + requested, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); + lo_tcontext.type = target->bounds; + + context_struct_compute_av(scontext, + &lo_tcontext, + tclass, + requested, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (source->bounds && target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + /* + * lo_scontext and lo_tcontext are already + * set up. + */ + + context_struct_compute_av(&lo_scontext, + &lo_tcontext, + tclass, + requested, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (masked) { + struct audit_buffer *ab; + char *stype_name + = policydb.p_type_val_to_name[source->value - 1]; + char *ttype_name + = policydb.p_type_val_to_name[target->value - 1]; + char *tclass_name + = policydb.p_class_val_to_name[tclass - 1]; + + /* mask violated permissions */ + avd->allowed &= ~masked; + + /* notice to userspace via audit message */ + ab = audit_log_start(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR); + if (!ab) + return; + + audit_log_format(ab, "av boundary violation: " + "source=%s target=%s tclass=%s", + stype_name, ttype_name, tclass_name); + avc_dump_av(ab, tclass, masked); + audit_log_end(ab); + } +} + +/* * Compute access vectors based on a context structure pair for * the permissions in a particular class. */ @@ -404,6 +503,14 @@ static int context_struct_compute_av(struct context *scontext, PROCESS__DYNTRANSITION); } + /* + * If the given source and target types have boundary + * constraint, lazy checks have to mask any violated + * permission and notice it to userspace via audit. + */ + type_attribute_bounds_av(scontext, tcontext, + tclass, requested, avd); + return 0; inval_class: @@ -549,6 +656,69 @@ out: return rc; } +/* + * security_bounded_transition - check whether the given + * transition is directed to bounded, or not. + * It returns 0, if @newsid is bounded by @oldsid. + * Otherwise, it returns error code. + * + * @oldsid : current security identifier + * @newsid : destinated security identifier + */ +int security_bounded_transition(u32 old_sid, u32 new_sid) +{ + struct context *old_context, *new_context; + struct type_datum *type; + int index; + int rc = -EINVAL; + + read_lock(&policy_rwlock); + + old_context = sidtab_search(&sidtab, old_sid); + if (!old_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, old_sid); + goto out; + } + + new_context = sidtab_search(&sidtab, new_sid); + if (!new_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, new_sid); + goto out; + } + + /* type/domain unchaned */ + if (old_context->type == new_context->type) { + rc = 0; + goto out; + } + + index = new_context->type; + while (true) { + type = policydb.type_val_to_struct[index - 1]; + BUG_ON(!type); + + /* not bounded anymore */ + if (!type->bounds) { + rc = -EPERM; + break; + } + + /* @newsid is bounded by @oldsid */ + if (type->bounds == old_context->type) { + rc = 0; + break; + } + index = type->bounds; + } +out: + read_unlock(&policy_rwlock); + + return rc; +} + + /** * security_compute_av - Compute access vector decisions. * @ssid: source security identifier @@ -794,7 +964,7 @@ static int string_to_context_struct(struct policydb *pol, *p++ = 0; typdatum = hashtab_search(pol->p_types.table, scontextp); - if (!typdatum) + if (!typdatum || typdatum->attribute) goto out; ctx->type = typdatum->value; -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply related [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) 2008-08-25 12:32 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei @ 2008-08-25 12:57 ` Stephen Smalley 2008-08-25 13:45 ` KaiGai Kohei 2008-08-26 7:11 ` KaiGai Kohei 2008-08-26 9:01 ` James Morris 2008-08-26 10:29 ` James Morris 2 siblings, 2 replies; 97+ messages in thread From: Stephen Smalley @ 2008-08-25 12:57 UTC (permalink / raw) To: KaiGai Kohei; +Cc: jmorris, paul.moore, jbrindle, selinux On Mon, 2008-08-25 at 21:32 +0900, KaiGai Kohei wrote: > The following patch is revised one for kernel. > > Updates: > - This patch is rebased on James's security-testing-2.6 tree. > - security_bounded_transition() is deployed just after read_unlock() > within do_each_thread() { ... } while_each_thread() loop again. > - The properties of type_datum are packed within the third word of > type entries in the kernel policy. > - Bounds checks on constraints are integrated within avc creation. > Lazy bounds checks are invoked at the tail of context_struct_compute_av(), > and it drops all of boundary violated permissions. It compares permissions > of a bounded type based on both of TE and constraints by a bounds type in > same time, so the bounded type always cannot have any wider permission than > its parent. > e.g) > When a type of child_t is bounded by parent_t and has mcssetcats attribute, > we cannot assign undominated categories because parent_t is not allowed to > assign them and it bounds permissions of child_t. > - Sanity checks for constraints are removed by the above reason. This looks good to me in terms of the functionality. Have you run any benchmarks to assess the performance impact on AVC misses? > > Thanks, > > Signed-off-by: KaiGai Kohei <kaigai@kaigai.gr.jp> > -- > security/selinux/avc.c | 2 +- > security/selinux/hooks.c | 11 ++- > security/selinux/include/avc.h | 4 + > security/selinux/include/security.h | 15 +++- > security/selinux/ss/policydb.c | 205 ++++++++++++++++++++++++++++++++--- > security/selinux/ss/policydb.h | 5 + > security/selinux/ss/services.c | 172 +++++++++++++++++++++++++++++- > 7 files changed, 395 insertions(+), 19 deletions(-) > > diff --git a/security/selinux/avc.c b/security/selinux/avc.c > index 114b4b4..cb30c7e 100644 > --- a/security/selinux/avc.c > +++ b/security/selinux/avc.c > @@ -136,7 +136,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) > * @tclass: target security class > * @av: access vector > */ > -static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) > +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) > { > const char **common_pts = NULL; > u32 common_base = 0; > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index 03fc6a8..e06e963 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -5219,8 +5219,12 @@ static int selinux_setprocattr(struct task_struct *p, > > if (sid == 0) > return -EINVAL; > - > - /* Only allow single threaded processes to change context */ > + /* > + * SELinux allows to change context in the following case only. > + * - Single threaded processes. > + * - Multi threaded processes intend to change its context into > + * more restricted domain (defined by TYPEBOUNDS statement). > + */ > if (atomic_read(&p->mm->mm_users) != 1) { > struct task_struct *g, *t; > struct mm_struct *mm = p->mm; > @@ -5228,11 +5232,14 @@ static int selinux_setprocattr(struct task_struct *p, > do_each_thread(g, t) { > if (t->mm == mm && t != p) { > read_unlock(&tasklist_lock); > + if (!security_bounded_transition(tsec->sid, sid)) > + goto boundary_ok; > return -EPERM; > } > } while_each_thread(g, t); > read_unlock(&tasklist_lock); > } > +boundary_ok: > > /* Check permissions for the transition. */ > error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, > diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h > index 7b9769f..d12ff1a 100644 > --- a/security/selinux/include/avc.h > +++ b/security/selinux/include/avc.h > @@ -12,6 +12,7 @@ > #include <linux/kdev_t.h> > #include <linux/spinlock.h> > #include <linux/init.h> > +#include <linux/audit.h> > #include <linux/in6.h> > #include <linux/path.h> > #include <asm/system.h> > @@ -126,6 +127,9 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, > u32 events, u32 ssid, u32 tsid, > u16 tclass, u32 perms); > > +/* Shows permission in human readable form */ > +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av); > + > /* Exported to selinuxfs */ > int avc_get_hash_stats(char *page); > extern unsigned int avc_cache_threshold; > diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h > index 7c54300..7244737 100644 > --- a/security/selinux/include/security.h > +++ b/security/selinux/include/security.h > @@ -27,13 +27,14 @@ > #define POLICYDB_VERSION_RANGETRANS 21 > #define POLICYDB_VERSION_POLCAP 22 > #define POLICYDB_VERSION_PERMISSIVE 23 > +#define POLICYDB_VERSION_BOUNDARY 24 > > /* Range of policy versions we understand*/ > #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE > #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX > #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE > #else > -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE > +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY > #endif > > #define CONTEXT_MNT 0x01 > @@ -62,6 +63,16 @@ enum { > extern int selinux_policycap_netpeer; > extern int selinux_policycap_openperm; > > +/* > + * type_datum properties > + * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY > + */ > +#define TYPEDATUM_PROPERTY_PRIMARY 0x0001 > +#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 > + > +/* limitation of boundary depth */ > +#define POLICYDB_BOUNDS_MAXDEPTH 4 > + > int security_load_policy(void *data, size_t len); > > int security_policycap_supported(unsigned int req_cap); > @@ -117,6 +128,8 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen, > int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, > u16 tclass); > > +int security_bounded_transition(u32 oldsid, u32 newsid); > + > int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); > > int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, > diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c > index 2391761..7dbe756 100644 > --- a/security/selinux/ss/policydb.c > +++ b/security/selinux/ss/policydb.c > @@ -30,6 +30,7 @@ > #include <linux/slab.h> > #include <linux/string.h> > #include <linux/errno.h> > +#include <linux/audit.h> > #include "security.h" > > #include "policydb.h" > @@ -116,7 +117,12 @@ static struct policydb_compat_info policydb_compat[] = { > .version = POLICYDB_VERSION_PERMISSIVE, > .sym_num = SYM_NUM, > .ocon_num = OCON_NUM, > - } > + }, > + { > + .version = POLICYDB_VERSION_BOUNDARY, > + .sym_num = SYM_NUM, > + .ocon_num = OCON_NUM, > + }, > }; > > static struct policydb_compat_info *policydb_lookup_compat(int version) > @@ -254,7 +260,9 @@ static int role_index(void *key, void *datum, void *datap) > > role = datum; > p = datap; > - if (!role->value || role->value > p->p_roles.nprim) > + if (!role->value > + || role->value > p->p_roles.nprim > + || role->bounds > p->p_roles.nprim) > return -EINVAL; > p->p_role_val_to_name[role->value - 1] = key; > p->role_val_to_struct[role->value - 1] = role; > @@ -270,9 +278,12 @@ static int type_index(void *key, void *datum, void *datap) > p = datap; > > if (typdatum->primary) { > - if (!typdatum->value || typdatum->value > p->p_types.nprim) > + if (!typdatum->value > + || typdatum->value > p->p_types.nprim > + || typdatum->bounds > p->p_types.nprim) > return -EINVAL; > p->p_type_val_to_name[typdatum->value - 1] = key; > + p->type_val_to_struct[typdatum->value - 1] = typdatum; > } > > return 0; > @@ -285,7 +296,9 @@ static int user_index(void *key, void *datum, void *datap) > > usrdatum = datum; > p = datap; > - if (!usrdatum->value || usrdatum->value > p->p_users.nprim) > + if (!usrdatum->value > + || usrdatum->value > p->p_users.nprim > + || usrdatum->bounds > p->p_users.nprim) > return -EINVAL; > p->p_user_val_to_name[usrdatum->value - 1] = key; > p->user_val_to_struct[usrdatum->value - 1] = usrdatum; > @@ -438,6 +451,14 @@ static int policydb_index_others(struct policydb *p) > goto out; > } > > + p->type_val_to_struct = > + kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)), > + GFP_KERNEL); > + if (!p->type_val_to_struct) { > + rc = -ENOMEM; > + goto out; > + } > + > if (cond_init_bool_indexes(p)) { > rc = -ENOMEM; > goto out; > @@ -625,6 +646,7 @@ void policydb_destroy(struct policydb *p) > kfree(p->class_val_to_struct); > kfree(p->role_val_to_struct); > kfree(p->user_val_to_struct); > + kfree(p->type_val_to_struct); > > avtab_destroy(&p->te_avtab); > > @@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) > { > char *key = NULL; > struct role_datum *role; > - int rc; > - __le32 buf[2]; > + int rc, to_read = 2; > + __le32 buf[3]; > u32 len; > > role = kzalloc(sizeof(*role), GFP_KERNEL); > @@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) > goto out; > } > > - rc = next_entry(buf, fp, sizeof buf); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + to_read = 3; > + > + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); > if (rc < 0) > goto bad; > > len = le32_to_cpu(buf[0]); > role->value = le32_to_cpu(buf[1]); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + role->bounds = le32_to_cpu(buf[2]); > > key = kmalloc(len + 1, GFP_KERNEL); > if (!key) { > @@ -1236,8 +1263,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) > { > char *key = NULL; > struct type_datum *typdatum; > - int rc; > - __le32 buf[3]; > + int rc, to_read = 3; > + __le32 buf[4]; > u32 len; > > typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); > @@ -1246,13 +1273,27 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) > return rc; > } > > - rc = next_entry(buf, fp, sizeof buf); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + to_read = 4; > + > + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); > if (rc < 0) > goto bad; > > len = le32_to_cpu(buf[0]); > typdatum->value = le32_to_cpu(buf[1]); > - typdatum->primary = le32_to_cpu(buf[2]); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { > + u32 prop = le32_to_cpu(buf[2]); > + > + if (prop & TYPEDATUM_PROPERTY_PRIMARY) > + typdatum->primary = 1; > + if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE) > + typdatum->attribute = 1; > + > + typdatum->bounds = le32_to_cpu(buf[3]); > + } else { > + typdatum->primary = le32_to_cpu(buf[2]); > + } > > key = kmalloc(len + 1, GFP_KERNEL); > if (!key) { > @@ -1309,8 +1350,8 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) > { > char *key = NULL; > struct user_datum *usrdatum; > - int rc; > - __le32 buf[2]; > + int rc, to_read = 2; > + __le32 buf[3]; > u32 len; > > usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); > @@ -1319,12 +1360,17 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) > goto out; > } > > - rc = next_entry(buf, fp, sizeof buf); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + to_read = 3; > + > + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); > if (rc < 0) > goto bad; > > len = le32_to_cpu(buf[0]); > usrdatum->value = le32_to_cpu(buf[1]); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + usrdatum->bounds = le32_to_cpu(buf[2]); > > key = kmalloc(len + 1, GFP_KERNEL); > if (!key) { > @@ -1465,6 +1511,133 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) > cat_read, > }; > > +static int user_bounds_sanity_check(void *key, void *datum, void *datap) > +{ > + struct user_datum *upper, *user; > + struct policydb *p = datap; > + int depth = 0; > + > + upper = user = datum; > + while (upper->bounds) { > + struct ebitmap_node *node; > + unsigned long bit; > + > + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { > + printk(KERN_ERR "SELinux: user %s: " > + "too deep or looped boundary", > + (char *) key); > + return -EINVAL; > + } > + > + upper = p->user_val_to_struct[upper->bounds - 1]; > + ebitmap_for_each_positive_bit(&user->roles, node, bit) { > + if (ebitmap_get_bit(&upper->roles, bit)) > + continue; > + > + printk(KERN_ERR > + "SELinux: boundary violated policy: " > + "user=%s role=%s bounds=%s\n", > + p->p_user_val_to_name[user->value - 1], > + p->p_role_val_to_name[bit], > + p->p_user_val_to_name[upper->value - 1]); > + > + return -EINVAL; > + } > + } > + > + return 0; > +} > + > +static int role_bounds_sanity_check(void *key, void *datum, void *datap) > +{ > + struct role_datum *upper, *role; > + struct policydb *p = datap; > + int depth = 0; > + > + upper = role = datum; > + while (upper->bounds) { > + struct ebitmap_node *node; > + unsigned long bit; > + > + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { > + printk(KERN_ERR "SELinux: role %s: " > + "too deep or looped bounds\n", > + (char *) key); > + return -EINVAL; > + } > + > + upper = p->role_val_to_struct[upper->bounds - 1]; > + ebitmap_for_each_positive_bit(&role->types, node, bit) { > + if (ebitmap_get_bit(&upper->types, bit)) > + continue; > + > + printk(KERN_ERR > + "SELinux: boundary violated policy: " > + "role=%s type=%s bounds=%s\n", > + p->p_role_val_to_name[role->value - 1], > + p->p_type_val_to_name[bit], > + p->p_role_val_to_name[upper->value - 1]); > + > + return -EINVAL; > + } > + } > + > + return 0; > +} > + > +static int type_bounds_sanity_check(void *key, void *datum, void *datap) > +{ > + struct type_datum *upper, *type; > + struct policydb *p = datap; > + int depth = 0; > + > + upper = type = datum; > + while (upper->bounds) { > + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { > + printk(KERN_ERR "SELinux: type %s: " > + "too deep or looped boundary\n", > + (char *) key); > + return -EINVAL; > + } > + > + upper = p->type_val_to_struct[upper->bounds - 1]; > + if (upper->attribute) { > + printk(KERN_ERR "SELinux: type %s: " > + "bounded by attribute %s", > + (char *) key, > + p->p_type_val_to_name[upper->value - 1]); > + return -EINVAL; > + } > + } > + > + return 0; > +} > + > +static int policydb_bounds_sanity_check(struct policydb *p) > +{ > + int rc; > + > + if (p->policyvers < POLICYDB_VERSION_BOUNDARY) > + return 0; > + > + rc = hashtab_map(p->p_users.table, > + user_bounds_sanity_check, p); > + if (rc) > + return rc; > + > + rc = hashtab_map(p->p_roles.table, > + role_bounds_sanity_check, p); > + if (rc) > + return rc; > + > + rc = hashtab_map(p->p_types.table, > + type_bounds_sanity_check, p); > + if (rc) > + return rc; > + > + return 0; > +} > + > extern int ss_initialized; > > /* > @@ -1961,6 +2134,10 @@ int policydb_read(struct policydb *p, void *fp) > goto bad; > } > > + rc = policydb_bounds_sanity_check(p); > + if (rc) > + goto bad; > + > rc = 0; > out: > return rc; > diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h > index 4253370..55152d4 100644 > --- a/security/selinux/ss/policydb.h > +++ b/security/selinux/ss/policydb.h > @@ -61,6 +61,7 @@ struct class_datum { > /* Role attributes */ > struct role_datum { > u32 value; /* internal role value */ > + u32 bounds; /* boundary of role */ > struct ebitmap dominates; /* set of roles dominated by this role */ > struct ebitmap types; /* set of authorized types for role */ > }; > @@ -81,12 +82,15 @@ struct role_allow { > /* Type attributes */ > struct type_datum { > u32 value; /* internal type value */ > + u32 bounds; /* boundary of type */ > unsigned char primary; /* primary name? */ > + unsigned char attribute;/* attribute ?*/ > }; > > /* User attributes */ > struct user_datum { > u32 value; /* internal user value */ > + u32 bounds; /* bounds of user */ > struct ebitmap roles; /* set of authorized roles for user */ > struct mls_range range; /* MLS range (min - max) for user */ > struct mls_level dfltlevel; /* default login MLS level for user */ > @@ -209,6 +213,7 @@ struct policydb { > struct class_datum **class_val_to_struct; > struct role_datum **role_val_to_struct; > struct user_datum **user_val_to_struct; > + struct type_datum **type_val_to_struct; > > /* type enforcement access vectors and transitions */ > struct avtab te_avtab; > diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c > index b52f923..1ed94a3 100644 > --- a/security/selinux/ss/services.c > +++ b/security/selinux/ss/services.c > @@ -88,6 +88,11 @@ static u32 latest_granting; > static int context_struct_to_string(struct context *context, char **scontext, > u32 *scontext_len); > > +static int context_struct_compute_av(struct context *scontext, > + struct context *tcontext, > + u16 tclass, > + u32 requested, > + struct av_decision *avd); > /* > * Return the boolean value of a constraint expression > * when it is applied to the specified source and target > @@ -274,6 +279,100 @@ mls_ops: > } > > /* > + * security_boundary_permission - drops violated permissions > + * on boundary constraint. > + */ > +static void type_attribute_bounds_av(struct context *scontext, > + struct context *tcontext, > + u16 tclass, > + u32 requested, > + struct av_decision *avd) > +{ > + struct context lo_scontext; > + struct context lo_tcontext; > + struct av_decision lo_avd; > + struct type_datum *source > + = policydb.type_val_to_struct[scontext->type - 1]; > + struct type_datum *target > + = policydb.type_val_to_struct[tcontext->type - 1]; > + u32 masked = 0; > + > + if (source->bounds) { > + memset(&lo_avd, 0, sizeof(lo_avd)); > + > + memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); > + lo_scontext.type = source->bounds; > + > + context_struct_compute_av(&lo_scontext, > + tcontext, > + tclass, > + requested, > + &lo_avd); > + if ((lo_avd.allowed & avd->allowed) == avd->allowed) > + return; /* no masked permission */ > + masked = ~lo_avd.allowed & avd->allowed; > + } > + > + if (target->bounds) { > + memset(&lo_avd, 0, sizeof(lo_avd)); > + > + memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); > + lo_tcontext.type = target->bounds; > + > + context_struct_compute_av(scontext, > + &lo_tcontext, > + tclass, > + requested, > + &lo_avd); > + if ((lo_avd.allowed & avd->allowed) == avd->allowed) > + return; /* no masked permission */ > + masked = ~lo_avd.allowed & avd->allowed; > + } > + > + if (source->bounds && target->bounds) { > + memset(&lo_avd, 0, sizeof(lo_avd)); > + /* > + * lo_scontext and lo_tcontext are already > + * set up. > + */ > + > + context_struct_compute_av(&lo_scontext, > + &lo_tcontext, > + tclass, > + requested, > + &lo_avd); > + if ((lo_avd.allowed & avd->allowed) == avd->allowed) > + return; /* no masked permission */ > + masked = ~lo_avd.allowed & avd->allowed; > + } > + > + if (masked) { > + struct audit_buffer *ab; > + char *stype_name > + = policydb.p_type_val_to_name[source->value - 1]; > + char *ttype_name > + = policydb.p_type_val_to_name[target->value - 1]; > + char *tclass_name > + = policydb.p_class_val_to_name[tclass - 1]; > + > + /* mask violated permissions */ > + avd->allowed &= ~masked; > + > + /* notice to userspace via audit message */ > + ab = audit_log_start(current->audit_context, > + GFP_ATOMIC, AUDIT_SELINUX_ERR); > + if (!ab) > + return; > + > + audit_log_format(ab, "av boundary violation: " > + "source=%s target=%s tclass=%s", > + stype_name, ttype_name, tclass_name); > + avc_dump_av(ab, tclass, masked); > + audit_log_end(ab); > + } > +} > + > +/* > * Compute access vectors based on a context structure pair for > * the permissions in a particular class. > */ > @@ -404,6 +503,14 @@ static int context_struct_compute_av(struct context *scontext, > PROCESS__DYNTRANSITION); > } > > + /* > + * If the given source and target types have boundary > + * constraint, lazy checks have to mask any violated > + * permission and notice it to userspace via audit. > + */ > + type_attribute_bounds_av(scontext, tcontext, > + tclass, requested, avd); > + > return 0; > > inval_class: > @@ -549,6 +656,69 @@ out: > return rc; > } > > +/* > + * security_bounded_transition - check whether the given > + * transition is directed to bounded, or not. > + * It returns 0, if @newsid is bounded by @oldsid. > + * Otherwise, it returns error code. > + * > + * @oldsid : current security identifier > + * @newsid : destinated security identifier > + */ > +int security_bounded_transition(u32 old_sid, u32 new_sid) > +{ > + struct context *old_context, *new_context; > + struct type_datum *type; > + int index; > + int rc = -EINVAL; > + > + read_lock(&policy_rwlock); > + > + old_context = sidtab_search(&sidtab, old_sid); > + if (!old_context) { > + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", > + __func__, old_sid); > + goto out; > + } > + > + new_context = sidtab_search(&sidtab, new_sid); > + if (!new_context) { > + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", > + __func__, new_sid); > + goto out; > + } > + > + /* type/domain unchaned */ > + if (old_context->type == new_context->type) { > + rc = 0; > + goto out; > + } > + > + index = new_context->type; > + while (true) { > + type = policydb.type_val_to_struct[index - 1]; > + BUG_ON(!type); > + > + /* not bounded anymore */ > + if (!type->bounds) { > + rc = -EPERM; > + break; > + } > + > + /* @newsid is bounded by @oldsid */ > + if (type->bounds == old_context->type) { > + rc = 0; > + break; > + } > + index = type->bounds; > + } > +out: > + read_unlock(&policy_rwlock); > + > + return rc; > +} > + > + > /** > * security_compute_av - Compute access vector decisions. > * @ssid: source security identifier > @@ -794,7 +964,7 @@ static int string_to_context_struct(struct policydb *pol, > *p++ = 0; > > typdatum = hashtab_search(pol->p_types.table, scontextp); > - if (!typdatum) > + if (!typdatum || typdatum->attribute) > goto out; > > ctx->type = typdatum->value; > -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) 2008-08-25 12:57 ` Stephen Smalley @ 2008-08-25 13:45 ` KaiGai Kohei 2008-08-26 7:11 ` KaiGai Kohei 1 sibling, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-25 13:45 UTC (permalink / raw) To: Stephen Smalley; +Cc: KaiGai Kohei, jmorris, paul.moore, jbrindle, selinux Stephen Smalley wrote: > On Mon, 2008-08-25 at 21:32 +0900, KaiGai Kohei wrote: >> The following patch is revised one for kernel. >> >> Updates: >> - This patch is rebased on James's security-testing-2.6 tree. >> - security_bounded_transition() is deployed just after read_unlock() >> within do_each_thread() { ... } while_each_thread() loop again. >> - The properties of type_datum are packed within the third word of >> type entries in the kernel policy. >> - Bounds checks on constraints are integrated within avc creation. >> Lazy bounds checks are invoked at the tail of context_struct_compute_av(), >> and it drops all of boundary violated permissions. It compares permissions >> of a bounded type based on both of TE and constraints by a bounds type in >> same time, so the bounded type always cannot have any wider permission than >> its parent. >> e.g) >> When a type of child_t is bounded by parent_t and has mcssetcats attribute, >> we cannot assign undominated categories because parent_t is not allowed to >> assign them and it bounds permissions of child_t. >> - Sanity checks for constraints are removed by the above reason. > > This looks good to me in terms of the functionality. > Have you run any benchmarks to assess the performance impact on AVC > misses? Not yet. I'll measure it tomorrow, please wait for a while. Thanks, -- KaiGai Kohei <kaigai@kaigai.gr.jp> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) 2008-08-25 12:57 ` Stephen Smalley 2008-08-25 13:45 ` KaiGai Kohei @ 2008-08-26 7:11 ` KaiGai Kohei 1 sibling, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-26 7:11 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux [-- Attachment #1: Type: text/plain, Size: 3634 bytes --] Stephen Smalley wrote: > On Mon, 2008-08-25 at 21:32 +0900, KaiGai Kohei wrote: >> The following patch is revised one for kernel. >> >> Updates: >> - This patch is rebased on James's security-testing-2.6 tree. >> - security_bounded_transition() is deployed just after read_unlock() >> within do_each_thread() { ... } while_each_thread() loop again. >> - The properties of type_datum are packed within the third word of >> type entries in the kernel policy. >> - Bounds checks on constraints are integrated within avc creation. >> Lazy bounds checks are invoked at the tail of context_struct_compute_av(), >> and it drops all of boundary violated permissions. It compares permissions >> of a bounded type based on both of TE and constraints by a bounds type in >> same time, so the bounded type always cannot have any wider permission than >> its parent. >> e.g) >> When a type of child_t is bounded by parent_t and has mcssetcats attribute, >> we cannot assign undominated categories because parent_t is not allowed to >> assign them and it bounds permissions of child_t. >> - Sanity checks for constraints are removed by the above reason. > > This looks good to me in terms of the functionality. > Have you run any benchmarks to assess the performance impact on AVC > misses? I measured it with attached program named as "avc_misses.c". It invokes lstat() for given files, and iterates it 1,000 times. The given files have inidividually different categories, so we can see the performance impact on AVC misses when the number of files is greater than /selinux/avc/cache_threshold . This program is executed in a domain with/without its boundary on pathed kernel, and did on vanilla kernel. The following result shows elapsed times of avc_misses on the second time scale. Because lstat() is invoked for all given files, we guess the result is basically proportional to number of files (= # of categories). | Number of | No bounds | Bounds | No bounds | | Files and | 2.6.27-rc4 | 2.6.27-rc4 | 2.6.27-rc4 | | Categories | patched | patched | vanilla | +------------+--------------+--------------+-------------+ | 100 | 2.338 | 2.372 | 2.216 | | 200 | 4.742 | 4.739 | 4.451 | | 300 | 7.275 | 7.116 | 6.844 | | 400 | 9.685 | 9.616 | 9.337 | | 500 | 12.509 | 12.187 | 12.049 |___ | 600 | 28.532 | 29.736 | 26.092 | | AVC misses for | 700 | 45.554 | 50.631 | 40.878 | | all lstat() | 800 | 59.908 | 72.730 | 54.208 | V +------------+--------------+--------------+-------------+ (*) /selinux/avc/cache_threshold is 512 (default) CPU: Pentium4 (3.20Ghz), single core When # of files is less than cache_threshold, it shows a little performance impact in both of Bounds/No-bounds cases. When # of files is greater than cache_threshold, it shows a significant performance impact. The difference between No-bounds ans Bounds cases got grown. However, it is extremely artifactual measurement result. In the typical case, the rate of AVC hits is greater than 99%. I don't think we don't need to worry about such level of performance impact. e.g) avcstat just after rawhide bootup. [kaigai@saba ~]$ /usr/sbin/avcstat lookups hits misses allocs reclaims frees 619277 614912 4365 4365 3840 3863 Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> [-- Attachment #2: avc_misses.c --] [-- Type: text/plain, Size: 949 bytes --] #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc, char *argv[]) { struct stat st_buf; struct timeval t1, t2; double delta; int nloops, rc, i, j; if (argc < 3) { fprintf(stderr, "usage: %s <# of loops> <files ...>\n", argv[0]); return 1; } nloops = atol(argv[1]); if (nloops < 1000) nloops = 1000; gettimeofday(&t1, NULL); for (i=0; i < nloops; i++) { for (j=2; argv[j]; j++) { rc = lstat(argv[j], &st_buf); if (rc < 0) { fprintf(stderr, "lstat(%s,...) : %s\n", argv[j], strerror(errno)); return 1; } } } gettimeofday(&t2, NULL); delta = (((double)(t2.tv_sec - t1.tv_sec)) * 1000000.0 + ((double)(t2.tv_usec - t1.tv_usec))) / 1000000.0; printf("result: %.3f [s] (%d of files, %d of loops)\n", delta, argc - 2, nloops); return 0; } [-- Attachment #3: avc_misses_figure.png --] [-- Type: image/png, Size: 47810 bytes --] ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) 2008-08-25 12:32 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei 2008-08-25 12:57 ` Stephen Smalley @ 2008-08-26 9:01 ` James Morris 2008-08-26 10:29 ` James Morris 2 siblings, 0 replies; 97+ messages in thread From: James Morris @ 2008-08-26 9:01 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, paul.moore, jbrindle, selinux On Mon, 25 Aug 2008, KaiGai Kohei wrote: > The following patch is revised one for kernel. For future reference, the patch needs to have a description in the body of the email, and you can leave the updates under the '---' line. See http://www.zipworld.com.au/~akpm/linux/patches/stuff/tpp.txt -- James Morris <jmorris@namei.org> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) 2008-08-25 12:32 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei 2008-08-25 12:57 ` Stephen Smalley 2008-08-26 9:01 ` James Morris @ 2008-08-26 10:29 ` James Morris 2008-08-26 10:47 ` James Morris ` (2 more replies) 2 siblings, 3 replies; 97+ messages in thread From: James Morris @ 2008-08-26 10:29 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, paul.moore, jbrindle, selinux On Mon, 25 Aug 2008, KaiGai Kohei wrote: > @@ -5228,11 +5232,14 @@ static int selinux_setprocattr(struct task_struct *p, > do_each_thread(g, t) { > if (t->mm == mm && t != p) { > read_unlock(&tasklist_lock); > + if (!security_bounded_transition(tsec->sid, sid)) > + goto boundary_ok; > return -EPERM; Propagate the return value of security_bounded_transition(). Also, if the user/role bounds are not being used, should they be included in this? From the kernel point of view, unused code should never be added. - James -- James Morris <jmorris@namei.org> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) 2008-08-26 10:29 ` James Morris @ 2008-08-26 10:47 ` James Morris 2008-08-27 1:15 ` KaiGai Kohei ` (2 more replies) 2008-08-27 1:11 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei 2008-08-28 7:35 ` [PATCH] SELinux: add boundary support and thread context assignment KaiGai Kohei 2 siblings, 3 replies; 97+ messages in thread From: James Morris @ 2008-08-26 10:47 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, paul.moore, jbrindle, selinux Could you also please add tests for this (at least one which should fail and one which should succeed) to the Linux Test Project? - James -- James Morris <jmorris@namei.org> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) 2008-08-26 10:47 ` James Morris @ 2008-08-27 1:15 ` KaiGai Kohei 2008-08-27 8:04 ` [LTP][PATCH 1/2] Replacement of deprecated interfaces KaiGai Kohei 2008-08-27 8:05 ` [LTP][PATCH 2/2] Add a new test case for bounds types KaiGai Kohei 2 siblings, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-27 1:15 UTC (permalink / raw) To: James Morris; +Cc: Stephen Smalley, paul.moore, jbrindle, selinux James Morris wrote: > Could you also please add tests for this (at least one which should fail > and one which should succeed) to the Linux Test Project? I checked out LTP CVS tree just now, and seeing it. Please wait for a few days... -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [LTP][PATCH 1/2] Replacement of deprecated interfaces 2008-08-26 10:47 ` James Morris 2008-08-27 1:15 ` KaiGai Kohei @ 2008-08-27 8:04 ` KaiGai Kohei 2008-08-27 12:14 ` Stephen Smalley 2008-08-27 8:05 ` [LTP][PATCH 2/2] Add a new test case for bounds types KaiGai Kohei 2 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-08-27 8:04 UTC (permalink / raw) To: James Morris; +Cc: Stephen Smalley, paul.moore, jbrindle, selinux [-- Attachment #1: Type: text/plain, Size: 624 bytes --] James Morris wrote: > Could you also please add tests for this (at least one which should fail > and one which should succeed) to the Linux Test Project? > > > - James Policies stored in ltp/testcases/kernel/security/selinux-testsuite/refpolicy/ invokes massive deprecated interfaces on selinux-policy-3.5.4. This patch fixes them according to the warning messages which encourage to replace older ones. BTW, I'm not happy with the test_policy.pp does not allow to invoke test scripts from unconfined_t domain. Is it to be fixed? Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> [-- Attachment #2: ltp-selinux-refpolicy-fixes.patch --] [-- Type: text/x-patch, Size: 27273 bytes --] Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_ipc.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_ipc.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_ipc.te (revision 2) @@ -72,12 +72,12 @@ # Allow all of these domains to be entered from user domains. # via a shell script in the test directory or by another program. miscfiles_domain_entry_test_files(ipcdomain) -userdom_sysadm_entry_spec_domtrans_to(ipcdomain) +sysadm_entry_spec_domtrans(ipcdomain) corecmd_bin_entry_type(ipcdomain) -userdom_sysadm_bin_spec_domtrans_to(ipcdomain) +sysadm_bin_spec_domtrans_to(ipcdomain) allow test_ipc_base_t self:sem create_sem_perms; allow test_ipc_base_t self:shm create_sem_perms; allow test_ipc_base_t self:shm lock; # ipcrm needs this... -userdom_search_generic_user_home_dirs(test_ipc_base_t) +unprivuser_search_home_dirs(test_ipc_base_t) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_file.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_file.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_file.te (revision 2) @@ -43,7 +43,6 @@ # Allow execution of helper programs. corecmd_exec_bin(fileopdomain) -corecmd_exec_sbin(fileopdomain) domain_exec_all_entry_files(fileopdomain) libs_use_ld_so(fileopdomain) libs_use_shared_libs(fileopdomain) @@ -52,13 +51,13 @@ # Allow all of these domains to be entered from sysadm domain miscfiles_domain_entry_test_files(fileopdomain) -userdom_sysadm_entry_spec_domtrans_to(fileopdomain) +sysadm_entry_spec_domtrans(fileopdomain) corecmd_bin_entry_type(fileopdomain) -userdom_sysadm_bin_spec_domtrans_to(fileopdomain) +sysadm_bin_spec_domtrans_to(fileopdomain) -corecmd_sbin_entry_type(fileopdomain) -userdom_sysadm_sbin_spec_domtrans_to(fileopdomain) +corecmd_bin_entry_type(fileopdomain) +sysadm_bin_spec_domtrans_to(fileopdomain) allow fileop_t fileop_exec_t:file entrypoint; domain_auto_trans(test_fileop_t, fileop_exec_t, fileop_t) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_sysctl.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_sysctl.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_sysctl.te (revision 2) @@ -19,8 +19,8 @@ # Allow all of these domains to be entered from sysadm domain # via /sbin/sysctl. -corecmd_sbin_entry_type(sysctldomain) -userdom_sysadm_sbin_spec_domtrans_to(sysctldomain) +corecmd_bin_entry_type(sysctldomain) +sysadm_bin_spec_domtrans_to(sysctldomain) # Allow the first domain to perform sysctl operations. kernel_rw_all_sysctls(test_sysctl_t) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_create.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_create.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_create.te (revision 2) @@ -24,10 +24,10 @@ typeattribute test_create_no_t test_create_d; allow test_create_no_t self:process ~fork; -allow test_create_no_t proc_t:dir r_dir_perms; +allow test_create_no_t proc_t:dir list_dir_perms; allow test_create_no_t proc_t:lnk_file read; -allow test_create_no_t self:dir r_dir_perms; -allow test_create_no_t self:notdevfile_class_set r_file_perms; +allow test_create_no_t self:dir list_dir_perms; +allow test_create_no_t self:notdevfile_class_set read_file_perms; libs_use_ld_so(test_create_no_t) libs_use_shared_libs(test_create_no_t) @@ -35,14 +35,14 @@ allow test_create_no_t self:process setexec; selinux_get_fs_mount(test_create_no_t) -allow test_create_no_t { root_t bin_t sbin_t lib_t locale_t usr_t devpts_t home_root_t }:dir r_dir_perms; -allow test_create_no_t lib_t:lnk_file r_file_perms; +allow test_create_no_t { root_t bin_t sbin_t lib_t locale_t usr_t devpts_t home_root_t }:dir list_dir_perms; +allow test_create_no_t lib_t:lnk_file read_file_perms; allow test_create_no_t { devtty_t null_device_t zero_device_t }:chr_file rw_file_perms; -allow test_create_no_t locale_t:dir r_dir_perms; -allow test_create_no_t locale_t:{ file lnk_file } r_file_perms; +allow test_create_no_t locale_t:dir list_dir_perms; +allow test_create_no_t locale_t:{ file lnk_file } read_file_perms; allow test_create_no_t privfd:fd use; -userdom_use_sysadm_ptys(test_create_no_t) -userdom_use_sysadm_ttys(test_create_no_t) +sysadm_use_ptys(test_create_no_t) +sysadm_use_ttys(test_create_no_t) # General rules for the test_create_d @@ -50,4 +50,4 @@ role sysadm_r types test_create_d; role system_r types test_create_d; miscfiles_domain_entry_test_files(test_create_d) -userdom_sysadm_entry_spec_domtrans_to(test_create_d) +sysadm_entry_spec_domtrans(test_create_d) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_capable_file.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_capable_file.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_capable_file.te (revision 2) @@ -35,7 +35,6 @@ # Allow execution of helper programs. corecmd_exec_bin(capabledomain) -corecmd_exec_sbin(capabledomain) domain_exec_all_entry_files(capabledomain) files_exec_etc_files(capabledomain) libs_use_ld_so(capabledomain) @@ -45,9 +44,9 @@ # Allow test_file_t and bin_t to be entered from sysadm role miscfiles_domain_entry_test_files(capabledomain) -userdom_sysadm_entry_spec_domtrans_to(capabledomain) +sysadm_entry_spec_domtrans(capabledomain) corecmd_bin_entry_type(capabledomain) -userdom_sysadm_bin_spec_domtrans_to(capabledomain) +sysadm_bin_spec_domtrans_to(capabledomain) # Allow these domains to create a temporay file. allow capabledomain test_file_t:file { setattr rw_file_perms }; Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_setnice.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_setnice.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_setnice.te (revision 2) @@ -25,7 +25,6 @@ # Allow execution of helper programs. corecmd_exec_bin(setnicedomain) -corecmd_exec_sbin(setnicedomain) domain_exec_all_entry_files(setnicedomain) files_exec_etc_files(setnicedomain) libs_use_ld_so(setnicedomain) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_fdreceive.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_fdreceive.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_fdreceive.te (revision 2) @@ -35,7 +35,7 @@ # Allow all of these domains to be entered from the sysadm domain. miscfiles_domain_entry_test_files(fdreceivedomain) -userdom_sysadm_entry_spec_domtrans_to(fdreceivedomain) +sysadm_entry_spec_domtrans(fdreceivedomain) # Grant the necessary permissions for the server domain. ## Create the Unix domain socket file. Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_link.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_link.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_link.te (revision 2) @@ -69,5 +69,5 @@ # Allow all of these domains to be entered from sysadm domain corecmd_bin_entry_type(test_link_domain) -userdom_sysadm_bin_spec_domtrans_to(test_link_domain) +sysadm_bin_spec_domtrans_to(test_link_domain) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_sigkill.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_sigkill.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_sigkill.te (revision 2) @@ -43,7 +43,7 @@ # Allow all of these domains to be entered from the sysadm domains, # via kill or a program in the test directory. miscfiles_domain_entry_test_files(killdomain) -userdom_sysadm_entry_spec_domtrans_to(killdomain) +sysadm_entry_spec_domtrans(killdomain) corecmd_bin_entry_type(killdomain) -userdom_sysadm_bin_spec_domtrans_to(killdomain) +sysadm_bin_spec_domtrans_to(killdomain) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_global.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_global.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_global.te (revision 2) @@ -13,7 +13,7 @@ # Allow the test domains to access the sysadm terminal. # This allows read and write sysadm ttys and ptys. -userdom_use_sysadm_terms(testdomain) +sysadm_use_terms(testdomain) # Allow the test domains to access the test directory and files # even if they are not root owned. @@ -64,9 +64,9 @@ type null_device_t; type zero_device_t; } -allow testdomain { root_t etc_t bin_t sbin_t lib_t usr_t devpts_t }:dir r_dir_perms; -allow testdomain lib_t:{ file lnk_file } r_file_perms; -allow testdomain etc_t:file r_file_perms; +allow testdomain { root_t etc_t bin_t sbin_t lib_t usr_t devpts_t }:dir list_dir_perms; +allow testdomain lib_t:{ file lnk_file } read_file_perms; +allow testdomain etc_t:file read_file_perms; allow testdomain { devtty_t null_device_t zero_device_t }:chr_file rw_file_perms; miscfiles_read_localization(testdomain) domain_use_interactive_fds(testdomain) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_open.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_open.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_open.te (revision 2) @@ -32,4 +32,4 @@ # Allow all of these domains to be entered from sysadm domain miscfiles_domain_entry_test_files(test_open_domain) -userdom_sysadm_entry_spec_domtrans_to(test_open_domain) +sysadm_entry_spec_domtrans(test_open_domain) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_getsid.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_getsid.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_getsid.te (revision 2) @@ -25,7 +25,7 @@ # Allow domain to be entered from the sysadm domain. miscfiles_domain_entry_test_files(test_getsid_d) -userdom_sysadm_entry_spec_domtrans_to(test_getsid_d) +sysadm_entry_spec_domtrans(test_getsid_d) # Give test_getsid_yes_t the permission needed. allow test_getsid_yes_t test_getsid_target_t:process getsession; Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_inherit.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_inherit.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_inherit.te (revision 2) @@ -37,7 +37,7 @@ # Allow all of these domains to be entered from the sysadm domain. miscfiles_domain_entry_test_files(inheritdomain) -userdom_sysadm_entry_spec_domtrans_to(inheritdomain) +sysadm_entry_spec_domtrans(inheritdomain) # Grant the necessary permissions for the parent domain. allow test_inherit_parent_t test_inherit_file_t:file rw_file_perms; @@ -61,4 +61,4 @@ allow test_inherit_nowrite_t test_inherit_parent_t:fd use; allow test_inherit_nowrite_t test_inherit_parent_t:fifo_file rw_file_perms; allow test_inherit_nowrite_t test_inherit_parent_t:process sigchld; -allow test_inherit_nowrite_t test_inherit_file_t:file r_file_perms; +allow test_inherit_nowrite_t test_inherit_file_t:file read_file_perms; Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_getpgid.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_getpgid.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_getpgid.te (revision 2) @@ -25,7 +25,7 @@ # Allow domain to be entered from the sysadm domain miscfiles_domain_entry_test_files(test_getpgid_d) -userdom_sysadm_entry_spec_domtrans_to(test_getpgid_d) +sysadm_entry_spec_domtrans(test_getpgid_d) # Give test_getpgid_yes_t the permission needed. allow test_getpgid_yes_t test_getpgid_target_t:process getpgid; Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_relabel.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_relabel.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_relabel.te (revision 2) @@ -40,5 +40,5 @@ # Allow all of these domains to be entered from sysadm domain corecmd_bin_entry_type(test_relabel_domain) -userdom_sysadm_bin_spec_domtrans_to(test_relabel_domain) +sysadm_bin_spec_domtrans_to(test_relabel_domain) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_execshare.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_execshare.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_execshare.te (revision 2) @@ -25,7 +25,7 @@ # Allow all of these domains to be entered from the sysadm domain. miscfiles_domain_entry_test_files(execsharedomain) -userdom_sysadm_entry_spec_domtrans_to(execsharedomain) +sysadm_entry_spec_domtrans(execsharedomain) # Grant the necessary permissions for the child domain. domain_entry_file_spec_domtrans(test_execshare_parent_t, test_execshare_child_t) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_getsched.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_getsched.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_getsched.te (revision 2) @@ -25,7 +25,7 @@ # Allow domain to be entered from the sysadm domain. miscfiles_domain_entry_test_files(test_getsched_d) -userdom_sysadm_entry_spec_domtrans_to(test_getsched_d) +sysadm_entry_spec_domtrans(test_getsched_d) # Give test_getsched_yes_t the permission needed. allow test_getsched_yes_t test_getsched_target_t:process getsched; Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_dyntrace.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_dyntrace.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_dyntrace.te (revision 2) @@ -28,7 +28,7 @@ # Allow test_files_t to be entered from the sysadm domain. miscfiles_domain_entry_test_files(dyntracedomain) -userdom_sysadm_entry_spec_domtrans_to(dyntracedomain) +sysadm_entry_spec_domtrans(dyntracedomain) miscfiles_exec_test_files(dyntracedomain) # Grant the necessary permissions for the child domain. Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_ioctl.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_ioctl.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_ioctl.te (revision 2) @@ -23,7 +23,6 @@ # Allow execution of helper programs. corecmd_exec_bin(ioctldomain) -corecmd_exec_sbin(ioctldomain) domain_exec_all_entry_files(ioctldomain) files_exec_etc_files(ioctldomain) libs_use_ld_so(ioctldomain) @@ -34,9 +33,9 @@ # Allow all of these domains to be entered from sysadm domain # via a shell script in the test directory or by.... miscfiles_domain_entry_test_files(ioctldomain) -userdom_sysadm_entry_spec_domtrans_to(ioctldomain) +sysadm_entry_spec_domtrans(ioctldomain) corecmd_bin_entry_type(ioctldomain) -userdom_sysadm_bin_spec_domtrans_to(ioctldomain) +sysadm_bin_spec_domtrans_to(ioctldomain) # Allow the test domains some access to the temp file allow test_ioctl_t test_ioctl_file_t:file { read getattr setattr ioctl }; Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_entrypoint.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_entrypoint.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_entrypoint.te (revision 2) @@ -17,5 +17,4 @@ # Allow this domain to be entered via its entrypoint type. domain_entry_file(test_entrypoint_t, test_entrypoint_execute_t) -userdom_sysadm_entry_spec_domtrans_to(test_entrypoint_t) - +sysadm_entry_spec_domtrans(test_entrypoint_t) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_ptrace.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_ptrace.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_ptrace.te (revision 2) @@ -27,7 +27,7 @@ # Allow the tracer domain to trace the traced domain. allow test_ptrace_tracer_t test_ptrace_traced_t:process ptrace; -userdom_search_generic_user_home_dirs(test_ptrace_traced_t) +unprivuser_search_home_dirs(test_ptrace_traced_t) # Let the tracer wait on the traced domain. allow test_ptrace_traced_t test_ptrace_tracer_t:process sigchld; @@ -35,4 +35,4 @@ # Allow all of these domains to be entered from the sysadm domains. # via a program in the test directory. miscfiles_domain_entry_test_files(ptracedomain) -userdom_sysadm_entry_spec_domtrans_to(ptracedomain) +sysadm_entry_spec_domtrans(ptracedomain) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_setpgid.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_setpgid.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_setpgid.te (revision 2) @@ -18,27 +18,28 @@ typeattribute test_setpgid_no_t test_setpgid_d; allow test_setpgid_no_t self:process ~{ setpgid setcurrent }; -allow test_setpgid_no_t proc_t:dir r_dir_perms; +allow test_setpgid_no_t proc_t:dir list_dir_perms; allow test_setpgid_no_t proc_t:lnk_file read; -allow test_setpgid_no_t self:dir r_dir_perms; -allow test_setpgid_no_t self:notdevfile_class_set r_file_perms; +allow test_setpgid_no_t self:dir list_dir_perms; +allow test_setpgid_no_t self:notdevfile_class_set read_file_perms; libs_use_ld_so(test_setpgid_no_t) libs_use_shared_libs(test_setpgid_no_t) allow test_setpgid_no_t self:process setexec; selinux_get_fs_mount(test_setpgid_no_t) -allow test_setpgid_no_t { root_t bin_t sbin_t lib_t locale_t usr_t devpts_t home_root_t }:dir r_dir_perms; -allow test_setpgid_no_t lib_t:lnk_file r_file_perms; +allow test_setpgid_no_t { root_t bin_t sbin_t lib_t locale_t usr_t devpts_t home_root_t }:dir list_dir_perms; +allow test_setpgid_no_t lib_t:lnk_file read_file_perms; allow test_setpgid_no_t { devtty_t null_device_t zero_device_t }:chr_file rw_file_perms; -allow test_setpgid_no_t locale_t:dir r_dir_perms; -allow test_setpgid_no_t locale_t:{ file lnk_file } r_file_perms; +allow test_setpgid_no_t locale_t:dir list_dir_perms; +allow test_setpgid_no_t locale_t:{ file lnk_file } read_file_perms; allow test_setpgid_no_t privfd:fd use; -userdom_use_sysadm_ptys(test_setpgid_no_t) -userdom_use_sysadm_ttys(test_setpgid_no_t) +sysadm_use_ptys(test_setpgid_no_t) +sysadm_use_ttys(test_setpgid_no_t) # Allow domain to be entered from the sysadm domain. role sysadm_r types test_setpgid_d; role system_r types test_setpgid_d; miscfiles_domain_entry_test_files(test_setpgid_d) -userdom_sysadm_entry_spec_domtrans_to(test_setpgid_d) +sysadm_entry_spec_domtrans(test_setpgid_d) +userdom_entry_spec_domtrans_unpriv_users(test_setpgid_d) \ No newline at end of file Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_setsched.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_setsched.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_task_setsched.te (revision 2) @@ -26,7 +26,7 @@ # Allow domain to be entered from the sysadm domain. miscfiles_domain_entry_test_files(test_setsched_d) -userdom_sysadm_entry_spec_domtrans_to(test_setsched_d) +sysadm_entry_spec_domtrans(test_setsched_d) # Allow these domains to execute renice. corecmd_bin_entry_type(test_setsched_d) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_setattr.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_setattr.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_setattr.te (revision 2) @@ -27,5 +27,5 @@ # Allow all of these domains to be entered from sysadm domain corecmd_bin_entry_type(test_setattr_domain) -userdom_sysadm_bin_spec_domtrans_to(test_setattr_domain) +sysadm_bin_spec_domtrans_to(test_setattr_domain) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_transition.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_transition.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_transition.te (revision 2) @@ -30,4 +30,4 @@ allow test_transition_todomain_t test_transition_fromdomain_t:fd use; # Allow all of these domains to be entered from the sysadm domain. -userdom_sysadm_entry_spec_domtrans_to(transitiondomain) +sysadm_entry_spec_domtrans(transitiondomain) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_capable_net.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_capable_net.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_capable_net.te (revision 2) @@ -28,7 +28,7 @@ corenet_raw_sendrecv_all_nodes(capabledomain) corenet_tcp_sendrecv_all_ports(capabledomain) corenet_udp_sendrecv_all_ports(capabledomain) -corenet_non_ipsec_sendrecv(capabledomain) +corenet_all_recvfrom_unlabeled(capabledomain) corenet_tcp_bind_all_nodes(capabledomain) corenet_udp_bind_all_nodes(capabledomain) sysnet_read_config(capabledomain) @@ -44,8 +44,8 @@ allow capabledomain hi_reserved_port_t:tcp_socket name_bind; # Allow sbin_t to be entered from admin via certain utils. -corecmd_sbin_entry_type(capabledomain) -userdom_sysadm_sbin_spec_domtrans_to(capabledomain) +corecmd_bin_entry_type(capabledomain) +sysadm_bin_spec_domtrans_to(capabledomain) require { type ifconfig_exec_t; Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_dyntrans.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_dyntrans.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_dyntrans.te (revision 2) @@ -28,5 +28,4 @@ # Allow all of these domains to be entered from the sysadm domain. miscfiles_domain_entry_test_files(dyntransdomain) -userdom_sysadm_entry_spec_domtrans_to(dyntransdomain) - +sysadm_entry_spec_domtrans(dyntransdomain) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_exectrace.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_exectrace.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_exectrace.te (revision 2) @@ -28,7 +28,7 @@ # Allow all of these domains to be entered from the sysadm domain. miscfiles_domain_entry_test_files(exectracedomain) -userdom_sysadm_entry_spec_domtrans_to(exectracedomain) +sysadm_entry_spec_domtrans(exectracedomain) # Grant the necessary permissions for the child domain. domain_entry_file_spec_domtrans(test_exectrace_parent_t, test_exectrace_child_t) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_wait.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_wait.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_wait.te (revision 2) @@ -25,7 +25,7 @@ # Allow all of these domains to be entered from the sysadm domain. miscfiles_domain_entry_test_files(waitdomain) -userdom_sysadm_entry_spec_domtrans_to(waitdomain) +sysadm_entry_spec_domtrans(waitdomain) # Grant permissions for a domain transition from parent to child, # including the ability to wait on the child. Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_execute_no_trans.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_execute_no_trans.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_execute_no_trans.te (revision 2) @@ -19,8 +19,8 @@ # Allow this domain to be entered via the shell. corecmd_shell_entry_type(test_execute_notrans_t) -userdom_sysadm_entry_spec_domtrans_to(test_execute_notrans_t) +sysadm_entry_spec_domtrans(test_execute_notrans_t) #Allow test_execute_notrans permissions to the allowed type can_exec(test_execute_notrans_t,test_execute_notrans_allowed_t) -allow test_execute_notrans_t test_execute_notrans_denied_t:file rx_file_perms; +allow test_execute_notrans_t test_execute_notrans_denied_t:file { mmap_file_perms ioctl lock }; Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_mkdir.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_mkdir.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_mkdir.te (revision 2) @@ -56,4 +56,4 @@ # Allow all of these domains to be entered from sysadm domain corecmd_bin_entry_type(test_mkdir_domain) -userdom_sysadm_bin_spec_domtrans_to(test_mkdir_domain) +sysadm_bin_spec_domtrans_to(test_mkdir_domain) Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_rename.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_rename.te (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_rename.te (revision 2) @@ -103,5 +103,5 @@ # Allow all of these domains to be entered from sysadm domain corecmd_bin_entry_type(test_rename_domain) -userdom_sysadm_bin_spec_domtrans_to(test_rename_domain) +sysadm_bin_spec_domtrans_to(test_rename_domain) ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [LTP][PATCH 1/2] Replacement of deprecated interfaces 2008-08-27 8:04 ` [LTP][PATCH 1/2] Replacement of deprecated interfaces KaiGai Kohei @ 2008-08-27 12:14 ` Stephen Smalley 2008-08-28 6:26 ` KaiGai Kohei 0 siblings, 1 reply; 97+ messages in thread From: Stephen Smalley @ 2008-08-27 12:14 UTC (permalink / raw) To: KaiGai Kohei; +Cc: James Morris, paul.moore, jbrindle, selinux On Wed, 2008-08-27 at 17:04 +0900, KaiGai Kohei wrote: > James Morris wrote: > > Could you also please add tests for this (at least one which should fail > > and one which should succeed) to the Linux Test Project? > > > > > > - James > > Policies stored in ltp/testcases/kernel/security/selinux-testsuite/refpolicy/ > invokes massive deprecated interfaces on selinux-policy-3.5.4. > > This patch fixes them according to the warning messages which encourage to > replace older ones. > > BTW, I'm not happy with the test_policy.pp does not allow to invoke test > scripts from unconfined_t domain. Is it to be fixed? I don't quite follow. Did you follow the instructions in the selinux-testsuite README? I can run the test scripts either using the test_selinux.sh script or by manually loading the policy and then individually running them as described in the README. Watch out that your patch doesn't disturb the existing misc/sbin_deprecated.patch that gets applied by test_selinux.sh. Keep in mind that this testsuite gets run on everything from RHEL4 to F9. -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [LTP][PATCH 1/2] Replacement of deprecated interfaces 2008-08-27 12:14 ` Stephen Smalley @ 2008-08-28 6:26 ` KaiGai Kohei 2008-08-28 12:10 ` Subrata Modak 2008-10-23 9:48 ` Subrata Modak 0 siblings, 2 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-28 6:26 UTC (permalink / raw) To: Stephen Smalley; +Cc: James Morris, paul.moore, jbrindle, selinux [-- Attachment #1: Type: text/plain, Size: 2507 bytes --] Stephen Smalley wrote: > On Wed, 2008-08-27 at 17:04 +0900, KaiGai Kohei wrote: >> James Morris wrote: >>> Could you also please add tests for this (at least one which should fail >>> and one which should succeed) to the Linux Test Project? >>> >>> >>> - James >> Policies stored in ltp/testcases/kernel/security/selinux-testsuite/refpolicy/ >> invokes massive deprecated interfaces on selinux-policy-3.5.4. >> >> This patch fixes them according to the warning messages which encourage to >> replace older ones. >> >> BTW, I'm not happy with the test_policy.pp does not allow to invoke test >> scripts from unconfined_t domain. Is it to be fixed? > > I don't quite follow. Did you follow the instructions in the > selinux-testsuite README? I didn't read the README file carefully, Oops. The update_refpolicy.sh fixes some of deprecated interfaces and inject an interface to kick test script from unconfined domain. So, I can run the testsuite which includs bounds test without any problems on Rawhide. # However, I got some warnings for deprecated interfaces/macros # like r_dir_perms, userdom_sysadm_bin_spec_domtrans_to or # userdom_use_sysadm_ptys. The attached patch is a new test case of the boundary feature, which contains six tests, as follows: test01: It tries to invoke setcon() with bounded domain in a multi-threaded process. The expected result is success. test02: It tries to invoke setcon() with unrelated domain in a multi-threaded process. The expected result is fail. test03: It makes a bounded domain try to read a file, when its bounds domain can read the file. The expected result is success. test04: It makes a bounded domain try to write a file, when its bounds domain cannot write the file. The expected result is fail, because write permission is boundary violated. test05: It tries to write a bounded type, even if the domain cannot write to its bounds type. The expected result is fail. test06: It makes a bounded domain try to set an attribute of bounded type. Thanks, > I can run the test scripts either using the > test_selinux.sh script or by manually loading the policy and then > individually running them as described in the README. Watch out that > your patch doesn't disturb the existing misc/sbin_deprecated.patch that > gets applied by test_selinux.sh. Keep in mind that this testsuite gets > run on everything from RHEL4 to F9. -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> [-- Attachment #2: ltp-selinux-bounds-tests.2.patch --] [-- Type: text/x-patch, Size: 11629 bytes --] Index: ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile (working copy) @@ -3,7 +3,7 @@ ifeq (redhat-release-4, $(findstring redhat-release-4, $(REDHAT_RELEASE))) SUBDIRS=domain_trans entrypoint execshare exectrace execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink relabel rename rxdir sem setattr setnice shm sigkill stat sysctl task_create task_setnice task_setscheduler task_getscheduler task_getsid task_getpgid task_setpgid wait file ioctl capable_file capable_net capable_sys else - SUBDIRS=domain_trans entrypoint execshare exectrace execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink relabel rename rxdir sem setattr setnice shm sigkill stat sysctl task_create task_setnice task_setscheduler task_getscheduler task_getsid task_getpgid task_setpgid wait file ioctl capable_file capable_net capable_sys dyntrace dyntrans + SUBDIRS=domain_trans entrypoint execshare exectrace execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink relabel rename rxdir sem setattr setnice shm sigkill stat sysctl task_create task_setnice task_setscheduler task_getscheduler task_getsid task_getpgid task_setpgid wait file ioctl capable_file capable_net capable_sys dyntrace dyntrans bounds endif all: Index: ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c (revision 0) +++ ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c (revision 0) @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2008 NEC Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <pthread.h> +#include <selinux/selinux.h> +#include <selinux/context.h> + +static int thread_status = 0; + +static void *worker(void *datap) +{ + security_context_t security_context = datap; + int rc; + + rc = setcon(security_context); + if (rc < 0) + thread_status = errno; + + return NULL; +} + +int main(int argc, char *argv[]) +{ + security_context_t security_context; + context_t context; + pthread_t thread; + int rc; + + if (argc != 2) { + fprintf(stderr, "usage: %s <new domain>\n", argv[0]); + return 1; + } + + rc = getcon(&security_context); + if (rc < 0) { + fprintf(stderr, "%s: unable to get my context\n", argv[0]); + return 1; + } + + context = context_new(security_context); + if (!context) { + fprintf(stderr, "%s: unable to create context structure\n", argv[0]); + return 1; + } + + if (context_type_set(context, argv[1])) { + fprintf(stderr, "%s: unable to set new type\n", argv[0]); + return 1; + } + + freecon(security_context); + security_context = context_str(context); + if (!security_context) { + fprintf(stderr, "%s: unable to obtain new context string\n", argv[0]); + return 1; + } + + rc = pthread_create(&thread, NULL, worker, security_context); + if (rc) { + fprintf(stderr, "%s: unable to kick a new thread\n", argv[0]); + return 1; + } + + rc = pthread_join(thread, NULL); + if (rc) { + fprintf(stderr, "%s: unable to join its thread\n", argv[0]); + return 1; + } + + fprintf(stderr, "%s: setcon('%s') : %s\n", + argv[0], argv[1], strerror(thread_status)); + + return thread_status; +} Index: ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh (revision 0) +++ ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh (revision 0) @@ -0,0 +1,166 @@ +#!/bin/sh +# +# Copyright (c) 2008 NEC Corporation +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) +# any later version. +# + +setup() +{ + export TCID="setup" + export TST_COUNT=0 + export TST_TOTAL=4 + + # Remove any leftover test directories from prior failed runs. + rm -rf $SELINUXTMPDIR/bounds_file* + + # Create test files + dd if=/dev/zero of=$SELINUXTMPDIR/bounds_file count=1 + dd if=/dev/zero of=$SELINUXTMPDIR/bounds_file_red count=1 + dd if=/dev/zero of=$SELINUXTMPDIR/bounds_file_blue count=1 + chcon -t test_bounds_file_t $SELINUXTMPDIR/bounds_file + chcon -t test_bounds_file_red_t $SELINUXTMPDIR/bounds_file_red + chcon -t test_bounds_file_blue_t $SELINUXTMPDIR/bounds_file_blue +} + +test01() +{ + TCID="test01" + TST_COUNT=1 + RC=0 + + runcon -t test_bounds_parent_t \ + -- selinux_bounds_thread test_bounds_child_t 2>&1 + RC=$? + if [ $RC -eq 0 ]; + then + echo "$TCID PASS : thread dyntrans passed." + else + echo "$TCID FAIL : thread dynstrans failed." + fi + return $RC +} + +test02() +{ + TCID="test02" + TST_COUND=2 + RC=0 + + runcon -t test_bounds_parent_t \ + -- selinux_bounds_thread test_bounds_unbound_t 2>&1 + RC=$? + if [ $RC -ne 0 ]; # we expect this to fail + then + echo "$TCID PASS : thread dyntrans to unbound domain failed." + RC=0 + else + echo "$TCID FAIL : thread dyntrans to unbound domain succeeded." + RC=1 + fi + return $RC +} + +test03() +{ + TCID="test03" + TST_COUND=3 + RC=0 + + runcon -t test_bounds_child_t \ + -- dd if=$SELINUXTMPDIR/bounds_file of=/dev/null count=1 + RC=$? + if [ $RC -eq 0 ]; + then + echo "$TCID PASS : unbounded action to be allowed." + else + echo "$TCID FAIL : unbounded action to be allowed." + fi + return $RC +} + +test04() +{ + TCID="test04" + TST_COUNT=4 + RC=0 + + runcon -t test_bounds_child_t \ + -- dd if=/dev/zero of=$SELINUXTMPDIR/bounds_file count=1 + RC=$? + if [ $RC -ne 0 ]; # we expect this to fail + then + echo "$TCID PASS : bounded action to be denied." + RC=0 + else + echo "$TCID FAIL : bounded action to be denied." + RC=1 + fi + return $RC +} + +test05() +{ + TCID="test05" + TST_COUNT=5 + RC=0 + + runcon -t test_bounds_parent_t \ + -- dd if=/dev/zero of=$SELINUXTMPDIR/bounds_file_red count=1 + RC=$? + if [ $RC -ne 0 ]; # we expect this to fail + then + echo "$TCID PASS : actions to bounded type to be denied." + RC=0 + else + echo "$TCID FAIL : actions to bounded type to be denied." + RC=1 + fi + return $RC +} + +test06() +{ + TCID="test06" + TST_COUNT=6 + RC=0 + + runcon -t test_bounds_child_t -- chmod 0777 $SELINUXTMPDIR/bounds_file_blue + RC=$? + if [ $RC -eq 0 ]; + then + echo "$TCID PASS : bounds of subject can setattr bounds of target" + else + echo "$TCID FAIL : bounds of subject can setattr bounds of target" + fi + return $RC +} + +cleanup() +{ + # Cleanup + rm -rf $SELINUXTMPDIR/bounds_file* +} + +# Function: main +# +# Description: - Execute all tests, exit with test status. +# +# Exit: - zero on success +# - non-zero on failure. +# +RC=0 # Return value from setup, and test functions. +EXIT_VAL=0 + +setup +test01 || EXIT_VAL=$RC +test02 || EXIT_VAL=$RC +test03 || EXIT_VAL=$RC +test04 || EXIT_VAL=$RC +test05 || EXIT_VAL=$RC +test06 || EXIT_VAL=$RC +cleanup +exit $EXIT_VAL Property changes on: ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh ___________________________________________________________________ Added: svn:executable + * Index: ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile (revision 0) +++ ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile (revision 0) @@ -0,0 +1,11 @@ +TARGETS=$(patsubst %.c,%,$(wildcard *.c)) +LDLIBS += -lselinux -lpthread + +all: $(TARGETS) + +install: + @set -e; for i in $(TARGETS); do ln -f $$i ../../../../../bin/$$i; done + ln -f selinux_bounds.sh ../../../../../bin/ + +clean: + rm -f $(TARGETS) \ No newline at end of file Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile (revision 1) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile (working copy) @@ -1,7 +1,7 @@ POLICYDEVEL = /usr/share/selinux/devel SEMODULE = /usr/sbin/semodule -TARGETS=test_global.te test_capable_file.te test_capable_net.te \ +TARGETS=test_global.te test_bounds.te test_capable_file.te test_capable_net.te \ test_capable_sys.te test_dyntrace.te test_dyntrans.te test_entrypoint.te \ test_execshare.te test_exectrace.te test_execute_no_trans.te \ test_fdreceive.te test_file.te test_inherit.te test_ioctl.te test_ipc.te \ Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te (revision 0) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te (revision 0) @@ -0,0 +1,57 @@ +################################# +# +# Policy for testing boundary features +# + +attribute test_bounds_domain; +unconfined_runs_test(test_bounds_domain) +corecmd_bin_entry_type(test_bounds_domain) + +# Type for test files +type test_bounds_file_t; +files_type(test_bounds_file_t) + +# Bounded type for files +type test_bounds_file_red_t; +files_type(test_bounds_file_red_t) + +type test_bounds_file_blue_t; +files_type(test_bounds_file_blue_t) + +# Domain for process that allows to other domains +type test_bounds_parent_t; +domain_type(test_bounds_parent_t) +typeattribute test_bounds_parent_t test_bounds_domain; +typeattribute test_bounds_parent_t testdomain; + +allow test_bounds_parent_t test_bounds_file_t : file { read_file_perms setattr }; +allow test_bounds_parent_t test_bounds_file_red_t : file { rw_file_perms }; + +# Domain for process that has a bounds type +type test_bounds_child_t; +domain_type(test_bounds_child_t) +typeattribute test_bounds_child_t test_bounds_domain; +typeattribute test_bounds_child_t testdomain; + +allow test_bounds_child_t test_bounds_file_t : file { rw_file_perms }; +allow test_bounds_child_t test_bounds_file_blue_t : file { getattr setattr }; + +# Domain for process that does not have any bounds type +type test_bounds_unbound_t; +domain_type(test_bounds_unbound_t) +typeattribute test_bounds_unbound_t test_bounds_domain; +typeattribute test_bounds_unbound_t testdomain; + +allow test_bounds_unbound_t test_bounds_file_t : file { rw_file_perms }; + +# Definition of boundary relationship +typebounds test_bounds_parent_t test_bounds_child_t; +typebounds test_bounds_file_t test_bounds_file_red_t, test_bounds_file_blue_t; + +# Allow the test_bounds_parent_t to dyntrans +allow test_bounds_parent_t test_bounds_child_t : process { dyntransition }; +allow test_bounds_parent_t test_bounds_unbound_t : process { dyntransition }; + +# Allow all of these domains to be entered from sysadm domain +miscfiles_domain_entry_test_files(test_bounds_domain) +sysadm_entry_spec_domtrans(test_bounds_domain) ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [LTP][PATCH 1/2] Replacement of deprecated interfaces 2008-08-28 6:26 ` KaiGai Kohei @ 2008-08-28 12:10 ` Subrata Modak 2008-08-28 12:52 ` KaiGai Kohei 2008-10-23 9:48 ` Subrata Modak 1 sibling, 1 reply; 97+ messages in thread From: Subrata Modak @ 2008-08-28 12:10 UTC (permalink / raw) To: KaiGai Kohei Cc: Stephen Smalley, James Morris, paul.moore, jbrindle, selinux, ltp-list Thanks KaiGai. Stephen/James, Would be ACK-ing these 2 patches ? 1) [LTP][PATCH 1/2] Replacement of deprecated interfaces, & 2) [LTP][PATCH 2/2] Add a new test case for bounds types, Regards-- Subrata On Thu, 2008-08-28 at 15:26 +0900, KaiGai Kohei wrote: > Stephen Smalley wrote: > > On Wed, 2008-08-27 at 17:04 +0900, KaiGai Kohei wrote: > >> James Morris wrote: > >>> Could you also please add tests for this (at least one which should fail > >>> and one which should succeed) to the Linux Test Project? > >>> > >>> > >>> - James > >> Policies stored in ltp/testcases/kernel/security/selinux-testsuite/refpolicy/ > >> invokes massive deprecated interfaces on selinux-policy-3.5.4. > >> > >> This patch fixes them according to the warning messages which encourage to > >> replace older ones. > >> > >> BTW, I'm not happy with the test_policy.pp does not allow to invoke test > >> scripts from unconfined_t domain. Is it to be fixed? > > > > I don't quite follow. Did you follow the instructions in the > > selinux-testsuite README? > > I didn't read the README file carefully, Oops. > > The update_refpolicy.sh fixes some of deprecated interfaces and > inject an interface to kick test script from unconfined domain. > So, I can run the testsuite which includs bounds test without > any problems on Rawhide. > > # However, I got some warnings for deprecated interfaces/macros > # like r_dir_perms, userdom_sysadm_bin_spec_domtrans_to or > # userdom_use_sysadm_ptys. > > The attached patch is a new test case of the boundary feature, > which contains six tests, as follows: > > test01: It tries to invoke setcon() with bounded domain in a multi-threaded > process. The expected result is success. > test02: It tries to invoke setcon() with unrelated domain in a multi-threaded > process. The expected result is fail. > test03: It makes a bounded domain try to read a file, when its bounds domain > can read the file. The expected result is success. > test04: It makes a bounded domain try to write a file, when its bounds domain > cannot write the file. The expected result is fail, because write > permission is boundary violated. > test05: It tries to write a bounded type, even if the domain cannot write to > its bounds type. The expected result is fail. > test06: It makes a bounded domain try to set an attribute of bounded type. > > Thanks, > > > I can run the test scripts either using the > > test_selinux.sh script or by manually loading the policy and then > > individually running them as described in the README. Watch out that > > your patch doesn't disturb the existing misc/sbin_deprecated.patch that > > gets applied by test_selinux.sh. Keep in mind that this testsuite gets > > run on everything from RHEL4 to F9. > -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [LTP][PATCH 1/2] Replacement of deprecated interfaces 2008-08-28 12:10 ` Subrata Modak @ 2008-08-28 12:52 ` KaiGai Kohei 2008-08-28 13:34 ` Subrata Modak 0 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-08-28 12:52 UTC (permalink / raw) To: subrata Cc: KaiGai Kohei, Stephen Smalley, James Morris, paul.moore, jbrindle, selinux, ltp-list Subrata, Two patches I sent yesterday should be gone away. The latest patch I sent today is revised, however, it is too early to apply LTP tree, because the new test checks a new kernel feature which is not included yet. Thanks, Subrata Modak wrote: > Thanks KaiGai. > > Stephen/James, > > Would be ACK-ing these 2 patches ? > > 1) [LTP][PATCH 1/2] Replacement of deprecated interfaces, & > 2) [LTP][PATCH 2/2] Add a new test case for bounds types, > > Regards-- > Subrata > > On Thu, 2008-08-28 at 15:26 +0900, KaiGai Kohei wrote: >> Stephen Smalley wrote: >>> On Wed, 2008-08-27 at 17:04 +0900, KaiGai Kohei wrote: >>>> James Morris wrote: >>>>> Could you also please add tests for this (at least one which should fail >>>>> and one which should succeed) to the Linux Test Project? >>>>> >>>>> >>>>> - James >>>> Policies stored in ltp/testcases/kernel/security/selinux-testsuite/refpolicy/ >>>> invokes massive deprecated interfaces on selinux-policy-3.5.4. >>>> >>>> This patch fixes them according to the warning messages which encourage to >>>> replace older ones. >>>> >>>> BTW, I'm not happy with the test_policy.pp does not allow to invoke test >>>> scripts from unconfined_t domain. Is it to be fixed? >>> I don't quite follow. Did you follow the instructions in the >>> selinux-testsuite README? >> I didn't read the README file carefully, Oops. >> >> The update_refpolicy.sh fixes some of deprecated interfaces and >> inject an interface to kick test script from unconfined domain. >> So, I can run the testsuite which includs bounds test without >> any problems on Rawhide. >> >> # However, I got some warnings for deprecated interfaces/macros >> # like r_dir_perms, userdom_sysadm_bin_spec_domtrans_to or >> # userdom_use_sysadm_ptys. >> >> The attached patch is a new test case of the boundary feature, >> which contains six tests, as follows: >> >> test01: It tries to invoke setcon() with bounded domain in a multi-threaded >> process. The expected result is success. >> test02: It tries to invoke setcon() with unrelated domain in a multi-threaded >> process. The expected result is fail. >> test03: It makes a bounded domain try to read a file, when its bounds domain >> can read the file. The expected result is success. >> test04: It makes a bounded domain try to write a file, when its bounds domain >> cannot write the file. The expected result is fail, because write >> permission is boundary violated. >> test05: It tries to write a bounded type, even if the domain cannot write to >> its bounds type. The expected result is fail. >> test06: It makes a bounded domain try to set an attribute of bounded type. >> >> Thanks, >> >>> I can run the test scripts either using the >>> test_selinux.sh script or by manually loading the policy and then >>> individually running them as described in the README. Watch out that >>> your patch doesn't disturb the existing misc/sbin_deprecated.patch that >>> gets applied by test_selinux.sh. Keep in mind that this testsuite gets >>> run on everything from RHEL4 to F9. > > > -- > This message was distributed to subscribers of the selinux mailing list. > If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with > the words "unsubscribe selinux" without quotes as the message. > -- KaiGai Kohei <kaigai@kaigai.gr.jp> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [LTP][PATCH 1/2] Replacement of deprecated interfaces 2008-08-28 12:52 ` KaiGai Kohei @ 2008-08-28 13:34 ` Subrata Modak 0 siblings, 0 replies; 97+ messages in thread From: Subrata Modak @ 2008-08-28 13:34 UTC (permalink / raw) To: KaiGai Kohei Cc: KaiGai Kohei, Stephen Smalley, James Morris, paul.moore, jbrindle, selinux, ltp-list On Thu, 2008-08-28 at 21:52 +0900, KaiGai Kohei wrote: > Subrata, > > Two patches I sent yesterday should be gone away. > > The latest patch I sent today is revised, however, it is too early > to apply LTP tree, because the new test checks a new kernel feature > which is not included yet. Oops. Please send those to the ltp-list whenever the respective features are in, and you feel it is the right time to go in ;-) Regards-- Subrata > > Thanks, > > Subrata Modak wrote: > > Thanks KaiGai. > > > > Stephen/James, > > > > Would be ACK-ing these 2 patches ? > > > > 1) [LTP][PATCH 1/2] Replacement of deprecated interfaces, & > > 2) [LTP][PATCH 2/2] Add a new test case for bounds types, > > > > Regards-- > > Subrata > > > > On Thu, 2008-08-28 at 15:26 +0900, KaiGai Kohei wrote: > >> Stephen Smalley wrote: > >>> On Wed, 2008-08-27 at 17:04 +0900, KaiGai Kohei wrote: > >>>> James Morris wrote: > >>>>> Could you also please add tests for this (at least one which should fail > >>>>> and one which should succeed) to the Linux Test Project? > >>>>> > >>>>> > >>>>> - James > >>>> Policies stored in ltp/testcases/kernel/security/selinux-testsuite/refpolicy/ > >>>> invokes massive deprecated interfaces on selinux-policy-3.5.4. > >>>> > >>>> This patch fixes them according to the warning messages which encourage to > >>>> replace older ones. > >>>> > >>>> BTW, I'm not happy with the test_policy.pp does not allow to invoke test > >>>> scripts from unconfined_t domain. Is it to be fixed? > >>> I don't quite follow. Did you follow the instructions in the > >>> selinux-testsuite README? > >> I didn't read the README file carefully, Oops. > >> > >> The update_refpolicy.sh fixes some of deprecated interfaces and > >> inject an interface to kick test script from unconfined domain. > >> So, I can run the testsuite which includs bounds test without > >> any problems on Rawhide. > >> > >> # However, I got some warnings for deprecated interfaces/macros > >> # like r_dir_perms, userdom_sysadm_bin_spec_domtrans_to or > >> # userdom_use_sysadm_ptys. > >> > >> The attached patch is a new test case of the boundary feature, > >> which contains six tests, as follows: > >> > >> test01: It tries to invoke setcon() with bounded domain in a multi-threaded > >> process. The expected result is success. > >> test02: It tries to invoke setcon() with unrelated domain in a multi-threaded > >> process. The expected result is fail. > >> test03: It makes a bounded domain try to read a file, when its bounds domain > >> can read the file. The expected result is success. > >> test04: It makes a bounded domain try to write a file, when its bounds domain > >> cannot write the file. The expected result is fail, because write > >> permission is boundary violated. > >> test05: It tries to write a bounded type, even if the domain cannot write to > >> its bounds type. The expected result is fail. > >> test06: It makes a bounded domain try to set an attribute of bounded type. > >> > >> Thanks, > >> > >>> I can run the test scripts either using the > >>> test_selinux.sh script or by manually loading the policy and then > >>> individually running them as described in the README. Watch out that > >>> your patch doesn't disturb the existing misc/sbin_deprecated.patch that > >>> gets applied by test_selinux.sh. Keep in mind that this testsuite gets > >>> run on everything from RHEL4 to F9. > > > > > > -- > > This message was distributed to subscribers of the selinux mailing list. > > If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with > > the words "unsubscribe selinux" without quotes as the message. > > > > -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [LTP][PATCH 1/2] Replacement of deprecated interfaces 2008-08-28 6:26 ` KaiGai Kohei 2008-08-28 12:10 ` Subrata Modak @ 2008-10-23 9:48 ` Subrata Modak 1 sibling, 0 replies; 97+ messages in thread From: Subrata Modak @ 2008-10-23 9:48 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, James Morris, paul.moore, jbrindle, selinux On Thu, 2008-08-28 at 15:26 +0900, KaiGai Kohei wrote: > Stephen Smalley wrote: > > On Wed, 2008-08-27 at 17:04 +0900, KaiGai Kohei wrote: > >> James Morris wrote: > >>> Could you also please add tests for this (at least one which should fail > >>> and one which should succeed) to the Linux Test Project? > >>> > >>> > >>> - James > >> Policies stored in ltp/testcases/kernel/security/selinux-testsuite/refpolicy/ > >> invokes massive deprecated interfaces on selinux-policy-3.5.4. > >> > >> This patch fixes them according to the warning messages which encourage to > >> replace older ones. > >> > >> BTW, I'm not happy with the test_policy.pp does not allow to invoke test > >> scripts from unconfined_t domain. Is it to be fixed? > > > > I don't quite follow. Did you follow the instructions in the > > selinux-testsuite README? > > I didn't read the README file carefully, Oops. > > The update_refpolicy.sh fixes some of deprecated interfaces and > inject an interface to kick test script from unconfined domain. > So, I can run the testsuite which includs bounds test without > any problems on Rawhide. > > # However, I got some warnings for deprecated interfaces/macros > # like r_dir_perms, userdom_sysadm_bin_spec_domtrans_to or > # userdom_use_sysadm_ptys. > > The attached patch is a new test case of the boundary feature, > which contains six tests, as follows: > > test01: It tries to invoke setcon() with bounded domain in a multi-threaded > process. The expected result is success. > test02: It tries to invoke setcon() with unrelated domain in a multi-threaded > process. The expected result is fail. > test03: It makes a bounded domain try to read a file, when its bounds domain > can read the file. The expected result is success. > test04: It makes a bounded domain try to write a file, when its bounds domain > cannot write the file. The expected result is fail, because write > permission is boundary violated. > test05: It tries to write a bounded type, even if the domain cannot write to > its bounds type. The expected result is fail. > test06: It makes a bounded domain try to set an attribute of bounded type. > > Thanks, > Thanks too. Added. Regards-- Subrata > > I can run the test scripts either using the > > test_selinux.sh script or by manually loading the policy and then > > individually running them as described in the README. Watch out that > > your patch doesn't disturb the existing misc/sbin_deprecated.patch that > > gets applied by test_selinux.sh. Keep in mind that this testsuite gets > > run on everything from RHEL4 to F9. > -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [LTP][PATCH 2/2] Add a new test case for bounds types 2008-08-26 10:47 ` James Morris 2008-08-27 1:15 ` KaiGai Kohei 2008-08-27 8:04 ` [LTP][PATCH 1/2] Replacement of deprecated interfaces KaiGai Kohei @ 2008-08-27 8:05 ` KaiGai Kohei 2008-10-22 13:00 ` Subrata Modak 2 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-08-27 8:05 UTC (permalink / raw) To: James Morris; +Cc: Stephen Smalley, paul.moore, jbrindle, selinux [-- Attachment #1: Type: text/plain, Size: 1804 bytes --] James Morris wrote: > Could you also please add tests for this (at least one which should fail > and one which should succeed) to the Linux Test Project? > > > - James The attached patch adds a new test case to check correctness of boundary feature. It contains four sub tests, as follows: test01: It tries to invoke setcon() with bounded domain in a multi-threaded process. The expected result is success. test02: It tries to invoke setcon() with unrelated domain in a multi-threaded process. The expected result is fail. test03: It makes a bounded domain try to read a file, when its bounds domain can read the file. The expected result is success. test04: It makes a bounded domain try to write a file, when its bounds domain cannot write the file. The expected result is fail, even if the bounded domain is allowed to write the file. ---- The result of execution [root@saba tests]# ./runtest.sh bounds /home/kaigai/develop/ltp/testcases/kernel/security/selinux-testsuite/tests Running with security context=unconfined_u:unconfined_r:unconfined_t:SystemLow-SystemHigh 1+0 records in 1+0 records out 1024 bytes (1.0 kB) copied, 8.6321e-05 s, 11.9 MB/s All systems go test01 PASS : thread dyntrans passed. setcon() on multithread process failed: Operation not permitted All systems go test02 PASS : thread dyntrans to unbound domain failed. 2+0 records in 2+0 records out 1024 bytes (1.0 kB) copied, 4.2932e-05 s, 23.9 MB/s test03 PASS : unbounded action to be allowed. dd: opening `/tmp/selinux/test_file': Permission denied test04 PASS : bounded action to be denied. Done. [root@saba tests]# (*) I added a bit ad-hoc policy to invoke the script from the shell. -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> [-- Attachment #2: ltp-selinux-bounds-tests.patch --] [-- Type: text/x-patch, Size: 10683 bytes --] Index: ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile (revision 2) +++ ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile (revision 3) @@ -3,7 +3,7 @@ ifeq (redhat-release-4, $(findstring redhat-release-4, $(REDHAT_RELEASE))) SUBDIRS=domain_trans entrypoint execshare exectrace execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink relabel rename rxdir sem setattr setnice shm sigkill stat sysctl task_create task_setnice task_setscheduler task_getscheduler task_getsid task_getpgid task_setpgid wait file ioctl capable_file capable_net capable_sys else - SUBDIRS=domain_trans entrypoint execshare exectrace execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink relabel rename rxdir sem setattr setnice shm sigkill stat sysctl task_create task_setnice task_setscheduler task_getscheduler task_getsid task_getpgid task_setpgid wait file ioctl capable_file capable_net capable_sys dyntrace dyntrans + SUBDIRS=domain_trans entrypoint execshare exectrace execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink relabel rename rxdir sem setattr setnice shm sigkill stat sysctl task_create task_setnice task_setscheduler task_getscheduler task_getsid task_getpgid task_setpgid wait file ioctl capable_file capable_net capable_sys dyntrace dyntrans bounds endif all: Index: ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c (revision 0) +++ ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c (revision 3) @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2008 NEC Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <selinux/selinux.h> +#include <selinux/context.h> + +static int thread_status = 0; + +static void *worker(void *datap) +{ + security_context_t security_context = datap; + int rc; + + rc = setcon(security_context); + if (rc < 0) { + perror("setcon() on multithread process failed"); + thread_status = 1; + } + + return NULL; +} + +int main(int argc, char *argv[]) +{ + security_context_t security_context; + context_t context; + pthread_t thread; + int rc; + + if (argc != 2) { + fprintf(stderr, "usage: %s <new domain>\n", argv[0]); + return 1; + } + + rc = getcon(&security_context); + if (rc < 0) { + fprintf(stderr, "%s: unable to get my context\n", argv[0]); + return 1; + } + + context = context_new(security_context); + if (!context) { + fprintf(stderr, "%s: unable to create context structure\n", argv[0]); + return 1; + } + + if (context_type_set(context, argv[1])) { + fprintf(stderr, "%s: unable to set new type\n", argv[0]); + return 1; + } + + freecon(security_context); + security_context = context_str(context); + if (!security_context) { + fprintf(stderr, "%s: unable to obtain new context string\n", argv[0]); + return 1; + } + + rc = pthread_create(&thread, NULL, worker, security_context); + if (rc) { + fprintf(stderr, "%s: unable to kick a new thread\n", argv[0]); + return 1; + } + + rc = pthread_join(thread, NULL); + if (rc) { + fprintf(stderr, "%s: unable to join its thread\n", argv[0]); + return 1; + } + + printf("All systems go\n"); + return thread_status; +} Index: ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh (revision 0) +++ ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh (revision 3) @@ -0,0 +1,123 @@ +#!/bin/sh +# +# Copyright (c) 2008 NEC Corporation +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) +# any later version. +# + +setup() +{ + export TCID="setup" + export TST_COUNT=0 + export TST_TOTAL=4 + + # Remove any leftover test directories from prior failed runs. + rm -rf $SELINUXTMPDIR/test_file + + # Create a test files + dd if=/dev/zero of=$SELINUXTMPDIR/test_file count=1 bs=1024 + chcon -t test_bounds_file_t $SELINUXTMPDIR/test_file +} + +test01() +{ + TCID="test01" + TST_COUNT=1 + RC=0 + + runcon -t test_bounds_parent_t \ + -- selinux_bounds_thread test_bounds_child_t 2>&1 + RC=$? + if [ $RC -eq 0 ]; + then + echo "$TCID PASS : thread dyntrans passed." + else + echo "$TCID FAIL : thread dynstrans failed." + fi + return $RC +} + +test02() +{ + TCID="test02" + TST_COUND=2 + RC=0 + + runcon -t test_bounds_parent_t \ + -- selinux_bounds_thread test_bounds_unbound_t 2>&1 + RC=$? + if [ $RC -ne 0 ]; # we expect this to fail + then + echo "$TCID PASS : thread dyntrans to unbound domain failed." + RC=0 + else + echo "$TCID FAIL : thread dyntrans to unbound domain succeeded." + RC=1 + fi + return $RC +} + +test03() +{ + TCID="test03" + TST_COUND=3 + RC=0 + + runcon -t test_bounds_child_t \ + -- dd if=$SELINUXTMPDIR/test_file of=/dev/null + RC=$? + if [ $RC -eq 0 ]; + then + echo "$TCID PASS : unbounded action to be allowed." + else + echo "$TCID FAIL : unbounded action to be allowed." + fi + return $RC +} + +test04() +{ + TCID="test04" + TST_COUNT=4 + RC=0 + + runcon -t test_bounds_child_t \ + -- dd if=/dev/zero of=$SELINUXTMPDIR/test_file count=1 bs=1024 + RC=$? + if [ $RC -ne 0 ]; # we expect this to fail + then + echo "$TCID PASS : bounded action to be denied." + RC=0 + else + echo "$TCID FAIL : bounded action to be denied." + RC=1 + fi + return $RC +} + +cleanup() +{ + # Cleanup + rm -rf $SELINUXTMPDIR/test_file +} + +# Function: main +# +# Description: - Execute all tests, exit with test status. +# +# Exit: - zero on success +# - non-zero on failure. +# +RC=0 # Return value from setup, and test functions. +EXIT_VAL=0 + +setup +test01 || EXIT_VAL=$RC +test02 || EXIT_VAL=$RC +test03 || EXIT_VAL=$RC +test04 || EXIT_VAL=$RC +cleanup +exit $EXIT_VAL Property changes on: ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh ___________________________________________________________________ Added: svn:executable + * Index: ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile (revision 0) +++ ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile (revision 3) @@ -0,0 +1,11 @@ +TARGETS=$(patsubst %.c,%,$(wildcard *.c)) +LDLIBS += -lselinux -lpthread + +all: $(TARGETS) + +install: + @set -e; for i in $(TARGETS); do ln -f $$i ../../../../../bin/$$i; done + ln -f selinux_bounds.sh ../../../../../bin/ + +clean: + rm -f $(TARGETS) \ No newline at end of file Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile (revision 2) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile (revision 3) @@ -1,7 +1,7 @@ POLICYDEVEL = /usr/share/selinux/devel SEMODULE = /usr/sbin/semodule -TARGETS=test_global.te test_capable_file.te test_capable_net.te \ +TARGETS=test_global.te test_bounds.te test_capable_file.te test_capable_net.te \ test_capable_sys.te test_dyntrace.te test_dyntrans.te test_entrypoint.te \ test_execshare.te test_exectrace.te test_execute_no_trans.te \ test_fdreceive.te test_file.te test_inherit.te test_ioctl.te test_ipc.te \ Index: ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te =================================================================== --- ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te (revision 0) +++ ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te (revision 3) @@ -0,0 +1,65 @@ +################################# +# +# Policy for testing boundary features +# + +attribute test_bounds_domain; + +# Domain for process that allows to other domains +type test_bounds_parent_t; +domain_type(test_bounds_parent_t) +typeattribute test_bounds_parent_t test_bounds_domain; +typeattribute test_bounds_parent_t testdomain; + +# Domain for process that has a bounds type +type test_bounds_child_t; +domain_type(test_bounds_child_t) +typeattribute test_bounds_child_t test_bounds_domain; +typeattribute test_bounds_child_t testdomain; + +# Domain for process that does not have any bounds type +type test_bounds_unbound_t; +domain_type(test_bounds_unbound_t) +typeattribute test_bounds_unbound_t test_bounds_domain; +typeattribute test_bounds_unbound_t testdomain; + +# Types for test files +type test_bounds_file_t; +files_type(test_bounds_file_t) + +# Definition of boundary relationship +typebounds test_bounds_parent_t test_bounds_child_t; + +# Allow the test_bounds_parent_t to dyntrans +allow test_bounds_parent_t test_bounds_child_t : process { dyntransition }; +allow test_bounds_parent_t test_bounds_unbound_t : process { dyntransition }; + +# Allow domains to access test_bounds_file_t +allow test_bounds_parent_t test_bounds_file_t : file { read_file_perms }; +allow test_bounds_child_t test_bounds_file_t : file { rw_file_perms }; +allow test_bounds_unbound_t test_bounds_file_t : file { rw_file_perms }; + +# Allow execution of helper programs. +corecmd_exec_bin(test_bounds_domain) +allow test_bounds_domain bin_t : file { entrypoint }; +libs_use_ld_so(test_bounds_domain) +libs_use_shared_libs(test_bounds_domain) +libs_exec_ld_so(test_bounds_domain) +libs_exec_lib_files(test_bounds_domain) + +# Allow all of these domains to be entered from sysadm domain +miscfiles_domain_entry_test_files(test_bounds_domain) +sysadm_entry_spec_domtrans(test_bounds_domain) + +# Allow to invoke script on targeted policy +optional_policy(` + gen_require(` + role unconfined_r; + type unconfined_t; + ') + + role unconfined_r types test_bounds_domain; + allow unconfined_t test_bounds_domain : process { transition }; + + userdom_use_user_terminals(unconfined, test_bounds_domain) +') ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [LTP][PATCH 2/2] Add a new test case for bounds types 2008-08-27 8:05 ` [LTP][PATCH 2/2] Add a new test case for bounds types KaiGai Kohei @ 2008-10-22 13:00 ` Subrata Modak 2008-10-23 8:10 ` KaiGai Kohei 0 siblings, 1 reply; 97+ messages in thread From: Subrata Modak @ 2008-10-22 13:00 UTC (permalink / raw) To: KaiGai Kohei Cc: James Morris, Stephen Smalley, paul.moore, jbrindle, selinux, ltp-list Kaigai, Are these tests ready to be added to LTP ? Regards-- Subrata On Wed, 2008-08-27 at 17:05 +0900, KaiGai Kohei wrote: > James Morris wrote: > > Could you also please add tests for this (at least one which should > fail > > and one which should succeed) to the Linux Test Project? > > > > > > - James > > The attached patch adds a new test case to check correctness of > boundary > feature. It contains four sub tests, as follows: > > test01: It tries to invoke setcon() with bounded domain in a > multi-threaded > process. The expected result is success. > test02: It tries to invoke setcon() with unrelated domain in a > multi-threaded > process. The expected result is fail. > test03: It makes a bounded domain try to read a file, when its bounds > domain > can read the file. The expected result is success. > test04: It makes a bounded domain try to write a file, when its bounds > domain > cannot write the file. The expected result is fail, even if > the bounded > domain is allowed to write the file. > > ---- The result of execution > [root@saba tests]# ./runtest.sh bounds > /home/kaigai/develop/ltp/testcases/kernel/security/selinux-testsuite/tests > Running with security > context=unconfined_u:unconfined_r:unconfined_t:SystemLow-SystemHigh > 1+0 records in > 1+0 records out > 1024 bytes (1.0 kB) copied, 8.6321e-05 s, 11.9 MB/s > All systems go > test01 PASS : thread dyntrans passed. > setcon() on multithread process failed: Operation not permitted > All systems go > test02 PASS : thread dyntrans to unbound domain failed. > 2+0 records in > 2+0 records out > 1024 bytes (1.0 kB) copied, 4.2932e-05 s, 23.9 MB/s > test03 PASS : unbounded action to be allowed. > dd: opening `/tmp/selinux/test_file': Permission denied > test04 PASS : bounded action to be denied. > Done. > [root@saba tests]# > > (*) I added a bit ad-hoc policy to invoke the script from the shell. > > -- > OSS Platform Development Division, NEC > KaiGai Kohei <kaigai@ak.jp.nec.com> > > > > > > > > differences > between files > attachment > (ltp-selinux-bounds-tests.patch) > > Index: ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile > =================================================================== > --- > ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile (revision 2) > +++ > ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile (revision 3) > @@ -3,7 +3,7 @@ > ifeq (redhat-release-4, $(findstring redhat-release-4, > $(REDHAT_RELEASE))) > SUBDIRS=domain_trans entrypoint execshare exectrace > execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink > relabel rename rxdir sem setattr setnice shm sigkill stat sysctl > task_create task_setnice task_setscheduler task_getscheduler > task_getsid task_getpgid task_setpgid wait file ioctl capable_file > capable_net capable_sys > else > - SUBDIRS=domain_trans entrypoint execshare exectrace > execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink > relabel rename rxdir sem setattr setnice shm sigkill stat sysctl > task_create task_setnice task_setscheduler task_getscheduler > task_getsid task_getpgid task_setpgid wait file ioctl capable_file > capable_net capable_sys dyntrace dyntrans > + SUBDIRS=domain_trans entrypoint execshare exectrace > execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink > relabel rename rxdir sem setattr setnice shm sigkill stat sysctl > task_create task_setnice task_setscheduler task_getscheduler > task_getsid task_getpgid task_setpgid wait file ioctl capable_file > capable_net capable_sys dyntrace dyntrans bounds > endif > > all: > Index: > ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c > =================================================================== > --- > ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c (revision 0) > +++ > ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c (revision 3) > @@ -0,0 +1,82 @@ > +/* > + * Copyright (c) 2008 NEC Corporation > + * > + * This program is free software; you can redistribute it and/or > modify it > + * under the terms of the GNU General Public License as published by > the Free > + * Software Foundation; either version 2 of the License, or (at your > option) > + * any later version. > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <pthread.h> > +#include <selinux/selinux.h> > +#include <selinux/context.h> > + > +static int thread_status = 0; > + > +static void *worker(void *datap) > +{ > + security_context_t security_context = datap; > + int rc; > + > + rc = setcon(security_context); > + if (rc < 0) { > + perror("setcon() on multithread process failed"); > + thread_status = 1; > + } > + > + return NULL; > +} > + > +int main(int argc, char *argv[]) > +{ > + security_context_t security_context; > + context_t context; > + pthread_t thread; > + int rc; > + > + if (argc != 2) { > + fprintf(stderr, "usage: %s <new domain>\n", argv[0]); > + return 1; > + } > + > + rc = getcon(&security_context); > + if (rc < 0) { > + fprintf(stderr, "%s: unable to get my context\n", > argv[0]); > + return 1; > + } > + > + context = context_new(security_context); > + if (!context) { > + fprintf(stderr, "%s: unable to create context > structure\n", argv[0]); > + return 1; > + } > + > + if (context_type_set(context, argv[1])) { > + fprintf(stderr, "%s: unable to set new type\n", > argv[0]); > + return 1; > + } > + > + freecon(security_context); > + security_context = context_str(context); > + if (!security_context) { > + fprintf(stderr, "%s: unable to obtain new context > string\n", argv[0]); > + return 1; > + } > + > + rc = pthread_create(&thread, NULL, worker, security_context); > + if (rc) { > + fprintf(stderr, "%s: unable to kick a new thread\n", > argv[0]); > + return 1; > + } > + > + rc = pthread_join(thread, NULL); > + if (rc) { > + fprintf(stderr, "%s: unable to join its thread\n", > argv[0]); > + return 1; > + } > + > + printf("All systems go\n"); > + return thread_status; > +} > Index: > ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh > =================================================================== > --- > ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh (revision 0) > +++ > ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh (revision 3) > @@ -0,0 +1,123 @@ > +#!/bin/sh > +# > +# Copyright (c) 2008 NEC Corporation > +# > +# This program is free software; you can redistribute it and/or > modify it > +# under the terms of the GNU General Public License as published by > the Free > +# Software Foundation; either version 2 of the License, or (at your > option) > +# any later version. > +# > + > +setup() > +{ > + export TCID="setup" > + export TST_COUNT=0 > + export TST_TOTAL=4 > + > + # Remove any leftover test directories from prior failed runs. > + rm -rf $SELINUXTMPDIR/test_file > + > + # Create a test files > + dd if=/dev/zero of=$SELINUXTMPDIR/test_file count=1 bs=1024 > + chcon -t test_bounds_file_t $SELINUXTMPDIR/test_file > +} > + > +test01() > +{ > + TCID="test01" > + TST_COUNT=1 > + RC=0 > + > + runcon -t test_bounds_parent_t \ > + -- selinux_bounds_thread test_bounds_child_t 2>&1 > + RC=$? > + if [ $RC -eq 0 ]; > + then > + echo "$TCID PASS : thread dyntrans passed." > + else > + echo "$TCID FAIL : thread dynstrans failed." > + fi > + return $RC > +} > + > +test02() > +{ > + TCID="test02" > + TST_COUND=2 > + RC=0 > + > + runcon -t test_bounds_parent_t \ > + -- selinux_bounds_thread test_bounds_unbound_t 2>&1 > + RC=$? > + if [ $RC -ne 0 ]; # we expect this to fail > + then > + echo "$TCID PASS : thread dyntrans to unbound domain > failed." > + RC=0 > + else > + echo "$TCID FAIL : thread dyntrans to unbound domain > succeeded." > + RC=1 > + fi > + return $RC > +} > + > +test03() > +{ > + TCID="test03" > + TST_COUND=3 > + RC=0 > + > + runcon -t test_bounds_child_t \ > + -- dd if=$SELINUXTMPDIR/test_file of=/dev/null > + RC=$? > + if [ $RC -eq 0 ]; > + then > + echo "$TCID PASS : unbounded action to be allowed." > + else > + echo "$TCID FAIL : unbounded action to be allowed." > + fi > + return $RC > +} > + > +test04() > +{ > + TCID="test04" > + TST_COUNT=4 > + RC=0 > + > + runcon -t test_bounds_child_t \ > + -- dd if=/dev/zero of=$SELINUXTMPDIR/test_file count=1 > bs=1024 > + RC=$? > + if [ $RC -ne 0 ]; # we expect this to fail > + then > + echo "$TCID PASS : bounded action to be denied." > + RC=0 > + else > + echo "$TCID FAIL : bounded action to be denied." > + RC=1 > + fi > + return $RC > +} > + > +cleanup() > +{ > + # Cleanup > + rm -rf $SELINUXTMPDIR/test_file > +} > + > +# Function: main > +# > +# Description: - Execute all tests, exit with test status. > +# > +# Exit: - zero on success > +# - non-zero on failure. > +# > +RC=0 # Return value from setup, and test functions. > +EXIT_VAL=0 > + > +setup > +test01 || EXIT_VAL=$RC > +test02 || EXIT_VAL=$RC > +test03 || EXIT_VAL=$RC > +test04 || EXIT_VAL=$RC > +cleanup > +exit $EXIT_VAL > > Property changes on: > ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh > ___________________________________________________________________ > Added: svn:executable > + * > > Index: > ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile > =================================================================== > --- > ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile (revision 0) > +++ > ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile (revision 3) > @@ -0,0 +1,11 @@ > +TARGETS=$(patsubst %.c,%,$(wildcard *.c)) > +LDLIBS += -lselinux -lpthread > + > +all: $(TARGETS) > + > +install: > + @set -e; for i in $(TARGETS); do ln -f > $$i ../../../../../bin/$$i; done > + ln -f selinux_bounds.sh ../../../../../bin/ > + > +clean: > + rm -f $(TARGETS) > \ No newline at end of file > Index: > ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile > =================================================================== > --- > ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile (revision 2) > +++ > ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile (revision 3) > @@ -1,7 +1,7 @@ > POLICYDEVEL = /usr/share/selinux/devel > SEMODULE = /usr/sbin/semodule > > -TARGETS=test_global.te test_capable_file.te test_capable_net.te \ > +TARGETS=test_global.te test_bounds.te test_capable_file.te > test_capable_net.te \ > test_capable_sys.te test_dyntrace.te test_dyntrans.te > test_entrypoint.te \ > test_execshare.te test_exectrace.te test_execute_no_trans.te \ > test_fdreceive.te test_file.te test_inherit.te test_ioctl.te > test_ipc.te \ > Index: > ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te > =================================================================== > --- > ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te (revision 0) > +++ > ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te (revision 3) > @@ -0,0 +1,65 @@ > +################################# > +# > +# Policy for testing boundary features > +# > + > +attribute test_bounds_domain; > + > +# Domain for process that allows to other domains > +type test_bounds_parent_t; > +domain_type(test_bounds_parent_t) > +typeattribute test_bounds_parent_t test_bounds_domain; > +typeattribute test_bounds_parent_t testdomain; > + > +# Domain for process that has a bounds type > +type test_bounds_child_t; > +domain_type(test_bounds_child_t) > +typeattribute test_bounds_child_t test_bounds_domain; > +typeattribute test_bounds_child_t testdomain; > + > +# Domain for process that does not have any bounds type > +type test_bounds_unbound_t; > +domain_type(test_bounds_unbound_t) > +typeattribute test_bounds_unbound_t test_bounds_domain; > +typeattribute test_bounds_unbound_t testdomain; > + > +# Types for test files > +type test_bounds_file_t; > +files_type(test_bounds_file_t) > + > +# Definition of boundary relationship > +typebounds test_bounds_parent_t test_bounds_child_t; > + > +# Allow the test_bounds_parent_t to dyntrans > +allow test_bounds_parent_t test_bounds_child_t : process > { dyntransition }; > +allow test_bounds_parent_t test_bounds_unbound_t : process > { dyntransition }; > + > +# Allow domains to access test_bounds_file_t > +allow test_bounds_parent_t test_bounds_file_t : file > { read_file_perms }; > +allow test_bounds_child_t test_bounds_file_t : file > { rw_file_perms }; > +allow test_bounds_unbound_t test_bounds_file_t : file > { rw_file_perms }; > + > +# Allow execution of helper programs. > +corecmd_exec_bin(test_bounds_domain) > +allow test_bounds_domain bin_t : file { entrypoint }; > +libs_use_ld_so(test_bounds_domain) > +libs_use_shared_libs(test_bounds_domain) > +libs_exec_ld_so(test_bounds_domain) > +libs_exec_lib_files(test_bounds_domain) > + > +# Allow all of these domains to be entered from sysadm domain > +miscfiles_domain_entry_test_files(test_bounds_domain) > +sysadm_entry_spec_domtrans(test_bounds_domain) > + > +# Allow to invoke script on targeted policy > +optional_policy(` > + gen_require(` > + role unconfined_r; > + type unconfined_t; > + ') > + > + role unconfined_r types test_bounds_domain; > + allow unconfined_t test_bounds_domain : process > { transition }; > + > + userdom_use_user_terminals(unconfined, test_bounds_domain) > +') > -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [LTP][PATCH 2/2] Add a new test case for bounds types 2008-10-22 13:00 ` Subrata Modak @ 2008-10-23 8:10 ` KaiGai Kohei 2008-10-23 9:30 ` Subrata Modak 0 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-10-23 8:10 UTC (permalink / raw) To: subrata Cc: James Morris, Stephen Smalley, paul.moore, jbrindle, selinux, ltp-list Subrata Modak wrote: > Kaigai, > > Are these tests ready to be added to LTP ? I agree to the patch to be added to LTP, if it is acceptable. :) Please note that the type bounds feature now got merged into Linus's tree, but it is not released officially. (I guess the v2.6.28 will be released about three months later.) I'm not good at the policy in LTP' community, so I will support their decision. Thanks, > Regards-- > Subrata > > On Wed, 2008-08-27 at 17:05 +0900, KaiGai Kohei wrote: >> James Morris wrote: >>> Could you also please add tests for this (at least one which should >> fail >>> and one which should succeed) to the Linux Test Project? >>> >>> >>> - James >> The attached patch adds a new test case to check correctness of >> boundary >> feature. It contains four sub tests, as follows: >> >> test01: It tries to invoke setcon() with bounded domain in a >> multi-threaded >> process. The expected result is success. >> test02: It tries to invoke setcon() with unrelated domain in a >> multi-threaded >> process. The expected result is fail. >> test03: It makes a bounded domain try to read a file, when its bounds >> domain >> can read the file. The expected result is success. >> test04: It makes a bounded domain try to write a file, when its bounds >> domain >> cannot write the file. The expected result is fail, even if >> the bounded >> domain is allowed to write the file. >> >> ---- The result of execution >> [root@saba tests]# ./runtest.sh bounds >> /home/kaigai/develop/ltp/testcases/kernel/security/selinux-testsuite/tests >> Running with security >> context=unconfined_u:unconfined_r:unconfined_t:SystemLow-SystemHigh >> 1+0 records in >> 1+0 records out >> 1024 bytes (1.0 kB) copied, 8.6321e-05 s, 11.9 MB/s >> All systems go >> test01 PASS : thread dyntrans passed. >> setcon() on multithread process failed: Operation not permitted >> All systems go >> test02 PASS : thread dyntrans to unbound domain failed. >> 2+0 records in >> 2+0 records out >> 1024 bytes (1.0 kB) copied, 4.2932e-05 s, 23.9 MB/s >> test03 PASS : unbounded action to be allowed. >> dd: opening `/tmp/selinux/test_file': Permission denied >> test04 PASS : bounded action to be denied. >> Done. >> [root@saba tests]# >> >> (*) I added a bit ad-hoc policy to invoke the script from the shell. >> >> -- >> OSS Platform Development Division, NEC >> KaiGai Kohei <kaigai@ak.jp.nec.com> >> >> >> >> >> >> >> >> differences >> between files >> attachment >> (ltp-selinux-bounds-tests.patch) >> >> Index: ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile >> =================================================================== >> --- >> ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile (revision 2) >> +++ >> ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile (revision 3) >> @@ -3,7 +3,7 @@ >> ifeq (redhat-release-4, $(findstring redhat-release-4, >> $(REDHAT_RELEASE))) >> SUBDIRS=domain_trans entrypoint execshare exectrace >> execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink >> relabel rename rxdir sem setattr setnice shm sigkill stat sysctl >> task_create task_setnice task_setscheduler task_getscheduler >> task_getsid task_getpgid task_setpgid wait file ioctl capable_file >> capable_net capable_sys >> else >> - SUBDIRS=domain_trans entrypoint execshare exectrace >> execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink >> relabel rename rxdir sem setattr setnice shm sigkill stat sysctl >> task_create task_setnice task_setscheduler task_getscheduler >> task_getsid task_getpgid task_setpgid wait file ioctl capable_file >> capable_net capable_sys dyntrace dyntrans >> + SUBDIRS=domain_trans entrypoint execshare exectrace >> execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink >> relabel rename rxdir sem setattr setnice shm sigkill stat sysctl >> task_create task_setnice task_setscheduler task_getscheduler >> task_getsid task_getpgid task_setpgid wait file ioctl capable_file >> capable_net capable_sys dyntrace dyntrans bounds >> endif >> >> all: >> Index: >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c >> =================================================================== >> --- >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c (revision 0) >> +++ >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c (revision 3) >> @@ -0,0 +1,82 @@ >> +/* >> + * Copyright (c) 2008 NEC Corporation >> + * >> + * This program is free software; you can redistribute it and/or >> modify it >> + * under the terms of the GNU General Public License as published by >> the Free >> + * Software Foundation; either version 2 of the License, or (at your >> option) >> + * any later version. >> + */ >> + >> +#include <stdio.h> >> +#include <stdlib.h> >> +#include <pthread.h> >> +#include <selinux/selinux.h> >> +#include <selinux/context.h> >> + >> +static int thread_status = 0; >> + >> +static void *worker(void *datap) >> +{ >> + security_context_t security_context = datap; >> + int rc; >> + >> + rc = setcon(security_context); >> + if (rc < 0) { >> + perror("setcon() on multithread process failed"); >> + thread_status = 1; >> + } >> + >> + return NULL; >> +} >> + >> +int main(int argc, char *argv[]) >> +{ >> + security_context_t security_context; >> + context_t context; >> + pthread_t thread; >> + int rc; >> + >> + if (argc != 2) { >> + fprintf(stderr, "usage: %s <new domain>\n", argv[0]); >> + return 1; >> + } >> + >> + rc = getcon(&security_context); >> + if (rc < 0) { >> + fprintf(stderr, "%s: unable to get my context\n", >> argv[0]); >> + return 1; >> + } >> + >> + context = context_new(security_context); >> + if (!context) { >> + fprintf(stderr, "%s: unable to create context >> structure\n", argv[0]); >> + return 1; >> + } >> + >> + if (context_type_set(context, argv[1])) { >> + fprintf(stderr, "%s: unable to set new type\n", >> argv[0]); >> + return 1; >> + } >> + >> + freecon(security_context); >> + security_context = context_str(context); >> + if (!security_context) { >> + fprintf(stderr, "%s: unable to obtain new context >> string\n", argv[0]); >> + return 1; >> + } >> + >> + rc = pthread_create(&thread, NULL, worker, security_context); >> + if (rc) { >> + fprintf(stderr, "%s: unable to kick a new thread\n", >> argv[0]); >> + return 1; >> + } >> + >> + rc = pthread_join(thread, NULL); >> + if (rc) { >> + fprintf(stderr, "%s: unable to join its thread\n", >> argv[0]); >> + return 1; >> + } >> + >> + printf("All systems go\n"); >> + return thread_status; >> +} >> Index: >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh >> =================================================================== >> --- >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh (revision 0) >> +++ >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh (revision 3) >> @@ -0,0 +1,123 @@ >> +#!/bin/sh >> +# >> +# Copyright (c) 2008 NEC Corporation >> +# >> +# This program is free software; you can redistribute it and/or >> modify it >> +# under the terms of the GNU General Public License as published by >> the Free >> +# Software Foundation; either version 2 of the License, or (at your >> option) >> +# any later version. >> +# >> + >> +setup() >> +{ >> + export TCID="setup" >> + export TST_COUNT=0 >> + export TST_TOTAL=4 >> + >> + # Remove any leftover test directories from prior failed runs. >> + rm -rf $SELINUXTMPDIR/test_file >> + >> + # Create a test files >> + dd if=/dev/zero of=$SELINUXTMPDIR/test_file count=1 bs=1024 >> + chcon -t test_bounds_file_t $SELINUXTMPDIR/test_file >> +} >> + >> +test01() >> +{ >> + TCID="test01" >> + TST_COUNT=1 >> + RC=0 >> + >> + runcon -t test_bounds_parent_t \ >> + -- selinux_bounds_thread test_bounds_child_t 2>&1 >> + RC=$? >> + if [ $RC -eq 0 ]; >> + then >> + echo "$TCID PASS : thread dyntrans passed." >> + else >> + echo "$TCID FAIL : thread dynstrans failed." >> + fi >> + return $RC >> +} >> + >> +test02() >> +{ >> + TCID="test02" >> + TST_COUND=2 >> + RC=0 >> + >> + runcon -t test_bounds_parent_t \ >> + -- selinux_bounds_thread test_bounds_unbound_t 2>&1 >> + RC=$? >> + if [ $RC -ne 0 ]; # we expect this to fail >> + then >> + echo "$TCID PASS : thread dyntrans to unbound domain >> failed." >> + RC=0 >> + else >> + echo "$TCID FAIL : thread dyntrans to unbound domain >> succeeded." >> + RC=1 >> + fi >> + return $RC >> +} >> + >> +test03() >> +{ >> + TCID="test03" >> + TST_COUND=3 >> + RC=0 >> + >> + runcon -t test_bounds_child_t \ >> + -- dd if=$SELINUXTMPDIR/test_file of=/dev/null >> + RC=$? >> + if [ $RC -eq 0 ]; >> + then >> + echo "$TCID PASS : unbounded action to be allowed." >> + else >> + echo "$TCID FAIL : unbounded action to be allowed." >> + fi >> + return $RC >> +} >> + >> +test04() >> +{ >> + TCID="test04" >> + TST_COUNT=4 >> + RC=0 >> + >> + runcon -t test_bounds_child_t \ >> + -- dd if=/dev/zero of=$SELINUXTMPDIR/test_file count=1 >> bs=1024 >> + RC=$? >> + if [ $RC -ne 0 ]; # we expect this to fail >> + then >> + echo "$TCID PASS : bounded action to be denied." >> + RC=0 >> + else >> + echo "$TCID FAIL : bounded action to be denied." >> + RC=1 >> + fi >> + return $RC >> +} >> + >> +cleanup() >> +{ >> + # Cleanup >> + rm -rf $SELINUXTMPDIR/test_file >> +} >> + >> +# Function: main >> +# >> +# Description: - Execute all tests, exit with test status. >> +# >> +# Exit: - zero on success >> +# - non-zero on failure. >> +# >> +RC=0 # Return value from setup, and test functions. >> +EXIT_VAL=0 >> + >> +setup >> +test01 || EXIT_VAL=$RC >> +test02 || EXIT_VAL=$RC >> +test03 || EXIT_VAL=$RC >> +test04 || EXIT_VAL=$RC >> +cleanup >> +exit $EXIT_VAL >> >> Property changes on: >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh >> ___________________________________________________________________ >> Added: svn:executable >> + * >> >> Index: >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile >> =================================================================== >> --- >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile (revision 0) >> +++ >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile (revision 3) >> @@ -0,0 +1,11 @@ >> +TARGETS=$(patsubst %.c,%,$(wildcard *.c)) >> +LDLIBS += -lselinux -lpthread >> + >> +all: $(TARGETS) >> + >> +install: >> + @set -e; for i in $(TARGETS); do ln -f >> $$i ../../../../../bin/$$i; done >> + ln -f selinux_bounds.sh ../../../../../bin/ >> + >> +clean: >> + rm -f $(TARGETS) >> \ No newline at end of file >> Index: >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile >> =================================================================== >> --- >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile (revision 2) >> +++ >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile (revision 3) >> @@ -1,7 +1,7 @@ >> POLICYDEVEL = /usr/share/selinux/devel >> SEMODULE = /usr/sbin/semodule >> >> -TARGETS=test_global.te test_capable_file.te test_capable_net.te \ >> +TARGETS=test_global.te test_bounds.te test_capable_file.te >> test_capable_net.te \ >> test_capable_sys.te test_dyntrace.te test_dyntrans.te >> test_entrypoint.te \ >> test_execshare.te test_exectrace.te test_execute_no_trans.te \ >> test_fdreceive.te test_file.te test_inherit.te test_ioctl.te >> test_ipc.te \ >> Index: >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te >> =================================================================== >> --- >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te (revision 0) >> +++ >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te (revision 3) >> @@ -0,0 +1,65 @@ >> +################################# >> +# >> +# Policy for testing boundary features >> +# >> + >> +attribute test_bounds_domain; >> + >> +# Domain for process that allows to other domains >> +type test_bounds_parent_t; >> +domain_type(test_bounds_parent_t) >> +typeattribute test_bounds_parent_t test_bounds_domain; >> +typeattribute test_bounds_parent_t testdomain; >> + >> +# Domain for process that has a bounds type >> +type test_bounds_child_t; >> +domain_type(test_bounds_child_t) >> +typeattribute test_bounds_child_t test_bounds_domain; >> +typeattribute test_bounds_child_t testdomain; >> + >> +# Domain for process that does not have any bounds type >> +type test_bounds_unbound_t; >> +domain_type(test_bounds_unbound_t) >> +typeattribute test_bounds_unbound_t test_bounds_domain; >> +typeattribute test_bounds_unbound_t testdomain; >> + >> +# Types for test files >> +type test_bounds_file_t; >> +files_type(test_bounds_file_t) >> + >> +# Definition of boundary relationship >> +typebounds test_bounds_parent_t test_bounds_child_t; >> + >> +# Allow the test_bounds_parent_t to dyntrans >> +allow test_bounds_parent_t test_bounds_child_t : process >> { dyntransition }; >> +allow test_bounds_parent_t test_bounds_unbound_t : process >> { dyntransition }; >> + >> +# Allow domains to access test_bounds_file_t >> +allow test_bounds_parent_t test_bounds_file_t : file >> { read_file_perms }; >> +allow test_bounds_child_t test_bounds_file_t : file >> { rw_file_perms }; >> +allow test_bounds_unbound_t test_bounds_file_t : file >> { rw_file_perms }; >> + >> +# Allow execution of helper programs. >> +corecmd_exec_bin(test_bounds_domain) >> +allow test_bounds_domain bin_t : file { entrypoint }; >> +libs_use_ld_so(test_bounds_domain) >> +libs_use_shared_libs(test_bounds_domain) >> +libs_exec_ld_so(test_bounds_domain) >> +libs_exec_lib_files(test_bounds_domain) >> + >> +# Allow all of these domains to be entered from sysadm domain >> +miscfiles_domain_entry_test_files(test_bounds_domain) >> +sysadm_entry_spec_domtrans(test_bounds_domain) >> + >> +# Allow to invoke script on targeted policy >> +optional_policy(` >> + gen_require(` >> + role unconfined_r; >> + type unconfined_t; >> + ') >> + >> + role unconfined_r types test_bounds_domain; >> + allow unconfined_t test_bounds_domain : process >> { transition }; >> + >> + userdom_use_user_terminals(unconfined, test_bounds_domain) >> +') >> > > > -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [LTP][PATCH 2/2] Add a new test case for bounds types 2008-10-23 8:10 ` KaiGai Kohei @ 2008-10-23 9:30 ` Subrata Modak 0 siblings, 0 replies; 97+ messages in thread From: Subrata Modak @ 2008-10-23 9:30 UTC (permalink / raw) To: KaiGai Kohei Cc: James Morris, Stephen Smalley, paul.moore, jbrindle, selinux, ltp-list Thanks Kaigai. On Thu, 2008-10-23 at 17:10 +0900, KaiGai Kohei wrote: > Subrata Modak wrote: > > Kaigai, > > > > Are these tests ready to be added to LTP ? > > I agree to the patch to be added to LTP, if it is acceptable. :) > > Please note that the type bounds feature now got merged into Linus's tree, > but it is not released officially. > (I guess the v2.6.28 will be released about three months later.) > > I'm not good at the policy in LTP' community, so I will support their decision. Well, we started to induct test cases into LTP for which they are still to make it to the Mainline Kernel - something we call as LTP-mm. Since, it is inside Linus´s tree, then it will become part of Mainline soon. So, we can go ahead and add the tests. People can start using it immediately on release of the corresponding feature. Thanks once again. Regards-- Subrata > > Thanks, > > > Regards-- > > Subrata > > > > On Wed, 2008-08-27 at 17:05 +0900, KaiGai Kohei wrote: > >> James Morris wrote: > >>> Could you also please add tests for this (at least one which should > >> fail > >>> and one which should succeed) to the Linux Test Project? > >>> > >>> > >>> - James > >> The attached patch adds a new test case to check correctness of > >> boundary > >> feature. It contains four sub tests, as follows: > >> > >> test01: It tries to invoke setcon() with bounded domain in a > >> multi-threaded > >> process. The expected result is success. > >> test02: It tries to invoke setcon() with unrelated domain in a > >> multi-threaded > >> process. The expected result is fail. > >> test03: It makes a bounded domain try to read a file, when its bounds > >> domain > >> can read the file. The expected result is success. > >> test04: It makes a bounded domain try to write a file, when its bounds > >> domain > >> cannot write the file. The expected result is fail, even if > >> the bounded > >> domain is allowed to write the file. > >> > >> ---- The result of execution > >> [root@saba tests]# ./runtest.sh bounds > >> /home/kaigai/develop/ltp/testcases/kernel/security/selinux-testsuite/tests > >> Running with security > >> context=unconfined_u:unconfined_r:unconfined_t:SystemLow-SystemHigh > >> 1+0 records in > >> 1+0 records out > >> 1024 bytes (1.0 kB) copied, 8.6321e-05 s, 11.9 MB/s > >> All systems go > >> test01 PASS : thread dyntrans passed. > >> setcon() on multithread process failed: Operation not permitted > >> All systems go > >> test02 PASS : thread dyntrans to unbound domain failed. > >> 2+0 records in > >> 2+0 records out > >> 1024 bytes (1.0 kB) copied, 4.2932e-05 s, 23.9 MB/s > >> test03 PASS : unbounded action to be allowed. > >> dd: opening `/tmp/selinux/test_file': Permission denied > >> test04 PASS : bounded action to be denied. > >> Done. > >> [root@saba tests]# > >> > >> (*) I added a bit ad-hoc policy to invoke the script from the shell. > >> > >> -- > >> OSS Platform Development Division, NEC > >> KaiGai Kohei <kaigai@ak.jp.nec.com> > >> > >> > >> > >> > >> > >> > >> > >> differences > >> between files > >> attachment > >> (ltp-selinux-bounds-tests.patch) > >> > >> Index: ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile > >> =================================================================== > >> --- > >> ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile (revision 2) > >> +++ > >> ltp/testcases/kernel/security/selinux-testsuite/tests/Makefile (revision 3) > >> @@ -3,7 +3,7 @@ > >> ifeq (redhat-release-4, $(findstring redhat-release-4, > >> $(REDHAT_RELEASE))) > >> SUBDIRS=domain_trans entrypoint execshare exectrace > >> execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink > >> relabel rename rxdir sem setattr setnice shm sigkill stat sysctl > >> task_create task_setnice task_setscheduler task_getscheduler > >> task_getsid task_getpgid task_setpgid wait file ioctl capable_file > >> capable_net capable_sys > >> else > >> - SUBDIRS=domain_trans entrypoint execshare exectrace > >> execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink > >> relabel rename rxdir sem setattr setnice shm sigkill stat sysctl > >> task_create task_setnice task_setscheduler task_getscheduler > >> task_getsid task_getpgid task_setpgid wait file ioctl capable_file > >> capable_net capable_sys dyntrace dyntrans > >> + SUBDIRS=domain_trans entrypoint execshare exectrace > >> execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink > >> relabel rename rxdir sem setattr setnice shm sigkill stat sysctl > >> task_create task_setnice task_setscheduler task_getscheduler > >> task_getsid task_getpgid task_setpgid wait file ioctl capable_file > >> capable_net capable_sys dyntrace dyntrans bounds > >> endif > >> > >> all: > >> Index: > >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c > >> =================================================================== > >> --- > >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c (revision 0) > >> +++ > >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds_thread.c (revision 3) > >> @@ -0,0 +1,82 @@ > >> +/* > >> + * Copyright (c) 2008 NEC Corporation > >> + * > >> + * This program is free software; you can redistribute it and/or > >> modify it > >> + * under the terms of the GNU General Public License as published by > >> the Free > >> + * Software Foundation; either version 2 of the License, or (at your > >> option) > >> + * any later version. > >> + */ > >> + > >> +#include <stdio.h> > >> +#include <stdlib.h> > >> +#include <pthread.h> > >> +#include <selinux/selinux.h> > >> +#include <selinux/context.h> > >> + > >> +static int thread_status = 0; > >> + > >> +static void *worker(void *datap) > >> +{ > >> + security_context_t security_context = datap; > >> + int rc; > >> + > >> + rc = setcon(security_context); > >> + if (rc < 0) { > >> + perror("setcon() on multithread process failed"); > >> + thread_status = 1; > >> + } > >> + > >> + return NULL; > >> +} > >> + > >> +int main(int argc, char *argv[]) > >> +{ > >> + security_context_t security_context; > >> + context_t context; > >> + pthread_t thread; > >> + int rc; > >> + > >> + if (argc != 2) { > >> + fprintf(stderr, "usage: %s <new domain>\n", argv[0]); > >> + return 1; > >> + } > >> + > >> + rc = getcon(&security_context); > >> + if (rc < 0) { > >> + fprintf(stderr, "%s: unable to get my context\n", > >> argv[0]); > >> + return 1; > >> + } > >> + > >> + context = context_new(security_context); > >> + if (!context) { > >> + fprintf(stderr, "%s: unable to create context > >> structure\n", argv[0]); > >> + return 1; > >> + } > >> + > >> + if (context_type_set(context, argv[1])) { > >> + fprintf(stderr, "%s: unable to set new type\n", > >> argv[0]); > >> + return 1; > >> + } > >> + > >> + freecon(security_context); > >> + security_context = context_str(context); > >> + if (!security_context) { > >> + fprintf(stderr, "%s: unable to obtain new context > >> string\n", argv[0]); > >> + return 1; > >> + } > >> + > >> + rc = pthread_create(&thread, NULL, worker, security_context); > >> + if (rc) { > >> + fprintf(stderr, "%s: unable to kick a new thread\n", > >> argv[0]); > >> + return 1; > >> + } > >> + > >> + rc = pthread_join(thread, NULL); > >> + if (rc) { > >> + fprintf(stderr, "%s: unable to join its thread\n", > >> argv[0]); > >> + return 1; > >> + } > >> + > >> + printf("All systems go\n"); > >> + return thread_status; > >> +} > >> Index: > >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh > >> =================================================================== > >> --- > >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh (revision 0) > >> +++ > >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh (revision 3) > >> @@ -0,0 +1,123 @@ > >> +#!/bin/sh > >> +# > >> +# Copyright (c) 2008 NEC Corporation > >> +# > >> +# This program is free software; you can redistribute it and/or > >> modify it > >> +# under the terms of the GNU General Public License as published by > >> the Free > >> +# Software Foundation; either version 2 of the License, or (at your > >> option) > >> +# any later version. > >> +# > >> + > >> +setup() > >> +{ > >> + export TCID="setup" > >> + export TST_COUNT=0 > >> + export TST_TOTAL=4 > >> + > >> + # Remove any leftover test directories from prior failed runs. > >> + rm -rf $SELINUXTMPDIR/test_file > >> + > >> + # Create a test files > >> + dd if=/dev/zero of=$SELINUXTMPDIR/test_file count=1 bs=1024 > >> + chcon -t test_bounds_file_t $SELINUXTMPDIR/test_file > >> +} > >> + > >> +test01() > >> +{ > >> + TCID="test01" > >> + TST_COUNT=1 > >> + RC=0 > >> + > >> + runcon -t test_bounds_parent_t \ > >> + -- selinux_bounds_thread test_bounds_child_t 2>&1 > >> + RC=$? > >> + if [ $RC -eq 0 ]; > >> + then > >> + echo "$TCID PASS : thread dyntrans passed." > >> + else > >> + echo "$TCID FAIL : thread dynstrans failed." > >> + fi > >> + return $RC > >> +} > >> + > >> +test02() > >> +{ > >> + TCID="test02" > >> + TST_COUND=2 > >> + RC=0 > >> + > >> + runcon -t test_bounds_parent_t \ > >> + -- selinux_bounds_thread test_bounds_unbound_t 2>&1 > >> + RC=$? > >> + if [ $RC -ne 0 ]; # we expect this to fail > >> + then > >> + echo "$TCID PASS : thread dyntrans to unbound domain > >> failed." > >> + RC=0 > >> + else > >> + echo "$TCID FAIL : thread dyntrans to unbound domain > >> succeeded." > >> + RC=1 > >> + fi > >> + return $RC > >> +} > >> + > >> +test03() > >> +{ > >> + TCID="test03" > >> + TST_COUND=3 > >> + RC=0 > >> + > >> + runcon -t test_bounds_child_t \ > >> + -- dd if=$SELINUXTMPDIR/test_file of=/dev/null > >> + RC=$? > >> + if [ $RC -eq 0 ]; > >> + then > >> + echo "$TCID PASS : unbounded action to be allowed." > >> + else > >> + echo "$TCID FAIL : unbounded action to be allowed." > >> + fi > >> + return $RC > >> +} > >> + > >> +test04() > >> +{ > >> + TCID="test04" > >> + TST_COUNT=4 > >> + RC=0 > >> + > >> + runcon -t test_bounds_child_t \ > >> + -- dd if=/dev/zero of=$SELINUXTMPDIR/test_file count=1 > >> bs=1024 > >> + RC=$? > >> + if [ $RC -ne 0 ]; # we expect this to fail > >> + then > >> + echo "$TCID PASS : bounded action to be denied." > >> + RC=0 > >> + else > >> + echo "$TCID FAIL : bounded action to be denied." > >> + RC=1 > >> + fi > >> + return $RC > >> +} > >> + > >> +cleanup() > >> +{ > >> + # Cleanup > >> + rm -rf $SELINUXTMPDIR/test_file > >> +} > >> + > >> +# Function: main > >> +# > >> +# Description: - Execute all tests, exit with test status. > >> +# > >> +# Exit: - zero on success > >> +# - non-zero on failure. > >> +# > >> +RC=0 # Return value from setup, and test functions. > >> +EXIT_VAL=0 > >> + > >> +setup > >> +test01 || EXIT_VAL=$RC > >> +test02 || EXIT_VAL=$RC > >> +test03 || EXIT_VAL=$RC > >> +test04 || EXIT_VAL=$RC > >> +cleanup > >> +exit $EXIT_VAL > >> > >> Property changes on: > >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/selinux_bounds.sh > >> ___________________________________________________________________ > >> Added: svn:executable > >> + * > >> > >> Index: > >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile > >> =================================================================== > >> --- > >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile (revision 0) > >> +++ > >> ltp/testcases/kernel/security/selinux-testsuite/tests/bounds/Makefile (revision 3) > >> @@ -0,0 +1,11 @@ > >> +TARGETS=$(patsubst %.c,%,$(wildcard *.c)) > >> +LDLIBS += -lselinux -lpthread > >> + > >> +all: $(TARGETS) > >> + > >> +install: > >> + @set -e; for i in $(TARGETS); do ln -f > >> $$i ../../../../../bin/$$i; done > >> + ln -f selinux_bounds.sh ../../../../../bin/ > >> + > >> +clean: > >> + rm -f $(TARGETS) > >> \ No newline at end of file > >> Index: > >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile > >> =================================================================== > >> --- > >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile (revision 2) > >> +++ > >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/Makefile (revision 3) > >> @@ -1,7 +1,7 @@ > >> POLICYDEVEL = /usr/share/selinux/devel > >> SEMODULE = /usr/sbin/semodule > >> > >> -TARGETS=test_global.te test_capable_file.te test_capable_net.te \ > >> +TARGETS=test_global.te test_bounds.te test_capable_file.te > >> test_capable_net.te \ > >> test_capable_sys.te test_dyntrace.te test_dyntrans.te > >> test_entrypoint.te \ > >> test_execshare.te test_exectrace.te test_execute_no_trans.te \ > >> test_fdreceive.te test_file.te test_inherit.te test_ioctl.te > >> test_ipc.te \ > >> Index: > >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te > >> =================================================================== > >> --- > >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te (revision 0) > >> +++ > >> ltp/testcases/kernel/security/selinux-testsuite/refpolicy/test_bounds.te (revision 3) > >> @@ -0,0 +1,65 @@ > >> +################################# > >> +# > >> +# Policy for testing boundary features > >> +# > >> + > >> +attribute test_bounds_domain; > >> + > >> +# Domain for process that allows to other domains > >> +type test_bounds_parent_t; > >> +domain_type(test_bounds_parent_t) > >> +typeattribute test_bounds_parent_t test_bounds_domain; > >> +typeattribute test_bounds_parent_t testdomain; > >> + > >> +# Domain for process that has a bounds type > >> +type test_bounds_child_t; > >> +domain_type(test_bounds_child_t) > >> +typeattribute test_bounds_child_t test_bounds_domain; > >> +typeattribute test_bounds_child_t testdomain; > >> + > >> +# Domain for process that does not have any bounds type > >> +type test_bounds_unbound_t; > >> +domain_type(test_bounds_unbound_t) > >> +typeattribute test_bounds_unbound_t test_bounds_domain; > >> +typeattribute test_bounds_unbound_t testdomain; > >> + > >> +# Types for test files > >> +type test_bounds_file_t; > >> +files_type(test_bounds_file_t) > >> + > >> +# Definition of boundary relationship > >> +typebounds test_bounds_parent_t test_bounds_child_t; > >> + > >> +# Allow the test_bounds_parent_t to dyntrans > >> +allow test_bounds_parent_t test_bounds_child_t : process > >> { dyntransition }; > >> +allow test_bounds_parent_t test_bounds_unbound_t : process > >> { dyntransition }; > >> + > >> +# Allow domains to access test_bounds_file_t > >> +allow test_bounds_parent_t test_bounds_file_t : file > >> { read_file_perms }; > >> +allow test_bounds_child_t test_bounds_file_t : file > >> { rw_file_perms }; > >> +allow test_bounds_unbound_t test_bounds_file_t : file > >> { rw_file_perms }; > >> + > >> +# Allow execution of helper programs. > >> +corecmd_exec_bin(test_bounds_domain) > >> +allow test_bounds_domain bin_t : file { entrypoint }; > >> +libs_use_ld_so(test_bounds_domain) > >> +libs_use_shared_libs(test_bounds_domain) > >> +libs_exec_ld_so(test_bounds_domain) > >> +libs_exec_lib_files(test_bounds_domain) > >> + > >> +# Allow all of these domains to be entered from sysadm domain > >> +miscfiles_domain_entry_test_files(test_bounds_domain) > >> +sysadm_entry_spec_domtrans(test_bounds_domain) > >> + > >> +# Allow to invoke script on targeted policy > >> +optional_policy(` > >> + gen_require(` > >> + role unconfined_r; > >> + type unconfined_t; > >> + ') > >> + > >> + role unconfined_r types test_bounds_domain; > >> + allow unconfined_t test_bounds_domain : process > >> { transition }; > >> + > >> + userdom_use_user_terminals(unconfined, test_bounds_domain) > >> +') > >> > > > > > > > > -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) 2008-08-26 10:29 ` James Morris 2008-08-26 10:47 ` James Morris @ 2008-08-27 1:11 ` KaiGai Kohei 2008-08-28 7:35 ` [PATCH] SELinux: add boundary support and thread context assignment KaiGai Kohei 2 siblings, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-27 1:11 UTC (permalink / raw) To: James Morris; +Cc: Stephen Smalley, paul.moore, jbrindle, selinux James Morris wrote: > On Mon, 25 Aug 2008, KaiGai Kohei wrote: > >> @@ -5228,11 +5232,14 @@ static int selinux_setprocattr(struct task_struct *p, >> do_each_thread(g, t) { >> if (t->mm == mm && t != p) { >> read_unlock(&tasklist_lock); >> + if (!security_bounded_transition(tsec->sid, sid)) >> + goto boundary_ok; >> return -EPERM; > > Propagate the return value of security_bounded_transition(). OK, I'll fix it on the next. > Also, if the user/role bounds are not being used, should they be included > in this? From the kernel point of view, unused code should never be > added. Existing named based hierarchy on users/roles are implemented using bounds feature. If a user "staff_u.foo" is defined, the toolchain implicitly defines bounds relationship with "staff_u", for example. I don't provide an explicit way to define bounds (like TYPEBOUNDS) between users/roles, but we can define it with existing grammar. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH] SELinux: add boundary support and thread context assignment 2008-08-26 10:29 ` James Morris 2008-08-26 10:47 ` James Morris 2008-08-27 1:11 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei @ 2008-08-28 7:35 ` KaiGai Kohei 2008-08-28 12:43 ` Stephen Smalley 2008-08-28 15:06 ` James Morris 2 siblings, 2 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-28 7:35 UTC (permalink / raw) To: James Morris, Stephen Smalley; +Cc: paul.moore, jbrindle, selinux The purpose of this patch is to assign per-thread security context under a constraint. It enables multi-threaded server application to kick a request handler with its fair security context, and helps some of userspace object managers to handle user's request. When we assign a per-thread security context, it must not have wider permissions than the original one. Because a multi-threaded process shares a single local memory, an arbitary per-thread security context also means another thread can easily refer violated information. The constraint on a per-thread security context requires a new domain has to be equal or weaker than its original one, when it tries to assign a per-thread security context. Bounds relationship between two types is a way to ensure a domain can never have wider permission than its bounds. We can define it in two explicit or implicit ways. The first way is using new TYPEBOUNDS statement. It enables to define a boundary of types explicitly. The other one expand the concept of existing named based hierarchy. If we defines a type with "." separated name like "httpd_t.php", toolchain implicitly set its bounds on "httpd_t". This feature requires a new policy version. The 24th version (POLICYDB_VERSION_BOUNDARY) enables to ship them into kernel space, and the following patch enables to handle it. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> --- Updates: * rev.7 - The return code of security_bounded_transition() is returned to userspace, if it makes an error. * rev.6 - It reverrs the place of bounds checks for dyntrans on multithreaded. - Kernel policy version.24 utilize the third word of type entries as a property fields. - Check on type bounds are integrated with creation of avc entries. violated permissions are masked and reported via audit. * rev.5 - A missing number * rev.4 - It removes checks on the set relation of attributes at policy load time. - Several cosmetic changes * rev.3 - Kenel policy version.24 got support to load attributes into kernel space. - Boundary checks on constraints are moved to policy load time. * rev.2 - A new policy statement: TYPEBOUNDS - Existing hierarchy checks are moved to kernel space from toolchain * rev.1 - The initial version --- security/selinux/avc.c | 2 +- security/selinux/hooks.c | 15 ++- security/selinux/include/avc.h | 4 + security/selinux/include/security.h | 15 +++- security/selinux/ss/policydb.c | 205 ++++++++++++++++++++++++++++++++--- security/selinux/ss/policydb.h | 5 + security/selinux/ss/services.c | 172 +++++++++++++++++++++++++++++- 7 files changed, 398 insertions(+), 20 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 114b4b4..cb30c7e 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -136,7 +136,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) * @tclass: target security class * @av: access vector */ -static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) { const char **common_pts = NULL; u32 common_base = 0; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 03fc6a8..38dfca7 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5219,8 +5219,12 @@ static int selinux_setprocattr(struct task_struct *p, if (sid == 0) return -EINVAL; - - /* Only allow single threaded processes to change context */ + /* + * SELinux allows to change context in the following case only. + * - Single threaded processes. + * - Multi threaded processes intend to change its context into + * more restricted domain (defined by TYPEBOUNDS statement). + */ if (atomic_read(&p->mm->mm_users) != 1) { struct task_struct *g, *t; struct mm_struct *mm = p->mm; @@ -5228,11 +5232,16 @@ static int selinux_setprocattr(struct task_struct *p, do_each_thread(g, t) { if (t->mm == mm && t != p) { read_unlock(&tasklist_lock); - return -EPERM; + error = security_bounded_transition(tsec->sid, sid); + if (!error) + goto boundary_ok; + + return error; } } while_each_thread(g, t); read_unlock(&tasklist_lock); } +boundary_ok: /* Check permissions for the transition. */ error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 7b9769f..d12ff1a 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -12,6 +12,7 @@ #include <linux/kdev_t.h> #include <linux/spinlock.h> #include <linux/init.h> +#include <linux/audit.h> #include <linux/in6.h> #include <linux/path.h> #include <asm/system.h> @@ -126,6 +127,9 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, u32 events, u32 ssid, u32 tsid, u16 tclass, u32 perms); +/* Shows permission in human readable form */ +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av); + /* Exported to selinuxfs */ int avc_get_hash_stats(char *page); extern unsigned int avc_cache_threshold; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 7c54300..7244737 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -27,13 +27,14 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY #endif #define CONTEXT_MNT 0x01 @@ -62,6 +63,16 @@ enum { extern int selinux_policycap_netpeer; extern int selinux_policycap_openperm; +/* + * type_datum properties + * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY + */ +#define TYPEDATUM_PROPERTY_PRIMARY 0x0001 +#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 + +/* limitation of boundary depth */ +#define POLICYDB_BOUNDS_MAXDEPTH 4 + int security_load_policy(void *data, size_t len); int security_policycap_supported(unsigned int req_cap); @@ -117,6 +128,8 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen, int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass); +int security_bounded_transition(u32 oldsid, u32 newsid); + int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 2391761..7dbe756 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -30,6 +30,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/audit.h> #include "security.h" #include "policydb.h" @@ -116,7 +117,12 @@ static struct policydb_compat_info policydb_compat[] = { .version = POLICYDB_VERSION_PERMISSIVE, .sym_num = SYM_NUM, .ocon_num = OCON_NUM, - } + }, + { + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -254,7 +260,9 @@ static int role_index(void *key, void *datum, void *datap) role = datum; p = datap; - if (!role->value || role->value > p->p_roles.nprim) + if (!role->value + || role->value > p->p_roles.nprim + || role->bounds > p->p_roles.nprim) return -EINVAL; p->p_role_val_to_name[role->value - 1] = key; p->role_val_to_struct[role->value - 1] = role; @@ -270,9 +278,12 @@ static int type_index(void *key, void *datum, void *datap) p = datap; if (typdatum->primary) { - if (!typdatum->value || typdatum->value > p->p_types.nprim) + if (!typdatum->value + || typdatum->value > p->p_types.nprim + || typdatum->bounds > p->p_types.nprim) return -EINVAL; p->p_type_val_to_name[typdatum->value - 1] = key; + p->type_val_to_struct[typdatum->value - 1] = typdatum; } return 0; @@ -285,7 +296,9 @@ static int user_index(void *key, void *datum, void *datap) usrdatum = datum; p = datap; - if (!usrdatum->value || usrdatum->value > p->p_users.nprim) + if (!usrdatum->value + || usrdatum->value > p->p_users.nprim + || usrdatum->bounds > p->p_users.nprim) return -EINVAL; p->p_user_val_to_name[usrdatum->value - 1] = key; p->user_val_to_struct[usrdatum->value - 1] = usrdatum; @@ -438,6 +451,14 @@ static int policydb_index_others(struct policydb *p) goto out; } + p->type_val_to_struct = + kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)), + GFP_KERNEL); + if (!p->type_val_to_struct) { + rc = -ENOMEM; + goto out; + } + if (cond_init_bool_indexes(p)) { rc = -ENOMEM; goto out; @@ -625,6 +646,7 @@ void policydb_destroy(struct policydb *p) kfree(p->class_val_to_struct); kfree(p->role_val_to_struct); kfree(p->user_val_to_struct); + kfree(p->type_val_to_struct); avtab_destroy(&p->te_avtab); @@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct role_datum *role; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; role = kzalloc(sizeof(*role), GFP_KERNEL); @@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + role->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1236,8 +1263,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct type_datum *typdatum; - int rc; - __le32 buf[3]; + int rc, to_read = 3; + __le32 buf[4]; u32 len; typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); @@ -1246,13 +1273,27 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) return rc; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 4; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); typdatum->value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { + u32 prop = le32_to_cpu(buf[2]); + + if (prop & TYPEDATUM_PROPERTY_PRIMARY) + typdatum->primary = 1; + if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE) + typdatum->attribute = 1; + + typdatum->bounds = le32_to_cpu(buf[3]); + } else { + typdatum->primary = le32_to_cpu(buf[2]); + } key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1309,8 +1350,8 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct user_datum *usrdatum; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); @@ -1319,12 +1360,17 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + usrdatum->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1465,6 +1511,133 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) cat_read, }; +static int user_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct user_datum *upper, *user; + struct policydb *p = datap; + int depth = 0; + + upper = user = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: user %s: " + "too deep or looped boundary", + (char *) key); + return -EINVAL; + } + + upper = p->user_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&user->roles, node, bit) { + if (ebitmap_get_bit(&upper->roles, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "user=%s role=%s bounds=%s\n", + p->p_user_val_to_name[user->value - 1], + p->p_role_val_to_name[bit], + p->p_user_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int role_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct role_datum *upper, *role; + struct policydb *p = datap; + int depth = 0; + + upper = role = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: role %s: " + "too deep or looped bounds\n", + (char *) key); + return -EINVAL; + } + + upper = p->role_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&role->types, node, bit) { + if (ebitmap_get_bit(&upper->types, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "role=%s type=%s bounds=%s\n", + p->p_role_val_to_name[role->value - 1], + p->p_type_val_to_name[bit], + p->p_role_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int type_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct type_datum *upper, *type; + struct policydb *p = datap; + int depth = 0; + + upper = type = datum; + while (upper->bounds) { + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: type %s: " + "too deep or looped boundary\n", + (char *) key); + return -EINVAL; + } + + upper = p->type_val_to_struct[upper->bounds - 1]; + if (upper->attribute) { + printk(KERN_ERR "SELinux: type %s: " + "bounded by attribute %s", + (char *) key, + p->p_type_val_to_name[upper->value - 1]); + return -EINVAL; + } + } + + return 0; +} + +static int policydb_bounds_sanity_check(struct policydb *p) +{ + int rc; + + if (p->policyvers < POLICYDB_VERSION_BOUNDARY) + return 0; + + rc = hashtab_map(p->p_users.table, + user_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_roles.table, + role_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_types.table, + type_bounds_sanity_check, p); + if (rc) + return rc; + + return 0; +} + extern int ss_initialized; /* @@ -1961,6 +2134,10 @@ int policydb_read(struct policydb *p, void *fp) goto bad; } + rc = policydb_bounds_sanity_check(p); + if (rc) + goto bad; + rc = 0; out: return rc; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 4253370..55152d4 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -61,6 +61,7 @@ struct class_datum { /* Role attributes */ struct role_datum { u32 value; /* internal role value */ + u32 bounds; /* boundary of role */ struct ebitmap dominates; /* set of roles dominated by this role */ struct ebitmap types; /* set of authorized types for role */ }; @@ -81,12 +82,15 @@ struct role_allow { /* Type attributes */ struct type_datum { u32 value; /* internal type value */ + u32 bounds; /* boundary of type */ unsigned char primary; /* primary name? */ + unsigned char attribute;/* attribute ?*/ }; /* User attributes */ struct user_datum { u32 value; /* internal user value */ + u32 bounds; /* bounds of user */ struct ebitmap roles; /* set of authorized roles for user */ struct mls_range range; /* MLS range (min - max) for user */ struct mls_level dfltlevel; /* default login MLS level for user */ @@ -209,6 +213,7 @@ struct policydb { struct class_datum **class_val_to_struct; struct role_datum **role_val_to_struct; struct user_datum **user_val_to_struct; + struct type_datum **type_val_to_struct; /* type enforcement access vectors and transitions */ struct avtab te_avtab; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b52f923..1ed94a3 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -88,6 +88,11 @@ static u32 latest_granting; static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len); +static int context_struct_compute_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + u32 requested, + struct av_decision *avd); /* * Return the boolean value of a constraint expression * when it is applied to the specified source and target @@ -274,6 +279,100 @@ mls_ops: } /* + * security_boundary_permission - drops violated permissions + * on boundary constraint. + */ +static void type_attribute_bounds_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + u32 requested, + struct av_decision *avd) +{ + struct context lo_scontext; + struct context lo_tcontext; + struct av_decision lo_avd; + struct type_datum *source + = policydb.type_val_to_struct[scontext->type - 1]; + struct type_datum *target + = policydb.type_val_to_struct[tcontext->type - 1]; + u32 masked = 0; + + if (source->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); + lo_scontext.type = source->bounds; + + context_struct_compute_av(&lo_scontext, + tcontext, + tclass, + requested, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); + lo_tcontext.type = target->bounds; + + context_struct_compute_av(scontext, + &lo_tcontext, + tclass, + requested, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (source->bounds && target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + /* + * lo_scontext and lo_tcontext are already + * set up. + */ + + context_struct_compute_av(&lo_scontext, + &lo_tcontext, + tclass, + requested, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (masked) { + struct audit_buffer *ab; + char *stype_name + = policydb.p_type_val_to_name[source->value - 1]; + char *ttype_name + = policydb.p_type_val_to_name[target->value - 1]; + char *tclass_name + = policydb.p_class_val_to_name[tclass - 1]; + + /* mask violated permissions */ + avd->allowed &= ~masked; + + /* notice to userspace via audit message */ + ab = audit_log_start(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR); + if (!ab) + return; + + audit_log_format(ab, "av boundary violation: " + "source=%s target=%s tclass=%s", + stype_name, ttype_name, tclass_name); + avc_dump_av(ab, tclass, masked); + audit_log_end(ab); + } +} + +/* * Compute access vectors based on a context structure pair for * the permissions in a particular class. */ @@ -404,6 +503,14 @@ static int context_struct_compute_av(struct context *scontext, PROCESS__DYNTRANSITION); } + /* + * If the given source and target types have boundary + * constraint, lazy checks have to mask any violated + * permission and notice it to userspace via audit. + */ + type_attribute_bounds_av(scontext, tcontext, + tclass, requested, avd); + return 0; inval_class: @@ -549,6 +656,69 @@ out: return rc; } +/* + * security_bounded_transition - check whether the given + * transition is directed to bounded, or not. + * It returns 0, if @newsid is bounded by @oldsid. + * Otherwise, it returns error code. + * + * @oldsid : current security identifier + * @newsid : destinated security identifier + */ +int security_bounded_transition(u32 old_sid, u32 new_sid) +{ + struct context *old_context, *new_context; + struct type_datum *type; + int index; + int rc = -EINVAL; + + read_lock(&policy_rwlock); + + old_context = sidtab_search(&sidtab, old_sid); + if (!old_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, old_sid); + goto out; + } + + new_context = sidtab_search(&sidtab, new_sid); + if (!new_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, new_sid); + goto out; + } + + /* type/domain unchaned */ + if (old_context->type == new_context->type) { + rc = 0; + goto out; + } + + index = new_context->type; + while (true) { + type = policydb.type_val_to_struct[index - 1]; + BUG_ON(!type); + + /* not bounded anymore */ + if (!type->bounds) { + rc = -EPERM; + break; + } + + /* @newsid is bounded by @oldsid */ + if (type->bounds == old_context->type) { + rc = 0; + break; + } + index = type->bounds; + } +out: + read_unlock(&policy_rwlock); + + return rc; +} + + /** * security_compute_av - Compute access vector decisions. * @ssid: source security identifier @@ -794,7 +964,7 @@ static int string_to_context_struct(struct policydb *pol, *p++ = 0; typdatum = hashtab_search(pol->p_types.table, scontextp); - if (!typdatum) + if (!typdatum || typdatum->attribute) goto out; ctx->type = typdatum->value; -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply related [flat|nested] 97+ messages in thread
* Re: [PATCH] SELinux: add boundary support and thread context assignment 2008-08-28 7:35 ` [PATCH] SELinux: add boundary support and thread context assignment KaiGai Kohei @ 2008-08-28 12:43 ` Stephen Smalley 2008-08-28 15:06 ` James Morris 1 sibling, 0 replies; 97+ messages in thread From: Stephen Smalley @ 2008-08-28 12:43 UTC (permalink / raw) To: KaiGai Kohei; +Cc: James Morris, paul.moore, jbrindle, selinux On Thu, 2008-08-28 at 16:35 +0900, KaiGai Kohei wrote: > The purpose of this patch is to assign per-thread security context > under a constraint. It enables multi-threaded server application > to kick a request handler with its fair security context, and > helps some of userspace object managers to handle user's request. > > When we assign a per-thread security context, it must not have wider > permissions than the original one. Because a multi-threaded process > shares a single local memory, an arbitary per-thread security context > also means another thread can easily refer violated information. > > The constraint on a per-thread security context requires a new domain > has to be equal or weaker than its original one, when it tries to assign > a per-thread security context. > > Bounds relationship between two types is a way to ensure a domain can > never have wider permission than its bounds. We can define it in two > explicit or implicit ways. > > The first way is using new TYPEBOUNDS statement. It enables to define > a boundary of types explicitly. The other one expand the concept of > existing named based hierarchy. If we defines a type with "." separated > name like "httpd_t.php", toolchain implicitly set its bounds on "httpd_t". > > This feature requires a new policy version. > The 24th version (POLICYDB_VERSION_BOUNDARY) enables to ship them into > kernel space, and the following patch enables to handle it. > > Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> Acked-by: Stephen Smalley <sds@tycho.nsa.gov> BTW, it seems like you should replace the hierarchy checking code in libsepol with one based on the new construct for static checking of hierarchies in userspace (only applied if expand-check=1 of course). > > --- > Updates: > > * rev.7 > - The return code of security_bounded_transition() is returned to > userspace, if it makes an error. > * rev.6 > - It reverrs the place of bounds checks for dyntrans on multithreaded. > - Kernel policy version.24 utilize the third word of type entries as > a property fields. > - Check on type bounds are integrated with creation of avc entries. > violated permissions are masked and reported via audit. > * rev.5 > - A missing number > * rev.4 > - It removes checks on the set relation of attributes at policy load time. > - Several cosmetic changes > * rev.3 > - Kenel policy version.24 got support to load attributes into kernel space. > - Boundary checks on constraints are moved to policy load time. > * rev.2 > - A new policy statement: TYPEBOUNDS > - Existing hierarchy checks are moved to kernel space from toolchain > * rev.1 > - The initial version > > --- > security/selinux/avc.c | 2 +- > security/selinux/hooks.c | 15 ++- > security/selinux/include/avc.h | 4 + > security/selinux/include/security.h | 15 +++- > security/selinux/ss/policydb.c | 205 ++++++++++++++++++++++++++++++++--- > security/selinux/ss/policydb.h | 5 + > security/selinux/ss/services.c | 172 +++++++++++++++++++++++++++++- > 7 files changed, 398 insertions(+), 20 deletions(-) > > diff --git a/security/selinux/avc.c b/security/selinux/avc.c > index 114b4b4..cb30c7e 100644 > --- a/security/selinux/avc.c > +++ b/security/selinux/avc.c > @@ -136,7 +136,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) > * @tclass: target security class > * @av: access vector > */ > -static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) > +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) > { > const char **common_pts = NULL; > u32 common_base = 0; > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index 03fc6a8..38dfca7 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -5219,8 +5219,12 @@ static int selinux_setprocattr(struct task_struct *p, > > if (sid == 0) > return -EINVAL; > - > - /* Only allow single threaded processes to change context */ > + /* > + * SELinux allows to change context in the following case only. > + * - Single threaded processes. > + * - Multi threaded processes intend to change its context into > + * more restricted domain (defined by TYPEBOUNDS statement). > + */ > if (atomic_read(&p->mm->mm_users) != 1) { > struct task_struct *g, *t; > struct mm_struct *mm = p->mm; > @@ -5228,11 +5232,16 @@ static int selinux_setprocattr(struct task_struct *p, > do_each_thread(g, t) { > if (t->mm == mm && t != p) { > read_unlock(&tasklist_lock); > - return -EPERM; > + error = security_bounded_transition(tsec->sid, sid); > + if (!error) > + goto boundary_ok; > + > + return error; > } > } while_each_thread(g, t); > read_unlock(&tasklist_lock); > } > +boundary_ok: > > /* Check permissions for the transition. */ > error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, > diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h > index 7b9769f..d12ff1a 100644 > --- a/security/selinux/include/avc.h > +++ b/security/selinux/include/avc.h > @@ -12,6 +12,7 @@ > #include <linux/kdev_t.h> > #include <linux/spinlock.h> > #include <linux/init.h> > +#include <linux/audit.h> > #include <linux/in6.h> > #include <linux/path.h> > #include <asm/system.h> > @@ -126,6 +127,9 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, > u32 events, u32 ssid, u32 tsid, > u16 tclass, u32 perms); > > +/* Shows permission in human readable form */ > +void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av); > + > /* Exported to selinuxfs */ > int avc_get_hash_stats(char *page); > extern unsigned int avc_cache_threshold; > diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h > index 7c54300..7244737 100644 > --- a/security/selinux/include/security.h > +++ b/security/selinux/include/security.h > @@ -27,13 +27,14 @@ > #define POLICYDB_VERSION_RANGETRANS 21 > #define POLICYDB_VERSION_POLCAP 22 > #define POLICYDB_VERSION_PERMISSIVE 23 > +#define POLICYDB_VERSION_BOUNDARY 24 > > /* Range of policy versions we understand*/ > #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE > #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX > #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE > #else > -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE > +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY > #endif > > #define CONTEXT_MNT 0x01 > @@ -62,6 +63,16 @@ enum { > extern int selinux_policycap_netpeer; > extern int selinux_policycap_openperm; > > +/* > + * type_datum properties > + * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY > + */ > +#define TYPEDATUM_PROPERTY_PRIMARY 0x0001 > +#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 > + > +/* limitation of boundary depth */ > +#define POLICYDB_BOUNDS_MAXDEPTH 4 > + > int security_load_policy(void *data, size_t len); > > int security_policycap_supported(unsigned int req_cap); > @@ -117,6 +128,8 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen, > int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, > u16 tclass); > > +int security_bounded_transition(u32 oldsid, u32 newsid); > + > int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); > > int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, > diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c > index 2391761..7dbe756 100644 > --- a/security/selinux/ss/policydb.c > +++ b/security/selinux/ss/policydb.c > @@ -30,6 +30,7 @@ > #include <linux/slab.h> > #include <linux/string.h> > #include <linux/errno.h> > +#include <linux/audit.h> > #include "security.h" > > #include "policydb.h" > @@ -116,7 +117,12 @@ static struct policydb_compat_info policydb_compat[] = { > .version = POLICYDB_VERSION_PERMISSIVE, > .sym_num = SYM_NUM, > .ocon_num = OCON_NUM, > - } > + }, > + { > + .version = POLICYDB_VERSION_BOUNDARY, > + .sym_num = SYM_NUM, > + .ocon_num = OCON_NUM, > + }, > }; > > static struct policydb_compat_info *policydb_lookup_compat(int version) > @@ -254,7 +260,9 @@ static int role_index(void *key, void *datum, void *datap) > > role = datum; > p = datap; > - if (!role->value || role->value > p->p_roles.nprim) > + if (!role->value > + || role->value > p->p_roles.nprim > + || role->bounds > p->p_roles.nprim) > return -EINVAL; > p->p_role_val_to_name[role->value - 1] = key; > p->role_val_to_struct[role->value - 1] = role; > @@ -270,9 +278,12 @@ static int type_index(void *key, void *datum, void *datap) > p = datap; > > if (typdatum->primary) { > - if (!typdatum->value || typdatum->value > p->p_types.nprim) > + if (!typdatum->value > + || typdatum->value > p->p_types.nprim > + || typdatum->bounds > p->p_types.nprim) > return -EINVAL; > p->p_type_val_to_name[typdatum->value - 1] = key; > + p->type_val_to_struct[typdatum->value - 1] = typdatum; > } > > return 0; > @@ -285,7 +296,9 @@ static int user_index(void *key, void *datum, void *datap) > > usrdatum = datum; > p = datap; > - if (!usrdatum->value || usrdatum->value > p->p_users.nprim) > + if (!usrdatum->value > + || usrdatum->value > p->p_users.nprim > + || usrdatum->bounds > p->p_users.nprim) > return -EINVAL; > p->p_user_val_to_name[usrdatum->value - 1] = key; > p->user_val_to_struct[usrdatum->value - 1] = usrdatum; > @@ -438,6 +451,14 @@ static int policydb_index_others(struct policydb *p) > goto out; > } > > + p->type_val_to_struct = > + kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)), > + GFP_KERNEL); > + if (!p->type_val_to_struct) { > + rc = -ENOMEM; > + goto out; > + } > + > if (cond_init_bool_indexes(p)) { > rc = -ENOMEM; > goto out; > @@ -625,6 +646,7 @@ void policydb_destroy(struct policydb *p) > kfree(p->class_val_to_struct); > kfree(p->role_val_to_struct); > kfree(p->user_val_to_struct); > + kfree(p->type_val_to_struct); > > avtab_destroy(&p->te_avtab); > > @@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) > { > char *key = NULL; > struct role_datum *role; > - int rc; > - __le32 buf[2]; > + int rc, to_read = 2; > + __le32 buf[3]; > u32 len; > > role = kzalloc(sizeof(*role), GFP_KERNEL); > @@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) > goto out; > } > > - rc = next_entry(buf, fp, sizeof buf); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + to_read = 3; > + > + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); > if (rc < 0) > goto bad; > > len = le32_to_cpu(buf[0]); > role->value = le32_to_cpu(buf[1]); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + role->bounds = le32_to_cpu(buf[2]); > > key = kmalloc(len + 1, GFP_KERNEL); > if (!key) { > @@ -1236,8 +1263,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) > { > char *key = NULL; > struct type_datum *typdatum; > - int rc; > - __le32 buf[3]; > + int rc, to_read = 3; > + __le32 buf[4]; > u32 len; > > typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); > @@ -1246,13 +1273,27 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) > return rc; > } > > - rc = next_entry(buf, fp, sizeof buf); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + to_read = 4; > + > + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); > if (rc < 0) > goto bad; > > len = le32_to_cpu(buf[0]); > typdatum->value = le32_to_cpu(buf[1]); > - typdatum->primary = le32_to_cpu(buf[2]); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { > + u32 prop = le32_to_cpu(buf[2]); > + > + if (prop & TYPEDATUM_PROPERTY_PRIMARY) > + typdatum->primary = 1; > + if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE) > + typdatum->attribute = 1; > + > + typdatum->bounds = le32_to_cpu(buf[3]); > + } else { > + typdatum->primary = le32_to_cpu(buf[2]); > + } > > key = kmalloc(len + 1, GFP_KERNEL); > if (!key) { > @@ -1309,8 +1350,8 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) > { > char *key = NULL; > struct user_datum *usrdatum; > - int rc; > - __le32 buf[2]; > + int rc, to_read = 2; > + __le32 buf[3]; > u32 len; > > usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); > @@ -1319,12 +1360,17 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) > goto out; > } > > - rc = next_entry(buf, fp, sizeof buf); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + to_read = 3; > + > + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); > if (rc < 0) > goto bad; > > len = le32_to_cpu(buf[0]); > usrdatum->value = le32_to_cpu(buf[1]); > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + usrdatum->bounds = le32_to_cpu(buf[2]); > > key = kmalloc(len + 1, GFP_KERNEL); > if (!key) { > @@ -1465,6 +1511,133 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) > cat_read, > }; > > +static int user_bounds_sanity_check(void *key, void *datum, void *datap) > +{ > + struct user_datum *upper, *user; > + struct policydb *p = datap; > + int depth = 0; > + > + upper = user = datum; > + while (upper->bounds) { > + struct ebitmap_node *node; > + unsigned long bit; > + > + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { > + printk(KERN_ERR "SELinux: user %s: " > + "too deep or looped boundary", > + (char *) key); > + return -EINVAL; > + } > + > + upper = p->user_val_to_struct[upper->bounds - 1]; > + ebitmap_for_each_positive_bit(&user->roles, node, bit) { > + if (ebitmap_get_bit(&upper->roles, bit)) > + continue; > + > + printk(KERN_ERR > + "SELinux: boundary violated policy: " > + "user=%s role=%s bounds=%s\n", > + p->p_user_val_to_name[user->value - 1], > + p->p_role_val_to_name[bit], > + p->p_user_val_to_name[upper->value - 1]); > + > + return -EINVAL; > + } > + } > + > + return 0; > +} > + > +static int role_bounds_sanity_check(void *key, void *datum, void *datap) > +{ > + struct role_datum *upper, *role; > + struct policydb *p = datap; > + int depth = 0; > + > + upper = role = datum; > + while (upper->bounds) { > + struct ebitmap_node *node; > + unsigned long bit; > + > + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { > + printk(KERN_ERR "SELinux: role %s: " > + "too deep or looped bounds\n", > + (char *) key); > + return -EINVAL; > + } > + > + upper = p->role_val_to_struct[upper->bounds - 1]; > + ebitmap_for_each_positive_bit(&role->types, node, bit) { > + if (ebitmap_get_bit(&upper->types, bit)) > + continue; > + > + printk(KERN_ERR > + "SELinux: boundary violated policy: " > + "role=%s type=%s bounds=%s\n", > + p->p_role_val_to_name[role->value - 1], > + p->p_type_val_to_name[bit], > + p->p_role_val_to_name[upper->value - 1]); > + > + return -EINVAL; > + } > + } > + > + return 0; > +} > + > +static int type_bounds_sanity_check(void *key, void *datum, void *datap) > +{ > + struct type_datum *upper, *type; > + struct policydb *p = datap; > + int depth = 0; > + > + upper = type = datum; > + while (upper->bounds) { > + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { > + printk(KERN_ERR "SELinux: type %s: " > + "too deep or looped boundary\n", > + (char *) key); > + return -EINVAL; > + } > + > + upper = p->type_val_to_struct[upper->bounds - 1]; > + if (upper->attribute) { > + printk(KERN_ERR "SELinux: type %s: " > + "bounded by attribute %s", > + (char *) key, > + p->p_type_val_to_name[upper->value - 1]); > + return -EINVAL; > + } > + } > + > + return 0; > +} > + > +static int policydb_bounds_sanity_check(struct policydb *p) > +{ > + int rc; > + > + if (p->policyvers < POLICYDB_VERSION_BOUNDARY) > + return 0; > + > + rc = hashtab_map(p->p_users.table, > + user_bounds_sanity_check, p); > + if (rc) > + return rc; > + > + rc = hashtab_map(p->p_roles.table, > + role_bounds_sanity_check, p); > + if (rc) > + return rc; > + > + rc = hashtab_map(p->p_types.table, > + type_bounds_sanity_check, p); > + if (rc) > + return rc; > + > + return 0; > +} > + > extern int ss_initialized; > > /* > @@ -1961,6 +2134,10 @@ int policydb_read(struct policydb *p, void *fp) > goto bad; > } > > + rc = policydb_bounds_sanity_check(p); > + if (rc) > + goto bad; > + > rc = 0; > out: > return rc; > diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h > index 4253370..55152d4 100644 > --- a/security/selinux/ss/policydb.h > +++ b/security/selinux/ss/policydb.h > @@ -61,6 +61,7 @@ struct class_datum { > /* Role attributes */ > struct role_datum { > u32 value; /* internal role value */ > + u32 bounds; /* boundary of role */ > struct ebitmap dominates; /* set of roles dominated by this role */ > struct ebitmap types; /* set of authorized types for role */ > }; > @@ -81,12 +82,15 @@ struct role_allow { > /* Type attributes */ > struct type_datum { > u32 value; /* internal type value */ > + u32 bounds; /* boundary of type */ > unsigned char primary; /* primary name? */ > + unsigned char attribute;/* attribute ?*/ > }; > > /* User attributes */ > struct user_datum { > u32 value; /* internal user value */ > + u32 bounds; /* bounds of user */ > struct ebitmap roles; /* set of authorized roles for user */ > struct mls_range range; /* MLS range (min - max) for user */ > struct mls_level dfltlevel; /* default login MLS level for user */ > @@ -209,6 +213,7 @@ struct policydb { > struct class_datum **class_val_to_struct; > struct role_datum **role_val_to_struct; > struct user_datum **user_val_to_struct; > + struct type_datum **type_val_to_struct; > > /* type enforcement access vectors and transitions */ > struct avtab te_avtab; > diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c > index b52f923..1ed94a3 100644 > --- a/security/selinux/ss/services.c > +++ b/security/selinux/ss/services.c > @@ -88,6 +88,11 @@ static u32 latest_granting; > static int context_struct_to_string(struct context *context, char **scontext, > u32 *scontext_len); > > +static int context_struct_compute_av(struct context *scontext, > + struct context *tcontext, > + u16 tclass, > + u32 requested, > + struct av_decision *avd); > /* > * Return the boolean value of a constraint expression > * when it is applied to the specified source and target > @@ -274,6 +279,100 @@ mls_ops: > } > > /* > + * security_boundary_permission - drops violated permissions > + * on boundary constraint. > + */ > +static void type_attribute_bounds_av(struct context *scontext, > + struct context *tcontext, > + u16 tclass, > + u32 requested, > + struct av_decision *avd) > +{ > + struct context lo_scontext; > + struct context lo_tcontext; > + struct av_decision lo_avd; > + struct type_datum *source > + = policydb.type_val_to_struct[scontext->type - 1]; > + struct type_datum *target > + = policydb.type_val_to_struct[tcontext->type - 1]; > + u32 masked = 0; > + > + if (source->bounds) { > + memset(&lo_avd, 0, sizeof(lo_avd)); > + > + memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); > + lo_scontext.type = source->bounds; > + > + context_struct_compute_av(&lo_scontext, > + tcontext, > + tclass, > + requested, > + &lo_avd); > + if ((lo_avd.allowed & avd->allowed) == avd->allowed) > + return; /* no masked permission */ > + masked = ~lo_avd.allowed & avd->allowed; > + } > + > + if (target->bounds) { > + memset(&lo_avd, 0, sizeof(lo_avd)); > + > + memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); > + lo_tcontext.type = target->bounds; > + > + context_struct_compute_av(scontext, > + &lo_tcontext, > + tclass, > + requested, > + &lo_avd); > + if ((lo_avd.allowed & avd->allowed) == avd->allowed) > + return; /* no masked permission */ > + masked = ~lo_avd.allowed & avd->allowed; > + } > + > + if (source->bounds && target->bounds) { > + memset(&lo_avd, 0, sizeof(lo_avd)); > + /* > + * lo_scontext and lo_tcontext are already > + * set up. > + */ > + > + context_struct_compute_av(&lo_scontext, > + &lo_tcontext, > + tclass, > + requested, > + &lo_avd); > + if ((lo_avd.allowed & avd->allowed) == avd->allowed) > + return; /* no masked permission */ > + masked = ~lo_avd.allowed & avd->allowed; > + } > + > + if (masked) { > + struct audit_buffer *ab; > + char *stype_name > + = policydb.p_type_val_to_name[source->value - 1]; > + char *ttype_name > + = policydb.p_type_val_to_name[target->value - 1]; > + char *tclass_name > + = policydb.p_class_val_to_name[tclass - 1]; > + > + /* mask violated permissions */ > + avd->allowed &= ~masked; > + > + /* notice to userspace via audit message */ > + ab = audit_log_start(current->audit_context, > + GFP_ATOMIC, AUDIT_SELINUX_ERR); > + if (!ab) > + return; > + > + audit_log_format(ab, "av boundary violation: " > + "source=%s target=%s tclass=%s", > + stype_name, ttype_name, tclass_name); > + avc_dump_av(ab, tclass, masked); > + audit_log_end(ab); > + } > +} > + > +/* > * Compute access vectors based on a context structure pair for > * the permissions in a particular class. > */ > @@ -404,6 +503,14 @@ static int context_struct_compute_av(struct context *scontext, > PROCESS__DYNTRANSITION); > } > > + /* > + * If the given source and target types have boundary > + * constraint, lazy checks have to mask any violated > + * permission and notice it to userspace via audit. > + */ > + type_attribute_bounds_av(scontext, tcontext, > + tclass, requested, avd); > + > return 0; > > inval_class: > @@ -549,6 +656,69 @@ out: > return rc; > } > > +/* > + * security_bounded_transition - check whether the given > + * transition is directed to bounded, or not. > + * It returns 0, if @newsid is bounded by @oldsid. > + * Otherwise, it returns error code. > + * > + * @oldsid : current security identifier > + * @newsid : destinated security identifier > + */ > +int security_bounded_transition(u32 old_sid, u32 new_sid) > +{ > + struct context *old_context, *new_context; > + struct type_datum *type; > + int index; > + int rc = -EINVAL; > + > + read_lock(&policy_rwlock); > + > + old_context = sidtab_search(&sidtab, old_sid); > + if (!old_context) { > + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", > + __func__, old_sid); > + goto out; > + } > + > + new_context = sidtab_search(&sidtab, new_sid); > + if (!new_context) { > + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", > + __func__, new_sid); > + goto out; > + } > + > + /* type/domain unchaned */ > + if (old_context->type == new_context->type) { > + rc = 0; > + goto out; > + } > + > + index = new_context->type; > + while (true) { > + type = policydb.type_val_to_struct[index - 1]; > + BUG_ON(!type); > + > + /* not bounded anymore */ > + if (!type->bounds) { > + rc = -EPERM; > + break; > + } > + > + /* @newsid is bounded by @oldsid */ > + if (type->bounds == old_context->type) { > + rc = 0; > + break; > + } > + index = type->bounds; > + } > +out: > + read_unlock(&policy_rwlock); > + > + return rc; > +} > + > + > /** > * security_compute_av - Compute access vector decisions. > * @ssid: source security identifier > @@ -794,7 +964,7 @@ static int string_to_context_struct(struct policydb *pol, > *p++ = 0; > > typdatum = hashtab_search(pol->p_types.table, scontextp); > - if (!typdatum) > + if (!typdatum || typdatum->attribute) > goto out; > > ctx->type = typdatum->value; > > -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH] SELinux: add boundary support and thread context assignment 2008-08-28 7:35 ` [PATCH] SELinux: add boundary support and thread context assignment KaiGai Kohei 2008-08-28 12:43 ` Stephen Smalley @ 2008-08-28 15:06 ` James Morris 1 sibling, 0 replies; 97+ messages in thread From: James Morris @ 2008-08-28 15:06 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, paul.moore, jbrindle, selinux On Thu, 28 Aug 2008, KaiGai Kohei wrote: > This feature requires a new policy version. > The 24th version (POLICYDB_VERSION_BOUNDARY) enables to ship them into > kernel space, and the following patch enables to handle it. > > Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> Applied to: git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6#next This should appear in linux-next within 24 hrs and be tested there. - James -- James Morris <jmorris@namei.org> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) 2008-08-05 5:47 ` [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei 2008-08-05 5:55 ` [PATCH 1/3] " KaiGai Kohei @ 2008-08-05 5:55 ` KaiGai Kohei 2008-08-06 10:14 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.3) KaiGai Kohei 2008-10-09 17:10 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) Joshua Brindle 2008-08-05 5:55 ` [PATCH 3/3] " KaiGai Kohei 2008-08-11 17:58 ` [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) Joshua Brindle 3 siblings, 2 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-05 5:55 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux [2/3] thread-context-checkpolicy.2.patch It enables to support TYPEBOUNDS statement and to expand existing hierarchies implicitly. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> -- module_compiler.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ policy_define.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++- policy_define.h | 1 policy_parse.y | 5 ++ policy_scan.l | 2 + 5 files changed, 186 insertions(+), 1 deletion(-) Index: checkpolicy/policy_define.c =================================================================== --- checkpolicy/policy_define.c (revision 2938) +++ checkpolicy/policy_define.c (working copy) @@ -1127,6 +1127,77 @@ return 0; } +static int define_typebounds_helper(char *bounds_id, char *type_id) +{ + type_datum_t *bounds, *type; + + if (!is_id_in_scope(SYM_TYPES, bounds_id)) { + yyerror2("type %s is not within scope", bounds_id); + return -1; + } + + bounds = hashtab_search(policydbp->p_types.table, bounds_id); + if (!bounds || bounds->flavor == TYPE_ATTRIB) { + yyerror2("hoge unknown type %s", bounds_id); + return -1; + } + + if (!is_id_in_scope(SYM_TYPES, type_id)) { + yyerror2("type %s is not within scope", type_id); + return -1; + } + + type = hashtab_search(policydbp->p_types.table, type_id); + if (!type || type->flavor == TYPE_ATTRIB) { + yyerror2("type %s is not declared", type_id); + return -1; + } + + if (type->flavor == TYPE_TYPE && !type->primary) { + type = policydbp->type_val_to_struct[type->s.value - 1]; + } else if (type->flavor == TYPE_ALIAS) { + type = policydbp->type_val_to_struct[type->primary - 1]; + } + + if (!type->bounds) + type->bounds = bounds->s.value; + else if (type->bounds != bounds->s.value) { + yyerror2("type %s has inconsistent master {%s,%s}", + type_id, + policydbp->p_type_val_to_name[type->bounds - 1], + policydbp->p_type_val_to_name[bounds->s.value - 1]); + return -1; + } + + return 0; +} + +int define_typebounds(void) +{ + char *bounds, *id; + + if (pass == 1) { + while ((id = queue_remove(id_queue))) + free(id); + return 0; + } + + bounds = (char *) queue_remove(id_queue); + if (!bounds) { + yyerror("no type name for typebounds definition?"); + return -1; + } + + while ((id = queue_remove(id_queue))) { + if (define_typebounds_helper(bounds, id)) + return -1; + free(id); + } + free(bounds); + + return 0; +} + int define_type(int alias) { char *id; @@ -1134,12 +1205,32 @@ int newattr = 0; if (pass == 2) { - while ((id = queue_remove(id_queue))) + /* + * If type name contains ".", we have to define boundary + * relationship implicitly to keep compatibility with + * old name based hierarchy. + */ + if ((id = queue_remove(id_queue))) { + char *bounds, *delim; + + if ((delim = strrchr(id, '.')) + && (bounds = strdup(id))) { + bounds[(size_t)(delim - id)] = '\0'; + + if (define_typebounds_helper(bounds, id)) + return -1; + free(bounds); + } free(id); + } + if (alias) { while ((id = queue_remove(id_queue))) free(id); } + + while ((id = queue_remove(id_queue))) + free(id); return 0; } Index: checkpolicy/policy_scan.l =================================================================== --- checkpolicy/policy_scan.l (revision 2938) +++ checkpolicy/policy_scan.l (working copy) @@ -82,6 +82,8 @@ typealias { return(TYPEALIAS); } TYPEATTRIBUTE | typeattribute { return(TYPEATTRIBUTE); } +TYPEBOUNDS | +typebounds { return(TYPEBOUNDS); } TYPE | type { return(TYPE); } BOOL | Index: checkpolicy/policy_define.h =================================================================== --- checkpolicy/policy_define.h (revision 2938) +++ checkpolicy/policy_define.h (working copy) @@ -47,6 +47,7 @@ int define_te_avtab(int which); int define_typealias(void); int define_typeattribute(void); +int define_typebounds(void); int define_type(int alias); int define_user(void); int define_validatetrans(constraint_expr_t *expr); Index: checkpolicy/policy_parse.y =================================================================== --- checkpolicy/policy_parse.y (revision 2938) +++ checkpolicy/policy_parse.y (working copy) @@ -92,6 +92,7 @@ %token ROLES %token TYPEALIAS %token TYPEATTRIBUTE +%token TYPEBOUNDS %token TYPE %token TYPES %token ALIAS @@ -258,6 +259,7 @@ | type_def | typealias_def | typeattribute_def + | typebounds_def | bool_def | transition_def | range_trans_def @@ -278,6 +280,9 @@ typeattribute_def : TYPEATTRIBUTE identifier id_comma_list ';' {if (define_typeattribute()) return -1;} ; +typebounds_def : TYPEBOUNDS identifier id_comma_list ';' + {if (define_typebounds()) return -1;} + ; opt_attr_list : ',' id_comma_list | ; Index: checkpolicy/module_compiler.c =================================================================== --- checkpolicy/module_compiler.c (revision 2938) +++ checkpolicy/module_compiler.c (working copy) @@ -163,6 +163,43 @@ return retval; } +static int role_implicit_bounds(hashtab_t roles_tab, + char *role_id, role_datum_t *role) +{ + role_datum_t *bounds; + char *bounds_id, *delim; + + delim = strrchr(role_id, '.'); + if (!delim) + return 0; /* no implicit boundary */ + + bounds_id = strdup(role_id); + if (!bounds_id) { + yyerror("out of memory"); + return -1; + } + bounds_id[(size_t)(delim - role_id)] = '\0'; + + bounds = hashtab_search(roles_tab, bounds_id); + if (!bounds) { + yyerror2("role %s doesn't exist, is implicit bounds of %s", + bounds_id, role_id); + return -1; + } + + if (!role->bounds) + role->bounds = bounds->s.value; + else if (role->bounds != bounds->s.value) { + yyerror2("role %s has inconsistent bounds %s/%s", + role_id, bounds_id, + policydbp->p_role_val_to_name[role->bounds - 1]); + return -1; + } + free(bounds_id); + + return 0; +} + role_datum_t *declare_role(void) { char *id = queue_remove(id_queue), *dest_id = NULL; @@ -217,6 +254,12 @@ } role_datum_init(dest_role); dest_role->s.value = value; + if (role_implicit_bounds(roles_tab, dest_id, dest_role)) { + free(dest_id); + role_datum_destroy(dest_role); + free(dest_role); + return NULL; + } if (hashtab_insert(roles_tab, dest_id, dest_role)) { yyerror("Out of memory!"); free(dest_id); @@ -323,6 +366,43 @@ } } +static int user_implicit_bounds(hashtab_t users_tab, + char *user_id, user_datum_t *user) +{ + user_datum_t *bounds; + char *bounds_id, *delim; + + delim = strrchr(user_id, '.'); + if (!delim) + return 0; /* no implicit boundary */ + + bounds_id = strdup(user_id); + if (!bounds_id) { + yyerror("out of memory"); + return -1; + } + bounds_id[(size_t)(delim - user_id)] = '\0'; + + bounds = hashtab_search(users_tab, bounds_id); + if (!bounds) { + yyerror2("user %s doesn't exist, is implicit bounds of %s", + bounds_id, user_id); + return -1; + } + + if (!user->bounds) + user->bounds = bounds->s.value; + else if (user->bounds != bounds->s.value) { + yyerror2("user %s has inconsistent bounds %s/%s", + user_id, bounds_id, + policydbp->p_role_val_to_name[user->bounds - 1]); + return -1; + } + free(bounds_id); + + return 0; +} + user_datum_t *declare_user(void) { char *id = queue_remove(id_queue), *dest_id = NULL; @@ -378,6 +458,12 @@ } user_datum_init(dest_user); dest_user->s.value = value; + if (user_implicit_bounds(users_tab, dest_id, dest_user)) { + free(dest_id); + user_datum_destroy(dest_user); + free(dest_user); + return NULL; + } if (hashtab_insert(users_tab, dest_id, dest_user)) { yyerror("Out of memory!"); free(dest_id); -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 2/3] Thread/Child-Domain Assignment (rev.3) 2008-08-05 5:55 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei @ 2008-08-06 10:14 ` KaiGai Kohei 2008-10-09 17:10 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) Joshua Brindle 1 sibling, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-06 10:14 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux KaiGai Kohei wrote: > [2/3] thread-context-checkpolicy.2.patch > It enables to support TYPEBOUNDS statement and to expand > existing hierarchies implicitly. > > Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> > -- > module_compiler.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ > policy_define.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++- > policy_define.h | 1 > policy_parse.y | 5 ++ > policy_scan.l | 2 + > 5 files changed, 186 insertions(+), 1 deletion(-) The patch for checkpolicy is unchanged at revision 3. Please apply the previous version, as is. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) 2008-08-05 5:55 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei 2008-08-06 10:14 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.3) KaiGai Kohei @ 2008-10-09 17:10 ` Joshua Brindle 2008-10-10 1:19 ` KaiGai Kohei 1 sibling, 1 reply; 97+ messages in thread From: Joshua Brindle @ 2008-10-09 17:10 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, jmorris, paul.moore, jbrindle, selinux KaiGai Kohei wrote: > [2/3] thread-context-checkpolicy.2.patch > It enables to support TYPEBOUNDS statement and to expand > existing hierarchies implicitly. > > Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> > Merged into checkpolicy-2.0.17 -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) 2008-10-09 17:10 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) Joshua Brindle @ 2008-10-10 1:19 ` KaiGai Kohei 2008-10-10 1:22 ` Joshua Brindle 0 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-10-10 1:19 UTC (permalink / raw) To: Joshua Brindle; +Cc: Stephen Smalley, jmorris, paul.moore, jbrindle, selinux Joshua Brindle wrote: > KaiGai Kohei wrote: >> [2/3] thread-context-checkpolicy.2.patch >> It enables to support TYPEBOUNDS statement and to expand >> existing hierarchies implicitly. >> >> Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> >> > > Merged into checkpolicy-2.0.17 Has the repository completely moved to git now? If so, is it possible to provide "git://..." connection? -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) 2008-10-10 1:19 ` KaiGai Kohei @ 2008-10-10 1:22 ` Joshua Brindle 0 siblings, 0 replies; 97+ messages in thread From: Joshua Brindle @ 2008-10-10 1:22 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, jmorris, paul.moore, jbrindle, selinux KaiGai Kohei wrote: > Joshua Brindle wrote: > >> KaiGai Kohei wrote: >> >>> [2/3] thread-context-checkpolicy.2.patch >>> It enables to support TYPEBOUNDS statement and to expand >>> existing hierarchies implicitly. >>> >>> Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> >>> >>> >> Merged into checkpolicy-2.0.17 >> > > Has the repository completely moved to git now? > > Yes > If so, is it possible to provide "git://..." connection? > git clone http://oss.tresys.com/git/selinux.git http://userspace.selinuxproject.org/trac/wiki/Contributing <http://userspace.selinuxproject.org/trac/wiki/Contributing> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 3/3] Thread/Child-Domain Assignment (rev.2) 2008-08-05 5:47 ` [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei 2008-08-05 5:55 ` [PATCH 1/3] " KaiGai Kohei 2008-08-05 5:55 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei @ 2008-08-05 5:55 ` KaiGai Kohei 2008-08-06 10:13 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.3) KaiGai Kohei 2008-08-25 12:32 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) KaiGai Kohei 2008-08-11 17:58 ` [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) Joshua Brindle 3 siblings, 2 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-05 5:55 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux [3/3] thread-context-libsepol.2.patch It enables to support binary policy format version 24. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> -- include/sepol/policydb/policydb.h | 19 +++++- src/expand.c | 105 ++++++++++++++++++++++++++++++++++++ src/link.c | 108 ++++++++++++++++++++++++++++++++++++-- src/policydb.c | 80 +++++++++++++++++++++------- src/write.c | 6 ++ 5 files changed, 294 insertions(+), 24 deletions(-) Index: libsepol/include/sepol/policydb/policydb.h =================================================================== --- libsepol/include/sepol/policydb/policydb.h (revision 2938) +++ libsepol/include/sepol/policydb/policydb.h (working copy) @@ -119,6 +119,7 @@ ebitmap_t dominates; /* set of roles dominated by this role */ type_set_t types; /* set of authorized types for role */ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ + uint32_t bounds; /* bounds role, if exist */ } role_datum_t; typedef struct role_trans { @@ -145,6 +146,7 @@ ebitmap_t types; /* types with this attribute */ #define TYPE_FLAGS_PERMISSIVE 0x01 uint32_t flags; + uint32_t bounds; /* bounds type, if exist */ } type_datum_t; /* User attributes */ @@ -156,6 +158,7 @@ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ mls_range_t exp_range; /* expanded range used for validation */ mls_level_t exp_dfltlevel; /* expanded range used for validation */ + uint32_t bounds; /* bounds user, if exist */ } user_datum_t; /* Sensitivity attributes */ @@ -595,10 +598,11 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY /* Module versions and specific changes*/ #define MOD_POLICYDB_VERSION_BASE 4 @@ -608,12 +612,23 @@ #define MOD_POLICYDB_VERSION_MLS_USERS 6 #define MOD_POLICYDB_VERSION_POLCAP 7 #define MOD_POLICYDB_VERSION_PERMISSIVE 8 +#define MOD_POLICYDB_VERSION_BOUNDARY 9 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_PERMISSIVE +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_BOUNDARY #define POLICYDB_CONFIG_MLS 1 +/* macros to check policy feature */ + +/* TODO: add other features here */ + +#define policydb_has_boundary_feature(p) \ + (((p)->policy_type == POLICY_KERN \ + && p->policyvers >= POLICYDB_VERSION_BOUNDARY) || \ + ((p)->policy_type != POLICY_KERN \ + && p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY)) + /* the config flags related to unknown classes/perms are bits 2 and 3 */ #define DENY_UNKNOWN SEPOL_DENY_UNKNOWN #define REJECT_UNKNOWN SEPOL_REJECT_UNKNOWN Index: libsepol/src/policydb.c =================================================================== --- libsepol/src/policydb.c (revision 2938) +++ libsepol/src/policydb.c (working copy) @@ -110,6 +110,12 @@ .sym_num = SYM_NUM, .ocon_num = OCON_NODE6 + 1, }, + { + .type = POLICY_KERN, + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, { .type = POLICY_BASE, .version = MOD_POLICYDB_VERSION_BASE, @@ -141,6 +147,12 @@ .ocon_num = OCON_NODE6 + 1, }, { + .type = POLICY_BASE, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, + { .type = POLICY_MOD, .version = MOD_POLICYDB_VERSION_BASE, .sym_num = SYM_NUM, @@ -170,6 +182,12 @@ .sym_num = SYM_NUM, .ocon_num = 0 }, + { + .type = POLICY_MOD, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = 0 + }, }; #if 0 @@ -1855,20 +1873,25 @@ { char *key = 0; role_datum_t *role; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; role = calloc(1, sizeof(role_datum_t)); if (!role) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + role->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) @@ -1918,30 +1941,44 @@ type_datum_t *typdatum; uint32_t buf[5]; size_t len; - int rc, to_read; + int rc, to_read, items = 0; typdatum = calloc(1, sizeof(type_datum_t)); if (!typdatum) return -1; - if (p->policy_type == POLICY_KERN) - to_read = 3; - else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - to_read = 5; - else - to_read = 4; + if (p->policy_type == POLICY_KERN) { + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 4; + else + to_read = 3; + } else { + if (p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY) + to_read = 6; + else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) + to_read = 5; + else + to_read = 4; + } rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; - len = le32_to_cpu(buf[0]); - typdatum->s.value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); - if (p->policy_type != POLICY_KERN) { - typdatum->flavor = le32_to_cpu(buf[3]); + len = le32_to_cpu(buf[items++]); + typdatum->s.value = le32_to_cpu(buf[items++]); + typdatum->primary = le32_to_cpu(buf[items++]); + + if (p->policy_type == POLICY_KERN) { + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + typdatum->bounds = le32_to_cpu(buf[items++]); + } else { + if (p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY) + typdatum->bounds = le32_to_cpu(buf[items++]); + + typdatum->flavor = le32_to_cpu(buf[items++]); if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - typdatum->flags = le32_to_cpu(buf[4]); + typdatum->flags = le32_to_cpu(buf[items++]); if (ebitmap_read(&typdatum->types, fp)) goto bad; } @@ -2293,20 +2330,25 @@ { char *key = 0; user_datum_t *usrdatum; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; usrdatum = calloc(1, sizeof(user_datum_t)); if (!usrdatum) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + usrdatum->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) Index: libsepol/src/expand.c =================================================================== --- libsepol/src/expand.c (revision 2938) +++ libsepol/src/expand.c (working copy) @@ -466,6 +466,100 @@ return 0; } +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_TYPES)) + return 0; + + bounds_val = state->typemap[type->bounds - 1]; + + dest = hashtab_search(state->out->p_types.table, (char *)key); + if (!dest) { + ERR(state->handle, "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_ROLES)) + return 0; + + bounds_val = state->rolemap[role->bounds - 1]; + + dest = hashtab_search(state->out->p_roles.table, (char *)key); + if (!dest) { + ERR(state->handle, "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_USERS)) + return 0; + + bounds_val = state->usermap[user->bounds - 1]; + + dest = hashtab_search(state->out->p_users.table, (char *)key); + if (!dest) { + ERR(state->handle, "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be certain that * the out symbol table will have the type that the alias refers. Otherwise, we * won't be able to find the type value for the alias. We can't depend on the @@ -2393,6 +2487,11 @@ goto cleanup; } + /* copy type bounds */ + if (hashtab_map(state.base->p_types.table, + type_bounds_copy_callback, &state)) + goto cleanup; + /* copy aliases */ if (hashtab_map(state.base->p_types.table, alias_copy_callback, &state)) goto cleanup; @@ -2406,6 +2505,9 @@ /* copy roles */ if (hashtab_map(state.base->p_roles.table, role_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_roles.table, + role_bounds_copy_callback, &state)) + goto cleanup; /* copy MLS's sensitivity level and categories - this needs to be done * before expanding users (they need to be indexed too) */ @@ -2421,6 +2523,9 @@ /* copy users */ if (hashtab_map(state.base->p_users.table, user_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_users.table, + user_bounds_copy_callback, &state)) + goto cleanup; /* copy bools */ if (hashtab_map(state.base->p_bools.table, bool_copy_callback, &state)) Index: libsepol/src/write.c =================================================================== --- libsepol/src/write.c (revision 2938) +++ libsepol/src/write.c (working copy) @@ -920,6 +920,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(role->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(role->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; @@ -957,6 +959,8 @@ buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(typdatum->s.value); buf[items++] = cpu_to_le32(typdatum->primary); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(typdatum->bounds); if (p->policy_type != POLICY_KERN) { buf[items++] = cpu_to_le32(typdatum->flavor); if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) @@ -997,6 +1001,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(usrdatum->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(usrdatum->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; Index: libsepol/src/link.c =================================================================== --- libsepol/src/link.c (revision 2938) +++ libsepol/src/link.c (working copy) @@ -660,6 +660,97 @@ user_copy_callback, bool_copy_callback, sens_copy_callback, cat_copy_callback}; +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + bounds_val = state->cur->map[SYM_TYPES][type->bounds - 1]; + + dest = hashtab_search(state->base->p_types.table, key); + if (!dest) { + ERR(state->handle, + "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + bounds_val = state->cur->map[SYM_ROLES][role->bounds - 1]; + + dest = hashtab_search(state->base->p_roles.table, key); + if (!dest) { + ERR(state->handle, + "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + bounds_val = state->cur->map[SYM_USERS][user->bounds - 1]; + + dest = hashtab_search(state->base->p_users.table, key); + if (!dest) { + ERR(state->handle, + "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be * certain that the base symbol table will have the type that the * alias refers. Otherwise, we won't be able to find the type value @@ -1362,11 +1453,22 @@ } } - if (hashtab_map - (src_symtab[SYM_TYPES].table, alias_copy_callback, state)) { + if (hashtab_map(src_symtab[SYM_TYPES].table, + type_bounds_copy_callback, state)) return -1; - } + if (hashtab_map(src_symtab[SYM_TYPES].table, + alias_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_ROLES].table, + role_bounds_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_USERS].table, + user_bounds_copy_callback, state)) + return -1; + /* then fix bitmaps associated with those newly copied identifiers */ for (i = 0; i < SYM_NUM; i++) { if (fix_callback_f[i] != NULL && -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 3/3] Thread/Child-Domain Assignment (rev.3) 2008-08-05 5:55 ` [PATCH 3/3] " KaiGai Kohei @ 2008-08-06 10:13 ` KaiGai Kohei 2008-08-25 12:32 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) KaiGai Kohei 1 sibling, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-06 10:13 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux The following patch is the revised one for the libsepol. It is reworked to deliver entries of attributes into kernelspace. It enables to show boundary violation at policy load time in human readable form. [3/3] thread-context-libsepol.3.patch It enables to support binary policy format version 24. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> -- include/sepol/policydb/policydb.h | 21 +++++- src/expand.c | 132 ++++++++++++++++++++++++++++++-------- src/link.c | 108 ++++++++++++++++++++++++++++++- src/policydb.c | 80 +++++++++++++++++------ src/write.c | 15 ++++ 5 files changed, 305 insertions(+), 51 deletions(-) Index: libsepol/include/sepol/policydb/policydb.h =================================================================== --- libsepol/include/sepol/policydb/policydb.h (revision 2938) +++ libsepol/include/sepol/policydb/policydb.h (working copy) @@ -119,6 +119,7 @@ ebitmap_t dominates; /* set of roles dominated by this role */ type_set_t types; /* set of authorized types for role */ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ + uint32_t bounds; /* bounds role, if exist */ } role_datum_t; typedef struct role_trans { @@ -145,6 +146,7 @@ ebitmap_t types; /* types with this attribute */ #define TYPE_FLAGS_PERMISSIVE 0x01 uint32_t flags; + uint32_t bounds; /* bounds type, if exist */ } type_datum_t; /* User attributes */ @@ -156,6 +158,7 @@ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ mls_range_t exp_range; /* expanded range used for validation */ mls_level_t exp_dfltlevel; /* expanded range used for validation */ + uint32_t bounds; /* bounds user, if exist */ } user_datum_t; /* Sensitivity attributes */ @@ -595,10 +598,11 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY /* Module versions and specific changes*/ #define MOD_POLICYDB_VERSION_BASE 4 @@ -608,12 +612,25 @@ #define MOD_POLICYDB_VERSION_MLS_USERS 6 #define MOD_POLICYDB_VERSION_POLCAP 7 #define MOD_POLICYDB_VERSION_PERMISSIVE 8 +#define MOD_POLICYDB_VERSION_BOUNDARY 9 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_PERMISSIVE +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_BOUNDARY #define POLICYDB_CONFIG_MLS 1 +/* macros to check policy feature */ + +/* TODO: add other features here */ + +#define policydb_has_boundary_feature(p) \ + (((p)->policy_type == POLICY_KERN \ + && p->policyvers >= POLICYDB_VERSION_BOUNDARY) || \ + ((p)->policy_type != POLICY_KERN \ + && p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY)) + +#define POLICYDB_BOUNDS_ATTRIBUTE_FLAG 0xffffffff + /* the config flags related to unknown classes/perms are bits 2 and 3 */ #define DENY_UNKNOWN SEPOL_DENY_UNKNOWN #define REJECT_UNKNOWN SEPOL_REJECT_UNKNOWN Index: libsepol/src/policydb.c =================================================================== --- libsepol/src/policydb.c (revision 2938) +++ libsepol/src/policydb.c (working copy) @@ -110,6 +110,12 @@ .sym_num = SYM_NUM, .ocon_num = OCON_NODE6 + 1, }, + { + .type = POLICY_KERN, + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, { .type = POLICY_BASE, .version = MOD_POLICYDB_VERSION_BASE, @@ -141,6 +147,12 @@ .ocon_num = OCON_NODE6 + 1, }, { + .type = POLICY_BASE, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, + { .type = POLICY_MOD, .version = MOD_POLICYDB_VERSION_BASE, .sym_num = SYM_NUM, @@ -170,6 +182,12 @@ .sym_num = SYM_NUM, .ocon_num = 0 }, + { + .type = POLICY_MOD, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = 0 + }, }; #if 0 @@ -1855,20 +1873,25 @@ { char *key = 0; role_datum_t *role; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; role = calloc(1, sizeof(role_datum_t)); if (!role) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + role->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) @@ -1916,32 +1939,46 @@ { char *key = 0; type_datum_t *typdatum; - uint32_t buf[5]; + uint32_t buf[6]; size_t len; - int rc, to_read; + int rc, to_read, items = 0; typdatum = calloc(1, sizeof(type_datum_t)); if (!typdatum) return -1; - if (p->policy_type == POLICY_KERN) - to_read = 3; - else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - to_read = 5; - else - to_read = 4; + if (p->policy_type == POLICY_KERN) { + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 4; + else + to_read = 3; + } else { + if (p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY) + to_read = 6; + else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) + to_read = 5; + else + to_read = 4; + } rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; + len = le32_to_cpu(buf[items++]); + typdatum->s.value = le32_to_cpu(buf[items++]); + typdatum->primary = le32_to_cpu(buf[items++]); + if (policydb_has_boundary_feature(p)) { + uint32_t bounds = le32_to_cpu(buf[items++]); - len = le32_to_cpu(buf[0]); - typdatum->s.value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); + if (bounds == POLICYDB_BOUNDS_ATTRIBUTE_FLAG) + typdatum->flavor = TYPE_ATTRIB; + else + typdatum->bounds = bounds; + } if (p->policy_type != POLICY_KERN) { - typdatum->flavor = le32_to_cpu(buf[3]); + typdatum->flavor = le32_to_cpu(buf[items++]); if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - typdatum->flags = le32_to_cpu(buf[4]); + typdatum->flags = le32_to_cpu(buf[items++]); if (ebitmap_read(&typdatum->types, fp)) goto bad; } @@ -2293,20 +2330,25 @@ { char *key = 0; user_datum_t *usrdatum; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; usrdatum = calloc(1, sizeof(user_datum_t)); if (!usrdatum) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + usrdatum->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) Index: libsepol/src/expand.c =================================================================== --- libsepol/src/expand.c (revision 2938) +++ libsepol/src/expand.c (working copy) @@ -466,6 +466,100 @@ return 0; } +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_TYPES)) + return 0; + + bounds_val = state->typemap[type->bounds - 1]; + + dest = hashtab_search(state->out->p_types.table, (char *)key); + if (!dest) { + ERR(state->handle, "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_ROLES)) + return 0; + + bounds_val = state->rolemap[role->bounds - 1]; + + dest = hashtab_search(state->out->p_roles.table, (char *)key); + if (!dest) { + ERR(state->handle, "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_USERS)) + return 0; + + bounds_val = state->usermap[user->bounds - 1]; + + dest = hashtab_search(state->out->p_users.table, (char *)key); + if (!dest) { + ERR(state->handle, "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be certain that * the out symbol table will have the type that the alias refers. Otherwise, we * won't be able to find the type value for the alias. We can't depend on the @@ -1865,31 +1959,6 @@ return 0; } -static void type_destroy(hashtab_key_t key, hashtab_datum_t datum, void *p - __attribute__ ((unused))) -{ - free(key); - type_datum_destroy((type_datum_t *) datum); - free(datum); -} - -static int type_attr_remove(hashtab_key_t key - __attribute__ ((unused)), hashtab_datum_t datum, - void *args) -{ - type_datum_t *typdatum; - policydb_t *p; - - typdatum = (type_datum_t *) datum; - p = (policydb_t *) args; - if (typdatum->flavor == TYPE_ATTRIB) { - p->type_val_to_struct[typdatum->s.value - 1] = NULL; - p->p_type_val_to_name[typdatum->s.value - 1] = NULL; - return 1; - } - return 0; -} - /* converts typeset using typemap and expands into ebitmap_t types using the attributes in the passed in policy. * this should not be called until after all the blocks have been processed and the attributes in target policy * are complete. */ @@ -2393,6 +2462,11 @@ goto cleanup; } + /* copy type bounds */ + if (hashtab_map(state.base->p_types.table, + type_bounds_copy_callback, &state)) + goto cleanup; + /* copy aliases */ if (hashtab_map(state.base->p_types.table, alias_copy_callback, &state)) goto cleanup; @@ -2406,6 +2480,9 @@ /* copy roles */ if (hashtab_map(state.base->p_roles.table, role_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_roles.table, + role_bounds_copy_callback, &state)) + goto cleanup; /* copy MLS's sensitivity level and categories - this needs to be done * before expanding users (they need to be indexed too) */ @@ -2421,6 +2498,9 @@ /* copy users */ if (hashtab_map(state.base->p_users.table, user_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_users.table, + user_bounds_copy_callback, &state)) + goto cleanup; /* copy bools */ if (hashtab_map(state.base->p_bools.table, bool_copy_callback, &state)) @@ -2510,8 +2590,6 @@ } if (hashtab_map(state.out->p_types.table, type_attr_map, &state)) goto cleanup; - hashtab_map_remove_on_error(state.out->p_types.table, - type_attr_remove, type_destroy, state.out); if (check) { if (hierarchy_check_constraints(handle, state.out)) goto cleanup; Index: libsepol/src/write.c =================================================================== --- libsepol/src/write.c (revision 2938) +++ libsepol/src/write.c (working copy) @@ -920,6 +920,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(role->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(role->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; @@ -952,11 +954,22 @@ typdatum = (type_datum_t *) datum; + if (p->policy_type == POLICY_KERN + && p->policyvers < POLICYDB_VERSION_BOUNDARY + && typdatum->flavor == TYPE_ATTRIB) + return POLICYDB_SUCCESS; + len = strlen(key); items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(typdatum->s.value); buf[items++] = cpu_to_le32(typdatum->primary); + if (policydb_has_boundary_feature(p)) { + if (typdatum->flavor == TYPE_ATTRIB) + buf[items++] = cpu_to_le32(POLICYDB_BOUNDS_ATTRIBUTE_FLAG); + else + buf[items++] = cpu_to_le32(typdatum->bounds); + } if (p->policy_type != POLICY_KERN) { buf[items++] = cpu_to_le32(typdatum->flavor); if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) @@ -997,6 +1010,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(usrdatum->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(usrdatum->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; Index: libsepol/src/link.c =================================================================== --- libsepol/src/link.c (revision 2938) +++ libsepol/src/link.c (working copy) @@ -660,6 +660,97 @@ user_copy_callback, bool_copy_callback, sens_copy_callback, cat_copy_callback}; +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + bounds_val = state->cur->map[SYM_TYPES][type->bounds - 1]; + + dest = hashtab_search(state->base->p_types.table, key); + if (!dest) { + ERR(state->handle, + "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + bounds_val = state->cur->map[SYM_ROLES][role->bounds - 1]; + + dest = hashtab_search(state->base->p_roles.table, key); + if (!dest) { + ERR(state->handle, + "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + bounds_val = state->cur->map[SYM_USERS][user->bounds - 1]; + + dest = hashtab_search(state->base->p_users.table, key); + if (!dest) { + ERR(state->handle, + "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be * certain that the base symbol table will have the type that the * alias refers. Otherwise, we won't be able to find the type value @@ -1362,11 +1453,22 @@ } } - if (hashtab_map - (src_symtab[SYM_TYPES].table, alias_copy_callback, state)) { + if (hashtab_map(src_symtab[SYM_TYPES].table, + type_bounds_copy_callback, state)) return -1; - } + if (hashtab_map(src_symtab[SYM_TYPES].table, + alias_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_ROLES].table, + role_bounds_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_USERS].table, + user_bounds_copy_callback, state)) + return -1; + /* then fix bitmaps associated with those newly copied identifiers */ for (i = 0; i < SYM_NUM; i++) { if (fix_callback_f[i] != NULL && -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) 2008-08-05 5:55 ` [PATCH 3/3] " KaiGai Kohei 2008-08-06 10:13 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.3) KaiGai Kohei @ 2008-08-25 12:32 ` KaiGai Kohei 2008-08-28 15:51 ` Joshua Brindle 1 sibling, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-08-25 12:32 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux The following patch is revised one for libsepol. Updates: - The properties of type_datum are packed within the third word of type entries in the kernel policy. The first bit (TYPEDATUM_PROPERTY_PRIMARY) means the entry is a primary type, and the second bit (TYPEDATUM_PROPERTY_ATTRIBUTE) means the entry is an attribute. Thanks, Signed-off-by: KaiGai Kohei <kaigai@kaigai.gr.jp> -- include/sepol/policydb/policydb.h | 26 ++++++- src/expand.c | 132 ++++++++++++++++++++++++++++++-------- src/link.c | 108 ++++++++++++++++++++++++++++++- src/policydb.c | 89 +++++++++++++++++++------ src/write.c | 28 +++++++- 5 files changed, 329 insertions(+), 54 deletions(-) Index: libsepol/include/sepol/policydb/policydb.h =================================================================== --- libsepol/include/sepol/policydb/policydb.h (revision 2938) +++ libsepol/include/sepol/policydb/policydb.h (working copy) @@ -119,6 +119,7 @@ ebitmap_t dominates; /* set of roles dominated by this role */ type_set_t types; /* set of authorized types for role */ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ + uint32_t bounds; /* bounds role, if exist */ } role_datum_t; typedef struct role_trans { @@ -145,8 +146,16 @@ ebitmap_t types; /* types with this attribute */ #define TYPE_FLAGS_PERMISSIVE 0x01 uint32_t flags; + uint32_t bounds; /* bounds type, if exist */ } type_datum_t; +/* + * type_datum properties + * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY + */ +#define TYPEDATUM_PROPERTY_PRIMARY 0x0001 +#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 + /* User attributes */ typedef struct user_datum { symtab_datum_t s; @@ -156,6 +165,7 @@ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ mls_range_t exp_range; /* expanded range used for validation */ mls_level_t exp_dfltlevel; /* expanded range used for validation */ + uint32_t bounds; /* bounds user, if exist */ } user_datum_t; /* Sensitivity attributes */ @@ -595,10 +605,11 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY /* Module versions and specific changes*/ #define MOD_POLICYDB_VERSION_BASE 4 @@ -608,12 +619,23 @@ #define MOD_POLICYDB_VERSION_MLS_USERS 6 #define MOD_POLICYDB_VERSION_POLCAP 7 #define MOD_POLICYDB_VERSION_PERMISSIVE 8 +#define MOD_POLICYDB_VERSION_BOUNDARY 9 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_PERMISSIVE +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_BOUNDARY #define POLICYDB_CONFIG_MLS 1 +/* macros to check policy feature */ + +/* TODO: add other features here */ + +#define policydb_has_boundary_feature(p) \ + (((p)->policy_type == POLICY_KERN \ + && p->policyvers >= POLICYDB_VERSION_BOUNDARY) || \ + ((p)->policy_type != POLICY_KERN \ + && p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY)) + /* the config flags related to unknown classes/perms are bits 2 and 3 */ #define DENY_UNKNOWN SEPOL_DENY_UNKNOWN #define REJECT_UNKNOWN SEPOL_REJECT_UNKNOWN Index: libsepol/src/policydb.c =================================================================== --- libsepol/src/policydb.c (revision 2938) +++ libsepol/src/policydb.c (working copy) @@ -110,6 +110,12 @@ .sym_num = SYM_NUM, .ocon_num = OCON_NODE6 + 1, }, + { + .type = POLICY_KERN, + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, { .type = POLICY_BASE, .version = MOD_POLICYDB_VERSION_BASE, @@ -141,6 +147,12 @@ .ocon_num = OCON_NODE6 + 1, }, { + .type = POLICY_BASE, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, + { .type = POLICY_MOD, .version = MOD_POLICYDB_VERSION_BASE, .sym_num = SYM_NUM, @@ -170,6 +182,12 @@ .sym_num = SYM_NUM, .ocon_num = 0 }, + { + .type = POLICY_MOD, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = 0 + }, }; #if 0 @@ -1855,20 +1873,25 @@ { char *key = 0; role_datum_t *role; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; role = calloc(1, sizeof(role_datum_t)); if (!role) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + role->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) @@ -1916,32 +1939,53 @@ { char *key = 0; type_datum_t *typdatum; - uint32_t buf[5]; + uint32_t buf[6]; size_t len; - int rc, to_read; + int rc, to_read, items = 0; typdatum = calloc(1, sizeof(type_datum_t)); if (!typdatum) return -1; - if (p->policy_type == POLICY_KERN) - to_read = 3; - else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - to_read = 5; - else - to_read = 4; + if (p->policy_type == POLICY_KERN) { + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 4; + else + to_read = 3; + } else { + if (p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY) + to_read = 6; + else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) + to_read = 5; + else + to_read = 4; + } rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; + len = le32_to_cpu(buf[items++]); + typdatum->s.value = le32_to_cpu(buf[items++]); + if (p->policy_type == POLICY_KERN) { + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { + uint32_t prop = le32_to_cpu(buf[items++]); - len = le32_to_cpu(buf[0]); - typdatum->s.value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); - if (p->policy_type != POLICY_KERN) { - typdatum->flavor = le32_to_cpu(buf[3]); + if (prop & TYPEDATUM_PROPERTY_PRIMARY) + typdatum->primary = 1; + if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE) + typdatum->flavor = TYPE_ATTRIB; + + typdatum->bounds = le32_to_cpu(buf[items++]); + } else { + typdatum->primary = le32_to_cpu(buf[items++]); + } + } else { + typdatum->primary = le32_to_cpu(buf[items++]); + if (p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY) + typdatum->bounds = le32_to_cpu(buf[items++]); + typdatum->flavor = le32_to_cpu(buf[items++]); if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - typdatum->flags = le32_to_cpu(buf[4]); + typdatum->flags = le32_to_cpu(buf[items++]); if (ebitmap_read(&typdatum->types, fp)) goto bad; } @@ -2293,20 +2337,25 @@ { char *key = 0; user_datum_t *usrdatum; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; usrdatum = calloc(1, sizeof(user_datum_t)); if (!usrdatum) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + usrdatum->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) Index: libsepol/src/expand.c =================================================================== --- libsepol/src/expand.c (revision 2938) +++ libsepol/src/expand.c (working copy) @@ -466,6 +466,100 @@ return 0; } +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_TYPES)) + return 0; + + bounds_val = state->typemap[type->bounds - 1]; + + dest = hashtab_search(state->out->p_types.table, (char *)key); + if (!dest) { + ERR(state->handle, "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_ROLES)) + return 0; + + bounds_val = state->rolemap[role->bounds - 1]; + + dest = hashtab_search(state->out->p_roles.table, (char *)key); + if (!dest) { + ERR(state->handle, "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_USERS)) + return 0; + + bounds_val = state->usermap[user->bounds - 1]; + + dest = hashtab_search(state->out->p_users.table, (char *)key); + if (!dest) { + ERR(state->handle, "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be certain that * the out symbol table will have the type that the alias refers. Otherwise, we * won't be able to find the type value for the alias. We can't depend on the @@ -1865,31 +1959,6 @@ return 0; } -static void type_destroy(hashtab_key_t key, hashtab_datum_t datum, void *p - __attribute__ ((unused))) -{ - free(key); - type_datum_destroy((type_datum_t *) datum); - free(datum); -} - -static int type_attr_remove(hashtab_key_t key - __attribute__ ((unused)), hashtab_datum_t datum, - void *args) -{ - type_datum_t *typdatum; - policydb_t *p; - - typdatum = (type_datum_t *) datum; - p = (policydb_t *) args; - if (typdatum->flavor == TYPE_ATTRIB) { - p->type_val_to_struct[typdatum->s.value - 1] = NULL; - p->p_type_val_to_name[typdatum->s.value - 1] = NULL; - return 1; - } - return 0; -} - /* converts typeset using typemap and expands into ebitmap_t types using the attributes in the passed in policy. * this should not be called until after all the blocks have been processed and the attributes in target policy * are complete. */ @@ -2393,6 +2462,11 @@ goto cleanup; } + /* copy type bounds */ + if (hashtab_map(state.base->p_types.table, + type_bounds_copy_callback, &state)) + goto cleanup; + /* copy aliases */ if (hashtab_map(state.base->p_types.table, alias_copy_callback, &state)) goto cleanup; @@ -2406,6 +2480,9 @@ /* copy roles */ if (hashtab_map(state.base->p_roles.table, role_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_roles.table, + role_bounds_copy_callback, &state)) + goto cleanup; /* copy MLS's sensitivity level and categories - this needs to be done * before expanding users (they need to be indexed too) */ @@ -2421,6 +2498,9 @@ /* copy users */ if (hashtab_map(state.base->p_users.table, user_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_users.table, + user_bounds_copy_callback, &state)) + goto cleanup; /* copy bools */ if (hashtab_map(state.base->p_bools.table, bool_copy_callback, &state)) @@ -2510,8 +2590,6 @@ } if (hashtab_map(state.out->p_types.table, type_attr_map, &state)) goto cleanup; - hashtab_map_remove_on_error(state.out->p_types.table, - type_attr_remove, type_destroy, state.out); if (check) { if (hierarchy_check_constraints(handle, state.out)) goto cleanup; Index: libsepol/src/write.c =================================================================== --- libsepol/src/write.c (revision 2938) +++ libsepol/src/write.c (working copy) @@ -920,6 +920,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(role->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(role->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; @@ -952,12 +954,32 @@ typdatum = (type_datum_t *) datum; + if (p->policy_type == POLICY_KERN + && p->policyvers < POLICYDB_VERSION_BOUNDARY + && typdatum->flavor == TYPE_ATTRIB) + return POLICYDB_SUCCESS; + len = strlen(key); items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(typdatum->s.value); - buf[items++] = cpu_to_le32(typdatum->primary); - if (p->policy_type != POLICY_KERN) { + if (p->policy_type == POLICY_KERN) { + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { + uint32_t prop = 0; + + if (typdatum->primary) + prop |= TYPEDATUM_PROPERTY_PRIMARY; + if (typdatum->flavor == TYPE_ATTRIB) + prop |= TYPEDATUM_PROPERTY_ATTRIBUTE; + buf[items++] = cpu_to_le32(prop); + buf[items++] = cpu_to_le32(typdatum->bounds); + } else { + buf[items++] = cpu_to_le32(typdatum->primary); + } + } else { + buf[items++] = cpu_to_le32(typdatum->primary); + if (p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY) + buf[items++] = cpu_to_le32(typdatum->bounds); buf[items++] = cpu_to_le32(typdatum->flavor); if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) buf[items++] = cpu_to_le32(typdatum->flags); @@ -997,6 +1019,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(usrdatum->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(usrdatum->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; Index: libsepol/src/link.c =================================================================== --- libsepol/src/link.c (revision 2938) +++ libsepol/src/link.c (working copy) @@ -660,6 +660,97 @@ user_copy_callback, bool_copy_callback, sens_copy_callback, cat_copy_callback}; +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + bounds_val = state->cur->map[SYM_TYPES][type->bounds - 1]; + + dest = hashtab_search(state->base->p_types.table, key); + if (!dest) { + ERR(state->handle, + "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + bounds_val = state->cur->map[SYM_ROLES][role->bounds - 1]; + + dest = hashtab_search(state->base->p_roles.table, key); + if (!dest) { + ERR(state->handle, + "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + bounds_val = state->cur->map[SYM_USERS][user->bounds - 1]; + + dest = hashtab_search(state->base->p_users.table, key); + if (!dest) { + ERR(state->handle, + "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be * certain that the base symbol table will have the type that the * alias refers. Otherwise, we won't be able to find the type value @@ -1362,11 +1453,22 @@ } } - if (hashtab_map - (src_symtab[SYM_TYPES].table, alias_copy_callback, state)) { + if (hashtab_map(src_symtab[SYM_TYPES].table, + type_bounds_copy_callback, state)) return -1; - } + if (hashtab_map(src_symtab[SYM_TYPES].table, + alias_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_ROLES].table, + role_bounds_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_USERS].table, + user_bounds_copy_callback, state)) + return -1; + /* then fix bitmaps associated with those newly copied identifiers */ for (i = 0; i < SYM_NUM; i++) { if (fix_callback_f[i] != NULL && -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) 2008-08-25 12:32 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) KaiGai Kohei @ 2008-08-28 15:51 ` Joshua Brindle 2008-08-29 1:54 ` KaiGai Kohei 0 siblings, 1 reply; 97+ messages in thread From: Joshua Brindle @ 2008-08-28 15:51 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, jmorris, paul.moore, selinux KaiGai Kohei wrote: > The following patch is revised one for libsepol. > > Updates: > - The properties of type_datum are packed within the third word of > type entries in the kernel policy. > The first bit (TYPEDATUM_PROPERTY_PRIMARY) means the entry is a > primary type, and the second bit (TYPEDATUM_PROPERTY_ATTRIBUTE) > means the entry is an attribute. > I didn't see an answer in the current threads, and this looks like the latest userland patch series so I'll ask here: What is the purpose of the properties? We can infer what it knows from the other fields in the datum, right? One thing that bothers me is that we read prop out of the binary but it is nowhere in the type datum, I can see this leading to errors in the reader where a developer can't figure out where the extra data is coming from. I will echo Steve's request to remove the old hierarchy stuff from here and add in the implementation necessary to do validation of the bounds in libsepol. The rest of the patchset looks good, after reading through it (I haven't applied and tested it yet). > Thanks, > > Signed-off-by: KaiGai Kohei <kaigai@kaigai.gr.jp> > -- > include/sepol/policydb/policydb.h | 26 ++++++- > src/expand.c | 132 ++++++++++++++++++++++++++++++-------- > src/link.c | 108 ++++++++++++++++++++++++++++++- > src/policydb.c | 89 +++++++++++++++++++------ > src/write.c | 28 +++++++- > 5 files changed, 329 insertions(+), 54 deletions(-) > > Index: libsepol/include/sepol/policydb/policydb.h > =================================================================== > --- libsepol/include/sepol/policydb/policydb.h (revision 2938) > +++ libsepol/include/sepol/policydb/policydb.h (working copy) > @@ -119,6 +119,7 @@ > ebitmap_t dominates; /* set of roles dominated by this role */ > type_set_t types; /* set of authorized types for role */ > ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ > + uint32_t bounds; /* bounds role, if exist */ > } role_datum_t; > > typedef struct role_trans { > @@ -145,8 +146,16 @@ > ebitmap_t types; /* types with this attribute */ > #define TYPE_FLAGS_PERMISSIVE 0x01 > uint32_t flags; > + uint32_t bounds; /* bounds type, if exist */ > } type_datum_t; > > +/* > + * type_datum properties > + * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY > + */ > +#define TYPEDATUM_PROPERTY_PRIMARY 0x0001 > +#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 > + > /* User attributes */ > typedef struct user_datum { > symtab_datum_t s; > @@ -156,6 +165,7 @@ > ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ > mls_range_t exp_range; /* expanded range used for validation */ > mls_level_t exp_dfltlevel; /* expanded range used for validation */ > + uint32_t bounds; /* bounds user, if exist */ > } user_datum_t; > > /* Sensitivity attributes */ > @@ -595,10 +605,11 @@ > #define POLICYDB_VERSION_RANGETRANS 21 > #define POLICYDB_VERSION_POLCAP 22 > #define POLICYDB_VERSION_PERMISSIVE 23 > +#define POLICYDB_VERSION_BOUNDARY 24 > > /* Range of policy versions we understand*/ > #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE > -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE > +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY > > /* Module versions and specific changes*/ > #define MOD_POLICYDB_VERSION_BASE 4 > @@ -608,12 +619,23 @@ > #define MOD_POLICYDB_VERSION_MLS_USERS 6 > #define MOD_POLICYDB_VERSION_POLCAP 7 > #define MOD_POLICYDB_VERSION_PERMISSIVE 8 > +#define MOD_POLICYDB_VERSION_BOUNDARY 9 > > #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE > -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_PERMISSIVE > +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_BOUNDARY > > #define POLICYDB_CONFIG_MLS 1 > > +/* macros to check policy feature */ > + > +/* TODO: add other features here */ > + > +#define policydb_has_boundary_feature(p) \ > + (((p)->policy_type == POLICY_KERN \ > + && p->policyvers >= POLICYDB_VERSION_BOUNDARY) || \ > + ((p)->policy_type != POLICY_KERN \ > + && p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY)) > + > /* the config flags related to unknown classes/perms are bits 2 and 3 */ > #define DENY_UNKNOWN SEPOL_DENY_UNKNOWN > #define REJECT_UNKNOWN SEPOL_REJECT_UNKNOWN > Index: libsepol/src/policydb.c > =================================================================== > --- libsepol/src/policydb.c (revision 2938) > +++ libsepol/src/policydb.c (working copy) > @@ -110,6 +110,12 @@ > .sym_num = SYM_NUM, > .ocon_num = OCON_NODE6 + 1, > }, > + { > + .type = POLICY_KERN, > + .version = POLICYDB_VERSION_BOUNDARY, > + .sym_num = SYM_NUM, > + .ocon_num = OCON_NODE6 + 1, > + }, > { > .type = POLICY_BASE, > .version = MOD_POLICYDB_VERSION_BASE, > @@ -141,6 +147,12 @@ > .ocon_num = OCON_NODE6 + 1, > }, > { > + .type = POLICY_BASE, > + .version = MOD_POLICYDB_VERSION_BOUNDARY, > + .sym_num = SYM_NUM, > + .ocon_num = OCON_NODE6 + 1, > + }, > + { > .type = POLICY_MOD, > .version = MOD_POLICYDB_VERSION_BASE, > .sym_num = SYM_NUM, > @@ -170,6 +182,12 @@ > .sym_num = SYM_NUM, > .ocon_num = 0 > }, > + { > + .type = POLICY_MOD, > + .version = MOD_POLICYDB_VERSION_BOUNDARY, > + .sym_num = SYM_NUM, > + .ocon_num = 0 > + }, > }; > > #if 0 > @@ -1855,20 +1873,25 @@ > { > char *key = 0; > role_datum_t *role; > - uint32_t buf[2]; > + uint32_t buf[3]; > size_t len; > - int rc; > + int rc, to_read = 2; > > role = calloc(1, sizeof(role_datum_t)); > if (!role) > return -1; > > - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); > + if (policydb_has_boundary_feature(p)) > + to_read = 3; > + > + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); > if (rc < 0) > goto bad; > > len = le32_to_cpu(buf[0]); > role->s.value = le32_to_cpu(buf[1]); > + if (policydb_has_boundary_feature(p)) > + role->bounds = le32_to_cpu(buf[2]); > > key = malloc(len + 1); > if (!key) > @@ -1916,32 +1939,53 @@ > { > char *key = 0; > type_datum_t *typdatum; > - uint32_t buf[5]; > + uint32_t buf[6]; > size_t len; > - int rc, to_read; > + int rc, to_read, items = 0; > > typdatum = calloc(1, sizeof(type_datum_t)); > if (!typdatum) > return -1; > > - if (p->policy_type == POLICY_KERN) > - to_read = 3; > - else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) > - to_read = 5; > - else > - to_read = 4; > + if (p->policy_type == POLICY_KERN) { > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) > + to_read = 4; > + else > + to_read = 3; > + } else { > + if (p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY) > + to_read = 6; > + else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) > + to_read = 5; > + else > + to_read = 4; > + } > > rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); > if (rc < 0) > goto bad; > + len = le32_to_cpu(buf[items++]); > + typdatum->s.value = le32_to_cpu(buf[items++]); > + if (p->policy_type == POLICY_KERN) { > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { > + uint32_t prop = le32_to_cpu(buf[items++]); > > - len = le32_to_cpu(buf[0]); > - typdatum->s.value = le32_to_cpu(buf[1]); > - typdatum->primary = le32_to_cpu(buf[2]); > - if (p->policy_type != POLICY_KERN) { > - typdatum->flavor = le32_to_cpu(buf[3]); > + if (prop & TYPEDATUM_PROPERTY_PRIMARY) > + typdatum->primary = 1; > + if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE) > + typdatum->flavor = TYPE_ATTRIB; > + > + typdatum->bounds = le32_to_cpu(buf[items++]); > + } else { > + typdatum->primary = le32_to_cpu(buf[items++]); > + } > + } else { > + typdatum->primary = le32_to_cpu(buf[items++]); > + if (p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY) > + typdatum->bounds = le32_to_cpu(buf[items++]); > + typdatum->flavor = le32_to_cpu(buf[items++]); > if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) > - typdatum->flags = le32_to_cpu(buf[4]); > + typdatum->flags = le32_to_cpu(buf[items++]); > if (ebitmap_read(&typdatum->types, fp)) > goto bad; > } > @@ -2293,20 +2337,25 @@ > { > char *key = 0; > user_datum_t *usrdatum; > - uint32_t buf[2]; > + uint32_t buf[3]; > size_t len; > - int rc; > + int rc, to_read = 2; > > usrdatum = calloc(1, sizeof(user_datum_t)); > if (!usrdatum) > return -1; > > - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); > + if (policydb_has_boundary_feature(p)) > + to_read = 3; > + > + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); > if (rc < 0) > goto bad; > > len = le32_to_cpu(buf[0]); > usrdatum->s.value = le32_to_cpu(buf[1]); > + if (policydb_has_boundary_feature(p)) > + usrdatum->bounds = le32_to_cpu(buf[2]); > > key = malloc(len + 1); > if (!key) > Index: libsepol/src/expand.c > =================================================================== > --- libsepol/src/expand.c (revision 2938) > +++ libsepol/src/expand.c (working copy) > @@ -466,6 +466,100 @@ > return 0; > } > > +/* > + * The boundaries have to be copied after the types/roles/users are copied, > + * because it refers hashtab to lookup destinated objects. > + */ > +static int type_bounds_copy_callback(hashtab_key_t key, > + hashtab_datum_t datum, void *data) > +{ > + expand_state_t *state = (expand_state_t *) data; > + type_datum_t *type = (type_datum_t *) datum; > + type_datum_t *dest; > + uint32_t bounds_val; > + > + if (!type->bounds) > + return 0; > + > + if (!is_id_enabled((char *)key, state->base, SYM_TYPES)) > + return 0; > + > + bounds_val = state->typemap[type->bounds - 1]; > + > + dest = hashtab_search(state->out->p_types.table, (char *)key); > + if (!dest) { > + ERR(state->handle, "Type lookup failed for %s", (char *)key); > + return -1; > + } > + if (dest->bounds != 0 && dest->bounds != bounds_val) { > + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); > + return -1; > + } > + dest->bounds = bounds_val; > + > + return 0; > +} > + > +static int role_bounds_copy_callback(hashtab_key_t key, > + hashtab_datum_t datum, void *data) > +{ > + expand_state_t *state = (expand_state_t *) data; > + role_datum_t *role = (role_datum_t *) datum; > + role_datum_t *dest; > + uint32_t bounds_val; > + > + if (!role->bounds) > + return 0; > + > + if (!is_id_enabled((char *)key, state->base, SYM_ROLES)) > + return 0; > + > + bounds_val = state->rolemap[role->bounds - 1]; > + > + dest = hashtab_search(state->out->p_roles.table, (char *)key); > + if (!dest) { > + ERR(state->handle, "Role lookup failed for %s", (char *)key); > + return -1; > + } > + if (dest->bounds != 0 && dest->bounds != bounds_val) { > + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); > + return -1; > + } > + dest->bounds = bounds_val; > + > + return 0; > +} > + > +static int user_bounds_copy_callback(hashtab_key_t key, > + hashtab_datum_t datum, void *data) > +{ > + expand_state_t *state = (expand_state_t *) data; > + user_datum_t *user = (user_datum_t *) datum; > + user_datum_t *dest; > + uint32_t bounds_val; > + > + if (!user->bounds) > + return 0; > + > + if (!is_id_enabled((char *)key, state->base, SYM_USERS)) > + return 0; > + > + bounds_val = state->usermap[user->bounds - 1]; > + > + dest = hashtab_search(state->out->p_users.table, (char *)key); > + if (!dest) { > + ERR(state->handle, "User lookup failed for %s", (char *)key); > + return -1; > + } > + if (dest->bounds != 0 && dest->bounds != bounds_val) { > + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); > + return -1; > + } > + dest->bounds = bounds_val; > + > + return 0; > +} > + > /* The aliases have to be copied after the types and attributes to be certain that > * the out symbol table will have the type that the alias refers. Otherwise, we > * won't be able to find the type value for the alias. We can't depend on the > @@ -1865,31 +1959,6 @@ > return 0; > } > > -static void type_destroy(hashtab_key_t key, hashtab_datum_t datum, void *p > - __attribute__ ((unused))) > -{ > - free(key); > - type_datum_destroy((type_datum_t *) datum); > - free(datum); > -} > - > -static int type_attr_remove(hashtab_key_t key > - __attribute__ ((unused)), hashtab_datum_t datum, > - void *args) > -{ > - type_datum_t *typdatum; > - policydb_t *p; > - > - typdatum = (type_datum_t *) datum; > - p = (policydb_t *) args; > - if (typdatum->flavor == TYPE_ATTRIB) { > - p->type_val_to_struct[typdatum->s.value - 1] = NULL; > - p->p_type_val_to_name[typdatum->s.value - 1] = NULL; > - return 1; > - } > - return 0; > -} > - > /* converts typeset using typemap and expands into ebitmap_t types using the attributes in the passed in policy. > * this should not be called until after all the blocks have been processed and the attributes in target policy > * are complete. */ > @@ -2393,6 +2462,11 @@ > goto cleanup; > } > > + /* copy type bounds */ > + if (hashtab_map(state.base->p_types.table, > + type_bounds_copy_callback, &state)) > + goto cleanup; > + > /* copy aliases */ > if (hashtab_map(state.base->p_types.table, alias_copy_callback, &state)) > goto cleanup; > @@ -2406,6 +2480,9 @@ > /* copy roles */ > if (hashtab_map(state.base->p_roles.table, role_copy_callback, &state)) > goto cleanup; > + if (hashtab_map(state.base->p_roles.table, > + role_bounds_copy_callback, &state)) > + goto cleanup; > > /* copy MLS's sensitivity level and categories - this needs to be done > * before expanding users (they need to be indexed too) */ > @@ -2421,6 +2498,9 @@ > /* copy users */ > if (hashtab_map(state.base->p_users.table, user_copy_callback, &state)) > goto cleanup; > + if (hashtab_map(state.base->p_users.table, > + user_bounds_copy_callback, &state)) > + goto cleanup; > > /* copy bools */ > if (hashtab_map(state.base->p_bools.table, bool_copy_callback, &state)) > @@ -2510,8 +2590,6 @@ > } > if (hashtab_map(state.out->p_types.table, type_attr_map, &state)) > goto cleanup; > - hashtab_map_remove_on_error(state.out->p_types.table, > - type_attr_remove, type_destroy, state.out); > if (check) { > if (hierarchy_check_constraints(handle, state.out)) > goto cleanup; > Index: libsepol/src/write.c > =================================================================== > --- libsepol/src/write.c (revision 2938) > +++ libsepol/src/write.c (working copy) > @@ -920,6 +920,8 @@ > items = 0; > buf[items++] = cpu_to_le32(len); > buf[items++] = cpu_to_le32(role->s.value); > + if (policydb_has_boundary_feature(p)) > + buf[items++] = cpu_to_le32(role->bounds); > items2 = put_entry(buf, sizeof(uint32_t), items, fp); > if (items != items2) > return POLICYDB_ERROR; > @@ -952,12 +954,32 @@ > > typdatum = (type_datum_t *) datum; > > + if (p->policy_type == POLICY_KERN > + && p->policyvers < POLICYDB_VERSION_BOUNDARY > + && typdatum->flavor == TYPE_ATTRIB) > + return POLICYDB_SUCCESS; > + > len = strlen(key); > items = 0; > buf[items++] = cpu_to_le32(len); > buf[items++] = cpu_to_le32(typdatum->s.value); > - buf[items++] = cpu_to_le32(typdatum->primary); > - if (p->policy_type != POLICY_KERN) { > + if (p->policy_type == POLICY_KERN) { > + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { > + uint32_t prop = 0; > + > + if (typdatum->primary) > + prop |= TYPEDATUM_PROPERTY_PRIMARY; > + if (typdatum->flavor == TYPE_ATTRIB) > + prop |= TYPEDATUM_PROPERTY_ATTRIBUTE; > + buf[items++] = cpu_to_le32(prop); > + buf[items++] = cpu_to_le32(typdatum->bounds); > + } else { > + buf[items++] = cpu_to_le32(typdatum->primary); > + } > + } else { > + buf[items++] = cpu_to_le32(typdatum->primary); > + if (p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY) > + buf[items++] = cpu_to_le32(typdatum->bounds); > buf[items++] = cpu_to_le32(typdatum->flavor); > if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) > buf[items++] = cpu_to_le32(typdatum->flags); > @@ -997,6 +1019,8 @@ > items = 0; > buf[items++] = cpu_to_le32(len); > buf[items++] = cpu_to_le32(usrdatum->s.value); > + if (policydb_has_boundary_feature(p)) > + buf[items++] = cpu_to_le32(usrdatum->bounds); > items2 = put_entry(buf, sizeof(uint32_t), items, fp); > if (items != items2) > return POLICYDB_ERROR; > Index: libsepol/src/link.c > =================================================================== > --- libsepol/src/link.c (revision 2938) > +++ libsepol/src/link.c (working copy) > @@ -660,6 +660,97 @@ > user_copy_callback, bool_copy_callback, sens_copy_callback, > cat_copy_callback}; > > +/* > + * The boundaries have to be copied after the types/roles/users are copied, > + * because it refers hashtab to lookup destinated objects. > + */ > +static int type_bounds_copy_callback(hashtab_key_t key, > + hashtab_datum_t datum, void *data) > +{ > + link_state_t *state = (link_state_t *) data; > + type_datum_t *type = (type_datum_t *) datum; > + type_datum_t *dest; > + uint32_t bounds_val; > + > + if (!type->bounds) > + return 0; > + > + bounds_val = state->cur->map[SYM_TYPES][type->bounds - 1]; > + > + dest = hashtab_search(state->base->p_types.table, key); > + if (!dest) { > + ERR(state->handle, > + "Type lookup failed for %s", (char *)key); > + return -1; > + } > + if (dest->bounds != 0 && dest->bounds != bounds_val) { > + ERR(state->handle, > + "Inconsistent boundary for %s", (char *)key); > + return -1; > + } > + dest->bounds = bounds_val; > + > + return 0; > +} > + > +static int role_bounds_copy_callback(hashtab_key_t key, > + hashtab_datum_t datum, void *data) > +{ > + link_state_t *state = (link_state_t *) data; > + role_datum_t *role = (role_datum_t *) datum; > + role_datum_t *dest; > + uint32_t bounds_val; > + > + if (!role->bounds) > + return 0; > + > + bounds_val = state->cur->map[SYM_ROLES][role->bounds - 1]; > + > + dest = hashtab_search(state->base->p_roles.table, key); > + if (!dest) { > + ERR(state->handle, > + "Role lookup failed for %s", (char *)key); > + return -1; > + } > + if (dest->bounds != 0 && dest->bounds != bounds_val) { > + ERR(state->handle, > + "Inconsistent boundary for %s", (char *)key); > + return -1; > + } > + dest->bounds = bounds_val; > + > + return 0; > +} > + > +static int user_bounds_copy_callback(hashtab_key_t key, > + hashtab_datum_t datum, void *data) > +{ > + link_state_t *state = (link_state_t *) data; > + user_datum_t *user = (user_datum_t *) datum; > + user_datum_t *dest; > + uint32_t bounds_val; > + > + if (!user->bounds) > + return 0; > + > + bounds_val = state->cur->map[SYM_USERS][user->bounds - 1]; > + > + dest = hashtab_search(state->base->p_users.table, key); > + if (!dest) { > + ERR(state->handle, > + "User lookup failed for %s", (char *)key); > + return -1; > + } > + if (dest->bounds != 0 && dest->bounds != bounds_val) { > + ERR(state->handle, > + "Inconsistent boundary for %s", (char *)key); > + return -1; > + } > + dest->bounds = bounds_val; > + > + return 0; > +} > + > /* The aliases have to be copied after the types and attributes to be > * certain that the base symbol table will have the type that the > * alias refers. Otherwise, we won't be able to find the type value > @@ -1362,11 +1453,22 @@ > } > } > > - if (hashtab_map > - (src_symtab[SYM_TYPES].table, alias_copy_callback, state)) { > + if (hashtab_map(src_symtab[SYM_TYPES].table, > + type_bounds_copy_callback, state)) > return -1; > - } > > + if (hashtab_map(src_symtab[SYM_TYPES].table, > + alias_copy_callback, state)) > + return -1; > + > + if (hashtab_map(src_symtab[SYM_ROLES].table, > + role_bounds_copy_callback, state)) > + return -1; > + > + if (hashtab_map(src_symtab[SYM_USERS].table, > + user_bounds_copy_callback, state)) > + return -1; > + > /* then fix bitmaps associated with those newly copied identifiers */ > for (i = 0; i < SYM_NUM; i++) { > if (fix_callback_f[i] != NULL && > > -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) 2008-08-28 15:51 ` Joshua Brindle @ 2008-08-29 1:54 ` KaiGai Kohei 2008-08-29 3:01 ` Joshua Brindle 0 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-08-29 1:54 UTC (permalink / raw) To: Joshua Brindle; +Cc: Stephen Smalley, jmorris, paul.moore, selinux Joshua Brindle wrote: > KaiGai Kohei wrote: >> The following patch is revised one for libsepol. >> >> Updates: >> - The properties of type_datum are packed within the third word of >> type entries in the kernel policy. >> The first bit (TYPEDATUM_PROPERTY_PRIMARY) means the entry is a >> primary type, and the second bit (TYPEDATUM_PROPERTY_ATTRIBUTE) >> means the entry is an attribute. >> > > I didn't see an answer in the current threads, and this looks like > the latest userland patch series so I'll ask here: What is the purpose > of the properties? We can infer what it knows from the other fields > in the datum, right? Stephen said as follows: http://marc.info/?l=selinux&m=121882413424973&w=2 | Keeping the type attribute names in the types symtab in the kernel | policy allows tools like audit2why and apol to extract the original | attribute names and display them. I originally shed them because the | kernel didn't need to use that information and we don't want attribute | names to be used in security contexts, but it costs us little to save | them and check for them, and it benefits the tools. The purpose of property bits is to identify the sort of type_datum. The kernel policy version >= 24 allows to load attribute entries, so we have to distinguish them from existing type entries. Any type_datum entries of attributes were removed at the tail of expand_module in the older version, so we can assume any entries loaded to kernel space are primary or alias type. Because existing format of type_datum does not have any information to identify it to be an attribute, we had to add a flag bit to mark. > One thing that bothers me is that we read prop out of the binary but > it is nowhere in the type datum, I can see this leading to errors in > the reader where a developer can't figure out where the extra data is > coming from. (?_?) I'm not sure about your concern. The type_datum has the following format in the newer kernel policy: +0 +-------------------------+ | length of name | +4 +-------------------------+ | type identifier value | +8 +-------------------------+ | property bits | <--- TYPEDATUM_PROPERTY_PRIMARY +12 +-------------------------+ TYPEDATUM_PROPERTY_ATTRIBUTE | bounds type identifier | +16 +-------------------------+ | text representation of | | name (variable length) | +-------------------------+ Any property bits are packed within the third field. > I will echo Steve's request to remove the old hierarchy stuff from here > and add in the implementation necessary to do validation of the bounds > in libsepol. OK, please wait for a few days. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* RE: [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) 2008-08-29 1:54 ` KaiGai Kohei @ 2008-08-29 3:01 ` Joshua Brindle 2008-09-01 6:26 ` KaiGai Kohei 0 siblings, 1 reply; 97+ messages in thread From: Joshua Brindle @ 2008-08-29 3:01 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, jmorris, paul.moore, selinux KaiGai Kohei wrote: > Joshua Brindle wrote: >> KaiGai Kohei wrote: >>> The following patch is revised one for libsepol. >>> >>> Updates: >>> - The properties of type_datum are packed within the third word of >>> type entries in the kernel policy. >>> The first bit (TYPEDATUM_PROPERTY_PRIMARY) means the entry is a >>> primary type, and the second bit (TYPEDATUM_PROPERTY_ATTRIBUTE) >>> means the entry is an attribute. >>> >> >> I didn't see an answer in the current threads, and this looks like > > the latest userland patch series so I'll ask here: What is > the purpose > of the properties? We can infer what it knows > from the other fields >> in the datum, right? > > Stephen said as follows: > http://marc.info/?l=selinux&m=121882413424973&w=2 > >> Keeping the type attribute names in the types symtab in the kernel >> policy allows tools like audit2why and apol to extract the original >> attribute names and display them. I originally shed them because the >> kernel didn't need to use that information and we don't want >> attribute names to be used in security contexts, but it costs us >> little to save them and check for them, and it benefits the tools. > No, I got that part. What I was missing was that we identified attributes differently in modules and kernel policy now, kind of confusing. > The purpose of property bits is to identify the sort of type_datum. > The kernel policy version >= 24 allows to load attribute > entries, so we have to distinguish them from existing type entries. > > Any type_datum entries of attributes were removed at the tail > of expand_module in the older version, so we can assume any > entries loaded to kernel space are primary or alias type. > Because existing format of type_datum does not have any > information to identify it to be an attribute, we had to add > a flag bit to mark. > >> One thing that bothers me is that we read prop out of the binary but > > it is nowhere in the type datum, I can see this leading to > errors in > the reader where a developer can't figure out > where the extra data is > coming from. > > (?_?) I'm not sure about your concern. > > The type_datum has the following format in the newer kernel policy: > > +0 +-------------------------+ > | length of name | > +4 +-------------------------+ > | type identifier value | > +8 +-------------------------+ > | property bits | <--- TYPEDATUM_PROPERTY_PRIMARY > +12 +-------------------------+ TYPEDATUM_PROPERTY_ATTRIBUTE > | bounds type identifier | > +16 +-------------------------+ > | text representation of | > | name (variable length) | > +-------------------------+ > > Any property bits are packed within the third field. > I don't see that in the libsepol patch: @@ -145,8 +146,16 @@ ebitmap_t types; /* types with this attribute */ #define TYPE_FLAGS_PERMISSIVE 0x01 uint32_t flags; + uint32_t bounds; /* bounds type, if exist */ } type_datum_t; > >> I will echo Steve's request to remove the old hierarchy stuff from >> here > > and add in the implementation necessary to do validation > of the bounds > in libsepol. > > OK, please wait for a few days. > > Thanks, -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) 2008-08-29 3:01 ` Joshua Brindle @ 2008-09-01 6:26 ` KaiGai Kohei 2008-09-01 9:08 ` [PATCH] libsepol : Add support for a new policy version (POLICYDB_VERSION_BOUNDARY) KaiGai Kohei 2008-09-01 14:47 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) Joshua Brindle 0 siblings, 2 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-09-01 6:26 UTC (permalink / raw) To: Joshua Brindle; +Cc: Stephen Smalley, jmorris, paul.moore, selinux Joshua Brindle wrote: > KaiGai Kohei wrote: >> Joshua Brindle wrote: >>> KaiGai Kohei wrote: >>>> The following patch is revised one for libsepol. >>>> >>>> Updates: >>>> - The properties of type_datum are packed within the third word of >>>> type entries in the kernel policy. >>>> The first bit (TYPEDATUM_PROPERTY_PRIMARY) means the entry is a >>>> primary type, and the second bit (TYPEDATUM_PROPERTY_ATTRIBUTE) >>>> means the entry is an attribute. >>>> >>> I didn't see an answer in the current threads, and this looks like >> > the latest userland patch series so I'll ask here: What is >> the purpose > of the properties? We can infer what it knows >> from the other fields >>> in the datum, right? >> Stephen said as follows: >> http://marc.info/?l=selinux&m=121882413424973&w=2 >> >>> Keeping the type attribute names in the types symtab in the kernel >>> policy allows tools like audit2why and apol to extract the original >>> attribute names and display them. I originally shed them because the >>> kernel didn't need to use that information and we don't want >>> attribute names to be used in security contexts, but it costs us >>> little to save them and check for them, and it benefits the tools. > > No, I got that part. What I was missing was that we identified > attributes differently in modules and kernel policy now, kind of > confusing. I see, modular policy format now has "primary", "flavor" and "flags" members to identify its properties. It is different from newer kernel policy format. >> The type_datum has the following format in the newer kernel policy: >> >> +0 +-------------------------+ >> | length of name | >> +4 +-------------------------+ >> | type identifier value | >> +8 +-------------------------+ >> | property bits | <--- TYPEDATUM_PROPERTY_PRIMARY >> +12 +-------------------------+ TYPEDATUM_PROPERTY_ATTRIBUTE >> | bounds type identifier | >> +16 +-------------------------+ >> | text representation of | >> | name (variable length) | >> +-------------------------+ >> >> Any property bits are packed within the third field. >> > > I don't see that in the libsepol patch: > > @@ -145,8 +146,16 @@ > ebitmap_t types; /* types with this attribute */ > #define TYPE_FLAGS_PERMISSIVE 0x01 > uint32_t flags; > + uint32_t bounds; /* bounds type, if exist */ > } type_datum_t; Are you saying members within type_datum_t should have one-to-one mapping with its disk format? If so, it is the most straightforward approach to add a "flavor" member for the disk format of kernel policy. I guess your opinion is that smaller differences between kernel and modular policy format is better. If so, what do you think about an idea which packs all properties of a type into a property bits members. We need 1 bit for "primary" property, 2 bits for "flavor" and 1 bit for "permissive", so it is enough to store them into a 32bits variable. In this case, the format of type_datum is changed as follows: + 0 : length of name (unchanged) + 4 : type identifier value (unchanged) + 8 : property bits which can have following bits +12 : bounds type identifier (newly added) +16 : ebitmap of attributes (modular only/unchanged) +XX : text representation of type name (*) flavor in modular policy format is deprecated. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* [PATCH] libsepol : Add support for a new policy version (POLICYDB_VERSION_BOUNDARY) 2008-09-01 6:26 ` KaiGai Kohei @ 2008-09-01 9:08 ` KaiGai Kohei 2008-09-01 14:47 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) Joshua Brindle 1 sibling, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-09-01 9:08 UTC (permalink / raw) To: Joshua Brindle; +Cc: Stephen Smalley, jmorris, paul.moore, selinux The attached patch for libsepol add suport for a new policy version named as (MOD_)POLICYDB_VERSION_BOUNDARY. This version contains the following features. - Boundary feature support: The upcoming kernel has a feature to define boundary relationship between two users, roles and types. It enables to restrict a bounded one can never have wider permissions than its bounds one. Any XXXX_datum_t structure have "u32 bounds" member to indicate its bounds, and we can handle it with the latest version of policy format provided by this patch. - Loading attributes into kernel space: The upcoming kernel also allows to load entries of attribute. The attached patch turn off to drop them, when it tries to write kernel policy with its version is equal or greater than POLICYDB_VERSION_BOUNDARY. Any entries of attribute has a property of TYPEDATUM_PROPERTY_ATTRIBUTE. - Improvement of type_datum format on kernel/modular policy. The type_datum entry has several its attribute fields like "primary", "flavor" and "flags", and these are stored within separated fields on-disk format. This patch enables to pack them into a single field. Currently four bits are defined, and rest of them are reserved. #define TYPEDATUM_PROPERTY_PRIMARY 0x0001 #define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 #define TYPEDATUM_PROPERTY_ALIAS 0x0004 /* userspace only */ #define TYPEDATUM_PROPERTY_PERMISSIVE 0x0008 /* userspace only */ TODO: - Replace implementation of existing name based hierarchy checkings by the newer boundary feature. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> --- include/sepol/policydb/policydb.h | 28 +++++++- src/expand.c | 132 ++++++++++++++++++++++++++++++-------- src/link.c | 108 ++++++++++++++++++++++++++++++- src/policydb.c | 72 +++++++++++++++++--- src/write.c | 54 ++++++++++++--- 5 files changed, 342 insertions(+), 52 deletions(-) --- Index: libsepol/include/sepol/policydb/policydb.h =================================================================== --- libsepol/include/sepol/policydb/policydb.h (revision 2950) +++ libsepol/include/sepol/policydb/policydb.h (working copy) @@ -119,6 +119,7 @@ ebitmap_t dominates; /* set of roles dominated by this role */ type_set_t types; /* set of authorized types for role */ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ + uint32_t bounds; /* bounds role, if exist */ } role_datum_t; typedef struct role_trans { @@ -145,8 +146,18 @@ ebitmap_t types; /* types with this attribute */ #define TYPE_FLAGS_PERMISSIVE 0x01 uint32_t flags; + uint32_t bounds; /* bounds type, if exist */ } type_datum_t; +/* + * Properties of type_datum + * available on the policy version >= (MOD_)POLICYDB_VERSION_BOUNDARY + */ +#define TYPEDATUM_PROPERTY_PRIMARY 0x0001 +#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 +#define TYPEDATUM_PROPERTY_ALIAS 0x0004 /* userspace only */ +#define TYPEDATUM_PROPERTY_PERMISSIVE 0x0008 /* userspace only */ + /* User attributes */ typedef struct user_datum { symtab_datum_t s; @@ -156,6 +167,7 @@ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ mls_range_t exp_range; /* expanded range used for validation */ mls_level_t exp_dfltlevel; /* expanded range used for validation */ + uint32_t bounds; /* bounds user, if exist */ } user_datum_t; /* Sensitivity attributes */ @@ -595,10 +607,11 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY /* Module versions and specific changes*/ #define MOD_POLICYDB_VERSION_BASE 4 @@ -608,12 +621,23 @@ #define MOD_POLICYDB_VERSION_MLS_USERS 6 #define MOD_POLICYDB_VERSION_POLCAP 7 #define MOD_POLICYDB_VERSION_PERMISSIVE 8 +#define MOD_POLICYDB_VERSION_BOUNDARY 9 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_PERMISSIVE +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_BOUNDARY #define POLICYDB_CONFIG_MLS 1 +/* macros to check policy feature */ + +/* TODO: add other features here */ + +#define policydb_has_boundary_feature(p) \ + (((p)->policy_type == POLICY_KERN \ + && p->policyvers >= POLICYDB_VERSION_BOUNDARY) || \ + ((p)->policy_type != POLICY_KERN \ + && p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY)) + /* the config flags related to unknown classes/perms are bits 2 and 3 */ #define DENY_UNKNOWN SEPOL_DENY_UNKNOWN #define REJECT_UNKNOWN SEPOL_REJECT_UNKNOWN Index: libsepol/src/policydb.c =================================================================== --- libsepol/src/policydb.c (revision 2950) +++ libsepol/src/policydb.c (working copy) @@ -110,6 +110,12 @@ .sym_num = SYM_NUM, .ocon_num = OCON_NODE6 + 1, }, + { + .type = POLICY_KERN, + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, { .type = POLICY_BASE, .version = MOD_POLICYDB_VERSION_BASE, @@ -141,6 +147,12 @@ .ocon_num = OCON_NODE6 + 1, }, { + .type = POLICY_BASE, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, + { .type = POLICY_MOD, .version = MOD_POLICYDB_VERSION_BASE, .sym_num = SYM_NUM, @@ -170,6 +182,12 @@ .sym_num = SYM_NUM, .ocon_num = 0 }, + { + .type = POLICY_MOD, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = 0 + }, }; #if 0 @@ -1855,20 +1873,25 @@ { char *key = 0; role_datum_t *role; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; role = calloc(1, sizeof(role_datum_t)); if (!role) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + role->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) @@ -1924,7 +1947,9 @@ if (!typdatum) return -1; - if (p->policy_type == POLICY_KERN) + if (policydb_has_boundary_feature(p)) + to_read = 4; + else if (p->policy_type == POLICY_KERN) to_read = 3; else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) to_read = 5; @@ -1937,11 +1962,31 @@ len = le32_to_cpu(buf[0]); typdatum->s.value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); + if (policydb_has_boundary_feature(p)) { + uint32_t properties = le32_to_cpu(buf[2]); + + if (properties & TYPEDATUM_PROPERTY_PRIMARY) + typdatum->primary = 1; + if (properties & TYPEDATUM_PROPERTY_ATTRIBUTE) + typdatum->flavor = TYPE_ATTRIB; + if (properties & TYPEDATUM_PROPERTY_ALIAS + && p->policy_type != POLICY_KERN) + typdatum->flavor = TYPE_ALIAS; + if (properties & TYPEDATUM_PROPERTY_PERMISSIVE + && p->policy_type != POLICY_KERN) + typdatum->flags |= TYPE_FLAGS_PERMISSIVE; + + typdatum->bounds = le32_to_cpu(buf[3]); + } else { + typdatum->primary = le32_to_cpu(buf[2]); + if (p->policy_type != POLICY_KERN) { + typdatum->flavor = le32_to_cpu(buf[3]); + if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) + typdatum->flags = le32_to_cpu(buf[4]); + } + } + if (p->policy_type != POLICY_KERN) { - typdatum->flavor = le32_to_cpu(buf[3]); - if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - typdatum->flags = le32_to_cpu(buf[4]); if (ebitmap_read(&typdatum->types, fp)) goto bad; } @@ -2293,20 +2338,25 @@ { char *key = 0; user_datum_t *usrdatum; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; usrdatum = calloc(1, sizeof(user_datum_t)); if (!usrdatum) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + usrdatum->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) Index: libsepol/src/expand.c =================================================================== --- libsepol/src/expand.c (revision 2950) +++ libsepol/src/expand.c (working copy) @@ -466,6 +466,100 @@ return 0; } +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_TYPES)) + return 0; + + bounds_val = state->typemap[type->bounds - 1]; + + dest = hashtab_search(state->out->p_types.table, (char *)key); + if (!dest) { + ERR(state->handle, "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_ROLES)) + return 0; + + bounds_val = state->rolemap[role->bounds - 1]; + + dest = hashtab_search(state->out->p_roles.table, (char *)key); + if (!dest) { + ERR(state->handle, "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_USERS)) + return 0; + + bounds_val = state->usermap[user->bounds - 1]; + + dest = hashtab_search(state->out->p_users.table, (char *)key); + if (!dest) { + ERR(state->handle, "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be certain that * the out symbol table will have the type that the alias refers. Otherwise, we * won't be able to find the type value for the alias. We can't depend on the @@ -1865,31 +1959,6 @@ return 0; } -static void type_destroy(hashtab_key_t key, hashtab_datum_t datum, void *p - __attribute__ ((unused))) -{ - free(key); - type_datum_destroy((type_datum_t *) datum); - free(datum); -} - -static int type_attr_remove(hashtab_key_t key - __attribute__ ((unused)), hashtab_datum_t datum, - void *args) -{ - type_datum_t *typdatum; - policydb_t *p; - - typdatum = (type_datum_t *) datum; - p = (policydb_t *) args; - if (typdatum->flavor == TYPE_ATTRIB) { - p->type_val_to_struct[typdatum->s.value - 1] = NULL; - p->p_type_val_to_name[typdatum->s.value - 1] = NULL; - return 1; - } - return 0; -} - /* converts typeset using typemap and expands into ebitmap_t types using the attributes in the passed in policy. * this should not be called until after all the blocks have been processed and the attributes in target policy * are complete. */ @@ -2393,6 +2462,11 @@ goto cleanup; } + /* copy type bounds */ + if (hashtab_map(state.base->p_types.table, + type_bounds_copy_callback, &state)) + goto cleanup; + /* copy aliases */ if (hashtab_map(state.base->p_types.table, alias_copy_callback, &state)) goto cleanup; @@ -2406,6 +2480,9 @@ /* copy roles */ if (hashtab_map(state.base->p_roles.table, role_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_roles.table, + role_bounds_copy_callback, &state)) + goto cleanup; /* copy MLS's sensitivity level and categories - this needs to be done * before expanding users (they need to be indexed too) */ @@ -2421,6 +2498,9 @@ /* copy users */ if (hashtab_map(state.base->p_users.table, user_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_users.table, + user_bounds_copy_callback, &state)) + goto cleanup; /* copy bools */ if (hashtab_map(state.base->p_bools.table, bool_copy_callback, &state)) @@ -2510,8 +2590,6 @@ } if (hashtab_map(state.out->p_types.table, type_attr_map, &state)) goto cleanup; - hashtab_map_remove_on_error(state.out->p_types.table, - type_attr_remove, type_destroy, state.out); if (check) { if (hierarchy_check_constraints(handle, state.out)) goto cleanup; Index: libsepol/src/write.c =================================================================== --- libsepol/src/write.c (revision 2950) +++ libsepol/src/write.c (working copy) @@ -920,6 +920,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(role->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(role->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; @@ -952,19 +954,51 @@ typdatum = (type_datum_t *) datum; + /* + * The kernel policy version less than 24 (= POLICYDB_VERSION_BOUNDARY) + * does not support to load entries of attribute, so we skip to write it. + */ + if (p->policy_type == POLICY_KERN + && p->policyvers < POLICYDB_VERSION_BOUNDARY + && typdatum->flavor == TYPE_ATTRIB) + return POLICYDB_SUCCESS; + len = strlen(key); items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(typdatum->s.value); - buf[items++] = cpu_to_le32(typdatum->primary); - if (p->policy_type != POLICY_KERN) { - buf[items++] = cpu_to_le32(typdatum->flavor); - if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - buf[items++] = cpu_to_le32(typdatum->flags); - else if (typdatum->flags & TYPE_FLAGS_PERMISSIVE) - WARN(fp->handle, "Warning! Module policy version %d cannnot " - "support permissive types, but one was defined", - p->policyvers); + if (policydb_has_boundary_feature(p)) { + uint32_t properties = 0; + + if (typdatum->primary) + properties |= TYPEDATUM_PROPERTY_PRIMARY; + + if (typdatum->flavor == TYPE_ATTRIB) { + properties |= TYPEDATUM_PROPERTY_ATTRIBUTE; + } else if (typdatum->flavor == TYPE_ALIAS + && p->policy_type != POLICY_KERN) + properties |= TYPEDATUM_PROPERTY_ALIAS; + + if (typdatum->flags & TYPE_FLAGS_PERMISSIVE + && p->policy_type != POLICY_KERN) + properties |= TYPEDATUM_PROPERTY_PERMISSIVE; + + buf[items++] = cpu_to_le32(properties); + buf[items++] = cpu_to_le32(typdatum->bounds); + } else { + buf[items++] = cpu_to_le32(typdatum->primary); + + if (p->policy_type != POLICY_KERN) { + buf[items++] = cpu_to_le32(typdatum->flavor); + + if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) + buf[items++] = cpu_to_le32(typdatum->flags); + else if (typdatum->flags & TYPE_FLAGS_PERMISSIVE) + WARN(fp->handle, "Warning! Module policy " + "version %d cannnot suport permissive " + "types, but one was defined", + p->policyvers); + } } items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) @@ -997,6 +1031,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(usrdatum->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(usrdatum->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; Index: libsepol/src/link.c =================================================================== --- libsepol/src/link.c (revision 2950) +++ libsepol/src/link.c (working copy) @@ -660,6 +660,97 @@ user_copy_callback, bool_copy_callback, sens_copy_callback, cat_copy_callback}; +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + bounds_val = state->cur->map[SYM_TYPES][type->bounds - 1]; + + dest = hashtab_search(state->base->p_types.table, key); + if (!dest) { + ERR(state->handle, + "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + bounds_val = state->cur->map[SYM_ROLES][role->bounds - 1]; + + dest = hashtab_search(state->base->p_roles.table, key); + if (!dest) { + ERR(state->handle, + "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + bounds_val = state->cur->map[SYM_USERS][user->bounds - 1]; + + dest = hashtab_search(state->base->p_users.table, key); + if (!dest) { + ERR(state->handle, + "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be * certain that the base symbol table will have the type that the * alias refers. Otherwise, we won't be able to find the type value @@ -1362,11 +1453,22 @@ } } - if (hashtab_map - (src_symtab[SYM_TYPES].table, alias_copy_callback, state)) { + if (hashtab_map(src_symtab[SYM_TYPES].table, + type_bounds_copy_callback, state)) return -1; - } + if (hashtab_map(src_symtab[SYM_TYPES].table, + alias_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_ROLES].table, + role_bounds_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_USERS].table, + user_bounds_copy_callback, state)) + return -1; + /* then fix bitmaps associated with those newly copied identifiers */ for (i = 0; i < SYM_NUM; i++) { if (fix_callback_f[i] != NULL && -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* RE: [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) 2008-09-01 6:26 ` KaiGai Kohei 2008-09-01 9:08 ` [PATCH] libsepol : Add support for a new policy version (POLICYDB_VERSION_BOUNDARY) KaiGai Kohei @ 2008-09-01 14:47 ` Joshua Brindle 2008-09-01 16:11 ` KaiGai Kohei 1 sibling, 1 reply; 97+ messages in thread From: Joshua Brindle @ 2008-09-01 14:47 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, jmorris, paul.moore, selinux KaiGai Kohei wrote: > Joshua Brindle wrote: >> KaiGai Kohei wrote: >>> Joshua Brindle wrote: >>>> KaiGai Kohei wrote: >>>>> The following patch is revised one for libsepol. >>>>> >>>>> Updates: >>>>> - The properties of type_datum are packed within the third word >>>>> of type entries in the kernel policy. >>>>> The first bit (TYPEDATUM_PROPERTY_PRIMARY) means the entry is a >>>>> primary type, and the second bit (TYPEDATUM_PROPERTY_ATTRIBUTE) >>>>> means the entry is an attribute. >>>>> >>>> I didn't see an answer in the current threads, and this looks like >>> > the latest userland patch series so I'll ask here: What is the >>> purpose > of the properties? We can infer what it knows from the >>> other fields >>>> in the datum, right? >>> Stephen said as follows: >>> http://marc.info/?l=selinux&m=121882413424973&w=2 >>> >>>> Keeping the type attribute names in the types symtab in the kernel >>>> policy allows tools like audit2why and apol to extract the original >>>> attribute names and display them. I originally shed them because >>>> the kernel didn't need to use that information and we don't want >>>> attribute names to be used in security contexts, but it costs us >>>> little to save them and check for them, and it benefits the tools. >> >> No, I got that part. What I was missing was that we identified >> attributes differently in modules and kernel policy now, kind of >> confusing. > > I see, modular policy format now has "primary", "flavor" and "flags" > members to identify its properties. It is different from > newer kernel policy format. > >>> The type_datum has the following format in the newer kernel policy: >>> >>> +0 +-------------------------+ >>> | length of name | >>> +4 +-------------------------+ >>> | type identifier value | >>> +8 +-------------------------+ >>> | property bits | <--- TYPEDATUM_PROPERTY_PRIMARY >>> +12 +-------------------------+ TYPEDATUM_PROPERTY_ATTRIBUTE >>> | bounds type identifier | >>> +16 +-------------------------+ >>> | text representation of | >>> | name (variable length) | >>> +-------------------------+ >>> >>> Any property bits are packed within the third field. >>> >> >> I don't see that in the libsepol patch: >> >> @@ -145,8 +146,16 @@ >> ebitmap_t types; /* types with this attribute */ >> #define TYPE_FLAGS_PERMISSIVE 0x01 >> uint32_t flags; >> + uint32_t bounds; /* bounds type, if exist */ >> } type_datum_t; > > Are you saying members within type_datum_t should have > one-to-one mapping with its disk format? If so, it is the > most straightforward approach to add a "flavor" member for > the disk format of kernel policy. > No, don't add flavor to the kernel format. Add the property to the libsepol type_datum > I guess your opinion is that smaller differences between > kernel and modular policy format is better. If so, what do No, I'm fine with differences in those 2 formats. I just don't want to get confused later when the on-disk format has extra fields that the structs in libsepol don't have. > you think about an idea which packs all properties of a type > into a property bits members. > We need 1 bit for "primary" property, 2 bits for "flavor" and > 1 bit for "permissive", so it is enough to store them into a 32bits > variable. > > In this case, the format of type_datum is changed as follows: > > + 0 : length of name (unchanged) > + 4 : type identifier value (unchanged) > + 8 : property bits which can have following bits > +12 : bounds type identifier (newly added) > +16 : ebitmap of attributes (modular only/unchanged) > +XX : text representation of type name > (*) flavor in modular policy format is deprecated. > > Thanks, -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) 2008-09-01 14:47 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) Joshua Brindle @ 2008-09-01 16:11 ` KaiGai Kohei 2008-09-09 2:04 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei 0 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-09-01 16:11 UTC (permalink / raw) To: Joshua Brindle Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux >> I guess your opinion is that smaller differences between >> kernel and modular policy format is better. If so, what do > > No, I'm fine with differences in those 2 formats. I just don't want to > get confused later when the on-disk format has extra fields that the > structs in libsepol don't have. How do you think the approach on the latest libsepol patch? It integrates all fields to represent its properties ("flavor", "primary" and "flags") into a single field deployed on the third word of type_datum entry. These are encoded/decoded by type_read/type_write. I think an extra field like a new "properties" is unavoidable, because existing fields of properties are external specification of libsepol. So we cannot re-organize them arbitrarily. Thanks, -- KaiGai Kohei <kaigai@kaigai.gr.jp> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-09-01 16:11 ` KaiGai Kohei @ 2008-09-09 2:04 ` KaiGai Kohei 2008-09-12 18:17 ` Joshua Brindle 0 siblings, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-09-09 2:04 UTC (permalink / raw) To: Joshua Brindle, Stephen Smalley Cc: KaiGai Kohei, jmorris, paul.moore, selinux The attached patch for libsepol add suport for a new policy version named as (MOD_)POLICYDB_VERSION_BOUNDARY. Userspace hierarchy checks are reworked in this revision. FEATURES: - Boundary feature support: The upcoming kernel has a feature to define boundary relationship between two users, roles and types. It enables to restrict a bounded one can never have wider permissions than its bounds one. Any XXXX_datum_t structure have "u32 bounds" member to indicate its bounds, and we can handle it with the latest version of policy format provided by this patch. - Loading attributes into kernel space: The upcoming kernel also allows to load entries of attribute. The attached patch turn off to drop them, when it tries to write kernel policy with its version is equal or greater than POLICYDB_VERSION_BOUNDARY. Any entries of attribute has a property of TYPEDATUM_PROPERTY_ATTRIBUTE. - Improvement of type_datum format on kernel/modular policy. The type_datum entry has several its attribute fields like "primary", "flavor" and "flags", and these are stored within separated fields on-disk format. This patch enables to pack them into a single field. Currently four bits are defined, and rest of them are reserved. #define TYPEDATUM_PROPERTY_PRIMARY 0x0001 #define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 #define TYPEDATUM_PROPERTY_ALIAS 0x0004 /* userspace only */ #define TYPEDATUM_PROPERTY_PERMISSIVE 0x0008 /* userspace only */ - Hierarchy checks are reworked The existing userspace hierarchy checks are reworked for the upcoming boundary feature. It can handle parent one based on both newer bounds relationship and existing name-based hierarchy. In addition, I put a trick to evaluate conditional rules correctly. The following example shows a confusable case. A_t is the bounds of B_t, so B_t can never has wider permission than A_t. Example) allow B_t X_t : file { read_file_perms }; if (A_can_write_X) { allow A_t X_t : file { write_file_perms }; } else { allow A_t X_t : file { read_file_perms }; } A_t's permissions on X_t is depend on the 'A_can_write_X', however, a part of them, like 'read', are unconditionally allowed. If we can find common permission on both of true/false lists, these are pulled up to unconditional rules. Thus, B_t's read permission on X_t is not hierarchy violated in the above example. It also matches the upcoming kernel behavior no need to say. Thanks, Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> --- include/sepol/policydb/policydb.h | 28 ++ src/expand.c | 132 +++++++++-- src/hierarchy.c | 436 +++++++++++++++++++++----------------- src/link.c | 108 +++++++++ src/policydb.c | 72 +++++- src/write.c | 54 +++- 6 files changed, 585 insertions(+), 245 deletions(-) --- Index: libsepol/include/sepol/policydb/policydb.h =================================================================== --- libsepol/include/sepol/policydb/policydb.h (revision 2950) +++ libsepol/include/sepol/policydb/policydb.h (working copy) @@ -119,6 +119,7 @@ ebitmap_t dominates; /* set of roles dominated by this role */ type_set_t types; /* set of authorized types for role */ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ + uint32_t bounds; /* bounds role, if exist */ } role_datum_t; typedef struct role_trans { @@ -145,8 +146,18 @@ ebitmap_t types; /* types with this attribute */ #define TYPE_FLAGS_PERMISSIVE 0x01 uint32_t flags; + uint32_t bounds; /* bounds type, if exist */ } type_datum_t; +/* + * Properties of type_datum + * available on the policy version >= (MOD_)POLICYDB_VERSION_BOUNDARY + */ +#define TYPEDATUM_PROPERTY_PRIMARY 0x0001 +#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 +#define TYPEDATUM_PROPERTY_ALIAS 0x0004 /* userspace only */ +#define TYPEDATUM_PROPERTY_PERMISSIVE 0x0008 /* userspace only */ + /* User attributes */ typedef struct user_datum { symtab_datum_t s; @@ -156,6 +167,7 @@ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ mls_range_t exp_range; /* expanded range used for validation */ mls_level_t exp_dfltlevel; /* expanded range used for validation */ + uint32_t bounds; /* bounds user, if exist */ } user_datum_t; /* Sensitivity attributes */ @@ -595,10 +607,11 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY /* Module versions and specific changes*/ #define MOD_POLICYDB_VERSION_BASE 4 @@ -608,12 +621,23 @@ #define MOD_POLICYDB_VERSION_MLS_USERS 6 #define MOD_POLICYDB_VERSION_POLCAP 7 #define MOD_POLICYDB_VERSION_PERMISSIVE 8 +#define MOD_POLICYDB_VERSION_BOUNDARY 9 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_PERMISSIVE +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_BOUNDARY #define POLICYDB_CONFIG_MLS 1 +/* macros to check policy feature */ + +/* TODO: add other features here */ + +#define policydb_has_boundary_feature(p) \ + (((p)->policy_type == POLICY_KERN \ + && p->policyvers >= POLICYDB_VERSION_BOUNDARY) || \ + ((p)->policy_type != POLICY_KERN \ + && p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY)) + /* the config flags related to unknown classes/perms are bits 2 and 3 */ #define DENY_UNKNOWN SEPOL_DENY_UNKNOWN #define REJECT_UNKNOWN SEPOL_REJECT_UNKNOWN Index: libsepol/src/policydb.c =================================================================== --- libsepol/src/policydb.c (revision 2950) +++ libsepol/src/policydb.c (working copy) @@ -110,6 +110,12 @@ .sym_num = SYM_NUM, .ocon_num = OCON_NODE6 + 1, }, + { + .type = POLICY_KERN, + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, { .type = POLICY_BASE, .version = MOD_POLICYDB_VERSION_BASE, @@ -141,6 +147,12 @@ .ocon_num = OCON_NODE6 + 1, }, { + .type = POLICY_BASE, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, + { .type = POLICY_MOD, .version = MOD_POLICYDB_VERSION_BASE, .sym_num = SYM_NUM, @@ -170,6 +182,12 @@ .sym_num = SYM_NUM, .ocon_num = 0 }, + { + .type = POLICY_MOD, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = 0 + }, }; #if 0 @@ -1855,20 +1873,25 @@ { char *key = 0; role_datum_t *role; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; role = calloc(1, sizeof(role_datum_t)); if (!role) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + role->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) @@ -1924,7 +1947,9 @@ if (!typdatum) return -1; - if (p->policy_type == POLICY_KERN) + if (policydb_has_boundary_feature(p)) + to_read = 4; + else if (p->policy_type == POLICY_KERN) to_read = 3; else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) to_read = 5; @@ -1937,11 +1962,31 @@ len = le32_to_cpu(buf[0]); typdatum->s.value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); + if (policydb_has_boundary_feature(p)) { + uint32_t properties = le32_to_cpu(buf[2]); + + if (properties & TYPEDATUM_PROPERTY_PRIMARY) + typdatum->primary = 1; + if (properties & TYPEDATUM_PROPERTY_ATTRIBUTE) + typdatum->flavor = TYPE_ATTRIB; + if (properties & TYPEDATUM_PROPERTY_ALIAS + && p->policy_type != POLICY_KERN) + typdatum->flavor = TYPE_ALIAS; + if (properties & TYPEDATUM_PROPERTY_PERMISSIVE + && p->policy_type != POLICY_KERN) + typdatum->flags |= TYPE_FLAGS_PERMISSIVE; + + typdatum->bounds = le32_to_cpu(buf[3]); + } else { + typdatum->primary = le32_to_cpu(buf[2]); + if (p->policy_type != POLICY_KERN) { + typdatum->flavor = le32_to_cpu(buf[3]); + if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) + typdatum->flags = le32_to_cpu(buf[4]); + } + } + if (p->policy_type != POLICY_KERN) { - typdatum->flavor = le32_to_cpu(buf[3]); - if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - typdatum->flags = le32_to_cpu(buf[4]); if (ebitmap_read(&typdatum->types, fp)) goto bad; } @@ -2293,20 +2338,25 @@ { char *key = 0; user_datum_t *usrdatum; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; usrdatum = calloc(1, sizeof(user_datum_t)); if (!usrdatum) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + usrdatum->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) Index: libsepol/src/hierarchy.c =================================================================== --- libsepol/src/hierarchy.c (revision 2950) +++ libsepol/src/hierarchy.c (working copy) @@ -1,11 +1,16 @@ /* Authors: Joshua Brindle <jbrindle@tresys.com> * Jason Tang <jtang@tresys.com> * + * Updates: KaiGai Kohei <kaigai@ak.jp.nec.com> + * adds checks based on newer boundary facility. + * * A set of utility functions that aid policy decision when dealing * with hierarchal namespaces. * * Copyright (C) 2005 Tresys Technology, LLC * + * Copyright (c) 2008 NEC Corporation + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -41,36 +46,77 @@ int numerr; } hierarchy_args_t; -/* This merely returns the string part before the last '.' - * it does no verification of the existance of the parent - * in the policy, you must do this yourself. +/* + * find_parent_(type|role|user) * - * Caller must free parent after use. + * This function returns the parent datum of given XXX_datum_t + * object or NULL, if it doesn't exist. + * + * If the given datum has a valid bounds, this function merely + * returns the indicated object. Otherwise, it looks up the + * parent based on the based hierarchy. */ -static int find_parent(char *type, char **parent) +#define find_parent_template(prefix) \ +int find_parent_##prefix(hierarchy_args_t *a, \ + prefix##_datum_t *datum, \ + prefix##_datum_t **parent) \ +{ \ + char *parent_name, *datum_name, *tmp; \ + \ + if (datum->bounds) \ + *parent = a->p->prefix##_val_to_struct[datum->bounds - 1]; \ + else { \ + datum_name = a->p->p_##prefix##_val_to_name[datum->s.value - 1]; \ + \ + tmp = strrchr(datum_name, '.'); \ + /* no '.' means it has no parent */ \ + if (!tmp) { \ + *parent = NULL; \ + return 0; \ + } \ + \ + parent_name = strdup(datum_name); \ + if (!parent_name) \ + return -1; \ + parent_name[tmp - datum_name] = '\0'; \ + \ + *parent = hashtab_search(a->p->p_##prefix##s.table, parent_name); \ + if (!*parent) { \ + /* Orphan type/role/user */ \ + ERR(a->handle, \ + "%s doesn't exist, %s is an orphan", \ + parent_name, \ + a->p->p_##prefix##_val_to_name[datum->s.value - 1]); \ + free(parent_name); \ + return -1; \ + } \ + free(parent_name); \ + } \ + \ + return 0; \ +} + +static find_parent_template(type) +static find_parent_template(role) +static find_parent_template(user) + +static void compute_avtab_datum(hierarchy_args_t *args, + avtab_key_t *key, + avtab_datum_t *result) { - char *tmp; - int len; + avtab_datum_t *avdatp; + uint32_t av = 0; - assert(type); - - tmp = strrchr(type, '.'); - /* no '.' means it has no parent */ - if (!tmp) { - *parent = NULL; - return 0; + avdatp = avtab_search(args->expa, key); + if (avdatp) + av = avdatp->data; + if (args->opt_cond_list) { + avdatp = cond_av_list_search(key, args->opt_cond_list); + if (avdatp) + av |= avdatp->data; } - /* allocate buffer for part of string before the '.' */ - len = tmp - type; - *parent = (char *)malloc(sizeof(char) * (len + 1)); - - if (!(*parent)) - return -1; - memcpy(*parent, type, len); - (*parent)[len] = '\0'; - - return 0; + result->data = av; } /* This function verifies that the type passed in either has a parent or is in the @@ -79,41 +125,26 @@ static int check_type_hierarchy_callback(hashtab_key_t k, hashtab_datum_t d, void *args) { - char *parent; hierarchy_args_t *a; - type_datum_t *t, *t2; - char *key; + type_datum_t *t, *tp; a = (hierarchy_args_t *) args; t = (type_datum_t *) d; - key = (char *)k; if (t->flavor == TYPE_ATTRIB) { /* It's an attribute, we don't care */ return 0; } - - if (find_parent(key, &parent)) + if (find_parent_type(a, t, &tp) < 0) return -1; - if (!parent) { - /* This type is in the root namespace */ - return 0; - } - - t2 = hashtab_search(a->p->p_types.table, parent); - if (!t2) { - /* If the parent does not exist this type is an orphan, not legal */ - ERR(a->handle, "type %s does not exist, %s is an orphan", - parent, a->p->p_type_val_to_name[t->s.value - 1]); - a->numerr++; - } else if (t2->flavor == TYPE_ATTRIB) { + if (tp && tp->flavor == TYPE_ATTRIB) { /* The parent is an attribute but the child isn't, not legal */ - ERR(a->handle, "type %s is a child of an attribute", - a->p->p_type_val_to_name[t->s.value - 1]); + ERR(a->handle, "type %s is a child of an attribute %s", + (char *) k, a->p->p_type_val_to_name[tp->s.value - 1]); a->numerr++; + return -1; } - free(parent); return 0; } @@ -126,136 +157,176 @@ static int check_avtab_hierarchy_callback(avtab_key_t * k, avtab_datum_t * d, void *args) { - char *parent; avtab_key_t key; - avtab_datum_t *avdatump; - hierarchy_args_t *a; - uint32_t av = 0; - type_datum_t *t = NULL, *t2 = NULL; + hierarchy_args_t *a = (hierarchy_args_t *) args; + type_datum_t *s, *t1 = NULL, *t2 = NULL; + avtab_datum_t av; if (!(k->specified & AVTAB_ALLOWED)) { /* This is not an allow rule, no checking done */ return 0; } - a = (hierarchy_args_t *) args; - if (find_parent(a->p->p_type_val_to_name[k->source_type - 1], &parent)) + /* search for parent first */ + s = a->p->type_val_to_struct[k->source_type - 1]; + if (find_parent_type(a, s, &t1) < 0) return -1; - - /* search for parent first */ - if (parent) { - t = hashtab_search(a->p->p_types.table, parent); - if (!t) { - /* This error was already covered by type_check_hierarchy */ - free(parent); - return 0; - } - free(parent); - - key.source_type = t->s.value; + if (t1) { + /* + * search for access allowed between type 1's + * parent and type 2. + */ + key.source_type = t1->s.value; key.target_type = k->target_type; key.target_class = k->target_class; key.specified = AVTAB_ALLOWED; + compute_avtab_datum(a, &key, &av); - avdatump = avtab_search(a->expa, &key); - if (avdatump) { - /* search for access allowed between type 1's parent and type 2 */ - if ((avdatump->data & d->data) == d->data) { - return 0; - } - av = avdatump->data; - } - if (a->opt_cond_list) { - /* if a conditional list is present search it before continuing */ - avdatump = cond_av_list_search(&key, a->opt_cond_list); - if (avdatump) { - if (((av | avdatump->data) & d->data) == - d->data) { - return 0; - } - } - } + if ((av.data & d->data) == d->data) + return 0; } /* next we try type 1 and type 2's parent */ - if (find_parent(a->p->p_type_val_to_name[k->target_type - 1], &parent)) + s = a->p->type_val_to_struct[k->target_type - 1]; + if (find_parent_type(a, s, &t2) < 0) return -1; - - if (parent) { - t2 = hashtab_search(a->p->p_types.table, parent); - if (!t2) { - /* This error was already covered by type_check_hierarchy */ - free(parent); - return 0; - } - free(parent); - + if (t2) { + /* + * search for access allowed between type 1 and + * type 2's parent. + */ key.source_type = k->source_type; key.target_type = t2->s.value; key.target_class = k->target_class; key.specified = AVTAB_ALLOWED; + compute_avtab_datum(a, &key, &av); - avdatump = avtab_search(a->expa, &key); - if (avdatump) { - if ((avdatump->data & d->data) == d->data) { - return 0; - } - av = avdatump->data; - } - if (a->opt_cond_list) { - /* if a conditional list is present search it before continuing */ - avdatump = cond_av_list_search(&key, a->opt_cond_list); - if (avdatump) { - if (((av | avdatump->data) & d->data) == - d->data) { - return 0; - } - } - } + if ((av.data & d->data) == d->data) + return 0; } - if (t && t2) { - key.source_type = t->s.value; + if (t1 && t2) { + /* + * search for access allowed between type 1's parent + * and type 2's parent. + */ + key.source_type = t1->s.value; key.target_type = t2->s.value; key.target_class = k->target_class; key.specified = AVTAB_ALLOWED; + compute_avtab_datum(a, &key, &av); - avdatump = avtab_search(a->expa, &key); - if (avdatump) { - if ((avdatump->data & d->data) == d->data) { - return 0; - } - av = avdatump->data; - } - if (a->opt_cond_list) { - /* if a conditional list is present search it before continuing */ - avdatump = cond_av_list_search(&key, a->opt_cond_list); - if (avdatump) { - if (((av | avdatump->data) & d->data) == - d->data) { - return 0; - } - } - } + if ((av.data & d->data) == d->data) + return 0; } - if (!t && !t2) { - /* Neither one of these types have parents and - * therefore the hierarchical constraint does not apply */ + /* + * Neither one of these types have parents and + * therefore the hierarchical constraint does not apply + */ + if (!t1 && !t2) return 0; - } - /* At this point there is a violation of the hierarchal constraint, send error condition back */ + /* + * At this point there is a violation of the hierarchal + * constraint, send error condition back + */ ERR(a->handle, "hierarchy violation between types %s and %s : %s { %s }", a->p->p_type_val_to_name[k->source_type - 1], a->p->p_type_val_to_name[k->target_type - 1], a->p->p_class_val_to_name[k->target_class - 1], - sepol_av_to_string(a->p, k->target_class, d->data & ~av)); + sepol_av_to_string(a->p, k->target_class, d->data & ~av.data)); a->numerr++; return 0; } +/* + * If same permissions are allowed for same combination of + * source and target, we can evaluate them as unconditional + * one. + * See the following example. A_t type is bounds of B_t type, + * so B_t can never have wider permissions then A_t. + * A_t has conditional permission on X_t, however, a part of + * them (getattr and read) are unconditionaly allowed to A_t. + * + * Example) + * typebounds A_t B_t; + * + * allow B_t X_t : file { getattr }; + * if (foo_bool) { + * allow A_t X_t : file { getattr read }; + * } else { + * allow A_t X_t : file { getattr read write }; + * } + * + * We have to pull up them as unconditional ones in this case, + * because it seems to us B_t is violated to bounds constraints + * during unconditional policy checking. + */ +static int pullup_unconditional_perms(cond_list_t * cond_list, + hierarchy_args_t * args) +{ + cond_list_t *cur_node; + cond_av_list_t *cur_av, *expl_true = NULL, *expl_false = NULL; + avtab_t expa_true, expa_false; + avtab_datum_t *avdatp; + avtab_datum_t avdat; + avtab_ptr_t avnode; + + for (cur_node = cond_list; cur_node; cur_node = cur_node->next) { + if (avtab_init(&expa_true)) + goto oom0; + if (avtab_init(&expa_false)) + goto oom1; + if (expand_cond_av_list(args->p, cur_node->true_list, + &expl_true, &expa_true)) + goto oom2; + if (expand_cond_av_list(args->p, cur_node->false_list, + &expl_false, &expa_false)) + goto oom3; + for (cur_av = expl_true; cur_av; cur_av = cur_av->next) { + avdatp = avtab_search(&expa_false, + &cur_av->node->key); + if (!avdatp) + continue; + + avdat.data = (cur_av->node->datum.data + & avdatp->data); + if (!avdat.data) + continue; + + avnode = avtab_search_node(args->expa, + &cur_av->node->key); + if (avnode) { + avnode->datum.data |= avdat.data; + } else { + if (avtab_insert(args->expa, + &cur_av->node->key, + &avdat)) + goto oom4; + } + } + cond_av_list_destroy(expl_false); + cond_av_list_destroy(expl_true); + avtab_destroy(&expa_false); + avtab_destroy(&expa_true); + } + return 0; + +oom4: + cond_av_list_destroy(expl_false); +oom3: + cond_av_list_destroy(expl_true); +oom2: + avtab_destroy(&expa_false); +oom1: + avtab_destroy(&expa_true); +oom0: + ERR(args->handle, "out of memory on conditional av list expansion"); + return 1; +} + static int check_cond_avtab_hierarchy(cond_list_t * cond_list, hierarchy_args_t * args) { @@ -264,37 +335,51 @@ cond_av_list_t *cur_av, *expl = NULL; avtab_t expa; hierarchy_args_t *a = (hierarchy_args_t *) args; + avtab_datum_t avdat, *uncond; - for (cur_node = cond_list; cur_node != NULL; cur_node = cur_node->next) { + for (cur_node = cond_list; cur_node; cur_node = cur_node->next) { + /* + * Check true condition + */ if (avtab_init(&expa)) goto oom; - if (expand_cond_av_list - (args->p, cur_node->true_list, &expl, &expa)) { + if (expand_cond_av_list(args->p, cur_node->true_list, + &expl, &expa)) { avtab_destroy(&expa); goto oom; } args->opt_cond_list = expl; - for (cur_av = expl; cur_av != NULL; cur_av = cur_av->next) { + for (cur_av = expl; cur_av; cur_av = cur_av->next) { + avdat.data = cur_av->node->datum.data; + uncond = avtab_search(a->expa, &cur_av->node->key); + if (uncond) + avdat.data |= uncond->data; rc = check_avtab_hierarchy_callback(&cur_av->node->key, - &cur_av->node-> - datum, args); + &avdat, args); if (rc) - a->numerr++; + args->numerr++; } cond_av_list_destroy(expl); - avtab_destroy(&expa); + + /* + * Check false condition + */ if (avtab_init(&expa)) goto oom; - if (expand_cond_av_list - (args->p, cur_node->false_list, &expl, &expa)) { + if (expand_cond_av_list(args->p, cur_node->false_list, + &expl, &expa)) { avtab_destroy(&expa); goto oom; } args->opt_cond_list = expl; - for (cur_av = expl; cur_av != NULL; cur_av = cur_av->next) { + for (cur_av = expl; cur_av; cur_av = cur_av->next) { + avdat.data = cur_av->node->datum.data; + uncond = avtab_search(a->expa, &cur_av->node->key); + if (uncond) + avdat.data |= uncond->data; + rc = check_avtab_hierarchy_callback(&cur_av->node->key, - &cur_av->node-> - datum, args); + &avdat, args); if (rc) a->numerr++; } @@ -317,40 +402,21 @@ __attribute__ ((unused)), hashtab_datum_t d, void *args) { - char *parent; hierarchy_args_t *a; role_datum_t *r, *rp; a = (hierarchy_args_t *) args; r = (role_datum_t *) d; - if (find_parent(a->p->p_role_val_to_name[r->s.value - 1], &parent)) + if (find_parent_role(a, r, &rp) < 0) return -1; - if (!parent) { - /* This role has no parent */ - return 0; - } - - rp = hashtab_search(a->p->p_roles.table, parent); - if (!rp) { - /* Orphan role */ - ERR(a->handle, "role %s doesn't exist, %s is an orphan", - parent, a->p->p_role_val_to_name[r->s.value - 1]); - free(parent); - a->numerr++; - return 0; - } - - if (!ebitmap_contains(&rp->types.types, &r->types.types)) { - /* This is a violation of the hiearchal constraint, return error condition */ + if (rp && !ebitmap_contains(&rp->types.types, &r->types.types)) { + /* hierarchical constraint violation, return error */ ERR(a->handle, "Role hierarchy violation, %s exceeds %s", - a->p->p_role_val_to_name[r->s.value - 1], parent); + (char *) k, a->p->p_role_val_to_name[rp->s.value - 1]); a->numerr++; } - - free(parent); - return 0; } @@ -362,40 +428,21 @@ __attribute__ ((unused)), hashtab_datum_t d, void *args) { - char *parent; hierarchy_args_t *a; user_datum_t *u, *up; a = (hierarchy_args_t *) args; u = (user_datum_t *) d; - if (find_parent(a->p->p_user_val_to_name[u->s.value - 1], &parent)) + if (find_parent_user(a, u, &up) < 0) return -1; - if (!parent) { - /* This user has no parent */ - return 0; - } - - up = hashtab_search(a->p->p_users.table, parent); - if (!up) { - /* Orphan user */ - ERR(a->handle, "user %s doesn't exist, %s is an orphan", - parent, a->p->p_user_val_to_name[u->s.value - 1]); - free(parent); - a->numerr++; - return 0; - } - - if (!ebitmap_contains(&up->roles.roles, &u->roles.roles)) { + if (up && !ebitmap_contains(&up->roles.roles, &u->roles.roles)) { /* hierarchical constraint violation, return error */ ERR(a->handle, "User hierarchy violation, %s exceeds %s", - a->p->p_user_val_to_name[u->s.value - 1], parent); + (char *) k, a->p->p_user_val_to_name[up->s.value - 1]); a->numerr++; } - - free(parent); - return 0; } @@ -420,6 +467,9 @@ if (hashtab_map(p->p_types.table, check_type_hierarchy_callback, &args)) goto bad; + if (pullup_unconditional_perms(p->cond_list, &args)) + return -1; + if (avtab_map(&expa, check_avtab_hierarchy_callback, &args)) goto bad; Index: libsepol/src/expand.c =================================================================== --- libsepol/src/expand.c (revision 2950) +++ libsepol/src/expand.c (working copy) @@ -466,6 +466,100 @@ return 0; } +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_TYPES)) + return 0; + + bounds_val = state->typemap[type->bounds - 1]; + + dest = hashtab_search(state->out->p_types.table, (char *)key); + if (!dest) { + ERR(state->handle, "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_ROLES)) + return 0; + + bounds_val = state->rolemap[role->bounds - 1]; + + dest = hashtab_search(state->out->p_roles.table, (char *)key); + if (!dest) { + ERR(state->handle, "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_USERS)) + return 0; + + bounds_val = state->usermap[user->bounds - 1]; + + dest = hashtab_search(state->out->p_users.table, (char *)key); + if (!dest) { + ERR(state->handle, "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be certain that * the out symbol table will have the type that the alias refers. Otherwise, we * won't be able to find the type value for the alias. We can't depend on the @@ -1865,31 +1959,6 @@ return 0; } -static void type_destroy(hashtab_key_t key, hashtab_datum_t datum, void *p - __attribute__ ((unused))) -{ - free(key); - type_datum_destroy((type_datum_t *) datum); - free(datum); -} - -static int type_attr_remove(hashtab_key_t key - __attribute__ ((unused)), hashtab_datum_t datum, - void *args) -{ - type_datum_t *typdatum; - policydb_t *p; - - typdatum = (type_datum_t *) datum; - p = (policydb_t *) args; - if (typdatum->flavor == TYPE_ATTRIB) { - p->type_val_to_struct[typdatum->s.value - 1] = NULL; - p->p_type_val_to_name[typdatum->s.value - 1] = NULL; - return 1; - } - return 0; -} - /* converts typeset using typemap and expands into ebitmap_t types using the attributes in the passed in policy. * this should not be called until after all the blocks have been processed and the attributes in target policy * are complete. */ @@ -2393,6 +2462,11 @@ goto cleanup; } + /* copy type bounds */ + if (hashtab_map(state.base->p_types.table, + type_bounds_copy_callback, &state)) + goto cleanup; + /* copy aliases */ if (hashtab_map(state.base->p_types.table, alias_copy_callback, &state)) goto cleanup; @@ -2406,6 +2480,9 @@ /* copy roles */ if (hashtab_map(state.base->p_roles.table, role_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_roles.table, + role_bounds_copy_callback, &state)) + goto cleanup; /* copy MLS's sensitivity level and categories - this needs to be done * before expanding users (they need to be indexed too) */ @@ -2421,6 +2498,9 @@ /* copy users */ if (hashtab_map(state.base->p_users.table, user_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_users.table, + user_bounds_copy_callback, &state)) + goto cleanup; /* copy bools */ if (hashtab_map(state.base->p_bools.table, bool_copy_callback, &state)) @@ -2510,8 +2590,6 @@ } if (hashtab_map(state.out->p_types.table, type_attr_map, &state)) goto cleanup; - hashtab_map_remove_on_error(state.out->p_types.table, - type_attr_remove, type_destroy, state.out); if (check) { if (hierarchy_check_constraints(handle, state.out)) goto cleanup; Index: libsepol/src/write.c =================================================================== --- libsepol/src/write.c (revision 2950) +++ libsepol/src/write.c (working copy) @@ -920,6 +920,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(role->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(role->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; @@ -952,19 +954,51 @@ typdatum = (type_datum_t *) datum; + /* + * The kernel policy version less than 24 (= POLICYDB_VERSION_BOUNDARY) + * does not support to load entries of attribute, so we skip to write it. + */ + if (p->policy_type == POLICY_KERN + && p->policyvers < POLICYDB_VERSION_BOUNDARY + && typdatum->flavor == TYPE_ATTRIB) + return POLICYDB_SUCCESS; + len = strlen(key); items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(typdatum->s.value); - buf[items++] = cpu_to_le32(typdatum->primary); - if (p->policy_type != POLICY_KERN) { - buf[items++] = cpu_to_le32(typdatum->flavor); - if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - buf[items++] = cpu_to_le32(typdatum->flags); - else if (typdatum->flags & TYPE_FLAGS_PERMISSIVE) - WARN(fp->handle, "Warning! Module policy version %d cannnot " - "support permissive types, but one was defined", - p->policyvers); + if (policydb_has_boundary_feature(p)) { + uint32_t properties = 0; + + if (typdatum->primary) + properties |= TYPEDATUM_PROPERTY_PRIMARY; + + if (typdatum->flavor == TYPE_ATTRIB) { + properties |= TYPEDATUM_PROPERTY_ATTRIBUTE; + } else if (typdatum->flavor == TYPE_ALIAS + && p->policy_type != POLICY_KERN) + properties |= TYPEDATUM_PROPERTY_ALIAS; + + if (typdatum->flags & TYPE_FLAGS_PERMISSIVE + && p->policy_type != POLICY_KERN) + properties |= TYPEDATUM_PROPERTY_PERMISSIVE; + + buf[items++] = cpu_to_le32(properties); + buf[items++] = cpu_to_le32(typdatum->bounds); + } else { + buf[items++] = cpu_to_le32(typdatum->primary); + + if (p->policy_type != POLICY_KERN) { + buf[items++] = cpu_to_le32(typdatum->flavor); + + if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) + buf[items++] = cpu_to_le32(typdatum->flags); + else if (typdatum->flags & TYPE_FLAGS_PERMISSIVE) + WARN(fp->handle, "Warning! Module policy " + "version %d cannnot suport permissive " + "types, but one was defined", + p->policyvers); + } } items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) @@ -997,6 +1031,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(usrdatum->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(usrdatum->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; Index: libsepol/src/link.c =================================================================== --- libsepol/src/link.c (revision 2950) +++ libsepol/src/link.c (working copy) @@ -660,6 +660,97 @@ user_copy_callback, bool_copy_callback, sens_copy_callback, cat_copy_callback}; +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + bounds_val = state->cur->map[SYM_TYPES][type->bounds - 1]; + + dest = hashtab_search(state->base->p_types.table, key); + if (!dest) { + ERR(state->handle, + "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + bounds_val = state->cur->map[SYM_ROLES][role->bounds - 1]; + + dest = hashtab_search(state->base->p_roles.table, key); + if (!dest) { + ERR(state->handle, + "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + bounds_val = state->cur->map[SYM_USERS][user->bounds - 1]; + + dest = hashtab_search(state->base->p_users.table, key); + if (!dest) { + ERR(state->handle, + "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be * certain that the base symbol table will have the type that the * alias refers. Otherwise, we won't be able to find the type value @@ -1362,11 +1453,22 @@ } } - if (hashtab_map - (src_symtab[SYM_TYPES].table, alias_copy_callback, state)) { + if (hashtab_map(src_symtab[SYM_TYPES].table, + type_bounds_copy_callback, state)) return -1; - } + if (hashtab_map(src_symtab[SYM_TYPES].table, + alias_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_ROLES].table, + role_bounds_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_USERS].table, + user_bounds_copy_callback, state)) + return -1; + /* then fix bitmaps associated with those newly copied identifiers */ for (i = 0; i < SYM_NUM; i++) { if (fix_callback_f[i] != NULL && -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-09-09 2:04 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei @ 2008-09-12 18:17 ` Joshua Brindle 2008-09-12 23:20 ` KaiGai Kohei 0 siblings, 1 reply; 97+ messages in thread From: Joshua Brindle @ 2008-09-12 18:17 UTC (permalink / raw) To: KaiGai Kohei; +Cc: Stephen Smalley, KaiGai Kohei, jmorris, paul.moore, selinux KaiGai Kohei wrote: > The attached patch for libsepol add suport for a new policy version > named as (MOD_)POLICYDB_VERSION_BOUNDARY. > Userspace hierarchy checks are reworked in this revision. > > FEATURES: > > - Boundary feature support: > The upcoming kernel has a feature to define boundary relationship > between two users, roles and types. It enables to restrict a bounded > one can never have wider permissions than its bounds one. > Any XXXX_datum_t structure have "u32 bounds" member to indicate its > bounds, and we can handle it with the latest version of policy format > provided by this patch. > > - Loading attributes into kernel space: > The upcoming kernel also allows to load entries of attribute. > The attached patch turn off to drop them, when it tries to write > kernel policy with its version is equal or greater than > POLICYDB_VERSION_BOUNDARY. > Any entries of attribute has a property of TYPEDATUM_PROPERTY_ATTRIBUTE. > > - Improvement of type_datum format on kernel/modular policy. > The type_datum entry has several its attribute fields like "primary", > "flavor" and "flags", and these are stored within separated fields > on-disk format. This patch enables to pack them into a single field. > Currently four bits are defined, and rest of them are reserved. > #define TYPEDATUM_PROPERTY_PRIMARY 0x0001 > #define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 > #define TYPEDATUM_PROPERTY_ALIAS 0x0004 /* userspace only */ > #define TYPEDATUM_PROPERTY_PERMISSIVE 0x0008 /* userspace only */ > > - Hierarchy checks are reworked > The existing userspace hierarchy checks are reworked for the upcoming > boundary feature. It can handle parent one based on both newer bounds > relationship and existing name-based hierarchy. > > In addition, I put a trick to evaluate conditional rules correctly. > The following example shows a confusable case. A_t is the bounds of B_t, > so B_t can never has wider permission than A_t. > > Example) > allow B_t X_t : file { read_file_perms }; > if (A_can_write_X) { > allow A_t X_t : file { write_file_perms }; > } else { > allow A_t X_t : file { read_file_perms }; > } > > A_t's permissions on X_t is depend on the 'A_can_write_X', however, > a part of them, like 'read', are unconditionally allowed. > If we can find common permission on both of true/false lists, these > are pulled up to unconditional rules. > Thus, B_t's read permission on X_t is not hierarchy violated in the > above example. It also matches the upcoming kernel behavior no need > to say. > Was this the latest patch? I can't seem to apply it either to the latest git HEAD or to the last svn revision: [root@misterfreeze trunk]# patch -p0 --dry-run -F5< /root/selinux/patch patching file libsepol/include/sepol/policydb/policydb.h Hunk #1 succeeded at 119 with fuzz 3. Hunk #2 FAILED at 146. Hunk #3 succeeded at 167 with fuzz 3. Hunk #4 FAILED at 607. Hunk #5 FAILED at 621. 3 out of 5 hunks FAILED -- saving rejects to file libsepol/include/sepol/policydb/policydb.h.rej patching file libsepol/src/policydb.c Hunk #1 succeeded at 110 with fuzz 3. Hunk #2 succeeded at 147 with fuzz 3. Hunk #3 succeeded at 182 with fuzz 3. Hunk #4 FAILED at 1873. Hunk #5 succeeded at 1947 with fuzz 3. Hunk #6 FAILED at 1962. Hunk #7 FAILED at 2338. 3 out of 7 hunks FAILED -- saving rejects to file libsepol/src/policydb.c.rej patching file libsepol/src/hierarchy.c Hunk #1 FAILED at 1. Hunk #2 FAILED at 46. Hunk #3 FAILED at 125. Hunk #4 FAILED at 157. Hunk #5 FAILED at 335. Hunk #6 FAILED at 402. Hunk #7 FAILED at 428. Hunk #8 succeeded at 467 with fuzz 3. 7 out of 8 hunks FAILED -- saving rejects to file libsepol/src/hierarchy.c.rej patching file libsepol/src/expand.c Hunk #1 succeeded at 466 with fuzz 3. Hunk #2 succeeded at 1959 with fuzz 3. Hunk #3 succeeded at 2462 with fuzz 3. Hunk #4 succeeded at 2480 with fuzz 3. Hunk #5 succeeded at 2498 with fuzz 3. Hunk #6 succeeded at 2590 with fuzz 3. patching file libsepol/src/write.c Hunk #1 succeeded at 920 with fuzz 3. Hunk #2 FAILED at 954. Hunk #3 succeeded at 1031 with fuzz 3. 1 out of 3 hunks FAILED -- saving rejects to file libsepol/src/write.c.rej patching file libsepol/src/link.c Hunk #1 succeeded at 660 with fuzz 3. Hunk #2 FAILED at 1453. 1 out of 2 hunks FAILED -- saving rejects to file libsepol/src/link.c.rej -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-09-12 18:17 ` Joshua Brindle @ 2008-09-12 23:20 ` KaiGai Kohei 2008-09-15 13:44 ` Joshua Brindle 2008-09-30 14:00 ` Joshua Brindle 0 siblings, 2 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-09-12 23:20 UTC (permalink / raw) To: Joshua Brindle Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux [-- Attachment #1: Type: text/plain, Size: 4787 bytes --] Joshua Brindle wrote: > KaiGai Kohei wrote: >> The attached patch for libsepol add suport for a new policy version >> named as (MOD_)POLICYDB_VERSION_BOUNDARY. >> Userspace hierarchy checks are reworked in this revision. >> >> FEATURES: >> >> - Boundary feature support: >> The upcoming kernel has a feature to define boundary relationship >> between two users, roles and types. It enables to restrict a bounded >> one can never have wider permissions than its bounds one. >> Any XXXX_datum_t structure have "u32 bounds" member to indicate its >> bounds, and we can handle it with the latest version of policy format >> provided by this patch. >> >> - Loading attributes into kernel space: >> The upcoming kernel also allows to load entries of attribute. >> The attached patch turn off to drop them, when it tries to write >> kernel policy with its version is equal or greater than >> POLICYDB_VERSION_BOUNDARY. >> Any entries of attribute has a property of TYPEDATUM_PROPERTY_ATTRIBUTE. >> >> - Improvement of type_datum format on kernel/modular policy. >> The type_datum entry has several its attribute fields like "primary", >> "flavor" and "flags", and these are stored within separated fields >> on-disk format. This patch enables to pack them into a single field. >> Currently four bits are defined, and rest of them are reserved. >> #define TYPEDATUM_PROPERTY_PRIMARY 0x0001 >> #define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 >> #define TYPEDATUM_PROPERTY_ALIAS 0x0004 /* userspace only */ >> #define TYPEDATUM_PROPERTY_PERMISSIVE 0x0008 /* userspace only */ >> >> - Hierarchy checks are reworked >> The existing userspace hierarchy checks are reworked for the upcoming >> boundary feature. It can handle parent one based on both newer bounds >> relationship and existing name-based hierarchy. >> >> In addition, I put a trick to evaluate conditional rules correctly. >> The following example shows a confusable case. A_t is the bounds of B_t, >> so B_t can never has wider permission than A_t. >> >> Example) >> allow B_t X_t : file { read_file_perms }; >> if (A_can_write_X) { >> allow A_t X_t : file { write_file_perms }; >> } else { >> allow A_t X_t : file { read_file_perms }; >> } >> >> A_t's permissions on X_t is depend on the 'A_can_write_X', however, >> a part of them, like 'read', are unconditionally allowed. >> If we can find common permission on both of true/false lists, these >> are pulled up to unconditional rules. >> Thus, B_t's read permission on X_t is not hierarchy violated in the >> above example. It also matches the upcoming kernel behavior no need >> to say. >> > > Was this the latest patch? I can't seem to apply it either to the latest > git HEAD or to the last svn revision: Sorry, my Thunderbird translated any tabs into spaces. The patch is made based on the latest subversion repository. Can you apply the attached one correctly? Thanks, > [root@misterfreeze trunk]# patch -p0 --dry-run -F5< /root/selinux/patch > patching file libsepol/include/sepol/policydb/policydb.h > Hunk #1 succeeded at 119 with fuzz 3. > Hunk #2 FAILED at 146. > Hunk #3 succeeded at 167 with fuzz 3. > Hunk #4 FAILED at 607. > Hunk #5 FAILED at 621. > 3 out of 5 hunks FAILED -- saving rejects to file libsepol/include/sepol/policydb/policydb.h.rej > patching file libsepol/src/policydb.c > Hunk #1 succeeded at 110 with fuzz 3. > Hunk #2 succeeded at 147 with fuzz 3. > Hunk #3 succeeded at 182 with fuzz 3. > Hunk #4 FAILED at 1873. > Hunk #5 succeeded at 1947 with fuzz 3. > Hunk #6 FAILED at 1962. > Hunk #7 FAILED at 2338. > 3 out of 7 hunks FAILED -- saving rejects to file libsepol/src/policydb.c.rej > patching file libsepol/src/hierarchy.c > Hunk #1 FAILED at 1. > Hunk #2 FAILED at 46. > Hunk #3 FAILED at 125. > Hunk #4 FAILED at 157. > Hunk #5 FAILED at 335. > Hunk #6 FAILED at 402. > Hunk #7 FAILED at 428. > Hunk #8 succeeded at 467 with fuzz 3. > 7 out of 8 hunks FAILED -- saving rejects to file libsepol/src/hierarchy.c.rej > patching file libsepol/src/expand.c > Hunk #1 succeeded at 466 with fuzz 3. > Hunk #2 succeeded at 1959 with fuzz 3. > Hunk #3 succeeded at 2462 with fuzz 3. > Hunk #4 succeeded at 2480 with fuzz 3. > Hunk #5 succeeded at 2498 with fuzz 3. > Hunk #6 succeeded at 2590 with fuzz 3. > patching file libsepol/src/write.c > Hunk #1 succeeded at 920 with fuzz 3. > Hunk #2 FAILED at 954. > Hunk #3 succeeded at 1031 with fuzz 3. > 1 out of 3 hunks FAILED -- saving rejects to file libsepol/src/write.c.rej > patching file libsepol/src/link.c > Hunk #1 succeeded at 660 with fuzz 3. > Hunk #2 FAILED at 1453. > 1 out of 2 hunks FAILED -- saving rejects to file libsepol/src/link.c.rej > -- KaiGai Kohei <kaigai@kaigai.gr.jp> [-- Attachment #2: thread-context-libsepol.6.patch --] [-- Type: application/octect-stream, Size: 34663 bytes --] ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-09-12 23:20 ` KaiGai Kohei @ 2008-09-15 13:44 ` Joshua Brindle 2008-09-16 1:50 ` KaiGai Kohei 2008-09-30 14:00 ` Joshua Brindle 1 sibling, 1 reply; 97+ messages in thread From: Joshua Brindle @ 2008-09-15 13:44 UTC (permalink / raw) To: KaiGai Kohei; +Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux KaiGai Kohei wrote: > Joshua Brindle wrote: >> KaiGai Kohei wrote: >>> The attached patch for libsepol add suport for a new policy version >>> named as (MOD_)POLICYDB_VERSION_BOUNDARY. >>> Userspace hierarchy checks are reworked in this revision. >>> >>> FEATURES: >>> >>> - Boundary feature support: >>> The upcoming kernel has a feature to define boundary relationship >>> between two users, roles and types. It enables to restrict a bounded >>> one can never have wider permissions than its bounds one. >>> Any XXXX_datum_t structure have "u32 bounds" member to indicate its >>> bounds, and we can handle it with the latest version of policy format >>> provided by this patch. >>> >>> - Loading attributes into kernel space: >>> The upcoming kernel also allows to load entries of attribute. >>> The attached patch turn off to drop them, when it tries to write >>> kernel policy with its version is equal or greater than >>> POLICYDB_VERSION_BOUNDARY. >>> Any entries of attribute has a property of TYPEDATUM_PROPERTY_ATTRIBUTE. >>> >>> - Improvement of type_datum format on kernel/modular policy. >>> The type_datum entry has several its attribute fields like "primary", >>> "flavor" and "flags", and these are stored within separated fields >>> on-disk format. This patch enables to pack them into a single field. >>> Currently four bits are defined, and rest of them are reserved. >>> #define TYPEDATUM_PROPERTY_PRIMARY 0x0001 >>> #define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 >>> #define TYPEDATUM_PROPERTY_ALIAS 0x0004 /* userspace only */ >>> #define TYPEDATUM_PROPERTY_PERMISSIVE 0x0008 /* userspace only */ >>> >>> - Hierarchy checks are reworked >>> The existing userspace hierarchy checks are reworked for the upcoming >>> boundary feature. It can handle parent one based on both newer bounds >>> relationship and existing name-based hierarchy. >>> >>> In addition, I put a trick to evaluate conditional rules correctly. >>> The following example shows a confusable case. A_t is the bounds of B_t, >>> so B_t can never has wider permission than A_t. >>> >>> Example) >>> allow B_t X_t : file { read_file_perms }; >>> if (A_can_write_X) { >>> allow A_t X_t : file { write_file_perms }; >>> } else { >>> allow A_t X_t : file { read_file_perms }; >>> } >>> >>> A_t's permissions on X_t is depend on the 'A_can_write_X', however, >>> a part of them, like 'read', are unconditionally allowed. >>> If we can find common permission on both of true/false lists, these >>> are pulled up to unconditional rules. >>> Thus, B_t's read permission on X_t is not hierarchy violated in the >>> above example. It also matches the upcoming kernel behavior no need >>> to say. >>> >> Was this the latest patch? I can't seem to apply it either to the latest >> git HEAD or to the last svn revision: > > Sorry, my Thunderbird translated any tabs into spaces. > The patch is made based on the latest subversion repository. > Can you apply the attached one correctly? > Yes it applies. I'm going to have to track down that bug you reported before merging this because it prevents policy compilation. -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-09-15 13:44 ` Joshua Brindle @ 2008-09-16 1:50 ` KaiGai Kohei 0 siblings, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-09-16 1:50 UTC (permalink / raw) To: Joshua Brindle Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux Joshua Brindle wrote: > KaiGai Kohei wrote: >> Joshua Brindle wrote: >>> KaiGai Kohei wrote: >>>> The attached patch for libsepol add suport for a new policy version >>>> named as (MOD_)POLICYDB_VERSION_BOUNDARY. >>>> Userspace hierarchy checks are reworked in this revision. >>>> >>>> FEATURES: >>>> >>>> - Boundary feature support: >>>> The upcoming kernel has a feature to define boundary relationship >>>> between two users, roles and types. It enables to restrict a bounded >>>> one can never have wider permissions than its bounds one. >>>> Any XXXX_datum_t structure have "u32 bounds" member to indicate its >>>> bounds, and we can handle it with the latest version of policy format >>>> provided by this patch. >>>> >>>> - Loading attributes into kernel space: >>>> The upcoming kernel also allows to load entries of attribute. >>>> The attached patch turn off to drop them, when it tries to write >>>> kernel policy with its version is equal or greater than >>>> POLICYDB_VERSION_BOUNDARY. >>>> Any entries of attribute has a property of TYPEDATUM_PROPERTY_ATTRIBUTE. >>>> >>>> - Improvement of type_datum format on kernel/modular policy. >>>> The type_datum entry has several its attribute fields like "primary", >>>> "flavor" and "flags", and these are stored within separated fields >>>> on-disk format. This patch enables to pack them into a single field. >>>> Currently four bits are defined, and rest of them are reserved. >>>> #define TYPEDATUM_PROPERTY_PRIMARY 0x0001 >>>> #define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 >>>> #define TYPEDATUM_PROPERTY_ALIAS 0x0004 /* userspace only */ >>>> #define TYPEDATUM_PROPERTY_PERMISSIVE 0x0008 /* userspace only */ >>>> >>>> - Hierarchy checks are reworked >>>> The existing userspace hierarchy checks are reworked for the upcoming >>>> boundary feature. It can handle parent one based on both newer bounds >>>> relationship and existing name-based hierarchy. >>>> >>>> In addition, I put a trick to evaluate conditional rules correctly. >>>> The following example shows a confusable case. A_t is the bounds of B_t, >>>> so B_t can never has wider permission than A_t. >>>> >>>> Example) >>>> allow B_t X_t : file { read_file_perms }; >>>> if (A_can_write_X) { >>>> allow A_t X_t : file { write_file_perms }; >>>> } else { >>>> allow A_t X_t : file { read_file_perms }; >>>> } >>>> >>>> A_t's permissions on X_t is depend on the 'A_can_write_X', however, >>>> a part of them, like 'read', are unconditionally allowed. >>>> If we can find common permission on both of true/false lists, these >>>> are pulled up to unconditional rules. >>>> Thus, B_t's read permission on X_t is not hierarchy violated in the >>>> above example. It also matches the upcoming kernel behavior no need >>>> to say. >>>> >>> Was this the latest patch? I can't seem to apply it either to the latest >>> git HEAD or to the last svn revision: >> Sorry, my Thunderbird translated any tabs into spaces. >> The patch is made based on the latest subversion repository. >> Can you apply the attached one correctly? >> > > Yes it applies. I'm going to have to track down that bug you reported > before merging this because it prevents policy compilation. Note that the patch does not contain fixes for a bug I reported. IMO, alias types of invisible primay should be simply filtered at the expand_module(), or should generate an error. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-09-12 23:20 ` KaiGai Kohei 2008-09-15 13:44 ` Joshua Brindle @ 2008-09-30 14:00 ` Joshua Brindle 2008-10-01 7:53 ` KaiGai Kohei 1 sibling, 1 reply; 97+ messages in thread From: Joshua Brindle @ 2008-09-30 14:00 UTC (permalink / raw) To: KaiGai Kohei; +Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux KaiGai Kohei wrote: > Joshua Brindle wrote: >> KaiGai Kohei wrote: >>> The attached patch for libsepol add suport for a new policy version >>> named as (MOD_)POLICYDB_VERSION_BOUNDARY. >>> Userspace hierarchy checks are reworked in this revision. >>> I'm seeing a couple problems. First when writing out the policy it doesn't seem to respect policyvers, I told it to generate a version 23 and it still made a 24. Second it is failing to downgrade the 24 to 23 since my kernel doesn't support 24. >>> FEATURES: >>> >>> - Boundary feature support: >>> The upcoming kernel has a feature to define boundary relationship >>> between two users, roles and types. It enables to restrict a bounded >>> one can never have wider permissions than its bounds one. >>> Any XXXX_datum_t structure have "u32 bounds" member to indicate its >>> bounds, and we can handle it with the latest version of policy format >>> provided by this patch. >>> >>> - Loading attributes into kernel space: >>> The upcoming kernel also allows to load entries of attribute. >>> The attached patch turn off to drop them, when it tries to write >>> kernel policy with its version is equal or greater than >>> POLICYDB_VERSION_BOUNDARY. >>> Any entries of attribute has a property of TYPEDATUM_PROPERTY_ATTRIBUTE. >>> >>> - Improvement of type_datum format on kernel/modular policy. >>> The type_datum entry has several its attribute fields like "primary", >>> "flavor" and "flags", and these are stored within separated fields >>> on-disk format. This patch enables to pack them into a single field. >>> Currently four bits are defined, and rest of them are reserved. >>> #define TYPEDATUM_PROPERTY_PRIMARY 0x0001 >>> #define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 >>> #define TYPEDATUM_PROPERTY_ALIAS 0x0004 /* userspace only */ >>> #define TYPEDATUM_PROPERTY_PERMISSIVE 0x0008 /* userspace only */ >>> >>> - Hierarchy checks are reworked >>> The existing userspace hierarchy checks are reworked for the upcoming >>> boundary feature. It can handle parent one based on both newer bounds >>> relationship and existing name-based hierarchy. >>> >>> In addition, I put a trick to evaluate conditional rules correctly. >>> The following example shows a confusable case. A_t is the bounds of B_t, >>> so B_t can never has wider permission than A_t. >>> >>> Example) >>> allow B_t X_t : file { read_file_perms }; >>> if (A_can_write_X) { >>> allow A_t X_t : file { write_file_perms }; >>> } else { >>> allow A_t X_t : file { read_file_perms }; >>> } >>> >>> A_t's permissions on X_t is depend on the 'A_can_write_X', however, >>> a part of them, like 'read', are unconditionally allowed. >>> If we can find common permission on both of true/false lists, these >>> are pulled up to unconditional rules. >>> Thus, B_t's read permission on X_t is not hierarchy violated in the >>> above example. It also matches the upcoming kernel behavior no need >>> to say. >>> >> Was this the latest patch? I can't seem to apply it either to the latest >> git HEAD or to the last svn revision: > > Sorry, my Thunderbird translated any tabs into spaces. > The patch is made based on the latest subversion repository. > Can you apply the attached one correctly? > > Thanks, > >> [root@misterfreeze trunk]# patch -p0 --dry-run -F5< /root/selinux/patch >> patching file libsepol/include/sepol/policydb/policydb.h >> Hunk #1 succeeded at 119 with fuzz 3. >> Hunk #2 FAILED at 146. >> Hunk #3 succeeded at 167 with fuzz 3. >> Hunk #4 FAILED at 607. >> Hunk #5 FAILED at 621. >> 3 out of 5 hunks FAILED -- saving rejects to file libsepol/include/sepol/policydb/policydb.h.rej >> patching file libsepol/src/policydb.c >> Hunk #1 succeeded at 110 with fuzz 3. >> Hunk #2 succeeded at 147 with fuzz 3. >> Hunk #3 succeeded at 182 with fuzz 3. >> Hunk #4 FAILED at 1873. >> Hunk #5 succeeded at 1947 with fuzz 3. >> Hunk #6 FAILED at 1962. >> Hunk #7 FAILED at 2338. >> 3 out of 7 hunks FAILED -- saving rejects to file libsepol/src/policydb.c.rej >> patching file libsepol/src/hierarchy.c >> Hunk #1 FAILED at 1. >> Hunk #2 FAILED at 46. >> Hunk #3 FAILED at 125. >> Hunk #4 FAILED at 157. >> Hunk #5 FAILED at 335. >> Hunk #6 FAILED at 402. >> Hunk #7 FAILED at 428. >> Hunk #8 succeeded at 467 with fuzz 3. >> 7 out of 8 hunks FAILED -- saving rejects to file libsepol/src/hierarchy.c.rej >> patching file libsepol/src/expand.c >> Hunk #1 succeeded at 466 with fuzz 3. >> Hunk #2 succeeded at 1959 with fuzz 3. >> Hunk #3 succeeded at 2462 with fuzz 3. >> Hunk #4 succeeded at 2480 with fuzz 3. >> Hunk #5 succeeded at 2498 with fuzz 3. >> Hunk #6 succeeded at 2590 with fuzz 3. >> patching file libsepol/src/write.c >> Hunk #1 succeeded at 920 with fuzz 3. >> Hunk #2 FAILED at 954. >> Hunk #3 succeeded at 1031 with fuzz 3. >> 1 out of 3 hunks FAILED -- saving rejects to file libsepol/src/write.c.rej >> patching file libsepol/src/link.c >> Hunk #1 succeeded at 660 with fuzz 3. >> Hunk #2 FAILED at 1453. >> 1 out of 2 hunks FAILED -- saving rejects to file libsepol/src/link.c.rej >> > > -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-09-30 14:00 ` Joshua Brindle @ 2008-10-01 7:53 ` KaiGai Kohei 2008-10-01 19:56 ` Joshua Brindle 2008-10-04 23:30 ` Joshua Brindle 0 siblings, 2 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-10-01 7:53 UTC (permalink / raw) To: Joshua Brindle Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux Joshua Brindle wrote: > KaiGai Kohei wrote: >> Joshua Brindle wrote: >>> KaiGai Kohei wrote: >>>> The attached patch for libsepol add suport for a new policy version >>>> named as (MOD_)POLICYDB_VERSION_BOUNDARY. >>>> Userspace hierarchy checks are reworked in this revision. >>>> > > I'm seeing a couple problems. First when writing out the policy > it doesn't seem to respect policyvers, I told it to generate > a version 23 and it still made a 24. Are you saying a configuration of "policy-version = 23" at semanage.conf is ignored? I could not reproduce it in my environment. Could you tell me the steps to reproduce it? I injected several printf()'s, but it shows a proper policyvers which reflects semanage.conf correctly. > Second it is failing to downgrade the 24 to 23 since my kernel doesn't support 24. Also, please tell me the steps. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-10-01 7:53 ` KaiGai Kohei @ 2008-10-01 19:56 ` Joshua Brindle 2008-10-04 23:30 ` Joshua Brindle 1 sibling, 0 replies; 97+ messages in thread From: Joshua Brindle @ 2008-10-01 19:56 UTC (permalink / raw) To: KaiGai Kohei; +Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux KaiGai Kohei wrote: > Joshua Brindle wrote: >> KaiGai Kohei wrote: >>> Joshua Brindle wrote: >>>> KaiGai Kohei wrote: >>>>> The attached patch for libsepol add suport for a new policy version >>>>> named as (MOD_)POLICYDB_VERSION_BOUNDARY. >>>>> Userspace hierarchy checks are reworked in this revision. >>>>> >> I'm seeing a couple problems. First when writing out the policy >> it doesn't seem to respect policyvers, I told it to generate >> a version 23 and it still made a 24. > > Are you saying a configuration of "policy-version = 23" at semanage.conf > is ignored? I could not reproduce it in my environment. > Could you tell me the steps to reproduce it? > > I injected several printf()'s, but it shows a proper policyvers > which reflects semanage.conf correctly. > >> Second it is failing to downgrade the 24 to 23 since my kernel doesn't support 24. > Err, ok. I'm getting inconsistent results now: [root@misterfreeze policy]# semodule -B SELinux: Could not load policy file /etc/selinux/targeted/policy/policy.23: Invalid argument /usr/sbin/load_policy: Can't load policy: Invalid argument libsemanage.semanage_reload_policy: load_policy returned error code 2. I'm not sure what is causing it but it is a different problem than I had before, I'll investigate and see what is going on. -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-10-01 7:53 ` KaiGai Kohei 2008-10-01 19:56 ` Joshua Brindle @ 2008-10-04 23:30 ` Joshua Brindle 2008-10-06 9:19 ` KaiGai Kohei 2008-10-06 12:30 ` Stephen Smalley 1 sibling, 2 replies; 97+ messages in thread From: Joshua Brindle @ 2008-10-04 23:30 UTC (permalink / raw) To: KaiGai Kohei; +Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux KaiGai Kohei wrote: > Joshua Brindle wrote: >> KaiGai Kohei wrote: >>> Joshua Brindle wrote: >>>> KaiGai Kohei wrote: >>>>> The attached patch for libsepol add suport for a new policy version >>>>> named as (MOD_)POLICYDB_VERSION_BOUNDARY. >>>>> Userspace hierarchy checks are reworked in this revision. >>>>> >> I'm seeing a couple problems. First when writing out the policy >> it doesn't seem to respect policyvers, I told it to generate >> a version 23 and it still made a 24. > > Are you saying a configuration of "policy-version = 23" at semanage.conf > is ignored? I could not reproduce it in my environment. > Could you tell me the steps to reproduce it? > > I injected several printf()'s, but it shows a proper policyvers > which reflects semanage.conf correctly. > >> Second it is failing to downgrade the 24 to 23 since my kernel doesn't support 24. > I'm not sure why this wasn't happening to you but from what I can tell the new patch was returning from type_write when an attribute was passed in, however the length of the table was not updated. This caused policydb_read to read over the edge of the type symbol table, resulting in badness. Rather than trying to calculate the length without attributes I just removed the attribute check. This causes attributes to be written for all versions, but this should not cause any problems at all. Do you have a problem with this Stephen? index 6f1f655..d2c2c32 100644 --- a/libsepol/src/write.c +++ b/libsepol/src/write.c @@ -954,15 +954,6 @@ static int type_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) typdatum = (type_datum_t *) datum; - /* - * The kernel policy version less than 24 (= POLICYDB_VERSION_BOUNDARY) - * does not support to load entries of attribute, so we skip to write it. - */ - if (p->policy_type == POLICY_KERN - && p->policyvers < POLICYDB_VERSION_BOUNDARY - && typdatum->flavor == TYPE_ATTRIB) - return POLICYDB_SUCCESS; - len = strlen(key); items = 0; buf[items++] = cpu_to_le32(len); -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply related [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-10-04 23:30 ` Joshua Brindle @ 2008-10-06 9:19 ` KaiGai Kohei 2008-10-06 19:13 ` Joshua Brindle 2008-10-06 12:30 ` Stephen Smalley 1 sibling, 1 reply; 97+ messages in thread From: KaiGai Kohei @ 2008-10-06 9:19 UTC (permalink / raw) To: Joshua Brindle Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux Joshua Brindle wrote: > KaiGai Kohei wrote: >> Joshua Brindle wrote: >>> KaiGai Kohei wrote: >>>> Joshua Brindle wrote: >>>>> KaiGai Kohei wrote: >>>>>> The attached patch for libsepol add suport for a new policy version >>>>>> named as (MOD_)POLICYDB_VERSION_BOUNDARY. >>>>>> Userspace hierarchy checks are reworked in this revision. >>>>>> >>> I'm seeing a couple problems. First when writing out the policy >>> it doesn't seem to respect policyvers, I told it to generate >>> a version 23 and it still made a 24. >> Are you saying a configuration of "policy-version = 23" at semanage.conf >> is ignored? I could not reproduce it in my environment. >> Could you tell me the steps to reproduce it? >> >> I injected several printf()'s, but it shows a proper policyvers >> which reflects semanage.conf correctly. >> >>> Second it is failing to downgrade the 24 to 23 since my kernel doesn't support 24. > > I'm not sure why this wasn't happening to you but from what I can tell the new patch > was returning from type_write when an attribute was passed in, however the length of > the table was not updated. This caused policydb_read to read over the edge of the type > symbol table, resulting in badness. Hmm.... It seems to me what you pointed out is a bug of my patch. It prevents to deliver actual number of type/attribute symbols to policy file, but it is unclear why does it makes libsepol ignore the policyvers. (I guess it may be a separated matter.) > Rather than trying to calculate the length without attributes I just removed > the attribute check. This causes attributes to be written for all versions, > but this should not cause any problems at all. The reason why I injected such an ad-hoc code is that we cannot decide the policy version written when type_attr_remove() is invoked. Is it impossible to move it to policydb_write()? It is invoked after the policyvers is fixed by caller. Thanks, > Do you have a problem with this Stephen? > > index 6f1f655..d2c2c32 100644 > --- a/libsepol/src/write.c > +++ b/libsepol/src/write.c > @@ -954,15 +954,6 @@ static int type_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) > > typdatum = (type_datum_t *) datum; > > - /* > - * The kernel policy version less than 24 (= POLICYDB_VERSION_BOUNDARY) > - * does not support to load entries of attribute, so we skip to write it. > - */ > - if (p->policy_type == POLICY_KERN > - && p->policyvers < POLICYDB_VERSION_BOUNDARY > - && typdatum->flavor == TYPE_ATTRIB) > - return POLICYDB_SUCCESS; > - > len = strlen(key); > items = 0; > buf[items++] = cpu_to_le32(len); > > -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-10-06 9:19 ` KaiGai Kohei @ 2008-10-06 19:13 ` Joshua Brindle 2008-10-07 6:39 ` KaiGai Kohei 0 siblings, 1 reply; 97+ messages in thread From: Joshua Brindle @ 2008-10-06 19:13 UTC (permalink / raw) To: KaiGai Kohei; +Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux KaiGai Kohei wrote: > Joshua Brindle wrote: >> KaiGai Kohei wrote: >>> Joshua Brindle wrote: >>>> KaiGai Kohei wrote: >>>>> Joshua Brindle wrote: >>>>>> KaiGai Kohei wrote: >>>>>>> The attached patch for libsepol add suport for a new policy version >>>>>>> named as (MOD_)POLICYDB_VERSION_BOUNDARY. >>>>>>> Userspace hierarchy checks are reworked in this revision. >>>>>>> >>>> I'm seeing a couple problems. First when writing out the policy >>>> it doesn't seem to respect policyvers, I told it to generate >>>> a version 23 and it still made a 24. >>> Are you saying a configuration of "policy-version = 23" at semanage.conf >>> is ignored? I could not reproduce it in my environment. >>> Could you tell me the steps to reproduce it? >>> >>> I injected several printf()'s, but it shows a proper policyvers >>> which reflects semanage.conf correctly. >>> >>>> Second it is failing to downgrade the 24 to 23 since my kernel doesn't support 24. >> I'm not sure why this wasn't happening to you but from what I can tell the new patch >> was returning from type_write when an attribute was passed in, however the length of >> the table was not updated. This caused policydb_read to read over the edge of the type >> symbol table, resulting in badness. > > Hmm.... > It seems to me what you pointed out is a bug of my patch. It prevents to deliver > actual number of type/attribute symbols to policy file, but it is unclear why does > it makes libsepol ignore the policyvers. > (I guess it may be a separated matter.) > >> Rather than trying to calculate the length without attributes I just removed >> the attribute check. This causes attributes to be written for all versions, >> but this should not cause any problems at all. > > The reason why I injected such an ad-hoc code is that we cannot decide the policy > version written when type_attr_remove() is invoked. > Is it impossible to move it to policydb_write()? > It is invoked after the policyvers is fixed by caller. It isn't impossible. You are going to have to make it walk to type symbol table to calculate the length without attributes, then write that length instead of the total symtab length. -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-10-06 19:13 ` Joshua Brindle @ 2008-10-07 6:39 ` KaiGai Kohei 2008-10-09 15:30 ` Joshua Brindle 2008-10-09 17:11 ` Joshua Brindle 0 siblings, 2 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-10-07 6:39 UTC (permalink / raw) To: Joshua Brindle Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux [-- Attachment #1: Type: text/plain, Size: 3472 bytes --] >> Hmm.... >> It seems to me what you pointed out is a bug of my patch. It prevents to deliver >> actual number of type/attribute symbols to policy file, but it is unclear why does >> it makes libsepol ignore the policyvers. >> (I guess it may be a separated matter.) >> >>> Rather than trying to calculate the length without attributes I just removed >>> the attribute check. This causes attributes to be written for all versions, >>> but this should not cause any problems at all. >> The reason why I injected such an ad-hoc code is that we cannot decide the policy >> version written when type_attr_remove() is invoked. >> Is it impossible to move it to policydb_write()? >> It is invoked after the policyvers is fixed by caller. > > It isn't impossible. You are going to have to make it walk to type > symbol table to calculate the length without attributes, then write > that length instead of the total symtab length. The attached patch enables to fixup the number of type/attribute entries to be written. The type_attr_uncount() decrements the number of attribute entries skipped at type_write(). At first, I had a plan to invoke type_attr_remove() with hashtab_map_remove_on_error(), but it means the given policydb structure is modified at policydb_write() and implicit changes to external interface. Differences from the previous version are here: $ diff -NU3 thread-context-libsepol.6.patch thread-context-libsepol.7.patch --- thread-context-libsepol.6.patch 2008-09-09 10:24:41.000000000 +0900 +++ thread-context-libsepol.7.patch 2008-10-07 14:50:32.000000000 +0900 @@ -1086,6 +1086,45 @@ items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; +@@ -1515,6 +1551,19 @@ + return POLICYDB_SUCCESS; + } + ++static int type_attr_uncount(hashtab_key_t key __attribute__ ((unused)), ++ hashtab_datum_t datum, void *args) ++{ ++ type_datum_t *typdatum = datum; ++ uint32_t *p_nel = args; ++ ++ if (typdatum->flavor == TYPE_ATTRIB) { ++ /* uncount attribute from total number of types */ ++ (*p_nel)--; ++ } ++ return 0; ++} ++ + /* + * Write the configuration data in a policy database + * structure to a policy database binary representation +@@ -1646,6 +1695,18 @@ + for (i = 0; i < num_syms; i++) { + buf[0] = cpu_to_le32(p->symtab[i].nprim); + buf[1] = cpu_to_le32(p->symtab[i].table->nel); ++ ++ /* ++ * A special case when writing type/attribute symbol table. ++ * The kernel policy version less than 24 does not support ++ * to load entries of attribute, so we have to re-calculate ++ * the actual number of types except for attributes. ++ */ ++ if (i == SYM_TYPES && ++ p->policyvers < POLICYDB_VERSION_BOUNDARY && ++ p->policy_type == POLICY_KERN) { ++ hashtab_map(p->symtab[i].table, type_attr_uncount, &buf[1]); ++ } + items = put_entry(buf, sizeof(uint32_t), 2, fp); + if (items != 2) + return POLICYDB_ERROR; Index: libsepol/src/link.c =================================================================== --- libsepol/src/link.c (revision 2950) -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> [-- Attachment #2: thread-context-libsepol.7.patch --] [-- Type: text/x-patch, Size: 35844 bytes --] Index: libsepol/include/sepol/policydb/policydb.h =================================================================== --- libsepol/include/sepol/policydb/policydb.h (revision 2950) +++ libsepol/include/sepol/policydb/policydb.h (working copy) @@ -119,6 +119,7 @@ ebitmap_t dominates; /* set of roles dominated by this role */ type_set_t types; /* set of authorized types for role */ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ + uint32_t bounds; /* bounds role, if exist */ } role_datum_t; typedef struct role_trans { @@ -145,8 +146,18 @@ ebitmap_t types; /* types with this attribute */ #define TYPE_FLAGS_PERMISSIVE 0x01 uint32_t flags; + uint32_t bounds; /* bounds type, if exist */ } type_datum_t; +/* + * Properties of type_datum + * available on the policy version >= (MOD_)POLICYDB_VERSION_BOUNDARY + */ +#define TYPEDATUM_PROPERTY_PRIMARY 0x0001 +#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 +#define TYPEDATUM_PROPERTY_ALIAS 0x0004 /* userspace only */ +#define TYPEDATUM_PROPERTY_PERMISSIVE 0x0008 /* userspace only */ + /* User attributes */ typedef struct user_datum { symtab_datum_t s; @@ -156,6 +167,7 @@ ebitmap_t cache; /* This is an expanded set used for context validation during parsing */ mls_range_t exp_range; /* expanded range used for validation */ mls_level_t exp_dfltlevel; /* expanded range used for validation */ + uint32_t bounds; /* bounds user, if exist */ } user_datum_t; /* Sensitivity attributes */ @@ -595,10 +607,11 @@ #define POLICYDB_VERSION_RANGETRANS 21 #define POLICYDB_VERSION_POLCAP 22 #define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY /* Module versions and specific changes*/ #define MOD_POLICYDB_VERSION_BASE 4 @@ -608,12 +621,23 @@ #define MOD_POLICYDB_VERSION_MLS_USERS 6 #define MOD_POLICYDB_VERSION_POLCAP 7 #define MOD_POLICYDB_VERSION_PERMISSIVE 8 +#define MOD_POLICYDB_VERSION_BOUNDARY 9 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_PERMISSIVE +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_BOUNDARY #define POLICYDB_CONFIG_MLS 1 +/* macros to check policy feature */ + +/* TODO: add other features here */ + +#define policydb_has_boundary_feature(p) \ + (((p)->policy_type == POLICY_KERN \ + && p->policyvers >= POLICYDB_VERSION_BOUNDARY) || \ + ((p)->policy_type != POLICY_KERN \ + && p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY)) + /* the config flags related to unknown classes/perms are bits 2 and 3 */ #define DENY_UNKNOWN SEPOL_DENY_UNKNOWN #define REJECT_UNKNOWN SEPOL_REJECT_UNKNOWN Index: libsepol/src/policydb.c =================================================================== --- libsepol/src/policydb.c (revision 2950) +++ libsepol/src/policydb.c (working copy) @@ -110,6 +110,12 @@ .sym_num = SYM_NUM, .ocon_num = OCON_NODE6 + 1, }, + { + .type = POLICY_KERN, + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, { .type = POLICY_BASE, .version = MOD_POLICYDB_VERSION_BASE, @@ -141,6 +147,12 @@ .ocon_num = OCON_NODE6 + 1, }, { + .type = POLICY_BASE, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + }, + { .type = POLICY_MOD, .version = MOD_POLICYDB_VERSION_BASE, .sym_num = SYM_NUM, @@ -170,6 +182,12 @@ .sym_num = SYM_NUM, .ocon_num = 0 }, + { + .type = POLICY_MOD, + .version = MOD_POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = 0 + }, }; #if 0 @@ -1855,20 +1873,25 @@ { char *key = 0; role_datum_t *role; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; role = calloc(1, sizeof(role_datum_t)); if (!role) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + role->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) @@ -1924,7 +1947,9 @@ if (!typdatum) return -1; - if (p->policy_type == POLICY_KERN) + if (policydb_has_boundary_feature(p)) + to_read = 4; + else if (p->policy_type == POLICY_KERN) to_read = 3; else if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) to_read = 5; @@ -1937,11 +1962,31 @@ len = le32_to_cpu(buf[0]); typdatum->s.value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); + if (policydb_has_boundary_feature(p)) { + uint32_t properties = le32_to_cpu(buf[2]); + + if (properties & TYPEDATUM_PROPERTY_PRIMARY) + typdatum->primary = 1; + if (properties & TYPEDATUM_PROPERTY_ATTRIBUTE) + typdatum->flavor = TYPE_ATTRIB; + if (properties & TYPEDATUM_PROPERTY_ALIAS + && p->policy_type != POLICY_KERN) + typdatum->flavor = TYPE_ALIAS; + if (properties & TYPEDATUM_PROPERTY_PERMISSIVE + && p->policy_type != POLICY_KERN) + typdatum->flags |= TYPE_FLAGS_PERMISSIVE; + + typdatum->bounds = le32_to_cpu(buf[3]); + } else { + typdatum->primary = le32_to_cpu(buf[2]); + if (p->policy_type != POLICY_KERN) { + typdatum->flavor = le32_to_cpu(buf[3]); + if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) + typdatum->flags = le32_to_cpu(buf[4]); + } + } + if (p->policy_type != POLICY_KERN) { - typdatum->flavor = le32_to_cpu(buf[3]); - if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - typdatum->flags = le32_to_cpu(buf[4]); if (ebitmap_read(&typdatum->types, fp)) goto bad; } @@ -2293,20 +2338,25 @@ { char *key = 0; user_datum_t *usrdatum; - uint32_t buf[2]; + uint32_t buf[3]; size_t len; - int rc; + int rc, to_read = 2; usrdatum = calloc(1, sizeof(user_datum_t)); if (!usrdatum) return -1; - rc = next_entry(buf, fp, sizeof(uint32_t) * 2); + if (policydb_has_boundary_feature(p)) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(uint32_t) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->s.value = le32_to_cpu(buf[1]); + if (policydb_has_boundary_feature(p)) + usrdatum->bounds = le32_to_cpu(buf[2]); key = malloc(len + 1); if (!key) Index: libsepol/src/hierarchy.c =================================================================== --- libsepol/src/hierarchy.c (revision 2950) +++ libsepol/src/hierarchy.c (working copy) @@ -1,11 +1,16 @@ /* Authors: Joshua Brindle <jbrindle@tresys.com> * Jason Tang <jtang@tresys.com> * + * Updates: KaiGai Kohei <kaigai@ak.jp.nec.com> + * adds checks based on newer boundary facility. + * * A set of utility functions that aid policy decision when dealing * with hierarchal namespaces. * * Copyright (C) 2005 Tresys Technology, LLC * + * Copyright (c) 2008 NEC Corporation + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -41,36 +46,77 @@ int numerr; } hierarchy_args_t; -/* This merely returns the string part before the last '.' - * it does no verification of the existance of the parent - * in the policy, you must do this yourself. +/* + * find_parent_(type|role|user) * - * Caller must free parent after use. + * This function returns the parent datum of given XXX_datum_t + * object or NULL, if it doesn't exist. + * + * If the given datum has a valid bounds, this function merely + * returns the indicated object. Otherwise, it looks up the + * parent based on the based hierarchy. */ -static int find_parent(char *type, char **parent) +#define find_parent_template(prefix) \ +int find_parent_##prefix(hierarchy_args_t *a, \ + prefix##_datum_t *datum, \ + prefix##_datum_t **parent) \ +{ \ + char *parent_name, *datum_name, *tmp; \ + \ + if (datum->bounds) \ + *parent = a->p->prefix##_val_to_struct[datum->bounds - 1]; \ + else { \ + datum_name = a->p->p_##prefix##_val_to_name[datum->s.value - 1]; \ + \ + tmp = strrchr(datum_name, '.'); \ + /* no '.' means it has no parent */ \ + if (!tmp) { \ + *parent = NULL; \ + return 0; \ + } \ + \ + parent_name = strdup(datum_name); \ + if (!parent_name) \ + return -1; \ + parent_name[tmp - datum_name] = '\0'; \ + \ + *parent = hashtab_search(a->p->p_##prefix##s.table, parent_name); \ + if (!*parent) { \ + /* Orphan type/role/user */ \ + ERR(a->handle, \ + "%s doesn't exist, %s is an orphan", \ + parent_name, \ + a->p->p_##prefix##_val_to_name[datum->s.value - 1]); \ + free(parent_name); \ + return -1; \ + } \ + free(parent_name); \ + } \ + \ + return 0; \ +} + +static find_parent_template(type) +static find_parent_template(role) +static find_parent_template(user) + +static void compute_avtab_datum(hierarchy_args_t *args, + avtab_key_t *key, + avtab_datum_t *result) { - char *tmp; - int len; + avtab_datum_t *avdatp; + uint32_t av = 0; - assert(type); - - tmp = strrchr(type, '.'); - /* no '.' means it has no parent */ - if (!tmp) { - *parent = NULL; - return 0; + avdatp = avtab_search(args->expa, key); + if (avdatp) + av = avdatp->data; + if (args->opt_cond_list) { + avdatp = cond_av_list_search(key, args->opt_cond_list); + if (avdatp) + av |= avdatp->data; } - /* allocate buffer for part of string before the '.' */ - len = tmp - type; - *parent = (char *)malloc(sizeof(char) * (len + 1)); - - if (!(*parent)) - return -1; - memcpy(*parent, type, len); - (*parent)[len] = '\0'; - - return 0; + result->data = av; } /* This function verifies that the type passed in either has a parent or is in the @@ -79,41 +125,26 @@ static int check_type_hierarchy_callback(hashtab_key_t k, hashtab_datum_t d, void *args) { - char *parent; hierarchy_args_t *a; - type_datum_t *t, *t2; - char *key; + type_datum_t *t, *tp; a = (hierarchy_args_t *) args; t = (type_datum_t *) d; - key = (char *)k; if (t->flavor == TYPE_ATTRIB) { /* It's an attribute, we don't care */ return 0; } - - if (find_parent(key, &parent)) + if (find_parent_type(a, t, &tp) < 0) return -1; - if (!parent) { - /* This type is in the root namespace */ - return 0; - } - - t2 = hashtab_search(a->p->p_types.table, parent); - if (!t2) { - /* If the parent does not exist this type is an orphan, not legal */ - ERR(a->handle, "type %s does not exist, %s is an orphan", - parent, a->p->p_type_val_to_name[t->s.value - 1]); - a->numerr++; - } else if (t2->flavor == TYPE_ATTRIB) { + if (tp && tp->flavor == TYPE_ATTRIB) { /* The parent is an attribute but the child isn't, not legal */ - ERR(a->handle, "type %s is a child of an attribute", - a->p->p_type_val_to_name[t->s.value - 1]); + ERR(a->handle, "type %s is a child of an attribute %s", + (char *) k, a->p->p_type_val_to_name[tp->s.value - 1]); a->numerr++; + return -1; } - free(parent); return 0; } @@ -126,136 +157,176 @@ static int check_avtab_hierarchy_callback(avtab_key_t * k, avtab_datum_t * d, void *args) { - char *parent; avtab_key_t key; - avtab_datum_t *avdatump; - hierarchy_args_t *a; - uint32_t av = 0; - type_datum_t *t = NULL, *t2 = NULL; + hierarchy_args_t *a = (hierarchy_args_t *) args; + type_datum_t *s, *t1 = NULL, *t2 = NULL; + avtab_datum_t av; if (!(k->specified & AVTAB_ALLOWED)) { /* This is not an allow rule, no checking done */ return 0; } - a = (hierarchy_args_t *) args; - if (find_parent(a->p->p_type_val_to_name[k->source_type - 1], &parent)) + /* search for parent first */ + s = a->p->type_val_to_struct[k->source_type - 1]; + if (find_parent_type(a, s, &t1) < 0) return -1; - - /* search for parent first */ - if (parent) { - t = hashtab_search(a->p->p_types.table, parent); - if (!t) { - /* This error was already covered by type_check_hierarchy */ - free(parent); - return 0; - } - free(parent); - - key.source_type = t->s.value; + if (t1) { + /* + * search for access allowed between type 1's + * parent and type 2. + */ + key.source_type = t1->s.value; key.target_type = k->target_type; key.target_class = k->target_class; key.specified = AVTAB_ALLOWED; + compute_avtab_datum(a, &key, &av); - avdatump = avtab_search(a->expa, &key); - if (avdatump) { - /* search for access allowed between type 1's parent and type 2 */ - if ((avdatump->data & d->data) == d->data) { - return 0; - } - av = avdatump->data; - } - if (a->opt_cond_list) { - /* if a conditional list is present search it before continuing */ - avdatump = cond_av_list_search(&key, a->opt_cond_list); - if (avdatump) { - if (((av | avdatump->data) & d->data) == - d->data) { - return 0; - } - } - } + if ((av.data & d->data) == d->data) + return 0; } /* next we try type 1 and type 2's parent */ - if (find_parent(a->p->p_type_val_to_name[k->target_type - 1], &parent)) + s = a->p->type_val_to_struct[k->target_type - 1]; + if (find_parent_type(a, s, &t2) < 0) return -1; - - if (parent) { - t2 = hashtab_search(a->p->p_types.table, parent); - if (!t2) { - /* This error was already covered by type_check_hierarchy */ - free(parent); - return 0; - } - free(parent); - + if (t2) { + /* + * search for access allowed between type 1 and + * type 2's parent. + */ key.source_type = k->source_type; key.target_type = t2->s.value; key.target_class = k->target_class; key.specified = AVTAB_ALLOWED; + compute_avtab_datum(a, &key, &av); - avdatump = avtab_search(a->expa, &key); - if (avdatump) { - if ((avdatump->data & d->data) == d->data) { - return 0; - } - av = avdatump->data; - } - if (a->opt_cond_list) { - /* if a conditional list is present search it before continuing */ - avdatump = cond_av_list_search(&key, a->opt_cond_list); - if (avdatump) { - if (((av | avdatump->data) & d->data) == - d->data) { - return 0; - } - } - } + if ((av.data & d->data) == d->data) + return 0; } - if (t && t2) { - key.source_type = t->s.value; + if (t1 && t2) { + /* + * search for access allowed between type 1's parent + * and type 2's parent. + */ + key.source_type = t1->s.value; key.target_type = t2->s.value; key.target_class = k->target_class; key.specified = AVTAB_ALLOWED; + compute_avtab_datum(a, &key, &av); - avdatump = avtab_search(a->expa, &key); - if (avdatump) { - if ((avdatump->data & d->data) == d->data) { - return 0; - } - av = avdatump->data; - } - if (a->opt_cond_list) { - /* if a conditional list is present search it before continuing */ - avdatump = cond_av_list_search(&key, a->opt_cond_list); - if (avdatump) { - if (((av | avdatump->data) & d->data) == - d->data) { - return 0; - } - } - } + if ((av.data & d->data) == d->data) + return 0; } - if (!t && !t2) { - /* Neither one of these types have parents and - * therefore the hierarchical constraint does not apply */ + /* + * Neither one of these types have parents and + * therefore the hierarchical constraint does not apply + */ + if (!t1 && !t2) return 0; - } - /* At this point there is a violation of the hierarchal constraint, send error condition back */ + /* + * At this point there is a violation of the hierarchal + * constraint, send error condition back + */ ERR(a->handle, "hierarchy violation between types %s and %s : %s { %s }", a->p->p_type_val_to_name[k->source_type - 1], a->p->p_type_val_to_name[k->target_type - 1], a->p->p_class_val_to_name[k->target_class - 1], - sepol_av_to_string(a->p, k->target_class, d->data & ~av)); + sepol_av_to_string(a->p, k->target_class, d->data & ~av.data)); a->numerr++; return 0; } +/* + * If same permissions are allowed for same combination of + * source and target, we can evaluate them as unconditional + * one. + * See the following example. A_t type is bounds of B_t type, + * so B_t can never have wider permissions then A_t. + * A_t has conditional permission on X_t, however, a part of + * them (getattr and read) are unconditionaly allowed to A_t. + * + * Example) + * typebounds A_t B_t; + * + * allow B_t X_t : file { getattr }; + * if (foo_bool) { + * allow A_t X_t : file { getattr read }; + * } else { + * allow A_t X_t : file { getattr read write }; + * } + * + * We have to pull up them as unconditional ones in this case, + * because it seems to us B_t is violated to bounds constraints + * during unconditional policy checking. + */ +static int pullup_unconditional_perms(cond_list_t * cond_list, + hierarchy_args_t * args) +{ + cond_list_t *cur_node; + cond_av_list_t *cur_av, *expl_true = NULL, *expl_false = NULL; + avtab_t expa_true, expa_false; + avtab_datum_t *avdatp; + avtab_datum_t avdat; + avtab_ptr_t avnode; + + for (cur_node = cond_list; cur_node; cur_node = cur_node->next) { + if (avtab_init(&expa_true)) + goto oom0; + if (avtab_init(&expa_false)) + goto oom1; + if (expand_cond_av_list(args->p, cur_node->true_list, + &expl_true, &expa_true)) + goto oom2; + if (expand_cond_av_list(args->p, cur_node->false_list, + &expl_false, &expa_false)) + goto oom3; + for (cur_av = expl_true; cur_av; cur_av = cur_av->next) { + avdatp = avtab_search(&expa_false, + &cur_av->node->key); + if (!avdatp) + continue; + + avdat.data = (cur_av->node->datum.data + & avdatp->data); + if (!avdat.data) + continue; + + avnode = avtab_search_node(args->expa, + &cur_av->node->key); + if (avnode) { + avnode->datum.data |= avdat.data; + } else { + if (avtab_insert(args->expa, + &cur_av->node->key, + &avdat)) + goto oom4; + } + } + cond_av_list_destroy(expl_false); + cond_av_list_destroy(expl_true); + avtab_destroy(&expa_false); + avtab_destroy(&expa_true); + } + return 0; + +oom4: + cond_av_list_destroy(expl_false); +oom3: + cond_av_list_destroy(expl_true); +oom2: + avtab_destroy(&expa_false); +oom1: + avtab_destroy(&expa_true); +oom0: + ERR(args->handle, "out of memory on conditional av list expansion"); + return 1; +} + static int check_cond_avtab_hierarchy(cond_list_t * cond_list, hierarchy_args_t * args) { @@ -264,37 +335,51 @@ cond_av_list_t *cur_av, *expl = NULL; avtab_t expa; hierarchy_args_t *a = (hierarchy_args_t *) args; + avtab_datum_t avdat, *uncond; - for (cur_node = cond_list; cur_node != NULL; cur_node = cur_node->next) { + for (cur_node = cond_list; cur_node; cur_node = cur_node->next) { + /* + * Check true condition + */ if (avtab_init(&expa)) goto oom; - if (expand_cond_av_list - (args->p, cur_node->true_list, &expl, &expa)) { + if (expand_cond_av_list(args->p, cur_node->true_list, + &expl, &expa)) { avtab_destroy(&expa); goto oom; } args->opt_cond_list = expl; - for (cur_av = expl; cur_av != NULL; cur_av = cur_av->next) { + for (cur_av = expl; cur_av; cur_av = cur_av->next) { + avdat.data = cur_av->node->datum.data; + uncond = avtab_search(a->expa, &cur_av->node->key); + if (uncond) + avdat.data |= uncond->data; rc = check_avtab_hierarchy_callback(&cur_av->node->key, - &cur_av->node-> - datum, args); + &avdat, args); if (rc) - a->numerr++; + args->numerr++; } cond_av_list_destroy(expl); - avtab_destroy(&expa); + + /* + * Check false condition + */ if (avtab_init(&expa)) goto oom; - if (expand_cond_av_list - (args->p, cur_node->false_list, &expl, &expa)) { + if (expand_cond_av_list(args->p, cur_node->false_list, + &expl, &expa)) { avtab_destroy(&expa); goto oom; } args->opt_cond_list = expl; - for (cur_av = expl; cur_av != NULL; cur_av = cur_av->next) { + for (cur_av = expl; cur_av; cur_av = cur_av->next) { + avdat.data = cur_av->node->datum.data; + uncond = avtab_search(a->expa, &cur_av->node->key); + if (uncond) + avdat.data |= uncond->data; + rc = check_avtab_hierarchy_callback(&cur_av->node->key, - &cur_av->node-> - datum, args); + &avdat, args); if (rc) a->numerr++; } @@ -317,40 +402,21 @@ __attribute__ ((unused)), hashtab_datum_t d, void *args) { - char *parent; hierarchy_args_t *a; role_datum_t *r, *rp; a = (hierarchy_args_t *) args; r = (role_datum_t *) d; - if (find_parent(a->p->p_role_val_to_name[r->s.value - 1], &parent)) + if (find_parent_role(a, r, &rp) < 0) return -1; - if (!parent) { - /* This role has no parent */ - return 0; - } - - rp = hashtab_search(a->p->p_roles.table, parent); - if (!rp) { - /* Orphan role */ - ERR(a->handle, "role %s doesn't exist, %s is an orphan", - parent, a->p->p_role_val_to_name[r->s.value - 1]); - free(parent); - a->numerr++; - return 0; - } - - if (!ebitmap_contains(&rp->types.types, &r->types.types)) { - /* This is a violation of the hiearchal constraint, return error condition */ + if (rp && !ebitmap_contains(&rp->types.types, &r->types.types)) { + /* hierarchical constraint violation, return error */ ERR(a->handle, "Role hierarchy violation, %s exceeds %s", - a->p->p_role_val_to_name[r->s.value - 1], parent); + (char *) k, a->p->p_role_val_to_name[rp->s.value - 1]); a->numerr++; } - - free(parent); - return 0; } @@ -362,40 +428,21 @@ __attribute__ ((unused)), hashtab_datum_t d, void *args) { - char *parent; hierarchy_args_t *a; user_datum_t *u, *up; a = (hierarchy_args_t *) args; u = (user_datum_t *) d; - if (find_parent(a->p->p_user_val_to_name[u->s.value - 1], &parent)) + if (find_parent_user(a, u, &up) < 0) return -1; - if (!parent) { - /* This user has no parent */ - return 0; - } - - up = hashtab_search(a->p->p_users.table, parent); - if (!up) { - /* Orphan user */ - ERR(a->handle, "user %s doesn't exist, %s is an orphan", - parent, a->p->p_user_val_to_name[u->s.value - 1]); - free(parent); - a->numerr++; - return 0; - } - - if (!ebitmap_contains(&up->roles.roles, &u->roles.roles)) { + if (up && !ebitmap_contains(&up->roles.roles, &u->roles.roles)) { /* hierarchical constraint violation, return error */ ERR(a->handle, "User hierarchy violation, %s exceeds %s", - a->p->p_user_val_to_name[u->s.value - 1], parent); + (char *) k, a->p->p_user_val_to_name[up->s.value - 1]); a->numerr++; } - - free(parent); - return 0; } @@ -420,6 +467,9 @@ if (hashtab_map(p->p_types.table, check_type_hierarchy_callback, &args)) goto bad; + if (pullup_unconditional_perms(p->cond_list, &args)) + return -1; + if (avtab_map(&expa, check_avtab_hierarchy_callback, &args)) goto bad; Index: libsepol/src/expand.c =================================================================== --- libsepol/src/expand.c (revision 2950) +++ libsepol/src/expand.c (working copy) @@ -466,6 +466,100 @@ return 0; } +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_TYPES)) + return 0; + + bounds_val = state->typemap[type->bounds - 1]; + + dest = hashtab_search(state->out->p_types.table, (char *)key); + if (!dest) { + ERR(state->handle, "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_ROLES)) + return 0; + + bounds_val = state->rolemap[role->bounds - 1]; + + dest = hashtab_search(state->out->p_roles.table, (char *)key); + if (!dest) { + ERR(state->handle, "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + expand_state_t *state = (expand_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + if (!is_id_enabled((char *)key, state->base, SYM_USERS)) + return 0; + + bounds_val = state->usermap[user->bounds - 1]; + + dest = hashtab_search(state->out->p_users.table, (char *)key); + if (!dest) { + ERR(state->handle, "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be certain that * the out symbol table will have the type that the alias refers. Otherwise, we * won't be able to find the type value for the alias. We can't depend on the @@ -1865,31 +1959,6 @@ return 0; } -static void type_destroy(hashtab_key_t key, hashtab_datum_t datum, void *p - __attribute__ ((unused))) -{ - free(key); - type_datum_destroy((type_datum_t *) datum); - free(datum); -} - -static int type_attr_remove(hashtab_key_t key - __attribute__ ((unused)), hashtab_datum_t datum, - void *args) -{ - type_datum_t *typdatum; - policydb_t *p; - - typdatum = (type_datum_t *) datum; - p = (policydb_t *) args; - if (typdatum->flavor == TYPE_ATTRIB) { - p->type_val_to_struct[typdatum->s.value - 1] = NULL; - p->p_type_val_to_name[typdatum->s.value - 1] = NULL; - return 1; - } - return 0; -} - /* converts typeset using typemap and expands into ebitmap_t types using the attributes in the passed in policy. * this should not be called until after all the blocks have been processed and the attributes in target policy * are complete. */ @@ -2393,6 +2462,11 @@ goto cleanup; } + /* copy type bounds */ + if (hashtab_map(state.base->p_types.table, + type_bounds_copy_callback, &state)) + goto cleanup; + /* copy aliases */ if (hashtab_map(state.base->p_types.table, alias_copy_callback, &state)) goto cleanup; @@ -2406,6 +2480,9 @@ /* copy roles */ if (hashtab_map(state.base->p_roles.table, role_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_roles.table, + role_bounds_copy_callback, &state)) + goto cleanup; /* copy MLS's sensitivity level and categories - this needs to be done * before expanding users (they need to be indexed too) */ @@ -2421,6 +2498,9 @@ /* copy users */ if (hashtab_map(state.base->p_users.table, user_copy_callback, &state)) goto cleanup; + if (hashtab_map(state.base->p_users.table, + user_bounds_copy_callback, &state)) + goto cleanup; /* copy bools */ if (hashtab_map(state.base->p_bools.table, bool_copy_callback, &state)) @@ -2510,8 +2590,6 @@ } if (hashtab_map(state.out->p_types.table, type_attr_map, &state)) goto cleanup; - hashtab_map_remove_on_error(state.out->p_types.table, - type_attr_remove, type_destroy, state.out); if (check) { if (hierarchy_check_constraints(handle, state.out)) goto cleanup; Index: libsepol/src/write.c =================================================================== --- libsepol/src/write.c (revision 2950) +++ libsepol/src/write.c (working copy) @@ -920,6 +920,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(role->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(role->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; @@ -952,19 +954,51 @@ typdatum = (type_datum_t *) datum; + /* + * The kernel policy version less than 24 (= POLICYDB_VERSION_BOUNDARY) + * does not support to load entries of attribute, so we skip to write it. + */ + if (p->policy_type == POLICY_KERN + && p->policyvers < POLICYDB_VERSION_BOUNDARY + && typdatum->flavor == TYPE_ATTRIB) + return POLICYDB_SUCCESS; + len = strlen(key); items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(typdatum->s.value); - buf[items++] = cpu_to_le32(typdatum->primary); - if (p->policy_type != POLICY_KERN) { - buf[items++] = cpu_to_le32(typdatum->flavor); - if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) - buf[items++] = cpu_to_le32(typdatum->flags); - else if (typdatum->flags & TYPE_FLAGS_PERMISSIVE) - WARN(fp->handle, "Warning! Module policy version %d cannnot " - "support permissive types, but one was defined", - p->policyvers); + if (policydb_has_boundary_feature(p)) { + uint32_t properties = 0; + + if (typdatum->primary) + properties |= TYPEDATUM_PROPERTY_PRIMARY; + + if (typdatum->flavor == TYPE_ATTRIB) { + properties |= TYPEDATUM_PROPERTY_ATTRIBUTE; + } else if (typdatum->flavor == TYPE_ALIAS + && p->policy_type != POLICY_KERN) + properties |= TYPEDATUM_PROPERTY_ALIAS; + + if (typdatum->flags & TYPE_FLAGS_PERMISSIVE + && p->policy_type != POLICY_KERN) + properties |= TYPEDATUM_PROPERTY_PERMISSIVE; + + buf[items++] = cpu_to_le32(properties); + buf[items++] = cpu_to_le32(typdatum->bounds); + } else { + buf[items++] = cpu_to_le32(typdatum->primary); + + if (p->policy_type != POLICY_KERN) { + buf[items++] = cpu_to_le32(typdatum->flavor); + + if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) + buf[items++] = cpu_to_le32(typdatum->flags); + else if (typdatum->flags & TYPE_FLAGS_PERMISSIVE) + WARN(fp->handle, "Warning! Module policy " + "version %d cannnot suport permissive " + "types, but one was defined", + p->policyvers); + } } items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) @@ -997,6 +1031,8 @@ items = 0; buf[items++] = cpu_to_le32(len); buf[items++] = cpu_to_le32(usrdatum->s.value); + if (policydb_has_boundary_feature(p)) + buf[items++] = cpu_to_le32(usrdatum->bounds); items2 = put_entry(buf, sizeof(uint32_t), items, fp); if (items != items2) return POLICYDB_ERROR; @@ -1515,6 +1551,19 @@ return POLICYDB_SUCCESS; } +static int type_attr_uncount(hashtab_key_t key __attribute__ ((unused)), + hashtab_datum_t datum, void *args) +{ + type_datum_t *typdatum = datum; + uint32_t *p_nel = args; + + if (typdatum->flavor == TYPE_ATTRIB) { + /* uncount attribute from total number of types */ + (*p_nel)--; + } + return 0; +} + /* * Write the configuration data in a policy database * structure to a policy database binary representation @@ -1646,6 +1695,18 @@ for (i = 0; i < num_syms; i++) { buf[0] = cpu_to_le32(p->symtab[i].nprim); buf[1] = cpu_to_le32(p->symtab[i].table->nel); + + /* + * A special case when writing type/attribute symbol table. + * The kernel policy version less than 24 does not support + * to load entries of attribute, so we have to re-calculate + * the actual number of types except for attributes. + */ + if (i == SYM_TYPES && + p->policyvers < POLICYDB_VERSION_BOUNDARY && + p->policy_type == POLICY_KERN) { + hashtab_map(p->symtab[i].table, type_attr_uncount, &buf[1]); + } items = put_entry(buf, sizeof(uint32_t), 2, fp); if (items != 2) return POLICYDB_ERROR; Index: libsepol/src/link.c =================================================================== --- libsepol/src/link.c (revision 2950) +++ libsepol/src/link.c (working copy) @@ -660,6 +660,97 @@ user_copy_callback, bool_copy_callback, sens_copy_callback, cat_copy_callback}; +/* + * The boundaries have to be copied after the types/roles/users are copied, + * because it refers hashtab to lookup destinated objects. + */ +static int type_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + type_datum_t *type = (type_datum_t *) datum; + type_datum_t *dest; + uint32_t bounds_val; + + if (!type->bounds) + return 0; + + bounds_val = state->cur->map[SYM_TYPES][type->bounds - 1]; + + dest = hashtab_search(state->base->p_types.table, key); + if (!dest) { + ERR(state->handle, + "Type lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int role_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + role_datum_t *role = (role_datum_t *) datum; + role_datum_t *dest; + uint32_t bounds_val; + + if (!role->bounds) + return 0; + + bounds_val = state->cur->map[SYM_ROLES][role->bounds - 1]; + + dest = hashtab_search(state->base->p_roles.table, key); + if (!dest) { + ERR(state->handle, + "Role lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + +static int user_bounds_copy_callback(hashtab_key_t key, + hashtab_datum_t datum, void *data) +{ + link_state_t *state = (link_state_t *) data; + user_datum_t *user = (user_datum_t *) datum; + user_datum_t *dest; + uint32_t bounds_val; + + if (!user->bounds) + return 0; + + bounds_val = state->cur->map[SYM_USERS][user->bounds - 1]; + + dest = hashtab_search(state->base->p_users.table, key); + if (!dest) { + ERR(state->handle, + "User lookup failed for %s", (char *)key); + return -1; + } + if (dest->bounds != 0 && dest->bounds != bounds_val) { + ERR(state->handle, + "Inconsistent boundary for %s", (char *)key); + return -1; + } + dest->bounds = bounds_val; + + return 0; +} + /* The aliases have to be copied after the types and attributes to be * certain that the base symbol table will have the type that the * alias refers. Otherwise, we won't be able to find the type value @@ -1362,11 +1453,22 @@ } } - if (hashtab_map - (src_symtab[SYM_TYPES].table, alias_copy_callback, state)) { + if (hashtab_map(src_symtab[SYM_TYPES].table, + type_bounds_copy_callback, state)) return -1; - } + if (hashtab_map(src_symtab[SYM_TYPES].table, + alias_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_ROLES].table, + role_bounds_copy_callback, state)) + return -1; + + if (hashtab_map(src_symtab[SYM_USERS].table, + user_bounds_copy_callback, state)) + return -1; + /* then fix bitmaps associated with those newly copied identifiers */ for (i = 0; i < SYM_NUM; i++) { if (fix_callback_f[i] != NULL && ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-10-07 6:39 ` KaiGai Kohei @ 2008-10-09 15:30 ` Joshua Brindle 2008-10-09 17:00 ` Joshua Brindle 2008-10-10 0:57 ` KaiGai Kohei 2008-10-09 17:11 ` Joshua Brindle 1 sibling, 2 replies; 97+ messages in thread From: Joshua Brindle @ 2008-10-09 15:30 UTC (permalink / raw) To: KaiGai Kohei; +Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux KaiGai Kohei wrote: >>> Hmm.... >>> It seems to me what you pointed out is a bug of my patch. It prevents to deliver >>> actual number of type/attribute symbols to policy file, but it is unclear why does >>> it makes libsepol ignore the policyvers. >>> (I guess it may be a separated matter.) >>> >>> >>>> Rather than trying to calculate the length without attributes I just removed >>>> the attribute check. This causes attributes to be written for all versions, >>>> but this should not cause any problems at all. >>>> >>> The reason why I injected such an ad-hoc code is that we cannot decide the policy >>> version written when type_attr_remove() is invoked. >>> Is it impossible to move it to policydb_write()? >>> It is invoked after the policyvers is fixed by caller. >>> >> It isn't impossible. You are going to have to make it walk to type >> symbol table to calculate the length without attributes, then write >> that length instead of the total symtab length. >> > > The attached patch enables to fixup the number of type/attribute entries > to be written. The type_attr_uncount() decrements the number of attribute > entries skipped at type_write(). > > At first, I had a plan to invoke type_attr_remove() with > hashtab_map_remove_on_error(), but it means the given policydb structure > is modified at policydb_write() and implicit changes to external interface. > > This does not cause a hierarchy error, is this an expected limitation? typebounds goodbye_world_t hello_world_t; allow hello_world_t self: file ~{read }; allow goodbye_world_t self: file *; -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-10-09 15:30 ` Joshua Brindle @ 2008-10-09 17:00 ` Joshua Brindle 2008-10-10 0:57 ` KaiGai Kohei 1 sibling, 0 replies; 97+ messages in thread From: Joshua Brindle @ 2008-10-09 17:00 UTC (permalink / raw) To: KaiGai Kohei; +Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux Joshua Brindle wrote: > KaiGai Kohei wrote: > >>>> Hmm.... >>>> It seems to me what you pointed out is a bug of my patch. It prevents to deliver >>>> actual number of type/attribute symbols to policy file, but it is unclear why does >>>> it makes libsepol ignore the policyvers. >>>> (I guess it may be a separated matter.) >>>> >>>> >>>> >>>>> Rather than trying to calculate the length without attributes I just removed >>>>> the attribute check. This causes attributes to be written for all versions, >>>>> but this should not cause any problems at all. >>>>> >>>>> >>>> The reason why I injected such an ad-hoc code is that we cannot decide the policy >>>> version written when type_attr_remove() is invoked. >>>> Is it impossible to move it to policydb_write()? >>>> It is invoked after the policyvers is fixed by caller. >>>> >>>> >>> It isn't impossible. You are going to have to make it walk to type >>> symbol table to calculate the length without attributes, then write >>> that length instead of the total symtab length. >>> >>> >> The attached patch enables to fixup the number of type/attribute entries >> to be written. The type_attr_uncount() decrements the number of attribute >> entries skipped at type_write(). >> >> At first, I had a plan to invoke type_attr_remove() with >> hashtab_map_remove_on_error(), but it means the given policydb structure >> is modified at policydb_write() and implicit changes to external interface. >> >> >> > > This does not cause a hierarchy error, is this an expected limitation? > > typebounds goodbye_world_t hello_world_t; > > allow hello_world_t self: file ~{read }; > > allow goodbye_world_t self: file *; I'm going to go ahead and merge this with the expectation that the above will get fixed. -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-10-09 15:30 ` Joshua Brindle 2008-10-09 17:00 ` Joshua Brindle @ 2008-10-10 0:57 ` KaiGai Kohei 1 sibling, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-10-10 0:57 UTC (permalink / raw) To: Joshua Brindle Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux Joshua Brindle wrote: > KaiGai Kohei wrote: >>>> Hmm.... >>>> It seems to me what you pointed out is a bug of my patch. It prevents to deliver >>>> actual number of type/attribute symbols to policy file, but it is unclear why does >>>> it makes libsepol ignore the policyvers. >>>> (I guess it may be a separated matter.) >>>> >>>> >>>>> Rather than trying to calculate the length without attributes I just removed >>>>> the attribute check. This causes attributes to be written for all versions, >>>>> but this should not cause any problems at all. >>>>> >>>> The reason why I injected such an ad-hoc code is that we cannot decide the policy >>>> version written when type_attr_remove() is invoked. >>>> Is it impossible to move it to policydb_write()? >>>> It is invoked after the policyvers is fixed by caller. >>>> >>> It isn't impossible. You are going to have to make it walk to type >>> symbol table to calculate the length without attributes, then write >>> that length instead of the total symtab length. >>> >> The attached patch enables to fixup the number of type/attribute entries >> to be written. The type_attr_uncount() decrements the number of attribute >> entries skipped at type_write(). >> >> At first, I had a plan to invoke type_attr_remove() with >> hashtab_map_remove_on_error(), but it means the given policydb structure >> is modified at policydb_write() and implicit changes to external interface. >> >> > > This does not cause a hierarchy error, is this an expected limitation? > > typebounds goodbye_world_t hello_world_t; > > allow hello_world_t self: file ~{read }; > > allow goodbye_world_t self: file *; This case should not cause a hierarchy error. Please assume a domain "S" is a bounds of "S.sub" and a type "T" is a bounds of "T.sub", and P(x,y) means permissions between "x" and "y". As you know, the hierarchy constraint has the following rules. 1. P(S.sub, T) is equal or smaller than P(S, T) 2. P(S, T.sub) is equal or smaller than P(S, T) 3. P(S.sub, T.sub) is equal or smaller than P(S, T) 4. rest of permissions are violated. Your case matches the third rule which should be allowed. P(hello_world_t, hello_world_t) = file : ~{ read } is equal or smaller than P(goodbye_world_t, goodbye_world_t) = file : * It is an expected behavior, not a limitation. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-10-07 6:39 ` KaiGai Kohei 2008-10-09 15:30 ` Joshua Brindle @ 2008-10-09 17:11 ` Joshua Brindle 1 sibling, 0 replies; 97+ messages in thread From: Joshua Brindle @ 2008-10-09 17:11 UTC (permalink / raw) To: KaiGai Kohei; +Cc: KaiGai Kohei, Stephen Smalley, jmorris, paul.moore, selinux KaiGai Kohei wrote: >>> Hmm.... >>> It seems to me what you pointed out is a bug of my patch. It prevents to deliver >>> actual number of type/attribute symbols to policy file, but it is unclear why does >>> it makes libsepol ignore the policyvers. >>> (I guess it may be a separated matter.) >>> >>> >>>> Rather than trying to calculate the length without attributes I just removed >>>> the attribute check. This causes attributes to be written for all versions, >>>> but this should not cause any problems at all. >>>> >>> The reason why I injected such an ad-hoc code is that we cannot decide the policy >>> version written when type_attr_remove() is invoked. >>> Is it impossible to move it to policydb_write()? >>> It is invoked after the policyvers is fixed by caller. >>> >> It isn't impossible. You are going to have to make it walk to type >> symbol table to calculate the length without attributes, then write >> that length instead of the total symtab length. >> > > The attached patch enables to fixup the number of type/attribute entries > to be written. The type_attr_uncount() decrements the number of attribute > entries skipped at type_write(). > > At first, I had a plan to invoke type_attr_remove() with > hashtab_map_remove_on_error(), but it means the given policydb structure > is modified at policydb_write() and implicit changes to external interface. > > Merged in to libsepol-2.0.34 -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-10-04 23:30 ` Joshua Brindle 2008-10-06 9:19 ` KaiGai Kohei @ 2008-10-06 12:30 ` Stephen Smalley 2008-10-06 19:13 ` Joshua Brindle 1 sibling, 1 reply; 97+ messages in thread From: Stephen Smalley @ 2008-10-06 12:30 UTC (permalink / raw) To: Joshua Brindle; +Cc: KaiGai Kohei, KaiGai Kohei, jmorris, paul.moore, selinux On Sat, 2008-10-04 at 19:30 -0400, Joshua Brindle wrote: > KaiGai Kohei wrote: > > Joshua Brindle wrote: > >> KaiGai Kohei wrote: > >>> Joshua Brindle wrote: > >>>> KaiGai Kohei wrote: > >>>>> The attached patch for libsepol add suport for a new policy version > >>>>> named as (MOD_)POLICYDB_VERSION_BOUNDARY. > >>>>> Userspace hierarchy checks are reworked in this revision. > >>>>> > >> I'm seeing a couple problems. First when writing out the policy > >> it doesn't seem to respect policyvers, I told it to generate > >> a version 23 and it still made a 24. > > > > Are you saying a configuration of "policy-version = 23" at semanage.conf > > is ignored? I could not reproduce it in my environment. > > Could you tell me the steps to reproduce it? > > > > I injected several printf()'s, but it shows a proper policyvers > > which reflects semanage.conf correctly. > > > >> Second it is failing to downgrade the 24 to 23 since my kernel doesn't support 24. > > > > I'm not sure why this wasn't happening to you but from what I can tell the new patch was returning from type_write when an attribute was passed in, however the length of the table was not updated. This caused policydb_read to read over the edge of the type symbol table, resulting in badness. > > Rather than trying to calculate the length without attributes I just removed the attribute check. This causes attributes to be written for all versions, but this should not cause any problems at all. > > Do you have a problem with this Stephen? The problem with writing attributes to older policy versions is that older kernels will then treat the attribute like any other type and allow its use in a security context. > > index 6f1f655..d2c2c32 100644 > --- a/libsepol/src/write.c > +++ b/libsepol/src/write.c > @@ -954,15 +954,6 @@ static int type_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) > > typdatum = (type_datum_t *) datum; > > - /* > - * The kernel policy version less than 24 (= POLICYDB_VERSION_BOUNDARY) > - * does not support to load entries of attribute, so we skip to write it. > - */ > - if (p->policy_type == POLICY_KERN > - && p->policyvers < POLICYDB_VERSION_BOUNDARY > - && typdatum->flavor == TYPE_ATTRIB) > - return POLICYDB_SUCCESS; > - > len = strlen(key); > items = 0; > buf[items++] = cpu_to_le32(len); -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) 2008-10-06 12:30 ` Stephen Smalley @ 2008-10-06 19:13 ` Joshua Brindle 0 siblings, 0 replies; 97+ messages in thread From: Joshua Brindle @ 2008-10-06 19:13 UTC (permalink / raw) To: Stephen Smalley; +Cc: KaiGai Kohei, KaiGai Kohei, jmorris, paul.moore, selinux Stephen Smalley wrote: > On Sat, 2008-10-04 at 19:30 -0400, Joshua Brindle wrote: >> KaiGai Kohei wrote: >>> Joshua Brindle wrote: >>>> KaiGai Kohei wrote: >>>>> Joshua Brindle wrote: >>>>>> KaiGai Kohei wrote: >>>>>>> The attached patch for libsepol add suport for a new policy version >>>>>>> named as (MOD_)POLICYDB_VERSION_BOUNDARY. >>>>>>> Userspace hierarchy checks are reworked in this revision. >>>>>>> >>>> I'm seeing a couple problems. First when writing out the policy >>>> it doesn't seem to respect policyvers, I told it to generate >>>> a version 23 and it still made a 24. >>> Are you saying a configuration of "policy-version = 23" at semanage.conf >>> is ignored? I could not reproduce it in my environment. >>> Could you tell me the steps to reproduce it? >>> >>> I injected several printf()'s, but it shows a proper policyvers >>> which reflects semanage.conf correctly. >>> >>>> Second it is failing to downgrade the 24 to 23 since my kernel doesn't support 24. >> I'm not sure why this wasn't happening to you but from what I can tell the new patch was returning from type_write when an attribute was passed in, however the length of the table was not updated. This caused policydb_read to read over the edge of the type symbol table, resulting in badness. >> >> Rather than trying to calculate the length without attributes I just removed the attribute check. This causes attributes to be written for all versions, but this should not cause any problems at all. >> >> Do you have a problem with this Stephen? > > The problem with writing attributes to older policy versions is that > older kernels will then treat the attribute like any other type and > allow its use in a security context. Yes, I remembered that after I sent the first email.. doh -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* RE: [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) 2008-08-05 5:47 ` [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei ` (2 preceding siblings ...) 2008-08-05 5:55 ` [PATCH 3/3] " KaiGai Kohei @ 2008-08-11 17:58 ` Joshua Brindle 2008-08-13 5:53 ` KaiGai Kohei 3 siblings, 1 reply; 97+ messages in thread From: Joshua Brindle @ 2008-08-11 17:58 UTC (permalink / raw) To: KaiGai Kohei, Stephen Smalley; +Cc: jmorris, paul.moore, selinux KaiGai Kohei wrote: > The series of patches enables to assign a thread an > indivisual "more bounded" domain, even if the process is > multithreaded. > > We can apply this feature to set up a server application > which handles user's request withing more restricted domain, > to protect potential harms come from application bugs. > > It also adds a new binary policy format (version 24). > This version allows each definitions of user/role/type to > have its bounds. The bounds restrict capabilities of bounded > one with similar rules compared to existing name based > hierarchy support. > The following rules are enhanced from existing hierarchy stuff. > - A bounded type cannot have any attribute which does not assigned > to the upper type. I don't think this is necessary. The original hierarchy implementation only cared about the net effect of the rules, not how they were granted. For example, if I have 2 attributes, one that is given more privilege and one that is given a subset of the first why should I have to assign both to the 'parent' type? I don't agree with this constraint. > - CONSTRAIN/MLSCONSTRAIN rules are enhanced to pay attention the > boundary relationships. > > I adds a new statement of TYPEBOUNDS as follows: > > TYPEBOUNDS <parent type> <child type 1> [, <child type 2> ...] ; > > It defines boundary relationships between two types, and > these are loaded to the kernelspace via policy version 24. > (*) The existing hierarchy is implicitly expanded to bounds. > > I don't provide a statement to define boundary relationships > between roles/users, although existing hierarchy supports > them, because its purpose/worth is unclear now. Its purpose was the same as the original purpose for the hierarchy. The hierarchy was developed for policy access control using the policy management server. One would 'label' policy objects using the hierarchy and could constrain the permissions (or roles or types) using the hierarchical constraints. I request that you not remove this support in your version. Additionally, if you are enforcing hierarchy on types in constrain statements you should also do this for roles and users, for consistency. -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* Re: [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) 2008-08-11 17:58 ` [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) Joshua Brindle @ 2008-08-13 5:53 ` KaiGai Kohei 0 siblings, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-13 5:53 UTC (permalink / raw) To: Joshua Brindle; +Cc: Stephen Smalley, jmorris, paul.moore, selinux Joshua Brindle wrote: > KaiGai Kohei wrote: >> The series of patches enables to assign a thread an >> indivisual "more bounded" domain, even if the process is >> multithreaded. >> >> We can apply this feature to set up a server application >> which handles user's request withing more restricted domain, >> to protect potential harms come from application bugs. >> >> It also adds a new binary policy format (version 24). >> This version allows each definitions of user/role/type to >> have its bounds. The bounds restrict capabilities of bounded >> one with similar rules compared to existing name based >> hierarchy support. >> The following rules are enhanced from existing hierarchy stuff. >> - A bounded type cannot have any attribute which does not assigned >> to the upper type. > > I don't think this is necessary. The original hierarchy implementation > only cared about the net effect of the rules, not how they were granted. > For example, if I have 2 attributes, one that is given more privilege > and one that is given a subset of the first why should I have to assign > both to the 'parent' type? I don't agree with this constraint. The major purpose of attribute bounds was a restriction for CONSTRAIN or MLSCONSTRAINT rules. However, I reconsidered it can be implemented by sanity checks at policy load time, without any attribute bounds. I agree what you pointed out. In the next patch, I'll remove the code to check attribute bounds. >> - CONSTRAIN/MLSCONSTRAIN rules are enhanced to pay attention the >> boundary relationships. >> >> I adds a new statement of TYPEBOUNDS as follows: >> >> TYPEBOUNDS <parent type> <child type 1> [, <child type 2> ...] ; >> >> It defines boundary relationships between two types, and >> these are loaded to the kernelspace via policy version 24. >> (*) The existing hierarchy is implicitly expanded to bounds. >> >> I don't provide a statement to define boundary relationships >> between roles/users, although existing hierarchy supports >> them, because its purpose/worth is unclear now. > > Its purpose was the same as the original purpose for the hierarchy. The > hierarchy was developed for policy access control using the policy > management server. One would 'label' policy objects using the hierarchy > and could constrain the permissions (or roles or types) using the > hierarchical constraints. I request that you not remove this support in > your version. Sorry, my explanation might be misleadable. :( The new facilities can have a data structure to represent boundary relationships between two types/roles/users. However, I didn't add a new syntax in policy language statement, like USERBOUNDS/ROLEBOUNDS. If the checkmodule founds existing name based hierarhies, implicit boundary relationships are assigned to them, and these are shipped into kernelspace later. For example, a definition of "staff_u.kaigai" does not define a SElinux user only, but a boundary relationship between "staff_u" and "staff_u.kaigai" is also defined automatically. I consider this behavior can follows existing name based hierarchies. > Additionally, if you are enforcing hierarchy on types in constrain > statements you should also do this for roles and users, for consistency. The latest kernel patch enables to check (MLS)CONSTRAINT statement for users, roles and types. Please show constraint_bounds_sanity_check() which is invoked at policy load time. Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
* A toy of SQL injection (Re: [PATCH 0/3] Thread/Child-Domain Assignment) 2008-07-25 12:51 ` [PATCH 0/3] Thread/Child-Domain Assignment KaiGai Kohei ` (4 preceding siblings ...) 2008-08-05 5:47 ` [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei @ 2008-08-14 8:55 ` KaiGai Kohei 5 siblings, 0 replies; 97+ messages in thread From: KaiGai Kohei @ 2008-08-14 8:55 UTC (permalink / raw) To: Stephen Smalley; +Cc: jmorris, paul.moore, jbrindle, selinux The following PHP script is an example to assign individual security context. A modified version of Apache kicks a child thread for each requirests and assign its security context based on authenticated username via HTTP basic authentication. http://kaigai.myhome.cx/index.php Three users are set up (curry, sushi and noodle) with same password of "selinux", to confirm they have individual security context. No need to say, this script has a vulnerability of SQL injection. It can help to image how SELinux aware LAPP stack protect from web application bugs. (*) The above URL and environment is temporary one, so I'll close it without any notices after a week. :-) Thanks, -- OSS Platform Development Division, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message. ^ permalink raw reply [flat|nested] 97+ messages in thread
end of thread, other threads:[~2008-10-23 9:48 UTC | newest] Thread overview: 97+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-07-15 10:06 [RFC] An idea of thread/child-domain assignment KaiGai Kohei 2008-07-15 13:38 ` Stephen Smalley 2008-07-16 2:17 ` KaiGai Kohei 2008-07-16 6:08 ` KaiGai Kohei 2008-07-16 12:00 ` Stephen Smalley 2008-07-16 12:18 ` Stephen Smalley 2008-07-18 6:21 ` KaiGai Kohei 2008-07-23 3:58 ` KaiGai Kohei 2008-07-25 12:51 ` [PATCH 0/3] Thread/Child-Domain Assignment KaiGai Kohei 2008-07-25 13:03 ` [PATCH 1/3] " KaiGai Kohei 2008-07-25 13:44 ` Stephen Smalley 2008-07-25 17:06 ` Joshua Brindle 2008-07-26 8:24 ` KaiGai Kohei 2008-07-25 17:07 ` Joshua Brindle 2008-07-26 7:55 ` KaiGai Kohei 2008-07-26 17:28 ` Stephen Smalley 2008-07-26 18:14 ` Joshua Brindle 2008-07-28 3:06 ` KaiGai Kohei 2008-07-28 17:31 ` Stephen Smalley 2008-07-29 6:51 ` KaiGai Kohei 2008-07-29 12:06 ` Stephen Smalley 2008-07-30 14:10 ` Joshua Brindle 2008-07-30 14:57 ` Stephen Smalley 2008-08-01 6:26 ` KaiGai Kohei 2008-07-25 13:03 ` [PATCH 2/3] " KaiGai Kohei 2008-07-29 7:15 ` KaiGai Kohei 2008-07-29 12:25 ` Scott Schmit 2008-07-29 13:28 ` Stephen Smalley 2008-07-25 13:04 ` [PATCH 3/3] " KaiGai Kohei 2008-07-25 13:04 ` [PATCH 4/3] " KaiGai Kohei 2008-08-05 5:47 ` [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei 2008-08-05 5:55 ` [PATCH 1/3] " KaiGai Kohei 2008-08-05 12:53 ` Stephen Smalley 2008-08-06 10:05 ` KaiGai Kohei 2008-08-06 10:13 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.3) KaiGai Kohei 2008-08-14 7:38 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.4) KaiGai Kohei 2008-08-15 18:13 ` Stephen Smalley 2008-08-20 9:41 ` KaiGai Kohei 2008-08-25 12:32 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei 2008-08-25 12:57 ` Stephen Smalley 2008-08-25 13:45 ` KaiGai Kohei 2008-08-26 7:11 ` KaiGai Kohei 2008-08-26 9:01 ` James Morris 2008-08-26 10:29 ` James Morris 2008-08-26 10:47 ` James Morris 2008-08-27 1:15 ` KaiGai Kohei 2008-08-27 8:04 ` [LTP][PATCH 1/2] Replacement of deprecated interfaces KaiGai Kohei 2008-08-27 12:14 ` Stephen Smalley 2008-08-28 6:26 ` KaiGai Kohei 2008-08-28 12:10 ` Subrata Modak 2008-08-28 12:52 ` KaiGai Kohei 2008-08-28 13:34 ` Subrata Modak 2008-10-23 9:48 ` Subrata Modak 2008-08-27 8:05 ` [LTP][PATCH 2/2] Add a new test case for bounds types KaiGai Kohei 2008-10-22 13:00 ` Subrata Modak 2008-10-23 8:10 ` KaiGai Kohei 2008-10-23 9:30 ` Subrata Modak 2008-08-27 1:11 ` [PATCH 1/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei 2008-08-28 7:35 ` [PATCH] SELinux: add boundary support and thread context assignment KaiGai Kohei 2008-08-28 12:43 ` Stephen Smalley 2008-08-28 15:06 ` James Morris 2008-08-05 5:55 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) KaiGai Kohei 2008-08-06 10:14 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.3) KaiGai Kohei 2008-10-09 17:10 ` [PATCH 2/3] Thread/Child-Domain Assignment (rev.2) Joshua Brindle 2008-10-10 1:19 ` KaiGai Kohei 2008-10-10 1:22 ` Joshua Brindle 2008-08-05 5:55 ` [PATCH 3/3] " KaiGai Kohei 2008-08-06 10:13 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.3) KaiGai Kohei 2008-08-25 12:32 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) KaiGai Kohei 2008-08-28 15:51 ` Joshua Brindle 2008-08-29 1:54 ` KaiGai Kohei 2008-08-29 3:01 ` Joshua Brindle 2008-09-01 6:26 ` KaiGai Kohei 2008-09-01 9:08 ` [PATCH] libsepol : Add support for a new policy version (POLICYDB_VERSION_BOUNDARY) KaiGai Kohei 2008-09-01 14:47 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.4) Joshua Brindle 2008-09-01 16:11 ` KaiGai Kohei 2008-09-09 2:04 ` [PATCH 3/3] Thread/Child-Domain Assignment (rev.6) KaiGai Kohei 2008-09-12 18:17 ` Joshua Brindle 2008-09-12 23:20 ` KaiGai Kohei 2008-09-15 13:44 ` Joshua Brindle 2008-09-16 1:50 ` KaiGai Kohei 2008-09-30 14:00 ` Joshua Brindle 2008-10-01 7:53 ` KaiGai Kohei 2008-10-01 19:56 ` Joshua Brindle 2008-10-04 23:30 ` Joshua Brindle 2008-10-06 9:19 ` KaiGai Kohei 2008-10-06 19:13 ` Joshua Brindle 2008-10-07 6:39 ` KaiGai Kohei 2008-10-09 15:30 ` Joshua Brindle 2008-10-09 17:00 ` Joshua Brindle 2008-10-10 0:57 ` KaiGai Kohei 2008-10-09 17:11 ` Joshua Brindle 2008-10-06 12:30 ` Stephen Smalley 2008-10-06 19:13 ` Joshua Brindle 2008-08-11 17:58 ` [PATCH 0/3] Thread/Child-Domain Assignment (rev.2) Joshua Brindle 2008-08-13 5:53 ` KaiGai Kohei 2008-08-14 8:55 ` A toy of SQL injection (Re: [PATCH 0/3] Thread/Child-Domain Assignment) KaiGai Kohei
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.