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 }