package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
)
const (
opensslConfig = "/usr/share/tests/osbuild-composer/x509/openssl.cnf"
osbuildCAExt = "osbuild_ca_ext"
osbuildClientExt = "osbuild_client_ext"
)
type certificateKeyPair struct {
baseDir string
}
func (ckp certificateKeyPair) remove() {
err := os.RemoveAll(ckp.baseDir)
if err != nil {
log.Printf("cannot delete the certificate key pair: %v", err)
}
}
func (ckp certificateKeyPair) certificate() string {
return path.Join(ckp.baseDir, "crt")
}
func (ckp certificateKeyPair) key() string {
return path.Join(ckp.baseDir, "key")
}
func newSelfSignedCertificateKeyPair(subj string) (*certificateKeyPair, error) {
dir, err := ioutil.TempDir("", "osbuild-auth-tests-")
if err != nil {
return nil, fmt.Errorf("cannot create a temporary directory for the certificate: %v", err)
}
ckp := certificateKeyPair{baseDir: dir}
cmd := exec.Command(
"openssl", "req", "-nodes", "-x509",
"-subj", subj,
"-out", ckp.certificate(),
"-keyout", ckp.key(),
)
err = cmd.Run()
if err != nil {
return nil, fmt.Errorf("cannot generate a self-signed certificate: %v", err)
}
return &ckp, nil
}
type ca struct {
BaseDir string
}
func (c ca) remove() {
err := os.RemoveAll(c.BaseDir)
if err != nil {
log.Printf("cannot delete the ca: %v", err)
}
}
func (c ca) certificate() string {
return path.Join(c.BaseDir, "ca.cert.pem")
}
func (c ca) key() string {
return path.Join(c.BaseDir, "private", "ca.key.pem")
}
func newCA(subj string) (*ca, error) {
baseDir, err := ioutil.TempDir("", "osbuild-auth-tests-ca")
if err != nil {
return nil, fmt.Errorf("cannot create a temporary dir for a new CA: %v", err)
}
err = os.Mkdir(path.Join(baseDir, "certs"), 0700)
if err != nil {
innerErr := os.RemoveAll(baseDir)
if innerErr != nil {
log.Print(innerErr)
}
return nil, fmt.Errorf("cannot create certs dir for the new CA: %v", err)
}
err = os.Mkdir(path.Join(baseDir, "private"), 0700)
if err != nil {
innerErr := os.RemoveAll(baseDir)
if innerErr != nil {
log.Print(innerErr)
}
return nil, fmt.Errorf("cannot create private dir for the new CA: %v", err)
}
f, err := os.Create(path.Join(baseDir, "index.txt"))
if err != nil {
innerErr := os.RemoveAll(baseDir)
if innerErr != nil {
log.Print(innerErr)
}
return nil, fmt.Errorf("cannot create index file for the new CA: %v", err)
}
f.Close()
c := ca{
BaseDir: baseDir,
}
cmd := exec.Command(
"openssl", "req",
"-config", opensslConfig,
"-new", "-nodes", "-x509", "-extensions", osbuildCAExt,
"-subj", subj,
"-keyout", c.key(),
"-out", c.certificate(),
)
err = cmd.Run()
if err != nil {
innerErr := os.RemoveAll(baseDir)
if innerErr != nil {
log.Print(innerErr)
}
return nil, fmt.Errorf("cannot create the CA: %v", err)
}
return &c, nil
}
func (c ca) newCertificateKeyPair(subj, extensions, addext string) (*certificateKeyPair, error) {
dir, err := ioutil.TempDir("", "osbuild-auth-tests-")
if err != nil {
return nil, fmt.Errorf("cannot create a temporary directory for the certificate: %v", err)
}
ckp := certificateKeyPair{baseDir: dir}
certificateRequest := path.Join(dir, "csr")
args := []string{
"req", "-new", "-nodes",
"-subj", subj,
"-keyout", ckp.key(),
"-out", certificateRequest,
"-config", opensslConfig,
}
if addext != "" {
args = append(args, "-addext", addext)
}
cmd := exec.Command(
"openssl",
args...,
)
err = cmd.Run()
if err != nil {
return nil, fmt.Errorf("cannot generate a private key and a certificate request: %v", err)
}
defer os.Remove(certificateRequest)
cmd = exec.Command(
"openssl", "ca",
"-batch",
"-config", opensslConfig,
"-extensions", extensions,
"-in", certificateRequest,
"-out", ckp.certificate(),
)
// this command must be run in the CA base directory
cmd.Dir = c.BaseDir
err = cmd.Run()
if err != nil {
return nil, fmt.Errorf("cannot sign the certificate: %v", err)
}
return &ckp, nil
}