-/* $Id: cgi.c,v 1.89 2014/07/25 19:37:34 schwarze Exp $ */
+/* $Id: cgi.c,v 1.99 2014/10/07 18:20:06 schwarze Exp $ */
/*
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014 Ingo Schwarze <schwarze@usta.de>
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifdef HAVE_CONFIG_H
#include "config.h"
-#endif
+
+#include <sys/types.h>
+#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void catman(const struct req *, const char *);
static void format(const struct req *, const char *);
static void html_print(const char *);
-static void html_printquery(const struct req *);
static void html_putchar(char);
static int http_decode(char *);
static void http_parse(struct req *, const char *);
static void http_print(const char *);
static void http_putchar(char);
-static void http_printquery(const struct req *);
+static void http_printquery(const struct req *, const char *);
static void pathgen(struct req *);
static void pg_error_badrequest(const char *);
static void pg_error_internal(void);
"All Sections",
"1 - General Commands",
"2 - System Calls",
- "3 - Subroutines",
- "3p - Perl Subroutines",
- "4 - Special Files",
+ "3 - Library Functions",
+ "3p - Perl Library",
+ "4 - Device Drivers",
"5 - File Formats",
"6 - Games",
- "7 - Macros and Conventions",
- "8 - Maintenance Commands",
- "9 - Kernel Interface"
+ "7 - Miscellaneous Information",
+ "8 - System Manager\'s Manual",
+ "9 - Kernel Developer\'s Manual"
};
static const int sec_MAX = sizeof(sec_names) / sizeof(char *);
}
static void
-http_printquery(const struct req *req)
+http_printquery(const struct req *req, const char *sep)
{
if (NULL != req->q.query) {
http_print(req->q.query);
}
if (0 == req->q.equal)
- printf("&apropos=1");
+ printf("%sapropos=1", sep);
if (NULL != req->q.sec) {
- printf("&sec=");
+ printf("%ssec=", sep);
http_print(req->q.sec);
}
if (NULL != req->q.arch) {
- printf("&arch=");
+ printf("%sarch=", sep);
http_print(req->q.arch);
}
if (NULL != req->q.manpath &&
strcmp(req->q.manpath, req->p[0])) {
- printf("&manpath=");
+ printf("%smanpath=", sep);
http_print(req->q.manpath);
}
}
-static void
-html_printquery(const struct req *req)
-{
-
- if (NULL != req->q.query) {
- printf("query=");
- html_print(req->q.query);
- }
- if (0 == req->q.equal)
- printf("&apropos=1");
- if (NULL != req->q.sec) {
- printf("&sec=");
- html_print(req->q.sec);
- }
- if (NULL != req->q.arch) {
- printf("&arch=");
- html_print(req->q.arch);
- }
- if (NULL != req->q.manpath &&
- strcmp(req->q.manpath, req->p[0])) {
- printf("&manpath=");
- html_print(req->q.manpath);
- }
-}
-
static void
http_print(const char *p)
{
resp_begin_http(code, msg);
- printf("<!DOCTYPE HTML PUBLIC "
- " \"-//W3C//DTD HTML 4.01//EN\""
- " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
+ printf("<!DOCTYPE html>\n"
"<HTML>\n"
"<HEAD>\n"
- "<META HTTP-EQUIV=\"Content-Type\""
- " CONTENT=\"text/html; charset=utf-8\">\n"
+ "<META CHARSET=\"UTF-8\" />\n"
"<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
" TYPE=\"text/css\" media=\"all\">\n"
"<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
printf("Status: 303 See Other\r\n");
printf("Location: http://%s%s/%s/%s?",
HTTP_HOST, scriptname, req->q.manpath, r[0].file);
- http_printquery(req);
+ http_printquery(req, "&");
printf("\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n");
"<TD CLASS=\"title\">\n"
"<A HREF=\"%s/%s/%s?",
scriptname, req->q.manpath, r[i].file);
- html_printquery(req);
+ http_printquery(req, "&");
printf("\">");
html_print(r[i].names);
printf("</A>\n"
format(const struct req *req, const char *file)
{
struct mparse *mp;
- int fd;
struct mdoc *mdoc;
struct man *man;
void *vp;
+ char *opts;
enum mandoclevel rc;
- char opts[PATH_MAX + 128];
+ int fd;
+ int usepath;
if (-1 == (fd = open(file, O_RDONLY, 0))) {
puts("<P>You specified an invalid manual file.</P>");
return;
}
- snprintf(opts, sizeof(opts), "fragment,man=%s?"
- "manpath=%s&query=%%N&sec=%%S%s%s",
- scriptname, req->q.manpath,
- req->q.arch ? "&arch=" : "",
- req->q.arch ? req->q.arch : "");
+ usepath = strcmp(req->q.manpath, req->p[0]);
+ mandoc_asprintf(&opts,
+ "fragment,man=%s?query=%%N&sec=%%S%s%s%s%s",
+ scriptname,
+ req->q.arch ? "&arch=" : "",
+ req->q.arch ? req->q.arch : "",
+ usepath ? "&manpath=" : "",
+ usepath ? req->q.manpath : "");
mparse_result(mp, &mdoc, &man, NULL);
if (NULL == man && NULL == mdoc) {
html_free(vp);
mparse_free(mp);
+ free(opts);
}
static void
struct mansearch search;
struct manpaths paths;
struct manpage *res;
- char **cp;
- const char *ep, *start;
+ char **argv;
+ char *query, *rp, *wp;
size_t ressz;
- int i, sz;
+ int argc;
/*
* Begin by chdir()ing into the root of the manpath.
search.arch = req->q.arch;
search.sec = req->q.sec;
- search.deftype = req->q.equal ? TYPE_Nm : (TYPE_Nm | TYPE_Nd);
- search.flags = req->q.equal ? MANSEARCH_MAN : 0;
+ search.outkey = "Nd";
+ search.argmode = req->q.equal ? ARG_NAME : ARG_EXPR;
paths.sz = 1;
paths.paths = mandoc_malloc(sizeof(char *));
paths.paths[0] = mandoc_strdup(".");
/*
- * Poor man's tokenisation: just break apart by spaces.
- * Yes, this is half-ass. But it works for now.
+ * Break apart at spaces with backslash-escaping.
*/
- ep = req->q.query;
- while (ep && isspace((unsigned char)*ep))
- ep++;
-
- sz = 0;
- cp = NULL;
- while (ep && '\0' != *ep) {
- cp = mandoc_reallocarray(cp, sz + 1, sizeof(char *));
- start = ep;
- while ('\0' != *ep && ! isspace((unsigned char)*ep))
- ep++;
- cp[sz] = mandoc_malloc((ep - start) + 1);
- memcpy(cp[sz], start, ep - start);
- cp[sz++][ep - start] = '\0';
- while (isspace((unsigned char)*ep))
- ep++;
+ argc = 0;
+ argv = NULL;
+ rp = query = mandoc_strdup(req->q.query);
+ for (;;) {
+ while (isspace((unsigned char)*rp))
+ rp++;
+ if (*rp == '\0')
+ break;
+ argv = mandoc_reallocarray(argv, argc + 1, sizeof(char *));
+ argv[argc++] = wp = rp;
+ for (;;) {
+ if (isspace((unsigned char)*rp)) {
+ *wp = '\0';
+ rp++;
+ break;
+ }
+ if (rp[0] == '\\' && rp[1] != '\0')
+ rp++;
+ if (wp != rp)
+ *wp = *rp;
+ if (*rp == '\0')
+ break;
+ wp++;
+ rp++;
+ }
}
- if (0 == mansearch(&search, &paths, sz, cp, "Nd", &res, &ressz))
+ if (0 == mansearch(&search, &paths, argc, argv, &res, &ressz))
pg_noresult(req, "You entered an invalid query.");
else if (0 == ressz)
pg_noresult(req, "No results found.");
else
pg_searchres(req, res, ressz);
- for (i = 0; i < sz; i++)
- free(cp[i]);
- free(cp);
-
- for (i = 0; i < (int)ressz; i++) {
- free(res[i].file);
- free(res[i].names);
- free(res[i].output);
- }
- free(res);
-
+ free(query);
+ mansearch_free(res, ressz);
free(paths.paths[0]);
free(paths.paths);
}
main(void)
{
struct req req;
+ struct itimerval itimer;
const char *path;
const char *querystring;
int i;
+ /* Poor man's ReDoS mitigation. */
+
+ itimer.it_value.tv_sec = 2;
+ itimer.it_value.tv_usec = 0;
+ itimer.it_interval.tv_sec = 2;
+ itimer.it_interval.tv_usec = 0;
+ if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) == -1) {
+ fprintf(stderr, "setitimer: %s\n", strerror(errno));
+ pg_error_internal();
+ return(EXIT_FAILURE);
+ }
+
/* Scan our run-time environment. */
if (NULL == (scriptname = getenv("SCRIPT_NAME")))