about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CHANGES9
-rw-r--r--WWW/Library/Implementation/HTTP.c4
-rw-r--r--lynx.cfg13
-rw-r--r--src/LYCookie.c206
-rw-r--r--src/LYGlobalDefs.h14
-rw-r--r--src/LYMain.c3
-rw-r--r--src/LYOptions.c25
-rw-r--r--src/LYReadCFG.c3
-rw-r--r--src/LYrcFile.c11
-rw-r--r--src/LYrcFile.h4
10 files changed, 219 insertions, 73 deletions
diff --git a/CHANGES b/CHANGES
index 44473daf..32c3355c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,11 +1,14 @@
--- $LynxId: CHANGES,v 1.1006 2019/01/19 01:23:05 tom Exp $
+-- $LynxId: CHANGES,v 1.1008 2019/01/25 14:28:10 tom Exp $
 ===============================================================================
 Changes since Lynx 2.8 release
 ===============================================================================
 
-2019-01-18 (2.9.0dev.1)
-* implement features of RFC6265 -TD
+2019-01-25 (2.9.0dev.1)
+* implement features of RFC 6265 (prompted by discussion on mailing list) -TD
   * recognize but ignore "HttpOnly" attribute
+  * add COOKIE_VERSION to lynx.cfg, which can be used to specify older cookie
+    RFC; provide special cases for RFCs 2109, 2965 and 6265.
+  * add selection for cookie version in options menu.
 * add dummy "check" rule to makefile.in to help with testing Arch package -TD
 * modify tidy_tls.c to work with TLS 1.3 when built with gnutls 3.6.5 or later
   (patch by Andreas Metzler).
