Skip to content

Commit

Permalink
Merge pull request #16 from neithernut/make-keyring-settable
Browse files Browse the repository at this point in the history
Make keyring selectable through arguments
  • Loading branch information
neithernut authored Apr 28, 2017
2 parents 6db3c28 + 28c6706 commit a7bf1b8
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 27 deletions.
36 changes: 24 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
pam_e4crypt
===========
# pam_e4crypt

This is a PAM module for unlocking transparently encrypted directories on ext4.

Expand All @@ -15,8 +14,7 @@ to directories for which she previously set a "policy" matching her passphrase
Note that the only encryption mode currently supported is aes256-xts.


Using this module
-----------------
## Using this module

This module should be invoked late during the authentication phase as well as
early during the session phase, after an invocation of the `pam_keyinit` module.
Expand Down Expand Up @@ -50,6 +48,9 @@ As this module is considered experimental, users also may want to specify
`required` for the session phase may break some setups. Hence, new users are
encouraged making the module `optional` for that phase, at least initially.


### Salt

User can have a specific salt stored in `$HOME/.ext4_encryption_salt`.
You can generate this salt with one of the following commands :

Expand All @@ -58,8 +59,22 @@ You can generate this salt with one of the following commands :
``` echo -n s:`head -c 16 /dev/urandom | xxd -p` > ~/.ext4_encryption_salt ```


Dependencies
------------
### Keyring

By default, keys are added to the session keyring. Using the `keyring` argument
in the PAM config, it is possible to specify an alternative keyring to which the
keys should be added. Use like:

```
session required pam_e4crypt.so keyring=<desc>
```

As `<desc>`, one may specify either a keyring's description or one of the
"special values" understood by `keyctl` (1.5), e.g. `@u` for the user specific
keyring.


## Dependencies

At runtime, the module requires:
* Linux-Kernel>=4.1
Expand All @@ -74,8 +89,7 @@ required in addition to:
* A C-compiler (e.g. gcc or clang)


Installation
------------
## Installation

The module is built using CMake. Run

Expand Down Expand Up @@ -109,8 +123,7 @@ cmake -DCMAKE_INSTALL_LIBDIR=<lib-base-path> <path-to-source>
to prepare the module for installation to `<lib-base-path>/security/`.


About passwords, mounts and policies
------------------------------------
## About passwords, mounts and policies

"policies" are actually keys generated from a passphrase and a salt. This PAM
module will generate keys from the user's passphrase and add them as policies,
Expand All @@ -126,8 +139,7 @@ This module could, in theory, print or log some information about that, but
currently, it doesn't. This may, however, change in the future.


Licensing
---------
## Licensing

The module is currently licensed under the GPLv2. Have a look at the LICENSE
file for more information. Also, you might want to consult a lawyer if you
Expand Down
146 changes: 131 additions & 15 deletions pam_e4crypt.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,40 @@ static const size_t hexchars_size = 16;



// utility functions


/**
* Retrieve the value of an argument
*
* @returns the value of the option or NULL if the argument doesn't match the
* name supplied
*/
static
char const*
get_modarg_value(
char const* modarg_name, ///< name of the argument
char const* modarg ///< the argument
) {
// match the name
const size_t name_length = strlen(modarg_name);
if (strncmp(modarg, modarg_name, name_length) != 0)
return NULL;

// an option either has a value concanated to the name via `=` or it is
// empty (e.g. the argument only contains the name)
if (modarg[name_length] != '=') {
if (modarg[name_length] == '\0')
return "";
}

// whatever comes after the `=` is a value
return modarg + name_length + 1;
}




/**
* Encryption key list
*
Expand Down Expand Up @@ -463,6 +497,76 @@ read_salt_data(
}




// keyring retrieval


/**
* Named key
*
* Represenation of a named key of some sort
*/
struct named_key {
char const* name;
key_serial_t id;
};


/**
* Special keys
*
* List of special named keys as understood by keyctl-1.5.10.
*/
static
struct named_key const special_keys[] = {
{"@t" , KEY_SPEC_THREAD_KEYRING },
{"@p" , KEY_SPEC_PROCESS_KEYRING },
{"@s" , KEY_SPEC_SESSION_KEYRING },
{"@u" , KEY_SPEC_USER_KEYRING },
{"@us", KEY_SPEC_USER_SESSION_KEYRING },
{"@g" , KEY_SPEC_GROUP_KEYRING },
{"@a" , KEY_SPEC_REQKEY_AUTH_KEY }
};


