From: rjk@greenend.org.uk <>
Date: Fri, 28 Dec 2007 16:58:50 +0000 (+0000)
Subject: keep cookie more private to disorder.cgi
X-Git-Tag: 3.0~155
X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/36bde473fdfeb75de65fb7b1920a6fb137f32f3c?hp=62ef2216d2c7c1c563ea163e2a0fdacccb54e31e
keep cookie more private to disorder.cgi
---
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 7b4ce0a..9b84937 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -66,6 +66,7 @@ libdisorder_a_SOURCES=charset.c charset.h \
timeval.h \
trackdb.h trackdb.c trackdb-int.h \
trackname.c trackname.h \
+ url.h url.c \
user.h user.c \
unicode.h unicode.c \
unidata.h unidata.c \
diff --git a/lib/kvp.c b/lib/kvp.c
index 471de78..f9081ef 100644
--- a/lib/kvp.c
+++ b/lib/kvp.c
@@ -128,7 +128,7 @@ int urlencode(struct sink *sink, const char *s, size_t n) {
* @param s String to encode
* @return Encoded string
*/
-const char *urlencodestring(const char *s) {
+char *urlencodestring(const char *s) {
struct dynstr d;
dynstr_init(&d);
@@ -140,13 +140,14 @@ const char *urlencodestring(const char *s) {
/** @brief URL-decode @p s
* @param s String to decode
* @param ns Length of string
- * @return Decoded string
+ * @return Decoded string or NULL
*/
-const char *urldecodestring(const char *s, size_t ns) {
+char *urldecodestring(const char *s, size_t ns) {
struct dynstr d;
dynstr_init(&d);
- urldecode(sink_dynstr(&d), s, ns);
+ if(urldecode(sink_dynstr(&d), s, ns))
+ return NULL;
dynstr_terminate(&d);
return d.vec;
}
diff --git a/lib/kvp.h b/lib/kvp.h
index 98c62cd..5240526 100644
--- a/lib/kvp.h
+++ b/lib/kvp.h
@@ -52,10 +52,10 @@ int urlencode(struct sink *sink, const char *s, size_t n);
/* url-encode the @n@ bytes at @s@, writing to @sink@. Return 0 on
* success, -1 on error. */
-const char *urlencodestring(const char *s);
+char *urlencodestring(const char *s);
/* return the url-encoded form of @s@ */
-const char *urldecodestring(const char *s, size_t ns);
+char *urldecodestring(const char *s, size_t ns);
#endif /* KVP_H */
diff --git a/lib/mime.c b/lib/mime.c
index 7ba4254..9883a3c 100644
--- a/lib/mime.c
+++ b/lib/mime.c
@@ -457,6 +457,8 @@ char *mime_qp(const char *s) {
* @param s Header field value
* @param cd Where to store result
* @return 0 on success, non-0 on error
+ *
+ * See RFC 2109.
*/
int parse_cookie(const char *s,
struct cookiedata *cd) {
@@ -529,6 +531,41 @@ const struct cookie *find_cookie(const struct cookiedata *cd,
return 0;
}
+/** @brief RFC822 quoting
+ * @param s String to quote
+ * @param force If non-0, always quote
+ * @return Possibly quoted string
+ */
+char *quote822(const char *s, int force) {
+ const char *t;
+ struct dynstr d[1];
+ int c;
+
+ if(!force) {
+ /* See if we need to quote */
+ for(t = s; (c = (unsigned char)*t); ++t) {
+ if(tspecial(c) || http_separator(c) || whitespace(c))
+ break;
+ }
+ if(*t)
+ force = 1;
+ }
+
+ if(!force)
+ return xstrdup(s);
+
+ dynstr_init(d);
+ dynstr_append(d, '"');
+ for(t = s; (c = (unsigned char)*t); ++t) {
+ if(c == '"' || c == '\\')
+ dynstr_append(d, '\\');
+ dynstr_append(d, c);
+ }
+ dynstr_append(d, '"');
+ dynstr_terminate(d);
+ return d->vec;
+}
+
/*
Local Variables:
c-basic-offset:2
diff --git a/lib/mime.h b/lib/mime.h
index b2286be..0fa8aea 100644
--- a/lib/mime.h
+++ b/lib/mime.h
@@ -55,7 +55,10 @@ int mime_rfc2388_content_disposition(const char *s,
char *mime_qp(const char *s);
-/** @brief Parsed form of an HTTP Cookie: header field */
+/** @brief Parsed form of an HTTP Cookie: header field
+ *
+ * See RFC 2109.
+ */
struct cookiedata {
/** @brief @c $Version or NULL if not set */
char *version;
@@ -67,7 +70,11 @@ struct cookiedata {
int ncookies;
};
-/** @brief A parsed cookie */
+/** @brief A parsed cookie
+ *
+ * See RFC 2109 and @ref
+ * cookiedata.
+ */
struct cookie {
/** @brief Cookie name */
char *name;
@@ -87,7 +94,7 @@ int parse_cookie(const char *s,
struct cookiedata *cd);
const struct cookie *find_cookie(const struct cookiedata *cd,
const char *name);
-
+char *quote822(const char *s, int force);
#endif /* MIME_H */
diff --git a/lib/test.c b/lib/test.c
index 0f38fc3..61b63fd 100644
--- a/lib/test.c
+++ b/lib/test.c
@@ -62,6 +62,7 @@
#include "configuration.h"
#include "addr.h"
#include "base64.h"
+#include "url.h"
static int tests, errors;
static int fail_first;
@@ -1371,6 +1372,8 @@ static void test_addr(void) {
0
};
+ printf("test_addr\n");
+
a.n = 1;
a.s = (char **)s;
s[0] = "smtp";
@@ -1402,6 +1405,26 @@ static void test_addr(void) {
check_string(name, "host localhost service nntp");
}
+static void test_url(void) {
+ struct url p;
+
+ printf("test_url\n");
+
+ insist(parse_url("http://www.example.com/example/path", &p) == 0);
+ check_string(p.scheme, "http");
+ check_string(p.host, "www.example.com");
+ insist(p.port == -1);
+ check_string(p.path, "/example/path");
+ insist(p.query == 0);
+
+ insist(parse_url("https://www.example.com:82/example%2fpath?+query+", &p) == 0);
+ check_string(p.scheme, "https");
+ check_string(p.host, "www.example.com");
+ insist(p.port == 82);
+ check_string(p.path, "/example/path");
+ check_string(p.query, "+query+");
+}
+
int main(void) {
mem_init();
fail_first = !!getenv("FAIL_FIRST");
@@ -1468,6 +1491,7 @@ int main(void) {
/* selection.c */
test_selection();
test_hash();
+ test_url();
fprintf(stderr, "%d errors out of %d tests\n", errors, tests);
return !!errors;
}
diff --git a/lib/url.c b/lib/url.c
new file mode 100644
index 0000000..784ee9f
--- /dev/null
+++ b/lib/url.c
@@ -0,0 +1,143 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2007 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+/** @file lib/url.c
+ * @brief URL support functions
+ */
+
+#include
+#include "types.h"
+
+#include
+#include
+#include
+
+#include "mem.h"
+#include "log.h"
+#include "printf.h"
+#include "url.h"
+#include "kvp.h"
+
+/** @brief Infer the for the web interface
+ * @return Inferred URL
+ *
+ * See RFC 3875.
+ */
+char *infer_url(void) {
+ const char *scheme = "http", *server, *script, *e;
+ char *url;
+ int port;
+
+ /* Figure out the server. 'MUST' be set and we don't cope if it
+ * is not. */
+ if(!(server = getenv("SERVER_NAME")))
+ fatal(0, "SERVER_NAME is not set");
+ server = xstrdup(server);
+
+ /* Figure out the port. 'MUST' be set but we cope if it is not. */
+ if((e = getenv("SERVER_PORT")))
+ port = atoi(e);
+ else
+ port = 80;
+
+ /* Figure out path to ourselves */
+ if(!(script = getenv("SCRIPT_NAME")))
+ fatal(0, "SCRIPT_NAME is not set");
+ if(script[0] != '/')
+ fatal(0, "SCRIPT_NAME does not start with a '/'");
+ script = xstrdup(script);
+
+ if(port == 80)
+ byte_xasprintf(&url, "%s://%s%s",
+ scheme, server, script);
+ else
+ byte_xasprintf(&url, "%s://%s:%d%s",
+ scheme, server, port, script);
+ return url;
+}
+
+/** @brief Parse a URL
+ * @param url URL to parsed
+ * @param parsed Where to store parsed URL data
+ * @return 0 on success, non-0 on error
+ *
+ * NB that URLs with usernames and passwords are NOT currently supported.
+ */
+int parse_url(const char *url, struct url *parsed) {
+ const char *s;
+ long n;
+
+ /* The scheme */
+ for(s = url; *s && *s != '/' && *s != ':'; ++s)
+ ;
+ if(*s == ':') {
+ parsed->scheme = xstrndup(url, s - url);
+ url = s + 1;
+ } else
+ parsed->scheme = 0;
+
+ /* The host and port */
+ if(*url == '/' && url[1] == '/') {
+ /* //user:password@host:port, but we don't support the
+ * user:password@ part. */
+ url += 2;
+ for(s = url; *s && *s != '/' && *s != ':'; ++s)
+ ;
+ parsed->host = xstrndup(url, s - url);
+ if(*s == ':') {
+ /* We have host:port[/...] */
+ ++s;
+ errno = 0;
+ n = strtol(s, (char **)&url, 10);
+ if(errno)
+ return -1;
+ if(n < 0 || n > 65535)
+ return -1;
+ parsed->port = n;
+ } else {
+ /* We just have host[/...] */
+ url = s;
+ parsed->port = -1;
+ }
+ }
+
+ /* The path */
+ for(s = url; *s && *s != '?'; ++s)
+ ;
+ if(!(parsed->path = urldecodestring(url, s - url)))
+ return -1;
+ url = s;
+
+ /* The query */
+ if(*url == '?')
+ parsed->query = xstrdup(url + 1);
+ else
+ parsed->query = 0;
+
+ return 0;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/url.h b/lib/url.h
new file mode 100644
index 0000000..3331456
--- /dev/null
+++ b/lib/url.h
@@ -0,0 +1,83 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2007 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+/** @file lib/url.h
+ * @brief URL support functions
+ */
+
+#ifndef URL_H
+#define URL_H
+
+/** @brief A parsed HTTP URL */
+struct url {
+ /** @brief URL scheme
+ *
+ * Typically "http" or "https". Might be NULL for a relative URL.
+ */
+ char *scheme;
+
+ /** @brief Username
+ *
+ * Might well be NULL. NB not supported currently.
+ */
+ char *user;
+
+ /** @brief Password
+ *
+ * Migth well be NULL. NB not supported currently.
+ */
+ char *password;
+
+ /** @brief Hostname
+ *
+ * Might be NULL for a relative URL.
+ */
+ char *host;
+
+ /** @brief Port number or -1 if none specified */
+ long port;
+
+ /** @brief Path
+ *
+ * May be the empty string. Never NULL. Will be decoded from the
+ * original URL.
+ */
+ char *path;
+
+ /** @brief Query
+ *
+ * NULL if there was no query part. Will NOT be decoded from the
+ * original URL.
+ */
+ char *query;
+};
+
+char *infer_url(void);
+int parse_url(const char *url, struct url *parsed);
+
+#endif /* URL_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/server/cgimain.c b/server/cgimain.c
index 53f7a9b..b377c8e 100644
--- a/server/cgimain.c
+++ b/server/cgimain.c
@@ -41,43 +41,7 @@
#include "mime.h"
#include "printf.h"
#include "dcgi.h"
-
-/** @brief Infer the base URL for the web interface if it's not set
- *
- * See RFC 3875.
- */
-static void infer_url(void) {
- if(!config->url) {
- const char *scheme = "http", *server, *script, *e;
- int port;
-
- /* Figure out the server. 'MUST' be set and we don't cope if it
- * is not. */
- if(!(server = getenv("SERVER_NAME")))
- fatal(0, "SERVER_NAME is not set");
- server = xstrdup(server);
-
- /* Figure out the port. 'MUST' be set but we cope if it is not. */
- if((e = getenv("SERVER_PORT")))
- port = atoi(e);
- else
- port = 80;
-
- /* Figure out path to ourselves */
- if(!(script = getenv("SCRIPT_NAME")))
- fatal(0, "SCRIPT_NAME is not set");
- if(script[0] != '/')
- fatal(0, "SCRIPT_NAME does not start with a '/'");
- script = xstrdup(script);
-
- if(port == 80)
- byte_xasprintf(&config->url, "%s://%s%s",
- scheme, server, script);
- else
- byte_xasprintf(&config->url, "%s://%s:%d%s",
- scheme, server, port, script);
- }
-}
+#include "url.h"
int main(int argc, char **argv) {
const char *cookie_env, *conf;
@@ -92,7 +56,8 @@ int main(int argc, char **argv) {
if((conf = getenv("DISORDER_CONFIG"))) configfile = xstrdup(conf);
if(getenv("DISORDER_DEBUG")) debugging = 1;
if(config_read(0)) exit(EXIT_FAILURE);
- infer_url();
+ if(!config->url)
+ config->url = infer_url();
memset(&g, 0, sizeof g);
memset(&s, 0, sizeof s);
s.g = &g;
diff --git a/server/dcgi.c b/server/dcgi.c
index 08203f8..a4f774a 100644
--- a/server/dcgi.c
+++ b/server/dcgi.c
@@ -54,6 +54,8 @@
#include "trackname.h"
#include "charset.h"
#include "dcgi.h"
+#include "url.h"
+#include "mime.h"
char *login_cookie;
@@ -103,21 +105,28 @@ static const char *front_url(void) {
static void header_cookie(struct sink *output) {
struct dynstr d[1];
- char *s;
+ struct url u;
+ memset(&u, 0, sizeof u);
+ dynstr_init(d);
+ parse_url(config->url, &u);
if(login_cookie) {
- dynstr_init(d);
- for(s = login_cookie; *s; ++s) {
- if(*s == '"')
- dynstr_append(d, '\\');
- dynstr_append(d, *s);
- }
- dynstr_terminate(d);
- byte_xasprintf(&s, "disorder=\"%s\"", d->vec); /* TODO domain, path, expiry */
- cgi_header(output, "Set-Cookie", s);
- } else
+ dynstr_append_string(d, "disorder=");
+ dynstr_append_string(d, quote822(login_cookie, 0));
+ } else {
/* Force browser to discard cookie */
- cgi_header(output, "Set-Cookie", "disorder=none;Max-Age=0");
+ dynstr_append_string(d, "disorder=none;Max-Age=0");
+ }
+ if(u.path) {
+ /* The default domain matches the request host, so we need not override
+ * that. But the default path only goes up to the rightmost /, which would
+ * cause the browser to expose the cookie to other CGI programs on the same
+ * web server. */
+ dynstr_append_string(d, ";Path=");
+ dynstr_append_string(d, quote822(u.path, 0));
+ }
+ dynstr_terminate(d);
+ cgi_header(output, "Set-Cookie", d->vec);
}
static void redirect(struct sink *output) {