github.com/adecaro/fabric-ca@v2.0.0-alpha+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 }