~ubuntu-branches/ubuntu/jaunty/edbrowse/jaunty-security

« back to all changes in this revision

Viewing changes to src/cookies.c

  • Committer: Bazaar Package Importer
  • Author(s): Kapil Hari Paranjape
  • Date: 2008-04-09 18:55:23 UTC
  • mfrom: (1.1.4 upstream) (3.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080409185523-dqokcloumyn1ibn4
Tags: 3.3.4-1
* New upstream version (3.3.4).
 - Convert between iso8859-1 and utf-8 on the fly.
 - Support reading of pdf using pdftohtml.
 - French translation of html documentation.
 - Old html documentation renamed to usersguide.
 - Additional documentation on philosophy.
* debian/control:
 - Changed homepage to sourcefource site.
 - Moved homepage from description to its own field.
 - Added "poppler-utils | xpdf-utils" to Recommends.
 - Added "www-browser", "mail-reader" and "editor" to Provides. 
 - Removed "XS-" from Vcs-Svn tag.
 - Standards-Version: 3.7.3
* debian/docs: Added new documentation files
  from "doc/" subdirectory.
* debian/watch: Updated to use sourceforge site.
* debian/edbrowse.doc-base:
  - Changed name of upstream provided html documentation from
    "ebdoc.html" to "usersguide.html".
  - Changed section from "net" to "Network/Web Browsing".
* debian/install: Compiled binary is now in "src/".

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* cookies.c
 
2
 * Cookies
 
3
 * (c) 2002 Mikulas Patocka
 
4
 * This file is part of the Links project, released under GPL
 
5
 *
 
6
 * Modified by Karl Dahlke for integration with edbrowse.
 
7
 */
 
8
 
 
9
#include "eb.h"
 
10
 
 
11
struct cookie {
 
12
    struct cookie *next;
 
13
    struct cookie *prev;
 
14
/* These are allocated */
 
15
    char *name, *value;
 
16
    char *server, *path, *domain;
 
17
    time_t expires;             /* zero means undefined */
 
18
    bool secure;
 
19
};
 
20
 
 
21
static void
 
22
freeCookie(struct cookie *c)
 
23
{
 
24
    nzFree(c->name);
 
25
    nzFree(c->value);
 
26
    nzFree(c->server);
 
27
    nzFree(c->path);
 
28
    nzFree(c->domain);
 
29
}                               /* freeCookie */
 
30
 
 
31
static struct listHead cookies = { &cookies, &cookies };
 
32
 
 
33
static bool displacedCookie;
 
34
static void
 
35
acceptCookie(struct cookie *c)
 
36
{
 
37
    struct cookie *d;
 
38
    displacedCookie = false;
 
39
    foreach(d, cookies) {
 
40
        if(stringEqualCI(d->name, c->name) &&
 
41
           stringEqualCI(d->domain, c->domain)) {
 
42
            displacedCookie = true;
 
43
            delFromList(d);
 
44
            freeCookie(d);
 
45
            nzFree(d);
 
46
            break;
 
47
        }
 
48
    }
 
49
    addToListBack(&cookies, c);
 
50
}                               /* acceptCookie */
 
51
 
 
52
static void
 
53
cookieIntoJar(const struct cookie *c)
 
54
{
 
55
    FILE *f;
 
56
    if(!cookieFile) {
 
57
        static bool warn = false;
 
58
        if(warn)
 
59
            return;
 
60
        i_puts(MSG_NoJar);
 
61
        warn = true;
 
62
        return;
 
63
    }
 
64
    if(c->expires <= time(0))
 
65
        return;                 /* not persistent, or out of date */
 
66
    f = fopen(cookieFile, "a");
 
67
    if(!f)
 
68
        return;
 
69
/* Netscape format */
 
70
/* I have no clue what the second argument is suppose to be, */
 
71
/* I'm always calling it false. */
 
72
    fprintf(f, "%s\tFALSE\t%s\t%s\t%u\t%s\t%s\n",
 
73
       c->domain, c->path,
 
74
       c->secure ? "TRUE" : "FALSE", (unsigned)c->expires, c->name, c->value);
 
75
    fclose(f);
 
76
    debugPrint(3, "into jar");
 
77
}                               /* cookieIntoJar */
 
78
 
 
79
/* Should this server really specify this domain in a cookie? */
 
80
/* Domain must be the trailing substring of server. */
 
81
bool
 
82
domainSecurityCheck(const char *server, const char *domain)
 
83
{
 
84
    int i, dl, nd;
 
85
    dl = strlen(domain);
 
86
/* x.com or x.y.z */
 
87
    if(dl < 5)
 
88
        return false;
 
89
    if(dl > strlen(server))
 
90
        return false;
 
91
    i = strlen(server) - dl;
 
92
    if(!stringEqualCI(server + i, domain))
 
93
        return false;
 
94
    if(i && server[i - 1] != '.')
 
95
        return false;
 
96
    nd = 2;                     /* number of dots */
 
97
    if(dl > 4 && domain[dl - 4] == '.') {
 
98
        static const char *const tld[] = {
 
99
            "com", "edu", "net", "org", "gov", "mil", "int", "biz", NULL
 
100
        };
 
101
        if(stringInListCI(tld, domain + dl - 3) >= 0)
 
102
            nd = 1;
 
103
    }
 
104
    for(i = 0; domain[i]; i++)
 
105
        if(domain[i] == '.')
 
106
            if(!--nd)
 
107
                return true;
 
108
    return false;
 
109
}                               /* domainSecurityCheck */
 
110
 
 
111
/* Let's jump right into it - parse a cookie, as received from a website. */
 
112
bool
 
113
receiveCookie(const char *url, const char *str)
 
114
{
 
115
    struct cookie *c;
 
116
    const char *p, *q, *server;
 
117
    char *date, *s;
 
118
 
 
119
    debugPrint(3, "%s", str);
 
120
 
 
121
    server = getHostURL(url);
 
122
    if(server == 0 || !*server)
 
123
        return false;
 
124
 
 
125
/* Cookie starts with name=value.  If we can't get that, go home. */
 
126
    for(p = str; *p != ';' && *p; p++) ;
 
127
    for(q = str; *q != '='; q++)
 
128
        if(!*q || q >= p)
 
129
            return false;
 
130
    if(str == q)
 
131
        return false;
 
132
 
 
133
    c = allocZeroMem(sizeof (struct cookie));
 
134
    c->name = pullString1(str, q);
 
135
    ++q;
 
136
    if(p - q > 0)
 
137
        c->value = pullString1(q, p);
 
138
    else
 
139
        c->value = EMPTYSTRING;
 
140
 
 
141
    c->server = cloneString(server);
 
142
 
 
143
    if(date = extractHeaderParam(str, "expires")) {
 
144
        c->expires = parseHeaderDate(date);
 
145
        nzFree(date);
 
146
    } else if(date = extractHeaderParam(str, "max-age")) {
 
147
        int n = stringIsNum(date);
 
148
        if(n >= 0) {
 
149
            time_t now = time(0);
 
150
            c->expires = now + n;
 
151
        }
 
152
        nzFree(date);
 
153
    }
 
154
 
 
155
    c->path = extractHeaderParam(str, "path");
 
156
    if(!c->path) {
 
157
/* The url indicates the path for this cookie, if a path is not explicitly given */
 
158
        const char *dir, *dirend;
 
159
        getDirURL(url, &dir, &dirend);
 
160
        c->path = pullString1(dir, dirend);
 
161
    } else {
 
162
        if(!c->path[0] || c->path[strlen(c->path) - 1] != '/')
 
163
            c->path = appendString(c->path, "/");
 
164
        if(c->path[0] != '/')
 
165
            c->path = prependString(c->path, "/");
 
166
    }
 
167
 
 
168
    if(!(c->domain = extractHeaderParam(str, "domain")))
 
169
        c->domain = cloneString(server);
 
170
    if(c->domain[0] == '.')
 
171
        strcpy(c->domain, c->domain + 1);
 
172
    if(!domainSecurityCheck(server, c->domain)) {
 
173
        nzFree(c->domain);
 
174
        c->domain = cloneString(server);
 
175
    }
 
176
 
 
177
    if(s = extractHeaderParam(str, "secure")) {
 
178
        c->secure = true;
 
179
        nzFree(s);
 
180
    }
 
181
 
 
182
    acceptCookie(c);
 
183
    cookieIntoJar(c);
 
184
    return true;
 
185
}                               /* receiveCookie */
 
186
 
 
187
/* I'm assuming I can read the cookie file, process it,
 
188
 * and if necessary, write it out again, with the expired cookies deleted,
 
189
 * all before another edbrowse process interferes.
 
190
 * I've given it some thought, and I think I can ignore the race conditions. */
 
191
void
 
192
cookiesFromJar(void)
 
193
{
 
194
    char *cbuf, *s, *t;
 
195
    FILE *f;
 
196
    int n, cnt, expired, displaced;
 
197
    time_t now;
 
198
    struct cookie *c;
 
199
 
 
200
    if(!cookieFile)
 
201
        return;
 
202
    if(!fileIntoMemory(cookieFile, &cbuf, &n))
 
203
        showErrorAbort();
 
204
    cbuf[n] = 0;
 
205
    time(&now);
 
206
 
 
207
    cnt = expired = displaced = 0;
 
208
    s = cbuf;
 
209
    while(*s) {
 
210
        ++cnt;
 
211
        c = allocZeroMem(sizeof (struct cookie));
 
212
        t = strchr(s, '\t');
 
213
        *t = 0;
 
214
        c->domain = cloneString(s);
 
215
        s = t + 1;
 
216
        t = strchr(s, '\t');
 
217
        s = t + 1;
 
218
        t = strchr(s, '\t');
 
219
        *t = 0;
 
220
        c->path = cloneString(s);
 
221
        s = t + 1;
 
222
        t = strchr(s, '\t');
 
223
        c->secure = (*s == 'T');
 
224
        s = t + 1;
 
225
        t = strchr(s, '\t');
 
226
        *t = 0;
 
227
        c->expires = (time_t) atol(s);
 
228
        s = t + 1;
 
229
        t = strchr(s, '\t');
 
230
        *t = 0;
 
231
        c->name = cloneString(s);
 
232
        s = t + 1;
 
233
        t = strchr(s, '\n');
 
234
        *t = 0;
 
235
        c->value = cloneString(s);
 
236
        s = t + 1;
 
237
 
 
238
        if(c->expires < now) {
 
239
            freeCookie(c);
 
240
            nzFree(c);
 
241
            ++expired;
 
242
        } else {
 
243
            acceptCookie(c);
 
244
            displaced += displacedCookie;
 
245
        }
 
246
    }
 
247
 
 
248
    debugPrint(3, "%d persistent cookies, %d expired, %d displaced",
 
249
       cnt, expired, displaced);
 
250
    nzFree(cbuf);
 
251
    if(!(expired + displaced))
 
252
        return;
 
253
 
 
254
/* Pour the cookies back into the jar */
 
255
    f = fopen(cookieFile, "w");
 
256
    if(!f)
 
257
        i_printfExit(MSG_NoRebCookie, cookieFile);
 
258
    foreach(c, cookies)
 
259
       fprintf(f, "%s\tFALSE\t%s\t%s\t%u\t%s\t%s\n",
 
260
       c->domain, c->path,
 
261
       c->secure ? "TRUE" : "FALSE", (unsigned)c->expires, c->name, c->value);
 
262
    fclose(f);
 
263
}                               /* cookiesFromJar */
 
264
 
 
265
static bool
 
266
isInDomain(const char *d, const char *s)
 
267
{
 
268
    int dl = strlen(d);
 
269
    int sl = strlen(s);
 
270
    int j = sl - dl;
 
271
    if(j < 0)
 
272
        return false;
 
273
    if(!memEqualCI(d, s + j, dl))
 
274
        return false;
 
275
    if(j && s[j - 1] != '.')
 
276
        return false;
 
277
    return true;
 
278
}                               /* isInDomain */
 
279
 
 
280
static bool
 
281
isPathPrefix(const char *d, const char *s)
 
282
{
 
283
    int dl = strlen(d);
 
284
    int sl = strlen(s);
 
285
    if(dl > sl)
 
286
        return false;
 
287
    return !memcmp(d, s, dl);
 
288
}                               /* isPathPrefix */
 
289
 
 
290
void
 
291
sendCookies(char **s, int *l, const char *url, bool issecure)
 
292
{
 
293
    const char *server = getHostURL(url);
 
294
    const char *data = getDataURL(url);
 
295
    int nc = 0;                 /* new cookie */
 
296
    struct cookie *c, *d;
 
297
    time_t now;
 
298
 
 
299
    if(!url || !server || !data)
 
300
        return;
 
301
 
 
302
    if(data > url && data[-1] == '/')
 
303
        data--;
 
304
    if(!*data)
 
305
        data = "/";
 
306
    time(&now);
 
307
 
 
308
    foreach(c, cookies) {
 
309
        if(!isInDomain(c->domain, server))
 
310
            continue;
 
311
        if(!isPathPrefix(c->path, data))
 
312
            continue;
 
313
        if(c->expires && c->expires < now) {
 
314
            d = c;
 
315
            c = c->prev;
 
316
            delFromList(d);
 
317
            freeCookie(d);
 
318
            nzFree(d);
 
319
            continue;
 
320
        }
 
321
        if(c->secure && !issecure)
 
322
            continue;
 
323
/* We're good to go. */
 
324
        if(!nc)
 
325
            stringAndString(s, l, "Cookie: "), nc = 1;
 
326
        else
 
327
            stringAndString(s, l, "; ");
 
328
        stringAndString(s, l, c->name);
 
329
        stringAndChar(s, l, '=');
 
330
        stringAndString(s, l, c->value);
 
331
        debugPrint(3, "send cookie %s=%s", c->name, c->value);
 
332
    }
 
333
 
 
334
    if(nc)
 
335
        stringAndString(s, l, eol);
 
336
}                               /* sendCookies */