/**
* Find a keyring matching a spec
*
* @returns the non-zero keyring id found or `0`, if no keyring was found
*
* This function returns the key specified by the string supplied. First, it is
* looked up in a list of special keys. If the key is not one of the special
* keys, a keyring is searched using `request_key()`. If no keyring could be
* found, `0` is returned and `errno` set.
*/
static
key_serial_t
parse_keyring(
char const* keyring ///< specification of the keyring
) {
// first, look up in the list of special keys
struct named_key const* curr;
curr = special_keys + sizeof(special_keys)/sizeof(special_keys[0]);
while (curr-- > special_keys)
if (strcmp(curr->name, keyring) == 0)
return curr->id;

// look up using the syscall
key_serial_t retval = request_key("keyring", keyring, NULL, 0);

// Make sure we return `0` instead of `-1` if no key was found. If we used
// negative values for errors, we would potentially mask special key ids.
if (retval > 0)
return retval;

// errno should already be set by `request_key` at this point.
return 0;
}




// PAM authentication module implementations


Expand Down Expand Up @@ -572,6 +676,28 @@ pam_sm_open_session(
const char** argv ///< arguments passed to the module
) {
int retval;
key_serial_t keyring = KEY_SPEC_SESSION_KEYRING;

// parse arguments passed to the module on the session line
for (int i = 0; i < argc; ++i) {
char const* option;

if (option = get_modarg_value("keyring", argv[i])) {
// A keyring option may have been passed. If so, we try to retrieve
// the key.
keyring = parse_keyring(option);
if (keyring != 0)
continue;

pam_log(LOG_ERR,
"Could not retrieve keyring '%s': %s",
option,
strerror(errno));
return PAM_SESSION_ERR;
}

pam_log(LOG_WARNING, "Unknown option for open_session: %s", argv[i]);
}

// get the keys we are about to insert
struct key_list* keys = NULL;
Expand All @@ -598,8 +724,6 @@ pam_sm_open_session(
uid_t old_uid = getuid();
uid_t old_gid = getgid();

key_serial_t session_keyring = 0;

if ((old_gid != pw->pw_gid) && (retval = setregid(pw->pw_gid, -1)) < 0) {
pam_log(LOG_ERR, "Could not set GID: %s", strerror(errno));
return PAM_SESSION_ERR;
Expand Down Expand Up @@ -630,35 +754,27 @@ pam_sm_open_session(
key_ref_str, pw->pw_uid, pw->pw_gid);

key_serial_t key = add_key(EXT2FS_KEY_TYPE_LOGON, key_ref_str,
ext4_key, sizeof(*ext4_key), KEY_SPEC_SESSION_KEYRING);
ext4_key, sizeof(*ext4_key), keyring);
if (key < 0) {
pam_log(LOG_ERR, "Could not add key: %s", strerror(errno));
continue;
}
}

if ((old_uid != pw->pw_uid) && (retval = setfsuid(old_uid) < 0)) {
if ((old_uid != pw->pw_uid) && (retval = setfsuid(old_uid) < 0))
pam_log(LOG_ERR, "Could not set GID: %s", strerror(errno));
session_keyring = 0;
}

reset_fsgid:
if ((old_gid != pw->pw_gid) && (retval = setfsgid(old_gid)) < 0) {
if ((old_gid != pw->pw_gid) && (retval = setfsgid(old_gid)) < 0)
pam_log(LOG_ERR, "Could not set UID: %s", strerror(errno));
session_keyring = 0;
}

reset_uid:
if ((old_uid != pw->pw_uid) && (retval = setreuid(old_uid, -1) < 0)) {
if ((old_uid != pw->pw_uid) && (retval = setreuid(old_uid, -1) < 0))
pam_log(LOG_ERR, "Could not set GID: %s", strerror(errno));
session_keyring = 0;
}

reset_gid:
if ((old_gid != pw->pw_gid) && (retval = setregid(old_gid, -1)) < 0) {
if ((old_gid != pw->pw_gid) && (retval = setregid(old_gid, -1)) < 0)
pam_log(LOG_ERR, "Could not set UID: %s", strerror(errno));
session_keyring = 0;
}

return retval;
}
Expand Down

0 comments on commit a7bf1b8

Please sign in to comment.