github.com/cactusblossom/fabric-ca@v0.0.0-20200611062428-0082fc643826/lib/serverenroll.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  	"crypto/x509"
    11  	"encoding/asn1"
    12  	"encoding/pem"
    13  	"time"
    14  
    15  	"github.com/cloudflare/cfssl/config"
    16  	"github.com/cloudflare/cfssl/csr"
    17  	cferr "github.com/cloudflare/cfssl/errors"
    18  	"github.com/cloudflare/cfssl/log"
    19  	"github.com/cloudflare/cfssl/signer"
    20  	"github.com/hyperledger/fabric-ca/api"
    21  	"github.com/hyperledger/fabric-ca/lib/caerrors"
    22  	"github.com/hyperledger/fabric-ca/lib/common"
    23  	"github.com/hyperledger/fabric-ca/lib/server/user"
    24  	"github.com/hyperledger/fabric-ca/util"
    25  	"github.com/pkg/errors"
    26  )
    27  
    28  const (
    29  	commonNameLength             = 64
    30  	serialNumberLength           = 64
    31  	countryNameLength            = 2
    32  	localityNameLength           = 128
    33  	stateOrProvinceNameLength    = 128
    34  	organizationNameLength       = 64
    35  	organizationalUnitNameLength = 64
    36  )
    37  
    38  var (
    39  	// The X.509 BasicConstraints object identifier (RFC 5280, 4.2.1.9)
    40  	basicConstraintsOID   = asn1.ObjectIdentifier{2, 5, 29, 19}
    41  	commonNameOID         = asn1.ObjectIdentifier{2, 5, 4, 3}
    42  	serialNumberOID       = asn1.ObjectIdentifier{2, 5, 4, 5}
    43  	countryOID            = asn1.ObjectIdentifier{2, 5, 4, 6}
    44  	localityOID           = asn1.ObjectIdentifier{2, 5, 4, 7}
    45  	stateOID              = asn1.ObjectIdentifier{2, 5, 4, 8}
    46  	organizationOID       = asn1.ObjectIdentifier{2, 5, 4, 10}
    47  	organizationalUnitOID = asn1.ObjectIdentifier{2, 5, 4, 11}
    48  )
    49  
    50  func newEnrollEndpoint(s *Server) *serverEndpoint {
    51  	return &serverEndpoint{
    52  		Path:      "enroll",
    53  		Methods:   []string{"POST"},
    54  		Handler:   enrollHandler,
    55  		Server:    s,
    56  		successRC: 201,
    57  	}
    58  }
    59  
    60  func newReenrollEndpoint(s *Server) *serverEndpoint {
    61  	return &serverEndpoint{
    62  		Path:      "reenroll",
    63  		Methods:   []string{"POST"},
    64  		Handler:   reenrollHandler,
    65  		Server:    s,
    66  		successRC: 201,
    67  	}
    68  }
    69  
    70  // Handle an enroll request, guarded by basic authentication
    71  func enrollHandler(ctx *serverRequestContextImpl) (interface{}, error) {
    72  	id, err := ctx.BasicAuthentication()
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	resp, err := handleEnroll(ctx, id)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	err = ctx.ui.LoginComplete()
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	return resp, nil
    85  }
    86  
    87  // Handle a reenroll request, guarded by token authentication
    88  func reenrollHandler(ctx *serverRequestContextImpl) (interface{}, error) {
    89  	// Authenticate the caller
    90  	id, err := ctx.TokenAuthentication()
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	return handleEnroll(ctx, id)
    95  }
    96  
    97  // Handle the common processing for enroll and reenroll
    98  func handleEnroll(ctx *serverRequestContextImpl, id string) (interface{}, error) {
    99  	var req api.EnrollmentRequestNet
   100  	err := ctx.ReadBody(&req)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	// Get the targeted CA
   105  	ca, err := ctx.GetCA()
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	// Set expiry based on the requested CA profile else use expiry from the default
   110  	// profile
   111  	profile := ca.Config.Signing.Default
   112  	if req.Profile != "" && ca.Config.Signing != nil &&
   113  		ca.Config.Signing.Profiles != nil && ca.Config.Signing.Profiles[req.Profile] != nil {
   114  		profile = ca.Config.Signing.Profiles[req.Profile]
   115  	}
   116  	req.NotAfter = time.Now().Round(time.Minute).Add(profile.Expiry).UTC()
   117  
   118  	caexpiry, err := ca.getCACertExpiry()
   119  	if err != nil {
   120  		return nil, errors.New("Failed to get CA certificate information")
   121  	}
   122  
   123  	// Make sure requested expiration for enrollment certificate is not after CA certificate
   124  	// expiration
   125  	if !caexpiry.IsZero() && req.NotAfter.After(caexpiry) {
   126  		log.Debugf("Requested expiry '%s' is after the CA certificate expiry '%s'. Will use CA cert expiry",
   127  			req.NotAfter, caexpiry)
   128  		req.NotAfter = caexpiry
   129  	}
   130  
   131  	// Process the sign request from the caller.
   132  	// Make sure it is authorized and do any swizzling appropriate to the request.
   133  	err = processSignRequest(id, &req.SignRequest, ca, ctx)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	// Get an attribute extension if one is being requested
   138  	ext, err := ctx.GetAttrExtension(req.AttrReqs, req.Profile)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	// If there is an extension requested, add it to the request
   143  	if ext != nil {
   144  		log.Debugf("Adding attribute extension to CSR: %+v", ext)
   145  		req.Extensions = append(req.Extensions, *ext)
   146  	}
   147  	// Sign the certificate
   148  	cert, err := ca.enrollSigner.Sign(req.SignRequest)
   149  	if err != nil {
   150  		return nil, errors.WithMessage(err, "Certificate signing failure")
   151  	}
   152  	// Add server info to the response
   153  	resp := &common.EnrollmentResponseNet{
   154  		Cert: util.B64Encode(cert),
   155  	}
   156  	err = ca.fillCAInfo(&resp.ServerInfo)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  	// Success
   161  	return resp, nil
   162  }
   163  
   164  // Process the sign request.
   165  // Make any authorization checks needed, depending on the contents
   166  // of the CSR (Certificate Signing Request).
   167  // In particular, if the request is for an intermediate CA certificate,
   168  // the caller must have the "hf.IntermediateCA" attribute.
   169  // Check to see that CSR values do not exceed the character limit
   170  // as specified in RFC 3280, page 103.
   171  // Set the OU fields of the request.
   172  func processSignRequest(id string, req *signer.SignRequest, ca *CA, ctx *serverRequestContextImpl) error {
   173  	// Decode and parse the request into a CSR so we can make checks
   174  	block, _ := pem.Decode([]byte(req.Request))
   175  	if block == nil {
   176  		return cferr.New(cferr.CSRError, cferr.DecodeFailed)
   177  	}
   178  	if block.Type != "NEW CERTIFICATE REQUEST" && block.Type != "CERTIFICATE REQUEST" {
   179  		return cferr.Wrap(cferr.CSRError,
   180  			cferr.BadRequest, errors.New("not a certificate or csr"))
   181  	}
   182  	csrReq, err := x509.ParseCertificateRequest(block.Bytes)
   183  	if err != nil {
   184  		return err
   185  	}
   186  	log.Debugf("Processing sign request: id=%s, CommonName=%s, Subject=%+v", id, csrReq.Subject.CommonName, req.Subject)
   187  	if (req.Subject != nil && req.Subject.CN != id) || csrReq.Subject.CommonName != id {
   188  		return errors.New("The CSR subject common name must equal the enrollment ID")
   189  	}
   190  	isForCACert, err := isRequestForCASigningCert(csrReq, ca, req.Profile)
   191  	if err != nil {
   192  		return err
   193  	}
   194  	if isForCACert {
   195  		// This is a request for a CA certificate, so make sure the caller
   196  		// has the 'hf.IntermediateCA' attribute
   197  		err := ca.attributeIsTrue(id, "hf.IntermediateCA")
   198  		if err != nil {
   199  			return err
   200  		}
   201  	}
   202  	// Check the CSR input length
   203  	err = csrInputLengthCheck(csrReq)
   204  	if err != nil {
   205  		return err
   206  	}
   207  	caller, err := ctx.GetCaller()
   208  	if err != nil {
   209  		return err
   210  	}
   211  	// Set the OUs in the request appropriately.
   212  	setRequestOUs(req, caller)
   213  	log.Debug("Finished processing sign request")
   214  	return nil
   215  }
   216  
   217  // Check to see if this is a request for a CA signing certificate.
   218  // This can occur if the profile or the CSR has the IsCA bit set.
   219  // See the X.509 BasicConstraints extension (RFC 5280, 4.2.1.9).
   220  func isRequestForCASigningCert(csrReq *x509.CertificateRequest, ca *CA, profile string) (bool, error) {
   221  	// Check the profile to see if the IsCA bit is set
   222  	sp := getSigningProfile(ca, profile)
   223  	if sp == nil {
   224  		return false, errors.Errorf("Invalid profile: '%s'", profile)
   225  	}
   226  	if sp.CAConstraint.IsCA {
   227  		log.Debugf("Request is for a CA signing certificate as set in profile '%s'", profile)
   228  		return true, nil
   229  	}
   230  	// Check the CSR to see if the IsCA bit is set
   231  	for _, val := range csrReq.Extensions {
   232  		if val.Id.Equal(basicConstraintsOID) {
   233  			var constraints csr.BasicConstraints
   234  			var rest []byte
   235  			var err error
   236  			if rest, err = asn1.Unmarshal(val.Value, &constraints); err != nil {
   237  				return false, caerrors.NewHTTPErr(400, caerrors.ErrBadCSR, "Failed parsing CSR constraints: %s", err)
   238  			} else if len(rest) != 0 {
   239  				return false, caerrors.NewHTTPErr(400, caerrors.ErrBadCSR, "Trailing data after X.509 BasicConstraints")
   240  			}
   241  			if constraints.IsCA {
   242  				log.Debug("Request is for a CA signing certificate as indicated in the CSR")
   243  				return true, nil
   244  			}
   245  		}
   246  	}
   247  	// The IsCA bit was not set
   248  	log.Debug("Request is not for a CA signing certificate")
   249  	return false, nil
   250  }
   251  
   252  func getSigningProfile(ca *CA, profile string) *config.SigningProfile {
   253  	if profile == "" {
   254  		return ca.Config.Signing.Default
   255  	}
   256  	return ca.Config.Signing.Profiles[profile]
   257  }
   258  
   259  // Checks to make sure that character limits are not exceeded for CSR fields
   260  func csrInputLengthCheck(req *x509.CertificateRequest) error {
   261  	log.Debug("Checking CSR fields to make sure that they do not exceed maximum character limits")
   262  
   263  	for _, n := range req.Subject.Names {
   264  		value := n.Value.(string)
   265  		switch {
   266  		case n.Type.Equal(commonNameOID):
   267  			if len(value) > commonNameLength {
   268  				return errors.Errorf("The CN '%s' exceeds the maximum character limit of %d", value, commonNameLength)
   269  			}
   270  		case n.Type.Equal(serialNumberOID):
   271  			if len(value) > serialNumberLength {
   272  				return errors.Errorf("The serial number '%s' exceeds the maximum character limit of %d", value, serialNumberLength)
   273  			}
   274  		case n.Type.Equal(organizationalUnitOID):
   275  			if len(value) > organizationalUnitNameLength {
   276  				return errors.Errorf("The organizational unit name '%s' exceeds the maximum character limit of %d", value, organizationalUnitNameLength)
   277  			}
   278  		case n.Type.Equal(organizationOID):
   279  			if len(value) > organizationNameLength {
   280  				return errors.Errorf("The organization name '%s' exceeds the maximum character limit of %d", value, organizationNameLength)
   281  			}
   282  		case n.Type.Equal(countryOID):
   283  			if len(value) > countryNameLength {
   284  				return errors.Errorf("The country name '%s' exceeds the maximum character limit of %d", value, countryNameLength)
   285  			}
   286  		case n.Type.Equal(localityOID):
   287  			if len(value) > localityNameLength {
   288  				return errors.Errorf("The locality name '%s' exceeds the maximum character limit of %d", value, localityNameLength)
   289  			}
   290  		case n.Type.Equal(stateOID):
   291  			if len(value) > stateOrProvinceNameLength {
   292  				return errors.Errorf("The state name '%s' exceeds the maximum character limit of %d", value, stateOrProvinceNameLength)
   293  			}
   294  		}
   295  	}
   296  
   297  	return nil
   298  }
   299  
   300  // Set the OU fields of the sign request based on the identity's type and affilation.
   301  // For example, if the type is 'peer' and the affiliation is 'a.b.c', the
   302  // OUs become 'OU=c,OU=b,OU=a,OU=peer'.
   303  // This is necessary because authorization decisions are made based on the OU fields,
   304  // so we ignore any OU values specified in the enroll request and set them according
   305  // to the type and affiliation.
   306  func setRequestOUs(req *signer.SignRequest, caller user.User) {
   307  	s := req.Subject
   308  	if s == nil {
   309  		s = &signer.Subject{}
   310  	}
   311  	names := []csr.Name{}
   312  	// Add non-OU fields from request
   313  	for _, name := range s.Names {
   314  		if name.C != "" || name.L != "" || name.O != "" || name.ST != "" || name.SerialNumber != "" {
   315  			name.OU = ""
   316  			names = append(names, name)
   317  		}
   318  	}
   319  	// Add an OU field with the type
   320  	names = append(names, csr.Name{OU: caller.GetType()})
   321  	for _, aff := range caller.GetAffiliationPath() {
   322  		names = append(names, csr.Name{OU: aff})
   323  	}
   324  	// Replace with new names
   325  	s.Names = names
   326  	req.Subject = s
   327  }