Issue #103: Child process cleanup under PAM session

* Added signal handling in PAM session process. Now SIGHUP signals are
  forward to child process, which is the actual service. Launcher process
  uses this type of signals to terminate service on http connection
  timeout.
This commit is contained in:
KLuka 2015-08-30 22:41:16 +02:00
parent eb2968b1d0
commit b828574899

View file

@ -139,9 +139,6 @@ int execle(const char *, const char *, ...);
extern int pthread_once(pthread_once_t *, void (*)(void))__attribute__((weak)); extern int pthread_once(pthread_once_t *, void (*)(void))__attribute__((weak));
#endif #endif
// From shellinabox/shellinaboxd.c
extern int enableUtmpLogging;
// If PAM support is available, take advantage of it. Otherwise, silently fall // If PAM support is available, take advantage of it. Otherwise, silently fall
// back on legacy operations for session management. // back on legacy operations for session management.
#if defined(HAVE_SECURITY_PAM_APPL_H) && defined(HAVE_DLOPEN) #if defined(HAVE_SECURITY_PAM_APPL_H) && defined(HAVE_DLOPEN)
@ -175,6 +172,13 @@ static int (*x_misc_conv)(int, const struct pam_message **,
static int launcher = -1; static int launcher = -1;
static uid_t restricted; static uid_t restricted;
// From shellinabox/shellinaboxd.c
extern int enableUtmpLogging;
#if defined(HAVE_SECURITY_PAM_APPL_H)
static int pamSessionSighupFlag;
#endif
// MacOS X has a somewhat unusual definition of getgrouplist() which can // MacOS X has a somewhat unusual definition of getgrouplist() which can
// trigger a compile warning. // trigger a compile warning.
#if defined(HAVE_GETGROUPLIST_TAKES_INTS) #if defined(HAVE_GETGROUPLIST_TAKES_INTS)
@ -676,7 +680,7 @@ void destroyUtmp(struct Utmp *utmp) {
UNUSED_RETURN(setresuid(0, 0, 0)); UNUSED_RETURN(setresuid(0, 0, 0));
UNUSED_RETURN(setresgid(0, 0, 0)); UNUSED_RETURN(setresgid(0, 0, 0));
if(enableUtmpLogging) { if (enableUtmpLogging) {
setutxent(); setutxent();
pututxline(&utmp->utmpx); pututxline(&utmp->utmpx);
endutxent(); endutxent();
@ -1495,6 +1499,17 @@ void setWindowSize(int pty, int width, int height) {
} }
} }
#if defined(HAVE_SECURITY_PAM_APPL_H)
static void pamSessionSighupHandler(int sig ATTR_UNUSED,
siginfo_t *info ATTR_UNUSED,
void *unused ATTR_UNUSED) {
UNUSED(sig);
UNUSED(info);
UNUSED(unused);
pamSessionSighupFlag = 1;
}
#endif
static void childProcess(struct Service *service, int width, int height, static void childProcess(struct Service *service, int width, int height,
struct Utmp *utmp, const char *peerName, const char *realIP, struct Utmp *utmp, const char *peerName, const char *realIP,
const char *url) { const char *url) {
@ -1555,7 +1570,7 @@ static void childProcess(struct Service *service, int width, int height,
UNUSED_RETURN(setresuid(0, 0, 0)); UNUSED_RETURN(setresuid(0, 0, 0));
UNUSED_RETURN(setresgid(0, 0, 0)); UNUSED_RETURN(setresgid(0, 0, 0));
#ifdef HAVE_UTMPX_H #ifdef HAVE_UTMPX_H
if(enableUtmpLogging) { if (enableUtmpLogging) {
setutxent(); setutxent();
struct utmpx utmpx = utmp->utmpx; struct utmpx utmpx = utmp->utmpx;
if (service->useLogin || service->authUser) { if (service->useLogin || service->authUser) {
@ -1585,7 +1600,7 @@ static void childProcess(struct Service *service, int width, int height,
#if defined(HAVE_SECURITY_PAM_APPL_H) #if defined(HAVE_SECURITY_PAM_APPL_H)
if (pam && !geteuid()) { if (pam && !geteuid()) {
if (pam_open_session(pam, PAM_SILENT) != PAM_SUCCESS) { if (pam_open_session(pam, PAM_SILENT) != PAM_SUCCESS) {
fprintf(stderr, "Access denied.\n"); fprintf(stderr, "[server] Unable to open PAM session!\n");
_exit(1); _exit(1);
} }
pid_t pid = fork(); pid_t pid = fork();
@ -1595,10 +1610,27 @@ static void childProcess(struct Service *service, int width, int height,
case 0: case 0:
break; break;
default:; default:;
// Finish all pending PAM operations. // This process is used for finishing all pending PAM operations.
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
sa.sa_sigaction = pamSessionSighupHandler;
check(!sigaction(SIGHUP, &sa, NULL));
int status, rc; int status, rc;
check(NOINTR(waitpid(pid, &status, 0)) == pid); while (1) {
rc = pam_close_session(pam, PAM_SILENT); pamSessionSighupFlag = 0;
int val = waitpid(pid, &status, 0);
if (val < 0 && errno == EINTR) {
if (pamSessionSighupFlag) {
// If SIGHUP signal is received it needs to be forwarded to child
// process, which is acctual service process.
kill(pid, SIGHUP);
}
} else {
break;
}
}
rc = pam_close_session(pam, PAM_SILENT);
pam_end(pam, rc | PAM_DATA_SILENT); pam_end(pam, rc | PAM_DATA_SILENT);
_exit(WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status)); _exit(WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status));
} }