diff --git a/cmd/mdmctl/apply_app.go b/cmd/mdmctl/apply_app.go index 94e1e63e..44dd89ab 100644 --- a/cmd/mdmctl/apply_app.go +++ b/cmd/mdmctl/apply_app.go @@ -25,13 +25,16 @@ func (cmd *applyCommand) applyApp(args []string) error { flagset := flag.NewFlagSet("app", flag.ExitOnError) var ( flPkgPath = flagset.String("pkg", "", "path to a distribution pkg.") + flOutputPath = flagset.String("out", "", "pkg output path for -sign and -sign-identity (optional, defaults to OS temporary directory)") flPkgURL = flagset.String("pkg-url", "", "use custom pkg url") flAppManifest = flagset.String("manifest", "-", `path to an app manifest. optional, will be created if file does not exist.`) - flHashSize = flagset.Int64("md5size", appmanifest.DefaultMD5Size, "md5 hash size in bytes (optional)") - flSign = flagset.String("sign", "", "sign package before importing, requires specifying a product ID (optional)") - flUpload = flagset.Bool("upload", false, "upload package and/or manifest to micromdm repository.") + flHashSize = flagset.Int64("md5size", appmanifest.DefaultMD5Size, "md5 hash size in bytes (optional)") + flSign = flagset.String("sign", "", "sign package before importing, requires specifying id of developer identity in keychain (optional, macOS only)") + flSignIdentity = flagset.String("sign-identity", "", "sign package before importing, requires specifying path to .p12 developer identity (optional)") + flIdentityPass = flagset.String("password", "", "password used for -sign-identity (optional)") + flUpload = flagset.Bool("upload", false, "upload package and/or manifest to micromdm repository.") ) flagset.Usage = usageFor(flagset, "mdmctl apply app [flags]") if err := flagset.Parse(args); err != nil { @@ -70,13 +73,27 @@ Please rebuild the package and re-run the command. } if !signed { - if *flSign == "" { + if *flSign == "" && *flSignIdentity == "" { flagset.Usage() - return errors.New(`MDM packages must be signed. Provide signed package or Developer ID with -sign flag`) + return errors.New(`MDM packages must be signed. Provide signed package or -sign or -sign-identity flags`) } - outpath := filepath.Join(os.TempDir(), filepath.Base(*flPkgPath)) - if err := signPackage(*flPkgPath, outpath, *flSign); err != nil { - return err + outpath := *flOutputPath + if outpath == "" { + outpath = filepath.Join(os.TempDir(), filepath.Base(*flPkgPath)) + } + if outpath == *flPkgPath { + return errors.New("-out can't be the same as -pkg") + } + + if *flSign != "" { + if err := signPackage(*flPkgPath, outpath, *flSign); err != nil { + return err + } + } else { + if err := signPackageWithIdentity(*flPkgPath, outpath, *flSignIdentity, *flIdentityPass); err != nil { + return err + } + } pkg = outpath // use signed package to create the manifest } diff --git a/cmd/mdmctl/sign.go b/cmd/mdmctl/sign.go index a102c7dc..fa6a5f3d 100644 --- a/cmd/mdmctl/sign.go +++ b/cmd/mdmctl/sign.go @@ -3,14 +3,31 @@ package main -import "fmt" +import ( + "fmt" + "os" + + goerrors "errors" + + macospkg "github.com/korylprince/go-macos-pkg" + "github.com/pkg/errors" +) func signPackage(path, outpath, developerID string) error { - fmt.Println("[WARNING] package signing only implemented on macOS") + fmt.Println("[WARNING] package signing with -sign only implemented on macOS. Use -sign-identity instead") return nil } func checkSignature(pkgpath string) (bool, error) { - fmt.Println("[WARNING] package signing only implemented on macOS. An unsigned macOS package will not install with MDM.") + buf, err := os.ReadFile(pkgpath) + if err != nil { + return false, errors.Wrap(err, "reading package") + } + if err = macospkg.VerifyPkg(buf); err != nil { + if goerrors.Is(err, macospkg.ErrNotSigned) { + return false, nil + } + return false, errors.Wrap(err, "verifying package") + } return true, nil } diff --git a/cmd/mdmctl/sign_identity.go b/cmd/mdmctl/sign_identity.go new file mode 100644 index 00000000..8a419bae --- /dev/null +++ b/cmd/mdmctl/sign_identity.go @@ -0,0 +1,36 @@ +package main + +import ( + "crypto/rsa" + "os" + + macospkg "github.com/korylprince/go-macos-pkg" + "github.com/pkg/errors" + "golang.org/x/crypto/pkcs12" +) + +func signPackageWithIdentity(pkgpath, outpath, identitypath, identitypass string) error { + identity, err := os.ReadFile(identitypath) + if err != nil { + return errors.Wrap(err, "reading identity") + } + key, cert, err := pkcs12.Decode(identity, identitypass) + if err != nil { + return errors.Wrap(err, "decoding identity") + } + + pkg, err := os.ReadFile(pkgpath) + if err != nil { + return errors.Wrap(err, "reading package") + } + + signed, err := macospkg.SignPkg(pkg, cert, key.(*rsa.PrivateKey)) + if err != nil { + return errors.Wrap(err, "signing package") + } + + if err = os.WriteFile(outpath, signed, 0644); err != nil { + return errors.Wrap(err, "writing signed package") + } + return nil +} diff --git a/go.mod b/go.mod index f3076616..61c4fa73 100644 --- a/go.mod +++ b/go.mod @@ -12,14 +12,15 @@ require ( github.com/groob/plist v0.0.0-20220217120414-63fa881b19a5 github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 github.com/kolide/kit v0.0.0-20180912215818-0c28f72eb2b0 + github.com/korylprince/go-macos-pkg v1.3.5 github.com/lib/pq v1.0.0 github.com/micromdm/go4 v0.0.0-20210104222236-8a0936d9e451 github.com/micromdm/scep/v2 v2.1.0 github.com/pkg/errors v0.8.0 github.com/pressly/goose v2.3.0+incompatible go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 - golang.org/x/net v0.0.0-20201021035429-f5854403a974 + golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 google.golang.org/protobuf v1.27.1 gopkg.in/Masterminds/squirrel.v1 v1.0.0-20170825200431-a6b93000bd21 ) @@ -31,6 +32,8 @@ require ( github.com/go-sql-driver/mysql v1.4.0 // indirect github.com/go-stack/stack v1.7.0 // indirect github.com/gorilla/context v1.1.1 // indirect + github.com/korylprince/go-cpio-odc v0.9.4 // indirect + github.com/korylprince/goxar v0.0.0-20211111233330-e9f257bcdf25 // indirect github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect @@ -38,7 +41,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 // indirect github.com/ziutek/mymysql v1.5.4 // indirect - golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect + golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect golang.org/x/text v0.3.3 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.2.0 // indirect diff --git a/go.sum b/go.sum index a9b27f3e..ec91e129 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,16 @@ github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 h1:5B0uxl2lzNRVkJVg+u github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= github.com/kolide/kit v0.0.0-20180912215818-0c28f72eb2b0 h1:8UB0NJBoB/lN2O9zG9+U2klyYE0UhvN2Zrn7HBjhwNY= github.com/kolide/kit v0.0.0-20180912215818-0c28f72eb2b0/go.mod h1:N3Yv8okDVC/5qZhPA9uxVYRfkp4mD2vrlQiSCWlNCpg= +github.com/korylprince/go-cpio-odc v0.9.3 h1:MGpo1PcJN2ajysEoAhF34zR/Peevo4N+T+OmhDLIUvk= +github.com/korylprince/go-cpio-odc v0.9.3/go.mod h1:1iHsjUXO64Hui0YsubGp0Tm/Uf04Iow2iy+1LJIarMw= +github.com/korylprince/go-cpio-odc v0.9.4 h1:N0Afrp7Z5qCZF8cbpzF5CvyiC/KHK5IcLJwqm5MWpNU= +github.com/korylprince/go-cpio-odc v0.9.4/go.mod h1:1iHsjUXO64Hui0YsubGp0Tm/Uf04Iow2iy+1LJIarMw= +github.com/korylprince/go-macos-pkg v1.3.4 h1:6McQ9bj3p/k/FeqFBHhdvXVvi+ftNcWXj4EWAEuEiz0= +github.com/korylprince/go-macos-pkg v1.3.4/go.mod h1:eLXotCZDK3vBt6AfDg62Fl20lCjrIm5e8V/8Q9YLHIo= +github.com/korylprince/go-macos-pkg v1.3.5 h1:jwwefLR1yRKQPNu51QTwk2B4abIjjISj/BHipcvKKGs= +github.com/korylprince/go-macos-pkg v1.3.5/go.mod h1:jlPGliXXjjCMpISR30/fL60hXKrCJ2LNQSptdx+WQkc= +github.com/korylprince/goxar v0.0.0-20211111233330-e9f257bcdf25 h1:XVf+U+WekiGPPD27w7qI+0Pe8NehaTWPqRuO3BddefY= +github.com/korylprince/goxar v0.0.0-20211111233330-e9f257bcdf25/go.mod h1:Lm6kfGMVHcpiBP6oOZd3ugv2EpQ/dOyW02tVIa3qo2Q= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= @@ -72,18 +82,21 @@ go.mozilla.org/pkcs7 v0.0.0-20210730143726-725912489c62/go.mod h1:SNgMg+EgDFwmvS go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=