diff --git a/internal/shares/model.go b/internal/shares/model.go index 6160bfe..94b70ca 100644 --- a/internal/shares/model.go +++ b/internal/shares/model.go @@ -28,6 +28,7 @@ type SMBConfig struct { Enabled bool `json:"enabled"` Workgroup string `json:"workgroup"` ServerString string `json:"server_string"` + Legacy bool `json:"legacy"` // allow SMB1 + NTLMv1 (IPMI/BMC virtual media, old devices) Shares []SMBShare `json:"shares"` } diff --git a/internal/shares/render.go b/internal/shares/render.go index 06d6aa8..d0a19a8 100644 --- a/internal/shares/render.go +++ b/internal/shares/render.go @@ -37,6 +37,12 @@ func RenderSMB(c Config, owner string) string { if anyGuest(c) { mapGuest = "Bad User" } + minProto := "server min protocol = SMB2" + if c.SMB.Legacy { + // old embedded clients (e.g. Supermicro BMC virtual media) only offer + // SMB1 "NT LM 0.12" and authenticate with NTLMv1 + minProto = "server min protocol = NT1\n ntlm auth = ntlmv1-permitted" + } g := []string{ "workgroup = " + c.SMB.Workgroup, "server string = " + c.SMB.ServerString, @@ -44,7 +50,7 @@ func RenderSMB(c Config, owner string) string { "security = user", "map to guest = " + mapGuest, "guest account = nobody", - "server min protocol = SMB2", + minProto, "unix charset = UTF-8", "logging = file", "log file = /var/log/samba/log.%m", diff --git a/internal/shares/shares_test.go b/internal/shares/shares_test.go index e117666..f9c5ba1 100644 --- a/internal/shares/shares_test.go +++ b/internal/shares/shares_test.go @@ -46,6 +46,13 @@ func TestRenderSMB(t *testing.T) { if out := RenderSMB(c, "ansible"); !strings.Contains(out, "map to guest = Never") || strings.Contains(out, "Bad User") { t.Errorf("user-only config must not map to guest:\n%s", out) } + if out := RenderSMB(c, "ansible"); strings.Contains(out, "NT1") || strings.Contains(out, "ntlmv1") { + t.Errorf("SMB1 enabled without the legacy flag") + } + c.SMB.Legacy = true + if out := RenderSMB(c, "ansible"); !strings.Contains(out, "\n server min protocol = NT1\n ntlm auth = ntlmv1-permitted\n") || strings.Contains(out, "SMB2") { + t.Errorf("legacy mode:\n%s", out) + } } func TestRenderExports(t *testing.T) { diff --git a/internal/web/shares.go b/internal/web/shares.go index fb61b18..b620a91 100644 --- a/internal/web/shares.go +++ b/internal/web/shares.go @@ -204,6 +204,7 @@ func (s *Server) smbSettings(w http.ResponseWriter, r *http.Request) { c.SMB.Enabled = r.FormValue("enabled") != "" c.SMB.Workgroup = strings.ToUpper(strings.TrimSpace(r.FormValue("workgroup"))) c.SMB.ServerString = strings.TrimSpace(r.FormValue("server_string")) + c.SMB.Legacy = r.FormValue("legacy") != "" if err := shares.ValidateComment(c.SMB.ServerString); err != nil { s.sharesFail(w, r, "Not saved: server description: "+err.Error(), smbForm{}, nfsForm{}) return diff --git a/internal/web/shares_test.go b/internal/web/shares_test.go index 0e59c0e..58b4c0c 100644 --- a/internal/web/shares_test.go +++ b/internal/web/shares_test.go @@ -226,3 +226,24 @@ func TestSharesNFSFlow(t *testing.T) { t.Errorf("delete: %d %v", rec.Code, f.calls) } } + +func TestSharesSMBLegacySetting(t *testing.T) { + srv, _, _ := newTestServer(t) + f := &shareRunner{users: "alice:1001:\n"} + withShareRunner(srv, f) + postForm(srv, "/shares/smb/save", url.Values{"name": {"iso"}, "path": {"/tank/data"}, "users": {"alice"}, "browseable": {"1"}}) + rec := postForm(srv, "/shares/smb/settings", url.Values{"enabled": {"1"}, "workgroup": {"workgroup"}, "server_string": {"NAS"}, "legacy": {"1"}}) + if rec.Code != http.StatusSeeOther || !srv.shares.Get().SMB.Legacy { + t.Fatalf("legacy not saved: %d %s", rec.Code, rec.Body.String()) + } + if conf := f.written(shares.DefaultSMBConf); !strings.Contains(conf, "server min protocol = NT1") { + t.Errorf("smb.conf without SMB1:\n%s", conf) + } + if !strings.Contains(get(srv, "/shares").Body.String(), `name="legacy" value="1" checked`) { + t.Errorf("legacy checkbox not shown as checked") + } + postForm(srv, "/shares/smb/settings", url.Values{"enabled": {"1"}, "workgroup": {"WORKGROUP"}, "server_string": {"NAS"}}) + if srv.shares.Get().SMB.Legacy || !strings.Contains(f.written(shares.DefaultSMBConf), "server min protocol = SMB2") { + t.Errorf("legacy not cleared") + } +} diff --git a/internal/web/templates/shares.html b/internal/web/templates/shares.html index 9ef1c3b..d98053d 100644 --- a/internal/web/templates/shares.html +++ b/internal/web/templates/shares.html @@ -18,6 +18,7 @@ +