Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Delkeys2 #8

Merged
merged 10 commits into from over 2 years ago

2 participants

David Naylor Gleb Kurtsou
David Naylor

The same as delkeys branch except rebased on master (including commit b8ba825)

DragonSA added some commits
David Naylor DragonSA Enable building of pam_pefs without being in the $SRCDIR tree.
This will allow pam_pefs to be built as a port.
9379002
David Naylor DragonSA Add support for removing keys at the end of a session.
These changes track the number of concurrent sessions and at the end of the
last session removes all active keys.  The code currently assumes that only one
key is used for login but supports additional keys that are added after login.
It does not support the user directory being remounted after login.
da4b67c
David Naylor DragonSA Only check for a valid fsroot once and do it before prompting for pas…
…sword.

This will skip over a password request if there is now way for pefs to validate
the password.
8510192
David Naylor DragonSA Add option to control auto-deleting of keys (opt-in).
The option `delkeys' will remove all active keys once all the user's sessions
have expired.  This assumes only one valid key is used for logins.

That assumption is used to handle corrupted session tracking files.  A better
method would be to compare the tracking file with either the system boot time
or the mount time (later is probably better).
e08dce1
David Naylor DragonSA Correct stale session file detection and add more debug information.
Add a message that describes the session file state and changes made to the
state and add error description when failing to open session tracking file.
2f09993
David Naylor DragonSA Properly handle retrying opening of session tracking file.
Retry exponential back-off open for EWOULDBLOCK and EAGAIN, also correctly
return from the function (only with a valid file descriptor or non repeating
errno).
dd742a6
David Naylor DragonSA Rename pefs_count -> pefs_session_count_incr and fix some bugs / impr…
…ovements.

Bugs fixed:
 - Start try from 1, otherwise an infinite loop
 - Check for failure from mkdir(2)

Improvements:
 - Change pefs_session_count_incr API, make incr and first_mount boolean
 - Eliminate need for dirname(3)
 - Move "delkeys" to a defined value

Requested By:	Gleb Kurtsou
57b12eb
David Naylor DragonSA Eliminate the use of stdio IO functions.
Convert:
 * fopen(3) -> flopen(3)  (eliminates some of the complexity if the session
                           count file does not exist)
 * rewind(3) -> lseek(2)
 * fscanf(3) -> read(2) + strtol(3)
 * fprintf(3) -> snprintf(3) + write(2)

For better security, use lstat(2) (and not lstat(2)).
f7f526d
David Naylor DragonSA Determine stale session counter files based on system boot time.
Previously, a stale session counter file was based on whether the passphrase
had been used previously.  This, however, assumes that only one passphrase is
used, which in the case of pefs, is incorrect.

The most common case causing a corrupt session file is for a reboot that leaves
the session counter file with a value above 0.  To handle this case the
modification time of the session counter file is compared against the boot time.
If it was modified before the last boot, it is forced to have a value of 0.
bc7416f
David Naylor DragonSA If user's home directory is a valid pefs mount then always count the …
…session.

This change allows the pam_pefs session functionality to be used independently
of the pam_pefs authentication functionality.  One particular use case is with
an ssh login, where the user adds keys.  In this case the keys will be cleared
as expected if pam_pefs is included in the ssh session.
5bd2d16
Gleb Kurtsou glk merged commit 02b3bb2 into from
Gleb Kurtsou glk closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 10 unique commits by 1 author.

Dec 17, 2011
David Naylor DragonSA Enable building of pam_pefs without being in the $SRCDIR tree.
This will allow pam_pefs to be built as a port.
9379002
David Naylor DragonSA Add support for removing keys at the end of a session.
These changes track the number of concurrent sessions and at the end of the
last session removes all active keys.  The code currently assumes that only one
key is used for login but supports additional keys that are added after login.
It does not support the user directory being remounted after login.
da4b67c
David Naylor DragonSA Only check for a valid fsroot once and do it before prompting for pas…
…sword.

This will skip over a password request if there is now way for pefs to validate
the password.
8510192
David Naylor DragonSA Add option to control auto-deleting of keys (opt-in).
The option `delkeys' will remove all active keys once all the user's sessions
have expired.  This assumes only one valid key is used for logins.

That assumption is used to handle corrupted session tracking files.  A better
method would be to compare the tracking file with either the system boot time
or the mount time (later is probably better).
e08dce1
David Naylor DragonSA Correct stale session file detection and add more debug information.
Add a message that describes the session file state and changes made to the
state and add error description when failing to open session tracking file.
2f09993
David Naylor DragonSA Properly handle retrying opening of session tracking file.
Retry exponential back-off open for EWOULDBLOCK and EAGAIN, also correctly
return from the function (only with a valid file descriptor or non repeating
errno).
dd742a6
David Naylor DragonSA Rename pefs_count -> pefs_session_count_incr and fix some bugs / impr…
…ovements.

