4 * AUTHOR: Andrew Doran <ad@interlude.eu.org>
5 * AUTHOR: Robert Collins <rbtcollins@hotmail.com>
6 * AUTHOR: Guido Serassio: <guido.serassio@acmeconsulting.it>
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
36 * Example ntlm authentication program for Squid, based on the
37 * original proxy_auth code from client_side.c, written by
38 * Jon Thackray <jrmt@uk.gdscorp.com>. Initial ntlm code by
39 * Andrew Doran <ad@interlude.eu.org>.
41 * This code gets the username and returns it. No validation is done.
42 * and by the way: it is a complete patch-up. Use the "real thing" NTLMSSP
45 * Revised by Guido Serassio: <guido.serassio@acmeconsulting.it>
47 * - Added negotiation of UNICODE char support
48 * - More detailed debugging info
52 /* undefine this to have strict protocol adherence. You don't really need
54 #define IGNORANCE_IS_BLISS
77 #define safe_free(x) if (x) { free(x); x = NULL; }
79 /* A couple of harmless helper macros */
80 #define SEND(X) debug("sending '%s' to squid\n",X); printf(X "\n");
82 #define SEND2(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
83 #define SEND3(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
85 /* no gcc, no debugging. varargs macros are a gcc extension */
86 #define SEND2(X,Y) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
87 #define SEND3(X,Y,Z) debug("sending '" X "' to squid\n",Y,Z); printf(X "\n",Y,Z);
93 #define BUFFER_SIZE 10240
95 const char *authenticate_ntlm_domain
= "WORKGROUP";
96 int strip_domain_enabled
= 0;
97 int NTLM_packet_debug_enabled
= 0;
100 hex_dump(unsigned char *data
, int size
)
102 /* dumps size bytes of *data to stdout. Looks like:
103 * [0000] 75 6E 6B 6E 6F 77 6E 20
104 * 30 FF 00 00 00 00 39 00 unknown 0.....9.
105 * (in a single line of course)
112 unsigned char *p
= data
;
115 char bytestr
[4] = {0};
116 char addrstr
[10] = {0};
117 char hexstr
[16 * 3 + 5] = {0};
118 char charstr
[16 * 1 + 5] = {0};
119 for (n
= 1; n
<= size
; n
++) {
121 /* store address for this line */
122 snprintf(addrstr
, sizeof(addrstr
), "%.4x",
126 if (xisalnum(c
) == 0) {
129 /* store hex str (for left side) */
130 snprintf(bytestr
, sizeof(bytestr
), "%02X ", *p
);
131 strncat(hexstr
, bytestr
, sizeof(hexstr
) - strlen(hexstr
) - 1);
133 /* store char str (for right side) */
134 snprintf(bytestr
, sizeof(bytestr
), "%c", c
);
135 strncat(charstr
, bytestr
, sizeof(charstr
) - strlen(charstr
) - 1);
139 fprintf(stderr
, "[%4.4s] %-50.50s %s\n", addrstr
, hexstr
, charstr
);
142 } else if (n
% 8 == 0) {
143 /* half line: add whitespaces */
144 strncat(hexstr
, " ", sizeof(hexstr
) - strlen(hexstr
) - 1);
145 strncat(charstr
, " ", sizeof(charstr
) - strlen(charstr
) - 1);
150 if (strlen(hexstr
) > 0) {
151 /* print rest of buffer if not empty */
152 fprintf(stderr
, "[%4.4s] %-50.50s %s\n", addrstr
, hexstr
, charstr
);
158 /* makes a null-terminated string lower-case. Changes CONTENTS! */
171 * -d enable debugging.
172 * -v enable verbose NTLM packet debugging.
173 * -l if specified, changes behavior on failures to last-ditch.
175 char *my_program_name
= NULL
;
181 "Usage: %s [-d] [-v] [-h]\n"
182 " -d enable debugging.\n"
183 " -S strip domain from username.\n"
184 " -v enable verbose NTLM packet debugging.\n"
185 " -h this message\n\n",
190 process_options(int argc
, char *argv
[])
192 int opt
, had_error
= 0;
195 while (-1 != (opt
= getopt(argc
, argv
, "hdvS"))) {
202 NTLM_packet_debug_enabled
= 1;
205 strip_domain_enabled
= 1;
212 /* fall thru to default */
214 fprintf(stderr
, "unknown option: -%c. Exiting\n", opt
);
224 main(int argc
, char *argv
[])
226 char buf
[BUFFER_SIZE
];
228 char user
[NTLM_MAX_FIELD_LENGTH
], domain
[NTLM_MAX_FIELD_LENGTH
];
229 char *p
, *decoded
= NULL
;
230 ntlmhdr
*packet
= NULL
;
231 char helper_command
[3];
235 setbuf(stdout
, NULL
);
236 setbuf(stderr
, NULL
);
238 my_program_name
= argv
[0];
240 process_options(argc
, argv
);
242 debug("%s build " __DATE__
", " __TIME__
" starting up...\n", my_program_name
);
244 while (fgets(buf
, BUFFER_SIZE
, stdin
) != NULL
) {
245 user
[0] = '\0'; /*no user code */
246 domain
[0] = '\0'; /*no domain code */
248 if ((p
= strchr(buf
, '\n')) != NULL
)
249 *p
= '\0'; /* strip \n */
250 buflen
= strlen(buf
); /* keep this so we only scan the buffer for \0 once per loop */
252 packet
= (ntlmhdr
*)base64_decode(buf
+ 3);
253 if (buflen
> 3 && NTLM_packet_debug_enabled
) {
254 strncpy(helper_command
, buf
, 2);
255 helper_command
[2] = '\0';
256 debug("Got '%s' from Squid with data:\n", helper_command
);
257 hex_dump((unsigned char*)packet
, ((buflen
- 3) * 3) / 4);
259 debug("Got '%s' from Squid\n", buf
);
261 if (strncasecmp(buf
, "YR", 2) == 0) {
262 char nonce
[NTLM_NONCE_LEN
];
264 ntlm_make_nonce(nonce
);
266 ntlm_negotiate
*nego
= (ntlm_negotiate
*)packet
;
267 ntlm_make_challenge(&chal
, authenticate_ntlm_domain
, NULL
, nonce
, NTLM_NONCE_LEN
, nego
->flags
);
269 ntlm_make_challenge(&chal
, authenticate_ntlm_domain
, NULL
, nonce
, NTLM_NONCE_LEN
, NEGOTIATE_ASCII
);
271 // TODO: find out what this context means, and why only the fake auth helper contains it.
272 chal
.context_high
= htole32(0x003a<<16);
274 len
= sizeof(chal
) - sizeof(chal
.payload
) + le16toh(chal
.target
.maxlen
);
275 data
= (char *) base64_encode_bin((char *) &chal
, len
);
276 if (NTLM_packet_debug_enabled
) {
277 printf("TT %s\n", data
);
278 debug("sending 'TT' to squid with data:\n");
279 hex_dump((unsigned char *)&chal
, len
);
281 SEND2("TT %s", data
);
282 } else if (strncasecmp(buf
, "KK ", 3) == 0) {
284 SEND("BH received KK with no data! user=");
285 } else if (!ntlm_validate_packet(packet
, NTLM_AUTHENTICATE
)) {
286 if (ntlm_unpack_auth((ntlm_authenticate
*)packet
, user
, domain
, (buflen
-3)) == 0) {
289 if (strip_domain_enabled
) {
290 SEND2("AF %s", user
);
292 SEND3("AF %s%s%s", domain
, (*domain
?"\\":""), user
);
297 SEND3("NA invalid credentials, user=%s%s%s", domain
, (*domain
?"\\":""), user
);
300 SEND("BH wrong packet type! user=");