Merge upstream/master

This commit is contained in:
Roland Shoemaker 2015-05-20 13:03:43 -07:00
commit 74ecad349b
8 changed files with 102 additions and 117 deletions

View File

@ -96,11 +96,9 @@ func NewCertificateAuthorityImpl(cadb core.CertificateAuthorityDatabase, config
return nil, err return nil, err
} }
// In test mode, load a private key from a file. In production, use an HSM. // In test mode, load a private key from a file.
if !config.TestMode { // TODO: This should rely on the CFSSL config, to make it easy to use a key
err = errors.New("OCSP signing with a PKCS#11 key not yet implemented.") // from a file vs an HSM. https://github.com/letsencrypt/boulder/issues/163
return nil, err
}
issuerKey, err := loadIssuerKey(config.IssuerKey) issuerKey, err := loadIssuerKey(config.IssuerKey)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -352,13 +352,6 @@ func TestFailNoSerial(t *testing.T) {
test.AssertError(t, err, "CA should have failed with no SerialPrefix") test.AssertError(t, err, "CA should have failed with no SerialPrefix")
} }
func TestFailNoTestMode(t *testing.T) {
cadb, _, caConfig := setup(t)
caConfig.TestMode = false
_, err := NewCertificateAuthorityImpl(cadb, caConfig)
test.AssertError(t, err, "CA should have failed with TestMode = false, but key provided")
}
func TestRevoke(t *testing.T) { func TestRevoke(t *testing.T) {
cadb, storageAuthority, caConfig := setup(t) cadb, storageAuthority, caConfig := setup(t)
ca, err := NewCertificateAuthorityImpl(cadb, caConfig) ca, err := NewCertificateAuthorityImpl(cadb, caConfig)

View File

@ -77,15 +77,16 @@ func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration, key
} }
func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) { func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) {
if regID == 0 { if regID <= 0 {
err = fmt.Errorf("Registration ID cannot be 0") err = fmt.Errorf("Invalid registration ID")
return authz, err
} }
identifier := request.Identifier identifier := request.Identifier
// Check that the identifier is present and appropriate // Check that the identifier is present and appropriate
if err = ra.PA.WillingToIssue(identifier); err != nil { if err = ra.PA.WillingToIssue(identifier); err != nil {
return return authz, err
} }
// Create validations // Create validations
@ -93,7 +94,7 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization
challenges, combinations := ra.PA.ChallengesFor(identifier) challenges, combinations := ra.PA.ChallengesFor(identifier)
authID, err := ra.SA.NewPendingAuthorization() authID, err := ra.SA.NewPendingAuthorization()
if err != nil { if err != nil {
return return authz, err
} }
for i := range challenges { for i := range challenges {
// Ignoring these errors because we construct the URLs to be correct // Ignoring these errors because we construct the URLs to be correct
@ -102,7 +103,7 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization
if !challenges[i].IsSane(false) { if !challenges[i].IsSane(false) {
err = fmt.Errorf("Challenge didn't pass sanity check: %+v", challenges[i]) err = fmt.Errorf("Challenge didn't pass sanity check: %+v", challenges[i])
return return authz, err
} }
} }
@ -118,7 +119,7 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization
// Store the authorization object, then return it // Store the authorization object, then return it
err = ra.SA.UpdatePendingAuthorization(authz) err = ra.SA.UpdatePendingAuthorization(authz)
return return authz, err
} }
func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (core.Certificate, error) { func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (core.Certificate, error) {
@ -150,6 +151,11 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest,
ra.log.Audit(fmt.Sprintf("Certificate request %s - %s", logEventResult, string(jsonLogEvent))) ra.log.Audit(fmt.Sprintf("Certificate request %s - %s", logEventResult, string(jsonLogEvent)))
}() }()
if regID <= 0 {
err = fmt.Errorf("Invalid registration ID")
return emptyCert, err
}
// Verify the CSR // Verify the CSR
// TODO: Verify that other aspects of the CSR are appropriate // TODO: Verify that other aspects of the CSR are appropriate
csr := req.CSR csr := req.CSR

View File

