Skip to content

Commit 255bb68

Browse files
committed
Add chauthtok hook
1 parent da563f7 commit 255bb68

File tree

1 file changed

+262
-0
lines changed

1 file changed

+262
-0
lines changed

pam_e4crypt.c

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212
// this is a PAM module implementing authentication (kinda) and session setup
1313
#define PAM_SM_AUTH
1414
#define PAM_SM_SESSION
15+
#define PAM_SM_PASSWORD
1516

1617
// std and system includes
1718
#include <fcntl.h>
1819
#include <sys/fsuid.h>
1920
#include <sys/ioctl.h>
21+
#include <sys/stat.h>
22+
#include <dirent.h>
2023
#include <unistd.h>
2124
#include <syslog.h>
2225
#include <limits.h>
@@ -41,6 +44,7 @@
4144
#define EXT2FS_KEY_DESC_PREFIX_SIZE 5
4245
#define SHA512_LENGTH 64
4346
#define PAM_E4CRYPT_KEY_DATA "pam_e4crypt_key_data"
47+
#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
4448

4549

4650
/**
@@ -342,6 +346,77 @@ key_get_key_ref(
342346
memset(key_ref2, 0, sizeof(key_ref2));
343347
}
344348

349+
static
350+
long
351+
set_policy(
352+
int flags,
353+
char* path,
354+
struct ext4_encryption_key* key
355+
) {
356+
struct ext4_encryption_policy policy;
357+
358+
policy.version = 0;
359+
policy.flags = 0; // TODO: Configure this? (What does it even do?)
360+
policy.contents_encryption_mode =
361+
EXT4_ENCRYPTION_MODE_AES_256_XTS;
362+
policy.filenames_encryption_mode =
363+
EXT4_ENCRYPTION_MODE_AES_256_CTS;
364+
365+
key_get_key_ref(key, &policy.master_key_descriptor[0]);
366+
367+
char key_ref_str[EXT4_KEY_REF_STR_BUF_SIZE] = {0};
368+
for (int i = 0; i < EXT4_KEY_DESCRIPTOR_SIZE; ++i) {
369+
sprintf(&key_ref_str[i * 2], "%02x", (unsigned char) policy.master_key_descriptor[i]);
370+
}
371+
key_ref_str[EXT4_KEY_REF_STR_BUF_SIZE - 1] = 0;
372+
373+
pam_log(LOG_NOTICE, "Setting policy for '%s' to '%s'", path, key_ref_str);
374+
375+
int fd = open(path, O_DIRECTORY);
376+
if (fd == -1) {
377+
pam_log(LOG_WARNING, "Couldn't open '%s'", path);
378+
return -1;
379+
}
380+
381+
long ret = ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &policy);
382+
close(fd);
383+
return ret;
384+
}
385+
386+
static
387+
int
388+
rmrf(
389+
int flags,
390+
char* path
391+
) {
392+
int ret = unlink(path);
393+
394+
if (ret == -1 && errno == EISDIR) {
395+
struct dirent **namelist;
396+
int n = scandir(path, &namelist, NULL, alphasort);
397+
if (n < 0) {
398+
pam_log(LOG_ERR, "Couldn't scan directory '%s' for removal", path);
399+
} else {
400+
char subpath[PATH_MAX] = {0};
401+
while (n--) {
402+
if (strcmp(namelist[n]->d_name, "..") == 0 || strcmp(namelist[n]->d_name, ".") == 0) {
403+
free(namelist[n]);
404+
continue;
405+
}
406+
407+
snprintf(subpath, PATH_MAX, "%s/%s", path, namelist[n]->d_name);
408+
free(namelist[n]);
409+
subpath[PATH_MAX - 1] = 0;
410+
rmrf(flags, subpath);
411+
}
412+
free(namelist);
413+
}
414+
415+
ret = rmdir(path);
416+
}
417+
418+
return ret;
419+
}
345420

346421
/**
347422
* Add a key to keyring
@@ -706,3 +781,190 @@ pam_sm_close_session(
706781
}
707782

708783

784+
/**
785+
* Update the wrapped salt/passphrase for this
786+
*/
787+
PAM_EXTERN
788+
int
789+
pam_sm_chauthtok(
790+
pam_handle_t * pamh,
791+
int flags,
792+
int argc,
793+
const char **argv
794+
) {
795+
char* auth_token = NULL;
796+
char* old_auth_token = NULL;
797+
798+
int retval = pam_get_item(pamh, PAM_AUTHTOK, (const void**) &auth_token);
799+
if ((retval != PAM_SUCCESS) || !auth_token) {
800+
pam_log(LOG_ERR, "Failed to get auth token!");
801+
return PAM_AUTHTOK_ERR;
802+
}
803+
804+
retval = pam_get_item(pamh, PAM_OLDAUTHTOK, (const void**) &old_auth_token);
805+
if ((retval != PAM_SUCCESS) || !auth_token) {
806+
pam_log(LOG_ERR, "Failed to get old auth token!");
807+
return PAM_AUTHTOK_RECOVERY_ERR;
808+
}
809+
810+
const char *username;
811+
retval = pam_get_item(pamh, PAM_USER, (void*) &username);
812+
if (retval != PAM_SUCCESS)
813+
return retval;
814+
struct passwd const* pw = pam_modutil_getpwnam(pamh, username);
815+
if (!pw) {
816+
pam_log(LOG_ERR, "error looking up user");
817+
return PAM_USER_UNKNOWN;
818+
}
819+
char saltpath[PATH_MAX];
820+
char wrappath[PATH_MAX] = {0};
821+
snprintf(saltpath, PATH_MAX, "%s/%s", pw->pw_dir, ".ext4_encryption_salt");
822+
823+
for (int i = 0; i < argc; ++i) {
824+
char const* option;
825+
826+
if (option = get_modarg_value("saltpath", argv[i])) {
827+
// If a custom saltpath has been passed, use it instead
828+
snprintf(saltpath, PATH_MAX, "%s/%s", option, pw->pw_name);
829+
continue;
830+
}
831+
832+
if (option = get_modarg_value("wrappath", argv[i])) {
833+
// If a custom wrappath has been passed, we'll use that
834+
snprintf(wrappath, PATH_MAX, "%s/%s", option, pw->pw_name);
835+
continue;
836+
}
837+
838+
pam_log(LOG_WARNING, "Unknown option for authenticate: %s", argv[i]);
839+
}
840+
841+
if (wrappath[0] == 0) {
842+
pam_log(LOG_WARNING, "No wrappath supplied!");
843+
return PAM_SUCCESS;
844+
}
845+
846+
struct ext4_encryption_key oldkey = generate_key(flags, saltpath, old_auth_token);
847+
add_key_to_keyring(flags, &oldkey, KEY_SPEC_SESSION_KEYRING, pw);
848+
849+
int ret = PAM_SUCCESS;
850+
851+
// Backup the old creds
852+
char wrapbakpath[PATH_MAX];
853+
snprintf(wrapbakpath, PATH_MAX, "%s.bak", wrappath);
854+
rmrf(flags, wrapbakpath);
855+
rename(wrappath, wrapbakpath);
856+
857+
char saltbakpath[PATH_MAX];
858+
snprintf(saltbakpath, PATH_MAX, "%s.bak", saltpath);
859+
rmrf(flags, saltbakpath);
860+
rename(saltpath, saltbakpath);
861+
862+
// Generate file paths
863+
char oldwrapauthpath[PATH_MAX];
864+
char newwrapauthpath[PATH_MAX];
865+
char oldwrapsaltpath[PATH_MAX];
866+
char newwrapsaltpath[PATH_MAX];
867+
868+
snprintf(oldwrapauthpath, PATH_MAX, "%s/auth", wrapbakpath);
869+
snprintf(newwrapauthpath, PATH_MAX, "%s/auth", wrappath);
870+
snprintf(oldwrapsaltpath, PATH_MAX, "%s/salt", wrapbakpath);
871+
snprintf(newwrapsaltpath, PATH_MAX, "%s/salt", wrappath);
872+
873+
// Some buffers
874+
char saltbuf[EXT4_MAX_SALT_SIZE] = {0};
875+
char passbuf[EXT4_MAX_PASSPHRASE_SIZE] = {0};
876+
877+
// Get file descriptors
878+
int randfd = open("/dev/urandom", O_RDONLY);
879+
int saltfd = creat(saltpath, 0740);
880+
881+
if (randfd == -1) {
882+
pam_log(LOG_ERR, "Couldn't open /dev/urandom. You're screwed kid!");
883+
goto error;
884+
}
885+
886+
if (saltfd == -1) {
887+
pam_log(LOG_ERR, "Couldn't open '%s'", saltpath);
888+
goto error;
889+
}
890+
891+
// Generate new salt
892+
read(randfd, saltbuf, EXT4_MAX_SALT_SIZE);
893+
write(saltfd, saltbuf, EXT4_MAX_SALT_SIZE);
894+
895+
// Set the new encrypted folder's policy
896+
mkdir(wrappath, 0740);
897+
struct ext4_encryption_key newkey = generate_key(flags, saltpath, auth_token);
898+
add_key_to_keyring(flags, &newkey, KEY_SPEC_SESSION_KEYRING, pw);
899+
900+
retval = set_policy(flags, wrappath, &newkey);
901+
902+
if (retval == -1) {
903+
pam_log(LOG_ERR, "Couldn't set policy for '%s', %d", wrappath, errno);
904+
goto error;
905+
}
906+
907+
int oldwrapauthfd = open(oldwrapauthpath, O_RDONLY);
908+
int newwrapauthfd = creat(newwrapauthpath, 0740);
909+
int oldwrapsaltfd = open(oldwrapsaltpath, O_RDONLY);
910+
int newwrapsaltfd = creat(newwrapsaltpath, 0740);
911+
912+
if (oldwrapauthfd == -1) {
913+
pam_log(LOG_ERR, "Couldn't open '%s'", oldwrapauthpath);
914+
goto error;
915+
}
916+
917+
if (newwrapauthfd == -1) {
918+
pam_log(LOG_ERR, "Couldn't open '%s'", newwrapauthpath);
919+
goto error;
920+
}
921+
922+
if (oldwrapsaltfd == -1) {
923+
pam_log(LOG_ERR, "Couldn't open '%s'", oldwrapsaltpath);
924+
goto error;
925+
}
926+
927+
if (newwrapsaltfd == -1) {
928+
pam_log(LOG_ERR, "Couldn't open '%s'", newwrapsaltpath);
929+
goto error;
930+
}
931+
932+
// Copy wrapped auth/salt
933+
retval = read(oldwrapauthfd, passbuf, EXT4_MAX_PASSPHRASE_SIZE);
934+
write(newwrapauthfd, passbuf, retval);
935+
retval = read(oldwrapsaltfd, saltbuf, EXT4_MAX_SALT_SIZE);
936+
write(newwrapsaltfd, saltbuf, retval);
937+
938+
goto cleanup;
939+
940+
error:
941+
ret = PAM_AUTHTOK_ERR;
942+
cleanup:
943+
close(randfd);
944+
close(saltfd);
945+
close(oldwrapauthfd);
946+
close(newwrapauthfd);
947+
close(oldwrapsaltfd);
948+
close(newwrapsaltfd);
949+
950+
memset(saltbuf, 0, EXT4_MAX_SALT_SIZE);
951+
memset(passbuf, 0, EXT4_MAX_PASSPHRASE_SIZE);
952+
memset(&oldkey, 0, sizeof(oldkey));
953+
memset(&newkey, 0, sizeof(newkey));
954+
955+
if (ret == PAM_SUCCESS) {
956+
pam_log(LOG_NOTICE, "Removing backups '%s', '%s'", saltbakpath, wrapbakpath);
957+
rmrf(flags, wrapbakpath);
958+
rmrf(flags, saltbakpath);
959+
} else {
960+
pam_log(LOG_WARNING, "Restoring backups");
961+
rmrf(flags, wrappath);
962+
rmrf(flags, saltpath);
963+
rename(wrapbakpath, wrappath);
964+
rename(saltbakpath, saltpath);
965+
}
966+
967+
// TODO: Remove keys from keyring afterwards
968+
969+
return ret;
970+
}

0 commit comments

Comments
 (0)