Blame modules/pam_console/handlers.c

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
}