Skip to content

Commit d45c47b

Browse files
authored
Merge pull request abbot#43 from vania-pooh/master
Fixed concurrent map read and map write in default secret providers (issue crashes web server)
2 parents cb43723 + 511d290 commit d45c47b

File tree

2 files changed

+30
-2
lines changed

2 files changed

+30
-2
lines changed

users.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package auth
22

3-
import "encoding/csv"
4-
import "os"
3+
import (
4+
"encoding/csv"
5+
"os"
6+
"sync"
7+
)
58

69
/*
710
SecretProvider is used by authenticators. Takes user name and realm
@@ -20,13 +23,16 @@ type File struct {
2023
Info os.FileInfo
2124
/* must be set in inherited types during initialization */
2225
Reload func()
26+
mu sync.Mutex
2327
}
2428

2529
func (f *File) ReloadIfNeeded() {
2630
info, err := os.Stat(f.Path)
2731
if err != nil {
2832
panic(err)
2933
}
34+
f.mu.Lock()
35+
defer f.mu.Unlock()
3036
if f.Info == nil || f.Info.ModTime() != info.ModTime() {
3137
f.Info = info
3238
f.Reload()
@@ -40,6 +46,7 @@ func (f *File) ReloadIfNeeded() {
4046
type HtdigestFile struct {
4147
File
4248
Users map[string]map[string]string
49+
mu sync.RWMutex
4350
}
4451

4552
func reload_htdigest(hf *HtdigestFile) {
@@ -57,6 +64,8 @@ func reload_htdigest(hf *HtdigestFile) {
5764
panic(err)
5865
}
5966

67+
hf.mu.Lock()
68+
defer hf.mu.Unlock()
6069
hf.Users = make(map[string]map[string]string)
6170
for _, record := range records {
6271
_, exists := hf.Users[record[1]]
@@ -77,6 +86,8 @@ func HtdigestFileProvider(filename string) SecretProvider {
7786
hf.Reload = func() { reload_htdigest(hf) }
7887
return func(user, realm string) string {
7988
hf.ReloadIfNeeded()
89+
hf.mu.RLock()
90+
defer hf.mu.RUnlock()
8091
_, exists := hf.Users[realm]
8192
if !exists {
8293
return ""
@@ -96,6 +107,7 @@ func HtdigestFileProvider(filename string) SecretProvider {
96107
type HtpasswdFile struct {
97108
File
98109
Users map[string]string
110+
mu sync.RWMutex
99111
}
100112

101113
func reload_htpasswd(h *HtpasswdFile) {
@@ -113,6 +125,8 @@ func reload_htpasswd(h *HtpasswdFile) {
113125
panic(err)
114126
}
115127

128+
h.mu.Lock()
129+
defer h.mu.Unlock()
116130
h.Users = make(map[string]string)
117131
for _, record := range records {
118132
h.Users[record[0]] = record[1]
@@ -129,7 +143,9 @@ func HtpasswdFileProvider(filename string) SecretProvider {
129143
h.Reload = func() { reload_htpasswd(h) }
130144
return func(user, realm string) string {
131145
h.ReloadIfNeeded()
146+
h.mu.RLock()
132147
password, exists := h.Users[user]
148+
h.mu.RUnlock()
133149
if !exists {
134150
return ""
135151
}

users_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package auth
22

33
import (
4+
"os"
45
"testing"
6+
"time"
57
)
68

79
func TestHtdigestFile(t *testing.T) {
@@ -31,3 +33,13 @@ func TestHtpasswdFile(t *testing.T) {
3133
t.Fatal("Got passwd for non-existant user:", passwd)
3234
}
3335
}
36+
37+
// TestConcurrent verifies potential race condition in users reading logic
38+
func TestConcurrent(t *testing.T) {
39+
secrets := HtpasswdFileProvider("test.htpasswd")
40+
os.Chtimes("test.htpasswd", time.Now(), time.Now())
41+
go func() {
42+
secrets("test", "blah")
43+
}()
44+
secrets("test", "blah")
45+
}

0 commit comments

Comments
 (0)