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