Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F145583765
D54415.id170174.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D54415.id170174.diff
View Options
diff --git a/lib/libpam/pam.d/cron b/lib/libpam/pam.d/cron
--- a/lib/libpam/pam.d/cron
+++ b/lib/libpam/pam.d/cron
@@ -6,3 +6,6 @@
# account
account required pam_nologin.so
account required pam_unix.so
+
+# session
+session required pam_permit.so
diff --git a/usr.sbin/cron/cron/do_command.c b/usr.sbin/cron/cron/do_command.c
--- a/usr.sbin/cron/cron/do_command.c
+++ b/usr.sbin/cron/cron/do_command.c
@@ -76,6 +76,41 @@
Debug(DPROC, ("[%d] main process returning to work\n", getpid()))
}
+#ifdef PAM
+static void
+pam_cleanup(pam_handle_t **pamhp, int *session_opened, int *cred_established,
+ char ***pam_envp, const char *usernm, pid_t pid, int log_errors,
+ int end_status)
+{
+ int pam_err;
+
+ if (*pamhp == NULL)
+ return;
+ if (*session_opened) {
+ pam_err = pam_close_session(*pamhp, PAM_SILENT);
+ if (log_errors && pam_err != PAM_SUCCESS) {
+ log_it(usernm, pid, "SESSION-CLOSE",
+ pam_strerror(*pamhp, pam_err));
+ }
+ *session_opened = 0;
+ }
+ if (*cred_established) {
+ pam_err = pam_setcred(*pamhp, PAM_DELETE_CRED);
+ if (log_errors && pam_err != PAM_SUCCESS) {
+ log_it(usernm, pid, "CRED-DELETE",
+ pam_strerror(*pamhp, pam_err));
+ }
+ *cred_established = 0;
+ }
+ if (*pam_envp != NULL) {
+ openpam_free_envlist(*pam_envp);
+ *pam_envp = NULL;
+ }
+ pam_end(*pamhp, end_status);
+ *pamhp = NULL;
+}
+#endif
+
static void
child_process(entry *e, user *u)
@@ -88,6 +123,14 @@
int bytes = 1;
int status = 0;
const char *homedir = NULL;
+#ifdef PAM
+ pam_handle_t *pamh = NULL;
+ int pam_err = PAM_SUCCESS;
+ int pam_session_opened = 0;
+ int pam_cred_established = 0;
+ /* Keep PAM env list in the middle process for the grandchild to use. */
+ char **pam_envp = NULL;
+#endif
# if defined(LOGIN_CAP)
struct passwd *pwd;
login_cap_t *lc;
@@ -115,8 +158,6 @@
* as any user.
*/
if (strcmp(u->name, SYS_NAME)) { /* not equal */
- pam_handle_t *pamh = NULL;
- int pam_err;
struct pam_conv pamc = {
.conv = openpam_nullconv,
.appdata_ptr = NULL
@@ -139,14 +180,50 @@
exit(ERROR_EXIT);
}
+ pam_err = pam_set_item(pamh, PAM_TTY, "cron");
+ if (pam_err != PAM_SUCCESS) {
+ log_it("CRON", getpid(), "error", "can't set PAM_TTY");
+ pam_cleanup(&pamh, &pam_session_opened,
+ &pam_cred_established, &pam_envp, usernm,
+ getpid(), 0, pam_err);
+ exit(ERROR_EXIT);
+ }
+
pam_err = pam_acct_mgmt(pamh, PAM_SILENT);
/* Expired password shouldn't prevent the job from running. */
if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD) {
log_it(usernm, getpid(), "USER", "account unavailable");
+ pam_cleanup(&pamh, &pam_session_opened,
+ &pam_cred_established, &pam_envp, usernm,
+ getpid(), 0, pam_err);
+ exit(ERROR_EXIT);
+ }
+
+ pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+ if (pam_err != PAM_SUCCESS) {
+ log_it(usernm, getpid(), "CRED",
+ pam_strerror(pamh, pam_err));
+ pam_cleanup(&pamh, &pam_session_opened,
+ &pam_cred_established, &pam_envp, usernm,
+ getpid(), 0, pam_err);
+ exit(ERROR_EXIT);
+ }
+ pam_cred_established = 1;
+
+ /* Establish the session while still root in the middle process. */
+ pam_err = pam_open_session(pamh, PAM_SILENT);
+ if (pam_err != PAM_SUCCESS) {
+ log_it(usernm, getpid(), "SESSION",
+ pam_strerror(pamh, pam_err));
+ pam_cleanup(&pamh, &pam_session_opened,
+ &pam_cred_established, &pam_envp, usernm,
+ getpid(), 0, pam_err);
exit(ERROR_EXIT);
}
+ pam_session_opened = 1;
- pam_end(pamh, pam_err);
+ /* Collect PAM env now; apply only in grandchild before exec. */
+ pam_envp = pam_getenvlist(pamh);
}
#endif
@@ -161,6 +238,13 @@
*/
if (pipe(stdin_pipe) != 0 || pipe(stdout_pipe) != 0) {
log_it("CRON", getpid(), "error", "can't pipe");
+#ifdef PAM
+ if (pamh != NULL && strcmp(u->name, SYS_NAME)) {
+ pam_cleanup(&pamh, &pam_session_opened,
+ &pam_cred_established, &pam_envp, usernm,
+ getpid(), 1, pam_err);
+ }
+#endif
exit(ERROR_EXIT);
}
@@ -207,12 +291,23 @@
switch (jobpid = fork()) {
case -1:
log_it("CRON", getpid(), "error", "can't fork");
+#ifdef PAM
+ if (pamh != NULL && strcmp(u->name, SYS_NAME)) {
+ pam_cleanup(&pamh, &pam_session_opened,
+ &pam_cred_established, &pam_envp, usernm,
+ getpid(), 1, pam_err);
+ }
+#endif
exit(ERROR_EXIT);
/*NOTREACHED*/
case 0:
Debug(DPROC, ("[%d] grandchild process fork()'ed\n",
getpid()))
+#ifdef PAM
+ /* Grandchild runs the user job; PAM handle remains in parent. */
+ pamh = NULL;
+#endif
if (e->uid == ROOT_UID)
Jitter = RootJitter;
if (Jitter != 0) {
@@ -329,8 +424,8 @@
* the homedir given by the pw entry otherwise.
*
* If !LOGIN_CAP, then HOME is always set in e->envp.
- *
- * XXX: probably should also consult PAM.
+ * PAM environment is applied later for the job; we do not
+ * use it for cwd to avoid changing historical behavior.
*/
{
char *new_home = env_get("HOME", e->envp);
@@ -351,6 +446,29 @@
char *shell = env_get("SHELL", e->envp);
char **p;
+#ifdef PAM
+ if (pam_envp != NULL) {
+ char **pp;
+
+ /* Apply PAM-provided env only to the job process. */
+ for (pp = pam_envp; *pp != NULL; pp++) {
+ /*
+ * Hand off each PAM string directly to the
+ * environment; this process must not free
+ * pam_envp after putenv() since the strings
+ * must persist until exec. The parent will
+ * free its copy after fork.
+ */
+ if (putenv(*pp) != 0) {
+ warn("putenv");
+ _exit(ERROR_EXIT);
+ }
+ }
+ /* Free the pointer array; strings stay for exec. */
+ free(pam_envp);
+ pam_envp = NULL;
+ }
+#endif
/* Apply the environment from the entry, overriding
* existing values (this will always set LOGNAME and
* SHELL). putenv should not fail unless malloc does.
@@ -400,6 +518,14 @@
break;
}
+#ifdef PAM
+ if (jobpid > 0 && pam_envp != NULL) {
+ /* Parent doesn't need PAM env list after the fork. */
+ openpam_free_envlist(pam_envp);
+ pam_envp = NULL;
+ }
+#endif
+
/* middle process, child of original cron, parent of process running
* the user's command.
*/
@@ -640,6 +766,14 @@
if (*input_data && stdinjob > 0)
wait_on_child(stdinjob, "grandchild stdinjob");
+
+#ifdef PAM
+ if (pamh != NULL && strcmp(u->name, SYS_NAME)) {
+ /* Close the PAM session after the job finishes. */
+ pam_cleanup(&pamh, &pam_session_opened, &pam_cred_established,
+ &pam_envp, usernm, getpid(), 1, PAM_SUCCESS);
+ }
+#endif
}
static WAIT_T
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Feb 22, 7:51 PM (1 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28942416
Default Alt Text
D54415.id170174.diff (6 KB)
Attached To
Mode
D54415: cron: Implement full PAM session lifecycle for user jobs
Attached
Detach File
Event Timeline
Log In to Comment