1 /** @file
2
3 A plugin that performs basic HTTP proxy authentication
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <unistd.h>
28
29 #include "ts/ts.h"
30 #include "tscore/ink_defs.h"
31
32 #define PLUGIN_NAME "basic_auth"
33
34 static char base64_codes[256];
35
36 static char *
base64_decode(const char * input)37 base64_decode(const char *input)
38 {
39 #define decode(A) ((unsigned int)base64_codes[(int)input[A]])
40
41 char *output;
42 char *obuf;
43 int len;
44
45 for (len = 0; (input[len] != '\0') && (input[len] != '='); len++) {
46 ;
47 }
48
49 output = obuf = (char *)TSmalloc((len * 6) / 8 + 3);
50
51 while (len > 0) {
52 *output++ = decode(0) << 2 | decode(1) >> 4;
53 *output++ = decode(1) << 4 | decode(2) >> 2;
54 *output++ = decode(2) << 6 | decode(3);
55 len -= 4;
56 input += 4;
57 }
58
59 /*
60 * We don't need to worry about leftover bits because
61 * we've allocated a few extra characters and if there
62 * are leftover bits they will be zeros because the extra
63 * inputs will be '='s and '=' decodes to 0.
64 */
65
66 *output = '\0';
67 return obuf;
68
69 #undef decode
70 }
71
72 static int
authorized(char * user,char * password)73 authorized(char *user, char *password)
74 {
75 /*
76 * This routine checks the validity of the user name and
77 * password. UNIX systems, enter your own authorization code
78 * here. ToDO: This doesn't do anything useful now.
79 */
80 if (user && password) {
81 return 1;
82 } else {
83 return 0;
84 }
85 }
86
87 static void
handle_dns(TSHttpTxn txnp,TSCont contp)88 handle_dns(TSHttpTxn txnp, TSCont contp)
89 {
90 TSMBuffer bufp;
91 TSMLoc hdr_loc;
92 TSMLoc field_loc;
93 const char *val;
94 const char *ptr;
95
96 char *user, *password;
97 int authval_length;
98
99 if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
100 TSError("[%s] Couldn't retrieve client request header", PLUGIN_NAME);
101 goto done;
102 }
103
104 field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_PROXY_AUTHORIZATION, TS_MIME_LEN_PROXY_AUTHORIZATION);
105 if (!field_loc) {
106 TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
107 TSError("[%s] No Proxy-Authorization field", PLUGIN_NAME);
108 goto done;
109 }
110
111 val = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, -1, &authval_length);
112 if (NULL == val) {
113 TSError("[%s] No value in Proxy-Authorization field", PLUGIN_NAME);
114 TSHandleMLocRelease(bufp, hdr_loc, field_loc);
115 TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
116 goto done;
117 }
118
119 ptr = val;
120 if (strncmp(ptr, "Basic", 5) != 0) {
121 TSError("[%s] No Basic auth type in Proxy-Authorization", PLUGIN_NAME);
122 TSHandleMLocRelease(bufp, hdr_loc, field_loc);
123 TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
124 goto done;
125 }
126
127 ptr += 5;
128 while ((*ptr == ' ') || (*ptr == '\t')) {
129 ptr += 1;
130 }
131
132 user = base64_decode(ptr);
133 password = strchr(user, ':');
134 if (!password) {
135 TSError("[%s] No password in authorization information", PLUGIN_NAME);
136 TSfree(user);
137 TSHandleMLocRelease(bufp, hdr_loc, field_loc);
138 TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
139 goto done;
140 }
141 *password = '\0';
142 password += 1;
143
144 if (!authorized(user, password)) {
145 TSError("[%s] %s:%s not authorized", PLUGIN_NAME, user, password);
146 TSfree(user);
147 TSHandleMLocRelease(bufp, hdr_loc, field_loc);
148 TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
149 goto done;
150 }
151
152 TSfree(user);
153 TSHandleMLocRelease(bufp, hdr_loc, field_loc);
154 TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
155 TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
156 return;
157
158 done:
159 TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
160 TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
161 }
162
163 static void
handle_response(TSHttpTxn txnp)164 handle_response(TSHttpTxn txnp)
165 {
166 TSMBuffer bufp;
167 TSMLoc hdr_loc;
168 TSMLoc field_loc;
169 const char *insert = "Basic realm=\"proxy\"";
170 int len = strlen(insert);
171
172 if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
173 TSError("[%s] Couldn't retrieve client response header", PLUGIN_NAME);
174 goto done;
175 }
176
177 TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED);
178 TSHttpHdrReasonSet(bufp, hdr_loc, TSHttpHdrReasonLookup(TS_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED),
179 strlen(TSHttpHdrReasonLookup(TS_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED)));
180
181 TSMimeHdrFieldCreate(bufp, hdr_loc, &field_loc); // Probably should check for errors
182 TSMimeHdrFieldNameSet(bufp, hdr_loc, field_loc, TS_MIME_FIELD_PROXY_AUTHENTICATE, TS_MIME_LEN_PROXY_AUTHENTICATE);
183 TSMimeHdrFieldValueStringInsert(bufp, hdr_loc, field_loc, -1, insert, len);
184 TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc);
185
186 TSHandleMLocRelease(bufp, hdr_loc, field_loc);
187 TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
188
189 done:
190 TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
191 }
192
193 static int
auth_plugin(TSCont contp,TSEvent event,void * edata)194 auth_plugin(TSCont contp, TSEvent event, void *edata)
195 {
196 TSHttpTxn txnp = (TSHttpTxn)edata;
197
198 switch (event) {
199 case TS_EVENT_HTTP_OS_DNS:
200 handle_dns(txnp, contp);
201 return 0;
202 case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
203 handle_response(txnp);
204 return 0;
205 default:
206 break;
207 }
208
209 return 0;
210 }
211
212 void
TSPluginInit(int argc ATS_UNUSED,const char * argv[]ATS_UNUSED)213 TSPluginInit(int argc ATS_UNUSED, const char *argv[] ATS_UNUSED)
214 {
215 int i, cc;
216 TSPluginRegistrationInfo info;
217
218 info.plugin_name = PLUGIN_NAME;
219 info.vendor_name = "Apache Software Foundation";
220 info.support_email = "dev@trafficserver.apache.org";
221
222 if (TSPluginRegister(&info) != TS_SUCCESS) {
223 TSError("[%s] Plugin registration failed", PLUGIN_NAME);
224 }
225
226 /* Build translation table */
227 for (i = 0, cc = 0; i < 256; i++) {
228 base64_codes[i] = 0;
229 }
230 for (i = 'A'; i <= 'Z'; i++) {
231 base64_codes[i] = cc++;
232 }
233 for (i = 'a'; i <= 'z'; i++) {
234 base64_codes[i] = cc++;
235 }
236 for (i = '0'; i <= '9'; i++) {
237 base64_codes[i] = cc++;
238 }
239 base64_codes['+'] = cc++;
240 base64_codes['/'] = cc++;
241
242 TSHttpHookAdd(TS_HTTP_OS_DNS_HOOK, TSContCreate(auth_plugin, NULL));
243 }
244