chiark / gitweb /
Doxygen-clean
[disorder] / cgi / login.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2008 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "disorder-cgi.h"
20
21 /** @brief Client used by CGI
22  *
23  * The caller should arrange for this to be created before any of
24  * these expansions are used (if it cannot connect then it's safe to
25  * leave it as NULL).
26  */
27 disorder_client *dcgi_client;
28
29 /** @brief Return true if @p a is better than @p b
30  *
31  * NB. We don't bother checking if the path is right, we merely check for the
32  * longest path.  This isn't a security hole: if the browser wants to send us
33  * bad cookies it's quite capable of sending just the right path anyway.  The
34  * point of choosing the longest path is to avoid using a cookie set by another
35  * CGI script which shares a path prefix with us, which would allow it to
36  * maliciously log users out.
37  *
38  * Such a script could still "maliciously" log someone in, if it had acquired a
39  * suitable cookie.  But it could just log in directly if it had that, so there
40  * is no obvious vulnerability here either.
41  */
42 static int better_cookie(const struct cookie *a, const struct cookie *b) {
43   if(a->path && b->path)
44     /* If both have a path then the one with the longest path is best */
45     return strlen(a->path) > strlen(b->path);
46   else if(a->path)
47     /* If only @p a has a path then it is better */
48     return 1;
49   else
50     /* If neither have a path, or if only @p b has a path, then @p b is
51      * better */
52     return 0;
53 }
54
55 /** @brief Login cookie */
56 char *dcgi_cookie;
57
58 /** @brief Set @ref dcgi_cookie */
59 void dcgi_get_cookie(void) {
60   const char *cookie_env;
61   int n, best_cookie;
62   struct cookiedata cd;
63
64   /* See if there's a cookie */
65   cookie_env = getenv("HTTP_COOKIE");
66   if(cookie_env) {
67     /* This will be an HTTP header */
68     if(!parse_cookie(cookie_env, &cd)) {
69       /* Pick the best available cookie from all those offered */
70       best_cookie = -1;
71       for(n = 0; n < cd.ncookies; ++n) {
72         /* Is this the right cookie? */
73         if(strcmp(cd.cookies[n].name, "disorder"))
74           continue;
75         /* Is it better than anything we've seen so far? */
76         if(best_cookie < 0
77            || better_cookie(&cd.cookies[n], &cd.cookies[best_cookie]))
78           best_cookie = n;
79       }
80       if(best_cookie != -1)
81         dcgi_cookie = cd.cookies[best_cookie].value;
82     } else
83       error(0, "could not parse cookie field '%s'", cookie_env);
84   }
85 }
86
87 /** @brief Return a Cookie: header */
88 char *dcgi_cookie_header(void) {
89   struct dynstr d[1];
90   struct url u;
91   char *s;
92
93   memset(&u, 0, sizeof u);
94   dynstr_init(d);
95   parse_url(config->url, &u);
96   if(dcgi_cookie) {
97     dynstr_append_string(d, "disorder=");
98     dynstr_append_string(d, dcgi_cookie);
99   } else {
100     /* Force browser to discard cookie */
101     dynstr_append_string(d, "disorder=none;Max-Age=0");
102   }
103   if(u.path) {
104     /* The default domain matches the request host, so we need not override
105      * that.  But the default path only goes up to the rightmost /, which would
106      * cause the browser to expose the cookie to other CGI programs on the same
107      * web server. */
108     dynstr_append_string(d, ";Version=1;Path=");
109     /* Formally we are supposed to quote the path, since it invariably has a
110      * slash in it.  However Safari does not parse quoted paths correctly, so
111      * this won't work.  Fortunately nothing else seems to care about proper
112      * quoting of paths, so in practice we get with it.  (See also
113      * parse_cookie() where we are liberal about cookie paths on the way back
114      * in.) */
115     dynstr_append_string(d, u.path);
116   }
117   dynstr_terminate(d);
118   byte_xasprintf(&s, "Set-Cookie: %s", d->vec);
119   return s;
120 }
121
122 /** @brief Log in as the current user or guest if none */
123 void dcgi_login(void) {
124   /* Junk old data */
125   dcgi_lookup_reset();
126   /* Junk the old connection if there is one */
127   if(dcgi_client)
128     disorder_close(dcgi_client);
129   /* Create a new connection */
130   dcgi_client = disorder_new(0);
131   /* Reconnect */
132   if(disorder_connect_cookie(dcgi_client, dcgi_cookie)) {
133     dcgi_error("connect");
134     exit(0);
135   }
136   /* If there was a cookie but it went bad, we forget it */
137   if(dcgi_cookie && !strcmp(disorder_user(dcgi_client), "guest"))
138     dcgi_cookie = 0;
139 }
140
141 /*
142 Local Variables:
143 c-basic-offset:2
144 comment-column:40
145 fill-column:79
146 indent-tabs-mode:nil
147 End:
148 */