Separate kiosk mode from restrict mode

Restrict mode will focus on sandboxing, while kiosk will continue
to restrict IRC access through a public kiosk. Kiosk mode without
restrict mode allows execution of man 1 catgirl with /help, assuming
external sandboxing.

The /list and /part commands are also added to the list of disabled
commands in kiosk mode, since they are pointless without access to
/join.
master
C. McEnroe 2021-01-23 00:03:58 -05:00
parent 6ee0aea9e5
commit 95bb627ffb
4 changed files with 58 additions and 28 deletions

View File

@ -1,4 +1,4 @@
.Dd January 16, 2021 .Dd January 22, 2021
.Dt CATGIRL 1 .Dt CATGIRL 1
.Os .Os
. .
@ -8,7 +8,7 @@
. .
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl Relv .Op Fl KRelv
.Op Fl C Ar copy .Op Fl C Ar copy
.Op Fl H Ar hash .Op Fl H Ar hash
.Op Fl I Ar highlight .Op Fl I Ar highlight
@ -123,6 +123,20 @@ The commands which can be filtered are:
.Sy QUIT , .Sy QUIT ,
.Sy SETNAME . .Sy SETNAME .
. .
.It Fl K , Cm kiosk
Disable the
.Ic /copy ,
.Ic /debug ,
.Ic /exec ,
.Ic /join ,
.Ic /list ,
.Ic /msg ,
.Ic /open ,
.Ic /part ,
.Ic /query ,
.Ic /quote
commands.
.
.It Fl N Ar util , Cm notify = Ar util .It Fl N Ar util , Cm notify = Ar util
Send notifications using a utility. Send notifications using a utility.
Use more than once to add arguments to Use more than once to add arguments to
@ -145,14 +159,19 @@ The default is the first available of
.It Fl R , Cm restrict .It Fl R , Cm restrict
Disable the Disable the
.Ic /copy , .Ic /copy ,
.Ic /debug , .Ic /exec
.Ic /exec , and
.Ic /join , .Ic /open
.Ic /msg , commands,
.Ic /open , as well as viewing this manual with
.Ic /query , .Ic /help .
.Ic /quote On
commands. .Ox ,
restrict system operations
and filesystem access with
.Xr pledge 2
and
.Xr unveil 2 .
. .
.It Fl S Ar host , Cm bind = Ar host .It Fl S Ar host , Cm bind = Ar host
Bind to source address Bind to source address

8
chat.c
View File

