From c3b312118e74d2f5f896fe3352890ce7d21ee283 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Fri, 24 Apr 2015 18:22:27 -0400 Subject: [PATCH 1/5] Add audit logging - Auditing for general errors in executables - Auditing for improper messages received by WFE - Automatic audit wlogging of software errors - Audit logging for mis-routed messages - Audit logging for certificate requests - Auditing for improper messages received by WFE - Add audit events table - Expect more details in TestRegistration in web-front-end_test.go - Remove "extra" debug details from web-front-end.go per Issue #174 - Improve test coverage of web-front-end.go - WFE audit updates for revocation support rebase - Add audit messages to RPC for Improper Messages and Error Conditions - Also note misrouted messages --- .gitignore | 3 + cmd/boulder-ca/main.go | 3 + cmd/boulder-ra/main.go | 3 + cmd/boulder-sa/main.go | 3 + cmd/boulder-va/main.go | 3 + cmd/boulder-wfe/main.go | 3 + cmd/boulder/main.go | 3 + cmd/shell.go | 4 +- core/interfaces.go | 2 +- core/objects.go | 2 +- docs/requirements/audit_events.csv | 10 ++ docs/requirements/print_comment_markers | 2 + log/audit-logger.go | 197 +++++++++++++++--------- log/audit-logger_test.go | 36 +++++ ra/registration-authority.go | 78 +++++++++- rpc/amqp-rpc.go | 25 +-- rpc/rpc-wrappers.go | 170 +++++++++++++++----- test/test-tools.go | 6 + wfe/web-front-end.go | 128 +++++++-------- wfe/web-front-end_test.go | 184 +++++++++++++++++++++- 20 files changed, 661 insertions(+), 204 deletions(-) create mode 100644 docs/requirements/audit_events.csv create mode 100755 docs/requirements/print_comment_markers diff --git a/.gitignore b/.gitignore index ee612ec5e..36266b9ae 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ _obj _test bin +# Test files +test/js/node_modules + # Architecture specific extensions/prefixes *.[568vq] [568vq].out diff --git a/cmd/boulder-ca/main.go b/cmd/boulder-ca/main.go index 7da59e8d9..bb3ba69e3 100644 --- a/cmd/boulder-ca/main.go +++ b/cmd/boulder-ca/main.go @@ -28,6 +28,9 @@ func main() { auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + defer auditlogger.AuditPanic() + blog.SetAuditLogger(auditlogger) cadb, err := ca.NewCertificateAuthorityDatabaseImpl(c.CA.DBDriver, c.CA.DBName) diff --git a/cmd/boulder-ra/main.go b/cmd/boulder-ra/main.go index 5ecf965d0..62034279c 100644 --- a/cmd/boulder-ra/main.go +++ b/cmd/boulder-ra/main.go @@ -25,6 +25,9 @@ func main() { auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + defer auditlogger.AuditPanic() + blog.SetAuditLogger(auditlogger) rai := ra.NewRegistrationAuthorityImpl() diff --git a/cmd/boulder-sa/main.go b/cmd/boulder-sa/main.go index f87fa8681..ebe970491 100644 --- a/cmd/boulder-sa/main.go +++ b/cmd/boulder-sa/main.go @@ -28,6 +28,9 @@ func main() { auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + defer auditlogger.AuditPanic() + blog.SetAuditLogger(auditlogger) sai, err := sa.NewSQLStorageAuthority(c.SA.DBDriver, c.SA.DBName) diff --git a/cmd/boulder-va/main.go b/cmd/boulder-va/main.go index ac0341fc3..b93f53e3b 100644 --- a/cmd/boulder-va/main.go +++ b/cmd/boulder-va/main.go @@ -25,6 +25,9 @@ func main() { auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + defer auditlogger.AuditPanic() + blog.SetAuditLogger(auditlogger) go cmd.ProfileCmd("VA", stats) diff --git a/cmd/boulder-wfe/main.go b/cmd/boulder-wfe/main.go index f127f8771..222c228b2 100644 --- a/cmd/boulder-wfe/main.go +++ b/cmd/boulder-wfe/main.go @@ -73,6 +73,9 @@ func main() { auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + defer auditlogger.AuditPanic() + blog.SetAuditLogger(auditlogger) wfe := wfe.NewWebFrontEndImpl() diff --git a/cmd/boulder/main.go b/cmd/boulder/main.go index f79d4bbe5..0d1ae80f1 100644 --- a/cmd/boulder/main.go +++ b/cmd/boulder/main.go @@ -65,6 +65,9 @@ func main() { auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + defer auditlogger.AuditPanic() + blog.SetAuditLogger(auditlogger) // Run StatsD profiling diff --git a/cmd/shell.go b/cmd/shell.go index b69f3d7d2..6e596e0e5 100644 --- a/cmd/shell.go +++ b/cmd/shell.go @@ -140,8 +140,8 @@ func (as *AppShell) Run() { // FailOnError exits and prints an error message if we encountered a problem func FailOnError(err error, msg string) { if err != nil { - fmt.Fprintf(os.Stderr, "%s: %s\n", msg, err) - os.Exit(1) + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + panic(fmt.Sprintf("%s: %s", msg, err)) } } diff --git a/core/interfaces.go b/core/interfaces.go index 1fc186810..f3accb0a3 100644 --- a/core/interfaces.go +++ b/core/interfaces.go @@ -68,7 +68,7 @@ type RegistrationAuthority interface { RevokeCertificate(x509.Certificate) error // [ValidationAuthority] - OnValidationUpdate(Authorization) + OnValidationUpdate(Authorization) error } type ValidationAuthority interface { diff --git a/core/objects.go b/core/objects.go index dba0d8fd9..4519fd8d0 100644 --- a/core/objects.go +++ b/core/objects.go @@ -321,7 +321,7 @@ func (jb *JsonBuffer) UnmarshalJSON(data []byte) (err error) { // thing exposed on the wire is the certificate itself. type Certificate struct { RegistrationID int64 `db:"registrationID"` - + // The parsed version of DER. Useful for extracting things like serial number. ParsedCertificate *x509.Certificate `db:"-"` diff --git a/docs/requirements/audit_events.csv b/docs/requirements/audit_events.csv new file mode 100644 index 000000000..d69e612cb --- /dev/null +++ b/docs/requirements/audit_events.csv @@ -0,0 +1,10 @@ +UUID,Shortname,CPS Reference,Description +11917fa4-10ef-4e0d-9105-bacbe7836a3c,Certificate Requests,,"All Certificate requests – Date and time of request, type of event, and request information are automatically logged by the application. This includes Issuance, renewal, and re-key requests as well as sender/requester DN, Certificate serial number, initial application, method of request (online, in-person), source of verification, name of document presented for identity proofing, all fields verified in the application, Certificate common name, new validity period dates, date and time of response and success or failure indication are automatically logged by the application, and all associated error messages and codes. Manual interactions with participants such as telephone or in person inquiries and results of verification calls will be logged manually in a logbook or in a computer-based recording/tracking system and include date/time, description of interaction and identity provided." +4e85d791-09c0-4ab3-a837-d3d67e945134,Revocation Requests,,"All Certificate Revocation requests – Date and time of Revocation request, sender/requester DN, Certificate serial number, subject DN of Certificate to revoke, Subscriber’s common name, Revocation reason, date and time of response and success or failure indication are automatically logged by the application; manual interactions with requestors such as telephone or in person inquiries and requests for Revocation are logged manually in a logbook or in a computer-based recording/tracking system. The date/time, description of interaction and identity provided are also recorded." +a88fd00b-fa62-4a2f-9226-3eef27e2a50e,Certificate Updates,,"The approval or rejection of a Certificate status change request – Identity of equipment operator who initiated the request, message contents, message source, destination, and success or failure indication are automatically logged by the application." +d510aa7e-ce9d-44ea-aa6d-4479a5652439,Software Updates and Migrations,,"Any security-relevant changes to the configuration of a component – Date and time of modification, name of modifier, description of modification, build information (i.e. size, version number) of any modified files and the reason for modification are manually logged during change management processes." +78722466-9519-42bd-8a16-9c1ec1ca29ea,Compromise Notifications,,"All Certificate compromise notification requests – Date and time of notification, identity of person making the notification, identification of entity compromised, and a description of the compromise are logged manually by the personnel who receive the notification (e.g. Help Desk, RA Operators, etc.) and by RA/RA Operators’ system processing logs." +9cc4d537-8534-4970-8665-4b382abe82f3,Error Conditions,,"Software error conditions – Date and time of event, and description of event are automatically logged by the application reporting the event or by the operating system." +03806e9f-b6f3-4b29-b0a2-46fae57646d5,Software Integrity Failures,,"Software check integrity failures – Date and time of event, and description of event are automatically logged by the application reporting the event or by the operating system." +0786b6f2-91ca-4f48-9883-842a19084c64,Improper Messages,,"Receipt of improper messages – Date and time of event, and description of event are automatically logged by the application reporting the event or by the operating system." +f523f21f-12d2-4c31-b2eb-ee4b7d96d60e,Misrouted Messages,,"Misrouted messages – Date and time of event, and description of event are automatically logged by the application reporting the event or by the operating system." diff --git a/docs/requirements/print_comment_markers b/docs/requirements/print_comment_markers new file mode 100755 index 000000000..0bff3b168 --- /dev/null +++ b/docs/requirements/print_comment_markers @@ -0,0 +1,2 @@ +#!/bin/bash +cat audit_events.csv | tail +2 | awk -F "," '{print "// AUDIT[", $2, "]", $1;}' diff --git a/log/audit-logger.go b/log/audit-logger.go index 164559ed4..683ef0cd9 100644 --- a/log/audit-logger.go +++ b/log/audit-logger.go @@ -27,6 +27,17 @@ var _Singleton singleton // The constant used to identify audit-specific messages const auditTag = "[AUDIT]" +// Constant used to indicate an emergency exit to the executor +const emergencyReturnValue = 13 + +// exitFunction closes the running system +type exitFunction func () + +// Default to calling os.Exit() +func defaultEmergencyExit() { + os.Exit(emergencyReturnValue) +} + // AuditLogger is a System Logger with additional audit-specific methods. // In addition to all the standard syslog.Writer methods from // http://golang.org/pkg/log/syslog/#Writer, you can also call @@ -35,6 +46,7 @@ const auditTag = "[AUDIT]" type AuditLogger struct { *syslog.Writer Stats statsd.Statter + exitFunction exitFunction } // Dial establishes a connection to the log daemon by passing through @@ -54,7 +66,12 @@ func NewAuditLogger(log *syslog.Writer, stats statsd.Statter) (*AuditLogger, err if log == nil { return nil, errors.New("Attempted to use a nil System Logger.") } - return &AuditLogger{log, stats}, nil + audit := &AuditLogger{ + log, + stats, + defaultEmergencyExit, + } + return audit, nil } // initializeAuditLogger should only be used in unit tests. Failures in this @@ -96,90 +113,118 @@ func GetAuditLogger() *AuditLogger { return _Singleton.log } +// Log the provided message at the appropriate level, writing to +// both stdout and the Logger, as well as informing statsd. +func (log *AuditLogger) logAtLevel(level, msg string) (err error) { + fmt.Printf("%s\n", msg) + log.Stats.Inc(level, 1, 1.0) + + switch level { + case "Logging.Alert": + err = log.Writer.Alert(msg) + case "Logging.Crit": + err = log.Writer.Crit(msg) + case "Logging.Debug": + err = log.Writer.Debug(msg) + case "Logging.Emerg": + err = log.Writer.Emerg(msg) + case "Logging.Err": + err = log.Writer.Err(msg) + case "Logging.Info": + err = log.Writer.Info(msg) + case "Logging.Warning": + err = log.Writer.Warning(msg) + } + return +} + +// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 +func (log *AuditLogger) auditAtLevel(level, msg string) (err error) { + // Submit a separate counter that marks an Audit event + log.Stats.Inc("Logging.Audit", 1, 1.0) + + text := fmt.Sprintf("%s %s", auditTag, msg) + return log.logAtLevel(level, text) +} + +// AuditPanic catches panics executables. This method should be added +// in a defer statement as early as possible +// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 +func (log *AuditLogger) AuditPanic() { + if err := recover(); err != nil { + log.Audit(fmt.Sprintf("Panic: %v", err)) + } +} + +// WarningErr formats an error for the Warn level. +func (log *AuditLogger) WarningErr(msg error) (err error) { + return log.logAtLevel("Logging.Warning", fmt.Sprintf("%s", msg)) +} + +// Alert level messages pass through normally. +func (log *AuditLogger) Alert(msg string) (err error) { + return log.logAtLevel("Logging.Alert", msg) +} + +// Crit level messages are automatically marked for audit +func (log *AuditLogger) Crit(msg string) (err error) { + return log.auditAtLevel("Logging.Crit", msg) +} + +// Debug level messages pass through normally. +func (log *AuditLogger) Debug(msg string) (err error) { + return log.logAtLevel("Logging.Debug", msg) +} + +// Emerg level messages are automatically marked for audit +func (log *AuditLogger) Emerg(msg string) (err error) { + return log.auditAtLevel("Logging.Emerg", msg) +} + +// Err level messages are automatically marked for audit +func (log *AuditLogger) Err(msg string) (err error) { + return log.auditAtLevel("Logging.Err", msg) +} + +// Info level messages pass through normally. +func (log *AuditLogger) Info(msg string) (err error) { + return log.logAtLevel("Logging.Info", msg) +} + +// Warning level messages pass through normally. +func (log *AuditLogger) Warning(msg string) (err error) { + return log.logAtLevel("Logging.Warning", msg) +} + +// Notice level messages pass through normally. +func (log *AuditLogger) Notice(msg string) (err error) { + return log.logAtLevel("Logging.Notice", msg) +} + // Audit sends a NOTICE-severity message that is prefixed with the // audit tag, for special handling at the upstream system logger. +// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 func (log *AuditLogger) Audit(msg string) (err error) { - fmt.Println(msg) - err = log.Notice(fmt.Sprintf("%s %s", auditTag, msg)) - - log.Stats.Inc("Logging.Audit", 1, 1.0) - - return + return log.auditAtLevel("Logging.Notice", msg) } -// Audit can format an error for auditing; it does so at ERR level. +// AuditErr can format an error for auditing; it does so at ERR level. +// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 func (log *AuditLogger) AuditErr(msg error) (err error) { - fmt.Println(msg) - err = log.Err(fmt.Sprintf("%s %s", auditTag, msg)) - - log.Stats.Inc("Logging.Audit", 1, 1.0) - - return + return log.auditAtLevel("Logging.Err", fmt.Sprintf("%s", msg)) } -// Warning formats an error for the Warn level. -func (log *AuditLogger) WarningErr(msg error) (err error) { - fmt.Println(msg) - err = log.Warning(fmt.Sprintf("%s", msg)) - - return +// SetEmergencyExitFunc changes the systems' behavior on an emergency exit. +func (log *AuditLogger) SetEmergencyExitFunc(exit exitFunction) { + log.exitFunction = exit } -func (log *AuditLogger) Alert(msg string) (err error) { - fmt.Println(msg) - log.Stats.Inc("Logging.Alert", 1, 1.0) - return log.Writer.Alert(msg) -} - -func (log *AuditLogger) Crit(msg string) (err error) { - fmt.Println(msg) - log.Stats.Inc("Logging.Crit", 1, 1.0) - return log.Writer.Crit(msg) -} - -func (log *AuditLogger) Debug(msg string) (err error) { - fmt.Println(msg) - log.Stats.Inc("Logging.Debug", 1, 1.0) - return log.Writer.Debug(msg) -} - -func (log *AuditLogger) Emerg(msg string) (err error) { - fmt.Println(msg) - log.Stats.Inc("Logging.Emerg", 1, 1.0) - return log.Writer.Emerg(msg) -} - -func (log *AuditLogger) Err(msg string) (err error) { - fmt.Println(msg) - log.Stats.Inc("Logging.Err", 1, 1.0) - return log.Writer.Err(msg) -} - -func (log *AuditLogger) Info(msg string) (err error) { - fmt.Println(msg) - log.Stats.Inc("Logging.Info", 1, 1.0) - return log.Writer.Info(msg) -} - -func (log *AuditLogger) Warning(msg string) (err error) { - fmt.Println(msg) - log.Stats.Inc("Logging.Warning", 1, 1.0) - return log.Writer.Warning(msg) -} - -func (log *AuditLogger) Notice(msg string) (err error) { - fmt.Println(msg) - log.Stats.Inc("Logging.Notice", 1, 1.0) - return log.Writer.Notice(msg) -} - -const EMERGENCY_RETVAL = 13 - +// EmergencyExit triggers an immediate Boulder shutdown in the event of serious +// errors. This function will provide the necessary housekeeping. +// Currently, make an emergency log entry and exit; the Activity Monitor +// should notice the Emerg level event and shut down all components. +// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 func (log *AuditLogger) EmergencyExit(msg string) { - // Some errors may be serious enough to trigger an immediate Boulder - // shutdown. This function will provide the necessary housekeeping. - // Currently, make an emergency log entry and exit; the Activity Monitor - // should notice the Emerg level event and shut down all components. - log.Emerg(msg) - os.Exit(EMERGENCY_RETVAL) + log.auditAtLevel("Logging.Emerg", msg) + log.exitFunction() } diff --git a/log/audit-logger_test.go b/log/audit-logger_test.go index 4dd399608..9881c2d0f 100644 --- a/log/audit-logger_test.go +++ b/log/audit-logger_test.go @@ -15,6 +15,7 @@ import ( ) func TestConstruction(t *testing.T) { + t.Parallel() writer, err := syslog.New(syslog.LOG_EMERG|syslog.LOG_KERN, "tag") test.AssertNotError(t, err, "Could not construct syslog object") @@ -24,6 +25,7 @@ func TestConstruction(t *testing.T) { } func TestSingleton(t *testing.T) { + t.Parallel() log1 := GetAuditLogger() test.AssertNotNil(t, log1, "Logger shouldn't be nil") @@ -53,24 +55,28 @@ func TestSingleton(t *testing.T) { } func TestDial(t *testing.T) { + t.Parallel() stats, _ := statsd.NewNoopClient(nil) _, err := Dial("", "", "tag", stats) test.AssertNotError(t, err, "Could not construct audit logger") } func TestDialError(t *testing.T) { + t.Parallel() stats, _ := statsd.NewNoopClient(nil) _, err := Dial("_fail", "_fail", "tag", stats) test.AssertError(t, err, "Audit Logger should have failed") } func TestConstructionNil(t *testing.T) { + t.Parallel() stats, _ := statsd.NewNoopClient(nil) _, err := NewAuditLogger(nil, stats) test.AssertError(t, err, "Nil shouldn't be permitted.") } func TestEmit(t *testing.T) { + t.Parallel() writer, err := syslog.New(syslog.LOG_EMERG|syslog.LOG_KERN, "tag") test.AssertNotError(t, err, "Could not construct syslog object") @@ -82,6 +88,7 @@ func TestEmit(t *testing.T) { } func TestEmitEmpty(t *testing.T) { + t.Parallel() writer, err := syslog.New(syslog.LOG_EMERG|syslog.LOG_KERN, "tag") test.AssertNotError(t, err, "Could not construct syslog object") @@ -93,6 +100,7 @@ func TestEmitEmpty(t *testing.T) { } func TestEmitErrors(t *testing.T) { + t.Parallel() stats, _ := statsd.NewNoopClient(nil) audit, _ := Dial("", "", "tag", stats) @@ -101,6 +109,7 @@ func TestEmitErrors(t *testing.T) { } func TestSyslogMethods(t *testing.T) { + t.Parallel() // Write all logs to UDP on a high port so as to not bother the system // which is running the test, particularly for Emerg() writer, err := syslog.Dial("udp", "127.0.0.1:65530", syslog.LOG_INFO|syslog.LOG_LOCAL0, "") @@ -120,3 +129,30 @@ func TestSyslogMethods(t *testing.T) { audit.Warning("audit-logger_test.go: warning") audit.Alert("audit-logger_test.go: alert") } + +func TestPanic(t *testing.T) { + t.Parallel() + stats, _ := statsd.NewNoopClient(nil) + audit, _ := Dial("", "", "tag", stats) + defer audit.AuditPanic() + panic("Test panic") + // Can't assert anything here or golint gets angry +} + +func TestEmergencyExit(t *testing.T) { + t.Parallel() + // Write all logs to UDP on a high port so as to not bother the system + // which is running the test, particularly for Emerg() + writer, err := syslog.Dial("udp", "127.0.0.1:65530", syslog.LOG_INFO|syslog.LOG_LOCAL0, "") + test.AssertNotError(t, err, "Could not construct syslog object") + + stats, _ := statsd.NewNoopClient(nil) + audit, err := NewAuditLogger(writer, stats) + test.AssertNotError(t, err, "Could not construct audit logger") + + called := false + + audit.SetEmergencyExitFunc(func(){ called = true }) + audit.EmergencyExit("Emergency!") + test.AssertEquals(t, called, true) +} diff --git a/ra/registration-authority.go b/ra/registration-authority.go index 92669a6b4..49ebc38f5 100644 --- a/ra/registration-authority.go +++ b/ra/registration-authority.go @@ -7,7 +7,9 @@ package ra import ( "crypto/x509" + "encoding/json" "fmt" + "math/big" "net/url" "regexp" "strconv" @@ -46,6 +48,21 @@ func lastPathSegment(url core.AcmeURL) string { return allButLastPathSegment.ReplaceAllString(url.Path, "") } +type certificateRequestEvent struct { + ID string `json:"omitempty"` + Requester int64 `json:"omitempty"` + SerialNumber *big.Int `json:"omitempty"` + RequestMethod string `json:"omitempty"` + VerificationMethods []string `json:"omitempty"` + VerifiedFields []string `json:"omitempty"` + CommonName string `json:"omitempty"` + NotBefore time.Time `json:"omitempty"` + NotAfter time.Time `json:"omitempty"` + RequestTime time.Time `json:"omitempty"` + ResponseTime time.Time `json:"omitempty"` + Error string `json:"omitempty"` +} + func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration, key jose.JsonWebKey) (reg core.Registration, err error) { reg = core.Registration{ Key: key, @@ -121,6 +138,7 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, return emptyCert, err } if csrPreviousDenied { + // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ra.log.Audit(fmt.Sprintf("CSR for names %v was previously revoked/denied", csr.DNSNames)) err = core.UnauthorizedError("CSR has already been revoked/denied") return emptyCert, err @@ -138,6 +156,7 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, // 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) @@ -153,8 +172,18 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, 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) + } // Validate that authorization key is authorized for all domains names := csr.DNSNames @@ -168,17 +197,50 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, } } - // Create the certificate - var cert core.Certificate - ra.log.Audit(fmt.Sprintf("Issuing certificate for %s", names)) - if cert, err = ra.CA.IssueCertificate(*csr, regID); err != nil { - return emptyCert, err + // Log the request + logEvent := certificateRequestEvent{ + ID: core.NewToken(), + Requester: registration.ID, + RequestMethod: "online", + VerificationMethods: verificationMethods, + VerifiedFields: []string{"subject.commonName", "subjectAltName"}, + CommonName: names[0], + RequestTime: time.Now(), } - cert.ParsedCertificate, err = x509.ParseCertificate([]byte(cert.DER)) + jsonLogEvent, err := json.Marshal(logEvent) if err != nil { return emptyCert, err } + // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c + ra.log.Audit(fmt.Sprintf("Certificate request - %s", string(jsonLogEvent))) + // 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() + logEvent.ResponseTime = time.Now() + jsonLogEvent, err = json.Marshal(logEvent) + if err != nil { + return emptyCert, err + } + // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c + ra.log.Audit(fmt.Sprintf("Certificate request - error - %s", string(jsonLogEvent))) + return cert, 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() + jsonLogEvent, err = json.Marshal(logEvent) + if err != nil { + return emptyCert, err + } + // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c + err = ra.log.Audit(fmt.Sprintf("Certificate request - result - %s", string(jsonLogEvent))) return cert, nil } @@ -213,7 +275,7 @@ func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) er return ra.CA.RevokeCertificate(core.SerialToString(cert.SerialNumber)) } -func (ra *RegistrationAuthorityImpl) OnValidationUpdate(authz core.Authorization) { +func (ra *RegistrationAuthorityImpl) OnValidationUpdate(authz core.Authorization) error { // Check to see whether the updated validations are sufficient // Current policy is to accept if any validation succeeded for _, val := range authz.Challenges { @@ -233,5 +295,5 @@ func (ra *RegistrationAuthorityImpl) OnValidationUpdate(authz core.Authorization } // Finalize the authorization (error ignored) - _ = ra.SA.FinalizeAuthorization(authz) + return ra.SA.FinalizeAuthorization(authz) } diff --git a/rpc/amqp-rpc.go b/rpc/amqp-rpc.go index 604992d20..8d0da90ff 100644 --- a/rpc/amqp-rpc.go +++ b/rpc/amqp-rpc.go @@ -7,11 +7,13 @@ package rpc import ( "errors" + "fmt" "log" "time" "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/streadway/amqp" "github.com/letsencrypt/boulder/core" + blog "github.com/letsencrypt/boulder/log" ) // TODO: AMQP-RPC messages should be wrapped in JWS. To implement that, @@ -51,7 +53,7 @@ func amqpConnect(url string) (ch *amqp.Channel, err error) { } // A simplified way to declare and subscribe to an AMQP queue -func amqpSubscribe(ch *amqp.Channel, name string) (msgs <-chan amqp.Delivery, err error) { +func amqpSubscribe(ch *amqp.Channel, name string, log *blog.AuditLogger) (msgs <-chan amqp.Delivery, err error) { err = ch.ExchangeDeclare( AmqpExchange, AmqpExchangeType, @@ -61,7 +63,7 @@ func amqpSubscribe(ch *amqp.Channel, name string) (msgs <-chan amqp.Delivery, er AmqpNoWait, nil) if err != nil { - log.Fatalf("Could not declare exchange: %s", err) + log.Crit(fmt.Sprintf("Could not declare exchange: %s", err)) return } @@ -73,7 +75,7 @@ func amqpSubscribe(ch *amqp.Channel, name string) (msgs <-chan amqp.Delivery, er AmqpNoWait, nil) if err != nil { - log.Fatalf("Could not declare queue: %s", err) + log.Crit(fmt.Sprintf("Could not declare queue: %s", err)) return } @@ -84,7 +86,7 @@ func amqpSubscribe(ch *amqp.Channel, name string) (msgs <-chan amqp.Delivery, er false, nil) if err != nil { - log.Fatalf("Could not bind queue: %s", err) + log.Crit(fmt.Sprintf("Could not bind queue: %s", err)) return } @@ -97,7 +99,7 @@ func amqpSubscribe(ch *amqp.Channel, name string) (msgs <-chan amqp.Delivery, er AmqpNoWait, nil) if err != nil { - log.Fatalf("Could not subscribe to queue: %s", err) + log.Crit(fmt.Sprintf("Could not subscribe to queue: %s", err)) return } @@ -113,6 +115,7 @@ func amqpSubscribe(ch *amqp.Channel, name string) (msgs <-chan amqp.Delivery, er type AmqpRPCServer struct { serverQueue string channel *amqp.Channel + log *blog.AuditLogger dispatchTable map[string]func([]byte) []byte } @@ -120,9 +123,11 @@ type AmqpRPCServer struct { // Note that you must call Start() to actually start the server // listening for requests. func NewAmqpRPCServer(serverQueue string, channel *amqp.Channel) *AmqpRPCServer { + log := blog.GetAuditLogger() return &AmqpRPCServer{ serverQueue: serverQueue, channel: channel, + log: log, dispatchTable: make(map[string]func([]byte) []byte), } } @@ -134,7 +139,7 @@ func (rpc *AmqpRPCServer) Handle(method string, handler func([]byte) []byte) { // Starts the AMQP-RPC server running in a separate thread. // There is currently no Stop() method. func (rpc *AmqpRPCServer) Start() (err error) { - msgs, err := amqpSubscribe(rpc.channel, rpc.serverQueue) + msgs, err := amqpSubscribe(rpc.channel, rpc.serverQueue, rpc.log) if err != nil { return } @@ -143,12 +148,14 @@ func (rpc *AmqpRPCServer) Start() (err error) { for msg := range msgs { // XXX-JWS: jws.Verify(body) cb, present := rpc.dispatchTable[msg.Type] - log.Printf(" [s<] received %s(%s) [%s]", msg.Type, core.B64enc(msg.Body), msg.CorrelationId) + rpc.log.Info(fmt.Sprintf(" [s<] received %s(%s) [%s]", msg.Type, core.B64enc(msg.Body), msg.CorrelationId)) if !present { + // AUDIT[ Misrouted Messages ] f523f21f-12d2-4c31-b2eb-ee4b7d96d60e + rpc.log.Audit(fmt.Sprintf("Misrouted message: %s - %s - %s", msg.Type, core.B64enc(msg.Body), msg.CorrelationId)) continue } response := cb(msg.Body) - log.Printf(" [s>] sending %s(%s) [%s]", msg.Type, core.B64enc(response), msg.CorrelationId) + rpc.log.Info(fmt.Sprintf(" [s>] sending %s(%s) [%s]", msg.Type, core.B64enc(response), msg.CorrelationId)) rpc.channel.Publish( AmqpExchange, msg.ReplyTo, @@ -200,7 +207,7 @@ func NewAmqpRPCCLient(clientQueue, serverQueue string, channel *amqp.Channel) (r } // Subscribe to the response queue and dispatch - msgs, err := amqpSubscribe(rpc.channel, clientQueue) + msgs, err := amqpSubscribe(rpc.channel, clientQueue, nil) if err != nil { return } diff --git a/rpc/rpc-wrappers.go b/rpc/rpc-wrappers.go index 2f8de43a6..3bfc0bc20 100644 --- a/rpc/rpc-wrappers.go +++ b/rpc/rpc-wrappers.go @@ -9,11 +9,12 @@ import ( "crypto/x509" "encoding/json" "errors" - "log" + "fmt" "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/streadway/amqp" "github.com/letsencrypt/boulder/core" jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" + blog "github.com/letsencrypt/boulder/log" ) // This file defines RPC wrappers around the ${ROLE}Impl classes, @@ -78,18 +79,32 @@ type certificateRequest struct { RegID int64 } +func improperMessage(method string, err error, obj interface{}) { + log := blog.GetAuditLogger() + log.Audit(fmt.Sprintf("Improper message. method: %s err: %s data: %+v", method, err, obj)) +} +func errorCondition(method string, err error, obj interface{}) { + log := blog.GetAuditLogger() + log.Audit(fmt.Sprintf("Error condition. method: %s err: %s data: %+v", method, err, obj)) +} + + func NewRegistrationAuthorityServer(serverQueue string, channel *amqp.Channel, impl core.RegistrationAuthority) (*AmqpRPCServer, error) { + log := blog.GetAuditLogger() rpc := NewAmqpRPCServer(serverQueue, channel) rpc.Handle(MethodNewRegistration, func(req []byte) (response []byte) { var rr registrationRequest - err := json.Unmarshal(req, &rr) - if err != nil { + if err := json.Unmarshal(req, &rr); err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodNewRegistration, err, req) return nil } reg, err := impl.NewRegistration(rr.Reg, rr.Key) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodNewRegistration, err, reg) return nil } @@ -103,11 +118,15 @@ func NewRegistrationAuthorityServer(serverQueue string, channel *amqp.Channel, i rpc.Handle(MethodNewAuthorization, func(req []byte) (response []byte) { var ar authorizationRequest if err := json.Unmarshal(req, &ar); err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodNewAuthorization, err, req) return nil } authz, err := impl.NewAuthorization(ar.Authz, ar.RegID) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodNewAuthorization, err, ar) return nil } @@ -119,21 +138,22 @@ func NewRegistrationAuthorityServer(serverQueue string, channel *amqp.Channel, i }) rpc.Handle(MethodNewCertificate, func(req []byte) []byte { - log.Printf(" [.] Entering MethodNewCertificate") + log.Info(fmt.Sprintf(" [.] Entering MethodNewCertificate")) var cr certificateRequest if err := json.Unmarshal(req, &cr); err != nil { - log.Printf(" [!] Error unmarshaling certificate request: %s", err.Error()) - log.Printf(" JSON data: %s", string(req)) + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodNewCertificate, err, req) return nil } - log.Printf(" [.] No problem unmarshaling request") + log.Info(fmt.Sprintf(" [.] No problem unmarshaling request")) cert, err := impl.NewCertificate(cr.Req, cr.RegID) if err != nil { - log.Printf(" [!] Error issuing new certificate: %s", err.Error()) + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodNewCertificate, err, cr) return nil } - log.Printf(" [.] No problem issuing new cert") + log.Info(fmt.Sprintf(" [.] No problem issuing new cert")) response, err := json.Marshal(cert) if err != nil { @@ -148,11 +168,15 @@ func NewRegistrationAuthorityServer(serverQueue string, channel *amqp.Channel, i } err := json.Unmarshal(req, &request) if err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodUpdateRegistration, err, req) return nil } reg, err := impl.UpdateRegistration(request.Base, request.Update) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodUpdateRegistration, err, request) return nil } @@ -171,11 +195,15 @@ func NewRegistrationAuthorityServer(serverQueue string, channel *amqp.Channel, i } err := json.Unmarshal(req, &authz) if err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodUpdateAuthorization, err, req) return nil } newAuthz, err := impl.UpdateAuthorization(authz.Authz, authz.Index, authz.Response) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodUpdateAuthorization, err, authz) return nil } @@ -189,21 +217,32 @@ func NewRegistrationAuthorityServer(serverQueue string, channel *amqp.Channel, i rpc.Handle(MethodRevokeCertificate, func(req []byte) []byte { certs, err := x509.ParseCertificates(req) if err != nil || len(certs) == 0 { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodRevokeCertificate, err, req) return nil } // Error explicitly ignored since response is nil anyway - _ = impl.RevokeCertificate(*certs[0]) + err = impl.RevokeCertificate(*certs[0]) + if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodRevokeCertificate, err, certs) + } return nil }) rpc.Handle(MethodOnValidationUpdate, func(req []byte) []byte { var authz core.Authorization if err := json.Unmarshal(req, &authz); err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodOnValidationUpdate, err, req) return nil } - impl.OnValidationUpdate(authz) + if err := impl.OnValidationUpdate(authz); err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodOnValidationUpdate, err, authz) + } return nil }) @@ -317,7 +356,7 @@ func (rac RegistrationAuthorityClient) RevokeCertificate(cert x509.Certificate) return } -func (rac RegistrationAuthorityClient) OnValidationUpdate(authz core.Authorization) { +func (rac RegistrationAuthorityClient) OnValidationUpdate(authz core.Authorization) (err error) { data, err := json.Marshal(authz) if err != nil { return @@ -335,11 +374,15 @@ func NewValidationAuthorityServer(serverQueue string, channel *amqp.Channel, imp rpc.Handle(MethodUpdateValidations, func(req []byte) []byte { var authz core.Authorization if err := json.Unmarshal(req, &authz); err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodUpdateValidations, err, req) return nil } - // Error explicitly ignored since response is nil anyway - _ = impl.UpdateValidations(authz) + if err := impl.UpdateValidations(authz); err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodUpdateValidations, err, authz) + } return nil }) @@ -387,11 +430,15 @@ func NewCertificateAuthorityServer(serverQueue string, channel *amqp.Channel, im csr, err := x509.ParseCertificateRequest(icReq.Bytes) if err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodIssueCertificate, err, req) return nil // XXX } cert, err := impl.IssueCertificate(*csr, icReq.RegID) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodIssueCertificate, err, csr) return nil // XXX } @@ -404,7 +451,11 @@ func NewCertificateAuthorityServer(serverQueue string, channel *amqp.Channel, im }) rpc.Handle(MethodRevokeCertificateCA, func(req []byte) []byte { - _ = impl.RevokeCertificate(string(req)) // XXX + if err := impl.RevokeCertificate(string(req)); err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodRevokeCertificateCA, err, req) + } + return nil }) @@ -466,37 +517,44 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c reg, err := impl.GetRegistration(intReq.ID) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodGetRegistration, err, req) return nil } - jsonReg, err := json.Marshal(reg) + response, err = json.Marshal(reg) if err != nil { return nil } - response = jsonReg return response }) rpc.Handle(MethodGetRegistrationByKey, func(req []byte) (response []byte) { var jwk jose.JsonWebKey - err := json.Unmarshal(req, &jwk) + if err := json.Unmarshal(req, &jwk); err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodGetRegistrationByKey, err, req) + } reg, err := impl.GetRegistrationByKey(jwk) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodGetRegistrationByKey, err, jwk) return nil } - jsonReg, err := json.Marshal(reg) + response, err = json.Marshal(reg) if err != nil { return nil } - response = jsonReg return response }) rpc.Handle(MethodGetAuthorization, func(req []byte) []byte { authz, err := impl.GetAuthorization(string(req)) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodGetAuthorization, err, req) return nil } @@ -519,6 +577,8 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c id, err := impl.AddCertificate(icReq.Bytes, icReq.RegID) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodAddCertificate, err, req) return nil } return []byte(id) @@ -528,12 +588,18 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c var registration core.Registration err := json.Unmarshal(req, registration) if err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodNewRegistration, err, req) return nil } + output, err := impl.NewRegistration(registration) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodNewRegistration, err, registration) return nil } + jsonOutput, err := json.Marshal(output) if err != nil { return nil @@ -543,7 +609,10 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c rpc.Handle(MethodNewPendingAuthorization, func(req []byte) (response []byte) { id, err := impl.NewPendingAuthorization() - if err == nil { + if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodNewPendingAuthorization, err, req) + } else { response = []byte(id) } return response @@ -551,31 +620,40 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c rpc.Handle(MethodUpdatePendingAuthorization, func(req []byte) []byte { var authz core.Authorization - err := json.Unmarshal(req, authz) - if err != nil { + if err := json.Unmarshal(req, authz); err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodUpdatePendingAuthorization, err, req) return nil } - // Error explicitly ignored since response is nil anyway - _ = impl.UpdatePendingAuthorization(authz) + if err := impl.UpdatePendingAuthorization(authz); err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodUpdatePendingAuthorization, err, authz) + } return nil }) rpc.Handle(MethodFinalizeAuthorization, func(req []byte) []byte { var authz core.Authorization - err := json.Unmarshal(req, authz) - if err != nil { + if err := json.Unmarshal(req, authz); err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodFinalizeAuthorization, err, req) return nil } - // Error explicitly ignored since response is nil anyway - _ = impl.FinalizeAuthorization(authz) + if err := impl.FinalizeAuthorization(authz); err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodFinalizeAuthorization, err, authz) + } return nil }) rpc.Handle(MethodGetCertificate, func(req []byte) (response []byte) { cert, err := impl.GetCertificate(string(req)) - if err == nil { + if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodGetCertificate, err, req) + } else { response = []byte(cert) } return response @@ -583,7 +661,10 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c rpc.Handle(MethodGetCertificateByShortSerial, func(req []byte) (response []byte) { cert, err := impl.GetCertificateByShortSerial(string(req)) - if err == nil { + if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodGetCertificateByShortSerial, err, req) + } else { response = []byte(cert) } return response @@ -592,6 +673,8 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c rpc.Handle(MethodGetCertificateStatus, func(req []byte) (response []byte) { status, err := impl.GetCertificateStatus(string(req)) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodGetCertificateStatus, err, req) return nil } @@ -609,13 +692,18 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c ReasonCode int } - err := json.Unmarshal(req, revokeReq) - if err != nil { + if err := json.Unmarshal(req, revokeReq); err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodMarkCertificateRevoked, err, req) return nil } // Error explicitly ignored since response is nil anyway - _ = impl.MarkCertificateRevoked(revokeReq.Serial, revokeReq.OcspResponse, revokeReq.ReasonCode) + err := impl.MarkCertificateRevoked(revokeReq.Serial, revokeReq.OcspResponse, revokeReq.ReasonCode) + if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodMarkCertificateRevoked, err, revokeReq) + } return nil }) @@ -624,12 +712,16 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c Names []string } - err := json.Unmarshal(req, csrReq) - if err != nil { + if err := json.Unmarshal(req, csrReq); err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodAddDeniedCSR, err, req) return nil } - err = impl.AddDeniedCSR(csrReq.Names) + if err := impl.AddDeniedCSR(csrReq.Names); err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodAddDeniedCSR, err, csrReq) + } return nil }) @@ -640,11 +732,15 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c err := json.Unmarshal(req, csrReq) if err != nil { + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodAlreadyDeniedCSR, err, req) return nil } exists, err := impl.AlreadyDeniedCSR(csrReq.Names) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + errorCondition(MethodAlreadyDeniedCSR, err, csrReq) return nil } diff --git a/test/test-tools.go b/test/test-tools.go index 45d78791f..c2e8bc630 100644 --- a/test/test-tools.go +++ b/test/test-tools.go @@ -87,6 +87,12 @@ func AssertContains(t *testing.T, haystack string, needle string) { } } +func AssertNotContains(t *testing.T, haystack string, needle string) { + if strings.Contains(haystack, needle) { + t.Errorf("%s String [%s] contains [%s]", caller(), haystack, needle) + } +} + func AssertSeverity(t *testing.T, data string, severity int) { expected := fmt.Sprintf("\"severity\":%d", severity) AssertContains(t, data, expected) diff --git a/wfe/web-front-end.go b/wfe/web-front-end.go index 31307d40f..41f523a28 100644 --- a/wfe/web-front-end.go +++ b/wfe/web-front-end.go @@ -26,6 +26,7 @@ import ( blog "github.com/letsencrypt/boulder/log" ) +// WebFrontEndImpl represents a Boulder web service and its resources type WebFrontEndImpl struct { RA core.RegistrationAuthority SA core.StorageGetter @@ -54,6 +55,7 @@ type WebFrontEndImpl struct { IssuerCert []byte } +// NewWebFrontEndImpl constructs a web service for Boulder func NewWebFrontEndImpl() WebFrontEndImpl { logger := blog.GetAuditLogger() logger.Notice("Web Front End Starting") @@ -122,7 +124,7 @@ func parseIDFromPath(path string) string { return re.ReplaceAllString(path, "") } -// Problem objects represent problem documents, which are +// ProblemType objects represent problem documents, which are // returned with HTTP error responses // https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00 type ProblemType string @@ -194,8 +196,9 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool) ([] return []byte(payload), key, reg, nil } -func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, message string, code int) { - problem := problem{Detail: message} +// Notify the client of an error condition and log it for audit purposes. +func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, details string, debug interface{}, code int) { + problem := problem{Detail: details} switch code { case http.StatusForbidden: problem.Type = UnauthorizedProblem @@ -210,9 +213,13 @@ func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, message stri } problemDoc, err := json.Marshal(problem) if err != nil { - problemDoc = []byte("{\"detail\": \"Problem marshalling error message.\"}") + return } - wfe.log.Debug("Sending error to client: " + string(problemDoc)) + + // Audit log "Receipt of improper messages" + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + wfe.log.Audit(fmt.Sprintf("Improper HTTP request - %d - %s - %s", code, details, debug)) + // Paraphrased from // https://golang.org/src/net/http/server.go#L1272 response.Header().Set("Content-Type", "application/problem+json") @@ -226,36 +233,34 @@ func link(url, relation string) string { func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, request *http.Request) { if request.Method != "POST" { - wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", "", http.StatusMethodNotAllowed) return } body, key, _, err := wfe.verifyPOST(request, false) if err != nil { - wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) return } var init, unmarshalled core.Registration err = json.Unmarshal(body, &unmarshalled) if err != nil { - wfe.sendError(response, "Error unmarshaling JSON", http.StatusBadRequest) + wfe.sendError(response, "Error unmarshaling JSON", err, http.StatusBadRequest) return } init.MergeUpdate(unmarshalled) reg, err := wfe.RA.NewRegistration(init, *key) if err != nil { - wfe.sendError(response, - fmt.Sprintf("Error creating new registration: %+v", err), - http.StatusInternalServerError) + wfe.sendError(response, "Error creating new registration", err, http.StatusInternalServerError) return } regURL := wfe.RegBase + string(reg.ID) responseBody, err := json.Marshal(reg) if err != nil { - wfe.sendError(response, "Error marshaling authz", http.StatusInternalServerError) + wfe.sendError(response, "Error marshaling authz", err, http.StatusInternalServerError) return } @@ -275,16 +280,16 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, request *http.Request) { if request.Method != "POST" { - wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + 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", http.StatusForbidden) + wfe.sendError(response, "No registration exists matching provided key", err, http.StatusForbidden) } else { - wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) } return } @@ -292,7 +297,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque var init core.Authorization if err = json.Unmarshal(body, &init); err != nil { - wfe.sendError(response, "Error unmarshaling JSON", http.StatusBadRequest) + wfe.sendError(response, "Error unmarshaling JSON", err, http.StatusBadRequest) return } @@ -300,7 +305,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque authz, err := wfe.RA.NewAuthorization(init, currReg.ID) if err != nil { wfe.sendError(response, - fmt.Sprintf("Error creating new authz: %+v", err), + "Error creating new authz", err, http.StatusInternalServerError) return } @@ -310,7 +315,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque authz.ID = "" responseBody, err := json.Marshal(authz) if err != nil { - wfe.sendError(response, "Error marshaling authz", http.StatusInternalServerError) + wfe.sendError(response, "Error marshaling authz", err, http.StatusInternalServerError) return } @@ -327,13 +332,13 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, request *http.Request) { if request.Method != "POST" { - wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed) return } body, requestKey, _, err := wfe.verifyPOST(request, false) if err != nil { - wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) return } @@ -343,36 +348,36 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ var revokeRequest RevokeRequest if err = json.Unmarshal(body, &revokeRequest); err != nil { wfe.log.Debug(fmt.Sprintf("Couldn't unmarshal in revoke request %s", string(body))) - wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) return } providedCert, err := x509.ParseCertificate(revokeRequest.CertificateDER) if err != nil { wfe.log.Debug("Couldn't parse cert in revoke request.") - wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) return } serial := core.SerialToString(providedCert.SerialNumber) certDER, err := wfe.SA.GetCertificate(serial) if err != nil || !bytes.Equal(certDER, revokeRequest.CertificateDER) { - wfe.sendError(response, "No such certificate", http.StatusNotFound) + wfe.sendError(response, "No such certificate", err, http.StatusNotFound) return } parsedCertificate, err := x509.ParseCertificate(certDER) if err != nil { - wfe.sendError(response, "Invalid certificate", http.StatusInternalServerError) + wfe.sendError(response, "Invalid certificate", err, http.StatusInternalServerError) return } certStatus, err := wfe.SA.GetCertificateStatus(serial) if err != nil { - wfe.sendError(response, "No such certificate", http.StatusNotFound) + wfe.sendError(response, "No such certificate", err, http.StatusNotFound) return } if certStatus.Status == core.OCSPStatusRevoked { - wfe.sendError(response, "Certificate already revoked", http.StatusConflict) + wfe.sendError(response, "Certificate already revoked", "", http.StatusConflict) return } @@ -382,6 +387,7 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ wfe.log.Debug("Key mismatch for revoke") wfe.sendError(response, "Revocation request must be signed by private key of cert to be revoked", + requestKey, http.StatusForbidden) return } @@ -390,6 +396,7 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ if err != nil { wfe.sendError(response, "Failed to revoke certificate", + err, http.StatusInternalServerError) } else { wfe.log.Debug(fmt.Sprintf("Revoked %v", serial)) @@ -401,16 +408,16 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request *http.Request) { if request.Method != "POST" { - wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed) return } body, key, reg, err := wfe.verifyPOST(request, true) if err != nil { if err == sql.ErrNoRows { - wfe.sendError(response, "No registration exists matching provided key", http.StatusForbidden) + wfe.sendError(response, "No registration exists matching provided key", err, http.StatusForbidden) } else { - wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) } return } @@ -419,7 +426,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request var init core.CertificateRequest if err = json.Unmarshal(body, &init); err != nil { fmt.Println(err) - wfe.sendError(response, "Error unmarshaling certificate request", http.StatusBadRequest) + wfe.sendError(response, "Error unmarshaling certificate request", err, http.StatusBadRequest) return } @@ -435,7 +442,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request cert, err := wfe.RA.NewCertificate(init, reg.ID) if err != nil { wfe.sendError(response, - fmt.Sprintf("Error creating new cert: %+v", err), + "Error creating new cert", err, http.StatusBadRequest) return } @@ -474,24 +481,22 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re } if !found { - wfe.sendError(response, - fmt.Sprintf("Unable to find challenge"), - http.StatusNotFound) + wfe.sendError(response, "Unable to find challenge", request.URL.RawQuery, http.StatusNotFound) return } switch request.Method { default: - wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", "", http.StatusMethodNotAllowed) return 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", http.StatusForbidden) + wfe.sendError(response, "No registration exists matching provided key", err, http.StatusForbidden) } else { - wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) } return } @@ -499,21 +504,23 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re var challengeResponse core.Challenge if err = json.Unmarshal(body, &challengeResponse); err != nil { - wfe.sendError(response, "Error unmarshaling authorization", http.StatusBadRequest) + wfe.sendError(response, "Error unmarshaling authorization", err, http.StatusBadRequest) return } // Check that the registration ID matching the key used matches // the registration ID on the authz object if currReg.ID != authz.RegistrationID { - wfe.sendError(response, "User registration ID doesn't match registration ID in authorization", http.StatusForbidden) + wfe.sendError(response, "User registration ID doesn't match registration ID in authorization", + fmt.Sprintf("User: %v != Authorization: %v", currReg.ID, authz.RegistrationID), + http.StatusForbidden) return } // Ask the RA to update this authorization updatedAuthz, err := wfe.RA.UpdateAuthorization(authz, challengeIndex, challengeResponse) if err != nil { - wfe.sendError(response, "Unable to update authorization", http.StatusInternalServerError) + wfe.sendError(response, "Unable to update authorization", err, http.StatusInternalServerError) return } @@ -521,7 +528,7 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re // assumption: UpdateAuthorization does not modify order of challenges jsonReply, err := json.Marshal(challenge) if err != nil { - wfe.sendError(response, "Failed to marshal challenge", http.StatusInternalServerError) + wfe.sendError(response, "Failed to marshal authz", err, http.StatusInternalServerError) return } @@ -544,16 +551,16 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request * idStr := parseIDFromPath(request.URL.Path) id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { - wfe.sendError(response, "Registration ID must be an integer", http.StatusBadRequest) + wfe.sendError(response, "Registration ID must be an integer", err, http.StatusBadRequest) return } else if id <= 0 { - wfe.sendError(response, "Registration ID must be a positive non-zero integer", http.StatusBadRequest) + wfe.sendError(response, "Registration ID must be a positive non-zero integer", id, http.StatusBadRequest) return } reg, err := wfe.SA.GetRegistration(id) if err != nil { wfe.sendError(response, - fmt.Sprintf("Unable to find registration: %+v", err), + "Unable to find registration", err, http.StatusNotFound) return } @@ -561,13 +568,13 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request * switch request.Method { default: - wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", "", http.StatusMethodNotAllowed) return case "GET": jsonReply, err := json.Marshal(reg) if err != nil { - wfe.sendError(response, "Failed to marshal authz", http.StatusInternalServerError) + wfe.sendError(response, "Failed to marshal authz", err, http.StatusInternalServerError) return } response.Header().Set("Content-Type", "application/json") @@ -578,9 +585,9 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request * 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) + wfe.sendError(response, "No registration exists matching provided key", err, http.StatusForbidden) } else { - wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) } return } @@ -588,20 +595,20 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request * var update core.Registration err = json.Unmarshal(body, &update) if err != nil { - wfe.sendError(response, "Error unmarshaling registration", http.StatusBadRequest) + wfe.sendError(response, "Error unmarshaling registration", err, 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", http.StatusInternalServerError) + 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", http.StatusInternalServerError) + wfe.sendError(response, "Failed to marshal authz", err, http.StatusInternalServerError) return } response.Header().Set("Content-Type", "application/json") @@ -617,7 +624,7 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request authz, err := wfe.SA.GetAuthorization(id) if err != nil { wfe.sendError(response, - fmt.Sprintf("Unable to find authorization: %+v", err), + "Unable to find authorization", err, http.StatusNotFound) return } @@ -630,13 +637,13 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request switch request.Method { default: - wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed) return case "GET": jsonReply, err := json.Marshal(authz) if err != nil { - wfe.sendError(response, "Failed to marshal authz", http.StatusInternalServerError) + wfe.sendError(response, "Failed to marshal authz", err, http.StatusInternalServerError) return } response.Header().Set("Content-Type", "application/json") @@ -649,35 +656,30 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request var allHex = regexp.MustCompile("^[0-9a-f]+$") -func (wfe *WebFrontEndImpl) notFound(response http.ResponseWriter) { - wfe.sendError(response, "Not found", http.StatusNotFound) -} - func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *http.Request) { path := request.URL.Path switch request.Method { default: - wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed) return case "GET": // Certificate paths consist of the CertBase path, plus exactly sixteen hex // digits. if !strings.HasPrefix(path, wfe.CertPath) { - wfe.notFound(response) + wfe.sendError(response, "Not found", path, http.StatusNotFound) return } serial := path[len(wfe.CertPath):] if len(serial) != 16 || !allHex.Match([]byte(serial)) { - wfe.notFound(response) + wfe.sendError(response, "Not found", serial, http.StatusNotFound) return } wfe.log.Debug(fmt.Sprintf("Requested certificate ID %s", serial)) cert, err := wfe.SA.GetCertificateByShortSerial(serial) if err != nil { - wfe.log.Debug(fmt.Sprintf("Not found cert: %v", err)) - wfe.notFound(response) + wfe.sendError(response, "Not found", err, http.StatusNotFound) return } diff --git a/wfe/web-front-end_test.go b/wfe/web-front-end_test.go index f1ae5bbfe..2b7c7a17d 100644 --- a/wfe/web-front-end_test.go +++ b/wfe/web-front-end_test.go @@ -7,7 +7,9 @@ package wfe import ( "crypto/x509" + "database/sql" "encoding/json" + "errors" "io" "io/ioutil" "net/http" @@ -29,11 +31,85 @@ type MockSA struct { // empty } -func (sa *MockSA) GetRegistration(int64) (core.Registration, error) { - return core.Registration{}, nil +const ( + test1KeyPublicJSON = ` + { + "kty":"RSA", + "n":"yNWVhtYEKJR21y9xsHV-PD_bYwbXSeNuFal46xYxVfRL5mqha7vttvjB_vc7Xg2RvgCxHPCqoxgMPTzHrZT75LjCwIW2K_klBYN8oYvTwwmeSkAz6ut7ZxPv-nZaT5TJhGk0NT2kh_zSpdriEJ_3vW-mqxYbbBmpvHqsa1_zx9fSuHYctAZJWzxzUZXykbWMWQZpEiE0J4ajj51fInEzVn7VxV-mzfMyboQjujPh7aNJxAWSq4oQEJJDgWwSh9leyoJoPpONHxh5nEE5AjE01FkGICSxjpZsF-w8hOTI3XXohUdu29Se26k2B0PolDSuj0GIQU6-W9TdLXSjBb2SpQ", + "e":"AAEAAQ" + }` + + test1KeyPrivatePEM = ` +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAyNWVhtYEKJR21y9xsHV+PD/bYwbXSeNuFal46xYxVfRL5mqh +a7vttvjB/vc7Xg2RvgCxHPCqoxgMPTzHrZT75LjCwIW2K/klBYN8oYvTwwmeSkAz +6ut7ZxPv+nZaT5TJhGk0NT2kh/zSpdriEJ/3vW+mqxYbbBmpvHqsa1/zx9fSuHYc +tAZJWzxzUZXykbWMWQZpEiE0J4ajj51fInEzVn7VxV+mzfMyboQjujPh7aNJxAWS +q4oQEJJDgWwSh9leyoJoPpONHxh5nEE5AjE01FkGICSxjpZsF+w8hOTI3XXohUdu +29Se26k2B0PolDSuj0GIQU6+W9TdLXSjBb2SpQIDAQABAoIBAHw58SXYV/Yp72Cn +jjFSW+U0sqWMY7rmnP91NsBjl9zNIe3C41pagm39bTIjB2vkBNR8ZRG7pDEB/QAc +Cn9Keo094+lmTArjL407ien7Ld+koW7YS8TyKADYikZo0vAK3qOy14JfQNiFAF9r +Bw61hG5/E58cK5YwQZe+YcyBK6/erM8fLrJEyw4CV49wWdq/QqmNYU1dx4OExAkl +KMfvYXpjzpvyyTnZuS4RONfHsO8+JTyJVm+lUv2x+bTce6R4W++UhQY38HakJ0x3 +XRfXooRv1Bletu5OFlpXfTSGz/5gqsfemLSr5UHncsCcFMgoFBsk2t/5BVukBgC7 +PnHrAjkCgYEA887PRr7zu3OnaXKxylW5U5t4LzdMQLpslVW7cLPD4Y08Rye6fF5s +O/jK1DNFXIoUB7iS30qR7HtaOnveW6H8/kTmMv/YAhLO7PAbRPCKxxcKtniEmP1x +ADH0tF2g5uHB/zeZhCo9qJiF0QaJynvSyvSyJFmY6lLvYZsAW+C+PesCgYEA0uCi +Q8rXLzLpfH2NKlLwlJTi5JjE+xjbabgja0YySwsKzSlmvYJqdnE2Xk+FHj7TCnSK +KUzQKR7+rEk5flwEAf+aCCNh3W4+Hp9MmrdAcCn8ZsKmEW/o7oDzwiAkRCmLw/ck +RSFJZpvFoxEg15riT37EjOJ4LBZ6SwedsoGA/a8CgYEA2Ve4sdGSR73/NOKZGc23 +q4/B4R2DrYRDPhEySnMGoPCeFrSU6z/lbsUIU4jtQWSaHJPu4n2AfncsZUx9WeSb +OzTCnh4zOw33R4N4W8mvfXHODAJ9+kCc1tax1YRN5uTEYzb2dLqPQtfNGxygA1DF +BkaC9CKnTeTnH3TlKgK8tUcCgYB7J1lcgh+9ntwhKinBKAL8ox8HJfkUM+YgDbwR +sEM69E3wl1c7IekPFvsLhSFXEpWpq3nsuMFw4nsVHwaGtzJYAHByhEdpTDLXK21P +heoKF1sioFbgJB1C/Ohe3OqRLDpFzhXOkawOUrbPjvdBM2Erz/r11GUeSlpNazs7 +vsoYXQKBgFwFM1IHmqOf8a2wEFa/a++2y/WT7ZG9nNw1W36S3P04K4lGRNRS2Y/S +snYiqxD9nL7pVqQP2Qbqbn0yD6d3G5/7r86F7Wu2pihM8g6oyMZ3qZvvRIBvKfWo +eROL1ve1vmQF3kjrMPhhK2kr6qdWnTE5XlPllVSZFQenSTzj98AO +-----END RSA PRIVATE KEY----- +` + + test2KeyPublicJSON = ` + { + "kty":"RSA", + "n":"m5Cpx3vZ0CjATirDpbILvq78fm3Dv5RBkO1VLWFmJj5Mb54vc9oYZWc1V1k-LJoESuuPHhaNO2Eu8T9tslQWcZSzr5NImxAwMk970gVQa-Hqv-Jr6xstrBpq7TKpXHTx2FnfA2wQrfIQSlBXu0t4jdUOr3oJh-QXvma8nLITdtjpC0AZNtqd0QkRJX_90SaNrl18Rr_0JrBH9ZmUSFcf3mo_BtL0Gx0jE3n-iwCI8rQtfyVP__9-n__r4IhalKLzaeio6o-qrdemh0EZgjKGCS1_RpTIeArkO8uia1KgOq-z-GfemKEm4s07WO_a0_9dLqbvpnyyZvUi405m3vGDfQ", + "e":"AAEAAQ" + }` +) + +func (sa *MockSA) GetRegistration(id int64) (core.Registration, error) { + if (id == 100) { + // Tag meaning "Missing" + return core.Registration{}, errors.New("missing") + } + if (id == 101) { + // Tag meaning "Malformed" + return core.Registration{}, nil + } + + keyJSON := []byte(test1KeyPublicJSON) + var parsedKey jose.JsonWebKey + parsedKey.UnmarshalJSON(keyJSON) + + return core.Registration{Key: parsedKey}, nil } -func (sa *MockSA) GetRegistrationByKey(jose.JsonWebKey) (core.Registration, error) { +func (sa *MockSA) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration, error) { + var test1KeyPublic jose.JsonWebKey + var test2KeyPublic jose.JsonWebKey + test1KeyPublic.UnmarshalJSON([]byte(test1KeyPublicJSON)) + test2KeyPublic.UnmarshalJSON([]byte(test2KeyPublicJSON)) + + if (core.KeyDigestEquals(jwk, test1KeyPublic)) { + return core.Registration{Key: jwk}, nil + } + + if (core.KeyDigestEquals(jwk, test2KeyPublic)) { + // No key found + return core.Registration{}, sql.ErrNoRows + } + + // Return a fake registration return core.Registration{}, nil } @@ -234,7 +310,7 @@ func TestIssueCertificate(t *testing.T) { test.AssertEquals(t, responseWriter.Body.String(), // TODO: I think this is wrong. The CSR in the payload above was created by openssl and should be valid. - "{\"type\":\"urn:acme:error:malformed\",\"detail\":\"Error creating new cert: Invalid signature on CSR\"}") + "{\"type\":\"urn:acme:error:malformed\",\"detail\":\"Error creating new cert\"}") } type MockRegistrationAuthority struct{} @@ -265,7 +341,8 @@ func (ra *MockRegistrationAuthority) RevokeCertificate(cert x509.Certificate) er return nil } -func (ra *MockRegistrationAuthority) OnValidationUpdate(authz core.Authorization) { +func (ra *MockRegistrationAuthority) OnValidationUpdate(authz core.Authorization) error { + return nil } func TestChallenge(t *testing.T) { @@ -329,7 +406,7 @@ func TestChallenge(t *testing.T) { "{\"type\":\"dns\",\"uri\":\"/acme/authz/asdf?challenge=foo\"}") } -func TestRegistration(t *testing.T) { +func TestNewRegistration(t *testing.T) { wfe := NewWebFrontEndImpl() wfe.RA = &MockRegistrationAuthority{} wfe.SA = &MockSA{} @@ -493,7 +570,7 @@ func TestAuthorization(t *testing.T) { responseWriter.Body.String(), "{\"type\":\"urn:acme:error:malformed\",\"detail\":\"Unable to read/verify body\"}") - + responseWriter.Body.Reset() wfe.NewAuthorization(responseWriter, &http.Request{ Method: "POST", @@ -506,3 +583,96 @@ func TestAuthorization(t *testing.T) { err := json.Unmarshal([]byte(responseWriter.Body.String()), &authz) test.AssertNotError(t, err, "Couldn't unmarshal returned authorization object") } + +func TestRegistration(t *testing.T) { + wfe := NewWebFrontEndImpl() + wfe.RA = &MockRegistrationAuthority{} + wfe.SA = &MockSA{} + wfe.Stats, _ = statsd.NewNoopClient() + responseWriter := httptest.NewRecorder() + + // Test invalid method + path, _ := url.Parse("/1") + wfe.Registration(responseWriter, &http.Request{ + Method: "MAKE-COFFEE", + Body: makeBody("invalid"), + URL: path, + }) + test.AssertEquals(t, + responseWriter.Body.String(), + "{\"type\":\"urn:acme:error:malformed\",\"detail\":\"Method not allowed\"}") + responseWriter.Body.Reset() + + // Test GET missing entry + 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") + wfe.Registration(responseWriter, &http.Request{ + Method: "GET", + URL: path, + }) + test.AssertNotContains(t, responseWriter.Body.String(), "urn:acme:error") + responseWriter.Body.Reset() + + // Test POST invalid JSON + path, _ = url.Parse("/2") + wfe.Registration(responseWriter, &http.Request{ + Method: "POST", + Body: makeBody("invalid"), + URL: path, + }) + test.AssertEquals(t, + responseWriter.Body.String(), + "{\"type\":\"urn:acme:error:malformed\",\"detail\":\"Unable to read/verify body\"}") + responseWriter.Body.Reset() + + // Test POST valid JSON but key is not registered + path, _ = url.Parse("/1") + wfe.Registration(responseWriter, &http.Request{ + Method: "POST", + Body: makeBody(`{ + "payload" : "ewogICJjb250YWN0IjogWwogICAgIm1haWx0bzpjZXJ0LWFkbWluQGV4YW1wbGUuY28ubnoiLAogICAgInRlbDorMjQ5NTU1MTIxMiIKICBdLAogICJhZ3JlZW1lbnQiOiAieWVzIgp9Cg", + "protected" : "eyJhbGciOiJQUzI1NiIsImp3ayI6eyJrdHkiOiJSU0EiLCJuIjoibTVDcHgzdlowQ2pBVGlyRHBiSUx2cTc4Zm0zRHY1UkJrTzFWTFdGbUpqNU1iNTR2YzlvWVpXYzFWMWstTEpvRVN1dVBIaGFOTzJFdThUOXRzbFFXY1pTenI1TklteEF3TWs5NzBnVlFhLUhxdi1KcjZ4c3RyQnBxN1RLcFhIVHgyRm5mQTJ3UXJmSVFTbEJYdTB0NGpkVU9yM29KaC1RWHZtYThuTElUZHRqcEMwQVpOdHFkMFFrUkpYXzkwU2FOcmwxOFJyXzBKckJIOVptVVNGY2YzbW9fQnRMMEd4MGpFM24taXdDSThyUXRmeVZQX185LW5fX3I0SWhhbEtMemFlaW82by1xcmRlbWgwRVpnaktHQ1MxX1JwVEllQXJrTzh1aWExS2dPcS16LUdmZW1LRW00czA3V09fYTBfOWRMcWJ2cG55eVp2VWk0MDVtM3ZHRGZRIiwiZSI6IkFBRUFBUSJ9fQ", + "signature" : "exg0HJRHk-oSDiaOlgtTkT_COqDRyIAJr4g9fDAJh5GF5evXAfT0Hbkfy4TYzqvF6oOldIaCylYhXjYtve4JLXEMdAj1DaR7kGVALskLg-XbiZ0-IaFBiDDaT6mwyLBTfstX4DD2OL7x0vyuTK16bHEIF0hncwHYVSoX5eFOBQLVu_gjxc7J5OZK4ugSJxZEilTVta0A9EdXdUxth0qqbZg_hJDmGOyNge03C71GbhMs-DF-rujlhe7L4VhcV3U0Wj8kSuAGn_DIHBJ1zM0H46PRgyz_9DgkJ6XnE5W8ZA3kF0VPFSp4ofqBhkFUXLXPPJJUEurAQxBJMaU31ef8bg" + }`), + URL: path, + }) + test.AssertEquals(t, + responseWriter.Body.String(), + "{\"type\":\"urn:acme:error:unauthorized\",\"detail\":\"No registration exists matching provided key\"}") + responseWriter.Body.Reset() + + // Test POST valid JSON with registration up in the mock + path, _ = url.Parse("/2") + wfe.Registration(responseWriter, &http.Request{ + Method: "POST", + Body: makeBody(`{ + "payload" : "ewogICJjb250YWN0IjogWwogICAgIm1haWx0bzpjZXJ0LWFkbWluQGV4YW1wbGUuY29tIiwKICAgICJ0ZWw6KzEyMDI1NTUxMjEyIgogIF0sCiAgImFncmVlbWVudCI6ICJ5ZXMiCn0K", + "protected" : "eyJhbGciOiJQUzI1NiIsImp3ayI6eyJrdHkiOiJSU0EiLCJuIjoieU5XVmh0WUVLSlIyMXk5eHNIVi1QRF9iWXdiWFNlTnVGYWw0NnhZeFZmUkw1bXFoYTd2dHR2akJfdmM3WGcyUnZnQ3hIUENxb3hnTVBUekhyWlQ3NUxqQ3dJVzJLX2tsQllOOG9ZdlR3d21lU2tBejZ1dDdaeFB2LW5aYVQ1VEpoR2swTlQya2hfelNwZHJpRUpfM3ZXLW1xeFliYkJtcHZIcXNhMV96eDlmU3VIWWN0QVpKV3p4elVaWHlrYldNV1FacEVpRTBKNGFqajUxZkluRXpWbjdWeFYtbXpmTXlib1FqdWpQaDdhTkp4QVdTcTRvUUVKSkRnV3dTaDlsZXlvSm9QcE9OSHhoNW5FRTVBakUwMUZrR0lDU3hqcFpzRi13OGhPVEkzWFhvaFVkdTI5U2UyNmsyQjBQb2xEU3VqMEdJUVU2LVc5VGRMWFNqQmIyU3BRIiwiZSI6IkFBRUFBUSJ9fQ", + "signature" : "qZ5WWZJxhub1VCNdgAv-y02YLIc9QtHS7lKxVAiRqXPynENsL7_x63whkfvvHHEUiSyyf9pJCLY9NJfiFr-b4QiBOS7QB4JGj8NTghAeFycPerb6e4XCVx9xKljefybAm5yDOUFjG8PYW-XqrxarnVuykRUBIZuBR2d7sxhH09D5uZzJC9I96D7qEliqiglTdzBCAupDY_V7YQc46UzmQ3O_NGWOHr9Z7WYNOZpADwBzfIyWZQlmq3HxS0xYPYbY8FLYI6NzsHTQFkVGCTmZ7KmsyYsYj6uldchn88zcG9KO-53hZh8S5Kdy5FXh8iB_HqUn4j8yKGC9YmK4ERLlGg" + }`), + URL: path, + }) + test.AssertNotContains(t, responseWriter.Body.String(), "urn:acme:error") + responseWriter.Body.Reset() +} \ No newline at end of file From 894703ae67e712032046b80eb2cba3ef6b0a9b65 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Fri, 15 May 2015 17:25:43 -0700 Subject: [PATCH 2/5] Follow-on work for Issue #62 - Documentation correction - Don't lose the problemDoc failback (merge issue, I guess?) - Add the start of an ack script to find methods implementing the audit UUIDs - Documentation fix (RA calls VA, not WFE) - Audit log revocations - Audit log unauthorized domains - Include all SANs in issuance audit log - Add a script to locate all audit markers --- README.md | 2 +- docs/requirements/README.md | 6 ++++ docs/requirements/find_audit_markers | 17 ++++++++++ docs/requirements/print_comment_markers | 5 +++ ra/registration-authority.go | 41 ++++++++++++++++++++----- wfe/web-front-end.go | 6 ++-- 6 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 docs/requirements/README.md create mode 100755 docs/requirements/find_audit_markers diff --git a/README.md b/README.md index e07100ac4..daf48e410 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ WebFE -> WebFE: [ verify authorization signature ] WebFE -> RA: UpdateAuthorization(Authorization) RA -> RA: [ add responses to authorization ] RA -> SA: Update(Authorization.ID, Authorization) -WebFE -> VA: UpdateValidations(Authorization) +RA -> VA: UpdateValidations(Authorization) WebFE -> Client: defer(authorizationID) VA -> SA: Update(Authorization.ID, Authorization) diff --git a/docs/requirements/README.md b/docs/requirements/README.md new file mode 100644 index 000000000..1d734c5f4 --- /dev/null +++ b/docs/requirements/README.md @@ -0,0 +1,6 @@ +This folder tracks software requirements using UUIDs interspersed with the code. + +This is a best-effort mechanism, and while ugly, all other mechanisms for requirements tracability are similarly ugly. + +All UUID sets are stored as CSV files in this directory; standard tools such as `grep` should be used to detect all +implementing code. diff --git a/docs/requirements/find_audit_markers b/docs/requirements/find_audit_markers new file mode 100755 index 000000000..4655588c4 --- /dev/null +++ b/docs/requirements/find_audit_markers @@ -0,0 +1,17 @@ +#!/bin/bash +# +# This method finds all instances of each UUID in the codebase. +# + +audit_dir=$(cd $(dirname ${0}); pwd) +root=$(dirname $(dirname ${audit_dir})) +cat ${audit_dir}/audit_events.csv | tail +2 | while read r; do + uuid=$(echo $r | awk -F "," '{print $1;}') + desc=$(echo $r | awk -F "," '{print $2;}') + echo "===================================" + echo "===================================" + echo ${desc} ${uuid} + echo "" + grep -C 3 -R --include "*.go" "${uuid}" "${root}" +done + diff --git a/docs/requirements/print_comment_markers b/docs/requirements/print_comment_markers index 0bff3b168..c4fdebc3c 100755 --- a/docs/requirements/print_comment_markers +++ b/docs/requirements/print_comment_markers @@ -1,2 +1,7 @@ #!/bin/bash +# +# Provides comment lines for each requirement that can be located with tools + +echo "Insert these comments into the codebase where appropriate, formatted exactly as printed." + cat audit_events.csv | tail +2 | awk -F "," '{print "// AUDIT[", $2, "]", $1;}' diff --git a/ra/registration-authority.go b/ra/registration-authority.go index 49ebc38f5..29cc245ab 100644 --- a/ra/registration-authority.go +++ b/ra/registration-authority.go @@ -56,6 +56,7 @@ type certificateRequestEvent struct { VerificationMethods []string `json:"omitempty"` VerifiedFields []string `json:"omitempty"` CommonName string `json:"omitempty"` + Names []string `json:"omitempty"` NotBefore time.Time `json:"omitempty"` NotAfter time.Time `json:"omitempty"` RequestTime time.Time `json:"omitempty"` @@ -190,14 +191,8 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, if len(csr.Subject.CommonName) > 0 { names = append(names, csr.Subject.CommonName) } - for _, name := range names { - if !authorizedDomains[name] { - err = core.UnauthorizedError(fmt.Sprintf("Key not authorized for name %s", name)) - return emptyCert, err - } - } - // Log the request + // Construct the log event logEvent := certificateRequestEvent{ ID: core.NewToken(), Requester: registration.ID, @@ -205,8 +200,28 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, VerificationMethods: verificationMethods, VerifiedFields: []string{"subject.commonName", "subjectAltName"}, CommonName: names[0], + Names: names, RequestTime: time.Now(), } + + // 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() + jsonLogEvent, logErr := json.Marshal(logEvent) + if logErr != nil { + return emptyCert, logErr + } + + // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c + ra.log.Audit(fmt.Sprintf("Certificate request unauthorized - %s", string(jsonLogEvent))) + return emptyCert, err + } + } + + // Log the request jsonLogEvent, err := json.Marshal(logEvent) if err != nil { return emptyCert, err @@ -272,7 +287,17 @@ func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization } func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) error { - return ra.CA.RevokeCertificate(core.SerialToString(cert.SerialNumber)) + serialString := core.SerialToString(cert.SerialNumber) + err := ra.CA.RevokeCertificate(serialString); + + // AUDIT[ Revocation Requests ] 4e85d791-09c0-4ab3-a837-d3d67e945134 + if err != nil { + ra.log.Audit(fmt.Sprintf("Revocation error - %s - %s", serialString, err)) + } else { + ra.log.Audit(fmt.Sprintf("Revocation - %s", serialString)) + } + + return err } func (ra *RegistrationAuthorityImpl) OnValidationUpdate(authz core.Authorization) error { diff --git a/wfe/web-front-end.go b/wfe/web-front-end.go index 41f523a28..981278ed4 100644 --- a/wfe/web-front-end.go +++ b/wfe/web-front-end.go @@ -211,13 +211,13 @@ func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, details stri case http.StatusInternalServerError: problem.Type = ServerInternalProblem } + problemDoc, err := json.Marshal(problem) if err != nil { - return + problemDoc = []byte("{\"detail\": \"Problem marshalling error message.\"}") } - // Audit log "Receipt of improper messages" - // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 wfe.log.Audit(fmt.Sprintf("Improper HTTP request - %d - %s - %s", code, details, debug)) // Paraphrased from From d2be0dcb95718c0005c3c32b91dc232db6a59615 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Mon, 18 May 2015 18:14:38 -0700 Subject: [PATCH 3/5] Review updates --- docs/requirements/README.md | 2 +- docs/requirements/find_audit_markers | 2 +- docs/requirements/print_comment_markers | 2 +- log/audit-logger.go | 6 +- ra/registration-authority.go | 83 ++++++++++++------------- rpc/amqp-rpc.go | 13 ++-- rpc/rpc-wrappers.go | 4 +- wfe/web-front-end.go | 15 ++++- 8 files changed, 66 insertions(+), 61 deletions(-) diff --git a/docs/requirements/README.md b/docs/requirements/README.md index 1d734c5f4..bf6aad418 100644 --- a/docs/requirements/README.md +++ b/docs/requirements/README.md @@ -1,6 +1,6 @@ This folder tracks software requirements using UUIDs interspersed with the code. -This is a best-effort mechanism, and while ugly, all other mechanisms for requirements tracability are similarly ugly. +This is a best-effort mechanism, and while ugly, all other mechanisms for requirements traceability are similarly ugly. All UUID sets are stored as CSV files in this directory; standard tools such as `grep` should be used to detect all implementing code. diff --git a/docs/requirements/find_audit_markers b/docs/requirements/find_audit_markers index 4655588c4..764371e88 100755 --- a/docs/requirements/find_audit_markers +++ b/docs/requirements/find_audit_markers @@ -5,7 +5,7 @@ audit_dir=$(cd $(dirname ${0}); pwd) root=$(dirname $(dirname ${audit_dir})) -cat ${audit_dir}/audit_events.csv | tail +2 | while read r; do +cat ${audit_dir}/audit_events.csv | tail -n +2 | while read r; do uuid=$(echo $r | awk -F "," '{print $1;}') desc=$(echo $r | awk -F "," '{print $2;}') echo "===================================" diff --git a/docs/requirements/print_comment_markers b/docs/requirements/print_comment_markers index c4fdebc3c..d34c15bb0 100755 --- a/docs/requirements/print_comment_markers +++ b/docs/requirements/print_comment_markers @@ -4,4 +4,4 @@ echo "Insert these comments into the codebase where appropriate, formatted exactly as printed." -cat audit_events.csv | tail +2 | awk -F "," '{print "// AUDIT[", $2, "]", $1;}' +cat audit_events.csv | tail -n +2 | awk -F "," '{print "// AUDIT[", $2, "]", $1;}' diff --git a/log/audit-logger.go b/log/audit-logger.go index 683ef0cd9..6f858a484 100644 --- a/log/audit-logger.go +++ b/log/audit-logger.go @@ -147,7 +147,7 @@ func (log *AuditLogger) auditAtLevel(level, msg string) (err error) { return log.logAtLevel(level, text) } -// AuditPanic catches panics executables. This method should be added +// AuditPanic catches panicking executables. This method should be added // in a defer statement as early as possible // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 func (log *AuditLogger) AuditPanic() { @@ -158,7 +158,7 @@ func (log *AuditLogger) AuditPanic() { // WarningErr formats an error for the Warn level. func (log *AuditLogger) WarningErr(msg error) (err error) { - return log.logAtLevel("Logging.Warning", fmt.Sprintf("%s", msg)) + return log.logAtLevel("Logging.Warning", msg.Error()) } // Alert level messages pass through normally. @@ -211,7 +211,7 @@ func (log *AuditLogger) Audit(msg string) (err error) { // AuditErr can format an error for auditing; it does so at ERR level. // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 func (log *AuditLogger) AuditErr(msg error) (err error) { - return log.auditAtLevel("Logging.Err", fmt.Sprintf("%s", msg)) + return log.auditAtLevel("Logging.Err", msg.Error()) } // SetEmergencyExitFunc changes the systems' behavior on an emergency exit. diff --git a/ra/registration-authority.go b/ra/registration-authority.go index 29cc245ab..f9c74e409 100644 --- a/ra/registration-authority.go +++ b/ra/registration-authority.go @@ -124,29 +124,58 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization 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() { + jsonLogEvent, logErr := json.Marshal(logEvent) + if logErr != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + ra.log.Audit(fmt.Sprintf("Certificate request logEvent could not be serialized. Raw: %+v", logEvent)) + return + } + + // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c + ra.log.Audit(fmt.Sprintf("Certificate request %s - %s", logEventResult, string(jsonLogEvent))) + }() // Verify the CSR // TODO: Verify that other aspects of the CSR are appropriate csr := req.CSR if err = core.VerifyCSR(csr); err != nil { - ra.log.Debug("Invalid signature on CSR:" + err.Error()) + 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 { - // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c - ra.log.Audit(fmt.Sprintf("CSR for names %v was previously revoked/denied", csr.DNSNames)) 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 } @@ -185,6 +214,8 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, for method, _ := range verificationMethodSet { verificationMethods = append(verificationMethods, method) } + logEvent.VerificationMethods = verificationMethods + // Validate that authorization key is authorized for all domains names := csr.DNSNames @@ -192,55 +223,23 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, names = append(names, csr.Subject.CommonName) } - // Construct the log event - logEvent := certificateRequestEvent{ - ID: core.NewToken(), - Requester: registration.ID, - RequestMethod: "online", - VerificationMethods: verificationMethods, - VerifiedFields: []string{"subject.commonName", "subjectAltName"}, - CommonName: names[0], - Names: names, - RequestTime: time.Now(), - } - // 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() - jsonLogEvent, logErr := json.Marshal(logEvent) - if logErr != nil { - return emptyCert, logErr - } - - // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c - ra.log.Audit(fmt.Sprintf("Certificate request unauthorized - %s", string(jsonLogEvent))) return emptyCert, err } } - // Log the request - jsonLogEvent, err := json.Marshal(logEvent) - if err != nil { - return emptyCert, err - } - // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c - ra.log.Audit(fmt.Sprintf("Certificate request - %s", string(jsonLogEvent))) + // 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() - logEvent.ResponseTime = time.Now() - jsonLogEvent, err = json.Marshal(logEvent) - if err != nil { - return emptyCert, err - } - // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c - ra.log.Audit(fmt.Sprintf("Certificate request - error - %s", string(jsonLogEvent))) - return cert, nil + return emptyCert, nil } cert.ParsedCertificate, err = x509.ParseCertificate([]byte(cert.DER)) @@ -250,12 +249,8 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, logEvent.NotBefore = cert.ParsedCertificate.NotBefore logEvent.NotAfter = cert.ParsedCertificate.NotAfter logEvent.ResponseTime = time.Now() - jsonLogEvent, err = json.Marshal(logEvent) - if err != nil { - return emptyCert, err - } - // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c - err = ra.log.Audit(fmt.Sprintf("Certificate request - result - %s", string(jsonLogEvent))) + + logEventResult = "successful" return cert, nil } diff --git a/rpc/amqp-rpc.go b/rpc/amqp-rpc.go index 8d0da90ff..ae6346109 100644 --- a/rpc/amqp-rpc.go +++ b/rpc/amqp-rpc.go @@ -8,7 +8,6 @@ package rpc import ( "errors" "fmt" - "log" "time" "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/streadway/amqp" @@ -195,6 +194,7 @@ type AmqpRPCCLient struct { channel *amqp.Channel pending map[string]chan []byte timeout time.Duration + log *blog.AuditLogger } func NewAmqpRPCCLient(clientQueue, serverQueue string, channel *amqp.Channel) (rpc *AmqpRPCCLient, err error) { @@ -204,6 +204,7 @@ func NewAmqpRPCCLient(clientQueue, serverQueue string, channel *amqp.Channel) (r channel: channel, pending: make(map[string]chan []byte), timeout: 10 * time.Second, + log: blog.GetAuditLogger(), } // Subscribe to the response queue and dispatch @@ -218,7 +219,7 @@ func NewAmqpRPCCLient(clientQueue, serverQueue string, channel *amqp.Channel) (r corrID := msg.CorrelationId responseChan, present := rpc.pending[corrID] - log.Printf(" [c<] received %s(%s) [%s]", msg.Type, core.B64enc(msg.Body), corrID) + rpc.log.Debug(fmt.Sprintf(" [c<] received %s(%s) [%s]", msg.Type, core.B64enc(msg.Body), corrID)) if !present { continue } @@ -243,7 +244,7 @@ func (rpc *AmqpRPCCLient) Dispatch(method string, body []byte) chan []byte { rpc.pending[corrID] = responseChan // Send the request - log.Printf(" [c>] sending %s(%s) [%s]", method, core.B64enc(body), corrID) + rpc.log.Debug(fmt.Sprintf(" [c>] sending %s(%s) [%s]", method, core.B64enc(body), corrID)) rpc.channel.Publish( AmqpExchange, rpc.serverQueue, @@ -264,15 +265,13 @@ func (rpc *AmqpRPCCLient) DispatchSync(method string, body []byte) (response []b case response = <-rpc.Dispatch(method, body): return case <-time.After(rpc.timeout): - log.Printf(" [c!] AMQP-RPC timeout [%s]", method) + rpc.log.Warning(fmt.Sprintf(" [c!] AMQP-RPC timeout [%s]", method)) err = errors.New("AMQP-RPC timeout") return } } func (rpc *AmqpRPCCLient) SyncDispatchWithTimeout(method string, body []byte, ttl time.Duration) (response []byte, err error) { - switch { - - } + err = errors.New("Not Implemented") return } diff --git a/rpc/rpc-wrappers.go b/rpc/rpc-wrappers.go index 3bfc0bc20..ab0eff924 100644 --- a/rpc/rpc-wrappers.go +++ b/rpc/rpc-wrappers.go @@ -374,8 +374,8 @@ func NewValidationAuthorityServer(serverQueue string, channel *amqp.Channel, imp rpc.Handle(MethodUpdateValidations, func(req []byte) []byte { var authz core.Authorization if err := json.Unmarshal(req, &authz); err != nil { - // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 - improperMessage(MethodUpdateValidations, err, req) + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + improperMessage(MethodUpdateValidations, err, req) return nil } diff --git a/wfe/web-front-end.go b/wfe/web-front-end.go index 981278ed4..8c4be3c4d 100644 --- a/wfe/web-front-end.go +++ b/wfe/web-front-end.go @@ -214,11 +214,22 @@ func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, details stri problemDoc, err := json.Marshal(problem) if err != nil { + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + wfe.log.Audit(fmt.Sprintf("Could not marshal error message: %s - %+v", err.Error(), problem)) problemDoc = []byte("{\"detail\": \"Problem marshalling error message.\"}") } - // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 - wfe.log.Audit(fmt.Sprintf("Improper HTTP request - %d - %s - %s", code, details, debug)) + switch(problem.Type) { + case ServerInternalProblem: + // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 + wfe.log.Audit(fmt.Sprintf("Internal error - %s - %s", details, debug)) + case MalformedProblem: + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + wfe.log.Audit(fmt.Sprintf("Improper HTTP request - %s - %s", details, debug)) + case UnauthorizedProblem: + // AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64 + wfe.log.Audit(fmt.Sprintf("Unauthorized HTTP request - %s - %s", details, debug)) + } // Paraphrased from // https://golang.org/src/net/http/server.go#L1272 From 39a61774e78630238984ae2c28ac190567ff2412 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Tue, 12 May 2015 09:01:00 -0700 Subject: [PATCH 4/5] Enforce `go fmt` is clean in the test run. Adds a routine to test.sh to ensure committers have run `go fmt` before submission. --- test.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test.sh b/test.sh index 9c3689b27..18a384d71 100755 --- a/test.sh +++ b/test.sh @@ -63,4 +63,15 @@ else run go test ${dirlist} fi +echo "Checking for unformatted files:" +unformatted=$(find . -name "*.go" -not -path "./Godeps/*" -print | xargs -n1 gofmt -l) +if [ "x${unformatted}" != "x" ] ; then + echo "Unformatted files found; setting failure state." + echo "Please run 'go fmt' on each of these files and amend your commit to continue." + FAILURE=1 + for f in ${unformatted}; do + echo "- ${f}" + done +fi + exit ${FAILURE} From 42302541bdf85dc407d50cf82e01f0bda203b7b2 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Mon, 18 May 2015 18:44:38 -0700 Subject: [PATCH 5/5] Run `go fmt` for PR #186 --- ca/certificate-authority_test.go | 18 +++++----- cmd/shell.go | 2 +- core/objects.go | 60 +++++++++++++++---------------- core/objects_test.go | 2 +- core/util.go | 30 ++++++++-------- core/util_test.go | 12 +++---- log/audit-logger.go | 4 +-- log/audit-logger_test.go | 2 +- mail/mailer.go | 6 ++-- ra/registration-authority.go | 13 ++++--- ra/registration-authority_test.go | 8 ++--- rpc/rpc-wrappers.go | 11 +++--- sa/storage-authority.go | 51 +++++++++++++------------- sa/storage-authority_test.go | 15 ++++---- va/validation-authority.go | 4 +-- va/validation-authority_test.go | 28 +++++++-------- wfe/web-front-end.go | 13 +++---- wfe/web-front-end_test.go | 35 +++++++++--------- 18 files changed, 152 insertions(+), 162 deletions(-) diff --git a/ca/certificate-authority_test.go b/ca/certificate-authority_test.go index ab9479296..2d02df91d 100644 --- a/ca/certificate-authority_test.go +++ b/ca/certificate-authority_test.go @@ -7,16 +7,16 @@ package ca import ( "bytes" - "crypto/x509" "crypto" + "crypto/x509" "encoding/asn1" "encoding/hex" "fmt" "io/ioutil" "net/http" + "os" "testing" "time" - "os" apisign "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/sign" "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/auth" @@ -260,7 +260,6 @@ func TestMain(m *testing.M) { caCertPEM, _ := ioutil.ReadFile(caCertFile) caCert, _ := helpers.ParseCertificatePEM(caCertPEM) - // Create an online CFSSL instance // This is designed to mimic what LE plans to do authHandler, _ := auth.New(authKey, nil) @@ -330,19 +329,18 @@ func setup(t *testing.T) (cadb core.CertificateAuthorityDatabase, storageAuthori ssa.InitTables() storageAuthority = ssa - cadb, _ = NewMockCertificateAuthorityDatabase() // Create a CA // Uncomment to test with a remote signer caConfig = Config{ - Server: hostPort, - AuthKey: authKey, - Profile: profileName, + Server: hostPort, + AuthKey: authKey, + Profile: profileName, SerialPrefix: 17, - IssuerCert: "../test/test-ca.pem", - IssuerKey: "../test/test-ca.key", - TestMode: true, + IssuerCert: "../test/test-ca.pem", + IssuerKey: "../test/test-ca.key", + TestMode: true, } return cadb, storageAuthority, caConfig } diff --git a/cmd/shell.go b/cmd/shell.go index 6e596e0e5..a3fe8aa7e 100644 --- a/cmd/shell.go +++ b/cmd/shell.go @@ -37,9 +37,9 @@ import ( _ "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cmd/cfssl" "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/codegangsta/cli" "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/streadway/amqp" + "github.com/letsencrypt/boulder/ca" blog "github.com/letsencrypt/boulder/log" "github.com/letsencrypt/boulder/rpc" - "github.com/letsencrypt/boulder/ca" ) // Config stores configuration parameters that applications diff --git a/core/objects.go b/core/objects.go index 4519fd8d0..37184f8ac 100644 --- a/core/objects.go +++ b/core/objects.go @@ -6,12 +6,12 @@ package core import ( - "strings" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" + "strings" "time" ) @@ -69,8 +69,8 @@ type CertificateRequest struct { } type rawCertificateRequest struct { - CSR JsonBuffer `json:"csr"` // The encoded CSR - Authorizations []AcmeURL `json:"authorizations"` // Authorizations + CSR JsonBuffer `json:"csr"` // The encoded CSR + Authorizations []AcmeURL `json:"authorizations"` // Authorizations } func (cr *CertificateRequest) UnmarshalJSON(data []byte) error { @@ -288,19 +288,19 @@ type Authorization struct { // Fields of this type get encoded and decoded JOSE-style, in base64url encoding // with stripped padding. -type JsonBuffer []byte; +type JsonBuffer []byte // Url-safe base64 encode that strips padding func base64URLEncode(data []byte) string { - var result = base64.URLEncoding.EncodeToString(data) - return strings.TrimRight(result, "=") + var result = base64.URLEncoding.EncodeToString(data) + return strings.TrimRight(result, "=") } // Url-safe base64 decoder that adds padding func base64URLDecode(data string) ([]byte, error) { - var missing = (4 - len(data)%4) % 4 - data += strings.Repeat("=", missing) - return base64.URLEncoding.DecodeString(data) + var missing = (4 - len(data)%4) % 4 + data += strings.Repeat("=", missing) + return base64.URLEncoding.DecodeString(data) } func (jb JsonBuffer) MarshalJSON() (result []byte, err error) { @@ -330,10 +330,10 @@ type Certificate struct { // * "revoked" - revoked Status AcmeStatus `db:"status"` - Serial string `db:"serial"` - Digest string `db:"digest"` - DER []byte `db:"der"` - Issued time.Time `db:"issued"` + Serial string `db:"serial"` + Digest string `db:"digest"` + DER []byte `db:"der"` + Issued time.Time `db:"issued"` } // CertificateStatus structs are internal to the server. They represent the @@ -346,58 +346,58 @@ type CertificateStatus struct { // that they accept the certificate, otherwise 0. SubscriberApproved bool `db:"subscriberApproved"` - // status: 'good' or 'revoked'. Note that good, expired certificates remain + // status: 'good' or 'revoked'. Note that good, expired certificates remain // with status 'good' but don't necessarily get fresh OCSP responses. Status OCSPStatus `db:"status"` - // ocspLastUpdated: The date and time of the last time we generated an OCSP - // response. If we have never generated one, this has the zero value of + // ocspLastUpdated: The date and time of the last time we generated an OCSP + // response. If we have never generated one, this has the zero value of // time.Time, i.e. Jan 1 1970. OCSPLastUpdated time.Time `db:"ocspLastUpdated"` - // revokedDate: If status is 'revoked', this is the date and time it was + // revokedDate: If status is 'revoked', this is the date and time it was // revoked. Otherwise it has the zero value of time.Time, i.e. Jan 1 1970. - RevokedDate time.Time `db:"revokedDate"` + RevokedDate time.Time `db:"revokedDate"` - // revokedReason: If status is 'revoked', this is the reason code for the - // revocation. Otherwise it is zero (which happens to be the reason + // revokedReason: If status is 'revoked', this is the reason code for the + // revocation. Otherwise it is zero (which happens to be the reason // code for 'unspecified'). - RevokedReason int `db:"revokedReason"` + RevokedReason int `db:"revokedReason"` LockCol int64 `json:"-"` } -// A large table of OCSP responses. This contains all historical OCSP -// responses we've signed, is append-only, and is likely to get quite +// A large table of OCSP responses. This contains all historical OCSP +// responses we've signed, is append-only, and is likely to get quite // large. We'll probably want administratively truncate it at some point. type OcspResponse struct { - ID int `db:"id"` + ID int `db:"id"` // serial: Same as certificate serial. - Serial string `db:"serial"` + Serial string `db:"serial"` // createdAt: The date the response was signed. CreatedAt time.Time `db:"createdAt"` // response: The encoded and signed CRL. - Response []byte `db:"response"` + Response []byte `db:"response"` } -// A large table of signed CRLs. This contains all historical CRLs +// A large table of signed CRLs. This contains all historical CRLs // we've signed, is append-only, and is likely to get quite large. type Crl struct { // serial: Same as certificate serial. - Serial string `db:"serial"` + Serial string `db:"serial"` // createdAt: The date the CRL was signed. CreatedAt time.Time `db:"createdAt"` // crl: The encoded and signed CRL. - Crl string `db:"crl"` + Crl string `db:"crl"` } type DeniedCsr struct { ID int `db:"id"` Names string `db:"names"` -} \ No newline at end of file +} diff --git a/core/objects_test.go b/core/objects_test.go index 9c84b04b4..059449ca0 100644 --- a/core/objects_test.go +++ b/core/objects_test.go @@ -58,4 +58,4 @@ func TestSanityCheck(t *testing.T) { test.Assert(t, !chall.IsSane(true), "IsSane should be false") chall.S = "KQqLsiS5j0CONR_eUXTUSUDNVaHODtc-0pD6ACif7U4" test.Assert(t, chall.IsSane(true), "IsSane should be true") -} \ No newline at end of file +} diff --git a/core/util.go b/core/util.go index 99c8136b9..59cbfec32 100644 --- a/core/util.go +++ b/core/util.go @@ -19,8 +19,8 @@ import ( "encoding/json" "errors" "fmt" - blog "github.com/letsencrypt/boulder/log" jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" + blog "github.com/letsencrypt/boulder/log" "hash" "io" "math/big" @@ -98,19 +98,19 @@ func Fingerprint256(data []byte) string { func KeyDigest(key crypto.PublicKey) (string, error) { switch t := key.(type) { - case *jose.JsonWebKey: - return KeyDigest(t.Key) - case jose.JsonWebKey: - return KeyDigest(t.Key) - default: - keyDER, err := x509.MarshalPKIXPublicKey(key) - if err != nil { - logger := blog.GetAuditLogger() - logger.Debug(fmt.Sprintf("Problem marshaling public key: %s", err)) - return "", err - } - spkiDigest := sha256.Sum256(keyDER) - return base64.StdEncoding.EncodeToString(spkiDigest[0:32]), nil + case *jose.JsonWebKey: + return KeyDigest(t.Key) + case jose.JsonWebKey: + return KeyDigest(t.Key) + default: + keyDER, err := x509.MarshalPKIXPublicKey(key) + if err != nil { + logger := blog.GetAuditLogger() + logger.Debug(fmt.Sprintf("Problem marshaling public key: %s", err)) + return "", err + } + spkiDigest := sha256.Sum256(keyDER) + return base64.StdEncoding.EncodeToString(spkiDigest[0:32]), nil } } @@ -224,7 +224,7 @@ func SerialToString(serial *big.Int) string { return fmt.Sprintf("%032x", serial) } -func StringToSerial(serial string) (*big.Int, error) { +func StringToSerial(serial string) (*big.Int, error) { var serialNum big.Int if len(serial) != 32 { return &serialNum, errors.New("Serial number should be 32 characters long") diff --git a/core/util_test.go b/core/util_test.go index 95470687c..089a7d66d 100644 --- a/core/util_test.go +++ b/core/util_test.go @@ -6,11 +6,11 @@ package core import ( - "testing" "fmt" "github.com/letsencrypt/boulder/test" "math" "math/big" + "testing" ) // challenges.go @@ -18,22 +18,22 @@ func TestNewToken(t *testing.T) { token := NewToken() fmt.Println(token) tokenLength := int(math.Ceil(32 * 8 / 6.0)) // 32 bytes, b64 encoded - test.AssertIntEquals(t,len(token),tokenLength) + test.AssertIntEquals(t, len(token), tokenLength) collider := map[string]bool{} // Test for very blatant RNG failures: // Try 2^20 birthdays in a 2^72 search space... // our naive collision probability here is 2^-32... - for i:=0; i < 1000000; i++ { + for i := 0; i < 1000000; i++ { token = NewToken()[:12] // just sample a portion - test.Assert(t,!collider[token],"Token collision!") + test.Assert(t, !collider[token], "Token collision!") collider[token] = true } return } func TestRandString(t *testing.T) { - // This is covered by NewToken - return + // This is covered by NewToken + return } func TestSerialUtils(t *testing.T) { diff --git a/log/audit-logger.go b/log/audit-logger.go index 6f858a484..13156c2d4 100644 --- a/log/audit-logger.go +++ b/log/audit-logger.go @@ -31,7 +31,7 @@ const auditTag = "[AUDIT]" const emergencyReturnValue = 13 // exitFunction closes the running system -type exitFunction func () +type exitFunction func() // Default to calling os.Exit() func defaultEmergencyExit() { @@ -45,7 +45,7 @@ func defaultEmergencyExit() { // to send a message as an audit event. type AuditLogger struct { *syslog.Writer - Stats statsd.Statter + Stats statsd.Statter exitFunction exitFunction } diff --git a/log/audit-logger_test.go b/log/audit-logger_test.go index 9881c2d0f..055c36d54 100644 --- a/log/audit-logger_test.go +++ b/log/audit-logger_test.go @@ -152,7 +152,7 @@ func TestEmergencyExit(t *testing.T) { called := false - audit.SetEmergencyExitFunc(func(){ called = true }) + audit.SetEmergencyExitFunc(func() { called = true }) audit.EmergencyExit("Emergency!") test.AssertEquals(t, called, true) } diff --git a/mail/mailer.go b/mail/mailer.go index e41ea54e9..b95a0072b 100644 --- a/mail/mailer.go +++ b/mail/mailer.go @@ -20,9 +20,9 @@ func NewMailer(server, port, username, password string) Mailer { auth := smtp.PlainAuth("", username, password, server) return Mailer{ Server: server, - Port: port, - Auth: auth, - From: username, + Port: port, + Auth: auth, + From: username, } } diff --git a/ra/registration-authority.go b/ra/registration-authority.go index f9c74e409..ad5cfdf04 100644 --- a/ra/registration-authority.go +++ b/ra/registration-authority.go @@ -15,8 +15,8 @@ import ( "strconv" "time" - "github.com/letsencrypt/boulder/core" jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" + "github.com/letsencrypt/boulder/core" blog "github.com/letsencrypt/boulder/log" "github.com/letsencrypt/boulder/policy" ) @@ -131,10 +131,10 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, // Construct the log event logEvent := certificateRequestEvent{ - ID: core.NewToken(), - Requester: regID, - RequestMethod: "online", - RequestTime: time.Now(), + ID: core.NewToken(), + Requester: regID, + RequestMethod: "online", + RequestTime: time.Now(), } // No matter what, log the request @@ -216,7 +216,6 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, } logEvent.VerificationMethods = verificationMethods - // Validate that authorization key is authorized for all domains names := csr.DNSNames if len(csr.Subject.CommonName) > 0 { @@ -283,7 +282,7 @@ func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) error { serialString := core.SerialToString(cert.SerialNumber) - err := ra.CA.RevokeCertificate(serialString); + err := ra.CA.RevokeCertificate(serialString) // AUDIT[ Revocation Requests ] 4e85d791-09c0-4ab3-a837-d3d67e945134 if err != nil { diff --git a/ra/registration-authority_test.go b/ra/registration-authority_test.go index 1ad47e060..783dab83f 100644 --- a/ra/registration-authority_test.go +++ b/ra/registration-authority_test.go @@ -18,9 +18,9 @@ import ( "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local" _ "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/mattn/go-sqlite3" + jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" "github.com/letsencrypt/boulder/ca" "github.com/letsencrypt/boulder/core" - jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" "github.com/letsencrypt/boulder/policy" "github.com/letsencrypt/boulder/sa" "github.com/letsencrypt/boulder/test" @@ -239,13 +239,13 @@ func TestCertificateKeyNotEqualAccountKey(t *testing.T) { authz := core.Authorization{} authz.ID, _ = sa.NewPendingAuthorization() authz.Identifier = core.AcmeIdentifier{ - Type: core.IdentifierDNS, + Type: core.IdentifierDNS, Value: "www.example.com", } csr := x509.CertificateRequest{ SignatureAlgorithm: x509.SHA256WithRSA, - PublicKey: AccountKey.Key, - DNSNames: []string{"www.example.com"}, + PublicKey: AccountKey.Key, + DNSNames: []string{"www.example.com"}, } csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csr, AccountPrivateKey.Key) test.AssertNotError(t, err, "Failed to sign CSR") diff --git a/rpc/rpc-wrappers.go b/rpc/rpc-wrappers.go index ab0eff924..ca9e74d0d 100644 --- a/rpc/rpc-wrappers.go +++ b/rpc/rpc-wrappers.go @@ -11,9 +11,9 @@ import ( "errors" "fmt" + jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/streadway/amqp" "github.com/letsencrypt/boulder/core" - jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" blog "github.com/letsencrypt/boulder/log" ) @@ -75,7 +75,7 @@ type authorizationRequest struct { } type certificateRequest struct { - Req core.CertificateRequest + Req core.CertificateRequest RegID int64 } @@ -88,7 +88,6 @@ func errorCondition(method string, err error, obj interface{}) { log.Audit(fmt.Sprintf("Error condition. method: %s err: %s data: %+v", method, err, obj)) } - func NewRegistrationAuthorityServer(serverQueue string, channel *amqp.Channel, impl core.RegistrationAuthority) (*AmqpRPCServer, error) { log := blog.GetAuditLogger() rpc := NewAmqpRPCServer(serverQueue, channel) @@ -381,7 +380,7 @@ func NewValidationAuthorityServer(serverQueue string, channel *amqp.Channel, imp if err := impl.UpdateValidations(authz); err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 - errorCondition(MethodUpdateValidations, err, authz) + errorCondition(MethodUpdateValidations, err, authz) } return nil }) @@ -626,10 +625,10 @@ func NewStorageAuthorityServer(serverQueue string, channel *amqp.Channel, impl c return nil } - if err := impl.UpdatePendingAuthorization(authz); err != nil { + if err := impl.UpdatePendingAuthorization(authz); err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 errorCondition(MethodUpdatePendingAuthorization, err, authz) - } + } return nil }) diff --git a/sa/storage-authority.go b/sa/storage-authority.go index 634622e07..2fa6a3925 100644 --- a/sa/storage-authority.go +++ b/sa/storage-authority.go @@ -18,8 +18,8 @@ import ( gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1" - "github.com/letsencrypt/boulder/core" jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" + "github.com/letsencrypt/boulder/core" blog "github.com/letsencrypt/boulder/log" ) @@ -51,7 +51,7 @@ type pendingauthzModel struct { type authzModel struct { core.Authorization - Sequence int64 `db:"sequence"` + Sequence int64 `db:"sequence"` } // Type converter @@ -293,21 +293,21 @@ func statusIsPending(status core.AcmeStatus) bool { return status == core.StatusPending || status == core.StatusProcessing || status == core.StatusUnknown } -func existingPending(tx *gorp.Transaction, id string) (bool) { +func existingPending(tx *gorp.Transaction, id string) bool { var count int64 - _ = tx.SelectOne(&count, "SELECT count(*) FROM pending_authz WHERE id = :id", map[string]interface{} {"id": id}) + _ = tx.SelectOne(&count, "SELECT count(*) FROM pending_authz WHERE id = :id", map[string]interface{}{"id": id}) return count > 0 } -func existingFinal(tx *gorp.Transaction, id string) (bool) { +func existingFinal(tx *gorp.Transaction, id string) bool { var count int64 - _ = tx.SelectOne(&count, "SELECT count(*) FROM authz WHERE id = :id", map[string]interface{} {"id": id}) + _ = tx.SelectOne(&count, "SELECT count(*) FROM authz WHERE id = :id", map[string]interface{}{"id": id}) return count > 0 } -func existingRegistration(tx *gorp.Transaction, id int64) (bool) { +func existingRegistration(tx *gorp.Transaction, id int64) bool { var count int64 - _ = tx.SelectOne(&count, "SELECT count(*) FROM registrations WHERE id = :id", map[string]interface{} {"id": id}) + _ = tx.SelectOne(&count, "SELECT count(*) FROM registrations WHERE id = :id", map[string]interface{}{"id": id}) return count > 0 } @@ -330,7 +330,7 @@ func (ssa *SQLStorageAuthority) GetRegistrationByKey(key jose.JsonWebKey) (reg c return } - err = ssa.dbMap.SelectOne(®, "SELECT * FROM registrations WHERE key = :key", map[string]interface{} {"key": string(keyJson)}) + err = ssa.dbMap.SelectOne(®, "SELECT * FROM registrations WHERE key = :key", map[string]interface{}{"key": string(keyJson)}) return } @@ -383,7 +383,7 @@ func (ssa *SQLStorageAuthority) GetCertificateByShortSerial(shortSerial string) var certificate core.Certificate err = ssa.dbMap.SelectOne(&certificate, "SELECT * FROM certificates WHERE serial LIKE :shortSerial", - map[string]interface{} {"shortSerial": shortSerial+"%"}) + map[string]interface{}{"shortSerial": shortSerial + "%"}) if err != nil { return } @@ -400,7 +400,7 @@ func (ssa *SQLStorageAuthority) GetCertificate(serial string) (cert []byte, err var certificate core.Certificate err = ssa.dbMap.SelectOne(&certificate, "SELECT * FROM certificates WHERE serial = :serial", - map[string]interface{} {"serial": serial}) + map[string]interface{}{"serial": serial}) if err != nil { return } @@ -644,21 +644,21 @@ func (ssa *SQLStorageAuthority) AddCertificate(certDER []byte, regID int64) (dig cert := &core.Certificate{ RegistrationID: regID, - Serial: serial, - Digest: digest, - DER: certDER, - Issued: time.Now(), + Serial: serial, + Digest: digest, + DER: certDER, + Issued: time.Now(), } certStatus := &core.CertificateStatus{ SubscriberApproved: false, - Status: core.OCSPStatus("good"), - OCSPLastUpdated: time.Time{}, - Serial: serial, - RevokedDate: time.Time{}, - RevokedReason: 0, - LockCol: 0, + Status: core.OCSPStatus("good"), + OCSPLastUpdated: time.Time{}, + Serial: serial, + RevokedDate: time.Time{}, + RevokedReason: 0, + LockCol: 0, } - + tx, err := ssa.dbMap.Begin() if err != nil { return @@ -706,7 +706,7 @@ func (ssa *SQLStorageAuthority) AlreadyDeniedCSR(names []string) (already bool, err = ssa.dbMap.SelectOne( &denied, "SELECT count(*) FROM deniedCsrs WHERE names = :names", - map[string]interface{} {"names": strings.ToLower(strings.Join(names, ","))}, + map[string]interface{}{"names": strings.ToLower(strings.Join(names, ","))}, ) if err != nil { return @@ -714,7 +714,6 @@ func (ssa *SQLStorageAuthority) AlreadyDeniedCSR(names []string) (already bool, if denied > 0 { already = true } - - return -} + return +} diff --git a/sa/storage-authority_test.go b/sa/storage-authority_test.go index 2942b36a7..8d2d33bea 100644 --- a/sa/storage-authority_test.go +++ b/sa/storage-authority_test.go @@ -6,24 +6,24 @@ package sa import ( - "crypto/x509" - "crypto/x509/pkix" "crypto/rand" "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" "encoding/json" "fmt" "net/url" "time" _ "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/mattn/go-sqlite3" - "github.com/letsencrypt/boulder/core" jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" + "github.com/letsencrypt/boulder/core" "github.com/letsencrypt/boulder/test" "io/ioutil" "testing" ) -func initSA(t *testing.T) (*SQLStorageAuthority) { +func initSA(t *testing.T) *SQLStorageAuthority { sa, err := NewSQLStorageAuthority("sqlite3", ":memory:") if err != nil { t.Fatalf("Failed to create SA") @@ -60,7 +60,7 @@ func TestAddRegistration(t *testing.T) { test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID)) expectedReg := core.Registration{ - ID: reg.ID, + ID: reg.ID, Key: jwk, } test.AssertEquals(t, dbReg.ID, expectedReg.ID) @@ -107,8 +107,7 @@ func TestAddAuthorization(t *testing.T) { chall := core.Challenge{Type: "simpleHttps", Status: core.StatusPending, URI: u, Token: "THISWOULDNTBEAGOODTOKEN", Path: "test-me"} combos := make([][]int, 1) - combos[0] = []int{0,1} - + combos[0] = []int{0, 1} newPa := core.Authorization{ID: paID, Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "wut.com"}, RegistrationID: 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) @@ -187,7 +186,7 @@ func TestGetCertificateByShortSerial(t *testing.T) { func TestDeniedCSR(t *testing.T) { key, _ := rsa.GenerateKey(rand.Reader, 512) template := &x509.CertificateRequest{ - Subject: pkix.Name{CommonName: "google.com"}, + Subject: pkix.Name{CommonName: "google.com"}, DNSNames: []string{"badguys.com", "reallybad.com"}, } csrBytes, _ := x509.CreateCertificateRequest(rand.Reader, template, key) diff --git a/va/validation-authority.go b/va/validation-authority.go index 25e10be3a..1bbf1cc55 100644 --- a/va/validation-authority.go +++ b/va/validation-authority.go @@ -32,7 +32,7 @@ func NewValidationAuthorityImpl(tm bool) ValidationAuthorityImpl { // Validation methods -func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdentifier, input core.Challenge) (core.Challenge) { +func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdentifier, input core.Challenge) core.Challenge { challenge := input if len(challenge.Path) == 0 { @@ -105,7 +105,7 @@ func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdenti return challenge } -func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, input core.Challenge) (core.Challenge) { +func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, input core.Challenge) core.Challenge { challenge := input if identifier.Type != "dns" { diff --git a/va/validation-authority_test.go b/va/validation-authority_test.go index ce7969aa1..f8f13a1e7 100644 --- a/va/validation-authority_test.go +++ b/va/validation-authority_test.go @@ -6,24 +6,24 @@ package va import ( - "testing" - "net" - "net/http" - "fmt" - "strings" - "math/big" "crypto/rand" "crypto/rsa" + "crypto/sha256" "crypto/tls" "crypto/x509" "crypto/x509/pkix" - "crypto/sha256" "encoding/base64" + "fmt" + "math/big" + "net" + "net/http" + "strings" + "testing" "time" + jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" "github.com/letsencrypt/boulder/core" "github.com/letsencrypt/boulder/test" - jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" ) func bigIntFromB64(b64 string) *big.Int { @@ -87,10 +87,10 @@ func dvsniSrv(t *testing.T, R, S []byte, waitChan chan bool) { Organization: []string{"tests"}, }, NotBefore: time.Now(), - NotAfter: time.Now().AddDate(0, 0, 1), + NotAfter: time.Now().AddDate(0, 0, 1), - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, DNSNames: []string{zName}, @@ -99,12 +99,12 @@ func dvsniSrv(t *testing.T, R, S []byte, waitChan chan bool) { certBytes, _ := x509.CreateCertificate(rand.Reader, template, template, &TheKey.PublicKey, &TheKey) cert := &tls.Certificate{ Certificate: [][]byte{certBytes}, - PrivateKey: &TheKey, + PrivateKey: &TheKey, } tlsConfig := &tls.Config{ Certificates: []tls.Certificate{*cert}, - ClientAuth: tls.NoClientCert, + ClientAuth: tls.NoClientCert, GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { fmt.Println(clientHello) return cert, nil @@ -160,7 +160,7 @@ func TestSimpleHttps(t *testing.T) { func TestDvsni(t *testing.T) { va := NewValidationAuthorityImpl(true) - a := []byte{1,2,3,4,5,6,7,8,9,0} + a := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} ba := core.B64enc(a) chall := core.Challenge{R: ba, S: ba} diff --git a/wfe/web-front-end.go b/wfe/web-front-end.go index 8c4be3c4d..85c3e5aff 100644 --- a/wfe/web-front-end.go +++ b/wfe/web-front-end.go @@ -6,9 +6,9 @@ package wfe import ( - "database/sql" "bytes" "crypto/x509" + "database/sql" "encoding/json" "errors" "fmt" @@ -67,7 +67,7 @@ func NewWebFrontEndImpl() WebFrontEndImpl { AuthzPath: "/acme/authz/", NewCertPath: "/acme/new-cert", CertPath: "/acme/cert/", - RevokeCertPath: "/acme/revoke-cert/", + RevokeCertPath: "/acme/revoke-cert/", TermsPath: "/terms", IssuerPath: "/acme/issuer-cert", } @@ -130,8 +130,8 @@ func parseIDFromPath(path string) string { type ProblemType string type problem struct { - Type ProblemType `json:"type,omitempty"` - Detail string `json:"detail,omitempty"` + Type ProblemType `json:"type,omitempty"` + Detail string `json:"detail,omitempty"` } const ( @@ -219,7 +219,7 @@ func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, details stri problemDoc = []byte("{\"detail\": \"Problem marshalling error message.\"}") } - switch(problem.Type) { + switch problem.Type { case ServerInternalProblem: // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 wfe.log.Audit(fmt.Sprintf("Internal error - %s - %s", details, debug)) @@ -305,7 +305,6 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque return } - var init core.Authorization if err = json.Unmarshal(body, &init); err != nil { wfe.sendError(response, "Error unmarshaling JSON", err, http.StatusBadRequest) @@ -433,7 +432,6 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request return } - var init core.CertificateRequest if err = json.Unmarshal(body, &init); err != nil { fmt.Println(err) @@ -512,7 +510,6 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re return } - var challengeResponse core.Challenge if err = json.Unmarshal(body, &challengeResponse); err != nil { wfe.sendError(response, "Error unmarshaling authorization", err, http.StatusBadRequest) diff --git a/wfe/web-front-end_test.go b/wfe/web-front-end_test.go index 2b7c7a17d..f3108c823 100644 --- a/wfe/web-front-end_test.go +++ b/wfe/web-front-end_test.go @@ -20,8 +20,8 @@ import ( "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd" - "github.com/letsencrypt/boulder/core" jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" + "github.com/letsencrypt/boulder/core" "github.com/letsencrypt/boulder/ra" "github.com/letsencrypt/boulder/test" @@ -78,11 +78,11 @@ eROL1ve1vmQF3kjrMPhhK2kr6qdWnTE5XlPllVSZFQenSTzj98AO ) func (sa *MockSA) GetRegistration(id int64) (core.Registration, error) { - if (id == 100) { + if id == 100 { // Tag meaning "Missing" return core.Registration{}, errors.New("missing") } - if (id == 101) { + if id == 101 { // Tag meaning "Malformed" return core.Registration{}, nil } @@ -100,11 +100,11 @@ func (sa *MockSA) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration, test1KeyPublic.UnmarshalJSON([]byte(test1KeyPublicJSON)) test2KeyPublic.UnmarshalJSON([]byte(test2KeyPublicJSON)) - if (core.KeyDigestEquals(jwk, test1KeyPublic)) { + if core.KeyDigestEquals(jwk, test1KeyPublic) { return core.Registration{Key: jwk}, nil } - if (core.KeyDigestEquals(jwk, test2KeyPublic)) { + if core.KeyDigestEquals(jwk, test2KeyPublic) { // No key found return core.Registration{}, sql.ErrNoRows } @@ -484,7 +484,7 @@ func TestNewRegistration(t *testing.T) { responseWriter.Body.Reset() wfe.NewRegistration(responseWriter, &http.Request{ Method: "POST", - Body: makeBody(signRequest(t, "{\"contact\":[\"tel:123456789\"]}")), + Body: makeBody(signRequest(t, "{\"contact\":[\"tel:123456789\"]}")), }) test.AssertEquals(t, responseWriter.Body.String(), "{\"key\":{\"kty\":\"RSA\",\"n\":\"z2NsNdHeqAiGdPP8KuxfQXat_uatOK9y12SyGpfKw1sfkizBIsNxERjNDke6Wp9MugN9srN3sr2TDkmQ-gK8lfWo0v1uG_QgzJb1vBdf_hH7aejgETRGLNJZOdaKDsyFnWq1WGJq36zsHcd0qhggTk6zVwqczSxdiWIAZzEakIUZ13KxXvoepYLY0Q-rEEQiuX71e4hvhfeJ4l7m_B-awn22UUVvo3kCqmaRlZT-36vmQhDGoBsoUo1KBEU44jfeK5PbNRk7vDJuH0B7qinr_jczHcvyD-2TtPzKaCioMtNh_VZbPNDaG67sYkQlC15-Ff3HPzKKJW2XvkVG91qMvQ\",\"e\":\"AAEAAQ\"},\"recoveryToken\":\"\",\"contact\":[\"tel:123456789\"],\"thumbprint\":\"\"}") @@ -570,11 +570,10 @@ func TestAuthorization(t *testing.T) { responseWriter.Body.String(), "{\"type\":\"urn:acme:error:malformed\",\"detail\":\"Unable to read/verify body\"}") - responseWriter.Body.Reset() wfe.NewAuthorization(responseWriter, &http.Request{ Method: "POST", - Body: makeBody(signRequest(t, "{\"identifier\":{\"type\":\"dns\",\"value\":\"test.com\"}}")), + Body: makeBody(signRequest(t, "{\"identifier\":{\"type\":\"dns\",\"value\":\"test.com\"}}")), }) test.AssertEquals(t, responseWriter.Body.String(), "{\"identifier\":{\"type\":\"dns\",\"value\":\"test.com\"},\"expires\":\"0001-01-01T00:00:00Z\"}") @@ -595,8 +594,8 @@ func TestRegistration(t *testing.T) { path, _ := url.Parse("/1") wfe.Registration(responseWriter, &http.Request{ Method: "MAKE-COFFEE", - Body: makeBody("invalid"), - URL: path, + Body: makeBody("invalid"), + URL: path, }) test.AssertEquals(t, responseWriter.Body.String(), @@ -607,7 +606,7 @@ func TestRegistration(t *testing.T) { path, _ = url.Parse("/100") wfe.Registration(responseWriter, &http.Request{ Method: "GET", - URL: path, + URL: path, }) test.AssertEquals(t, responseWriter.Body.String(), @@ -618,7 +617,7 @@ func TestRegistration(t *testing.T) { path, _ = url.Parse("/101") wfe.Registration(responseWriter, &http.Request{ Method: "GET", - URL: path, + URL: path, }) test.AssertEquals(t, responseWriter.Body.String(), @@ -629,17 +628,17 @@ func TestRegistration(t *testing.T) { path, _ = url.Parse("/1") wfe.Registration(responseWriter, &http.Request{ Method: "GET", - URL: path, + URL: path, }) - test.AssertNotContains(t, responseWriter.Body.String(), "urn:acme:error") + test.AssertNotContains(t, responseWriter.Body.String(), "urn:acme:error") responseWriter.Body.Reset() // Test POST invalid JSON path, _ = url.Parse("/2") wfe.Registration(responseWriter, &http.Request{ Method: "POST", - Body: makeBody("invalid"), - URL: path, + Body: makeBody("invalid"), + URL: path, }) test.AssertEquals(t, responseWriter.Body.String(), @@ -673,6 +672,6 @@ func TestRegistration(t *testing.T) { }`), URL: path, }) - test.AssertNotContains(t, responseWriter.Body.String(), "urn:acme:error") + test.AssertNotContains(t, responseWriter.Body.String(), "urn:acme:error") responseWriter.Body.Reset() -} \ No newline at end of file +}