fleet/server/mdm/nanodep/tokenpki/parse.go
Roberto Dip 05818902cd
implement OTA enrollment (#21942)
for #21019

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

<!-- Note that API documentation changes are now addressed by the
product design team. -->

- [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/Committing-Changes.md#changes-files)
for more information.
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [x] Added/updated tests
- [x] Manual QA for all new/changed functionality
2024-09-10 16:52:17 -03:00

73 lines
2 KiB
Go

// Package tokenpki includes helpers and utilities for exchanging certificates
// and parsing token PKCS#7 S/MIME messages from the Apple ABM/ASM/BE portals.
package tokenpki
import (
"bufio"
"bytes"
"crypto"
"crypto/x509"
"encoding/base64"
"io"
"net/textproto"
"github.com/smallstep/pkcs7"
)
// UnwrapSMIME removes the S/MIME-like header wrapper around the raw encrypted
// CMS/PKCS#7 data in the downloaded token ".p7m" file from the ABM/ASM/BE
// portal.
func UnwrapSMIME(smime []byte) ([]byte, error) {
r := textproto.NewReader(bufio.NewReader(bytes.NewReader(smime)))
if _, err := r.ReadMIMEHeader(); err != nil {
return nil, err
}
d := base64.NewDecoder(base64.StdEncoding, r.DotReader())
b := new(bytes.Buffer)
_, _ = io.Copy(b, d) // writes to bytes.Buffer never fail
return b.Bytes(), nil
}
// UnwrapTokenJSON removes the S/MIME-like header wrapper around the
// the decrypted JSON tokens from the token header.
func UnwrapTokenJSON(wrapped []byte) ([]byte, error) {
r := textproto.NewReader(bufio.NewReader(bytes.NewReader(wrapped)))
if _, err := r.ReadMIMEHeader(); err != nil {
return nil, err
}
tokenJSON := new(bytes.Buffer)
for {
line, err := r.ReadLineBytes()
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
line = bytes.TrimPrefix(line, []byte("-----BEGIN MESSAGE-----"))
line = bytes.TrimPrefix(line, []byte("-----END MESSAGE-----"))
_, err = tokenJSON.Write(line)
if err != nil {
return nil, err
}
}
return tokenJSON.Bytes(), nil
}
// DecryptTokenJSON decrypts and decodes the downloaded token ".p7m" file from
// the ABM/ASM/BE portal to return the actual JSON contained within.
func DecryptTokenJSON(tokenBytes []byte, cert *x509.Certificate, key crypto.PrivateKey) ([]byte, error) {
p7Bytes, err := UnwrapSMIME(tokenBytes)
if err != nil {
return nil, err
}
p7, err := pkcs7.Parse(p7Bytes)
if err != nil {
return nil, err
}
decrypted, err := p7.Decrypt(cert, key)
if err != nil {
return nil, err
}
return UnwrapTokenJSON(decrypted)
}