func modelToChallenge(cm *challModel) (core.Challenge, error) { c := core.Challenge{ ID: cm.ID, Type: cm.Type, Status: cm.Status, Token: cm.Token, ProvidedKeyAuthorization: cm.KeyAuthorization, } if len(cm.Error) > 0 { var problem probs.ProblemDetails err := json.Unmarshal(cm.Error, &problem) if err != nil { return core.Challenge{}, err } c.Error = &problem } if len(cm.ValidationRecord) > 0 { var vr []core.ValidationRecord err := json.Unmarshal(cm.ValidationRecord, &vr) if err != nil { return core.Challenge{}, err } c.ValidationRecord = vr } return c, nil }
// setChallengeToken sets the token value both in the Token field and // in the serialized KeyAuthorization object. func setChallengeToken(ch *core.Challenge, token string) { ch.Token = token ka, err := ch.ExpectedKeyAuthorization() if err != nil { panic(err) } ch.ProvidedKeyAuthorization = ka }
// setChallengeToken sets the token value both in the Token field and // in the serialized KeyAuthorization object. func setChallengeToken(ch *core.Challenge, token string) (err error) { ch.Token = token keyAuthorization, err := core.NewKeyAuthorization(token, ch.AccountKey) if err != nil { return } ch.KeyAuthorization = &keyAuthorization return }
// challengeType == "tls-sni-00" or "dns-00", since they're the same func createChallenge(challengeType string) core.Challenge { chall := core.Challenge{ Type: challengeType, Status: core.StatusPending, Token: core.NewToken(), ValidationRecord: []core.ValidationRecord{}, AccountKey: accountKey, } chall.ProvidedKeyAuthorization, _ = chall.ExpectedKeyAuthorization() return chall }
// challengeType == "tls-sni-00" or "dns-00", since they're the same func createChallenge(challengeType string) core.Challenge { chall := core.Challenge{ Type: challengeType, Status: core.StatusPending, Token: core.NewToken(), ValidationRecord: []core.ValidationRecord{}, AccountKey: accountKey, } keyAuthorization, _ := core.NewKeyAuthorization(chall.Token, accountKey) chall.KeyAuthorization = &keyAuthorization return chall }
func modelToChallenge(cm *challModel) (core.Challenge, error) { c := core.Challenge{ ID: cm.ID, Type: cm.Type, Status: cm.Status, Validated: cm.Validated, Token: cm.Token, TLS: cm.TLS, } if len(cm.KeyAuthorization) > 0 { ka, err := core.NewKeyAuthorizationFromString(cm.KeyAuthorization) if err != nil { return core.Challenge{}, err } c.KeyAuthorization = &ka } if len(cm.Error) > 0 { var problem core.ProblemDetails err := json.Unmarshal(cm.Error, &problem) if err != nil { return core.Challenge{}, err } c.Error = &problem } if len(cm.ValidationRecord) > 0 { var vr []core.ValidationRecord err := json.Unmarshal(cm.ValidationRecord, &vr) if err != nil { return core.Challenge{}, err } c.ValidationRecord = vr } if len(cm.AccountKey) > 0 { var ak jose.JsonWebKey err := json.Unmarshal(cm.AccountKey, &ak) if err != nil { return core.Challenge{}, err } c.AccountKey = &ak } return c, nil }
// setChallengeErrorFromDNSError checks the error returned from Lookup... // methods and tests if the error was an underlying net.OpError or an error // caused by resolver returning SERVFAIL or other invalid Rcodes and sets // the challenge.Error field accordingly. func setChallengeErrorFromDNSError(err error, challenge *core.Challenge) { challenge.Error = &core.ProblemDetails{Type: core.ConnectionProblem} if netErr, ok := err.(*net.OpError); ok { if netErr.Timeout() { challenge.Error.Detail = "DNS query timed out" } else if netErr.Temporary() { challenge.Error.Detail = "Temporary network connectivity error" } } else { challenge.Error.Detail = "Server failure at resolver" } }
func TestSimpleHttpRedirectLookup(t *testing.T) { tls := false chall := core.Challenge{ Token: expectedToken, TLS: &tls, ValidationRecord: []core.ValidationRecord{}, AccountKey: accountKey, } hs := simpleSrv(t, expectedToken, tls) defer hs.Close() port, err := getPort(hs) test.AssertNotError(t, err, "failed to get test server port") va := NewValidationAuthorityImpl(&PortConfig{SimpleHTTPPort: port}) va.DNSResolver = &mocks.MockDNS{} log.Clear() chall.Token = pathMoved finChall, err := va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/301" to ".*/valid"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 2) log.Clear() chall.Token = pathFound finChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/302" to ".*/301"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/301" to ".*/valid"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 3) log.Clear() chall.Token = pathRedirectLookupInvalid finChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusInvalid) test.AssertError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`No IPv4 addresses found for invalid.invalid`)), 1) log.Clear() chall.Token = pathRedirectLookup finChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/re-lookup" to ".*other.valid/path"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for other.valid \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) log.Clear() chall.Token = pathRedirectPort finChall, err = va.validateSimpleHTTP(ident, chall) fmt.Println(finChall.ValidationRecord) test.AssertEquals(t, finChall.Status, core.StatusInvalid) test.AssertError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/port-redirect" to ".*other.valid:8080/path"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for other.valid \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) }
func TestDvsni(t *testing.T) { va := NewValidationAuthorityImpl(true) va.DNSResolver = core.NewDNSResolver(time.Second*5, []string{"8.8.8.8:53"}) a := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} ba := core.B64enc(a) chall := core.Challenge{R: ba, S: ba} invalidChall, err := va.validateDvsni(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Server's not up yet; expected refusal. Where did we connect?") test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem) waitChan := make(chan bool, 1) stopChan := make(chan bool, 1) go dvsniSrv(t, a, a, stopChan, waitChan) defer func() { stopChan <- true }() <-waitChan finChall, err := va.validateDvsni(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, "") invalidChall, err = va.validateDvsni(core.AcmeIdentifier{Type: core.IdentifierType("ip"), Value: "127.0.0.1"}, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "IdentifierType IP shouldn't have worked.") test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem) va.TestMode = false invalidChall, err = va.validateDvsni(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Domain name is invalid.") test.AssertEquals(t, invalidChall.Error.Type, core.UnknownHostProblem) va.TestMode = true chall.R = ba[5:] invalidChall, err = va.validateDvsni(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "R Should be illegal Base64") test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem) chall.R = ba chall.S = "!@#" invalidChall, err = va.validateDvsni(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "S Should be illegal Base64") test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem) chall.S = ba chall.Nonce = "wait-long" started := time.Now() invalidChall, err = va.validateDvsni(ident, chall) took := time.Since(started) // Check that the HTTP connection times out after 5 seconds and doesn't block for 10 seconds test.Assert(t, (took > (time.Second * 5)), "HTTP timed out before 5 seconds") test.Assert(t, (took < (time.Second * 10)), "HTTP connection didn't timeout after 5 seconds") test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Connection should've timed out") test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem) }
func modelToChallenge(cm *challModel) (core.Challenge, error) { c := core.Challenge{ Type: cm.Type, Status: cm.Status, Validated: cm.Validated, Token: cm.Token, TLS: cm.TLS, } if len(cm.URI) > 0 { uri, err := core.ParseAcmeURL(cm.URI) if err != nil { return core.Challenge{}, err } c.URI = uri } if len(cm.Validation) > 0 { val, err := jose.ParseSigned(string(cm.Validation)) if err != nil { return core.Challenge{}, err } c.Validation = val } if len(cm.Error) > 0 { var problem core.ProblemDetails err := json.Unmarshal(cm.Error, &problem) if err != nil { return core.Challenge{}, err } c.Error = &problem } if len(cm.ValidationRecord) > 0 { var vr []core.ValidationRecord err := json.Unmarshal(cm.ValidationRecord, &vr) if err != nil { return core.Challenge{}, err } c.ValidationRecord = vr } return c, nil }
func TestSimpleHttps(t *testing.T) { va := NewValidationAuthorityImpl(true) chall := core.Challenge{Path: "test", Token: expectedToken} invalidChall, err := va.validateSimpleHTTPS(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Server's not up yet; expected refusal. Where did we connect?") stopChan := make(chan bool, 1) waitChan := make(chan bool, 1) go simpleSrv(t, expectedToken, stopChan, waitChan) defer func() { stopChan <- true }() <-waitChan finChall, err := va.validateSimpleHTTPS(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, chall.Path) chall.Path = path404 invalidChall, err = va.validateSimpleHTTPS(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Should have found a 404 for the challenge.") chall.Path = pathWrongToken invalidChall, err = va.validateSimpleHTTPS(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "The path should have given us the wrong token.") chall.Path = "" invalidChall, err = va.validateSimpleHTTPS(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Empty paths shouldn't work either.") chall.Path = "validish" invalidChall, err = va.validateSimpleHTTPS(core.AcmeIdentifier{Type: core.IdentifierType("ip"), Value: "127.0.0.1"}, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "IdentifierType IP shouldn't have worked.") }
// challengeType == "tls-sni-00" or "dns-00", since they're the same func createChallenge(challengeType string) core.Challenge { chall := core.Challenge{ Type: challengeType, Status: core.StatusPending, Token: core.NewToken(), ValidationRecord: []core.ValidationRecord{}, AccountKey: accountKey, } keyAuthorization, _ := core.NewKeyAuthorization(chall.Token, accountKey) chall.KeyAuthorization = &keyAuthorization // TODO(https://github.com/letsencrypt/boulder/issues/894): Remove this block validationPayload, _ := json.Marshal(map[string]interface{}{ "type": chall.Type, "token": chall.Token, }) signer, _ := jose.NewSigner(jose.RS256, &TheKey) chall.Validation, _ = signer.Sign(validationPayload, "") return chall }
func TestSimpleHttpRedirectLookup(t *testing.T) { va := NewValidationAuthorityImpl(true) va.DNSResolver = &mocks.MockDNS{} tls := false chall := core.Challenge{Token: expectedToken, TLS: &tls, ValidationRecord: []core.ValidationRecord{}} stopChan := make(chan bool, 1) waitChan := make(chan bool, 1) go simpleSrv(t, expectedToken, stopChan, waitChan, tls) defer func() { stopChan <- true }() <-waitChan log.Clear() chall.Token = pathMoved finChall, err := va.validateSimpleHTTP(ident, chall, AccountKey) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/301" to ".*/valid"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 2) log.Clear() chall.Token = pathFound finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/302" to ".*/301"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/301" to ".*/valid"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 3) log.Clear() chall.Token = pathRedirectLookupInvalid finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey) test.AssertEquals(t, finChall.Status, core.StatusInvalid) test.AssertError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`No IPv4 addresses found for invalid.invalid`)), 1) log.Clear() chall.Token = pathRedirectLookup finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/re-lookup" to ".*other.valid/path"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for other.valid \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) log.Clear() chall.Token = pathRedirectPort finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey) fmt.Println(finChall.ValidationRecord) test.AssertEquals(t, finChall.Status, core.StatusInvalid) test.AssertError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/port-redirect" to ".*other.valid:8080/path"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for other.valid \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) }
func TestDvsni(t *testing.T) { va := NewValidationAuthorityImpl(true) a := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} ba := core.B64enc(a) chall := core.Challenge{R: ba, S: ba} invalidChall, err := va.validateDvsni(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Server's not up yet; expected refusal. Where did we connect?") waitChan := make(chan bool, 1) stopChan := make(chan bool, 1) go dvsniSrv(t, a, a, stopChan, waitChan) defer func() { stopChan <- true }() <-waitChan finChall, err := va.validateDvsni(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, "") chall.R = ba[5:] invalidChall, err = va.validateDvsni(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "R Should be illegal Base64") invalidChall, err = va.validateSimpleHTTPS(core.AcmeIdentifier{Type: core.IdentifierType("ip"), Value: "127.0.0.1"}, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Forgot path; that should be an error.") chall.R = ba chall.S = "!@#" invalidChall, err = va.validateDvsni(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "S Should be illegal Base64") }
// TODO(https://github.com/letsencrypt/boulder/issues/894): Remove this method func TestSimpleHttp(t *testing.T) { tls := false chall := core.Challenge{ Type: core.ChallengeTypeSimpleHTTP, Token: expectedToken, TLS: &tls, ValidationRecord: []core.ValidationRecord{}, AccountKey: accountKey, } // NOTE: We do not attempt to shut down the server. The problem is that the // "wait-long" handler sleeps for ten seconds, but this test finishes in less // than that. So if we try to call hs.Close() at the end of the test, we'll be // closing the test server while a request is still pending. Unfortunately, // there appears to be an issue in httptest that trips Go's race detector when // that happens, failing the test. So instead, we live with leaving the server // around till the process exits. // TODO(#661): add hs.Close back, see ticket for blocker hs := simpleSrv(t, expectedToken, tls) goodPort, err := getPort(hs) test.AssertNotError(t, err, "failed to get test server port") // Attempt to fail a challenge by telling the VA to connect to a port we are // not listening on. badPort := goodPort + 1 if badPort == 65536 { badPort = goodPort - 1 } stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{HTTPPort: badPort}, nil, stats, clock.Default()) va.DNSResolver = &mocks.DNSResolver{} invalidChall, err := va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Server's down; expected refusal. Where did we connect?") test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem) va = NewValidationAuthorityImpl(&PortConfig{HTTPPort: goodPort}, nil, stats, clock.Default()) va.DNSResolver = &mocks.DNSResolver{} log.Clear() finChall, err := va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, "Error validating simpleHttp") test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1) log.Clear() chall.Token = path404 invalidChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Should have found a 404 for the challenge.") test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem) test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1) log.Clear() chall.Token = pathWrongToken // The "wrong token" will actually be the expectedToken. It's wrong // because it doesn't match pathWrongToken. invalidChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Should have found the wrong token value.") test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem) test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1) log.Clear() chall.Token = pathMoved finChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, "Failed to follow 301 redirect") test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/`+pathMoved+`" to ".*/`+pathValid+`"`)), 1) log.Clear() chall.Token = pathFound finChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, "Failed to follow 302 redirect") test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/`+pathFound+`" to ".*/`+pathMoved+`"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/`+pathMoved+`" to ".*/`+pathValid+`"`)), 1) ipIdentifier := core.AcmeIdentifier{Type: core.IdentifierType("ip"), Value: "127.0.0.1"} invalidChall, err = va.validateSimpleHTTP(ipIdentifier, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "IdentifierType IP shouldn't have worked.") test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem) invalidChall, err = va.validateSimpleHTTP(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Domain name is invalid.") test.AssertEquals(t, invalidChall.Error.Type, core.UnknownHostProblem) chall.Token = "wait-long" started := time.Now() invalidChall, err = va.validateSimpleHTTP(ident, chall) took := time.Since(started) // Check that the HTTP connection times out after 5 seconds and doesn't block for 10 seconds test.Assert(t, (took > (time.Second * 5)), "HTTP timed out before 5 seconds") test.Assert(t, (took < (time.Second * 10)), "HTTP connection didn't timeout after 5 seconds") test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Connection should've timed out") test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem) }
// prepChallengeForDisplay takes a core.Challenge and prepares it for display to // the client by filling in its URI field and clearing its AccountKey and ID // fields. // TODO: Come up with a cleaner way to do this. // https://github.com/letsencrypt/boulder/issues/761 func (wfe *WebFrontEndImpl) prepChallengeForDisplay(authz core.Authorization, challenge *core.Challenge) { challenge.URI = fmt.Sprintf("%s%s/%d", wfe.ChallengeBase, authz.ID, challenge.ID) challenge.AccountKey = nil // 0 is considered "empty" for the purpose of the JSON omitempty tag. challenge.ID = 0 }
func TestSimpleHttp(t *testing.T) { va := NewValidationAuthorityImpl(true) va.DNSResolver = &mocks.MockDNS{} tls := false chall := core.Challenge{Type: core.ChallengeTypeSimpleHTTP, Token: expectedToken, TLS: &tls} invalidChall, err := va.validateSimpleHTTP(ident, chall, AccountKey) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Server's not up yet; expected refusal. Where did we connect?") test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem) stopChan := make(chan bool, 1) waitChan := make(chan bool, 1) go simpleSrv(t, expectedToken, stopChan, waitChan, tls) defer func() { stopChan <- true }() <-waitChan log.Clear() finChall, err := va.validateSimpleHTTP(ident, chall, AccountKey) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, "Error validating simpleHttp") test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1) log.Clear() chall.Token = path404 invalidChall, err = va.validateSimpleHTTP(ident, chall, AccountKey) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Should have found a 404 for the challenge.") test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem) test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1) log.Clear() chall.Token = pathWrongToken // The "wrong token" will actually be the expectedToken. It's wrong // because it doesn't match pathWrongToken. invalidChall, err = va.validateSimpleHTTP(ident, chall, AccountKey) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Should have found the wrong token value.") test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem) test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1) log.Clear() chall.Token = pathMoved finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, "Failed to follow 301 redirect") test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/301" to ".*/valid"`)), 1) log.Clear() chall.Token = pathFound finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, "Failed to follow 302 redirect") test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/302" to ".*/301"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/301" to ".*/valid"`)), 1) ipIdentifier := core.AcmeIdentifier{Type: core.IdentifierType("ip"), Value: "127.0.0.1"} invalidChall, err = va.validateSimpleHTTP(ipIdentifier, chall, AccountKey) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "IdentifierType IP shouldn't have worked.") test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem) va.TestMode = false invalidChall, err = va.validateSimpleHTTP(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall, AccountKey) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Domain name is invalid.") test.AssertEquals(t, invalidChall.Error.Type, core.UnknownHostProblem) va.TestMode = true chall.Token = "wait-long" started := time.Now() invalidChall, err = va.validateSimpleHTTP(ident, chall, AccountKey) took := time.Since(started) // Check that the HTTP connection times out after 5 seconds and doesn't block for 10 seconds test.Assert(t, (took > (time.Second * 5)), "HTTP timed out before 5 seconds") test.Assert(t, (took < (time.Second * 10)), "HTTP connection didn't timeout after 5 seconds") test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Connection should've timed out") test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem) }
func httpSrv(t *testing.T, token string) *httptest.Server { m := http.NewServeMux() server := httptest.NewUnstartedServer(m) defaultToken := token currentToken := defaultToken m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if !strings.HasPrefix(r.Host, "localhost:") && !strings.HasPrefix(r.Host, "other.valid:") { t.Errorf("Bad Host header: " + r.Host) } if strings.HasSuffix(r.URL.Path, path404) { t.Logf("HTTPSRV: Got a 404 req\n") http.NotFound(w, r) } else if strings.HasSuffix(r.URL.Path, path500) { t.Logf("HTTPSRV: Got a 500 req\n") http.Error(w, "Internal Server Error", http.StatusInternalServerError) } else if strings.HasSuffix(r.URL.Path, pathMoved) { t.Logf("HTTPSRV: Got a 301 redirect req\n") if currentToken == defaultToken { currentToken = pathMoved } http.Redirect(w, r, pathValid, 301) } else if strings.HasSuffix(r.URL.Path, pathFound) { t.Logf("HTTPSRV: Got a 302 redirect req\n") if currentToken == defaultToken { currentToken = pathFound } http.Redirect(w, r, pathMoved, 302) } else if strings.HasSuffix(r.URL.Path, pathWait) { t.Logf("HTTPSRV: Got a wait req\n") time.Sleep(time.Second * 3) } else if strings.HasSuffix(r.URL.Path, pathWaitLong) { t.Logf("HTTPSRV: Got a wait-long req\n") time.Sleep(time.Second * 10) } else if strings.HasSuffix(r.URL.Path, pathReLookup) { t.Logf("HTTPSRV: Got a redirect req to a valid hostname\n") if currentToken == defaultToken { currentToken = pathReLookup } port, err := getPort(server) test.AssertNotError(t, err, "failed to get server test port") http.Redirect(w, r, fmt.Sprintf("http://other.valid:%d/path", port), 302) } else if strings.HasSuffix(r.URL.Path, pathReLookupInvalid) { t.Logf("HTTPSRV: Got a redirect req to an invalid hostname\n") http.Redirect(w, r, "http://invalid.invalid/path", 302) } else if strings.HasSuffix(r.URL.Path, pathRedirectToFailingURL) { t.Logf("HTTPSRV: Redirecting to a URL that will fail\n") http.Redirect(w, r, fmt.Sprintf("http://other.valid/%s", path500), 301) } else if strings.HasSuffix(r.URL.Path, pathLooper) { t.Logf("HTTPSRV: Got a loop req\n") http.Redirect(w, r, r.URL.String(), 301) } else if strings.HasSuffix(r.URL.Path, pathRedirectPort) { t.Logf("HTTPSRV: Got a port redirect req\n") http.Redirect(w, r, "http://other.valid:8080/path", 302) } else if r.Header.Get("User-Agent") == rejectUserAgent { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("found trap User-Agent")) } else { t.Logf("HTTPSRV: Got a valid req\n") t.Logf("HTTPSRV: Path = %s\n", r.URL.Path) ch := core.Challenge{Token: currentToken} keyAuthz, _ := ch.ExpectedKeyAuthorization(accountKey) t.Logf("HTTPSRV: Key Authz = '%s%s'\n", keyAuthz, "\\n\\r \\t") fmt.Fprint(w, keyAuthz, "\n\r \t") currentToken = defaultToken } }) server.Start() return server }
func TestSimpleHttp(t *testing.T) { va := NewValidationAuthorityImpl(true) va.DNSResolver = core.NewDNSResolver(time.Second*5, []string{"8.8.8.8:53"}) chall := core.Challenge{Path: "test", Token: expectedToken} invalidChall, err := va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Server's not up yet; expected refusal. Where did we connect?") test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem) stopChan := make(chan bool, 1) waitChan := make(chan bool, 1) go simpleSrv(t, expectedToken, stopChan, waitChan) defer func() { stopChan <- true }() <-waitChan finChall, err := va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, chall.Path) tls := false chall.TLS = &tls finChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, finChall.Status, core.StatusValid) test.AssertNotError(t, err, chall.Path) tls = true chall.TLS = &tls chall.Path = path404 invalidChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Should have found a 404 for the challenge.") test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem) chall.Path = pathWrongToken invalidChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "The path should have given us the wrong token.") test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem) chall.Path = "" invalidChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Empty paths shouldn't work either.") test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem) chall.Path = "validish" invalidChall, err = va.validateSimpleHTTP(core.AcmeIdentifier{Type: core.IdentifierType("ip"), Value: "127.0.0.1"}, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "IdentifierType IP shouldn't have worked.") test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem) va.TestMode = false chall.Path = "alsoValidish" invalidChall, err = va.validateSimpleHTTP(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Domain name is invalid.") test.AssertEquals(t, invalidChall.Error.Type, core.UnknownHostProblem) va.TestMode = true chall.Path = "%" invalidChall, err = va.validateSimpleHTTP(ident, chall) test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Path doesn't consist of URL-safe characters.") test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem) chall.Path = "wait-long" started := time.Now() invalidChall, err = va.validateSimpleHTTP(ident, chall) took := time.Since(started) // Check that the HTTP connection times out after 5 seconds and doesn't block for 10 seconds test.Assert(t, (took > (time.Second * 5)), "HTTP timed out before 5 seconds") test.Assert(t, (took < (time.Second * 10)), "HTTP connection didn't timeout after 5 seconds") test.AssertEquals(t, invalidChall.Status, core.StatusInvalid) test.AssertError(t, err, "Connection should've timed out") test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem) }
// setChallengeToken sets the token value, and sets the ProvidedKeyAuthorization // to match. func setChallengeToken(ch *core.Challenge, token string) { ch.Token = token ch.ProvidedKeyAuthorization = token + ".9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI" }
// prepChallengeForDisplay takes a core.Challenge and prepares it for display to // the client by filling in its URI field and clearing its ID field. // TODO: Come up with a cleaner way to do this. // https://github.com/letsencrypt/boulder/issues/761 func (wfe *WebFrontEndImpl) prepChallengeForDisplay(request *http.Request, authz core.Authorization, challenge *core.Challenge) { challenge.URI = wfe.relativeEndpoint(request, fmt.Sprintf("%s%s/%d", challengePath, authz.ID, challenge.ID)) // 0 is considered "empty" for the purpose of the JSON omitempty tag. challenge.ID = 0 }