diff --git a/hawk/app/controllers/application_controller.rb b/hawk/app/controllers/application_controller.rb index b4bce8fb4..e6bcf2d4a 100644 --- a/hawk/app/controllers/application_controller.rb +++ b/hawk/app/controllers/application_controller.rb @@ -10,6 +10,7 @@ class ApplicationController < ActionController::Base layout :detect_current_layout around_action :inject_current_user + around_action :inject_current_pass around_action :inject_current_cib before_action :set_users_locale before_action :set_current_home @@ -23,6 +24,7 @@ class ApplicationController < ActionController::Base helper_method :production_cib helper_method :current_cib helper_method :current_user + helper_method :current_pass rescue_from Cib::CibError do |e| respond_to do |format| @@ -59,7 +61,7 @@ def production_cib def current_cib if current_user @current_cib ||= begin - Cib.new(params[:cib_id] || production_cib, current_user, params[:debug] == "file", cookies[:stonithwarning].nil? || cookies[:stonithwarning] == true) + Cib.new(params[:cib_id] || production_cib, current_user, current_pass, params[:debug] == "file", cookies[:stonithwarning].nil? || cookies[:stonithwarning] == true) end end end @@ -96,6 +98,12 @@ def inject_current_user yield end + def inject_current_pass + current_controller = self + Thread.current[:current_pass] = proc { current_controller.send(:current_pass) } + yield + end + def set_users_locale available = [params[:locale], cookies[:locale], default_locale].compact.first I18n.locale = FastGettext.set_locale(available) @@ -173,6 +181,10 @@ def current_user @current_user ||= session[:username] || login_from_cookie end + def current_pass + @current_pass ||= session[:password] + end + def is_god? current_user == "hacluster" || current_user == "root" end diff --git a/hawk/app/controllers/sessions_controller.rb b/hawk/app/controllers/sessions_controller.rb index ea536acd5..5fda1795d 100644 --- a/hawk/app/controllers/sessions_controller.rb +++ b/hawk/app/controllers/sessions_controller.rb @@ -27,6 +27,7 @@ def create if @session.valid? reset_session session[:username] = @session.username + session[:password] = @session.password # generate random value, store in attrd_updater (1024 Bits) value = SecureRandom.hex(128) @@ -66,6 +67,7 @@ def destroy cookies.delete :hawk_remember_me_id cookies.delete :hawk_remember_me_key session[:username] = nil + session[:password] = nil reset_session respond_to do |format| diff --git a/hawk/app/lib/invoker.rb b/hawk/app/lib/invoker.rb index 86e71b3d5..2f6f914b7 100644 --- a/hawk/app/lib/invoker.rb +++ b/hawk/app/lib/invoker.rb @@ -27,7 +27,7 @@ def initialize # cleaned up further) # Returns [out, err, exitstatus] def run(*cmd) - out, err, status = Util.capture3(*cmd) + out, err, status = Util.run_as(current_user, current_pass, *cmd) [out, fudge_error(status.exitstatus, err), status.exitstatus] end @@ -73,7 +73,7 @@ def crm_configure_load_update(cmd) # Invoke cibadmin with command line arguments. Returns stdout as string, # Raises NotFoundError, SecurityError or RuntimeError on failure. def cibadmin(*cmd) - out, err, status = Util.capture3('cibadmin', *cmd) + out, err, status = Util.run_as(current_user, current_pass, 'cibadmin', *cmd) case status.exitstatus when 0 return out @@ -105,7 +105,7 @@ def cibadmin_modify(xml) # Used by the simulator def crm_simulate(*cmd) - Util.capture3('crm_simulate', *cmd) + Util.run_as(current_user, current_pass, 'crm_simulate', *cmd) end private @@ -132,7 +132,7 @@ def invoke_crm(input, *cmd) end cmd << { stdin_data: input } - out, err, status = Util.capture3('crm', *cmd) + out, err, status = Util.run_as(current_user, current_pass, 'crm', *cmd) [out, fudge_error(status.exitstatus, err), status.exitstatus] end @@ -153,4 +153,8 @@ def fudge_error(exitstatus, stderr) def current_user Thread.current[:current_user].call end + + def current_pass + Thread.current[:current_pass].call + end end diff --git a/hawk/app/lib/util.rb b/hawk/app/lib/util.rb index 82fdacf60..5d5903ae9 100644 --- a/hawk/app/lib/util.rb +++ b/hawk/app/lib/util.rb @@ -19,7 +19,7 @@ def numeric?(n) # DON'T USE THIS FUNCTION DIRECTLY - it's subject to deadlocks e.g.: # http://coldattic.info/shvedsky/pro/blogs/a-foo-walks-into-a-bar/posts/63 # Rather you should prefer capture3. - def popen3(*cmd) + def popen3(user, *cmd) raise SecurityError, "Util::popen3 called with < 2 args" if cmd.length < 2 pw = IO::pipe # pipe[0] for read, pipe[1] for write pr = IO::pipe @@ -39,8 +39,13 @@ def popen3(*cmd) STDERR.reopen(pe[1]) pe[1].close - # RORSCAN_INL: cmd always has > 1 elem, so safe from shell injection - exec(*cmd) + if user.to_s.strip.empty? or user == "hacluster" or user == "root" + # RORSCAN_INL: cmd always has > 1 elem, so safe from shell injection + exec(*cmd) + else + command = ['su', '-', user, 'sh', '-c', "#{cmd.join(" ")}"] + exec(*command) + end } wait_thr = Process.detach(pid) @@ -73,7 +78,7 @@ def capture3(*cmd) end Rails.logger.debug "Executing `#{cmd.join(' ').inspect}` through `capture3`" stdin_data = opts.delete(:stdin_data) || '' - Util.popen3(*cmd) {|i, o, e, t| + Util.popen3(nil, *cmd) {|i, o, e, t| out_reader = Thread.new { o.read } err_reader = Thread.new { e.read } i.write stdin_data @@ -83,6 +88,28 @@ def capture3(*cmd) end module_function :capture3 + def run_as(user, pass, *cmd) + if Hash === cmd.last + opts = cmd.pop.dup + else + opts = {} + end + Rails.logger.debug "Executing `#{cmd.join(' ').inspect}` as `#{user}` through `run_as`" + stdin_data = opts.delete(:stdin_data) || '' + Util.popen3(user, *cmd) {|i, o, e, t| + out_reader = Thread.new { o.read } + err_reader = Thread.new { e.read } + if not user.to_s.strip.empty? and user != "hacluster" and user != "root" + i.write pass + i.write "\n" + end + i.write stdin_data + i.close + [out_reader.value, err_reader.value, t.value] + } + end + module_function :run_as + def ensure_home_for(user) old_home = ENV['HOME'] ENV['HOME'] = begin diff --git a/hawk/app/models/cib.rb b/hawk/app/models/cib.rb index be0f680c9..b0be4902e 100644 --- a/hawk/app/models/cib.rb +++ b/hawk/app/models/cib.rb @@ -483,7 +483,7 @@ def warning(msg, additions = {}) error(msg, :warning, additions) end - def initialize(id, user, use_file = false, stonithwarning = false) + def initialize(id, user, pass, use_file = false, stonithwarning = false) Rails.logger.debug "Cib.initialize #{id}, #{user}, #{use_file}" if use_file @@ -511,7 +511,7 @@ def initialize(id, user, use_file = false, stonithwarning = false) init_offline_cluster id, user, use_file return end - out, err, status = Util.capture3('cibadmin', '-Ql') + out, err, status = Util.run_as(user, pass, 'cibadmin', '-Ql') case status.exitstatus when 0 @xml = REXML::Document.new(out)