Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
cloud-kernel
提交
bd71164a
cloud-kernel
项目概览
openanolis
/
cloud-kernel
1 年多 前同步成功
通知
160
Star
36
Fork
7
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
10
列表
看板
标记
里程碑
合并请求
2
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
cloud-kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
10
Issue
10
列表
看板
标记
里程碑
合并请求
2
合并请求
2
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
bd71164a
编写于
5月 12, 2013
作者:
J
James Morris
浏览文件
操作
浏览文件
下载
差异文件
Merge tag 'aa-3.10' of
git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
into ra-next
上级
f722406f
2654bfbc
变更
19
显示空白变更内容
内联
并排
Showing
19 changed file
with
286 addition
and
248 deletion
+286
-248
security/apparmor/audit.c
security/apparmor/audit.c
+1
-1
security/apparmor/context.c
security/apparmor/context.c
+28
-16
security/apparmor/domain.c
security/apparmor/domain.c
+9
-17
security/apparmor/include/apparmor.h
security/apparmor/include/apparmor.h
+11
-1
security/apparmor/include/context.h
security/apparmor/include/context.h
+43
-18
security/apparmor/include/file.h
security/apparmor/include/file.h
+7
-7
security/apparmor/include/match.h
security/apparmor/include/match.h
+13
-8
security/apparmor/include/policy.h
security/apparmor/include/policy.h
+8
-8
security/apparmor/include/procattr.h
security/apparmor/include/procattr.h
+0
-1
security/apparmor/include/sid.h
security/apparmor/include/sid.h
+3
-1
security/apparmor/ipc.c
security/apparmor/ipc.c
+4
-9
security/apparmor/lib.c
security/apparmor/lib.c
+13
-7
security/apparmor/lsm.c
security/apparmor/lsm.c
+34
-35
security/apparmor/match.c
security/apparmor/match.c
+12
-11
security/apparmor/path.c
security/apparmor/path.c
+1
-1
security/apparmor/policy.c
security/apparmor/policy.c
+84
-97
security/apparmor/policy_unpack.c
security/apparmor/policy_unpack.c
+3
-1
security/apparmor/procattr.c
security/apparmor/procattr.c
+0
-6
security/apparmor/resource.c
security/apparmor/resource.c
+12
-3
未找到文件。
security/apparmor/audit.c
浏览文件 @
bd71164a
...
@@ -88,7 +88,7 @@ static const char *const aa_audit_type[] = {
...
@@ -88,7 +88,7 @@ static const char *const aa_audit_type[] = {
"HINT"
,
"HINT"
,
"STATUS"
,
"STATUS"
,
"ERROR"
,
"ERROR"
,
"KILLED"
"KILLED"
,
"AUTO"
"AUTO"
};
};
...
...
security/apparmor/context.c
浏览文件 @
bd71164a
...
@@ -68,6 +68,23 @@ void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old)
...
@@ -68,6 +68,23 @@ void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old)
aa_get_profile
(
new
->
onexec
);
aa_get_profile
(
new
->
onexec
);
}
}
/**
* aa_get_task_profile - Get another task's profile
* @task: task to query (NOT NULL)
*
* Returns: counted reference to @task's profile
*/
struct
aa_profile
*
aa_get_task_profile
(
struct
task_struct
*
task
)
{
struct
aa_profile
*
p
;
rcu_read_lock
();
p
=
aa_get_profile
(
__aa_task_profile
(
task
));
rcu_read_unlock
();
return
p
;
}
/**
/**
* aa_replace_current_profile - replace the current tasks profiles
* aa_replace_current_profile - replace the current tasks profiles
* @profile: new profile (NOT NULL)
* @profile: new profile (NOT NULL)
...
@@ -76,7 +93,7 @@ void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old)
...
@@ -76,7 +93,7 @@ void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old)
*/
*/
int
aa_replace_current_profile
(
struct
aa_profile
*
profile
)
int
aa_replace_current_profile
(
struct
aa_profile
*
profile
)
{
{
struct
aa_task_cxt
*
cxt
=
current_c
red
()
->
security
;
struct
aa_task_cxt
*
cxt
=
current_c
xt
()
;
struct
cred
*
new
;
struct
cred
*
new
;
BUG_ON
(
!
profile
);
BUG_ON
(
!
profile
);
...
@@ -87,17 +104,13 @@ int aa_replace_current_profile(struct aa_profile *profile)
...
@@ -87,17 +104,13 @@ int aa_replace_current_profile(struct aa_profile *profile)
if
(
!
new
)
if
(
!
new
)
return
-
ENOMEM
;
return
-
ENOMEM
;
cxt
=
new
->
security
;
cxt
=
cred_cxt
(
new
)
;
if
(
unconfined
(
profile
)
||
(
cxt
->
profile
->
ns
!=
profile
->
ns
))
{
if
(
unconfined
(
profile
)
||
(
cxt
->
profile
->
ns
!=
profile
->
ns
))
/* if switching to unconfined or a different profile namespace
/* if switching to unconfined or a different profile namespace
* clear out context state
* clear out context state
*/
*/
aa_put_profile
(
cxt
->
previous
);
aa_clear_task_cxt_trans
(
cxt
);
aa_put_profile
(
cxt
->
onexec
);
cxt
->
previous
=
NULL
;
cxt
->
onexec
=
NULL
;
cxt
->
token
=
0
;
}
/* be careful switching cxt->profile, when racing replacement it
/* be careful switching cxt->profile, when racing replacement it
* is possible that cxt->profile->replacedby is the reference keeping
* is possible that cxt->profile->replacedby is the reference keeping
* @profile valid, so make sure to get its reference before dropping
* @profile valid, so make sure to get its reference before dropping
...
@@ -123,7 +136,7 @@ int aa_set_current_onexec(struct aa_profile *profile)
...
@@ -123,7 +136,7 @@ int aa_set_current_onexec(struct aa_profile *profile)
if
(
!
new
)
if
(
!
new
)
return
-
ENOMEM
;
return
-
ENOMEM
;
cxt
=
new
->
security
;
cxt
=
cred_cxt
(
new
)
;
aa_get_profile
(
profile
);
aa_get_profile
(
profile
);
aa_put_profile
(
cxt
->
onexec
);
aa_put_profile
(
cxt
->
onexec
);
cxt
->
onexec
=
profile
;
cxt
->
onexec
=
profile
;
...
@@ -150,7 +163,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token)
...
@@ -150,7 +163,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token)
return
-
ENOMEM
;
return
-
ENOMEM
;
BUG_ON
(
!
profile
);
BUG_ON
(
!
profile
);
cxt
=
new
->
security
;
cxt
=
cred_cxt
(
new
)
;
if
(
!
cxt
->
previous
)
{
if
(
!
cxt
->
previous
)
{
/* transfer refcount */
/* transfer refcount */
cxt
->
previous
=
cxt
->
profile
;
cxt
->
previous
=
cxt
->
profile
;
...
@@ -187,7 +200,7 @@ int aa_restore_previous_profile(u64 token)
...
@@ -187,7 +200,7 @@ int aa_restore_previous_profile(u64 token)
if
(
!
new
)
if
(
!
new
)
return
-
ENOMEM
;
return
-
ENOMEM
;
cxt
=
new
->
security
;
cxt
=
cred_cxt
(
new
)
;
if
(
cxt
->
token
!=
token
)
{
if
(
cxt
->
token
!=
token
)
{
abort_creds
(
new
);
abort_creds
(
new
);
return
-
EACCES
;
return
-
EACCES
;
...
@@ -205,11 +218,10 @@ int aa_restore_previous_profile(u64 token)
...
@@ -205,11 +218,10 @@ int aa_restore_previous_profile(u64 token)
aa_get_profile
(
cxt
->
profile
);
aa_get_profile
(
cxt
->
profile
);
aa_put_profile
(
cxt
->
previous
);
aa_put_profile
(
cxt
->
previous
);
}
}
/*
clear exec && prev information when restoring to previous conte
xt */
/*
ref has been transfered so avoid putting ref in clear_task_c
xt */
cxt
->
previous
=
NULL
;
cxt
->
previous
=
NULL
;
cxt
->
token
=
0
;
/* clear exec && prev information when restoring to previous context */
aa_put_profile
(
cxt
->
onexec
);
aa_clear_task_cxt_trans
(
cxt
);
cxt
->
onexec
=
NULL
;
commit_creds
(
new
);
commit_creds
(
new
);
return
0
;
return
0
;
...
...
security/apparmor/domain.c
浏览文件 @
bd71164a
...
@@ -62,17 +62,14 @@ static int may_change_ptraced_domain(struct task_struct *task,
...
@@ -62,17 +62,14 @@ static int may_change_ptraced_domain(struct task_struct *task,
struct
aa_profile
*
to_profile
)
struct
aa_profile
*
to_profile
)
{
{
struct
task_struct
*
tracer
;
struct
task_struct
*
tracer
;
const
struct
cred
*
cred
=
NULL
;
struct
aa_profile
*
tracerp
=
NULL
;
struct
aa_profile
*
tracerp
=
NULL
;
int
error
=
0
;
int
error
=
0
;
rcu_read_lock
();
rcu_read_lock
();
tracer
=
ptrace_parent
(
task
);
tracer
=
ptrace_parent
(
task
);
if
(
tracer
)
{
if
(
tracer
)
/* released below */
/* released below */
cred
=
get_task_cred
(
tracer
);
tracerp
=
aa_get_task_profile
(
tracer
);
tracerp
=
aa_cred_profile
(
cred
);
}
/* not ptraced */
/* not ptraced */
if
(
!
tracer
||
unconfined
(
tracerp
))
if
(
!
tracer
||
unconfined
(
tracerp
))
...
@@ -82,8 +79,7 @@ static int may_change_ptraced_domain(struct task_struct *task,
...
@@ -82,8 +79,7 @@ static int may_change_ptraced_domain(struct task_struct *task,
out:
out:
rcu_read_unlock
();
rcu_read_unlock
();
if
(
cred
)
aa_put_profile
(
tracerp
);
put_cred
(
cred
);
return
error
;
return
error
;
}
}
...
@@ -360,7 +356,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
...
@@ -360,7 +356,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
if
(
bprm
->
cred_prepared
)
if
(
bprm
->
cred_prepared
)
return
0
;
return
0
;
cxt
=
bprm
->
cred
->
security
;
cxt
=
cred_cxt
(
bprm
->
cred
)
;
BUG_ON
(
!
cxt
);
BUG_ON
(
!
cxt
);
profile
=
aa_get_profile
(
aa_newest_version
(
cxt
->
profile
));
profile
=
aa_get_profile
(
aa_newest_version
(
cxt
->
profile
));
...
@@ -443,6 +439,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
...
@@ -443,6 +439,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
}
else
{
}
else
{
error
=
-
ENOENT
;
error
=
-
ENOENT
;
info
=
"profile not found"
;
info
=
"profile not found"
;
/* remove MAY_EXEC to audit as failure */
perms
.
allow
&=
~
MAY_EXEC
;
}
}
}
}
}
else
if
(
COMPLAIN_MODE
(
profile
))
{
}
else
if
(
COMPLAIN_MODE
(
profile
))
{
...
@@ -514,11 +512,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
...
@@ -514,11 +512,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
cxt
->
profile
=
new_profile
;
cxt
->
profile
=
new_profile
;
/* clear out all temporary/transitional state from the context */
/* clear out all temporary/transitional state from the context */
aa_put_profile
(
cxt
->
previous
);
aa_clear_task_cxt_trans
(
cxt
);
aa_put_profile
(
cxt
->
onexec
);
cxt
->
previous
=
NULL
;
cxt
->
onexec
=
NULL
;
cxt
->
token
=
0
;
audit:
audit:
error
=
aa_audit_file
(
profile
,
&
perms
,
GFP_KERNEL
,
OP_EXEC
,
MAY_EXEC
,
error
=
aa_audit_file
(
profile
,
&
perms
,
GFP_KERNEL
,
OP_EXEC
,
MAY_EXEC
,
...
@@ -557,7 +551,7 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm)
...
@@ -557,7 +551,7 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm)
void
apparmor_bprm_committing_creds
(
struct
linux_binprm
*
bprm
)
void
apparmor_bprm_committing_creds
(
struct
linux_binprm
*
bprm
)
{
{
struct
aa_profile
*
profile
=
__aa_current_profile
();
struct
aa_profile
*
profile
=
__aa_current_profile
();
struct
aa_task_cxt
*
new_cxt
=
bprm
->
cred
->
security
;
struct
aa_task_cxt
*
new_cxt
=
cred_cxt
(
bprm
->
cred
)
;
/* bail out if unconfined or not changing profile */
/* bail out if unconfined or not changing profile */
if
((
new_cxt
->
profile
==
profile
)
||
if
((
new_cxt
->
profile
==
profile
)
||
...
@@ -634,7 +628,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
...
@@ -634,7 +628,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
/* released below */
/* released below */
cred
=
get_current_cred
();
cred
=
get_current_cred
();
cxt
=
cred
->
security
;
cxt
=
cred
_cxt
(
cred
)
;
profile
=
aa_cred_profile
(
cred
);
profile
=
aa_cred_profile
(
cred
);
previous_profile
=
cxt
->
previous
;
previous_profile
=
cxt
->
previous
;
...
@@ -750,7 +744,6 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
...
@@ -750,7 +744,6 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
bool
permtest
)
bool
permtest
)
{
{
const
struct
cred
*
cred
;
const
struct
cred
*
cred
;
struct
aa_task_cxt
*
cxt
;
struct
aa_profile
*
profile
,
*
target
=
NULL
;
struct
aa_profile
*
profile
,
*
target
=
NULL
;
struct
aa_namespace
*
ns
=
NULL
;
struct
aa_namespace
*
ns
=
NULL
;
struct
file_perms
perms
=
{};
struct
file_perms
perms
=
{};
...
@@ -770,7 +763,6 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
...
@@ -770,7 +763,6 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
}
}
cred
=
get_current_cred
();
cred
=
get_current_cred
();
cxt
=
cred
->
security
;
profile
=
aa_cred_profile
(
cred
);
profile
=
aa_cred_profile
(
cred
);
/*
/*
...
...
security/apparmor/include/apparmor.h
浏览文件 @
bd71164a
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
#ifndef __APPARMOR_H
#ifndef __APPARMOR_H
#define __APPARMOR_H
#define __APPARMOR_H
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/fs.h>
#include "match.h"
#include "match.h"
...
@@ -64,9 +65,18 @@ extern int apparmor_initialized __initdata;
...
@@ -64,9 +65,18 @@ extern int apparmor_initialized __initdata;
/* fn's in lib */
/* fn's in lib */
char
*
aa_split_fqname
(
char
*
args
,
char
**
ns_name
);
char
*
aa_split_fqname
(
char
*
args
,
char
**
ns_name
);
void
aa_info_message
(
const
char
*
str
);
void
aa_info_message
(
const
char
*
str
);
void
*
kvmalloc
(
size_t
size
);
void
*
__aa_kvmalloc
(
size_t
size
,
gfp_t
flags
);
void
kvfree
(
void
*
buffer
);
void
kvfree
(
void
*
buffer
);
static
inline
void
*
kvmalloc
(
size_t
size
)
{
return
__aa_kvmalloc
(
size
,
0
);
}
static
inline
void
*
kvzalloc
(
size_t
size
)
{
return
__aa_kvmalloc
(
size
,
__GFP_ZERO
);
}
/**
/**
* aa_strneq - compare null terminated @str to a non null terminated substring
* aa_strneq - compare null terminated @str to a non null terminated substring
...
...
security/apparmor/include/context.h
浏览文件 @
bd71164a
...
@@ -21,6 +21,9 @@
...
@@ -21,6 +21,9 @@
#include "policy.h"
#include "policy.h"
#define cred_cxt(X) (X)->security
#define current_cxt() cred_cxt(current_cred())
/* struct aa_file_cxt - the AppArmor context the file was opened in
/* struct aa_file_cxt - the AppArmor context the file was opened in
* @perms: the permission the file was opened with
* @perms: the permission the file was opened with
*
*
...
@@ -80,23 +83,8 @@ int aa_replace_current_profile(struct aa_profile *profile);
...
@@ -80,23 +83,8 @@ int aa_replace_current_profile(struct aa_profile *profile);
int
aa_set_current_onexec
(
struct
aa_profile
*
profile
);
int
aa_set_current_onexec
(
struct
aa_profile
*
profile
);
int
aa_set_current_hat
(
struct
aa_profile
*
profile
,
u64
token
);
int
aa_set_current_hat
(
struct
aa_profile
*
profile
,
u64
token
);
int
aa_restore_previous_profile
(
u64
cookie
);
int
aa_restore_previous_profile
(
u64
cookie
);
struct
aa_profile
*
aa_get_task_profile
(
struct
task_struct
*
task
);
/**
* __aa_task_is_confined - determine if @task has any confinement
* @task: task to check confinement of (NOT NULL)
*
* If @task != current needs to be called in RCU safe critical section
*/
static
inline
bool
__aa_task_is_confined
(
struct
task_struct
*
task
)
{
struct
aa_task_cxt
*
cxt
=
__task_cred
(
task
)
->
security
;
BUG_ON
(
!
cxt
||
!
cxt
->
profile
);
if
(
unconfined
(
aa_newest_version
(
cxt
->
profile
)))
return
0
;
return
1
;
}
/**
/**
* aa_cred_profile - obtain cred's profiles
* aa_cred_profile - obtain cred's profiles
...
@@ -108,11 +96,35 @@ static inline bool __aa_task_is_confined(struct task_struct *task)
...
@@ -108,11 +96,35 @@ static inline bool __aa_task_is_confined(struct task_struct *task)
*/
*/
static
inline
struct
aa_profile
*
aa_cred_profile
(
const
struct
cred
*
cred
)
static
inline
struct
aa_profile
*
aa_cred_profile
(
const
struct
cred
*
cred
)
{
{
struct
aa_task_cxt
*
cxt
=
cred
->
security
;
struct
aa_task_cxt
*
cxt
=
cred
_cxt
(
cred
)
;
BUG_ON
(
!
cxt
||
!
cxt
->
profile
);
BUG_ON
(
!
cxt
||
!
cxt
->
profile
);
return
aa_newest_version
(
cxt
->
profile
);
return
aa_newest_version
(
cxt
->
profile
);
}
}
/**
* __aa_task_profile - retrieve another task's profile
* @task: task to query (NOT NULL)
*
* Returns: @task's profile without incrementing its ref count
*
* If @task != current needs to be called in RCU safe critical section
*/
static
inline
struct
aa_profile
*
__aa_task_profile
(
struct
task_struct
*
task
)
{
return
aa_cred_profile
(
__task_cred
(
task
));
}
/**
* __aa_task_is_confined - determine if @task has any confinement
* @task: task to check confinement of (NOT NULL)
*
* If @task != current needs to be called in RCU safe critical section
*/
static
inline
bool
__aa_task_is_confined
(
struct
task_struct
*
task
)
{
return
!
unconfined
(
__aa_task_profile
(
task
));
}
/**
/**
* __aa_current_profile - find the current tasks confining profile
* __aa_current_profile - find the current tasks confining profile
*
*
...
@@ -136,7 +148,7 @@ static inline struct aa_profile *__aa_current_profile(void)
...
@@ -136,7 +148,7 @@ static inline struct aa_profile *__aa_current_profile(void)
*/
*/
static
inline
struct
aa_profile
*
aa_current_profile
(
void
)
static
inline
struct
aa_profile
*
aa_current_profile
(
void
)
{
{
const
struct
aa_task_cxt
*
cxt
=
current_c
red
()
->
security
;
const
struct
aa_task_cxt
*
cxt
=
current_c
xt
()
;
struct
aa_profile
*
profile
;
struct
aa_profile
*
profile
;
BUG_ON
(
!
cxt
||
!
cxt
->
profile
);
BUG_ON
(
!
cxt
||
!
cxt
->
profile
);
...
@@ -151,4 +163,17 @@ static inline struct aa_profile *aa_current_profile(void)
...
@@ -151,4 +163,17 @@ static inline struct aa_profile *aa_current_profile(void)
return
profile
;
return
profile
;
}
}
/**
* aa_clear_task_cxt_trans - clear transition tracking info from the cxt
* @cxt: task context to clear (NOT NULL)
*/
static
inline
void
aa_clear_task_cxt_trans
(
struct
aa_task_cxt
*
cxt
)
{
aa_put_profile
(
cxt
->
previous
);
aa_put_profile
(
cxt
->
onexec
);
cxt
->
previous
=
NULL
;
cxt
->
onexec
=
NULL
;
cxt
->
token
=
0
;
}
#endif
/* __AA_CONTEXT_H */
#endif
/* __AA_CONTEXT_H */
security/apparmor/include/file.h
浏览文件 @
bd71164a
...
@@ -186,11 +186,6 @@ static inline void aa_free_file_rules(struct aa_file_rules *rules)
...
@@ -186,11 +186,6 @@ static inline void aa_free_file_rules(struct aa_file_rules *rules)
aa_free_domain_entries
(
&
rules
->
trans
);
aa_free_domain_entries
(
&
rules
->
trans
);
}
}
#define ACC_FMODE(x) (("\000\004\002\006"[(x)&O_ACCMODE]) | (((x) << 1) & 0x40))
/* from namei.c */
#define MAP_OPEN_FLAGS(x) ((((x) + 1) & O_ACCMODE) ? (x) + 1 : (x))
/**
/**
* aa_map_file_perms - map file flags to AppArmor permissions
* aa_map_file_perms - map file flags to AppArmor permissions
* @file: open file to map flags to AppArmor permissions
* @file: open file to map flags to AppArmor permissions
...
@@ -199,8 +194,13 @@ static inline void aa_free_file_rules(struct aa_file_rules *rules)
...
@@ -199,8 +194,13 @@ static inline void aa_free_file_rules(struct aa_file_rules *rules)
*/
*/
static
inline
u32
aa_map_file_to_perms
(
struct
file
*
file
)
static
inline
u32
aa_map_file_to_perms
(
struct
file
*
file
)
{
{
int
flags
=
MAP_OPEN_FLAGS
(
file
->
f_flags
);
int
flags
=
file
->
f_flags
;
u32
perms
=
ACC_FMODE
(
file
->
f_mode
);
u32
perms
=
0
;
if
(
file
->
f_mode
&
FMODE_WRITE
)
perms
|=
MAY_WRITE
;
if
(
file
->
f_mode
&
FMODE_READ
)
perms
|=
MAY_READ
;
if
((
flags
&
O_APPEND
)
&&
(
perms
&
MAY_WRITE
))
if
((
flags
&
O_APPEND
)
&&
(
perms
&
MAY_WRITE
))
perms
=
(
perms
&
~
MAY_WRITE
)
|
MAY_APPEND
;
perms
=
(
perms
&
~
MAY_WRITE
)
|
MAY_APPEND
;
...
...
security/apparmor/include/match.h
浏览文件 @
bd71164a
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
* This file contains AppArmor policy dfa matching engine definitions.
* This file contains AppArmor policy dfa matching engine definitions.
*
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-201
0
Canonical Ltd.
* Copyright 2009-201
2
Canonical Ltd.
*
*
* This program is free software; you can redistribute it and/or
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* modify it under the terms of the GNU General Public License as
...
@@ -16,25 +16,30 @@
...
@@ -16,25 +16,30 @@
#define __AA_MATCH_H
#define __AA_MATCH_H
#include <linux/kref.h>
#include <linux/kref.h>
#include <linux/workqueue.h>
#define DFA_NOMATCH 0
#define DFA_NOMATCH 0
#define DFA_START 1
#define DFA_START 1
#define DFA_VALID_PERM_MASK 0xffffffff
#define DFA_VALID_PERM2_MASK 0xffffffff
/**
/**
* The format used for transition tables is based on the GNU flex table
* The format used for transition tables is based on the GNU flex table
* file format (--tables-file option; see Table File Format in the flex
* file format (--tables-file option; see Table File Format in the flex
* info pages and the flex sources for documentation). The magic number
* info pages and the flex sources for documentation). The magic number
* used in the header is 0x1B5E783D instead of 0xF13C57B1 though, because
* used in the header is 0x1B5E783D instead of 0xF13C57B1 though, because
* the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used
* new tables have been defined and others YY_ID_CHK (check) and YY_ID_DEF
* slightly differently (see the apparmor-parser package).
* (default) tables are used slightly differently (see the apparmor-parser
* package).
*
*
* The data in the packed dfa is stored in network byte order, and the tables
* are arranged for flexibility. We convert the table data to host native
* byte order.
*
* The dfa begins with a table set header, and is followed by the actual
* tables.
*/
*/
#define YYTH_MAGIC 0x1B5E783D
#define YYTH_MAGIC 0x1B5E783D
#define YYTH_DEF_RECURSE 0x1
/* DEF Table is recursive */
struct
table_set_header
{
struct
table_set_header
{
u32
th_magic
;
/* YYTH_MAGIC */
u32
th_magic
;
/* YYTH_MAGIC */
...
@@ -63,7 +68,7 @@ struct table_set_header {
...
@@ -63,7 +68,7 @@ struct table_set_header {
#define YYTD_DATA32 4
#define YYTD_DATA32 4
#define YYTD_DATA64 8
#define YYTD_DATA64 8
/*
Each ACCEPT2 table
gets 6 dedicated flags, YYTD_DATAX define the
/*
ACCEPT & ACCEPT2 tables
gets 6 dedicated flags, YYTD_DATAX define the
* first flags
* first flags
*/
*/
#define ACCEPT1_FLAGS(X) ((X) & 0x3f)
#define ACCEPT1_FLAGS(X) ((X) & 0x3f)
...
...
security/apparmor/include/policy.h
浏览文件 @
bd71164a
...
@@ -32,13 +32,13 @@
...
@@ -32,13 +32,13 @@
extern
const
char
*
const
profile_mode_names
[];
extern
const
char
*
const
profile_mode_names
[];
#define APPARMOR_NAMES_MAX_INDEX 3
#define APPARMOR_NAMES_MAX_INDEX 3
#define
COMPLAIN_MODE(_profile)
\
#define
PROFILE_MODE(_profile, _mode)
\
((aa_g_profile_mode ==
APPARMOR_COMPLAIN) ||
\
((aa_g_profile_mode ==
(_mode)) ||
\
((_profile)->mode ==
APPARMOR_COMPLAIN
))
((_profile)->mode ==
(_mode)
))
#define
KILL_MODE(_profile) \
#define
COMPLAIN_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_COMPLAIN)
((aa_g_profile_mode == APPARMOR_KILL) || \
((_profile)->mode == APPARMOR_KILL)
)
#define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL
)
#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
...
@@ -105,6 +105,7 @@ struct aa_ns_acct {
...
@@ -105,6 +105,7 @@ struct aa_ns_acct {
* @acct: accounting for the namespace
* @acct: accounting for the namespace
* @unconfined: special unconfined profile for the namespace
* @unconfined: special unconfined profile for the namespace
* @sub_ns: list of namespaces under the current namespace.
* @sub_ns: list of namespaces under the current namespace.
* @uniq_null: uniq value used for null learning profiles
*
*
* An aa_namespace defines the set profiles that are searched to determine
* An aa_namespace defines the set profiles that are searched to determine
* which profile to attach to a task. Profiles can not be shared between
* which profile to attach to a task. Profiles can not be shared between
...
@@ -127,6 +128,7 @@ struct aa_namespace {
...
@@ -127,6 +128,7 @@ struct aa_namespace {
struct
aa_ns_acct
acct
;
struct
aa_ns_acct
acct
;
struct
aa_profile
*
unconfined
;
struct
aa_profile
*
unconfined
;
struct
list_head
sub_ns
;
struct
list_head
sub_ns
;
atomic_t
uniq_null
;
};
};
/* struct aa_policydb - match engine for a policy
/* struct aa_policydb - match engine for a policy
...
@@ -148,7 +150,6 @@ struct aa_policydb {
...
@@ -148,7 +150,6 @@ struct aa_policydb {
* @rename: optional profile name that this profile renamed
* @rename: optional profile name that this profile renamed
* @xmatch: optional extended matching for unconfined executables names
* @xmatch: optional extended matching for unconfined executables names
* @xmatch_len: xmatch prefix len, used to determine xmatch priority
* @xmatch_len: xmatch prefix len, used to determine xmatch priority
* @sid: the unique security id number of this profile
* @audit: the auditing mode of the profile
* @audit: the auditing mode of the profile
* @mode: the enforcement mode of the profile
* @mode: the enforcement mode of the profile
* @flags: flags controlling profile behavior
* @flags: flags controlling profile behavior
...
@@ -184,7 +185,6 @@ struct aa_profile {
...
@@ -184,7 +185,6 @@ struct aa_profile {
struct
aa_dfa
*
xmatch
;
struct
aa_dfa
*
xmatch
;
int
xmatch_len
;
int
xmatch_len
;
u32
sid
;
enum
audit_mode
audit
;
enum
audit_mode
audit
;
enum
profile_mode
mode
;
enum
profile_mode
mode
;
u32
flags
;
u32
flags
;
...
...
security/apparmor/include/procattr.h
浏览文件 @
bd71164a
...
@@ -21,6 +21,5 @@
...
@@ -21,6 +21,5 @@
int
aa_getprocattr
(
struct
aa_profile
*
profile
,
char
**
string
);
int
aa_getprocattr
(
struct
aa_profile
*
profile
,
char
**
string
);
int
aa_setprocattr_changehat
(
char
*
args
,
size_t
size
,
int
test
);
int
aa_setprocattr_changehat
(
char
*
args
,
size_t
size
,
int
test
);
int
aa_setprocattr_changeprofile
(
char
*
fqname
,
bool
onexec
,
int
test
);
int
aa_setprocattr_changeprofile
(
char
*
fqname
,
bool
onexec
,
int
test
);
int
aa_setprocattr_permipc
(
char
*
fqname
);
#endif
/* __AA_PROCATTR_H */
#endif
/* __AA_PROCATTR_H */
security/apparmor/include/sid.h
浏览文件 @
bd71164a
...
@@ -16,7 +16,9 @@
...
@@ -16,7 +16,9 @@
#include <linux/types.h>
#include <linux/types.h>
struct
aa_profile
;
/* sid value that will not be allocated */
#define AA_SID_INVALID 0
#define AA_SID_ALLOC AA_SID_INVALID
u32
aa_alloc_sid
(
void
);
u32
aa_alloc_sid
(
void
);
void
aa_free_sid
(
u32
sid
);
void
aa_free_sid
(
u32
sid
);
...
...
security/apparmor/ipc.c
浏览文件 @
bd71164a
...
@@ -95,23 +95,18 @@ int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,
...
@@ -95,23 +95,18 @@ int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,
* - tracer profile has CAP_SYS_PTRACE
* - tracer profile has CAP_SYS_PTRACE
*/
*/
struct
aa_profile
*
tracer_p
;
struct
aa_profile
*
tracer_p
=
aa_get_task_profile
(
tracer
);
/* cred released below */
const
struct
cred
*
cred
=
get_task_cred
(
tracer
);
int
error
=
0
;
int
error
=
0
;
tracer_p
=
aa_cred_profile
(
cred
);
if
(
!
unconfined
(
tracer_p
))
{
if
(
!
unconfined
(
tracer_p
))
{
/* lcred released below */
struct
aa_profile
*
tracee_p
=
aa_get_task_profile
(
tracee
);
const
struct
cred
*
lcred
=
get_task_cred
(
tracee
);
struct
aa_profile
*
tracee_p
=
aa_cred_profile
(
lcred
);
error
=
aa_may_ptrace
(
tracer
,
tracer_p
,
tracee_p
,
mode
);
error
=
aa_may_ptrace
(
tracer
,
tracer_p
,
tracee_p
,
mode
);
error
=
aa_audit_ptrace
(
tracer_p
,
tracee_p
,
error
);
error
=
aa_audit_ptrace
(
tracer_p
,
tracee_p
,
error
);
put_cred
(
lcred
);
aa_put_profile
(
tracee_p
);
}
}
put_cred
(
cred
);
aa_put_profile
(
tracer_p
);
return
error
;
return
error
;
}
}
security/apparmor/lib.c
浏览文件 @
bd71164a
...
@@ -45,8 +45,10 @@ char *aa_split_fqname(char *fqname, char **ns_name)
...
@@ -45,8 +45,10 @@ char *aa_split_fqname(char *fqname, char **ns_name)
*
ns_name
=
skip_spaces
(
&
name
[
1
]);
*
ns_name
=
skip_spaces
(
&
name
[
1
]);
if
(
split
)
{
if
(
split
)
{
/* overwrite ':' with \0 */
/* overwrite ':' with \0 */
*
split
=
0
;
*
split
++
=
0
;
name
=
skip_spaces
(
split
+
1
);
if
(
strncmp
(
split
,
"//"
,
2
)
==
0
)
split
+=
2
;
name
=
skip_spaces
(
split
);
}
else
}
else
/* a ns name without a following profile is allowed */
/* a ns name without a following profile is allowed */
name
=
NULL
;
name
=
NULL
;
...
@@ -75,15 +77,16 @@ void aa_info_message(const char *str)
...
@@ -75,15 +77,16 @@ void aa_info_message(const char *str)
}
}
/**
/**
* kvmalloc - do allocation preferring kmalloc but falling back to vmalloc
* __aa_kvmalloc - do allocation preferring kmalloc but falling back to vmalloc
* @size: size of allocation
* @size: how many bytes of memory are required
* @flags: the type of memory to allocate (see kmalloc).
*
*
* Return: allocated buffer or NULL if failed
* Return: allocated buffer or NULL if failed
*
*
* It is possible that policy being loaded from the user is larger than
* It is possible that policy being loaded from the user is larger than
* what can be allocated by kmalloc, in those cases fall back to vmalloc.
* what can be allocated by kmalloc, in those cases fall back to vmalloc.
*/
*/
void
*
kvmalloc
(
size_t
size
)
void
*
__aa_kvmalloc
(
size_t
size
,
gfp_t
flags
)
{
{
void
*
buffer
=
NULL
;
void
*
buffer
=
NULL
;
...
@@ -92,13 +95,16 @@ void *kvmalloc(size_t size)
...
@@ -92,13 +95,16 @@ void *kvmalloc(size_t size)
/* do not attempt kmalloc if we need more than 16 pages at once */
/* do not attempt kmalloc if we need more than 16 pages at once */
if
(
size
<=
(
16
*
PAGE_SIZE
))
if
(
size
<=
(
16
*
PAGE_SIZE
))
buffer
=
kmalloc
(
size
,
GFP_NOIO
|
__GFP_NOWARN
);
buffer
=
kmalloc
(
size
,
flags
|
GFP_NOIO
|
__GFP_NOWARN
);
if
(
!
buffer
)
{
if
(
!
buffer
)
{
/* see kvfree for why size must be at least work_struct size
/* see kvfree for why size must be at least work_struct size
* when allocated via vmalloc
* when allocated via vmalloc
*/
*/
if
(
size
<
sizeof
(
struct
work_struct
))
if
(
size
<
sizeof
(
struct
work_struct
))
size
=
sizeof
(
struct
work_struct
);
size
=
sizeof
(
struct
work_struct
);
if
(
flags
&
__GFP_ZERO
)
buffer
=
vzalloc
(
size
);
else
buffer
=
vmalloc
(
size
);
buffer
=
vmalloc
(
size
);
}
}
return
buffer
;
return
buffer
;
...
...
security/apparmor/lsm.c
浏览文件 @
bd71164a
...
@@ -48,8 +48,8 @@ int apparmor_initialized __initdata;
...
@@ -48,8 +48,8 @@ int apparmor_initialized __initdata;
*/
*/
static
void
apparmor_cred_free
(
struct
cred
*
cred
)
static
void
apparmor_cred_free
(
struct
cred
*
cred
)
{
{
aa_free_task_context
(
cred
->
security
);
aa_free_task_context
(
cred
_cxt
(
cred
)
);
cred
->
security
=
NULL
;
cred
_cxt
(
cred
)
=
NULL
;
}
}
/*
/*
...
@@ -62,7 +62,7 @@ static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
...
@@ -62,7 +62,7 @@ static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
if
(
!
cxt
)
if
(
!
cxt
)
return
-
ENOMEM
;
return
-
ENOMEM
;
cred
->
security
=
cxt
;
cred
_cxt
(
cred
)
=
cxt
;
return
0
;
return
0
;
}
}
...
@@ -77,8 +77,8 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
...
@@ -77,8 +77,8 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
if
(
!
cxt
)
if
(
!
cxt
)
return
-
ENOMEM
;
return
-
ENOMEM
;
aa_dup_task_context
(
cxt
,
old
->
security
);
aa_dup_task_context
(
cxt
,
cred_cxt
(
old
)
);
new
->
security
=
cxt
;
cred_cxt
(
new
)
=
cxt
;
return
0
;
return
0
;
}
}
...
@@ -87,8 +87,8 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
...
@@ -87,8 +87,8 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
*/
*/
static
void
apparmor_cred_transfer
(
struct
cred
*
new
,
const
struct
cred
*
old
)
static
void
apparmor_cred_transfer
(
struct
cred
*
new
,
const
struct
cred
*
old
)
{
{
const
struct
aa_task_cxt
*
old_cxt
=
old
->
security
;
const
struct
aa_task_cxt
*
old_cxt
=
cred_cxt
(
old
)
;
struct
aa_task_cxt
*
new_cxt
=
new
->
security
;
struct
aa_task_cxt
*
new_cxt
=
cred_cxt
(
new
)
;
aa_dup_task_context
(
new_cxt
,
old_cxt
);
aa_dup_task_context
(
new_cxt
,
old_cxt
);
}
}
...
@@ -469,7 +469,6 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd)
...
@@ -469,7 +469,6 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd)
static
int
common_mmap
(
int
op
,
struct
file
*
file
,
unsigned
long
prot
,
static
int
common_mmap
(
int
op
,
struct
file
*
file
,
unsigned
long
prot
,
unsigned
long
flags
)
unsigned
long
flags
)
{
{
struct
dentry
*
dentry
;
int
mask
=
0
;
int
mask
=
0
;
if
(
!
file
||
!
file
->
f_security
)
if
(
!
file
||
!
file
->
f_security
)
...
@@ -486,7 +485,6 @@ static int common_mmap(int op, struct file *file, unsigned long prot,
...
@@ -486,7 +485,6 @@ static int common_mmap(int op, struct file *file, unsigned long prot,
if
(
prot
&
PROT_EXEC
)
if
(
prot
&
PROT_EXEC
)
mask
|=
AA_EXEC_MMAP
;
mask
|=
AA_EXEC_MMAP
;
dentry
=
file
->
f_path
.
dentry
;
return
common_file_perm
(
op
,
file
,
mask
);
return
common_file_perm
(
op
,
file
,
mask
);
}
}
...
@@ -507,11 +505,9 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
...
@@ -507,11 +505,9 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
char
**
value
)
char
**
value
)
{
{
int
error
=
-
ENOENT
;
int
error
=
-
ENOENT
;
struct
aa_profile
*
profile
;
/* released below */
/* released below */
const
struct
cred
*
cred
=
get_task_cred
(
task
);
const
struct
cred
*
cred
=
get_task_cred
(
task
);
struct
aa_task_cxt
*
cxt
=
cred
->
security
;
struct
aa_task_cxt
*
cxt
=
cred_cxt
(
cred
);
profile
=
aa_cred_profile
(
cred
);
if
(
strcmp
(
name
,
"current"
)
==
0
)
if
(
strcmp
(
name
,
"current"
)
==
0
)
error
=
aa_getprocattr
(
aa_newest_version
(
cxt
->
profile
),
error
=
aa_getprocattr
(
aa_newest_version
(
cxt
->
profile
),
...
@@ -533,6 +529,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
...
@@ -533,6 +529,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
static
int
apparmor_setprocattr
(
struct
task_struct
*
task
,
char
*
name
,
static
int
apparmor_setprocattr
(
struct
task_struct
*
task
,
char
*
name
,
void
*
value
,
size_t
size
)
void
*
value
,
size_t
size
)
{
{
struct
common_audit_data
sa
;
struct
apparmor_audit_data
aad
=
{
0
,};
char
*
command
,
*
args
=
value
;
char
*
command
,
*
args
=
value
;
size_t
arg_size
;
size_t
arg_size
;
int
error
;
int
error
;
...
@@ -576,30 +574,31 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
...
@@ -576,30 +574,31 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
}
else
if
(
strcmp
(
command
,
"permprofile"
)
==
0
)
{
}
else
if
(
strcmp
(
command
,
"permprofile"
)
==
0
)
{
error
=
aa_setprocattr_changeprofile
(
args
,
!
AA_ONEXEC
,
error
=
aa_setprocattr_changeprofile
(
args
,
!
AA_ONEXEC
,
AA_DO_TEST
);
AA_DO_TEST
);
}
else
if
(
strcmp
(
command
,
"permipc"
)
==
0
)
{
}
else
error
=
aa_setprocattr_permipc
(
args
);
goto
fail
;
}
else
{
struct
common_audit_data
sa
;
struct
apparmor_audit_data
aad
=
{
0
,};
sa
.
type
=
LSM_AUDIT_DATA_NONE
;
sa
.
aad
=
&
aad
;
aad
.
op
=
OP_SETPROCATTR
;
aad
.
info
=
name
;
aad
.
error
=
-
EINVAL
;
return
aa_audit
(
AUDIT_APPARMOR_DENIED
,
__aa_current_profile
(),
GFP_KERNEL
,
&
sa
,
NULL
);
}
}
else
if
(
strcmp
(
name
,
"exec"
)
==
0
)
{
}
else
if
(
strcmp
(
name
,
"exec"
)
==
0
)
{
if
(
strcmp
(
command
,
"exec"
)
==
0
)
error
=
aa_setprocattr_changeprofile
(
args
,
AA_ONEXEC
,
error
=
aa_setprocattr_changeprofile
(
args
,
AA_ONEXEC
,
!
AA_DO_TEST
);
!
AA_DO_TEST
);
}
else
{
else
goto
fail
;
}
else
/* only support the "current" and "exec" process attributes */
/* only support the "current" and "exec" process attributes */
return
-
EINVAL
;
return
-
EINVAL
;
}
if
(
!
error
)
if
(
!
error
)
error
=
size
;
error
=
size
;
return
error
;
return
error
;
fail:
sa
.
type
=
LSM_AUDIT_DATA_NONE
;
sa
.
aad
=
&
aad
;
aad
.
profile
=
aa_current_profile
();
aad
.
op
=
OP_SETPROCATTR
;
aad
.
info
=
name
;
aad
.
error
=
-
EINVAL
;
aa_audit_msg
(
AUDIT_APPARMOR_DENIED
,
&
sa
,
NULL
);
return
-
EINVAL
;
}
}
static
int
apparmor_task_setrlimit
(
struct
task_struct
*
task
,
static
int
apparmor_task_setrlimit
(
struct
task_struct
*
task
,
...
@@ -886,7 +885,7 @@ static int __init set_init_cxt(void)
...
@@ -886,7 +885,7 @@ static int __init set_init_cxt(void)
return
-
ENOMEM
;
return
-
ENOMEM
;
cxt
->
profile
=
aa_get_profile
(
root_ns
->
unconfined
);
cxt
->
profile
=
aa_get_profile
(
root_ns
->
unconfined
);
cred
->
security
=
cxt
;
cred
_cxt
(
cred
)
=
cxt
;
return
0
;
return
0
;
}
}
...
@@ -915,8 +914,11 @@ static int __init apparmor_init(void)
...
@@ -915,8 +914,11 @@ static int __init apparmor_init(void)
error
=
register_security
(
&
apparmor_ops
);
error
=
register_security
(
&
apparmor_ops
);
if
(
error
)
{
if
(
error
)
{
struct
cred
*
cred
=
(
struct
cred
*
)
current
->
real_cred
;
aa_free_task_context
(
cred_cxt
(
cred
));
cred_cxt
(
cred
)
=
NULL
;
AA_ERROR
(
"Unable to register AppArmor
\n
"
);
AA_ERROR
(
"Unable to register AppArmor
\n
"
);
goto
set_init_cxt
_out
;
goto
register_security
_out
;
}
}
/* Report that AppArmor successfully initialized */
/* Report that AppArmor successfully initialized */
...
@@ -930,9 +932,6 @@ static int __init apparmor_init(void)
...
@@ -930,9 +932,6 @@ static int __init apparmor_init(void)
return
error
;
return
error
;
set_init_cxt_out:
aa_free_task_context
(
current
->
real_cred
->
security
);
register_security_out:
register_security_out:
aa_free_root_ns
();
aa_free_root_ns
();
...
...
security/apparmor/match.c
浏览文件 @
bd71164a
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
* This file contains AppArmor dfa based regular expression matching engine
* This file contains AppArmor dfa based regular expression matching engine
*
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-201
0
Canonical Ltd.
* Copyright 2009-201
2
Canonical Ltd.
*
*
* This program is free software; you can redistribute it and/or
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* modify it under the terms of the GNU General Public License as
...
@@ -23,6 +23,8 @@
...
@@ -23,6 +23,8 @@
#include "include/apparmor.h"
#include "include/apparmor.h"
#include "include/match.h"
#include "include/match.h"
#define base_idx(X) ((X) & 0xffffff)
/**
/**
* unpack_table - unpack a dfa table (one of accept, default, base, next check)
* unpack_table - unpack a dfa table (one of accept, default, base, next check)
* @blob: data to unpack (NOT NULL)
* @blob: data to unpack (NOT NULL)
...
@@ -30,7 +32,7 @@
...
@@ -30,7 +32,7 @@
*
*
* Returns: pointer to table else NULL on failure
* Returns: pointer to table else NULL on failure
*
*
* NOTE: must be freed by kvfree (not k
malloc
)
* NOTE: must be freed by kvfree (not k
free
)
*/
*/
static
struct
table_header
*
unpack_table
(
char
*
blob
,
size_t
bsize
)
static
struct
table_header
*
unpack_table
(
char
*
blob
,
size_t
bsize
)
{
{
...
@@ -57,7 +59,7 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
...
@@ -57,7 +59,7 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
if
(
bsize
<
tsize
)
if
(
bsize
<
tsize
)
goto
out
;
goto
out
;
table
=
kv
m
alloc
(
tsize
);
table
=
kv
z
alloc
(
tsize
);
if
(
table
)
{
if
(
table
)
{
*
table
=
th
;
*
table
=
th
;
if
(
th
.
td_flags
==
YYTD_DATA8
)
if
(
th
.
td_flags
==
YYTD_DATA8
)
...
@@ -137,8 +139,7 @@ static int verify_dfa(struct aa_dfa *dfa, int flags)
...
@@ -137,8 +139,7 @@ static int verify_dfa(struct aa_dfa *dfa, int flags)
for
(
i
=
0
;
i
<
state_count
;
i
++
)
{
for
(
i
=
0
;
i
<
state_count
;
i
++
)
{
if
(
DEFAULT_TABLE
(
dfa
)[
i
]
>=
state_count
)
if
(
DEFAULT_TABLE
(
dfa
)[
i
]
>=
state_count
)
goto
out
;
goto
out
;
/* TODO: do check that DEF state recursion terminates */
if
(
base_idx
(
BASE_TABLE
(
dfa
)[
i
])
+
255
>=
trans_count
)
{
if
(
BASE_TABLE
(
dfa
)[
i
]
+
255
>=
trans_count
)
{
printk
(
KERN_ERR
"AppArmor DFA next/check upper "
printk
(
KERN_ERR
"AppArmor DFA next/check upper "
"bounds error
\n
"
);
"bounds error
\n
"
);
goto
out
;
goto
out
;
...
@@ -314,7 +315,7 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
...
@@ -314,7 +315,7 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
u8
*
equiv
=
EQUIV_TABLE
(
dfa
);
u8
*
equiv
=
EQUIV_TABLE
(
dfa
);
/* default is direct to next state */
/* default is direct to next state */
for
(;
len
;
len
--
)
{
for
(;
len
;
len
--
)
{
pos
=
base
[
state
]
+
equiv
[(
u8
)
*
str
++
];
pos
=
base
_idx
(
base
[
state
])
+
equiv
[(
u8
)
*
str
++
];
if
(
check
[
pos
]
==
state
)
if
(
check
[
pos
]
==
state
)
state
=
next
[
pos
];
state
=
next
[
pos
];
else
else
...
@@ -323,7 +324,7 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
...
@@ -323,7 +324,7 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
}
else
{
}
else
{
/* default is direct to next state */
/* default is direct to next state */
for
(;
len
;
len
--
)
{
for
(;
len
;
len
--
)
{
pos
=
base
[
state
]
+
(
u8
)
*
str
++
;
pos
=
base
_idx
(
base
[
state
])
+
(
u8
)
*
str
++
;
if
(
check
[
pos
]
==
state
)
if
(
check
[
pos
]
==
state
)
state
=
next
[
pos
];
state
=
next
[
pos
];
else
else
...
@@ -364,7 +365,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
...
@@ -364,7 +365,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
u8
*
equiv
=
EQUIV_TABLE
(
dfa
);
u8
*
equiv
=
EQUIV_TABLE
(
dfa
);
/* default is direct to next state */
/* default is direct to next state */
while
(
*
str
)
{
while
(
*
str
)
{
pos
=
base
[
state
]
+
equiv
[(
u8
)
*
str
++
];
pos
=
base
_idx
(
base
[
state
])
+
equiv
[(
u8
)
*
str
++
];
if
(
check
[
pos
]
==
state
)
if
(
check
[
pos
]
==
state
)
state
=
next
[
pos
];
state
=
next
[
pos
];
else
else
...
@@ -373,7 +374,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
...
@@ -373,7 +374,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
}
else
{
}
else
{
/* default is direct to next state */
/* default is direct to next state */
while
(
*
str
)
{
while
(
*
str
)
{
pos
=
base
[
state
]
+
(
u8
)
*
str
++
;
pos
=
base
_idx
(
base
[
state
])
+
(
u8
)
*
str
++
;
if
(
check
[
pos
]
==
state
)
if
(
check
[
pos
]
==
state
)
state
=
next
[
pos
];
state
=
next
[
pos
];
else
else
...
@@ -409,14 +410,14 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
...
@@ -409,14 +410,14 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
u8
*
equiv
=
EQUIV_TABLE
(
dfa
);
u8
*
equiv
=
EQUIV_TABLE
(
dfa
);
/* default is direct to next state */
/* default is direct to next state */
pos
=
base
[
state
]
+
equiv
[(
u8
)
c
];
pos
=
base
_idx
(
base
[
state
])
+
equiv
[(
u8
)
c
];
if
(
check
[
pos
]
==
state
)
if
(
check
[
pos
]
==
state
)
state
=
next
[
pos
];
state
=
next
[
pos
];
else
else
state
=
def
[
state
];
state
=
def
[
state
];
}
else
{
}
else
{
/* default is direct to next state */
/* default is direct to next state */
pos
=
base
[
state
]
+
(
u8
)
c
;
pos
=
base
_idx
(
base
[
state
])
+
(
u8
)
c
;
if
(
check
[
pos
]
==
state
)
if
(
check
[
pos
]
==
state
)
state
=
next
[
pos
];
state
=
next
[
pos
];
else
else
...
...
security/apparmor/path.c
浏览文件 @
bd71164a
...
@@ -174,7 +174,7 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer,
...
@@ -174,7 +174,7 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer,
if
(
info
&&
error
)
{
if
(
info
&&
error
)
{
if
(
error
==
-
ENOENT
)
if
(
error
==
-
ENOENT
)
*
info
=
"Failed name lookup - deleted entry"
;
*
info
=
"Failed name lookup - deleted entry"
;
else
if
(
error
==
-
E
STALE
)
else
if
(
error
==
-
E
ACCES
)
*
info
=
"Failed name lookup - disconnected path"
;
*
info
=
"Failed name lookup - disconnected path"
;
else
if
(
error
==
-
ENAMETOOLONG
)
else
if
(
error
==
-
ENAMETOOLONG
)
*
info
=
"Failed name lookup - name too long"
;
*
info
=
"Failed name lookup - name too long"
;
...
...
security/apparmor/policy.c
浏览文件 @
bd71164a
...
@@ -87,7 +87,6 @@
...
@@ -87,7 +87,6 @@
#include "include/policy.h"
#include "include/policy.h"
#include "include/policy_unpack.h"
#include "include/policy_unpack.h"
#include "include/resource.h"
#include "include/resource.h"
#include "include/sid.h"
/* root profile namespace */
/* root profile namespace */
...
@@ -292,7 +291,6 @@ static struct aa_namespace *alloc_namespace(const char *prefix,
...
@@ -292,7 +291,6 @@ static struct aa_namespace *alloc_namespace(const char *prefix,
if
(
!
ns
->
unconfined
)
if
(
!
ns
->
unconfined
)
goto
fail_unconfined
;
goto
fail_unconfined
;
ns
->
unconfined
->
sid
=
aa_alloc_sid
();
ns
->
unconfined
->
flags
=
PFLAG_UNCONFINED
|
PFLAG_IX_ON_NAME_ERROR
|
ns
->
unconfined
->
flags
=
PFLAG_UNCONFINED
|
PFLAG_IX_ON_NAME_ERROR
|
PFLAG_IMMUTABLE
;
PFLAG_IMMUTABLE
;
...
@@ -303,6 +301,8 @@ static struct aa_namespace *alloc_namespace(const char *prefix,
...
@@ -303,6 +301,8 @@ static struct aa_namespace *alloc_namespace(const char *prefix,
*/
*/
ns
->
unconfined
->
ns
=
aa_get_namespace
(
ns
);
ns
->
unconfined
->
ns
=
aa_get_namespace
(
ns
);
atomic_set
(
&
ns
->
uniq_null
,
0
);
return
ns
;
return
ns
;
fail_unconfined:
fail_unconfined:
...
@@ -497,7 +497,6 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
...
@@ -497,7 +497,6 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
/* released when @new is freed */
/* released when @new is freed */
new
->
parent
=
aa_get_profile
(
old
->
parent
);
new
->
parent
=
aa_get_profile
(
old
->
parent
);
new
->
ns
=
aa_get_namespace
(
old
->
ns
);
new
->
ns
=
aa_get_namespace
(
old
->
ns
);
new
->
sid
=
old
->
sid
;
__list_add_profile
(
&
policy
->
profiles
,
new
);
__list_add_profile
(
&
policy
->
profiles
,
new
);
/* inherit children */
/* inherit children */
list_for_each_entry_safe
(
child
,
tmp
,
&
old
->
base
.
profiles
,
base
.
list
)
{
list_for_each_entry_safe
(
child
,
tmp
,
&
old
->
base
.
profiles
,
base
.
list
)
{
...
@@ -635,83 +634,6 @@ void __init aa_free_root_ns(void)
...
@@ -635,83 +634,6 @@ void __init aa_free_root_ns(void)
aa_put_namespace
(
ns
);
aa_put_namespace
(
ns
);
}
}
/**
* aa_alloc_profile - allocate, initialize and return a new profile
* @hname: name of the profile (NOT NULL)
*
* Returns: refcount profile or NULL on failure
*/
struct
aa_profile
*
aa_alloc_profile
(
const
char
*
hname
)
{
struct
aa_profile
*
profile
;
/* freed by free_profile - usually through aa_put_profile */
profile
=
kzalloc
(
sizeof
(
*
profile
),
GFP_KERNEL
);
if
(
!
profile
)
return
NULL
;
if
(
!
policy_init
(
&
profile
->
base
,
NULL
,
hname
))
{
kzfree
(
profile
);
return
NULL
;
}
/* refcount released by caller */
return
profile
;
}
/**
* aa_new_null_profile - create a new null-X learning profile
* @parent: profile that caused this profile to be created (NOT NULL)
* @hat: true if the null- learning profile is a hat
*
* Create a null- complain mode profile used in learning mode. The name of
* the profile is unique and follows the format of parent//null-sid.
*
* null profiles are added to the profile list but the list does not
* hold a count on them so that they are automatically released when
* not in use.
*
* Returns: new refcounted profile else NULL on failure
*/
struct
aa_profile
*
aa_new_null_profile
(
struct
aa_profile
*
parent
,
int
hat
)
{
struct
aa_profile
*
profile
=
NULL
;
char
*
name
;
u32
sid
=
aa_alloc_sid
();
/* freed below */
name
=
kmalloc
(
strlen
(
parent
->
base
.
hname
)
+
2
+
7
+
8
,
GFP_KERNEL
);
if
(
!
name
)
goto
fail
;
sprintf
(
name
,
"%s//null-%x"
,
parent
->
base
.
hname
,
sid
);
profile
=
aa_alloc_profile
(
name
);
kfree
(
name
);
if
(
!
profile
)
goto
fail
;
profile
->
sid
=
sid
;
profile
->
mode
=
APPARMOR_COMPLAIN
;
profile
->
flags
=
PFLAG_NULL
;
if
(
hat
)
profile
->
flags
|=
PFLAG_HAT
;
/* released on free_profile */
profile
->
parent
=
aa_get_profile
(
parent
);
profile
->
ns
=
aa_get_namespace
(
parent
->
ns
);
write_lock
(
&
profile
->
ns
->
lock
);
__list_add_profile
(
&
parent
->
base
.
profiles
,
profile
);
write_unlock
(
&
profile
->
ns
->
lock
);
/* refcount released by caller */
return
profile
;
fail:
aa_free_sid
(
sid
);
return
NULL
;
}
/**
/**
* free_profile - free a profile
* free_profile - free a profile
* @profile: the profile to free (MAYBE NULL)
* @profile: the profile to free (MAYBE NULL)
...
@@ -749,7 +671,6 @@ static void free_profile(struct aa_profile *profile)
...
@@ -749,7 +671,6 @@ static void free_profile(struct aa_profile *profile)
aa_free_cap_rules
(
&
profile
->
caps
);
aa_free_cap_rules
(
&
profile
->
caps
);
aa_free_rlimit_rules
(
&
profile
->
rlimits
);
aa_free_rlimit_rules
(
&
profile
->
rlimits
);
aa_free_sid
(
profile
->
sid
);
aa_put_dfa
(
profile
->
xmatch
);
aa_put_dfa
(
profile
->
xmatch
);
aa_put_dfa
(
profile
->
policy
.
dfa
);
aa_put_dfa
(
profile
->
policy
.
dfa
);
...
@@ -790,6 +711,81 @@ void aa_free_profile_kref(struct kref *kref)
...
@@ -790,6 +711,81 @@ void aa_free_profile_kref(struct kref *kref)
free_profile
(
p
);
free_profile
(
p
);
}
}
/**
* aa_alloc_profile - allocate, initialize and return a new profile
* @hname: name of the profile (NOT NULL)
*
* Returns: refcount profile or NULL on failure
*/
struct
aa_profile
*
aa_alloc_profile
(
const
char
*
hname
)
{
struct
aa_profile
*
profile
;
/* freed by free_profile - usually through aa_put_profile */
profile
=
kzalloc
(
sizeof
(
*
profile
),
GFP_KERNEL
);
if
(
!
profile
)
return
NULL
;
if
(
!
policy_init
(
&
profile
->
base
,
NULL
,
hname
))
{
kzfree
(
profile
);
return
NULL
;
}
/* refcount released by caller */
return
profile
;
}
/**
* aa_new_null_profile - create a new null-X learning profile
* @parent: profile that caused this profile to be created (NOT NULL)
* @hat: true if the null- learning profile is a hat
*
* Create a null- complain mode profile used in learning mode. The name of
* the profile is unique and follows the format of parent//null-<uniq>.
*
* null profiles are added to the profile list but the list does not
* hold a count on them so that they are automatically released when
* not in use.
*
* Returns: new refcounted profile else NULL on failure
*/
struct
aa_profile
*
aa_new_null_profile
(
struct
aa_profile
*
parent
,
int
hat
)
{
struct
aa_profile
*
profile
=
NULL
;
char
*
name
;
int
uniq
=
atomic_inc_return
(
&
parent
->
ns
->
uniq_null
);
/* freed below */
name
=
kmalloc
(
strlen
(
parent
->
base
.
hname
)
+
2
+
7
+
8
,
GFP_KERNEL
);
if
(
!
name
)
goto
fail
;
sprintf
(
name
,
"%s//null-%x"
,
parent
->
base
.
hname
,
uniq
);
profile
=
aa_alloc_profile
(
name
);
kfree
(
name
);
if
(
!
profile
)
goto
fail
;
profile
->
mode
=
APPARMOR_COMPLAIN
;
profile
->
flags
=
PFLAG_NULL
;
if
(
hat
)
profile
->
flags
|=
PFLAG_HAT
;
/* released on free_profile */
profile
->
parent
=
aa_get_profile
(
parent
);
profile
->
ns
=
aa_get_namespace
(
parent
->
ns
);
write_lock
(
&
profile
->
ns
->
lock
);
__list_add_profile
(
&
parent
->
base
.
profiles
,
profile
);
write_unlock
(
&
profile
->
ns
->
lock
);
/* refcount released by caller */
return
profile
;
fail:
return
NULL
;
}
/* TODO: profile accounting - setup in remove */
/* TODO: profile accounting - setup in remove */
/**
/**
...
@@ -972,7 +968,6 @@ static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy,
...
@@ -972,7 +968,6 @@ static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy,
profile
->
parent
=
aa_get_profile
((
struct
aa_profile
*
)
policy
);
profile
->
parent
=
aa_get_profile
((
struct
aa_profile
*
)
policy
);
__list_add_profile
(
&
policy
->
profiles
,
profile
);
__list_add_profile
(
&
policy
->
profiles
,
profile
);
/* released on free_profile */
/* released on free_profile */
profile
->
sid
=
aa_alloc_sid
();
profile
->
ns
=
aa_get_namespace
(
ns
);
profile
->
ns
=
aa_get_namespace
(
ns
);
}
}
...
@@ -1110,14 +1105,8 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
...
@@ -1110,14 +1105,8 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
if
(
!
error
)
{
if
(
!
error
)
{
if
(
rename_profile
)
if
(
rename_profile
)
__replace_profile
(
rename_profile
,
new_profile
);
__replace_profile
(
rename_profile
,
new_profile
);
if
(
old_profile
)
{
if
(
old_profile
)
/* when there are both rename and old profiles
* inherit old profiles sid
*/
if
(
rename_profile
)
aa_free_sid
(
new_profile
->
sid
);
__replace_profile
(
old_profile
,
new_profile
);
__replace_profile
(
old_profile
,
new_profile
);
}
if
(
!
(
old_profile
||
rename_profile
))
if
(
!
(
old_profile
||
rename_profile
))
__add_new_profile
(
ns
,
policy
,
new_profile
);
__add_new_profile
(
ns
,
policy
,
new_profile
);
}
}
...
@@ -1167,7 +1156,6 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
...
@@ -1167,7 +1156,6 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
if
(
fqname
[
0
]
==
':'
)
{
if
(
fqname
[
0
]
==
':'
)
{
char
*
ns_name
;
char
*
ns_name
;
name
=
aa_split_fqname
(
fqname
,
&
ns_name
);
name
=
aa_split_fqname
(
fqname
,
&
ns_name
);
if
(
ns_name
)
{
/* released below */
/* released below */
ns
=
aa_find_namespace
(
root
,
ns_name
);
ns
=
aa_find_namespace
(
root
,
ns_name
);
if
(
!
ns
)
{
if
(
!
ns
)
{
...
@@ -1175,7 +1163,6 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
...
@@ -1175,7 +1163,6 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
error
=
-
ENOENT
;
error
=
-
ENOENT
;
goto
fail
;
goto
fail
;
}
}
}
}
else
}
else
/* released below */
/* released below */
ns
=
aa_get_namespace
(
root
);
ns
=
aa_get_namespace
(
root
);
...
...
security/apparmor/policy_unpack.c
浏览文件 @
bd71164a
...
@@ -27,7 +27,6 @@
...
@@ -27,7 +27,6 @@
#include "include/match.h"
#include "include/match.h"
#include "include/policy.h"
#include "include/policy.h"
#include "include/policy_unpack.h"
#include "include/policy_unpack.h"
#include "include/sid.h"
/*
/*
* The AppArmor interface treats data as a type byte followed by the
* The AppArmor interface treats data as a type byte followed by the
...
@@ -290,6 +289,9 @@ static int unpack_strdup(struct aa_ext *e, char **string, const char *name)
...
@@ -290,6 +289,9 @@ static int unpack_strdup(struct aa_ext *e, char **string, const char *name)
return
res
;
return
res
;
}
}
#define DFA_VALID_PERM_MASK 0xffffffff
#define DFA_VALID_PERM2_MASK 0xffffffff
/**
/**
* verify_accept - verify the accept tables of a dfa
* verify_accept - verify the accept tables of a dfa
* @dfa: dfa to verify accept tables of (NOT NULL)
* @dfa: dfa to verify accept tables of (NOT NULL)
...
...
security/apparmor/procattr.c
浏览文件 @
bd71164a
...
@@ -163,9 +163,3 @@ int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test)
...
@@ -163,9 +163,3 @@ int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test)
name
=
aa_split_fqname
(
fqname
,
&
ns_name
);
name
=
aa_split_fqname
(
fqname
,
&
ns_name
);
return
aa_change_profile
(
ns_name
,
name
,
onexec
,
test
);
return
aa_change_profile
(
ns_name
,
name
,
onexec
,
test
);
}
}
int
aa_setprocattr_permipc
(
char
*
fqname
)
{
/* TODO: add ipc permission querying */
return
-
ENOTSUPP
;
}
security/apparmor/resource.c
浏览文件 @
bd71164a
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
#include <linux/audit.h>
#include <linux/audit.h>
#include "include/audit.h"
#include "include/audit.h"
#include "include/context.h"
#include "include/resource.h"
#include "include/resource.h"
#include "include/policy.h"
#include "include/policy.h"
...
@@ -90,17 +91,25 @@ int aa_map_resource(int resource)
...
@@ -90,17 +91,25 @@ int aa_map_resource(int resource)
int
aa_task_setrlimit
(
struct
aa_profile
*
profile
,
struct
task_struct
*
task
,
int
aa_task_setrlimit
(
struct
aa_profile
*
profile
,
struct
task_struct
*
task
,
unsigned
int
resource
,
struct
rlimit
*
new_rlim
)
unsigned
int
resource
,
struct
rlimit
*
new_rlim
)
{
{
struct
aa_profile
*
task_profile
;
int
error
=
0
;
int
error
=
0
;
rcu_read_lock
();
task_profile
=
aa_get_profile
(
aa_cred_profile
(
__task_cred
(
task
)));
rcu_read_unlock
();
/* TODO: extend resource control to handle other (non current)
/* TODO: extend resource control to handle other (non current)
* processes. AppArmor rules currently have the implicit assumption
* profiles. AppArmor rules currently have the implicit assumption
* that the task is setting the resource of the current process
* that the task is setting the resource of a task confined with
* the same profile.
*/
*/
if
(
(
task
!=
current
->
group_leader
)
||
if
(
profile
!=
task_profile
||
(
profile
->
rlimits
.
mask
&
(
1
<<
resource
)
&&
(
profile
->
rlimits
.
mask
&
(
1
<<
resource
)
&&
new_rlim
->
rlim_max
>
profile
->
rlimits
.
limits
[
resource
].
rlim_max
))
new_rlim
->
rlim_max
>
profile
->
rlimits
.
limits
[
resource
].
rlim_max
))
error
=
-
EACCES
;
error
=
-
EACCES
;
aa_put_profile
(
task_profile
);
return
audit_resource
(
profile
,
resource
,
new_rlim
->
rlim_max
,
error
);
return
audit_resource
(
profile
,
resource
,
new_rlim
->
rlim_max
,
error
);
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录