diff --git a/pam/pam.c b/pam/pam.c index 1479dfaf..1d6aefeb 100644 --- a/pam/pam.c +++ b/pam/pam.c @@ -20,6 +20,7 @@ #include "pam.h" #include +#include #include #include #include @@ -107,3 +108,7 @@ void freeSecret(pam_handle_t* pamh, char* data, int error_status) { munlock(data, size); free(data); } + +void infoMessage(pam_handle_t* pamh, const char* message) { + pam_info(pamh, "%s", message); +} diff --git a/pam/pam.go b/pam/pam.go index 54a60e29..f79e2d41 100644 --- a/pam/pam.go +++ b/pam/pam.go @@ -166,6 +166,13 @@ func (h *Handle) err() error { return errors.New(s) } +// InfoMessage sends a message to the application using pam_info(). +func (h *Handle) InfoMessage(message string) { + cMessage := C.CString(message) + defer C.free(unsafe.Pointer(cMessage)) + C.infoMessage(h.handle, cMessage) +} + // Transaction represents a wrapped pam_handle_t type created with pam_start // form an application. type Transaction Handle diff --git a/pam/pam.h b/pam/pam.h index 54bb1933..3cb609a8 100644 --- a/pam/pam.h +++ b/pam/pam.h @@ -41,4 +41,7 @@ void *copyIntoSecret(void *data); // CleaupFunc that Zeros wipes a C string and unlocks and frees its memory. void freeSecret(pam_handle_t *pamh, char *data, int error_status); +// Sends a message to the application using pam_info(). +void infoMessage(pam_handle_t *pamh, const char *message); + #endif // FSCRYPT_PAM_H diff --git a/pam_fscrypt/pam_fscrypt.go b/pam_fscrypt/pam_fscrypt.go index 2e31af93..963d9a5c 100644 --- a/pam_fscrypt/pam_fscrypt.go +++ b/pam_fscrypt/pam_fscrypt.go @@ -29,6 +29,7 @@ package main */ import "C" import ( + "fmt" "log" "unsafe" @@ -300,6 +301,14 @@ func lockLoginPolicies(handle *pam.Handle) (bool, error) { return needDropCaches, nil } +var noOldAuthTokMessage string = ` +pam_fscrypt: cannot update login protector for '%s' because old passphrase +was not given. This is expected when changing a user's passphrase as root. +You'll need to manually update the protector's passphrase using: + + fscrypt metadata change-passphrase --protector=%s:%s +` + // Chauthtok rewraps the login protector when the passphrase changes. func Chauthtok(handle *pam.Handle, _ map[string]bool) error { if err := handle.StartAsPamUser(); err != nil { @@ -322,6 +331,9 @@ func Chauthtok(handle *pam.Handle, _ map[string]bool) error { } authtok, err := handle.GetItem(pam.Oldauthtok) if err != nil { + handle.InfoMessage(fmt.Sprintf(noOldAuthTokMessage, + handle.PamUser.Username, + protector.Context.Mount.Path, protector.Descriptor())) return nil, errors.Wrap(err, "could not get OLDAUTHTOK") } return crypto.NewKeyFromCString(authtok)