Rework pam_getenvlist so it doesn't leak
This commit is contained in:
@@ -22,20 +22,16 @@ const (
|
|||||||
TextInfo = C.PAM_TEXT_INFO
|
TextInfo = C.PAM_TEXT_INFO
|
||||||
)
|
)
|
||||||
|
|
||||||
// Objects implementing the ConversationHandler interface can
|
// Objects implementing the ConversationHandler interface can be registered as
|
||||||
// be registered as conversation callbacks to be used during
|
// conversation callbacks to be used during PAM authentication. RespondPAM
|
||||||
// PAM authentication. RespondPAM receives a message style
|
// receives a message style and a message string. It is expected to return a
|
||||||
// (one of PROMPT_ECHO_OFF, PROMPT_ECHO_ON, ERROR_MSG, or
|
// response string.
|
||||||
// TEXT_INFO) and a message string. It is expected to return
|
|
||||||
// a response string and a bool indicating success or failure.
|
|
||||||
type ConversationHandler interface {
|
type ConversationHandler interface {
|
||||||
RespondPAM(Style, string) (string, error)
|
RespondPAM(Style, string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConversationFunc is an adapter to allow the use of ordinary
|
// ConversationFunc is an adapter to allow the use of ordinary functions as
|
||||||
// functions as conversation callbacks. ConversationFunc(f) is
|
// conversation callbacks.
|
||||||
// a ConversationHandler that calls f, where f must have
|
|
||||||
// the signature func(int,string)(string,bool).
|
|
||||||
type ConversationFunc func(Style, string) (string, error)
|
type ConversationFunc func(Style, string) (string, error)
|
||||||
|
|
||||||
func (f ConversationFunc) RespondPAM(s Style, msg string) (string, error) {
|
func (f ConversationFunc) RespondPAM(s Style, msg string) (string, error) {
|
||||||
@@ -73,15 +69,14 @@ func cbPAMConv(s C.int, msg *C.char, appdata unsafe.Pointer) (*C.char, C.int) {
|
|||||||
return C.CString(r), C.PAM_SUCCESS
|
return C.CString(r), C.PAM_SUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transaction is the application's handle for a single PAM transaction.
|
// Transaction is the application's handle for a PAM transaction.
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
handle *C.pam_handle_t
|
handle *C.pam_handle_t
|
||||||
conv *Conversation
|
conv *Conversation
|
||||||
status C.int
|
status C.int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ends a PAM transaction. From Linux-PAM documentation: "The [status] argument
|
// Finalize a PAM transaction.
|
||||||
// should be set to the value returned by the last PAM library call."
|
|
||||||
func TransactionFinalizer(t *Transaction) {
|
func TransactionFinalizer(t *Transaction) {
|
||||||
C.pam_end(t.handle, t.status)
|
C.pam_end(t.handle, t.status)
|
||||||
C.free(unsafe.Pointer(t.conv.conv))
|
C.free(unsafe.Pointer(t.conv.conv))
|
||||||
@@ -138,13 +133,7 @@ const (
|
|||||||
UserPrompt = C.PAM_USER_PROMPT
|
UserPrompt = C.PAM_USER_PROMPT
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sets a PAM informational item. Legal values of i are listed here
|
// pam_set_item
|
||||||
// (excluding Linux extensions):
|
|
||||||
//
|
|
||||||
// http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/adg-interface-by-app-expected.html#adg-pam_set_item
|
|
||||||
//
|
|
||||||
// PAM_CONV is not supported in order to simplify the Go API
|
|
||||||
// (and due to the fact that it is completely unnecessary).
|
|
||||||
func (t *Transaction) SetItem(i Item, item string) error {
|
func (t *Transaction) SetItem(i Item, item string) error {
|
||||||
cs := unsafe.Pointer(C.CString(item))
|
cs := unsafe.Pointer(C.CString(item))
|
||||||
defer C.free(cs)
|
defer C.free(cs)
|
||||||
@@ -155,8 +144,7 @@ func (t *Transaction) SetItem(i Item, item string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets a PAM item. Legal values of itemType are as specified by the
|
// pam_get_item
|
||||||
// documentation of SetItem.
|
|
||||||
func (t *Transaction) GetItem(i Item) (string, error) {
|
func (t *Transaction) GetItem(i Item) (string, error) {
|
||||||
var s unsafe.Pointer
|
var s unsafe.Pointer
|
||||||
t.status = C.pam_get_item(t.handle, C.int(i), &s)
|
t.status = C.pam_get_item(t.handle, C.int(i), &s)
|
||||||
@@ -254,11 +242,7 @@ func (t *Transaction) GetEnv(name string) string {
|
|||||||
return C.GoString(value)
|
return C.GoString(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnvList internally calls pam_getenvlist and then uses some very
|
// pam_getenvlist
|
||||||
// dangerous code to pull out the returned environment data and mash
|
|
||||||
// it into a map[string]string. This call may be safe, but it hasn't
|
|
||||||
// been tested on enough platforms/architectures/PAM-implementations to
|
|
||||||
// be sure.
|
|
||||||
func (t *Transaction) GetEnvList() (map[string]string, error) {
|
func (t *Transaction) GetEnvList() (map[string]string, error) {
|
||||||
env := make(map[string]string)
|
env := make(map[string]string)
|
||||||
p := C.pam_getenvlist(t.handle)
|
p := C.pam_getenvlist(t.handle)
|
||||||
@@ -266,15 +250,14 @@ func (t *Transaction) GetEnvList() (map[string]string, error) {
|
|||||||
t.status = C.PAM_BUF_ERR
|
t.status = C.PAM_BUF_ERR
|
||||||
return nil, t
|
return nil, t
|
||||||
}
|
}
|
||||||
list := (uintptr)(unsafe.Pointer(p))
|
for *p != nil {
|
||||||
for *(*uintptr)(unsafe.Pointer(list)) != 0 {
|
chunks := strings.SplitN(C.GoString(*p), "=", 2)
|
||||||
entry := *(*uintptr)(unsafe.Pointer(list))
|
|
||||||
nameval := C.GoString((*C.char)(unsafe.Pointer(entry)))
|
|
||||||
chunks := strings.SplitN(nameval, "=", 2)
|
|
||||||
if len(chunks) == 2 {
|
if len(chunks) == 2 {
|
||||||
env[chunks[0]] = chunks[1]
|
env[chunks[0]] = chunks[1]
|
||||||
list += (uintptr)(unsafe.Sizeof(list))
|
|
||||||
}
|
}
|
||||||
|
C.free(unsafe.Pointer(*p))
|
||||||
|
p = (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(p)))
|
||||||
}
|
}
|
||||||
|
C.free(unsafe.Pointer(p))
|
||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,15 @@ package pam
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"os/user"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPAM_001(t *testing.T) {
|
func TestPAM_001(t *testing.T) {
|
||||||
|
u, _ := user.Current()
|
||||||
|
if u.Uid != "0" {
|
||||||
|
t.Skip("run this test as root")
|
||||||
|
}
|
||||||
tx, err := StartFunc("", "test", func(s Style, msg string) (string, error) {
|
tx, err := StartFunc("", "test", func(s Style, msg string) (string, error) {
|
||||||
return "secret", nil
|
return "secret", nil
|
||||||
})
|
})
|
||||||
@@ -19,6 +24,10 @@ func TestPAM_001(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_002(t *testing.T) {
|
func TestPAM_002(t *testing.T) {
|
||||||
|
u, _ := user.Current()
|
||||||
|
if u.Uid != "0" {
|
||||||
|
t.Skip("run this test as root")
|
||||||
|
}
|
||||||
tx, err := StartFunc("", "", func(s Style, msg string) (string, error) {
|
tx, err := StartFunc("", "", func(s Style, msg string) (string, error) {
|
||||||
switch s {
|
switch s {
|
||||||
case PromptEchoOn:
|
case PromptEchoOn:
|
||||||
@@ -53,6 +62,10 @@ func (c Credentials) RespondPAM(s Style, msg string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_003(t *testing.T) {
|
func TestPAM_003(t *testing.T) {
|
||||||
|
u, _ := user.Current()
|
||||||
|
if u.Uid != "0" {
|
||||||
|
t.Skip("run this test as root")
|
||||||
|
}
|
||||||
c := Credentials{
|
c := Credentials{
|
||||||
User: "test",
|
User: "test",
|
||||||
Password: "secret",
|
Password: "secret",
|
||||||
@@ -68,6 +81,10 @@ func TestPAM_003(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_004(t *testing.T) {
|
func TestPAM_004(t *testing.T) {
|
||||||
|
u, _ := user.Current()
|
||||||
|
if u.Uid != "0" {
|
||||||
|
t.Skip("run this test as root")
|
||||||
|
}
|
||||||
c := Credentials{
|
c := Credentials{
|
||||||
Password: "secret",
|
Password: "secret",
|
||||||
}
|
}
|
||||||
@@ -80,3 +97,17 @@ func TestPAM_004(t *testing.T) {
|
|||||||
t.Fatalf("authenticate #error: %v", err)
|
t.Fatalf("authenticate #error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetEnvList(t *testing.T) {
|
||||||
|
tx, err := StartFunc("passwd", "test", func(s Style, msg string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("start #error: %v", err)
|
||||||
|
}
|
||||||
|
m, err := tx.GetEnvList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("getenvlist #error: %v", err)
|
||||||
|
}
|
||||||
|
t.Log(m)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user