1 |
/* |
2 |
* security/ccsecurity/signal.c |
3 |
* |
4 |
* Copyright (C) 2005-2010 NTT DATA CORPORATION |
5 |
* |
6 |
* Version: 1.8.0-pre 2010/08/01 |
7 |
* |
8 |
* This file is applicable to both 2.4.30 and 2.6.11 and later. |
9 |
* See README.ccs for ChangeLog. |
10 |
* |
11 |
*/ |
12 |
|
13 |
#include "internal.h" |
14 |
|
15 |
/* To support PID namespace. */ |
16 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) |
17 |
#define find_task_by_pid ccsecurity_exports.find_task_by_vpid |
18 |
#endif |
19 |
|
20 |
/** |
21 |
* ccs_audit_signal_log - Audit signal log. |
22 |
* |
23 |
* @r: Pointer to "struct ccs_request_info". |
24 |
* |
25 |
* Returns 0 on success, negative value otherwise. |
26 |
*/ |
27 |
static int ccs_audit_signal_log(struct ccs_request_info *r) |
28 |
{ |
29 |
const int sig = r->param.signal.sig; |
30 |
const char *dest_domain = r->param.signal.dest_pattern; |
31 |
ccs_write_log(r, "ipc signal %d %s\n", sig, dest_domain); |
32 |
if (r->granted) |
33 |
return 0; |
34 |
ccs_warn_log(r, "signal %d to %s", sig, ccs_last_word(dest_domain)); |
35 |
return ccs_supervisor(r, "ipc signal %d %s\n", sig, dest_domain); |
36 |
} |
37 |
|
38 |
static bool ccs_check_signal_acl(struct ccs_request_info *r, |
39 |
const struct ccs_acl_info *ptr) |
40 |
{ |
41 |
const struct ccs_signal_acl *acl = |
42 |
container_of(ptr, typeof(*acl), head); |
43 |
if (acl->sig == r->param.signal.sig) { |
44 |
const int len = acl->domainname->total_len; |
45 |
if (!strncmp(acl->domainname->name, |
46 |
r->param.signal.dest_pattern, len)) { |
47 |
switch (r->param.signal.dest_pattern[len]) { |
48 |
case ' ': |
49 |
case '\0': |
50 |
return true; |
51 |
} |
52 |
} |
53 |
} |
54 |
return false; |
55 |
} |
56 |
|
57 |
/** |
58 |
* ccs_signal_acl2 - Check permission for signal. |
59 |
* |
60 |
* @sig: Signal number. |
61 |
* @pid: Target's PID. |
62 |
* |
63 |
* Returns 0 on success, negative value otherwise. |
64 |
* |
65 |
* Caller holds ccs_read_lock(). |
66 |
*/ |
67 |
static int ccs_signal_acl2(const int sig, const int pid) |
68 |
{ |
69 |
struct ccs_request_info r; |
70 |
struct ccs_domain_info *dest = NULL; |
71 |
int error; |
72 |
const struct ccs_domain_info * const domain = ccs_current_domain(); |
73 |
if (ccs_init_request_info(&r, CCS_MAC_SIGNAL) == CCS_CONFIG_DISABLED) |
74 |
return 0; |
75 |
if (!sig) |
76 |
return 0; /* No check for NULL signal. */ |
77 |
r.param_type = CCS_TYPE_SIGNAL_ACL; |
78 |
r.param.signal.sig = sig; |
79 |
r.param.signal.dest_pattern = domain->domainname->name; |
80 |
r.granted = true; |
81 |
if (ccsecurity_exports.sys_getpid() == pid) { |
82 |
ccs_audit_signal_log(&r); |
83 |
return 0; /* No check for self process. */ |
84 |
} |
85 |
{ /* Simplified checking. */ |
86 |
struct task_struct *p = NULL; |
87 |
ccs_tasklist_lock(); |
88 |
if (pid > 0) |
89 |
p = find_task_by_pid((pid_t) pid); |
90 |
else if (pid == 0) |
91 |
p = current; |
92 |
else if (pid == -1) |
93 |
dest = &ccs_kernel_domain; |
94 |
else |
95 |
p = find_task_by_pid((pid_t) -pid); |
96 |
if (p) |
97 |
dest = ccs_task_domain(p); |
98 |
ccs_tasklist_unlock(); |
99 |
} |
100 |
if (!dest) |
101 |
return 0; /* I can't find destinatioin. */ |
102 |
if (domain == dest) { |
103 |
ccs_audit_signal_log(&r); |
104 |
return 0; /* No check for self domain. */ |
105 |
} |
106 |
r.param.signal.dest_pattern = dest->domainname->name; |
107 |
do { |
108 |
ccs_check_acl(&r, ccs_check_signal_acl); |
109 |
error = ccs_audit_signal_log(&r); |
110 |
} while (error == CCS_RETRY_REQUEST); |
111 |
return error; |
112 |
} |
113 |
|
114 |
/** |
115 |
* ccs_signal_acl - Check permission for signal. |
116 |
* |
117 |
* @pid: Target's PID. |
118 |
* @sig: Signal number. |
119 |
* |
120 |
* Returns 0 on success, negative value otherwise. |
121 |
*/ |
122 |
static int ccs_signal_acl(const int pid, const int sig) |
123 |
{ |
124 |
int error; |
125 |
if (!sig) |
126 |
error = 0; |
127 |
else { |
128 |
const int idx = ccs_read_lock(); |
129 |
error = ccs_signal_acl2(sig, pid); |
130 |
ccs_read_unlock(idx); |
131 |
} |
132 |
return error; |
133 |
} |
134 |
|
135 |
/** |
136 |
* ccs_signal_acl0 - Permission check for signal(). |
137 |
* |
138 |
* @tgid: Unused. |
139 |
* @pid: PID |
140 |
* @sig: Signal number. |
141 |
* |
142 |
* Returns 0 on success, negative value otherwise. |
143 |
*/ |
144 |
static int ccs_signal_acl0(pid_t tgid, pid_t pid, int sig) |
145 |
{ |
146 |
return ccs_signal_acl(pid, sig); |
147 |
} |
148 |
|
149 |
static bool ccs_same_signal_entry(const struct ccs_acl_info *a, |
150 |
const struct ccs_acl_info *b) |
151 |
{ |
152 |
const struct ccs_signal_acl *p1 = container_of(a, typeof(*p1), head); |
153 |
const struct ccs_signal_acl *p2 = container_of(b, typeof(*p2), head); |
154 |
return p1->head.type == p2->head.type && p1->head.cond == p2->head.cond |
155 |
&& p1->head.type == CCS_TYPE_SIGNAL_ACL && p1->sig == p2->sig |
156 |
&& p1->domainname == p2->domainname; |
157 |
} |
158 |
|
159 |
/** |
160 |
* ccs_write_signal - Write "struct ccs_signal_acl" list. |
161 |
* |
162 |
* @data: String to parse. |
163 |
* @domain: Pointer to "struct ccs_domain_info". |
164 |
* @condition: Pointer to "struct ccs_condition". Maybe NULL. |
165 |
* @is_delete: True if it is a delete request. |
166 |
* |
167 |
* Returns 0 on success, negative value otherwise. |
168 |
*/ |
169 |
static int ccs_write_signal(char *data, struct ccs_domain_info *domain, |
170 |
struct ccs_condition *condition, |
171 |
const bool is_delete) |
172 |
{ |
173 |
struct ccs_signal_acl e = { .head.type = CCS_TYPE_SIGNAL_ACL, |
174 |
.head.cond = condition }; |
175 |
int error; |
176 |
int sig; |
177 |
char *domainname = strchr(data, ' '); |
178 |
if (sscanf(data, "%d", &sig) != 1 || !domainname || |
179 |
!ccs_correct_domain(domainname + 1)) |
180 |
return -EINVAL; |
181 |
e.sig = sig; |
182 |
e.domainname = ccs_get_name(domainname + 1); |
183 |
if (!e.domainname) |
184 |
return -ENOMEM; |
185 |
error = ccs_update_domain(&e.head, sizeof(e), is_delete, domain, |
186 |
ccs_same_signal_entry, NULL); |
187 |
ccs_put_name(e.domainname); |
188 |
return error; |
189 |
} |
190 |
|
191 |
int ccs_write_ipc(char *data, struct ccs_domain_info *domain, |
192 |
struct ccs_condition *condition, const bool is_delete) |
193 |
{ |
194 |
if (ccs_str_starts(&data, "signal ")) |
195 |
return ccs_write_signal(data, domain, condition, is_delete); |
196 |
return -EINVAL; |
197 |
} |
198 |
|
199 |
void __init ccs_signal_init(void) |
200 |
{ |
201 |
ccsecurity_ops.kill_permission = ccs_signal_acl; |
202 |
ccsecurity_ops.tgkill_permission = ccs_signal_acl0; |
203 |
ccsecurity_ops.tkill_permission = ccs_signal_acl; |
204 |
ccsecurity_ops.sigqueue_permission = ccs_signal_acl; |
205 |
ccsecurity_ops.tgsigqueue_permission = ccs_signal_acl0; |
206 |
} |