1
17 package org.apache.tomcat.util.http;
18
19 import java.nio.charset.Charset;
20 import java.nio.charset.StandardCharsets;
21 import java.text.FieldPosition;
22 import java.util.BitSet;
23 import java.util.Date;
24
25 import org.apache.juli.logging.Log;
26 import org.apache.juli.logging.LogFactory;
27 import org.apache.tomcat.util.buf.ByteChunk;
28 import org.apache.tomcat.util.buf.MessageBytes;
29 import org.apache.tomcat.util.http.parser.Cookie;
30 import org.apache.tomcat.util.res.StringManager;
31
32 public class Rfc6265CookieProcessor extends CookieProcessorBase {
33
34 private static final Log log = LogFactory.getLog(Rfc6265CookieProcessor.class);
35
36 private static final StringManager sm =
37 StringManager.getManager(Rfc6265CookieProcessor.class.getPackage().getName());
38
39 private static final BitSet domainValid = new BitSet(128);
40
41 static {
42 for (char c = '0'; c <= '9'; c++) {
43 domainValid.set(c);
44 }
45 for (char c = 'a'; c <= 'z'; c++) {
46 domainValid.set(c);
47 }
48 for (char c = 'A'; c <= 'Z'; c++) {
49 domainValid.set(c);
50 }
51 domainValid.set('.');
52 domainValid.set('-');
53 }
54
55
56 @Override
57 public Charset getCharset() {
58 return StandardCharsets.UTF_8;
59 }
60
61
62 @Override
63 public void parseCookieHeader(MimeHeaders headers,
64 ServerCookies serverCookies) {
65
66 if (headers == null) {
67
68 return;
69 }
70
71
72 int pos = headers.findHeader("Cookie", 0);
73 while (pos >= 0) {
74 MessageBytes cookieValue = headers.getValue(pos);
75
76 if (cookieValue != null && !cookieValue.isNull() ) {
77 if (cookieValue.getType() != MessageBytes.T_BYTES ) {
78 if (log.isDebugEnabled()) {
79 Exception e = new Exception();
80
81 log.debug("Cookies: Parsing cookie as String. Expected bytes.", e);
82 }
83 cookieValue.toBytes();
84 }
85 if (log.isDebugEnabled()) {
86 log.debug("Cookies: Parsing b[]: " + cookieValue.toString());
87 }
88 ByteChunk bc = cookieValue.getByteChunk();
89
90 Cookie.parseCookie(bc.getBytes(), bc.getOffset(), bc.getLength(),
91 serverCookies);
92 }
93
94
95 pos = headers.findHeader("Cookie", ++pos);
96 }
97 }
98
99
100 @Override
101 public String generateHeader(javax.servlet.http.Cookie cookie) {
102
103
104 StringBuffer header = new StringBuffer();
105
106
107
108
109
110
111 header.append(cookie.getName());
112 header.append('=');
113 String value = cookie.getValue();
114 if (value != null && value.length() > 0) {
115 validateCookieValue(value);
116 header.append(value);
117 }
118
119
120 int maxAge = cookie.getMaxAge();
121 if (maxAge > -1) {
122
123 header.append("; Max-Age=");
124 header.append(maxAge);
125
126
127
128
129
130
131 header.append ("; Expires=");
132
133 if (maxAge == 0) {
134 header.append(ANCIENT_DATE);
135 } else {
136 COOKIE_DATE_FORMAT.get().format(
137 new Date(System.currentTimeMillis() + maxAge * 1000L),
138 header,
139 new FieldPosition(0));
140 }
141 }
142
143 String domain = cookie.getDomain();
144 if (domain != null && domain.length() > 0) {
145 validateDomain(domain);
146 header.append("; Domain=");
147 header.append(domain);
148 }
149
150 String path = cookie.getPath();
151 if (path != null && path.length() > 0) {
152 validatePath(path);
153 header.append("; Path=");
154 header.append(path);
155 }
156
157 if (cookie.getSecure()) {
158 header.append("; Secure");
159 }
160
161 if (cookie.isHttpOnly()) {
162 header.append("; HttpOnly");
163 }
164
165 SameSiteCookies sameSiteCookiesValue = getSameSiteCookies();
166
167 if (!sameSiteCookiesValue.equals(SameSiteCookies.UNSET)) {
168 header.append("; SameSite=");
169 header.append(sameSiteCookiesValue.getValue());
170 }
171
172 return header.toString();
173 }
174
175
176 private void validateCookieValue(String value) {
177 int start = 0;
178 int end = value.length();
179
180 if (end > 1 && value.charAt(0) == '"' && value.charAt(end - 1) == '"') {
181 start = 1;
182 end--;
183 }
184
185 char[] chars = value.toCharArray();
186 for (int i = start; i < end; i++) {
187 char c = chars[i];
188 if (c < 0x21 || c == 0x22 || c == 0x2c || c == 0x3b || c == 0x5c || c == 0x7f) {
189 throw new IllegalArgumentException(sm.getString(
190 "rfc6265CookieProcessor.invalidCharInValue", Integer.toString(c)));
191 }
192 }
193 }
194
195
196 private void validateDomain(String domain) {
197 int i = 0;
198 int prev = -1;
199 int cur = -1;
200 char[] chars = domain.toCharArray();
201 while (i < chars.length) {
202 prev = cur;
203 cur = chars[i];
204 if (!domainValid.get(cur)) {
205 throw new IllegalArgumentException(sm.getString(
206 "rfc6265CookieProcessor.invalidDomain", domain));
207 }
208
209 if ((prev == '.' || prev == -1) && (cur == '.' || cur == '-')) {
210 throw new IllegalArgumentException(sm.getString(
211 "rfc6265CookieProcessor.invalidDomain", domain));
212 }
213
214 if (prev == '-' && cur == '.') {
215 throw new IllegalArgumentException(sm.getString(
216 "rfc6265CookieProcessor.invalidDomain", domain));
217 }
218 i++;
219 }
220
221 if (cur == '.' || cur == '-') {
222 throw new IllegalArgumentException(sm.getString(
223 "rfc6265CookieProcessor.invalidDomain", domain));
224 }
225 }
226
227
228 private void validatePath(String path) {
229 char[] chars = path.toCharArray();
230
231 for (int i = 0; i < chars.length; i++) {
232 char ch = chars[i];
233 if (ch < 0x20 || ch > 0x7E || ch == ';') {
234 throw new IllegalArgumentException(sm.getString(
235 "rfc6265CookieProcessor.invalidPath", path));
236 }
237 }
238 }
239 }
240