Repository: go
Issue: crypto/x509: certificate validation in Windows fails to validate IP in SAN
Labels: OS-Windows,NeedsInvestigation

<!--
Please answer these questions before submitting your issue. Thanks!
For questions please use one of our forums: https://github.com/golang/go/wiki/Questions
-->

### What version of Go are you using (`go version`)?

<pre>
$ go version
go version go1.13.5 darwin/amd64
</pre>

### Does this issue reproduce with the latest release?

Yes

### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/ganni/Library/Caches/go-build"
GOENV="/Users/ganni/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/ganni/.gvm/pkgsets/go1.13.5/global"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/Users/ganni/.gvm/gos/go1.13.5"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/Users/ganni/.gvm/gos/go1.13.5/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/jk/kkbbm14x25l9hj_xj1rb4scm002zdg/T/go-build540477719=/tmp/go-build -gno-record-gcc-switches -fno-common"
</pre></details>

### What did you do?

I ran the following quick program to reproduce it:

```
package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"
)

func main() {
	client := http.Client{
		Timeout: 5 * time.Second,
	}
	resp, err := client.Get("https://192.168.210.129")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
		return
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
		return
	}

	fmt.Printf("Response:\n\n%s", string(body))
}
```

Where `192.168.210.129` is a custom server using a self-signed certificate. The notable aspects of this cert are that I'm connecting via IP (which is not present in the CN) and it is only present in the `IP Address` field of the SAN, like so:

```
IP Address=192.168.210.129
```

When I compile the binary to run on a Windows box, I run the following:

```
GOOS=windows GOARCH=386 go build -o quicktest.exe main.go
```

### What did you expect to see?

I expect to see a response from the server. This server is healthy and I can access it from my browser, so I expect to see an HTML output from the last line.

### What did you see instead?

I see the following output while running:

```
PS C:\> C:\quicktest.exe
Get https://192.168.210.129: x509: certificate is valid for 192.168.210.129, not 192.168.210.129
```

The interesting part is that it that's the same IP address in both notes.

I then ran a modified version of `go` to add a `panic` near https://sourcegraph.com/github.com/golang/go@release-branch.go1.11/-/blob/src/crypto/x509/root_windows.go#L129, changing:

```
  if status.Error != 0 {
    switch status.Error {
    case syscall.CERT_E_EXPIRED:
      return CertificateInvalidError{c, Expired, ""}
    case syscall.CERT_E_CN_NO_MATCH:
      return HostnameError{c, opts.DNSName}
```

to

```
  if status.Error != 0 {
    switch status.Error {
    case syscall.CERT_E_EXPIRED:
      return CertificateInvalidError{c, Expired, ""}
    case syscall.CERT_E_CN_NO_MATCH:
      panic("failed here")
      // return HostnameError{c, opts.DNSName}
```

and see the following stack trace to verify this is indeed where it fails, which is Windows-specific.

```
Windows PowerShell
Copyright (C) 2016 Microsoft Corporation. All rights reserved.

PS C:\> & 'C:\quicktestcustom.exe'
panic: failed here

goroutine 21 [running]:
crypto/x509.checkChainSSLServerPolicy(0x128cc2c0, 0xb2d7b8, 0x128adb6c, 0xb14850, 0x128adab8)
        /code/github.com/golang/go-darwin-amd64-bootstrap/src/crypto/x509/root_windows.go:130 +0x2c7
crypto/x509.(*Certificate).systemVerify(0x128cc2c0, 0x128adb6c, 0x0, 0x0, 0x0, 0x0, 0x0)
        /code/github.com/golang/go-darwin-amd64-bootstrap/src/crypto/x509/root_windows.go:213 +0x652
crypto/x509.(*Certificate).Verify(0x128cc2c0, 0x1289a280, 0xf, 0x128f65c0, 0x0, 0x526d9c8, 0xbf88e6b7, 0x112bdfd, 0x0, 0
x814560, ...)
        /code/github.com/golang/go-darwin-amd64-bootstrap/src/crypto/x509/verify.go:750 +0x5b6
crypto/tls.(*Conn).verifyServerCertificate(0x128fc000, 0x12892320, 0x1, 0x1, 0x3eb, 0x0)
        /code/github.com/golang/go-darwin-amd64-bootstrap/src/crypto/tls/handshake_client.go:815 +0x1e4
crypto/tls.(*clientHandshakeState).doFullHandshake(0x128adec8, 0x128ba0e0, 0x68)
        /code/github.com/golang/go-darwin-amd64-bootstrap/src/crypto/tls/handshake_client.go:452 +0x136c
crypto/tls.(*clientHandshakeState).handshake(0x128adec8, 0x128a26c0, 0x0)
        /code/github.com/golang/go-darwin-amd64-bootstrap/src/crypto/tls/handshake_client.go:397 +0x343
crypto/tls.(*Conn).clientHandshake(0x128fc000, 0x0, 0x0)
        /code/github.com/golang/go-darwin-amd64-bootstrap/src/crypto/tls/handshake_client.go:206 +0x47a
crypto/tls.(*Conn).Handshake(0x128fc000, 0x0, 0x0)
        /code/github.com/golang/go-darwin-amd64-bootstrap/src/crypto/tls/conn.go:1340 +0xe0
net/http.(*persistConn).addTLS.func2(0x0, 0x128fc000, 0x128f4400, 0x128f43c0)
        /code/github.com/golang/go-darwin-amd64-bootstrap/src/net/http/transport.go:1453 +0x34
created by net/http.(*persistConn).addTLS
        /code/github.com/golang/go-darwin-amd64-bootstrap/src/net/http/transport.go:1449 +0x175
```

I also further validated I do not see such issues on Linux or Mac machines.

For additional debugging help, I should also mention I only have access to one specific Windows machine that I tested with at the time, so I'm not sure if this is apparent on other Windows versions.

My Windows version is: `Windows Server 2012 R2 Standard`