webdav/question.md
Artur Gurgul a0a338067b init
2025-04-25 08:44:55 +02:00

250 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Im 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 <security/pam_appl.h>
#include <stdlib.h>
#include <string.h>
// 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 <stdlib.h>
#include <security/pam_appl.h>
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
}
```