From 043184ad65a59162d8c34b9ef9b7e8bc69748c83 Mon Sep 17 00:00:00 2001
From: Evan Broder <broder@mit.edu>
Date: Thu, 25 Mar 2010 15:09:26 -0400
Subject: [PATCH] Reset the SIGCHLD handler while running aklog.
If the SIGCHLD handler isn't set to SIG_DFL and the child returns
before the parent gets around to calling waitpid(), waitpid will
immediately error out with ECHILD.
Some versions of GDM use a custom SIGCHLD handler, which they leave in
place when running the PAM stack, triggering this race condition.
Work around the race by setting the SIGCHLD handler to SIG_DFL before
running aklog, then set it back to the original handler when done.
---
tokens.c | 26 ++++++++++++++++++++------
1 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/tokens.c b/tokens.c
index 7a523fc..6078589 100644
a
|
b
|
pamafs_should_ignore(struct pam_args *args, const struct passwd *pwd) |
102 | 102 | static int |
103 | 103 | pamafs_run_aklog(pam_handle_t *pamh, struct pam_args *args, struct passwd *pwd) |
104 | 104 | { |
105 | | int res, argc, arg, i; |
| 105 | int res, argc, arg, i, ret = PAM_SUCCESS; |
106 | 106 | char **env; |
107 | 107 | const char **argv; |
108 | 108 | pid_t child; |
| 109 | struct sigaction act, oldact; |
109 | 110 | |
110 | 111 | /* Sanity check that we have some program to run. */ |
111 | 112 | if (args->program == NULL) { |
… |
… |
pamafs_run_aklog(pam_handle_t *pamh, struct pam_args *args, struct passwd *pwd) |
135 | 136 | argv[arg] = NULL; |
136 | 137 | |
137 | 138 | /* |
| 139 | * Set the SIGCHLD handler back to SIG_DFL. If the handler is set |
| 140 | * to something else and the child returns before the parent calls |
| 141 | * waitpid, waitpid will error with ECHILD, introducing a race |
| 142 | * condition. GDM has been known to do this. |
| 143 | */ |
| 144 | memset(&act, 0, sizeof(act)); |
| 145 | act.sa_handler = SIG_DFL; |
| 146 | sigaction(SIGCHLD, &act, &oldact); |
| 147 | |
| 148 | /* |
138 | 149 | * Run the program. Be sure to use _exit instead of exit in the |
139 | 150 | * subprocess so that we won't run exit handlers or double-flush stdio |
140 | 151 | * buffers in the child process. |
… |
… |
pamafs_run_aklog(pam_handle_t *pamh, struct pam_args *args, struct passwd *pwd) |
145 | 156 | child = fork(); |
146 | 157 | if (child < 0) { |
147 | 158 | pamafs_error("cannot fork: %s", strerror(errno)); |
148 | | return PAM_SESSION_ERR; |
| 159 | ret = PAM_SESSION_ERR; |
| 160 | goto done; |
149 | 161 | } else if (child == 0) { |
150 | 162 | if (setuid(pwd->pw_uid) < 0) { |
151 | 163 | pamafs_error("cannot setuid to UID %lu: %s", |
… |
… |
pamafs_run_aklog(pam_handle_t *pamh, struct pam_args *args, struct passwd *pwd) |
164 | 176 | } |
165 | 177 | free(argv); |
166 | 178 | pamafs_free_envlist(env); |
167 | | if (waitpid(child, &res, 0) && WIFEXITED(res) && WEXITSTATUS(res) == 0) |
168 | | return PAM_SUCCESS; |
169 | | else |
170 | | return PAM_SESSION_ERR; |
| 179 | if (!waitpid(child, &res, 0) || !WIFEXITED(res) || WEXITSTATUS(res) != 0) |
| 180 | ret = PAM_SESSION_ERR; |
| 181 | |
| 182 | done: |
| 183 | sigaction(SIGCHLD, &oldact, NULL); |
| 184 | return ret; |
171 | 185 | } |
172 | 186 | |
173 | 187 | |