Results of code review

This commit is contained in:
Neale Pickett 2010-09-14 18:04:33 -06:00
parent bbd4405491
commit 04d3c2c66c
14 changed files with 153 additions and 67 deletions

Binary file not shown.

View File

@ -42,8 +42,8 @@
inkscape:document-units="px" inkscape:document-units="px"
inkscape:current-layer="layer1" inkscape:current-layer="layer1"
showgrid="false" showgrid="false"
inkscape:window-width="704" inkscape:window-width="1276"
inkscape:window-height="1010" inkscape:window-height="1006"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="14" /> inkscape:window-y="14" />
<metadata <metadata
@ -322,7 +322,7 @@
sodipodi:role="line" sodipodi:role="line"
x="381.88013" x="381.88013"
y="787.68048" y="787.68048"
id="tspan2823">and NMT ACS chapter</tspan><tspan id="tspan2823">and NMT ACM chapter</tspan><tspan
sodipodi:role="line" sodipodi:role="line"
x="381.88013" x="381.88013"
y="811.68048" y="811.68048"

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

View File

@ -17,7 +17,15 @@
inkscape:output_extension="org.inkscape.output.svg.inkscape" inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.0"> version="1.0">
<defs <defs
id="defs2569" /> id="defs2569">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 495 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="765 : 495 : 1"
inkscape:persp3d-origin="382.5 : 330 : 1"
id="perspective2545" />
</defs>
<sodipodi:namedview <sodipodi:namedview
id="base" id="base"
pagecolor="#ffffff" pagecolor="#ffffff"
@ -703,7 +711,7 @@
sodipodi:role="line" sodipodi:role="line"
x="381.88013" x="381.88013"
y="787.68048" y="787.68048"
id="tspan2823">and NMT ACS chapter</tspan><tspan id="tspan2823">and NMT ACM chapter</tspan><tspan
sodipodi:role="line" sodipodi:role="line"
x="381.88013" x="381.88013"
y="811.68048" y="811.68048"

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

View File

@ -70,8 +70,8 @@
inkscape:document-units="px" inkscape:document-units="px"
inkscape:current-layer="layer1" inkscape:current-layer="layer1"
showgrid="false" showgrid="false"
inkscape:window-width="704" inkscape:window-width="1276"
inkscape:window-height="1010" inkscape:window-height="1006"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="14" inkscape:window-y="14"
showguides="true" showguides="true"
@ -119,7 +119,7 @@
sodipodi:role="line" sodipodi:role="line"
x="381.88013" x="381.88013"
y="787.68048" y="787.68048"
id="tspan2823">and NMT ACS chapter</tspan><tspan id="tspan2823">and NMT ACM chapter</tspan><tspan
sodipodi:role="line" sodipodi:role="line"
x="381.88013" x="381.88013"
y="811.68048" y="811.68048"

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

49
doc/packages.txt Normal file
View File

@ -0,0 +1,49 @@
CTF Packages
============
Packages are squashfs files.
A hypothetical package named pkgname.sfs will be mounted under
/srv/pkgname. The following top-level files and directories are
significant:
* /www/ - Will appear as http://host/pkgname/, CGI can be run
* /bin/ - Will be added to $PATH
* /puzzles/ - Will appear as a puzzle category (see "Puzzles" below)
* /answers.txt - Puzzle answers for category pkgname (see "Puzzles" below)
Puzzles
-------
To expose puzzles, place them in the /puzzles/ directory, like so:
/puzzles/10/index.html
/puzzles/20/index.html
/puzzles/20/script.cgi
/puzzles/30/index.html
/puzzles/30/something.jpg
/puzzles/40/index.html
where the second directory is the point value of the puzzle. This means
that no two puzzles in a category can have the same point value.
Files will be served up from the web server, and CGI scripts will be
executed.
Store answers to your puzzles in /answers.txt. Answers are one per
line, with the point value appearing first, followed by a space, then
the answer. Answers are case sensitive. You may have multiple answers
for each point value.
10 zip file
10 zip
10 ZIP
10 pkzip
10 PKZIP
20 varname
30 JFIF
40 0x8040fe67
40 8040FE67
40 8040fe67

