#include <sys/param.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <syslog.h>
#include <deflt.h>
#include <bsm/adt.h>
#include <bsm/adt_event.h>
#include <alloca.h>
#include <assert.h>
#include <errno.h>
#include <door.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <synch.h>
#include <thread.h>
#include <unistd.h>
#include <wait.h>
#include <limits.h>
#include <zone.h>
#include <priv.h>
#include <pwd.h>
#include <utmpx.h>
#include <procfs.h>
#include <poll.h>
#include <termio.h>
#include <security/pam_appl.h>
#include <time.h>
#include <sys/console.h>
#include <assert.h>
#include <syslog.h>
#include <sys/vt.h>
#include <sys/vtdaemon.h>
#define VT_TMPDIR "/var/run/vt"
#define VT_DAEMON_ARG 0
#define VT_DAEMON_CONSOLE_FILE "/dev/vt/1"
#define VT_IS_SYSTEM_CONSOLE(vtno) ((vtno) == 1)
#define DEF_ATTEMPTS 3
int daemonfd;
static boolean_t vt_hotkeys = B_TRUE;
static boolean_t vt_secure = B_TRUE;
static char vt_door_path[MAXPATHLEN];
static int vt_door = -1;
static mutex_t vt_mutex = DEFAULTMUTEX;
static boolean_t vt_hotkeys_pending = B_FALSE;
static boolean_t vt_auth_doing = B_FALSE;
static adt_session_data_t **vt_ah_array = NULL;
static int vtnodecount = 0;
static int vt_audit_start(adt_session_data_t **, pid_t);
static void vt_audit_event(adt_session_data_t *, au_event_t, int);
static void vt_check_source_audit(void);
static int
vt_setup_signal(int signo, int mask)
{
sigset_t set;
(void) sigemptyset(&set);
(void) sigaddset(&set, signo);
if (mask)
return (sigprocmask(SIG_BLOCK, &set, NULL));
else
return (sigprocmask(SIG_UNBLOCK, &set, NULL));
}
static void
do_activate_screenlock(int display_num)
{
char dpy[16];
(void) snprintf(dpy, sizeof (dpy), "%d", display_num);
(void) execl("/usr/lib/vtxlock", "vtxlock", dpy, NULL);
}
static void
vt_activate_screenlock(int display)
{
pid_t pid;
if ((pid = fork()) == -1)
return;
if (pid == 0) {
do_activate_screenlock(display);
exit(0);
}
while (waitpid(pid, (int *)0, 0) != pid)
continue;
}
static void
vt_read_utx(int target_vt, pid_t *pid, char name[])
{
struct utmpx *u;
char ttyntail[sizeof (u->ut_line)];
*pid = (pid_t)-1;
if (VT_IS_SYSTEM_CONSOLE(target_vt))
(void) snprintf(ttyntail, sizeof (ttyntail),
"%s", "console");
else
(void) snprintf(ttyntail, sizeof (ttyntail),
"%s%d", "vt/", target_vt);
setutxent();
while ((u = getutxent()) != NULL)
if ((u->ut_type == USER_PROCESS) &&
(!nonuserx(*u)) &&
(u->ut_host[0] == '\0') &&
(strncmp(u->ut_line, ttyntail, sizeof (u->ut_line)) == 0)) {
*pid = u->ut_pid;
if (name != NULL) {
(void) strncpy(name, u->ut_user,
sizeof (u->ut_user));
name[sizeof (u->ut_user)] = '\0';
}
break;
}
endutxent();
}
static boolean_t
vt_is_tipline(void)
{
static int is_tipline = 0;
int fd;
static char termbuf[MAX_TERM_TYPE_LEN];
static struct cons_getterm cons_term = { sizeof (termbuf), termbuf};
if (is_tipline != 0)
return (is_tipline == 1);
if ((fd = open("/dev/console", O_RDONLY)) < 0)
return (B_FALSE);
if (ioctl(fd, CONS_GETTERM, &cons_term) != 0 &&
errno == ENODEV) {
is_tipline = 1;
} else {
is_tipline = -1;
}
(void) close(fd);
return (is_tipline == 1);
}
static int
validate_target_vt(int target_vt)
{
int fd;
struct vt_stat state;
if (target_vt < 1)
return (-1);
if ((fd = open(VT_DAEMON_CONSOLE_FILE, O_WRONLY)) < 0)
return (-1);
if (ioctl(fd, VT_GETSTATE, &state) != 0) {
(void) close(fd);
return (-1);
}
(void) close(fd);
if (state.v_active == target_vt) {
return (1);
}
if (target_vt == 1) {
if (vt_is_tipline())
return (-1);
target_vt = 0;
}
if (target_vt < 8 * sizeof (state.v_state)) {
if ((state.v_state & (1 << target_vt)) != 0) {
return (0);
} else {
return (-1);
}
}
return (0);
}
static void
vt_do_activate(int target_vt)
{
(void) ioctl(daemonfd, VT_ACTIVATE, target_vt);
(void) mutex_lock(&vt_mutex);
vt_hotkeys_pending = B_FALSE;
(void) mutex_unlock(&vt_mutex);
}
#define VT_EV_AUTH 1
#define VT_EV_LOCK 2
#define VT_EV_ACTIVATE 3
#define VT_EV_TERMINATE_AUTH 4
typedef struct vt_evt {
int ve_cmd;
int ve_info;
} vt_evt_t;
static int eventstream[2];
boolean_t
eventstream_init(void)
{
if (pipe(eventstream) == -1)
return (B_FALSE);
return (B_TRUE);
}
void
eventstream_write(int channel, vt_evt_t *pevt)
{
(void) write(eventstream[channel], pevt, sizeof (vt_evt_t));
}
static boolean_t
eventstream_read(int channel, vt_evt_t *pevt)
{
ssize_t rval;
rval = read(eventstream[channel], pevt, sizeof (vt_evt_t));
return (rval > 0);
}
static void
vt_ev_request(int cmd, int info)
{
int channel;
vt_evt_t ve;
ve.ve_cmd = cmd;
ve.ve_info = info;
channel = (cmd == VT_EV_TERMINATE_AUTH) ? 1 : 0;
eventstream_write(channel, &ve);
}
static void
vt_clear_events(void)
{
int rval = 0;
struct stat buf;
vt_evt_t evt;
while (rval == 0) {
rval = fstat(eventstream[0], &buf);
if (rval != -1 && buf.st_size > 0)
(void) eventstream_read(0, &evt);
else
break;
}
}
static int vt_conv(int, const struct pam_message **,
struct pam_response **, void *);
static void
catch(int x)
{
(void) signal(SIGINT, catch);
}
static int
vt_poll()
{
struct pollfd pollfds[2];
vt_evt_t ve;
int ret;
pollfds[0].fd = eventstream[0];
pollfds[1].fd = daemonfd;
pollfds[0].events = pollfds[1].events =
POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
for (;;) {
pollfds[0].revents = pollfds[1].revents = 0;
ret = poll(pollfds,
sizeof (pollfds) / sizeof (struct pollfd), -1);
if (ret == -1 && errno != EINTR) {
continue;
}
if (ret == -1 && errno == EINTR)
return (-1);
if (pollfds[0].revents) {
(void) eventstream_read(0, &ve);
return (0);
}
if (pollfds[1].revents)
return (1);
return (0);
}
}
static char
vt_getchar(int fd)
{
char c;
int cnt;
cnt = read(fd, &c, 1);
if (cnt > 0) {
return (c);
}
return (EOF);
}
static char *
vt_getinput(int noecho)
{
int c;
int i = 0;
struct termio tty;
tcflag_t tty_flags;
char input[PAM_MAX_RESP_SIZE];
if (noecho) {
(void) ioctl(daemonfd, TCGETA, &tty);
tty_flags = tty.c_lflag;
tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
(void) ioctl(daemonfd, TCSETAF, &tty);
}
while ((vt_poll()) == 1) {
if ((c = vt_getchar(daemonfd)) != '\n' && c != '\r' &&
c != EOF && (i < PAM_MAX_RESP_SIZE - 1))
input[i++] = (char)c;
else
break;
}
input[i] = '\0';
if (noecho) {
tty.c_lflag = tty_flags;
(void) ioctl(daemonfd, TCSETAW, &tty);
(void) fputc('\n', stdout);
}
return (strdup(input));
}
static int
vt_conv(int num_msg, const struct pam_message **msg,
struct pam_response **response, void *appdata_ptr)
{
const struct pam_message *m;
struct pam_response *r;
int i, k;
if (num_msg >= PAM_MAX_NUM_MSG) {
syslog(LOG_ERR, "too many messages %d >= %d",
num_msg, PAM_MAX_NUM_MSG);
*response = NULL;
return (PAM_CONV_ERR);
}
*response = calloc(num_msg, sizeof (struct pam_response));
if (*response == NULL)
return (PAM_BUF_ERR);
m = *msg;
r = *response;
for (i = 0; i < num_msg; i++) {
int echo_off = 0;
if (m->msg == NULL) {
syslog(LOG_ERR, "message[%d]: %d/NULL\n",
i, m->msg_style);
goto err;
}
if (m->msg[strlen(m->msg)] == '\n')
m->msg[strlen(m->msg)] = '\0';
r->resp = NULL;
r->resp_retcode = 0;
switch (m->msg_style) {
case PAM_PROMPT_ECHO_OFF:
echo_off = 1;
case PAM_PROMPT_ECHO_ON:
(void) fputs(m->msg, stdout);
r->resp = vt_getinput(echo_off);
break;
case PAM_ERROR_MSG:
(void) fputs(m->msg, stdout);
(void) fputs("\n", stdout);
break;
case PAM_TEXT_INFO:
(void) fputs(m->msg, stdout);
(void) fputs("\n", stdout);
break;
default:
syslog(LOG_ERR, "message[%d]: unknown type"
"%d/val=\"%s\"", i, m->msg_style, m->msg);
goto err;
}
m++;
r++;
}
return (PAM_SUCCESS);
err:
r = *response;
for (k = 0; k < i; k++, r++) {
if (r->resp) {
bzero(r->resp, strlen(r->resp));
free(r->resp);
r->resp = NULL;
}
}
free(*response);
*response = NULL;
return (PAM_CONV_ERR);
}
#define DEF_FILE "/etc/default/login"
static boolean_t
vt_default(void)
{
int flags;
char *ptr;
boolean_t retval = B_FALSE;
if ((defopen(DEF_FILE)) == 0) {
flags = defcntl(DC_GETFLAGS, 0);
TURNOFF(flags, DC_CASE);
(void) defcntl(DC_SETFLAGS, flags);
if ((ptr = defread("PASSREQ=")) != NULL &&
strcasecmp("YES", ptr) == 0)
retval = B_TRUE;
(void) defopen(NULL);
}
return (retval);
}
#define VT_CLEAR_SCREEN_STR "\033[2J\033[1;1H"
static void
vt_do_auth(int target_vt)
{
char user_name[sizeof (((struct utmpx *)0)->ut_line) + 1] = {'\0'};
pam_handle_t *vt_pamh;
int err;
int pam_flag = 0;
int chpasswd_tries;
struct pam_conv pam_conv = {vt_conv, NULL};
pid_t pid;
adt_session_data_t *ah;
vt_read_utx(target_vt, &pid, user_name);
if (pid == (pid_t)-1 || user_name[0] == '\0')
return;
if ((err = pam_start("vtdaemon", user_name, &pam_conv,
&vt_pamh)) != PAM_SUCCESS)
return;
(void) ioctl(daemonfd, VT_ACTIVATE, VT_DAEMON_ARG);
(void) write(daemonfd, VT_CLEAR_SCREEN_STR,
strlen(VT_CLEAR_SCREEN_STR));
(void) ioctl(daemonfd, VT_SET_TARGET, target_vt);
(void) mutex_lock(&vt_mutex);
vt_auth_doing = B_TRUE;
vt_hotkeys_pending = B_FALSE;
(void) mutex_unlock(&vt_mutex);
ah = vt_ah_array[target_vt - 1];
if (vt_default())
pam_flag = PAM_DISALLOW_NULL_AUTHTOK;
do {
if (VT_IS_SYSTEM_CONSOLE(target_vt))
(void) fprintf(stdout,
"\nUnlock user %s on the system console\n",
user_name);
else
(void) fprintf(stdout,
"\nUnlock user %s on vt/%d\n", user_name,
target_vt);
err = pam_authenticate(vt_pamh, pam_flag);
(void) mutex_lock(&vt_mutex);
if (vt_hotkeys_pending) {
(void) mutex_unlock(&vt_mutex);
break;
}
(void) mutex_unlock(&vt_mutex);
if (err == PAM_SUCCESS) {
err = pam_acct_mgmt(vt_pamh, pam_flag);
(void) mutex_lock(&vt_mutex);
if (vt_hotkeys_pending) {
(void) mutex_unlock(&vt_mutex);
break;
}
(void) mutex_unlock(&vt_mutex);
if (err == PAM_NEW_AUTHTOK_REQD) {
chpasswd_tries = 0;
do {
err = pam_chauthtok(vt_pamh,
PAM_CHANGE_EXPIRED_AUTHTOK);
chpasswd_tries++;
(void) mutex_lock(&vt_mutex);
if (vt_hotkeys_pending) {
(void) mutex_unlock(&vt_mutex);
break;
}
(void) mutex_unlock(&vt_mutex);
} while ((err == PAM_AUTHTOK_ERR ||
err == PAM_TRY_AGAIN) &&
chpasswd_tries < DEF_ATTEMPTS);
(void) mutex_lock(&vt_mutex);
if (vt_hotkeys_pending) {
(void) mutex_unlock(&vt_mutex);
break;
}
(void) mutex_unlock(&vt_mutex);
vt_audit_event(ah, ADT_passwd, err);
}
}
if (err != PAM_SUCCESS) {
(void) fprintf(stdout, "%s",
pam_strerror(vt_pamh, err));
vt_audit_event(ah, ADT_screenunlock, err);
}
(void) mutex_lock(&vt_mutex);
if (vt_hotkeys_pending) {
(void) mutex_unlock(&vt_mutex);
break;
}
(void) mutex_unlock(&vt_mutex);
} while (err != PAM_SUCCESS);
(void) mutex_lock(&vt_mutex);
if (!vt_hotkeys_pending) {
(void) ioctl(daemonfd, VT_ACTIVATE, target_vt);
vt_audit_event(ah, ADT_screenunlock, err);
(void) adt_end_session(ah);
vt_ah_array[target_vt - 1] = NULL;
}
(void) mutex_unlock(&vt_mutex);
(void) pam_end(vt_pamh, err);
(void) mutex_lock(&vt_mutex);
vt_auth_doing = B_FALSE;
vt_clear_events();
(void) mutex_unlock(&vt_mutex);
}
static void __NORETURN
vt_serve_events(void)
{
struct pollfd pollfds[1];
int ret;
vt_evt_t ve;
pollfds[0].fd = eventstream[1];
pollfds[0].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
for (;;) {
pollfds[0].revents = 0;
ret = poll(pollfds,
sizeof (pollfds) / sizeof (struct pollfd), -1);
if (ret == -1 && errno == EINTR) {
continue;
}
if (pollfds[0].revents && eventstream_read(1, &ve)) {
switch (ve.ve_cmd) {
case VT_EV_AUTH:
vt_do_auth(ve.ve_info);
break;
case VT_EV_LOCK:
vt_activate_screenlock(ve.ve_info);
break;
case VT_EV_ACTIVATE:
vt_do_activate(ve.ve_info);
break;
}
}
}
}
static void
vt_check_target_session(uint32_t target_vt)
{
pid_t pid = (pid_t)-1;
if (!vt_secure) {
vt_ev_request(VT_EV_ACTIVATE, target_vt);
return;
}
vt_read_utx(target_vt, &pid, NULL);
if (pid == (pid_t)-1) {
vt_ev_request(VT_EV_ACTIVATE, target_vt);
return;
}
vt_ev_request(VT_EV_AUTH, target_vt);
}
static boolean_t
vt_get_active_disp_info(struct vt_dispinfo *vd)
{
int fd;
struct vt_stat state;
char vtname[16];
if ((fd = open(VT_DAEMON_CONSOLE_FILE, O_RDONLY)) < 0)
return (B_FALSE);
if (ioctl(fd, VT_GETSTATE, &state) != 0) {
(void) close(fd);
return (B_FALSE);
}
(void) close(fd);
(void) snprintf(vtname, sizeof (vtname), "/dev/vt/%d", state.v_active);
if ((fd = open(vtname, O_RDONLY)) < 0)
return (B_FALSE);
if (ioctl(fd, VT_GETDISPINFO, vd) != 0) {
(void) close(fd);
return (B_FALSE);
}
(void) close(fd);
return (B_TRUE);
}
static boolean_t
vt_check_disp_active(pid_t x_pid)
{
struct vt_dispinfo vd;
if (vt_get_active_disp_info(&vd) &&
vd.v_pid == x_pid)
return (B_TRUE);
return (B_FALSE);
}
static boolean_t
vt_get_disp_info(pid_t x_pid, int *logged_in, int *display_num)
{
struct vt_dispinfo vd;
if (!vt_get_active_disp_info(&vd) ||
vd.v_pid != x_pid)
return (B_FALSE);
*logged_in = vd.v_login;
*display_num = vd.v_dispnum;
return (B_TRUE);
}
static void
vt_terminate_auth(void)
{
struct timespec sleeptime;
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = 1000000;
(void) mutex_lock(&vt_mutex);
while (vt_auth_doing) {
vt_ev_request(VT_EV_TERMINATE_AUTH, 0);
if (vt_auth_doing) {
(void) mutex_unlock(&vt_mutex);
(void) nanosleep(&sleeptime, NULL);
sleeptime.tv_nsec *= 2;
(void) mutex_lock(&vt_mutex);
}
}
(void) mutex_unlock(&vt_mutex);
}
static void
vt_do_hotkeys(pid_t pid, uint32_t target_vt)
{
int logged_in;
int display_num;
if (validate_target_vt(target_vt) != 0)
return;
(void) mutex_lock(&vt_mutex);
if (vt_hotkeys_pending) {
(void) mutex_unlock(&vt_mutex);
return;
}
vt_hotkeys_pending = B_TRUE;
(void) mutex_unlock(&vt_mutex);
vt_terminate_auth();
if (pid == 0) {
if (vt_secure)
vt_check_source_audit();
vt_check_target_session(target_vt);
return;
}
if (!vt_get_disp_info(pid, &logged_in, &display_num)) {
(void) mutex_lock(&vt_mutex);
vt_hotkeys_pending = B_FALSE;
(void) mutex_unlock(&vt_mutex);
return;
}
if (logged_in && vt_secure)
vt_ev_request(VT_EV_LOCK, display_num);
vt_check_target_session(target_vt);
}
static void
server_for_door(void *cookie, char *args, size_t alen, door_desc_t *dp,
uint_t n_desc)
{
ucred_t *uc = NULL;
vt_cmd_arg_t *vtargp;
vtargp = (vt_cmd_arg_t *)args;
if (vtargp == NULL ||
alen != sizeof (vt_cmd_arg_t) ||
door_ucred(&uc) != 0) {
(void) door_return(NULL, 0, NULL, 0);
return;
}
switch (vtargp->vt_ev) {
case VT_EV_X_EXIT:
if (vt_check_disp_active(ucred_getpid(uc)))
vt_do_hotkeys(0, vtargp->vt_num);
break;
case VT_EV_HOTKEYS:
if (!vt_hotkeys)
break;
vt_do_hotkeys(ucred_getpid(uc), vtargp->vt_num);
break;
default:
break;
}
ucred_free(uc);
(void) door_return(NULL, 0, NULL, 0);
}
static boolean_t
setup_door(void)
{
if ((vt_door = door_create(server_for_door, NULL,
DOOR_UNREF | DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) {
syslog(LOG_ERR, "door_create failed: %s", strerror(errno));
return (B_FALSE);
}
(void) fdetach(vt_door_path);
if (fattach(vt_door, vt_door_path) != 0) {
syslog(LOG_ERR, "fattach to %s failed: %s",
vt_door_path, strerror(errno));
(void) door_revoke(vt_door);
(void) fdetach(vt_door_path);
vt_door = -1;
return (B_FALSE);
}
return (B_TRUE);
}
static boolean_t
make_daemon_exclusive(void)
{
int doorfd = -1;
boolean_t ret = B_FALSE;
struct stat st;
struct flock flock;
top:
if ((doorfd = open(vt_door_path, O_CREAT|O_RDWR,
S_IREAD|S_IWRITE|S_IRGRP|S_IROTH)) < 0) {
syslog(LOG_ERR, "failed to open %s", vt_door_path);
goto out;
}
if (fstat(doorfd, &st) < 0) {
syslog(LOG_ERR, "failed to stat %s", vt_door_path);
goto out;
}
flock.l_type = F_WRLCK;
flock.l_whence = SEEK_SET;
flock.l_start = (off_t)0;
flock.l_len = (off_t)0;
if (fcntl(doorfd, F_SETLK, &flock) < 0) {
syslog(LOG_ERR, "vtdaemon is already running!");
goto out;
}
if (strcmp(st.st_fstype, "namefs") == 0) {
struct door_info info;
if (door_info(doorfd, &info) == 0 && info.di_target != -1) {
syslog(LOG_ERR, "vtdaemon is already running!");
goto out;
}
(void) fdetach(vt_door_path);
(void) close(doorfd);
goto top;
}
ret = setup_door();
out:
(void) close(doorfd);
return (ret);
}
static boolean_t
mkvtdir(void)
{
struct stat st;
if (mkdir(VT_TMPDIR, S_IRWXU|S_IROTH|S_IXOTH|S_IRGRP|S_IXGRP) < 0 &&
errno != EEXIST) {
syslog(LOG_ERR, "could not mkdir '%s'", VT_TMPDIR);
return (B_FALSE);
}
if ((stat(VT_TMPDIR, &st) < 0) || !S_ISDIR(st.st_mode)) {
syslog(LOG_ERR, "'%s' is not a directory", VT_TMPDIR);
return (B_FALSE);
}
(void) chmod(VT_TMPDIR, S_IRWXU|S_IROTH|S_IXOTH|S_IRGRP|S_IXGRP);
return (B_TRUE);
}
int
main(int argc, char *argv[])
{
int i;
int opt;
priv_set_t *privset;
int active;
openlog("vtdaemon", LOG_PID | LOG_CONS, 0);
if ((privset = priv_allocset()) == NULL) {
syslog(LOG_ERR, "priv_allocset failed");
return (1);
}
if (getppriv(PRIV_EFFECTIVE, privset) != 0) {
syslog(LOG_ERR, "getppriv failed", "getppriv");
priv_freeset(privset);
return (1);
}
if (priv_isfullset(privset) == B_FALSE) {
syslog(LOG_ERR, "You lack sufficient privilege "
"to run this command (all privs required)");
priv_freeset(privset);
return (1);
}
priv_freeset(privset);
while ((opt = getopt(argc, argv, "ksrc:")) != EOF) {
switch (opt) {
case 'k':
vt_hotkeys = B_FALSE;
break;
case 's':
vt_secure = B_FALSE;
break;
case 'c':
vtnodecount = atoi(optarg);
break;
default:
break;
}
}
(void) vt_setup_signal(SIGINT, 1);
if (!mkvtdir())
return (1);
if (!eventstream_init())
return (1);
(void) snprintf(vt_door_path, sizeof (vt_door_path),
VT_TMPDIR "/vtdaemon_door");
if (!make_daemon_exclusive())
return (1);
(void) vt_setup_signal(SIGINT, 0);
(void) sigset(SIGPIPE, SIG_IGN);
(void) signal(SIGQUIT, SIG_IGN);
(void) signal(SIGINT, catch);
for (i = 0; i < 3; i++)
(void) close(i);
(void) setsid();
if ((daemonfd = open(VT_DAEMON_CONSOLE_FILE, O_RDWR)) < 0) {
return (1);
}
if (daemonfd != 0)
(void) dup2(daemonfd, STDIN_FILENO);
if (daemonfd != 1)
(void) dup2(daemonfd, STDOUT_FILENO);
if (vtnodecount >= 2)
(void) ioctl(daemonfd, VT_CONFIG, vtnodecount);
if ((vt_ah_array = calloc(vtnodecount - 1,
sizeof (adt_session_data_t *))) == NULL)
return (1);
(void) ioctl(daemonfd, VT_GETACTIVE, &active);
if (active == 1) {
(void) write(daemonfd, VT_CLEAR_SCREEN_STR,
strlen(VT_CLEAR_SCREEN_STR));
}
vt_serve_events();
}
static int
vt_audit_start(adt_session_data_t **ah, pid_t pid)
{
ucred_t *uc;
if (adt_start_session(ah, NULL, 0))
return (-1);
if ((uc = ucred_get(pid)) == NULL) {
(void) adt_end_session(*ah);
return (-1);
}
if (adt_set_from_ucred(*ah, uc, ADT_NEW)) {
ucred_free(uc);
(void) adt_end_session(*ah);
return (-1);
}
ucred_free(uc);
return (0);
}
static void
vt_audit_event(adt_session_data_t *ah, au_event_t event_id, int status)
{
adt_event_data_t *event;
if ((event = adt_alloc_event(ah, event_id)) == NULL) {
return;
}
(void) adt_put_event(event,
status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM + status);
adt_free_event(event);
}
static void
vt_check_source_audit(void)
{
int fd;
int source_vt;
int real_vt;
struct vt_stat state;
pid_t pid;
adt_session_data_t *ah;
if ((fd = open(VT_DAEMON_CONSOLE_FILE, O_WRONLY)) < 0)
return;
if (ioctl(fd, VT_GETSTATE, &state) != 0 ||
ioctl(fd, VT_GETACTIVE, &real_vt) != 0) {
(void) close(fd);
return;
}
source_vt = state.v_active;
(void) close(fd);
if (real_vt == 1)
return;
vt_read_utx(source_vt, &pid, NULL);
if (pid == (pid_t)-1)
return;
if (vt_audit_start(&ah, pid) != 0) {
syslog(LOG_ERR, "audit start failed ");
return;
}
if (vt_ah_array[source_vt - 1] != NULL)
(void) adt_end_session(vt_ah_array[source_vt - 1]);
vt_ah_array[source_vt - 1] = ah;
vt_audit_event(ah, ADT_screenlock, PAM_SUCCESS);
}