diff --git a/tools/windows-mdm/poc-mdm-server/.gitignore b/tools/windows-mdm/poc-mdm-server/.gitignore new file mode 100644 index 0000000000..ac75f32b7b --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/.gitignore @@ -0,0 +1,18 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Mac System Files +.DS_Store + +# Ignore The Folder With My HTTPS Certifciates +certs/ diff --git a/tools/windows-mdm/poc-mdm-server/LICENSE b/tools/windows-mdm/poc-mdm-server/LICENSE new file mode 100644 index 0000000000..13db870aac --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Oscar Beaumont + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/windows-mdm/poc-mdm-server/README.md b/tools/windows-mdm/poc-mdm-server/README.md new file mode 100644 index 0000000000..9ac47cba06 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/README.md @@ -0,0 +1,631 @@ + +# Windows MDM Server Demo + +This project is a working and minimal implementation of the Windows device enrollment and management protocols. It was based on an initial implementation of the MS-MDE enrollment protocols [here](https://github.com/oscartbeaumont/windows_mdm). + +This project uses the protocols: + +- [MS-MDE](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-mde/d9e18701-cd4c-4fdb-8a3e-c1ddd33b1307) +- [MS-MDM](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-mdm/33769a92-ac31-47ef-ae7b-dc8501f7104f) +- [MS-WSTEP](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wstep/4766a85d-0d18-4fa1-a51f-e5cb98b752ea) +- [MS-XCEP](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-xcep/08ec4475-32c2-457d-8c27-5a176660a210) +- [OMA Device Management Protocol](https://www.openmobilealliance.org/release/DM/V1_2_1-20080617-A/OMA-TS-DM_Protocol-V1_2_1-20080617-A.pdf) + + +The steps for MDE device enrollment correspond to five phases as shown in the following diagram: + +![enter image description here](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-mde/ms-mde_files/image002.png) + +## License + +This code is MIT licensed and it was forked from [here](https://github.com/oscartbeaumont/windows_mdm). Initial implementation credit goes to [Oscar Beaumont](https://github.com/oscartbeaumont). + +## Usage + +On the server side, you just need to run the project using the already provided cert and keys. So go to the project folder and run. + +```bash +go run . +``` + +On the Windows client side, you need to import a custom CA certificate to the certificate store, and populate the `hosts` file before running the Windows Enrollment. The certificate to import is on the certs directory and it is called `dev_cert_mdmwindows_com.pfx`. You need to copy this certificate to the client machine and run the powershell command below. This is required because the project uses a local dev https endpoint. + + 1) Import certificate to Trusted CAs repository (be sure to update the path to the pfx certificate) + + powershell -ep bypass "$mypwd = ConvertTo-SecureString -String 'testpassword' -Force -AsPlainText ; Import-PfxCertificate -FilePath c:\path\to\dev_cert_mdmwindows_com.pfx -CertStoreLocation Cert:\LocalMachine\Root -Password $mypwd" + + 2) Add mdmwindows.com to the list of static DNS + + echo mdmwindows.com >> %SystemRoot%\System32\drivers\etc\hosts + echo autodiscovery.mdmwindows.com >> %SystemRoot%\System32\drivers\etc\hosts + echo enterpriseenrollment.mdmwindows.com >> %SystemRoot%\System32\drivers\etc\hosts + +## Protocol Details + +Below is the raw https exchange of the MS-MDE and MS-MDM protocols when run using the -verbose mode: + + +### MDM Server HTTP Endpoints Auto Discovery Flow + + + ============================= Input Request ============================= + ----------- Input Header ----------- + GET /EnrollmentServer/Discovery.svc HTTP/2.0 + Host: enterpriseenrollment.mdmwindows.com + Cache-Control: no-cache + Pragma: no-cache + User-Agent: ENROLLClient + + + ----------- Empty Input Body ----------- + ========================================================================= + + + + ============================= Output Response ============================= + ----------- Response Header ----------- + HTTP/1.1 200 OK + Connection: close + + + ----------- Empty Response Body ----------- + ========================================================================= + + ============================= Input Request ============================= + ----------- Input Header ----------- + POST /EnrollmentServer/Discovery.svc HTTP/2.0 + Host: enterpriseenrollment.mdmwindows.com + Content-Length: 1042 + Content-Type: application/soap+xml; charset=utf-8 + User-Agent: ENROLLClient + + + ----------- Input Body ----------- + + + + http://schemas.microsoft.com/windows/management/2012/01/enrollment/IDiscoveryService/Discover + urn:uuid:748132ec-a575-4329-b01b-6171a9cf8478 + + http://www.w3.org/2005/08/addressing/anonymous + + https://EnterpriseEnrollment.mdmwindows.com:443/EnrollmentServer/Discovery.svc + + + + + demo@mdmwindows.com + 4.0 + CIMClient_Windows + 10.0.19043.2364 + 72 + + OnPremise + Federated + + + + + + ========================================================================= + + + + + ============================= Output Response ============================= + ----------- Response Header ----------- + HTTP/1.1 200 OK + Content-Length: 1107 + Content-Type: application/soap+xml; charset=utf-8 + + + ----------- Response Body ----------- + + + + + http://schemas.microsoft.com/windows/management/2012/01/enrollment/IDiscoveryService/DiscoverResponse + 8c6060c4-3d78-4d73-ae17-e8bce88426ee + + urn:uuid:748132ec-a575-4329-b01b-6171a9cf8478 + + + + + OnPremise + 4.0 + https://mdmwindows.com/EnrollmentServer/Policy.svc + https://mdmwindows.com/EnrollmentServer/Enrollment.svc + + + + + ========================================================================= + +## MDM Certificate Enrollment Policy Flow (MS-XCEP) + + +============================= Input Request ============================= +----------- Input Header ----------- + POST /EnrollmentServer/Policy.svc HTTP/2.0 +Host: mdmwindows.com +Content-Length: 1495 +Content-Type: application/soap+xml; charset=utf-8 +User-Agent: ENROLLClient + + +----------- Input Body ----------- + + + + http://schemas.microsoft.com/windows/pki/2009/01/enrollmentpolicy/IPolicy/GetPolicies + urn:uuid:72048B64-0F19-448F-8C2E-B4C661860AA0 + + http://www.w3.org/2005/08/addressing/anonymous + + https://mdmwindows.com/EnrollmentServer/Policy.svc + + + demo@mdmwindows.com + demo + + + + + + + + + + + + + +========================================================================= + + + + +============================= Output Response ============================= +----------- Response Header ----------- + HTTP/1.1 200 OK +Content-Length: 1378 +Content-Type: application/soap+xml; charset=utf-8 + + +----------- Response Body ----------- + + + + + http://schemas.microsoft.com/windows/pki/2009/01/enrollmentpolicy/IPolicy/GetPoliciesResponse + urn:uuid:72048B64-0F19-448F-8C2E-B4C661860AA0 + + + + + + + + 3 + + 2048 + + + + + + + + + + 1.3.6.1.4.1.311.20.2 + 1 + 5 + Certificate Template Name + + + + + +========================================================================= + + + +### MDM Certificate Enrollment Extensions Flow (MS-WSTEP) + + + ============================= Input Request ============================= + ----------- Input Header ----------- + POST /EnrollmentServer/Enrollment.svc HTTP/2.0 + Host: mdmwindows.com + Content-Length: 4295 + Content-Type: application/soap+xml; charset=utf-8 + User-Agent: ENROLLClient + + + ----------- Input Body ----------- + + + + http://schemas.microsoft.com/windows/pki/2009/01/enrollment/RST/wstep + urn:uuid:0d5a1441-5891-453b-becf-a2e5f6ea3749 + + http://www.w3.org/2005/08/addressing/anonymous + + https://mdmwindows.com/EnrollmentServer/Enrollment.svc + + + demo@mdmwindows.com + demo + + + + + + http://schemas.microsoft.com/5.0.0.0/ConfigurationManager/Enrollment/DeviceEnrollmentToken + http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue + MIICzjCCAboCAQAwSzFJMEcGA1UEAxNAQkE0MDVBNUQtQjFDNy00NzM3LTgxQzgtOEYzNzlBITFFMDhDNkU5NUQ4QkI4NDNCMTI3OEZGNDVCQzYwQ0M2ADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJiaMezO2srqSMWGm702z0XoaScezbTDbC7oLEFRTBe20cXUduHZXyR2UJvrbztQQhuzy8Fie/Y8FNOvBfs6qb+/S/iYGvK9Ju0SJaz5KLthuUK0BLj5GtAvHnEIKk1RkYZPBTMVwS8n+iJTged8C0XhOAVWk8pDsthlrLuUlURSOMji5ftN+dsygDfAWLbJc2imikdKx1sWwDNdSNDph+RjhZHWroABKBrLbmGatRMyxt+xdV/GYvd1rLl9HWZt+IIYPtMBWlnSjVnEMs6UdU7spK7FOr6lhkuQ5wXN1uelLdpj7Zl2pKL1iJgOMLn7N23dzjDzTRfjpUNL/rNvlg0CAwEAAaBCMEAGCSqGSIb3DQEJDjEzMDEwLwYKKwYBBAGCN0IBAAQhMUUwOEM2RTk1RDhCQjg0M0IxMjc4RkY0NUJDNjBDQzYAMAkGBSsOAwIdBQADggEBADYVlS6XuWXSBFjRSGQmKJmVe1a+8TQfRUVpakKKkMDlH7aqyOKZB00nL1vNNXO6xdaWb5ViyKdsTNwnXz/BhSmZaLYGS8Qi2N5HPo1XQOdAGj+Nee0R4Nun+q9b+zfNFXo8fJuNiUaOCaDrKX5pOcALRSJBF2Kv1mBxkixJNJQgWj/JPiCr76llqH06ODf9zbmofOdEYwa2XpT43mWD7gecI8zIi+N3KxJue6hL1sLHDa5nIJR1QLjr0BddJLSm5DKiHDyIGQqPFfQXgjlyPZC9X48noxizgwv8/pwkjoRCM/dvR0QyZb3jm0Ah4MWyTlnWZp6Kl9LAUWSJoJXBMK0= + + + true + + + 3B3ED6D0EA88CBFCF37D36F90F22FE61172348C0162FC3840D6703149870CE76 + + + en-US + + + true + + + 72 + + + DESKTOP-28FGAI6 + + + 00-0C-29-51-60-9D + + + 1A-77-20-52-41-53 + + + 1A-77-20-52-41-53 + + + 00-0C-29-51-60-A7 + + + 18-14-20-52-41-53 + + + 00-0C-29-51-60-93 + + + 1E08C6E95D8BB843B1278FF45BC60CC6 + + + Full + + + CIMClient_Windows + + + 10.0.19043.2364 + + + 10.0.19043.2364 + + + false + + + + + + ========================================================================= + + + + + ============================= Output Response ============================= + ----------- Response Header ----------- + HTTP/1.1 200 OK + Content-Length: 8598 + Content-Type: application/soap+xml; charset=utf-8 + + + ----------- Response Body ----------- + + + + + http://schemas.microsoft.com/windows/pki/2009/01/enrollment/RSTRC/wstep + urn:uuid:0d5a1441-5891-453b-becf-a2e5f6ea3749 + + + 2018-11-30T00:32:59.420Z + 2018-12-30T00:37:59.420Z + + + + + + + http://schemas.microsoft.com/5.0.0.0/ConfigurationManager/Enrollment/DeviceEnrollmentToken + + + PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48d2FwLXByb3Zpc2lvbmluZ2RvYyB2ZXJzaW9uPSIxLjEiPjxjaGFyYWN0ZXJpc3RpYyB0eXBlPSJDZXJ0aWZpY2F0ZVN0b3JlIj48Y2hhcmFjdGVyaXN0aWMgdHlwZT0iUm9vdCI+PGNoYXJhY3RlcmlzdGljIHR5cGU9IlN5c3RlbSI+PGNoYXJhY3RlcmlzdGljIHR5cGU9IkQ5QTg4RTA0QUYxOEE0RDM5OUNFRTYyRjJDNzE0NjlDM0FFMUU2NzUiPjxwYXJtIG5hbWU9IkVuY29kZWRDZXJ0aWZpY2F0ZSIgdmFsdWU9Ik1JSUZUakNDQXphZ0F3SUJBZ0lVQU1sQkJEYjU2bUZGVVpPaDM1TW1QVHVWNkpjd0RRWUpLb1pJaHZjTkFRRUxCUUF3UHpFWk1CY0dBMVVFQ2d3UVRXRjBkSEpoZUNCSlpHVnVkR2wwZVRFaU1DQUdBMVVFQXd3WlYybHVaRzkzY3lCTlJFMGdSR1Z0YnlCSlpHVnVkR2wwZVRBZUZ3MHlNVEF4TURNd01qUTBNREphRncweU5EQXhNRE13TWpRME1ESmFNRDh4R1RBWEJnTlZCQW9NRUUxaGRIUnlZWGdnU1dSbGJuUnBkSGt4SWpBZ0JnTlZCQU1NR1ZkcGJtUnZkM01nVFVSTklFUmxiVzhnU1dSbGJuUnBkSGt3Z2dJaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQ0R3QXdnZ0lLQW9JQ0FRQytnNjJHaHNFR2U0WGYvNWw4MG1POEZDOHNNWTZxR0MwZEI4YXZjSlhQdVIxTjREUVpBRkhIS2pnTTFMcFk0NVB6eHhTbUQxWTBSZFF3YUpMejAvV1F6c0RBRmhQRTdCeEI1SjBSVU1ZaVg5Yk01cCsyZmlmMFhua2xCUjE2RG5vNi9aeHdsdnZtMW1TN1RQUkZNcUhGZFB5WW0wZVc4RzAxUXBkMWVhVDdKQVhEcjN1a25yeXpmTjUxN3hzaGxJSmhVYUJtTTZRWng2L3UrS3ZhWkRGWmk1akdTekVJVHFFcy8zcFU4UFcvQm1OR1pYUkNWd2NHVGJwSG9IejczVlg3VlNEb1poWTVQNXp0VzUvZ29wOVJEQ0dxU0lJck5rNGJhOTlGd1liTnJPWDVnYktQOHJJN3VEdXBLRVlTaE5xQ250VC9ETjZXTUVSVWhkYkgzVExXeWhMSzhrbmxQTWVOSG9QTjFXK0pSZUowZVk4d0JWUVBHUENxdmJnZ3lYZ1drOTRHT3ExdDhiTmljSkRXVFpaQy9nTzRlV2FyU0RlVEJoRS80TlhWTDF5YVpkVEY0TUdBa1VLN24xYkJWT0MzTFQ4dzFEWWJIc290NmRvUTNEQ1M3NGVia1d6aHNKbjRxLyswUXFZTkREaG5FRWxRYWhvSmtCNEgrWGxLNktIeE9WNlpQQlpaTVRMVHNLTDZXRjgxb1k5N2lEc3hhNTd0d1J5a04raXoyYkxwQlBTa1I4ajU2Nnhhb3U4VGo4T2t5ODZCeG42V25MWUxvWFpld0M0VkhQRFYwUDRHQXVBeXZhWXpyM3owV20ydW9FZDBBT1Eyb3dteTAwNnduWW1yWVF4NGtqUGZVakF0UExjZE9iallvTWszTzNZaE5iUWU0TUtOWDdXL1R3SURBUUFCbzBJd1FEQU9CZ05WSFE4QkFmOEVCQU1DQVFZd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWNsME15cjlpNjAzKytXM3BPMm5WVlZXdmNZc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnSUJBR01GQXByVmgrK3dXU0NOakl0RkF6bW5qcFRwRmZuVnpQbHZrNXJyU2xrajVTMHlYbk9hU3VOQ25kekhwdURhYzZLd1IwY0NEUVVXNjdnWEgxdUZ3ZTE0MGtOTy92ajkycFFqcUgzTmR4ek51YkE5cXBsRzFqRXN2NXVyNEpWY1NjT002RzlxY2FHUEhTbTRkRFNBazdBUWFDQnV2RUV6Qno3L2o2QTlqS0Y4RHJ4bzU2MkYxb0xIWHVjdTJIU0VuSXJxZWdadDAwbjg3WEpnUXNVTGxoMHB1ejFkRk9FYWNMZHdvM1oxTnpOOUxEamt2Q01NUi9wbFJZVUx1cGhiaEdaL3JkME0wYzdIT0k5MGMyaS82dFlXeDM2TjZiWC9LMTlzQTE1N2ZjY1piQzhFb05iYVI2RlJzUlpQN25RSGtRR204M29kT0cza2tQelJ4b3lTbStIL1ZhM0YyRVZ6VlhRUk9vRHArMktRSThJUmpRMjVwTWxDSCs1Qm5OVmpSMkZ3cHZFU0FKZ0tWZGQ4RkVPQkJPV0dKZ2xaamx3Rm1ZQnVETWE4UnZmeStEU2NMNGxCYTFPMEx1N0xwRjBpNkRyYUZHajBxS3k5SjRkc1FOaXB5elRsR3dpczF1M0E4RFNSbXphNWxzMEtlalQzaXQ5OWQva1A4L2lVam5XOVdvSDRYcVZMMHlCaDUzMExCV1F3QktBck5zenRSNzAvT01mQ0ZnbWFVOEN3VGdrU0dQNFdyK0UzVXd1QWxhQThnWERTYndmM2x4OGlnTUpmRGtPVDVxNWNrb3BNcHpCMGJrbVhVVk9YcUVCRjVwOTA2c3o1UmNzdTRkNnMwZDQ1MnVPSTJnQTBGOXVrWEFKd1A4UTVlUS9PSnBwanF1S1ByQzJnSzhRTDB1THIiIC8+PC9jaGFyYWN0ZXJpc3RpYz48L2NoYXJhY3RlcmlzdGljPjwvY2hhcmFjdGVyaXN0aWM+PGNoYXJhY3RlcmlzdGljIHR5cGU9Ik15Ij48Y2hhcmFjdGVyaXN0aWMgdHlwZT0iVXNlciI+PGNoYXJhY3RlcmlzdGljIHR5cGU9IkFEQTdCMzVBMzBGOTg2NDY1Q0U0QzE3NkQ1MjdENEI4MDg0OEVFRjQiPjxwYXJtIG5hbWU9IkVuY29kZWRDZXJ0aWZpY2F0ZSIgdmFsdWU9Ik1JSUVMVENDQWhXZ0F3SUJBZ0lCQWpBTkJna3Foa2lHOXcwQkFRVUZBREEvTVJrd0Z3WURWUVFLREJCTllYUjBjbUY0SUVsa1pXNTBhWFI1TVNJd0lBWURWUVFEREJsWGFXNWtiM2R6SUUxRVRTQkVaVzF2SUVsa1pXNTBhWFI1TUI0WERUSXlNVEl6TURFNU1UZzBNMW9YRFRJek1USXpNREU1TVRnME0xb3dLekVwTUNjR0ExVUVBeE1nTVVVd09FTTJSVGsxUkRoQ1FqZzBNMEl4TWpjNFJrWTBOVUpETmpCRFF6WXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDWW1qSHN6dHJLNmtqRmhwdTlOczlGNkdrbkhzMjB3Mnd1NkN4QlVVd1h0dEhGMUhiaDJWOGtkbENiNjI4N1VFSWJzOHZCWW52MlBCVFRyd1g3T3FtL3YwdjRtQnJ5dlNidEVpV3MrU2k3WWJsQ3RBUzQrUnJRTHg1eENDcE5VWkdHVHdVekZjRXZKL29pVTRIbmZBdEY0VGdGVnBQS1E3TFlaYXk3bEpWRVVqakk0dVg3VGZuYk1vQTN3RmkyeVhOb3BvcEhTc2RiRnNBelhValE2WWZrWTRXUjFxNkFBU2dheTI1aG1yVVRNc2Jmc1hWZnhtTDNkYXk1ZlIxbWJmaUNHRDdUQVZwWjBvMVp4RExPbEhWTzdLU3V4VHErcFlaTGtPY0Z6ZGJucFMzYVkrMlpkcVNpOVlpWURqQzUremR0M2M0dzgwMFg0NlZEUy82emI1WU5BZ01CQUFHalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmQmdOVkhTTUVHREFXZ0JSeVhRekt2MkxyVGY3NWJlazdhZFZWVmE5eGl6QU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FnRUFWOFlRNHErL2xXVUJVVHdEOVIwMXhmcEphQW5FUm4wSmxIdUMzU2tRY05XN3I4cC83L2tDdENjVDY5VFR2QlVzRENNZy9PdXVCODQxTGdraFhRRWtQdHZqa3l4aUVYaytkRzZiUXQ1QWpLSWtIWUtjZEY0TzA5M0NZTm9xWElpZTg4SnduRzNzUDl3S3kyQWZwNXhYTXlJRVFqdkdNYVlneUxQUDAyd21uMmp2QU5PTlpDUHVFbTV4VnlLRzltTU1ZWWFFNXVIenB5TU5JK3BzUnVKMU8zbFlBSFhMMjkwTlBiSjl2QzRnMDRDUWRhWUZzRjl6M3F2dGlEOWVCNE9PRHRabnNrZFpsVURQSE1RYWMrRjdVMVhPdHJuU3VmN0tYOXo2U3U2VEllOWFVa3hobVgxeCtmNW9QczdVSzREWHB1NWJzTGZUMDVFN1RpclA4OTErWHR1Q3BCUnJOOFVDTlZOV2J3aFFGOE1IUXkrRk5oQm43OGJLcnhpT1FDZGM1ZWVtdVlhaVBaTHNhYnFhNExadGlIODlOeEJpVmVtbDA3aDY3RjdXNE9VeVpGWUlpdVFxUzAvdjcxaWdmd3dQVUkyd0RhWGo2b3J5K3NxV3grV3JJdFVyNWxSMTdPVU4xVUpMVkNmcCtXaldCSlhEYnRueVcvWFdzd29UcHVDMXI5bHhDWEZpaFJJSGZGV2p0OWprbHJCRFF6dDYvMmphVWprR2V2SFhieWlsd3B6djlYcUczMDBjdG9mUHdwb2RjaVVFVmZtaHFzeG1NRHhabzNpODZiMGZuS3dhN3JUSmlVZWFpTkRuSFJoMzlKcVpGVVNOZElVLzFJVzlUall1NXZQOEovV2l1akFnTnNyZ1AyN2xBYTBzZUFDNFJqWUQyN0Y3S0o1WjhBTT0iIC8+PC9jaGFyYWN0ZXJpc3RpYz48Y2hhcmFjdGVyaXN0aWMgdHlwZT0iUHJpdmF0ZUtleUNvbnRhaW5lciIgLz48L2NoYXJhY3RlcmlzdGljPjwvY2hhcmFjdGVyaXN0aWM+PC9jaGFyYWN0ZXJpc3RpYz48Y2hhcmFjdGVyaXN0aWMgdHlwZT0iQVBQTElDQVRJT04iPjxwYXJtIG5hbWU9IkFQUElEIiB2YWx1ZT0idzciIC8+PHBhcm0gbmFtZT0iUFJPVklERVItSUQiIHZhbHVlPSJERU1PIE1ETSIgLz48cGFybSBuYW1lPSJOQU1FIiB2YWx1ZT0iRmxlZXRETSBEZW1vIFNlcnZlciAtIFdpbmRvd3MiIC8+PHBhcm0gbmFtZT0iQUREUiIgdmFsdWU9Imh0dHBzOi8vbWRtd2luZG93cy5jb20vTWFuYWdlbWVudFNlcnZlci9NRE0uc3ZjIiAvPjxwYXJtIG5hbWU9IlNlcnZlckxpc3QiIHZhbHVlPSJodHRwczovL21kbXdpbmRvd3MuY29tL01hbmFnZW1lbnRTZXJ2ZXIvU2VydmVyTGlzdC5zdmMiIC8+PHBhcm0gbmFtZT0iUk9MRSIgdmFsdWU9IjQyOTQ5NjcyOTUiIC8+PHBhcm0gbmFtZT0iQkFDS0NPTVBBVFJFVFJZRElTQUJMRUQiIC8+PHBhcm0gbmFtZT0iREVGQVVMVEVOQ09ESU5HIiB2YWx1ZT0iYXBwbGljYXRpb24vdm5kLnN5bmNtbC5kbSt4bWwiIC8+PGNoYXJhY3RlcmlzdGljIHR5cGU9IkFQUEFVVEgiPjxwYXJtIG5hbWU9IkFBVVRITEVWRUwiIHZhbHVlPSJDTElFTlQiIC8+PHBhcm0gbmFtZT0iQUFVVEhUWVBFIiB2YWx1ZT0iRElHRVNUIiAvPjxwYXJtIG5hbWU9IkFBVVRIU0VDUkVUIiB2YWx1ZT0iZHVtbXkiIC8+PHBhcm0gbmFtZT0iQUFVVEhEQVRBIiB2YWx1ZT0ibm9uY2UiIC8+PC9jaGFyYWN0ZXJpc3RpYz48Y2hhcmFjdGVyaXN0aWMgdHlwZT0iQVBQQVVUSCI+PHBhcm0gbmFtZT0iQUFVVEhMRVZFTCIgdmFsdWU9IkFQUFNSViIgLz48cGFybSBuYW1lPSJBQVVUSFRZUEUiIHZhbHVlPSJESUdFU1QiIC8+PHBhcm0gbmFtZT0iQUFVVEhOQU1FIiB2YWx1ZT0iZHVtbXkiIC8+PHBhcm0gbmFtZT0iQUFVVEhTRUNSRVQiIHZhbHVlPSJkdW1teSIgLz48cGFybSBuYW1lPSJBQVVUSERBVEEiIHZhbHVlPSJub25jZSIgLz48L2NoYXJhY3RlcmlzdGljPjwvY2hhcmFjdGVyaXN0aWM+PGNoYXJhY3RlcmlzdGljIHR5cGU9IkRNQ2xpZW50Ij48Y2hhcmFjdGVyaXN0aWMgdHlwZT0iUHJvdmlkZXIiPjxjaGFyYWN0ZXJpc3RpYyB0eXBlPSJERU1PIE1ETSI+PGNoYXJhY3RlcmlzdGljIHR5cGU9IlBvbGwiPjxwYXJtIG5hbWU9Ik51bWJlck9mRmlyc3RSZXRyaWVzIiB2YWx1ZT0iOCIgZGF0YXR5cGU9ImludGVnZXIiIC8+PC9jaGFyYWN0ZXJpc3RpYz48L2NoYXJhY3RlcmlzdGljPjwvY2hhcmFjdGVyaXN0aWM+PC9jaGFyYWN0ZXJpc3RpYz48L3dhcC1wcm92aXNpb25pbmdkb2M+ + + + 0 + + + + + + ========================================================================= + + + + +### MDM - Device Management Flow (MS-MDM) + + ============================= Input Request ============================= + ----------- Input Header ----------- + POST /ManagementServer/MDM.svc?mode=Maintenance&Platform=WoA HTTP/2.0 + Host: mdmwindows.com + Accept: application/vnd.syncml.dm+xml, application/vnd.syncml.dm+wbxml, application/octet-stream + Accept-Charset: UTF-8 + Client-Request-Id: 0 + Content-Length: 991 + Content-Type: application/vnd.syncml.dm+xml + Ms-Cv: a/tCeBgffEqA5408.0.0.0 + User-Agent: MSFT OMA DM Client/1.2.0.1 + + + ----------- Input Body ----------- + + + + 1.2 + DM/1.2 + 1 + 1 + + https://mdmwindows.com/ManagementServer/MDM.svc + + + 1E08C6E95D8BB843B1278FF45BC60CC6 + + + + + 2 + 1201 + + + 3 + 1224 + + + com.microsoft/MDM/LoginStatus + + user + + + + 4 + + + ./DevInfo/DevId + + 1E08C6E95D8BB843B1278FF45BC60CC6 + + + + ./DevInfo/Man + + VMware, Inc. + + + + ./DevInfo/Mod + + VMware7,1 + + + + ./DevInfo/DmV + + 1.3 + + + + ./DevInfo/Lang + + en-US + + + + + + ========================================================================= + + + + + ============================= Output Response ============================= + ----------- Response Header ----------- + HTTP/1.1 200 OK + Content-Length: 1736 + Content-Type: application/vnd.syncml.dm+xml + + + ----------- Response Body ----------- + + + + + 1.2 + DM/1.2 + 1 + 1 + + 1E08C6E95D8BB843B1278FF45BC60CC6 + + + https://mdmwindows.com/ManagementServer/MDM.svc + + + + + 1 + 1 + 0 + SyncHdr + 200 + + + 2 + 1 + 2 + Alert + 200 + + + 3 + 1 + 3 + Alert + 200 + + + 4 + 1 + 4 + Replace + 200 + + + 5 + + + ./Vendor/MSFT/Personalization/DesktopImageUrl + + + chr + text/plain + + https://fleetdm.com/images/articles/fleet-4.24.0-cover-1600x900@2x.jpg + + + + 6 + + + ./Vendor/MSFT/Personalization/LockScreenImageUrl + + + chr + text/plain + + https://fleetdm.com/images/articles/fleet-4.24.0-cover-1600x900@2x.jpg + + + + + + ========================================================================= + + + 192.168.8.10 - - [30/Dec/2022:16:59:44 -0300] "POST /ManagementServer/MDM.svc?mode=Maintenance&Platform=WoA HTTP/2.0" 200 1400 + + + ============================= Input Request ============================= + ----------- Input Header ----------- + POST /ManagementServer/MDM.svc?mode=Maintenance&Platform=WoA HTTP/2.0 + Host: mdmwindows.com + Accept: application/vnd.syncml.dm+xml, application/vnd.syncml.dm+wbxml, application/octet-stream + Accept-Charset: UTF-8 + Client-Request-Id: 0 + Content-Length: 633 + Content-Type: application/vnd.syncml.dm+xml + Ms-Cv: a/tCeBgffEqA5408.0.0.0 + User-Agent: MSFT OMA DM Client/1.2.0.1 + + + ----------- Input Body ----------- + + + + 1.2 + DM/1.2 + 1 + 2 + + https://mdmwindows.com/ManagementServer/MDM.svc + + + 1E08C6E95D8BB843B1278FF45BC60CC6 + + + + + 1 + 1 + 0 + SyncHdr + 200 + + + 2 + 1 + 5 + Replace + 202 + + + 3 + 1 + 6 + Replace + 202 + + + + + ========================================================================= + + + + + ============================= Output Response ============================= + ----------- Response Header ----------- + HTTP/1.1 200 OK + Content-Type: application/vnd.syncml.dm+xml + Content-Length: 0 + + + ----------- Response Body ----------- + + ========================================================================= + + diff --git a/tools/windows-mdm/poc-mdm-server/certs/dev_cert_mdmwindows_com.key b/tools/windows-mdm/poc-mdm-server/certs/dev_cert_mdmwindows_com.key new file mode 100644 index 0000000000..2b98e1d218 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/certs/dev_cert_mdmwindows_com.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA1RuIhpHWDudu07FfJ2bTAzm8DaD5F9WSxC8cKm6OCttN7tIj +FzyayThorwTZ37+VeJMZ7/bP/bqxTkF7Y59geEsFlDnLvQWm2Hh4f79MV08cWx/T +B3Wqgk6jPfr0dS2sQZv+JiS6/OR0BCTpUPZwM7eQiN3SoPDZEibIaLt53ECVc8ze +mD8cHrNmCk8HIWOmhzP5UzM21pXy8JOo2aEql6iVBoUa0QVfJVX9EHDPnmJmeA6f +d0uDZTvi8YSj+GqcmcBmGTe/rbpfaJjPiaXce0bF2JKjmK38bjgK29fHoEZ+Scw3 +ul160fxMV1bBhIcaJt+uEQyOKkdqLYPZ+lZ+yQIDAQABAoIBAHbGfMZ8G/F8njGQ +53b/gVaH5D84W/0jxURg+XLQ4Yw9hOc56eL2nVLPhNEfhAuILVfhrRAo4O4LEu2J +46q31r3VGovt1pdIwiBerNKOnY8AAc7sIuNCesFb8PIHoB57UUnUFsfNqwZukhcJ +N50vbYP1qLIP6GhZNLNAOGzfKOFPfTA1hpgXkUAYFUC6D5DwIMNuxX/5QYjAw8fK +e0NsehjuzTb+HckazvB/B0SpPZyezQrpJU5AEWzxQgcL+Qx5BdcOPyR83vdH8PoJ +83uj4FhNA16dHmo87AGe6d2FncadCoLpxjcJgjctqcDnJFdAdPcJOcA76qYeDh4E +nkSali0CgYEA4POeRx1RnUI7YN1b4JwY8qTj8ucMTaZk/Kjb1BPXX5rqC6Vx7I4N +UNCLNvPn6U/1W9NXzijamyddxcNtHcy8jXqlYawOebE3MAZvayBlC4yPYCd230LJ +ADl5auQeNj2ktJbJwYgZQcwnMEu3SGYIMvy96pKdWilUenPbfO9pj/cCgYEA8oVr +W17vQJX+8hmItcnqEHoCFXi2hM/vKZCHF/MrknJcpANTo7rRQaxn3xIqZRkt6xJz +vp5yl20pzl/KDSMxLd4H51fKy6Fzk0hvFpdqbsxLidu+rf1o3Nice6WBLqz+XMtV +i/dTqvD9b+CVmnoVmwVHoAja2QTZCAQnLGqCNz8CgYEAwZ7PGFTS/7GXXEuLnmud +SZTFozhdraRP/ez1sbgWQ/MaCkYwJbUrHukxOm57qaUqAgyJ4ifl6W/b1bHdBK5J +iNkM6mHm37W6U7rmQeXTMzqb2d5+AbMBQRE3QdrxaixqzQmQxOR5INowzPAO5OD1 +o7VJXlMt3wH99ZwtSn7jdIcCgYEAzxEDfNwtwyNOrj8G7tAbPT4vEU4j6HnxZbe0 +4MoK5dsnJhKBE0aq7Dvb5CaKdA9vmUoD8Tkv9gKKs14uEdF+Z/8vGGNpDzwmhhZO +YyedBEUCKg6pW70GD6oS0a+aANRLyccCn6LomQdyHFfQ5Dhgwh9b7FQjJzBwbdu9 +5rp5u9kCgYA1KZWTkSxjUEycTNKgQz4MKFCEbYkm8B6lfAY5GYhTI1fsoympgd4d +RYmQUuhHrLHP4s85NOSoRdrMD8MiRyhHna+FoaqIEIW5ErMkNDM9SYs4TyrIxf7l +R42IvNH76CcJpcWYhQ1PCLdsbuyO9lMkXBylPMI1VPZkjUZqj/Ixfw== +-----END RSA PRIVATE KEY----- diff --git a/tools/windows-mdm/poc-mdm-server/certs/dev_cert_mdmwindows_com.pfx b/tools/windows-mdm/poc-mdm-server/certs/dev_cert_mdmwindows_com.pfx new file mode 100644 index 0000000000..ec91c8cc01 Binary files /dev/null and b/tools/windows-mdm/poc-mdm-server/certs/dev_cert_mdmwindows_com.pfx differ diff --git a/tools/windows-mdm/poc-mdm-server/certs/dev_cert_mdmwindows_com_cert.pem b/tools/windows-mdm/poc-mdm-server/certs/dev_cert_mdmwindows_com_cert.pem new file mode 100644 index 0000000000..1b9ce35969 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/certs/dev_cert_mdmwindows_com_cert.pem @@ -0,0 +1,27 @@ +Bag Attributes + localKeyID: 01 00 00 00 + friendlyName: mdmwindows.com +subject=CN = *.mdmwindows.com + +issuer=CN = *.mdmwindows.com + +-----BEGIN CERTIFICATE----- +MIIDPTCCAiWgAwIBAgIQGQpbyNZeY7hBkxKCbRRfaTANBgkqhkiG9w0BAQsFADAb +MRkwFwYDVQQDDBAqLm1kbXdpbmRvd3MuY29tMB4XDTIyMTIyODE0MTUxMloXDTMy +MTIyODE0MjUxMlowGzEZMBcGA1UEAwwQKi5tZG13aW5kb3dzLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBANUbiIaR1g7nbtOxXydm0wM5vA2g+RfV +ksQvHCpujgrbTe7SIxc8msk4aK8E2d+/lXiTGe/2z/26sU5Be2OfYHhLBZQ5y70F +pth4eH+/TFdPHFsf0wd1qoJOoz369HUtrEGb/iYkuvzkdAQk6VD2cDO3kIjd0qDw +2RImyGi7edxAlXPM3pg/HB6zZgpPByFjpocz+VMzNtaV8vCTqNmhKpeolQaFGtEF +XyVV/RBwz55iZngOn3dLg2U74vGEo/hqnJnAZhk3v626X2iYz4ml3HtGxdiSo5it +/G44CtvXx6BGfknMN7pdetH8TFdWwYSHGibfrhEMjipHai2D2fpWfskCAwEAAaN9 +MHswDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD +ATArBgNVHREEJDAigg5tZG13aW5kb3dzLmNvbYIQKi5tZG13aW5kb3dzLmNvbTAd +BgNVHQ4EFgQUsFD9edObvrbuLZopzjgGtpr8ZTQwDQYJKoZIhvcNAQELBQADggEB +AEEfa9BS75jG4D2fJ1/Q9Xn/SPsaAtwUYW+ilGCqYBfQ8lBmXGN8z8WETdw5xus3 +FGdIYtw8SKF5fp3TOJlNkiF0LNhAEvwDEkNCtOK9XpqTScjDi2WT1c+gJmPyHj7M ++vn9+gHFI7tUT+JImqU1I6tzD2OsZS5H1Vow+QwD3/DswSoUKM+zQreJGKaKLZqo +i6B/fdS+XkYWymwXmQiu+7D8RwTGEMIrfPFon90I9APrDhOmjiDa7L+xs7zRfT4J +fzIbHn867msNrZzwPAmf3fRhEk8cwHD5jfgnXuoVL4icPG57rUvCfX+QI9FfpjH4 +ZIbwI+HQg86S0hVwqhGs1RI= +-----END CERTIFICATE----- diff --git a/tools/windows-mdm/poc-mdm-server/go.mod b/tools/windows-mdm/poc-mdm-server/go.mod new file mode 100644 index 0000000000..eec8c9df4a --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/go.mod @@ -0,0 +1,10 @@ +module github.com/oscartbeaumont/windows_mdm + +go 1.12 + +require ( + github.com/ernesto-jimenez/httplogger v0.0.0-20220128121225-117514c3f345 + github.com/go-xmlfmt/xmlfmt v1.1.2 + github.com/gorilla/handlers v1.4.2 + github.com/gorilla/mux v1.7.3 +) diff --git a/tools/windows-mdm/poc-mdm-server/go.sum b/tools/windows-mdm/poc-mdm-server/go.sum new file mode 100644 index 0000000000..20420b983e --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/go.sum @@ -0,0 +1,8 @@ +github.com/ernesto-jimenez/httplogger v0.0.0-20220128121225-117514c3f345 h1:AZLrCR38RDhsyCQakz1UxCx72As18Ai5mObrKvT8DK8= +github.com/ernesto-jimenez/httplogger v0.0.0-20220128121225-117514c3f345/go.mod h1:pw+gaKQ52Cl/SrERU62yQAiWauPpLgKpuR1hkxwL4tM= +github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= +github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= +github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= +github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= diff --git a/tools/windows-mdm/poc-mdm-server/identity/identity.crt b/tools/windows-mdm/poc-mdm-server/identity/identity.crt new file mode 100644 index 0000000000..7a596cb359 Binary files /dev/null and b/tools/windows-mdm/poc-mdm-server/identity/identity.crt differ diff --git a/tools/windows-mdm/poc-mdm-server/identity/identity.key b/tools/windows-mdm/poc-mdm-server/identity/identity.key new file mode 100644 index 0000000000..0c01e31f61 Binary files /dev/null and b/tools/windows-mdm/poc-mdm-server/identity/identity.key differ diff --git a/tools/windows-mdm/poc-mdm-server/main.go b/tools/windows-mdm/poc-mdm-server/main.go new file mode 100644 index 0000000000..ce15abb84f --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/main.go @@ -0,0 +1,179 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/http/httputil" + "os" + + "github.com/go-xmlfmt/xmlfmt" + "github.com/gorilla/mux" +) + +// Code forked from https://github.com/oscartbeaumont/windows_mdm +// Global config, populated via Command line flags +var domain string +var deepLinkUserEmail string +var authPolicy string +var profileDir string +var staticDir string +var verbose bool + +func main() { + fmt.Println("Starting Windows MDM Demo Server") + + // Parse CMD flags. This populates the varibles defined above + flag.StringVar(&domain, "domain", "mdmwindows.com", "Your servers primary domain") + flag.StringVar(&deepLinkUserEmail, "dl-user-email", "demo@mdmwindows.com", "An email of the enrolling user when using the Deeplink ('/deeplink')") + flag.StringVar(&authPolicy, "auth-policy", "OnPremise", "An email of the enrolling user when using the Deeplink ('/deeplink')") + flag.StringVar(&profileDir, "mdm-profile-dir", "./profile", "The MDM policy directory contains the SyncML MDM profile commmands to enforce to enrolled devices") + flag.StringVar(&staticDir, "static-dir", "./static", "The directory to serve static files") + flag.BoolVar(&verbose, "verbose", false, "HTTP traffic dump") + flag.Parse() + + // Verify authPolicy is valid + if authPolicy != "Federated" && authPolicy != "OnPremise" { + panic("unsupported authpolicy") + } + + // Checking if profile directory exists + _, err := os.Stat(profileDir) + if err != nil { + if os.IsNotExist(err) { + panic("profile directory does not exists") + } else { + panic(err) + } + } + + // Checking if static directory exists + _, err = os.Stat(staticDir) + if err != nil { + if os.IsNotExist(err) { + panic("static directory does not exists") + } else { + panic(err) + } + } + + // Create HTTP request router + r := mux.NewRouter() + + //MS-MDE and MS-MDM endpoints + r.Path("/EnrollmentServer/Discovery.svc").Methods("GET", "POST").HandlerFunc(DiscoveryHandler) + r.Path("/EnrollmentServer/Policy.svc").Methods("POST").HandlerFunc(PolicyHandler) + r.Path("/EnrollmentServer/Enrollment.svc").Methods("POST").HandlerFunc(EnrollHandler) + r.Path("/ManagementServer/MDM.svc").Methods("POST").HandlerFunc(ManageHandler) + + //Static root endpoint + r.Path("/").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=UTF-8") + w.Write([]byte(`

