File builder

* Code that will build a certificate file from its configuration
  * Run it on all configured files from main
This commit is contained in:
Emmanuel BENOîT 2021-11-05 14:24:04 +01:00
parent 0e642c85a6
commit a651e408ed
2 changed files with 192 additions and 0 deletions

177
buildcert.go Normal file
View file

@ -0,0 +1,177 @@
package main
import (
"encoding/pem"
"fmt"
"io/ioutil"
)
// Max supported CA chain length
const MAX_CA_CHAIN_LENGTH = 8
type (
// Certificate building, including the configuration, LDAP connection,
// and the array of chunks that's being built.
tCertificateBuilder struct {
config *tCertificateFileConfig
conn *tLdapConn
data [][]byte
}
)
// Initialize a certificate file building using a LDAP connection and
// certificate file configuration.
func NewCertificateBuilder(conn *tLdapConn, config *tCertificateFileConfig) tCertificateBuilder {
return tCertificateBuilder{
config: config,
conn: conn,
data: make([][]byte, 0),
}
}
// Build the certificate file's data, returning any error that occurs while
// reading the source data.
func (b *tCertificateBuilder) Build() error {
err := b.appendPemFiles(b.config.PrependFiles)
if err != nil {
return err
}
err = b.appendCertificate()
if err != nil {
return err
}
err = b.appendCaCertificates()
if err != nil {
return err
}
err = b.appendPemFiles(b.config.AppendFiles)
if err != nil {
return err
}
if b.config.Reverse {
b.reverseChunks()
}
return nil
}
// Append PEM files from a list.
func (b *tCertificateBuilder) appendPemFiles(files []string) error {
for _, path := range files {
var err error
err = b.appendPem(path)
if err != nil {
return err
}
}
return nil
}
// Append a PEM file to the current list of data chunks
func (b *tCertificateBuilder) appendPem(input string) error {
data, err := ioutil.ReadFile(input)
if err != nil {
return fmt.Errorf("Could not load '%s': %w", input, err)
}
rest := data
hadBlock := false
for {
var block *pem.Block
block, rest = pem.Decode(rest)
if block == nil {
break
}
hadBlock = true
b.data = append(b.data, pem.EncodeToMemory(block))
}
if hadBlock {
return nil
} else {
return fmt.Errorf("No PEM blocks found in '%s'", input)
}
}
// Append the main, end-entity certificate from the LDAP
func (b *tCertificateBuilder) appendCertificate() error {
if b.config.Certificate != "" {
dn := b.conn.Config.Structure.BaseDN
if dn != "" {
dn = "," + dn
}
dn = b.config.Certificate + dn
data, err := b.conn.getEndEntityCertificate(dn)
if err != nil {
return err
}
b.data = append(b.data, data)
}
return nil
}
// Append all CA certificates, reading the list from the LDAP or from the
// configuration.
func (b *tCertificateBuilder) appendCaCertificates() error {
if len(b.config.CACertificates) != 0 {
return b.appendListedCaCerts()
} else if b.config.CAChainOf != "" {
return b.appendChainedCaCerts()
} else {
return nil
}
}
// Append CA certificates based on a list of DNs
func (b *tCertificateBuilder) appendListedCaCerts() error {
bdn := b.conn.Config.Structure.BaseDN
if bdn != "" {
bdn = "," + bdn
}
for _, dn := range b.config.CACertificates {
data, _, err := b.conn.getCaCertificate(dn + bdn)
if err != nil {
return err
}
if data == nil {
return fmt.Errorf("No CA certificate at DN '%s'", dn)
}
b.data = append(b.data, data)
}
return nil
}
// Append CA certificates by following a chain starting at some DN
func (b *tCertificateBuilder) appendChainedCaCerts() error {
nFound := 0
dn := b.config.CAChainOf
if b.conn.Config.Structure.BaseDN != "" {
dn = dn + "," + b.conn.Config.Structure.BaseDN
}
for {
data, nextDn, err := b.conn.getCaCertificate(dn)
if err != nil {
return err
}
if nFound != 0 {
if data == nil {
return fmt.Errorf("No CA certificate at DN '%s'", dn)
}
b.data = append(b.data, data)
}
if nextDn == "" {
return nil
}
dn = nextDn
nFound += 1
if nFound == MAX_CA_CHAIN_LENGTH {
return fmt.Errorf("DN '%s': CA chain length exceeded", dn)
}
}
}
// Reverse the chunks in the list
func (b *tCertificateBuilder) reverseChunks() {
l := len(b.data) / 2
for i := 0; i < l/2; i++ {
j := l - i - 1
b.data[i], b.data[j] = b.data[j], b.data[i]
}
}

15
main.go
View file

@ -70,4 +70,19 @@ func main() {
log.WithField("error", err).Fatal("Failed to initialize socket.")
}
listener.Close()
conn := getLdapConnection(cfg.LdapConfig)
if conn == nil {
return
}
defer conn.close()
for i := range cfg.Certificates {
builder := NewCertificateBuilder(conn, &cfg.Certificates[i])
err := builder.Build()
if err != nil {
log.WithField("error", err).Error("Failed to build data for certificate '", cfg.Certificates[i].Path)
continue
}
// FIXME: check existing file, try to write
}
}