1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2012, OmniTI Computer Consulting, Inc. All rights reserved.
23 */
24
25 #include <sys/kmem.h>
26 #include <sys/systm.h>
27 #include <sys/stropts.h>
28 #include <sys/strsun.h>
29 #include <sys/socketvar.h>
30 #include <sys/sockfilter.h>
31 #include <sys/note.h>
32 #include <sys/taskq.h>
33
34 static struct modlmisc httpf_modlmisc = {
35 &mod_miscops,
36 "Kernel HTTP socket filter"
37 };
38
39 static struct modlinkage httpf_modlinkage = {
40 MODREV_1,
41 &httpf_modlmisc,
42 NULL
43 };
44 /*
45 * Name of the HTTP filter
46 */
47 #define HTTPFILT_MODULE "httpfilt"
48 #define MAX_HTTP_FILTER_SIZE 8192
49
50 /*
51 * httpf filter cookie
52 */
53 typedef struct httpf {
54 size_t httpf_bytes_in; /* bytes read */
55 int httpf_method; /* HTTP method */
56 int httpf_sm; /* \r\n\r\n | \n\n state machine */
57 char httpf_ff[4]; /* the first 4 bytes of the data stream */
58 union {
59 char httpfu_ff[4]; /* the first 4 bytes of the data stream */
60 uint32_t httpfu_w; /* in an easy-to-compare 32-bit word */
61 } httpf_u;
62 #define httpf_ff httpf_u.httpfu_ff
63 #define httpf_ffw httpf_u.httpfu_w
64 } httpf_t;
65
66 #ifdef _BIG_ENDIAN
67 #define CHAR_TO_32(a, b, c, d) ((d) + ((c) << 8) + ((b) << 16) + ((a) << 24))
68 #else
69 #define CHAR_TO_32(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
70 #endif
71
72 #define WORD_GET CHAR_TO_32('G', 'E', 'T', ' ')
73 #define WORD_PUT CHAR_TO_32('P', 'U', 'T', ' ')
74 #define WORD_POST CHAR_TO_32('P', 'O', 'S', 'T')
75 #define WORD_HEAD CHAR_TO_32('H', 'E', 'A', 'D')
76
77 #define HTTPF_METHOD_INVALID -1
78 #define HTTPF_METHOD_UNSET 0
79 #define HTTPF_METHOD_GET 1
80 #define HTTPF_METHOD_HEAD 2
81 #define HTTPF_METHOD_PUT 3
82 #define HTTPF_METHOD_POST 4
83
84 static int
85 httpf_method_from_ff(uint32_t ffw)
86 {
87 switch (ffw) {
88 case WORD_GET:
89 return (HTTPF_METHOD_GET);
90 case WORD_PUT:
91 return (HTTPF_METHOD_PUT);
92 case WORD_POST:
93 return (HTTPF_METHOD_POST);
94 case WORD_HEAD:
95 return (HTTPF_METHOD_HEAD);
96 }
97
98 return (HTTPF_METHOD_INVALID);
99 }
100
101 static int
102 httpf_progress_crlfcrlf_state(httpf_t *httpf, unsigned char ch) {
103 #define HTTPF_STATE(a) httpf->httpf_sm = (a)
104 #define IF_HTTPF_TOKEN(a) if ((ch) == (a))
105 switch (httpf->httpf_sm) {
106 case 0:
107 IF_HTTPF_TOKEN('\r') HTTPF_STATE(1);
108 IF_HTTPF_TOKEN('\n') HTTPF_STATE(3);
109 break;
110 case 1:
111 IF_HTTPF_TOKEN('\n') HTTPF_STATE(2);
112 else HTTPF_STATE(0);
113 break;
114 case 2:
115 IF_HTTPF_TOKEN('\n') return (0);
116 IF_HTTPF_TOKEN('\r') HTTPF_STATE(3);
117 else HTTPF_STATE(0);
118 break;
119 case 3:
120 IF_HTTPF_TOKEN('\n') return (0);
121 IF_HTTPF_TOKEN('\r') HTTPF_STATE(1);
122 else HTTPF_STATE(0);
123 break;
124 }
125 return (-1);
126 }
127
128 static int
129 httpf_process_input(httpf_t *httpf, mblk_t *mp) {
130 int i, blen = MBLKL(mp), dlen = msgdsize(mp);
131
132 for (i = 0; i < dlen; i++) {
133 while (blen-- == 0) {
134 DTRACE_PROBE(http__zero__len__mblk);
135 mp = mp->b_cont;
136 /*
137 * If we are here we must have some of "dlen" bytes
138 * left, which means we MUST have a non-zero-length
139 * mblk in the chain. We will never fall off the
140 * edge after a zero-length mblk UNLESS there's no
141 * dlen left, which means we'll never get here!
142 */
143 ASSERT(mp != NULL);
144 blen = MBLKL(mp);
145 }
146
147 /* Collect the first four bytes for a protocol validation */
148 if (httpf->httpf_method == HTTPF_METHOD_UNSET &&
149 httpf->httpf_bytes_in < 4)
150 httpf->httpf_ff[httpf->httpf_bytes_in] = mp->b_rptr[i];
151
152 httpf->httpf_bytes_in++;
153
154 /*
155 * if we haven't yet determined out HTTP method, do it at
156 * exactly 4 bytes into the stream.
157 */
158 if (httpf->httpf_method == HTTPF_METHOD_UNSET &&
159 httpf->httpf_bytes_in == 4) {
160 /*
161 * if we find no good method, we can't defer this stream
162 */
163 httpf->httpf_method =
164 httpf_method_from_ff(httpf->httpf_ffw);
165 if (httpf->httpf_method == HTTPF_METHOD_INVALID)
166 return (-1);
167 }
168
169 /*
170 * if the method is set, start looking for either
171 * \r\n\r\n or \n\n
172 */
173 if (httpf->httpf_method > HTTPF_METHOD_UNSET)
174 if (httpf_progress_crlfcrlf_state(httpf, mp->b_rptr[i])
175 == 0) {
176 return (1);
177 }
178 }
179 return (0);
180 }
181
182 /*
183 * Allocate httpf state
184 */
185 sof_rval_t
186 httpf_attach_passive_cb(sof_handle_t handle, sof_handle_t ph,
187 void *parg, struct sockaddr *laddr, socklen_t laddrlen,
188 struct sockaddr *faddr, socklen_t faddrlen, void **cookiep)
189 {
190 httpf_t *new;
191
192 _NOTE(ARGUNUSED(handle, ph, parg, faddr, faddrlen, laddr, laddrlen));
193
194 /* Allocate the SSL context for the new connection */
195 new = kmem_zalloc(sizeof (httpf_t), KM_NOSLEEP);
196 if (new == NULL)
197 return (SOF_RVAL_ENOMEM);
198
199 new->httpf_bytes_in = 0;
200 new->httpf_method = HTTPF_METHOD_UNSET;
201
202 *cookiep = new;
203 /*
204 * We are awaiting a request, defer the notification of this
205 * connection until it is completed.
206 */
207 return (SOF_RVAL_DEFER);
208 }
209
210 void
211 httpf_detach_cb(sof_handle_t handle, void *cookie, cred_t *cr)
212 {
213 httpf_t *httpf = (httpf_t *)cookie;
214
215 _NOTE(ARGUNUSED(handle, cr));
216
217 if (httpf == NULL)
218 return;
219
220 kmem_free(httpf, sizeof (httpf_t));
221 }
222
223 /*
224 * Called for each incoming segment.
225 */
226 mblk_t *
227 httpf_data_in_cb(sof_handle_t handle, void *cookie, mblk_t *mp, int flags,
228 size_t *lenp)
229 {
230 httpf_t *httpf = cookie;
231
232 _NOTE(ARGUNUSED(flags, lenp));
233
234 if (httpf == NULL) {
235 sof_bypass(handle);
236 return (mp);
237 }
238
239 if (mp == NULL)
240 return (mp);
241
242 if (httpf_process_input(httpf, mp))
243 sof_newconn_ready(handle);
244
245 if (httpf->httpf_bytes_in > MAX_HTTP_FILTER_SIZE)
246 sof_newconn_ready(handle);
247
248 return (mp);
249 }
250
251 sof_ops_t httpf_ops = {
252 .sofop_attach_passive = httpf_attach_passive_cb,
253 .sofop_detach = httpf_detach_cb,
254 .sofop_data_in = httpf_data_in_cb,
255 };
256
257 int
258 _init(void)
259 {
260 int error;
261
262 if ((error = sof_register(SOF_VERSION, HTTPFILT_MODULE, &httpf_ops, 0))
263 != 0) {
264 return (error);
265 }
266 if ((error = mod_install(&httpf_modlinkage)) != 0)
267 (void) sof_unregister(HTTPFILT_MODULE);
268
269 return (error);
270 }
271
272 int
273 _fini(void)
274 {
275 int error;
276
277 if ((error = sof_unregister(HTTPFILT_MODULE)) != 0)
278 return (error);
279
280 return (mod_remove(&httpf_modlinkage));
281 }
282
283 int
284 _info(struct modinfo *modinfop)
285 {
286 return (mod_info(&httpf_modlinkage, modinfop));
287 }