2008-11-26 22:16:48 +00:00
|
|
|
#! /usr/bin/env ruby
|
|
|
|
#
|
|
|
|
|
|
|
|
require 'getoptlong'
|
|
|
|
require 'tmpdir'
|
|
|
|
|
|
|
|
$my_version = '$Id$'
|
|
|
|
$random_length = 32
|
|
|
|
$prefix = "stress"
|
|
|
|
$max_rules = 200
|
|
|
|
$min_rules = 5
|
|
|
|
|
|
|
|
def get_random_name(len=$random_length)
|
|
|
|
return sprintf("%0#{len}x", rand(2 ** (4 * len)))
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_random_path()
|
|
|
|
out = ""
|
|
|
|
0.upto(rand(20)) do
|
|
|
|
out = "#{out}/#{get_random_name(4)}"
|
|
|
|
end
|
|
|
|
return out
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_random_mode()
|
|
|
|
case rand(10)
|
|
|
|
when 0..4
|
|
|
|
return "r"
|
|
|
|
when 5..7
|
|
|
|
return "rw"
|
|
|
|
when 8
|
|
|
|
return "Px"
|
|
|
|
when 9
|
|
|
|
return "rix"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Abstract class, though may become a real class for generation of
|
|
|
|
# random types of rules
|
|
|
|
class Rule
|
|
|
|
end
|
|
|
|
|
|
|
|
class FileRule < Rule
|
|
|
|
def initialize(path=get_random_path(), mode=get_random_mode())
|
|
|
|
@path = path
|
|
|
|
@mode = mode
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
return " #{@path} #{@mode},"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class CapRule < Rule
|
|
|
|
CAP_LIST = [
|
|
|
|
"chown",
|
|
|
|
"dac_override",
|
|
|
|
"dac_read_search",
|
|
|
|
"fowner",
|
|
|
|
"fsetid",
|
|
|
|
"kill",
|
|
|
|
"setgid",
|
|
|
|
"setuid",
|
|
|
|
"setpcap",
|
|
|
|
"linux_immutable",
|
|
|
|
"net_bind_service",
|
|
|
|
"net_broadcast",
|
|
|
|
"net_admin",
|
|
|
|
"net_raw",
|
|
|
|
"ipc_lock",
|
|
|
|
"ipc_owner",
|
|
|
|
"sys_module",
|
|
|
|
"sys_rawio",
|
|
|
|
"sys_chroot",
|
|
|
|
"sys_ptrace",
|
|
|
|
"sys_pacct",
|
|
|
|
"sys_admin",
|
|
|
|
"sys_boot",
|
|
|
|
"sys_nice",
|
|
|
|
"sys_resource",
|
|
|
|
"sys_time",
|
|
|
|
"sys_tty_config",
|
|
|
|
"mknod",
|
|
|
|
"lease",
|
|
|
|
"audit_write",
|
2010-03-12 03:05:25 -08:00
|
|
|
"audit_control",
|
|
|
|
"setfcap",
|
|
|
|
"mac_override",
|
|
|
|
"mac_admin"
|
2008-11-26 22:16:48 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
def initialize()
|
|
|
|
@cap = CAP_LIST[rand(CAP_LIST.length)]
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
return " capability #{@cap},"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def prefix_to_s(name)
|
|
|
|
out = []
|
|
|
|
out << "#"
|
|
|
|
out << "# prefix for #{name}"
|
|
|
|
out << "# generated by #{__FILE__} #{$my_version}"
|
|
|
|
out << "#include <tunables/global>"
|
|
|
|
out << "#"
|
|
|
|
end
|
|
|
|
|
2010-03-12 03:05:25 -08:00
|
|
|
class Flags
|
|
|
|
FLAG_LIST = [
|
|
|
|
"complain",
|
|
|
|
"audit",
|
|
|
|
"chroot_relative",
|
|
|
|
"namespace_relative",
|
|
|
|
"mediate_deleted",
|
|
|
|
"delegate_deleted",
|
|
|
|
"attach_disconnected",
|
|
|
|
"no_attach_disconnected",
|
|
|
|
"chroot_attach",
|
|
|
|
"chroot_no_attach"
|
|
|
|
]
|
|
|
|
|
|
|
|
FLAG_CONFLICTS = [
|
|
|
|
["chroot_relative", "namespace_relative"],
|
|
|
|
["mediate_deleted", "delegate_deleted"],
|
|
|
|
["attach_disconnected", "no_attach_disconnected"],
|
|
|
|
["chroot_attach", "chroot_no_attach"]
|
|
|
|
]
|
|
|
|
|
|
|
|
def initialize()
|
|
|
|
@flags = []
|
|
|
|
if rand(2) == 1
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
0.upto(4 - Math.log(rand(32) + 1).to_int) do |x|
|
|
|
|
@flags << FLAG_LIST[rand(FLAG_LIST.length)]
|
|
|
|
end
|
|
|
|
|
|
|
|
FLAG_CONFLICTS.each do |c|
|
|
|
|
if @flags.include?(c[0]) and @flags.include?(c[1])
|
|
|
|
@flags.delete(c[rand(2)])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
if @flags.empty?
|
|
|
|
return ""
|
|
|
|
end
|
|
|
|
out = @flags.join(",")
|
|
|
|
return "flags=(#{out})"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-11-26 22:16:48 +00:00
|
|
|
class Profile
|
|
|
|
attr_reader :rvalue
|
|
|
|
attr_reader :name
|
|
|
|
|
|
|
|
def initialize()
|
|
|
|
@rvalue = get_random_name()
|
|
|
|
@name = "/does/not/exist/#{@rvalue}"
|
|
|
|
@rules = []
|
2010-03-12 03:05:25 -08:00
|
|
|
@flags = Flags.new()
|
2008-11-26 22:16:48 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def generate_rules
|
2010-03-12 03:05:25 -08:00
|
|
|
@rules << FileRule.new(@name, "rm").to_s
|
2008-11-26 22:16:48 +00:00
|
|
|
0.upto(rand($max_rules - $min_rules) + $min_rules) do |x|
|
|
|
|
case rand(100)
|
|
|
|
when 0..19
|
2010-03-12 03:05:25 -08:00
|
|
|
@rules << CapRule.new.to_s
|
2008-11-26 22:16:48 +00:00
|
|
|
when 19..100
|
2010-03-12 03:05:25 -08:00
|
|
|
@rules << FileRule.new.to_s
|
2008-11-26 22:16:48 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
out = []
|
|
|
|
out << "#"
|
|
|
|
out << "# profile for #{@name}"
|
|
|
|
out << "# generated by #{__FILE__} #{$my_version}"
|
|
|
|
out << "#"
|
2010-03-12 03:05:25 -08:00
|
|
|
out << "#{@name} #{@flags} {"
|
2008-11-26 22:16:48 +00:00
|
|
|
out << " #include <abstractions/base>"
|
|
|
|
out << ""
|
2010-03-12 03:05:25 -08:00
|
|
|
@rules.sort.each { |r| out << " #{r}" }
|
2008-11-26 22:16:48 +00:00
|
|
|
out << "}"
|
|
|
|
out << ""
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def showUsage
|
|
|
|
warn "#{$my_version}"
|
|
|
|
warn "usage: #{__FILE__} count"
|
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
|
|
|
|
def gen_profiles_dir(profiles)
|
|
|
|
# Mu, no secure tmpdir creation in base ruby
|
|
|
|
begin
|
|
|
|
dirname = "#{Dir.tmpdir}/#{$prefix}-#{get_random_name(32)}"
|
|
|
|
Dir.mkdir(dirname, 0755)
|
|
|
|
rescue Errno::EEXIST
|
|
|
|
retry
|
|
|
|
end
|
|
|
|
|
|
|
|
profiles.each do |p|
|
|
|
|
open("#{dirname}/#{p.rvalue}", File::CREAT|File::EXCL|File::WRONLY, 0644) do |file|
|
|
|
|
file.puts(prefix_to_s(p.name))
|
|
|
|
file.puts(p.to_s)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return dirname
|
|
|
|
end
|
|
|
|
|
|
|
|
def gen_profiles_file(profiles)
|
|
|
|
# Mu, no secure tempfile creation in base ruby
|
|
|
|
begin
|
|
|
|
filename = "#{Dir.tmpdir}/#{$prefix}-#{get_random_name(32)}"
|
|
|
|
File.open(filename, File::CREAT|File::EXCL|File::WRONLY, 0644) do |file|
|
|
|
|
file.puts(prefix_to_s(filename))
|
|
|
|
profiles.each { |p| file.puts(p.to_s) }
|
|
|
|
end
|
|
|
|
rescue Errno::EEXIST
|
|
|
|
retry
|
|
|
|
end
|
|
|
|
|
|
|
|
return filename
|
|
|
|
end
|
|
|
|
|
|
|
|
if __FILE__ == $0
|
|
|
|
|
|
|
|
keep_files = true
|
|
|
|
opts = GetoptLong.new(
|
|
|
|
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
|
|
|
|
[ '--seed', '-s', GetoptLong::REQUIRED_ARGUMENT ],
|
|
|
|
[ '--keep-files', '-k', GetoptLong::NO_ARGUMENT ],
|
|
|
|
[ '--max-rules', '-M', GetoptLong::REQUIRED_ARGUMENT ],
|
|
|
|
[ '--min-rules', '-m', GetoptLong::REQUIRED_ARGUMENT ]
|
|
|
|
)
|
|
|
|
|
|
|
|
opts.each do |opt, arg|
|
|
|
|
case opt
|
|
|
|
when '--help'
|
|
|
|
showUsage
|
|
|
|
when '--seed'
|
|
|
|
srand(arg.to_i)
|
|
|
|
when '--keep-files'
|
|
|
|
keep_files = true
|
|
|
|
when '--max-rules'
|
|
|
|
$max_rules = arg.to_i
|
|
|
|
when '--min-rules'
|
|
|
|
$min_rules = arg.to_i
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
showUsage if ARGV.length != 1
|
|
|
|
count = ARGV.shift.to_i
|
|
|
|
showUsage if count < 1
|
|
|
|
profiles = []
|
|
|
|
while profiles.length < count do
|
|
|
|
profiles << Profile.new()
|
|
|
|
end
|
|
|
|
profiles.each { |p| p.generate_rules }
|
|
|
|
|
|
|
|
begin
|
|
|
|
profiles_dir = gen_profiles_dir(profiles)
|
|
|
|
profile_single = gen_profiles_file(profiles)
|
|
|
|
|
|
|
|
ensure
|
|
|
|
if (keep_files == false)
|
|
|
|
Dir.foreach(profiles_dir) do |filename|
|
|
|
|
File.delete("#{profiles_dir}/#{filename}") if (filename != '.' and filename != '..')
|
|
|
|
end
|
|
|
|
Dir.rmdir(profiles_dir)
|
|
|
|
File.delete(profile_single)
|
|
|
|
else
|
|
|
|
puts "PROFILEDIR=#{profiles_dir}; export PROFILEDIR"
|
|
|
|
puts "PROFILESINGLE=#{profile_single}; export PROFILESINGLE"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|