384 lines
8.4 KiB
C
384 lines
8.4 KiB
C
/*
|
|
* Copyright (c) 2003 Marius Aamodt Eriksen <marius@monkey.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/un.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/tree.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <event.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#ifdef __linux__
|
|
#include <bits/posix1_lim.h>
|
|
#ifndef LOGIN_NAME_MAX
|
|
#define LOGIN_NAME_MAX _POSIX_LOGIN_NAME_MAX
|
|
#endif
|
|
#endif /* __linux__ */
|
|
|
|
#include "intercept.h"
|
|
#include "systrace.h"
|
|
|
|
extern int connected;
|
|
extern char dirpath[];
|
|
|
|
static struct event listen_ev;
|
|
static struct event uilisten_ev;
|
|
|
|
static int cradle_server(char *path, char *uipath, char *guipath);
|
|
static void listen_cb(int, short, void *);
|
|
static void msg_cb(int, short, void *);
|
|
static void ui_cb(int, short, void *);
|
|
static void gensig_cb(int, short, void *);
|
|
|
|
static FILE *ui_fl = NULL;
|
|
static struct event ui_ev, sigterm_ev, sigint_ev;
|
|
static char buffer[4096];
|
|
static char title[4096];
|
|
static char *xuipath, *xpath;
|
|
static volatile int got_sigusr1 = 0;
|
|
|
|
struct client {
|
|
struct event ev;
|
|
FILE *fl;
|
|
int buffered;
|
|
TAILQ_ENTRY(client) next;
|
|
};
|
|
|
|
TAILQ_HEAD(client_head, client) clientq;
|
|
|
|
/* fake signal handler */
|
|
static void
|
|
sigusr1_handler(int sig)
|
|
{
|
|
got_sigusr1 = 1;
|
|
}
|
|
|
|
static void
|
|
gensig_cb(int sig, short ev, void *data)
|
|
{
|
|
unlink(xpath);
|
|
unlink(xuipath);
|
|
|
|
rmdir(dirpath);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
static int
|
|
mkunserv(char *path)
|
|
{
|
|
int s;
|
|
struct sockaddr_un sun;
|
|
|
|
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
|
|
err(1, "socket()");
|
|
|
|
memset(&sun, 0, sizeof (sun));
|
|
sun.sun_family = AF_UNIX;
|
|
|
|
if (strlcpy(sun.sun_path, path, sizeof (sun.sun_path)) >=
|
|
sizeof (sun.sun_path))
|
|
errx(1, "Path too long: %s", path);
|
|
|
|
if (bind(s, (struct sockaddr *)&sun, sizeof(sun)) == -1)
|
|
err(1, "bind()");
|
|
|
|
if (chmod(path, S_IRUSR | S_IWUSR) == -1)
|
|
err(1, "chmod()");
|
|
|
|
if (listen(s, 10) == -1)
|
|
err(1, "listen()");
|
|
|
|
return (s);
|
|
}
|
|
|
|
static int
|
|
cradle_server(char *path, char *uipath, char *guipath)
|
|
{
|
|
int s, uis;
|
|
pid_t pid, newpid;
|
|
sigset_t none, set, oldset;
|
|
sig_t oldhandler;
|
|
|
|
sigemptyset(&none);
|
|
sigemptyset(&set);
|
|
sigaddset(&set, SIGUSR1);
|
|
if (sigprocmask(SIG_BLOCK, &set, &oldset) == -1)
|
|
err(1, "sigprocmask()");
|
|
oldhandler = signal(SIGUSR1, sigusr1_handler);
|
|
if (oldhandler == SIG_ERR)
|
|
err(1, "signal()");
|
|
|
|
xpath = path;
|
|
xuipath = uipath;
|
|
|
|
pid = getpid();
|
|
newpid = fork();
|
|
|
|
switch (newpid) {
|
|
case -1:
|
|
err(1, "fork()");
|
|
case 0:
|
|
break;
|
|
default:
|
|
/*
|
|
* Parent goes to sleep waiting for server to start.
|
|
* When it wakes up, we can start the GUI.
|
|
*/
|
|
sigsuspend(&none);
|
|
if (signal(SIGUSR1, oldhandler) == SIG_ERR)
|
|
err(1, "signal()");
|
|
if (sigprocmask(SIG_SETMASK, &oldset, NULL) == -1)
|
|
err(1, "sigprocmask()");
|
|
if (got_sigusr1) {
|
|
requestor_start(guipath, 1);
|
|
return (0);
|
|
} else
|
|
return (-1);
|
|
}
|
|
|
|
setsid();
|
|
snprintf(title, sizeof(title), "cradle server for UID %d", getuid());
|
|
setproctitle(title);
|
|
|
|
TAILQ_INIT(&clientq);
|
|
|
|
event_init();
|
|
|
|
s = mkunserv(path);
|
|
uis = mkunserv(uipath);
|
|
|
|
signal_set(&sigterm_ev, SIGTERM, gensig_cb, NULL);
|
|
if (signal_add(&sigterm_ev, NULL) == -1)
|
|
err(1, "signal_add()");
|
|
|
|
signal_set(&sigint_ev, SIGINT, gensig_cb, NULL);
|
|
if (signal_add(&sigint_ev, NULL) == -1)
|
|
err(1, "signal_add()");
|
|
|
|
event_set(&listen_ev, s, EV_READ, listen_cb, NULL);
|
|
if (event_add(&listen_ev, NULL) == -1)
|
|
err(1, "event_add()");
|
|
|
|
event_set(&uilisten_ev, uis, EV_READ, listen_cb, &listen_cb);
|
|
if (event_add(&uilisten_ev, NULL) == -1)
|
|
err(1, "event_add()");
|
|
|
|
kill(pid, SIGUSR1);
|
|
|
|
event_dispatch();
|
|
errx(1, "event_dispatch()");
|
|
/* NOTREACHED */
|
|
/* gcc fodder */
|
|
return (-1);
|
|
}
|
|
|
|
void
|
|
cradle_start(char *path, char *uipath, char *guipath)
|
|
{
|
|
int s;
|
|
struct sockaddr_un sun;
|
|
|
|
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if (s == -1)
|
|
err(1, "socket()");
|
|
|
|
memset(&sun, 0, sizeof(sun));
|
|
sun.sun_family = AF_UNIX;
|
|
|
|
if (strlcpy(sun.sun_path, path, sizeof (sun.sun_path)) >=
|
|
sizeof (sun.sun_path))
|
|
errx(1, "Path too long: %s", path);
|
|
|
|
while (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
|
|
if (errno != ENOENT)
|
|
err(1, "connect()");
|
|
|
|
if (cradle_server(path, uipath, guipath) == -1)
|
|
errx(1, "failed contacting or starting cradle server");
|
|
}
|
|
|
|
if (dup2(s, fileno(stdin)) == -1)
|
|
err(1, "dup2");
|
|
if (dup2(s, fileno(stdout)) == -1)
|
|
err(1, "dup2");
|
|
setlinebuf(stdout);
|
|
|
|
connected = 1;
|
|
}
|
|
|
|
static void
|
|
listen_cb(int fd, short which, void *arg)
|
|
{
|
|
int s, ui = arg != NULL;
|
|
struct sockaddr sa;
|
|
struct client *cli;
|
|
socklen_t salen = sizeof(sa);
|
|
struct event *ev;
|
|
|
|
s = accept(fd, &sa, &salen);
|
|
if (s == -1) {
|
|
warn("accept()");
|
|
goto out;
|
|
}
|
|
|
|
if (ui) {
|
|
if (ui_fl != NULL)
|
|
goto out;
|
|
|
|
if ((ui_fl = fdopen(s, "w+")) == NULL)
|
|
err(1, "fdopen()");
|
|
setvbuf(ui_fl, NULL, _IONBF, 0);
|
|
event_set(&ui_ev, s, EV_READ | EV_PERSIST, ui_cb, NULL);
|
|
|
|
/* Dequeue UI-pending events */
|
|
while ((cli = TAILQ_FIRST(&clientq)) != NULL) {
|
|
TAILQ_REMOVE(&clientq, cli, next);
|
|
msg_cb(fileno(cli->fl), EV_READ, cli);
|
|
if (ui_fl == NULL)
|
|
break;
|
|
}
|
|
|
|
if (event_add(&ui_ev, NULL) == -1)
|
|
err(1, "event_add()");
|
|
} else {
|
|
if ((cli = calloc(1, sizeof(*cli))) == NULL)
|
|
err(1, "calloc()");
|
|
|
|
if ((cli->fl = fdopen(s, "w+")) == NULL)
|
|
err(1, "fdopen()");
|
|
|
|
setvbuf(cli->fl, NULL, _IONBF, 0);
|
|
event_set(&cli->ev, s, EV_READ, msg_cb, cli);
|
|
if (event_add(&cli->ev, NULL) == -1)
|
|
err(1, "event_add()");
|
|
}
|
|
out:
|
|
ev = ui ? &uilisten_ev : &listen_ev;
|
|
if (event_add(ev, NULL) == -1)
|
|
err(1, "event_add()");
|
|
}
|
|
|
|
static void
|
|
msg_cb(int fd, short which, void *arg)
|
|
{
|
|
struct client *cli = arg;
|
|
char line[4096];
|
|
|
|
if (ui_fl == NULL) {
|
|
TAILQ_INSERT_TAIL(&clientq, cli, next);
|
|
return;
|
|
}
|
|
|
|
/* Policy question from systrace */
|
|
if (!cli->buffered)
|
|
if (fgets(buffer, sizeof(buffer), cli->fl) == NULL)
|
|
goto out_eof;
|
|
cli->buffered = 0;
|
|
|
|
if (fputs(buffer, ui_fl) == EOF)
|
|
goto out_uieof0;
|
|
again_answer:
|
|
/* Policy answer from UI */
|
|
if (fgets(line, sizeof(line), ui_fl) == NULL)
|
|
goto out_uieof0;
|
|
if (fputs(line, cli->fl) == EOF)
|
|
goto out_eof;
|
|
/* Status from systrace */
|
|
while (1) {
|
|
if (fgets(line, sizeof(line), cli->fl) == NULL)
|
|
goto out_eof;
|
|
if (fputs(line, ui_fl) == EOF)
|
|
goto out_uieof1;
|
|
if (strcmp(line, "WRONG\n") == 0)
|
|
goto again_answer;
|
|
if (strcmp(line, "OKAY\n") == 0)
|
|
break;
|
|
}
|
|
|
|
out_event:
|
|
if (event_add(&cli->ev, NULL) == -1)
|
|
err(1, "event_add()");
|
|
return;
|
|
|
|
out_eof:
|
|
fclose(cli->fl);
|
|
free(cli);
|
|
return;
|
|
|
|
out_uieof0:
|
|
fclose(ui_fl);
|
|
ui_fl = NULL;
|
|
cli->buffered = 1;
|
|
TAILQ_INSERT_HEAD(&clientq, cli, next);
|
|
return;
|
|
|
|
out_uieof1:
|
|
fclose(ui_fl);
|
|
ui_fl = NULL;
|
|
while (1) {
|
|
/* We have a line coming in... */
|
|
if (strcmp(line, "WRONG\n") == 0)
|
|
if (fputs("kill\n", cli->fl) == EOF)
|
|
goto out_eof;
|
|
if (strcmp(line, "OKAY\n") == 0)
|
|
break;
|
|
if (fgets(line, sizeof(line), cli->fl) == NULL)
|
|
goto out_eof;
|
|
}
|
|
|
|
goto out_event;
|
|
}
|
|
|
|
/*
|
|
* Hack to catch "idle" EOFs from the UI
|
|
*/
|
|
static void
|
|
ui_cb(int fd, short which, void *arg)
|
|
{
|
|
char c;
|
|
|
|
fread(&c, sizeof(c), 1, ui_fl);
|
|
|
|
if (feof(ui_fl)) {
|
|
ui_fl = NULL;
|
|
event_del(&ui_ev);
|
|
} else
|
|
warnx("Junk from UI");
|
|
}
|