]>
Commit | Line | Data |
---|---|---|
75aa769b AJ |
1 | /* |
2 | * $Id$ | |
3 | * | |
4 | * AUTHOR: Andrew Doran <ad@interlude.eu.org> | |
5 | * AUTHOR: Robert Collins <rbtcollins@hotmail.com> | |
6 | * AUTHOR: Guido Serassio: <guido.serassio@acmeconsulting.it> | |
7 | * | |
8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
9 | * ---------------------------------------------------------- | |
10 | * | |
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. | |
19 | * | |
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. | |
24 | * | |
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. | |
29 | * | |
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. | |
33 | * | |
34 | */ | |
35 | /* | |
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>. | |
40 | * | |
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 | |
43 | * if you can. | |
44 | * | |
45 | * Revised by Guido Serassio: <guido.serassio@acmeconsulting.it> | |
46 | * | |
47 | * - Added negotiation of UNICODE char support | |
48 | * - More detailed debugging info | |
49 | * | |
50 | */ | |
51 | ||
52 | /* undefine this to have strict protocol adherence. You don't really need | |
53 | * that though */ | |
54 | #define IGNORANCE_IS_BLISS | |
55 | ||
56 | #include "config.h" | |
57 | #include "ntlmauth.h" | |
58 | #include "util.h" | |
59 | ||
60 | #if HAVE_CTYPE_H | |
61 | #include <ctype.h> | |
62 | #endif | |
63 | #if HAVE_STRING_H | |
64 | #include <string.h> | |
65 | #endif | |
66 | #if HAVE_CRYPT_H | |
67 | #include <crypt.h> | |
68 | #endif | |
69 | #if HAVE_PWD_H | |
70 | #include <pwd.h> | |
71 | #endif | |
72 | #if HAVE_GETOPT_H | |
73 | #include <getopt.h> | |
74 | #endif | |
75 | ||
76 | ||
77 | #define safe_free(x) if (x) { free(x); x = NULL; } | |
78 | ||
79 | /* A couple of harmless helper macros */ | |
80 | #define SEND(X) debug("sending '%s' to squid\n",X); printf(X "\n"); | |
81 | #ifdef __GNUC__ | |
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); | |
84 | #else | |
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); | |
88 | #endif | |
89 | ||
90 | #define ERR "ERR\n" | |
91 | #define OK "OK\n" | |
92 | ||
93 | #define BUFFER_SIZE 10240 | |
94 | ||
95 | const char *authenticate_ntlm_domain = "WORKGROUP"; | |
96 | int strip_domain_enabled = 0; | |
97 | int NTLM_packet_debug_enabled = 0; | |
98 | ||
99 | static void | |
100 | hex_dump(unsigned char *data, int size) | |
101 | { | |
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) | |
106 | */ | |
107 | ||
108 | if (!data) | |
109 | return; | |
110 | ||
111 | if (debug_enabled) { | |
112 | unsigned char *p = data; | |
113 | unsigned char c; | |
114 | int n; | |
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++) { | |
120 | if (n % 16 == 1) { | |
121 | /* store address for this line */ | |
122 | snprintf(addrstr, sizeof(addrstr), "%.4x", | |
123 | (int) (p - data)); | |
124 | } | |
125 | c = *p; | |
126 | if (xisalnum(c) == 0) { | |
127 | c = '.'; | |
128 | } | |
129 | /* store hex str (for left side) */ | |
130 | snprintf(bytestr, sizeof(bytestr), "%02X ", *p); | |
131 | strncat(hexstr, bytestr, sizeof(hexstr) - strlen(hexstr) - 1); | |
132 | ||
133 | /* store char str (for right side) */ | |
134 | snprintf(bytestr, sizeof(bytestr), "%c", c); | |
135 | strncat(charstr, bytestr, sizeof(charstr) - strlen(charstr) - 1); | |
136 | ||
137 | if (n % 16 == 0) { | |
138 | /* line completed */ | |
139 | fprintf(stderr, "[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr); | |
140 | hexstr[0] = 0; | |
141 | charstr[0] = 0; | |
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); | |
146 | } | |
147 | p++; /* next byte */ | |
148 | } | |
149 | ||
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); | |
153 | } | |
154 | } | |
155 | } | |
156 | ||
157 | ||
158 | /* makes a null-terminated string lower-case. Changes CONTENTS! */ | |
159 | static void | |
160 | lc(char *string) | |
161 | { | |
162 | char *p = string, c; | |
163 | while ((c = *p)) { | |
164 | *p = xtolower(c); | |
165 | p++; | |
166 | } | |
167 | } | |
168 | ||
169 | /* | |
170 | * options: | |
171 | * -d enable debugging. | |
172 | * -v enable verbose NTLM packet debugging. | |
173 | * -l if specified, changes behavior on failures to last-ditch. | |
174 | */ | |
175 | char *my_program_name = NULL; | |
176 | ||
177 | static void | |
178 | usage(void) | |
179 | { | |
180 | fprintf(stderr, | |
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", | |
186 | my_program_name); | |
187 | } | |
188 | ||
189 | static void | |
190 | process_options(int argc, char *argv[]) | |
191 | { | |
192 | int opt, had_error = 0; | |
193 | ||
194 | opterr = 0; | |
195 | while (-1 != (opt = getopt(argc, argv, "hdvS"))) { | |
196 | switch (opt) { | |
197 | case 'd': | |
198 | debug_enabled = 1; | |
199 | break; | |
200 | case 'v': | |
201 | debug_enabled = 1; | |
202 | NTLM_packet_debug_enabled = 1; | |
203 | break; | |
204 | case 'S': | |
205 | strip_domain_enabled = 1; | |
206 | break; | |
207 | case 'h': | |
208 | usage(); | |
209 | exit(0); | |
210 | case '?': | |
211 | opt = optopt; | |
212 | /* fall thru to default */ | |
213 | default: | |
214 | fprintf(stderr, "unknown option: -%c. Exiting\n", opt); | |
215 | usage(); | |
216 | had_error = 1; | |
217 | } | |
218 | } | |
219 | if (had_error) | |
220 | exit(1); | |
221 | } | |
222 | ||
223 | int | |
224 | main(int argc, char *argv[]) | |
225 | { | |
226 | char buf[BUFFER_SIZE]; | |
227 | int buflen = 0; | |
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]; | |
232 | int len; | |
233 | char *data = NULL; | |
234 | ||
235 | setbuf(stdout, NULL); | |
236 | setbuf(stderr, NULL); | |
237 | ||
238 | my_program_name = argv[0]; | |
239 | ||
240 | process_options(argc, argv); | |
241 | ||
242 | debug("%s build " __DATE__ ", " __TIME__ " starting up...\n", my_program_name); | |
243 | ||
244 | while (fgets(buf, BUFFER_SIZE, stdin) != NULL) { | |
245 | user[0] = '\0'; /*no user code */ | |
246 | domain[0] = '\0'; /*no domain code */ | |
247 | ||
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 */ | |
251 | if (buflen > 3) | |
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); | |
258 | } else | |
259 | debug("Got '%s' from Squid\n", buf); | |
260 | ||
261 | if (strncasecmp(buf, "YR", 2) == 0) { | |
262 | char nonce[NTLM_NONCE_LEN]; | |
263 | ntlm_challenge chal; | |
264 | ntlm_make_nonce(nonce); | |
265 | if (buflen > 3) { | |
266 | ntlm_negotiate *nego = (ntlm_negotiate *)packet; | |
267 | ntlm_make_challenge(&chal, authenticate_ntlm_domain, NULL, nonce, NTLM_NONCE_LEN, nego->flags); | |
268 | } else { | |
269 | ntlm_make_challenge(&chal, authenticate_ntlm_domain, NULL, nonce, NTLM_NONCE_LEN, NEGOTIATE_ASCII); | |
270 | } | |
271 | // TODO: find out what this context means, and why only the fake auth helper contains it. | |
272 | chal.context_high = htole32(0x003a<<16); | |
273 | ||
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); | |
280 | } else | |
281 | SEND2("TT %s", data); | |
282 | } else if (strncasecmp(buf, "KK ", 3) == 0) { | |
283 | if (!packet) { | |
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) { | |
287 | lc(user); | |
288 | lc(domain); | |
289 | if (strip_domain_enabled) { | |
290 | SEND2("AF %s", user); | |
291 | } else { | |
292 | SEND3("AF %s%s%s", domain, (*domain?"\\":""), user); | |
293 | } | |
294 | } else { | |
295 | lc(user); | |
296 | lc(domain); | |
297 | SEND3("NA invalid credentials, user=%s%s%s", domain, (*domain?"\\":""), user); | |
298 | } | |
299 | } else { | |
300 | SEND("BH wrong packet type! user="); | |
301 | } | |
302 | } | |
303 | } | |
304 | exit(0); | |
305 | } |