View File

@ -1,3 +1,4 @@
CFLAGS = -Wall
TARGETS = in.tokend pointscli claim.cgi puzzler.cgi puzzles.cgi TARGETS = in.tokend pointscli claim.cgi puzzler.cgi puzzles.cgi
all: build all: build

View File

@ -4,11 +4,8 @@
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
char team[9]; char team[TEAM_MAX] = {0};
char token[100]; char token[TOKEN_MAX] = {0};
team[0] = 0;
token[0] = 0;
if (-1 == cgi_init(argv)) { if (-1 == cgi_init(argv)) {
return 0; return 0;
@ -65,9 +62,14 @@ main(int argc, char *argv[])
} }
category[i] = '\0'; category[i] = '\0';
award_and_log_uniquely(team, category, 1, {
"tokens.db", char line[TEAM_MAX + TOKEN_MAX + 1];
my_snprintf(line, sizeof(line),
"%s %s", team, token); "%s %s", team, token);
award_and_log_uniquely(team, category, 1,
"tokens.db", line);
}
} }

View File

@ -10,6 +10,21 @@
#include <time.h> #include <time.h>
#include "common.h" #include "common.h"
#ifdef NODUMP
# define DUMPf(fmt, args...)
#else
# define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args)
#endif
#define DUMP() DUMPf("")
#define DUMP_d(v) DUMPf("%s = %d", #v, v)
#define DUMP_x(v) DUMPf("%s = 0x%x", #v, v)
#define DUMP_s(v) DUMPf("%s = %s", #v, v)
#define DUMP_c(v) DUMPf("%s = '%c' (0x%02x)", #v, v, v)
#define DUMP_p(v) DUMPf("%s = %p", #v, v)
#define POST_MAX 1024
/* /*
* CGI * CGI
*/ */
@ -51,6 +66,9 @@ read_char_stdin()
char *p = getenv("CONTENT_LENGTH"); char *p = getenv("CONTENT_LENGTH");
if (p) { if (p) {
inlen = atoi(p); inlen = atoi(p);
if (inlen > POST_MAX) {
inlen = POST_MAX;
}
} else { } else {
inlen = 0; inlen = 0;
} }
@ -224,7 +242,7 @@ cgi_error(char *fmt, ...)
*/ */
#define EOL(c) ((EOF == (c)) || (0 == (c)) || ('\n' == (c))) #define EOL(c) ((EOF == (c)) || ('\n' == (c)))
int int
fgrepx(char const *needle, char const *filename) fgrepx(char const *needle, char const *filename)
@ -240,19 +258,20 @@ fgrepx(char const *needle, char const *filename)
/* This list of cases would have looked so much nicer in OCaml. I /* This list of cases would have looked so much nicer in OCaml. I
apologize. */ apologize. */
if (EOL(c) && (0 == *p)) { if (EOL(c) && ('\0' == *p)) {
found = 1; found = 1;
break; break;
} else if (EOF == c) { } else if (EOF == c) { /* End of file */
break; break;
} else if ((0 == p) || (*p != c)) { } else if (('\0' == p) || (*p != c)) {
p = needle; p = needle;
/* Discard the rest of the line */
do { do {
c = fgetc(f); c = fgetc(f);
} while (! EOL(c)); } while (! EOL(c));
} else if ('\n' == c) { } else if (EOL(c)) {
p = needle; p = needle;
} else { } else { /* It matched */
p += 1; p += 1;
} }
} }
@ -271,7 +290,7 @@ my_snprintf(char *buf, size_t buflen, char *fmt, ...)
va_start(ap, fmt); va_start(ap, fmt);
len = vsnprintf(buf, buflen - 1, fmt, ap); len = vsnprintf(buf, buflen - 1, fmt, ap);
va_end(ap); va_end(ap);
buf[buflen] = '\0'; buf[buflen - 1] = '\0';
if (len >= buflen) { if (len >= buflen) {
return buflen - 1; return buflen - 1;
} else { } else {
@ -320,8 +339,8 @@ team_exists(char const *teamhash)
} }
} }
/* lstat seems to be the preferred way to check for existence. */ /* stat seems to be the preferred way to check for existence. */
ret = lstat(srv_path("teams/names/%s", teamhash), &buf); ret = stat(srv_path("teams/names/%s", teamhash), &buf);
if (-1 == ret) { if (-1 == ret) {
return 0; return 0;
} }
@ -396,27 +415,23 @@ award_points(char const *teamhash,
return 0; return 0;
} }
/** Award points iff they haven't been logged.
If [line] is not in [dbfile], append it and give [points] to [team]
in [category].
*/
void void
award_and_log_uniquely(char const *team, award_and_log_uniquely(char const *team,
char const *category, char const *category,
long points, long points,
char const *dbfile, char const *dbfile,
char const *fmt, ...) char const *line)
{ {
char *dbpath = srv_path(dbfile); char *dbpath = srv_path(dbfile);
char line[200];
int len;
int ret; int ret;
int fd; int fd;
va_list ap;
/* Make sure they haven't already claimed these points */ /* Make sure they haven't already claimed these points */
va_start(ap, fmt);
len = vsnprintf(line, sizeof(line), fmt, ap);
va_end(ap);
if (sizeof(line) <= len) {
cgi_error("Log line too long");
}
if (fgrepx(line, dbpath)) { if (fgrepx(line, dbpath)) {
cgi_page("Already claimed", cgi_page("Already claimed",
"<p>Your team has already claimed these points.</p>"); "<p>Your team has already claimed these points.</p>");
@ -437,11 +452,11 @@ award_and_log_uniquely(char const *team,
} }
/* Log that we did so */ /* 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); lseek(fd, 0, SEEK_END);
if (-1 == write(fd, line, len+1)) { if (-1 == write(fd, line, strlen(line))) {
cgi_error("Unable to append log");
}
if (-1 == write(fd, "\n", 1)) {
cgi_error("Unable to append log"); cgi_error("Unable to append log");
} }
close(fd); close(fd);

View File

@ -5,6 +5,7 @@
#define TEAM_MAX 40 #define TEAM_MAX 40
#define CAT_MAX 40 #define CAT_MAX 40
#define TOKEN_MAX 40
int cgi_init(char *global_argv[]); int cgi_init(char *global_argv[]);
size_t cgi_item(char *str, size_t maxlen); size_t cgi_item(char *str, size_t maxlen);
@ -24,6 +25,6 @@ void award_and_log_uniquely(char const *team,
char const *category, char const *category,
long points, long points,
char const *logfile, char const *logfile,
char const *fmt, ...); char const *line);
#endif #endif

View File

@ -120,7 +120,7 @@ main(int argc, char *argv[])
bubblebabble(digest, crap, itokenlen); bubblebabble(digest, crap, itokenlen);
/* Append digest to service name. */ /* Append digest to service name. */
tokenlen = (size_t)snprintf(token, sizeof(token), tokenlen = (size_t)my_snprintf(token, sizeof(token),
"%s:%s", "%s:%s",
service, digest); service, digest);
} }
@ -131,7 +131,7 @@ main(int argc, char *argv[])
int ret; int ret;
do { do {
fd = open(srv_path("tokens.db"), O_WRONLY | O_CREAT, 0644); fd = open(srv_path("tokens.db"), O_WRONLY | O_CREAT, 0666);
if (-1 == fd) break; if (-1 == fd) break;
ret = lockf(fd, F_LOCK, 0); ret = lockf(fd, F_LOCK, 0);
@ -150,16 +150,18 @@ main(int argc, char *argv[])
if (-1 == ret) break; if (-1 == ret) break;
} while (0); } while (0);
if (-1 == ret) { if ((-1 == fd) || (-1 == ret)) {
printf("!%s", strerror(errno)); printf("!%s", strerror(errno));
return 0; return 0;
} }
} }
/* Encrypt the token. Note that now tokenlen is in uint32_ts, not /* Encrypt the token. Note that now tokenlen is in uint32_ts, not
chars! */ chars! Also remember that token must be big enough to hold a
multiple of 4 chars, since tea will go ahead and jumble them up for
you. If the compiler aligns words this shouldn't be a problem. */
{ {
tokenlen = (tokenlen + (tokenlen % 4)) / 4; tokenlen = (tokenlen + (tokenlen % sizeof(uint32_t))) / sizeof(uint32_t);
tea_encode(key, (uint32_t *)token, tokenlen); tea_encode(key, (uint32_t *)token, tokenlen);
} }

