gitee.com/hyperledger/fabric-ca@v2.0.0-alpha+incompatible/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 "github.com/cloudflare/cfssl/log" 14 "github.com/hyperledger/fabric-ca/api" 15 "github.com/hyperledger/fabric-ca/lib/caerrors" 16 "github.com/hyperledger/fabric-ca/lib/server/db" 17 "github.com/hyperledger/fabric-ca/util" 18 ) 19 20 type revocationResponseNet struct { 21 RevokedCerts []api.RevokedCert 22 CRL string 23 } 24 25 // CertificateStatus represents status of an enrollment certificate 26 type CertificateStatus string 27 28 const ( 29 // Revoked is the status of a revoked certificate 30 Revoked CertificateStatus = "revoked" 31 // Good is the status of a active certificate 32 Good = "good" 33 ) 34 35 func newRevokeEndpoint(s *Server) *serverEndpoint { 36 return &serverEndpoint{ 37 Path: "revoke", 38 Methods: []string{"POST"}, 39 Handler: revokeHandler, 40 Server: s, 41 } 42 } 43 44 // Handle an revoke request 45 func revokeHandler(ctx *serverRequestContextImpl) (interface{}, error) { 46 // Parse revoke request body 47 var req api.RevocationRequestNet 48 err := ctx.ReadBody(&req) 49 if err != nil { 50 return nil, err 51 } 52 // Authentication 53 caller, err := ctx.TokenAuthentication() 54 if err != nil { 55 return nil, err 56 } 57 // Get targeted CA 58 ca, err := ctx.GetCA() 59 if err != nil { 60 return nil, err 61 } 62 63 req.AKI = parseInput(req.AKI) 64 req.Serial = parseInput(req.Serial) 65 66 certDBAccessor := ca.certDBAccessor 67 registry := ca.registry 68 reason := util.RevocationReasonCodes[req.Reason] 69 70 result := &revocationResponseNet{} 71 if req.Serial != "" && req.AKI != "" { 72 calleraki := strings.ToLower(strings.TrimLeft(hex.EncodeToString(ctx.enrollmentCert.AuthorityKeyId), "0")) 73 callerserial := strings.ToLower(strings.TrimLeft(util.GetSerialAsHex(ctx.enrollmentCert.SerialNumber), "0")) 74 75 certificate, err := certDBAccessor.GetCertificateWithID(req.Serial, req.AKI) 76 if err != nil { 77 return nil, caerrors.NewHTTPErr(404, caerrors.ErrRevCertNotFound, "Certificate with serial %s and AKI %s was not found: %s", 78 req.Serial, req.AKI, err) 79 } 80 81 // Authorization 82 err = checkAuth(caller, certificate.ID, ca) 83 if err != nil { 84 return nil, err 85 } 86 87 if certificate.Status == string(Revoked) { 88 return nil, caerrors.NewHTTPErr(404, caerrors.ErrCertAlreadyRevoked, "Certificate with serial %s and AKI %s was already revoked", 89 req.Serial, req.AKI) 90 } 91 92 if req.Name != "" && req.Name != certificate.ID { 93 return nil, caerrors.NewHTTPErr(400, caerrors.ErrCertWrongOwner, "Certificate with serial %s and AKI %s is not owned by %s", 94 req.Serial, req.AKI, req.Name) 95 } 96 97 userInfo, err := registry.GetUser(certificate.ID, nil) 98 if err != nil { 99 return nil, caerrors.NewHTTPErr(404, caerrors.ErrRevokeIDNotFound, "Identity %s was not found: %s", certificate.ID, err) 100 } 101 102 if !((req.AKI == calleraki) && (req.Serial == callerserial)) { 103 err = ctx.CanManageUser(userInfo) 104 if err != nil { 105 return nil, err 106 } 107 } 108 109 err = certDBAccessor.RevokeCertificate(req.Serial, req.AKI, reason) 110 if err != nil { 111 return nil, caerrors.NewHTTPErr(500, caerrors.ErrRevokeFailure, "Revoke of certificate <%s,%s> failed: %s", req.Serial, req.AKI, err) 112 } 113 result.RevokedCerts = append(result.RevokedCerts, api.RevokedCert{Serial: req.Serial, AKI: req.AKI}) 114 } else if req.Name != "" { 115 // Authorization 116 err = checkAuth(caller, req.Name, ca) 117 if err != nil { 118 return nil, err 119 } 120 121 user, err := registry.GetUser(req.Name, nil) 122 if err != nil { 123 return nil, caerrors.NewHTTPErr(404, caerrors.ErrRevokeIDNotFound, "Identity %s was not found: %s", req.Name, err) 124 } 125 126 // Set user state to -1 for revoked user 127 if user != nil { 128 caller, err := ctx.GetCaller() 129 if err != nil { 130 return nil, err 131 } 132 133 if caller.GetName() != user.GetName() { 134 err = ctx.CanManageUser(user) 135 if err != nil { 136 return nil, err 137 } 138 } 139 140 err = user.Revoke() 141 if err != nil { 142 return nil, caerrors.NewHTTPErr(500, caerrors.ErrRevokeUpdateUser, "Failed to revoke user: %s", err) 143 } 144 } 145 146 var recs []db.CertRecord 147 recs, err = certDBAccessor.RevokeCertificatesByID(req.Name, reason) 148 if err != nil { 149 return nil, caerrors.NewHTTPErr(500, caerrors.ErrNoCertsRevoked, "Failed to revoke certificates for '%s': %s", 150 req.Name, err) 151 } 152 153 if len(recs) == 0 { 154 log.Warningf("No certificates were revoked for '%s' but the ID was disabled", req.Name) 155 } else { 156 log.Debugf("Revoked the following certificates owned by '%s': %+v", req.Name, recs) 157 for _, certRec := range recs { 158 result.RevokedCerts = append(result.RevokedCerts, api.RevokedCert{AKI: certRec.AKI, Serial: certRec.Serial}) 159 } 160 } 161 } else { 162 return nil, caerrors.NewHTTPErr(400, caerrors.ErrMissingRevokeArgs, "Either Name or Serial and AKI are required for a revoke request") 163 } 164 165 log.Debugf("Revoke was successful: %+v", req) 166 167 if req.GenCRL && len(result.RevokedCerts) > 0 { 168 log.Debugf("Generating CRL") 169 crl, err := genCRL(ca, api.GenCRLRequest{CAName: ca.Config.CA.Name}) 170 if err != nil { 171 return nil, err 172 } 173 result.CRL = util.B64Encode(crl) 174 } 175 return result, nil 176 } 177 178 func parseInput(input string) string { 179 return strings.Replace(strings.TrimLeft(strings.ToLower(input), "0"), ":", "", -1) 180 } 181 182 func checkAuth(callerName, revokeUserName string, ca *CA) error { 183 if callerName != revokeUserName { 184 // Make sure that the caller has the "hf.Revoker" attribute. 185 err := ca.attributeIsTrue(callerName, "hf.Revoker") 186 if err != nil { 187 return caerrors.NewAuthorizationErr(caerrors.ErrNotRevoker, "Caller does not have authority to revoke") 188 } 189 } 190 return nil 191 }