cap2 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +22 -17
- data/ext/cap2/cap2.c +160 -249
- data/lib/cap2.rb +1 -3
- data/lib/cap2.so +0 -0
- data/lib/cap2/file.rb +60 -15
- data/lib/cap2/process.rb +48 -6
- data/lib/cap2/version.rb +1 -1
- data/spec/file_spec.rb +39 -38
- data/spec/process_spec.rb +3 -3
- metadata +2 -3
- data/lib/cap2/set_methods.rb +0 -26
data/README.md
CHANGED
@@ -49,16 +49,16 @@ Capabilities are referenced using lower cased symbols, and without the CAP_ pref
|
|
49
49
|
|
50
50
|
### Querying Capabilities
|
51
51
|
|
52
|
-
There are three methods - `permitted?`, `
|
52
|
+
There are three methods - `permitted?`, `enabled?` and `inheritable?` - defined on both `Cap2::Process` and `Cap2::File` for querying capabilities. Each of these methods, Cap2::File#enabled? being the exception, take a capability symbol and return true / false if the capability is in / not in the relevant set:
|
53
53
|
|
54
54
|
```
|
55
|
-
# the init daemon - all caps permitted &
|
55
|
+
# the init daemon - all caps permitted & enabled but not inheritable
|
56
56
|
init = Cap2.process(1) # => #<Cap2::Process @pid=1>
|
57
57
|
|
58
58
|
init.permitted?(:kill) # => true
|
59
59
|
init.permitted?(:chown) # => true
|
60
60
|
|
61
|
-
init.
|
61
|
+
init.enabled?(:fowner) # => true
|
62
62
|
|
63
63
|
init.inheritable?(:kill) # => false
|
64
64
|
init.inheritable?(:fowner) # => false
|
@@ -69,11 +69,15 @@ ping = Cap2.file('/bin/ping') # => #<Cap2::File @filename="/bin/ping">
|
|
69
69
|
ping.permitted?(:net_raw) # => true
|
70
70
|
ping.permitted?(:mknod) # => false
|
71
71
|
|
72
|
-
ping.
|
72
|
+
ping.enabled? # => true
|
73
73
|
|
74
74
|
ping.inheritable?(:net_raw) # => false
|
75
75
|
```
|
76
76
|
|
77
|
+
#### Why does Cap2::File#enabled? not take an arguement?
|
78
|
+
|
79
|
+
Under the hood, the file effective set is just a single bit. When enabled, any process which exec's the file will have the capabilities from it's resulting permitted set also in it's effective set.
|
80
|
+
|
77
81
|
### Modifying Capabilities
|
78
82
|
|
79
83
|
Cap2 provides different levels of control over process and file capabilities.
|
@@ -83,7 +87,7 @@ Cap2 provides different levels of control over process and file capabilities.
|
|
83
87
|
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
88
|
|
85
89
|
```
|
86
|
-
Cap2.process.
|
90
|
+
Cap2.process.enabled?(:setfcap) # => true - needed to set file capabilities
|
87
91
|
|
88
92
|
file = Cap2.file('/tmp/file') # => #<Cap2::File @filename="/tmp/file">
|
89
93
|
|
@@ -96,26 +100,27 @@ file.unpermit(:mknod) # => true
|
|
96
100
|
file.permitted?(:mknod) # => false
|
97
101
|
```
|
98
102
|
|
99
|
-
To modify the effective
|
103
|
+
To modify the effective bit of a file (i.e. whether the resulting permitted capabilities of any process that exec's the file will also be enabled in it's effective set), use `Cap2::File#enable` and `Cap2::File#disable`:
|
100
104
|
|
101
105
|
```
|
102
|
-
Cap2.process.
|
106
|
+
Cap2.process.enabled?(:setfcap) # => true - needed to set file capabilities
|
103
107
|
|
104
108
|
file = Cap2.file('/tmp/file') # => #<Cap2::File @filename="/tmp/file">
|
105
109
|
|
106
|
-
file.
|
110
|
+
file.permitted?(:net_raw) # => true
|
111
|
+
file.enabled? # => false
|
107
112
|
|
108
|
-
file.
|
109
|
-
file.
|
113
|
+
file.enable # => true
|
114
|
+
file.enabled? # => true
|
110
115
|
|
111
|
-
file.
|
112
|
-
file.
|
116
|
+
file.disable # => true
|
117
|
+
file.enabled? # => false
|
113
118
|
```
|
114
119
|
|
115
120
|
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
121
|
|
117
122
|
```
|
118
|
-
Cap2.process.
|
123
|
+
Cap2.process.enabled?(:setfcap) # => true - needed to set file capabilities
|
119
124
|
|
120
125
|
file = Cap2.file('/tmp/file') # => #<Cap2::File @filename="/tmp/file">
|
121
126
|
|
@@ -132,22 +137,22 @@ file.inheritable?(:fowner) # => false
|
|
132
137
|
|
133
138
|
Cap2 can be used to enable / disable capabilities of the current Ruby process.
|
134
139
|
|
135
|
-
Suppose the ruby binary file permits
|
140
|
+
Suppose the ruby binary file permits `:kill`, but does not enable it on exec:
|
136
141
|
|
137
142
|
```
|
138
143
|
ruby = Cap2.file('/usr/bin/ruby') # => #<Cap2::File @filename="/usr/bin/ruby">
|
139
144
|
ruby.permitted?(:kill) # => true
|
140
|
-
ruby.
|
145
|
+
ruby.enabled? # => false
|
141
146
|
```
|
142
147
|
|
143
148
|
and we want to kill a process running as root with pid 1000:
|
144
149
|
|
145
150
|
```
|
146
|
-
Cap2.process.
|
151
|
+
Cap2.process.enabled?(:kill) # => false
|
147
152
|
Process.kill("TERM", 1000) # => Errno::EPERM: Operation not permitted
|
148
153
|
|
149
154
|
Cap2.process.enable(:kill) # => true
|
150
|
-
Cap2.process.
|
155
|
+
Cap2.process.enabled?(:kill) # => true
|
151
156
|
Process.kill("TERM", 1000) # => 1
|
152
157
|
```
|
153
158
|
|
data/ext/cap2/cap2.c
CHANGED
@@ -1,29 +1,10 @@
|
|
1
1
|
#include <ruby.h>
|
2
|
+
#include <stdbool.h>
|
2
3
|
#include <errno.h>
|
3
4
|
#include <unistd.h>
|
4
5
|
#include <sys/capability.h>
|
5
6
|
#include "cap2.h"
|
6
7
|
|
7
|
-
/*
|
8
|
-
* Converts a Ruby symbol into cap_flag_t set, defined in <sys/capability.h>
|
9
|
-
*
|
10
|
-
* Raises an ArgumentError if set is not a valid capability set.
|
11
|
-
*/
|
12
|
-
cap_flag_t cap2_sym_to_set(VALUE set) {
|
13
|
-
char *set_s;
|
14
|
-
|
15
|
-
Check_Type(set, T_SYMBOL);
|
16
|
-
|
17
|
-
set = rb_sym_to_s(set);
|
18
|
-
|
19
|
-
set_s = StringValueCStr(set);
|
20
|
-
|
21
|
-
if(strcmp(set_s, "permitted") == 0) return CAP_PERMITTED;
|
22
|
-
else if(strcmp(set_s, "effective") == 0) return CAP_EFFECTIVE;
|
23
|
-
else if(strcmp(set_s, "inheritable") == 0) return CAP_INHERITABLE;
|
24
|
-
else rb_raise(rb_eArgError, "unknown set %s", set_s);
|
25
|
-
}
|
26
|
-
|
27
8
|
/*
|
28
9
|
* Lookup the value of a capability in cap2_caps, defined in cap2.h
|
29
10
|
* (cap2.h is generated dynamically by extconf.rb).
|
@@ -52,21 +33,50 @@ cap_value_t cap2_sym_to_cap(VALUE cap) {
|
|
52
33
|
return cap2_cap_value(StringValueCStr(cap));
|
53
34
|
}
|
54
35
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
36
|
+
VALUE cap2_caps_to_hash(cap_t cap_d) {
|
37
|
+
int i;
|
38
|
+
cap_flag_value_t cap_value;
|
39
|
+
VALUE caps, permitted, effective, inheritable;
|
40
|
+
|
41
|
+
permitted = rb_ary_new();
|
42
|
+
effective = rb_ary_new();
|
43
|
+
inheritable = rb_ary_new();
|
63
44
|
|
64
|
-
|
65
|
-
|
45
|
+
for(i = 0; i < __CAP_COUNT; i++) {
|
46
|
+
cap_get_flag(cap_d, cap2_caps[i].value, CAP_PERMITTED, &cap_value);
|
47
|
+
if(cap_value == CAP_SET)
|
48
|
+
rb_ary_push(permitted, ID2SYM(rb_intern(cap2_caps[i].name)));
|
49
|
+
|
50
|
+
cap_get_flag(cap_d, cap2_caps[i].value, CAP_EFFECTIVE, &cap_value);
|
51
|
+
if(cap_value == CAP_SET)
|
52
|
+
rb_ary_push(effective, ID2SYM(rb_intern(cap2_caps[i].name)));
|
53
|
+
|
54
|
+
cap_get_flag(cap_d, cap2_caps[i].value, CAP_INHERITABLE, &cap_value);
|
55
|
+
if(cap_value == CAP_SET)
|
56
|
+
rb_ary_push(inheritable, ID2SYM(rb_intern(cap2_caps[i].name)));
|
57
|
+
}
|
66
58
|
|
67
|
-
|
59
|
+
caps = rb_hash_new();
|
68
60
|
|
69
|
-
|
61
|
+
rb_hash_aset(
|
62
|
+
caps,
|
63
|
+
ID2SYM(rb_intern("permitted")),
|
64
|
+
rb_funcall(permitted, rb_intern("to_set"), 0)
|
65
|
+
);
|
66
|
+
|
67
|
+
rb_hash_aset(
|
68
|
+
caps,
|
69
|
+
ID2SYM(rb_intern("effective")),
|
70
|
+
rb_funcall(effective, rb_intern("to_set"), 0)
|
71
|
+
);
|
72
|
+
|
73
|
+
rb_hash_aset(
|
74
|
+
caps,
|
75
|
+
ID2SYM(rb_intern("inheritable")),
|
76
|
+
rb_funcall(inheritable, rb_intern("to_set"), 0)
|
77
|
+
);
|
78
|
+
|
79
|
+
return caps;
|
70
80
|
}
|
71
81
|
|
72
82
|
/*
|
@@ -80,14 +90,12 @@ static int cap2_process_pid(VALUE process) {
|
|
80
90
|
return FIX2INT(pid);
|
81
91
|
}
|
82
92
|
|
83
|
-
|
84
|
-
* Return a cap_t struct containing the capabilities of the given Process object.
|
85
|
-
*/
|
86
|
-
static cap_t cap2_process_caps(VALUE process) {
|
93
|
+
VALUE cap2_process_getcaps(VALUE self) {
|
87
94
|
cap_t cap_d;
|
88
95
|
int pid;
|
96
|
+
VALUE result;
|
89
97
|
|
90
|
-
pid = cap2_process_pid(
|
98
|
+
pid = cap2_process_pid(self);
|
91
99
|
|
92
100
|
cap_d = cap_get_pid(pid);
|
93
101
|
|
@@ -97,102 +105,80 @@ static cap_t cap2_process_caps(VALUE process) {
|
|
97
105
|
"Failed to get capabilities for proccess %d: (%s)\n",
|
98
106
|
pid, strerror(errno)
|
99
107
|
);
|
108
|
+
} else {
|
109
|
+
result = cap2_caps_to_hash(cap_d);
|
110
|
+
cap_free(cap_d);
|
111
|
+
return result;
|
100
112
|
}
|
101
|
-
|
102
|
-
return cap_d;
|
103
113
|
}
|
104
114
|
|
105
115
|
/*
|
106
|
-
*
|
107
|
-
* object.
|
116
|
+
* Set the capabilities for self from the caps hash stored in @caps.
|
108
117
|
*/
|
109
|
-
|
118
|
+
VALUE cap2_process_setcaps(VALUE self) {
|
119
|
+
int i;
|
110
120
|
cap_t cap_d;
|
111
|
-
|
112
|
-
cap_value_t
|
121
|
+
VALUE caps, cap_array, cap_sym;
|
122
|
+
cap_value_t cap_values[__CAP_COUNT];
|
113
123
|
|
114
|
-
|
124
|
+
cap_d = cap_init();
|
115
125
|
|
116
|
-
|
117
|
-
rb_raise(
|
118
|
-
rb_eRuntimeError,
|
119
|
-
"Cannot set capabilities for other processes"
|
120
|
-
);
|
126
|
+
caps = rb_iv_get(self, "@caps");
|
121
127
|
|
122
|
-
|
128
|
+
// permitted
|
129
|
+
cap_array = rb_funcall(
|
130
|
+
rb_hash_aref(caps, ID2SYM(rb_intern("permitted"))),
|
131
|
+
rb_intern("to_a"),
|
132
|
+
0
|
133
|
+
);
|
123
134
|
|
124
|
-
|
135
|
+
for(i = 0; i < RARRAY_LEN(cap_array); i++) {
|
136
|
+
cap_sym = RARRAY_PTR(cap_array)[i];
|
137
|
+
cap_values[i] = cap2_sym_to_cap(cap_sym);
|
138
|
+
}
|
139
|
+
|
140
|
+
cap_set_flag(cap_d, CAP_PERMITTED, i, cap_values, CAP_SET);
|
125
141
|
|
126
|
-
|
142
|
+
// effective
|
143
|
+
cap_array = rb_funcall(
|
144
|
+
rb_hash_aref(caps, ID2SYM(rb_intern("effective"))),
|
145
|
+
rb_intern("to_a"),
|
146
|
+
0
|
147
|
+
);
|
148
|
+
|
149
|
+
for(i = 0; i < RARRAY_LEN(cap_array); i++) {
|
150
|
+
cap_sym = RARRAY_PTR(cap_array)[i];
|
151
|
+
cap_values[i] = cap2_sym_to_cap(cap_sym);
|
152
|
+
}
|
153
|
+
|
154
|
+
cap_set_flag(cap_d, CAP_EFFECTIVE, i, cap_values, CAP_SET);
|
155
|
+
|
156
|
+
// inheritable
|
157
|
+
cap_array = rb_funcall(
|
158
|
+
rb_hash_aref(caps, ID2SYM(rb_intern("inheritable"))),
|
159
|
+
rb_intern("to_a"),
|
160
|
+
0
|
161
|
+
);
|
162
|
+
|
163
|
+
for(i = 0; i < RARRAY_LEN(cap_array); i++) {
|
164
|
+
cap_sym = RARRAY_PTR(cap_array)[i];
|
165
|
+
cap_values[i] = cap2_sym_to_cap(cap_sym);
|
166
|
+
}
|
167
|
+
|
168
|
+
cap_set_flag(cap_d, CAP_INHERITABLE, i, cap_values, CAP_SET);
|
127
169
|
|
128
170
|
if(cap_set_proc(cap_d) == -1) {
|
129
171
|
rb_raise(
|
130
172
|
rb_eRuntimeError,
|
131
|
-
"Failed to set capabilities for process
|
132
|
-
|
173
|
+
"Failed to set capabilities for current process: (%s)\n",
|
174
|
+
strerror(errno)
|
133
175
|
);
|
134
176
|
} else {
|
177
|
+
cap_free(cap_d);
|
135
178
|
return Qtrue;
|
136
179
|
}
|
137
180
|
}
|
138
181
|
|
139
|
-
/*
|
140
|
-
* call-seq:
|
141
|
-
* has?(set, capability) -> true or false
|
142
|
-
*
|
143
|
-
* Return whether the process has the given capability enabled in the given set.
|
144
|
-
*
|
145
|
-
* Cap2.process(1).has?(:permitted, :kill) #=> true
|
146
|
-
* Cap2.process(1000).has?(:permitted, :kill) #=> false
|
147
|
-
*/
|
148
|
-
VALUE cap2_process_has_cap(VALUE self, VALUE set_sym, VALUE cap_sym) {
|
149
|
-
cap_t cap_d;
|
150
|
-
VALUE result;
|
151
|
-
|
152
|
-
cap_d = cap2_process_caps(self);
|
153
|
-
|
154
|
-
result = cap2_has_cap(cap_d, set_sym, cap_sym);
|
155
|
-
|
156
|
-
cap_free(cap_d);
|
157
|
-
|
158
|
-
return result;
|
159
|
-
}
|
160
|
-
|
161
|
-
/*
|
162
|
-
* call-seq:
|
163
|
-
* enable(capability) -> true or false
|
164
|
-
*
|
165
|
-
* Enable the given capability for this process.
|
166
|
-
*
|
167
|
-
* Raises a RuntimeError if the process's pid is not the same as the current
|
168
|
-
* pid (you cannot enable capabilities for other processes, that's their job).
|
169
|
-
*
|
170
|
-
* process = Cap2.process #=> <Cap2::Process>
|
171
|
-
* process.permitted?(:kill) #=> true
|
172
|
-
* process.effective?(:kill) #=> false
|
173
|
-
* process.enable(:kill) #=> true
|
174
|
-
* process.effective?(:kill) #=> true
|
175
|
-
*/
|
176
|
-
VALUE cap2_process_enable(VALUE self, VALUE cap_sym) {
|
177
|
-
return cap2_process_set_cap(self, CAP_EFFECTIVE, cap_sym, CAP_SET);
|
178
|
-
}
|
179
|
-
|
180
|
-
/*
|
181
|
-
* call-seq:
|
182
|
-
* disable(capability) -> true or false
|
183
|
-
*
|
184
|
-
* Disable the given capability for this process.
|
185
|
-
*
|
186
|
-
* process = Cap2.process #=> <Cap2::Process>
|
187
|
-
* process.permitted?(:kill) #=> true
|
188
|
-
* process.effective?(:kill) #=> true
|
189
|
-
* process.disable(:kill) #=> true
|
190
|
-
* process.effective?(:kill) #=> false
|
191
|
-
*/
|
192
|
-
VALUE cap2_process_disable(VALUE self, VALUE cap_sym) {
|
193
|
-
return cap2_process_set_cap(self, CAP_EFFECTIVE, cap_sym, CAP_CLEAR);
|
194
|
-
}
|
195
|
-
|
196
182
|
/*
|
197
183
|
* Convert @filename stored in the given File object to a char* and return it.
|
198
184
|
*/
|
@@ -205,13 +191,14 @@ static char *cap2_file_filename(VALUE file) {
|
|
205
191
|
}
|
206
192
|
|
207
193
|
/*
|
208
|
-
* Return a
|
194
|
+
* Return a caps hash containing the capabilities of self.
|
209
195
|
*/
|
210
|
-
|
196
|
+
VALUE cap2_file_getcaps(VALUE self) {
|
211
197
|
cap_t cap_d;
|
212
198
|
char *filename;
|
199
|
+
VALUE result;
|
213
200
|
|
214
|
-
filename = cap2_file_filename(
|
201
|
+
filename = cap2_file_filename(self);
|
215
202
|
|
216
203
|
cap_d = cap_get_file(filename);
|
217
204
|
|
@@ -221,30 +208,70 @@ static cap_t cap2_file_caps(VALUE file) {
|
|
221
208
|
"Failed to get capabilities for file %s: (%s)\n",
|
222
209
|
filename, strerror(errno)
|
223
210
|
);
|
211
|
+
} else {
|
212
|
+
result = cap2_caps_to_hash(cap_d);
|
213
|
+
cap_free(cap_d);
|
214
|
+
return result;
|
224
215
|
}
|
225
|
-
|
226
|
-
return cap_d;
|
227
216
|
}
|
228
217
|
|
229
218
|
/*
|
230
|
-
*
|
231
|
-
* object.
|
219
|
+
* Set the capabilities for self from the caps hash stored in @caps.
|
232
220
|
*/
|
233
|
-
|
221
|
+
VALUE cap2_file_setcaps(VALUE self) {
|
222
|
+
int i;
|
234
223
|
cap_t cap_d;
|
235
224
|
char *filename;
|
236
|
-
|
225
|
+
VALUE caps, cap_array, cap_sym;
|
226
|
+
cap_value_t cap_values[__CAP_COUNT];
|
237
227
|
|
238
|
-
|
228
|
+
cap_d = cap_init();
|
239
229
|
|
240
|
-
caps
|
230
|
+
caps = rb_iv_get(self, "@caps");
|
241
231
|
|
242
|
-
|
232
|
+
// permitted
|
233
|
+
cap_array = rb_funcall(
|
234
|
+
rb_hash_aref(caps, ID2SYM(rb_intern("permitted"))),
|
235
|
+
rb_intern("to_a"),
|
236
|
+
0
|
237
|
+
);
|
238
|
+
|
239
|
+
for(i = 0; i < RARRAY_LEN(cap_array); i++) {
|
240
|
+
cap_sym = RARRAY_PTR(cap_array)[i];
|
241
|
+
cap_values[i] = cap2_sym_to_cap(cap_sym);
|
242
|
+
}
|
243
|
+
|
244
|
+
cap_set_flag(cap_d, CAP_PERMITTED, i, cap_values, CAP_SET);
|
245
|
+
|
246
|
+
// effective
|
247
|
+
cap_array = rb_funcall(
|
248
|
+
rb_hash_aref(caps, ID2SYM(rb_intern("effective"))),
|
249
|
+
rb_intern("to_a"),
|
250
|
+
0
|
251
|
+
);
|
252
|
+
|
253
|
+
for(i = 0; i < RARRAY_LEN(cap_array); i++) {
|
254
|
+
cap_sym = RARRAY_PTR(cap_array)[i];
|
255
|
+
cap_values[i] = cap2_sym_to_cap(cap_sym);
|
256
|
+
}
|
257
|
+
|
258
|
+
cap_set_flag(cap_d, CAP_EFFECTIVE, i, cap_values, CAP_SET);
|
259
|
+
|
260
|
+
// inheritable
|
261
|
+
cap_array = rb_funcall(
|
262
|
+
rb_hash_aref(caps, ID2SYM(rb_intern("inheritable"))),
|
263
|
+
rb_intern("to_a"),
|
264
|
+
0
|
265
|
+
);
|
266
|
+
|
267
|
+
for(i = 0; i < RARRAY_LEN(cap_array); i++) {
|
268
|
+
cap_sym = RARRAY_PTR(cap_array)[i];
|
269
|
+
cap_values[i] = cap2_sym_to_cap(cap_sym);
|
270
|
+
}
|
243
271
|
|
244
|
-
|
245
|
-
cap_d = cap_init();
|
272
|
+
cap_set_flag(cap_d, CAP_INHERITABLE, i, cap_values, CAP_SET);
|
246
273
|
|
247
|
-
|
274
|
+
filename = cap2_file_filename(self);
|
248
275
|
|
249
276
|
if(cap_set_file(filename, cap_d) == -1) {
|
250
277
|
rb_raise(
|
@@ -253,123 +280,13 @@ static VALUE cap2_file_set_cap(VALUE file, cap_flag_t set, VALUE cap_sym, cap_fl
|
|
253
280
|
filename, strerror(errno)
|
254
281
|
);
|
255
282
|
} else {
|
283
|
+
cap_free(cap_d);
|
256
284
|
return Qtrue;
|
257
285
|
}
|
258
286
|
}
|
259
287
|
|
260
|
-
/*
|
261
|
-
* call-seq:
|
262
|
-
* has?(set, capability) -> true or false
|
263
|
-
*
|
264
|
-
* Return whether the file has the given capability enabled in the given set.
|
265
|
-
*
|
266
|
-
* Cap2.file('/bin/ping').has?(:permitted, :net_raw) #=> true
|
267
|
-
* Cap2.file('/tmp/ping').has?(:permitted, :net_raw) #=> false
|
268
|
-
*/
|
269
|
-
VALUE cap2_file_has_cap(VALUE self, VALUE set_sym, VALUE cap_sym) {
|
270
|
-
cap_t cap_d;
|
271
|
-
VALUE result;
|
272
|
-
|
273
|
-
cap_d = cap2_file_caps(self);
|
274
|
-
|
275
|
-
result = cap2_has_cap(cap_d, set_sym, cap_sym);
|
276
|
-
|
277
|
-
cap_free(cap_d);
|
278
|
-
|
279
|
-
return result;
|
280
|
-
}
|
281
|
-
|
282
|
-
/*
|
283
|
-
* call-seq:
|
284
|
-
* permit(capability) -> true or false
|
285
|
-
*
|
286
|
-
* Permit processes executing this file to enable the given capability.
|
287
|
-
*
|
288
|
-
* file = Cap2.file('/tmp/killer') #=> <Cap2::File>
|
289
|
-
* file.permitted?(:kill) #=> false
|
290
|
-
* file.permit(:kill) #=> true
|
291
|
-
* file.permitted?(:kill) #=> true
|
292
|
-
*/
|
293
|
-
VALUE cap2_file_permit(VALUE self, VALUE cap_sym) {
|
294
|
-
return cap2_file_set_cap(self, CAP_PERMITTED, cap_sym, CAP_SET);
|
295
|
-
}
|
296
|
-
|
297
|
-
/*
|
298
|
-
* call-seq:
|
299
|
-
* unpermit(capability) -> true or false
|
300
|
-
*
|
301
|
-
* Dont permit processes executing ths file to enable the given capability.
|
302
|
-
*
|
303
|
-
* file = Cap2.file('/tmp/foo') #=> <Cap2::File>
|
304
|
-
* file.permit(:kill) #=> true
|
305
|
-
* file.permitted?(:kill) #=> true
|
306
|
-
* file.unpermit(:kill) #=> true
|
307
|
-
* file.permitted?(:kill) #=> false
|
308
|
-
*/
|
309
|
-
VALUE cap2_file_unpermit(VALUE self, VALUE cap_sym) {
|
310
|
-
return cap2_file_set_cap(self, CAP_PERMITTED, cap_sym, CAP_CLEAR);
|
311
|
-
}
|
312
|
-
|
313
|
-
/*
|
314
|
-
* call-seq:
|
315
|
-
* allow_inherit(capability) -> true or false
|
316
|
-
*
|
317
|
-
* Allow processes executing this file to inherit the given capability.
|
318
|
-
*
|
319
|
-
* file = Cap2.file('/tmp/foo') #=> <Cap2::File>
|
320
|
-
* file.inheritable?(:kill) #=> false
|
321
|
-
* file.allow_inherit(:kill) #=> true
|
322
|
-
* file.inheritable?(:kill) #=> true
|
323
|
-
*/
|
324
|
-
VALUE cap2_file_allow_inherit(VALUE self, VALUE cap_sym) {
|
325
|
-
return cap2_file_set_cap(self, CAP_INHERITABLE, cap_sym, CAP_SET);
|
326
|
-
}
|
327
|
-
|
328
|
-
/*
|
329
|
-
* call-seq:
|
330
|
-
* disallow_inherit(capability) -> true or false
|
331
|
-
*
|
332
|
-
* Dont allow processes executing this file to inherit the given capability.
|
333
|
-
*
|
334
|
-
* file = Cap2.file('/tmp/foo') #=> <Cap2::File>
|
335
|
-
* file.inheritable?(:kill) #=> true
|
336
|
-
* file.allow_inherit(:kill) #=> true
|
337
|
-
* file.inheritable?(:kill) #=> false
|
338
|
-
*/
|
339
|
-
VALUE cap2_file_disallow_inherit(VALUE self, VALUE cap_sym) {
|
340
|
-
return cap2_file_set_cap(self, CAP_INHERITABLE, cap_sym, CAP_CLEAR);
|
341
|
-
}
|
342
|
-
|
343
|
-
/*
|
344
|
-
* call-seq:
|
345
|
-
* set_effective(capability) -> true or false
|
346
|
-
*
|
347
|
-
* Enable the given capability when a proces executes this file.
|
348
|
-
*
|
349
|
-
* file = Cap2.file('/tmp/foo') #=> <Cap2::File>
|
350
|
-
* file.effective?(:kill) #=> false
|
351
|
-
* file.set_effective(:kill) #=> true
|
352
|
-
* file.effective?(:kill) #=> true
|
353
|
-
*/
|
354
|
-
VALUE cap2_file_set_effective(VALUE self, VALUE cap_sym) {
|
355
|
-
return cap2_file_set_cap(self, CAP_EFFECTIVE, cap_sym, CAP_SET);
|
356
|
-
}
|
357
|
-
|
358
|
-
/*
|
359
|
-
* call-seq:
|
360
|
-
* disable_on_exec(capability) -> true or false
|
361
|
-
*
|
362
|
-
* Dont enable the given capability when a process executes this file.
|
363
|
-
*
|
364
|
-
* file = Cap2.file('/tmp/foo') #=> <Cap2::File>
|
365
|
-
* file.effective?(:kill) #=> true
|
366
|
-
* file.disable_on_exec(:kill) #=> true
|
367
|
-
* file.effective?(:kill) #=> false
|
368
|
-
*/
|
369
|
-
VALUE cap2_file_clear_effective(VALUE self, VALUE cap_sym) {
|
370
|
-
return cap2_file_set_cap(self, CAP_EFFECTIVE, cap_sym, CAP_CLEAR);
|
371
|
-
}
|
372
288
|
void Init_cap2(void) {
|
289
|
+
int i;
|
373
290
|
VALUE rb_mCap2;
|
374
291
|
VALUE rb_cCap2File;
|
375
292
|
VALUE rb_cCap2Process;
|
@@ -377,16 +294,10 @@ void Init_cap2(void) {
|
|
377
294
|
rb_mCap2 = rb_define_module("Cap2");
|
378
295
|
|
379
296
|
rb_cCap2Process = rb_define_class_under(rb_mCap2, "Process", rb_cObject);
|
380
|
-
rb_define_method(rb_cCap2Process, "
|
381
|
-
rb_define_method(rb_cCap2Process, "
|
382
|
-
rb_define_method(rb_cCap2Process, "disable", cap2_process_disable, 1);
|
297
|
+
rb_define_method(rb_cCap2Process, "getcaps", cap2_process_getcaps, 0);
|
298
|
+
rb_define_method(rb_cCap2Process, "save", cap2_process_setcaps, 0);
|
383
299
|
|
384
300
|
rb_cCap2File = rb_define_class_under(rb_mCap2, "File", rb_cObject);
|
385
|
-
rb_define_method(rb_cCap2File, "
|
386
|
-
rb_define_method(rb_cCap2File, "
|
387
|
-
rb_define_method(rb_cCap2File, "unpermit", cap2_file_unpermit, 1);
|
388
|
-
rb_define_method(rb_cCap2File, "allow_inherit", cap2_file_allow_inherit, 1);
|
389
|
-
rb_define_method(rb_cCap2File, "disallow_inherit", cap2_file_disallow_inherit, 1);
|
390
|
-
rb_define_method(rb_cCap2File, "set_effective", cap2_file_set_effective, 1);
|
391
|
-
rb_define_method(rb_cCap2File, "disable_on_exec", cap2_file_clear_effective, 1);
|
301
|
+
rb_define_method(rb_cCap2File, "getcaps", cap2_file_getcaps, 0);
|
302
|
+
rb_define_method(rb_cCap2File, "save", cap2_file_setcaps, 0);
|
392
303
|
}
|
data/lib/cap2.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'set'
|
1
2
|
require 'cap2.so'
|
2
3
|
require 'cap2/process'
|
3
4
|
require 'cap2/file'
|
@@ -49,7 +50,4 @@ module Cap2
|
|
49
50
|
|
50
51
|
# Raised when trying to initialise a File object for a non-existent file.
|
51
52
|
class FileNotFound < StandardError; end
|
52
|
-
|
53
|
-
# Raised when trying to enable unpermitted / uninheritable file capabilities.
|
54
|
-
class IncompatibleCapabilities < StandardError; end
|
55
53
|
end
|
data/lib/cap2.so
CHANGED
Binary file
|
data/lib/cap2/file.rb
CHANGED
@@ -1,26 +1,71 @@
|
|
1
|
-
require 'cap2/set_methods'
|
2
|
-
|
3
1
|
module Cap2
|
4
|
-
# A class with methods for
|
2
|
+
# A class with methods for managing capabilities for the
|
5
3
|
# file with filename provided to the initialize method.
|
6
4
|
class File
|
7
|
-
include SetMethods
|
8
|
-
|
9
5
|
# Initialize a new File object for the given filename.
|
10
6
|
def initialize(filename)
|
11
7
|
@filename = filename
|
8
|
+
@caps = getcaps
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns whether the given capability is permitted
|
12
|
+
def permitted?(capability)
|
13
|
+
reload
|
14
|
+
@caps[:permitted].include? capability
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns whether the given capability is inheritable
|
18
|
+
def inheritable?(capability)
|
19
|
+
reload
|
20
|
+
@caps[:inheritable].include? capability
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns whether or not the file has any effective
|
24
|
+
# capabilities.
|
25
|
+
def enabled?
|
26
|
+
reload
|
27
|
+
!@caps[:effective].empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Permit processes executing this file to enable the given capability.
|
31
|
+
def permit(capability)
|
32
|
+
@caps[:permitted].add(capability)
|
33
|
+
save
|
34
|
+
end
|
35
|
+
|
36
|
+
# Dont permit processes executing this file to enable the given capability.
|
37
|
+
def unpermit(capability)
|
38
|
+
@caps[:permitted].delete(capability)
|
39
|
+
save
|
40
|
+
end
|
41
|
+
|
42
|
+
# Allow processes executing this file to inherit the given capability.
|
43
|
+
def allow_inherit(capability)
|
44
|
+
@caps[:inheritable].add(capability)
|
45
|
+
save
|
46
|
+
end
|
47
|
+
|
48
|
+
# Dont allow processes executing this file to inherit the given capability.
|
49
|
+
def disallow_inherit(capability)
|
50
|
+
@caps[:inheritable].delete(capability)
|
51
|
+
save
|
52
|
+
end
|
53
|
+
|
54
|
+
# Enable the permitted capabilities when a proces executes this file.
|
55
|
+
def enable
|
56
|
+
@caps[:effective] = @caps[:permitted] + @caps[:inheritable]
|
57
|
+
save
|
58
|
+
end
|
59
|
+
|
60
|
+
# Dont enable the permitted capabilities when a proces executes this file.
|
61
|
+
def disable
|
62
|
+
@caps[:effective].clear
|
63
|
+
save
|
12
64
|
end
|
13
65
|
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
66
|
+
private
|
67
|
+
def reload
|
68
|
+
@caps = getcaps
|
24
69
|
end
|
25
70
|
end
|
26
71
|
end
|
data/lib/cap2/process.rb
CHANGED
@@ -1,14 +1,56 @@
|
|
1
|
-
require 'cap2/set_methods'
|
2
|
-
|
3
1
|
module Cap2
|
4
|
-
# A class with methods for
|
2
|
+
# A class with methods for managing capabilities for the
|
5
3
|
# process with pid provided to the initialize method.
|
6
4
|
class Process
|
7
|
-
include SetMethods
|
8
|
-
|
9
5
|
# Initialize a new Process object for the given pid.
|
10
6
|
def initialize(pid)
|
11
|
-
@pid
|
7
|
+
@pid = pid
|
8
|
+
@caps = getcaps
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns whether the given capability is permitted
|
12
|
+
def permitted?(capability)
|
13
|
+
reload
|
14
|
+
@caps[:permitted].include? capability
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns whether the given capability is enabled
|
18
|
+
def enabled?(capability)
|
19
|
+
reload
|
20
|
+
@caps[:effective].include? capability
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns whether the given capability is inheritable
|
24
|
+
def inheritable?(capability)
|
25
|
+
reload
|
26
|
+
@caps[:inheritable].include? capability
|
27
|
+
end
|
28
|
+
|
29
|
+
# Enable the given capability for this process.
|
30
|
+
def enable(capability)
|
31
|
+
check_pid
|
32
|
+
@caps[:effective].add(capability)
|
33
|
+
save
|
34
|
+
end
|
35
|
+
|
36
|
+
# Disable the given capability for this process.
|
37
|
+
def disable(capability)
|
38
|
+
check_pid
|
39
|
+
@caps[:effective].delete(capability)
|
40
|
+
save
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
# Raises a RuntimeError if the process's pid is not the same as the current
|
45
|
+
# pid (you cannot enable capabilities for other processes, that's their job).
|
46
|
+
def check_pid
|
47
|
+
unless @pid == Process.pid
|
48
|
+
raise 'Cannot modify capabilities of other processes'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def reload
|
53
|
+
@caps = getcaps
|
12
54
|
end
|
13
55
|
end
|
14
56
|
end
|
data/lib/cap2/version.rb
CHANGED
data/spec/file_spec.rb
CHANGED
@@ -19,17 +19,17 @@ describe Cap2::File do
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
describe '#
|
23
|
-
context "when the file
|
24
|
-
it { should_not
|
22
|
+
describe '#enabled?' do
|
23
|
+
context "when the file's enabled bit is not set" do
|
24
|
+
it { should_not be_enabled }
|
25
25
|
end
|
26
26
|
|
27
|
-
context
|
27
|
+
context "when the file's enabled bit is set" do
|
28
28
|
before(:each) do
|
29
|
-
run_as_root('permit(:dac_override)', '
|
29
|
+
run_as_root('permit(:dac_override)', 'enable')
|
30
30
|
end
|
31
31
|
|
32
|
-
it { should
|
32
|
+
it { should be_enabled }
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -83,48 +83,49 @@ describe Cap2::File do
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
-
|
87
|
-
context 'when
|
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
|
86
|
+
context 'enabling and disabling' do
|
87
|
+
context 'when at least one capability is permitted' do
|
98
88
|
before(:each) do
|
99
|
-
run_as_root('permit(:
|
89
|
+
run_as_root('permit(:kill)')
|
100
90
|
end
|
101
91
|
|
102
|
-
|
103
|
-
|
104
|
-
|
92
|
+
describe '#enable' do
|
93
|
+
specify do
|
94
|
+
expect { running_as_root('enable') }.to \
|
95
|
+
change { subject.enabled? }.from(false).to(true)
|
96
|
+
end
|
105
97
|
end
|
106
|
-
end
|
107
98
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
99
|
+
describe '#disable' do
|
100
|
+
before(:each) do
|
101
|
+
run_as_root('enable')
|
102
|
+
end
|
112
103
|
|
113
|
-
|
114
|
-
|
115
|
-
|
104
|
+
specify do
|
105
|
+
expect { running_as_root('disable') }.to \
|
106
|
+
change { subject.enabled? }.from(true).to(false)
|
107
|
+
end
|
116
108
|
end
|
117
109
|
end
|
118
|
-
end
|
119
110
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
111
|
+
context 'when no capabilities are permitted or inheritable' do
|
112
|
+
describe '#enable' do
|
113
|
+
specify do
|
114
|
+
expect { running_as_root('enable') }.to_not \
|
115
|
+
change { subject.enabled? }.from(false)
|
116
|
+
end
|
117
|
+
end
|
124
118
|
|
125
|
-
|
126
|
-
|
127
|
-
|
119
|
+
describe '#disable' do
|
120
|
+
before(:each) do
|
121
|
+
run_as_root('enable')
|
122
|
+
end
|
123
|
+
|
124
|
+
specify do
|
125
|
+
expect { running_as_root('disable') }.to_not \
|
126
|
+
change { subject.enabled? }.from(false)
|
127
|
+
end
|
128
|
+
end
|
128
129
|
end
|
129
130
|
end
|
130
131
|
|
data/spec/process_spec.rb
CHANGED
@@ -15,17 +15,17 @@ describe Cap2::Process do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
describe '#
|
18
|
+
describe '#enabled?' do
|
19
19
|
context "when the process doesn't have the given capability" do
|
20
20
|
subject { Cap2::Process.new(Process.pid) }
|
21
21
|
|
22
|
-
it { should_not
|
22
|
+
it { should_not be_enabled(:dac_override) }
|
23
23
|
end
|
24
24
|
|
25
25
|
context 'when the process does have the given capability' do
|
26
26
|
subject { Cap2::Process.new(1) }
|
27
27
|
|
28
|
-
it { should
|
28
|
+
it { should be_enabled(:dac_override) }
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
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.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-07 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
15
|
\ available in Linux kernels.\n\n These capabilities are a partitioning of
|
@@ -32,7 +32,6 @@ files:
|
|
32
32
|
- lib/cap2/process.rb
|
33
33
|
- lib/cap2/version.rb
|
34
34
|
- lib/cap2/file.rb
|
35
|
-
- lib/cap2/set_methods.rb
|
36
35
|
- lib/cap2.so
|
37
36
|
- spec/process_spec.rb
|
38
37
|
- spec/cap2_spec.rb
|
data/lib/cap2/set_methods.rb
DELETED
@@ -1,26 +0,0 @@
|
|
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
|
-
|