1 /* Internal cookies implementation */
10 #include <sys/types.h>
11 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
22 #include "bfu/dialog.h"
23 #include "cookies/cookies.h"
24 #include "cookies/dialogs.h"
25 #include "cookies/parser.h"
26 #include "config/home.h"
27 #include "config/kbdbind.h"
28 #include "config/options.h"
29 #include "intl/gettext/libintl.h"
30 #include "main/module.h"
31 #include "main/object.h"
32 #include "main/select.h"
33 #include "protocol/date.h"
34 #include "protocol/header.h"
35 #include "protocol/protocol.h"
36 #include "protocol/uri.h"
37 #include "session/session.h"
38 #include "terminal/terminal.h"
39 #include "util/conv.h"
41 #include "util/error.h"
43 #include "util/file.h"
44 #include "util/memory.h"
45 #include "util/secsave.h"
46 #include "util/string.h"
47 #include "util/time.h"
49 #define COOKIES_FILENAME "cookies"
52 static int cookies_nosave
= 0;
54 static INIT_LIST_OF(struct cookie
, cookies
);
57 LIST_HEAD(struct c_domain
);
59 unsigned char domain
[1]; /* Must be at end of struct. */
62 /* List of domains for which there may be cookies. This supposedly
63 * speeds up @send_cookies for other domains. Each element is a
64 * struct c_domain. No other data structures have pointers to these
65 * objects. Currently the domains remain in the list until
66 * @done_cookies clears the whole list. */
67 static INIT_LIST_OF(struct c_domain
, c_domains
);
69 /* List of servers for which there are cookies. */
70 static INIT_LIST_OF(struct cookie_server
, cookie_servers
);
72 /* Only @set_cookies_dirty may make this nonzero. */
73 static int cookies_dirty
= 0;
78 COOKIES_ACCEPT_POLICY
,
80 COOKIES_PARANOID_SECURITY
,
87 static struct option_info cookies_options
[] = {
88 INIT_OPT_TREE("", N_("Cookies"),
90 N_("Cookies options.")),
92 INIT_OPT_INT("cookies", N_("Accept policy"),
94 COOKIES_ACCEPT_NONE
, COOKIES_ACCEPT_ALL
, COOKIES_ACCEPT_ALL
,
95 N_("Cookies accepting policy:\n"
96 "0 is accept no cookies\n"
97 "1 is ask for confirmation before accepting cookie\n"
98 "2 is accept all cookies")),
100 INIT_OPT_INT("cookies", N_("Maximum age"),
101 "max_age", 0, -1, 10000, -1,
102 N_("Cookie maximum age (in days):\n"
103 "-1 is use cookie's expiration date if any\n"
104 "0 is force expiration at the end of session, ignoring\n"
105 " cookie's expiration date\n"
106 "1+ is use cookie's expiration date, but limit age to the\n"
107 " given number of days")),
109 INIT_OPT_BOOL("cookies", N_("Paranoid security"),
110 "paranoid_security", 0, 0,
111 N_("When enabled, we'll require three dots in cookies domain "
112 "for all non-international domains (instead of just two "
113 "dots). Some countries have generic second level domains "
114 "(eg. .com.pl, .co.uk) and allowing sites to set cookies "
115 "for these generic domains could potentially be very bad. "
116 "Note, it is off by default as it breaks a lot of sites.")),
118 INIT_OPT_BOOL("cookies", N_("Saving"),
120 N_("Whether cookies should be loaded from and saved to "
123 INIT_OPT_BOOL("cookies", N_("Resaving"),
125 N_("Save cookies after each change in cookies list? "
126 "No effect when cookie saving (cookies.save) is off.")),
131 #define get_opt_cookies(which) cookies_options[(which)].option.value
132 #define get_cookies_accept_policy() get_opt_cookies(COOKIES_ACCEPT_POLICY).number
133 #define get_cookies_max_age() get_opt_cookies(COOKIES_MAX_AGE).number
134 #define get_cookies_paranoid_security() get_opt_cookies(COOKIES_PARANOID_SECURITY).number
135 #define get_cookies_save() get_opt_cookies(COOKIES_SAVE).number
136 #define get_cookies_resave() get_opt_cookies(COOKIES_RESAVE).number
138 struct cookie_server
*
139 get_cookie_server(unsigned char *host
, int hostlen
)
141 struct cookie_server
*sort_spot
= NULL
;
142 struct cookie_server
*cs
;
144 foreach (cs
, cookie_servers
) {
145 /* XXX: We must count with cases like "x.co" vs "x.co.uk"
147 int cslen
= strlen(cs
->host
);
148 int cmp
= c_strncasecmp(cs
->host
, host
, hostlen
);
150 if (!sort_spot
&& (cmp
> 0 || (cmp
== 0 && cslen
> hostlen
))) {
151 /* This is the first @cs with name greater than @host,
152 * our dream sort spot! */
153 sort_spot
= cs
->prev
;
156 if (cmp
|| cslen
!= hostlen
)
163 cs
= mem_calloc(1, sizeof(*cs
) + hostlen
);
164 if (!cs
) return NULL
;
166 memcpy(cs
->host
, host
, hostlen
);
167 object_nolock(cs
, "cookie_server");
169 cs
->box_item
= add_listbox_folder(&cookie_browser
, NULL
, cs
);
174 /* No sort spot found, therefore this sorts at the end. */
175 add_to_list_end(cookie_servers
, cs
);
176 del_from_list(cs
->box_item
);
177 add_to_list_end(cookie_browser
.root
.child
, cs
->box_item
);
179 /* Sort spot found, sort after it. */
180 add_at_pos(sort_spot
, cs
);
181 if (sort_spot
!= (struct cookie_server
*) &cookie_servers
) {
182 del_from_list(cs
->box_item
);
183 add_at_pos(sort_spot
->box_item
, cs
->box_item
);
184 } /* else we are already at the top anyway. */
191 done_cookie_server(struct cookie_server
*cs
)
194 if (is_object_used(cs
)) return;
196 if (cs
->box_item
) done_listbox_item(&cookie_browser
, cs
->box_item
);
202 done_cookie(struct cookie
*c
)
204 if (c
->box_item
) done_listbox_item(&cookie_browser
, c
->box_item
);
205 if (c
->server
) done_cookie_server(c
->server
);
206 mem_free_if(c
->name
);
207 mem_free_if(c
->value
);
208 mem_free_if(c
->path
);
209 mem_free_if(c
->domain
);
213 /* The cookie @c can be either in @cookies or in @cookie_queries.
214 * Because changes in @cookie_queries should not affect the cookie
215 * file, this function does not set @cookies_dirty. Instead, the
216 * caller must do that if appropriate. */
218 delete_cookie(struct cookie
*c
)
225 /* Check whether cookie's domain matches server.
226 * It returns 1 if ok, 0 else. */
228 is_domain_security_ok(unsigned char *domain
, unsigned char *server
, int server_len
)
234 if (domain
[0] == '.') domain
++;
235 domain_len
= strlen(domain
);
237 /* Match domain and server.. */
239 /* XXX: Hmm, can't we use c_strlcasecmp() here? --pasky */
241 if (domain_len
> server_len
) return 0;
243 /* Ensure that the domain is atleast a substring of the server before
245 if (c_strncasecmp(domain
, server
+ server_len
- domain_len
, domain_len
))
248 /* Allow domains which are same as servers. --<rono@sentuny.com.au> */
249 /* Mozilla does it as well ;))) and I can't figure out any security
251 if (server_len
== domain_len
)
254 /* Check whether the server is an IP address, and require an exact host
255 * match for the cookie, so any chance of IP address funkiness is
256 * eliminated (e.g. the alias 127.1 domain-matching 99.54.127.1). Idea
257 * from mozilla. (bug 562) */
258 if (is_ip_address(server
, server_len
))
261 /* Also test if domain is secure en ugh.. */
265 if (get_cookies_paranoid_security()) {
266 /* This is somehow controversial attempt (by the way violating
267 * RFC) to increase cookies security in national domains, done
268 * by Mikulas. As it breaks a lot of sites, I decided to make
269 * this optional and off by default. I also don't think this
270 * improves security considerably, as it's SITE'S fault and
271 * also no other browser probably does it. --pasky */
272 /* Mikulas' comment: Some countries have generic 2-nd level
273 * domains (like .com.pl, .co.uk ...) and it would be very bad
274 * if someone set cookies for these generic domains. Imagine
275 * for example that server http://brutalporn.com.pl sets cookie
276 * Set-Cookie: user_is=perverse_pig; domain=.com.pl -- then
277 * this cookie would be sent to all commercial servers in
281 if (domain_len
> 0) {
282 int pos
= end_with_known_tld(domain
, domain_len
);
284 if (pos
>= 1 && domain
[pos
- 1] == '.')
289 for (i
= 0; domain
[i
]; i
++)
290 if (domain
[i
] == '.' && !--need_dots
)
293 if (need_dots
> 0) return 0;
297 /* Allocate a struct cookie and initialize it with the specified
298 * values (rather than copies). Returns NULL on error. On success,
299 * the cookie is basically safe for @done_cookie or @accept_cookie,
300 * although you may also want to set the remaining members and check
301 * @get_cookies_accept_policy and @is_domain_security_ok.
303 * The unsigned char * arguments must be allocated with @mem_alloc or
304 * equivalent, because @done_cookie will @mem_free them. Likewise,
305 * the caller must already have locked @server. If @init_cookie
306 * fails, then it frees the strings itself, and unlocks @server.
308 * If any parameter is NULL, then @init_cookie fails and does not
309 * consider that a bug. This means callers can use e.g. @stracpy
310 * and let @init_cookie check whether the call ran out of memory. */
312 init_cookie(unsigned char *name
, unsigned char *value
,
313 unsigned char *path
, unsigned char *domain
,
314 struct cookie_server
*server
)
316 struct cookie
*cookie
= mem_calloc(1, sizeof(*cookie
));
318 if (!cookie
|| !name
|| !value
|| !path
|| !domain
|| !server
) {
324 done_cookie_server(server
);
327 object_nolock(cookie
, "cookie"); /* Debugging purpose. */
330 cookie
->value
= value
;
331 cookie
->domain
= domain
;
333 cookie
->server
= server
; /* the caller already locked it for us */
339 set_cookie(struct uri
*uri
, unsigned char *str
)
341 unsigned char *path
, *domain
;
342 struct cookie
*cookie
;
343 struct cookie_str cstr
;
346 if (get_cookies_accept_policy() == COOKIES_ACCEPT_NONE
)
350 DBG("set_cookie -> (%s) %s", struri(uri
), str
);
353 if (!parse_cookie_str(&cstr
, str
)) return;
355 switch (parse_header_param(str
, "path", &path
)) {
356 unsigned char *path_end
;
358 case HEADER_PARAM_FOUND
:
360 || path
[strlen(path
) - 1] != '/')
361 add_to_strn(&path
, "/");
363 if (path
[0] != '/') {
364 add_to_strn(&path
, "x");
365 memmove(path
+ 1, path
, strlen(path
) - 1);
370 case HEADER_PARAM_NOT_FOUND
:
371 path
= get_uri_string(uri
, URI_PATH
);
375 path_end
= strrchr(path
, '/');
384 if (parse_header_param(str
, "domain", &domain
) == HEADER_PARAM_NOT_FOUND
)
385 domain
= memacpy(uri
->host
, uri
->hostlen
);
386 if (domain
&& domain
[0] == '.')
387 memmove(domain
, domain
+ 1, strlen(domain
));
389 cookie
= init_cookie(memacpy(str
, cstr
.nam_end
- str
),
390 memacpy(cstr
.val_start
, cstr
.val_end
- cstr
.val_start
),
393 get_cookie_server(uri
->host
, uri
->hostlen
));
395 /* @cookie now owns @path and @domain. */
398 /* We don't actually set ->accept at the moment. But I have kept it
399 * since it will maybe help to fix bug 77 - Support for more
400 * finegrained control upon accepting of cookies. */
401 if (!cookie
->server
->accept
) {
410 /* Set cookie expiration if needed.
411 * Cookie expires at end of session by default,
412 * set to 0 by calloc().
415 * -1 is use cookie's expiration date if any
416 * 0 is force expiration at the end of session,
417 * ignoring cookie's expiration date
418 * 1+ is use cookie's expiration date,
419 * but limit age to the given number of days.
422 max_age
= get_cookies_max_age();
427 switch (parse_header_param(str
, "expires", &date
)) {
428 case HEADER_PARAM_FOUND
:
429 expires
= parse_date(&date
, NULL
, 0, 1); /* Convert date to seconds. */
435 time_t seconds
= ((time_t) max_age
)*24*3600;
436 time_t deadline
= time(NULL
) + seconds
;
438 if (expires
> deadline
) /* Over-aged cookie ? */
442 cookie
->expires
= expires
;
446 case HEADER_PARAM_NOT_FOUND
:
455 cookie
->secure
= (parse_header_param(str
, "secure", NULL
)
456 == HEADER_PARAM_FOUND
);
460 DBG("Got cookie %s = %s from %s, domain %s, "
461 "expires at %"TIME_PRINT_FORMAT
", secure %d", cookie
->name
,
462 cookie
->value
, cookie
->server
->host
, cookie
->domain
,
463 (time_print_T
) cookie
->expires
, cookie
->secure
);
467 if (!is_domain_security_ok(cookie
->domain
, uri
->host
, uri
->hostlen
)) {
469 DBG("Domain security violated: %s vs %.*s", cookie
->domain
,
470 uri
->hostlen
, uri
->host
);
472 mem_free(cookie
->domain
);
473 cookie
->domain
= memacpy(uri
->host
, uri
->hostlen
);
476 /* We have already check COOKIES_ACCEPT_NONE */
477 if (get_cookies_accept_policy() == COOKIES_ACCEPT_ASK
) {
478 add_to_list(cookie_queries
, cookie
);
479 add_questions_entry(accept_cookie_dialog
, cookie
);
483 accept_cookie(cookie
);
487 accept_cookie(struct cookie
*cookie
)
490 struct listbox_item
*root
= cookie
->server
->box_item
;
494 cookie
->box_item
= add_listbox_leaf(&cookie_browser
, root
, cookie
);
496 /* Do not weed out duplicates when loading the cookie file. It doesn't
497 * scale at all, being O(N^2) and taking about 2s with my 500 cookies
498 * (so if you don't notice that 100ms with your 100 cookies, that's
499 * not an argument). --pasky */
500 if (!cookies_nosave
) {
501 struct cookie
*c
, *next
;
503 foreachsafe (c
, next
, cookies
) {
504 if (c_strcasecmp(c
->name
, cookie
->name
)
505 || c_strcasecmp(c
->domain
, cookie
->domain
))
509 /* @set_cookies_dirty will be called below. */
513 add_to_list(cookies
, cookie
);
516 /* XXX: This crunches CPU too. --pasky */
517 foreach (cd
, c_domains
)
518 if (!c_strcasecmp(cd
->domain
, cookie
->domain
))
521 domain_len
= strlen(cookie
->domain
);
522 /* One byte is reserved for domain in struct c_domain. */
523 cd
= mem_alloc(sizeof(*cd
) + domain_len
);
526 memcpy(cd
->domain
, cookie
->domain
, domain_len
+ 1);
527 add_to_list(c_domains
, cd
);
531 static unsigned int cookie_id
= 0;
534 delete_cookie(struct cookie
*c
)
540 if (!c_strcasecmp(d
->domain
, c
->domain
))
543 foreach (cd
, c_domains
) {
544 if (!c_strcasecmp(cd
->domain
, c
->domain
)) {
558 cookie
*find_cookie_id(void *idp
)
572 reject_cookie(void *idp
)
574 struct cookie
*c
= find_cookie_id(idp
);
579 set_cookies_dirty(); /* @find_cookie_id doesn't use @cookie_queries */
584 cookie_default(void *idp
, int a
)
586 struct cookie
*c
= find_cookie_id(idp
);
588 if (c
) c
->server
->accept
= a
;
593 accept_cookie_always(void *idp
)
595 cookie_default(idp
, 1);
600 accept_cookie_never(void *idp
)
602 cookie_default(idp
, 0);
609 is_path_prefix(unsigned char *d
, unsigned char *s
)
613 /* TODO: strlcmp()? --pasky */
615 if (dl
> strlen(s
)) return 0;
617 return !memcmp(d
, s
, dl
);
622 send_cookies(struct uri
*uri
)
625 struct cookie
*c
, *next
;
626 unsigned char *path
= NULL
;
627 static struct string header
;
630 if (!uri
->host
|| !uri
->data
)
633 foreach (cd
, c_domains
)
634 if (is_in_domain(cd
->domain
, uri
->host
, uri
->hostlen
)) {
635 path
= get_uri_string(uri
, URI_PATH
);
639 if (!path
) return NULL
;
641 init_string(&header
);
644 foreachsafe (c
, next
, cookies
) {
645 if (!is_in_domain(c
->domain
, uri
->host
, uri
->hostlen
)
646 || !is_path_prefix(c
->path
, path
))
649 if (c
->expires
&& c
->expires
<= now
) {
651 DBG("Cookie %s=%s (exp %"TIME_PRINT_FORMAT
") expired.",
652 c
->name
, c
->value
, (time_print_T
) c
->expires
);
660 /* Not sure if this is 100% right..? --pasky */
661 if (c
->secure
&& uri
->protocol
!= PROTOCOL_HTTPS
)
665 add_to_string(&header
, "; ");
667 add_to_string(&header
, c
->name
);
668 add_char_to_string(&header
, '=');
669 add_to_string(&header
, c
->value
);
671 DBG("Cookie: %s=%s", c
->name
, c
->value
);
677 if (!header
.length
) {
678 done_string(&header
);
685 static void done_cookies(struct module
*module
);
690 /* Buffer size is set to be enough to read long lines that
691 * save_cookies may write. 6 is choosen after the fprintf(..) call
692 * in save_cookies(). --Zas */
693 unsigned char in_buffer
[6 * MAX_STR_LEN
];
694 unsigned char *cookfile
= COOKIES_FILENAME
;
699 cookfile
= straconcat(elinks_home
, cookfile
,
700 (unsigned char *) NULL
);
701 if (!cookfile
) return;
704 /* Do it here, as we will delete whole cookies list if the file was
707 done_cookies(&cookies_module
);
710 fp
= fopen(cookfile
, "rb");
711 if (elinks_home
) mem_free(cookfile
);
714 /* XXX: We don't want to overwrite the cookies file
715 * periodically to our death. */
719 while (fgets(in_buffer
, 6 * MAX_STR_LEN
, fp
)) {
720 struct cookie
*cookie
;
721 unsigned char *p
, *q
= in_buffer
;
722 enum { NAME
= 0, VALUE
, SERVER
, PATH
, DOMAIN
, EXPIRES
, SECURE
, MEMBERS
} member
;
729 /* First find all members. */
730 for (member
= NAME
; member
< MEMBERS
; member
++, q
= ++p
) {
733 if (member
+ 1 != MEMBERS
) break; /* last field ? */
738 members
[member
].pos
= q
;
739 members
[member
].len
= p
- q
;
742 if (member
!= MEMBERS
) continue; /* Invalid line. */
744 /* Skip expired cookies if any. */
745 expires
= str_to_time_t(members
[EXPIRES
].pos
);
746 if (!expires
|| expires
<= now
) {
751 /* Prepare cookie if all members and fields was read. */
752 cookie
= mem_calloc(1, sizeof(*cookie
));
753 if (!cookie
) continue;
755 cookie
->server
= get_cookie_server(members
[SERVER
].pos
, members
[SERVER
].len
);
756 cookie
->name
= memacpy(members
[NAME
].pos
, members
[NAME
].len
);
757 cookie
->value
= memacpy(members
[VALUE
].pos
, members
[VALUE
].len
);
758 cookie
->path
= memacpy(members
[PATH
].pos
, members
[PATH
].len
);
759 cookie
->domain
= memacpy(members
[DOMAIN
].pos
, members
[DOMAIN
].len
);
761 /* Check whether all fields were correctly allocated. */
762 if (!cookie
->server
|| !cookie
->name
|| !cookie
->value
763 || !cookie
->path
|| !cookie
->domain
) {
768 cookie
->expires
= expires
;
769 cookie
->secure
= !!atoi(members
[SECURE
].pos
);
771 accept_cookie(cookie
);
779 resave_cookies_bottom_half(void *always_null
)
781 if (get_cookies_save() && get_cookies_resave())
782 save_cookies(NULL
); /* checks cookies_dirty */
785 /* Note that the cookies have been modified, and register a bottom
786 * half for saving them if appropriate. We use a bottom half so that
787 * if something makes multiple changes and calls this for each change,
788 * the cookies get saved only once at the end. */
790 set_cookies_dirty(void)
792 /* Do not check @cookies_dirty here. If the previous attempt
793 * to save cookies failed, @cookies_dirty can still be nonzero
794 * even though @resave_cookies_bottom_half is no longer in the
797 /* If @resave_cookies_bottom_half is already in the queue,
798 * @register_bottom_half does nothing. */
799 register_bottom_half(resave_cookies_bottom_half
, NULL
);
802 /* @term is non-NULL if the user told ELinks to save cookies, or NULL
803 * if ELinks decided that on its own. In the former case, this
804 * function reports errors to @term, unless CONFIG_SMALL is defined.
805 * In the latter case, this function does not save the cookies if it
806 * thinks the file is already up to date. */
808 save_cookies(struct terminal
*term
) {
810 unsigned char *cookfile
;
811 struct secure_save_info
*ssi
;
815 # define CANNOT_SAVE_COOKIES(flags, message)
817 # define CANNOT_SAVE_COOKIES(flags, message) \
820 info_box(term, flags, N_("Cannot save cookies"),\
821 ALIGN_LEFT, message); \
825 if (cookies_nosave
) {
826 assert(term
== NULL
);
831 CANNOT_SAVE_COOKIES(0, N_("ELinks was started without a home directory."));
834 if (!cookies_dirty
&& !term
)
836 if (get_cmd_opt_bool("anonymous")) {
837 CANNOT_SAVE_COOKIES(0, N_("ELinks was started with the -anonymous option."));
841 cookfile
= straconcat(elinks_home
, COOKIES_FILENAME
,
842 (unsigned char *) NULL
);
844 CANNOT_SAVE_COOKIES(0, N_("Out of memory"));
848 ssi
= secure_open(cookfile
);
851 CANNOT_SAVE_COOKIES(MSGBOX_NO_TEXT_INTL
,
852 secsave_strerror(secsave_errno
, term
));
857 foreach (c
, cookies
) {
858 if (!c
->expires
|| c
->expires
<= now
) continue;
859 if (secure_fprintf(ssi
, "%s\t%s\t%s\t%s\t%s\t%"TIME_PRINT_FORMAT
"\t%d\n",
862 empty_string_or_(c
->path
),
863 empty_string_or_(c
->domain
),
864 (time_print_T
) c
->expires
, c
->secure
) < 0)
868 secsave_errno
= SS_ERR_OTHER
; /* @secure_close doesn't always set it */
869 if (!secure_close(ssi
)) cookies_dirty
= 0;
871 CANNOT_SAVE_COOKIES(MSGBOX_NO_TEXT_INTL
,
872 secsave_strerror(secsave_errno
, term
));
874 #undef CANNOT_SAVE_COOKIES
878 init_cookies(struct module
*module
)
880 if (get_cookies_save())
884 /* Like @delete_cookie, this function does not set @cookies_dirty.
885 * The caller must do that if appropriate. */
887 free_cookies_list(LIST_OF(struct cookie
) *list
)
889 while (!list_empty(*list
)) {
890 struct cookie
*cookie
= list
->next
;
892 delete_cookie(cookie
);
897 done_cookies(struct module
*module
)
899 free_list(c_domains
);
901 if (!cookies_nosave
&& get_cookies_save())
904 free_cookies_list(&cookies
);
905 free_cookies_list(&cookie_queries
);
906 /* If @save_cookies failed above, @cookies_dirty can still be
907 * nonzero. Now if @resave_cookies_bottom_half were in the
908 * queue, it could save the empty @cookies list to the file.
913 struct module cookies_module
= struct_module(
914 /* name: */ N_("Cookies"),
915 /* options: */ cookies_options
,
917 /* submodules: */ NULL
,
919 /* init: */ init_cookies
,
920 /* done: */ done_cookies