launcher icon indicating copy to clipboard operation
launcher copied to clipboard

We should experiment with better certificate validation

Open directionless opened this issue 2 years ago • 3 comments

We have a request to see if we can validate a certificate. This seems pretty interesting, though I have no idea how feasible it will prove to be.

There are 2 different kinds of validation. I'm not yet sure which the customer wants (internal slack thread). One kind is a digital signature, this would validate the the device has access to the private key. The second kind would be to verify the signature on the cert against a CA bundle, this would validate the signer. Either/both of these has a bunch of questions...

  • Where are the certs stored? If everything is on disk, and there are no passphrases, digital signatures are easy
  • If the key is stored in the macOS keychain, and launcher is allowed to read it, maybe we can use SecKeyCreateSignature
  • No idea about windows
  • If we wanted to validate the CA, how is that sent? (bundle on disk? Bundle in the same keychain? Etc)
  • What if there are intermediaries?

I think the biggest risk is about launcher being disallowed to read the private key material.

directionless avatar May 20 '23 01:05 directionless

Maybe the first thing to look at are the MDM certs. All devices that are in an MDM have a certificate, usually delievered by SCEP.

directionless avatar May 24 '23 13:05 directionless

I found certstore, which is a little out of date, but the underlying macOS framework calls look correct. Writing a test program with that I can:

  1. List the identities present (identities are things that have a cert and associated key)
  2. Sign things
  3. But only if keychain grants access to the key

And by default, the interesting identities do not allow access. For example, my client's MDM issued cert:

Screenshot 2023-06-28 at 16 38 07

I can bypass that with a password, but that's not going to work for launcher.

Test Code:

package main

import (
	"crypto"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"time"

	"github.com/github/smimesign/certstore"
)

func main() {

	if err := certList(); err != nil {
		fmt.Printf("Error: %s\n", err)
	}

}

func certList() error {
	store, err := certstore.Open()
	if err != nil {
		return fmt.Errorf("opening certstore: %w", err)
	}
	defer store.Close()

	idents, err := store.Identities()
	if err != nil {
		return fmt.Errorf("getting identities: %w", err)
	}

	for _, ident := range idents {
		tryIdent(ident)

	}

	return nil
}

func tryIdent(ident certstore.Identity) {
	defer fmt.Println("\n")

	crt, err := ident.Certificate()
	if err != nil {
		fmt.Printf("Not a cert: %s\n", err)
		return
	}

	fmt.Printf("Subject: %s\nIssuer: %s\n", crt.Subject.String(), crt.Issuer.String())

	nonce := time.Now().String()
	fmt.Printf("msg: %s\n", nonce)

	signer, err := ident.Signer()
	if err != nil {
		fmt.Printf("error getting signer: %s", err)
		return
	}

	// Digest and sign our message.
	digest := sha256.Sum256([]byte(nonce))
	signature, err := signer.Sign(rand.Reader, digest[:], crypto.SHA256)
	if err != nil {
		fmt.Printf("error signing: %s", err)
		return
	}

	fmt.Printf("sig: %s\n", base64.StdEncoding.EncodeToString(signature))
}

directionless avatar Jun 28 '23 23:06 directionless

We've decided to not pursue this right now. But some notes before I mothball it.

Our usually pattern would be to have this brokered by launcher. That pattern would look like select * from cert_sign where message = 'test123' and then launcher would use the certificate to sign that data (plus some kolide extras) and pass the signature back to k2 for verification. Though this approach fits with our current model, it's still has launcher in the loop.

If we wanted to do something that fit the OS patterns and had more server verification, I could imagine having launcher trigger some outbound connection to a server we control that's tied to using these certs. We would then have that server force mTLS validation. And we'd need that server to communicate the connection back to k2. Not really sure how this would work -- we'd need run some servers somewhere we controlled the TLS termination. And we'd need a way to communicate that connection verification back to k2.

directionless avatar Oct 17 '23 20:10 directionless