diff --git a/WWW/Library/Implementation/HTTP.c b/WWW/Library/Implementation/HTTP.c
index 4de00395..ca7da8a5 100644
--- a/WWW/Library/Implementation/HTTP.c
+++ b/WWW/Library/Implementation/HTTP.c
@@ -1,5 +1,5 @@
 /*
- * $LynxId: HTTP.c,v 1.176 2018/12/26 01:24:09 tom Exp $
+ * $LynxId: HTTP.c,v 1.177 2019/01/25 01:37:35 tom Exp $
  *
  * HyperText Tranfer Protocol	- Client implementation		HTTP.c
  * ==========================
@@ -1637,7 +1637,7 @@ static int HTLoadHTTP(const char *arg,
 	     * If we do have a cookie set, add it to the request buffer.  - FM
 	     */
 	    if (cookie != NULL) {
-		if (*cookie != '$') {
+		if (*cookie != '$' && USE_RFC_2965) {
 		    /*
 		     * It's a historical cookie, so signal to the server that
 		     * we support modern cookies.  - FM
diff --git a/lynx.cfg b/lynx.cfg
index bcec95d1..4a225c09 100644
--- a/lynx.cfg
+++ b/lynx.cfg
@@ -1,4 +1,4 @@
-# $LynxId: lynx.cfg,v 1.303 2018/12/25 21:27:01 tom Exp $
+# $LynxId: lynx.cfg,v 1.306 2019/01/25 23:35:20 tom Exp $
 # lynx.cfg file.
 # The default placement for this file is /usr/local/lib/lynx.cfg (Unix)
 #                                     or Lynx_Dir:lynx.cfg (VMS)
@@ -1323,6 +1323,17 @@ DEFAULT_INDEX_FILE:http://scout.wisc.edu/
 
 .h1 Cookies
 
+.h2 COOKIE_VERSION
+# Select the RFC cookie version using the RFC number.  Most users will not
+# need to change this, but because RFC 6265 makes incompatible changes versus
+# the older RFCs, it is interesting to compare behavior.
+#
+# For reference:
+.url http://tools.ietf.org/html/rfc6265
+.url http://tools.ietf.org/html/rfc2965
+.url http://tools.ietf.org/html/rfc2109
+#COOKIE_VERSION:RFC-6265
+
 .h2 SET_COOKIES
 # If SET_COOKIES is set FALSE, Lynx will ignore Set-Cookie headers
 # in http server replies.  Note that if a COOKIE_FILE is in use (see
diff --git a/src/LYCookie.c b/src/LYCookie.c
index fd86fe3c..7b139c48 100644
--- a/src/LYCookie.c
+++ b/src/LYCookie.c
@@ -1,5 +1,5 @@
 /*
- * $LynxId: LYCookie.c,v 1.137 2019/01/19 01:40:16 tom Exp $
+ * $LynxId: LYCookie.c,v 1.146 2019/01/25 14:16:47 tom Exp $
  *
  *			       Lynx Cookie Support		   LYCookie.c
  *			       ===================
@@ -26,6 +26,9 @@
  *	Modified to follow RFC-6265 regarding leading dot of Domain, and
  *	matching of hostname vs domain (2011/06/10 -TD)
  *
+ *	Modified to address differences between RFC-6262 versus RFC-2109 and
+ * 	RFC-2965 by making the older behavior optional (2019/01/25 -TD)
+ *
  *  FM's TO DO: (roughly in order of decreasing priority)
       * Persistent cookies are still experimental.  Presently cookies
 	lose many pieces of information that distinguish
@@ -83,6 +86,11 @@
 #define LeadingDot(s)     ((s)[0] == '.')
 #define SkipLeadingDot(s) (LeadingDot(s) ? ((s) + 1) : (s))
 
+#define AssumeCookieVersion(p) \
+	if (USE_RFC_2965 && (p)->version < 1) { \
+	    (p)->version = 1; \
+	}
+
 /*
  *  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
@@ -210,13 +218,63 @@ static void LYCookieJar_free(void)
 }
 #endif /* LY_FIND_LEAKS */
 
-static BOOLEAN has_embedded_dot(char *value)
+/*
+ * RFC 2109 -
+ * 4.2.2  Set-Cookie Syntax
+ * An explicitly specified domain must always start with a dot.
+ * 4.3.2  Rejecting Cookies
+ * ...rejects a cookie (shall not store its information) if any of the
+ * following is true:
+ * ...
+ * The value for the Domain attribute contains no embedded dots or does not
+ * start with a dot.
+ *
+ * RFC 2965 -
+ * 3.2.2  Set-Cookie2 Syntax
+ * Domain=value
+ *    OPTIONAL.  The value of the Domain attribute specifies the domain
+ *    for which the cookie is valid.  If an explicitly specified value
+ *    does not start with a dot, the user agent supplies a leading dot.
+ */
+static BOOLEAN has_embedded_dot(const char *value)
+{
+    BOOLEAN leading = NO;
+    BOOLEAN embedded = NO;
+    const char *p;
+
+    for (p = value; *p != '\0'; ++p) {
+	if (*p == '.') {
+	    if (p == value) {
+		leading = YES;
+	    } else if (p[1] != '\0') {
+		embedded = YES;
+	    } else {
+		embedded = NO;
+	    }
+	}
+    }
+    return (leading || USE_RFC_2965) && embedded;
+}
+
+/*
+ * RFC 6265 -
+ * 4.1.2.3.  The Domain Attribute
+ * (Note that a leading %x2E ("."), if present, is ignored even though that
+ * character is not permitted, but a trailing %x2E ("."), if present, will
+ * cause the user agent to ignore the attribute.)
+ */
+static BOOLEAN has_trailing_dot(const char *value)
 {
     BOOLEAN result = NO;
-    char *first_dot = StrChr(value, '.');
+    const char *p;
 
-    if (first_dot != NULL && first_dot[1] != '\0') {
-	result = YES;
+    for (p = value; *p != '\0'; ++p) {
+	if (*p == '.') {
+	    if (p[1] == '\0') {
+		result = YES;
+		break;
+	    }
+	}
     }
     return result;
 }
@@ -400,14 +458,29 @@ static void store_cookie(cookie * co, const char *hostname,
      * Apply sanity checks.
      *
      * RFC 2109 -
-     * Section 4.3.2, condition 1:  The value for the Path attribute is
-     * not a prefix of the request-URI.
+     * Section 4.3.2, condition 1:  The value for the Path attribute is not a
+     * prefix of the request-URI.
+     *
+     * If cookie checking for this domain is set to INVCHECK_LOOSE, then we
+     * want to bypass this check.  The user should be queried if set to
+     * INVCHECK_QUERY.
      *
-     * If cookie checking for this domain is set to INVCHECK_LOOSE,
-     * then we want to bypass this check.  The user should be queried
-     * if set to INVCHECK_QUERY.
+     * RFC 6265 -
+     * Section 4.1.2.4 describes Path, but omits any mention of the user agent
+     * rejecting a cookie because of Path.  Instead, it deals only with the
+     * cases where a cookie returned by the user agent would be valid, based on
+     * Path.  In section 8.6, RFC 6265 presents an example which would not have
+     * been valid with RFC 2109 to claim that the Path attribute is unreliable
+     * from the standpoint of the user agent.
+     *
+     * RFC 6265 does not go into any detail regarding its differences from the
+     * older RFCs 2109 / 2965.  The relevant text covering all of these changes
+     * is just this (no case studies are cited):
+     * User agents MUST implement the more liberal processing rules defined in
+     * Section 5, in order to maximize interoperability with existing servers
+     * that do not conform to the well-behaved profile defined in Section 4.
      */
-    if (!is_prefix(co->path, path)) {
+    if (!USE_RFC_6265 && !is_prefix(co->path, path)) {
 	invcheck_behaviour_t invcheck_bv = (de ? de->invcheck_bv
 					    : DEFAULT_INVCHECK_BV);
 
@@ -470,15 +543,30 @@ static void store_cookie(cookie * co, const char *hostname,
 	    freeCookie(co);
 	    return;
 	}
-	if (!has_embedded_dot(co->ddomain)) {
-	    CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->ddomain));
-	    freeCookie(co);
-	    return;
+	if (!USE_RFC_6265) {
+	    if (!has_embedded_dot(co->ddomain)) {
+		CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->ddomain));
+		freeCookie(co);
+		return;
+	    }
+	} else {
+	    if (has_trailing_dot(co->ddomain)) {
+		CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->ddomain));
+		freeCookie(co);
+		return;
+	    }
 	}
 
 	/*
+	 * RFC 2109 -
 	 * Section 4.3.2, condition 3:  The value for the request-host does not
 	 * domain-match the Domain attribute.
+	 *
+	 * RFC 6265 -
+	 * Section 4.1.2.3,
+	 * The user agent will reject cookies unless the Domain attribute
+	 * specifies a scope for the cookie that would include the origin
+	 * server.
 	 */
 	if (!domain_matches(hostname, co->ddomain)) {
 	    CTrace((tfp,
@@ -497,26 +585,34 @@ static void store_cookie(cookie * co, const char *hostname,
 	 * If cookie checking for this domain is set to INVCHECK_LOOSE, then we
 	 * want to bypass this check.  The user should be queried if set to
 	 * INVCHECK_QUERY.
+	 *
+	 * RFC 6265 -
+	 * There is nothing comparable in RFC 6265, since this check appears to
+	 * have reflected assumptions about how domain names were constructed
+	 * when RFC 2109 was written.  Section 5.1.3.  (Domain Matching) is
+	 * loosely related to these assumptions.
 	 */
-	ptr = ((hostname + strlen(hostname)) - strlen(co->domain));
-	if (StrChr(hostname, '.') < ptr) {
-	    invcheck_behaviour_t invcheck_bv = (de ? de->invcheck_bv
-						: DEFAULT_INVCHECK_BV);
+	if (!USE_RFC_6265) {
+	    ptr = ((hostname + strlen(hostname)) - strlen(co->domain));
+	    if (StrChr(hostname, '.') < ptr) {
+		invcheck_behaviour_t invcheck_bv = (de ? de->invcheck_bv
+						    : DEFAULT_INVCHECK_BV);
 
-	    switch (invcheck_bv) {
-	    case INVCHECK_LOOSE:
-		break;		/* continue as if nothing were wrong */
+		switch (invcheck_bv) {
+		case INVCHECK_LOOSE:
+		    break;	/* continue as if nothing were wrong */
 
-	    case INVCHECK_QUERY:
-		invprompt_reasons |= FAILS_COND4;
-		break;		/* will prompt later if we get that far */
+		case INVCHECK_QUERY:
+		    invprompt_reasons |= FAILS_COND4;
+		    break;	/* will prompt later if we get that far */
 
-	    case INVCHECK_STRICT:
-		CTrace((tfp,
-			"store_cookie: Rejecting because '%s' is not a prefix of '%s'.\n",
-			co->path, path));
-		freeCookie(co);
-		return;
+		case INVCHECK_STRICT:
+		    CTrace((tfp,
+			    "store_cookie: Rejecting because '%s' is not a prefix of '%s'.\n",
+			    co->path, path));
+		    freeCookie(co);
+		    return;
+		}
 	    }
 	}
     }
@@ -681,7 +777,7 @@ static void store_cookie(cookie * co, const char *hostname,
 	 * Get confirmation if we need it, and add cookie if confirmed or
 	 * 'allow' is set to always.  - FM
 	 *
-	 * Cookies read from file are accepted without confirmation prompting. 
+	 * Cookies read from file are accepted without confirmation prompting.
 	 * (Prompting may actually not be possible if LYLoadCookies is called
 	 * before curses is setup.) Maybe this should instead depend on
 	 * LYSetCookies and/or LYCookieAcceptDomains and/or
@@ -776,7 +872,7 @@ static char *scan_cookie_sublist(char *hostname,
 	    }
 
 	    /*
-	     * Skip if we have a port list and the current port is not listed. 
+	     * Skip if we have a port list and the current port is not listed.
 	     * - FM
 	     */
 	    if (co->PortList && !port_matches(port, co->PortList)) {
@@ -809,7 +905,7 @@ static char *scan_cookie_sublist(char *hostname,
 		 * Section 2.2 of RFC1945 says:
 		 *
 		 * HTTP/1.0 headers may be folded onto multiple lines if each
-		 * continuation line begins with a space or horizontal tab. 
+		 * continuation line begins with a space or horizontal tab.
 		 * All linear whitespace, including folding, has the same
 		 * semantics as SP.  [...] However, folding of header lines is
 		 * not expected by some applications, and should not be
@@ -929,18 +1025,18 @@ static unsigned parse_attribute(unsigned flags,
 	    }
 	} else {
 	    /*
-	     * If secure has a value, assume someone misused it as cookie name. 
+	     * If secure has a value, assume someone misused it as cookie name.
 	     * - FM
 	     */
 	    known_attr = NO;
 	}
-    } else if (is_attr("httponly", 8)) {
+    } else if (USE_RFC_6265 && is_attr("httponly", 8)) {
 	if (value == NULL) {
 	    known_attr = YES;	/* known, but irrelevant to lynx */
 	} else {
 	    known_attr = NO;
 	}
-    } else if (is_attr("discard", 7)) {
+    } else if (USE_RFC_2965 && is_attr("discard", 7)) {
 	if (value == NULL) {
 	    known_attr = YES;
 	    if (cur_cookie != NULL) {
@@ -948,12 +1044,12 @@ static unsigned parse_attribute(unsigned flags,
 	    }
 	} else {
 	    /*
-	     * If discard has a value, assume someone used it as a cookie name. 
+	     * If discard has a value, assume someone used it as a cookie name.
 	     * - FM
 	     */
 	    known_attr = NO;
 	}
-    } else if (is_attr("comment", 7)) {
+    } else if ((USE_RFC_2109 || USE_RFC_2965) && is_attr("comment", 7)) {
 	known_attr = YES;
 	if (cur_cookie != NULL && value &&
 	/*
@@ -963,7 +1059,7 @@ static unsigned parse_attribute(unsigned flags,
 	    StrAllocCopy(cur_cookie->comment, value);
 	    *cookie_len += (int) strlen(cur_cookie->comment);
 	}
-    } else if (is_attr("commentURL", 10)) {
+    } else if (USE_RFC_2965 && is_attr("commentURL", 10)) {
 	known_attr = YES;
 	if (cur_cookie != NULL && value &&
 	/*
@@ -1045,7 +1141,7 @@ static unsigned parse_attribute(unsigned flags,
 	    cur_cookie->flags |= COOKIE_FLAG_PATH_SET;
 	    CTrace((tfp, " ->%.*s\n", cur_cookie->pathlen, cur_cookie->path));
 	}
-    } else if (is_attr("port", 4)) {
+    } else if (USE_RFC_2965 && is_attr("port", 4)) {
 	if (cur_cookie != NULL && value &&
 	/*
 	 * Don't process a repeat port.  - FM
@@ -1064,6 +1160,7 @@ static unsigned parse_attribute(unsigned flags,
 		} else {
 		    StrAllocCopy(cur_cookie->PortList, value);
 		    *cookie_len += (int) strlen(cur_cookie->PortList);
+		    CTrace((tfp, " ->%s\n", cur_cookie->PortList));
 		}
 		known_attr = YES;
 	    } else {
@@ -1079,7 +1176,7 @@ static unsigned parse_attribute(unsigned flags,
 	    }
 	    known_attr = YES;
 	}
-    } else if (is_attr("version", 7)) {
+    } else if ((USE_RFC_2109 || USE_RFC_2965) && is_attr("version", 7)) {
 	known_attr = YES;
 	if (cur_cookie != NULL && value &&
 	/*
@@ -1390,12 +1487,7 @@ static void LYProcessSetCookies(const char *SetCookie,
 		if (cookie_len <= max_cookies_buffer
 		    && cur_cookie != NULL
 		    && !(parse_flags & FLAGS_INVALID_PORT)) {
-		    /*
-		     * Assume version 1 if not set to that or higher.  - FM
-		     */
-		    if (cur_cookie->version < 1) {
-			cur_cookie->version = 1;
-		    }
+		    AssumeCookieVersion(cur_cookie);
 		    HTList_appendObject(CombinedCookies, cur_cookie);
 		} else if (cur_cookie != NULL) {
 		    CTrace((tfp,
@@ -1444,9 +1536,7 @@ static void LYProcessSetCookies(const char *SetCookie,
     if (NumCookies <= max_cookies_domain
 	&& cookie_len <= max_cookies_buffer
 	&& cur_cookie != NULL && !(parse_flags & FLAGS_INVALID_PORT)) {
-	if (cur_cookie->version < 1) {
-	    cur_cookie->version = 1;
-	}
+	AssumeCookieVersion(cur_cookie);
 	HTList_appendObject(CombinedCookies, cur_cookie);
     } else if (cur_cookie != NULL && !(parse_flags & FLAGS_INVALID_PORT)) {
 	CTrace((tfp, "LYProcessSetCookies: Rejecting Set-Cookie2: %s=%s\n",
@@ -1689,9 +1779,7 @@ static void LYProcessSetCookies(const char *SetCookie,
 		     * at least 1, and mark it for quoting.  - FM
 		     */
 		    if (SetCookie2 != NULL) {
-			if (cur_cookie->version < 1) {
-			    cur_cookie->version = 1;
-			}
+			AssumeCookieVersion(cur_cookie);
 			cur_cookie->quoted = TRUE;
 		    }
 		    HTList_appendObject(CombinedCookies, cur_cookie);
@@ -1738,9 +1826,7 @@ static void LYProcessSetCookies(const char *SetCookie,
 	&& cookie_len <= max_cookies_buffer
 	&& cur_cookie != NULL) {
 	if (SetCookie2 != NULL) {
-	    if (cur_cookie->version < 1) {
-		cur_cookie->version = 1;
-	    }
+	    AssumeCookieVersion(cur_cookie);
 	    cur_cookie->quoted = TRUE;
 	}
 	HTList_appendObject(CombinedCookies, cur_cookie);
@@ -1759,7 +1845,7 @@ static void LYProcessSetCookies(const char *SetCookie,
     }
 
     /*
-     * OK, now we can actually store any cookies in the CombinedCookies list. 
+     * OK, now we can actually store any cookies in the CombinedCookies list.
      * - FM
      */
     cl = CombinedCookies;
@@ -2263,7 +2349,7 @@ static int LYHandleCookies(const char *arg,
 	    FREE(domain);
 	} else {
 	    /*
-	     * If there is a path string (not just a slash) in the LYNXCOOKIE: 
+	     * If there is a path string (not just a slash) in the LYNXCOOKIE:
 	     * URL, that's a cookie's lynxID and this is a request to delete it
 	     * from the Cookie Jar.  - FM
 	     */
@@ -2332,7 +2418,7 @@ static int LYHandleCookies(const char *arg,
 		/*
 		 * Prompt whether to delete all of the cookies in this domain,
 		 * or the domain if no cookies in it, or to change its 'allow'
-		 * setting, or to cancel, and then act on the user's response. 
+		 * setting, or to cancel, and then act on the user's response.
 		 * - FM
 		 */
 		if (HTList_isEmpty(de->cookie_list)) {
diff --git a/src/LYGlobalDefs.h b/src/LYGlobalDefs.h
index e1203c02..ac1df5fb 100644
--- a/src/LYGlobalDefs.h
+++ b/src/LYGlobalDefs.h
@@ -1,5 +1,5 @@
 /*
- * $LynxId: LYGlobalDefs.h,v 1.146 2018/03/27 23:05:13 tom Exp $
+ * $LynxId: LYGlobalDefs.h,v 1.150 2019/01/25 02:00:58 tom Exp $
  *
  * global variable definitions
  */
@@ -585,6 +585,18 @@ extern "C" {
 	,FORCE_PROMPT_NO	/* assume "no" where a prompt would be used */
     } FORCE_PROMPT;
 
+    extern int cookie_version;
+
+    typedef enum {
+	COOKIES_RFC_2109
+	,COOKIES_RFC_2965
+	,COOKIES_RFC_6265
+    } COOKIES_VERSION;
+
+#define USE_RFC_2109 (cookie_version == (COOKIES_RFC_2109))
+#define USE_RFC_2965 (cookie_version == (COOKIES_RFC_2965))
+#define USE_RFC_6265 (cookie_version == (COOKIES_RFC_6265))
+
 #ifdef USE_SSL
     extern int ssl_noprompt;
 #endif
diff --git a/src/LYMain.c b/src/LYMain.c
index 7c8eca0e..db53bbb5 100644
--- a/src/LYMain.c
+++ b/src/LYMain.c
@@ -1,5 +1,5 @@
 /*
- * $LynxId: LYMain.c,v 1.283 2018/12/28 14:21:31 tom Exp $
+ * $LynxId: LYMain.c,v 1.285 2019/01/25 00:06:42 tom Exp $
  */
 #include <HTUtils.h>
 #include <HTTP.h>
@@ -587,6 +587,7 @@ BOOLEAN LYQuitDefaultYes = QUIT_DEFAULT_YES;
 BOOLEAN dont_wrap_pre = FALSE;
 
 int cookie_noprompt;
+int cookie_version = COOKIES_RFC_6265;
 
 #ifdef USE_SSL
 int ssl_noprompt = FORCE_PROMPT_DFT;
diff --git a/src/LYOptions.c b/src/LYOptions.c
index 33bf66cf..c37cc440 100644
--- a/src/LYOptions.c
+++ b/src/LYOptions.c
@@ -1,4 +1,4 @@
-/* $LynxId: LYOptions.c,v 1.179 2018/12/26 01:23:33 tom Exp $ */
+/* $LynxId: LYOptions.c,v 1.181 2019/01/25 00:06:30 tom Exp $ */
 #include <HTUtils.h>
 #include <HTFTP.h>
 #include <HTTP.h>		/* 'reloading' flag */
@@ -2244,9 +2244,20 @@ static OptValues prompt_values[] =
     {FORCE_PROMPT_NO, prompt_no_string, prompt_no_string},
     END_OPTIONS
 };
-
 static const char *cookie_prompt_string = RC_FORCE_COOKIE_PROMPT;
 
+static const char RFC_2109_string[] = N_("RFC 2109");
+static const char RFC_2965_string[] = N_("RFC 2965");
+static const char RFC_6265_string[] = N_("RFC 6265");
+static OptValues cookies_values[] =
+{
+    {COOKIES_RFC_2109, RFC_2109_string, RFC_2109_string},
+    {COOKIES_RFC_2965, RFC_2965_string, RFC_2965_string},
+    {COOKIES_RFC_6265, RFC_6265_string, RFC_6265_string},
+    END_OPTIONS
+};
+static const char *cookie_version_string = RC_COOKIE_VERSION;
+
 #ifdef USE_SSL
 static const char *ssl_prompt_string = RC_FORCE_SSL_PROMPT;
 #endif
@@ -3088,6 +3099,10 @@ int postoptions(DocInfo *newdoc)
 	}
 #endif
 
+	/* Cookie Version: SELECT */
+	if (!strcmp(data[i].tag, cookie_version_string))
+	    GetOptValues(cookies_values, data[i].value, &cookie_version);
+
 	/* Cookie Prompting: SELECT */
 	if (!strcmp(data[i].tag, cookie_prompt_string))
 	    GetOptValues(prompt_values, data[i].value, &cookie_noprompt);
@@ -3799,6 +3814,12 @@ static int gen_options(char **newfile)
 	      cookies_accept_all_string);
     EndSelect(fp0);
 
+    /* Cookie Version: SELECT */
+    PutLabel(fp0, gettext("Cookie RFC-version"), cookie_version_string);
+    BeginSelect(fp0, cookie_version_string);
+    PutOptValues(fp0, cookie_version, cookies_values);
+    EndSelect(fp0);
+
     /* Cookie Prompting: SELECT */
     PutLabel(fp0, gettext("Invalid-Cookie Prompting"), cookie_prompt_string);
     BeginSelect(fp0, cookie_prompt_string);
diff --git a/src/LYReadCFG.c b/src/LYReadCFG.c
index 1b6caec9..0c2cd417 100644
--- a/src/LYReadCFG.c
+++ b/src/LYReadCFG.c
@@ -1,5 +1,5 @@
 /*
- * $LynxId: LYReadCFG.c,v 1.193 2018/05/11 00:03:10 tom Exp $
+ * $LynxId: LYReadCFG.c,v 1.194 2019/01/25 13:43:17 tom Exp $
  */
 #ifndef NO_RULES
 #include <HTRules.h>
@@ -1483,6 +1483,7 @@ static Config_Type Config_Table [] =
      PARSE_STR(RC_COOKIE_SAVE_FILE,     LYCookieSaveFile),
 #endif /* USE_PERSISTENT_COOKIES */
      PARSE_STR(RC_COOKIE_STRICT_INVALID_DOMAIN, LYCookieSStrictCheckDomains),
+     PARSE_ENU(RC_COOKIE_VERSION,       cookie_version, tbl_cookie_version),
      PARSE_Env(RC_CSO_PROXY,            0),
 #ifdef VMS
      PARSE_PRG(RC_CSWING_PATH,          ppCSWING),
diff --git a/src/LYrcFile.c b/src/LYrcFile.c
index 6942b6dc..8cdec2b4 100644
--- a/src/LYrcFile.c
+++ b/src/LYrcFile.c
@@ -1,4 +1,4 @@
-/* $LynxId: LYrcFile.c,v 1.103 2019/01/02 21:13:45 tom Exp $ */
+/* $LynxId: LYrcFile.c,v 1.104 2019/01/25 01:47:06 tom Exp $ */
 #include <HTUtils.h>
 #include <HTFTP.h>
 #include <LYUtils.h>
@@ -169,6 +169,13 @@ static Config_Enum tbl_visited_links[] = {
     { NULL,		DEFAULT_VISITED_LINKS }
 };
 
+Config_Enum tbl_cookie_version[] = {
+    { "RFC-2109",	COOKIES_RFC_2109	},
+    { "RFC-2965",	COOKIES_RFC_2965	},
+    { "RFC-6265",	COOKIES_RFC_6265	},
+    { NULL,		-1			}
+};
+
 Config_Enum tbl_force_prompt[] = {
     { "prompt",		FORCE_PROMPT_DFT	},
     { "yes",		FORCE_PROMPT_YES	},
@@ -452,6 +459,8 @@ file lists such as FTP directories.  The options are:\n\
 #endif
     MAYBE_ENU(RC_FORCE_COOKIE_PROMPT,   cookie_noprompt,    tbl_force_prompt,
 	      MSG_ENABLE_LYNXRC),
+    MAYBE_ENU(RC_COOKIE_VERSION,        cookie_version,     tbl_cookie_version,
+	      MSG_ENABLE_LYNXRC),
 #ifdef USE_SSL
     MAYBE_ENU(RC_FORCE_SSL_PROMPT,      ssl_noprompt,       tbl_force_prompt,
 	      MSG_ENABLE_LYNXRC),
diff --git a/src/LYrcFile.h b/src/LYrcFile.h
index 3cf07c01..bcba7f5b 100644
--- a/src/LYrcFile.h
+++ b/src/LYrcFile.h
@@ -1,5 +1,5 @@
 /*
- * $LynxId: LYrcFile.h,v 1.51 2018/03/27 23:03:51 tom Exp $
+ * $LynxId: LYrcFile.h,v 1.53 2019/01/25 13:40:18 tom Exp $
  */
 #ifndef LYRCFILE_H
 #define LYRCFILE_H
@@ -54,6 +54,7 @@
 #define RC_COOKIE_REJECT_DOMAINS        "cookie_reject_domains"
 #define RC_COOKIE_SAVE_FILE             "cookie_save_file"
 #define RC_COOKIE_STRICT_INVALID_DOMAIN "cookie_strict_invalid_domains"
+#define RC_COOKIE_VERSION               "cookie_version"
 #define RC_COPY_PATH                    "copy_path"
 #define RC_CSO_PROXY                    "cso_proxy"
 #define RC_CSWING_PATH                  "cswing_path"
@@ -289,6 +290,7 @@
 #define RC_ZCAT_PATH                    "zcat_path"
 #define RC_ZIP_PATH                     "zip_path"
 
+extern Config_Enum tbl_cookie_version[];
 extern Config_Enum tbl_force_prompt[];
 extern Config_Enum tbl_keypad_mode[];
 extern Config_Enum tbl_multi_bookmarks[];