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 }