Repository: hashicorp/vault (Go)

Issue:
Nil-Pointer error on AppRole login
<!-- Please reserve GitHub issues for bug reports and feature requests.

For questions, the best place to get answers is on our [mailing list](https://groups.google.com/forum/#!forum/vault-tool), as they will get more visibility from experienced users than the issue tracker.

Please note: We take Vault's security and our users' trust very seriously. If you believe you have found a security issue in Vault, please responsibly disclose by contacting us at security@hashicorp.com. Our PGP key is available at [our security page](https://www.hashicorp.com/security/).

-->

**Describe the bug**
```
2018-06-07 13:55:02.621399 I | http: panic serving 172.18.0.1:41090: assignment to entry in nil map
goroutine 2077 [running]:
net/http.(*conn).serve.func1(0xc420a5f900)
        /goroot/src/net/http/server.go:1726 +0xd0
panic(0x1f14b80, 0x25414d0)
        /goroot/src/runtime/panic.go:502 +0x229
github.com/hashicorp/vault/builtin/credential/approle.(*backend).pathLoginUpdate(0xc42014ad80, 0x255e720, 0xc4205d72c0, 0xc420852ea0, 0xc42094d880, 0x0, 0x0, 0x0)
        /gopath/src/github.com/hashicorp/vault/builtin/credential/approle/path_login.go:265 +0x6e0
github.com/hashicorp/vault/builtin/credential/approle.(*backend).(github.com/hashicorp/vault/builtin/credential/approle.pathLoginUpdate)-fm(0x255e720, 0xc4205d72c0, 0xc420852ea0, 0xc42094d880, 0xc4206cb8e8, 0x3039302d61396601, 0xc42076f0a8)
        /gopath/src/github.com/hashicorp/vault/builtin/credential/approle/path_login.go:30 +0x52
github.com/hashicorp/vault/logical/framework.(*Backend).HandleRequest(0xc4206cba00, 0x255e720, 0xc4205d72c0, 0xc420852ea0, 0x0, 0x0, 0x0)
        /gopath/src/github.com/hashicorp/vault/logical/framework/backend.go:223 +0x475
github.com/hashicorp/vault/vault.(*Router).routeCommon(0xc4201e6400, 0x255e720, 0xc4205d72c0, 0xc420852ea0, 0x0, 0x0, 0x8000000, 0x0, 0x0)
        /gopath/src/github.com/hashicorp/vault/vault/router.go:516 +0x794
github.com/hashicorp/vault/vault.(*Router).Route(0xc4201e6400, 0x255e720, 0xc4205d72c0, 0xc420852ea0, 0xc4203ed530, 0x0, 0x0)
        /gopath/src/github.com/hashicorp/vault/vault/router.go:376 +0x4e
github.com/hashicorp/vault/vault.(*Core).handleLoginRequest(0xc420090900, 0x255e720, 0xc4205d72c0, 0xc420852ea0, 0x0, 0x0, 0x0, 0x0)
        /gopath/src/github.com/hashicorp/vault/vault/request_handling.go:664 +0x53b
github.com/hashicorp/vault/vault.(*Core).HandleRequest(0xc420090900, 0xc420852ea0, 0x0, 0x0, 0x0)
        /gopath/src/github.com/hashicorp/vault/vault/request_handling.go:273 +0x35a
github.com/hashicorp/vault/http.request(0xc420090900, 0x255cf20, 0xc420a79ce0, 0xc4201a4f00, 0xc420852ea0, 0x0, 0x0)
        /gopath/src/github.com/hashicorp/vault/http/handler.go:408 +0x3c
github.com/hashicorp/vault/http.handleLogical.func1(0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /gopath/src/github.com/hashicorp/vault/http/logical.go:148 +0xfb
net/http.HandlerFunc.ServeHTTP(0xc420024960, 0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /goroot/src/net/http/server.go:1947 +0x44
github.com/hashicorp/vault/http.handleRequestForwarding.func1(0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /gopath/src/github.com/hashicorp/vault/http/handler.go:360 +0x1ca
net/http.HandlerFunc.ServeHTTP(0xc420024980, 0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /goroot/src/net/http/server.go:1947 +0x44
net/http.(*ServeMux).ServeHTTP(0xc4201e9b00, 0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /goroot/src/net/http/server.go:2337 +0x130
github.com/hashicorp/vault/http.wrapHelpHandler.func1(0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /gopath/src/github.com/hashicorp/vault/http/help.go:22 +0x158
net/http.HandlerFunc.ServeHTTP(0xc4200249e0, 0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /goroot/src/net/http/server.go:1947 +0x44
github.com/hashicorp/vault/http.wrapCORSHandler.func1(0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /gopath/src/github.com/hashicorp/vault/http/cors.go:32 +0x10a
net/http.HandlerFunc.ServeHTTP(0xc420024a00, 0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /goroot/src/net/http/server.go:1947 +0x44
github.com/hashicorp/vault/http.wrapGenericHandler.func1(0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /gopath/src/github.com/hashicorp/vault/http/handler.go:128 +0xb1
net/http.HandlerFunc.ServeHTTP(0xc420024a20, 0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /goroot/src/net/http/server.go:1947 +0x44
github.com/hashicorp/vault/vendor/github.com/hashicorp/go-cleanhttp.PrintablePathCheckHandler.func1(0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /gopath/src/github.com/hashicorp/vault/vendor/github.com/hashicorp/go-cleanhttp/handlers.go:40 +0xcf
net/http.HandlerFunc.ServeHTTP(0xc420024a40, 0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /goroot/src/net/http/server.go:1947 +0x44
net/http.serverHandler.ServeHTTP(0xc4200af930, 0x255cf20, 0xc420a79ce0, 0xc4201a4f00)
        /goroot/src/net/http/server.go:2694 +0xbc
net/http.(*conn).serve(0xc420a5f900, 0x255e720, 0xc4205d7140)
        /goroot/src/net/http/server.go:1830 +0x651
created by net/http.(*Server).Serve
        /goroot/src/net/http/server.go:2795 +0x27b
```

**Expected behavior**
Successful login as in `v0.10.1` instead of a crash

**Environment:**
* Vault Server Version (retrieve with `vault status`): `v0.10.2`
* Vault CLI Version (retrieve with `vault version`): `n/a`
* Server Operating System/Architecture: `Linux amd64`
* Client library: `hvac==0.2.17`


Discussion:
Are you binding your role only via CIDR block?
This issue was caused by #4470 where-in a non-reused function was removed. The function that was removed was always allocating the metadata, which was not performed by the calling function after its removal.
@jefferai in that specific case: Yes.
@Luzifer A quick workaround would be to add some metadata (https://www.vaultproject.io/api/auth/approle/index.html#metadata) during secret ID generation.



Patch:
diff --git a/builtin/credential/approle/path_login.go b/builtin/credential/approle/path_login.go
index fce24f540ba..cec371e7a63 100644
--- a/builtin/credential/approle/path_login.go
+++ b/builtin/credential/approle/path_login.go
@@ -83,7 +83,7 @@ func (b *backend) pathLoginUpdate(ctx context.Context, req *logical.Request, dat
 		return logical.ErrorResponse("invalid role ID"), nil
 	}
 
-	var metadata map[string]string
+	metadata := make(map[string]string)
 	if role.BindSecretID {
 		secretID := strings.TrimSpace(data.Get("secret_id").(string))
 		if secretID == "" {
@@ -261,6 +261,12 @@ func (b *backend) pathLoginUpdate(ctx context.Context, req *logical.Request, dat
 		}
 	}
 
+	// For some reason, if metadata was set to nil while processing secret ID
+	// binding, ensure that it is initialized again to avoid a panic.
+	if metadata == nil {
+		metadata = make(map[string]string)
+	}
+
 	// Always include the role name, for later filtering
 	metadata["role_name"] = role.name
 


Test patch:
diff --git a/builtin/credential/approle/path_login_test.go b/builtin/credential/approle/path_login_test.go
index b5f5cbf6c27..ec23be6d1a4 100644
--- a/builtin/credential/approle/path_login_test.go
+++ b/builtin/credential/approle/path_login_test.go
@@ -8,6 +8,58 @@ import (
 	"github.com/hashicorp/vault/logical"
 )
 
+func TestAppRole_BoundCIDRLogin(t *testing.T) {
+	var resp *logical.Response
+	var err error
+	b, s := createBackendWithStorage(t)
+
+	// Create a role with secret ID binding disabled and only bound cidr list
+	// enabled
+	resp, err = b.HandleRequest(context.Background(), &logical.Request{
+		Path:      "role/testrole",
+		Operation: logical.CreateOperation,
+		Data: map[string]interface{}{
+			"bind_secret_id":  false,
+			"bound_cidr_list": []string{"127.0.0.1/8"},
+		},
+		Storage: s,
+	})
+	if err != nil || (resp != nil && resp.IsError()) {
+		t.Fatalf("err:%v resp:%#v", err, resp)
+	}
+
+	// Read the role ID
+	resp, err = b.HandleRequest(context.Background(), &logical.Request{
+		Path:      "role/testrole/role-id",
+		Operation: logical.ReadOperation,
+		Storage:   s,
+	})
+	if err != nil || (resp != nil && resp.IsError()) {
+		t.Fatalf("err:%v resp:%#v", err, resp)
+	}
+
+	roleID := resp.Data["role_id"]
+
+	// Fill in the connection information and login with just the role ID
+	resp, err = b.HandleRequest(context.Background(), &logical.Request{
+		Path:      "login",
+		Operation: logical.UpdateOperation,
+		Data: map[string]interface{}{
+			"role_id": roleID,
+		},
+		Storage:    s,
+		Connection: &logical.Connection{RemoteAddr: "127.0.0.1"},
+	})
+	if err != nil || (resp != nil && resp.IsError()) {
+		t.Fatalf("err:%v resp:%#v", err, resp)
+	}
+
+	// Login should pass
+	if resp.Auth == nil {
+		t.Fatalf("expected login to succeed")
+	}
+}
+
 func TestAppRole_RoleLogin(t *testing.T) {
 	var resp *logical.Response
 	var err error