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