FleetDM Windows MDM Demo Server

.
`)) + w.Write([]byte(`
`)) + }) + + //Static file serve + fileServer := http.FileServer(http.Dir(staticDir)) + r.PathPrefix("/").Handler(http.StripPrefix("/static", fileServer)) + + // Start HTTPS Server + fmt.Println("HTTPS server listening on port 443") + err = http.ListenAndServeTLS(":443", "./certs/dev_cert_mdmwindows_com_cert.pem", "./certs/dev_cert_mdmwindows_com.key", globalHandler(r)) + if err != nil { + panic(err) + } +} + +// drainBody reads all of bytes to memory and then returns two equivalent +// ReadClosers yielding the same bytes. +// +// It returns an error if the initial slurp of all bytes fails. It does not attempt +// to make the returned ReadClosers have identical error-matching behavior. +func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, body []byte, err error) { + if b == nil || b == http.NoBody { + // No copying needed. Preserve the magic sentinel meaning of NoBody. + return http.NoBody, http.NoBody, nil, nil + } + var buf bytes.Buffer + if _, err = buf.ReadFrom(b); err != nil { + return nil, b, nil, err + } + if err = b.Close(); err != nil { + return nil, b, nil, err + } + return io.NopCloser(&buf), io.NopCloser(bytes.NewReader(buf.Bytes())), buf.Bytes(), nil +} + +// global HTTP handler to log input and output https traffic +func globalHandler(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + if verbose { + // grabbing Input Header and Body + reqHeader, err := httputil.DumpRequest(r, false) + if err != nil { + panic(err) + } + + var bodyBytes []byte + reqBodySave := r.Body + if r.Body != nil { + reqBodySave, r.Body, bodyBytes, err = drainBody(r.Body) + if err != nil { + panic(err) + } + } + r.Body = reqBodySave + + var beautifiedReqBody string + if len(bodyBytes) > 0 { + beautifiedReqBody = xmlfmt.FormatXML(string(bodyBytes), " ", " ") + } + + fmt.Printf("\n\n============================= Input Request =============================\n") + fmt.Println("----------- Input Header -----------\n", string(reqHeader)) + if len(beautifiedReqBody) > 0 { + fmt.Println("----------- Input Body -----------\n", string(beautifiedReqBody)) + } else { + fmt.Printf("----------- Empty Input Body -----------\n") + } + fmt.Printf("=========================================================================\n\n\n") + } + + rec := httptest.NewRecorder() + h.ServeHTTP(rec, r) + + if verbose { + // grabbing Output Header and Body + var beautifiedResponseBody string + responseBody := rec.Body.Bytes() + if len(responseBody) > 0 { + beautifiedResponseBody = xmlfmt.FormatXML(string(responseBody), " ", " ") + } + + responseHeader, err := httputil.DumpResponse(rec.Result(), false) + if err != nil { + panic(err) + } + + fmt.Printf("\n\n============================= Output Response =============================\n") + fmt.Println("----------- Response Header -----------\n", string(responseHeader)) + if len(beautifiedResponseBody) > 0 { + fmt.Println("----------- Response Body -----------\n", string(beautifiedResponseBody)) + } else { + fmt.Printf("----------- Empty Response Body -----------\n") + } + fmt.Printf("=========================================================================\n\n\n") + } + + // we copy the captured response headers to our new response + for k, v := range rec.Header() { + w.Header()[k] = v + } + w.Write(rec.Body.Bytes()) + }) +} diff --git a/tools/windows-mdm/poc-mdm-server/mde_discovery.go b/tools/windows-mdm/poc-mdm-server/mde_discovery.go new file mode 100644 index 0000000000..e7730378d9 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/mde_discovery.go @@ -0,0 +1,65 @@ +package main + +import ( + "io/ioutil" + "net/http" + "regexp" + "strconv" + "strings" +) + +// DiscoveryHandler is the HTTP handler assosiated with the enrollment protocol's discovery endpoint. +func DiscoveryHandler(w http.ResponseWriter, r *http.Request) { + // Return HTTP Status 200 Ok when a HTTP GET request is received. + if r.Method == http.MethodGet { + w.WriteHeader(http.StatusOK) + return + } + + // Read The HTTP Request body + bodyRaw, err := ioutil.ReadAll(r.Body) + if err != nil { + panic(err) + } + body := string(bodyRaw) + + // Retrieve the MessageID From The Body For The Response + messageID := strings.Replace(strings.Replace(regexp.MustCompile(`[\s\S]*?<\/a:MessageID>`).FindStringSubmatch(body)[0], "", "", -1), "", "", -1) + + var extraParams = "" + if authPolicy == "Federated" { + extraParams += "https://" + domain + "/EnrollmentServer/Auth" + } + + // Create response payload + response := []byte(` + + + http://schemas.microsoft.com/windows/management/2012/01/enrollment/IDiscoveryService/DiscoverResponse + 8c6060c4-3d78-4d73-ae17-e8bce88426ee + + ` + messageID + ` + + + + + ` + authPolicy + ` + 4.0 + https://` + domain + `/EnrollmentServer/Policy.svc + https://` + domain + `/EnrollmentServer/Enrollment.svc + ` + extraParams + ` + + + + + `) + + // Return response body + w.Header().Set("Content-Type", "application/soap+xml; charset=utf-8") + w.Header().Set("Content-Length", strconv.Itoa(len(response))) + w.Write(response) +} diff --git a/tools/windows-mdm/poc-mdm-server/mde_enrollment.go b/tools/windows-mdm/poc-mdm-server/mde_enrollment.go new file mode 100644 index 0000000000..93e1162cb6 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/mde_enrollment.go @@ -0,0 +1,220 @@ +package main + +import ( + "crypto/rand" + "crypto/sha1" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "fmt" + "io/ioutil" + "math/big" + mathrand "math/rand" + "net/http" + "regexp" + "strconv" + "strings" + "time" +) + +// EnrollHandler is the HTTP handler assosiated with the enrollment protocol's enrollment endpoint. +func EnrollHandler(w http.ResponseWriter, r *http.Request) { + // Read The HTTP Request body + bodyRaw, err := ioutil.ReadAll(r.Body) + if err != nil { + panic(err) + } + body := string(bodyRaw) + + // Retrieve the MessageID From The Body For The Response + messageID := strings.Replace(strings.Replace(regexp.MustCompile(`[\s\S]*?<\/a:MessageID>`).FindStringSubmatch(body)[0], "", "", -1), "", "", -1) + + // Retrieve the BinarySecurityToken (which contains a Certificate Signing Request) From The Body For The Response + binarySecurityToken := strings.Replace(strings.Replace(regexp.MustCompile(`[\s\S]*?<\/wsse:BinarySecurityToken>`).FindStringSubmatch(body)[0], ``, "", -1), "", "", -1) + + // Retrieve the DeviceID From The Body For The Response + deviceID := strings.Replace(strings.Replace(regexp.MustCompile(`[\s\S]*?<\/ac:Value><\/ac:ContextItem>`).FindStringSubmatch(body)[0], ``, "", -1), "", "", -1) + + // Retrieve the EnrollmentType From The Body For The Response + enrollmentType := strings.Replace(strings.Replace(regexp.MustCompile(`[\s\S]*?<\/ac:Value><\/ac:ContextItem>`).FindStringSubmatch(body)[0], ``, "", -1), "", "", -1) + + /* Sign binary security token */ + // Load raw Root CA + rootCertificateDer, err := ioutil.ReadFile("./identity/identity.crt") + if err != nil { + panic(err) + } + rootPrivateKeyDer, err := ioutil.ReadFile("./identity/identity.key") + if err != nil { + panic(err) + } + + // Convert the raw Root CA cert & key to parsed version + rootCert, err := x509.ParseCertificate(rootCertificateDer) + if err != nil { + panic(err) + } + + rootPrivateKey, err := x509.ParsePKCS1PrivateKey(rootPrivateKeyDer) + if err != nil { + panic(err) + } + + // Decode Base64 + csrRaw, err := base64.StdEncoding.DecodeString(binarySecurityToken) + if err != nil { + panic(err) + } + + // Decode and verify CSR + csr, err := x509.ParseCertificateRequest(csrRaw) + if err != nil { + panic(err) + } + if err = csr.CheckSignature(); err != nil { + panic(err) + } + + // Create client identity certificate + NotBefore1 := time.Now().Add(time.Duration(mathrand.Int31n(120)) * -time.Minute) // This randomises the creation time a bit for added security (Recommended by x509 signing article not the MDM spec) + clientCertificate := &x509.Certificate{ + Signature: csr.Signature, + SignatureAlgorithm: csr.SignatureAlgorithm, + PublicKeyAlgorithm: csr.PublicKeyAlgorithm, + PublicKey: csr.PublicKey, + SerialNumber: big.NewInt(2), + Issuer: rootCert.Issuer, + Subject: pkix.Name{ + CommonName: deviceID, + }, // The Subject is not used from the CSR because the characters in it are causing issues. + NotBefore: NotBefore1, + NotAfter: NotBefore1.Add(365 * 24 * time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + + // Sign certificate with the identity + clientCRTRaw, err := x509.CreateCertificate(rand.Reader, clientCertificate, rootCert, csr.PublicKey, rootPrivateKey) + if err != nil { + panic(err) + } + + // Note: SHA-1 Hash OID is deprecated + + // Fingerprint (SHA-1 hash) of client certificate + h := sha1.New() + h.Write(clientCRTRaw) + signedClientCertFingerprint := strings.ToUpper(fmt.Sprintf("%x", h.Sum(nil))) // TODO: Cleanup -> This line is probally messer than it needs to be + + // Fingerprint (SHA-1 hash) of client certificate + h2 := sha1.New() + h2.Write(rootCertificateDer) + identityCertFingerprint := strings.ToUpper(fmt.Sprintf("%x", h2.Sum(nil))) // TODO: Cleanup -> This line is probally messer than it needs to be + + // Determain Certstore + certStore := "User" + if enrollmentType == "Device" { + certStore = "System" + } + + // End Sign binary security token + + // Generate WAP provisioning profile for inside the payload + wapProvisionProfile := ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ` + + wapProvisionProfileRaw := []byte(strings.ReplaceAll(strings.ReplaceAll(wapProvisionProfile, "\n", ""), "\t", "")) + + // Create response payload + response := []byte(` + + + http://schemas.microsoft.com/windows/pki/2009/01/enrollment/RSTRC/wstep + ` + messageID + ` + + + 2018-11-30T00:32:59.420Z + 2018-12-30T00:37:59.420Z + + + + + + + http://schemas.microsoft.com/5.0.0.0/ConfigurationManager/Enrollment/DeviceEnrollmentToken + + + + ` + base64.StdEncoding.EncodeToString(wapProvisionProfileRaw) + ` + + + 0 + + + + + `) + + // Return response body + w.Header().Set("Content-Type", "application/soap+xml; charset=utf-8") + w.Header().Set("Content-Length", strconv.Itoa(len(response))) + w.Write(response) +} diff --git a/tools/windows-mdm/poc-mdm-server/mde_policy.go b/tools/windows-mdm/poc-mdm-server/mde_policy.go new file mode 100644 index 0000000000..2ccb6aa4f9 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/mde_policy.go @@ -0,0 +1,65 @@ +package main + +import ( + "io/ioutil" + "net/http" + "regexp" + "strconv" + "strings" +) + +// PolicyHandler is the HTTP handler assosiated with the enrollment protocol's policy endpoint. +func PolicyHandler(w http.ResponseWriter, r *http.Request) { + // Read The HTTP Request body + bodyRaw, err := ioutil.ReadAll(r.Body) + if err != nil { + panic(err) + } + body := string(bodyRaw) + + // Retrieve the MessageID From The Body For The Response + messageID := strings.Replace(strings.Replace(regexp.MustCompile(`[\s\S]*?<\/a:MessageID>`).FindStringSubmatch(body)[0], "", "", -1), "", "", -1) + + // Create response payload + response := []byte(` + + + http://schemas.microsoft.com/windows/pki/2009/01/enrollmentpolicy/IPolicy/GetPoliciesResponse + ` + messageID + ` + + + + + + + + 3 + + 2048 + + + + + + + + + + 1.3.6.1.4.1.311.20.2 + 1 + 5 + Certificate Template Name + + + + + `) + + // Return response body + w.Header().Set("Content-Type", "application/soap+xml; charset=utf-8") + w.Header().Set("Content-Length", strconv.Itoa(len(response))) + w.Write(response) +} diff --git a/tools/windows-mdm/poc-mdm-server/mdm_manage.go b/tools/windows-mdm/poc-mdm-server/mdm_manage.go new file mode 100644 index 0000000000..2177ed87a8 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/mdm_manage.go @@ -0,0 +1,299 @@ +package main + +import ( + "encoding/xml" + "fmt" + "io/ioutil" + "net" + "net/http" + "os" + "strconv" + "strings" +) + +// SyncML XML Parsing Types - This needs to be improved +type SyncMLHeader struct { + DTD string `xml:"VerDTD"` + Version string `xml:"VerProto"` + SessionID int `xml:"SessionID"` + MsgID int `xml:"MsgID"` + Target string `xml:"Target>LocURI"` + Source string `xml:"Source>LocURI"` + MaxMsgSize int `xml:"Meta>A:MaxMsgSize"` +} + +type SyncMLCommandMeta struct { + XMLinfo string `xml:"xmlns,attr"` + Type string `xml:"Type"` +} + +type SyncMLCommandItem struct { + Meta SyncMLCommandMeta `xml:"Meta"` + Source string `xml:"Source>LocURI"` + Data string `xml:"Data"` +} + +type SyncMLCommand struct { + XMLName xml.Name + CmdID int `xml:",omitempty"` + MsgRef string `xml:",omitempty"` + CmdRef string `xml:",omitempty"` + Cmd string `xml:",omitempty"` + Target string `xml:"Target>LocURI"` + Source string `xml:"Source>LocURI"` + Data string `xml:",omitempty"` + Item []SyncMLCommandItem `xml:",any"` +} + +type SyncMLBody struct { + Item []SyncMLCommand `xml:",any"` +} + +type SyncMLMessage struct { + XMLinfo string `xml:"xmlns,attr"` + Header SyncMLHeader `xml:"SyncHdr"` + Body SyncMLBody `xml:"SyncBody"` +} + +// Returns the MDM configuration profile SyncML content from profile dir +func getConfigurationProfiles(cmdIDstart int) string { + + files, err := ioutil.ReadDir(profileDir) + if err != nil { + panic(err) + } + + var syncmlCommands string + for _, file := range files { + cmdIDstart++ + + fmt.Printf("\n--------- Command Request %d ---------\n", cmdIDstart) + fmt.Printf("Command payload retrieved from file %s\n", file.Name()) + fileContent, err := os.ReadFile(profileDir + "/" + file.Name()) + if err != nil { + panic(err) + } + + syncmlCommands += strings.Replace(string(fileContent), "xxcmdidxx", strconv.Itoa(cmdIDstart), -1) + "\n" + } + + //input sanitization + sanitizedSyncmlOutput := strings.ReplaceAll(syncmlCommands, "\r\n", "\n") + if len(sanitizedSyncmlOutput) > 0 { + fmt.Print("\n") + } + return sanitizedSyncmlOutput +} + +// Alert Command IDs +const DeviceUnenrollmentID = "1226" +const HostInitMessageID = "1201" + +// Checks if body contains a DM device unrollment SyncML message +func isDeviceUnenrollmentMessage(body SyncMLBody) bool { + for _, element := range body.Item { + if element.Data == DeviceUnenrollmentID { + return true + } + } + + return false +} + +// Checks if body contains a DM session initialization SyncML message sent by device +func isSessionInitializationMessage(body SyncMLBody) bool { + isUnenrollMessage := isDeviceUnenrollmentMessage(body) + + for _, element := range body.Item { + if element.Data == HostInitMessageID && !isUnenrollMessage { + return true + } + } + + return false +} + +// Get IP address from HTTP Request +func getIP(r *http.Request) (string, error) { + + //Get IP from the X-REAL-IP header + ip := r.Header.Get("X-REAL-IP") + netIP := net.ParseIP(ip) + if netIP != nil { + return ip, nil + } + + //Get IP from X-FORWARDED-FOR header + ips := r.Header.Get("X-FORWARDED-FOR") + splitIps := strings.Split(ips, ",") + for _, ip := range splitIps { + netIP := net.ParseIP(ip) + if netIP != nil { + return ip, nil + } + } + + //Get IP from RemoteAddr + ip, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + return "", err + } + netIP = net.ParseIP(ip) + if netIP != nil { + return ip, nil + } + return "", fmt.Errorf("no valid ip found") +} + +// ManageHandler is the HTTP handler assosiated with the mdm management service. This is what constantly pushes configuration profiles to the device. +func ManageHandler(w http.ResponseWriter, r *http.Request) { + // Read The HTTP Request body + bodyRaw, err := ioutil.ReadAll(r.Body) + if err != nil { + panic(err) + } + + var responseRaw []byte + var response string + var message SyncMLMessage + + //Parsing input SyncML message + if err := xml.Unmarshal(bodyRaw, &message); err != nil { + panic(err) + } + + // Cmd ID variable with getNextCmdID() increment statement hack + CmdID := 0 + getNextCmdID := func(i *int) string { *i++; return strconv.Itoa(*i) } + + // Retrieve the MessageID From The Body For The Response + DeviceID := message.Header.Source + + // Retrieve the SessionID From The Body For The Response + SessionID := message.Header.SessionID + + // Retrieve the MsgID From The Body For The Response + MsgID := message.Header.MsgID + + //Only handle DM session initialization SyncML message sent by device + + // Retrieve the IP Address from calling device + ipAddressBytes, err := getIP(r) + if err != nil { + panic(err) + } + + //Checking the SyncML message types + if isSessionInitializationMessage(message.Body) { + + fmt.Printf("\n========= New OMA-DM session from Windows Host %s (%s) =========\n", string(ipAddressBytes), r.UserAgent()) + + // Create response payload - MDM syncml configuration profiles commands will be enforced here + response = ` + + + + 1.2 + DM/1.2 + ` + strconv.Itoa(SessionID) + ` + ` + strconv.Itoa(MsgID) + ` + + ` + DeviceID + ` + + + https://` + domain + `/ManagementServer/MDM.svc + + + + + ` + getNextCmdID(&CmdID) + ` + ` + strconv.Itoa(MsgID) + ` + 0 + SyncHdr + 200 + + + ` + getNextCmdID(&CmdID) + ` + ` + strconv.Itoa(MsgID) + ` + 2 + Alert + 200 + + + ` + getNextCmdID(&CmdID) + ` + ` + strconv.Itoa(MsgID) + ` + 3 + Alert + 200 + + + ` + getNextCmdID(&CmdID) + ` + ` + strconv.Itoa(MsgID) + ` + 4 + Replace + 200 + + ` + getConfigurationProfiles(CmdID) + ` + + + ` + + // Return response + responseRaw = []byte(strings.ReplaceAll(strings.ReplaceAll(response, "\n", ""), "\t", "")) + w.Header().Set("Content-Type", "application/vnd.syncml.dm+xml") + w.Header().Set("Content-Length", strconv.Itoa(len(response))) + w.Write(responseRaw) + } else { + + //Log if this is a device unrollment message + if isDeviceUnenrollmentMessage(message.Body) { + fmt.Printf("\nWindows Device at %s was removed from MDM!\n\n", string(ipAddressBytes)) + } + + //Acknowledge the HTTP request sent by device + response = ` + + + + 1.2 + DM/1.2 + ` + strconv.Itoa(SessionID) + ` + ` + strconv.Itoa(MsgID) + ` + + ` + DeviceID + ` + + + https://` + domain + `/ManagementServer/MDM.svc + + + + + ` + getNextCmdID(&CmdID) + ` + ` + strconv.Itoa(MsgID) + ` + 0 + SyncHdr + 200 + + + + ` + + // Dump Response Payload + for _, element := range message.Body.Item { + if element.XMLName.Local != "Final" && element.Cmd != "SyncHdr" { + commandStr, _ := xml.MarshalIndent(element, "", " ") + if element.XMLName.Local == "Status" { + fmt.Printf("\n--------- Command Response %s - Return Code: %s ---------\n", element.CmdRef, element.Data) + } else { + fmt.Printf("%s\n", commandStr) + } + } + } + + // Return response body + responseRaw = []byte(strings.ReplaceAll(strings.ReplaceAll(response, "\n", ""), "\t", "")) + w.Header().Set("Content-Type", "application/vnd.syncml.dm+xml") + w.Header().Set("Content-Length", strconv.Itoa(len(response))) + w.Write(responseRaw) + } +} diff --git a/tools/windows-mdm/poc-mdm-server/patch/patch.go b/tools/windows-mdm/poc-mdm-server/patch/patch.go new file mode 100644 index 0000000000..a4a5258ae5 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/patch/patch.go @@ -0,0 +1,62 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "path" +) + +var ( + sourceFilePath = path.Join(os.Getenv("GOROOT"), "src", "encoding", "asn1", "asn1.go") + patchedFilePath = path.Join(os.Getenv("GOROOT"), "src", "encoding", "asn1", "asn1-patched.go") +) + +func main() { + // Check for the GOROOT env varible. Should be set by Go automatically + if os.Getenv("GOROOT") == "" { + panic("Plese set your GOROOT path") + } + + // Load The file and create a scanner + file, err := os.Open(sourceFilePath) + if err != nil { + panic(err) + } + scanner := bufio.NewScanner(file) + + // Open Output File + out, err2 := os.Create(patchedFilePath) + if err2 != nil { + panic(err2) + } + + // Loop of each line of the file checking it + for scanner.Scan() { + out.Write(scanner.Bytes()) + out.Write([]byte("\n")) + + if scanner.Text() == " b == '?' ||" { + scanner.Scan() + if scanner.Text() != " b == '!' || // Windows MDM Certificate Parsing Patch" { + out.Write([]byte(" b == '!' || // Windows MDM Certificate Parsing Patch\n")) + out.Write([]byte(" b == 0 || // Windows MDM Certificate Parsing Patch\n")) + } + + out.Write(scanner.Bytes()) + out.Write([]byte("\n")) + } + } + + // Close writters + file.Close() + out.Close() + + // Replace the main file with the patched one + if err := os.Rename(patchedFilePath, sourceFilePath); err != nil { + panic(err) + } + + // Success + fmt.Println("Patch Applied To Your Go Sources! Please be carefull with the certs you are loading as they could cause undesired outcomes in the future.") +} diff --git a/tools/windows-mdm/poc-mdm-server/profile/add_download_msi.xml b/tools/windows-mdm/poc-mdm-server/profile/add_download_msi.xml new file mode 100644 index 0000000000..2e5fc95f49 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/profile/add_download_msi.xml @@ -0,0 +1,8 @@ + + xxcmdidxx + + + ./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413BF7-7D99-482E-A7FB-C6616CC871FC%7D/DownloadInstall + + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/profile/exec_install_msi.xml b/tools/windows-mdm/poc-mdm-server/profile/exec_install_msi.xml new file mode 100644 index 0000000000..f2232fe4ec --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/profile/exec_install_msi.xml @@ -0,0 +1,31 @@ + + xxcmdidxx + + + ./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413BF7-7D99-482E-A7FB-C6616CC871FC%7D/DownloadInstall + + + xml + text/plain + + + <MsiInstallJob id="{90413BF7-7D99-482E-A7FB-C6616CC871FC}"> + <Product Version="1.0.0"> + <Download> + <ContentURLList> + <ContentURL>https://mdmwindows.com/static/fleet-osquery.msi</ContentURL> + </ContentURLList> + </Download> + <Validation> + <FileHash>3B9FD63248465A51500D41DECC794D1149506EB48EEF9D7A733516B482D16ABB</FileHash> + </Validation> + <Enforcement> + <CommandLine>/quiet</CommandLine> + <RetryCount>3</RetryCount> + <RetryInterval>5</RetryInterval> + </Enforcement> + </Product> + </MsiInstallJob> + + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/add_download_msi.xml b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/add_download_msi.xml new file mode 100644 index 0000000000..2e5fc95f49 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/add_download_msi.xml @@ -0,0 +1,8 @@ + + xxcmdidxx + + + ./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413BF7-7D99-482E-A7FB-C6616CC871FC%7D/DownloadInstall + + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/exec_install_msi.xml b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/exec_install_msi.xml new file mode 100644 index 0000000000..deb3853a31 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/exec_install_msi.xml @@ -0,0 +1,13 @@ + + xxcmdidxx + + + ./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413BF7-7D99-482E-A7FB-C6616CC871FC%7D/DownloadInstall + + + xml + text/plain + + <MsiInstallJob id="{90413BF7-7D99-482E-A7FB-C6616CC871FC}"><Product Version="1.4.0"><Download><ContentURLList><ContentURL>https://mdmwindows.com/static/fleet-osquery.msi</ContentURL></ContentURLList></Download><Validation><FileHash>3B9FD63248465A51500D41DECC794D1149506EB48EEF9D7A733516B482D16ABB</FileHash></Validation><Enforcement><CommandLine>/quiet</CommandLine><RetryCount>5</RetryCount><RetryInterval>3</RetryInterval></Enforcement></Product></MsiInstallJob> + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_csp_versions.xml b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_csp_versions.xml new file mode 100644 index 0000000000..a2eb7ac169 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_csp_versions.xml @@ -0,0 +1,8 @@ + + xxcmdidxx + + + ./Device/Vendor/MSFT/DeviceManageability/Capabilities/CSPVersions + + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_device_name.xml b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_device_name.xml new file mode 100644 index 0000000000..d08a026e15 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_device_name.xml @@ -0,0 +1,8 @@ + + xxcmdidxx + + + ./DevDetail/Ext/Microsoft/DeviceName + + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_hardware_version.xml b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_hardware_version.xml new file mode 100644 index 0000000000..8f1eba04e5 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_hardware_version.xml @@ -0,0 +1,8 @@ + + xxcmdidxx + + + ./DevDetail/HwV + + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_local_time.xml b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_local_time.xml new file mode 100644 index 0000000000..b0ca903df5 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_local_time.xml @@ -0,0 +1,8 @@ + + xxcmdidxx + + + ./DevDetail/Ext/Microsoft/LocalTime + + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_os_platform.xml b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_os_platform.xml new file mode 100644 index 0000000000..70da07b6b6 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_os_platform.xml @@ -0,0 +1,8 @@ + + xxcmdidxx + + + ./DevDetail/Ext/Microsoft/OSPlatform + + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_software_version.xml b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_software_version.xml new file mode 100644 index 0000000000..5ed47c5407 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_software_version.xml @@ -0,0 +1,8 @@ + + xxcmdidxx + + + ./DevDetail/SwV + + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_total_storage.xml b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_total_storage.xml new file mode 100644 index 0000000000..1220774e36 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/get_total_storage.xml @@ -0,0 +1,8 @@ + + xxcmdidxx + + + ./DevDetail/Ext/Microsoft/TotalStorage + + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/replace_personalization_desktop_image_url.xml b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/replace_personalization_desktop_image_url.xml new file mode 100644 index 0000000000..8cf6d4d389 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/replace_personalization_desktop_image_url.xml @@ -0,0 +1,13 @@ + + xxcmdidxx + + + ./Vendor/MSFT/Personalization/DesktopImageUrl + + + chr + text/plain + + https://fleetdm.com/images/articles/fleet-4.24.0-cover-1600x900@2x.jpg + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/replace_personalization_lock_screen_image_url.xml b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/replace_personalization_lock_screen_image_url.xml new file mode 100644 index 0000000000..3ea18697bc --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/sample_syncml_commands/replace_personalization_lock_screen_image_url.xml @@ -0,0 +1,13 @@ + + xxcmdidxx + + + ./Vendor/MSFT/Personalization/LockScreenImageUrl + + + chr + text/plain + + https://fleetdm.com/images/articles/fleet-4.24.0-cover-1600x900@2x.jpg + + \ No newline at end of file diff --git a/tools/windows-mdm/poc-mdm-server/static/hello.txt b/tools/windows-mdm/poc-mdm-server/static/hello.txt new file mode 100644 index 0000000000..7bd2398d95 --- /dev/null +++ b/tools/windows-mdm/poc-mdm-server/static/hello.txt @@ -0,0 +1 @@ +world \ No newline at end of file