Files
certmanager/internal/logic/service.go
Олег Бородин f25197e714 certmanager updates
2024-08-14 11:38:01 +02:00

466 lines
12 KiB
Go

package logic
import (
"context"
"crypto/sha256"
"crypto/x509"
"fmt"
"time"
"certmanager/internal/descriptor"
"certmanager/pkg/auxid"
"certmanager/pkg/cm509"
"certmanager/pkg/cmctl"
)
func (lg *Logic) CreateServicePair(ctx context.Context, accountID int64, params *cmctl.CreateServicePairParams) (*cmctl.CreateServicePairResult, error) {
var err error
res := &cmctl.CreateServicePairResult{}
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyServices)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
if params.ServiceCommonName == "" {
err := fmt.Errorf("No common name specified")
return res, err
}
var issuerDescr *descriptor.Issuer
var issuerExists bool
switch {
case params.IssuerID != 0:
issuerExists, issuerDescr, err = lg.db.GetIssuerByID(ctx, params.IssuerID)
if !issuerExists {
err := fmt.Errorf("No signer with id %d was found", params.IssuerID)
if err != nil {
return res, err
}
}
case params.IssuerName != "":
issuerExists, issuerDescr, err = lg.db.GetIssuerByName(ctx, params.IssuerName)
if !issuerExists {
err := fmt.Errorf("No signer with name %s was found", params.IssuerName)
if err != nil {
return res, err
}
}
default:
err := fmt.Errorf("Issuer id or name is not specified")
if err != nil {
return res, err
}
}
if !issuerExists {
err := fmt.Errorf("Issuer not found")
if err != nil {
return res, err
}
}
if issuerDescr == nil {
err := fmt.Errorf("Issuer descriptor is nil")
if err != nil {
return res, err
}
}
if issuerDescr.Revoked {
err := fmt.Errorf("The issuer revoked")
if err != nil {
return res, err
}
}
err = cm509.DoubleEncodedCertKeyMatch(issuerDescr.Cert, issuerDescr.Key)
if err != nil {
return res, err
}
newServiceID := auxid.GenID()
createServicePairParams := &cm509.CreateServicePairParams{
OrganizationName: params.ServiceOrganizationName,
OrganizationalUnitName: params.ServiceOrganizationalUnitName,
CommonName: params.ServiceCommonName,
IssuerKey: issuerDescr.Key,
IssuerCert: issuerDescr.Cert,
IPAddresses: params.InetAddresses,
DNSNames: params.Hostnames,
SerialNumber: newServiceID,
}
createSericePairRes, err := cm509.CreateServicePair(createServicePairParams)
if err != nil {
return res, err
}
serviceCertObj, err := cm509.ParseDoubleEncodedCerificate(createSericePairRes.Cert)
if err != nil {
return res, err
}
fingerprintBytes := sha256.Sum256(serviceCertObj.Raw)
fingerprint := fmt.Sprintf("sha256:%x", fingerprintBytes)
serviceDescr := &descriptor.Service{
ID: newServiceID,
Name: createSericePairRes.Name,
IssuerID: issuerDescr.ID,
IssuerName: issuerDescr.Name,
Cert: createSericePairRes.Cert,
Key: createSericePairRes.Key,
}
issuerDescrs, err := lg.GetIssuerChain(ctx, serviceDescr.IssuerID)
if err != nil {
return res, err
}
for _, issuerDescr := range issuerDescrs {
res.IssuerCertificates = append(res.IssuerCertificates, issuerDescr.Cert)
res.IssuerNames = append(res.IssuerNames, issuerDescr.Name)
}
err = lg.db.InsertService(ctx, serviceDescr)
if err != nil {
return res, err
}
res.ServiceName = createSericePairRes.Name
res.ServiceID = serviceDescr.ID
res.Certificate = createSericePairRes.Cert
res.Key = createSericePairRes.Key
res.IssuerID = issuerDescr.ID
res.IssuerName = issuerDescr.Name
res.IssuerCertificate = issuerDescr.Cert
res.Fingerprint = fingerprint
return res, err
}
func (lg *Logic) GetServicePair(ctx context.Context, accountID int64, params *cmctl.GetServicePairParams) (*cmctl.GetServicePairResult, error) {
var err error
res := &cmctl.GetServicePairResult{
IssuerCertificates: make([]string, 0),
}
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyServices)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
var serviceDescr *descriptor.Service
var serviceExists bool
switch {
case params.ServiceID != 0:
serviceExists, serviceDescr, err = lg.db.GetServiceByID(ctx, params.ServiceID)
if !serviceExists {
err := fmt.Errorf("No service was found for id %d", params.ServiceID)
if err != nil {
return res, err
}
}
case params.ServiceName != "":
serviceExists, serviceDescr, err = lg.db.GetServiceByName(ctx, params.ServiceName)
if !serviceExists {
err := fmt.Errorf("No service was found for name %s", params.ServiceName)
if err != nil {
return res, err
}
}
default:
err := fmt.Errorf("Service ID or name is not specified")
if err != nil {
return res, err
}
}
if serviceDescr == nil {
err := fmt.Errorf("Service descriptor is nil")
if err != nil {
return res, err
}
}
issuerExists, issuerDescr, err := lg.db.GetIssuerByID(ctx, serviceDescr.IssuerID)
if !issuerExists {
err := fmt.Errorf("No issuer for service was found")
if err != nil {
return res, err
}
}
serviceCertObj, err := cm509.ParseDoubleEncodedCerificate(serviceDescr.Cert)
if err != nil {
return res, err
}
issuerCertObj, err := cm509.ParseDoubleEncodedCerificate(issuerDescr.Cert)
if err != nil {
return res, err
}
if serviceCertObj.Subject.String() != serviceDescr.Name {
err := fmt.Errorf("The subject's identities in the database and certificate do not match")
if err != nil {
return res, err
}
}
if serviceCertObj.Issuer.String() != serviceDescr.IssuerName {
err := fmt.Errorf("The issuer's identities in the database and certificate do not match")
if err != nil {
return res, err
}
}
if serviceCertObj.Issuer.String() != issuerDescr.Name {
err := fmt.Errorf("The issuer's and service identities in the database and certificate do not match")
if err != nil {
return res, err
}
}
if serviceCertObj.Issuer.String() != issuerCertObj.Subject.String() {
err := fmt.Errorf("The issuer's and service identities into certificates do not match")
if err != nil {
return res, err
}
}
issuerDescrs, err := lg.GetIssuerChain(ctx, serviceDescr.IssuerID)
if err != nil {
return res, err
}
for _, issuerDescr := range issuerDescrs {
res.IssuerCertificates = append(res.IssuerCertificates, issuerDescr.Cert)
}
signerCertPool := x509.NewCertPool()
for _, issuerDescr := range issuerDescrs {
issuerCertObj, err := cm509.ParseDoubleEncodedCerificate(issuerDescr.Cert)
if err != nil {
return res, err
}
signerCertPool.AddCert(issuerCertObj)
}
opts := x509.VerifyOptions{
Roots: signerCertPool,
CurrentTime: time.Now(),
}
_, err = serviceCertObj.Verify(opts)
if err != nil {
return res, err
}
err = cm509.DoubleEncodedCertKeyMatch(serviceDescr.Cert, serviceDescr.Key)
if err != nil {
return res, err
}
fingerprintBytes := sha256.Sum256(serviceCertObj.Raw)
fingerprint := fmt.Sprintf("sha256:%x", fingerprintBytes)
res.Certificate = serviceDescr.Cert
res.Key = serviceDescr.Key
res.IssuerID = serviceDescr.IssuerID
res.IssuerName = serviceDescr.IssuerName
res.Revoked = serviceDescr.Revoked
res.IssuerCertificate = issuerDescr.Cert
res.Fingerprint = fingerprint
return res, err
}
func (lg *Logic) GetIssuerChain(ctx context.Context, firstIssuerID int64) ([]*descriptor.Issuer, error) {
var err error
res := make([]*descriptor.Issuer, 0)
firstIssuerExists, firstIssuerDescr, err := lg.db.GetIssuerByID(ctx, firstIssuerID)
if !firstIssuerExists {
err := fmt.Errorf("No issuer for service was found")
if err != nil {
return res, err
}
}
deep := 1
nextDescrs, err := lg.GetNextIssuerChain(ctx, deep, firstIssuerDescr)
if err != nil {
return res, err
}
res = append(res, nextDescrs...)
return res, err
}
func (lg *Logic) GetNextIssuerChain(ctx context.Context, deep int, firstIssuerDescr *descriptor.Issuer) ([]*descriptor.Issuer, error) {
var err error
res := make([]*descriptor.Issuer, 0)
res = append(res, firstIssuerDescr)
deep += 1
const maxDeep = 12
if deep > maxDeep {
err := fmt.Errorf("Cannot found root issuer after %d loops", maxDeep)
if err != nil {
return res, err
}
}
firstIssuerCertObj, err := cm509.ParseDoubleEncodedCerificate(firstIssuerDescr.Cert)
if err != nil {
return res, err
}
itIsSelfSignedRoot := (firstIssuerDescr.SignerID == firstIssuerDescr.SignerID) &&
(firstIssuerCertObj.Issuer.String() == firstIssuerCertObj.Subject.String())
if itIsSelfSignedRoot {
return res, err
}
lg.log.Debugf("%d %d", firstIssuerDescr.ID, firstIssuerDescr.SignerID)
nextIssuerExists, nextIssuerDescrs, err := lg.db.GetIssuerByID(ctx, firstIssuerDescr.SignerID)
if !nextIssuerExists {
err := fmt.Errorf("No issuer for service was found")
if err != nil {
return res, err
}
}
nextDescrs, err := lg.GetNextIssuerChain(ctx, deep, nextIssuerDescrs)
res = append(res, nextDescrs...)
return res, err
}
func (lg *Logic) ListServicePairs(ctx context.Context, accountID int64, params *cmctl.ListServicePairsParams) (*cmctl.ListServicePairsResult, error) {
var err error
res := &cmctl.ListServicePairsResult{
Services: make([]*cmctl.ServiceShortDescriptor, 0),
}
listServices, err := lg.db.ListServices(ctx)
if err != nil {
return res, err
}
for _, service := range listServices {
serviceShortDescr := cmctl.ServiceShortDescriptor{
ServiceID: service.ID,
IssuerID: service.IssuerID,
IssuerName: service.IssuerName,
Name: service.Name,
Revoked: service.Revoked,
}
res.Services = append(res.Services, &serviceShortDescr)
}
return res, err
}
func (lg *Logic) RevokeServicePair(ctx context.Context, accountID int64, params *cmctl.RevokeServicePairParams) (*cmctl.RevokeServicePairResult, error) {
var err error
res := &cmctl.RevokeServicePairResult{}
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyServices)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
var serviceDescr *descriptor.Service
var serviceExists bool
switch {
case params.ServiceID != 0:
serviceExists, serviceDescr, err = lg.db.GetServiceByID(ctx, params.ServiceID)
if !serviceExists {
err := fmt.Errorf("No signer with id %d was found", params.ServiceID)
if err != nil {
return res, err
}
}
case params.ServiceName != "":
serviceExists, serviceDescr, err = lg.db.GetServiceByName(ctx, params.ServiceName)
if !serviceExists {
err := fmt.Errorf("No signer with name %s was found", params.ServiceName)
if err != nil {
return res, err
}
}
default:
err := fmt.Errorf("Service ID or name is not specified")
if err != nil {
return res, err
}
}
if serviceDescr == nil {
err := fmt.Errorf("Service descriptor is nil")
if err != nil {
return res, err
}
}
if !serviceDescr.Revoked {
serviceDescr.Revoked = true
err = lg.db.UpdateServiceByID(ctx, serviceDescr.ID, serviceDescr)
if err != nil {
return res, err
}
}
return res, err
}
func (lg *Logic) UnrevokeServicePair(ctx context.Context, accountID int64, params *cmctl.UnrevokeServicePairParams) (*cmctl.UnrevokeServicePairResult, error) {
var err error
res := &cmctl.UnrevokeServicePairResult{}
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyServices)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
var serviceDescr *descriptor.Service
var serviceExists bool
switch {
case params.ServiceID != 0:
serviceExists, serviceDescr, err = lg.db.GetServiceByID(ctx, params.ServiceID)
if !serviceExists {
err := fmt.Errorf("No signer with id %d was found", params.ServiceID)
if err != nil {
return res, err
}
}
case params.ServiceName != "":
serviceExists, serviceDescr, err = lg.db.GetServiceByName(ctx, params.ServiceName)
if !serviceExists {
err := fmt.Errorf("No signer with name %s was found", params.ServiceName)
if err != nil {
return res, err
}
}
default:
err := fmt.Errorf("Service ID or name is not specified")
if err != nil {
return res, err
}
}
if serviceDescr == nil {
err := fmt.Errorf("Service descriptor is nil")
if err != nil {
return res, err
}
}
if serviceDescr.Revoked {
serviceDescr.Revoked = false
err = lg.db.UpdateServiceByID(ctx, serviceDescr.ID, serviceDescr)
if err != nil {
return res, err
}
}
return res, err
}