github.com/silveraid/fabric-ca@v1.1.0-preview.0.20180127000700-71974f53ab08/lib/serverrevoke.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8                   http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package lib
    18  
    19  import (
    20  	"encoding/hex"
    21  	"strings"
    22  
    23  	"github.com/cloudflare/cfssl/log"
    24  
    25  	"github.com/hyperledger/fabric-ca/api"
    26  	"github.com/hyperledger/fabric-ca/util"
    27  )
    28  
    29  type revocationResponseNet struct {
    30  	RevokedCerts []api.RevokedCert
    31  	CRL          string
    32  }
    33  
    34  // CertificateStatus represents status of an enrollment certificate
    35  type CertificateStatus string
    36  
    37  const (
    38  	// Revoked is the status of a revoked certificate
    39  	Revoked CertificateStatus = "revoked"
    40  	// Good is the status of a active certificate
    41  	Good = "good"
    42  )
    43  
    44  func newRevokeEndpoint(s *Server) *serverEndpoint {
    45  	return &serverEndpoint{
    46  		Methods: []string{"POST"},
    47  		Handler: revokeHandler,
    48  		Server:  s,
    49  	}
    50  }
    51  
    52  // Handle an revoke request
    53  func revokeHandler(ctx *serverRequestContext) (interface{}, error) {
    54  	// Parse revoke request body
    55  	var req api.RevocationRequestNet
    56  	err := ctx.ReadBody(&req)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	// Authentication
    61  	id, err := ctx.TokenAuthentication()
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	// Get targeted CA
    66  	ca, err := ctx.GetCA()
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	// Authorization
    71  	// Make sure that the caller has the "hf.Revoker" attribute.
    72  	err = ca.attributeIsTrue(id, "hf.Revoker")
    73  	if err != nil {
    74  		return nil, newHTTPErr(401, ErrNotRevoker, "Caller does not have authority to revoke")
    75  	}
    76  
    77  	req.AKI = strings.TrimLeft(strings.ToLower(req.AKI), "0")
    78  	req.Serial = strings.TrimLeft(strings.ToLower(req.Serial), "0")
    79  
    80  	certDBAccessor := ca.certDBAccessor
    81  	registry := ca.registry
    82  	reason := util.RevocationReasonCodes[req.Reason]
    83  
    84  	result := &revocationResponseNet{}
    85  	if req.Serial != "" && req.AKI != "" {
    86  		calleraki := strings.ToLower(strings.TrimLeft(hex.EncodeToString(ctx.enrollmentCert.AuthorityKeyId), "0"))
    87  		callerserial := strings.ToLower(strings.TrimLeft(util.GetSerialAsHex(ctx.enrollmentCert.SerialNumber), "0"))
    88  
    89  		certificate, err := certDBAccessor.GetCertificateWithID(req.Serial, req.AKI)
    90  		if err != nil {
    91  			return nil, newHTTPErr(404, ErrRevCertNotFound, "Certificate with serial %s and AKI %s was not found: %s",
    92  				req.Serial, req.AKI, err)
    93  		}
    94  
    95  		if certificate.Status == string(Revoked) {
    96  			return nil, newHTTPErr(404, ErrCertAlreadyRevoked, "Certificate with serial %s and AKI %s was already revoked",
    97  				req.Serial, req.AKI)
    98  		}
    99  
   100  		if req.Name != "" && req.Name != certificate.ID {
   101  			return nil, newHTTPErr(400, ErrCertWrongOwner, "Certificate with serial %s and AKI %s is not owned by %s",
   102  				req.Serial, req.AKI, req.Name)
   103  		}
   104  
   105  		userInfo, err := registry.GetUser(certificate.ID, nil)
   106  		if err != nil {
   107  			return nil, newHTTPErr(404, ErrRevokeIDNotFound, "Identity %s was not found: %s", certificate.ID, err)
   108  		}
   109  
   110  		if !((req.AKI == calleraki) && (req.Serial == callerserial)) {
   111  			err = ctx.CanManageUser(userInfo)
   112  			if err != nil {
   113  				return nil, err
   114  			}
   115  		}
   116  
   117  		err = certDBAccessor.RevokeCertificate(req.Serial, req.AKI, reason)
   118  		if err != nil {
   119  			return nil, newHTTPErr(500, ErrRevokeFailure, "Revoke of certificate <%s,%s> failed: %s", req.Serial, req.AKI, err)
   120  		}
   121  		result.RevokedCerts = append(result.RevokedCerts, api.RevokedCert{Serial: req.Serial, AKI: req.AKI})
   122  	} else if req.Name != "" {
   123  
   124  		user, err := registry.GetUser(req.Name, nil)
   125  		if err != nil {
   126  			return nil, newHTTPErr(404, ErrRevokeIDNotFound, "Identity %s was not found: %s", req.Name, err)
   127  		}
   128  
   129  		// Set user state to -1 for revoked user
   130  		if user != nil {
   131  			caller, err := ctx.GetCaller()
   132  			if err != nil {
   133  				return nil, err
   134  			}
   135  
   136  			if caller.GetName() != user.GetName() {
   137  				err = ctx.CanManageUser(user)
   138  				if err != nil {
   139  					return nil, err
   140  				}
   141  			}
   142  
   143  			err = user.Revoke()
   144  			if err != nil {
   145  				return nil, newHTTPErr(500, ErrRevokeUpdateUser, "Failed to revoke user: %s", err)
   146  			}
   147  		}
   148  
   149  		var recs []CertRecord
   150  		recs, err = certDBAccessor.RevokeCertificatesByID(req.Name, reason)
   151  		if err != nil {
   152  			return nil, newHTTPErr(500, ErrNoCertsRevoked, "Failed to revoke certificates for '%s': %s",
   153  				req.Name, err)
   154  		}
   155  
   156  		if len(recs) == 0 {
   157  			log.Warningf("No certificates were revoked for '%s' but the ID was disabled", req.Name)
   158  		} else {
   159  			log.Debugf("Revoked the following certificates owned by '%s': %+v", req.Name, recs)
   160  			for _, certRec := range recs {
   161  				result.RevokedCerts = append(result.RevokedCerts, api.RevokedCert{AKI: certRec.AKI, Serial: certRec.Serial})
   162  			}
   163  		}
   164  	} else {
   165  		return nil, newHTTPErr(400, ErrMissingRevokeArgs, "Either Name or Serial and AKI are required for a revoke request")
   166  	}
   167  
   168  	log.Debugf("Revoke was successful: %+v", req)
   169  
   170  	if req.GenCRL && len(result.RevokedCerts) > 0 {
   171  		log.Debugf("Generating CRL")
   172  		crl, err := genCRL(ca, api.GenCRLRequest{CAName: ca.Config.CA.Name})
   173  		if err != nil {
   174  			return nil, err
   175  		}
   176  		result.CRL = util.B64Encode(crl)
   177  	}
   178  	return result, nil
   179  }