diff --git a/glog_file.go b/glog_file.go index 9a56a69b..b54bd405 100644 --- a/glog_file.go +++ b/glog_file.go @@ -143,7 +143,12 @@ func create(tag string, t time.Time, dir string) (f *os.File, filename string, e func createInDir(dir, tag string, t time.Time) (f *os.File, name string, err error) { name, link := logName(tag, t) fname := filepath.Join(dir, name) - f, err = os.Create(fname) + // O_EXCL is important here, as it prevents a vulnerability. The general idea is that logs often + // live in an insecure directory (like /tmp), so an unprivileged attacker could create fname in + // advance as a symlink to a file the logging process can access, but the attacker cannot. O_EXCL + // fails the open if it already exists, thus prevent our this code from opening the existing file + // the attacker points us to. + f, err = os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) if err == nil { symlink := filepath.Join(dir, link) os.Remove(symlink) // ignore err diff --git a/glog_test.go b/glog_test.go index c1bf553d..8fd15cdf 100644 --- a/glog_test.go +++ b/glog_test.go @@ -772,3 +772,14 @@ func TestLogLength(t *testing.T) { len(c), logsink.MaxLogMessageLen, c) } } + +func TestCreateFailsIfExists(t *testing.T) { + tmp := t.TempDir() + now := time.Now() + if _, _, err := create("INFO", now, tmp); err != nil { + t.Errorf("create() failed on first call: %v", err) + } + if _, _, err := create("INFO", now, tmp); err == nil { + t.Errorf("create() succeeded on second call, want error") + } +}