2022-12-05 16:35:45 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-01-25 19:44:29 +00:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2022-12-05 16:35:45 +00:00
|
|
|
|
2023-01-25 19:44:29 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/pkg/fleethttp"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
2022-12-05 16:35:45 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
2023-01-25 19:44:29 +00:00
|
|
|
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
|
2022-12-05 16:35:45 +00:00
|
|
|
)
|
|
|
|
|
2023-01-25 19:44:29 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// GET /mdm/apple
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2022-12-05 16:35:45 +00:00
|
|
|
type getAppleMDMResponse struct {
|
|
|
|
*fleet.AppleMDM
|
|
|
|
Err error `json:"error,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r getAppleMDMResponse) error() error { return r.Err }
|
|
|
|
|
2022-12-27 14:26:59 +00:00
|
|
|
func getAppleMDMEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
2022-12-05 16:35:45 +00:00
|
|
|
appleMDM, err := svc.GetAppleMDM(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return getAppleMDMResponse{Err: err}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return getAppleMDMResponse{AppleMDM: appleMDM}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (svc *Service) GetAppleMDM(ctx context.Context) (*fleet.AppleMDM, error) {
|
|
|
|
if err := svc.authz.Authorize(ctx, &fleet.AppleMDM{}, fleet.ActionRead); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// if there is no apple mdm config, fail with a 404
|
|
|
|
if !svc.config.MDM.IsAppleAPNsSet() {
|
|
|
|
return nil, notFoundError{}
|
|
|
|
}
|
|
|
|
|
|
|
|
apns, _, _, err := svc.config.MDM.AppleAPNs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
appleMDM := &fleet.AppleMDM{
|
|
|
|
CommonName: apns.Leaf.Subject.CommonName,
|
|
|
|
Issuer: apns.Leaf.Issuer.CommonName,
|
|
|
|
RenewDate: apns.Leaf.NotAfter,
|
|
|
|
}
|
|
|
|
if apns.Leaf.SerialNumber != nil {
|
|
|
|
appleMDM.SerialNumber = apns.Leaf.SerialNumber.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
return appleMDM, nil
|
|
|
|
}
|
2022-12-12 20:45:53 +00:00
|
|
|
|
2023-01-25 19:44:29 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// GET /mdm/apple_bm
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2022-12-12 20:45:53 +00:00
|
|
|
type getAppleBMResponse struct {
|
|
|
|
*fleet.AppleBM
|
|
|
|
Err error `json:"error,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r getAppleBMResponse) error() error { return r.Err }
|
|
|
|
|
2022-12-27 14:26:59 +00:00
|
|
|
func getAppleBMEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
2022-12-12 20:45:53 +00:00
|
|
|
appleBM, err := svc.GetAppleBM(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return getAppleBMResponse{Err: err}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return getAppleBMResponse{AppleBM: appleBM}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (svc *Service) GetAppleBM(ctx context.Context) (*fleet.AppleBM, error) {
|
|
|
|
// skipauth: No authorization check needed due to implementation returning
|
|
|
|
// only license error.
|
|
|
|
svc.authz.SkipAuthorization(ctx)
|
|
|
|
|
|
|
|
return nil, fleet.ErrMissingLicense
|
|
|
|
}
|
2023-01-25 19:44:29 +00:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// GET /mdm/apple/request_csr
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
type requestMDMAppleCSRRequest struct {
|
|
|
|
EmailAddress string `json:"email_address"`
|
|
|
|
Organization string `json:"organization"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type requestMDMAppleCSRResponse struct {
|
|
|
|
*fleet.AppleCSR
|
|
|
|
Err error `json:"error,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r requestMDMAppleCSRResponse) error() error { return r.Err }
|
|
|
|
|
|
|
|
func requestMDMAppleCSREndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
|
|
|
req := request.(*requestMDMAppleCSRRequest)
|
|
|
|
|
|
|
|
csr, err := svc.RequestMDMAppleCSR(ctx, req.EmailAddress, req.Organization)
|
|
|
|
if err != nil {
|
|
|
|
return requestMDMAppleCSRResponse{Err: err}, nil
|
|
|
|
}
|
|
|
|
return requestMDMAppleCSRResponse{
|
|
|
|
AppleCSR: csr,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (svc *Service) RequestMDMAppleCSR(ctx context.Context, email, org string) (*fleet.AppleCSR, error) {
|
|
|
|
if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionWrite); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := fleet.ValidateEmail(email); err != nil {
|
|
|
|
if strings.TrimSpace(email) == "" {
|
|
|
|
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("email_address", "missing email address"))
|
|
|
|
}
|
|
|
|
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("email_address", fmt.Sprintf("invalid email address: %v", err)))
|
|
|
|
}
|
|
|
|
if strings.TrimSpace(org) == "" {
|
|
|
|
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("organization", "missing organization"))
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the raw SCEP CA cert and key (creating before the CSR signing
|
|
|
|
// request so that nothing can fail after the request is made, except for the
|
|
|
|
// network during the response of course)
|
|
|
|
scepCACert, scepCAKey, err := apple_mdm.NewSCEPCACertKey()
|
|
|
|
if err != nil {
|
|
|
|
return nil, ctxerr.Wrap(ctx, err, "generate SCEP CA cert and key")
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the APNs CSR
|
|
|
|
apnsCSR, apnsKey, err := apple_mdm.GenerateAPNSCSRKey(email, org)
|
|
|
|
if err != nil {
|
|
|
|
return nil, ctxerr.Wrap(ctx, err, "generate APNs CSR")
|
|
|
|
}
|
|
|
|
|
|
|
|
// request the signed APNs CSR from fleetdm.com
|
|
|
|
client := fleethttp.NewClient(fleethttp.WithTimeout(10 * time.Second))
|
|
|
|
if err := apple_mdm.GetSignedAPNSCSR(client, apnsCSR); err != nil {
|
|
|
|
err = fleet.NewUserMessageError(fmt.Errorf("FleetDM CSR request failed: %w", err), http.StatusBadGateway)
|
|
|
|
return nil, ctxerr.Wrap(ctx, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PEM-encode the cert and keys
|
|
|
|
scepCACertPEM := apple_mdm.EncodeCertPEM(scepCACert)
|
|
|
|
scepCAKeyPEM := apple_mdm.EncodePrivateKeyPEM(scepCAKey)
|
|
|
|
apnsKeyPEM := apple_mdm.EncodePrivateKeyPEM(apnsKey)
|
|
|
|
|
|
|
|
return &fleet.AppleCSR{
|
|
|
|
APNsKey: apnsKeyPEM,
|
|
|
|
SCEPCert: scepCACertPEM,
|
|
|
|
SCEPKey: scepCAKeyPEM,
|
|
|
|
}, nil
|
|
|
|
}
|