@ -159,7 +159,6 @@ static void sandbox(const char *trust, const char *cert, const char *priv) {
const char *path; const char *path;
const char *perm; const char *perm;
} paths[] = { } paths[] = {
{ "/usr/bin/man", "x" },
{ "/usr/share/terminfo", "r" }, { "/usr/share/terminfo", "r" },
{ tls_default_ca_cert_file(), "r" }, { tls_default_ca_cert_file(), "r" },
{ NULL, NULL }, { NULL, NULL },
@ -200,6 +199,7 @@ int main(int argc, char *argv[]) {
{ .val = 'C', .name = "copy", required_argument }, { .val = 'C', .name = "copy", required_argument },
{ .val = 'H', .name = "hash", required_argument }, { .val = 'H', .name = "hash", required_argument },
{ .val = 'I', .name = "highlight", required_argument }, { .val = 'I', .name = "highlight", required_argument },
{ .val = 'K', .name = "kiosk", no_argument },
{ .val = 'N', .name = "notify", required_argument }, { .val = 'N', .name = "notify", required_argument },
{ .val = 'O', .name = "open", required_argument }, { .val = 'O', .name = "open", required_argument },
{ .val = 'R', .name = "restrict", no_argument }, { .val = 'R', .name = "restrict", no_argument },
@ -236,6 +236,7 @@ int main(int argc, char *argv[]) {
break; case 'C': utilPush(&urlCopyUtil, optarg); break; case 'C': utilPush(&urlCopyUtil, optarg);
break; case 'H': parseHash(optarg); break; case 'H': parseHash(optarg);
break; case 'I': filterAdd(Hot, optarg); break; case 'I': filterAdd(Hot, optarg);
break; case 'K': self.kiosk = true;
break; case 'N': utilPush(&uiNotifyUtil, optarg); break; case 'N': utilPush(&uiNotifyUtil, optarg);
break; case 'O': utilPush(&urlOpenUtil, optarg); break; case 'O': utilPush(&urlOpenUtil, optarg);
break; case 'R': self.restricted = true; break; case 'R': self.restricted = true;
@ -325,7 +326,8 @@ int main(int argc, char *argv[]) {
sig_t cursesWinch = signal(SIGWINCH, signalHandler); sig_t cursesWinch = signal(SIGWINCH, signalHandler);
fcntl(irc, F_SETFD, FD_CLOEXEC); fcntl(irc, F_SETFD, FD_CLOEXEC);
if (!self.restricted) { bool pipes = !self.kiosk && !self.restricted;
if (pipes) {
int error = pipe(utilPipe); int error = pipe(utilPipe);
if (error) err(EX_OSERR, "pipe"); if (error) err(EX_OSERR, "pipe");
@ -345,7 +347,7 @@ int main(int argc, char *argv[]) {
{ .events = POLLIN, .fd = execPipe[0] }, { .events = POLLIN, .fd = execPipe[0] },
}; };
while (!self.quit) { while (!self.quit) {
int nfds = poll(fds, (self.restricted ? 2 : ARRAY_LEN(fds)), -1); int nfds = poll(fds, (pipes ? ARRAY_LEN(fds) : 2), -1);
if (nfds < 0 && errno != EINTR) err(EX_IOERR, "poll"); if (nfds < 0 && errno != EINTR) err(EX_IOERR, "poll");
if (nfds > 0) { if (nfds > 0) {
if (fds[0].revents) uiRead(); if (fds[0].revents) uiRead();

1
chat.h
View File

@ -183,6 +183,7 @@ enum Cap {
extern struct Self { extern struct Self {
bool debug; bool debug;
bool kiosk;
bool restricted; bool restricted;
size_t pos; size_t pos;
enum Cap caps; enum Cap caps;

View File

@ -457,6 +457,10 @@ static void commandHelp(uint id, char *params) {
replies[ReplyHelp]++; replies[ReplyHelp]++;
return; return;
} }
if (self.restricted) {
uiFormat(id, Warm, NULL, "See catgirl(1) or /help index");
return;
}
uiHide(); uiHide();
pid_t pid = fork(); pid_t pid = fork();
@ -474,7 +478,8 @@ static void commandHelp(uint id, char *params) {
enum Flag { enum Flag {
BIT(Multiline), BIT(Multiline),
BIT(Restricted), BIT(Restrict),
BIT(Kiosk),
}; };
static const struct Handler { static const struct Handler {
@ -485,37 +490,37 @@ static const struct Handler {
{ "/away", commandAway, 0 }, { "/away", commandAway, 0 },
{ "/ban", commandBan, 0 }, { "/ban", commandBan, 0 },
{ "/close", commandClose, 0 }, { "/close", commandClose, 0 },
{ "/copy", commandCopy, Restricted }, { "/copy", commandCopy, Restrict | Kiosk },
{ "/cs", commandCS, 0 }, { "/cs", commandCS, 0 },
{ "/debug", commandDebug, Restricted }, { "/debug", commandDebug, Kiosk },
{ "/deop", commandDeop, 0 }, { "/deop", commandDeop, 0 },
{ "/devoice", commandDevoice, 0 }, { "/devoice", commandDevoice, 0 },
{ "/except", commandExcept, 0 }, { "/except", commandExcept, 0 },
{ "/exec", commandExec, Multiline | Restricted }, { "/exec", commandExec, Multiline | Restrict },
{ "/help", commandHelp, 0 }, { "/help", commandHelp, 0 }, // Restrict special case.
{ "/highlight", commandHighlight, 0 }, { "/highlight", commandHighlight, 0 },
{ "/ignore", commandIgnore, 0 }, { "/ignore", commandIgnore, 0 },
{ "/invex", commandInvex, 0 }, { "/invex", commandInvex, 0 },
{ "/invite", commandInvite, 0 }, { "/invite", commandInvite, 0 },
{ "/join", commandJoin, Restricted }, { "/join", commandJoin, Kiosk },
{ "/kick", commandKick, 0 }, { "/kick", commandKick, 0 },
{ "/list", commandList, 0 }, { "/list", commandList, Kiosk },
{ "/me", commandMe, 0 }, { "/me", commandMe, 0 },
{ "/mode", commandMode, 0 }, { "/mode", commandMode, 0 },
{ "/move", commandMove, 0 }, { "/move", commandMove, 0 },
{ "/msg", commandMsg, Multiline | Restricted }, { "/msg", commandMsg, Multiline | Kiosk },
{ "/names", commandNames, 0 }, { "/names", commandNames, 0 },
{ "/nick", commandNick, 0 }, { "/nick", commandNick, 0 },
{ "/notice", commandNotice, Multiline }, { "/notice", commandNotice, Multiline },
{ "/ns", commandNS, 0 }, { "/ns", commandNS, 0 },
{ "/o", commandOpen, Restricted }, { "/o", commandOpen, Restrict | Kiosk },
{ "/op", commandOp, 0 }, { "/op", commandOp, 0 },
{ "/open", commandOpen, Restricted }, { "/open", commandOpen, Restrict | Kiosk },
{ "/ops", commandOps, 0 }, { "/ops", commandOps, 0 },
{ "/part", commandPart, 0 }, { "/part", commandPart, Kiosk },
{ "/query", commandQuery, Restricted }, { "/query", commandQuery, Kiosk },
{ "/quit", commandQuit, 0 }, { "/quit", commandQuit, 0 },
{ "/quote", commandQuote, Multiline | Restricted }, { "/quote", commandQuote, Multiline | Kiosk },
{ "/say", commandPrivmsg, Multiline }, { "/say", commandPrivmsg, Multiline },
{ "/setname", commandSetname, 0 }, { "/setname", commandSetname, 0 },
{ "/topic", commandTopic, 0 }, { "/topic", commandTopic, 0 },
@ -584,8 +589,11 @@ void command(uint id, char *input) {
uiFormat(id, Warm, NULL, "No such command %s", cmd); uiFormat(id, Warm, NULL, "No such command %s", cmd);
return; return;
} }
if (self.restricted && handler->flags & Restricted) { if (
uiFormat(id, Warm, NULL, "Command %s is restricted", cmd); (self.restricted && handler->flags & Restrict) ||
(self.kiosk && handler->flags & Kiosk)
) {
uiFormat(id, Warm, NULL, "Command %s is unavailable", cmd);
return; return;
} }