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 union 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
));
317 if (!cookie
|| !name
|| !value
|| !path
|| !domain
|| !server
) {
323 done_cookie_server(server
);
326 object_nolock(cookie
, "cookie"); /* Debugging purpose. */
329 cookie
->value
= value
;
330 cookie
->domain
= domain
;
332 cookie
->server
= server
; /* the caller already locked it for us */
338 set_cookie(struct uri
*uri
, unsigned char *str
)
340 unsigned char *path
, *domain
;
341 struct cookie
*cookie
;
342 struct cookie_str cstr
;
345 if (get_cookies_accept_policy() == COOKIES_ACCEPT_NONE
)
349 DBG("set_cookie -> (%s) %s", struri(uri
), str
);
352 if (!parse_cookie_str(&cstr
, str
)) return;
354 switch (parse_header_param(str
, "path", &path
)) {
355 unsigned char *path_end
;
357 case HEADER_PARAM_FOUND
:
359 || path
[strlen(path
) - 1] != '/')
360 add_to_strn(&path
, "/");
362 if (path
[0] != '/') {
363 add_to_strn(&path
, "x");
364 memmove(path
+ 1, path
, strlen(path
) - 1);
369 case HEADER_PARAM_NOT_FOUND
:
370 path
= get_uri_string(uri
, URI_PATH
);
374 path_end
= strrchr(path
, '/');
383 if (parse_header_param(str
, "domain", &domain
) == HEADER_PARAM_NOT_FOUND
)
384 domain
= memacpy(uri
->host
, uri
->hostlen
);
385 if (domain
&& domain
[0] == '.')
386 memmove(domain
, domain
+ 1, strlen(domain
));
388 cookie
= init_cookie(memacpy(str
, cstr
.nam_end
- str
),
389 memacpy(cstr
.val_start
, cstr
.val_end
- cstr
.val_start
),
392 get_cookie_server(uri
->host
, uri
->hostlen
));
394 /* @cookie now owns @path and @domain. */
397 /* We don't actually set ->accept at the moment. But I have kept it
398 * since it will maybe help to fix bug 77 - Support for more
399 * finegrained control upon accepting of cookies. */
400 if (!cookie
->server
->accept
) {
409 /* Set cookie expiration if needed.
410 * Cookie expires at end of session by default,
411 * set to 0 by calloc().
414 * -1 is use cookie's expiration date if any
415 * 0 is force expiration at the end of session,
416 * ignoring cookie's expiration date
417 * 1+ is use cookie's expiration date,
418 * but limit age to the given number of days.
421 max_age
= get_cookies_max_age();
426 switch (parse_header_param(str
, "expires", &date
)) {
427 case HEADER_PARAM_FOUND
:
428 expires
= parse_date(&date
, NULL
, 0, 1); /* Convert date to seconds. */
434 time_t seconds
= ((time_t) max_age
)*24*3600;
435 time_t deadline
= time(NULL
) + seconds
;
437 if (expires
> deadline
) /* Over-aged cookie ? */
441 cookie
->expires
= expires
;
445 case HEADER_PARAM_NOT_FOUND
:
454 cookie
->secure
= (parse_header_param(str
, "secure", NULL
)
455 == HEADER_PARAM_FOUND
);
459 DBG("Got cookie %s = %s from %s, domain %s, "
460 "expires at %"TIME_PRINT_FORMAT
", secure %d", cookie
->name
,
461 cookie
->value
, cookie
->server
->host
, cookie
->domain
,
462 (time_print_T
) cookie
->expires
, cookie
->secure
);
466 if (!is_domain_security_ok(cookie
->domain
, uri
->host
, uri
->hostlen
)) {
468 DBG("Domain security violated: %s vs %.*s", cookie
->domain
,
469 uri
->hostlen
, uri
->host
);
471 mem_free(cookie
->domain
);
472 cookie
->domain
= memacpy(uri
->host
, uri
->hostlen
);
475 /* We have already check COOKIES_ACCEPT_NONE */
476 if (get_cookies_accept_policy() == COOKIES_ACCEPT_ASK
) {
477 add_to_list(cookie_queries
, cookie
);
478 add_questions_entry(accept_cookie_dialog
, cookie
);
482 accept_cookie(cookie
);
486 accept_cookie(struct cookie
*cookie
)
489 struct listbox_item
*root
= cookie
->server
->box_item
;
493 cookie
->box_item
= add_listbox_leaf(&cookie_browser
, root
, cookie
);
495 /* Do not weed out duplicates when loading the cookie file. It doesn't
496 * scale at all, being O(N^2) and taking about 2s with my 500 cookies
497 * (so if you don't notice that 100ms with your 100 cookies, that's
498 * not an argument). --pasky */
499 if (!cookies_nosave
) {
500 struct cookie
*c
, *next
;
502 foreachsafe (c
, next
, cookies
) {
503 if (c_strcasecmp(c
->name
, cookie
->name
)
504 || c_strcasecmp(c
->domain
, cookie
->domain
))
508 /* @set_cookies_dirty will be called below. */
512 add_to_list(cookies
, cookie
);
515 /* XXX: This crunches CPU too. --pasky */
516 foreach (cd
, c_domains
)
517 if (!c_strcasecmp(cd
->domain
, cookie
->domain
))
520 domain_len
= strlen(cookie
->domain
);
521 /* One byte is reserved for domain in struct c_domain. */
522 cd
= mem_alloc(sizeof(*cd
) + domain_len
);
525 memcpy(cd
->domain
, cookie
->domain
, domain_len
+ 1);
526 add_to_list(c_domains
, cd
);
530 static unsigned int cookie_id
= 0;
533 delete_cookie(struct cookie
*c
)
539 if (!c_strcasecmp(d
->domain
, c
->domain
))
542 foreach (cd
, c_domains
) {
543 if (!c_strcasecmp(cd
->domain
, c
->domain
)) {
557 cookie
*find_cookie_id(void *idp
)
571 reject_cookie(void *idp
)
573 struct cookie
*c
= find_cookie_id(idp
);
578 set_cookies_dirty(); /* @find_cookie_id doesn't use @cookie_queries */
583 cookie_default(void *idp
, int a
)
585 struct cookie
*c
= find_cookie_id(idp
);
587 if (c
) c
->server
->accept
= a
;
592 accept_cookie_always(void *idp
)
594 cookie_default(idp
, 1);
599 accept_cookie_never(void *idp
)
601 cookie_default(idp
, 0);
608 is_path_prefix(unsigned char *d
, unsigned char *s
)
612 /* TODO: strlcmp()? --pasky */
614 if (dl
> strlen(s
)) return 0;
616 return !memcmp(d
, s
, dl
);
621 send_cookies(struct uri
*uri
)
624 struct cookie
*c
, *next
;
625 unsigned char *path
= NULL
;
626 static struct string header
;
629 if (!uri
->host
|| !uri
->data
)
632 foreach (cd
, c_domains
)
633 if (is_in_domain(cd
->domain
, uri
->host
, uri
->hostlen
)) {
634 path
= get_uri_string(uri
, URI_PATH
);
638 if (!path
) return NULL
;
640 init_string(&header
);
643 foreachsafe (c
, next
, cookies
) {
644 if (!is_in_domain(c
->domain
, uri
->host
, uri
->hostlen
)
645 || !is_path_prefix(c
->path
, path
))
648 if (c
->expires
&& c
->expires
<= now
) {
650 DBG("Cookie %s=%s (exp %"TIME_PRINT_FORMAT
") expired.",
651 c
->name
, c
->value
, (time_print_T
) c
->expires
);
659 /* Not sure if this is 100% right..? --pasky */
660 if (c
->secure
&& uri
->protocol
!= PROTOCOL_HTTPS
)
664 add_to_string(&header
, "; ");
666 add_to_string(&header
, c
->name
);
667 add_char_to_string(&header
, '=');
668 add_to_string(&header
, c
->value
);
670 DBG("Cookie: %s=%s", c
->name
, c
->value
);
676 if (!header
.length
) {
677 done_string(&header
);
684 static void done_cookies(struct module
*module
);
689 /* Buffer size is set to be enough to read long lines that
690 * save_cookies may write. 6 is choosen after the fprintf(..) call
691 * in save_cookies(). --Zas */
692 unsigned char in_buffer
[6 * MAX_STR_LEN
];
693 unsigned char *cookfile
= COOKIES_FILENAME
;
698 cookfile
= straconcat(elinks_home
, cookfile
,
699 (unsigned char *) NULL
);
700 if (!cookfile
) return;
703 /* Do it here, as we will delete whole cookies list if the file was
706 done_cookies(&cookies_module
);
709 fp
= fopen(cookfile
, "rb");
710 if (elinks_home
) mem_free(cookfile
);
713 /* XXX: We don't want to overwrite the cookies file
714 * periodically to our death. */
718 while (fgets(in_buffer
, 6 * MAX_STR_LEN
, fp
)) {
719 struct cookie
*cookie
;
720 unsigned char *p
, *q
= in_buffer
;
721 enum { NAME
= 0, VALUE
, SERVER
, PATH
, DOMAIN
, EXPIRES
, SECURE
, MEMBERS
} member
;
728 /* First find all members. */
729 for (member
= NAME
; member
< MEMBERS
; member
++, q
= ++p
) {
732 if (member
+ 1 != MEMBERS
) break; /* last field ? */
737 members
[member
].pos
= q
;
738 members
[member
].len
= p
- q
;
741 if (member
!= MEMBERS
) continue; /* Invalid line. */
743 /* Skip expired cookies if any. */
744 expires
= str_to_time_t(members
[EXPIRES
].pos
);
745 if (!expires
|| expires
<= now
) {
750 /* Prepare cookie if all members and fields was read. */
751 cookie
= mem_calloc(1, sizeof(*cookie
));
752 if (!cookie
) continue;
754 cookie
->server
= get_cookie_server(members
[SERVER
].pos
, members
[SERVER
].len
);
755 cookie
->name
= memacpy(members
[NAME
].pos
, members
[NAME
].len
);
756 cookie
->value
= memacpy(members
[VALUE
].pos
, members
[VALUE
].len
);
757 cookie
->path
= memacpy(members
[PATH
].pos
, members
[PATH
].len
);
758 cookie
->domain
= memacpy(members
[DOMAIN
].pos
, members
[DOMAIN
].len
);
760 /* Check whether all fields were correctly allocated. */
761 if (!cookie
->server
|| !cookie
->name
|| !cookie
->value
762 || !cookie
->path
|| !cookie
->domain
) {
767 cookie
->expires
= expires
;
768 cookie
->secure
= !!atoi(members
[SECURE
].pos
);
770 accept_cookie(cookie
);
778 resave_cookies_bottom_half(void *always_null
)
780 if (get_cookies_save() && get_cookies_resave())
781 save_cookies(NULL
); /* checks cookies_dirty */
784 /* Note that the cookies have been modified, and register a bottom
785 * half for saving them if appropriate. We use a bottom half so that
786 * if something makes multiple changes and calls this for each change,
787 * the cookies get saved only once at the end. */
789 set_cookies_dirty(void)
791 /* Do not check @cookies_dirty here. If the previous attempt
792 * to save cookies failed, @cookies_dirty can still be nonzero
793 * even though @resave_cookies_bottom_half is no longer in the
796 /* If @resave_cookies_bottom_half is already in the queue,
797 * @register_bottom_half does nothing. */
798 register_bottom_half(resave_cookies_bottom_half
, NULL
);
801 /* @term is non-NULL if the user told ELinks to save cookies, or NULL
802 * if ELinks decided that on its own. In the former case, this
803 * function reports errors to @term, unless CONFIG_SMALL is defined.
804 * In the latter case, this function does not save the cookies if it
805 * thinks the file is already up to date. */
807 save_cookies(struct terminal
*term
) {
809 unsigned char *cookfile
;
810 struct secure_save_info
*ssi
;
814 # define CANNOT_SAVE_COOKIES(flags, message)
816 # define CANNOT_SAVE_COOKIES(flags, message) \
819 info_box(term, flags, N_("Cannot save cookies"),\
820 ALIGN_LEFT, message); \
824 if (cookies_nosave
) {
825 assert(term
== NULL
);
830 CANNOT_SAVE_COOKIES(0, N_("ELinks was started without a home directory."));
833 if (!cookies_dirty
&& !term
)
835 if (get_cmd_opt_bool("anonymous")) {
836 CANNOT_SAVE_COOKIES(0, N_("ELinks was started with the -anonymous option."));
840 cookfile
= straconcat(elinks_home
, COOKIES_FILENAME
,
841 (unsigned char *) NULL
);
843 CANNOT_SAVE_COOKIES(0, N_("Out of memory"));
847 ssi
= secure_open(cookfile
);
850 CANNOT_SAVE_COOKIES(MSGBOX_NO_TEXT_INTL
,
851 secsave_strerror(secsave_errno
, term
));
856 foreach (c
, cookies
) {
857 if (!c
->expires
|| c
->expires
<= now
) continue;
858 if (secure_fprintf(ssi
, "%s\t%s\t%s\t%s\t%s\t%"TIME_PRINT_FORMAT
"\t%d\n",
861 empty_string_or_(c
->path
),
862 empty_string_or_(c
->domain
),
863 (time_print_T
) c
->expires
, c
->secure
) < 0)
867 secsave_errno
= SS_ERR_OTHER
; /* @secure_close doesn't always set it */
868 if (!secure_close(ssi
)) cookies_dirty
= 0;
870 CANNOT_SAVE_COOKIES(MSGBOX_NO_TEXT_INTL
,
871 secsave_strerror(secsave_errno
, term
));
873 #undef CANNOT_SAVE_COOKIES
877 init_cookies(struct module
*module
)
879 if (get_cookies_save())
883 /* Like @delete_cookie, this function does not set @cookies_dirty.
884 * The caller must do that if appropriate. */
886 free_cookies_list(LIST_OF(struct cookie
) *list
)
888 while (!list_empty(*list
)) {
889 struct cookie
*cookie
= list
->next
;
891 delete_cookie(cookie
);
896 done_cookies(struct module
*module
)
898 free_list(c_domains
);
900 if (!cookies_nosave
&& get_cookies_save())
903 free_cookies_list(&cookies
);
904 free_cookies_list(&cookie_queries
);
905 /* If @save_cookies failed above, @cookies_dirty can still be
906 * nonzero. Now if @resave_cookies_bottom_half were in the
907 * queue, it could save the empty @cookies list to the file.
912 struct module cookies_module
= struct_module(
913 /* name: */ N_("Cookies"),
914 /* options: */ cookies_options
,
916 /* submodules: */ NULL
,
918 /* init: */ init_cookies
,
919 /* done: */ done_cookies