github.com/hxx258456/fabric-ca-gm@v0.0.3-0.20221111064038-a268ad7e3a37/lib/serverrevoke.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package lib 8 9 import ( 10 "encoding/hex" 11 "strings" 12 13 log "gitee.com/zhaochuninhefei/zcgolog/zclog" 14 "github.com/hxx258456/fabric-ca-gm/internal/pkg/api" 15 "github.com/hxx258456/fabric-ca-gm/internal/pkg/util" 16 "github.com/hxx258456/fabric-ca-gm/lib/caerrors" 17 "github.com/hxx258456/fabric-ca-gm/lib/server/db" 18 "golang.org/x/crypto/ocsp" 19 ) 20 21 type revocationResponseNet struct { 22 RevokedCerts []api.RevokedCert 23 CRL string 24 } 25 26 // CertificateStatus represents status of an enrollment certificate 27 type CertificateStatus string 28 29 const ( 30 // Revoked is the status of a revoked certificate 31 Revoked CertificateStatus = "revoked" 32 // Good is the status of a active certificate 33 Good = "good" 34 ) 35 36 func newRevokeEndpoint(s *Server) *serverEndpoint { 37 return &serverEndpoint{ 38 Path: "revoke", 39 Methods: []string{"POST"}, 40 Handler: revokeHandler, 41 Server: s, 42 } 43 } 44 45 // revocationReasonCodes is a map between string reason codes to integers as defined in RFC 5280 46 var revocationReasonCodes = map[string]int{ 47 "unspecified": ocsp.Unspecified, 48 "keycompromise": ocsp.KeyCompromise, 49 "cacompromise": ocsp.CACompromise, 50 "affiliationchanged": ocsp.AffiliationChanged, 51 "superseded": ocsp.Superseded, 52 "cessationofoperation": ocsp.CessationOfOperation, 53 "certificatehold": ocsp.CertificateHold, 54 "removefromcrl": ocsp.RemoveFromCRL, 55 "privilegewithdrawn": ocsp.PrivilegeWithdrawn, 56 "aacompromise": ocsp.AACompromise, 57 } 58 59 // Handle an revoke request 60 func revokeHandler(ctx *serverRequestContextImpl) (interface{}, error) { 61 // Parse revoke request body 62 var req api.RevocationRequestNet 63 err := ctx.ReadBody(&req) 64 if err != nil { 65 return nil, err 66 } 67 // Authentication 68 caller, err := ctx.TokenAuthentication() 69 if err != nil { 70 return nil, err 71 } 72 // Get targeted CA 73 ca, err := ctx.GetCA() 74 if err != nil { 75 return nil, err 76 } 77 78 req.AKI = parseInput(req.AKI) 79 req.Serial = parseInput(req.Serial) 80 81 certDBAccessor := ca.certDBAccessor 82 registry := ca.registry 83 reason := revocationReasonCodes[req.Reason] 84 85 result := &revocationResponseNet{} 86 if req.Serial != "" && req.AKI != "" { 87 calleraki := strings.ToLower(strings.TrimLeft(hex.EncodeToString(ctx.enrollmentCert.AuthorityKeyId), "0")) 88 callerserial := strings.ToLower(strings.TrimLeft(util.GetSerialAsHex(ctx.enrollmentCert.SerialNumber), "0")) 89 90 certificate, err := certDBAccessor.GetCertificateWithID(req.Serial, req.AKI) 91 if err != nil { 92 return nil, caerrors.NewHTTPErr(404, caerrors.ErrRevCertNotFound, "Certificate with serial %s and AKI %s was not found: %s", 93 req.Serial, req.AKI, err) 94 } 95 96 // Authorization 97 err = checkAuth(caller, certificate.ID, ca) 98 if err != nil { 99 return nil, err 100 } 101 102 if certificate.Status == string(Revoked) { 103 return nil, caerrors.NewHTTPErr(404, caerrors.ErrCertAlreadyRevoked, "Certificate with serial %s and AKI %s was already revoked", 104 req.Serial, req.AKI) 105 } 106 107 if req.Name != "" && req.Name != certificate.ID { 108 return nil, caerrors.NewHTTPErr(400, caerrors.ErrCertWrongOwner, "Certificate with serial %s and AKI %s is not owned by %s", 109 req.Serial, req.AKI, req.Name) 110 } 111 112 userInfo, err := registry.GetUser(certificate.ID, nil) 113 if err != nil { 114 return nil, caerrors.NewHTTPErr(404, caerrors.ErrRevokeIDNotFound, "Identity %s was not found: %s", certificate.ID, err) 115 } 116 117 if !((req.AKI == calleraki) && (req.Serial == callerserial)) { 118 err = ctx.CanManageUser(userInfo) 119 if err != nil { 120 return nil, err 121 } 122 } 123 124 err = certDBAccessor.RevokeCertificate(req.Serial, req.AKI, reason) 125 if err != nil { 126 return nil, caerrors.NewHTTPErr(500, caerrors.ErrRevokeFailure, "Revoke of certificate <%s,%s> failed: %s", req.Serial, req.AKI, err) 127 } 128 result.RevokedCerts = append(result.RevokedCerts, api.RevokedCert{Serial: req.Serial, AKI: req.AKI}) 129 } else if req.Name != "" { 130 // Authorization 131 err = checkAuth(caller, req.Name, ca) 132 if err != nil { 133 return nil, err 134 } 135 136 user, err := registry.GetUser(req.Name, nil) 137 if err != nil { 138 return nil, caerrors.NewHTTPErr(404, caerrors.ErrRevokeIDNotFound, "Identity %s was not found: %s", req.Name, err) 139 } 140 141 // Set user state to -1 for revoked user 142 if user != nil { 143 caller, err := ctx.GetCaller() 144 if err != nil { 145 return nil, err 146 } 147 148 if caller.GetName() != user.GetName() { 149 err = ctx.CanManageUser(user) 150 if err != nil { 151 return nil, err 152 } 153 } 154 155 err = user.Revoke() 156 if err != nil { 157 return nil, caerrors.NewHTTPErr(500, caerrors.ErrRevokeUpdateUser, "Failed to revoke user: %s", err) 158 } 159 } 160 161 var recs []db.CertRecord 162 recs, err = certDBAccessor.RevokeCertificatesByID(req.Name, reason) 163 if err != nil { 164 return nil, caerrors.NewHTTPErr(500, caerrors.ErrNoCertsRevoked, "Failed to revoke certificates for '%s': %s", 165 req.Name, err) 166 } 167 168 if len(recs) == 0 { 169 log.Warnf("No certificates were revoked for '%s' but the ID was disabled", req.Name) 170 } else { 171 log.Debugf("Revoked the following certificates owned by '%s': %+v", req.Name, recs) 172 for _, certRec := range recs { 173 result.RevokedCerts = append(result.RevokedCerts, api.RevokedCert{AKI: certRec.AKI, Serial: certRec.Serial}) 174 } 175 } 176 } else { 177 return nil, caerrors.NewHTTPErr(400, caerrors.ErrMissingRevokeArgs, "Either Name or Serial and AKI are required for a revoke request") 178 } 179 180 log.Debugf("Revoke was successful: %+v", req) 181 182 if req.GenCRL && len(result.RevokedCerts) > 0 { 183 log.Debugf("Generating CRL") 184 crl, err := genCRL(ca, api.GenCRLRequest{CAName: ca.Config.CA.Name}) 185 if err != nil { 186 return nil, err 187 } 188 result.CRL = util.B64Encode(crl) 189 } 190 return result, nil 191 } 192 193 func parseInput(input string) string { 194 return strings.Replace(strings.TrimLeft(strings.ToLower(input), "0"), ":", "", -1) 195 } 196 197 func checkAuth(callerName, revokeUserName string, ca *CA) error { 198 if callerName != revokeUserName { 199 // Make sure that the caller has the "hf.Revoker" attribute. 200 err := ca.attributeIsTrue(callerName, "hf.Revoker") 201 if err != nil { 202 return caerrors.NewAuthorizationErr(caerrors.ErrNotRevoker, "Caller does not have authority to revoke") 203 } 204 } 205 return nil 206 }