-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3fcbd3b
commit 0c46445
Showing
7 changed files
with
178 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package nanomdm | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/micromdm/nanomdm/log" | ||
"github.com/micromdm/nanomdm/log/ctxlog" | ||
"github.com/micromdm/nanomdm/mdm" | ||
"github.com/micromdm/nanomdm/service" | ||
"github.com/micromdm/nanomdm/storage" | ||
) | ||
|
||
// UAService is a basic UserAuthenticate service that optionally implements | ||
// the "zero-length" UserAuthenticate protocol. | ||
// See https://developer.apple.com/documentation/devicemanagement/userauthenticate | ||
type UAService struct { | ||
logger log.Logger | ||
store storage.UserAuthenticateStore | ||
|
||
// By default the UserAuthenticate message will be rejected (410 | ||
// response). If this is set true then a static zero-length | ||
// digest challenge will be supplied to the first UserAuthenticate | ||
// check-in message. See the Discussion section of | ||
// https://developer.apple.com/documentation/devicemanagement/userauthenticate | ||
sendEmptyDigestChallenge bool | ||
storeRejectedUserAuth bool | ||
} | ||
|
||
// NewUAService creates a new UserAuthenticate check-in message handler. | ||
func NewUAService(store storage.UserAuthenticateStore, sendEmptyDigestChallenge bool) *UAService { | ||
return &UAService{ | ||
logger: log.NopLogger, | ||
store: store, | ||
sendEmptyDigestChallenge: sendEmptyDigestChallenge, | ||
} | ||
} | ||
|
||
const emptyDigestChallenge = `<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>DigestChallenge</key> | ||
<string></string> | ||
</dict> | ||
</plist>` | ||
|
||
var emptyDigestChallengeBytes = []byte(emptyDigestChallenge) | ||
|
||
// UserAuthenticate will decline management of a user unless configured | ||
// for the empty digest 2-step UserAuthenticate protocol. | ||
// It implements the NanoMDM service method for UserAuthenticate check-in messages. | ||
func (s *UAService) UserAuthenticate(r *mdm.Request, message *mdm.UserAuthenticate) ([]byte, error) { | ||
logger := ctxlog.Logger(r.Context, s.logger) | ||
if s.sendEmptyDigestChallenge || s.storeRejectedUserAuth { | ||
if err := s.store.StoreUserAuthenticate(r, message); err != nil { | ||
return nil, err | ||
} | ||
} | ||
// if the DigestResponse is empty then this is the first (of two) | ||
// UserAuthenticate messages depending on our response | ||
if message.DigestResponse == "" { | ||
if s.sendEmptyDigestChallenge { | ||
logger.Info( | ||
"msg", "sending empty DigestChallenge response to UserAuthenticate", | ||
) | ||
return emptyDigestChallengeBytes, nil | ||
} | ||
return nil, service.NewHTTPStatusError( | ||
http.StatusGone, | ||
fmt.Errorf("declining management of user: %s", r.ID), | ||
) | ||
} | ||
logger.Debug( | ||
"msg", "sending empty response to second UserAuthenticate", | ||
) | ||
return nil, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package nanomdm | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"testing" | ||
|
||
"github.com/micromdm/nanomdm/mdm" | ||
"github.com/micromdm/nanomdm/service" | ||
) | ||
|
||
type fauxStore struct { | ||
ua *mdm.UserAuthenticate | ||
} | ||
|
||
func (f *fauxStore) StoreUserAuthenticate(_ *mdm.Request, msg *mdm.UserAuthenticate) error { | ||
f.ua = msg | ||
return nil | ||
} | ||
|
||
func newMDMReq() *mdm.Request { | ||
return &mdm.Request{EnrollID: &mdm.EnrollID{ID: "<test>"}} | ||
} | ||
|
||
func TestUAServiceReject(t *testing.T) { | ||
store := &fauxStore{} | ||
s := NewUAService(store, false) | ||
_, err := s.UserAuthenticate(newMDMReq(), &mdm.UserAuthenticate{}) | ||
var httpErr *service.HTTPStatusError | ||
if !errors.As(err, &httpErr) { | ||
// should be returning a HTTPStatusError (to deny management) | ||
t.Fatalf("no error or incorrect error type") | ||
} | ||
if httpErr.Status != 410 { | ||
// if we've kept the "send-empty" false this needs to return a 410 | ||
// i.e. decline management of the user. | ||
t.Error("status not 410") | ||
} | ||
} | ||
|
||
func TestUAService(t *testing.T) { | ||
store := &fauxStore{} | ||
s := NewUAService(store, true) | ||
ret, err := s.UserAuthenticate(newMDMReq(), &mdm.UserAuthenticate{}) | ||
if err != nil { | ||
// should be no error | ||
t.Fatal(err) | ||
} | ||
if !bytes.Equal(ret, emptyDigestChallengeBytes) { | ||
t.Error("response bytes not equal") | ||
} | ||
// second request with DigestResponse | ||
ret, err = s.UserAuthenticate(newMDMReq(), &mdm.UserAuthenticate{DigestResponse: "test"}) | ||
if err != nil { | ||
// should be no error | ||
t.Fatal(err) | ||
} | ||
if ret != nil { | ||
t.Error("response bytes not empty") | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters