From 4f33c8b5e114edf8f6c7084a2065d00a606193ad Mon Sep 17 00:00:00 2001 From: Gabriel Nagy Date: Tue, 8 Aug 2023 21:20:36 +0300 Subject: [PATCH] Cleanly exit if cepces or getcert are not present In case certmonger or the cepces library are not installed we do not want to fail hard. Check for the existence of the binaries instead of debs to be more future-proof given that cepces is not yet packaged in Ubuntu. Fixes UDENG-1156 --- .../integration_tests/adsysctl_policy_test.go | 9 ++++++++ internal/policies/certificate/cert-autoenroll | 13 ++++++++++- .../certificate/cert-autoenroll_test.go | 23 ++++++++++++++++++- .../golden/enroll_with_cepces_not_installed | 6 +++++ .../enroll_with_certmonger_not_installed | 6 +++++ .../admock/vendor_samba/gp/util/logging.py | 5 ++++ 6 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 internal/policies/certificate/testdata/TestCertAutoenrollScript/golden/enroll_with_cepces_not_installed create mode 100644 internal/policies/certificate/testdata/TestCertAutoenrollScript/golden/enroll_with_certmonger_not_installed diff --git a/cmd/adsysd/integration_tests/adsysctl_policy_test.go b/cmd/adsysd/integration_tests/adsysctl_policy_test.go index 6530c50b3..f92871a1d 100644 --- a/cmd/adsysd/integration_tests/adsysctl_policy_test.go +++ b/cmd/adsysd/integration_tests/adsysctl_policy_test.go @@ -965,6 +965,15 @@ func TestPolicyUpdate(t *testing.T) { t.Setenv("ADSYS_WBCLIENT_BEHAVIOR", tc.winbindMockBehavior) + // Create fake certmonger and cepces binaries for the certificate manager + binDir := t.TempDir() + for _, executable := range []string{"getcert", "cepces-submit"} { + // #nosec G306. We want this asset to be executable. + err := os.WriteFile(filepath.Join(binDir, executable), []byte("#!/bin/sh\necho $@\n"), 0755) + require.NoError(t, err, "Setup: could not create %q binary", executable) + } + t.Setenv("PATH", binDir+":"+os.Getenv("PATH")) + // Some tests will need some initial state assets for _, k := range tc.clearDirs { err := os.RemoveAll(filepath.Join(adsysDir, k)) diff --git a/internal/policies/certificate/cert-autoenroll b/internal/policies/certificate/cert-autoenroll index 44c0b68d8..53eb10954 100755 --- a/internal/policies/certificate/cert-autoenroll +++ b/internal/policies/certificate/cert-autoenroll @@ -13,7 +13,7 @@ from samba.dcerpc import preg from vendor_samba.gp.gpclass import GPOStorage from vendor_samba.gp import gp_cert_auto_enroll_ext as cae -from vendor_samba.gp.util.logging import logger_init +from vendor_samba.gp.util.logging import logger_init, log class adsys_cert_auto_enroll(cae.gp_cert_auto_enroll_ext): def enroll(self, guid, entries, trust_dir, private_dir, global_trust_dir): @@ -78,6 +78,10 @@ def main(): # Set up logging logger_init('cert-autoenroll', lp.log_level()) + if not cepces_submit() or not certmonger(): + log.warning('certmonger and/or cepces not found, skipping certificate enrollment') + return + ext = adsys_cert_auto_enroll(lp, c, username, store) guid = f'adsys-cert-autoenroll-{args.object_name}' if args.action == 'enroll': @@ -126,6 +130,13 @@ def gpo_entries(entries_json): raise ValueError(f'GPO data must be a JSON array of objects') from exc return entries +def cepces_submit(): + certmonger_dirs = [os.environ.get('PATH'), '/usr/lib/certmonger', + '/usr/libexec/certmonger'] + return shutil.which('cepces-submit', path=':'.join(certmonger_dirs)) + +def certmonger(): + return shutil.which('getcert') if __name__ == "__main__": sys.exit(main()) diff --git a/internal/policies/certificate/cert-autoenroll_test.go b/internal/policies/certificate/cert-autoenroll_test.go index 39375cb7f..4fd691bc9 100644 --- a/internal/policies/certificate/cert-autoenroll_test.go +++ b/internal/policies/certificate/cert-autoenroll_test.go @@ -80,6 +80,9 @@ func TestCertAutoenrollScript(t *testing.T) { readOnlyPath bool autoenrollError bool + missingCertmonger bool + missingCepces bool + wantErr bool }{ "Enroll with simple configuration": {args: []string{"enroll", "keypress", "example.com"}}, @@ -89,6 +92,10 @@ func TestCertAutoenrollScript(t *testing.T) { "Unenroll": {args: []string{"unenroll", "keypress", "example.com"}}, + // Missing binary cases + "Enroll with certmonger not installed": {args: []string{"enroll", "keypress", "example.com"}, missingCertmonger: true}, + "Enroll with cepces not installed": {args: []string{"enroll", "keypress", "example.com"}, missingCepces: true}, + // Error cases "Error on missing arguments": {args: []string{"enroll"}, wantErr: true}, "Error on invalid flags": {args: []string{"enroll", "keypress", "example.com", "--invalid_flag"}, wantErr: true}, @@ -109,6 +116,17 @@ func TestCertAutoenrollScript(t *testing.T) { stateDir := t.TempDir() sambaCacheDir := filepath.Join(stateDir, "samba") globalTrustDir := filepath.Join(stateDir, "ca-certificates") + binDir := t.TempDir() + if !tc.missingCertmonger { + // #nosec G306. We want this asset to be executable. + err := os.WriteFile(filepath.Join(binDir, "getcert"), []byte("#!/bin/sh\necho $@\n"), 0755) + require.NoError(t, err, "Setup: could not create getcert binary") + } + if !tc.missingCepces { + // #nosec G306. We want this asset to be executable. + err := os.WriteFile(filepath.Join(binDir, "cepces-submit"), []byte("#!/bin/sh\necho $@\n"), 0755) + require.NoError(t, err, "Setup: could not create cepces binary") + } // Create a dummy cache file to ensure we don't fail when removing a non-empty directory testutils.CreatePath(t, filepath.Join(sambaCacheDir, "cert_gpo_state_HOST.tdb")) @@ -121,7 +139,10 @@ func TestCertAutoenrollScript(t *testing.T) { // #nosec G204: we control the command line name and only change it for tests cmd := exec.Command(certAutoenrollCmd, args...) - cmd.Env = append(os.Environ(), "PYTHONPATH="+pythonPath) + cmd.Env = append(os.Environ(), + "PYTHONPATH="+pythonPath, + "PATH="+binDir+":"+os.Getenv("PATH"), + ) if tc.autoenrollError { cmd.Env = append(os.Environ(), "ADSYS_WANT_AUTOENROLL_ERROR=1") } diff --git a/internal/policies/certificate/testdata/TestCertAutoenrollScript/golden/enroll_with_cepces_not_installed b/internal/policies/certificate/testdata/TestCertAutoenrollScript/golden/enroll_with_cepces_not_installed new file mode 100644 index 000000000..fefe24e79 --- /dev/null +++ b/internal/policies/certificate/testdata/TestCertAutoenrollScript/golden/enroll_with_cepces_not_installed @@ -0,0 +1,6 @@ +Loading smb.conf +[global] +realm = example.com + +Loading state file: #STATEDIR#/samba/cert_gpo_state_keypress.tdb +WARNING: certmonger and/or cepces not found, skipping certificate enrollment diff --git a/internal/policies/certificate/testdata/TestCertAutoenrollScript/golden/enroll_with_certmonger_not_installed b/internal/policies/certificate/testdata/TestCertAutoenrollScript/golden/enroll_with_certmonger_not_installed new file mode 100644 index 000000000..fefe24e79 --- /dev/null +++ b/internal/policies/certificate/testdata/TestCertAutoenrollScript/golden/enroll_with_certmonger_not_installed @@ -0,0 +1,6 @@ +Loading smb.conf +[global] +realm = example.com + +Loading state file: #STATEDIR#/samba/cert_gpo_state_keypress.tdb +WARNING: certmonger and/or cepces not found, skipping certificate enrollment diff --git a/internal/testutils/admock/vendor_samba/gp/util/logging.py b/internal/testutils/admock/vendor_samba/gp/util/logging.py index dd4bc4bfe..a42e95801 100644 --- a/internal/testutils/admock/vendor_samba/gp/util/logging.py +++ b/internal/testutils/admock/vendor_samba/gp/util/logging.py @@ -1,2 +1,7 @@ def logger_init(_name, _level): pass + +class log(object): + @staticmethod + def warning(msg): + print(f'WARNING: {msg}')