about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/LYCookie.c182
-rw-r--r--src/LYCookie.h3
2 files changed, 117 insertions, 68 deletions
diff --git a/src/LYCookie.c b/src/LYCookie.c
index ddc0ac9c..486c7c97 100644
--- a/src/LYCookie.c
+++ b/src/LYCookie.c
@@ -1,5 +1,5 @@
 /*
- * $LynxId: LYCookie.c,v 1.109 2011/05/13 20:40:20 tom Exp $
+ * $LynxId: LYCookie.c,v 1.118 2011/06/09 10:47:35 tom Exp $
  *
  *			       Lynx Cookie Support		   LYCookie.c
  *			       ===================
@@ -23,6 +23,9 @@
  *   http://www.ietf.org/internet-drafts/draft-ietf-http-state-man-mec-10.txt
  *		- kw					1998-12-11
  *
+ *	Modified to follow RFC-6265 regarding leading dot of Domain, and
+ *	matching of hostname vs domain (2011/06/10 -TD)
+ *
  *  TO DO: (roughly in order of decreasing priority)
       * Persistent cookies are still experimental.  Presently cookies
 	lose many pieces of information that distinguish
@@ -78,6 +81,9 @@
 
 #define CTrace(p) CTRACE2(TRACE_COOKIES, p)
 
+#define LeadingDot(s)     ((s)[0] == '.')
+#define SkipLeadingDot(s) (LeadingDot(s) ? ((s) + 1) : (s))
+
 /*
  *  The first level of the cookie list is a list indexed by the domain
  *  string; cookies with the same domain will be placed in the same
@@ -99,6 +105,7 @@ struct _cookie {
     char *comment;		/* Comment to show to user */
     char *commentURL;		/* URL for comment to show to user */
     char *domain;		/* Domain for which this cookie is valid */
+    char *ddomain;		/* Domain without leading "." */
     int port;			/* Server port from which this cookie was given (usu. 80) */
     char *PortList;		/* List of ports for which cookie can be sent */
     char *path;			/* Path prefix for which this cookie is valid */
@@ -162,12 +169,21 @@ static void freeCookie(cookie * co)
 	FREE(co->comment);
 	FREE(co->commentURL);
 	FREE(co->domain);
+	FREE(co->ddomain);
 	FREE(co->path);
 	FREE(co->PortList);
 	FREE(co);
     }
 }
 
+static void freeCookies(domain_entry * de)
+{
+    FREE(de->domain);
+    FREE(de->ddomain);
+    HTList_delete(de->cookie_list);
+    de->cookie_list = NULL;
+}
+
 #ifdef LY_FIND_LEAKS
 static void LYCookieJar_free(void)
 {
@@ -179,7 +195,7 @@ static void LYCookieJar_free(void)
     CTrace((tfp, "LYCookieJar_free\n"));
     while (dl) {
 	if ((de = dl->object) != NULL) {
-	    CTrace((tfp, "...LYCookieJar_free domain %s\n", de->domain));
+	    CTrace((tfp, "...LYCookieJar_free domain %s\n", NonNull(de->ddomain)));
 	    cl = de->cookie_list;
 	    while (cl) {
 		next = cl->next;
@@ -190,9 +206,7 @@ static void LYCookieJar_free(void)
 		}
 		cl = next;
 	    }
-	    FREE(de->domain);
-	    HTList_delete(de->cookie_list);
-	    de->cookie_list = NULL;
+	    freeCookies(de);
 	    FREE(dl->object);
 	}
 	dl = dl->next;
@@ -203,34 +217,45 @@ static void LYCookieJar_free(void)
 }
 #endif /* LY_FIND_LEAKS */
 
+static BOOLEAN has_embedded_dot(char *value)
+{
+    BOOLEAN result = NO;
+    char *first_dot = strchr(value, '.');
+
+    if (first_dot != NULL && first_dot[1] != '\0') {
+	result = YES;
+    }
+    return result;
+}
+
 /*
- *  Compare two hostnames as specified in Section 2 of:
- *   http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-state-man-mec-02.txt
- *	- AK & FM
+ * Compare a string against a domain as specified in RFC-6265 Section 5.1.3
  */
