// NewRegistration constructs a new Registration from a request. func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) { if err = core.GoodKey(init.Key.Key); err != nil { return core.Registration{}, core.MalformedRequestError(fmt.Sprintf("Invalid public key: %s", err.Error())) } if err = ra.checkRegistrationLimit(init.InitialIP); err != nil { return core.Registration{}, err } reg = core.Registration{ Key: init.Key, } reg.MergeUpdate(init) // This field isn't updatable by the end user, so it isn't copied by // MergeUpdate. But we need to fill it in for new registrations. reg.InitialIP = init.InitialIP err = ra.validateContacts(reg.Contact) if err != nil { return } // Store the authorization object, then return it reg, err = ra.SA.NewRegistration(reg) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(err.Error()) } ra.stats.Inc("RA.NewRegistrations", 1, 1.0) return }
// Unwraps a rpcError and returns the correct error type. func unwrapError(rpcError *rpcError) error { if rpcError != nil { switch rpcError.Type { case "InternalServerError": return core.InternalServerError(rpcError.Value) case "NotSupportedError": return core.NotSupportedError(rpcError.Value) case "MalformedRequestError": return core.MalformedRequestError(rpcError.Value) case "UnauthorizedError": return core.UnauthorizedError(rpcError.Value) case "NotFoundError": return core.NotFoundError(rpcError.Value) case "SignatureValidationError": return core.SignatureValidationError(rpcError.Value) case "NoSuchRegistrationError": return core.NoSuchRegistrationError(rpcError.Value) case "TooManyRPCRequestsError": return core.TooManyRPCRequestsError(rpcError.Value) case "RateLimitedError": return core.RateLimitedError(rpcError.Value) default: if strings.HasPrefix(rpcError.Type, "urn:") { return &probs.ProblemDetails{ Type: probs.ProblemType(rpcError.Type), Detail: rpcError.Value, HTTPStatus: rpcError.HTTPStatus, } } return errors.New(rpcError.Value) } } return nil }
// Unwraps a rpcError and returns the correct error type. func unwrapError(rpcError *rpcError) error { if rpcError != nil { switch rpcError.Type { case "InternalServerError": return core.InternalServerError(rpcError.Value) case "NotSupportedError": return core.NotSupportedError(rpcError.Value) case "MalformedRequestError": return core.MalformedRequestError(rpcError.Value) case "UnauthorizedError": return core.UnauthorizedError(rpcError.Value) case "NotFoundError": return core.NotFoundError(rpcError.Value) case "SyntaxError": return core.SyntaxError(rpcError.Value) case "SignatureValidationError": return core.SignatureValidationError(rpcError.Value) case "CertificateIssuanceError": return core.CertificateIssuanceError(rpcError.Value) case "NoSuchRegistrationError": return core.NoSuchRegistrationError(rpcError.Value) case "TooManyRPCRequestsError": return core.TooManyRPCRequestsError(rpcError.Value) case "RateLimitedError": return core.RateLimitedError(rpcError.Value) case "ServiceUnavailableError": return core.ServiceUnavailableError(rpcError.Value) default: return errors.New(rpcError.Value) } } return nil }
// UpdateAuthorization updates an authorization with new values. func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) { // Copy information over that the client is allowed to supply authz = base if challengeIndex >= len(authz.Challenges) { err = core.MalformedRequestError(fmt.Sprintf("Invalid challenge index: %d", challengeIndex)) return } authz.Challenges[challengeIndex] = authz.Challenges[challengeIndex].MergeResponse(response) // Store the updated version if err = ra.SA.UpdatePendingAuthorization(authz); err != nil { // This can pretty much only happen when the client corrupts the Challenge // data. err = core.MalformedRequestError("Challenge data was corrupted") return } // Look up the account key for this authorization reg, err := ra.SA.GetRegistration(authz.RegistrationID) if err != nil { err = core.InternalServerError(err.Error()) return } // Dispatch to the VA for service ra.VA.UpdateValidations(authz, challengeIndex, reg.Key) return }
// NewRegistration constructs a new Registration from a request. func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) { if err = core.GoodKey(init.Key.Key); err != nil { return core.Registration{}, core.MalformedRequestError(fmt.Sprintf("Invalid public key: %s", err.Error())) } reg = core.Registration{ Key: init.Key, } reg.MergeUpdate(init) err = validateContacts(reg.Contact, ra.DNSResolver, ra.stats) if err != nil { return } // Store the authorization object, then return it reg, err = ra.SA.NewRegistration(reg) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(err.Error()) } ra.stats.Inc("RA.NewRegistrations", 1, 1.0) return }
// Unwraps a RPCError and returns the correct error type. func unwrapError(rpcError RPCError) (err error) { if rpcError.Value != "" { switch rpcError.Type { case "InternalServerError": err = core.InternalServerError(rpcError.Value) case "NotSupportedError": err = core.NotSupportedError(rpcError.Value) case "MalformedRequestError": err = core.MalformedRequestError(rpcError.Value) case "UnauthorizedError": err = core.UnauthorizedError(rpcError.Value) case "NotFoundError": err = core.NotFoundError(rpcError.Value) case "SyntaxError": err = core.SyntaxError(rpcError.Value) case "SignatureValidationError": err = core.SignatureValidationError(rpcError.Value) case "CertificateIssuanceError": err = core.CertificateIssuanceError(rpcError.Value) default: err = errors.New(rpcError.Value) } } return }
func unwrapError(err error) error { code := grpc.Code(err) errBody := grpc.ErrorDesc(err) switch code { case InternalServerError: return core.InternalServerError(errBody) case NotSupportedError: return core.NotSupportedError(errBody) case MalformedRequestError: return core.MalformedRequestError(errBody) case UnauthorizedError: return core.UnauthorizedError(errBody) case NotFoundError: return core.NotFoundError(errBody) case SignatureValidationError: return core.SignatureValidationError(errBody) case NoSuchRegistrationError: return core.NoSuchRegistrationError(errBody) case RateLimitedError: return core.RateLimitedError(errBody) case LengthRequiredError: return core.LengthRequiredError(errBody) case BadNonceError: return core.BadNonceError(errBody) default: return err } }
func TestWrapError(t *testing.T) { testCases := []error{ core.InternalServerError("foo"), core.NotSupportedError("foo"), core.MalformedRequestError("foo"), core.UnauthorizedError("foo"), core.NotFoundError("foo"), core.SignatureValidationError("foo"), core.CertificateIssuanceError("foo"), core.NoSuchRegistrationError("foo"), core.RateLimitedError("foo"), core.TooManyRPCRequestsError("foo"), errors.New("foo"), } for _, c := range testCases { wrapped := wrapError(c) test.AssertEquals(t, wrapped.Type, reflect.TypeOf(c).Name()) test.AssertEquals(t, wrapped.Value, "foo") unwrapped := unwrapError(wrapped) test.AssertEquals(t, wrapped.Type, reflect.TypeOf(unwrapped).Name()) test.AssertEquals(t, unwrapped.Error(), "foo") } complicated := []struct { given error expected error }{ { &probs.ProblemDetails{ Type: probs.ConnectionProblem, Detail: "whoops", HTTPStatus: 417, }, &probs.ProblemDetails{ Type: probs.ConnectionProblem, Detail: "whoops", HTTPStatus: 417, }, }, { &probs.ProblemDetails{Type: "invalid", Detail: "hm"}, errors.New("hm"), }, { errors.New(""), errors.New(""), }, } for i, tc := range complicated { actual := unwrapError(wrapError(tc.given)) if !reflect.DeepEqual(tc.expected, actual) { t.Errorf("rpc error wrapping case %d: want %#v, got %#v", i, tc.expected, actual) } } }
// DeactivateAuthorization deactivates a currently valid authorization func (ra *RegistrationAuthorityImpl) DeactivateAuthorization(ctx context.Context, auth core.Authorization) error { if auth.Status != core.StatusValid && auth.Status != core.StatusPending { return core.MalformedRequestError("Only valid and pending authorizations can be deactivated") } err := ra.SA.DeactivateAuthorization(ctx, auth.ID) if err != nil { return core.InternalServerError(err.Error()) } return nil }
// DeactivateRegistration deactivates a valid registration func (ra *RegistrationAuthorityImpl) DeactivateRegistration(ctx context.Context, reg core.Registration) error { if reg.Status != core.StatusValid { return core.MalformedRequestError("Only valid registrations can be deactivated") } err := ra.SA.DeactivateRegistration(ctx, reg.ID) if err != nil { return core.InternalServerError(err.Error()) } return nil }
// UpdateAuthorization updates an authorization with new values. func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) { // Refuse to update expired authorizations if base.Expires == nil || base.Expires.Before(ra.clk.Now()) { err = core.NotFoundError("Expired authorization") return } // Copy information over that the client is allowed to supply authz = base if challengeIndex >= len(authz.Challenges) { err = core.MalformedRequestError(fmt.Sprintf("Invalid challenge index: %d", challengeIndex)) return } authz.Challenges[challengeIndex].KeyAuthorization = response.KeyAuthorization // At this point, the challenge should be sane as a complete challenge if !authz.Challenges[challengeIndex].IsSane(true) { err = core.MalformedRequestError("Response does not complete challenge") return } // Store the updated version if err = ra.SA.UpdatePendingAuthorization(authz); err != nil { // This can pretty much only happen when the client corrupts the Challenge // data. err = core.MalformedRequestError("Challenge data was corrupted") return } ra.stats.Inc("RA.NewPendingAuthorizations", 1, 1.0) // Look up the account key for this authorization reg, err := ra.SA.GetRegistration(authz.RegistrationID) if err != nil { err = core.InternalServerError(err.Error()) return } // Reject the update if the challenge in question was created // with a different account key if !core.KeyDigestEquals(reg.Key, authz.Challenges[challengeIndex].AccountKey) { err = core.UnauthorizedError("Challenge cannot be updated with a different key") return } // Dispatch to the VA for service ra.VA.UpdateValidations(authz, challengeIndex) ra.stats.Inc("RA.UpdatedPendingAuthorizations", 1, 1.0) return }
// UpdateRegistration updates an existing Registration with new values. func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration, update core.Registration) (reg core.Registration, err error) { base.MergeUpdate(update) err = validateContacts(base.Contact, ra.DNSResolver) if err != nil { return } reg = base err = ra.SA.UpdateRegistration(base) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(fmt.Sprintf("Could not update registration: %s", err)) } return }
func (ra *RegistrationAuthorityImpl) checkTotalCertificatesLimit() error { totalCertLimits := ra.rlPolicies.TotalCertificates() ra.tiMu.RLock() defer ra.tiMu.RUnlock() // If last update of the total issued count was more than five minutes ago, // or not yet updated, fail. if ra.clk.Now().After(ra.totalIssuedLastUpdate.Add(5*time.Minute)) || ra.totalIssuedLastUpdate.IsZero() { return core.InternalServerError(fmt.Sprintf("Total certificate count out of date: updated %s", ra.totalIssuedLastUpdate)) } if ra.totalIssuedCount >= totalCertLimits.Threshold { ra.totalCertsStats.Inc("Exceeded", 1) ra.log.Info(fmt.Sprintf("Rate limit exceeded, TotalCertificates, totalIssued: %d, lastUpdated %s", ra.totalIssuedCount, ra.totalIssuedLastUpdate)) return core.RateLimitedError("Global certificate issuance limit reached. Try again in an hour.") } ra.totalCertsStats.Inc("Pass", 1) return nil }
// UpdateRegistration updates an existing Registration with new values. func (ra *RegistrationAuthorityImpl) UpdateRegistration(ctx context.Context, base core.Registration, update core.Registration) (reg core.Registration, err error) { base.MergeUpdate(update) err = ra.validateContacts(ctx, base.Contact) if err != nil { return } reg = base err = ra.SA.UpdateRegistration(ctx, base) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(fmt.Sprintf("Could not update registration: %s", err)) } ra.stats.Inc("RA.UpdatedRegistrations", 1, 1.0) return }
func TestWrapError(t *testing.T) { testCases := []error{ core.InternalServerError("foo"), core.NotSupportedError("foo"), core.MalformedRequestError("foo"), core.UnauthorizedError("foo"), core.NotFoundError("foo"), core.SyntaxError("foo"), core.SignatureValidationError("foo"), core.CertificateIssuanceError("foo"), core.NoSuchRegistrationError("foo"), core.RateLimitedError("foo"), core.TooManyRPCRequestsError("foo"), } for _, c := range testCases { wrapped := wrapError(c) test.AssertEquals(t, wrapped.Type, reflect.TypeOf(c).Name()) test.AssertEquals(t, wrapped.Value, "foo") unwrapped := unwrapError(wrapped) test.AssertEquals(t, wrapped.Type, reflect.TypeOf(unwrapped).Name()) test.AssertEquals(t, unwrapped.Error(), "foo") } }
func TestErrors(t *testing.T) { testcases := []struct { err error expectedCode codes.Code }{ {core.MalformedRequestError("test 1"), MalformedRequestError}, {core.NotSupportedError("test 2"), NotSupportedError}, {core.UnauthorizedError("test 3"), UnauthorizedError}, {core.NotFoundError("test 4"), NotFoundError}, {core.LengthRequiredError("test 5"), LengthRequiredError}, {core.SignatureValidationError("test 6"), SignatureValidationError}, {core.RateLimitedError("test 7"), RateLimitedError}, {core.BadNonceError("test 8"), BadNonceError}, {core.NoSuchRegistrationError("test 9"), NoSuchRegistrationError}, {core.InternalServerError("test 10"), InternalServerError}, } for _, tc := range testcases { wrappedErr := wrapError(tc.err) test.AssertEquals(t, grpc.Code(wrappedErr), tc.expectedCode) test.AssertEquals(t, tc.err, unwrapError(wrappedErr)) } }
// UpdateRegistration updates an existing Registration with new values. Caller // is responsible for making sure that update.Key is only different from base.Key // if it is being called from the WFE key change endpoint. func (ra *RegistrationAuthorityImpl) UpdateRegistration(ctx context.Context, base core.Registration, update core.Registration) (core.Registration, error) { if changed := mergeUpdate(&base, update); !changed { // If merging the update didn't actually change the base then our work is // done, we can return before calling ra.SA.UpdateRegistration since theres // nothing for the SA to do return base, nil } err := ra.validateContacts(ctx, base.Contact) if err != nil { return core.Registration{}, err } err = ra.SA.UpdateRegistration(ctx, base) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(fmt.Sprintf("Could not update registration: %s", err)) return core.Registration{}, err } ra.stats.Inc("UpdatedRegistrations", 1) return base, nil }
// UpdateAuthorization updates an authorization with new values. func (ra *RegistrationAuthorityImpl) UpdateAuthorization(ctx context.Context, base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) { // Refuse to update expired authorizations if base.Expires == nil || base.Expires.Before(ra.clk.Now()) { err = core.NotFoundError("Expired authorization") return } authz = base if challengeIndex >= len(authz.Challenges) { err = core.MalformedRequestError(fmt.Sprintf("Invalid challenge index: %d", challengeIndex)) return } ch := &authz.Challenges[challengeIndex] // Copy information over that the client is allowed to supply ch.ProvidedKeyAuthorization = response.ProvidedKeyAuthorization if response.Type != "" && ch.Type != response.Type { // TODO(riking): Check the rate on this, uncomment error return if negligible ra.stats.Inc("RA.StartChallengeWrongType", 1, 1.0) // err = core.MalformedRequestError(fmt.Sprintf("Invalid update to challenge - provided type was %s but actual type is %s", response.Type, ch.Type)) // return } // Recompute the key authorization field provided by the client and // check it against the value provided expectedKeyAuthorization, err := ch.ExpectedKeyAuthorization() if err != nil { err = core.InternalServerError("Could not compute expected key authorization value") return } if expectedKeyAuthorization != ch.ProvidedKeyAuthorization { err = core.MalformedRequestError("Response does not complete challenge") return } // Double check before sending to VA if !ch.IsSaneForValidation() { err = core.MalformedRequestError("Response does not complete challenge") return } // Store the updated version if err = ra.SA.UpdatePendingAuthorization(ctx, authz); err != nil { // This can pretty much only happen when the client corrupts the Challenge // data. err = core.MalformedRequestError("Challenge data was corrupted") return } ra.stats.Inc("RA.NewPendingAuthorizations", 1, 1.0) // Look up the account key for this authorization reg, err := ra.SA.GetRegistration(ctx, authz.RegistrationID) if err != nil { err = core.InternalServerError(err.Error()) return } // Reject the update if the challenge in question was created // with a different account key if !core.KeyDigestEquals(reg.Key, ch.AccountKey) { err = core.UnauthorizedError("Challenge cannot be updated with a different key") return } // Dispatch to the VA for service vaCtx := context.Background() if !ra.useNewVARPC { // TODO(#1167): remove _ = ra.VA.UpdateValidations(vaCtx, authz, challengeIndex) ra.stats.Inc("RA.UpdatedPendingAuthorizations", 1, 1.0) } else { go func() { records, err := ra.VA.PerformValidation(vaCtx, authz.Identifier.Value, authz.Challenges[challengeIndex], authz) var prob *probs.ProblemDetails if p, ok := err.(*probs.ProblemDetails); ok { prob = p } else if err != nil { prob = probs.ServerInternal("Could not communicate with VA") ra.log.Err(fmt.Sprintf("Could not communicate with VA: %s", err)) } // Save the updated records challenge := &authz.Challenges[challengeIndex] challenge.ValidationRecord = records if !challenge.RecordsSane() && prob == nil { prob = probs.ServerInternal("Records for validation failed sanity check") } if prob != nil { challenge.Status = core.StatusInvalid challenge.Error = prob } else { challenge.Status = core.StatusValid } authz.Challenges[challengeIndex] = *challenge err = ra.OnValidationUpdate(vaCtx, authz) if err != nil { ra.log.Err(fmt.Sprintf("Could not record updated validation: err=[%s] regID=[%d]", err, authz.RegistrationID)) } }() ra.stats.Inc("RA.UpdatedPendingAuthorizations", 1, 1.0) } return }
// UpdateAuthorization updates an authorization with new values. func (ra *RegistrationAuthorityImpl) UpdateAuthorization(ctx context.Context, base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) { // Refuse to update expired authorizations if base.Expires == nil || base.Expires.Before(ra.clk.Now()) { err = core.NotFoundError("Expired authorization") return } authz = base if challengeIndex >= len(authz.Challenges) { err = core.MalformedRequestError(fmt.Sprintf("Invalid challenge index: %d", challengeIndex)) return } ch := &authz.Challenges[challengeIndex] if response.Type != "" && ch.Type != response.Type { // TODO(riking): Check the rate on this, uncomment error return if negligible ra.stats.Inc("StartChallengeWrongType", 1) // err = core.MalformedRequestError(fmt.Sprintf("Invalid update to challenge - provided type was %s but actual type is %s", response.Type, ch.Type)) // return } // When configured with `reuseValidAuthz` we can expect some clients to try // and update a challenge for an authorization that is already valid. In this // case we don't need to process the challenge update. It wouldn't be helpful, // the overall authorization is already good! We increment a stat for this // case and return early. if ra.reuseValidAuthz && authz.Status == core.StatusValid { ra.stats.Inc("ReusedValidAuthzChallenge", 1) return } // Look up the account key for this authorization reg, err := ra.SA.GetRegistration(ctx, authz.RegistrationID) if err != nil { err = core.InternalServerError(err.Error()) return } // Recompute the key authorization field provided by the client and // check it against the value provided expectedKeyAuthorization, err := ch.ExpectedKeyAuthorization(reg.Key) if err != nil { err = core.InternalServerError("Could not compute expected key authorization value") return } if expectedKeyAuthorization != response.ProvidedKeyAuthorization { err = core.MalformedRequestError("Provided key authorization was incorrect") return } // Copy information over that the client is allowed to supply ch.ProvidedKeyAuthorization = response.ProvidedKeyAuthorization // Double check before sending to VA if !ch.IsSaneForValidation() { err = core.MalformedRequestError("Response does not complete challenge") return } // Store the updated version if err = ra.SA.UpdatePendingAuthorization(ctx, authz); err != nil { ra.log.Warning(fmt.Sprintf( "Error calling ra.SA.UpdatePendingAuthorization: %s\n", err.Error())) err = core.InternalServerError("Could not update pending authorization") return } ra.stats.Inc("NewPendingAuthorizations", 1) // Dispatch to the VA for service vaCtx := context.Background() go func() { records, err := ra.VA.PerformValidation(vaCtx, authz.Identifier.Value, authz.Challenges[challengeIndex], authz) var prob *probs.ProblemDetails if p, ok := err.(*probs.ProblemDetails); ok { prob = p } else if err != nil { prob = probs.ServerInternal("Could not communicate with VA") ra.log.AuditErr(fmt.Sprintf("Could not communicate with VA: %s", err)) } // Save the updated records challenge := &authz.Challenges[challengeIndex] challenge.ValidationRecord = records if !challenge.RecordsSane() && prob == nil { prob = probs.ServerInternal("Records for validation failed sanity check") } if prob != nil { challenge.Status = core.StatusInvalid challenge.Error = prob } else { challenge.Status = core.StatusValid } authz.Challenges[challengeIndex] = *challenge err = ra.onValidationUpdate(vaCtx, authz) if err != nil { ra.log.AuditErr(fmt.Sprintf("Could not record updated validation: err=[%s] regID=[%d]", err, authz.RegistrationID)) } }() ra.stats.Inc("UpdatedPendingAuthorizations", 1) return }
// NewAuthorization constuct a new Authz from a request. func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) { reg, err := ra.SA.GetRegistration(regID) if err != nil { err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) return authz, err } identifier := request.Identifier // Check that the identifier is present and appropriate if err = ra.PA.WillingToIssue(identifier); err != nil { err = core.UnauthorizedError(err.Error()) return authz, err } // Check CAA records for the requested identifier present, valid, err := ra.VA.CheckCAARecords(identifier) if err != nil { return authz, err } // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ra.log.Audit(fmt.Sprintf("Checked CAA records for %s, registration ID %d [Present: %t, Valid for issuance: %t]", identifier.Value, regID, present, valid)) if !valid { err = errors.New("CAA check for identifier failed") return authz, err } // Create validations, but we have to update them with URIs later challenges, combinations := ra.PA.ChallengesFor(identifier) // Partially-filled object authz = core.Authorization{ Identifier: identifier, RegistrationID: regID, Status: core.StatusPending, Combinations: combinations, Challenges: challenges, } // Get a pending Auth first so we can get our ID back, then update with challenges authz, err = ra.SA.NewPendingAuthorization(authz) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(fmt.Sprintf("Invalid authorization request: %s", err)) return authz, err } // Construct all the challenge URIs for i := range authz.Challenges { // Ignoring these errors because we construct the URLs to be correct challengeURI, _ := core.ParseAcmeURL(ra.AuthzBase + authz.ID + "?challenge=" + strconv.Itoa(i)) authz.Challenges[i].URI = challengeURI // Add the account key used to generate the challenge authz.Challenges[i].AccountKey = ®.Key if !authz.Challenges[i].IsSane(false) { // InternalServerError because we generated these challenges, they should // be OK. err = core.InternalServerError(fmt.Sprintf("Challenge didn't pass sanity check: %+v", authz.Challenges[i])) return authz, err } } // Store the authorization object, then return it err = ra.SA.UpdatePendingAuthorization(authz) if err != nil { // InternalServerError because we created the authorization just above, // and adding Sane challenges should not break it. err = core.InternalServerError(err.Error()) } return authz, err }
// NewCertificate requests the issuance of a certificate. func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (cert core.Certificate, err error) { emptyCert := core.Certificate{} var logEventResult string // Assume the worst logEventResult = "error" // Construct the log event logEvent := certificateRequestEvent{ ID: core.NewToken(), Requester: regID, RequestMethod: "online", RequestTime: time.Now(), } // No matter what, log the request defer func() { // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ra.log.AuditObject(fmt.Sprintf("Certificate request - %s", logEventResult), logEvent) }() if regID <= 0 { err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) return emptyCert, err } registration, err := ra.SA.GetRegistration(regID) if err != nil { logEvent.Error = err.Error() return emptyCert, err } // Verify the CSR csr := req.CSR if err = core.VerifyCSR(csr); err != nil { logEvent.Error = err.Error() err = core.UnauthorizedError("Invalid signature on CSR") return emptyCert, err } logEvent.CommonName = csr.Subject.CommonName logEvent.Names = csr.DNSNames // Validate that authorization key is authorized for all domains names := make([]string, len(csr.DNSNames)) copy(names, csr.DNSNames) if len(csr.Subject.CommonName) > 0 { names = append(names, csr.Subject.CommonName) } if len(names) == 0 { err = core.UnauthorizedError("CSR has no names in it") logEvent.Error = err.Error() return emptyCert, err } csrPreviousDenied, err := ra.SA.AlreadyDeniedCSR(names) if err != nil { logEvent.Error = err.Error() return emptyCert, err } if csrPreviousDenied { err = core.UnauthorizedError("CSR has already been revoked/denied") logEvent.Error = err.Error() return emptyCert, err } if core.KeyDigestEquals(csr.PublicKey, registration.Key) { err = core.MalformedRequestError("Certificate public key must be different than account key") return emptyCert, err } // Check that each requested name has a valid authorization now := time.Now() earliestExpiry := time.Date(2100, 01, 01, 0, 0, 0, 0, time.UTC) for _, name := range names { authz, err := ra.SA.GetLatestValidAuthorization(registration.ID, core.AcmeIdentifier{Type: core.IdentifierDNS, Value: name}) if err != nil || authz.Expires.Before(now) { // unable to find a valid authorization or authz is expired err = core.UnauthorizedError(fmt.Sprintf("Key not authorized for name %s", name)) logEvent.Error = err.Error() return emptyCert, err } if authz.Expires.Before(earliestExpiry) { earliestExpiry = *authz.Expires } } // Mark that we verified the CN and SANs logEvent.VerifiedFields = []string{"subject.commonName", "subjectAltName"} // Create the certificate and log the result if cert, err = ra.CA.IssueCertificate(*csr, regID, earliestExpiry); err != nil { // While this could be InternalServerError for certain conditions, most // of the failure reasons (such as GoodKey failing) are caused by malformed // requests. logEvent.Error = err.Error() err = core.MalformedRequestError("Certificate request was invalid") return emptyCert, err } err = cert.MatchesCSR(csr, earliestExpiry) if err != nil { logEvent.Error = err.Error() return emptyCert, err } parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER)) if err != nil { // InternalServerError because the certificate from the CA should be // parseable. err = core.InternalServerError(err.Error()) logEvent.Error = err.Error() return emptyCert, err } logEvent.SerialNumber = core.SerialToString(parsedCertificate.SerialNumber) logEvent.CommonName = parsedCertificate.Subject.CommonName logEvent.NotBefore = parsedCertificate.NotBefore logEvent.NotAfter = parsedCertificate.NotAfter logEvent.ResponseTime = time.Now() logEventResult = "successful" return cert, nil }
// IssueCertificate attempts to convert a CSR into a signed Certificate, while // enforcing all policies. func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest, regID int64) (core.Certificate, error) { emptyCert := core.Certificate{} var err error key, ok := csr.PublicKey.(crypto.PublicKey) if !ok { err = core.MalformedRequestError("Invalid public key in CSR.") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } if err = core.GoodKey(key); err != nil { err = core.MalformedRequestError(fmt.Sprintf("Invalid public key in CSR: %s", err.Error())) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } if badSignatureAlgorithms[csr.SignatureAlgorithm] { err = core.MalformedRequestError("Invalid signature algorithm in CSR") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Pull hostnames from CSR // Authorization is checked by the RA commonName := "" hostNames := make([]string, len(csr.DNSNames)) copy(hostNames, csr.DNSNames) if len(csr.Subject.CommonName) > 0 { commonName = csr.Subject.CommonName hostNames = append(hostNames, csr.Subject.CommonName) } else if len(hostNames) > 0 { commonName = hostNames[0] } else { err = core.MalformedRequestError("Cannot issue a certificate without a hostname.") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Collapse any duplicate names. Note that this operation may re-order the names hostNames = core.UniqueNames(hostNames) if ca.MaxNames > 0 && len(hostNames) > ca.MaxNames { err = core.MalformedRequestError(fmt.Sprintf("Certificate request has %d > %d names", len(hostNames), ca.MaxNames)) ca.log.WarningErr(err) return emptyCert, err } // Verify that names are allowed by policy identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: commonName} if err = ca.PA.WillingToIssue(identifier); err != nil { err = core.MalformedRequestError(fmt.Sprintf("Policy forbids issuing for name %s", commonName)) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } for _, name := range hostNames { identifier = core.AcmeIdentifier{Type: core.IdentifierDNS, Value: name} if err = ca.PA.WillingToIssue(identifier); err != nil { err = core.MalformedRequestError(fmt.Sprintf("Policy forbids issuing for name %s", name)) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } } notAfter := ca.Clk.Now().Add(ca.ValidityPeriod) if ca.NotAfter.Before(notAfter) { err = core.InternalServerError("Cannot issue a certificate that expires after the intermediate certificate.") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Convert the CSR to PEM csrPEM := string(pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE REQUEST", Bytes: csr.Raw, })) // Get the next serial number tx, err := ca.DB.Begin() if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.AuditErr(err) return emptyCert, err } serialDec, err := ca.DB.IncrementAndGetSerial(tx) if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Serial increment failed, rolling back: err=[%v]", err)) tx.Rollback() return emptyCert, err } serialHex := fmt.Sprintf("%02X%014X", ca.Prefix, serialDec) // Send the cert off for signing req := signer.SignRequest{ Request: csrPEM, Profile: ca.profile, Hosts: hostNames, Subject: &signer.Subject{ CN: commonName, }, SerialSeq: serialHex, } certPEM, err := ca.Signer.Sign(req) if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Signer failed, rolling back: serial=[%s] err=[%v]", serialHex, err)) tx.Rollback() return emptyCert, err } if len(certPEM) == 0 { err = core.InternalServerError("No certificate returned by server") // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("PEM empty from Signer, rolling back: serial=[%s] err=[%v]", serialHex, err)) tx.Rollback() return emptyCert, err } block, _ := pem.Decode(certPEM) if block == nil || block.Type != "CERTIFICATE" { err = core.InternalServerError("Invalid certificate value returned") // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("PEM decode error, aborting and rolling back issuance: pem=[%s] err=[%v]", certPEM, err)) tx.Rollback() return emptyCert, err } certDER := block.Bytes cert := core.Certificate{ DER: certDER, } // This is one last check for uncaught errors if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Uncaught error, aborting and rolling back issuance: pem=[%s] err=[%v]", certPEM, err)) tx.Rollback() return emptyCert, err } // Store the cert with the certificate authority, if provided _, err = ca.SA.AddCertificate(certDER, regID) if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Failed RPC to store at SA, orphaning certificate: pem=[%s] err=[%v]", certPEM, err)) tx.Rollback() return emptyCert, err } if err = tx.Commit(); err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Failed to commit, orphaning certificate: pem=[%s] err=[%v]", certPEM, err)) return emptyCert, err } // Attempt to generate the OCSP Response now. If this raises an error, it is // logged but is not returned to the caller, as an error at this point does // not constitute an issuance failure. certObj, err := x509.ParseCertificate(certDER) if err != nil { ca.log.Warning(fmt.Sprintf("Post-Issuance OCSP failed parsing Certificate: %s", err)) return cert, nil } serial := core.SerialToString(certObj.SerialNumber) signRequest := ocsp.SignRequest{ Certificate: certObj, Status: string(core.OCSPStatusGood), } ocspResponse, err := ca.OCSPSigner.Sign(signRequest) if err != nil { ca.log.Warning(fmt.Sprintf("Post-Issuance OCSP failed signing: %s", err)) return cert, nil } err = ca.SA.UpdateOCSP(serial, ocspResponse) if err != nil { ca.log.Warning(fmt.Sprintf("Post-Issuance OCSP failed storing: %s", err)) return cert, nil } // Submit the certificate to any configured CT logs go ca.Publisher.SubmitToCT(certObj.Raw) // Do not return an err at this point; caller must know that the Certificate // was issued. (Also, it should be impossible for err to be non-nil here) return cert, nil }
func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool, resource core.AcmeResource) ([]byte, *jose.JsonWebKey, core.Registration, error) { var err error var reg core.Registration // Read body if request.Body == nil { err = core.MalformedRequestError("No body on POST") wfe.log.Debug(err.Error()) return nil, nil, reg, err } bodyBytes, err := ioutil.ReadAll(request.Body) if err != nil { err = core.InternalServerError(err.Error()) wfe.log.Debug(err.Error()) return nil, nil, reg, err } body := string(bodyBytes) // Parse as JWS parsedJws, err := jose.ParseSigned(body) if err != nil { puberr := core.SignatureValidationError("Parse error reading JWS") wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error())) return nil, nil, reg, puberr } // Verify JWS // NOTE: It might seem insecure for the WFE to be trusted to verify // client requests, i.e., that the verification should be done at the // RA. However the WFE is the RA's only view of the outside world // *anyway*, so it could always lie about what key was used by faking // the signature itself. if len(parsedJws.Signatures) > 1 { err = core.SignatureValidationError("Too many signatures on POST") wfe.log.Debug(err.Error()) return nil, nil, reg, err } if len(parsedJws.Signatures) == 0 { err = core.SignatureValidationError("POST JWS not signed") wfe.log.Debug(err.Error()) return nil, nil, reg, err } key := parsedJws.Signatures[0].Header.JsonWebKey payload, header, err := parsedJws.Verify(key) if err != nil { puberr := core.SignatureValidationError("JWS verification error") wfe.log.Debug(string(body)) wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error())) return nil, nil, reg, puberr } // Check that the request has a known anti-replay nonce // i.e., Nonce is in protected header and if err != nil || len(header.Nonce) == 0 { err = core.SignatureValidationError("JWS has no anti-replay nonce") wfe.log.Debug(err.Error()) return nil, nil, reg, err } else if !wfe.nonceService.Valid(header.Nonce) { err = core.SignatureValidationError(fmt.Sprintf("JWS has invalid anti-replay nonce")) wfe.log.Debug(err.Error()) return nil, nil, reg, err } reg, err = wfe.SA.GetRegistrationByKey(*key) if err != nil { // If we are requiring a valid registration, any failure to look up the // registration is an overall failure to verify. if regCheck { return nil, nil, reg, err } // Otherwise we just return an empty registration. The caller is expected // to use the returned key instead. reg = core.Registration{} } // Check that the "resource" field is present and has the correct value var parsedRequest struct { Resource string `json:"resource"` } err = json.Unmarshal([]byte(payload), &parsedRequest) if err != nil { puberr := core.SignatureValidationError("Request payload did not parse as JSON") wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error())) return nil, nil, reg, puberr } if parsedRequest.Resource == "" { err = core.MalformedRequestError("Request payload does not specify a resource") wfe.log.Debug(err.Error()) return nil, nil, reg, err } else if resource != core.AcmeResource(parsedRequest.Resource) { err = core.MalformedRequestError(fmt.Sprintf("Request payload has invalid resource: %s != %s", parsedRequest.Resource, resource)) wfe.log.Debug(err.Error()) return nil, nil, reg, err } return []byte(payload), key, reg, nil }
// MatchesCSR tests the contents of a generated certificate to make sure // that the PublicKey, CommonName, and DNSNames match those provided in // the CSR that was used to generate the certificate. It also checks the // following fields for: // * notBefore is not more than 24 hours ago // * BasicConstraintsValid is true // * IsCA is false // * ExtKeyUsage only contains ExtKeyUsageServerAuth & ExtKeyUsageClientAuth // * Subject only contains CommonName & Names func (ra *RegistrationAuthorityImpl) MatchesCSR(cert core.Certificate, csr *x509.CertificateRequest) (err error) { parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER)) if err != nil { return } // Check issued certificate matches what was expected from the CSR hostNames := make([]string, len(csr.DNSNames)) copy(hostNames, csr.DNSNames) if len(csr.Subject.CommonName) > 0 { hostNames = append(hostNames, csr.Subject.CommonName) } hostNames = core.UniqueLowerNames(hostNames) if !core.KeyDigestEquals(parsedCertificate.PublicKey, csr.PublicKey) { err = core.InternalServerError("Generated certificate public key doesn't match CSR public key") return } if len(csr.Subject.CommonName) > 0 && parsedCertificate.Subject.CommonName != strings.ToLower(csr.Subject.CommonName) { err = core.InternalServerError("Generated certificate CommonName doesn't match CSR CommonName") return } // Sort both slices of names before comparison. parsedNames := parsedCertificate.DNSNames sort.Strings(parsedNames) sort.Strings(hostNames) if !reflect.DeepEqual(parsedNames, hostNames) { err = core.InternalServerError("Generated certificate DNSNames don't match CSR DNSNames") return } if !reflect.DeepEqual(parsedCertificate.IPAddresses, csr.IPAddresses) { err = core.InternalServerError("Generated certificate IPAddresses don't match CSR IPAddresses") return } if !reflect.DeepEqual(parsedCertificate.EmailAddresses, csr.EmailAddresses) { err = core.InternalServerError("Generated certificate EmailAddresses don't match CSR EmailAddresses") return } if len(parsedCertificate.Subject.Country) > 0 || len(parsedCertificate.Subject.Organization) > 0 || len(parsedCertificate.Subject.OrganizationalUnit) > 0 || len(parsedCertificate.Subject.Locality) > 0 || len(parsedCertificate.Subject.Province) > 0 || len(parsedCertificate.Subject.StreetAddress) > 0 || len(parsedCertificate.Subject.PostalCode) > 0 || len(parsedCertificate.Subject.SerialNumber) > 0 { err = core.InternalServerError("Generated certificate Subject contains fields other than CommonName or Names") return } now := ra.clk.Now() if now.Sub(parsedCertificate.NotBefore) > time.Hour*24 { err = core.InternalServerError(fmt.Sprintf("Generated certificate is back dated %s", now.Sub(parsedCertificate.NotBefore))) return } if !parsedCertificate.BasicConstraintsValid { err = core.InternalServerError("Generated certificate doesn't have basic constraints set") return } if parsedCertificate.IsCA { err = core.InternalServerError("Generated certificate can sign other certificates") return } if !reflect.DeepEqual(parsedCertificate.ExtKeyUsage, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}) { err = core.InternalServerError("Generated certificate doesn't have correct key usage extensions") return } return }
// NewCertificate requests the issuance of a certificate. func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (cert core.Certificate, err error) { emptyCert := core.Certificate{} var logEventResult string // Assume the worst logEventResult = "error" // Construct the log event logEvent := certificateRequestEvent{ ID: core.NewToken(), Requester: regID, RequestMethod: "online", RequestTime: ra.clk.Now(), } // No matter what, log the request defer func() { // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ra.log.AuditObject(fmt.Sprintf("Certificate request - %s", logEventResult), logEvent) }() if regID <= 0 { err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) return emptyCert, err } registration, err := ra.SA.GetRegistration(regID) if err != nil { logEvent.Error = err.Error() return emptyCert, err } // Verify the CSR csr := req.CSR if err = core.VerifyCSR(csr); err != nil { logEvent.Error = err.Error() err = core.UnauthorizedError("Invalid signature on CSR") return emptyCert, err } logEvent.CommonName = csr.Subject.CommonName logEvent.Names = csr.DNSNames // Validate that authorization key is authorized for all domains names := make([]string, len(csr.DNSNames)) copy(names, csr.DNSNames) if len(csr.Subject.CommonName) > 0 { names = append(names, csr.Subject.CommonName) } if len(names) == 0 { err = core.UnauthorizedError("CSR has no names in it") logEvent.Error = err.Error() return emptyCert, err } csrPreviousDenied, err := ra.SA.AlreadyDeniedCSR(names) if err != nil { logEvent.Error = err.Error() return emptyCert, err } if csrPreviousDenied { err = core.UnauthorizedError("CSR has already been revoked/denied") logEvent.Error = err.Error() return emptyCert, err } if core.KeyDigestEquals(csr.PublicKey, registration.Key) { err = core.MalformedRequestError("Certificate public key must be different than account key") return emptyCert, err } // Check rate limits before checking authorizations. If someone is unable to // issue a cert due to rate limiting, we don't want to tell them to go get the // necessary authorizations, only to later fail the rate limit check. err = ra.checkLimits(names, registration.ID) if err != nil { logEvent.Error = err.Error() return emptyCert, err } err = ra.checkAuthorizations(names, ®istration) if err != nil { logEvent.Error = err.Error() return emptyCert, err } // Mark that we verified the CN and SANs logEvent.VerifiedFields = []string{"subject.commonName", "subjectAltName"} // Create the certificate and log the result if cert, err = ra.CA.IssueCertificate(*csr, regID); err != nil { logEvent.Error = err.Error() return emptyCert, err } err = ra.MatchesCSR(cert, csr) if err != nil { logEvent.Error = err.Error() return emptyCert, err } parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER)) if err != nil { // InternalServerError because the certificate from the CA should be // parseable. err = core.InternalServerError(err.Error()) logEvent.Error = err.Error() return emptyCert, err } logEvent.SerialNumber = core.SerialToString(parsedCertificate.SerialNumber) logEvent.CommonName = parsedCertificate.Subject.CommonName logEvent.NotBefore = parsedCertificate.NotBefore logEvent.NotAfter = parsedCertificate.NotAfter logEvent.ResponseTime = ra.clk.Now() logEventResult = "successful" ra.stats.Inc("RA.NewCertificates", 1, 1.0) return cert, nil }
// NewAuthorization constuct a new Authz from a request. Values (domains) in // request.Identifier will be lowercased before storage. func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) { reg, err := ra.SA.GetRegistration(regID) if err != nil { err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) return authz, err } identifier := request.Identifier identifier.Value = strings.ToLower(identifier.Value) // Check that the identifier is present and appropriate if err = ra.PA.WillingToIssue(identifier, regID); err != nil { return authz, err } limit := &ra.rlPolicies.PendingAuthorizationsPerAccount if err = checkPendingAuthorizationLimit(ra.SA, limit, regID); err != nil { return authz, err } if identifier.Type == core.IdentifierDNS { isSafe, err := ra.dc.IsSafe(identifier.Value) if err != nil { outErr := core.InternalServerError("unable to determine if domain was safe") ra.log.Warning(fmt.Sprintf("%s: %s", string(outErr), err)) return authz, outErr } if !isSafe { return authz, core.UnauthorizedError(fmt.Sprintf("%#v was considered an unsafe domain by a third-party API", identifier.Value)) } } // Create validations. The WFE will update them with URIs before sending them out. challenges, combinations, err := ra.PA.ChallengesFor(identifier, ®.Key) expires := ra.clk.Now().Add(ra.pendingAuthorizationLifetime) // Partially-filled object authz = core.Authorization{ Identifier: identifier, RegistrationID: regID, Status: core.StatusPending, Combinations: combinations, Challenges: challenges, Expires: &expires, } // Get a pending Auth first so we can get our ID back, then update with challenges authz, err = ra.SA.NewPendingAuthorization(authz) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(fmt.Sprintf("Invalid authorization request: %s", err)) return core.Authorization{}, err } // Check each challenge for sanity. for _, challenge := range authz.Challenges { if !challenge.IsSane(false) { // InternalServerError because we generated these challenges, they should // be OK. err = core.InternalServerError(fmt.Sprintf("Challenge didn't pass sanity check: %+v", challenge)) return core.Authorization{}, err } } return authz, err }
// NewAuthorization constructs a new Authz from a request. Values (domains) in // request.Identifier will be lowercased before storage. func (ra *RegistrationAuthorityImpl) NewAuthorization(ctx context.Context, request core.Authorization, regID int64) (authz core.Authorization, err error) { reg, err := ra.SA.GetRegistration(ctx, regID) if err != nil { err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) return authz, err } identifier := request.Identifier identifier.Value = strings.ToLower(identifier.Value) // Check that the identifier is present and appropriate if err = ra.PA.WillingToIssue(identifier, regID); err != nil { return authz, err } if err = ra.checkPendingAuthorizationLimit(ctx, regID); err != nil { return authz, err } if identifier.Type == core.IdentifierDNS { isSafeResp, err := ra.VA.IsSafeDomain(ctx, &vaPB.IsSafeDomainRequest{Domain: &identifier.Value}) if err != nil { outErr := core.InternalServerError("unable to determine if domain was safe") ra.log.Warning(fmt.Sprintf("%s: %s", string(outErr), err)) return authz, outErr } if !isSafeResp.GetIsSafe() { return authz, core.UnauthorizedError(fmt.Sprintf("%#v was considered an unsafe domain by a third-party API", identifier.Value)) } } if ra.reuseValidAuthz { auths, err := ra.SA.GetValidAuthorizations(ctx, regID, []string{identifier.Value}, ra.clk.Now()) if err != nil { outErr := core.InternalServerError( fmt.Sprintf("unable to get existing validations for regID: %d, identifier: %s", regID, identifier.Value)) ra.log.Warning(string(outErr)) } if existingAuthz, ok := auths[identifier.Value]; ok { // Use the valid existing authorization's ID to find a fully populated version // The results from `GetValidAuthorizations` are most notably missing // `Challenge` values that the client expects in the result. populatedAuthz, err := ra.SA.GetAuthorization(ctx, existingAuthz.ID) if err != nil { outErr := core.InternalServerError( fmt.Sprintf("unable to get existing authorization for auth ID: %s", existingAuthz.ID)) ra.log.Warning(fmt.Sprintf("%s: %s", string(outErr), existingAuthz.ID)) } // The existing authorization must not expire within the next 24 hours for // it to be OK for reuse reuseCutOff := ra.clk.Now().Add(time.Hour * 24) if populatedAuthz.Expires.After(reuseCutOff) { ra.stats.Inc("RA.ReusedValidAuthz", 1, 1.0) return populatedAuthz, nil } } } // Create validations. The WFE will update them with URIs before sending them out. challenges, combinations := ra.PA.ChallengesFor(identifier, ®.Key) expires := ra.clk.Now().Add(ra.pendingAuthorizationLifetime) // Partially-filled object authz = core.Authorization{ Identifier: identifier, RegistrationID: regID, Status: core.StatusPending, Combinations: combinations, Challenges: challenges, Expires: &expires, } // Get a pending Auth first so we can get our ID back, then update with challenges authz, err = ra.SA.NewPendingAuthorization(ctx, authz) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(fmt.Sprintf("Invalid authorization request: %s", err)) return core.Authorization{}, err } // Check each challenge for sanity. for _, challenge := range authz.Challenges { if !challenge.IsSaneForClientOffer() { // InternalServerError because we generated these challenges, they should // be OK. err = core.InternalServerError(fmt.Sprintf("Challenge didn't pass sanity check: %+v", challenge)) return core.Authorization{}, err } } return authz, err }
// verifyPOST reads and parses the request body, looks up the Registration // corresponding to its JWK, verifies the JWS signature, checks that the // resource field is present and correct in the JWS protected header, and // returns the JWS payload bytes, the key used to verify, and the corresponding // Registration (or error). If regCheck is false, verifyPOST will still try to // look up a registration object, and will return it if found. However, if no // registration object is found, verifyPOST will attempt to verify the JWS using // the key in the JWS headers, and return the key plus a dummy registration if // successful. If a caller passes regCheck = false, it should plan on validating // the key itself. verifyPOST also appends its errors to requestEvent.Errors so // code calling it does not need to if they imediately return a response to the // user. func (wfe *WebFrontEndImpl) verifyPOST(logEvent *requestEvent, request *http.Request, regCheck bool, resource core.AcmeResource) ([]byte, *jose.JsonWebKey, core.Registration, error) { var err error // TODO: We should return a pointer to a registration, which can be nil, // rather the a registration value with a sentinel value. // https://github.com/letsencrypt/boulder/issues/877 reg := core.Registration{ID: 0} if _, ok := request.Header["Content-Length"]; !ok { err = core.LengthRequiredError("Content-Length header is required for POST.") wfe.stats.Inc("WFE.HTTP.ClientErrors.LengthRequiredError", 1, 1.0) logEvent.AddError("missing Content-Length header on POST") return nil, nil, reg, err } // Read body if request.Body == nil { err = core.MalformedRequestError("No body on POST") wfe.stats.Inc("WFE.Errors.NoPOSTBody", 1, 1.0) logEvent.AddError("no body on POST") return nil, nil, reg, err } bodyBytes, err := ioutil.ReadAll(request.Body) if err != nil { err = core.InternalServerError("unable to read request body") wfe.stats.Inc("WFE.Errors.UnableToReadRequestBody", 1, 1.0) logEvent.AddError("unable to read request body") return nil, nil, reg, err } body := string(bodyBytes) // Parse as JWS parsedJws, err := jose.ParseSigned(body) if err != nil { puberr := core.SignatureValidationError("Parse error reading JWS") wfe.stats.Inc("WFE.Errors.UnableToParseJWS", 1, 1.0) logEvent.AddError("could not JSON parse body into JWS: %s", err) return nil, nil, reg, puberr } // Verify JWS // NOTE: It might seem insecure for the WFE to be trusted to verify // client requests, i.e., that the verification should be done at the // RA. However the WFE is the RA's only view of the outside world // *anyway*, so it could always lie about what key was used by faking // the signature itself. if len(parsedJws.Signatures) > 1 { err = core.SignatureValidationError("Too many signatures in POST body") wfe.stats.Inc("WFE.Errors.TooManyJWSSignaturesInPOST", 1, 1.0) logEvent.AddError("too many signatures in POST body: %d", len(parsedJws.Signatures)) return nil, nil, reg, err } if len(parsedJws.Signatures) == 0 { err = core.SignatureValidationError("POST JWS not signed") wfe.stats.Inc("WFE.Errors.JWSNotSignedInPOST", 1, 1.0) logEvent.AddError("no signatures in POST body") return nil, nil, reg, err } submittedKey := parsedJws.Signatures[0].Header.JsonWebKey if submittedKey == nil { err = core.SignatureValidationError("No JWK in JWS header") wfe.stats.Inc("WFE.Errors.NoJWKInJWSSignatureHeader", 1, 1.0) logEvent.AddError("no JWK in JWS signature header in POST body") return nil, nil, reg, err } var key *jose.JsonWebKey reg, err = wfe.SA.GetRegistrationByKey(*submittedKey) // Special case: If no registration was found, but regCheck is false, use an // empty registration and the submitted key. The caller is expected to do some // validation on the returned key. if _, ok := err.(core.NoSuchRegistrationError); ok && !regCheck { // When looking up keys from the registrations DB, we can be confident they // are "good". But when we are verifying against any submitted key, we want // to check its quality before doing the verify. if err = core.GoodKey(submittedKey.Key); err != nil { wfe.stats.Inc("WFE.Errors.JWKRejectedByGoodKey", 1, 1.0) logEvent.AddError("JWK in request was rejected by GoodKey: %s", err) return nil, nil, reg, err } key = submittedKey } else if err != nil { // For all other errors, or if regCheck is true, return error immediately. wfe.stats.Inc("WFE.Errors.UnableToGetRegistrationByKey", 1, 1.0) logEvent.AddError("unable to fetch registration by the given JWK: %s", err) return nil, nil, reg, err } else { // If the lookup was successful, use that key. key = ®.Key logEvent.Requester = reg.ID logEvent.Contacts = reg.Contact } payload, header, err := parsedJws.Verify(key) if err != nil { puberr := core.SignatureValidationError("JWS verification error") wfe.stats.Inc("WFE.Errors.JWSVerificationFailed", 1, 1.0) n := len(body) if n > 100 { n = 100 } logEvent.AddError("verification of JWS with the JWK failed: %v; body: %s", err, body[:n]) return nil, nil, reg, puberr } // Check that the request has a known anti-replay nonce // i.e., Nonce is in protected header and if err != nil || len(header.Nonce) == 0 { wfe.stats.Inc("WFE.Errors.JWSMissingNonce", 1, 1.0) logEvent.AddError("JWS is missing an anti-replay nonce") err = core.SignatureValidationError("JWS has no anti-replay nonce") return nil, nil, reg, err } else if !wfe.nonceService.Valid(header.Nonce) { wfe.stats.Inc("WFE.Errors.JWSInvalidNonce", 1, 1.0) logEvent.AddError("JWS has an invalid anti-replay nonce") err = core.SignatureValidationError(fmt.Sprintf("JWS has invalid anti-replay nonce")) return nil, nil, reg, err } // Check that the "resource" field is present and has the correct value var parsedRequest struct { Resource string `json:"resource"` } err = json.Unmarshal([]byte(payload), &parsedRequest) if err != nil { wfe.stats.Inc("WFE.Errors.UnparsableJWSPayload", 1, 1.0) logEvent.AddError("unable to JSON parse resource from JWS payload: %s", err) puberr := core.SignatureValidationError("Request payload did not parse as JSON") return nil, nil, reg, puberr } if parsedRequest.Resource == "" { wfe.stats.Inc("WFE.Errors.NoResourceInJWSPayload", 1, 1.0) logEvent.AddError("JWS request payload does not specifiy a resource") err = core.MalformedRequestError("Request payload does not specify a resource") return nil, nil, reg, err } else if resource != core.AcmeResource(parsedRequest.Resource) { wfe.stats.Inc("WFE.Errors.MismatchedResourceInJWSPayload", 1, 1.0) logEvent.AddError("JWS request payload does not match resource") err = core.MalformedRequestError(fmt.Sprintf("JWS resource payload does not match the HTTP resource: %s != %s", parsedRequest.Resource, resource)) return nil, nil, reg, err } return []byte(payload), key, reg, nil }
// IssueCertificate attempts to convert a CSR into a signed Certificate, while // enforcing all policies. Names (domains) in the CertificateRequest will be // lowercased before storage. func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest, regID int64) (core.Certificate, error) { emptyCert := core.Certificate{} var err error if err := ca.checkHSMFault(); err != nil { return emptyCert, err } key, ok := csr.PublicKey.(crypto.PublicKey) if !ok { err = core.MalformedRequestError("Invalid public key in CSR.") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } if err = ca.keyPolicy.GoodKey(key); err != nil { err = core.MalformedRequestError(fmt.Sprintf("Invalid public key in CSR: %s", err.Error())) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } if badSignatureAlgorithms[csr.SignatureAlgorithm] { err = core.MalformedRequestError("Invalid signature algorithm in CSR") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Pull hostnames from CSR // Authorization is checked by the RA commonName := "" hostNames := make([]string, len(csr.DNSNames)) copy(hostNames, csr.DNSNames) if len(csr.Subject.CommonName) > 0 { commonName = strings.ToLower(csr.Subject.CommonName) hostNames = append(hostNames, commonName) } else if len(hostNames) > 0 { commonName = strings.ToLower(hostNames[0]) } else { err = core.MalformedRequestError("Cannot issue a certificate without a hostname.") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Collapse any duplicate names. Note that this operation may re-order the names hostNames = core.UniqueLowerNames(hostNames) if ca.maxNames > 0 && len(hostNames) > ca.maxNames { err = core.MalformedRequestError(fmt.Sprintf("Certificate request has %d names, maximum is %d.", len(hostNames), ca.maxNames)) ca.log.WarningErr(err) return emptyCert, err } // Verify that names are allowed by policy identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: commonName} if err = ca.PA.WillingToIssue(identifier, regID); err != nil { err = core.MalformedRequestError(fmt.Sprintf("Policy forbids issuing for name %s", commonName)) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } for _, name := range hostNames { identifier = core.AcmeIdentifier{Type: core.IdentifierDNS, Value: name} if err = ca.PA.WillingToIssue(identifier, regID); err != nil { err = core.MalformedRequestError(fmt.Sprintf("Policy forbids issuing for name %s", name)) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } } notAfter := ca.clk.Now().Add(ca.validityPeriod) if ca.notAfter.Before(notAfter) { err = core.InternalServerError("Cannot issue a certificate that expires after the intermediate certificate.") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Convert the CSR to PEM csrPEM := string(pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE REQUEST", Bytes: csr.Raw, })) // We want 136 bits of random number, plus an 8-bit instance id prefix. const randBits = 136 serialBytes := make([]byte, randBits/8+1) serialBytes[0] = byte(ca.prefix) _, err = rand.Read(serialBytes[1:]) if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Serial randomness failed, err=[%v]", err)) return emptyCert, err } serialHex := hex.EncodeToString(serialBytes) serialBigInt := big.NewInt(0) serialBigInt = serialBigInt.SetBytes(serialBytes) var profile string switch key.(type) { case *rsa.PublicKey: profile = ca.rsaProfile case *ecdsa.PublicKey: profile = ca.ecdsaProfile default: err = core.InternalServerError(fmt.Sprintf("unsupported key type %T", key)) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Send the cert off for signing req := signer.SignRequest{ Request: csrPEM, Profile: profile, Hosts: hostNames, Subject: &signer.Subject{ CN: commonName, }, Serial: serialBigInt, } certPEM, err := ca.signer.Sign(req) ca.noteHSMFault(err) if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Signer failed, rolling back: serial=[%s] err=[%v]", serialHex, err)) return emptyCert, err } if len(certPEM) == 0 { err = core.InternalServerError("No certificate returned by server") // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("PEM empty from Signer, rolling back: serial=[%s] err=[%v]", serialHex, err)) return emptyCert, err } block, _ := pem.Decode(certPEM) if block == nil || block.Type != "CERTIFICATE" { err = core.InternalServerError("Invalid certificate value returned") // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("PEM decode error, aborting and rolling back issuance: pem=[%s] err=[%v]", certPEM, err)) return emptyCert, err } certDER := block.Bytes cert := core.Certificate{ DER: certDER, } // This is one last check for uncaught errors if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Uncaught error, aborting and rolling back issuance: pem=[%s] err=[%v]", certPEM, err)) return emptyCert, err } // Store the cert with the certificate authority, if provided _, err = ca.SA.AddCertificate(certDER, regID) if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf( "Failed RPC to store at SA, orphaning certificate: b64der=[%s] err=[%v], regID=[%d]", base64.StdEncoding.EncodeToString(certDER), err, regID, )) return emptyCert, err } // Submit the certificate to any configured CT logs go ca.Publisher.SubmitToCT(certDER) // Do not return an err at this point; caller must know that the Certificate // was issued. (Also, it should be impossible for err to be non-nil here) return cert, nil }
// NewAuthorization constuct a new Authz from a request. Values (domains) in // request.Identifier will be lowercased before storage. func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) { reg, err := ra.SA.GetRegistration(regID) if err != nil { err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) return authz, err } identifier := request.Identifier identifier.Value = strings.ToLower(identifier.Value) // Check that the identifier is present and appropriate if err = ra.PA.WillingToIssue(identifier, regID); err != nil { err = core.UnauthorizedError(err.Error()) return authz, err } limit := &ra.rlPolicies.PendingAuthorizationsPerAccount if err = checkPendingAuthorizationLimit(ra.SA, limit, regID); err != nil { return authz, err } // Check CAA records for the requested identifier present, valid, err := ra.VA.CheckCAARecords(identifier) if err != nil { return authz, err } // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ra.log.Audit(fmt.Sprintf("Checked CAA records for %s, registration ID %d [Present: %t, Valid for issuance: %t]", identifier.Value, regID, present, valid)) if !valid { err = errors.New("CAA check for identifier failed") return authz, err } // Create validations. The WFE will update them with URIs before sending them out. challenges, combinations, err := ra.PA.ChallengesFor(identifier, ®.Key) expires := ra.clk.Now().Add(ra.pendingAuthorizationLifetime) // Partially-filled object authz = core.Authorization{ Identifier: identifier, RegistrationID: regID, Status: core.StatusPending, Combinations: combinations, Challenges: challenges, Expires: &expires, } // Get a pending Auth first so we can get our ID back, then update with challenges authz, err = ra.SA.NewPendingAuthorization(authz) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(fmt.Sprintf("Invalid authorization request: %s", err)) return core.Authorization{}, err } // Check each challenge for sanity. for _, challenge := range authz.Challenges { if !challenge.IsSane(false) { // InternalServerError because we generated these challenges, they should // be OK. err = core.InternalServerError(fmt.Sprintf("Challenge didn't pass sanity check: %+v", challenge)) return core.Authorization{}, err } } return authz, err }