Hello community, here is the log from the commit of package yast2-rmt for openSUSE:Factory checked in at 2019-01-05 14:42:46 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/yast2-rmt (Old) and /work/SRC/openSUSE:Factory/.yast2-rmt.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "yast2-rmt" Sat Jan 5 14:42:46 2019 rev:11 rq:662765 version:1.2.0 Changes: -------- --- /work/SRC/openSUSE:Factory/yast2-rmt/yast2-rmt.changes 2018-11-08 09:48:58.533065517 +0100 +++ /work/SRC/openSUSE:Factory/.yast2-rmt.new.28833/yast2-rmt.changes 2019-01-05 14:42:47.248458375 +0100 @@ -1,0 +2,16 @@ +Fri Jan 4 09:46:24 UTC 2019 - hschmidt@suse.com + +- Release version 1.2.0 + +------------------------------------------------------------------- +Thu Dec 20 21:49:28 UTC 2018 - vzepedamas@suse.com + +- Allow user to skip registration (bsc#1119386) + +------------------------------------------------------------------- +Thu Dec 6 14:35:02 UTC 2018 - skotov@suse.com + +- version 1.1.2 +- [CVE-2018-17957] Secure MySQL credentials (bsc#1117602) + +------------------------------------------------------------------- Old: ---- yast2-rmt-1.1.1.tar.bz2 New: ---- yast2-rmt-1.2.0.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yast2-rmt.spec ++++++ --- /var/tmp/diff_new_pack.gpaxaQ/_old 2019-01-05 14:42:47.916457808 +0100 +++ /var/tmp/diff_new_pack.gpaxaQ/_new 2019-01-05 14:42:47.920457805 +0100 @@ -1,7 +1,7 @@ # # spec file for package yast2-rmt # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: yast2-rmt -Version: 1.1.1 +Version: 1.2.0 Release: 0 BuildArch: noarch ++++++ yast2-rmt-1.1.1.tar.bz2 -> yast2-rmt-1.2.0.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/package/yast2-rmt.changes new/yast2-rmt-1.2.0/package/yast2-rmt.changes --- old/yast2-rmt-1.1.1/package/yast2-rmt.changes 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/package/yast2-rmt.changes 2019-01-04 10:47:40.000000000 +0100 @@ -1,4 +1,20 @@ ------------------------------------------------------------------- +Fri Jan 4 09:46:24 UTC 2019 - hschmidt@suse.com + +- Release version 1.2.0 + +------------------------------------------------------------------- +Thu Dec 20 21:49:28 UTC 2018 - vzepedamas@suse.com + +- Allow user to skip registration (bsc#1119386) + +------------------------------------------------------------------- +Thu Dec 6 14:35:02 UTC 2018 - skotov@suse.com + +- version 1.1.2 +- [CVE-2018-17957] Secure MySQL credentials (bsc#1117602) + +------------------------------------------------------------------- Mon Oct 29 12:57:32 UTC 2018 - thutterer@suse.com - version 1.1.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/package/yast2-rmt.spec new/yast2-rmt-1.2.0/package/yast2-rmt.spec --- old/yast2-rmt-1.1.1/package/yast2-rmt.spec 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/package/yast2-rmt.spec 2019-01-04 10:47:40.000000000 +0100 @@ -17,7 +17,7 @@ Name: yast2-rmt -Version: 1.1.1 +Version: 1.2.0 Release: 0 BuildArch: noarch @@ -38,7 +38,7 @@ BuildRequires: rubygem(rspec) Summary: YaST2 - Module to configure RMT -License: GPL-2.0 or GPL-3.0 +License: GPL-2.0-only Group: System/YaST Url: https://github.com/yast/yast-rmt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/spec/rmt/maria_db/current_root_password_dialog_spec.rb new/yast2-rmt-1.2.0/spec/rmt/maria_db/current_root_password_dialog_spec.rb --- old/yast2-rmt-1.1.1/spec/rmt/maria_db/current_root_password_dialog_spec.rb 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/spec/rmt/maria_db/current_root_password_dialog_spec.rb 2019-01-04 10:47:40.000000000 +0100 @@ -24,6 +24,11 @@ subject(:dialog) { described_class.new } describe '#password_valid?' do + before do + expect(RMT::Utils).to receive(:create_protected_file).with("[client]\npassword=password\n").and_return(0) + expect(RMT::Utils).to receive(:remove_protected_file).with(anything).exactly(1).times + end + it 'returns true when exit code is 0' do expect(RMT::Utils).to receive(:run_command).and_return(0) expect(dialog.send(:password_valid?, 'password')).to be(true) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/spec/rmt/maria_db/new_root_password_dialog_spec.rb new/yast2-rmt-1.2.0/spec/rmt/maria_db/new_root_password_dialog_spec.rb --- old/yast2-rmt-1.1.1/spec/rmt/maria_db/new_root_password_dialog_spec.rb 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/spec/rmt/maria_db/new_root_password_dialog_spec.rb 2019-01-04 10:47:40.000000000 +0100 @@ -24,14 +24,19 @@ subject(:dialog) { described_class.new } describe '#set_root_password' do + before do + expect(RMT::Utils).to receive(:create_protected_file).with('SET PASSWORD FOR root@localhost=PASSWORD("password");').and_return(0) + expect(RMT::Utils).to receive(:remove_protected_file).with(anything).exactly(1).times + end + it 'returns true when exit code is 0' do expect(RMT::Utils).to receive(:run_command).and_return(0) - expect(dialog.set_root_password('localhost', 'password')).to be(true) + expect(dialog.set_root_password('password', 'localhost')).to be(true) end it 'returns false when exit code is not 0' do expect(RMT::Utils).to receive(:run_command).and_return(1) - expect(dialog.set_root_password('localhost', 'password')).to be(false) + expect(dialog.set_root_password('password', 'localhost')).to be(false) end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/spec/rmt/utils_spec.rb new/yast2-rmt-1.2.0/spec/rmt/utils_spec.rb --- old/yast2-rmt-1.1.1/spec/rmt/utils_spec.rb 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/spec/rmt/utils_spec.rb 2019-01-04 10:47:40.000000000 +0100 @@ -72,6 +72,21 @@ end end + describe '.create_protected_file' do + it 'returns file path' do + expect(Random).to receive(:rand).and_return(42) + expect(File).to receive(:open).with('/etc/rmt/protected_file42000', 'w', 0o600).and_return(true) + expect(described_class.create_protected_file('password')).to eq('/etc/rmt/protected_file42000') + end + end + + describe '.remove_protected_file' do + it 'calls File.delete' do + expect(File).to receive(:delete).and_return(true) + expect(described_class.remove_protected_file('test')).to eq(true) + end + end + describe '.ensure_default_values' do let(:config) do { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/spec/rmt/wizard_maria_db_page_spec.rb new/yast2-rmt-1.2.0/spec/rmt/wizard_maria_db_page_spec.rb --- old/yast2-rmt-1.1.1/spec/rmt/wizard_maria_db_page_spec.rb 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/spec/rmt/wizard_maria_db_page_spec.rb 2019-01-04 10:47:40.000000000 +0100 @@ -39,6 +39,7 @@ describe '#render_content' do it 'renders UI elements' do expect(Yast::Wizard).to receive(:SetNextButton).with(:next, Yast::Label.NextButton) + expect(Yast::Wizard).to receive(:HideBackButton) expect(Yast::Wizard).to receive(:SetContents) expect(Yast::UI).to receive(:ChangeWidget).with(Id(:db_username), :Value, config['database']['username']) @@ -191,25 +192,36 @@ end describe '#create_database_and_user' do + before do + allow(RMT::Utils).to receive(:create_protected_file).with(anything).and_return('test') + end + it "raises an error when can't create a database" do expect(RMT::Utils).to receive(:run_command).and_return(1) + expect(RMT::Utils).to receive(:remove_protected_file).with('test').exactly(1).times expect(Yast::Report).to receive(:Error).with('Database creation failed.') expect(mariadb_page.create_database_and_user).to be(false) end it "raises an error when can't create a user" do expect(RMT::Utils).to receive(:run_command).and_return(0, 1) + expect(RMT::Utils).to receive(:remove_protected_file).with('test').exactly(3).times expect(Yast::Report).to receive(:Error).with('User creation failed.') expect(mariadb_page.create_database_and_user).to be(false) end it 'returns true when there are no errors' do expect(RMT::Utils).to receive(:run_command).and_return(0, 0) + expect(RMT::Utils).to receive(:remove_protected_file).with('test').exactly(3).times expect(mariadb_page.create_database_and_user).to be(true) end end describe '#check_db_credentials' do + before do + allow(RMT::Utils).to receive(:create_protected_file).with(anything).and_return('test') + end + context 'when the required configuration keys are missing in the DB section' do let(:config) { { 'database' => {} } } @@ -218,10 +230,11 @@ context 'when the required configuration keys are present and are invalid' do it 'returns false' do + expect(RMT::Utils).to receive(:remove_protected_file).with('test').exactly(1).times expect(RMT::Execute).to receive(:on_target!).with( ['echo', 'select 1;'], [ - 'mysql', '-u', config['database']['username'], "-p#{config['database']['password']}", + 'mysql', '--defaults-extra-file=test', '-u', config['database']['username'], '-D', config['database']['database'], '-h', config['database']['host'] ] ).and_raise(Cheetah::ExecutionFailed.new('command', 255, '', 'Something went wrong')) @@ -231,10 +244,11 @@ context 'when the required configuration keys are present and are valid' do it 'returns true' do + expect(RMT::Utils).to receive(:remove_protected_file).with('test').exactly(1).times expect(RMT::Execute).to receive(:on_target!).with( ['echo', 'select 1;'], [ - 'mysql', '-u', config['database']['username'], "-p#{config['database']['password']}", + 'mysql', '--defaults-extra-file=test', '-u', config['database']['username'], '-D', config['database']['database'], '-h', config['database']['host'] ] ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/spec/rmt/wizard_scc_page_spec.rb new/yast2-rmt-1.2.0/spec/rmt/wizard_scc_page_spec.rb --- old/yast2-rmt-1.1.1/spec/rmt/wizard_scc_page_spec.rb 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/spec/rmt/wizard_scc_page_spec.rb 2019-01-04 10:47:40.000000000 +0100 @@ -29,6 +29,7 @@ it 'renders UI elements' do expect(Yast::Wizard).to receive(:SetAbortButton).with(:abort, Yast::Label.CancelButton) expect(Yast::Wizard).to receive(:SetNextButton).with(:next, Yast::Label.NextButton) + expect(Yast::Wizard).to receive(:SetBackButton).with(:skip, Yast::Label.SkipButton) expect(Yast::Wizard).to receive(:SetContents) expect(Yast::UI).to receive(:ChangeWidget).with(Id(:scc_username), :Value, config['scc']['username']) @@ -45,6 +46,34 @@ end end + describe '#skip_handler' do + let(:popup_params) do + [ + 'Skip SCC registration?', + "This is only recommended for air-gapped environments.\nRMT will not be able to sync and mirror data.\n\nDo you want to continue?", + 'Ignore and continue', + 'Go back', + :focus_no + ] + end + + context 'when cancel is clicked' do + it 'stays on the same page' do + expect(Yast::Popup).to receive(:AnyQuestion).with(*popup_params).and_return(false) + expect(scc_page).not_to receive(:finish_dialog) + scc_page.skip_handler + end + end + + context 'when ignore continue is clicked' do + it 'stays on the same page' do + expect(Yast::Popup).to receive(:AnyQuestion).with(*popup_params).and_return(true) + expect(scc_page).to receive(:finish_dialog).with(:next) + scc_page.skip_handler + end + end + end + describe '#next_handler' do before do expect(Yast::UI).to receive(:QueryWidget).with(Id(:scc_username), :Value) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/src/lib/rmt/maria_db/current_root_password_dialog.rb new/yast2-rmt-1.2.0/src/lib/rmt/maria_db/current_root_password_dialog.rb --- old/yast2-rmt-1.1.1/src/lib/rmt/maria_db/current_root_password_dialog.rb 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/src/lib/rmt/maria_db/current_root_password_dialog.rb 2019-01-04 10:47:40.000000000 +0100 @@ -35,9 +35,14 @@ private def password_valid?(password) - RMT::Utils.run_command( - "echo 'show databases;' | mysql -u root -p%1 2>/dev/null", - password - ) == 0 + root_pw_file = RMT::Utils.create_protected_file("[client]\npassword=#{password}\n") + begin + result = RMT::Utils.run_command( + "echo 'show databases;' | mysql --defaults-extra-file=#{root_pw_file} -u root 2>/dev/null" + ) == 0 + ensure + RMT::Utils.remove_protected_file(root_pw_file) + end + result end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/src/lib/rmt/maria_db/new_root_password_dialog.rb new/yast2-rmt-1.2.0/src/lib/rmt/maria_db/new_root_password_dialog.rb --- old/yast2-rmt-1.1.1/src/lib/rmt/maria_db/new_root_password_dialog.rb 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/src/lib/rmt/maria_db/new_root_password_dialog.rb 2019-01-04 10:47:40.000000000 +0100 @@ -33,10 +33,16 @@ end def set_root_password(new_root_password, hostname) - RMT::Utils.run_command( - "echo 'SET PASSWORD FOR root@%1=PASSWORD(\"%2\");' | mysql -u root 2>/dev/null", - hostname, - new_root_password - ) == 0 + command_file = RMT::Utils.create_protected_file( + "SET PASSWORD FOR root@#{hostname}=PASSWORD(\"#{new_root_password}\");" + ) + begin + result = RMT::Utils.run_command( + "mysql -u root < #{command_file} 2>/dev/null" + ) == 0 + ensure + RMT::Utils.remove_protected_file(command_file) + end + result end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/src/lib/rmt/utils.rb new/yast2-rmt-1.2.0/src/lib/rmt/utils.rb --- old/yast2-rmt-1.1.1/src/lib/rmt/utils.rb 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/src/lib/rmt/utils.rb 2019-01-04 10:47:40.000000000 +0100 @@ -28,6 +28,7 @@ extend Yast::I18n textdomain 'rmt' + PROTECTED_FILE_PATH = '/etc/rmt/protected_file'.freeze CONFIG_FILENAME = '/etc/rmt.conf'.freeze DEFAULT_CONFIG = { 'scc' => { @@ -73,6 +74,18 @@ ) end + def create_protected_file(password) + path = PROTECTED_FILE_PATH + (Random.rand * 1000).round.to_s + File.open(path, 'w', 0o600) do |file| + file.write(password) + end + path + end + + def remove_protected_file(path) + File.delete(path) + end + protected def ensure_default_values(config) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/src/lib/rmt/wizard_maria_db_page.rb new/yast2-rmt-1.2.0/src/lib/rmt/wizard_maria_db_page.rb --- old/yast2-rmt-1.1.1/src/lib/rmt/wizard_maria_db_page.rb 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/src/lib/rmt/wizard_maria_db_page.rb 2019-01-04 10:47:40.000000000 +0100 @@ -31,7 +31,7 @@ module RMT; end -class RMT::WizardMariaDBPage < Yast::Client +class RMT::WizardMariaDBPage < Yast::Client # rubocop:disable Metrics/ClassLength include ::UI::EventDispatcher def initialize(config) @@ -59,6 +59,7 @@ ) Wizard.SetNextButton(:next, Label.NextButton) + Wizard.HideBackButton() Wizard.SetContents( _('RMT Configuration - Step 2/5'), contents, @@ -124,13 +125,19 @@ return false if (!@config['database'][key] || @config['database'][key].empty?) end - RMT::Execute.on_target!( - ['echo', 'select 1;'], - [ - 'mysql', '-u', @config['database']['username'], "-p#{@config['database']['password']}", - '-D', @config['database']['database'], '-h', @config['database']['host'] - ] - ) + pw_file = RMT::Utils.create_protected_file("[client]\npassword=#{@config['database']['password']}\n") + begin + RMT::Execute.on_target!( + ['echo', 'select 1;'], + [ + 'mysql', "--defaults-extra-file=#{pw_file}", '-u', @config['database']['username'], + '-D', @config['database']['database'], '-h', @config['database']['host'] + ] + ) + ensure + RMT::Utils.remove_protected_file(pw_file) + end + true rescue Cheetah::ExecutionFailed false @@ -169,12 +176,16 @@ end def create_database_and_user - ret = RMT::Utils.run_command( - "echo 'create database if not exists %1 character set = \"utf8\"' | mysql -u root -h %2 -p%3 2>/dev/null", - @config['database']['database'], - @config['database']['host'], - @root_password - ) + root_pw_file = RMT::Utils.create_protected_file("[client]\npassword=#{@root_password}\n") + begin + ret = RMT::Utils.run_command( + "echo 'create database if not exists %1 character set = \"utf8\"' | mysql --defaults-extra-file=#{root_pw_file} -u root -h %2 2>/dev/null", + @config['database']['database'], + @config['database']['host'] + ) + ensure + RMT::Utils.remove_protected_file(root_pw_file) + end unless ret == 0 Report.Error(_('Database creation failed.')) @@ -182,15 +193,20 @@ end unless @config['database']['username'] == 'root' - ret = RMT::Utils.run_command( - "echo 'grant all on %1.* to \"%2\"\@%3 identified by \"%4\"' | mysql -u root -h %5 -p%6 >/dev/null", - @config['database']['database'], - @config['database']['username'], - @config['database']['host'], - @config['database']['password'], - @config['database']['host'], - @root_password + root_pw_file = RMT::Utils.create_protected_file("[client]\npassword=#{@root_password}\n") + config = Hash[@config['database'].map { |(k, v)| [k.to_sym, v] }] + command_file = RMT::Utils.create_protected_file( + "grant all on %<database>s.* to \"%<username>s\"\@%<host>s identified by \"%<password>s\"" % config ) + begin + ret = RMT::Utils.run_command( + "mysql --defaults-extra-file=#{root_pw_file} -u root -h %1 < #{command_file} >/dev/null", + @config['database']['host'] + ) + ensure + RMT::Utils.remove_protected_file(root_pw_file) + RMT::Utils.remove_protected_file(command_file) + end unless ret == 0 Report.Error(_('User creation failed.')) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-rmt-1.1.1/src/lib/rmt/wizard_scc_page.rb new/yast2-rmt-1.2.0/src/lib/rmt/wizard_scc_page.rb --- old/yast2-rmt-1.1.1/src/lib/rmt/wizard_scc_page.rb 2018-10-31 16:31:23.000000000 +0100 +++ new/yast2-rmt-1.2.0/src/lib/rmt/wizard_scc_page.rb 2019-01-04 10:47:40.000000000 +0100 @@ -34,6 +34,8 @@ def render_content Wizard.SetAbortButton(:abort, Label.CancelButton) Wizard.SetNextButton(:next, Label.NextButton) + Wizard.SetBackButton(:skip, Label.SkipButton) + contents = Frame( _('Organization Credentials'), @@ -69,6 +71,22 @@ finish_dialog(:abort) end + def skip_handler + @config['scc']['username'] = UI.QueryWidget(Id(:scc_username), :Value) + @config['scc']['password'] = UI.QueryWidget(Id(:scc_password), :Value) + + return unless Popup.AnyQuestion( + _('Skip SCC registration?'), + _("This is only recommended for air-gapped environments.\nRMT will not be able to sync and mirror data.\n\nDo you want to continue?"), + _('Ignore and continue'), + _('Go back'), + :focus_no + ) + + RMT::Utils.write_config_file(@config) + finish_dialog(:next) + end + def next_handler @config['scc']['username'] = UI.QueryWidget(Id(:scc_username), :Value) @config['scc']['password'] = UI.QueryWidget(Id(:scc_password), :Value)