@ -154,6 +154,9 @@ func assertAuthzEqual(t *testing.T, a1, a2 core.Authorization) {
func TestNewAuthorization(t *testing.T) { func TestNewAuthorization(t *testing.T) {
_, _, sa, ra := initAuthorities(t) _, _, sa, ra := initAuthorities(t)
_, err := ra.NewAuthorization(AuthzRequest, 0)
test.AssertError(t, err, "Authorization cannot have registrationID == 0")
authz, err := ra.NewAuthorization(AuthzRequest, 1) authz, err := ra.NewAuthorization(AuthzRequest, 1)
test.AssertNotError(t, err, "NewAuthorization failed") test.AssertNotError(t, err, "NewAuthorization failed")

View File

@ -56,6 +56,9 @@ func TestAddRegistration(t *testing.T) {
test.AssertNotError(t, err, "Couldn't create new registration") test.AssertNotError(t, err, "Couldn't create new registration")
test.Assert(t, reg.ID != 0, "ID shouldn't be 0") test.Assert(t, reg.ID != 0, "ID shouldn't be 0")
_, err = sa.GetRegistration(0)
test.AssertError(t, err, "Registration object for ID 0 was returned")
dbReg, err := sa.GetRegistration(reg.ID) dbReg, err := sa.GetRegistration(reg.ID)
test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID)) test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID))
@ -74,11 +77,15 @@ func TestAddRegistration(t *testing.T) {
test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID)) test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID))
dbReg, err = sa.GetRegistrationByKey(jwk) dbReg, err = sa.GetRegistrationByKey(jwk)
test.AssertNotError(t, err, "Couldn't update registration by key") test.AssertNotError(t, err, "Couldn't get registration by key")
test.AssertEquals(t, dbReg.ID, newReg.ID) test.AssertEquals(t, dbReg.ID, newReg.ID)
test.AssertEquals(t, dbReg.RecoveryToken, newReg.RecoveryToken) test.AssertEquals(t, dbReg.RecoveryToken, newReg.RecoveryToken)
test.AssertEquals(t, dbReg.Agreement, newReg.Agreement) test.AssertEquals(t, dbReg.Agreement, newReg.Agreement)
jwk.KeyID = "bad"
_, err = sa.GetRegistrationByKey(jwk)
test.AssertError(t, err, "Registration object for invalid key was returned")
} }
func TestAddAuthorization(t *testing.T) { func TestAddAuthorization(t *testing.T) {

View File

@ -132,12 +132,17 @@ function parseLink(link) {
} }
function post(url, body, callback) { function post(url, body, callback) {
var jws = crypto.generateSignature(state.accountPrivateKey, new Buffer(JSON.stringify(body))); var payload = JSON.stringify(body, null, 2);
var payload = JSON.stringify(jws); var jws = crypto.generateSignature(state.accountPrivateKey, new Buffer(payload));
var signed = JSON.stringify(jws, null, 2);
var req = request.post(url, callback); var req = request.post(url, callback);
console.log('Posting to', url, ':\n', payload); console.log('Posting to', url, ':\n', signed);
req.write(payload) console.log('Payload:', payload);
req.on('response', function(response) {
console.log(response.headers)
})
req.write(signed)
req.end(); req.end();
return req; return req;
} }
@ -276,13 +281,13 @@ function sendAgreement(answers) {
post(state.registrationURL, state.registration, post(state.registrationURL, state.registration,
function(err, resp, body) { function(err, resp, body) {
if (err || Math.floor(resp.statusCode / 100) != 2) { if (err || Math.floor(resp.statusCode / 100) != 2) {
console.log("Couldn't POST agreement back to server, aborting.");
console.log("error: " + err);
console.log(body); console.log(body);
console.log("error: " + err);
console.log("Couldn't POST agreement back to server, aborting.");
process.exit(1); process.exit(1);
} else {
inquirer.prompt(questions.domain, getChallenges);
} }
inquirer.prompt(questions.domain, getChallenges);
}); });
} }

View File

