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  }