View File

@ -1,20 +1,15 @@
#include <stdlib.h> #include <stdlib.h>
#include "common.h" #include "common.h"
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
char team[TEAM_MAX]; char team[TEAM_MAX] = {0};
char category[CAT_MAX]; char category[CAT_MAX] = {0};
char points_str[5]; char points_str[11] = {0};
char answer[500]; char answer[500] = {0};
long points = 0; long points = 0;
team[0] = 0;
category[0] = 0;
answer[0] = 0;
if (-1 == cgi_init(argv)) { if (-1 == cgi_init(argv)) {
return 0; return 0;
} }
@ -71,9 +66,14 @@ main(int argc, char *argv[])
} }
} }
award_and_log_uniquely(team, category, points, {
"puzzler.db", char line[TEAM_MAX + CAT_MAX + sizeof(points_str) + 2];
my_snprintf(line, sizeof(line),
"%s %s %ld", team, category, points); "%s %s %ld", team, category, points);
award_and_log_uniquely(team, category, points,
"puzzler.db", line);
}
cgi_page("Points awarded", cgi_page("Points awarded",
("<p>%d points for %s.</p>" ("<p>%d points for %s.</p>"

View File

@ -7,6 +7,7 @@
#include <stdio.h> #include <stdio.h>
#include "common.h" #include "common.h"
int int
longcmp(long *a, long *b) longcmp(long *a, long *b)
{ {
@ -15,13 +16,13 @@ longcmp(long *a, long *b)
return 0; return 0;
} }
#define PUZZLES_MAX 500 #define PUZZLES_MAX 100
/** Keeps track of the most points yet awarded in each category */ /** Keeps track of the most points yet awarded in each category */
struct { struct {
char cat[CAT_MAX]; char cat[CAT_MAX];
long points; long points;
} points_by_cat[100]; } points_by_cat[PUZZLES_MAX];
int ncats = 0; int ncats = 0;
void void
@ -37,14 +38,19 @@ read_points_by_cat()
} }
while (1) { while (1) {
if (2 != fscanf(f, "%*s %s %ld\n", &cat, &points)) { /* XXX: tokenize like cgi_item */
if (2 != fscanf(f, "%*s %s %ld\n", cat, &points)) {
break; break;
} }
for (i = 0; i < ncats; i += 1) { for (i = 0; i < ncats; i += 1) {
if (0 == strcmp(cat, points_by_cat[i].cat)) break; if (0 == strcmp(cat, points_by_cat[i].cat)) break;
} }
if (i == ncats) { if (i == ncats) {
strcpy(points_by_cat[i].cat, cat); if (PUZZLES_MAX == ncats) {
continue;
}
strncpy(points_by_cat[i].cat, cat, sizeof(points_by_cat[i].cat));
points_by_cat[i].points = 0;
ncats += 1; ncats += 1;
} }
if (points > points_by_cat[i].points) { if (points > points_by_cat[i].points) {
@ -76,12 +82,14 @@ main(int argc, char *argv[])
/* For each file in /srv/ ... */ /* For each file in /srv/ ... */
while (1) { while (1) {
struct dirent *e = readdir(srv); struct dirent *e = readdir(srv);
char *cat = e->d_name; char *cat;
DIR *puzzles; DIR *puzzles;
long catpoints[PUZZLES_MAX]; long catpoints[PUZZLES_MAX];
size_t ncatpoints = 0; size_t ncatpoints = 0;
if (! e) break; if (! e) break;
cat = e->d_name;
if ('.' == cat[0]) continue; if ('.' == cat[0]) continue;
/* We have to lstat anyway to see if it's a directory; may as /* We have to lstat anyway to see if it's a directory; may as
well just barge ahead and watch for errors. */ well just barge ahead and watch for errors. */