@ -275,7 +275,7 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques
return return
} }
regURL := wfe.RegBase + string(reg.ID) regURL := fmt.Sprintf("%s%d", wfe.RegBase, reg.ID)
responseBody, err := json.Marshal(reg) responseBody, err := json.Marshal(reg)
if err != nil { if err != nil {
wfe.sendError(response, "Error marshaling authz", err, http.StatusInternalServerError) wfe.sendError(response, "Error marshaling authz", err, http.StatusInternalServerError)
@ -485,7 +485,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
// TODO The spec says a client should send an Accept: application/pkix-cert // TODO The spec says a client should send an Accept: application/pkix-cert
// header; either explicitly insist or tolerate // header; either explicitly insist or tolerate
response.Header().Add("Location", certURL) response.Header().Add("Location", certURL)
response.Header().Add("Link", link(wfe.IssuerPath, "up")) response.Header().Add("Link", link(wfe.BaseURL+wfe.IssuerPath, "up"))
response.Header().Set("Content-Type", "application/pkix-cert") response.Header().Set("Content-Type", "application/pkix-cert")
response.WriteHeader(http.StatusCreated) response.WriteHeader(http.StatusCreated)
if _, err = response.Write(cert.DER); err != nil { if _, err = response.Write(cert.DER); err != nil {
@ -577,6 +577,24 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re
} }
func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *http.Request) { func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *http.Request) {
if request.Method != "POST" {
wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed)
return
}
body, _, currReg, err := wfe.verifyPOST(request, true)
if err != nil {
if err == sql.ErrNoRows {
wfe.sendError(response,
"No registration exists matching provided key",
err, http.StatusForbidden)
} else {
wfe.sendError(response,
"Unable to read/verify body", err, http.StatusBadRequest)
}
return
}
// Requests to this handler should have a path that leads to a known // Requests to this handler should have a path that leads to a known
// registration // registration
idStr := parseIDFromPath(request.URL.Path) idStr := parseIDFromPath(request.URL.Path)
@ -587,71 +605,43 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *
} else if id <= 0 { } else if id <= 0 {
wfe.sendError(response, "Registration ID must be a positive non-zero integer", id, http.StatusBadRequest) wfe.sendError(response, "Registration ID must be a positive non-zero integer", id, http.StatusBadRequest)
return return
} else if id != currReg.ID {
wfe.sendError(response, "Request signing key did not match registration key", "", http.StatusForbidden)
return
} }
reg, err := wfe.SA.GetRegistration(id)
var update core.Registration
err = json.Unmarshal(body, &update)
if err != nil { if err != nil {
wfe.sendError(response, wfe.sendError(response, "Error unmarshaling registration", err, http.StatusBadRequest)
"Unable to find registration", err,
http.StatusNotFound)
return return
} }
reg.ID = id
switch request.Method { if len(update.Agreement) > 0 && update.Agreement != wfe.SubscriberAgreementURL {
default: wfe.sendError(response, fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", update.Agreement, wfe.SubscriberAgreementURL), nil, http.StatusBadRequest)
wfe.sendError(response, "Method not allowed", "", http.StatusMethodNotAllowed)
return return
case "GET":
jsonReply, err := json.Marshal(reg)
if err != nil {
wfe.sendError(response, "Failed to marshal authz", err, http.StatusInternalServerError)
return
}
response.Header().Set("Content-Type", "application/json")
response.WriteHeader(http.StatusOK)
response.Write(jsonReply)
case "POST":
body, _, currReg, err := wfe.verifyPOST(request, true)
if err != nil {
if err == sql.ErrNoRows {
wfe.sendError(response, "No registration exists matching provided key", err, http.StatusForbidden)
} else {
wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
}
return
}
var update core.Registration
err = json.Unmarshal(body, &update)
if err != nil {
wfe.sendError(response, "Error unmarshaling registration", err, http.StatusBadRequest)
return
}
if len(update.Agreement) > 0 && update.Agreement != wfe.SubscriberAgreementURL {
wfe.sendError(response, fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", update.Agreement, wfe.SubscriberAgreementURL), nil, http.StatusBadRequest)
return
}
// Ask the RA to update this authorization
updatedReg, err := wfe.RA.UpdateRegistration(currReg, update)
if err != nil {
wfe.sendError(response, "Unable to update registration", err, http.StatusInternalServerError)
return
}
jsonReply, err := json.Marshal(updatedReg)
if err != nil {
wfe.sendError(response, "Failed to marshal authz", err, http.StatusInternalServerError)
return
}
response.Header().Set("Content-Type", "application/json")
response.WriteHeader(http.StatusAccepted)
response.Write(jsonReply)
} }
// MergeUpdate copies over only the fields that a client is allowed to modify.
// Note: The RA will also use MergeUpdate to filter out non-updateable fields,
// but we do it here too, so that/ input filtering happens as early in the
// request processing as possible.
currReg.MergeUpdate(update)
// Ask the RA to update this authorization.
updatedReg, err := wfe.RA.UpdateRegistration(currReg, currReg)
if err != nil {
wfe.sendError(response, "Unable to update registration", err, http.StatusInternalServerError)
return
}
jsonReply, err := json.Marshal(updatedReg)
if err != nil {
wfe.sendError(response, "Failed to marshal authz", err, http.StatusInternalServerError)
return
}
response.Header().Set("Content-Type", "application/json")
response.WriteHeader(http.StatusAccepted)
response.Write(jsonReply)
} }
func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request *http.Request) { func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request *http.Request) {

View File

@ -92,7 +92,7 @@ func (sa *MockSA) GetRegistration(id int64) (core.Registration, error) {
var parsedKey jose.JsonWebKey var parsedKey jose.JsonWebKey
parsedKey.UnmarshalJSON(keyJSON) parsedKey.UnmarshalJSON(keyJSON)
return core.Registration{Key: parsedKey, Agreement: "yup"}, nil return core.Registration{ID: id, Key: parsedKey, Agreement: "yup"}, nil
} }
func (sa *MockSA) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration, error) { func (sa *MockSA) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration, error) {
@ -102,16 +102,16 @@ func (sa *MockSA) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration,
test2KeyPublic.UnmarshalJSON([]byte(test2KeyPublicJSON)) test2KeyPublic.UnmarshalJSON([]byte(test2KeyPublicJSON))
if core.KeyDigestEquals(jwk, test1KeyPublic) { if core.KeyDigestEquals(jwk, test1KeyPublic) {
return core.Registration{Key: jwk}, nil return core.Registration{ID: 1, Key: jwk}, nil
} }
if core.KeyDigestEquals(jwk, test2KeyPublic) { if core.KeyDigestEquals(jwk, test2KeyPublic) {
// No key found // No key found
return core.Registration{}, sql.ErrNoRows return core.Registration{ID: 2}, sql.ErrNoRows
} }
// Return a fake registration // Return a fake registration
return core.Registration{Agreement: "yup"}, nil return core.Registration{ID: 1, Agreement: "yup"}, nil
} }
func (sa *MockSA) GetAuthorization(string) (core.Authorization, error) { func (sa *MockSA) GetAuthorization(string) (core.Authorization, error) {
@ -374,7 +374,7 @@ func TestChallenge(t *testing.T) {
URI: core.AcmeURL(*challengeURL), URI: core.AcmeURL(*challengeURL),
}, },
}, },
RegistrationID: 0, RegistrationID: 1,
} }
wfe.Challenge(authz, responseWriter, &http.Request{ wfe.Challenge(authz, responseWriter, &http.Request{
@ -614,35 +614,15 @@ func TestRegistration(t *testing.T) {
"{\"type\":\"urn:acme:error:malformed\",\"detail\":\"Method not allowed\"}") "{\"type\":\"urn:acme:error:malformed\",\"detail\":\"Method not allowed\"}")
responseWriter.Body.Reset() responseWriter.Body.Reset()
// Test GET missing entry // Test GET proper entry returns 405
path, _ = url.Parse("/100")
wfe.Registration(responseWriter, &http.Request{
Method: "GET",
URL: path,
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"type\":\"urn:acme:error:malformed\",\"detail\":\"Unable to find registration\"}")
responseWriter.Body.Reset()
// Test GET malformed entry
path, _ = url.Parse("/101")
wfe.Registration(responseWriter, &http.Request{
Method: "GET",
URL: path,
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"type\":\"urn:acme:error:serverInternal\",\"detail\":\"Failed to marshal authz\"}")
responseWriter.Body.Reset()
// Test GET proper entry
path, _ = url.Parse("/1") path, _ = url.Parse("/1")
wfe.Registration(responseWriter, &http.Request{ wfe.Registration(responseWriter, &http.Request{
Method: "GET", Method: "GET",
URL: path, URL: path,
}) })
test.AssertNotContains(t, responseWriter.Body.String(), "urn:acme:error") test.AssertEquals(t,
responseWriter.Body.String(),
"{\"type\":\"urn:acme:error:malformed\",\"detail\":\"Method not allowed\"}")
responseWriter.Body.Reset() responseWriter.Body.Reset()
// Test POST invalid JSON // Test POST invalid JSON
@ -658,7 +638,7 @@ func TestRegistration(t *testing.T) {
responseWriter.Body.Reset() responseWriter.Body.Reset()
// Test POST valid JSON but key is not registered // Test POST valid JSON but key is not registered
path, _ = url.Parse("/1") path, _ = url.Parse("/2")
wfe.Registration(responseWriter, &http.Request{ wfe.Registration(responseWriter, &http.Request{
Method: "POST", Method: "POST",
Body: makeBody(`{ Body: makeBody(`{
@ -684,6 +664,9 @@ func TestRegistration(t *testing.T) {
// Test POST valid JSON with registration up in the mock (with incorrect agreement URL) // Test POST valid JSON with registration up in the mock (with incorrect agreement URL)
result, err := signer.Sign([]byte("{\"agreement\":\"https://letsencrypt.org/im-bad\"}")) result, err := signer.Sign([]byte("{\"agreement\":\"https://letsencrypt.org/im-bad\"}"))
// Test POST valid JSON with registration up in the mock
path, _ = url.Parse("/1")
wfe.Registration(responseWriter, &http.Request{ wfe.Registration(responseWriter, &http.Request{
Method: "POST", Method: "POST",
Body: makeBody(result.FullSerialize()), Body: makeBody(result.FullSerialize()),