|
Packit |
7e982e |
/* handlers.c -- execute handlers specified in handlers configuration file
|
|
Packit |
7e982e |
Copyright (c) 2005 Red Hat, Inc.
|
|
Packit |
7e982e |
Written by Tomas Mraz <tmraz@redhat.com>
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
This program is free software; you can redistribute it and/or modify
|
|
Packit |
7e982e |
it under the terms of the GNU General Public License as published by
|
|
Packit |
7e982e |
the Free Software Foundation; either version 2, or (at your option)
|
|
Packit |
7e982e |
any later version.
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
This program is distributed in the hope that it will be useful,
|
|
Packit |
7e982e |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
7e982e |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
7e982e |
GNU General Public License for more details.
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
You should have received a copy of the GNU General Public License
|
|
Packit |
7e982e |
along with this program; if not, write to the Free Software Foundation,
|
|
Packit |
7e982e |
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
#include "config.h"
|
|
Packit |
7e982e |
#include <errno.h>
|
|
Packit |
7e982e |
#include <stdio.h>
|
|
Packit |
7e982e |
#include <stdlib.h>
|
|
Packit |
7e982e |
#include <string.h>
|
|
Packit |
7e982e |
#include <ctype.h>
|
|
Packit |
7e982e |
#include <unistd.h>
|
|
Packit |
7e982e |
#include <signal.h>
|
|
Packit |
7e982e |
#include <sys/wait.h>
|
|
Packit |
7e982e |
#include <sys/types.h>
|
|
Packit |
7e982e |
#include <pwd.h>
|
|
Packit |
7e982e |
#include <syslog.h>
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
#include "handlers.h"
|
|
Packit |
7e982e |
#include "pam_console.h"
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
enum types { UNKNOWN, LOCK, UNLOCK, CONSOLEDEVS };
|
|
Packit |
7e982e |
enum flags { HF_LOGFAIL, HF_WAIT, HF_SETUID, HF_TTY, HF_USER, HF_PARAM };
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
struct console_handler {
|
|
Packit |
7e982e |
char *executable;
|
|
Packit |
7e982e |
enum types type;
|
|
Packit |
7e982e |
char *flags; /* this is a double zero terminated array
|
|
Packit |
7e982e |
allocated in one blob with executable */
|
|
Packit |
7e982e |
struct console_handler *next;
|
|
Packit |
7e982e |
};
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
static struct console_handler *first_handler;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
static void
|
|
Packit |
7e982e |
console_free_handlers (struct console_handler *handler) {
|
|
Packit |
7e982e |
if (handler != NULL) {
|
|
Packit |
7e982e |
console_free_handlers(handler->next);
|
|
Packit |
7e982e |
free(handler->executable);
|
|
Packit |
7e982e |
free(handler);
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
int
|
|
Packit |
7e982e |
console_parse_handlers (pam_handle_t *pamh, const char *handlers_name) {
|
|
Packit |
7e982e |
FILE *fh;
|
|
Packit |
7e982e |
char linebuf[HANDLERS_MAXLINELEN+1];
|
|
Packit |
7e982e |
int forget;
|
|
Packit |
7e982e |
int skip = 0;
|
|
Packit |
7e982e |
int rv = PAM_SESSION_ERR;
|
|
Packit |
7e982e |
struct console_handler **previous_handler_ptr;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
fh = fopen(handlers_name, "r");
|
|
Packit |
7e982e |
if (fh == NULL) {
|
|
Packit |
7e982e |
_pam_log(pamh, LOG_ERR, FALSE, "cannot open file %s for reading", handlers_name);
|
|
Packit |
7e982e |
return rv;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
previous_handler_ptr = &first_handler;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
while (fgets(linebuf, sizeof(linebuf), fh) != NULL)
|
|
Packit |
7e982e |
{
|
|
Packit |
7e982e |
int len;
|
|
Packit |
7e982e |
char *ptr;
|
|
Packit |
7e982e |
char *tokptr;
|
|
Packit |
7e982e |
char *temp;
|
|
Packit |
7e982e |
char *destptr = NULL; /* needed to silence warning */
|
|
Packit |
7e982e |
struct console_handler *handler;
|
|
Packit |
7e982e |
enum states { EXECUTABLE, TYPE, FLAGS } state;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
len = strlen(linebuf);
|
|
Packit |
7e982e |
if (linebuf[len-1] != '\n') {
|
|
Packit |
7e982e |
_pam_log(pamh, LOG_INFO, FALSE, "line too long or not ending with new line char - will be ignored");
|
|
Packit |
7e982e |
skip = 1;
|
|
Packit |
7e982e |
continue;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
if (skip) {
|
|
Packit |
7e982e |
skip = 0;
|
|
Packit |
7e982e |
continue;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
linebuf[len-1] = '\0';
|
|
Packit |
7e982e |
if ((ptr=strchr(linebuf, '#')) != NULL) {
|
|
Packit |
7e982e |
*ptr = '\0';
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
for (ptr = linebuf; isspace(*ptr); ptr++);
|
|
Packit |
7e982e |
if (*ptr == '\0')
|
|
Packit |
7e982e |
continue;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
/* something on the line */
|
|
Packit |
7e982e |
if ((handler=calloc(sizeof(*handler), 1)) == NULL)
|
|
Packit |
7e982e |
goto fail_exit;
|
|
Packit |
7e982e |
*previous_handler_ptr = handler;
|
|
Packit |
7e982e |
previous_handler_ptr = &handler->next;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
if ((handler->executable=malloc(len-(ptr-linebuf)+1)) == NULL) {
|
|
Packit |
7e982e |
goto fail_exit;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
state = EXECUTABLE;
|
|
Packit |
7e982e |
handler->type = UNKNOWN;
|
|
Packit |
7e982e |
while ((tokptr=strtok_r(ptr, " \t", &temp)) != NULL) {
|
|
Packit |
7e982e |
if (state == EXECUTABLE) {
|
|
Packit |
7e982e |
strcpy(handler->executable, tokptr);
|
|
Packit |
7e982e |
ptr = NULL;
|
|
Packit |
7e982e |
handler->flags = destptr = handler->executable + strlen(handler->executable) + 1;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
else if (state == TYPE) {
|
|
Packit |
7e982e |
if (strcmp(tokptr, "lock") == 0) {
|
|
Packit |
7e982e |
handler->type = LOCK;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
else if (strcmp(tokptr, "unlock") == 0) {
|
|
Packit |
7e982e |
handler->type = UNLOCK;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
else if (strcmp(tokptr, "consoledevs") == 0) {
|
|
Packit |
7e982e |
handler->type = CONSOLEDEVS;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
if (state == FLAGS) {
|
|
Packit |
7e982e |
strcpy(destptr, tokptr);
|
|
Packit |
7e982e |
destptr += strlen(destptr) + 1;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
else {
|
|
Packit |
7e982e |
state++;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
*destptr = '\0';
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
forget = fclose(fh);
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
return PAM_SUCCESS;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
fail_exit:
|
|
Packit |
7e982e |
console_free_handlers(first_handler);
|
|
Packit |
7e982e |
return rv;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
static enum flags testflag(const char *flag) {
|
|
Packit |
7e982e |
if (strcmp(flag, "logfail") == 0) {
|
|
Packit |
7e982e |
return HF_LOGFAIL;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
if (strcmp(flag, "wait") == 0) {
|
|
Packit |
7e982e |
return HF_WAIT;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
if (strcmp(flag, "setuid") == 0) {
|
|
Packit |
7e982e |
return HF_SETUID;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
if (strcmp(flag, "tty") == 0) {
|
|
Packit |
7e982e |
return HF_TTY;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
if (strcmp(flag, "user") == 0) {
|
|
Packit |
7e982e |
return HF_USER;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
return HF_PARAM;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
static void
|
|
Packit |
7e982e |
call_exec(struct console_handler *handler, int nparams, const char *user, const char *tty) {
|
|
Packit |
7e982e |
const char *flagptr;
|
|
Packit |
7e982e |
const char **argv;
|
|
Packit |
7e982e |
int i = 0;
|
|
Packit |
7e982e |
argv = malloc(sizeof(*argv)*(nparams+2));
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
if (argv == NULL)
|
|
Packit |
7e982e |
return;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
argv[i++] = handler->executable;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
for (flagptr = handler->flags; *flagptr != '\0'; flagptr += strlen(flagptr)+1) {
|
|
Packit |
7e982e |
switch (testflag(flagptr)) {
|
|
Packit |
7e982e |
case HF_LOGFAIL:
|
|
Packit |
7e982e |
case HF_WAIT:
|
|
Packit |
7e982e |
case HF_SETUID:
|
|
Packit |
7e982e |
break;
|
|
Packit |
7e982e |
case HF_TTY:
|
|
Packit |
7e982e |
argv[i++] = tty;
|
|
Packit |
7e982e |
break;
|
|
Packit |
7e982e |
case HF_USER:
|
|
Packit |
7e982e |
argv[i++] = user;
|
|
Packit |
7e982e |
break;
|
|
Packit |
7e982e |
case HF_PARAM:
|
|
Packit |
7e982e |
argv[i++] = flagptr;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
argv[i] = NULL;
|
|
Packit |
7e982e |
execvp(handler->executable, (char * const *)argv);
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
static int
|
|
Packit |
7e982e |
execute_handler(pam_handle_t *pamh, struct console_handler *handler, const char *user, const char *tty) {
|
|
Packit |
7e982e |
const char *flagptr;
|
|
Packit |
7e982e |
int nparams = 0;
|
|
Packit |
7e982e |
int logfail = 0;
|
|
Packit |
7e982e |
int wait_exit = 0;
|
|
Packit |
7e982e |
int set_uid = 0;
|
|
Packit |
7e982e |
int child;
|
|
Packit |
7e982e |
int rv = 0;
|
|
Packit |
7e982e |
int max_fd;
|
|
Packit |
7e982e |
int fd;
|
|
Packit |
7e982e |
sighandler_t sighandler;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
for (flagptr = handler->flags; *flagptr != '\0'; flagptr += strlen(flagptr)+1) {
|
|
Packit |
7e982e |
switch (testflag(flagptr)) {
|
|
Packit |
7e982e |
case HF_LOGFAIL:
|
|
Packit |
7e982e |
logfail = 1;
|
|
Packit |
7e982e |
break;
|
|
Packit |
7e982e |
case HF_WAIT:
|
|
Packit |
7e982e |
wait_exit = 1;
|
|
Packit |
7e982e |
break;
|
|
Packit |
7e982e |
case HF_SETUID:
|
|
Packit |
7e982e |
set_uid = 1;
|
|
Packit |
7e982e |
break;
|
|
Packit |
7e982e |
case HF_TTY:
|
|
Packit |
7e982e |
case HF_USER:
|
|
Packit |
7e982e |
case HF_PARAM:
|
|
Packit |
7e982e |
nparams++;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
sighandler = signal(SIGCHLD, SIG_DFL);
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
child = fork();
|
|
Packit |
7e982e |
switch (child) {
|
|
Packit |
7e982e |
case -1:
|
|
Packit |
7e982e |
_pam_log(pamh, LOG_ERR, !logfail, "fork failed when executing handler '%s'",
|
|
Packit |
7e982e |
handler->executable);
|
|
Packit |
7e982e |
return -1;
|
|
Packit |
7e982e |
case 0:
|
|
Packit |
7e982e |
/* close all descriptors except std* */
|
|
Packit |
7e982e |
max_fd = getdtablesize();
|
|
Packit |
7e982e |
for(fd = 3; fd < max_fd; fd++)
|
|
Packit |
7e982e |
rv = close(fd); /* rv will be ignored */
|
|
Packit |
7e982e |
if (!wait_exit) {
|
|
Packit |
7e982e |
switch(fork()) {
|
|
Packit |
7e982e |
case 0:
|
|
Packit |
7e982e |
if(setsid() == -1) {
|
|
Packit |
7e982e |
_exit(255);
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
break;
|
|
Packit |
7e982e |
case -1:
|
|
Packit |
7e982e |
_exit(255);
|
|
Packit |
7e982e |
default:
|
|
Packit |
7e982e |
_exit(0);
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
if (set_uid) {
|
|
Packit |
7e982e |
struct passwd *pw;
|
|
Packit |
7e982e |
pw = getpwnam(user);
|
|
Packit |
7e982e |
if (pw == NULL)
|
|
Packit |
7e982e |
_exit(255);
|
|
Packit |
7e982e |
if (setgid(pw->pw_gid) == -1 ||
|
|
Packit |
7e982e |
setgroups(0, NULL) == -1 ||
|
|
Packit |
7e982e |
setuid(pw->pw_uid) == -1)
|
|
Packit |
7e982e |
_exit(255);
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
call_exec(handler, nparams, user, tty);
|
|
Packit |
7e982e |
_exit(255);
|
|
Packit |
7e982e |
default:
|
|
Packit |
7e982e |
break;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
waitpid(child, &rv, 0);
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
if (sighandler != SIG_ERR)
|
|
Packit |
7e982e |
signal(SIGCHLD, sighandler);
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
if (WIFEXITED(rv) && WEXITSTATUS(rv) != 0)
|
|
Packit |
7e982e |
_pam_log(pamh, LOG_ERR, !logfail, "handler '%s' returned %d on exit",
|
|
Packit |
7e982e |
handler->executable, (int)WEXITSTATUS(rv));
|
|
Packit |
7e982e |
else if (WIFSIGNALED(rv))
|
|
Packit |
7e982e |
_pam_log(pamh, LOG_ERR, !logfail, "handler '%s' caught a signal %d",
|
|
Packit |
7e982e |
handler->executable, (int)WTERMSIG(rv));
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
return 0;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
void
|
|
Packit |
7e982e |
console_run_handlers(pam_handle_t *pamh, int lock, const char *user, const char *tty) {
|
|
Packit |
7e982e |
struct console_handler *handler;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
for (handler = first_handler; handler != NULL; handler = handler->next) {
|
|
Packit |
7e982e |
if (lock && handler->type == LOCK) {
|
|
Packit |
7e982e |
execute_handler(pamh, handler, user, tty);
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
else if (!lock && handler->type == UNLOCK) {
|
|
Packit |
7e982e |
execute_handler(pamh, handler, user, tty);
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
const char *
|
|
Packit |
7e982e |
console_get_regexes(void) {
|
|
Packit |
7e982e |
struct console_handler *handler;
|
|
Packit |
7e982e |
|
|
Packit |
7e982e |
for (handler = first_handler; handler != NULL; handler = handler->next) {
|
|
Packit |
7e982e |
if (handler->type == CONSOLEDEVS) {
|
|
Packit |
7e982e |
return handler->flags;
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
}
|
|
Packit |
7e982e |
return NULL;
|
|
Packit |
7e982e |
}
|