github.com/hyperledger-gerrit-archive/fabric-ca@v2.0.0-alpha.0.20190916143245-4cd4192f0366+incompatible/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  	// If NotAfter is not set in the request, then set it to the expiry in the
   110  	// specified profile
   111  	if req.NotAfter.IsZero() {
   112  		profile := ca.Config.Signing.Default
   113  		if req.Profile != "" && ca.Config.Signing != nil &&
   114  			ca.Config.Signing.Profiles != nil && ca.Config.Signing.Profiles[req.Profile] != nil {
   115  			profile = ca.Config.Signing.Profiles[req.Profile]
   116  		}
   117  		req.NotAfter = time.Now().Round(time.Minute).Add(profile.Expiry).UTC()
   118  	}
   119  	caexpiry, err := ca.getCACertExpiry()
   120  	if err != nil {
   121  		return nil, errors.New("Failed to get CA certificate information")
   122  	}
   123  
   124  	// Make sure requested expiration for enrollment certificate is not after CA certificate
   125  	// expiration
   126  	if !caexpiry.IsZero() && req.NotAfter.After(caexpiry) {
   127  		log.Debugf("Requested expiry '%s' is after the CA certificate expiry '%s'. Will use CA cert expiry",
   128  			req.NotAfter, caexpiry)
   129  		req.NotAfter = caexpiry
   130  	}
   131  
   132  	// Process the sign request from the caller.
   133  	// Make sure it is authorized and do any swizzling appropriate to the request.
   134  	err = processSignRequest(id, &req.SignRequest, ca, ctx)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	// Get an attribute extension if one is being requested
   139  	ext, err := ctx.GetAttrExtension(req.AttrReqs, req.Profile)
   140  	if err != nil {
   141  		return nil, errors.WithMessage(err, "Failed to find requested attributes")
   142  	}
   143  	// If there is an extension requested, add it to the request
   144  	if ext != nil {
   145  		log.Debugf("Adding attribute extension to CSR: %+v", ext)
   146  		req.Extensions = append(req.Extensions, *ext)
   147  	}
   148  	// Sign the certificate
   149  	cert, err := ca.enrollSigner.Sign(req.SignRequest)
   150  	if err != nil {
   151  		return nil, errors.WithMessage(err, "Certificate signing failure")
   152  	}
   153  	// Add server info to the response
   154  	resp := &common.EnrollmentResponseNet{
   155  		Cert: util.B64Encode(cert),
   156  	}
   157  	err = ca.fillCAInfo(&resp.ServerInfo)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	// Success
   162  	return resp, nil
   163  }
   164  
   165  // Process the sign request.
   166  // Make any authorization checks needed, depending on the contents
   167  // of the CSR (Certificate Signing Request).
   168  // In particular, if the request is for an intermediate CA certificate,
   169  // the caller must have the "hf.IntermediateCA" attribute.
   170  // Check to see that CSR values do not exceed the character limit
   171  // as specified in RFC 3280, page 103.
   172  // Set the OU fields of the request.
   173  func processSignRequest(id string, req *signer.SignRequest, ca *CA, ctx *serverRequestContextImpl) error {
   174  	// Decode and parse the request into a CSR so we can make checks
   175  	block, _ := pem.Decode([]byte(req.Request))
   176  	if block == nil {
   177  		return caerrors.NewHTTPErr(400, caerrors.ErrBadCSR, "CSR Decode failed")
   178  	}
   179  	if block.Type != "NEW CERTIFICATE REQUEST" && block.Type != "CERTIFICATE REQUEST" {
   180  		return cferr.Wrap(cferr.CSRError,
   181  			cferr.BadRequest, errors.New("not a certificate or csr"))
   182  	}
   183  	csrReq, err := x509.ParseCertificateRequest(block.Bytes)
   184  	if err != nil {
   185  		return err
   186  	}
   187  	log.Debugf("Processing sign request: id=%s, CommonName=%s, Subject=%+v", id, csrReq.Subject.CommonName, req.Subject)
   188  	if (req.Subject != nil && req.Subject.CN != id) || csrReq.Subject.CommonName != id {
   189  		return caerrors.NewHTTPErr(403, caerrors.ErrCNInvalidEnroll, "The CSR subject common name must equal the enrollment ID")
   190  	}
   191  	isForCACert, err := isRequestForCASigningCert(csrReq, ca, req.Profile)
   192  	if err != nil {
   193  		return err
   194  	}
   195  	if isForCACert {
   196  		// This is a request for a CA certificate, so make sure the caller
   197  		// has the 'hf.IntermediateCA' attribute
   198  		err := ca.attributeIsTrue(id, "hf.IntermediateCA")
   199  		if err != nil {
   200  			return caerrors.NewAuthorizationErr(caerrors.ErrInvokerMissAttr, "Enrolled failed: %s", err)
   201  		}
   202  	}
   203  	// Check the CSR input length
   204  	err = csrInputLengthCheck(csrReq)
   205  	if err != nil {
   206  		return caerrors.NewHTTPErr(400, caerrors.ErrInputValidCSR, "CSR input validation failed: %s", err)
   207  	}
   208  	caller, err := ctx.GetCaller()
   209  	if err != nil {
   210  		return err
   211  	}
   212  	// Set the OUs in the request appropriately.
   213  	setRequestOUs(req, caller)
   214  	log.Debug("Finished processing sign request")
   215  	return nil
   216  }
   217  
   218  // Check to see if this is a request for a CA signing certificate.
   219  // This can occur if the profile or the CSR has the IsCA bit set.
   220  // See the X.509 BasicConstraints extension (RFC 5280, 4.2.1.9).
   221  func isRequestForCASigningCert(csrReq *x509.CertificateRequest, ca *CA, profile string) (bool, error) {
   222  	// Check the profile to see if the IsCA bit is set
   223  	sp := getSigningProfile(ca, profile)
   224  	if sp == nil {
   225  		return false, errors.Errorf("Invalid profile: '%s'", profile)
   226  	}
   227  	if sp.CAConstraint.IsCA {
   228  		log.Debugf("Request is for a CA signing certificate as set in profile '%s'", profile)
   229  		return true, nil
   230  	}
   231  	// Check the CSR to see if the IsCA bit is set
   232  	for _, val := range csrReq.Extensions {
   233  		if val.Id.Equal(basicConstraintsOID) {
   234  			var constraints csr.BasicConstraints
   235  			var rest []byte
   236  			var err error
   237  			if rest, err = asn1.Unmarshal(val.Value, &constraints); err != nil {
   238  				return false, caerrors.NewHTTPErr(400, caerrors.ErrBadCSR, "Failed parsing CSR constraints: %s", err)
   239  			} else if len(rest) != 0 {
   240  				return false, caerrors.NewHTTPErr(400, caerrors.ErrBadCSR, "Trailing data after X.509 BasicConstraints")
   241  			}
   242  			if constraints.IsCA {
   243  				log.Debug("Request is for a CA signing certificate as indicated in the CSR")
   244  				return true, nil
   245  			}
   246  		}
   247  	}
   248  	// The IsCA bit was not set
   249  	log.Debug("Request is not for a CA signing certificate")
   250  	return false, nil
   251  }
   252  
   253  func getSigningProfile(ca *CA, profile string) *config.SigningProfile {
   254  	if profile == "" {
   255  		return ca.Config.Signing.Default
   256  	}
   257  	return ca.Config.Signing.Profiles[profile]
   258  }
   259  
   260  // Checks to make sure that character limits are not exceeded for CSR fields
   261  func csrInputLengthCheck(req *x509.CertificateRequest) error {
   262  	log.Debug("Checking CSR fields to make sure that they do not exceed maximum character limits")
   263  
   264  	for _, n := range req.Subject.Names {
   265  		value := n.Value.(string)
   266  		switch {
   267  		case n.Type.Equal(commonNameOID):
   268  			if len(value) > commonNameLength {
   269  				return errors.Errorf("The CN '%s' exceeds the maximum character limit of %d", value, commonNameLength)
   270  			}
   271  		case n.Type.Equal(serialNumberOID):
   272  			if len(value) > serialNumberLength {
   273  				return errors.Errorf("The serial number '%s' exceeds the maximum character limit of %d", value, serialNumberLength)
   274  			}
   275  		case n.Type.Equal(organizationalUnitOID):
   276  			if len(value) > organizationalUnitNameLength {
   277  				return errors.Errorf("The organizational unit name '%s' exceeds the maximum character limit of %d", value, organizationalUnitNameLength)
   278  			}
   279  		case n.Type.Equal(organizationOID):
   280  			if len(value) > organizationNameLength {
   281  				return errors.Errorf("The organization name '%s' exceeds the maximum character limit of %d", value, organizationNameLength)
   282  			}
   283  		case n.Type.Equal(countryOID):
   284  			if len(value) > countryNameLength {
   285  				return errors.Errorf("The country name '%s' exceeds the maximum character limit of %d", value, countryNameLength)
   286  			}
   287  		case n.Type.Equal(localityOID):
   288  			if len(value) > localityNameLength {
   289  				return errors.Errorf("The locality name '%s' exceeds the maximum character limit of %d", value, localityNameLength)
   290  			}
   291  		case n.Type.Equal(stateOID):
   292  			if len(value) > stateOrProvinceNameLength {
   293  				return errors.Errorf("The state name '%s' exceeds the maximum character limit of %d", value, stateOrProvinceNameLength)
   294  			}
   295  		}
   296  	}
   297  
   298  	return nil
   299  }
   300  
   301  // Set the OU fields of the sign request based on the identity's type and affilation.
   302  // For example, if the type is 'peer' and the affiliation is 'a.b.c', the
   303  // OUs become 'OU=c,OU=b,OU=a,OU=peer'.
   304  // This is necessary because authorization decisions are made based on the OU fields,
   305  // so we ignore any OU values specified in the enroll request and set them according
   306  // to the type and affiliation.
   307  func setRequestOUs(req *signer.SignRequest, caller user.User) {
   308  	s := req.Subject
   309  	if s == nil {
   310  		s = &signer.Subject{}
   311  	}
   312  	names := []csr.Name{}
   313  	// Add non-OU fields from request
   314  	for _, name := range s.Names {
   315  		if name.C != "" || name.L != "" || name.O != "" || name.ST != "" || name.SerialNumber != "" {
   316  			name.OU = ""
   317  			names = append(names, name)
   318  		}
   319  	}
   320  	// Add an OU field with the type
   321  	names = append(names, csr.Name{OU: caller.GetType()})
   322  	for _, aff := range caller.GetAffiliationPath() {
   323  		names = append(names, csr.Name{OU: aff})
   324  	}
   325  	// Replace with new names
   326  	s.Names = names
   327  	req.Subject = s
   328  }