-static BOOLEAN host_matches(const char *A,
-			    const char *B)
+static BOOLEAN domain_matches(const char *value,
+			      const char *domain)
 {
-    /*
-     * The following line will handle both numeric IP addresses and FQDNs.  Do
-     * numeric addresses require special handling?
-     */
-    if (*B != '.' && !strcasecomp(A, B))
-	return YES;
+    BOOLEAN result = NO;
 
-    /*
-     * The following will pass a "dotted tail" match to "a.b.c.e" as described
-     * in Section 2 of draft-ietf-http-state-man-mec-10.txt.
-     */
-    if (*B == '.' && B[1] != '\0' && B[1] != '.' && *A != '.') {
-	int diff = (int) (strlen(A) - strlen(B));
-
-	if (diff > 0) {
-	    if (!strcasecomp((A + diff), B))
-		return YES;
+    if (isEmpty(value)) {
+	CTrace((tfp, "BUG: comparing empty value in domain_matches\n"));
+    } else if (isEmpty(domain)) {
+	CTrace((tfp, "BUG: comparing empty domain in domain_matches\n"));
+    } else {
+	if (!strcasecomp(value, domain)) {
+	    result = YES;
+	} else {
+	    int value_len = (int) strlen(value);
+	    int suffix_len = (int) strlen(domain);
+	    int offset = value_len - suffix_len;
+
+	    if (offset > 1
+		&& value[offset - 1] == '.'
+		&& !strcasecomp(value + offset, domain)) {
+		result = YES;
+	    }
 	}
     }
-    return NO;
+    return result;
 }
 
 /*
@@ -305,18 +330,20 @@ static domain_entry *find_domain_entry(const char *name)
 {
     HTList *hl;
     domain_entry *de = NULL;
+    const char *find;
 
     if (name != 0
-	&& *name != '\0') {
+	&& *(find = SkipLeadingDot(name)) != '\0') {
 	for (hl = domain_list; hl != NULL; hl = hl->next) {
 	    de = (domain_entry *) hl->object;
-	    if (de != NULL && de->domain != NULL) {
+	    if (de != NULL && de->domain != NULL && de->ddomain != NULL) {
 		CTrace((tfp,
-			"...test_domain_entry(%s) bv:%u, invcheck_bv:%u\n",
-			de->domain,
+			"...test_domain_entry(%s) ->(%s) bv:%u, invcheck_bv:%u\n",
+			find,
+			NonNull(de->ddomain),
 			de->bv,
 			de->invcheck_bv));
-		if (!strcasecomp(name, de->domain)) {
+		if (!strcasecomp(find, de->ddomain)) {
 		    break;
 		}
 	    }
@@ -330,6 +357,12 @@ static domain_entry *find_domain_entry(const char *name)
     return de;
 }
 
+static void SetCookieDomain(cookie * co, const char *domain)
+{
+    StrAllocCopy(co->ddomain, SkipLeadingDot(domain));
+    CTrace((tfp, "SetCookieDomain(%s)\n", co->ddomain));
+}
+
 /*
  *  Store a cookie somewhere in the domain list. - AK & FM
  */
@@ -418,20 +451,33 @@ static void store_cookie(cookie * co, const char *hostname,
 	}
 
 	/*
+	 * RFC 2109 -
 	 * Section 4.3.2, condition 2:  The value for the Domain attribute
 	 * contains no embedded dots or does not start with a dot.  (A dot is
 	 * embedded if it's neither the first nor last character.) Note that we
 	 * added a lead dot ourselves if a domain attribute value otherwise
 	 * qualified.  - FM
+	 *
+	 * RFC 6265 -
+	 * If the first character of the attribute-value string is %x2E ("."):
+	 *
+	 * Let cookie-domain be the attribute-value without the leading %x2E
+	 * (".") character.
+	 *
+	 * Otherwise:
+	 *
+	 * Let cookie-domain be the entire attribute-value.
+	 *
+	 * Convert the cookie-domain to lower case.
 	 */
-	if (co->domain[0] != '.' || co->domain[1] == '\0') {
-	    CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->domain));
+	SetCookieDomain(co, co->domain);
+	if (isEmpty(co->domain)) {
+	    CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->ddomain));
 	    freeCookie(co);
 	    return;
 	}
-	ptr = strchr((co->domain + 1), '.');
-	if (ptr == NULL || ptr[1] == '\0') {
-	    CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->domain));
+	if (!has_embedded_dot(co->ddomain)) {
+	    CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->ddomain));
 	    freeCookie(co);
 	    return;
 	}
