cap2 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +101 -31
- data/Rakefile +7 -0
- data/ext/cap2/cap2.c +362 -94
- data/lib/cap2.rb +12 -39
- data/lib/cap2.so +0 -0
- data/lib/cap2/file.rb +21 -6
- data/lib/cap2/process.rb +9 -6
- data/lib/cap2/set_methods.rb +26 -0
- data/lib/cap2/version.rb +1 -1
- data/spec/cap2_spec.rb +0 -32
- data/spec/file_spec.rb +136 -2
- data/spec/process_spec.rb +27 -1
- data/spec/spec_helper.rb +0 -1
- metadata +8 -7
- data/lib/cap2/entity.rb +0 -31
- data/spec/support/entity.rb +0 -38
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Cap2
|
2
2
|
====
|
3
3
|
|
4
|
-
Cap2 is a Ruby library for managing the POSIX 1003.1e capabilities available in Linux kernels. These capabilities are a partitioning of the all powerful root privilege into a set of distinct privileges. See
|
4
|
+
Cap2 is a Ruby library for managing the POSIX 1003.1e capabilities available in Linux kernels. These capabilities are a partitioning of the all powerful root privilege into a set of distinct privileges. See capabilities(7) for more information.
|
5
5
|
|
6
6
|
Installation
|
7
7
|
------------
|
@@ -32,57 +32,127 @@ $ sudo make install
|
|
32
32
|
$ gem install cap2
|
33
33
|
```
|
34
34
|
|
35
|
-
|
36
|
-
|
35
|
+
Usage
|
36
|
+
-----
|
37
37
|
|
38
|
-
|
38
|
+
Cap2 provides methods for querying and modifying capabilities for both processes and files.
|
39
39
|
|
40
|
-
|
40
|
+
Process capabilities are accessed via a `Cap2::Process` object (returned from `Cap2.process`) and file capabilities via a `Cap2::File` object (returned from `Cap2.file`):
|
41
41
|
|
42
42
|
```
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
Cap2.process # Returns a Cap2::Process for the current process
|
44
|
+
Cap2.process(1000) # Returns a Cap2::Process for the process with pid 1000
|
45
|
+
Cap2.file('/sbin/init') # Returns a Cap2::File for the /sbin/init file
|
46
46
|
```
|
47
47
|
|
48
|
-
|
48
|
+
Capabilities are referenced using lower cased symbols, and without the CAP_ prefix (e.g. `:kill` represents CAP_KILL).
|
49
|
+
|
50
|
+
### Querying Capabilities
|
51
|
+
|
52
|
+
There are three methods - `permitted?`, `effective?` and `inheritable?` - defined on both `Cap2::Process` and `Cap2::File` for querying capabilities. Each take a capability symbol and return true / false if the capability is in / not in the relevant set:
|
49
53
|
|
50
54
|
```
|
51
|
-
|
55
|
+
# the init daemon - all caps permitted & effective but not inheritable
|
56
|
+
init = Cap2.process(1) # => #<Cap2::Process @pid=1>
|
57
|
+
|
58
|
+
init.permitted?(:kill) # => true
|
59
|
+
init.permitted?(:chown) # => true
|
60
|
+
|
61
|
+
init.effective?(:fowner) # => true
|
62
|
+
|
63
|
+
init.inheritable?(:kill) # => false
|
64
|
+
init.inheritable?(:fowner) # => false
|
65
|
+
|
66
|
+
# assume /bin/ping is using file capabilities to enable CAP_NET_RAW on exec
|
67
|
+
ping = Cap2.file('/bin/ping') # => #<Cap2::File @filename="/bin/ping">
|
68
|
+
|
69
|
+
ping.permitted?(:net_raw) # => true
|
70
|
+
ping.permitted?(:mknod) # => false
|
71
|
+
|
72
|
+
ping.effective?(:net_raw) # => true
|
73
|
+
|
74
|
+
ping.inheritable?(:net_raw) # => false
|
75
|
+
```
|
76
|
+
|
77
|
+
### Modifying Capabilities
|
78
|
+
|
79
|
+
Cap2 provides different levels of control over process and file capabilities.
|
80
|
+
|
81
|
+
#### Files
|
82
|
+
|
83
|
+
To modify the permitted capabilities of a file (i.e. the capabilities which will be permitted in any process that exec's the file), use `Cap2::File#permit` and `Cap2::File#unpermit`:
|
84
|
+
|
85
|
+
```
|
86
|
+
Cap2.process.effective?(:setfcap) # => true - needed to set file capabilities
|
87
|
+
|
88
|
+
file = Cap2.file('/tmp/file') # => #<Cap2::File @filename="/tmp/file">
|
52
89
|
|
53
|
-
|
54
|
-
process.permitted?(:lease) # => true
|
55
|
-
process.permitted?(:fowner) # => false
|
90
|
+
file.permitted?(:mknod) # => false
|
56
91
|
|
57
|
-
|
58
|
-
|
92
|
+
file.permit(:mknod) # => true
|
93
|
+
file.permitted?(:mknod) # => true
|
59
94
|
|
60
|
-
|
61
|
-
|
95
|
+
file.unpermit(:mknod) # => true
|
96
|
+
file.permitted?(:mknod) # => false
|
62
97
|
```
|
63
98
|
|
64
|
-
|
99
|
+
To modify the effective capabilities of a file (i.e. the capabilities which will be enabled in any process that exec's the file), use `Cap2::File#enable_on_exec` and `Cap2::File#disable_on_exec`:
|
65
100
|
|
66
|
-
|
101
|
+
```
|
102
|
+
Cap2.process.effective?(:setfcap) # => true - needed to set file capabilities
|
103
|
+
|
104
|
+
file = Cap2.file('/tmp/file') # => #<Cap2::File @filename="/tmp/file">
|
105
|
+
|
106
|
+
file.effective?(:net_raw) # => false
|
67
107
|
|
108
|
+
file.enable_on_exec(:net_raw) # => true
|
109
|
+
file.effective?(:net_raw) # => true
|
110
|
+
|
111
|
+
file.disable_on_exec(:net_raw) # => true
|
112
|
+
file.effective?(:net_raw) # => false
|
68
113
|
```
|
69
|
-
|
70
|
-
|
71
|
-
|
114
|
+
|
115
|
+
To modify the inheritable capabilities of a file (i.e. the capabilities which will be ANDed with the inheritable set of any process that exec's the file to determine which capabilities are in the permitted set of the process), use `Cap2::File#allow_inherit` and `Cap2::File#disallow_inherit`:
|
116
|
+
|
72
117
|
```
|
118
|
+
Cap2.process.effective?(:setfcap) # => true - needed to set file capabilities
|
119
|
+
|
120
|
+
file = Cap2.file('/tmp/file') # => #<Cap2::File @filename="/tmp/file">
|
73
121
|
|
74
|
-
|
122
|
+
file.inheritable?(:fowner) # => false
|
75
123
|
|
124
|
+
file.allow_inherit(:fowner) # => true
|
125
|
+
file.inheritable?(:fowner) # => true
|
126
|
+
|
127
|
+
file.disallow_inherit(:fowner) # => true
|
128
|
+
file.inheritable?(:fowner) # => false
|
76
129
|
```
|
77
|
-
file = Cap2.file('/tmp/cap_test') # => #<Cap2::File>
|
78
130
|
|
79
|
-
|
80
|
-
|
81
|
-
|
131
|
+
#### Processes
|
132
|
+
|
133
|
+
Cap2 can be used to enable / disable capabilities of the current Ruby process.
|
82
134
|
|
83
|
-
file
|
84
|
-
file.effective?(:lease) # => false
|
135
|
+
Suppose the ruby binary file permits :kill, but does not enable it on exec:
|
85
136
|
|
86
|
-
file.inheritable?(:chown) # => false
|
87
|
-
file.inheritable?(:lease) # => true
|
88
137
|
```
|
138
|
+
ruby = Cap2.file('/usr/bin/ruby') # => #<Cap2::File @filename="/usr/bin/ruby">
|
139
|
+
ruby.permitted?(:kill) # => true
|
140
|
+
ruby.effective?(:kill) # => false
|
141
|
+
```
|
142
|
+
|
143
|
+
and we want to kill a process running as root with pid 1000:
|
144
|
+
|
145
|
+
```
|
146
|
+
Cap2.process.effective?(:kill) # => false
|
147
|
+
Process.kill("TERM", 1000) # => Errno::EPERM: Operation not permitted
|
148
|
+
|
149
|
+
Cap2.process.enable(:kill) # => true
|
150
|
+
Cap2.process.effective?(:kill) # => true
|
151
|
+
Process.kill("TERM", 1000) # => 1
|
152
|
+
```
|
153
|
+
|
154
|
+
Notes
|
155
|
+
-----
|
156
|
+
|
157
|
+
* Cap2 cannot be used to modify capabilities of other processes. If you are curious as to why, see the Notes section of [cap_set_proc(3)](http://linux.die.net/man/3/cap_set_proc) for an explanation.
|
158
|
+
* File capabilities are not supported for shell scripts due to security implications, similar to the reasons setuid is disabled, [see here](http://unix.stackexchange.com/questions/364/allow-setuid-on-shell-scripts#answer-2910) for more information.
|
data/Rakefile
CHANGED
@@ -9,3 +9,10 @@ desc 'Run all specs in the spec directory'
|
|
9
9
|
RSpec::Core::RakeTask.new(:spec => [:clobber, :compile]) do |t|
|
10
10
|
t.pattern = FileList['spec/**/*_spec.rb']
|
11
11
|
end
|
12
|
+
|
13
|
+
require 'rdoc/task'
|
14
|
+
|
15
|
+
RDoc::Task.new do |rd|
|
16
|
+
rd.title = 'Cap2'
|
17
|
+
rd.rdoc_files.include("lib/**/*.rb", "ext/**/*.c")
|
18
|
+
end
|
data/ext/cap2/cap2.c
CHANGED
@@ -1,149 +1,417 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
#include <errno.h>
|
3
|
+
#include <unistd.h>
|
3
4
|
#include <sys/capability.h>
|
4
5
|
|
5
|
-
|
6
|
+
/*
|
7
|
+
* Converts a Ruby symbol into cap_flag_t set, defined in <sys/capability.h>
|
8
|
+
*
|
9
|
+
* Raises an ArgumentError if set is not a valid capability set
|
10
|
+
*/
|
11
|
+
cap_flag_t cap2_sym_to_set(VALUE set) {
|
12
|
+
char *set_s;
|
13
|
+
|
14
|
+
Check_Type(set, T_SYMBOL);
|
15
|
+
|
16
|
+
set = rb_sym_to_s(set);
|
17
|
+
|
18
|
+
set_s = StringValueCStr(set);
|
19
|
+
|
20
|
+
if(strcmp(set_s, "permitted") == 0) return CAP_PERMITTED;
|
21
|
+
else if(strcmp(set_s, "effective") == 0) return CAP_EFFECTIVE;
|
22
|
+
else if(strcmp(set_s, "inheritable") == 0) return CAP_INHERITABLE;
|
23
|
+
else rb_raise(rb_eArgError, "unknown set %s", set_s);
|
24
|
+
}
|
25
|
+
|
26
|
+
/*
|
27
|
+
* Converts a Ruby symbol into cap_value_t capability value, defined
|
28
|
+
* in <linux/capability.h>
|
29
|
+
*
|
30
|
+
* Raises an ArgumentError if cap is not a valid capability value.
|
31
|
+
*/
|
32
|
+
cap_value_t cap2_sym_to_cap(VALUE cap) {
|
33
|
+
char *cap_s;
|
34
|
+
|
35
|
+
Check_Type(cap, T_SYMBOL);
|
36
|
+
|
37
|
+
cap = rb_sym_to_s(cap);
|
38
|
+
|
39
|
+
cap_s = StringValueCStr(cap);
|
40
|
+
|
41
|
+
if(strcmp(cap_s, "chown") == 0) return CAP_CHOWN;
|
42
|
+
else if(strcmp(cap_s, "dac_override") == 0) return CAP_DAC_OVERRIDE;
|
43
|
+
else if(strcmp(cap_s, "dac_read_search") == 0) return CAP_DAC_READ_SEARCH;
|
44
|
+
else if(strcmp(cap_s, "fowner") == 0) return CAP_FOWNER;
|
45
|
+
else if(strcmp(cap_s, "fsetid") == 0) return CAP_FSETID;
|
46
|
+
else if(strcmp(cap_s, "kill") == 0) return CAP_KILL;
|
47
|
+
else if(strcmp(cap_s, "setgid") == 0) return CAP_SETGID;
|
48
|
+
else if(strcmp(cap_s, "setuid") == 0) return CAP_SETUID;
|
49
|
+
else if(strcmp(cap_s, "setpcap") == 0) return CAP_SETPCAP;
|
50
|
+
else if(strcmp(cap_s, "linux_immutable") == 0) return CAP_LINUX_IMMUTABLE;
|
51
|
+
else if(strcmp(cap_s, "net_bind_service") == 0) return CAP_NET_BIND_SERVICE;
|
52
|
+
else if(strcmp(cap_s, "net_broadcast") == 0) return CAP_NET_BROADCAST;
|
53
|
+
else if(strcmp(cap_s, "net_admin") == 0) return CAP_NET_ADMIN;
|
54
|
+
else if(strcmp(cap_s, "net_raw") == 0) return CAP_NET_RAW;
|
55
|
+
else if(strcmp(cap_s, "ipc_lock") == 0) return CAP_IPC_LOCK;
|
56
|
+
else if(strcmp(cap_s, "ipc_owner") == 0) return CAP_IPC_OWNER;
|
57
|
+
else if(strcmp(cap_s, "sys_module") == 0) return CAP_SYS_MODULE;
|
58
|
+
else if(strcmp(cap_s, "sys_rawio") == 0) return CAP_SYS_RAWIO;
|
59
|
+
else if(strcmp(cap_s, "sys_chroot") == 0) return CAP_SYS_CHROOT;
|
60
|
+
else if(strcmp(cap_s, "sys_ptrace") == 0) return CAP_SYS_PTRACE;
|
61
|
+
else if(strcmp(cap_s, "sys_pacct") == 0) return CAP_SYS_PACCT;
|
62
|
+
else if(strcmp(cap_s, "sys_admin") == 0) return CAP_SYS_ADMIN;
|
63
|
+
else if(strcmp(cap_s, "sys_boot") == 0) return CAP_SYS_BOOT;
|
64
|
+
else if(strcmp(cap_s, "sys_nice") == 0) return CAP_SYS_NICE;
|
65
|
+
else if(strcmp(cap_s, "sys_resource") == 0) return CAP_SYS_RESOURCE;
|
66
|
+
else if(strcmp(cap_s, "sys_time") == 0) return CAP_SYS_TIME;
|
67
|
+
else if(strcmp(cap_s, "sys_tty_config") == 0) return CAP_SYS_TTY_CONFIG;
|
68
|
+
else if(strcmp(cap_s, "mknod") == 0) return CAP_MKNOD;
|
69
|
+
else if(strcmp(cap_s, "lease") == 0) return CAP_LEASE;
|
70
|
+
else if(strcmp(cap_s, "audit_write") == 0) return CAP_AUDIT_WRITE;
|
71
|
+
else if(strcmp(cap_s, "audit_control") == 0) return CAP_AUDIT_CONTROL;
|
72
|
+
else if(strcmp(cap_s, "setfcap") == 0) return CAP_SETFCAP;
|
73
|
+
else if(strcmp(cap_s, "mac_override") == 0) return CAP_MAC_OVERRIDE;
|
74
|
+
else if(strcmp(cap_s, "mac_admin") == 0) return CAP_MAC_ADMIN;
|
75
|
+
else if(strcmp(cap_s, "syslog") == 0) return CAP_SYSLOG;
|
76
|
+
else if(strcmp(cap_s, "wake_alarm") == 0) return CAP_WAKE_ALARM;
|
77
|
+
else rb_raise(rb_eArgError, "unknown capability %s", cap_s);
|
78
|
+
}
|
79
|
+
|
80
|
+
/*
|
81
|
+
* Returns a boolean representing whether cap_d has the given capability enabled
|
82
|
+
* in the given set
|
83
|
+
*/
|
84
|
+
VALUE cap2_has_cap(cap_t cap_d, VALUE set_sym, VALUE cap_sym) {
|
85
|
+
cap_flag_t set;
|
86
|
+
cap_value_t cap;
|
6
87
|
cap_flag_value_t flag_value = CAP_CLEAR;
|
7
88
|
|
89
|
+
set = cap2_sym_to_set(set_sym);
|
90
|
+
cap = cap2_sym_to_cap(cap_sym);
|
91
|
+
|
8
92
|
cap_get_flag(cap_d, cap, set, &flag_value);
|
9
93
|
|
10
94
|
return flag_value == CAP_SET ? Qtrue : Qfalse;
|
11
95
|
}
|
12
96
|
|
13
|
-
|
97
|
+
/*
|
98
|
+
* Convert @pid stored in the given Process object to an int and return it.
|
99
|
+
*/
|
100
|
+
static int cap2_process_pid(VALUE process) {
|
101
|
+
VALUE pid;
|
102
|
+
|
103
|
+
pid = rb_iv_get(process, "@pid");
|
104
|
+
|
105
|
+
return FIX2INT(pid);
|
106
|
+
}
|
107
|
+
|
108
|
+
/*
|
109
|
+
* Return a cap_t struct containing the capabilities of the given Process object.
|
110
|
+
*/
|
111
|
+
static cap_t cap2_process_caps(VALUE process) {
|
14
112
|
cap_t cap_d;
|
15
|
-
|
113
|
+
int pid;
|
16
114
|
|
17
|
-
|
115
|
+
pid = cap2_process_pid(process);
|
18
116
|
|
19
|
-
cap_d = cap_get_pid(
|
117
|
+
cap_d = cap_get_pid(pid);
|
20
118
|
|
21
119
|
if (cap_d == NULL) {
|
22
120
|
rb_raise(
|
23
121
|
rb_eRuntimeError,
|
24
122
|
"Failed to get capabilities for proccess %d: (%s)\n",
|
25
|
-
|
123
|
+
pid, strerror(errno)
|
26
124
|
);
|
27
125
|
}
|
28
126
|
|
29
|
-
|
30
|
-
|
127
|
+
return cap_d;
|
128
|
+
}
|
129
|
+
|
130
|
+
/*
|
131
|
+
* Enable/disable the given capability in the given set for the given Process
|
132
|
+
* object.
|
133
|
+
*/
|
134
|
+
static VALUE cap2_process_set_cap(VALUE process, cap_flag_t set, VALUE cap_sym, cap_flag_value_t set_or_clear) {
|
135
|
+
cap_t cap_d;
|
136
|
+
int pid;
|
137
|
+
cap_value_t caps[1];
|
138
|
+
|
139
|
+
pid = cap2_process_pid(process);
|
140
|
+
|
141
|
+
if((pid_t) pid != getpid())
|
142
|
+
rb_raise(
|
143
|
+
rb_eRuntimeError,
|
144
|
+
"Cannot set capabilities for other processes"
|
145
|
+
);
|
146
|
+
|
147
|
+
caps[0] = cap2_sym_to_cap(cap_sym);
|
148
|
+
|
149
|
+
cap_d = cap_get_pid(pid);
|
150
|
+
|
151
|
+
cap_set_flag(cap_d, set, 1, caps, set_or_clear);
|
152
|
+
|
153
|
+
if(cap_set_proc(cap_d) == -1) {
|
154
|
+
rb_raise(
|
155
|
+
rb_eRuntimeError,
|
156
|
+
"Failed to set capabilities for process %d: (%s)\n",
|
157
|
+
pid, strerror(errno)
|
158
|
+
);
|
159
|
+
} else {
|
160
|
+
return Qtrue;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
/*
|
165
|
+
* call-seq:
|
166
|
+
* has?(set, capability) -> true or false
|
167
|
+
*
|
168
|
+
* Return whether the process has the given capability enabled in the given set.
|
169
|
+
*
|
170
|
+
* Cap2.process(1).has?(:permitted, :kill) #=> true
|
171
|
+
* Cap2.process(1000).has?(:permitted, :kill) #=> false
|
172
|
+
*/
|
173
|
+
VALUE cap2_process_has_cap(VALUE self, VALUE set_sym, VALUE cap_sym) {
|
174
|
+
cap_t cap_d;
|
175
|
+
VALUE result;
|
176
|
+
|
177
|
+
cap_d = cap2_process_caps(self);
|
31
178
|
|
32
|
-
result = cap2_has_cap(
|
33
|
-
cap_d,
|
34
|
-
(cap_flag_t) set,
|
35
|
-
(cap_value_t) cap
|
36
|
-
);
|
179
|
+
result = cap2_has_cap(cap_d, set_sym, cap_sym);
|
37
180
|
|
38
181
|
cap_free(cap_d);
|
39
182
|
|
40
183
|
return result;
|
41
184
|
}
|
42
185
|
|
43
|
-
|
186
|
+
/*
|
187
|
+
* call-seq:
|
188
|
+
* enable(capability) -> true or false
|
189
|
+
*
|
190
|
+
* Enable the given capability for this process.
|
191
|
+
*
|
192
|
+
* Raises a RuntimeError if the process's pid is not the same as the current
|
193
|
+
* pid (you cannot enable capabilities for other processes, that's their job).
|
194
|
+
*
|
195
|
+
* process = Cap2.process #=> <Cap2::Process>
|
196
|
+
* process.permitted?(:kill) #=> true
|
197
|
+
* process.effective?(:kill) #=> false
|
198
|
+
* process.enable(:kill) #=> true
|
199
|
+
* process.effective?(:kill) #=> true
|
200
|
+
*/
|
201
|
+
VALUE cap2_process_enable(VALUE self, VALUE cap_sym) {
|
202
|
+
return cap2_process_set_cap(self, CAP_EFFECTIVE, cap_sym, CAP_SET);
|
203
|
+
}
|
204
|
+
|
205
|
+
/*
|
206
|
+
* call-seq:
|
207
|
+
* disable(capability) -> true or false
|
208
|
+
*
|
209
|
+
* Disable the given capability for this process.
|
210
|
+
*
|
211
|
+
* process = Cap2.process #=> <Cap2::Process>
|
212
|
+
* process.permitted?(:kill) #=> true
|
213
|
+
* process.effective?(:kill) #=> true
|
214
|
+
* process.disable(:kill) #=> true
|
215
|
+
* process.effective?(:kill) #=> false
|
216
|
+
*/
|
217
|
+
VALUE cap2_process_disable(VALUE self, VALUE cap_sym) {
|
218
|
+
return cap2_process_set_cap(self, CAP_EFFECTIVE, cap_sym, CAP_CLEAR);
|
219
|
+
}
|
220
|
+
|
221
|
+
/*
|
222
|
+
* Convert @filename stored in the given File object to a char* and return it.
|
223
|
+
*/
|
224
|
+
static char *cap2_file_filename(VALUE file) {
|
225
|
+
VALUE filename;
|
226
|
+
|
227
|
+
filename = rb_iv_get(file, "@filename");
|
228
|
+
|
229
|
+
return StringValueCStr(filename);
|
230
|
+
}
|
231
|
+
|
232
|
+
/*
|
233
|
+
* Return a cap_t struct containing the capabilities of the given File object.
|
234
|
+
*/
|
235
|
+
static cap_t cap2_file_caps(VALUE file) {
|
44
236
|
cap_t cap_d;
|
45
|
-
|
237
|
+
char *filename;
|
46
238
|
|
47
|
-
|
239
|
+
filename = cap2_file_filename(file);
|
48
240
|
|
49
|
-
cap_d = cap_get_file(
|
241
|
+
cap_d = cap_get_file(filename);
|
50
242
|
|
51
243
|
if (cap_d == NULL && errno != ENODATA) {
|
52
244
|
rb_raise(
|
53
245
|
rb_eRuntimeError,
|
54
246
|
"Failed to get capabilities for file %s: (%s)\n",
|
55
|
-
|
247
|
+
filename, strerror(errno)
|
248
|
+
);
|
249
|
+
}
|
250
|
+
|
251
|
+
return cap_d;
|
252
|
+
}
|
253
|
+
|
254
|
+
/*
|
255
|
+
* Enable/disable the given capability in the given set for the given File
|
256
|
+
* object.
|
257
|
+
*/
|
258
|
+
static VALUE cap2_file_set_cap(VALUE file, cap_flag_t set, VALUE cap_sym, cap_flag_value_t set_or_clear) {
|
259
|
+
cap_t cap_d;
|
260
|
+
char *filename;
|
261
|
+
cap_value_t caps[1];
|
262
|
+
|
263
|
+
filename = cap2_file_filename(file);
|
264
|
+
|
265
|
+
caps[0] = cap2_sym_to_cap(cap_sym);
|
266
|
+
|
267
|
+
cap_d = cap_get_file(filename);
|
268
|
+
|
269
|
+
if(cap_d == NULL)
|
270
|
+
cap_d = cap_init();
|
271
|
+
|
272
|
+
cap_set_flag(cap_d, set, 1, caps, set_or_clear);
|
273
|
+
|
274
|
+
if(cap_set_file(filename, cap_d) == -1) {
|
275
|
+
rb_raise(
|
276
|
+
rb_eRuntimeError,
|
277
|
+
"Failed to set capabilities for file %s: (%s)\n",
|
278
|
+
filename, strerror(errno)
|
56
279
|
);
|
280
|
+
} else {
|
281
|
+
return Qtrue;
|
57
282
|
}
|
283
|
+
}
|
58
284
|
|
59
|
-
|
60
|
-
|
285
|
+
/*
|
286
|
+
* call-seq:
|
287
|
+
* has?(set, capability) -> true or false
|
288
|
+
*
|
289
|
+
* Return whether the file has the given capability enabled in the given set.
|
290
|
+
*
|
291
|
+
* Cap2.file('/bin/ping').has?(:permitted, :net_raw) #=> true
|
292
|
+
* Cap2.file('/tmp/ping').has?(:permitted, :net_raw) #=> false
|
293
|
+
*/
|
294
|
+
VALUE cap2_file_has_cap(VALUE self, VALUE set_sym, VALUE cap_sym) {
|
295
|
+
cap_t cap_d;
|
296
|
+
VALUE result;
|
297
|
+
|
298
|
+
cap_d = cap2_file_caps(self);
|
61
299
|
|
62
|
-
result = cap2_has_cap(
|
63
|
-
cap_d,
|
64
|
-
(cap_flag_t) set,
|
65
|
-
(cap_value_t) cap
|
66
|
-
);
|
300
|
+
result = cap2_has_cap(cap_d, set_sym, cap_sym);
|
67
301
|
|
68
302
|
cap_free(cap_d);
|
69
303
|
|
70
304
|
return result;
|
71
305
|
}
|
72
306
|
|
73
|
-
|
74
|
-
|
307
|
+
/*
|
308
|
+
* call-seq:
|
309
|
+
* permit(capability) -> true or false
|
310
|
+
*
|
311
|
+
* Permit processes executing this file to enable the given capability.
|
312
|
+
*
|
313
|
+
* file = Cap2.file('/tmp/killer') #=> <Cap2::File>
|
314
|
+
* file.permitted?(:kill) #=> false
|
315
|
+
* file.permit(:kill) #=> true
|
316
|
+
* file.permitted?(:kill) #=> true
|
317
|
+
*/
|
318
|
+
VALUE cap2_file_permit(VALUE self, VALUE cap_sym) {
|
319
|
+
return cap2_file_set_cap(self, CAP_PERMITTED, cap_sym, CAP_SET);
|
320
|
+
}
|
75
321
|
|
76
|
-
|
77
|
-
|
322
|
+
/*
|
323
|
+
* call-seq:
|
324
|
+
* unpermit(capability) -> true or false
|
325
|
+
*
|
326
|
+
* Dont permit processes executing ths file to enable the given capability.
|
327
|
+
*
|
328
|
+
* file = Cap2.file('/tmp/foo') #=> <Cap2::File>
|
329
|
+
* file.permit(:kill) #=> true
|
330
|
+
* file.permitted?(:kill) #=> true
|
331
|
+
* file.unpermit(:kill) #=> true
|
332
|
+
* file.permitted?(:kill) #=> false
|
333
|
+
*/
|
334
|
+
VALUE cap2_file_unpermit(VALUE self, VALUE cap_sym) {
|
335
|
+
return cap2_file_set_cap(self, CAP_PERMITTED, cap_sym, CAP_CLEAR);
|
336
|
+
}
|
78
337
|
|
338
|
+
/*
|
339
|
+
* call-seq:
|
340
|
+
* allow_inherit(capability) -> true or false
|
341
|
+
*
|
342
|
+
* Allow processes executing this file to inherit the given capability.
|
343
|
+
*
|
344
|
+
* file = Cap2.file('/tmp/foo') #=> <Cap2::File>
|
345
|
+
* file.inheritable?(:kill) #=> false
|
346
|
+
* file.allow_inherit(:kill) #=> true
|
347
|
+
* file.inheritable?(:kill) #=> true
|
348
|
+
*/
|
349
|
+
VALUE cap2_file_allow_inherit(VALUE self, VALUE cap_sym) {
|
350
|
+
return cap2_file_set_cap(self, CAP_INHERITABLE, cap_sym, CAP_SET);
|
351
|
+
}
|
352
|
+
|
353
|
+
/*
|
354
|
+
* call-seq:
|
355
|
+
* disallow_inherit(capability) -> true or false
|
356
|
+
*
|
357
|
+
* Dont allow processes executing this file to inherit the given capability.
|
358
|
+
*
|
359
|
+
* file = Cap2.file('/tmp/foo') #=> <Cap2::File>
|
360
|
+
* file.inheritable?(:kill) #=> true
|
361
|
+
* file.allow_inherit(:kill) #=> true
|
362
|
+
* file.inheritable?(:kill) #=> false
|
363
|
+
*/
|
364
|
+
VALUE cap2_file_disallow_inherit(VALUE self, VALUE cap_sym) {
|
365
|
+
return cap2_file_set_cap(self, CAP_INHERITABLE, cap_sym, CAP_CLEAR);
|
366
|
+
}
|
367
|
+
|
368
|
+
/*
|
369
|
+
* call-seq:
|
370
|
+
* set_effective(capability) -> true or false
|
371
|
+
*
|
372
|
+
* Enable the given capability when a proces executes this file.
|
373
|
+
*
|
374
|
+
* file = Cap2.file('/tmp/foo') #=> <Cap2::File>
|
375
|
+
* file.effective?(:kill) #=> false
|
376
|
+
* file.set_effective(:kill) #=> true
|
377
|
+
* file.effective?(:kill) #=> true
|
378
|
+
*/
|
379
|
+
VALUE cap2_file_set_effective(VALUE self, VALUE cap_sym) {
|
380
|
+
return cap2_file_set_cap(self, CAP_EFFECTIVE, cap_sym, CAP_SET);
|
381
|
+
}
|
382
|
+
|
383
|
+
/*
|
384
|
+
* call-seq:
|
385
|
+
* disable_on_exec(capability) -> true or false
|
386
|
+
*
|
387
|
+
* Dont enable the given capability when a process executes this file.
|
388
|
+
*
|
389
|
+
* file = Cap2.file('/tmp/foo') #=> <Cap2::File>
|
390
|
+
* file.effective?(:kill) #=> true
|
391
|
+
* file.disable_on_exec(:kill) #=> true
|
392
|
+
* file.effective?(:kill) #=> false
|
393
|
+
*/
|
394
|
+
VALUE cap2_file_clear_effective(VALUE self, VALUE cap_sym) {
|
395
|
+
return cap2_file_set_cap(self, CAP_EFFECTIVE, cap_sym, CAP_CLEAR);
|
396
|
+
}
|
79
397
|
void Init_cap2(void) {
|
80
398
|
VALUE rb_mCap2;
|
81
|
-
|
82
|
-
|
83
|
-
// names of capabilities (e.g. 'dac_override') and the CAP_
|
84
|
-
// constants defined in linux/capability.h and
|
85
|
-
// sys/capability.h (e.g. CAP_DAC_OVERRIDE). They are
|
86
|
-
// assigned to Cap2::SETS and Cap2::CAPS respectively.
|
87
|
-
VALUE sets_hash, caps_hash;
|
399
|
+
VALUE rb_cCap2File;
|
400
|
+
VALUE rb_cCap2Process;
|
88
401
|
|
89
402
|
rb_mCap2 = rb_define_module("Cap2");
|
90
403
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
CapsHashSet("setgid", CAP_SETGID);
|
105
|
-
CapsHashSet("setuid", CAP_SETUID);
|
106
|
-
CapsHashSet("setpcap", CAP_SETPCAP);
|
107
|
-
CapsHashSet("linux_immutable", CAP_LINUX_IMMUTABLE);
|
108
|
-
CapsHashSet("net_bind_service", CAP_NET_BIND_SERVICE);
|
109
|
-
CapsHashSet("net_broadcast", CAP_NET_BROADCAST);
|
110
|
-
CapsHashSet("net_admin", CAP_NET_ADMIN);
|
111
|
-
CapsHashSet("net_raw", CAP_NET_RAW);
|
112
|
-
CapsHashSet("ipc_lock", CAP_IPC_LOCK);
|
113
|
-
CapsHashSet("ipc_owner", CAP_IPC_OWNER);
|
114
|
-
CapsHashSet("sys_module", CAP_SYS_MODULE);
|
115
|
-
CapsHashSet("sys_rawio", CAP_SYS_RAWIO);
|
116
|
-
CapsHashSet("sys_chroot", CAP_SYS_CHROOT);
|
117
|
-
CapsHashSet("sys_ptrace", CAP_SYS_PTRACE);
|
118
|
-
CapsHashSet("sys_pacct", CAP_SYS_PACCT);
|
119
|
-
CapsHashSet("sys_admin", CAP_SYS_ADMIN);
|
120
|
-
CapsHashSet("sys_boot", CAP_SYS_BOOT);
|
121
|
-
CapsHashSet("sys_nice", CAP_SYS_NICE);
|
122
|
-
CapsHashSet("sys_resource", CAP_SYS_RESOURCE);
|
123
|
-
CapsHashSet("sys_time", CAP_SYS_TIME);
|
124
|
-
CapsHashSet("sys_tty_config", CAP_SYS_TTY_CONFIG);
|
125
|
-
CapsHashSet("mknod", CAP_MKNOD);
|
126
|
-
CapsHashSet("lease", CAP_LEASE);
|
127
|
-
CapsHashSet("audit_write", CAP_AUDIT_WRITE);
|
128
|
-
CapsHashSet("audit_control", CAP_AUDIT_CONTROL);
|
129
|
-
CapsHashSet("setfcap", CAP_SETFCAP);
|
130
|
-
CapsHashSet("mac_override", CAP_MAC_OVERRIDE);
|
131
|
-
CapsHashSet("mac_admin", CAP_MAC_ADMIN);
|
132
|
-
CapsHashSet("syslog", CAP_SYSLOG);
|
133
|
-
CapsHashSet("wake_alarm", CAP_WAKE_ALARM);
|
134
|
-
rb_define_const(rb_mCap2, "CAPS", caps_hash);
|
135
|
-
|
136
|
-
rb_define_module_function(
|
137
|
-
rb_mCap2,
|
138
|
-
"pid_has_cap?",
|
139
|
-
cap2_pid_has_cap,
|
140
|
-
3
|
141
|
-
);
|
142
|
-
|
143
|
-
rb_define_module_function(
|
144
|
-
rb_mCap2,
|
145
|
-
"file_has_cap?",
|
146
|
-
cap2_file_has_cap,
|
147
|
-
3
|
148
|
-
);
|
404
|
+
rb_cCap2Process = rb_define_class_under(rb_mCap2, "Process", rb_cObject);
|
405
|
+
rb_define_method(rb_cCap2Process, "has?", cap2_process_has_cap, 2);
|
406
|
+
rb_define_method(rb_cCap2Process, "enable", cap2_process_enable, 1);
|
407
|
+
rb_define_method(rb_cCap2Process, "disable", cap2_process_disable, 1);
|
408
|
+
|
409
|
+
rb_cCap2File = rb_define_class_under(rb_mCap2, "File", rb_cObject);
|
410
|
+
rb_define_method(rb_cCap2File, "has?", cap2_file_has_cap, 2);
|
411
|
+
rb_define_method(rb_cCap2File, "permit", cap2_file_permit, 1);
|
412
|
+
rb_define_method(rb_cCap2File, "unpermit", cap2_file_unpermit, 1);
|
413
|
+
rb_define_method(rb_cCap2File, "allow_inherit", cap2_file_allow_inherit, 1);
|
414
|
+
rb_define_method(rb_cCap2File, "disallow_inherit", cap2_file_disallow_inherit, 1);
|
415
|
+
rb_define_method(rb_cCap2File, "set_effective", cap2_file_set_effective, 1);
|
416
|
+
rb_define_method(rb_cCap2File, "disable_on_exec", cap2_file_clear_effective, 1);
|
149
417
|
}
|
data/lib/cap2.rb
CHANGED
@@ -1,48 +1,15 @@
|
|
1
1
|
require 'cap2.so'
|
2
|
-
require 'cap2/entity'
|
3
2
|
require 'cap2/process'
|
4
3
|
require 'cap2/file'
|
5
4
|
|
5
|
+
# Cap2 is a module for querying the POSIX 1003.1e capabilities
|
6
|
+
# available in Linux kernels. These capabilities are a
|
7
|
+
# partitioning of the all powerful root privilege into a set
|
8
|
+
# of distinct privileges.
|
9
|
+
#
|
10
|
+
# For more information see capabilities(7).
|
6
11
|
module Cap2
|
7
|
-
# = Cap2
|
8
|
-
#
|
9
|
-
# Cap2 is a module for querying the POSIX 1003.1e capabilities
|
10
|
-
# available in Linux kernels. These capabilities are a
|
11
|
-
# partitioning of the all powerful root privilege into a set
|
12
|
-
# of distinct privileges.
|
13
12
|
class << self
|
14
|
-
# A wrapper function around the native Cap2.pid_has_cap?
|
15
|
-
# and Cap2.file_has_cap?. Returns true if the given
|
16
|
-
# capability is enabled in the given set for the given pid /
|
17
|
-
# filename.
|
18
|
-
#
|
19
|
-
# @param [String, Fixnum] pid_or_filename
|
20
|
-
# When a Fixnum, query the capabilities for the process
|
21
|
-
# with this value. When a String, query for the file
|
22
|
-
# with a filename of this value.
|
23
|
-
#
|
24
|
-
# @param [Symbol] set
|
25
|
-
# One of :permitted, :effective or :inheritable.
|
26
|
-
#
|
27
|
-
# @param [Symbol] cap
|
28
|
-
# A lower cased name of a capability, without the 'CAP_'
|
29
|
-
# prefix. For example, :chown would query the CAP_CHOWN
|
30
|
-
# capability
|
31
|
-
def has_capability?(pid_or_filename, set, cap)
|
32
|
-
# See ext/cap2/cap2.c for the definition of SETS and CAPS
|
33
|
-
set = SETS[set.to_s]
|
34
|
-
cap = CAPS[cap.to_s]
|
35
|
-
|
36
|
-
case pid_or_filename
|
37
|
-
when Fixnum
|
38
|
-
pid_has_cap?(pid_or_filename, set, cap)
|
39
|
-
when String
|
40
|
-
file_has_cap?(pid_or_filename, set, cap)
|
41
|
-
else
|
42
|
-
raise ArgumentError, "wrong argument type, expected Fixnum or String, got #{pid_or_filename.class}"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
13
|
# Returns a Cap2::Process initialized with the given pid,
|
47
14
|
# defaulting to the current pid.
|
48
15
|
def process(pid = ::Process.pid)
|
@@ -77,6 +44,12 @@ module Cap2
|
|
77
44
|
end
|
78
45
|
end
|
79
46
|
|
47
|
+
# Raised when trying to initialise a Process object for a non-existent pid.
|
80
48
|
class ProcessNotFound < StandardError; end
|
49
|
+
|
50
|
+
# Raised when trying to initialise a File object for a non-existent file.
|
81
51
|
class FileNotFound < StandardError; end
|
52
|
+
|
53
|
+
# Raised when trying to enable unpermitted / uninheritable file capabilities.
|
54
|
+
class IncompatibleCapabilities < StandardError; end
|
82
55
|
end
|
data/lib/cap2.so
ADDED
Binary file
|
data/lib/cap2/file.rb
CHANGED
@@ -1,11 +1,26 @@
|
|
1
|
+
require 'cap2/set_methods'
|
2
|
+
|
1
3
|
module Cap2
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
# A class with methods for querying capabilities for the
|
5
|
+
# file with filename provided to the initialize method.
|
6
|
+
class File
|
7
|
+
include SetMethods
|
8
|
+
|
9
|
+
# Initialize a new File object for the given filename.
|
7
10
|
def initialize(filename)
|
8
|
-
@
|
11
|
+
@filename = filename
|
12
|
+
end
|
13
|
+
|
14
|
+
# Enable the given capability in the file's effective set.
|
15
|
+
#
|
16
|
+
# The capability must be either permitted or inheritable (or else it cannot
|
17
|
+
# possibly be enabled in the new process).
|
18
|
+
def enable_on_exec(capability)
|
19
|
+
if permitted?(capability) || inheritable?(capability)
|
20
|
+
set_effective(capability)
|
21
|
+
else
|
22
|
+
raise IncompatibleCapabilities, 'cannot enable_on_exec a capability which is neither permitted nor inheritable'
|
23
|
+
end
|
9
24
|
end
|
10
25
|
end
|
11
26
|
end
|
data/lib/cap2/process.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
+
require 'cap2/set_methods'
|
2
|
+
|
1
3
|
module Cap2
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
# A class with methods for querying capabilities for the
|
5
|
+
# process with pid provided to the initialize method.
|
6
|
+
class Process
|
7
|
+
include SetMethods
|
8
|
+
|
9
|
+
# Initialize a new Process object for the given pid.
|
7
10
|
def initialize(pid)
|
8
|
-
@
|
11
|
+
@pid = pid
|
9
12
|
end
|
10
13
|
end
|
11
14
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Cap2
|
2
|
+
# A mixin for the Cap2::Process and Cap2::File
|
3
|
+
# classes providing convenience methods for querying
|
4
|
+
# permitted, effective and inheritable capabilities.
|
5
|
+
#
|
6
|
+
# Each method takes a capability argument, a lower
|
7
|
+
# cased name of a capability, without the 'CAP_' prefix.
|
8
|
+
# For example, :chown would query the CAP_CHOWN capability.
|
9
|
+
module SetMethods
|
10
|
+
# Returns whether the given capability is permitted
|
11
|
+
def permitted?(capability)
|
12
|
+
has?(:permitted, capability)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns whether the given capability is effective
|
16
|
+
def effective?(capability)
|
17
|
+
has?(:effective, capability)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns whether the given capability is inheritable
|
21
|
+
def inheritable?(capability)
|
22
|
+
has?(:inheritable, capability)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
data/lib/cap2/version.rb
CHANGED
data/spec/cap2_spec.rb
CHANGED
@@ -1,38 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Cap2 do
|
4
|
-
describe '.has_capability?' do
|
5
|
-
context 'for processes' do
|
6
|
-
context 'when the given process does not have the given capability' do
|
7
|
-
it { should_not have_capability(
|
8
|
-
Process.pid, :permitted, :dac_override) }
|
9
|
-
end
|
10
|
-
|
11
|
-
context 'when the given process does have the given capability' do
|
12
|
-
it { should have_capability(
|
13
|
-
1, :permitted, :dac_override) }
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
context 'for files' do
|
18
|
-
let(:file) { Tempfile.new('cap-test') }
|
19
|
-
|
20
|
-
context 'when the given file does not have the given capability' do
|
21
|
-
it { should_not have_capability(
|
22
|
-
file.path, :permitted, :dac_override) }
|
23
|
-
end
|
24
|
-
|
25
|
-
context 'when the given file does have the given capability' do
|
26
|
-
before(:each) do
|
27
|
-
system %{sudo setcap "cap_dac_override+p" #{file.path}}
|
28
|
-
end
|
29
|
-
|
30
|
-
it { should have_capability(
|
31
|
-
file.path, :permitted, :dac_override) }
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
4
|
describe '.process' do
|
37
5
|
let(:process) { double 'process' }
|
38
6
|
|
data/spec/file_spec.rb
CHANGED
@@ -1,6 +1,140 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Cap2::File do
|
4
|
-
|
5
|
-
|
4
|
+
let(:file) { Tempfile.new('cap-test') }
|
5
|
+
|
6
|
+
subject { Cap2::File.new(file.path) }
|
7
|
+
|
8
|
+
describe '#permitted?' do
|
9
|
+
context "when the file doesn't have the given capability" do
|
10
|
+
it { should_not be_permitted(:dac_override) }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when the process does have the given capability' do
|
14
|
+
before(:each) do
|
15
|
+
system %{sudo setcap "cap_dac_override+p" #{file.path}}
|
16
|
+
end
|
17
|
+
|
18
|
+
it { should be_permitted(:dac_override) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#effective?' do
|
23
|
+
context "when the file doesn't have the given capability" do
|
24
|
+
it { should_not be_effective(:dac_override) }
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when the process does have the given capability' do
|
28
|
+
before(:each) do
|
29
|
+
system %{sudo setcap "cap_dac_override+pe" #{file.path}}
|
30
|
+
end
|
31
|
+
|
32
|
+
it { should be_effective(:dac_override) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#inheritable?' do
|
37
|
+
context "when the file doesn't have the given capability" do
|
38
|
+
it { should_not be_inheritable(:dac_override) }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when the process does have the given capability' do
|
42
|
+
before(:each) do
|
43
|
+
system %{sudo setcap "cap_dac_override+i" #{file.path}}
|
44
|
+
end
|
45
|
+
|
46
|
+
it { should be_inheritable(:dac_override) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#permit' do
|
51
|
+
specify do
|
52
|
+
expect { running_as_root('permit(:fowner)') }.to \
|
53
|
+
change { subject.permitted?(:fowner) }.from(false).to(true)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#unpermit' do
|
58
|
+
before(:each) do
|
59
|
+
run_as_root('permit(:fowner)')
|
60
|
+
end
|
6
61
|
|
62
|
+
specify do
|
63
|
+
expect { running_as_root('unpermit(:fowner)') }.to \
|
64
|
+
change { subject.permitted?(:fowner) }.from(true).to(false)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#allow_inherit' do
|
69
|
+
specify do
|
70
|
+
expect { running_as_root('allow_inherit(:chown)') }.to \
|
71
|
+
change { subject.inheritable?(:chown) }.from(false).to(true)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#disallow_inherit' do
|
76
|
+
before(:each) do
|
77
|
+
run_as_root('allow_inherit(:chown)')
|
78
|
+
end
|
79
|
+
|
80
|
+
specify do
|
81
|
+
expect { running_as_root('disallow_inherit(:chown)') }.to \
|
82
|
+
change { subject.inheritable?(:chown) }.from(true).to(false)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#enable_on_exec' do
|
87
|
+
context 'when the capability is not permitted or inheritable' do
|
88
|
+
specify do
|
89
|
+
expect { subject.enable_on_exec(:lease) }.to \
|
90
|
+
raise_error(
|
91
|
+
Cap2::IncompatibleCapabilities,
|
92
|
+
'cannot enable_on_exec a capability which is neither permitted nor inheritable'
|
93
|
+
)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'when the capability is permitted' do
|
98
|
+
before(:each) do
|
99
|
+
run_as_root('permit(:lease)')
|
100
|
+
end
|
101
|
+
|
102
|
+
specify do
|
103
|
+
expect { running_as_root('enable_on_exec(:lease)') }.to \
|
104
|
+
change { subject.effective?(:lease) }.from(false).to(true)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when the capability is inheritable' do
|
109
|
+
before(:each) do
|
110
|
+
run_as_root('allow_inherit(:lease)')
|
111
|
+
end
|
112
|
+
|
113
|
+
specify do
|
114
|
+
expect { running_as_root('enable_on_exec(:lease)') }.to \
|
115
|
+
change { subject.effective?(:lease) }.from(false).to(true)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe '#disable_on_exec' do
|
121
|
+
before(:each) do
|
122
|
+
run_as_root('permit(:kill)', 'enable_on_exec(:kill)')
|
123
|
+
end
|
124
|
+
|
125
|
+
specify do
|
126
|
+
expect { running_as_root('disable_on_exec(:kill)') }.to \
|
127
|
+
change { subject.effective?(:kill) }.from(true).to(false)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# FIXME: Would like to call the given code on subject directly (e.g.
|
132
|
+
# `subject.permit(:fowner)`) but this would require the test
|
133
|
+
# suite to be run as root?
|
134
|
+
def run_as_root(*codes)
|
135
|
+
codes.each do |code|
|
136
|
+
system %{sudo ruby -Ilib -rcap2 -e 'Cap2.file("#{file.path}").#{code}'}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
alias running_as_root run_as_root
|
140
|
+
end
|
data/spec/process_spec.rb
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Cap2::Process do
|
4
|
-
|
4
|
+
describe '#permitted?' do
|
5
|
+
context "when the process doesn't have the given capability" do
|
6
|
+
subject { Cap2::Process.new(Process.pid) }
|
7
|
+
|
8
|
+
it { should_not be_permitted(:dac_override) }
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'when the process does have the given capability' do
|
12
|
+
subject { Cap2::Process.new(1) }
|
13
|
+
|
14
|
+
it { should be_permitted(:dac_override) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#effective?' do
|
19
|
+
context "when the process doesn't have the given capability" do
|
20
|
+
subject { Cap2::Process.new(Process.pid) }
|
21
|
+
|
22
|
+
it { should_not be_effective(:dac_override) }
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when the process does have the given capability' do
|
26
|
+
subject { Cap2::Process.new(1) }
|
27
|
+
|
28
|
+
it { should be_effective(:dac_override) }
|
29
|
+
end
|
30
|
+
end
|
5
31
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cap2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,12 +9,12 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-30 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! " Cap2 is a Ruby library for managing the POSIX 1003.1e capabilities\n
|
15
|
-
\ available in Linux kernels.\n
|
16
|
-
|
17
|
-
|
15
|
+
\ available in Linux kernels.\n\n These capabilities are a partitioning of
|
16
|
+
the all powerful root\n privilege into a set of distinct privileges.\n\n See
|
17
|
+
capabilites(7) for more information.\n"
|
18
18
|
email:
|
19
19
|
- lewismarshall86@gmail.com
|
20
20
|
executables: []
|
@@ -29,10 +29,10 @@ files:
|
|
29
29
|
- ext/cap2/cap2.c
|
30
30
|
- lib/cap2.rb
|
31
31
|
- lib/cap2/process.rb
|
32
|
-
- lib/cap2/entity.rb
|
33
32
|
- lib/cap2/version.rb
|
34
33
|
- lib/cap2/file.rb
|
35
|
-
-
|
34
|
+
- lib/cap2/set_methods.rb
|
35
|
+
- lib/cap2.so
|
36
36
|
- spec/process_spec.rb
|
37
37
|
- spec/cap2_spec.rb
|
38
38
|
- spec/spec_helper.rb
|
@@ -62,3 +62,4 @@ signing_key:
|
|
62
62
|
specification_version: 3
|
63
63
|
summary: A Ruby library for managing Linux file and process capabilities
|
64
64
|
test_files: []
|
65
|
+
has_rdoc:
|
data/lib/cap2/entity.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
module Cap2
|
2
|
-
class Entity
|
3
|
-
# = Entity
|
4
|
-
#
|
5
|
-
# A superclass for the Cap2::Process and Cap2::File
|
6
|
-
# classes providing convenience methods for querying
|
7
|
-
# permitted, effective and inheritable capabilities.
|
8
|
-
#
|
9
|
-
# Each method takes a capability argument, a lower
|
10
|
-
# cased name of a capability, without the 'CAP_' prefix.
|
11
|
-
# For example, :chown would query the CAP_CHOWN capability.
|
12
|
-
|
13
|
-
def permitted?(capability)
|
14
|
-
has?(:permitted, capability)
|
15
|
-
end
|
16
|
-
|
17
|
-
def effective?(capability)
|
18
|
-
has?(:effective, capability)
|
19
|
-
end
|
20
|
-
|
21
|
-
def inheritable?(capability)
|
22
|
-
has?(:inheritable, capability)
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
def has?(set, cap)
|
27
|
-
Cap2.has_capability?(@entity_id, set, cap)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
data/spec/support/entity.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
shared_examples_for 'an entity' do
|
4
|
-
let(:entity_id) { double 'entity id' }
|
5
|
-
let(:capability) { double 'capability' }
|
6
|
-
|
7
|
-
subject { described_class.new(entity_id) }
|
8
|
-
|
9
|
-
describe '#permitted?' do
|
10
|
-
it 'should call Cap2.has_capability? correctly' do
|
11
|
-
Cap2.
|
12
|
-
should_receive(:has_capability?).
|
13
|
-
with(entity_id, :permitted, capability)
|
14
|
-
|
15
|
-
subject.permitted?(capability)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
describe '#effective?' do
|
20
|
-
it 'should call Cap2.has_capability? correctly' do
|
21
|
-
Cap2.
|
22
|
-
should_receive(:has_capability?).
|
23
|
-
with(entity_id, :effective, capability)
|
24
|
-
|
25
|
-
subject.effective?(capability)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe '#inheritable?' do
|
30
|
-
it 'should call Cap2.has_capability? correctly' do
|
31
|
-
Cap2.
|
32
|
-
should_receive(:has_capability?).
|
33
|
-
with(entity_id, :inheritable, capability)
|
34
|
-
|
35
|
-
subject.inheritable?(capability)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|