pax_global_header00006660000000000000000000000064143114344500014511gustar00rootroot0000000000000052 comment=313ea6f3baf83aaff875512dbd9905cb07537d84 pam-1.1.0/000077500000000000000000000000001431143445000122655ustar00rootroot00000000000000pam-1.1.0/.github/000077500000000000000000000000001431143445000136255ustar00rootroot00000000000000pam-1.1.0/.github/workflows/000077500000000000000000000000001431143445000156625ustar00rootroot00000000000000pam-1.1.0/.github/workflows/test.yaml000066400000000000000000000011221431143445000175210ustar00rootroot00000000000000on: [push, pull_request] name: Test jobs: test: strategy: matrix: go-version: [1.18.x, 1.19.x] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: - name: Install Go uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} - name: Install PAM run: sudo apt install -y libpam-dev - name: Add a test user run: sudo useradd -d /tmp/test -p '$1$Qd8H95T5$RYSZQeoFbEB.gS19zS99A0' -s /bin/false test - name: Checkout code uses: actions/checkout@v3 - name: Test run: sudo go test -v ./... pam-1.1.0/LICENSE000066400000000000000000000025011431143445000132700ustar00rootroot00000000000000Copyright 2011, krockot Copyright 2015, Michael Steinert All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pam-1.1.0/README.md000066400000000000000000000014211431143445000135420ustar00rootroot00000000000000[![GoDoc](https://godoc.org/github.com/msteinert/pam?status.svg)](http://godoc.org/github.com/msteinert/pam) [![Go Report Card](https://goreportcard.com/badge/github.com/msteinert/pam)](https://goreportcard.com/report/github.com/msteinert/pam) # Go PAM This is a Go wrapper for the PAM application API. ## Testing To run the full suite, the tests must be run as the root user. To setup your system for testing, create a user named "test" with the password "secret". For example: ``` $ sudo useradd test \ -d /tmp/test \ -p '$1$Qd8H95T5$RYSZQeoFbEB.gS19zS99A0' \ -s /bin/false ``` Then execute the tests: ``` $ sudo GOPATH=$GOPATH $(which go) test -v ``` [1]: http://godoc.org/github.com/msteinert/pam [2]: http://www.linux-pam.org/Linux-PAM-html/Linux-PAM_ADG.html pam-1.1.0/callback.go000066400000000000000000000010161431143445000143460ustar00rootroot00000000000000package pam import "sync" var cb struct { sync.Mutex m map[int]interface{} c int } func init() { cb.m = make(map[int]interface{}) } func cbAdd(v interface{}) int { cb.Lock() defer cb.Unlock() cb.c++ cb.m[cb.c] = v return cb.c } func cbGet(c int) interface{} { cb.Lock() defer cb.Unlock() if v, ok := cb.m[c]; ok { return v } panic("Callback pointer not found") } func cbDelete(c int) { cb.Lock() defer cb.Unlock() if _, ok := cb.m[c]; !ok { panic("Callback pointer not found") } delete(cb.m, c) } pam-1.1.0/callback_test.go000066400000000000000000000010401431143445000154020ustar00rootroot00000000000000package pam import ( "reflect" "testing" ) func TestCallback_001(t *testing.T) { c := cbAdd(TestCallback_001) v := cbGet(c) if reflect.TypeOf(v) != reflect.TypeOf(TestCallback_001) { t.Error("Received unexpected value") } cbDelete(c) } func TestCallback_002(t *testing.T) { defer func() { recover() }() c := cbAdd(TestCallback_002) cbGet(c + 1) t.Error("Expected a panic") } func TestCallback_003(t *testing.T) { defer func() { recover() }() c := cbAdd(TestCallback_003) cbDelete(c + 1) t.Error("Expected a panic") } pam-1.1.0/example_test.go000066400000000000000000000021771431143445000153150ustar00rootroot00000000000000package pam_test import ( "bufio" "errors" "fmt" "os" "github.com/msteinert/pam" "golang.org/x/term" ) // This example uses the default PAM service to authenticate any users. This // should cause PAM to ask its conversation handler for a username and password // in sequence. func Example() { t, err := pam.StartFunc("", "", func(s pam.Style, msg string) (string, error) { switch s { case pam.PromptEchoOff: fmt.Print(msg) pw, err := term.ReadPassword(int(os.Stdin.Fd())) if err != nil { return "", err } fmt.Println() return string(pw), nil case pam.PromptEchoOn: fmt.Print(msg) s := bufio.NewScanner(os.Stdin) s.Scan() return s.Text(), nil case pam.ErrorMsg: fmt.Fprintf(os.Stderr, "%s\n", msg) return "", nil case pam.TextInfo: fmt.Println(msg) return "", nil default: return "", errors.New("unrecognized message style") } }) if err != nil { fmt.Fprintf(os.Stderr, "start: %s\n", err.Error()) os.Exit(1) } err = t.Authenticate(0) if err != nil { fmt.Fprintf(os.Stderr, "authenticate: %s\n", err.Error()) os.Exit(1) } fmt.Println("authentication succeeded!") } pam-1.1.0/go.mod000066400000000000000000000002601431143445000133710ustar00rootroot00000000000000module github.com/msteinert/pam go 1.17 require golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 require golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect pam-1.1.0/go.sum000066400000000000000000000006401431143445000134200ustar00rootroot00000000000000golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= pam-1.1.0/my-service000066400000000000000000000001411431143445000142670ustar00rootroot00000000000000# Custom stack to always permit, independent of the user name/pass auth required pam_permit.so pam-1.1.0/transaction.c000066400000000000000000000026751431143445000147700ustar00rootroot00000000000000#include "_cgo_export.h" #include #include #ifdef __sun #define PAM_CONST #else #define PAM_CONST const #endif int cb_pam_conv( int num_msg, PAM_CONST struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { *resp = calloc(num_msg, sizeof **resp); if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG) { return PAM_CONV_ERR; } if (!*resp) { return PAM_BUF_ERR; } for (size_t i = 0; i < num_msg; ++i) { struct cbPAMConv_return result = cbPAMConv( msg[i]->msg_style, (char *)msg[i]->msg, (long)appdata_ptr); if (result.r1 != PAM_SUCCESS) { goto error; } (*resp)[i].resp = result.r0; } return PAM_SUCCESS; error: for (size_t i = 0; i < num_msg; ++i) { if ((*resp)[i].resp) { memset((*resp)[i].resp, 0, strlen((*resp)[i].resp)); free((*resp)[i].resp); } } memset(*resp, 0, num_msg * sizeof *resp); free(*resp); *resp = NULL; return PAM_CONV_ERR; } void init_pam_conv(struct pam_conv *conv, long c) { conv->conv = cb_pam_conv; conv->appdata_ptr = (void *)c; } // pam_start_confdir is a recent PAM api to declare a confdir (mostly for tests) // weaken the linking dependency to detect if it’s present. int pam_start_confdir(const char *service_name, const char *user, const struct pam_conv *pam_conversation, const char *confdir, pam_handle_t **pamh) __attribute__ ((weak)); int check_pam_start_confdir(void) { if (pam_start_confdir == NULL) return 1; return 0; } pam-1.1.0/transaction.go000066400000000000000000000235001431143445000151410ustar00rootroot00000000000000// Package pam provides a wrapper for the PAM application API. package pam //#include //#include //#cgo CFLAGS: -Wall -std=c99 //#cgo LDFLAGS: -lpam //void init_pam_conv(struct pam_conv *conv, long c); //int pam_start_confdir(const char *service_name, const char *user, const struct pam_conv *pam_conversation, const char *confdir, pam_handle_t **pamh) __attribute__ ((weak)); //int check_pam_start_confdir(void); import "C" import ( "errors" "runtime" "strings" "unsafe" ) // Style is the type of message that the conversation handler should display. type Style int // Coversation handler style types. const ( // PromptEchoOff indicates the conversation handler should obtain a // string without echoing any text. PromptEchoOff Style = C.PAM_PROMPT_ECHO_OFF // PromptEchoOn indicates the conversation handler should obtain a // string while echoing text. PromptEchoOn = C.PAM_PROMPT_ECHO_ON // ErrorMsg indicates the conversation handler should display an // error message. ErrorMsg = C.PAM_ERROR_MSG // TextInfo indicates the conversation handler should display some // text. TextInfo = C.PAM_TEXT_INFO ) // ConversationHandler is an interface for objects that can be used as // conversation callbacks during PAM authentication. type ConversationHandler interface { // RespondPAM receives a message style and a message string. If the // message Style is PromptEchoOff or PromptEchoOn then the function // should return a response string. RespondPAM(Style, string) (string, error) } // ConversationFunc is an adapter to allow the use of ordinary functions as // conversation callbacks. type ConversationFunc func(Style, string) (string, error) // RespondPAM is a conversation callback adapter. func (f ConversationFunc) RespondPAM(s Style, msg string) (string, error) { return f(s, msg) } // cbPAMConv is a wrapper for the conversation callback function. //export cbPAMConv func cbPAMConv(s C.int, msg *C.char, c int) (*C.char, C.int) { var r string var err error v := cbGet(c) switch cb := v.(type) { case ConversationHandler: r, err = cb.RespondPAM(Style(s), C.GoString(msg)) } if err != nil { return nil, C.PAM_CONV_ERR } return C.CString(r), C.PAM_SUCCESS } // Transaction is the application's handle for a PAM transaction. type Transaction struct { handle *C.pam_handle_t conv *C.struct_pam_conv status C.int c int } // transactionFinalizer cleans up the PAM handle and deletes the callback // function. func transactionFinalizer(t *Transaction) { C.pam_end(t.handle, t.status) cbDelete(t.c) } // Start initiates a new PAM transaction. Service is treated identically to // how pam_start treats it internally. // // All application calls to PAM begin with Start*. The returned // transaction provides an interface to the remainder of the API. func Start(service, user string, handler ConversationHandler) (*Transaction, error) { return start(service, user, handler, "") } // StartFunc registers the handler func as a conversation handler. func StartFunc(service, user string, handler func(Style, string) (string, error)) (*Transaction, error) { return Start(service, user, ConversationFunc(handler)) } // StartConfDir initiates a new PAM transaction. Service is treated identically to // how pam_start treats it internally. // confdir allows to define where all pam services are defined. This is used to provide // custom paths for tests. // // All application calls to PAM begin with Start*. The returned // transaction provides an interface to the remainder of the API. func StartConfDir(service, user string, handler ConversationHandler, confDir string) (*Transaction, error) { if !CheckPamHasStartConfdir() { return nil, errors.New("StartConfDir() was used, but the pam version on the system is not recent enough") } return start(service, user, handler, confDir) } func start(service, user string, handler ConversationHandler, confDir string) (*Transaction, error) { t := &Transaction{ conv: &C.struct_pam_conv{}, c: cbAdd(handler), } C.init_pam_conv(t.conv, C.long(t.c)) runtime.SetFinalizer(t, transactionFinalizer) s := C.CString(service) defer C.free(unsafe.Pointer(s)) var u *C.char if len(user) != 0 { u = C.CString(user) defer C.free(unsafe.Pointer(u)) } if confDir == "" { t.status = C.pam_start(s, u, t.conv, &t.handle) } else { c := C.CString(confDir) defer C.free(unsafe.Pointer(c)) t.status = C.pam_start_confdir(s, u, t.conv, c, &t.handle) } if t.status != C.PAM_SUCCESS { return nil, t } return t, nil } func (t *Transaction) Error() string { return C.GoString(C.pam_strerror(t.handle, C.int(t.status))) } // Item is a an PAM information type. type Item int // PAM Item types. const ( // Service is the name which identifies the PAM stack. Service Item = C.PAM_SERVICE // User identifies the username identity used by a service. User = C.PAM_USER // Tty is the terminal name. Tty = C.PAM_TTY // Rhost is the requesting host name. Rhost = C.PAM_RHOST // Authtok is the currently active authentication token. Authtok = C.PAM_AUTHTOK // Oldauthtok is the old authentication token. Oldauthtok = C.PAM_OLDAUTHTOK // Ruser is the requesting user name. Ruser = C.PAM_RUSER // UserPrompt is the string use to prompt for a username. UserPrompt = C.PAM_USER_PROMPT ) // SetItem sets a PAM information item. func (t *Transaction) SetItem(i Item, item string) error { cs := unsafe.Pointer(C.CString(item)) defer C.free(cs) t.status = C.pam_set_item(t.handle, C.int(i), cs) if t.status != C.PAM_SUCCESS { return t } return nil } // GetItem retrieves a PAM information item. func (t *Transaction) GetItem(i Item) (string, error) { var s unsafe.Pointer t.status = C.pam_get_item(t.handle, C.int(i), &s) if t.status != C.PAM_SUCCESS { return "", t } return C.GoString((*C.char)(s)), nil } // Flags are inputs to various PAM functions than be combined with a bitwise // or. Refer to the official PAM documentation for which flags are accepted // by which functions. type Flags int // PAM Flag types. const ( // Silent indicates that no messages should be emitted. Silent Flags = C.PAM_SILENT // DisallowNullAuthtok indicates that authorization should fail // if the user does not have a registered authentication token. DisallowNullAuthtok = C.PAM_DISALLOW_NULL_AUTHTOK // EstablishCred indicates that credentials should be established // for the user. EstablishCred = C.PAM_ESTABLISH_CRED // DeleteCred inidicates that credentials should be deleted. DeleteCred = C.PAM_DELETE_CRED // ReinitializeCred indicates that credentials should be fully // reinitialized. ReinitializeCred = C.PAM_REINITIALIZE_CRED // RefreshCred indicates that the lifetime of existing credentials // should be extended. RefreshCred = C.PAM_REFRESH_CRED // ChangeExpiredAuthtok indicates that the authentication token // should be changed if it has expired. ChangeExpiredAuthtok = C.PAM_CHANGE_EXPIRED_AUTHTOK ) // Authenticate is used to authenticate the user. // // Valid flags: Silent, DisallowNullAuthtok func (t *Transaction) Authenticate(f Flags) error { t.status = C.pam_authenticate(t.handle, C.int(f)) if t.status != C.PAM_SUCCESS { return t } return nil } // SetCred is used to establish, maintain and delete the credentials of a // user. // // Valid flags: EstablishCred, DeleteCred, ReinitializeCred, RefreshCred func (t *Transaction) SetCred(f Flags) error { t.status = C.pam_setcred(t.handle, C.int(f)) if t.status != C.PAM_SUCCESS { return t } return nil } // AcctMgmt is used to determine if the user's account is valid. // // Valid flags: Silent, DisallowNullAuthtok func (t *Transaction) AcctMgmt(f Flags) error { t.status = C.pam_acct_mgmt(t.handle, C.int(f)) if t.status != C.PAM_SUCCESS { return t } return nil } // ChangeAuthTok is used to change the authentication token. // // Valid flags: Silent, ChangeExpiredAuthtok func (t *Transaction) ChangeAuthTok(f Flags) error { t.status = C.pam_chauthtok(t.handle, C.int(f)) if t.status != C.PAM_SUCCESS { return t } return nil } // OpenSession sets up a user session for an authenticated user. // // Valid flags: Slient func (t *Transaction) OpenSession(f Flags) error { t.status = C.pam_open_session(t.handle, C.int(f)) if t.status != C.PAM_SUCCESS { return t } return nil } // CloseSession closes a previously opened session. // // Valid flags: Silent func (t *Transaction) CloseSession(f Flags) error { t.status = C.pam_close_session(t.handle, C.int(f)) if t.status != C.PAM_SUCCESS { return t } return nil } // PutEnv adds or changes the value of PAM environment variables. // // NAME=value will set a variable to a value. // NAME= will set a variable to an empty value. // NAME (without an "=") will delete a variable. func (t *Transaction) PutEnv(nameval string) error { cs := C.CString(nameval) defer C.free(unsafe.Pointer(cs)) t.status = C.pam_putenv(t.handle, cs) if t.status != C.PAM_SUCCESS { return t } return nil } // GetEnv is used to retrieve a PAM environment variable. func (t *Transaction) GetEnv(name string) string { cs := C.CString(name) defer C.free(unsafe.Pointer(cs)) value := C.pam_getenv(t.handle, cs) if value == nil { return "" } return C.GoString(value) } func next(p **C.char) **C.char { return (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(p))) } // GetEnvList returns a copy of the PAM environment as a map. func (t *Transaction) GetEnvList() (map[string]string, error) { env := make(map[string]string) p := C.pam_getenvlist(t.handle) if p == nil { t.status = C.PAM_BUF_ERR return nil, t } for q := p; *q != nil; q = next(q) { chunks := strings.SplitN(C.GoString(*q), "=", 2) if len(chunks) == 2 { env[chunks[0]] = chunks[1] } C.free(unsafe.Pointer(*q)) } C.free(unsafe.Pointer(p)) return env, nil } // CheckPamHasStartConfdir return if pam on system supports pam_system_confdir func CheckPamHasStartConfdir() bool { return C.check_pam_start_confdir() == 0 } pam-1.1.0/transaction_test.go000066400000000000000000000172711431143445000162100ustar00rootroot00000000000000package pam import ( "errors" "os/user" "testing" ) func TestPAM_001(t *testing.T) { u, _ := user.Current() if u.Uid != "0" { t.Skip("run this test as root") } p := "secret" tx, err := StartFunc("", "test", func(s Style, msg string) (string, error) { return p, nil }) if err != nil { t.Fatalf("start #error: %v", err) } err = tx.Authenticate(0) if err != nil { t.Fatalf("authenticate #error: %v", err) } err = tx.AcctMgmt(Silent) if err != nil { t.Fatalf("acct_mgmt #error: %v", err) } err = tx.SetCred(Silent | EstablishCred) if err != nil { t.Fatalf("setcred #error: %v", err) } } 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) { switch s { case PromptEchoOn: return "test", nil case PromptEchoOff: return "secret", nil } return "", errors.New("unexpected") }) if err != nil { t.Fatalf("start #error: %v", err) } err = tx.Authenticate(0) if err != nil { t.Fatalf("authenticate #error: %v", err) } } type Credentials struct { User string Password string } func (c Credentials) RespondPAM(s Style, msg string) (string, error) { switch s { case PromptEchoOn: return c.User, nil case PromptEchoOff: return c.Password, nil } return "", errors.New("unexpected") } func TestPAM_003(t *testing.T) { u, _ := user.Current() if u.Uid != "0" { t.Skip("run this test as root") } c := Credentials{ User: "test", Password: "secret", } tx, err := Start("", "", c) if err != nil { t.Fatalf("start #error: %v", err) } err = tx.Authenticate(0) if err != nil { t.Fatalf("authenticate #error: %v", err) } } func TestPAM_004(t *testing.T) { u, _ := user.Current() if u.Uid != "0" { t.Skip("run this test as root") } c := Credentials{ Password: "secret", } tx, err := Start("", "test", c) if err != nil { t.Fatalf("start #error: %v", err) } err = tx.Authenticate(0) if err != nil { t.Fatalf("authenticate #error: %v", err) } } func TestPAM_005(t *testing.T) { u, _ := user.Current() if u.Uid != "0" { t.Skip("run this test as root") } tx, err := StartFunc("passwd", "test", func(s Style, msg string) (string, error) { return "secret", nil }) if err != nil { t.Fatalf("start #error: %v", err) } err = tx.ChangeAuthTok(Silent) if err != nil { t.Fatalf("chauthtok #error: %v", err) } } func TestPAM_006(t *testing.T) { u, _ := user.Current() if u.Uid != "0" { t.Skip("run this test as root") } tx, err := StartFunc("passwd", u.Username, func(s Style, msg string) (string, error) { return "secret", nil }) if err != nil { t.Fatalf("start #error: %v", err) } err = tx.OpenSession(Silent) if err != nil { t.Fatalf("open_session #error: %v", err) } err = tx.CloseSession(Silent) if err != nil { t.Fatalf("close_session #error: %v", err) } } func TestPAM_007(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) { return "", errors.New("Sorry, it didn't work") }) if err != nil { t.Fatalf("start #error: %v", err) } err = tx.Authenticate(0) if err == nil { t.Fatalf("authenticate #expected an error") } s := err.Error() if len(s) == 0 { t.Fatalf("error #expected an error message") } } func TestPAM_ConfDir(t *testing.T) { u, _ := user.Current() if u.Uid != "0" { t.Skip("run this test as root") } c := Credentials{ // the custom service always permits even with wrong password. Password: "wrongsecret", } tx, err := StartConfDir("my-service", "test", c, ".") if !CheckPamHasStartConfdir() { if err == nil { t.Fatalf("start should have errored out as pam_start_confdir is not available: %v", err) } // nothing else we do, we don't support it. return } if err != nil { t.Fatalf("start #error: %v", err) } err = tx.Authenticate(0) if err != nil { t.Fatalf("authenticate #error: %v", err) } } func TestPAM_ConfDir_FailNoServiceOrUnsupported(t *testing.T) { u, _ := user.Current() if u.Uid != "0" { t.Skip("run this test as root") } c := Credentials{ Password: "secret", } _, err := StartConfDir("does-not-exists", "test", c, ".") if err == nil { t.Fatalf("authenticate #expected an error") } s := err.Error() if len(s) == 0 { t.Fatalf("error #expected an error message") } } func TestItem(t *testing.T) { tx, _ := StartFunc("passwd", "test", func(s Style, msg string) (string, error) { return "", nil }) s, err := tx.GetItem(Service) if err != nil { t.Fatalf("getitem #error: %v", err) } if s != "passwd" { t.Fatalf("getitem #error: expected passwd, got %v", s) } s, err = tx.GetItem(User) if err != nil { t.Fatalf("getitem #error: %v", err) } if s != "test" { t.Fatalf("getitem #error: expected test, got %v", s) } err = tx.SetItem(User, "root") if err != nil { t.Fatalf("setitem #error: %v", err) } s, err = tx.GetItem(User) if err != nil { t.Fatalf("getitem #error: %v", err) } if s != "root" { t.Fatalf("getitem #error: expected root, got %v", s) } } func TestEnv(t *testing.T) { tx, err := StartFunc("", "", 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) } n := len(m) if n != 0 { t.Fatalf("putenv #error: expected 0 items, got %v", n) } vals := []string{ "VAL1=1", "VAL2=2", "VAL3=3", } for _, s := range vals { err = tx.PutEnv(s) if err != nil { t.Fatalf("putenv #error: %v", err) } } s := tx.GetEnv("VAL0") if s != "" { t.Fatalf("getenv #error: expected \"\", got %v", s) } s = tx.GetEnv("VAL1") if s != "1" { t.Fatalf("getenv #error: expected 1, got %v", s) } s = tx.GetEnv("VAL2") if s != "2" { t.Fatalf("getenv #error: expected 2, got %v", s) } s = tx.GetEnv("VAL3") if s != "3" { t.Fatalf("getenv #error: expected 3, got %v", s) } m, err = tx.GetEnvList() if err != nil { t.Fatalf("getenvlist #error: %v", err) } n = len(m) if n != 3 { t.Fatalf("getenvlist #error: expected 3 items, got %v", n) } if m["VAL1"] != "1" { t.Fatalf("getenvlist #error: expected 1, got %v", m["VAL1"]) } if m["VAL2"] != "2" { t.Fatalf("getenvlist #error: expected 2, got %v", m["VAL1"]) } if m["VAL3"] != "3" { t.Fatalf("getenvlist #error: expected 3, got %v", m["VAL1"]) } } func TestFailure_001(t *testing.T) { tx := Transaction{} _, err := tx.GetEnvList() if err == nil { t.Fatalf("getenvlist #expected an error") } } func TestFailure_002(t *testing.T) { tx := Transaction{} err := tx.PutEnv("") if err == nil { t.Fatalf("getenvlist #expected an error") } } func TestFailure_003(t *testing.T) { tx := Transaction{} err := tx.CloseSession(0) if err == nil { t.Fatalf("getenvlist #expected an error") } } func TestFailure_004(t *testing.T) { tx := Transaction{} err := tx.OpenSession(0) if err == nil { t.Fatalf("getenvlist #expected an error") } } func TestFailure_005(t *testing.T) { tx := Transaction{} err := tx.ChangeAuthTok(0) if err == nil { t.Fatalf("getenvlist #expected an error") } } func TestFailure_006(t *testing.T) { tx := Transaction{} err := tx.AcctMgmt(0) if err == nil { t.Fatalf("getenvlist #expected an error") } } func TestFailure_007(t *testing.T) { tx := Transaction{} err := tx.SetCred(0) if err == nil { t.Fatalf("getenvlist #expected an error") } } func TestFailure_008(t *testing.T) { tx := Transaction{} err := tx.SetItem(User, "test") if err == nil { t.Fatalf("getenvlist #expected an error") } } func TestFailure_009(t *testing.T) { tx := Transaction{} _, err := tx.GetItem(User) if err == nil { t.Fatalf("getenvlist #expected an error") } }