@@ -440,15 +486,16 @@ static void store_cookie(cookie * co, const char *hostname,
 	 * Section 4.3.2, condition 3:  The value for the request-host does not
 	 * domain-match the Domain attribute.
 	 */
-	if (!host_matches(hostname, co->domain)) {
+	if (!domain_matches(hostname, co->ddomain)) {
 	    CTrace((tfp,
 		    "store_cookie: Rejecting domain '%s' for host '%s'.\n",
-		    co->domain, hostname));
+		    co->ddomain, hostname));
 	    freeCookie(co);
 	    return;
 	}
 
 	/*
+	 * RFC 2109 -
 	 * Section 4.3.2, condition 4:  The request-host is an HDN (not IP
 	 * address) and has the form HD, where D is the value of the Domain
 	 * attribute, and H is a string that contains one or more dots.
@@ -493,12 +540,12 @@ static void store_cookie(cookie * co, const char *hostname,
 	if (invprompt_reasons & FAILS_COND4) {
 	    HTSprintf0(&msg,
 		       INVALID_COOKIE_DOMAIN_CONFIRMATION,
-		       co->domain,
+		       co->ddomain,
 		       hostname);
 	    if (!HTForcedPrompt(cookie_noprompt, msg, NO)) {
 		CTrace((tfp,
 			"store_cookie: Rejecting domain '%s' for host '%s'.\n",
-			co->domain,
+			co->ddomain,
 			hostname));
 		freeCookie(co);
 		FREE(msg);
@@ -535,6 +582,7 @@ static void store_cookie(cookie * co, const char *hostname,
 	de->invcheck_bv = DEFAULT_INVCHECK_BV;	/* should this go here? */
 	cookie_list = de->cookie_list = HTList_new();
 	StrAllocCopy(de->domain, co->domain);
+	StrAllocCopy(de->ddomain, co->ddomain);
 	HTList_appendObject(domain_list, de);
     }
 
