From a0a338067bbf27c307984fc552e3da08c3aaf708 Mon Sep 17 00:00:00 2001 From: Artur Gurgul Date: Thu, 24 Apr 2025 20:53:37 +0200 Subject: [PATCH] init --- Makefile | 8 ++ go.mod | 7 ++ go.sum | 2 + misc/default | 20 ++++ misc/webdav.service | 15 +++ question.md | 250 +++++++++++++++++++++++++++++++++++++++++++ readme.md | 21 ++++ src/file-manager.go | 93 ++++++++++++++++ src/fs.go | 133 +++++++++++++++++++++++ src/main.go | 159 +++++++++++++++++++++++++++ src/pam.c | 44 ++++++++ src/pam.go | 32 ++++++ src/pam_darwin.go | 6 ++ src/shadow.go | 53 +++++++++ src/unix.go | 30 ++++++ templates/index.html | 25 +++++ 16 files changed, 898 insertions(+) create mode 100644 Makefile create mode 100644 go.mod create mode 100644 go.sum create mode 100644 misc/default create mode 100644 misc/webdav.service create mode 100644 question.md create mode 100644 readme.md create mode 100644 src/file-manager.go create mode 100644 src/fs.go create mode 100755 src/main.go create mode 100755 src/pam.c create mode 100755 src/pam.go create mode 100644 src/pam_darwin.go create mode 100644 src/shadow.go create mode 100644 src/unix.go create mode 100644 templates/index.html diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9c0b936 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +# /usr/share/webdav/templates +all: + go build -o webdav ./src + mkdir -p /srv/bin + mv webdav /srv/bin/ + cp webdav.service /etc/systemd/system/ + systemctl enable webdav + systemctl start webdav \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6ac764e --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module webdav + +go 1.23.0 + +toolchain go1.23.1 + +require golang.org/x/net v0.37.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c56938b --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= diff --git a/misc/default b/misc/default new file mode 100644 index 0000000..bcda742 --- /dev/null +++ b/misc/default @@ -0,0 +1,20 @@ +server { + listen 80 default_server; + listen [::]:80 default_server; + + root /var/www/html; + + server_name _; + + location / { + proxy_pass http://unix:/run/webdav.sock:/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass_request_headers on; + client_max_body_size 2G; + } +} + diff --git a/misc/webdav.service b/misc/webdav.service new file mode 100644 index 0000000..e727353 --- /dev/null +++ b/misc/webdav.service @@ -0,0 +1,15 @@ +[Unit] +Description = WebDAV Server +After=network.target + +[Service] +User = root +Group = root +Type = simple +ExecStart = /srv/bin/webdav +#Restart = on-abort +Restart=on-failure +RestartSec = 5 + +[Install] +WantedBy = multi-user.target \ No newline at end of file diff --git a/question.md b/question.md new file mode 100644 index 0000000..8c614d9 --- /dev/null +++ b/question.md @@ -0,0 +1,250 @@ + +I’m having a problem with the WebDAV implementation I demonstrated below. The server works, but when it creates files, the content is empty. + +Files: + +```go: main.go + +package main + +import "C" + +import ( + "fmt" + "log" + "net/http" + "os" + "os/user" + + "golang.org/x/net/webdav" +) + +// WebDAV handler with authentication +func webdavHandler(w http.ResponseWriter, r *http.Request) { + username, password, ok := r.BasicAuth() + if !ok { + w.Header().Set("WWW-Authenticate", `Basic realm="WebDAV"`) + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + if !PAMAuthenticate(username, password) { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + + // Get the user's home directory + userInfo, err := user.Lookup(username) + if err != nil { + http.Error(w, "User not found", http.StatusNotFound) + return + } + + // webdavPath := userInfo.HomeDir + "/website" + webdavPath := userInfo.HomeDir + + // Check if the directory exists + if _, err := os.Stat(webdavPath); os.IsNotExist(err) { + http.Error(w, "WebDAV directory not found", http.StatusNotFound) + return + } + /* + handler := &webdav.Handler{ + Prefix: "/", + FileSystem: webdav.Dir(webdavPath), + LockSystem: webdav.NewMemLS(), + } + + fmt.Println("new connection") + */ + + uid, gid, err := GetUserData(username) + if err != nil { + http.Error(w, "WebDAV directory not found", http.StatusForbidden) + return + } + + handler := &webdav.Handler{ + Prefix: "/", + FileSystem: WebDavDir{Dir: webdav.Dir(webdavPath), UID: uid, GID: gid}, + LockSystem: webdav.NewMemLS(), + } + + fmt.Printf("New connection: %s\n", username) + + handler.ServeHTTP(w, r) +} + +func main() { + fmt.Println("WebDAV server running on :8800") + http.HandleFunc("/", webdavHandler) + // This binds to 127.0.0.1 + log.Fatal(http.ListenAndServe(":8800", nil)) + //log.Fatal(http.ListenAndServe("0.0.0.0:8800", nil)) +} + +``` + +```go: fs.go + +package main + +import ( + "context" + "log" + "os" + "path/filepath" + "os/user" + "strconv" + + "golang.org/x/net/webdav" +) + +type WebDavFile struct { + webdav.File + + FullPath string +} + +type WebDavDir struct { + webdav.Dir + + UID int + GID int +} + +func GetUserData(username string) (int, int, error) { + userInfo, err := user.Lookup(username) + if err != nil { + log.Fatalf("User not found: %v", err) + } + + uid, err := strconv.Atoi(userInfo.Uid) + if err != nil { + return 0, 0, err + } + + gid, err := strconv.Atoi(userInfo.Gid) + if err != nil { + return 0, 0, err + } + return uid, gid, nil +} + +func (fs WebDavDir) Mkdir(ctx context.Context, name string, perm os.FileMode) error { + err := fs.Dir.Mkdir(ctx, name, perm) + if err != nil { + return err + } + fullPath := filepath.Join(string(fs.Dir), name) + if chownErr := os.Chown(fullPath, fs.UID, fs.GID); chownErr != nil { + return chownErr + log.Printf("Mkdir chown error: %v", chownErr) + } + return nil +} + +func (fs WebDavDir) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) { + fullPath := filepath.Join(string(fs.Dir), name) + + log.Printf("OpenFile: %s, flag=%d, perm=%o", fullPath, flag, perm) + + f, err := fs.Dir.OpenFile(ctx, name, flag, perm) + if err != nil { + log.Printf("File opening error: %v", err) + return nil, err + } + + log.Printf("open file for: uid=%d, gid=%o", fs.UID, fs.GID) + + if chownErr := os.Chown(fullPath, fs.UID, fs.GID); chownErr != nil { + log.Printf("File chown error: %v", chownErr) + } + + return WebDavFile{ + File: f, + FullPath: fullPath, + }, nil +} + +func (f WebDavFile) Write(p []byte) (int, error) { + log.Printf("Writing %d bytes to %s", len(p), f.FullPath) + return f.File.Write(p) +} +``` + +```c: pam.c +#define _POSIX_C_SOURCE 200809L // Ensure strdup() is available + +#include +#include +#include + +// PAM Conversation Function +int pam_conv_func(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) { + struct pam_response *responses = (struct pam_response *)malloc(num_msg * sizeof(struct pam_response)); + if (!responses) return PAM_CONV_ERR; + + char *password = (char *)appdata_ptr; + + for (int i = 0; i < num_msg; i++) { + responses[i].resp_retcode = 0; + if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) { + responses[i].resp = strdup(password); + } else { + responses[i].resp = NULL; + } + } + + *resp = responses; + return PAM_SUCCESS; +} + +// Function to authenticate user with PAM +int authenticate_pam(const char *username, const char *password) { + struct pam_conv conv = { pam_conv_func, (void *)password }; + pam_handle_t *pamh = NULL; + + int retval = pam_start("login", username, &conv, &pamh); + if (retval != PAM_SUCCESS) return retval; + + retval = pam_authenticate(pamh, 0); + pam_end(pamh, retval); + + return retval; +} +``` +```go: pam.go +package main + +/* +#cgo CFLAGS: -std=c11 +#cgo LDFLAGS: -lpam +#include +#include + +extern int authenticate_pam(const char *username, const char *password); + +// Define PAM_SUCCESS explicitly for cgo +static const int CGO_PAM_SUCCESS = PAM_SUCCESS; +*/ +import "C" + +import ( + "unsafe" +) + +// Authenticate using PAM (directly linked C function) +func PAMAuthenticate(username, password string) bool { + cUsername := C.CString(username) + cPassword := C.CString(password) + defer C.free(unsafe.Pointer(cUsername)) + defer C.free(unsafe.Pointer(cPassword)) + + result := C.authenticate_pam(cUsername, cPassword) + return result == C.PAM_SUCCESS + //return result == C.CGO_PAM_SUCCESS +} +``` + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..d3650dd --- /dev/null +++ b/readme.md @@ -0,0 +1,21 @@ + + +```bash +useradd -mg users -s /usr/bin/zsh test +passwd test + +# make it accessible for to the Nginx +chmod 755 /home/test +chmod 755 /home/test/website +``` + +curl -u youruser:yourpass -T test.txt http://localhost:8800/test.txt + + +``` +GOTOOLCHAIN=auto go mod vendor +GOTOOLCHAIN=auto go run . +``` + + +apt install libpam0g-dev diff --git a/src/file-manager.go b/src/file-manager.go new file mode 100644 index 0000000..5dd6ce4 --- /dev/null +++ b/src/file-manager.go @@ -0,0 +1,93 @@ +package main + +import ( + "errors" + "fmt" + "io" + "net/http" + "html/template" + "os" + "encoding/json" +) +var templates = template.Must(template.ParseGlob("templates/*.html")) + +// curl -H "Accept: application/json" http://localhost:9988/ +func getRoot(w http.ResponseWriter, r *http.Request) { + fmt.Printf("got / request\n") + //io.WriteString(w, "This is my website!\n") + + c := ListFiles("") + fmt.Println(c) + + context := struct { + IsLoggedIn bool ; Files []struct {FileName string}}{ + IsLoggedIn: false, + Files: c, + } + + // Check if request wants JSON + if r.Header.Get("Accept") == "application/json" { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(context) + return + } + + + //tmpl, _ := templates.ParseFiles("templates/venue.html") + + templates.ExecuteTemplate(w, "index.html", context) +} + +func ListFiles(dir string) []struct {FileName string} { + context := []struct {FileName string} {} + + entries, err := os.ReadDir(".") + if err != nil { + fmt.Println("Error:", err) + return context + } + + for _, entry := range entries { + info, err := entry.Info() + if err != nil { + fmt.Println("Error getting info:", err) + continue + } + + context = append(context, struct {FileName string}{ + FileName: info.Name(), + }) + + //fmt.Printf("Name: %s, Size: %d bytes, IsDir: %t\n", info.Name(), info.Size(), info.IsDir()) + } + + return context +} + +func getHello(w http.ResponseWriter, r *http.Request) { + fmt.Printf("got /hello request\n") + io.WriteString(w, "Hello, HTTP!\n") +} + +func GetMenusHandler(w http.ResponseWriter, r *http.Request) { + //fmt.Fprintf(w, "Hello, World!") + templates.ExecuteTemplate(w, "index.html", nil) +} + +func HTTPmain() { + + + + http.HandleFunc("/", getRoot) + http.HandleFunc("/hello", getHello) + + err := http.ListenAndServe(":3333", nil) + + if errors.Is(err, http.ErrServerClosed) { + fmt.Printf("server closed\n") + } else if err != nil { + fmt.Printf("error starting server: %s\n", err) + os.Exit(1) + } +} + diff --git a/src/fs.go b/src/fs.go new file mode 100644 index 0000000..2e96682 --- /dev/null +++ b/src/fs.go @@ -0,0 +1,133 @@ +package main + + +import ( + "context" + "log" + "os" + "path/filepath" + "os/user" + "strconv" + + "golang.org/x/net/webdav" +) + +var globalLockSystem = webdav.NewMemLS() + +type WebDavFile struct { + //webdav.File + + FullPath string + File *os.File +} + +type WebDavDir struct { + webdav.Dir + + UID int + GID int +} + + +func GetUserData(username string) (int, int, error) { + userInfo, err := user.Lookup(username) + if err != nil { + log.Fatalf("User not found: %v", err) + } + + uid, err := strconv.Atoi(userInfo.Uid) + if err != nil { + return 0, 0, err + } + + gid, err := strconv.Atoi(userInfo.Gid) + if err != nil { + return 0, 0, err + } + return uid, gid, nil +} + +func (fs WebDavDir) Mkdir(ctx context.Context, name string, perm os.FileMode) error { + err := fs.Dir.Mkdir(ctx, name, perm) + if err != nil { + return err + } + fullPath := filepath.Join(string(fs.Dir), name) + if chownErr := os.Chown(fullPath, fs.UID, fs.GID); chownErr != nil { + log.Printf("Mkdir chown error: %v", chownErr) + return chownErr + } + return nil +} + +func (fs WebDavDir) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) { + fullPath := filepath.Join(string(fs.Dir), name) + log.Printf("OpenFile: %s, flag=%d, perm=%o", fullPath, flag, perm) + + //f, err := os.OpenFile(fullPath, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0666) + f, err := os.OpenFile(fullPath, flag, perm) + if err != nil { + return nil, err + } + + if chownErr := os.Chown(fullPath, fs.UID, fs.GID); chownErr != nil { + log.Printf("OpenFile chown error: %v", chownErr) + } + + return &WebDavFile{ + File: f, + FullPath: fullPath, + }, nil +} + +func (f *WebDavFile) Read(p []byte) (int, error) { return f.File.Read(p) } +func (f *WebDavFile) Write(p []byte) (int, error) { + log.Printf("Writing %d bytes to %s", len(p), f.FullPath) + return f.File.Write(p) +} +func (f *WebDavFile) Seek(offset int64, whence int) (int64, error) { return f.File.Seek(offset, whence) } +func (f *WebDavFile) Close() error { return f.File.Close() } +func (f *WebDavFile) Readdir(count int) ([]os.FileInfo, error) { return f.File.Readdir(count) } +func (f *WebDavFile) Stat() (os.FileInfo, error) { return f.File.Stat() } + +/* +func (fs WebDavDir) Mkdir(ctx context.Context, name string, perm os.FileMode) error { + err := fs.Dir.Mkdir(ctx, name, perm) + if err != nil { + return err + } + fullPath := filepath.Join(string(fs.Dir), name) + if chownErr := os.Chown(fullPath, fs.UID, fs.GID); chownErr != nil { + return chownErr + log.Printf("Mkdir chown error: %v", chownErr) + } + return nil +} + +func (fs WebDavDir) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) { + fullPath := filepath.Join(string(fs.Dir), name) + + log.Printf("OpenFile: %s, flag=%d, perm=%o", fullPath, flag, perm) + + f, err := fs.Dir.OpenFile(ctx, name, flag, perm) + if err != nil { + log.Printf("File opening error: %v", err) + return nil, err + } + + log.Printf("open file for: uid=%d, gid=%o", fs.UID, fs.GID) + + if chownErr := os.Chown(fullPath, fs.UID, fs.GID); chownErr != nil { + log.Printf("File chown error: %v", chownErr) + } + + return WebDavFile{ + File: f, + FullPath: fullPath, + }, nil +} + +func (f WebDavFile) Write(p []byte) (int, error) { + log.Printf("Writing %d bytes to %s", len(p), f.FullPath) + return f.File.Write(p) +}*/ \ No newline at end of file diff --git a/src/main.go b/src/main.go new file mode 100755 index 0000000..b76e11b --- /dev/null +++ b/src/main.go @@ -0,0 +1,159 @@ +package main + +import "C" + +import ( + "fmt" + "log" + "time" + "net/http" + "os" + "os/user" + "strconv" + "runtime" + "strings" + + "golang.org/x/net/webdav" +) + +// WebDAV handler with authentication +func webdavHandler(w http.ResponseWriter, r *http.Request) { + if runtime.GOOS != "linux" { + handler := &webdav.Handler{ + FileSystem: webdav.Dir("."), + LockSystem: webdav.NewMemLS(), + } + handler.ServeHTTP(w, r) + return + } + + username, password, ok := r.BasicAuth() + if !ok { + w.Header().Set("WWW-Authenticate", `Basic realm="WebDAV"`) + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + + start := time.Now() + if !PAMAuthenticate(username, password) { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + + elapsed := time.Since(start) + fmt.Printf("PAM checking time: %dms\n", elapsed.Milliseconds()) + + // start = time.Now() + // isValid, _ := checkPassword("user", "pass") + // elapsed = time.Since(start) + // fmt.Printf("Direct checking time: %dms\n %d", elapsed.Milliseconds(), isValid) + + // start = time.Now() + // isValid, _ = checkPassword("user", "pass1") + // elapsed = time.Since(start) + // fmt.Printf("Direct checking time: %dms\n %d", elapsed.Milliseconds(), isValid) + + // Get the user's home directory + userInfo, err := user.Lookup(username) + if err != nil { + http.Error(w, "User not found", http.StatusNotFound) + return + } + + // webdavPath := userInfo.HomeDir + "/website" + webdavPath := userInfo.HomeDir + + // Check if the directory exists + if _, err := os.Stat(webdavPath); os.IsNotExist(err) { + http.Error(w, "WebDAV directory not found", http.StatusNotFound) + return + } + /* + handler := &webdav.Handler{ + Prefix: "/", + FileSystem: webdav.Dir(webdavPath), + LockSystem: webdav.NewMemLS(), + } + + fmt.Println("new connection") + */ + + uid, gid, err := GetUserData(username) + if err != nil { + http.Error(w, "WebDAV directory not found", http.StatusForbidden) + return + } +/* + handler := &webdav.Handler{ + Prefix: "/", + FileSystem: WebDavDir{Dir: webdav.Dir(webdavPath), UID: uid, GID: gid}, + LockSystem: webdav.NewMemLS(), + } +*/ + + handler := &webdav.Handler{ + Prefix: "/", + FileSystem: WebDavDir{Dir: webdav.Dir(webdavPath), UID: uid, GID: gid}, + //LockSystem: webdav.NewMemLS(), + //LockSystem: nil, // disable locking + LockSystem: globalLockSystem, + Logger: func(r *http.Request, err error) { + log.Printf("%s %s, error: %v", r.Method, r.URL.Path, err) + }, + } + + fmt.Printf("New connection: %s\n", username) + + handler.ServeHTTP(w, r) +} + +// TODO: this method might not work corractly with gnome or other clients +func isWebDAVRequest(r *http.Request) bool { + switch r.Method { + case "PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK": + return true + } + + // Finder sends GET/OPTIONS with Depth header when doing WebDAV + if r.Header.Get("Depth") != "" { + return true + } + + // Optional: check User-Agent or other headers if needed + ua := r.UserAgent() + if strings.Contains(ua, "WebDAV") || strings.Contains(ua, "Microsoft-WebDAV") { + return true + } + + return false +} +func main() { + + // http.HandleFunc("/", webdavHandler) + // http.HandleFunc("/", getRoot) + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if isWebDAVRequest(r) { + webdavHandler(w, r) + } else { + getRoot(w, r) + } + }) + + // This binds to 127.0.0.1 prevents for accessing the app from the outside + //log.Fatal(http.ListenAndServe(":8800", nil)) + // + + + + envValue := os.Getenv("WEBDAV_SERV_PORT") + if port, err := strconv.ParseInt(envValue, 10, 64); err == nil { + fmt.Printf("WebDAV server running on :%d\n", port) + log.Fatal(http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", port), nil)) + + } else { + fmt.Println("WebDAV server running socket") + startUnixSocket() + } +} diff --git a/src/pam.c b/src/pam.c new file mode 100755 index 0000000..7f485b8 --- /dev/null +++ b/src/pam.c @@ -0,0 +1,44 @@ +// +build linux + +// it works with file ending file_linux.go (and c probably as well) + +#define _POSIX_C_SOURCE 200809L // Ensure strdup() is available + +#include +#include +#include + +// PAM Conversation Function +int pam_conv_func(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) { + struct pam_response *responses = (struct pam_response *)malloc(num_msg * sizeof(struct pam_response)); + if (!responses) return PAM_CONV_ERR; + + char *password = (char *)appdata_ptr; + + for (int i = 0; i < num_msg; i++) { + responses[i].resp_retcode = 0; + if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) { + responses[i].resp = strdup(password); + } else { + responses[i].resp = NULL; + } + } + + *resp = responses; + return PAM_SUCCESS; +} + +// Function to authenticate user with PAM +int authenticate_pam(const char *username, const char *password) { + struct pam_conv conv = { pam_conv_func, (void *)password }; + pam_handle_t *pamh = NULL; + + int retval = pam_start("login", username, &conv, &pamh); + if (retval != PAM_SUCCESS) return retval; + + retval = pam_authenticate(pamh, 0); + pam_end(pamh, retval); + + return retval; +} diff --git a/src/pam.go b/src/pam.go new file mode 100755 index 0000000..6c9bb3d --- /dev/null +++ b/src/pam.go @@ -0,0 +1,32 @@ +// +build linux + +package main + +/* +#cgo CFLAGS: -std=c11 +#cgo LDFLAGS: -lpam +#include +#include + +extern int authenticate_pam(const char *username, const char *password); + +// Define PAM_SUCCESS explicitly for cgo +static const int CGO_PAM_SUCCESS = PAM_SUCCESS; +*/ +import "C" + +import ( + "unsafe" +) + +// Authenticate using PAM (directly linked C function) +func PAMAuthenticate(username, password string) bool { + cUsername := C.CString(username) + cPassword := C.CString(password) + defer C.free(unsafe.Pointer(cUsername)) + defer C.free(unsafe.Pointer(cPassword)) + + result := C.authenticate_pam(cUsername, cPassword) + return result == C.PAM_SUCCESS + //return result == C.CGO_PAM_SUCCESS +} diff --git a/src/pam_darwin.go b/src/pam_darwin.go new file mode 100644 index 0000000..6268ec9 --- /dev/null +++ b/src/pam_darwin.go @@ -0,0 +1,6 @@ +package main + +// https://www.youtube.com/watch?v=mIfzwTfXnWI +func PAMAuthenticate(username, password string) bool { + return true +} \ No newline at end of file diff --git a/src/shadow.go b/src/shadow.go new file mode 100644 index 0000000..d16382e --- /dev/null +++ b/src/shadow.go @@ -0,0 +1,53 @@ +// +build linux + +package main + +/* +#cgo LDFLAGS: -lcrypt +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +int check_password(const char* username, const char* password) { + struct spwd *shadow_entry = getspnam(username); + if (!shadow_entry) { + return -1; // User not found or error + } + + const char *shadow_hash = shadow_entry->sp_pwdp; + if (!shadow_hash) { + return -2; + } + + char *calculated_hash = crypt(password, shadow_hash); + if (!calculated_hash) { + return -3; // Error in crypt + } + + return strcmp(shadow_hash, calculated_hash) == 0; +} +*/ +import "C" +import ( +"fmt" +//"os" +"unsafe" +) + +func checkPassword(username, password string) (bool, error) { + cUsername := C.CString(username) + cPassword := C.CString(password) + defer C.free(unsafe.Pointer(cUsername)) + defer C.free(unsafe.Pointer(cPassword)) + + result := C.check_password(cUsername, cPassword) + switch result { + case 1: return true, nil + case 0: return false, nil + default: return false, fmt.Errorf("Auth Error") + } +} diff --git a/src/unix.go b/src/unix.go new file mode 100644 index 0000000..1345457 --- /dev/null +++ b/src/unix.go @@ -0,0 +1,30 @@ +package main +import ( + "fmt" + "net" + "log" + "net/http" + "os" +) + +func startUnixSocket() { + socketPath := "/run/webdav.sock" + + // Remove the socket if it exists + if err := os.RemoveAll(socketPath); err != nil { + log.Fatalf("Failed to remove old socket: %v", err) + } + + listener, err := net.Listen("unix", socketPath) + if err != nil { + log.Fatalf("Failed to listen on socket: %v", err) + } + + // Make socket accessible to Nginx + if err := os.Chmod(socketPath, 0666); err != nil { + log.Fatalf("Failed to chmod socket: %v", err) + } + + fmt.Printf("WebDAV server listening on %s\n", socketPath) + log.Fatal(http.Serve(listener, nil)) +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..50549f6 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,25 @@ + + + + + File List + + + {{if .IsLoggedIn}} +

Welcome back!

+ {{else}} +

Please log in to access more features.

+ {{end}} + +

Files

+ {{if .Files}} +
    + {{range .Files}} +
  • {{.FileName}}
  • + {{end}} +
+ {{else}} +

No files available.

+ {{end}} + + \ No newline at end of file