/* * 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" "log" "os/user" "unsafe" "github.com/google/fscrypt/security" "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 // OrigUser is the user who invoked the PAM module (usually root) OrigUser *user.User // PamUser is the user who the PAM module is for PamUser *user.User } // NewHandle creates a Handle from a raw pointer. func NewHandle(pamh unsafe.Pointer) (*Handle, error) { var err error h := &Handle{ handle: (*C.pam_handle_t)(pamh), status: C.PAM_SUCCESS, } var pamUsername *C.char h.status = C.pam_get_user(h.handle, &pamUsername, nil) if err := h.err(); err != nil { return nil, err } if h.PamUser, err = user.Lookup(C.GoString(pamUsername)); err != nil { return nil, err } if h.OrigUser, err = util.EffectiveUser(); err != nil { return nil, err } return h, nil } 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() } // ClearData remotes the PAM data with the specified name. func (h *Handle) ClearData(name string) error { return h.setData(name, unsafe.Pointer(C.CString("")), C.CleanupFunc(C.freeData)) } // SetSecret sets a copy of the C string secret into the PAM data with the // specified name. This copy will be held in locked memory until this PAM data // is cleared. func (h *Handle) SetSecret(name string, secret unsafe.Pointer) error { return h.setData(name, C.copyIntoSecret(secret), C.CleanupFunc(C.freeSecret)) } // GetSecret returns a pointer to the C string PAM data with the specified name. // This a pointer directory to the data, so it shouldn't be modified. It should // have been previously set with SetSecret(). func (h *Handle) GetSecret(name string) (unsafe.Pointer, error) { return h.getData(name) } // SetString sets a string value for the PAM data with the specified name. func (h *Handle) SetString(name string, s string) error { return h.setData(name, unsafe.Pointer(C.CString(s)), C.CleanupFunc(C.freeData)) } // GetString gets a string value for the PAM data with the specified name. It // should have been previously set with SetString(). 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 } // 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() } // StartAsPamUser sets the effective privileges to that of the PAM user, and // configures the PAM user's keyrings to be properly linked. func (h *Handle) StartAsPamUser() error { if _, err := security.UserKeyringID(h.PamUser, true); err != nil { log.Printf("Setting up keyrings in PAM: %v", err) } return security.SetThreadPrivileges(h.PamUser) } // StopAsPamUser restores the original privileges that were running the // PAM module (this is usually root). As this error is often ignored in a defer // statement, any error is also logged. func (h *Handle) StopAsPamUser() error { err := security.SetThreadPrivileges(h.OrigUser) if err != nil { log.Print(err) } return err } 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.goConv, &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() }