github.com/bestbeforetoday/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  }