// checkAuthorizations checks that each requested name has a valid authorization // that won't expire before the certificate expires. Returns an error otherwise. func (ra *RegistrationAuthorityImpl) checkAuthorizations(ctx context.Context, names []string, registration *core.Registration) error { now := ra.clk.Now() var badNames []string for i := range names { names[i] = strings.ToLower(names[i]) } auths, err := ra.SA.GetValidAuthorizations(ctx, registration.ID, names, now) if err != nil { return err } for _, name := range names { authz := auths[name] if authz == nil { badNames = append(badNames, name) } else if authz.Expires == nil { return fmt.Errorf("Found an authorization with a nil Expires field: id %s", authz.ID) } else if authz.Expires.Before(now) { badNames = append(badNames, name) } } if len(badNames) > 0 { return core.UnauthorizedError(fmt.Sprintf( "Authorizations for these names not found or expired: %s", strings.Join(badNames, ", "))) } return nil }
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 } }
// 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 }
// 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 }
// 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 }
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) } } }
// 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 }
// checkAuthorizations checks that each requested name has a valid authorization // that won't expire before the certificate expires. Returns an error otherwise. func (ra *RegistrationAuthorityImpl) checkAuthorizations(names []string, registration *core.Registration) error { now := ra.clk.Now() var badNames []string 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) { badNames = append(badNames, name) } } if len(badNames) > 0 { return core.UnauthorizedError(fmt.Sprintf( "Authorizations for these names not found or expired: %s", strings.Join(badNames, ", "))) } return nil }
func TestUpdateAuthorizationReject(t *testing.T) { _, sa, ra, _, cleanUp := initAuthorities(t) defer cleanUp() // We know this is OK because of TestNewAuthorization authz, err := ra.NewAuthorization(AuthzRequest, Registration.ID) test.AssertNotError(t, err, "NewAuthorization failed") // Change the account key reg, err := sa.GetRegistration(authz.RegistrationID) test.AssertNotError(t, err, "GetRegistration failed") reg.Key = AccountKeyC // was AccountKeyA err = sa.UpdateRegistration(reg) test.AssertNotError(t, err, "UpdateRegistration failed") // Verify that the RA rejected the authorization request _, err = ra.UpdateAuthorization(authz, ResponseIndex, Response) test.AssertEquals(t, err, core.UnauthorizedError("Challenge cannot be updated with a different key")) t.Log("DONE TestUpdateAuthorizationReject") }
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)) } }
// 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 } // 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) 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 }
// 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 }
// 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 }
// NewCertificate requests the issuance of a certificate. func (ra *RegistrationAuthorityImpl) NewCertificate(ctx context.Context, 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() { 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(ctx, regID) if err != nil { logEvent.Error = err.Error() return emptyCert, err } // Verify the CSR csr := req.CSR if err := csrlib.VerifyCSR(csr, ra.maxNames, &ra.keyPolicy, ra.PA, ra.forceCNFromSAN, regID); err != nil { err = core.MalformedRequestError(err.Error()) 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(names) == 0 { err = core.UnauthorizedError("CSR has no names in it") 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(ctx, names, registration.ID) if err != nil { logEvent.Error = err.Error() return emptyCert, err } err = ra.checkAuthorizations(ctx, 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(ctx, *csr, regID); err != nil { logEvent.Error = err.Error() return emptyCert, err } if ra.publisher != nil { go func() { // Since we don't want this method to be canceled if the parent context // expires, pass a background context to it and run it in a goroutine. _ = ra.publisher.SubmitToCT(context.Background(), cert.DER) }() } 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 } now := ra.clk.Now() logEvent.SerialNumber = core.SerialToString(parsedCertificate.SerialNumber) logEvent.CommonName = parsedCertificate.Subject.CommonName logEvent.NotBefore = parsedCertificate.NotBefore logEvent.NotAfter = parsedCertificate.NotAfter logEvent.ResponseTime = now logEventResult = "successful" issuanceExpvar.Set(now.Unix()) ra.stats.Inc("NewCertificates", 1) 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 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 }
// 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 }
func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (core.Certificate, error) { emptyCert := core.Certificate{} var err error 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 = fmt.Errorf("Invalid registration ID") return emptyCert, err } // Verify the CSR // TODO: Verify that other aspects of the CSR are appropriate 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 csrPreviousDenied, err := ra.SA.AlreadyDeniedCSR(append(csr.DNSNames, csr.Subject.CommonName)) 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 } registration, err := ra.SA.GetRegistration(regID) if err != nil { 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 } // Gather authorized domains from the referenced authorizations authorizedDomains := map[string]bool{} verificationMethodSet := map[string]bool{} now := time.Now() for _, url := range req.Authorizations { id := lastPathSegment(url) authz, err := ra.SA.GetAuthorization(id) if err != nil || // Couldn't find authorization authz.RegistrationID != registration.ID || authz.Status != core.StatusValid || // Not finalized or not successful authz.Expires.Before(now) || // Expired authz.Identifier.Type != core.IdentifierDNS { // XXX: It may be good to fail here instead of ignoring invalid authorizations. // However, it seems like this treatment is more in the spirit of Postel's // law, and it hides information from attackers. continue } for _, challenge := range authz.Challenges { if challenge.Status == core.StatusValid { verificationMethodSet[challenge.Type] = true } } authorizedDomains[authz.Identifier.Value] = true } verificationMethods := []string{} for method, _ := range verificationMethodSet { verificationMethods = append(verificationMethods, method) } logEvent.VerificationMethods = verificationMethods // Validate that authorization key is authorized for all domains names := csr.DNSNames if len(csr.Subject.CommonName) > 0 { names = append(names, csr.Subject.CommonName) } // Validate all domains for _, name := range names { if !authorizedDomains[name] { err = core.UnauthorizedError(fmt.Sprintf("Key not authorized for name %s", name)) 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 var cert core.Certificate if cert, err = ra.CA.IssueCertificate(*csr, regID); err != nil { logEvent.Error = err.Error() return emptyCert, nil } cert.ParsedCertificate, err = x509.ParseCertificate([]byte(cert.DER)) logEvent.SerialNumber = cert.ParsedCertificate.SerialNumber logEvent.CommonName = cert.ParsedCertificate.Subject.CommonName logEvent.NotBefore = cert.ParsedCertificate.NotBefore logEvent.NotAfter = cert.ParsedCertificate.NotAfter logEvent.ResponseTime = time.Now() logEventResult = "successful" return cert, nil }