]> git.ipfire.org Git - thirdparty/squid.git/blame - helpers/ntlm_auth/mswin_sspi/ntlm_auth.c
Windows port: addition of native authentication helpers.
[thirdparty/squid.git] / helpers / ntlm_auth / mswin_sspi / ntlm_auth.c
CommitLineData
6e785d85 1/*
2 * win32_ntlm_auth: helper for NTLM Authentication for Squid Cache
3 *
4 * (C)2002,2003 Guido Serassio - Acme Consulting S.r.l.
5 *
6 * Authors:
7 * Guido Serassio <guido.serassio@acmeconsulting.it>
8 * Acme Consulting S.r.l., Italy <http://www.acmeconsulting.it>
9 *
10 * With contributions from others mentioned in the change history section
11 * below.
12 *
13 * Based on previous work of Francesco Chemolli and Robert Collins.
14 *
15 * Dependencies: Windows NT4 SP4 and later.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 * History:
32 *
33 * Version 1.21
34 * 21-02-2004 Guido Serassio
35 * Removed control of use of NTLM NEGOTIATE packet from
36 * command line, now the support is automatic.
37 * Version 1.20
38 * 30-11-2003 Guido Serassio
39 * Added support for NTLM local calls.
40 * Added control of use of NTLM NEGOTIATE packet from
41 * command line.
42 * Updated documentation.
43 * Version 1.10
44 * 07-09-2003 Guido Serassio
45 * Now is true NTLM authenticator.
46 * More debug info.
47 * Updated documentation.
48 * Version 1.0
49 * 29-06-2002 Guido Serassio
50 * First release.
51 *
52 *
53 */
54
55#include "util.h"
56#if HAVE_GETOPT_H
57#include <getopt.h>
58#endif
59#include "ntlm.h"
60#if HAVE_CTYPE_H
61#include <ctype.h>
62#endif
63
64#define BUFFER_SIZE 10240
65
66int debug_enabled = 0;
67int NTLM_packet_debug_enabled = 0;
68
69static int have_challenge;
70
71char * NTAllowedGroup;
72char * NTDisAllowedGroup;
73int UseDisallowedGroup = 0;
74int UseAllowedGroup = 0;
75#if FAIL_DEBUG
76int fail_debug_enabled = 0;
77#endif
78
79/* makes a null-terminated string upper-case. Changes CONTENTS! */
80void
81uc(char *string)
82{
83 char *p = string, c;
84 while ((c = *p)) {
85 *p = toupper(c);
86 p++;
87 }
88}
89
90/* makes a null-terminated string lower-case. Changes CONTENTS! */
91static void
92lc(char *string)
93{
94 char *p = string, c;
95 while ((c = *p)) {
96 *p = tolower(c);
97 p++;
98 }
99}
100
101void
102helperfail(const char *reason)
103{
104#if FAIL_DEBUG
105 fail_debug_enabled =1;
106#endif
107 SEND2("BH %s", reason);
108}
109
110/*
111 options:
112 -d enable debugging.
113 -v enable verbose NTLM packet debugging.
114 -l if specified, changes behavior on failures to last-ditch.
115 -A can specify a Windows Local Group name allowed to authenticate.
116 -D can specify a Windows Local Group name not allowed to authenticate.
117 */
118char *my_program_name = NULL;
119
120void
121usage()
122{
123 fprintf(stderr,
124 "Usage: %s [-d] [-v] [-A|D LocalUserGroup] [-h]\n"
125 " -d enable debugging.\n"
126 " -v enable verbose NTLM packet debugging.\n"
127 " -A specify a Windows Local Group name allowed to authenticate\n"
128 " -D specify a Windows Local Group name not allowed to authenticate\n"
129 " -h this message\n\n",
130 my_program_name);
131}
132
133
134void
135process_options(int argc, char *argv[])
136{
137 int opt, had_error = 0;
138
139 opterr =0;
140 while (-1 != (opt = getopt(argc, argv, "hdvA:D:"))) {
141 switch (opt) {
142 case 'A':
143 safe_free(NTAllowedGroup);
144 NTAllowedGroup=xstrdup(optarg);
145 UseAllowedGroup = 1;
146 break;
147 case 'D':
148 safe_free(NTDisAllowedGroup);
149 NTDisAllowedGroup=xstrdup(optarg);
150 UseDisallowedGroup = 1;
151 break;
152 case 'd':
153 debug_enabled = 1;
154 break;
155 case 'v':
156 debug_enabled = 1;
157 NTLM_packet_debug_enabled = 1;
158 break;
159 case 'h':
160 usage();
161 exit(0);
162 case '?':
163 opt = optopt;
164 /* fall thru to default */
165 default:
166 fprintf(stderr, "unknown option: -%c. Exiting\n", opt);
167 usage();
168 had_error = 1;
169 }
170 }
171 if (had_error)
172 exit(1);
173}
174
175
176const char *
177obtain_challenge(ntlm_negotiate * nego, int nego_length)
178{
179 const char *ch = NULL;
180
181 debug("attempting SSPI challenge retrieval\n");
182 ch = SSP_MakeChallenge(nego, nego_length);
183 if (ch) {
184 debug("Got it\n");
185 return ch; /* All went OK, returning */
186 }
187 return NULL;
188}
189
190
191int
192manage_request()
193{
194 ntlmhdr *fast_header;
195 char buf[BUFFER_SIZE];
196 char helper_command[3];
197 char *c, *decoded, *cred;
198 int plen;
199 int oversized = 0;
200 char * ErrorMessage;
201
202try_again:
203 if (fgets(buf, BUFFER_SIZE, stdin) == NULL)
204 return 0;
205
206 c = memchr(buf, '\n', BUFFER_SIZE); /* safer against overrun than strchr */
207 if (c) {
208 if (oversized) {
209 helperfail("illegal request received");
210 fprintf(stderr, "Illegal request received: '%s'\n", buf);
211 return 1;
212 }
213 *c = '\0';
214 } else {
215 fprintf(stderr, "No newline in '%s'\n", buf);
216 oversized = 1;
217 goto try_again;
218 }
219 if ((strlen(buf) > 3) && NTLM_packet_debug_enabled) {
220 decoded = base64_decode(buf + 3);
221 strncpy(helper_command, buf, 2);
222 debug("Got '%s' from Squid with data:\n", helper_command);
223 hex_dump(decoded, ((strlen(buf) - 3) * 3) / 4);
224 } else
225 debug("Got '%s' from Squid\n", buf);
226 if (memcmp(buf, "YR", 2) == 0) { /* refresh-request */
227 /* figure out what we got */
228 if (strlen(buf) > 3)
229 decoded = base64_decode(buf + 3);
230 else
231 decoded = base64_decode(ntlm_make_negotiate());
232 /* Note: we don't need to manage memory at this point, since
233 * base64_decode returns a pointer to static storage.
234 */
235 if (!decoded) { /* decoding failure, return error */
236 SEND("NA Packet format error, couldn't base64-decode");
237 return 1;
238 }
239 /* fast-track-decode request type. */
240 fast_header = (struct _ntlmhdr *) decoded;
241
242 /* sanity-check: it IS a NTLMSSP packet, isn't it? */
243 if (memcmp(fast_header->signature, "NTLMSSP", 8) != 0) {
244 SEND("NA Broken authentication packet");
245 return 1;
246 }
247 switch (fast_header->type) {
248 case NTLM_NEGOTIATE:
249 /* Obtain challenge against SSPI */
250 if (strlen(buf) > 3)
251 plen = (strlen(buf) - 3) * 3 / 4; /* we only need it here. Optimization */
252 else
253 plen = NEGOTIATE_LENGTH;
254 if ((c = (char *) obtain_challenge((ntlm_negotiate *) decoded, plen)) != NULL )
255 {
256 if (NTLM_packet_debug_enabled) {
257 printf("TT %s\n",c);
258 decoded = base64_decode(c);
259 debug("sending 'TT' to squid with data:\n");
260 hex_dump(decoded, (strlen(c) * 3) / 4);
261 if (NTLM_LocalCall)
262 debug("NTLM Local Call detected\n");
263 } else {
264 SEND2("TT %s", c);
265 }
266 have_challenge = 1;
267 } else
268 helperfail("can't obtain challenge");
269 return 1;
270 /* notreached */
271 case NTLM_CHALLENGE:
272 SEND
273 ("NA Got a challenge. We refuse to have our authority disputed");
274 return 1;
275 /* notreached */
276 case NTLM_AUTHENTICATE:
277 SEND("NA Got authentication request instead of negotiate request");
278 return 1;
279 /* notreached */
280 default:
281 helperfail("unknown refresh-request packet type");
282 return 1;
283 }
284 return 1;
285 }
286 if (memcmp(buf, "KK ", 3) == 0) { /* authenticate-request */
287 if (!have_challenge) {
288 helperfail("invalid challenge");
289 return 1;
290 }
291 /* figure out what we got */
292 decoded = base64_decode(buf + 3);
293 /* Note: we don't need to manage memory at this point, since
294 * base64_decode returns a pointer to static storage.
295 */
296
297 if (!decoded) { /* decoding failure, return error */
298 SEND("NA Packet format error, couldn't base64-decode");
299 return 1;
300 }
301 /* fast-track-decode request type. */
302 fast_header = (struct _ntlmhdr *) decoded;
303
304 /* sanity-check: it IS a NTLMSSP packet, isn't it? */
305 if (memcmp(fast_header->signature, "NTLMSSP", 8) != 0) {
306 SEND("NA Broken authentication packet");
307 return 1;
308 }
309 switch (fast_header->type) {
310 case NTLM_NEGOTIATE:
311 SEND("NA Invalid negotiation request received");
312 return 1;
313 /* notreached */
314 case NTLM_CHALLENGE:
315 SEND
316 ("NA Got a challenge. We refuse to have our authority disputed");
317 return 1;
318 /* notreached */
319 case NTLM_AUTHENTICATE:
320 /* check against SSPI */
321 plen = (strlen(buf) - 3) * 3 / 4; /* we only need it here. Optimization */
322 cred = ntlm_check_auth((ntlm_authenticate *) decoded, plen);
323 have_challenge = 0;
324 if (cred == NULL) {
325#if FAIL_DEBUG
326 fail_debug_enabled =1;
327#endif
328 switch (ntlm_errno) {
329 case NTLM_BAD_NTGROUP:
330 SEND("NA Incorrect Group Membership");
331 return 1;
332 case NTLM_BAD_REQUEST:
333 SEND("NA Incorrect Request Format");
334 return 1;
335 case NTLM_SSPI_ERROR:
336 FormatMessage(
337 FORMAT_MESSAGE_ALLOCATE_BUFFER |
338 FORMAT_MESSAGE_FROM_SYSTEM |
339 FORMAT_MESSAGE_IGNORE_INSERTS,
340 NULL,
341 GetLastError(),
342 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
343 (LPTSTR) &ErrorMessage,
344 0,
345 NULL);
346 if (ErrorMessage[strlen(ErrorMessage) - 1] == '\n')
347 ErrorMessage[strlen(ErrorMessage) - 1] = '\0';
348 if (ErrorMessage[strlen(ErrorMessage) - 1] == '\r')
349 ErrorMessage[strlen(ErrorMessage) - 1] = '\0';
350 SEND2("NA %s", ErrorMessage);
351 LocalFree(ErrorMessage);
352 return 1;
353 default:
354 SEND("NA Unknown Error");
355 return 1;
356 }
357 }
358 lc(cred); /* let's lowercase them for our convenience */
359 SEND2("AF %s", cred);
360 return 1;
361 default:
362 helperfail("unknown authentication packet type");
363 return 1;
364 }
365 return 1;
366 } else { /* not an auth-request */
367 helperfail("illegal request received");
368 fprintf(stderr, "Illegal request received: '%s'\n", buf);
369 return 1;
370 }
371 helperfail("detected protocol error");
372 return 1;
373/********* END ********/
374}
375
376int
377main(int argc, char *argv[])
378{
379 my_program_name = argv[0];
380
381 process_options(argc, argv);
382
383 debug("%s build " __DATE__ ", " __TIME__ " starting up...\n", my_program_name);
384
385 if (LoadSecurityDll(SSP_NTLM, NTLM_PACKAGE_NAME) == NULL) {
386 fprintf(stderr, "FATAL, can't initialize SSPI, exiting.\n");
387 exit(1);
388 }
389 debug("SSPI initialized OK\n");
390
391 atexit(UnloadSecurityDll);
392
393 /* initialize FDescs */
394 setbuf(stdout, NULL);
395 setbuf(stderr, NULL);
396
397 while (manage_request()) {
398 /* everything is done within manage_request */
399 }
400 exit(0);
401}