Bugs fixed:
 - Start try from 1, otherwise an infinite loop
 - Check for failure from mkdir(2)

Improvements:
 - Change pefs_session_count_incr API, make incr and first_mount boolean
 - Eliminate need for dirname(3)
 - Move "delkeys" to a defined value

Requested By:	Gleb Kurtsou
57b12eb
David Naylor DragonSA Eliminate the use of stdio IO functions.
Convert:
 * fopen(3) -> flopen(3)  (eliminates some of the complexity if the session
                           count file does not exist)
 * rewind(3) -> lseek(2)
 * fscanf(3) -> read(2) + strtol(3)
 * fprintf(3) -> snprintf(3) + write(2)

For better security, use lstat(2) (and not lstat(2)).
f7f526d
David Naylor DragonSA Determine stale session counter files based on system boot time.
Previously, a stale session counter file was based on whether the passphrase
had been used previously.  This, however, assumes that only one passphrase is
used, which in the case of pefs, is incorrect.

The most common case causing a corrupt session file is for a reboot that leaves
the session counter file with a value above 0.  To handle this case the
modification time of the session counter file is compared against the boot time.
If it was modified before the last boot, it is forced to have a value of 0.
bc7416f
David Naylor DragonSA If user's home directory is a valid pefs mount then always count the …
…session.

This change allows the pam_pefs session functionality to be used independently
of the pam_pefs authentication functionality.  One particular use case is with
an ssh login, where the user adds keys.  In this case the keys will be cleared
as expected if pam_pefs is included in the ssh session.
5bd2d16
This page is out of date. Refresh to see the latest.
2  Makefile
@@ -2,6 +2,6 @@ SUBDIR= sys/modules/pefs \
2 2 sbin/pefs
3 3
4 4 # Should be built from sources tree
5   -# SUBDIR+= lib/libpam/modules/pam_pefs
  5 +SUBDIR+= lib/libpam/modules/pam_pefs
6 6
7 7 .include <bsd.subdir.mk>
4 lib/libpam/modules/Makefile.inc
... ... @@ -0,0 +1,4 @@
  1 +# Include Makefiles from $SRCDIR
  2 +
  3 +.include </usr/src/lib/libpam/modules/Makefile.inc>
  4 +
190 lib/libpam/modules/pam_pefs/pam_pefs.c
@@ -39,13 +39,18 @@ __FBSDID("$FreeBSD$");
39 39 #include <sys/param.h>
40 40 #include <sys/ioctl.h>
41 41 #include <sys/wait.h>
  42 +#include <sys/stat.h>
42 43
  44 +#include <assert.h>
43 45 #include <errno.h>
44 46 #include <fcntl.h>
  47 +#include <libgen.h>
  48 +#include <libutil.h>
45 49 #include <paths.h>
46 50 #include <pwd.h>
47 51 #include <signal.h>
48 52 #include <stdarg.h>
  53 +#include <stdbool.h>
49 54 #include <stdio.h>
50 55 #include <stdlib.h>
51 56 #include <string.h>
@@ -68,6 +73,13 @@ __FBSDID("$FreeBSD$");
68 73 #define PAM_PEFS_OPT_IGNORE_MISSING "ignore_missing"
69 74 #define PAM_PEFS_KEYS "pam_pefs_keys"
70 75
  76 +#define PEFS_OPT_DELKEYS "delkeys"
  77 +
  78 +#define PEFS_SESSION_DIR "/var/run/pefs"
  79 +#define PEFS_SESSION_DIR_MODE 0700
  80 +#define PEFS_SESSION_FILE_MODE 0600
  81 +#define PEFS_SESSION_FILE_FLAGS O_RDWR | O_NONBLOCK | O_CREAT | O_EXLOCK
  82 +
