mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 21:47:20 +00:00
Co-authored-by: Jordan Montgomery <elijah.jordan.montgomery@gmail.com> Co-authored-by: Magnus Jensen <magnus@fleetdm.com>
160 lines
4.3 KiB
Go
160 lines
4.3 KiB
Go
package service
|
|
|
|
import (
|
|
"crypto/x509"
|
|
_ "embed"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
"testing"
|
|
"unicode/utf16"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/mdm/scep/depot"
|
|
filedepot "github.com/fleetdm/fleet/v4/server/mdm/scep/depot/file"
|
|
scepserver "github.com/fleetdm/fleet/v4/server/mdm/scep/server"
|
|
kitlog "github.com/go-kit/log"
|
|
"github.com/gorilla/mux"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
//go:embed testdata/testca/ca.key
|
|
var caKey []byte
|
|
|
|
//go:embed testdata/testca/ca.pem
|
|
var caPem []byte
|
|
|
|
// NewTestSCEPServer creates a new SCEP server for testing purposes. The depotPath should be the
|
|
// relative path to the directory where the test CA files are stored (e.g., "./testdata/testca")
|
|
func NewTestSCEPServer(t *testing.T) *httptest.Server {
|
|
t.Helper()
|
|
|
|
caDir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(caDir, "ca.key"), caKey, 0o644); err != nil {
|
|
t.Fatalf("failed to write ca.key: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(caDir, "ca.pem"), caPem, 0o644); err != nil {
|
|
t.Fatalf("failed to write ca.pem: %v", err)
|
|
}
|
|
|
|
var err error
|
|
var certDepot depot.Depot // cert storage
|
|
t.Cleanup(func() {
|
|
_ = os.Remove(caDir)
|
|
})
|
|
certDepot, err = filedepot.NewFileDepot(caDir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
certDepot = &noopDepot{certDepot}
|
|
crt, key, err := certDepot.CA([]byte{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var svc scepserver.Service // scep service
|
|
svc, err = scepserver.NewService(crt[0], key, scepserver.NopCSRSigner())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
logger := kitlog.NewNopLogger()
|
|
e := scepserver.MakeServerEndpoints(svc)
|
|
scepHandler := scepserver.MakeHTTPHandler(e, svc, logger)
|
|
r := mux.NewRouter()
|
|
r.Handle("/scep", scepHandler)
|
|
server := httptest.NewServer(r)
|
|
t.Cleanup(server.Close)
|
|
return server
|
|
}
|
|
|
|
type noopDepot struct{ depot.Depot }
|
|
|
|
func (d *noopDepot) Put(_ string, _ *x509.Certificate) error {
|
|
return nil
|
|
}
|
|
|
|
//go:embed testdata/mscep_admin_cache_full.html
|
|
var mscepAdminCacheFull []byte
|
|
|
|
//go:embed testdata/mscep_admin_insufficient_permissions.html
|
|
var mscepAdminInsufficientPermissions []byte
|
|
|
|
//go:embed testdata/mscep_admin_password.html
|
|
var mscepAdminPassword []byte
|
|
|
|
func NewTestNDESAdminServer(t *testing.T, responseTemplate string, responseStatus int) *httptest.Server {
|
|
t.Helper()
|
|
|
|
var returnPage func() []byte
|
|
returnStatus := http.StatusOK
|
|
ndesAdminServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(returnStatus)
|
|
if returnStatus == http.StatusOK {
|
|
_, err := w.Write(returnPage())
|
|
require.NoError(t, err)
|
|
}
|
|
}))
|
|
t.Cleanup(ndesAdminServer.Close)
|
|
|
|
// We need to convert the HTML page to UTF-16 encoding, which is used by Windows servers
|
|
convertHTML := func(html []byte) []byte {
|
|
datUTF16, err := utf16FromString(string(html))
|
|
require.NoError(t, err)
|
|
byteData := make([]byte, len(datUTF16)*2)
|
|
for i, v := range datUTF16 {
|
|
binary.LittleEndian.PutUint16(byteData[i*2:], v)
|
|
}
|
|
return byteData
|
|
}
|
|
|
|
switch responseTemplate {
|
|
case "mscep_admin_cache_full":
|
|
// Catch ths issue when NDES password cache is full
|
|
returnPage = func() []byte {
|
|
return convertHTML(mscepAdminCacheFull)
|
|
}
|
|
case "mscep_admin_insufficient_permissions":
|
|
// Catch this issue when account has insufficient permissions
|
|
returnPage = func() []byte {
|
|
return convertHTML(mscepAdminInsufficientPermissions)
|
|
}
|
|
case "mscep_admin_password":
|
|
// All good, NDES admin page loads correctly
|
|
returnPage = func() []byte {
|
|
return convertHTML(mscepAdminPassword)
|
|
}
|
|
default:
|
|
returnPage = func() []byte {
|
|
return []byte{}
|
|
}
|
|
}
|
|
|
|
return ndesAdminServer
|
|
}
|
|
|
|
func NewTestDynamicChallengeServer(t *testing.T) *httptest.Server {
|
|
t.Helper()
|
|
|
|
dynamicChallengeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
fmt.Println(r.URL.Path)
|
|
_, err := w.Write([]byte("dynamic challenge"))
|
|
require.NoError(t, err)
|
|
}))
|
|
t.Cleanup(dynamicChallengeServer.Close)
|
|
|
|
return dynamicChallengeServer
|
|
}
|
|
|
|
// utf16FromString returns the UTF-16 encoding of the UTF-8 string s, with a terminating NUL added.
|
|
// If s contains a NUL byte at any location, it returns (nil, syscall.EINVAL).
|
|
func utf16FromString(s string) ([]uint16, error) {
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] == 0 {
|
|
return nil, syscall.EINVAL
|
|
}
|
|
}
|
|
return utf16.Encode([]rune(s + "\x00")), nil
|
|
}
|