@@ -695,8 +743,8 @@ static char *scan_cookie_sublist(char *hostname,
 		    (co->value ? co->value : "(no value)")));
 	    CTrace((tfp, "\t%s %s %d %s %s %d%s\n",
 		    hostname,
-		    (co->domain ? co->domain : "(no domain)"),
-		    host_matches(hostname, co->domain),
+		    (co->ddomain ? co->ddomain : "(no domain)"),
+		    domain_matches(hostname, co->ddomain),
 		    path, co->path,
 		    (co->pathlen > 0)
 		    ? !is_prefix(co->path, path)
@@ -721,7 +769,7 @@ static char *scan_cookie_sublist(char *hostname,
 	 */
 	if (co->domain != 0 &&
 	    co->name != 0 &&
-	    host_matches(hostname, co->domain) &&
+	    domain_matches(hostname, co->ddomain) &&
 	    (co->pathlen == 0 || is_prefix(co->path, path))) {
 	    /*
 	     * Skip if the secure flag is set and we don't have a secure
@@ -989,6 +1037,7 @@ static unsigned parse_attribute(unsigned flags,
 	    }
 	    *cookie_len += (int) strlen(cur_cookie->domain);
 	    cur_cookie->flags |= COOKIE_FLAG_DOMAIN_SET;
+	    SetCookieDomain(cur_cookie, cur_cookie->domain);
 	}
     } else if (is_attr("path", 4)) {
 	known_attr = YES;
@@ -1390,6 +1439,7 @@ static void LYProcessSetCookies(const char *SetCookie,
 		cur_cookie->port = port;
 		parse_flags = 0;
 		cur_cookie->quoted = TRUE;
+		SetCookieDomain(cur_cookie, cur_cookie->domain);
 	    }
 	    FREE(value);
 	}
@@ -1683,6 +1733,7 @@ static void LYProcessSetCookies(const char *SetCookie,
 		parse_flags = 0;
 		cur_cookie->quoted = Quoted;
 		Quoted = FALSE;
+		SetCookieDomain(cur_cookie, cur_cookie->domain);
 	    }
 	    FREE(value);
 	}
@@ -1873,9 +1924,7 @@ char *LYAddCookieHeader(char *hostname,
 		 * No cookies in this domain, and no default accept/reject
 		 * choice was set by the user, so delete the domain.  - FM
 		 */
-		FREE(de->domain);
-		HTList_delete(de->cookie_list);
-		de->cookie_list = NULL;
+		freeCookies(de);
 		HTList_removeObject(domain_list, de);
 		FREE(de);
 	    }
@@ -1982,6 +2031,7 @@ void LYLoadCookies(char *cookie_file)
 	CTrace((tfp, "expires:\t%s\n", ctime(&expires)));
 	moo = newCookie();
 	StrAllocCopy(moo->domain, domain);
+	SetCookieDomain(moo, domain);
 	StrAllocCopy(moo->path, path);
 	StrAllocCopy(moo->name, name);
 	if (value[0] == '"' &&
@@ -2028,7 +2078,7 @@ void LYLoadCookies(char *cookie_file)
 	 */
 	moo->flags |= COOKIE_FLAG_FROM_FILE | COOKIE_FLAG_EXPIRES_SET |
 	    COOKIE_FLAG_PATH_SET;
-	if (domain[0] == '.')
+	if (LeadingDot(domain))
 	    moo->flags |= COOKIE_FLAG_DOMAIN_SET;
 	if (secure[0] != 'F')
 	    moo->flags |= COOKIE_FLAG_SECURE;
@@ -2101,8 +2151,10 @@ void LYStoreCookies(char *cookie_file)
 	    if ((co = (cookie *) cl->object) == NULL)
 		continue;
 
-	    CTrace((tfp, "LYStoreCookies: %" PRI_time_t " cf %" PRI_time_t " ",
-		    CAST_time_t (now), CAST_time_t (co->expires)));
+	    CTrace((tfp, "LYStoreCookies: %" PRI_time_t " %s %" PRI_time_t " ",
+		    CAST_time_t (now),
+		    (now < co->expires) ? "<" : ">",
+		    CAST_time_t (co->expires)));
 
 	    if ((co->flags & COOKIE_FLAG_DISCARD)) {
 		CTrace((tfp, "not stored - DISCARD\n"));
@@ -2125,15 +2177,15 @@ void LYStoreCookies(char *cookie_file)
 	    fprintf(cookie_handle, "%s\t%s\t%s\t%s\t%" PRI_time_t
 		    "\t%s\t%s%s%s\n",
 		    de->domain,
-		    (de->domain[0] == '.') ? "TRUE" : "FALSE",
+		    (co->flags & COOKIE_FLAG_DOMAIN_SET) ? "TRUE" : "FALSE",
 		    co->path,
-		    co->flags & COOKIE_FLAG_SECURE ? "TRUE" : "FALSE",
+		    (co->flags & COOKIE_FLAG_SECURE) ? "TRUE" : "FALSE",
 		    CAST_time_t (co->expires), co->name,
 		    (co->quoted ? "\"" : ""),
 		    NonNull(co->value),
 		    (co->quoted ? "\"" : ""));
 
-	    CTrace((tfp, "STORED\n"));
+	    CTrace((tfp, "STORED %s\n", de->ddomain));
 	}
     }
     if (cookie_handle != NULL) {
@@ -2251,9 +2303,7 @@ static int LYHandleCookies(const char *arg,
 			     * got confirmation on deleting the domain, so do
 			     * it.  - FM
 			     */
-			    FREE(de->domain);
-			    HTList_delete(de->cookie_list);
-			    de->cookie_list = NULL;
+			    freeCookies(de);
 			    HTList_removeObject(domain_list, de);
 			    FREE(de);
 			    HTProgress(DOMAIN_EATEN);
@@ -2292,7 +2342,7 @@ static int LYHandleCookies(const char *arg,
 			 * Set to accept all cookies from this domain.  - FM
 			 */
 			de->bv = ACCEPT_ALWAYS;
-			HTUserMsg2(ALWAYS_ALLOWING_COOKIES, de->domain);
+			HTUserMsg2(ALWAYS_ALLOWING_COOKIES, de->ddomain);
 			return (HT_NO_DATA);
 
 		    case 'C':
@@ -2309,9 +2359,7 @@ static int LYHandleCookies(const char *arg,
 			     * We had an empty domain, so we were asked to
 			     * delete it.  - FM
 			     */
-			    FREE(de->domain);
-			    HTList_delete(de->cookie_list);
-			    de->cookie_list = NULL;
+			    freeCookies(de);
 			    HTList_removeObject(domain_list, de);
 			    FREE(de);
 			    HTProgress(DOMAIN_EATEN);
@@ -2346,9 +2394,7 @@ static int LYHandleCookies(const char *arg,
 			 * Check whether to delete the empty domain.  - FM
 			 */
 			if (HTConfirm(DELETE_EMPTY_DOMAIN_CONFIRMATION)) {
-			    FREE(de->domain);
-			    HTList_delete(de->cookie_list);
-			    de->cookie_list = NULL;
+			    freeCookies(de);
 			    HTList_removeObject(domain_list, de);
 			    FREE(de);
 			    HTProgress(DOMAIN_EATEN);
@@ -2362,7 +2408,7 @@ static int LYHandleCookies(const char *arg,
 			 * domain.  - FM
 			 */
 			de->bv = QUERY_USER;
-			HTUserMsg2(PROMPTING_TO_ALLOW_COOKIES, de->domain);
+			HTUserMsg2(PROMPTING_TO_ALLOW_COOKIES, de->ddomain);
 			return (HT_NO_DATA);
 
 		    case 'V':
@@ -2370,7 +2416,7 @@ static int LYHandleCookies(const char *arg,
 			 * Set to reject all cookies from this domain.  - FM
 			 */
 			de->bv = REJECT_ALWAYS;
-			HTUserMsg2(NEVER_ALLOWING_COOKIES, de->domain);
+			HTUserMsg2(NEVER_ALLOWING_COOKIES, de->ddomain);
 			if ((!HTList_isEmpty(de->cookie_list)) &&
 			    HTConfirm(DELETE_ALL_COOKIES_IN_DOMAIN))
 			    goto Delete_all_cookies_in_domain;
@@ -2450,7 +2496,7 @@ static int LYHandleCookies(const char *arg,
 	 * Show the domain link and 'allow' setting.  - FM
 	 */
 	HTSprintf0(&buf, "<dt>%s<dd><a href=\"%s//%s/\">Domain=%s</a>\n",
-		   de->domain, STR_LYNXCOOKIE, de->domain, de->domain);
+		   de->ddomain, STR_LYNXCOOKIE, de->ddomain, de->ddomain);
 	PUTS(buf);
 	switch (de->bv) {
 	case (ACCEPT_ALWAYS):
@@ -2493,7 +2539,7 @@ static int LYHandleCookies(const char *arg,
 		StrAllocCopy(value, NO_VALUE);
 	    }
 	    HTSprintf0(&buf, "<dd><a href=\"%s//%s/%s\">%s=%s</a>\n",
-		       STR_LYNXCOOKIE, de->domain, co->lynxID, name, value);
+		       STR_LYNXCOOKIE, de->ddomain, co->lynxID, name, value);
 	    FREE(name);
 	    FREE(value);
 	    PUTS(buf);
@@ -2665,6 +2711,7 @@ static void cookie_domain_flag_set(char *domainstr,
 	    }
 
 	    StrAllocCopy(de->domain, strsmall);
+	    StrAllocCopy(de->ddomain, SkipLeadingDot(strsmall));
 	    de->cookie_list = HTList_new();
 	    HTList_appendObject(domain_list, de);
 	}
@@ -2727,6 +2774,7 @@ void LYConfigCookies(void)
     };
     unsigned n;
 
+    CTrace((tfp, "LYConfigCookies\n"));
     for (n = 0; n < TABLESIZE(table); n++) {
 	if (*(table[n].domain) != NULL) {
 	    cookie_domain_flag_set(*(table[n].domain), table[n].flag);
diff --git a/src/LYCookie.h b/src/LYCookie.h
index e1bf835f..ecbf9301 100644
--- a/src/LYCookie.h
+++ b/src/LYCookie.h
@@ -1,4 +1,4 @@
-/* $LynxId: LYCookie.h,v 1.19 2010/09/25 11:35:24 tom Exp $ */
+/* $LynxId: LYCookie.h,v 1.20 2011/06/07 08:29:39 tom Exp $ */
 #ifndef LYCOOKIES_H
 #define LYCOOKIES_H
 
@@ -35,6 +35,7 @@ extern "C" {
 
     struct _domain_entry {
 	char *domain;		/* Domain for which these cookies are valid */
+	char *ddomain;		/* Domain without leading "." */
 	behaviour_t bv;
 	invcheck_behaviour_t invcheck_bv;
 	HTList *cookie_list;