From e233fdaa61ecb482912169a768aa08fc770858ab Mon Sep 17 00:00:00 2001 From: Roland Shoemaker Date: Thu, 14 May 2015 14:14:36 -0700 Subject: [PATCH] switch authz and pending_authz to store registration ID instead of key (and update all the random stuff they touched) --- core/interfaces.go | 4 ++-- core/objects.go | 6 ++++-- ra/registration-authority.go | 11 ++++++++--- ra/registration-authority_test.go | 9 ++++++--- rpc/rpc-wrappers.go | 32 ++++++++++++++++++++++++------- sa/storage-authority.go | 14 +++++++------- sa/storage-authority_test.go | 9 +++++---- wfe/web-front-end.go | 21 ++++++++++++-------- wfe/web-front-end_test.go | 10 +++++----- 9 files changed, 75 insertions(+), 41 deletions(-) diff --git a/core/interfaces.go b/core/interfaces.go index 432af8a22..b3202e0df 100644 --- a/core/interfaces.go +++ b/core/interfaces.go @@ -53,7 +53,7 @@ type RegistrationAuthority interface { NewRegistration(Registration, jose.JsonWebKey) (Registration, error) // [WebFrontEnd] - NewAuthorization(Authorization, jose.JsonWebKey) (Authorization, error) + NewAuthorization(Authorization, int) (Authorization, error) // [WebFrontEnd] NewCertificate(CertificateRequest, jose.JsonWebKey) (Certificate, error) @@ -88,7 +88,7 @@ type PolicyAuthority interface { } type StorageGetter interface { - GetRegistration(string) (Registration, error) + GetRegistration(int) (Registration, error) GetRegistrationByKey(jose.JsonWebKey) (Registration, error) GetAuthorization(string) (Authorization, error) GetCertificate(string) ([]byte, error) diff --git a/core/objects.go b/core/objects.go index 8009c3b42..0b2b50a4e 100644 --- a/core/objects.go +++ b/core/objects.go @@ -100,7 +100,7 @@ func (cr CertificateRequest) MarshalJSON() ([]byte, error) { // to account keys. type Registration struct { // Unique identifier - ID string `json:"-" db:"id"` + ID int `json:"-" db:"id"` // Account key to which the details are attached Key jose.JsonWebKey `json:"key" db:"key"` @@ -261,7 +261,9 @@ type Authorization struct { Identifier AcmeIdentifier `json:"identifier,omitempty" db:"identifier"` // The account key that is authorized for the identifier - Key jose.JsonWebKey `json:"key,omitempty" db:"key"` + // Key jose.JsonWebKey `json:"key,omitempty" db:"key"` + // The registration ID associated with the authorization + RegID int `json:"regID,omitempty" db:"regID"` // The status of the validation of this authorization Status AcmeStatus `json:"status,omitempty" db:"status"` diff --git a/ra/registration-authority.go b/ra/registration-authority.go index e7b3efb27..851dc2495 100644 --- a/ra/registration-authority.go +++ b/ra/registration-authority.go @@ -58,7 +58,7 @@ func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration, key return } -func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, key jose.JsonWebKey) (authz core.Authorization, err error) { +func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int) (authz core.Authorization, err error) { identifier := request.Identifier // Check that the identifier is present and appropriate @@ -88,7 +88,7 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization authz = core.Authorization{ ID: authID, Identifier: identifier, - Key: key, + RegID: regID, Status: core.StatusPending, Challenges: challenges, Combinations: combinations, @@ -120,6 +120,11 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, return emptyCert, err } + registration, err := ra.SA.GetRegistrationByKey(requestKey) + if err != nil { + return emptyCert, err + } + // Gather authorized domains from the referenced authorizations authorizedDomains := map[string]bool{} now := time.Now() @@ -127,7 +132,7 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, id := lastPathSegment(url) authz, err := ra.SA.GetAuthorization(id) if err != nil || // Couldn't find authorization - !core.KeyDigestEquals(authz.Key, requestKey) || + authz.RegID != registration.ID || // Key doesn't match authz.Status != core.StatusValid || // Not finalized or not successful authz.Expires.Before(now) || // Expired authz.Identifier.Type != core.IdentifierDNS { diff --git a/ra/registration-authority_test.go b/ra/registration-authority_test.go index 860631761..4b661d1f1 100644 --- a/ra/registration-authority_test.go +++ b/ra/registration-authority_test.go @@ -127,14 +127,14 @@ func assertAuthzEqual(t *testing.T, a1, a2 core.Authorization) { test.Assert(t, a1.ID == a2.ID, "ret != DB: ID") test.Assert(t, a1.Identifier == a2.Identifier, "ret != DB: Identifier") test.Assert(t, a1.Status == a2.Status, "ret != DB: Status") - test.Assert(t, core.KeyDigestEquals(a1.Key, a2.Key), "ret != DB: Key") + test.Assert(t, a1.RegID == a2.RegID, "ret != DB: Key") // Not testing: Contact, Challenges } func TestNewAuthorization(t *testing.T) { _, _, sa, ra := initAuthorities(t) - authz, err := ra.NewAuthorization(AuthzRequest, AccountKey) + authz, err := ra.NewAuthorization(AuthzRequest, 1) test.AssertNotError(t, err, "NewAuthorization failed") // Verify that returned authz same as DB @@ -143,7 +143,7 @@ func TestNewAuthorization(t *testing.T) { assertAuthzEqual(t, authz, dbAuthz) // Verify that the returned authz has the right information - test.Assert(t, core.KeyDigestEquals(authz.Key, AccountKey), "Initial authz did not get the right key") + test.Assert(t, authz.RegID == 1, "Initial authz did not get the right key") test.Assert(t, authz.Identifier == AuthzRequest.Identifier, "Initial authz had wrong identifier") test.Assert(t, authz.Status == core.StatusPending, "Initial authz not pending") @@ -216,6 +216,8 @@ func TestOnValidationUpdate(t *testing.T) { func TestNewCertificate(t *testing.T) { _, _, sa, ra := initAuthorities(t) + sa.NewRegistration(core.Registration{Key: AccountKey}) + AuthzFinal.RegID = 1 AuthzFinal.ID, _ = sa.NewPendingAuthorization() sa.UpdatePendingAuthorization(AuthzFinal) sa.FinalizeAuthorization(AuthzFinal) @@ -225,6 +227,7 @@ func TestNewCertificate(t *testing.T) { AuthzFinalWWW.Identifier.Value = "www.example.com" AuthzFinalWWW.ID, _ = sa.NewPendingAuthorization() sa.FinalizeAuthorization(AuthzFinalWWW) + sa.DumpTables() // Construct a cert request referencing the two authorizations url1, _ := url.Parse("http://doesnt.matter/" + AuthzFinal.ID) diff --git a/rpc/rpc-wrappers.go b/rpc/rpc-wrappers.go index 850591ccc..f32d83950 100644 --- a/rpc/rpc-wrappers.go +++ b/rpc/rpc-wrappers.go @@ -70,7 +70,7 @@ type registrationRequest struct { type authorizationRequest struct { Authz core.Authorization - Key jose.JsonWebKey + RegID int } type certificateRequest struct { @@ -106,7 +106,7 @@ func NewRegistrationAuthorityServer(serverQueue string, channel *amqp.Channel, i return nil } - authz, err := impl.NewAuthorization(ar.Authz, ar.Key) + authz, err := impl.NewAuthorization(ar.Authz, ar.RegID) if err != nil { return nil } @@ -239,8 +239,8 @@ func (rac RegistrationAuthorityClient) NewRegistration(reg core.Registration, ke return } -func (rac RegistrationAuthorityClient) NewAuthorization(authz core.Authorization, key jose.JsonWebKey) (newAuthz core.Authorization, err error) { - data, err := json.Marshal(authorizationRequest{authz, key}) +func (rac RegistrationAuthorityClient) NewAuthorization(authz core.Authorization, regID int) (newAuthz core.Authorization, err error) { + data, err := json.Marshal(authorizationRequest{authz, regID}) if err != nil { return } @@ -436,7 +436,15 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c rpc := NewAmqpRPCServer(serverQueue, channel) rpc.Handle(MethodGetRegistration, func(req []byte) (response []byte) { - reg, err := impl.GetRegistration(string(req)) + var intReq struct { + ID int + } + err := json.Unmarshal(req, &intReq) + if err != nil { + return nil + } + + reg, err := impl.GetRegistration(intReq.ID) if err != nil { return nil } @@ -635,8 +643,18 @@ func NewStorageAuthorityClient(clientQueue, serverQueue string, channel *amqp.Ch return } -func (cac StorageAuthorityClient) GetRegistration(id string) (reg core.Registration, err error) { - jsonReg, err := cac.rpc.DispatchSync(MethodGetRegistration, []byte(id)) +func (cac StorageAuthorityClient) GetRegistration(id int) (reg core.Registration, err error) { + var intReq struct { + ID int + } + intReq.ID = id + + data, err := json.Marshal(intReq) + if err != nil { + return + } + + jsonReg, err := cac.rpc.DispatchSync(MethodGetRegistration, data) if err != nil { return } diff --git a/sa/storage-authority.go b/sa/storage-authority.go index d01f61bdb..f45337c81 100644 --- a/sa/storage-authority.go +++ b/sa/storage-authority.go @@ -175,7 +175,7 @@ func NewSQLStorageAuthority(driver string, name string) (ssa *SQLStorageAuthorit } func (ssa *SQLStorageAuthority) InitTables() (err error) { - ssa.dbMap.AddTableWithName(core.Registration{}, "registrations").SetKeys(false, "ID").SetVersionCol("LockCol") + ssa.dbMap.AddTableWithName(core.Registration{}, "registrations").SetKeys(true, "ID").SetVersionCol("LockCol") ssa.dbMap.AddTableWithName(pendingauthzModel{}, "pending_authz").SetKeys(false, "ID").SetVersionCol("LockCol") ssa.dbMap.AddTableWithName(authzModel{}, "authz").SetKeys(false, "ID") ssa.dbMap.AddTableWithName(core.Certificate{}, "certificates").SetKeys(false, "Serial") @@ -305,19 +305,19 @@ func existingFinal(tx *gorp.Transaction, id string) (bool) { return count > 0 } -func existingRegistration(tx *gorp.Transaction, id string) (bool) { +func existingRegistration(tx *gorp.Transaction, id int) (bool) { var count int64 _ = tx.SelectOne(&count, "SELECT count(*) FROM registrations WHERE id = :id", map[string]interface{} {"id": id}) return count > 0 } -func (ssa *SQLStorageAuthority) GetRegistration(id string) (reg core.Registration, err error) { +func (ssa *SQLStorageAuthority) GetRegistration(id int) (reg core.Registration, err error) { regObj, err := ssa.dbMap.Get(core.Registration{}, id) if err != nil { return } if regObj == nil { - err = fmt.Errorf("No registrations with ID %s", id) + err = fmt.Errorf("No registrations with ID %d", id) return } reg = *regObj.(*core.Registration) @@ -432,11 +432,11 @@ func (ssa *SQLStorageAuthority) NewRegistration(reg core.Registration) (output c } // Check that it doesn't exist already - id := core.NewToken() + /*id := core.NewToken() for existingRegistration(tx, id) { id = core.NewToken() } - reg.ID = id + reg.ID = id*/ err = tx.Insert(®) if err != nil { @@ -506,7 +506,7 @@ func (ssa *SQLStorageAuthority) UpdateRegistration(reg core.Registration) (err e } if !existingRegistration(tx, reg.ID) { - err = errors.New("Requested registration not found " + reg.ID) + err = fmt.Errorf("Requested registration not found %v", reg.ID) tx.Rollback() return } diff --git a/sa/storage-authority_test.go b/sa/storage-authority_test.go index a543b6280..5099ca8a1 100644 --- a/sa/storage-authority_test.go +++ b/sa/storage-authority_test.go @@ -11,6 +11,7 @@ import ( "crypto/rand" "crypto/rsa" "encoding/json" + "fmt" "net/url" "time" @@ -53,10 +54,10 @@ func TestAddRegistration(t *testing.T) { Key: jwk, }) test.AssertNotError(t, err, "Couldn't create new registration") - test.Assert(t, reg.ID != "", "ID shouldn't be blank") + // test.Assert(t, reg.ID != "", "ID shouldn't be blank") dbReg, err := sa.GetRegistration(reg.ID) - test.AssertNotError(t, err, "Couldn't get registration with ID "+reg.ID) + test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID)) expectedReg := core.Registration{ ID: reg.ID, @@ -70,7 +71,7 @@ func TestAddRegistration(t *testing.T) { newReg := core.Registration{ID: reg.ID, Key: jwk, RecoveryToken: "RBNvo1WzZ4oRRq0W9", Contact: []core.AcmeURL{u}, Agreement: "yes"} err = sa.UpdateRegistration(newReg) - test.AssertNotError(t, err, "Couldn't update registration with ID "+reg.ID) + test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID)) dbReg, err = sa.GetRegistrationByKey(jwk) test.AssertNotError(t, err, "Couldn't update registration by key") @@ -109,7 +110,7 @@ func TestAddAuthorization(t *testing.T) { combos[0] = []int{0,1} - newPa := core.Authorization{ID: paID, Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "wut.com"}, Key: jwk, Status: core.StatusPending, Expires: time.Now().AddDate(0, 0, 1), Challenges: []core.Challenge{chall}, Combinations: combos, Contact: []core.AcmeURL{u}} + newPa := core.Authorization{ID: paID, Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "wut.com"}, RegID: 0, Status: core.StatusPending, Expires: time.Now().AddDate(0, 0, 1), Challenges: []core.Challenge{chall}, Combinations: combos, Contact: []core.AcmeURL{u}} err = sa.UpdatePendingAuthorization(newPa) test.AssertNotError(t, err, "Couldn't update pending authorization with ID "+paID) diff --git a/wfe/web-front-end.go b/wfe/web-front-end.go index 83eb7e580..95954fb21 100644 --- a/wfe/web-front-end.go +++ b/wfe/web-front-end.go @@ -15,6 +15,7 @@ import ( "net/http" "net/url" "regexp" + "strconv" "strings" "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd" @@ -248,7 +249,6 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques } regURL := wfe.RegBase + string(reg.ID) - reg.ID = "" responseBody, err := json.Marshal(reg) if err != nil { wfe.sendError(response, "Error marshaling authz", http.StatusInternalServerError) @@ -275,7 +275,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque return } - body, key, _, err := wfe.verifyPOST(request, true) + body, _, currReg, err := wfe.verifyPOST(request, true) if err != nil { if err == sql.ErrNoRows { wfe.sendError(response, "No registration exists matching provided key", http.StatusForbidden) @@ -293,7 +293,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque } // Create new authz and return - authz, err := wfe.RA.NewAuthorization(init, *key) + authz, err := wfe.RA.NewAuthorization(init, currReg.ID) if err != nil { wfe.sendError(response, fmt.Sprintf("Error creating new authz: %+v", err), @@ -408,7 +408,7 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re return case "POST": - body, key, _, err := wfe.verifyPOST(request, true) + body, _, currReg, err := wfe.verifyPOST(request, true) if err != nil { if err == sql.ErrNoRows { wfe.sendError(response, "No registration exists matching provided key", http.StatusForbidden) @@ -426,7 +426,7 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re } // Check that the signing key is the right key - if !core.KeyDigestEquals(key, authz.Key) { + if currReg.ID != authz.RegID { wfe.sendError(response, "Signing key does not match key in authorization", http.StatusForbidden) return } @@ -462,7 +462,12 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *http.Request) { // Requests to this handler should have a path that leads to a known // registration - id := parseIDFromPath(request.URL.Path) + idStr := parseIDFromPath(request.URL.Path) + id, err := strconv.Atoi(idStr) + if err != nil { + wfe.sendError(response, "Registration ID must be an integer", http.StatusBadRequest) + return + } reg, err := wfe.SA.GetRegistration(id) if err != nil { wfe.sendError(response, @@ -488,7 +493,7 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request * response.Write(jsonReply) case "POST": - body, _, _, err := wfe.verifyPOST(request, true) + body, _, currReg, err := wfe.verifyPOST(request, true) if err != nil { if err == sql.ErrNoRows { wfe.sendError(response, "No registration exists matching provided key", http.StatusForbidden) @@ -506,7 +511,7 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request * } // Ask the RA to update this authorization - updatedReg, err := wfe.RA.UpdateRegistration(reg, update) + updatedReg, err := wfe.RA.UpdateRegistration(currReg, update) if err != nil { wfe.sendError(response, "Unable to update registration", http.StatusInternalServerError) return diff --git a/wfe/web-front-end_test.go b/wfe/web-front-end_test.go index 665e46082..f36508da9 100644 --- a/wfe/web-front-end_test.go +++ b/wfe/web-front-end_test.go @@ -29,7 +29,7 @@ type MockSA struct { // empty } -func (sa *MockSA) GetRegistration(string) (core.Registration, error) { +func (sa *MockSA) GetRegistration(int) (core.Registration, error) { return core.Registration{}, nil } @@ -244,8 +244,8 @@ func (ra *MockRegistrationAuthority) NewRegistration(reg core.Registration, jwk return reg, nil } -func (ra *MockRegistrationAuthority) NewAuthorization(authz core.Authorization, jwk jose.JsonWebKey) (core.Authorization, error) { - authz.Key = jwk +func (ra *MockRegistrationAuthority) NewAuthorization(authz core.Authorization, regID int) (core.Authorization, error) { + authz.RegID = regID return authz, nil } @@ -296,7 +296,7 @@ func TestChallenge(t *testing.T) { URI: core.AcmeURL(*challengeURL), }, }, - Key: key, + RegID: 0, } wfe.Challenge(authz, responseWriter, &http.Request{ @@ -500,7 +500,7 @@ func TestAuthorization(t *testing.T) { Body: makeBody(signRequest(t, "{\"identifier\":{\"type\":\"dns\",\"value\":\"test.com\"}}")), }) - test.AssertEquals(t, responseWriter.Body.String(), "{\"identifier\":{\"type\":\"dns\",\"value\":\"test.com\"},\"key\":{\"kty\":\"RSA\",\"n\":\"z2NsNdHeqAiGdPP8KuxfQXat_uatOK9y12SyGpfKw1sfkizBIsNxERjNDke6Wp9MugN9srN3sr2TDkmQ-gK8lfWo0v1uG_QgzJb1vBdf_hH7aejgETRGLNJZOdaKDsyFnWq1WGJq36zsHcd0qhggTk6zVwqczSxdiWIAZzEakIUZ13KxXvoepYLY0Q-rEEQiuX71e4hvhfeJ4l7m_B-awn22UUVvo3kCqmaRlZT-36vmQhDGoBsoUo1KBEU44jfeK5PbNRk7vDJuH0B7qinr_jczHcvyD-2TtPzKaCioMtNh_VZbPNDaG67sYkQlC15-Ff3HPzKKJW2XvkVG91qMvQ\",\"e\":\"AAEAAQ\"},\"expires\":\"0001-01-01T00:00:00Z\"}") + test.AssertEquals(t, responseWriter.Body.String(), "{\"identifier\":{\"type\":\"dns\",\"value\":\"test.com\"},\"expires\":\"0001-01-01T00:00:00Z\"}") var authz core.Authorization err := json.Unmarshal([]byte(responseWriter.Body.String()), &authz)