71 83 static int pam_pefs_debug;
72 84
73 85 void
@@ -92,6 +104,95 @@ pefs_warn(const char *fmt, ...)
92 104 }
93 105
94 106 static int
  107 +flopen_retry(const char *filename)
  108 +{
  109 + int fd, try;
  110 +
  111 + for (try = 1; try <= 1024; try *= 2) {
  112 + fd = flopen(filename, PEFS_SESSION_FILE_FLAGS, PEFS_SESSION_FILE_MODE);
  113 + if (fd > 0)
  114 + return fd;
  115 + else if (!(errno == EWOULDBLOCK || errno == EAGAIN))
  116 + return -1;
  117 + // Exponential back-off up to 1 second
  118 + usleep(try * 1000000 / 1024);
  119 + }
  120 + return -1;
  121 +}
  122 +
  123 +static int
  124 +pefs_session_count_incr(const char *user, const bool incr)
  125 +{
  126 + struct stat sb;
  127 + struct timespec tp_uptime, tp_now;
  128 + ssize_t offset;
  129 + int fd, total = 0;
  130 + char filename[MAXPATHLEN + 1], buf[16];
  131 +
  132 + snprintf(filename, MAXPATHLEN + 1, "%s/%s", PEFS_SESSION_DIR, user);
  133 +
  134 + if (lstat(PEFS_SESSION_DIR, &sb) == -1) {
  135 + if (mkdir(PEFS_SESSION_DIR, PEFS_SESSION_DIR_MODE)) {
  136 + pefs_warn("unable to create session directory %s: %s",
  137 + PEFS_SESSION_DIR, strerror(errno));
  138 + return (-1);
  139 + }
  140 + } else if (!S_ISDIR(sb.st_mode)) {
  141 + pefs_warn("%s is not a directory", dirname(filename));
  142 + return (-1);
  143 + }
  144 +
  145 + if ((fd = flopen_retry(filename)) == -1) {
  146 + pefs_warn("unable to create session counter file %s: %s",
  147 + filename, strerror(errno));
  148 + return (-1);
  149 + }
  150 +
  151 + if ((offset = pread(fd, buf, sizeof(buf) - 1, 0)) == -1) {
  152 + pefs_warn("unable to read from the session counter file %s: %s",
  153 + filename, strerror(errno));
  154 + close(fd);
  155 + return (-1);
  156 + }
  157 + buf[offset] = '\0';
  158 + total = (int)strtol(buf, NULL, 10);
  159 +
  160 + /*
  161 + * Determine if this is the first increment of the session file.
  162 + *
  163 + * It is considered the first increment if the session file has not
  164 + * been modified since the last boot time.
  165 + */
  166 + if (incr) {
  167 + bzero(&sb, sizeof(sb));
  168 + fstat(fd, &sb);
  169 + clock_gettime(CLOCK_REALTIME_FAST, &tp_now);
  170 + clock_gettime(CLOCK_UPTIME_FAST, &tp_uptime);
  171 + if (sb.st_mtime <= tp_now.tv_sec - tp_uptime.tv_sec)
  172 + if (total > 0) {
  173 + pefs_warn("stale session counter file: %s",
  174 + filename);
  175 + total = 0;
  176 + }
  177 + }
  178 +
  179 + lseek(fd, 0L, SEEK_SET);
  180 + ftruncate(fd, 0L);
  181 +
  182 + total += incr ? 1 : -1;
  183 + pefs_warn("%s: session count %s%i%i", user, incr > 0 ? "+" : "", total);
  184 + if (total < 0) {
  185 + pefs_warn("corrupted session counter file: %s", filename);
  186 + total = 0;
  187 + }
  188 + offset = snprintf(buf, sizeof(buf), "%i", total);
  189 + pwrite(fd, buf, offset, 0);
  190 + close(fd);
  191 +
  192 + return (total);
  193 +}
  194 +
  195 +static int
