Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
raspberrypi-kernel
提交
93c98a48
R
raspberrypi-kernel
项目概览
openeuler
/
raspberrypi-kernel
通知
13
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
raspberrypi-kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
93c98a48
编写于
6月 09, 2017
作者:
J
John Johansen
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
apparmor: move exec domain mediation to using labels
Signed-off-by:
N
John Johansen
<
john.johansen@canonical.com
>
上级
5379a331
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
678 addition
and
259 deletion
+678
-259
security/apparmor/domain.c
security/apparmor/domain.c
+592
-258
security/apparmor/include/lib.h
security/apparmor/include/lib.h
+86
-1
未找到文件。
security/apparmor/domain.c
浏览文件 @
93c98a48
...
...
@@ -87,42 +87,236 @@ static int may_change_ptraced_domain(struct aa_label *to_label,
return
error
;
}
/**** TODO: dedup to aa_label_match - needs perm and dfa, merging
* specifically this is an exact copy of aa_label_match except
* aa_compute_perms is replaced with aa_compute_fperms
* and policy.dfa with file.dfa
****/
/* match a profile and its associated ns component if needed
* Assumes visibility test has already been done.
* If a subns profile is not to be matched should be prescreened with
* visibility test.
*/
static
inline
unsigned
int
match_component
(
struct
aa_profile
*
profile
,
struct
aa_profile
*
tp
,
bool
stack
,
unsigned
int
state
)
{
const
char
*
ns_name
;
if
(
stack
)
state
=
aa_dfa_match
(
profile
->
file
.
dfa
,
state
,
"&"
);
if
(
profile
->
ns
==
tp
->
ns
)
return
aa_dfa_match
(
profile
->
file
.
dfa
,
state
,
tp
->
base
.
hname
);
/* try matching with namespace name and then profile */
ns_name
=
aa_ns_name
(
profile
->
ns
,
tp
->
ns
,
true
);
state
=
aa_dfa_match_len
(
profile
->
file
.
dfa
,
state
,
":"
,
1
);
state
=
aa_dfa_match
(
profile
->
file
.
dfa
,
state
,
ns_name
);
state
=
aa_dfa_match_len
(
profile
->
file
.
dfa
,
state
,
":"
,
1
);
return
aa_dfa_match
(
profile
->
file
.
dfa
,
state
,
tp
->
base
.
hname
);
}
/**
* label_compound_match - find perms for full compound label
* @profile: profile to find perms for
* @label: label to check access permissions for
* @stack: whether this is a stacking request
* @start: state to start match in
* @subns: whether to do permission checks on components in a subns
* @request: permissions to request
* @perms: perms struct to set
*
* Returns: 0 on success else ERROR
*
* For the label A//&B//&C this does the perm match for A//&B//&C
* @perms should be preinitialized with allperms OR a previous permission
* check to be stacked.
*/
static
int
label_compound_match
(
struct
aa_profile
*
profile
,
struct
aa_label
*
label
,
bool
stack
,
unsigned
int
state
,
bool
subns
,
u32
request
,
struct
aa_perms
*
perms
)
{
struct
aa_profile
*
tp
;
struct
label_it
i
;
struct
path_cond
cond
=
{
};
/* find first subcomponent that is visible */
label_for_each
(
i
,
label
,
tp
)
{
if
(
!
aa_ns_visible
(
profile
->
ns
,
tp
->
ns
,
subns
))
continue
;
state
=
match_component
(
profile
,
tp
,
stack
,
state
);
if
(
!
state
)
goto
fail
;
goto
next
;
}
/* no component visible */
*
perms
=
allperms
;
return
0
;
next:
label_for_each_cont
(
i
,
label
,
tp
)
{
if
(
!
aa_ns_visible
(
profile
->
ns
,
tp
->
ns
,
subns
))
continue
;
state
=
aa_dfa_match
(
profile
->
file
.
dfa
,
state
,
"//&"
);
state
=
match_component
(
profile
,
tp
,
false
,
state
);
if
(
!
state
)
goto
fail
;
}
*
perms
=
aa_compute_fperms
(
profile
->
file
.
dfa
,
state
,
&
cond
);
aa_apply_modes_to_perms
(
profile
,
perms
);
if
((
perms
->
allow
&
request
)
!=
request
)
return
-
EACCES
;
return
0
;
fail:
*
perms
=
nullperms
;
return
-
EACCES
;
}
/**
* label_components_match - find perms for all subcomponents of a label
* @profile: profile to find perms for
* @label: label to check access permissions for
* @stack: whether this is a stacking request
* @start: state to start match in
* @subns: whether to do permission checks on components in a subns
* @request: permissions to request
* @perms: an initialized perms struct to add accumulation to
*
* Returns: 0 on success else ERROR
*
* For the label A//&B//&C this does the perm match for each of A and B and C
* @perms should be preinitialized with allperms OR a previous permission
* check to be stacked.
*/
static
int
label_components_match
(
struct
aa_profile
*
profile
,
struct
aa_label
*
label
,
bool
stack
,
unsigned
int
start
,
bool
subns
,
u32
request
,
struct
aa_perms
*
perms
)
{
struct
aa_profile
*
tp
;
struct
label_it
i
;
struct
aa_perms
tmp
;
struct
path_cond
cond
=
{
};
unsigned
int
state
=
0
;
/* find first subcomponent to test */
label_for_each
(
i
,
label
,
tp
)
{
if
(
!
aa_ns_visible
(
profile
->
ns
,
tp
->
ns
,
subns
))
continue
;
state
=
match_component
(
profile
,
tp
,
stack
,
start
);
if
(
!
state
)
goto
fail
;
goto
next
;
}
/* no subcomponents visible - no change in perms */
return
0
;
next:
tmp
=
aa_compute_fperms
(
profile
->
file
.
dfa
,
state
,
&
cond
);
aa_apply_modes_to_perms
(
profile
,
&
tmp
);
aa_perms_accum
(
perms
,
&
tmp
);
label_for_each_cont
(
i
,
label
,
tp
)
{
if
(
!
aa_ns_visible
(
profile
->
ns
,
tp
->
ns
,
subns
))
continue
;
state
=
match_component
(
profile
,
tp
,
stack
,
start
);
if
(
!
state
)
goto
fail
;
tmp
=
aa_compute_fperms
(
profile
->
file
.
dfa
,
state
,
&
cond
);
aa_apply_modes_to_perms
(
profile
,
&
tmp
);
aa_perms_accum
(
perms
,
&
tmp
);
}
if
((
perms
->
allow
&
request
)
!=
request
)
return
-
EACCES
;
return
0
;
fail:
*
perms
=
nullperms
;
return
-
EACCES
;
}
/**
* label_match - do a multi-component label match
* @profile: profile to match against (NOT NULL)
* @label: label to match (NOT NULL)
* @stack: whether this is a stacking request
* @state: state to start in
* @subns: whether to match subns components
* @request: permission request
* @perms: Returns computed perms (NOT NULL)
*
* Returns: the state the match finished in, may be the none matching state
*/
static
int
label_match
(
struct
aa_profile
*
profile
,
struct
aa_label
*
label
,
bool
stack
,
unsigned
int
state
,
bool
subns
,
u32
request
,
struct
aa_perms
*
perms
)
{
int
error
;
*
perms
=
nullperms
;
error
=
label_compound_match
(
profile
,
label
,
stack
,
state
,
subns
,
request
,
perms
);
if
(
!
error
)
return
error
;
*
perms
=
allperms
;
return
label_components_match
(
profile
,
label
,
stack
,
state
,
subns
,
request
,
perms
);
}
/******* end TODO: dedup *****/
/**
* change_profile_perms - find permissions for change_profile
* @profile: the current profile (NOT NULL)
* @
ns: the namespace being switched to
(NOT NULL)
* @
name: the name of the profile to change to (NOT NULL)
* @
target: label to transition to
(NOT NULL)
* @
stack: whether this is a stacking request
* @request: requested perms
* @start: state to start matching in
*
*
* Returns: permission set
*
* currently only matches full label A//&B//&C or individual components A, B, C
* not arbitrary combinations. Eg. A//&B, C
*/
static
struct
aa_perms
change_profile_perms
(
struct
aa_profile
*
profile
,
struct
aa_ns
*
ns
,
const
char
*
name
,
u32
request
,
unsigned
int
start
)
static
int
change_profile_perms
(
struct
aa_profile
*
profile
,
struct
aa_label
*
target
,
bool
stack
,
u32
request
,
unsigned
int
start
,
struct
aa_perms
*
perms
)
{
if
(
profile_unconfined
(
profile
))
{
perms
->
allow
=
AA_MAY_CHANGE_PROFILE
|
AA_MAY_ONEXEC
;
perms
->
audit
=
perms
->
quiet
=
perms
->
kill
=
0
;
return
0
;
}
/* TODO: add profile in ns screening */
return
label_match
(
profile
,
target
,
stack
,
start
,
true
,
request
,
perms
);
}
static
struct
aa_perms
change_profile_perms_wrapper
(
struct
aa_profile
*
profile
,
struct
aa_profile
*
target
,
u32
request
,
unsigned
int
start
)
{
struct
aa_perms
perms
;
struct
path_cond
cond
=
{
};
unsigned
int
state
;
if
(
profile_unconfined
(
profile
))
{
perms
.
allow
=
AA_MAY_CHANGE_PROFILE
|
AA_MAY_ONEXEC
;
perms
.
audit
=
perms
.
quiet
=
perms
.
kill
=
0
;
return
perms
;
}
else
if
(
!
profile
->
file
.
dfa
)
{
return
nullperms
;
}
else
if
((
ns
==
profile
->
ns
))
{
/* try matching against rules with out namespace prepended */
aa_str_perms
(
profile
->
file
.
dfa
,
start
,
name
,
&
cond
,
&
perms
);
if
(
COMBINED_PERM_MASK
(
perms
)
&
request
)
return
perms
;
}
/* try matching with namespace name and then profile */
state
=
aa_dfa_match
(
profile
->
file
.
dfa
,
start
,
ns
->
base
.
name
);
state
=
aa_dfa_match_len
(
profile
->
file
.
dfa
,
state
,
":"
,
1
);
aa_str_perms
(
profile
->
file
.
dfa
,
state
,
name
,
&
cond
,
&
perms
);
if
(
change_profile_perms
(
profile
,
&
target
->
label
,
false
,
request
,
start
,
&
perms
))
return
nullperms
;
return
perms
;
}
...
...
@@ -173,10 +367,10 @@ static struct aa_profile *__attach_match(const char *name,
* @list: list to search (NOT NULL)
* @name: the executable name to match against (NOT NULL)
*
* Returns:
profile
or NULL if no match found
* Returns:
label
or NULL if no match found
*/
static
struct
aa_
profile
*
find_attach
(
struct
aa_ns
*
ns
,
struct
list_head
*
list
,
const
char
*
name
)
static
struct
aa_
label
*
find_attach
(
struct
aa_ns
*
ns
,
struct
list_head
*
list
,
const
char
*
name
)
{
struct
aa_profile
*
profile
;
...
...
@@ -184,49 +378,7 @@ static struct aa_profile *find_attach(struct aa_ns *ns,
profile
=
aa_get_profile
(
__attach_match
(
name
,
list
));
rcu_read_unlock
();
return
profile
;
}
/**
* separate_fqname - separate the namespace and profile names
* @fqname: the fqname name to split (NOT NULL)
* @ns_name: the namespace name if it exists (NOT NULL)
*
* This is the xtable equivalent routine of aa_split_fqname. It finds the
* split in an xtable fqname which contains an embedded \0 instead of a :
* if a namespace is specified. This is done so the xtable is constant and
* isn't re-split on every lookup.
*
* Either the profile or namespace name may be optional but if the namespace
* is specified the profile name termination must be present. This results
* in the following possible encodings:
* profile_name\0
* :ns_name\0profile_name\0
* :ns_name\0\0
*
* NOTE: the xtable fqname is pre-validated at load time in unpack_trans_table
*
* Returns: profile name if it is specified else NULL
*/
static
const
char
*
separate_fqname
(
const
char
*
fqname
,
const
char
**
ns_name
)
{
const
char
*
name
;
if
(
fqname
[
0
]
==
':'
)
{
/* In this case there is guaranteed to be two \0 terminators
* in the string. They are verified at load time by
* by unpack_trans_table
*/
*
ns_name
=
fqname
+
1
;
/* skip : */
name
=
*
ns_name
+
strlen
(
*
ns_name
)
+
1
;
if
(
!*
name
)
name
=
NULL
;
}
else
{
*
ns_name
=
NULL
;
name
=
fqname
;
}
return
name
;
return
profile
?
&
profile
->
label
:
NULL
;
}
static
const
char
*
next_name
(
int
xtype
,
const
char
*
name
)
...
...
@@ -238,295 +390,477 @@ static const char *next_name(int xtype, const char *name)
* x_table_lookup - lookup an x transition name via transition table
* @profile: current profile (NOT NULL)
* @xindex: index into x transition table
* @name: returns: name tested to find label (NOT NULL)
*
* Returns: refcounted
profile
, or NULL on failure (MAYBE NULL)
* Returns: refcounted
label
, or NULL on failure (MAYBE NULL)
*/
static
struct
aa_profile
*
x_table_lookup
(
struct
aa_profile
*
profile
,
u32
xindex
)
static
struct
aa_label
*
x_table_lookup
(
struct
aa_profile
*
profile
,
u32
xindex
,
const
char
**
name
)
{
struct
aa_profile
*
new_profile
=
NULL
;
struct
aa_ns
*
ns
=
profile
->
ns
;
struct
aa_label
*
label
=
NULL
;
u32
xtype
=
xindex
&
AA_X_TYPE_MASK
;
int
index
=
xindex
&
AA_X_INDEX_MASK
;
const
char
*
name
;
/* index is guaranteed to be in range, validated at load time */
for
(
name
=
profile
->
file
.
trans
.
table
[
index
];
!
new_profile
&&
name
;
name
=
next_name
(
xtype
,
name
))
{
struct
aa_ns
*
new_ns
;
const
char
*
xname
=
NULL
;
AA_BUG
(
!
name
);
new_ns
=
NULL
;
/* index is guaranteed to be in range, validated at load time */
/* TODO: move lookup parsing to unpack time so this is a straight
* index into the resultant label
*/
for
(
*
name
=
profile
->
file
.
trans
.
table
[
index
];
!
label
&&
*
name
;
*
name
=
next_name
(
xtype
,
*
name
))
{
if
(
xindex
&
AA_X_CHILD
)
{
struct
aa_profile
*
new_profile
;
/* release by caller */
new_profile
=
aa_find_child
(
profile
,
name
);
new_profile
=
aa_find_child
(
profile
,
*
name
);
if
(
new_profile
)
label
=
&
new_profile
->
label
;
continue
;
}
else
if
(
*
name
==
':'
)
{
/* switching namespace */
const
char
*
ns_name
;
xname
=
name
=
separate_fqname
(
name
,
&
ns_name
);
if
(
!
xname
)
/* no name so use profile name */
xname
=
profile
->
base
.
hname
;
if
(
*
ns_name
==
'@'
)
{
/* TODO: variable support */
;
}
/* released below */
new_ns
=
aa_find_ns
(
ns
,
ns_name
);
if
(
!
new_ns
)
continue
;
}
else
if
(
*
name
==
'@'
)
{
/* TODO: variable support */
continue
;
}
else
{
/* basic namespace lookup */
xname
=
name
;
}
/* released by caller */
new_profile
=
aa_lookup_profile
(
new_ns
?
new_ns
:
ns
,
xname
);
aa_put_ns
(
new_ns
)
;
label
=
aa_label_parse
(
&
profile
->
label
,
*
name
,
GFP_ATOMIC
,
true
,
false
);
if
(
IS_ERR
(
label
))
label
=
NULL
;
}
/* released by caller */
return
new_profile
;
return
label
;
}
/**
* x_to_
profile - get target profile
for a given xindex
* x_to_
label - get target label
for a given xindex
* @profile: current profile (NOT NULL)
* @name: name to lookup (NOT NULL)
* @xindex: index into x transition table
* @lookupname: returns: name used in lookup if one was specified (NOT NULL)
*
* find
profile
for a transition index
* find
label
for a transition index
*
* Returns: refcounted
profile
or NULL if not found available
* Returns: refcounted
label
or NULL if not found available
*/
static
struct
aa_profile
*
x_to_profile
(
struct
aa_profile
*
profile
,
const
char
*
name
,
u32
xindex
)
static
struct
aa_label
*
x_to_label
(
struct
aa_profile
*
profile
,
const
char
*
name
,
u32
xindex
,
const
char
**
lookupname
,
const
char
**
info
)
{
struct
aa_
profile
*
new_profile
=
NULL
;
struct
aa_
label
*
new
=
NULL
;
struct
aa_ns
*
ns
=
profile
->
ns
;
u32
xtype
=
xindex
&
AA_X_TYPE_MASK
;
const
char
*
stack
=
NULL
;
switch
(
xtype
)
{
case
AA_X_NONE
:
/* fail exec unless ix || ux fallback - handled by caller */
return
NULL
;
*
lookupname
=
NULL
;
break
;
case
AA_X_TABLE
:
/* TODO: fix when perm mapping done at unload */
stack
=
profile
->
file
.
trans
.
table
[
xindex
&
AA_X_INDEX_MASK
];
if
(
*
stack
!=
'&'
)
{
/* released by caller */
new
=
x_table_lookup
(
profile
,
xindex
,
lookupname
);
stack
=
NULL
;
break
;
}
/* fall through to X_NAME */
case
AA_X_NAME
:
if
(
xindex
&
AA_X_CHILD
)
/* released by caller */
new
_profile
=
find_attach
(
ns
,
&
profile
->
base
.
profiles
,
name
);
new
=
find_attach
(
ns
,
&
profile
->
base
.
profiles
,
name
);
else
/* released by caller */
new_profile
=
find_attach
(
ns
,
&
ns
->
base
.
profiles
,
name
);
break
;
case
AA_X_TABLE
:
/* released by caller */
new_profile
=
x_table_lookup
(
profile
,
xindex
);
new
=
find_attach
(
ns
,
&
ns
->
base
.
profiles
,
name
);
*
lookupname
=
name
;
break
;
}
if
(
!
new
)
{
if
(
xindex
&
AA_X_INHERIT
)
{
/* (p|c|n)ix - don't change profile but do
* use the newest version
*/
*
info
=
"ix fallback"
;
/* no profile && no error */
new
=
aa_get_newest_label
(
&
profile
->
label
);
}
else
if
(
xindex
&
AA_X_UNCONFINED
)
{
new
=
aa_get_newest_label
(
ns_unconfined
(
profile
->
ns
));
*
info
=
"ux fallback"
;
}
}
if
(
new
&&
stack
)
{
/* base the stack on post domain transition */
struct
aa_label
*
base
=
new
;
new
=
aa_label_parse
(
base
,
stack
,
GFP_ATOMIC
,
true
,
false
);
if
(
IS_ERR
(
new
))
new
=
NULL
;
aa_put_label
(
base
);
}
/* released by caller */
return
new
_profile
;
return
new
;
}
/**
* apparmor_bprm_set_creds - set the new creds on the bprm struct
* @bprm: binprm for the exec (NOT NULL)
*
* Returns: %0 or error on failure
*/
int
apparmor_bprm_set_creds
(
struct
linux_binprm
*
bprm
)
static
struct
aa_label
*
profile_transition
(
struct
aa_profile
*
profile
,
const
struct
linux_binprm
*
bprm
,
char
*
buffer
,
struct
path_cond
*
cond
,
bool
*
secure_exec
)
{
struct
aa_task_ctx
*
ctx
;
struct
aa_label
*
label
;
struct
aa_profile
*
profile
,
*
new_profile
=
NULL
;
struct
aa_ns
*
ns
;
char
*
buffer
=
NULL
;
unsigned
int
state
;
struct
aa_label
*
new
=
NULL
;
const
char
*
info
=
NULL
,
*
name
=
NULL
,
*
target
=
NULL
;
unsigned
int
state
=
profile
->
file
.
start
;
struct
aa_perms
perms
=
{};
struct
path_cond
cond
=
{
file_inode
(
bprm
->
file
)
->
i_uid
,
file_inode
(
bprm
->
file
)
->
i_mode
};
const
char
*
name
=
NULL
,
*
info
=
NULL
;
bool
nonewprivs
=
false
;
int
error
=
0
;
if
(
bprm
->
cred_prepared
)
return
0
;
ctx
=
cred_ctx
(
bprm
->
cred
);
AA_BUG
(
!
ctx
);
label
=
aa_get_newest_label
(
ctx
->
label
);
profile
=
labels_profile
(
label
);
/* buffer freed below, name is pointer into buffer */
get_buffers
(
buffer
);
/*
* get the namespace from the replacement profile as replacement
* can change the namespace
*/
ns
=
profile
->
ns
;
state
=
profile
->
file
.
start
;
AA_BUG
(
!
profile
);
AA_BUG
(
!
bprm
);
AA_BUG
(
!
buffer
);
error
=
aa_path_name
(
&
bprm
->
file
->
f_path
,
profile
->
path_flags
,
buffer
,
&
name
,
&
info
,
profile
->
disconnected
);
if
(
error
)
{
if
(
profile_unconfined
(
profile
)
||
(
profile
->
label
.
flags
&
FLAG_IX_ON_NAME_ERROR
))
(
profile
->
label
.
flags
&
FLAG_IX_ON_NAME_ERROR
))
{
AA_DEBUG
(
"name lookup ix on error"
);
error
=
0
;
new
=
aa_get_newest_label
(
&
profile
->
label
);
}
name
=
bprm
->
filename
;
goto
audit
;
}
/* Test for onexec first as onexec directives override other
* x transitions.
*/
if
(
profile_unconfined
(
profile
))
{
/* unconfined task */
if
(
ctx
->
onexec
)
/* change_profile on exec already been granted */
new_profile
=
labels_profile
(
aa_get_label
(
ctx
->
onexec
));
else
new_profile
=
find_attach
(
ns
,
&
ns
->
base
.
profiles
,
name
);
if
(
!
new_profile
)
goto
cleanup
;
/*
* NOTE: Domain transitions from unconfined are allowed
* even when no_new_privs is set because this aways results
* in a further reduction of permissions.
*/
goto
apply
;
new
=
find_attach
(
profile
->
ns
,
&
profile
->
ns
->
base
.
profiles
,
name
);
if
(
new
)
{
AA_DEBUG
(
"unconfined attached to new label"
);
return
new
;
}
AA_DEBUG
(
"unconfined exec no attachment"
);
return
aa_get_newest_label
(
&
profile
->
label
);
}
/* find exec permissions for name */
state
=
aa_str_perms
(
profile
->
file
.
dfa
,
state
,
name
,
&
cond
,
&
perms
);
if
(
ctx
->
onexec
)
{
struct
aa_perms
cp
;
info
=
"change_profile onexec"
;
new_profile
=
labels_profile
(
aa_get_newest_label
(
ctx
->
onexec
));
if
(
!
(
perms
.
allow
&
AA_MAY_ONEXEC
))
goto
audit
;
/* test if this exec can be paired with change_profile onexec.
* onexec permission is linked to exec with a standard pairing
* exec\0change_profile
*/
state
=
aa_dfa_null_transition
(
profile
->
file
.
dfa
,
state
);
cp
=
change_profile_perms
(
profile
,
labels_ns
(
ctx
->
onexec
),
labels_profile
(
ctx
->
onexec
)
->
base
.
name
,
AA_MAY_ONEXEC
,
state
);
if
(
!
(
cp
.
allow
&
AA_MAY_ONEXEC
))
goto
audit
;
goto
apply
;
}
state
=
aa_str_perms
(
profile
->
file
.
dfa
,
state
,
name
,
cond
,
&
perms
);
if
(
perms
.
allow
&
MAY_EXEC
)
{
/* exec permission determine how to transition */
new_profile
=
x_to_profile
(
profile
,
name
,
perms
.
xindex
);
if
(
!
new_profile
)
{
if
(
perms
.
xindex
&
AA_X_INHERIT
)
{
/* (p|c|n)ix - don't change profile but do
* use the newest version, which was picked
* up above when getting profile
*/
info
=
"ix fallback"
;
new_profile
=
aa_get_profile
(
profile
);
goto
x_clear
;
}
else
if
(
perms
.
xindex
&
AA_X_UNCONFINED
)
{
new_profile
=
aa_get_newest_profile
(
ns
->
unconfined
);
info
=
"ux fallback"
;
}
else
{
error
=
-
EACCES
;
info
=
"profile not found"
;
/* remove MAY_EXEC to audit as failure */
perms
.
allow
&=
~
MAY_EXEC
;
}
new
=
x_to_label
(
profile
,
name
,
perms
.
xindex
,
&
target
,
&
info
);
if
(
new
&&
new
->
proxy
==
profile
->
label
.
proxy
&&
info
)
{
/* hack ix fallback - improve how this is detected */
goto
audit
;
}
else
if
(
!
new
)
{
error
=
-
EACCES
;
info
=
"profile transition not found"
;
/* remove MAY_EXEC to audit as failure */
perms
.
allow
&=
~
MAY_EXEC
;
}
}
else
if
(
COMPLAIN_MODE
(
profile
))
{
/* no exec permission - are we in learning mode */
new_profile
=
aa_new_null_profile
(
profile
,
false
,
name
,
GFP_ATOMIC
);
/* no exec permission - learning mode */
struct
aa_profile
*
new_profile
=
aa_new_null_profile
(
profile
,
false
,
name
,
GFP_ATOMIC
);
if
(
!
new_profile
)
{
error
=
-
ENOMEM
;
info
=
"could not create null profile"
;
}
else
}
else
{
error
=
-
EACCES
;
new
=
&
new_profile
->
label
;
}
perms
.
xindex
|=
AA_X_UNSAFE
;
}
else
/* fail exec */
error
=
-
EACCES
;
/*
* Policy has specified a domain transition, if no_new_privs then
* fail the exec.
if
(
!
new
)
goto
audit
;
/* Policy has specified a domain transitions. if no_new_privs and
* confined and not transitioning to the current domain fail.
*
* NOTE: Domain transitions from unconfined and to stritly stacked
* subsets are allowed even when no_new_privs is set because this
* aways results in a further reduction of permissions.
*/
if
(
bprm
->
unsafe
&
LSM_UNSAFE_NO_NEW_PRIVS
)
{
if
((
bprm
->
unsafe
&
LSM_UNSAFE_NO_NEW_PRIVS
)
&&
!
profile_unconfined
(
profile
)
&&
!
aa_label_is_subset
(
new
,
&
profile
->
label
))
{
error
=
-
EPERM
;
goto
cleanup
;
info
=
"no new privs"
;
nonewprivs
=
true
;
perms
.
allow
&=
~
MAY_EXEC
;
goto
audit
;
}
if
(
!
(
perms
.
xindex
&
AA_X_UNSAFE
))
{
if
(
DEBUG_ON
)
{
dbg_printk
(
"apparmor: scrubbing environment variables"
" for %s profile="
,
name
);
aa_label_printk
(
new
,
GFP_ATOMIC
);
dbg_printk
(
"
\n
"
);
}
*
secure_exec
=
true
;
}
if
(
!
new_profile
)
audit:
aa_audit_file
(
profile
,
&
perms
,
OP_EXEC
,
MAY_EXEC
,
name
,
target
,
new
,
cond
->
uid
,
info
,
error
);
if
(
!
new
||
nonewprivs
)
{
aa_put_label
(
new
);
return
ERR_PTR
(
error
);
}
return
new
;
}
static
int
profile_onexec
(
struct
aa_profile
*
profile
,
struct
aa_label
*
onexec
,
bool
stack
,
const
struct
linux_binprm
*
bprm
,
char
*
buffer
,
struct
path_cond
*
cond
,
bool
*
secure_exec
)
{
unsigned
int
state
=
profile
->
file
.
start
;
struct
aa_perms
perms
=
{};
const
char
*
xname
=
NULL
,
*
info
=
"change_profile onexec"
;
int
error
=
-
EACCES
;
AA_BUG
(
!
profile
);
AA_BUG
(
!
onexec
);
AA_BUG
(
!
bprm
);
AA_BUG
(
!
buffer
);
if
(
profile_unconfined
(
profile
))
{
/* change_profile on exec already granted */
/*
* NOTE: Domain transitions from unconfined are allowed
* even when no_new_privs is set because this aways results
* in a further reduction of permissions.
*/
return
0
;
}
error
=
aa_path_name
(
&
bprm
->
file
->
f_path
,
profile
->
path_flags
,
buffer
,
&
xname
,
&
info
,
profile
->
disconnected
);
if
(
error
)
{
if
(
profile_unconfined
(
profile
)
||
(
profile
->
label
.
flags
&
FLAG_IX_ON_NAME_ERROR
))
{
AA_DEBUG
(
"name lookup ix on error"
);
error
=
0
;
}
xname
=
bprm
->
filename
;
goto
audit
;
}
/* find exec permissions for name */
state
=
aa_str_perms
(
profile
->
file
.
dfa
,
state
,
xname
,
cond
,
&
perms
);
if
(
!
(
perms
.
allow
&
AA_MAY_ONEXEC
))
{
info
=
"no change_onexec valid for executable"
;
goto
audit
;
}
/* test if this exec can be paired with change_profile onexec.
* onexec permission is linked to exec with a standard pairing
* exec\0change_profile
*/
state
=
aa_dfa_null_transition
(
profile
->
file
.
dfa
,
state
);
error
=
change_profile_perms
(
profile
,
onexec
,
stack
,
AA_MAY_ONEXEC
,
state
,
&
perms
);
if
(
error
)
{
perms
.
allow
&=
~
AA_MAY_ONEXEC
;
goto
audit
;
}
/* Policy has specified a domain transitions. if no_new_privs and
* confined and not transitioning to the current domain fail.
*
* NOTE: Domain transitions from unconfined and to stritly stacked
* subsets are allowed even when no_new_privs is set because this
* aways results in a further reduction of permissions.
*/
if
((
bprm
->
unsafe
&
LSM_UNSAFE_NO_NEW_PRIVS
)
&&
!
profile_unconfined
(
profile
)
&&
!
aa_label_is_subset
(
onexec
,
&
profile
->
label
))
{
error
=
-
EPERM
;
info
=
"no new privs"
;
perms
.
allow
&=
~
AA_MAY_ONEXEC
;
goto
audit
;
}
if
(
!
(
perms
.
xindex
&
AA_X_UNSAFE
))
{
if
(
DEBUG_ON
)
{
dbg_printk
(
"apparmor: scrubbing environment "
"variables for %s label="
,
xname
);
aa_label_printk
(
onexec
,
GFP_ATOMIC
);
dbg_printk
(
"
\n
"
);
}
*
secure_exec
=
true
;
}
audit:
return
aa_audit_file
(
profile
,
&
perms
,
OP_EXEC
,
AA_MAY_ONEXEC
,
xname
,
NULL
,
onexec
,
cond
->
uid
,
info
,
error
);
}
/* ensure none ns domain transitions are correctly applied with onexec */
static
struct
aa_label
*
handle_onexec
(
struct
aa_label
*
label
,
struct
aa_label
*
onexec
,
bool
stack
,
const
struct
linux_binprm
*
bprm
,
char
*
buffer
,
struct
path_cond
*
cond
,
bool
*
unsafe
)
{
struct
aa_profile
*
profile
;
struct
aa_label
*
new
;
int
error
;
AA_BUG
(
!
label
);
AA_BUG
(
!
onexec
);
AA_BUG
(
!
bprm
);
AA_BUG
(
!
buffer
);
if
(
!
stack
)
{
error
=
fn_for_each_in_ns
(
label
,
profile
,
profile_onexec
(
profile
,
onexec
,
stack
,
bprm
,
buffer
,
cond
,
unsafe
));
if
(
error
)
return
ERR_PTR
(
error
);
new
=
fn_label_build_in_ns
(
label
,
profile
,
GFP_ATOMIC
,
aa_get_newest_label
(
onexec
),
profile_transition
(
profile
,
bprm
,
buffer
,
cond
,
unsafe
));
}
else
{
/* TODO: determine how much we want to losen this */
error
=
fn_for_each_in_ns
(
label
,
profile
,
profile_onexec
(
profile
,
onexec
,
stack
,
bprm
,
buffer
,
cond
,
unsafe
));
if
(
error
)
return
ERR_PTR
(
error
);
new
=
fn_label_build_in_ns
(
label
,
profile
,
GFP_ATOMIC
,
aa_label_merge
(
&
profile
->
label
,
onexec
,
GFP_ATOMIC
),
profile_transition
(
profile
,
bprm
,
buffer
,
cond
,
unsafe
));
}
if
(
new
)
return
new
;
/* TODO: get rid of GLOBAL_ROOT_UID */
error
=
fn_for_each_in_ns
(
label
,
profile
,
aa_audit_file
(
profile
,
&
nullperms
,
OP_CHANGE_ONEXEC
,
AA_MAY_ONEXEC
,
bprm
->
filename
,
NULL
,
onexec
,
GLOBAL_ROOT_UID
,
"failed to build target label"
,
-
ENOMEM
));
return
ERR_PTR
(
error
);
}
/**
* apparmor_bprm_set_creds - set the new creds on the bprm struct
* @bprm: binprm for the exec (NOT NULL)
*
* Returns: %0 or error on failure
*
* TODO: once the other paths are done see if we can't refactor into a fn
*/
int
apparmor_bprm_set_creds
(
struct
linux_binprm
*
bprm
)
{
struct
aa_task_ctx
*
ctx
;
struct
aa_label
*
label
,
*
new
=
NULL
;
struct
aa_profile
*
profile
;
char
*
buffer
=
NULL
;
const
char
*
info
=
NULL
;
int
error
=
0
;
bool
unsafe
=
false
;
struct
path_cond
cond
=
{
file_inode
(
bprm
->
file
)
->
i_uid
,
file_inode
(
bprm
->
file
)
->
i_mode
};
if
(
bprm
->
cred_prepared
)
return
0
;
ctx
=
cred_ctx
(
bprm
->
cred
);
AA_BUG
(
!
ctx
);
label
=
aa_get_newest_label
(
ctx
->
label
);
/* buffer freed below, name is pointer into buffer */
get_buffers
(
buffer
);
/* Test for onexec first as onexec override other x transitions. */
if
(
ctx
->
onexec
)
new
=
handle_onexec
(
label
,
ctx
->
onexec
,
ctx
->
token
,
bprm
,
buffer
,
&
cond
,
&
unsafe
);
else
new
=
fn_label_build
(
label
,
profile
,
GFP_ATOMIC
,
profile_transition
(
profile
,
bprm
,
buffer
,
&
cond
,
&
unsafe
));
AA_BUG
(
!
new
);
if
(
IS_ERR
(
new
))
{
error
=
PTR_ERR
(
new
);
goto
done
;
}
else
if
(
!
new
)
{
error
=
-
ENOMEM
;
goto
done
;
}
/* TODO: Add ns level no_new_privs subset test */
if
(
bprm
->
unsafe
&
LSM_UNSAFE_SHARE
)
{
/* FIXME: currently don't mediate shared state */
;
}
if
(
bprm
->
unsafe
&
LSM_UNSAFE_PTRACE
)
{
error
=
may_change_ptraced_domain
(
&
new_profile
->
label
,
&
info
);
if
(
bprm
->
unsafe
&
(
LSM_UNSAFE_PTRACE
))
{
/* TODO: test needs to be profile of label to new */
error
=
may_change_ptraced_domain
(
new
,
&
info
);
if
(
error
)
goto
audit
;
}
/* Determine if secure exec is needed.
* Can be at this point for the following reasons:
* 1. unconfined switching to confined
* 2. confined switching to different confinement
* 3. confined switching to unconfined
*
* Cases 2 and 3 are marked as requiring secure exec
* (unless policy specified "unsafe exec")
*
* bprm->unsafe is used to cache the AA_X_UNSAFE permission
* to avoid having to recompute in secureexec
*/
if
(
!
(
perms
.
xindex
&
AA_X_UNSAFE
))
{
AA_DEBUG
(
"scrubbing environment variables for %s profile=%s
\n
"
,
name
,
new_profile
->
base
.
hname
);
if
(
unsafe
)
{
if
(
DEBUG_ON
)
{
dbg_printk
(
"scrubbing environment variables for %s "
"label="
,
bprm
->
filename
);
aa_label_printk
(
new
,
GFP_ATOMIC
);
dbg_printk
(
"
\n
"
);
}
bprm
->
unsafe
|=
AA_SECURE_X_NEEDED
;
}
apply:
/* when transitioning profiles clear unsafe personality bits */
bprm
->
per_clear
|=
PER_CLEAR_ON_SETID
;
x_clear:
if
(
label
->
proxy
!=
new
->
proxy
)
{
/* when transitioning clear unsafe personality bits */
if
(
DEBUG_ON
)
{
dbg_printk
(
"apparmor: clearing unsafe personality "
"bits. %s label="
,
bprm
->
filename
);
aa_label_printk
(
new
,
GFP_ATOMIC
);
dbg_printk
(
"
\n
"
);
}
bprm
->
per_clear
|=
PER_CLEAR_ON_SETID
;
}
aa_put_label
(
ctx
->
label
);
/* transfer new profile reference will be released when ctx is freed */
ctx
->
label
=
&
new_profile
->
label
;
new_profile
=
NULL
;
/* transfer reference, released when ctx is freed */
ctx
->
label
=
new
;
/* clear out all temporary/transitional state from the context */
done:
/* clear out temporary/transitional state from the context */
aa_clear_task_ctx_trans
(
ctx
);
audit:
error
=
aa_audit_file
(
profile
,
&
perms
,
OP_EXEC
,
MAY_EXEC
,
name
,
new_profile
?
new_profile
->
base
.
hname
:
NULL
,
new_profile
?
&
new_profile
->
label
:
NULL
,
cond
.
uid
,
info
,
error
);
cleanup:
aa_put_profile
(
new_profile
);
aa_put_label
(
label
);
put_buffers
(
buffer
);
return
error
;
audit:
error
=
fn_for_each
(
label
,
profile
,
aa_audit_file
(
profile
,
&
nullperms
,
OP_EXEC
,
MAY_EXEC
,
bprm
->
filename
,
NULL
,
new
,
file_inode
(
bprm
->
file
)
->
i_uid
,
info
,
error
));
aa_put_label
(
new
);
goto
done
;
}
/**
...
...
@@ -778,8 +1112,8 @@ int aa_change_profile(const char *fqname, int flags)
}
}
perms
=
change_profile_perms
(
profile
,
target
->
ns
,
target
->
base
.
hname
,
request
,
profile
->
file
.
start
);
perms
=
change_profile_perms
_wrapper
(
profile
,
target
,
request
,
profile
->
file
.
start
);
if
(
!
(
perms
.
allow
&
request
))
{
error
=
-
EACCES
;
goto
audit
;
...
...
security/apparmor/include/lib.h
浏览文件 @
93c98a48
...
...
@@ -211,4 +211,89 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix,
const
char
*
name
,
gfp_t
gfp
);
void
aa_policy_destroy
(
struct
aa_policy
*
policy
);
#endif
/* AA_LIB_H */
/*
* fn_label_build - abstract out the build of a label transition
* @L: label the transition is being computed for
* @P: profile parameter derived from L by this macro, can be passed to FN
* @GFP: memory allocation type to use
* @FN: fn to call for each profile transition. @P is set to the profile
*
* Returns: new label on success
* ERR_PTR if build @FN fails
* NULL if label_build fails due to low memory conditions
*
* @FN must return a label or ERR_PTR on failure. NULL is not allowed
*/
#define fn_label_build(L, P, GFP, FN) \
({ \
__label__ __cleanup, __done; \
struct aa_label *__new_; \
\
if ((L)->size > 1) { \
/* TODO: add cache of transitions already done */
\
struct label_it __i; \
int __j, __k, __count; \
DEFINE_VEC(label, __lvec); \
DEFINE_VEC(profile, __pvec); \
if (vec_setup(label, __lvec, (L)->size, (GFP))) { \
__new_ = NULL; \
goto __done; \
} \
__j = 0; \
label_for_each(__i, (L), (P)) { \
__new_ = (FN); \
AA_BUG(!__new_); \
if (IS_ERR(__new_)) \
goto __cleanup; \
__lvec[__j++] = __new_; \
} \
for (__j = __count = 0; __j < (L)->size; __j++) \
__count += __lvec[__j]->size; \
if (!vec_setup(profile, __pvec, __count, (GFP))) { \
for (__j = __k = 0; __j < (L)->size; __j++) { \
label_for_each(__i, __lvec[__j], (P)) \
__pvec[__k++] = aa_get_profile(P); \
} \
__count -= aa_vec_unique(__pvec, __count, 0); \
if (__count > 1) { \
__new_ = aa_vec_find_or_create_label(__pvec,\
__count, (GFP)); \
/* only fails if out of Mem */
\
if (!__new_) \
__new_ = NULL; \
} else \
__new_ = aa_get_label(&__pvec[0]->label); \
vec_cleanup(profile, __pvec, __count); \
} else \
__new_ = NULL; \
__cleanup: \
vec_cleanup(label, __lvec, (L)->size); \
} else { \
(P) = labels_profile(L); \
__new_ = (FN); \
} \
__done: \
if (!__new_) \
AA_DEBUG("label build failed\n"); \
(__new_); \
})
#define __fn_build_in_ns(NS, P, NS_FN, OTHER_FN) \
({ \
struct aa_label *__new; \
if ((P)->ns != (NS)) \
__new = (OTHER_FN); \
else \
__new = (NS_FN); \
(__new); \
})
#define fn_label_build_in_ns(L, P, GFP, NS_FN, OTHER_FN) \
({ \
fn_label_build((L), (P), (GFP), \
__fn_build_in_ns(labels_ns(L), (P), (NS_FN), (OTHER_FN))); \
})
#endif
/* __AA_LIB_H */
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录