From 869a63e504fee42bce7fe3e0aff5b5a0593bf15d Mon Sep 17 00:00:00 2001 From: "Joe Richey joerichey@google.com" Date: Mon, 17 Jul 2017 17:22:54 -0700 Subject: [PATCH] pam: Add Go wrappers around PAM functions This commit provides a Go interface to the PAM functions. --- pam/pam.go | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 pam/pam.go diff --git a/pam/pam.go b/pam/pam.go new file mode 100644 index 0000000..010d4d2 --- /dev/null +++ b/pam/pam.go @@ -0,0 +1,190 @@ +/* + * pam.go - Utility functions for interfacing with the PAM libraries. + * + * Copyright 2017 Google Inc. + * Author: Joe Richey (joerichey@google.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package pam + +/* +#cgo LDFLAGS: -lpam +#include "pam.h" + +#include +#include +#include +*/ +import "C" +import ( + "errors" + "fmt" + "unsafe" + + "github.com/google/fscrypt/util" +) + +// Handle wraps the C pam_handle_t type. This is used from within modules. +type Handle struct { + handle *C.pam_handle_t + status C.int +} + +// NewHandle creates a Handle from a raw pointer. +func NewHandle(pamh unsafe.Pointer) *Handle { + return &Handle{ + handle: (*C.pam_handle_t)(pamh), + status: C.PAM_SUCCESS, + } +} + +func (h *Handle) setData(name string, data unsafe.Pointer, cleanup C.CleanupFunc) error { + cName := C.CString(name) + defer C.free(unsafe.Pointer(cName)) + h.status = C.pam_set_data(h.handle, cName, data, cleanup) + return h.err() +} + +func (h *Handle) getData(name string) (unsafe.Pointer, error) { + var data unsafe.Pointer + cName := C.CString(name) + defer C.free(unsafe.Pointer(cName)) + h.status = C.pam_get_data(h.handle, cName, &data) + return data, h.err() +} + +func (h *Handle) SetSecret(name string, secret unsafe.Pointer) error { + return h.setData(name, C.copyIntoSecret(secret), C.CleanupFunc(C.freeSecret)) +} + +func (h *Handle) GetSecret(name string) (unsafe.Pointer, error) { + return h.getData(name) +} + +func (h *Handle) ClearSecret(name string) error { + return h.setData(name, unsafe.Pointer(C.CString("")), C.CleanupFunc(C.freeData)) +} + +func (h *Handle) SetString(name string, s string) error { + return h.setData(name, unsafe.Pointer(C.CString(s)), C.CleanupFunc(C.freeData)) +} + +func (h *Handle) GetString(name string) (string, error) { + data, err := h.getData(name) + if err != nil { + return "", err + } + return C.GoString((*C.char)(data)), nil +} + +func (h *Handle) SetSlice(name string, slice []string) error { + sliceLength := uintptr(len(slice)) + memorySize := (sliceLength + 1) * unsafe.Sizeof(uintptr(0)) + data := C.malloc(C.size_t(memorySize)) + + cSlice := util.PointerSlice(data) + for i, str := range slice { + cSlice[i] = unsafe.Pointer(C.CString(str)) + } + cSlice[sliceLength] = nil + + return h.setData(name, data, C.CleanupFunc(C.freeArray)) +} + +func (h *Handle) GetSlice(name string) ([]string, error) { + data, err := h.getData(name) + if err != nil { + return nil, err + } + + var slice []string + for _, cString := range util.PointerSlice(data) { + if cString == nil { + return slice, nil + } + slice = append(slice, C.GoString((*C.char)(cString))) + } + panic("We will never get here") +} + +// GetItem retrieves a PAM information item. This a pointer directory to the +// data, so it shouldn't be modified. +func (h *Handle) GetItem(i Item) (unsafe.Pointer, error) { + var data unsafe.Pointer + h.status = C.pam_get_item(h.handle, C.int(i), &data) + return data, h.err() +} + +// GetUID retrieves the UID of the corresponding PAM_USER. +func (h *Handle) GetUID() (int64, error) { + var pamUsername *C.char + h.status = C.pam_get_user(h.handle, &pamUsername, nil) + if err := h.err(); err != nil { + return 0, err + } + + pwd := C.getpwnam(pamUsername) + if pwd == nil { + return 0, fmt.Errorf("unknown user %q", C.GoString(pamUsername)) + } + return int64(pwd.pw_uid), nil +} + +func (h *Handle) err() error { + if h.status == C.PAM_SUCCESS { + return nil + } + s := C.GoString(C.pam_strerror(h.handle, C.int(h.status))) + return errors.New(s) +} + +// Transaction represents a wrapped pam_handle_t type created with pam_start +// form an application. +type Transaction Handle + +// Start initializes a pam Transaction. End() should be called after the +// Transaction is no longer needed. +func Start(service, username string) (*Transaction, error) { + cService := C.CString(service) + defer C.free(unsafe.Pointer(cService)) + cUsername := C.CString(username) + defer C.free(unsafe.Pointer(cUsername)) + + t := &Transaction{ + handle: nil, + status: C.PAM_SUCCESS, + } + t.status = C.pam_start(cService, cUsername, &C.conv, &t.handle) + return t, (*Handle)(t).err() +} + +// End finalizes a pam Transaction with pam_end(). +func (t *Transaction) End() { + C.pam_end(t.handle, t.status) +} + +// Authenticate returns a boolean indicating if the user authenticated correctly +// or not. If the authentication check did not complete, an error is returned. +func (t *Transaction) Authenticate(quiet bool) (bool, error) { + var flags C.int = C.PAM_DISALLOW_NULL_AUTHTOK + if quiet { + flags |= C.PAM_SILENT + } + t.status = C.pam_authenticate(t.handle, flags) + if t.status == C.PAM_AUTH_ERR { + return false, nil + } + return true, (*Handle)(t).err() +} -- 2.39.5