github.com/zcqzcg/fabric-ca@v2.0.0-alpha.0.20200416163940-d878ee6db75a+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 // 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, errors.WithMessage(err, "Failed to find requested attributes") 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 caerrors.NewHTTPErr(400, caerrors.ErrBadCSR, "CSR Decode failed") 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 caerrors.NewHTTPErr(403, caerrors.ErrCNInvalidEnroll, "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 caerrors.NewAuthorizationErr(caerrors.ErrInvokerMissAttr, "Enrolled failed: %s", err) 200 } 201 } 202 // Check the CSR input length 203 err = csrInputLengthCheck(csrReq) 204 if err != nil { 205 return caerrors.NewHTTPErr(400, caerrors.ErrInputValidCSR, "CSR input validation failed: %s", 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 }