95 196 pam_pefs_getfsroot(const char *homedir)
96 197 {
97 198 char fsroot[MAXPATHLEN];
@@ -252,10 +353,12 @@ pam_sm_open_session(pam_handle_t *pamh, int flags __unused,
252 353 struct pefs_keychain *kc;
253 354 struct passwd *pwd;
254 355 const char *user;
255   - int fd, pam_err;
  356 + int fd, pam_err, pam_pefs_delkeys;
  357 +
  358 + pam_pefs_debug = 1;
256 359
257 360 pam_err = pam_get_data(pamh, PAM_PEFS_KEYS, (const void **)&kch);
258   - if (pam_err != PAM_SUCCESS || kch == NULL || TAILQ_EMPTY(kch))
  361 + if (pam_err != PAM_SUCCESS)
259 362 return (PAM_SUCCESS);
260 363
261 364 pam_err = pam_get_user(pamh, &user, NULL);
@@ -268,6 +371,7 @@ pam_sm_open_session(pam_handle_t *pamh, int flags __unused,
268 371 return (PAM_SYSTEM_ERR);
269 372
270 373 pam_pefs_debug = (openpam_get_option(pamh, PAM_OPT_DEBUG) != NULL);
  374 + pam_pefs_delkeys = (openpam_get_option(pamh, PEFS_OPT_DELKEYS) != NULL);
271 375
272 376 /* Switch to user credentials */
273 377 pam_err = openpam_borrow_cred(pamh, pwd);
@@ -277,29 +381,89 @@ pam_sm_open_session(pam_handle_t *pamh, int flags __unused,
277 381 if (pefs_getfsroot(pwd->pw_dir, 0, NULL, 0) != 0)
278 382 return PAM_SYSTEM_ERR;
279 383
280   - fd = open(pwd->pw_dir, O_RDONLY);
281   - TAILQ_FOREACH(kc, kch, kc_entry) {
282   - if (ioctl(fd, PEFS_ADDKEY, &kc->kc_key) == -1) {
283   - pefs_warn("cannot add key: %s: %s", pwd->pw_dir,
284   - strerror(errno));
285   - break;
  384 + if (!(kch == NULL || TAILQ_EMPTY(kch))) {
  385 + fd = open(pwd->pw_dir, O_RDONLY);
  386 + TAILQ_FOREACH(kc, kch, kc_entry) {
  387 + if (ioctl(fd, PEFS_ADDKEY, &kc->kc_key) == -1) {
  388 + pefs_warn("cannot add key: %s: %s", pwd->pw_dir,
  389 + strerror(errno));
  390 + break;
  391 + }
286 392 }
287   - }
288   - close(fd);
  393 + close(fd);
289 394
290   - /* Remove keys from memmory */
291   - pefs_keychain_free(kch);
  395 + /* Remove keys from memory */
  396 + pefs_keychain_free(kch);
  397 + }
292 398
293 399 /* Switch back to arbitrator credentials */
294 400 openpam_restore_cred(pamh);
295 401
  402 + /* Increment login count */
  403 + if (pam_pefs_delkeys)
  404 + pefs_session_count_incr(user, true);
  405 +
296 406 return (PAM_SUCCESS);
297 407 }
298 408
299 409 PAM_EXTERN int
300   -pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused,
  410 +pam_sm_close_session(pam_handle_t *pamh, int flags __unused,
301 411 int argc __unused, const char *argv[] __unused)
302 412 {
  413 + struct pefs_xkey k;
  414 + struct passwd *pwd;
  415 + const char *user;
  416 + int fd, pam_err, pam_pefs_delkeys;
  417 +
  418 + pam_err = pam_get_user(pamh, &user, NULL);
  419 + if (pam_err != PAM_SUCCESS)
  420 + return (pam_err);
  421 +
  422 + pwd = getpwnam(user);
  423 + if (pwd == NULL)
  424 + return (PAM_USER_UNKNOWN);
  425 + if (pwd->pw_dir == NULL)
  426 + return (PAM_SYSTEM_ERR);
  427 +
  428 + pam_pefs_debug = (openpam_get_option(pamh, PAM_OPT_DEBUG) != NULL);
  429 + pam_pefs_delkeys = (openpam_get_option(pamh, PEFS_OPT_DELKEYS) != NULL);
  430 +
  431 + /* Switch to user credentials */
  432 + pam_err = openpam_borrow_cred(pamh, pwd);
  433 + if (pam_err != PAM_SUCCESS)
  434 + return (pam_err);
  435 +
  436 + if (pefs_getfsroot(pwd->pw_dir, 0, NULL, 0) != 0)
  437 + return PAM_SYSTEM_ERR;
  438 +
  439 + /* Switch back to arbitrator credentials */
  440 + openpam_restore_cred(pamh);
  441 +
  442 + /* Decrease login count and remove keys if at zero */
  443 + if (pam_pefs_delkeys && pefs_session_count_incr(user, false) == 0) {
  444 + /* Switch to user credentials */
  445 + pam_err = openpam_borrow_cred(pamh, pwd);
  446 + if (pam_err != PAM_SUCCESS)
  447 + return (pam_err);
  448 +
  449 + fd = open(pwd->pw_dir, O_RDONLY);
  450 +
  451 + bzero(&k, sizeof(k));
  452 + while (1) {
  453 + if (ioctl(fd, PEFS_GETKEY, &k) == -1)
  454 + break;
  455 +
  456 + if (ioctl(fd, PEFS_DELKEY, &k) == -1) {
  457 + pefs_warn("cannot del key: %s: %s", pwd->pw_dir,
  458 + strerror(errno));
  459 + k.pxk_index++;
  460 + }
  461 + }
  462 + close(fd);
  463 +
  464 + /* Switch back to arbitrator credentials */
  465 + openpam_restore_cred(pamh);
  466 + }
303 467
304 468 return (PAM_SUCCESS);
305 469 }

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.