mirror of https://github.com/dirtbags/moth.git
Add puzzler.cgi, put more into common.c
This commit is contained in:
parent
c9c24d9a8b
commit
c3bf43b132
|
@ -1,10 +1,11 @@
|
||||||
TARGETS = in.tokend claim.cgi pointscli
|
TARGETS = in.tokend claim.cgi puzzler.cgi pointscli
|
||||||
|
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
|
|
||||||
in.tokend: in.tokend.o xxtea.o
|
in.tokend: in.tokend.o xxtea.o
|
||||||
|
|
||||||
claim.cgi: claim.cgi.o cgi.o common.o
|
claim.cgi: claim.cgi.o cgi.o common.o
|
||||||
|
puzzler.cgi: puzzler.cgi.o cgi.o common.o
|
||||||
pointscli: common.o pointscli.o
|
pointscli: common.o pointscli.o
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
36
src/cgi.c
36
src/cgi.c
|
@ -11,10 +11,10 @@ cgi_init()
|
||||||
char *rm = getenv("REQUEST_METHOD");
|
char *rm = getenv("REQUEST_METHOD");
|
||||||
|
|
||||||
if (! (rm && (0 == strcmp(rm, "POST")))) {
|
if (! (rm && (0 == strcmp(rm, "POST")))) {
|
||||||
printf("405 Method not allowed\n"
|
printf("405 Method not allowed\r\n"
|
||||||
"Allow: POST\n"
|
"Allow: POST\r\n"
|
||||||
"Content-type: text/html\n"
|
"Content-type: text/html\r\n"
|
||||||
"\n"
|
"\r\n"
|
||||||
"<h1>Method not allowed</h1>\n"
|
"<h1>Method not allowed</h1>\n"
|
||||||
"<p>I only speak POST. Sorry.</p>\n");
|
"<p>I only speak POST. Sorry.</p>\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -93,23 +93,25 @@ cgi_item(char *str, size_t maxlen)
|
||||||
void
|
void
|
||||||
cgi_page(char *title, char *fmt, ...)
|
cgi_page(char *title, char *fmt, ...)
|
||||||
{
|
{
|
||||||
FILE *p;
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
printf("Content-type: text/html\r\n"
|
printf(("Content-type: text/html\r\n"
|
||||||
"\r\n");
|
"\r\n"
|
||||||
fflush(stdout);
|
"<!DOCTYPE html>\n"
|
||||||
p = popen("./template", "w");
|
"<html>\n"
|
||||||
if (NULL == p) {
|
" <head>\n"
|
||||||
printf("<h1>%s</h1>\n", title);
|
" <title>%s</title>\n"
|
||||||
p = stdout;
|
" <link rel=\"stylesheet\" href=\"ctf.css\" type=\"text/css\">\n"
|
||||||
} else {
|
" </head>\n"
|
||||||
fprintf(p, "Title: %s\n", title);
|
" <body>\n"
|
||||||
}
|
" <h1>%s</h1>\n"),
|
||||||
|
title, title);
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vfprintf(p, fmt, ap);
|
vprintf(fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
fclose(p);
|
printf("\n"
|
||||||
|
" </body>\n"
|
||||||
|
"</html>\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
141
src/claim.cgi.c
141
src/claim.cgi.c
|
@ -11,12 +11,10 @@ int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
char team[9];
|
char team[9];
|
||||||
size_t teamlen;
|
|
||||||
char token[100];
|
char token[100];
|
||||||
size_t tokenlen;
|
|
||||||
|
|
||||||
/* XXX: This code needs to be tested */
|
/* XXX: This code needs to be tested */
|
||||||
abort();
|
return 1;
|
||||||
|
|
||||||
if (-1 == cgi_init()) {
|
if (-1 == cgi_init()) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -26,34 +24,16 @@ main(int argc, char *argv[])
|
||||||
while (1) {
|
while (1) {
|
||||||
size_t len;
|
size_t len;
|
||||||
char key[20];
|
char key[20];
|
||||||
int i;
|
|
||||||
|
|
||||||
len = cgi_item(key, sizeof(key));
|
len = cgi_item(key, sizeof(key));
|
||||||
if (0 == len) break;
|
if (0 == len) break;
|
||||||
if (1 == len) {
|
switch (key[0]) {
|
||||||
if ('t' == key[0]) {
|
case 't':
|
||||||
teamlen = cgi_item(team, sizeof(team) - 1);
|
cgi_item(team, sizeof(team));
|
||||||
|
break;
|
||||||
/* End string at # read or first bad char */
|
case 'k':
|
||||||
for (i = 0; i < teamlen; i += 1) {
|
cgi_item(token, sizeof(token));
|
||||||
if (! isalnum(team[i])) {
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
team[i] = '\0';
|
|
||||||
} else if ('k' == key[0]) {
|
|
||||||
tokenlen = cgi_item(token, sizeof(token) - 1);
|
|
||||||
|
|
||||||
/* End string at # read or first bad char */
|
|
||||||
for (i = 0; i < tokenlen; i += 1) {
|
|
||||||
if ((! isalnum(token[i])) &&
|
|
||||||
(token[i] != '-') &&
|
|
||||||
(token[i] != ':')) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
token[i] = '\0';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,87 +41,42 @@ main(int argc, char *argv[])
|
||||||
cgi_page("No such team", "");
|
cgi_page("No such team", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! fgrepx(token, claimlog)) {
|
/* Any weird characters in token name? */
|
||||||
cgi_page("Invalid token",
|
|
||||||
"<p>Sorry, that token's no good.</p>");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the token's unclaimed, award points and log the claim */
|
|
||||||
{
|
{
|
||||||
FILE *f;
|
char *p;
|
||||||
int claimed = 0;
|
|
||||||
char needle[100];
|
|
||||||
|
|
||||||
f = fopen(claimlog, "rw");
|
for (p = token; *p; p += 1) {
|
||||||
if (! f) {
|
if ((! isalnum(*p)) &&
|
||||||
cgi_error("Couldn't fopen(\"%s\", \"rw\")", claimlog);
|
(*p != '-') &&
|
||||||
}
|
(*p != ':')) {
|
||||||
sprintf(needle, "%s %s", team, token);
|
cgi_page("Invalid token", "");
|
||||||
while (1) {
|
|
||||||
char line[100];
|
|
||||||
char *p;
|
|
||||||
int pos;
|
|
||||||
|
|
||||||
if (NULL == fgets(line, sizeof(line), f)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Skip to past first space */
|
|
||||||
for (p = line; (*p && (*p != ' ')); p += 1);
|
|
||||||
|
|
||||||
if (0 == mystrcmp(p, needle)) {
|
|
||||||
claimed = 1;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (claimed) {
|
|
||||||
cgi_page("Already claimed",
|
|
||||||
"<p>Apparently you've already claimed that token.</p>");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now register the points */
|
|
||||||
{
|
|
||||||
char category[20];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Pull category name out of the token */
|
|
||||||
for (i = 0; token[i] != ':'; i += 1) {
|
|
||||||
category[i] = token[i];
|
|
||||||
}
|
|
||||||
category[i] = '\0';
|
|
||||||
|
|
||||||
award_points(team, category, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finally, append an entry to the log file. I figure it's better
|
|
||||||
to give points first and fail here, than it is to lock them out
|
|
||||||
of making points and then fail to award them. */
|
|
||||||
{
|
|
||||||
char timestamp[40];
|
|
||||||
time_t t;
|
|
||||||
struct tm *tmp;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
t = time(NULL);
|
|
||||||
tmp = localtime(&t);
|
|
||||||
if (NULL == tmp) {
|
|
||||||
cgi_error("I... uh... couldn't figure out what time it is.");
|
|
||||||
}
|
|
||||||
if (0 == strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", tmp)) {
|
|
||||||
cgi_error("I forgot how to format time.");
|
|
||||||
}
|
|
||||||
fseek(f, 0, SEEK_SET);
|
|
||||||
if (-1 == lockf(fileno(f), F_LOCK, 0)) {
|
|
||||||
cgi_error("Unable to lock the log file.");
|
|
||||||
}
|
|
||||||
fseek(f, 0, SEEK_END);
|
|
||||||
fprintf(f, "%s %s %s\n", timestamp, team, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cgi_page("Points awarded", "<!-- success -->");
|
|
||||||
|
/* Does the token exist? */
|
||||||
|
if (! fgrepx(token, tokenlog)) {
|
||||||
|
cgi_page("Token does not exist", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Award points */
|
||||||
|
{
|
||||||
|
char category[40];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Pull category name out of the token */
|
||||||
|
for (i = 0; token[i] != ':'; i += 1) {
|
||||||
|
category[i] = token[i];
|
||||||
|
}
|
||||||
|
category[i] = '\0';
|
||||||
|
|
||||||
|
award_and_log_uniquely(team, category, 1,
|
||||||
|
claimlog, "%s %s", team, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cgi_page("Point awarded", "<!-- success -->");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
93
src/common.c
93
src/common.c
|
@ -2,9 +2,11 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include "cgi.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#define EOL(c) ((EOF == (c)) || (0 == (c)) || ('\n' == (c)))
|
#define EOL(c) ((EOF == (c)) || (0 == (c)) || ('\n' == (c)))
|
||||||
|
@ -46,7 +48,24 @@ fgrepx(char const *needle, char const *filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
team_exists(char *teamhash)
|
my_snprintf(char *buf, size_t buflen, char *fmt, ...)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
len = vsnprintf(buf, buflen - 1, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
if (len >= 0) {
|
||||||
|
buf[len] = '\0';
|
||||||
|
return len;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
team_exists(char const *teamhash)
|
||||||
{
|
{
|
||||||
struct stat buf;
|
struct stat buf;
|
||||||
char filename[100];
|
char filename[100];
|
||||||
|
@ -63,7 +82,7 @@ team_exists(char *teamhash)
|
||||||
/* Build filename. */
|
/* Build filename. */
|
||||||
ret = snprintf(filename, sizeof(filename),
|
ret = snprintf(filename, sizeof(filename),
|
||||||
"%s/%s", teamdir, teamhash);
|
"%s/%s", teamdir, teamhash);
|
||||||
if (sizeof(filename) == ret) {
|
if (sizeof(filename) <= ret) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +96,9 @@ team_exists(char *teamhash)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
award_points(char *teamhash, char *category, int points)
|
award_points(char const *teamhash,
|
||||||
|
char const *category,
|
||||||
|
int points)
|
||||||
{
|
{
|
||||||
char line[100];
|
char line[100];
|
||||||
int linelen;
|
int linelen;
|
||||||
|
@ -94,7 +115,7 @@ award_points(char *teamhash, char *category, int points)
|
||||||
linelen = snprintf(line, sizeof(line),
|
linelen = snprintf(line, sizeof(line),
|
||||||
"%u %s %s %d\n",
|
"%u %s %s %d\n",
|
||||||
now, teamhash, category, points);
|
now, teamhash, category, points);
|
||||||
if (sizeof(line) == linelen) {
|
if (sizeof(line) <= linelen) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,8 +123,8 @@ award_points(char *teamhash, char *category, int points)
|
||||||
This works, as long as nobody ever tries to edit the log file.
|
This works, as long as nobody ever tries to edit the log file.
|
||||||
Editing the log file would require locking it, which would block
|
Editing the log file would require locking it, which would block
|
||||||
everything trying to score, effectively taking down the entire
|
everything trying to score, effectively taking down the entire
|
||||||
contest. If you can't lock it first (nothing in busybox lets you
|
contest. If you can't lock it first--and nothing in busybox lets
|
||||||
do this), you have to bring down pretty much everything manually
|
you do this--you have to bring down pretty much everything manually
|
||||||
anyway.
|
anyway.
|
||||||
|
|
||||||
By putting new scores into new files and periodically appending
|
By putting new scores into new files and periodically appending
|
||||||
|
@ -115,13 +136,20 @@ award_points(char *teamhash, char *category, int points)
|
||||||
nicer on the fs.
|
nicer on the fs.
|
||||||
|
|
||||||
The fact that this makes the code simpler is just gravy.
|
The fact that this makes the code simpler is just gravy.
|
||||||
|
|
||||||
|
Note that doing this means there's a little time between when a
|
||||||
|
score's written and when the scoreboard (which only reads the log
|
||||||
|
file) picks it up. It's not a big deal for the points log, but
|
||||||
|
this situation makes this technique unsuitable for writing log
|
||||||
|
files that prevent people from double-scoring, like the puzzler or
|
||||||
|
token log.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
filenamelen = snprintf(filename, sizeof(filename),
|
filenamelen = snprintf(filename, sizeof(filename),
|
||||||
"%s/%d.%d.%s.%s.%d",
|
"%s/%d.%d.%s.%s.%d",
|
||||||
pointsdir, now, getpid(),
|
pointsdir, now, getpid(),
|
||||||
teamhash, category, points);
|
teamhash, category, points);
|
||||||
if (sizeof(filename) == filenamelen) {
|
if (sizeof(filename) <= filenamelen) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,3 +166,54 @@ award_points(char *teamhash, char *category, int points)
|
||||||
close(fd);
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
award_and_log_uniquely(char const *team,
|
||||||
|
char const *category,
|
||||||
|
int points,
|
||||||
|
char const *logfile,
|
||||||
|
char const *fmt, ...)
|
||||||
|
{
|
||||||
|
char line[100];
|
||||||
|
int len;
|
||||||
|
int ret;
|
||||||
|
int fd;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
/* Make sure they haven't already claimed these points */
|
||||||
|
/* Leave room for a newline later on */
|
||||||
|
va_start(ap, fmt);
|
||||||
|
len = vsnprintf(line, sizeof(line)-1, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
if (sizeof(line) <= len) {
|
||||||
|
cgi_error("Log line too long");
|
||||||
|
}
|
||||||
|
if (fgrepx(line, logfile)) {
|
||||||
|
cgi_page("Already claimed",
|
||||||
|
"<p>Your team has already claimed these points.</p>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open and lock logfile */
|
||||||
|
fd = open(logfile, O_WRONLY | O_CREAT, 0666);
|
||||||
|
if (-1 == fd) {
|
||||||
|
cgi_error("Unable to open log");
|
||||||
|
}
|
||||||
|
if (-1 == lockf(fd, F_LOCK, 0)) {
|
||||||
|
cgi_error("Unable to lock log");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Award points */
|
||||||
|
if (0 != award_points(team, category, points)) {
|
||||||
|
cgi_error("Unable to award points");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Log that we did so */
|
||||||
|
/* We can turn that trailing NUL into a newline now since write
|
||||||
|
doesn't use C strings */
|
||||||
|
line[len] = '\n';
|
||||||
|
lseek(fd, 0, SEEK_END);
|
||||||
|
if (-1 == write(fd, line, len+1)) {
|
||||||
|
cgi_error("Unable to log your award");
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
11
src/common.h
11
src/common.h
|
@ -4,8 +4,15 @@
|
||||||
#define teamdir "/var/lib/ctf/teams/names"
|
#define teamdir "/var/lib/ctf/teams/names"
|
||||||
#define pointsdir "/var/lib/ctf/points/new"
|
#define pointsdir "/var/lib/ctf/points/new"
|
||||||
|
|
||||||
int team_exists(char *teamhash);
|
|
||||||
int award_points(char *teamhash, char *category, int point);
|
|
||||||
int fgrepx(char const *needle, char const *filename);
|
int fgrepx(char const *needle, char const *filename);
|
||||||
|
int team_exists(char const *teamhash);
|
||||||
|
int award_points(char const *teamhash,
|
||||||
|
char const *category,
|
||||||
|
int point);
|
||||||
|
void award_and_log_uniquely(char const *team,
|
||||||
|
char const *category,
|
||||||
|
int points,
|
||||||
|
char const *logfile,
|
||||||
|
char const *fmt, ...);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "common.h"
|
||||||
|
#include "cgi.h"
|
||||||
|
|
||||||
|
char const *logfile = "/var/lib/ctf/puzzler.log";
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv)
|
||||||
|
{
|
||||||
|
char team[9];
|
||||||
|
char category[30];
|
||||||
|
char points_str[5];
|
||||||
|
char answer[500];
|
||||||
|
int points;
|
||||||
|
|
||||||
|
if (-1 == cgi_init()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read in team and answer */
|
||||||
|
while (1) {
|
||||||
|
size_t len;
|
||||||
|
char key[20];
|
||||||
|
|
||||||
|
len = cgi_item(key, sizeof(key));
|
||||||
|
if (0 == len) break;
|
||||||
|
switch (key[0]) {
|
||||||
|
case 't':
|
||||||
|
cgi_item(team, sizeof(team));
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
cgi_item(category, sizeof(category));
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
cgi_item(points_str, sizeof(points_str));
|
||||||
|
points = atoi(points_str);
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
cgi_item(answer, sizeof(answer));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check to see if team exists */
|
||||||
|
if (! team_exists(team)) {
|
||||||
|
cgi_page("No such team", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate category name (prevent directory traversal) */
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
for (p = category; *p; p += 1) {
|
||||||
|
if (! isalnum(*p)) {
|
||||||
|
cgi_page("Invalid category", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check answer (also assures category exists) */
|
||||||
|
{
|
||||||
|
char filename[100];
|
||||||
|
char needle[100];
|
||||||
|
|
||||||
|
my_snprintf(filename, sizeof(filename),
|
||||||
|
"/srv/%s/answers.txt", category);
|
||||||
|
my_snprintf(needle, sizeof(needle),
|
||||||
|
"%d %s", points, answer);
|
||||||
|
if (! fgrepx(needle, filename)) {
|
||||||
|
cgi_page("Wrong answer", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
award_and_log_uniquely(team, category, points,
|
||||||
|
logfile, "%s %s %d", team, category, points);
|
||||||
|
|
||||||
|
cgi_page("Points awarded",
|
||||||
|
"<p>%d points for %s.</p>", points, team);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue