]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/ntlm_auth/SMB/ntlm_auth.c
Major rewrite of proxy authentication to support other schemes than
[thirdparty/squid.git] / helpers / ntlm_auth / SMB / ntlm_auth.c
1 /*
2 * (C) 2000 Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it>
3 * Distributed freely under the terms of the GNU General Public License,
4 * version 2. See the file COPYING for licensing details
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
14 *
15 * Warning! We MIGHT be open to buffer overflows caused by malformed headers
16 *
17 * DONE list:
18 * use hashtable to cache authentications. Yummy performance-boost, security
19 * loss should be negligible for two reasons:
20 * - if they-re using NT, there's no security to speak of anyways
21 * - it can't get worse than basic authentication.
22 * cache expiration
23 * challenge hash expiry and renewal.
24 * PDC disconnect, after X minutes of inactivity
25 *
26 * TODO list:
27 * change syntax from options-driven to args-driven, with args domain
28 * or domain[/\]server, and an arbitrary number of backup Domain Controllers
29 * we don't really need the "status" management, it's more for debugging
30 * purposes. Remove it.
31 * Maybe we can cache the created challenge, saving more time?
32 *
33 */
34
35
36 #include "config.h"
37 #include "ntlmauth.h"
38 #include "ntlm.h"
39 #include "util.h"
40
41 #define BUFFER_SIZE 10240
42
43 #if HAVE_STDLIB_H
44 #include <stdlib.h>
45 #endif
46
47
48 #if HAVE_GETOPT_H
49 #include <getopt.h>
50 #endif
51
52
53
54 #ifdef HAVE_STRING_H
55 #include <string.h>
56 #endif
57 #ifdef HAVE_CTYPE_H
58 #include <ctype.h>
59 #endif
60
61 char load_balance = 0, failover_enabled = 0, protocol_pedantic = 0;
62
63 dc *controllers = NULL;
64 int numcontrollers = 0;
65 dc *current_dc;
66
67 /* housekeeping cycle and periodic operations */
68 static unsigned char need_dc_resurrection = 0;
69 static void
70 resurrect_dead_dc()
71 {
72 int j;
73 dc *c = controllers;
74
75 need_dc_resurrection = 0;
76 for (j = 0; j < numcontrollers; j++)
77 if (c->status != DC_OK && is_dc_ok(c->domain, c->controller))
78 c->status = DC_OK;
79 }
80
81 /* makes a null-terminated string upper-case. Changes CONTENTS! */
82 static void
83 uc(char *string)
84 {
85 char *p = string, c;
86 while ((c = *p)) {
87 *p = toupper(c);
88 p++;
89 }
90 }
91
92 /* makes a null-terminated string lower-case. Changes CONTENTS! */
93 static void
94 lc(char *string)
95 {
96 char *p = string, c;
97 while ((c = *p)) {
98 *p = tolower(c);
99 p++;
100 }
101 }
102
103 /*
104 * options:
105 * -b try load-balancing the domain-controllers
106 * -f fail-over to another DC if DC connection fails.
107 * domain\controller ...
108 */
109 void
110 process_options(int argc, char *argv[])
111 {
112 int opt, j, had_error = 0;
113 dc *new_dc = NULL, *last_dc = NULL;
114 while (-1 != (opt = getopt(argc, argv, "bf"))) {
115 switch (opt) {
116 case 'b':
117 load_balance = 1;
118 break;
119 case 'f':
120 failover_enabled = 1;
121 break;
122 default:
123 fprintf(stderr, "unknown option: -%c. Exiting\n", opt);
124 had_error = 1;
125 }
126 }
127 if (had_error)
128 exit(1);
129 /* Okay, now begin filling controllers up */
130 /* we can avoid memcpy-ing, and just reuse argv[] */
131 for (j = optind; j < argc; j++) {
132 char *d, *c;
133 d = argv[j];
134 if (NULL == (c = strchr(d, '\\')) && NULL == (c = strchr(d, '/'))) {
135 fprintf(stderr, "Couldn't grok domain-controller %s\n", d);
136 continue;
137 }
138 *c++ = '\0';
139 new_dc = (dc *) malloc(sizeof(dc));
140 if (!new_dc) {
141 fprintf(stderr, "Malloc error while parsing DC options\n");
142 continue;
143 }
144 /* capitalize */
145 uc(c);
146 uc(d);
147 numcontrollers++;
148 new_dc->domain = d;
149 new_dc->controller = c;
150 new_dc->status = DC_OK;
151 if (controllers == NULL) { /* first controller */
152 controllers = new_dc;
153 last_dc = new_dc;
154 } else {
155 last_dc->next = new_dc; /* can't be null */
156 last_dc = new_dc;
157 }
158 }
159 if (numcontrollers == 0) {
160 fprintf(stderr, "You must specify at least one domain-controller!\n");
161 exit(1);
162 }
163 last_dc->next = controllers; /* close the queue, now it's circular */
164 }
165
166 /* tries connecting to the domain controllers in the "controllers" ring,
167 * with failover if the adequate option is specified.
168 */
169 const char *
170 obtain_challenge()
171 {
172 int j = 0;
173 const char *ch;
174 for (j = 0; j < numcontrollers; j++) {
175 if (current_dc->status == DC_OK) {
176 ch = make_challenge(current_dc->domain, current_dc->controller);
177 if (ch)
178 return ch; /* All went OK, returning */
179 /* Huston, we've got a problem. Take this DC out of the loop */
180 current_dc->status = DC_DEAD;
181 need_dc_resurrection = 1;
182 }
183 if (failover_enabled == 0) /* No failover. Just return */
184 return NULL;
185 /* Try with the next */
186 current_dc = current_dc->next;
187 }
188 return NULL;
189 }
190
191 void
192 manage_request()
193 {
194 ntlmhdr *fast_header;
195 char buf[10240];
196 const char *ch;
197 char *ch2, *decoded, *cred;
198 int plen;
199
200 if (fgets(buf, BUFFER_SIZE, stdin) == NULL)
201 exit(0); /* BIIG buffer */
202 ch2 = memchr(buf, '\n', BUFFER_SIZE); /* safer against overrun than strchr */
203 if (ch2) {
204 *ch2 = '\0'; /* terminate the string at newline. */
205 ch = ch2;
206 }
207 debug("ntlm authenticator. Got '%s' from Squid\n", buf);
208
209 if (memcmp(buf, "KK ", 3) == 0) { /* authenticate-request */
210 /* figure out what we got */
211 decoded = base64_decode(buf + 3);
212 /* Note: we don't need to manage memory at this point, since
213 * base64_decode returns a pointer to static storage.
214 */
215
216 if (!decoded) { /* decoding failure, return error */
217 SEND("NA Packet format error, couldn't base64-decode");
218 return;
219 }
220 /* fast-track-decode request type. */
221 fast_header = (struct _ntlmhdr *) decoded;
222
223 /* sanity-check: it IS a NTLMSSP packet, isn't it? */
224 if (memcmp(fast_header->signature, "NTLMSSP", 8) != 0) {
225 SEND("NA Broken authentication packet");
226 return;
227 }
228 switch (fast_header->type) {
229 case NTLM_NEGOTIATE:
230 SEND("NA Invalid negotiation request received");
231 return;
232 /* notreached */
233 case NTLM_CHALLENGE:
234 SEND("NA Got a challenge. We refuse to have our authority disputed");
235 return;
236 /* notreached */
237 case NTLM_AUTHENTICATE:
238 /* check against the DC */
239 plen = strlen(buf) * 3 / 4; /* we only need it here. Optimization */
240 cred = ntlm_check_auth((ntlm_authenticate *) decoded, plen);
241 if (cred == NULL) {
242 switch (ntlm_errno) {
243 case NTLM_LOGON_ERROR:
244 SEND("NA authentication failure");
245 dc_disconnect();
246 current_dc = current_dc->next;
247 return;
248 case NTLM_SERVER_ERROR:
249 SEND("BH Domain Controller Error");
250 dc_disconnect();
251 current_dc = current_dc->next;
252 return;
253 case NTLM_PROTOCOL_ERROR:
254 SEND("BH Domain Controller communication error");
255 dc_disconnect();
256 current_dc = current_dc->next;
257 return;
258 case NTLM_NOT_CONNECTED:
259 SEND("BH Domain Controller (or network) died on us");
260 dc_disconnect();
261 current_dc = current_dc->next;
262 return;
263 case NTLM_BAD_PROTOCOL:
264 SEND("BH Domain controller failure");
265 dc_disconnect();
266 current_dc = current_dc->next;
267 return;
268 default:
269 SEND("BH Unhandled error while talking to Domain Controller");
270 dc_disconnect();
271 current_dc = current_dc->next;
272 return;
273 }
274 }
275 lc(cred); /* let's lowercase them for our convenience */
276 SEND2("AF %s", cred);
277 return;
278 default:
279 SEND("BH unknown authentication packet type");
280 return;
281 }
282
283
284 return;
285 }
286 if (memcmp(buf, "YR", 2) == 0) { /* refresh-request */
287 dc_disconnect();
288 ch = obtain_challenge();
289 while (ch == NULL) {
290 sleep(30);
291 ch = obtain_challenge();
292 }
293 SEND2("TT %s", ch);
294 if (need_dc_resurrection) /* looks like a good moment... */
295 resurrect_dead_dc();
296 return;
297 }
298 SEND("BH Helper detected protocol error");
299 return;
300 /********* END ********/
301
302
303 }
304
305 int
306 main(int argc, char *argv[])
307 {
308
309 debug("starting up...\n");
310
311 process_options(argc, argv);
312
313 debug("options processed OK\n");
314
315 /* initialize FDescs */
316 setbuf(stdout, NULL);
317 setbuf(stderr, NULL);
318
319 /* select the first domain controller we're going to use */
320 current_dc = controllers;
321 if (load_balance != 0 && numcontrollers > 1) {
322 int n;
323 pid_t pid = getpid();
324 n = pid % numcontrollers;
325 debug("load balancing. Selected controller #%d\n", n);
326 while (n > 0) {
327 current_dc = current_dc->next;
328 n--;
329 }
330 }
331 while (1) {
332 debug("managing request\n");
333 manage_request();
334 }
335 return 0;
336 }