diff --git a/CHANGELOG.md b/CHANGELOG.md index 861af80..86a6e5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,10 @@ first public release - **utmpx time field portability** — replaced hardcoded `C.__uint32_t` / `C.__int32_t` casts with a C helper function (`auth/utmpx_time.h`) that lets the compiler handle type conversion. fixes build failure on systems where `ut_tv` uses `struct timeval` instead of the 32/64 compat struct - **cgo pointer safety for pam** — replaced `unsafe.Pointer(&h)` (Go stack pointer passed to C) with `storeHandle`/`loadHandle` helpers that keep the `cgo.Handle` in C-heap memory. fixes `cgo argument has Go pointer to unpinned Go pointer` panic on Go 1.22+ - **pam conversation callback** — fixed `unsafe.Slice` using `&r` (pointer-to-pointer) as the backing array instead of `r` (the calloc'd response array), which wrote password bytes into random stack memory. also now only responds to `PAM_PROMPT_ECHO_OFF`/`PAM_PROMPT_ECHO_ON` messages — info and error messages get nil. fixes `pam_unix: auth could not identify password` +- **cgo handle lifecycle** — moved `cgo.Handle` and its C-heap backing from `Validate` (where they were `defer`-freed on return) into the `Credentials` struct so they stay alive through `OpenSession` and until `CloseSession`. fixes `misuse of an invalid Handle` panic +- **session spawning** — replaced the broken `ForkExec` (which relaunched the same binary with made-up flags) with a direct `session.Spawn` call that drops privileges and runs the desktop in-process. fixes `flag provided but not defined: -user` +- **renamed extra/ files** — `lemurs.pam` → `latchd.pam`, `lemurs.service` → `latchd.service` +- **readme credits** — added a credits section acknowledging lemurs for the `extra/` directory structure and session scanning approach - **makefile `enable` and `update` targets** — `make enable` stops any running DM, disables the aliased display-manager, and enables latchd. `make update` runs uninstall → git pull → reinstall in one command - **missing `extra/` directory** — added `extra/config.toml`, `extra/xsetup.sh`, `extra/lemurs.pam`, and `extra/lemurs.service` so `make install` can find them - **makefile `deps` target** — added `make deps` to automatically install `libpam0g-dev` and `build-essential`, and verify go is present diff --git a/README.md b/README.md index 77e5359..eaec4ee 100644 --- a/README.md +++ b/README.md @@ -182,3 +182,7 @@ src/ ## license MIT or Apache-2.0 + +## credits + +the `extra/` directory structure and the session scanning / pam delegation approach are adapted from [lemurs](https://github.com/coastalwhite/lemurs), a rust display manager. diff --git a/extra/lemurs.pam b/extra/lemurs.pam deleted file mode 100644 index fef863f..0000000 --- a/extra/lemurs.pam +++ /dev/null @@ -1,5 +0,0 @@ -#%PAM-1.0 -auth include login -account include login -session include login -password include login diff --git a/extra/lemurs.service b/extra/lemurs.service deleted file mode 100644 index 27e4bb7..0000000 --- a/extra/lemurs.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=latchd display manager -After=systemd-user-sessions.service plymouth-quit-wait.service getty@tty2.service -Conflicts=getty@tty2.service - -[Service] -ExecStart=/usr/bin/latchd -StandardInput=tty -TTYPath=/dev/tty2 -TTYReset=yes -TTYVHangup=yes -Type=idle - -[Install] -Alias=display-manager.service diff --git a/src/auth/pam.go b/src/auth/pam.go index 86c2eb7..f817263 100644 --- a/src/auth/pam.go +++ b/src/auth/pam.go @@ -67,6 +67,8 @@ type Credentials struct { HomeDir string Shell string pamHandle *C.pam_handle_t + pamData unsafe.Pointer + pamCB cgo.Handle } //export latchd_pam_conv @@ -110,16 +112,16 @@ func Validate(user, pass, service string) (*Credentials, error) { defer C.free(unsafe.Pointer(cUser)) h := cgo.NewHandle(pass) - defer h.Delete() cData := C.storeHandle(C.uintptr_t(h)) - defer C.free(cData) conv := C.makePAMConv(cData) var pamh *C.pam_handle_t ret := C.pam_start(cSvc, cUser, &conv, &pamh) if ret != C.PAM_SUCCESS { + h.Delete() + C.free(cData) return nil, &AuthError{ErrPAMService, fmt.Sprintf("pam_start: %s", pamErr(pamh, ret))} } @@ -127,6 +129,8 @@ func Validate(user, pass, service string) (*Credentials, error) { if ret != C.PAM_SUCCESS { msg := fmt.Sprintf("auth failed: %s", pamErr(pamh, ret)) C.pam_end(pamh, ret) + h.Delete() + C.free(cData) return nil, &AuthError{ErrAccountValidation, msg} } @@ -134,12 +138,16 @@ func Validate(user, pass, service string) (*Credentials, error) { if ret != C.PAM_SUCCESS { msg := fmt.Sprintf("account: %s", pamErr(pamh, ret)) C.pam_end(pamh, ret) + h.Delete() + C.free(cData) return nil, &AuthError{ErrAccountValidation, msg} } u, err := ouser.Lookup(user) if err != nil { C.pam_end(pamh, ret) + h.Delete() + C.free(cData) return nil, &AuthError{ErrUsernameNotFound, fmt.Sprintf("lookup %s: %v", user, err)} } @@ -163,6 +171,8 @@ func Validate(user, pass, service string) (*Credentials, error) { HomeDir: u.HomeDir, Shell: "/bin/sh", pamHandle: pamh, + pamData: cData, + pamCB: h, }, nil } @@ -180,6 +190,11 @@ func (c *Credentials) CloseSession() { C.pam_end(c.pamHandle, 0) c.pamHandle = nil } + if c.pamData != nil { + C.free(c.pamData) + c.pamData = nil + } + c.pamCB.Delete() } func pamErr(h *C.pam_handle_t, e C.int) string {