]> granicus.if.org Git - apache/blob - server/util_cookies.c
Fix string constness to get rid of gcc compiler warnings by -Wwrite-strings.
[apache] / server / util_cookies.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "util_cookies.h"
18 #include "apr_lib.h"
19 #include "apr_strings.h"
20 #include "http_log.h"
21
22 #define LOG_PREFIX "ap_cookie: "
23
24 /**
25  * Write an RFC2109 compliant cookie.
26  *
27  * @param r The request
28  * @param name The name of the cookie.
29  * @param val The value to place in the cookie.
30  * @param attrs The string containing additional cookie attributes. If NULL, the
31  *              DEFAULT_ATTRS will be used.
32  * @param maxage If non zero, a Max-Age header will be added to the cookie.
33  */
34 AP_DECLARE(apr_status_t) ap_cookie_write(request_rec * r, const char *name, const char *val,
35                                          const char *attrs, long maxage, ...)
36 {
37
38     const char *buffer;
39     const char *rfc2109;
40     apr_table_t *t;
41     va_list vp;
42
43     /* handle expiry */
44     buffer = "";
45     if (maxage) {
46         buffer = apr_pstrcat(r->pool, "Max-Age=", apr_ltoa(r->pool, maxage), ";", NULL);
47     }
48
49     /* create RFC2109 compliant cookie */
50     rfc2109 = apr_pstrcat(r->pool, name, "=", val, ";",
51                           buffer,
52                           attrs && strlen(attrs) > 0 ?
53                           attrs : DEFAULT_ATTRS, NULL);
54     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, LOG_PREFIX
55                   "user '%s' set cookie: '%s'", r->user, rfc2109);
56
57     /* write the cookie to the header table(s) provided */
58     va_start(vp, maxage);
59     while ((t = va_arg(vp, apr_table_t *))) {
60         apr_table_addn(t, SET_COOKIE, rfc2109);
61     }
62     va_end(vp);
63
64     return APR_SUCCESS;
65
66 }
67
68 /**
69  * Write an RFC2965 compliant cookie.
70  *
71  * @param r The request
72  * @param name2 The name of the cookie.
73  * @param val The value to place in the cookie.
74  * @param attrs2 The string containing additional cookie attributes. If NULL, the
75  *               DEFAULT_ATTRS will be used.
76  * @param maxage If non zero, a Max-Age header will be added to the cookie.
77  */
78 AP_DECLARE(apr_status_t) ap_cookie_write2(request_rec * r, const char *name2, const char *val,
79                                           const char *attrs2, long maxage, ...)
80 {
81
82     const char *buffer;
83     const char *rfc2965;
84     apr_table_t *t;
85     va_list vp;
86
87     /* handle expiry */
88     buffer = "";
89     if (maxage) {
90         buffer = apr_pstrcat(r->pool, "Max-Age=", apr_ltoa(r->pool, maxage), ";", NULL);
91     }
92
93     /* create RFC2965 compliant cookie */
94     rfc2965 = apr_pstrcat(r->pool, name2, "=", val, ";",
95                           buffer,
96                           attrs2 && strlen(attrs2) > 0 ?
97                           attrs2 : DEFAULT_ATTRS, NULL);
98     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, LOG_PREFIX
99                   "user '%s' set cookie2: '%s'", r->user, rfc2965);
100
101     /* write the cookie to the header table(s) provided */
102     va_start(vp, maxage);
103     while ((t = va_arg(vp, apr_table_t *))) {
104         apr_table_addn(t, SET_COOKIE2, rfc2965);
105     }
106     va_end(vp);
107
108     return APR_SUCCESS;
109
110 }
111
112 /**
113  * Remove an RFC2109 compliant cookie.
114  *
115  * @param r The request
116  * @param name The name of the cookie.
117  */
118 AP_DECLARE(apr_status_t) ap_cookie_remove(request_rec * r, const char *name, const char *attrs, ...)
119 {
120     apr_table_t *t;
121     va_list vp;
122
123     /* create RFC2109 compliant cookie */
124     const char *rfc2109 = apr_pstrcat(r->pool, name, "=;Max-Age=0;",
125                                 attrs ? attrs : CLEAR_ATTRS, NULL);
126     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, LOG_PREFIX
127                   "user '%s' removed cookie: '%s'", r->user, rfc2109);
128
129     /* write the cookie to the header table(s) provided */
130     va_start(vp, attrs);
131     while ((t = va_arg(vp, apr_table_t *))) {
132         apr_table_addn(t, SET_COOKIE, rfc2109);
133     }
134     va_end(vp);
135
136     return APR_SUCCESS;
137
138 }
139
140 /**
141  * Remove an RFC2965 compliant cookie.
142  *
143  * @param r The request
144  * @param name2 The name of the cookie.
145  */
146 AP_DECLARE(apr_status_t) ap_cookie_remove2(request_rec * r, const char *name2, const char *attrs2, ...)
147 {
148     apr_table_t *t;
149     va_list vp;
150
151     /* create RFC2965 compliant cookie */
152     const char *rfc2965 = apr_pstrcat(r->pool, name2, "=;Max-Age=0;",
153                                 attrs2 ? attrs2 : CLEAR_ATTRS, NULL);
154     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, LOG_PREFIX
155                   "user '%s' removed cookie2: '%s'", r->user, rfc2965);
156
157     /* write the cookie to the header table(s) provided */
158     va_start(vp, attrs2);
159     while ((t = va_arg(vp, apr_table_t *))) {
160         apr_table_addn(t, SET_COOKIE2, rfc2965);
161     }
162     va_end(vp);
163
164     return APR_SUCCESS;
165
166 }
167
168 /* Iterate through the cookies, isolate our cookie and then remove it.
169  *
170  * If our cookie appears two or more times, but with different values,
171  * remove it twice and set the duplicated flag to true. Remove any
172  * $path or other attributes following our cookie if present. If we end
173  * up with an empty cookie, remove the whole header.
174  */
175 static int extract_cookie_line(ap_cookie_do * v, const char *key, const char *val)
176 {
177     char *last1, *last2;
178     char *cookie = apr_pstrdup(v->r->pool, val);
179     const char *name = apr_pstrcat(v->r->pool, v->name ? v->name : "", "=", NULL);
180     size_t len = strlen(name);
181     const char *new_cookie = "";
182     const char *comma = ",";
183     char *next1;
184     const char *semi = ";";
185     char *next2;
186     const char *sep = "";
187     int cookies = 0;
188
189     /* find the cookie called name */
190     int eat = 0;
191     next1 = apr_strtok(cookie, comma, &last1);
192     while (next1) {
193         next2 = apr_strtok(next1, semi, &last2);
194         while (next2) {
195             char *trim = next2;
196             while (apr_isspace(*trim)) {
197                 trim++;
198             }
199             if (!strncmp(trim, name, len)) {
200                 if (v->encoded) {
201                     if (strcmp(v->encoded, trim + len)) {
202                         v->duplicated = 1;
203                     }
204                 }
205                 v->encoded = apr_pstrdup(v->r->pool, trim + len);
206                 eat = 1;
207             }
208             else {
209                 if (*trim != '$') {
210                     cookies++;
211                     eat = 0;
212                 }
213                 if (!eat) {
214                     new_cookie = apr_pstrcat(v->r->pool, new_cookie, sep, next2, NULL);
215                 }
216             }
217             next2 = apr_strtok(NULL, semi, &last2);
218             sep = semi;
219         }
220
221         next1 = apr_strtok(NULL, comma, &last1);
222         sep = comma;
223     }
224
225     /* any cookies left over? */
226     if (cookies) {
227         apr_table_addn(v->new_cookies, key, new_cookie);
228     }
229
230     return 1;
231 }
232
233 /**
234  * Read a cookie called name, placing its value in val.
235  *
236  * Both the Cookie and Cookie2 headers are scanned for the cookie.
237  *
238  * If the cookie is duplicated, this function returns APR_EGENERAL. If found,
239  * and if remove is non zero, the cookie will be removed from the headers, and
240  * thus kept private from the backend.
241  */
242 AP_DECLARE(apr_status_t) ap_cookie_read(request_rec * r, const char *name, const char **val,
243                                         int remove)
244 {
245
246     ap_cookie_do v;
247     v.r = r;
248     v.encoded = NULL;
249     v.new_cookies = apr_table_make(r->pool, 10);
250     v.duplicated = 0;
251     v.name = name;
252
253     apr_table_do((int (*) (void *, const char *, const char *))
254                  extract_cookie_line, (void *) &v, r->headers_in,
255                  "Cookie", "Cookie2", NULL);
256     if (v.duplicated) {
257         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
258          "client submitted cookie '%s' more than once: %s", v.name, r->uri);
259         return APR_EGENERAL;
260     }
261
262     /* remove our cookie(s), and replace them */
263     if (remove) {
264         apr_table_unset(r->headers_in, "Cookie");
265         apr_table_unset(r->headers_in, "Cookie2");
266         r->headers_in = apr_table_overlay(r->pool, r->headers_in, v.new_cookies);
267     }
268
269     *val = v.encoded;
270
271     return APR_SUCCESS;
272
273 }
274
275 /**
276  * Sanity check a given string that it exists, is not empty,
277  * and does not contain the special characters '=', ';' and '&'.
278  *
279  * It is used to sanity check the cookie names.
280  */
281 AP_DECLARE(apr_status_t) ap_cookie_check_string(const char *string)
282 {
283     if (!string || !*string || ap_strchr_c(string, '=') || ap_strchr_c(string, '&') ||
284         ap_strchr_c(string, ';')) {
285         return APR_EGENERAL;
286     }
287     return APR_SUCCESS;
288 }