pledge 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +3 -0
- data/MIT-LICENSE +18 -0
- data/README.rdoc +99 -0
- data/Rakefile +30 -0
- data/ext/pledge/extconf.rb +5 -0
- data/ext/pledge/pledge.c +40 -0
- data/spec/pledge_spec.rb +112 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1ff2e2250fd7b7b8b8edab08f84d75d1140b75ee
|
4
|
+
data.tar.gz: 14e9e9cdc750d16d575e014e5e5b29769816f19d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a5e05296eac3da536dbf96189899d810baecef6c791ef9f356628f2f81a04c13f00a892247cf7e9495749f357e9316aefe6b5af324a8361112925861f1786b45
|
7
|
+
data.tar.gz: 3f7e3289b7011e2b54eac6b1ce35798667dda2d08b51815517a57b6476255ffd3628bb37fee313d24f4649a1f0bc654af1590bf0617401a5a8381fdebff16c06
|
data/CHANGELOG
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2016 Jeremy Evans
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal in the Software without restriction, including without limitation the
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
= pledge
|
2
|
+
|
3
|
+
pledge exposes OpenBSD's pledge(2) system call to ruby, allowing a
|
4
|
+
program to restrict the types of operations the program
|
5
|
+
can do after that point. Unlike other similar systems,
|
6
|
+
pledge is specifically designed for programs that need to
|
7
|
+
use a wide variety of operations on initialization, but
|
8
|
+
a fewer number after initialization (when user input will
|
9
|
+
be accepted).
|
10
|
+
|
11
|
+
plege(2) is supported on OpenBSD 5.9+.
|
12
|
+
|
13
|
+
== Usage
|
14
|
+
|
15
|
+
First, you need to require the library
|
16
|
+
|
17
|
+
require 'pledge'
|
18
|
+
|
19
|
+
Then you can use +Pledge.pledge+ as the interface to the pledge(2)
|
20
|
+
system call. You pass +Pledge.pledge+ a string containing tokens
|
21
|
+
for the operations you would like to allow (called promises).
|
22
|
+
For example, if you want to give the process the ability to read
|
23
|
+
from the the file system, but not read from the file system or
|
24
|
+
allow network access:
|
25
|
+
|
26
|
+
Pledge.pledge("rpath")
|
27
|
+
|
28
|
+
To allow read/write filesystem access, but not network access:
|
29
|
+
|
30
|
+
Pledge.pledge("rpath wpath cpath")
|
31
|
+
|
32
|
+
To allow inet/unix socket access and DNS queries, but not
|
33
|
+
filesystem access:
|
34
|
+
|
35
|
+
Pledge.pledge("inet unix dns")
|
36
|
+
|
37
|
+
+Pledge+ is a module that extends itself, you can include it
|
38
|
+
in other classes:
|
39
|
+
|
40
|
+
Object.send(:include, Pledge)
|
41
|
+
pledge("rpath")
|
42
|
+
|
43
|
+
== Options
|
44
|
+
|
45
|
+
See the pledge(2) man page for a description of the allowed
|
46
|
+
promises in the string passed to +Pledge.pledge+.
|
47
|
+
|
48
|
+
Using an unsupported promise will raise an exception. The "stdio"
|
49
|
+
promise is added automatically, as ruby does not function without
|
50
|
+
|
51
|
+
it.
|
52
|
+
|
53
|
+
== Reporting issues/bugs
|
54
|
+
|
55
|
+
This library uses GitHub Issues for tracking issues/bugs:
|
56
|
+
|
57
|
+
https://github.com/jeremyevans/ruby-pledge/issues
|
58
|
+
|
59
|
+
== Contributing
|
60
|
+
|
61
|
+
The source code is on GitHub:
|
62
|
+
|
63
|
+
https://github.com/jeremyevans/ruby-pledge
|
64
|
+
|
65
|
+
To get a copy:
|
66
|
+
|
67
|
+
git clone git://github.com/jeremyevans/ruby-pledge.git
|
68
|
+
|
69
|
+
== Requirements
|
70
|
+
|
71
|
+
* OpenBSD 5.9+
|
72
|
+
* ruby 1.8.7+
|
73
|
+
* rake-compiler (if compiling)
|
74
|
+
|
75
|
+
== Compiling
|
76
|
+
|
77
|
+
To build the library from a git checkout, use the compile task.
|
78
|
+
|
79
|
+
rake compile
|
80
|
+
|
81
|
+
== Running the specs
|
82
|
+
|
83
|
+
The rake spec task runs the specs. This is also the default rake
|
84
|
+
task. This will compile the library if not already compiled.
|
85
|
+
|
86
|
+
rake
|
87
|
+
|
88
|
+
== Known Issues
|
89
|
+
|
90
|
+
* You cannot create new threads after running +Pledge.pledge+, as
|
91
|
+
it uses syscalls that are not currently allowed by pledge(2). +fork+
|
92
|
+
still works, assuming you are using the +proc+ pledge.
|
93
|
+
|
94
|
+
* You cannot currently test +Pledge.pledge+ in irb/pry, as they use an
|
95
|
+
ioctl that is not currently allowed by pledge(2).
|
96
|
+
|
97
|
+
== Author
|
98
|
+
|
99
|
+
Jeremy Evans <code@jeremyevans.net>
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "rake"
|
2
|
+
require "rake/clean"
|
3
|
+
|
4
|
+
CLEAN.include %w'**.rbc rdoc'
|
5
|
+
|
6
|
+
desc "Do a full cleaning"
|
7
|
+
task :distclean do
|
8
|
+
CLEAN.include %w'tmp pkg tame*.gem lib/*.so'
|
9
|
+
Rake::Task[:clean].invoke
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Build the gem"
|
13
|
+
task :package do
|
14
|
+
sh %{gem build pledge.gemspec}
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run specs"
|
18
|
+
task :spec => :compile do
|
19
|
+
ruby = ENV['RUBY'] ||= FileUtils::RUBY
|
20
|
+
sh %{#{ruby} spec/pledge_spec.rb}
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Run specs"
|
24
|
+
task :default => :spec
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rake/extensiontask'
|
28
|
+
Rake::ExtensionTask.new('pledge')
|
29
|
+
rescue LoadError
|
30
|
+
end
|
data/ext/pledge/pledge.c
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#include <errno.h>
|
2
|
+
#include <ruby.h>
|
3
|
+
#include <unistd.h>
|
4
|
+
|
5
|
+
static VALUE ePledgeInvalidPromise;
|
6
|
+
static VALUE ePledgePermissionIncreaseAttempt;
|
7
|
+
static VALUE ePledgeError;
|
8
|
+
|
9
|
+
static VALUE rb_pledge(VALUE pledge_class, VALUE promises) {
|
10
|
+
SafeStringValue(promises);
|
11
|
+
promises = rb_str_dup(promises);
|
12
|
+
|
13
|
+
/* required for ruby to work */
|
14
|
+
rb_str_cat2(promises, " stdio");
|
15
|
+
promises = rb_funcall(promises, rb_intern("strip"), 0);
|
16
|
+
SafeStringValue(promises);
|
17
|
+
|
18
|
+
if (pledge(RSTRING_PTR(promises), NULL) != 0) {
|
19
|
+
switch(errno) {
|
20
|
+
case EINVAL:
|
21
|
+
rb_raise(ePledgeInvalidPromise, "invalid promise in promises string");
|
22
|
+
case EPERM:
|
23
|
+
rb_raise(ePledgePermissionIncreaseAttempt, "attempt to increase permissions");
|
24
|
+
default:
|
25
|
+
rb_raise(ePledgeError, "pledge error");
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
return Qnil;
|
30
|
+
}
|
31
|
+
|
32
|
+
void Init_pledge(void) {
|
33
|
+
VALUE cPledge;
|
34
|
+
cPledge = rb_define_module("Pledge");
|
35
|
+
rb_define_method(cPledge, "pledge", rb_pledge, 1);
|
36
|
+
rb_extend_object(cPledge, cPledge);
|
37
|
+
ePledgeError = rb_define_class_under(cPledge, "Error", rb_eStandardError);
|
38
|
+
ePledgeInvalidPromise = rb_define_class_under(cPledge, "InvalidPromise", ePledgeError);
|
39
|
+
ePledgePermissionIncreaseAttempt = rb_define_class_under(cPledge, "PermissionIncreaseAttempt", ePledgeError);
|
40
|
+
}
|
data/spec/pledge_spec.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require './lib/pledge'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
gem 'minitest'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
|
7
|
+
RUBY = ENV['RUBY'] || 'ruby'
|
8
|
+
|
9
|
+
describe "Pledge.pledge" do
|
10
|
+
def _pledged(status, promises, code)
|
11
|
+
system(RUBY, '-I', 'lib', '-r', 'pledge', '-e', "Pledge.pledge(#{promises.inspect}); #{code}").must_equal status
|
12
|
+
end
|
13
|
+
|
14
|
+
def pledged(code, promises="")
|
15
|
+
_pledged(true, promises, code)
|
16
|
+
end
|
17
|
+
|
18
|
+
def unpledged(code, promises="")
|
19
|
+
_pledged(false, promises, code)
|
20
|
+
end
|
21
|
+
|
22
|
+
def with_lib(lib)
|
23
|
+
rubyopt = ENV['RUBYOPT']
|
24
|
+
ENV['RUBYOPT'] = "#{rubyopt} -r#{lib}"
|
25
|
+
yield
|
26
|
+
ensure
|
27
|
+
ENV['RUBYOPT'] = rubyopt
|
28
|
+
end
|
29
|
+
|
30
|
+
after do
|
31
|
+
Dir['spec/_*'].each{|f| File.delete(f)}
|
32
|
+
Dir['*.core'].each{|f| File.delete(f)}
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should raise a Pledge::InvalidPromise for unsupported promises" do
|
36
|
+
proc{Pledge.pledge("foo")}.must_raise Pledge::InvalidPromise
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should raise a Pledge::PermissionIncreaseAttempt if attempting to increase permissinos" do
|
40
|
+
pledged("begin; Pledge.pledge('rpath'); rescue Pledge::PermissionIncreaseAttempt; exit 0; end; exit 1")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should produce a core file on failure" do
|
44
|
+
unpledged("File.read('#{__FILE__}')")
|
45
|
+
Dir['*.core'].wont_equal []
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should allow reading files if rpath is used" do
|
49
|
+
unpledged("File.read('#{__FILE__}')")
|
50
|
+
pledged("File.read('#{__FILE__}')", "rpath")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should allow creating files if cpath and wpath are used" do
|
54
|
+
unpledged("File.open('spec/_test', 'w'){}")
|
55
|
+
unpledged("File.open('spec/_test', 'w'){}", "cpath")
|
56
|
+
unpledged("File.open('spec/_test', 'w'){}", "wpath")
|
57
|
+
File.file?('spec/_test').must_equal false
|
58
|
+
pledged("File.open('#{'spec/_test'}', 'w'){}", "cpath wpath")
|
59
|
+
File.file?('spec/_test').must_equal true
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should allow writing to files if wpath and rpath are used" do
|
63
|
+
File.open('spec/_test', 'w'){}
|
64
|
+
unpledged("File.open('spec/_test', 'r+'){}")
|
65
|
+
pledged("File.open('#{'spec/_test'}', 'r+'){|f| f.write '1'}", "wpath rpath")
|
66
|
+
File.read('spec/_test').must_equal '1'
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should allow dns lookups if dns is used" do
|
70
|
+
with_lib('socket') do
|
71
|
+
unpledged("Socket.gethostbyname('google.com')")
|
72
|
+
pledged("Socket.gethostbyname('google.com')", "dns")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should allow internet access if inet is used" do
|
77
|
+
with_lib('net/http') do
|
78
|
+
unpledged("Net::HTTP.get('127.0.0.1', '/index.html') rescue nil")
|
79
|
+
promises = "inet"
|
80
|
+
# rpath necessary on ruby < 2.1, as it calls stat
|
81
|
+
promises << " rpath" if RUBY_VERSION < '2.1'
|
82
|
+
pledged("Net::HTTP.get('127.0.0.1', '/index.html') rescue nil", promises)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should allow killing programs if proc is used" do
|
87
|
+
unpledged("Process.kill(:URG, #{$$})")
|
88
|
+
pledged("Process.kill(:URG, #{$$})", "proc")
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should allow creating temp files if tmppath and rpath are used" do
|
92
|
+
with_lib('tempfile') do
|
93
|
+
unpledged("Tempfile.new('foo')")
|
94
|
+
unpledged("Tempfile.new('foo')", "tmppath")
|
95
|
+
unpledged("Tempfile.new('foo')", "rpath")
|
96
|
+
promises = "tmppath rpath"
|
97
|
+
# cpath necessary on ruby < 2.0, as it calls mkdir
|
98
|
+
promises << " cpath" if RUBY_VERSION < '2.0'
|
99
|
+
pledged("Tempfile.new('foo')", promises)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should allow unix sockets if unix and rpath is used" do
|
104
|
+
require 'socket'
|
105
|
+
us = UNIXServer.new('spec/_sock')
|
106
|
+
with_lib('socket') do
|
107
|
+
unpledged("UNIXSocket.new('spec/_sock').send('u', 0)")
|
108
|
+
pledged("UNIXSocket.new('spec/_sock').send('t', 0)", "unix")
|
109
|
+
end
|
110
|
+
us.accept.read.must_equal 't'
|
111
|
+
end
|
112
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pledge
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeremy Evans
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-11-03 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |
|
14
|
+
pledge exposes OpenBSD's pledge(2) system call to ruby, allowing a
|
15
|
+
program to restrict the types of operations the program
|
16
|
+
can do after that point. Unlike other similar systems,
|
17
|
+
pledge is specifically designed for programs that need to
|
18
|
+
use a wide variety of operations on initialization, but
|
19
|
+
a fewer number after initialization (when user input will
|
20
|
+
be accepted).
|
21
|
+
email: code@jeremyevans.net
|
22
|
+
executables: []
|
23
|
+
extensions:
|
24
|
+
- ext/pledge/extconf.rb
|
25
|
+
extra_rdoc_files:
|
26
|
+
- README.rdoc
|
27
|
+
- CHANGELOG
|
28
|
+
- MIT-LICENSE
|
29
|
+
files:
|
30
|
+
- CHANGELOG
|
31
|
+
- MIT-LICENSE
|
32
|
+
- README.rdoc
|
33
|
+
- Rakefile
|
34
|
+
- ext/pledge/extconf.rb
|
35
|
+
- ext/pledge/pledge.c
|
36
|
+
- spec/pledge_spec.rb
|
37
|
+
homepage: https://github.com/jeremyevans/ruby-pledge
|
38
|
+
licenses:
|
39
|
+
- MIT
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- "--quiet"
|
44
|
+
- "--line-numbers"
|
45
|
+
- "--inline-source"
|
46
|
+
- "--title"
|
47
|
+
- 'pledge: restrict system operations on OpenBSD'
|
48
|
+
- "--main"
|
49
|
+
- README.rdoc
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 1.8.7
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 2.5.1
|
65
|
+
signing_key:
|
66
|
+
specification_version: 4
|
67
|
+
summary: Restrict system operations on OpenBSD
|
68
|
+
test_files: []
|