Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fscrypt PAM module #25

Merged
merged 10 commits into from Aug 24, 2017
4 changes: 3 additions & 1 deletion .gitignore
@@ -1,2 +1,4 @@
/fscrypt
fscrypt
fscrypt.*
fscrypt_image
pam_fscrypt.so
17 changes: 14 additions & 3 deletions CONTRIBUTING.md
Expand Up @@ -15,6 +15,16 @@ You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.

## Reporting an Issue

Any bugs or problems found in fscrypt should be reported though the
[Github Issue Tracker](https://github.com/google/fscrypt/issues/new). When
reporting an issue, be sure to give as much information about the problem as
possible. If reporting an issue around the fscrypt command-line tool, post the
relevant output from fscrypt, running with the `--verbose` flag. For the
pam_fscrypt module, use the `debug` option with the module and post the relevant
parts of the syslog (usually at `/var/log/syslog`).

## Code reviews

All submissions, including submissions by project members, require review. We
Expand All @@ -30,9 +40,10 @@ these commands when writing your code.

### Building and Testing

As mentioned in `README.md`, running `make` will build the fscrypt executable.
Running `make go` will build each package and run the tests, but just running
`make go` with nothing else will skip the integration tests.
As mentioned in `README.md`, running `make` will build the fscrypt executable
and the PAM module `pam_fscrypt.so`. Running `make go` will build each package
and run the tests, but just running `make go` with nothing else will skip the
integration tests.

To run the integration tests, you will need a filesystem that supports
encryption. If you already have some empty filesystem at `/foo/bar`, just run:
Expand Down
34 changes: 26 additions & 8 deletions Makefile
Expand Up @@ -16,11 +16,16 @@
# the License.

NAME = fscrypt
PAM_NAME = pam_$(NAME)
PAM_MODULE = $(PAM_NAME).so

INSTALL = install
DESTDIR = /usr/local/bin
INSTALL ?= install
DESTDIR ?= /usr/local/bin
PAM_MODULE_DIR ?= /lib/security
PAM_CONFIG_DIR ?= /usr/share/pam-configs

CMD_PKG = github.com/google/$(NAME)/cmd/$(NAME)
PAM_PKG = github.com/google/$(NAME)/$(PAM_NAME)

SRC_FILES = $(shell find . -type f -name '*.go' -o -name "*.h" -o -name "*.c")
GO_FILES = $(shell find . -type f -name '*.go' -not -path "./vendor/*")
Expand Down Expand Up @@ -81,15 +86,20 @@ override GO_LINK_FLAGS += $(VERSION_FLAG) $(DATE_FLAG) -extldflags "$(LDFLAGS)"
override GO_FLAGS += --ldflags '$(GO_LINK_FLAGS)'

.PHONY: default all
default: $(NAME)

default: $(NAME) $(PAM_MODULE)
all: update format lint default test

$(NAME): $(SRC_FILES)
go build $(GO_FLAGS) -o $(NAME) $(CMD_PKG)

$(PAM_MODULE): $(SRC_FILES)
go build -buildmode=c-shared $(GO_FLAGS) -o $(PAM_MODULE) $(PAM_PKG)
rm -f $(PAM_NAME).h

.PHONY: clean
clean:
rm -rf $(NAME) $(IMAGE)
rm -f $(NAME) $(PAM_MODULE) $(IMAGE)

# Make sure go files build and tests pass.
.PHONY: test
Expand Down Expand Up @@ -129,14 +139,22 @@ lint:
@golint $(GO_PKGS) | grep -v "pb.go" | ./input_fail.py
@megacheck -unused.exported $(GO_PKGS)

.PHONY: install
install: $(NAME)
###### Installation commands #####
.PHONY: install_bin install_pam install uninstall
install_bin: $(NAME)
$(INSTALL) -d $(DESTDIR)
$(INSTALL) $(NAME) $(DESTDIR)

.PHONY: uninstall
install_pam: $(PAM_MODULE)
$(INSTALL) -d $(PAM_MODULE_DIR)
$(INSTALL) $(PAM_MODULE) $(PAM_MODULE_DIR)
$(INSTALL) -d $(PAM_CONFIG_DIR)
$(INSTALL) $(PAM_NAME)/config $(PAM_CONFIG_DIR)/$(NAME)

install: install_bin install_pam

uninstall:
rm -rf $(DESTDIR)/$(NAME)
rm -f $(DESTDIR)/$(NAME) $(PAM_MODULE_DIR)/$(PAM_MODULE) $(PAM_CONFIG_DIR)/$(NAME)

# Install the go tools used for checking/generating the code
.PHONY: go-tools
Expand Down
80 changes: 67 additions & 13 deletions README.md
Expand Up @@ -99,7 +99,6 @@ The following functionality is planned:
* `fscrypt backup` - Manages backups of the fscrypt metadata
* `fscrypt recovery` - Manages recovery keys for directories
* `fscrypt cleanup` - Scans filesystem for unused policies/protectors
* A PAM module to support login passphrase changes (see below)

See the example usage section below or run `fscrypt COMMAND --help` for more
information about each of the commands.
Expand All @@ -109,7 +108,7 @@ information about each of the commands.
fscrypt has the following build dependencies:
* [Go](https://golang.org/doc/install)
* A C compiler (`gcc` or `clang`)
* `make`
* `make`
* The [Argon2 Passphrase Hash](https://github.com/P-H-C/phc-winner-argon2)
library, which can be
[directly installed on Artful Ubuntu](https://packages.ubuntu.com/artful/libargon2-0-dev),
Expand All @@ -131,7 +130,7 @@ Once all the dependencies are installed, you can get the repository by running:
go get -d github.com/google/fscrypt/...
```
and then you can run `make` in `$GOPATH/github.com/google/fscrypt` to build the
executable in that directory. Running `sudo make install` installs the binary to
executable and PAM moudle in that directory. Running `sudo make install` installs the binary to
`/usr/local/bin`.

See the `Makefile` for instructions on how to customize the build. This includes
Expand All @@ -155,6 +154,51 @@ fscrypt has the following runtime dependencies:

The dynamic libraries are not needed if you built a static executable.

### Setting up the PAM module

Note that to make use of the installed PAM module, your
[PAM configuration files](http://www.linux-pam.org/Linux-PAM-html/sag-configuration.html)
in `/etc/pam.d` must be modified to add fscrypt.

#### Automatic setup on Ubuntu

fscrypt automatically installs the
[PAM config file](https://wiki.ubuntu.com/PAMConfigFrameworkSpec)
`pam_fscrypt/config` to `/usr/share/pam-configs/fscrypt`. This file contains
reasonable defaults for the PAM module. To automatically apply these changes,
run `sudo pam-auth-update` and follow the on-screen instructions.

#### Manual setup

The fscrypt PAM module implements the Auth, Session, and Password
[types](http://www.linux-pam.org/Linux-PAM-html/sag-configuration-file.html).

The Password functionality of `pam_fscrypt.so` is used to automatically rewrap
a user's login protector when their unix passphrase changes. An easy way to get
the working is to add the line:
```
password optional pam_fscrypt.so
```
after `pam_unix.so` in `/etc/pam.d/common-password` or similar.

The Auth and Session functionality of `pam_fscrypt.so` are used to automatically
unlock directories when logging in as a user. An easy way to get this working is
to add the line:
```
auth optional pam_fscrypt.so
```
after `pam_unix.so` in `/etc/pam.d/common-password` or similar, and to add the
line:
```
session optional pam_fscrypt.so drop_caches lock_policies
```
after `pam_unix.so` in `/etc/pam.d/common-session` or similar. The
`lock_policies` option locks the directories protected with the user's login
passphrase when the last session ends. The `drop_caches` option tells fscrypt to
clear the filesystem caches when the last session closes, ensuring all the
locked data is inaccessible. All the types also support the `debug` option which
prints additional debug information to the syslog.

## Note about stability

fscrypt follows [semantic versioning](http://semver.org). As such, all versions
Expand Down Expand Up @@ -502,23 +546,33 @@ file for more information about singing the CLA and submitting a pull request.
## Troubleshooting

In general, if you are encountering issues with fscrypt,
[open an issue](https://github.com/google/fscrypt/issues/new). We will try our
best to help.
[open an issue](https://github.com/google/fscrypt/issues/new), following the
guidelines in `CONTRIBUTING.md`. We will try our best to help.

#### I changed my login passphrase, now all my directories are inaccessible

We do not currently support the changing of the login passphrase. This will
change when the appropriate module is completed. Until then, you can fix it by
first finding the necessary protector (with `fscrypt status PATH`) and then
running:
The PAM module provided by fscrypt (`pam_fscrypt.so`) should automatically
detect changes to a user's login passphrase so that they can still access their
encrypted directories. However, sometimes the login passphrase can become
desynchronized from a user's login protector. This usually happens when the PAM
passphrase is managed by an external system, if the PAM module is not installed,
or if the PAM module is not properly configured.

To fix your login protector, you first should find the appropriate protector ID
by running `fscrypt status "/"`. Then, change the passphrase for this protector
by running:
```
fscrypt metadata change-passphrase --protector=MOUNTPOINT:ID
fscrypt metadata change-passphrase --protector=/:ID
```

#### I can still see files or filenames after running `fscrypt purge MOUNTPOINT`
#### Directories using my login passphrase are not automatically unlocking.

Either the PAM module is not installed correctly, or your login passphrase
changed and things got out of sync. Another reason that these directories might
not unlock is if your session starts without password authentication. The most
common case of this is public-key ssh login.

You need to unmount `MOUNTPOINT` to clear the necessary caches. See
`fscrypt purge --help` for more information
To trigger a password authentication event, run `su $(whoami) -c exit`.

#### Getting "encryption not enabled" on an ext4 filesystem.

Expand Down
8 changes: 7 additions & 1 deletion actions/policy.go
Expand Up @@ -278,13 +278,19 @@ func (policy *Policy) Lock() error {
return err
}

// UsesProtector returns if the policy is protected with the protector
func (policy *Policy) UsesProtector(protector *Protector) bool {
_, ok := policy.findWrappedKeyIndex(protector.Descriptor())
return ok
}

// AddProtector updates the data that is wrapping the Policy Key so that the
// provided Protector is now protecting the specified Policy. If an error is
// returned, no data has been changed. If the policy and protector are on
// different filesystems, a link will be created between them. The policy and
// protector must both be unlocked.
func (policy *Policy) AddProtector(protector *Protector) error {
if _, ok := policy.findWrappedKeyIndex(protector.Descriptor()); ok {
if policy.UsesProtector(protector) {
return ErrAlreadyProtected
}
if policy.key == nil || protector.key == nil {
Expand Down
4 changes: 2 additions & 2 deletions cmd/fscrypt/commands.go
Expand Up @@ -33,7 +33,7 @@ import (
"github.com/google/fscrypt/actions"
"github.com/google/fscrypt/filesystem"
"github.com/google/fscrypt/metadata"
"github.com/google/fscrypt/util"
"github.com/google/fscrypt/security"
)

// Setup is a command which can to global or per-filesystem initialization.
Expand Down Expand Up @@ -371,7 +371,7 @@ func purgeAction(c *cli.Context) error {
fmt.Fprintf(c.App.Writer, "Policies purged for %q.\n", ctx.Mount.Path)

if dropCachesFlag.Value {
if err = util.DropInodeCache(); err != nil {
if err = security.DropInodeCache(); err != nil {
return newExitError(c, err)
}
fmt.Fprintf(c.App.Writer, "Global inode cache cleared.\n")
Expand Down
1 change: 0 additions & 1 deletion crypto/key.go
Expand Up @@ -175,7 +175,6 @@ func (key *Key) resize(requestedSize int) (*Key, error) {
// string allocated by C. Note that this method is unsafe as this C copy has no
// locking or wiping functionality. The key shouldn't contain any `\0` bytes.
func (key *Key) UnsafeToCString() unsafe.Pointer {
// Memory for the key must be moved into a C string allocated by C.
size := C.size_t(key.Len())
data := C.calloc(size+1, 1)
C.memcpy(data, util.Ptr(key.data), size)
Expand Down
4 changes: 2 additions & 2 deletions pam/login.go
Expand Up @@ -38,7 +38,7 @@ import (

// Pam error values
var (
ErrPAMPassphrase = errors.New("incorrect login passphrase")
ErrPassphrase = errors.New("incorrect login passphrase")
)

// Global state is needed for the PAM callback, so we guard this function with a
Expand Down Expand Up @@ -107,7 +107,7 @@ func IsUserLoginToken(username string, token *crypto.Key, quiet bool) error {
}

if !authenticated {
return ErrPAMPassphrase
return ErrPassphrase
}
return nil
}