From 4a416722a4ffa3da1fc09ed2444249f2ed120237 Mon Sep 17 00:00:00 2001 From: Magnus Jensen Date: Fri, 14 Nov 2025 17:44:14 +0200 Subject: [PATCH] DUSW: Allow user-scoped SCEP profiles on Windows (#35672) **Related issue:** Resolves #35503 For this one I opted for a simple approach of just duplicating the arrays one for user, and one for device, then with the first loc uri that checks in of either device or user, sets the respective arrays, I thought while this was more LOC's it was easier to look at and maintain compared to regex matches etc, let me know if you think otherwise. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually --- ...ploy-user-scoped-scep-profiles-for-windows | 1 + server/fleet/windows_mdm.go | 132 ++++++++++++++--- server/fleet/windows_mdm_test.go | 32 +++- server/mdm/microsoft/profile_verifier.go | 2 +- server/mdm/microsoft/profile_verifier_test.go | 15 +- .../service/integration_mdm_profiles_test.go | 29 ++-- ...ndows-scep.xml => windows-device-scep.xml} | 0 .../testdata/profiles/windows-user-scep.xml | 140 ++++++++++++++++++ 8 files changed, 309 insertions(+), 42 deletions(-) create mode 100644 changes/34243-deploy-user-scoped-scep-profiles-for-windows rename server/service/testdata/profiles/{windows-scep.xml => windows-device-scep.xml} (100%) create mode 100644 server/service/testdata/profiles/windows-user-scep.xml diff --git a/changes/34243-deploy-user-scoped-scep-profiles-for-windows b/changes/34243-deploy-user-scoped-scep-profiles-for-windows new file mode 100644 index 0000000000..0173949ecf --- /dev/null +++ b/changes/34243-deploy-user-scoped-scep-profiles-for-windows @@ -0,0 +1 @@ +* Add ability to deploy user-scoped SCEP profiles for Windows hosts \ No newline at end of file diff --git a/server/fleet/windows_mdm.go b/server/fleet/windows_mdm.go index 1a28ed1f57..aef433e361 100644 --- a/server/fleet/windows_mdm.go +++ b/server/fleet/windows_mdm.go @@ -204,7 +204,7 @@ func validateFleetProvidedLocURI(locURI string, enableCustomOSUpdates bool) erro // and then based on it being marked Optional, or Required in the description. // A list containg all valid SCEP Profile LocURIs, a combination of optional and required to validate for non-SCEP LocURIs. -var validSCEPProfileLocURIs = slices.Concat([]string{ +var validDeviceSCEPProfileLocURIs = slices.Concat([]string{ fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/AADKeyIdentifierList", FleetVarSCEPWindowsCertificateID.WithPrefix()), fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/ContainerName", FleetVarSCEPWindowsCertificateID.WithPrefix()), fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/CustomTextToShowInPrompt", FleetVarSCEPWindowsCertificateID.WithPrefix()), @@ -215,9 +215,22 @@ var validSCEPProfileLocURIs = slices.Concat([]string{ fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/TemplateName", FleetVarSCEPWindowsCertificateID.WithPrefix()), fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/ValidPeriod", FleetVarSCEPWindowsCertificateID.WithPrefix()), fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/ValidPeriodUnits", FleetVarSCEPWindowsCertificateID.WithPrefix()), -}, requiredSCEPProfileLocURIs) +}, requiredDeviceSCEPProfileLocURIs) -var requiredSCEPProfileLocURIs = []string{ +var validUserSCEPProfileLocURIs = slices.Concat([]string{ + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/AADKeyIdentifierList", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/ContainerName", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/CustomTextToShowInPrompt", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/KeyProtection", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/RetryCount", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/RetryDelay", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/SubjectAlternativeNames", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/TemplateName", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/ValidPeriod", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/ValidPeriodUnits", FleetVarSCEPWindowsCertificateID.WithPrefix()), +}, requiredUserSCEPProfileLocURIs) + +var requiredDeviceSCEPProfileLocURIs = []string{ fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s", FleetVarSCEPWindowsCertificateID.WithPrefix()), fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/CAThumbprint", FleetVarSCEPWindowsCertificateID.WithPrefix()), fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/Challenge", FleetVarSCEPWindowsCertificateID.WithPrefix()), @@ -229,15 +242,34 @@ var requiredSCEPProfileLocURIs = []string{ fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/SubjectName", FleetVarSCEPWindowsCertificateID.WithPrefix()), } -var validExecSCEPProfileLocURIs = []string{ +var requiredUserSCEPProfileLocURIs = []string{ + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/CAThumbprint", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/Challenge", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/EKUMapping", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/HashAlgorithm", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/KeyLength", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/KeyUsage", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/ServerURL", FleetVarSCEPWindowsCertificateID.WithPrefix()), + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/SubjectName", FleetVarSCEPWindowsCertificateID.WithPrefix()), +} + +var validDeviceExecSCEPProfileLocURIs = []string{ fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/Enroll", FleetVarSCEPWindowsCertificateID.WithPrefix()), } +var validUserExecSCEPProfileLocURIs = []string{ + fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s/Install/Enroll", FleetVarSCEPWindowsCertificateID.WithPrefix()), +} + type windowsSCEPProfileValidator struct { - totalLocURIs int - totalExecLocURIs int - foundLocURIs map[string]bool - foundExecLocURIs map[string]bool + totalLocURIs int + totalExecLocURIs int + foundLocURIs map[string]bool + foundExecLocURIs map[string]bool + requiredSCEPProfileLocURIs *[]string + validSCEPProfileLocURIs *[]string + validExecSCEPProfileLocURIs *[]string } func newWindowsSCEPProfileValidator() *windowsSCEPProfileValidator { @@ -254,12 +286,16 @@ func (v *windowsSCEPProfileValidator) isSCEPProfile() bool { func (v *windowsSCEPProfileValidator) validateLocURI(locURI string) error { sanitizedLocURI := strings.TrimSpace(locURI) - // If we see a LocURI with SCEP prefix, but no Fleet Var we fail early. - if v.isSCEPLocURIWithoutFleetVar(sanitizedLocURI) { - return fmt.Errorf("You must use %q after \"./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/\".", FleetVarSCEPWindowsCertificateID.WithPrefix()) + if err := v.setLocURIArrays(sanitizedLocURI); err != nil { + return err } - if slices.Contains(validSCEPProfileLocURIs, sanitizedLocURI) { + // If we see a LocURI with SCEP prefix, but no Fleet Var we fail early. + if v.isSCEPLocURIWithoutFleetVar(sanitizedLocURI) { + return fmt.Errorf("You must use %q after \"ClientCertificateInstall/SCEP/\".", FleetVarSCEPWindowsCertificateID.WithPrefix()) + } + + if slices.Contains(*v.validSCEPProfileLocURIs, sanitizedLocURI) { v.foundLocURIs[sanitizedLocURI] = true } @@ -270,12 +306,16 @@ func (v *windowsSCEPProfileValidator) validateLocURI(locURI string) error { func (v *windowsSCEPProfileValidator) validateExecLocURI(locURI string) error { sanitizedLocURI := strings.TrimSpace(locURI) - // If we see a LocURI with SCEP prefix, but no Fleet Var we fail early. - if v.isSCEPLocURIWithoutFleetVar(sanitizedLocURI) { - return fmt.Errorf("You must use %q after \"./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/\".", FleetVarSCEPWindowsCertificateID.WithPrefix()) + if err := v.setLocURIArrays(sanitizedLocURI); err != nil { + return err } - if slices.Contains(validExecSCEPProfileLocURIs, sanitizedLocURI) { + // If we see a LocURI with SCEP prefix, but no Fleet Var we fail early. + if v.isSCEPLocURIWithoutFleetVar(sanitizedLocURI) { + return fmt.Errorf("You must use %q after \"ClientCertificateInstall/SCEP/\".", FleetVarSCEPWindowsCertificateID.WithPrefix()) + } + + if slices.Contains(*v.validExecSCEPProfileLocURIs, sanitizedLocURI) { v.foundExecLocURIs[sanitizedLocURI] = true } @@ -283,17 +323,53 @@ func (v *windowsSCEPProfileValidator) validateExecLocURI(locURI string) error { return nil } +func (v *windowsSCEPProfileValidator) setLocURIArrays(locURI string) error { + switch { + case IsWindowsSCEPLocURI(locURI) && v.validExecSCEPProfileLocURIs == nil: + if strings.HasPrefix(locURI, "./User") { + v.requiredSCEPProfileLocURIs = &requiredUserSCEPProfileLocURIs + v.validSCEPProfileLocURIs = &validUserSCEPProfileLocURIs + v.validExecSCEPProfileLocURIs = &validUserExecSCEPProfileLocURIs + } else { + v.requiredSCEPProfileLocURIs = &requiredDeviceSCEPProfileLocURIs + v.validSCEPProfileLocURIs = &validDeviceSCEPProfileLocURIs + v.validExecSCEPProfileLocURIs = &validDeviceExecSCEPProfileLocURIs + } + case !IsWindowsSCEPLocURI(locURI) && v.validExecSCEPProfileLocURIs == nil: + // Not a SCEP profile, set empty arrays to avoid nil pointer dereference later. + emptyArray := []string{} + v.requiredSCEPProfileLocURIs = &emptyArray + v.validSCEPProfileLocURIs = &emptyArray + v.validExecSCEPProfileLocURIs = &emptyArray + case IsWindowsSCEPLocURI(locURI) && v.validExecSCEPProfileLocURIs != nil: + // Check against mixing Device and User SCEP LocURIs. + firstValidLocURI := (*v.validSCEPProfileLocURIs)[0] + if strings.HasPrefix(firstValidLocURI, "./Device") && strings.HasPrefix(locURI, "./User") || + strings.HasPrefix(firstValidLocURI, "./User") && strings.HasPrefix(locURI, "./Device") { + return errors.New("All elements in the SCEP profile must start either with \"./Device\" or \"./User\".") + } + } + + return nil +} + // isSCEPLocURIWithoutFleetVar checks that the provided locURI starts with the SCEP prefix // and that it includes the required Fleet Var for SCEP Windows Certificate ID. // Skips any locURI that does not start with the SCEP prefix. func (v windowsSCEPProfileValidator) isSCEPLocURIWithoutFleetVar(locURI string) bool { - if strings.HasPrefix(locURI, "./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/") && - !strings.HasPrefix(locURI, fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s", FleetVarSCEPWindowsCertificateID.WithPrefix())) { + if (strings.HasPrefix(locURI, "./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/") && + !strings.HasPrefix(locURI, fmt.Sprintf("./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/%s", FleetVarSCEPWindowsCertificateID.WithPrefix()))) || (strings.HasPrefix(locURI, "./User/Vendor/MSFT/ClientCertificateInstall/SCEP/") && + !strings.HasPrefix(locURI, fmt.Sprintf("./User/Vendor/MSFT/ClientCertificateInstall/SCEP/%s", FleetVarSCEPWindowsCertificateID.WithPrefix()))) { return true } return false } +func IsWindowsSCEPLocURI(locURI string) bool { + return strings.HasPrefix(locURI, "./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/") || + strings.HasPrefix(locURI, "./User/Vendor/MSFT/ClientCertificateInstall/SCEP/") +} + func (v *windowsSCEPProfileValidator) finalizeValidation() error { if !v.isSCEPProfile() { // Cheeky validation here, to only allow Exec elements in SCEP profiles. @@ -303,20 +379,30 @@ func (v *windowsSCEPProfileValidator) finalizeValidation() error { return nil // Not a SCEP profile, nothing to validate here. } + // If we reach here with empty arrays something has gone wrong. + if len(*v.validExecSCEPProfileLocURIs) == 0 || len(*v.validSCEPProfileLocURIs) == 0 || len(*v.requiredSCEPProfileLocURIs) == 0 { + return errors.New("Internal error validating SCEP profile LocURIs.") + } + + // ADD CODE HERE + // Verify that we do not have any non-scep loc URIs present if v.totalLocURIs != len(v.foundLocURIs) { - return errors.New("Only options that have starting with \"./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/\" can be added to SCEP profile.") + return errors.New("Only options that have starting with \"ClientCertificateInstall/SCEP/\" can be added to SCEP profile.") } // Check that at least one Exec LocURI is present and it matches the only one we have in the array. - if len(v.foundExecLocURIs) != 1 && !v.foundExecLocURIs[validExecSCEPProfileLocURIs[0]] { - return errors.New("Couldn't add. \"./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/Enroll\" must be included within . Please add and try again.") + validExecLocURIs := *v.validExecSCEPProfileLocURIs + if len(v.foundExecLocURIs) != 1 && !v.foundExecLocURIs[validExecLocURIs[0]] { + return errors.New("Couldn't add. \"ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/Enroll\" must be included within . Please add and try again.") } // Check that all required LocURIs are present - for _, requiredLocURI := range requiredSCEPProfileLocURIs { + for _, requiredLocURI := range *v.requiredSCEPProfileLocURIs { if !v.foundLocURIs[requiredLocURI] { - return fmt.Errorf("%q is missing. Please add and try again", requiredLocURI) + trimmedPrefix := strings.TrimPrefix(requiredLocURI, "./Device/Vendor/MSFT/") + trimmedPrefix = strings.TrimPrefix(trimmedPrefix, "./User/Vendor/MSFT/") + return fmt.Errorf("%q is missing. Please add and try again.", trimmedPrefix) } } diff --git a/server/fleet/windows_mdm_test.go b/server/fleet/windows_mdm_test.go index 7c45c5ceb1..b40fde809f 100644 --- a/server/fleet/windows_mdm_test.go +++ b/server/fleet/windows_mdm_test.go @@ -613,7 +613,7 @@ func TestValidateUserProvided(t *testing.T) { `), }, - wantErr: "Only options that have starting with \"./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/\" can be added to SCEP profile.", + wantErr: "Only options that have starting with \"ClientCertificateInstall/SCEP/\" can be added to SCEP profile.", }, { name: "SCEP profile without Exec block", @@ -626,7 +626,7 @@ func TestValidateUserProvided(t *testing.T) { `), }, - wantErr: "\"./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/Enroll\" must be included within . Please add and try again.", + wantErr: "\"ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/Enroll\" must be included within . Please add and try again.", }, { name: "SCEP profile with Exec block, but worng LocURI ", @@ -646,7 +646,7 @@ func TestValidateUserProvided(t *testing.T) { `), }, - wantErr: "Couldn't add. \"./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/Enroll\" must be included within . Please add and try again.", + wantErr: "Couldn't add. \"ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/Enroll\" must be included within . Please add and try again.", }, { name: fmt.Sprintf("SCEP profile with missing $FLEET_VAR_%s after SCEP LocURI", FleetVarSCEPWindowsCertificateID), @@ -666,7 +666,7 @@ func TestValidateUserProvided(t *testing.T) { `), }, - wantErr: fmt.Sprintf("You must use \"$FLEET_VAR_%s\" after \"./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/\".", FleetVarSCEPWindowsCertificateID), + wantErr: fmt.Sprintf("You must use \"$FLEET_VAR_%s\" after \"ClientCertificateInstall/SCEP/\".", FleetVarSCEPWindowsCertificateID), }, { name: "SCEP Profile with missing required LocURI", @@ -688,7 +688,7 @@ func TestValidateUserProvided(t *testing.T) { `), }, - wantErr: fmt.Sprintf("\"./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_%s/Install/CAThumbprint\" is missing.", FleetVarSCEPWindowsCertificateID), + wantErr: fmt.Sprintf("\"ClientCertificateInstall/SCEP/$FLEET_VAR_%s/Install/CAThumbprint\" is missing. Please add and try again.", FleetVarSCEPWindowsCertificateID), }, { name: "Only SCEP profiles can have Exec elements", @@ -705,6 +705,28 @@ func TestValidateUserProvided(t *testing.T) { }, wantErr: "Only SCEP profiles can include elements.", }, + { + name: "Either device or user SCEP profiles, not both", + profile: MDMWindowsConfigProfile{ + SyncML: []byte(` + + + + ./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID + + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/Enroll + + + + `), + }, + wantErr: "All elements in the SCEP profile must start either with \"./Device\" or \"./User\".", + }, } for _, tt := range tests { diff --git a/server/mdm/microsoft/profile_verifier.go b/server/mdm/microsoft/profile_verifier.go index f1452b7c4b..30686585d1 100644 --- a/server/mdm/microsoft/profile_verifier.go +++ b/server/mdm/microsoft/profile_verifier.go @@ -170,7 +170,7 @@ func compareResultsToExpectedProfiles(ctx context.Context, logger kitlog.Logger, } err = LoopOverExpectedHostProfiles(ctx, logger, ds, host, func(profile *fleet.ExpectedMDMProfile, ref, locURI, wantData string) { - if strings.HasPrefix(strings.TrimSpace(locURI), "./Device/Vendor/MSFT/ClientCertificateInstall/SCEP") { + if strings.Contains(strings.TrimSpace(locURI), "/Vendor/MSFT/ClientCertificateInstall/SCEP") { verified[profile.Name] = struct{}{} // We delete here if by some accident it was marked as missing before delete(missing, profile.Name) diff --git a/server/mdm/microsoft/profile_verifier_test.go b/server/mdm/microsoft/profile_verifier_test.go index c5238b08dc..770a91fa35 100644 --- a/server/mdm/microsoft/profile_verifier_test.go +++ b/server/mdm/microsoft/profile_verifier_test.go @@ -605,6 +605,14 @@ func TestVerifyHostMDMProfilesHappyPaths(t *testing.T) { Data: "non related data", }, }), 0}, + {"N2", syncml.ForTestWithData([]syncml.TestCommand{ + { + Verb: "Replace", + LocURI: ` + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/bogus-key-value`, + Data: "non related data", + }, + }), 0}, }, existingProfiles: []fleet.HostMDMWindowsProfile{ { @@ -612,8 +620,13 @@ func TestVerifyHostMDMProfilesHappyPaths(t *testing.T) { Name: "N1", Status: &fleet.MDMDeliveryPending, }, + { + ProfileUUID: "uuid-N2", + Name: "N2", + Status: &fleet.MDMDeliveryPending, + }, }, - toVerify: []string{"N1"}, + toVerify: []string{"N1", "N2"}, }, } diff --git a/server/service/integration_mdm_profiles_test.go b/server/service/integration_mdm_profiles_test.go index c746c0459d..f0419f4c88 100644 --- a/server/service/integration_mdm_profiles_test.go +++ b/server/service/integration_mdm_profiles_test.go @@ -7909,10 +7909,21 @@ func (s *integrationMDMTestSuite) TestWindowsProfilesFleetVariableSubstitution() "Profile should be verified in host details API for no-vars host") } -//go:embed testdata/profiles/windows-scep.xml -var windowsSCEPProfileBytes []byte +//go:embed testdata/profiles/windows-device-scep.xml +var windowsDeviceSCEPProfileBytes []byte -func (s *integrationMDMTestSuite) TestWindowsSCEPProfile() { +func (s *integrationMDMTestSuite) TestWindowsDeviceSCEPProfile() { + testWindowsSCEPProfile(s, windowsDeviceSCEPProfileBytes) +} + +//go:embed testdata/profiles/windows-user-scep.xml +var windowsUserSCEPProfileBytes []byte + +func (s *integrationMDMTestSuite) TestWindowsUserSCEPProfile() { + testWindowsSCEPProfile(s, windowsUserSCEPProfileBytes) +} + +func testWindowsSCEPProfile(s *integrationMDMTestSuite, windowsScepProfile []byte) { t := s.T() ctx := context.Background() scepServer := scep_server.StartTestSCEPServer(t) @@ -7953,7 +7964,7 @@ func (s *integrationMDMTestSuite) TestWindowsSCEPProfile() { // Upload SCEP profile with missing CA resp := s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", batchSetMDMProfilesRequest{Profiles: []fleet.MDMProfileBatchPayload{ - {Name: "WindowsSCEPProfile", Contents: windowsSCEPProfileBytes}, + {Name: "WindowsSCEPProfile", Contents: windowsScepProfile}, }}, http.StatusBadRequest) errMsg := extractServerErrorText(resp.Body) @@ -7972,7 +7983,7 @@ func (s *integrationMDMTestSuite) TestWindowsSCEPProfile() { s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", batchSetMDMProfilesRequest{Profiles: []fleet.MDMProfileBatchPayload{ - {Name: "WindowsSCEPProfile", Contents: windowsSCEPProfileBytes}, + {Name: "WindowsSCEPProfile", Contents: windowsScepProfile}, }}, http.StatusNoContent) @@ -8022,17 +8033,11 @@ func (s *integrationMDMTestSuite) TestWindowsSCEPProfile() { foundProfile = true profileUUID = p.ProfileUUID require.NotNil(t, p.Status) - assert.Equal(t, fleet.MDMDeliveryVerified, *p.Status) + require.Equal(t, fleet.MDMDeliveryVerified, *p.Status) } } require.True(t, foundProfile, "WindowsSCEPProfile not found for host") - mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error { - mysql.DumpTable(t, q, "host_mdm_windows_profiles") - mysql.DumpTable(t, q, "host_mdm_managed_certificates") - return nil - }) - // Attempt simple SCEP call with GetCACaps operation to verify SCEP server is reachable identifier := host.UUID + "," + profileUUID + "," + "INTEGRATION" scepRes := s.DoRawWithHeaders("GET", apple_mdm.SCEPProxyPath+identifier+"/pkiclient.exe", nil, http.StatusOK, nil, "operation", "GetCACaps") diff --git a/server/service/testdata/profiles/windows-scep.xml b/server/service/testdata/profiles/windows-device-scep.xml similarity index 100% rename from server/service/testdata/profiles/windows-scep.xml rename to server/service/testdata/profiles/windows-device-scep.xml diff --git a/server/service/testdata/profiles/windows-user-scep.xml b/server/service/testdata/profiles/windows-user-scep.xml new file mode 100644 index 0000000000..8aa9fcbff5 --- /dev/null +++ b/server/service/testdata/profiles/windows-user-scep.xml @@ -0,0 +1,140 @@ + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID + + + node + + + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/RetryCount + + + int + + 3 + + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/RetryDelay + + + int + + 10 + + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/KeyUsage + + + int + + 160 + + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/KeyLength + + + int + + 1024 + + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/HashAlgorithm + + + chr + + SHA-1 + + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/SubjectName + + + chr + + CN=$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID + + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/EKUMapping + + + chr + + 1.3.6.1.5.5.7.3.2 + + + + 10 + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/ServerURL + + + chr + + $FLEET_VAR_CUSTOM_SCEP_PROXY_URL_INTEGRATION + + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/Challenge + + + chr + + $FLEET_VAR_CUSTOM_SCEP_CHALLENGE_INTEGRATION + + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/CAThumbprint + + + chr + + 2133EC6A3CFB8418837BB395188D1A62CA2B96A6 + + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/Enroll + + + \ No newline at end of file