]>
Commit | Line | Data |
---|---|---|
ca02e0ec | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
ca02e0ec AJ |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
eb3dea38 MM |
9 | /* |
10 | * ----------------------------------------------------------------------------- | |
11 | * | |
12 | * Author: Markus Moeller (markus_moeller at compuserve.com) | |
13 | * | |
14 | * Copyright (C) 2011 Markus Moeller. All rights reserved. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. | |
29 | * | |
30 | * ----------------------------------------------------------------------------- | |
31 | */ | |
eb3dea38 | 32 | |
f7f3304a | 33 | #include "squid.h" |
8bdd0cec | 34 | #include "base64.h" |
eb3dea38 | 35 | |
074d6a40 AJ |
36 | #include <cerrno> |
37 | #include <cstring> | |
38 | #include <cstdlib> | |
39 | #include <ctime> | |
eb3dea38 MM |
40 | #if HAVE_NETDB_H |
41 | #include <netdb.h> | |
42 | #endif | |
43 | #if HAVE_UNISTD_H | |
44 | #include <unistd.h> | |
45 | #endif | |
eb3dea38 MM |
46 | |
47 | #if !defined(HAVE_DECL_XMALLOC) || !HAVE_DECL_XMALLOC | |
48 | #define xmalloc malloc | |
49 | #endif | |
50 | #if !defined(HAVE_DECL_XSTRDUP) || !HAVE_DECL_XSTRDUP | |
51 | #define xstrdup strdup | |
52 | #endif | |
53 | #if !defined(HAVE_DECL_XFREE) || !HAVE_DECL_XFREE | |
54 | #define xfree free | |
55 | #endif | |
56 | ||
57 | #undef PROGRAM | |
58 | #define PROGRAM "negotiate_wrapper" | |
59 | #undef VERSION | |
60 | #define VERSION "1.0.1" | |
61 | ||
62 | #ifndef MAX_AUTHTOKEN_LEN | |
63 | #define MAX_AUTHTOKEN_LEN 65535 | |
64 | #endif | |
65 | ||
66 | static const unsigned char ntlmProtocol[] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0}; | |
67 | ||
68 | static const char * | |
69 | LogTime() | |
70 | { | |
eb3dea38 MM |
71 | struct timeval now; |
72 | static time_t last_t = 0; | |
73 | static char buf[128]; | |
74 | ||
75 | gettimeofday(&now, NULL); | |
76 | if (now.tv_sec != last_t) { | |
3a2e0253 AJ |
77 | time_t *tmp = (time_t *) & now.tv_sec; |
78 | struct tm *tm = localtime(tmp); | |
eb3dea38 MM |
79 | strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm); |
80 | last_t = now.tv_sec; | |
81 | } | |
82 | return buf; | |
83 | } | |
84 | ||
85 | void usage(void) | |
86 | { | |
87 | fprintf(stderr, "Usage: \n"); | |
88 | fprintf(stderr, "negotiate_wrapper [-h] [-d] --ntlm ntlm helper + arguments --kerberos kerberos helper + arguments\n"); | |
89 | fprintf(stderr, "-h help\n"); | |
90 | fprintf(stderr, "-d full debug\n"); | |
91 | fprintf(stderr, "--ntlm full ntlm helper path with arguments\n"); | |
92 | fprintf(stderr, "--kerberos full kerberos helper path with arguments\n"); | |
93 | } | |
94 | ||
af845cee AJ |
95 | static void |
96 | closeFds(FILE *a, FILE *b, FILE *c, FILE *d) | |
97 | { | |
e0702229 | 98 | if (a) |
af845cee | 99 | fclose(a); |
e0702229 | 100 | if (b) |
af845cee | 101 | fclose(b); |
e0702229 | 102 | if (c) |
af845cee | 103 | fclose(c); |
e0702229 | 104 | if (d) |
af845cee AJ |
105 | fclose(d); |
106 | } | |
107 | ||
108 | static int | |
109 | processingLoop(FILE *FDKIN, FILE *FDKOUT, FILE *FDNIN, FILE *FDNOUT) | |
eb3dea38 MM |
110 | { |
111 | char buf[MAX_AUTHTOKEN_LEN]; | |
112 | char tbuff[MAX_AUTHTOKEN_LEN]; | |
113 | char buff[MAX_AUTHTOKEN_LEN+2]; | |
a86a4d3c | 114 | char *c; |
eb3dea38 | 115 | int length; |
e3a5c24c | 116 | uint8_t *token = NULL; |
af845cee AJ |
117 | |
118 | while (1) { | |
119 | if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) { | |
e3a5c24c | 120 | xfree(token); |
af845cee AJ |
121 | if (ferror(stdin)) { |
122 | if (debug_enabled) | |
123 | fprintf(stderr, | |
124 | "%s| %s: fgets() failed! dying..... errno=%d (%s)\n", | |
125 | LogTime(), PROGRAM, ferror(stdin), | |
126 | strerror(ferror(stdin))); | |
127 | ||
128 | fprintf(stdout, "BH input error\n"); | |
129 | return 1; /* BIIG buffer */ | |
130 | } | |
131 | fprintf(stdout, "BH input error\n"); | |
132 | return 0; | |
133 | } | |
134 | c = static_cast<char*>(memchr(buf, '\n', sizeof(buf) - 1)); | |
135 | if (c) { | |
136 | *c = '\0'; | |
137 | length = c - buf; | |
138 | if (debug_enabled) | |
139 | fprintf(stderr, "%s| %s: Got '%s' from squid (length: %d).\n", | |
140 | LogTime(), PROGRAM, buf, length); | |
141 | } else { | |
142 | if (debug_enabled) | |
143 | fprintf(stderr, "%s| %s: Oversized message\n", LogTime(), | |
144 | PROGRAM); | |
145 | fprintf(stdout, "BH Oversized message\n"); | |
146 | continue; | |
147 | } | |
148 | ||
149 | if (buf[0] == '\0') { | |
150 | if (debug_enabled) | |
151 | fprintf(stderr, "%s| %s: Invalid request\n", LogTime(), | |
152 | PROGRAM); | |
153 | fprintf(stdout, "BH Invalid request\n"); | |
154 | continue; | |
155 | } | |
156 | if (strlen(buf) < 2) { | |
157 | if (debug_enabled) | |
158 | fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(), | |
159 | PROGRAM, buf); | |
160 | fprintf(stdout, "BH Invalid request\n"); | |
161 | continue; | |
162 | } | |
163 | if (!strncmp(buf, "QQ", 2)) { | |
164 | fprintf(stdout, "BH quit command\n"); | |
92c2f0bd | 165 | xfree(token); |
af845cee AJ |
166 | return 0; |
167 | } | |
168 | if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) { | |
169 | if (debug_enabled) | |
170 | fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(), | |
171 | PROGRAM, buf); | |
172 | fprintf(stdout, "BH Invalid request\n"); | |
173 | continue; | |
174 | } | |
175 | if (strlen(buf) <= 3) { | |
176 | if (debug_enabled) | |
177 | fprintf(stderr, "%s| %s: Invalid negotiate request [%s]\n", | |
178 | LogTime(), PROGRAM, buf); | |
179 | fprintf(stdout, "BH Invalid negotiate request\n"); | |
180 | continue; | |
181 | } | |
182 | length = BASE64_DECODE_LENGTH(strlen(buf+3)); | |
183 | if (debug_enabled) | |
184 | fprintf(stderr, "%s| %s: Decode '%s' (decoded length: %d).\n", | |
185 | LogTime(), PROGRAM, buf + 3, (int) length); | |
186 | ||
e3a5c24c AJ |
187 | safe_free(token); |
188 | if (!(token = static_cast<uint8_t *>(xmalloc(length)))) { | |
af845cee AJ |
189 | fprintf(stderr, "%s| %s: Error allocating memory for token\n", LogTime(), PROGRAM); |
190 | return 1; | |
191 | } | |
192 | ||
193 | struct base64_decode_ctx ctx; | |
194 | base64_decode_init(&ctx); | |
195 | size_t dstLen = 0; | |
196 | if (!base64_decode_update(&ctx, &dstLen, token, strlen(buf+3), reinterpret_cast<const uint8_t*>(buf+3)) || | |
197 | !base64_decode_final(&ctx)) { | |
198 | if (debug_enabled) | |
199 | fprintf(stderr, "%s| %s: Invalid base64 token [%s]\n", LogTime(), PROGRAM, buf+3); | |
200 | fprintf(stdout, "BH Invalid negotiate request token\n"); | |
201 | continue; | |
202 | } | |
203 | length = dstLen; | |
204 | token[dstLen] = '\0'; | |
205 | ||
206 | if ((static_cast<size_t>(length) >= sizeof(ntlmProtocol) + 1) && | |
207 | (!memcmp(token, ntlmProtocol, sizeof ntlmProtocol))) { | |
af845cee AJ |
208 | if (debug_enabled) |
209 | fprintf(stderr, "%s| %s: received type %d NTLM token\n", | |
210 | LogTime(), PROGRAM, (int) *((unsigned char *) token + | |
211 | sizeof ntlmProtocol)); | |
212 | fprintf(FDNIN, "%s\n",buf); | |
213 | if (fgets(tbuff, sizeof(tbuff) - 1, FDNOUT) == NULL) { | |
e3a5c24c | 214 | xfree(token); |
af845cee AJ |
215 | if (ferror(FDNOUT)) { |
216 | fprintf(stderr, | |
217 | "fgets() failed! dying..... errno=%d (%s)\n", | |
218 | ferror(FDNOUT), strerror(ferror(FDNOUT))); | |
219 | return 1; | |
220 | } | |
221 | fprintf(stderr, "%s| %s: Error reading NTLM helper response\n", | |
222 | LogTime(), PROGRAM); | |
223 | return 0; | |
224 | } | |
225 | /* | |
e3a5c24c AJ |
226 | * Need to translate NTLM reply to Negotiate reply: |
227 | * AF user => AF blob user | |
228 | * NA reason => NA blob reason | |
229 | * Set blob to '=' | |
230 | */ | |
af845cee AJ |
231 | if (strlen(tbuff) >= 3 && (!strncmp(tbuff,"AF ",3) || !strncmp(tbuff,"NA ",3))) { |
232 | strncpy(buff,tbuff,3); | |
233 | buff[3]='='; | |
234 | for (unsigned int i=2; i<=strlen(tbuff); ++i) | |
235 | buff[i+2] = tbuff[i]; | |
236 | } else { | |
237 | strcpy(buff,tbuff); | |
238 | } | |
239 | } else { | |
af845cee AJ |
240 | if (debug_enabled) |
241 | fprintf(stderr, "%s| %s: received Kerberos token\n", | |
242 | LogTime(), PROGRAM); | |
243 | ||
244 | fprintf(FDKIN, "%s\n",buf); | |
245 | if (fgets(buff, sizeof(buff) - 1, FDKOUT) == NULL) { | |
e3a5c24c | 246 | xfree(token); |
af845cee AJ |
247 | if (ferror(FDKOUT)) { |
248 | fprintf(stderr, | |
249 | "fgets() failed! dying..... errno=%d (%s)\n", | |
250 | ferror(FDKOUT), strerror(ferror(FDKOUT))); | |
251 | return 1; | |
252 | } | |
253 | fprintf(stderr, "%s| %s: Error reading Kerberos helper response\n", | |
254 | LogTime(), PROGRAM); | |
255 | return 0; | |
256 | } | |
257 | } | |
258 | fprintf(stdout,"%s",buff); | |
259 | if (debug_enabled) | |
260 | fprintf(stderr, "%s| %s: Return '%s'\n", | |
261 | LogTime(), PROGRAM, buff); | |
262 | } | |
263 | ||
e3a5c24c | 264 | xfree(token); |
af845cee AJ |
265 | return 1; |
266 | } | |
267 | ||
268 | int | |
269 | main(int argc, char *const argv[]) | |
270 | { | |
eb3dea38 MM |
271 | int nstart = 0, kstart = 0; |
272 | int nend = 0, kend = 0; | |
eb3dea38 | 273 | char **nargs, **kargs; |
eb3dea38 | 274 | int fpid; |
eb3dea38 MM |
275 | int pkin[2]; |
276 | int pkout[2]; | |
277 | int pnin[2]; | |
278 | int pnout[2]; | |
279 | ||
280 | setbuf(stdout, NULL); | |
281 | setbuf(stdin, NULL); | |
282 | ||
283 | if (argc ==1 || !strncasecmp(argv[1],"-h",2)) { | |
284 | usage(); | |
285 | return 0; | |
286 | } | |
287 | ||
72841dbb | 288 | int j = 1; |
eb3dea38 | 289 | if (!strncasecmp(argv[1],"-d",2)) { |
af845cee | 290 | debug_enabled = 1; |
eb3dea38 MM |
291 | j = 2; |
292 | } | |
293 | ||
72841dbb | 294 | for (int i=j; i<argc; ++i) { |
eb3dea38 MM |
295 | if (!strncasecmp(argv[i],"--ntlm",6)) |
296 | nstart = i; | |
297 | if (!strncasecmp(argv[i],"--kerberos",10)) | |
298 | kstart = i; | |
299 | } | |
300 | if (nstart > kstart) { | |
301 | kend = nstart-1; | |
302 | nend = argc-1; | |
303 | } else { | |
304 | kend = argc-1; | |
305 | nend = kstart-1; | |
306 | } | |
307 | if (nstart == 0 || kstart == 0 || kend-kstart <= 0 || nend-nstart <= 0 ) { | |
308 | usage(); | |
309 | return 0; | |
310 | } | |
311 | ||
af845cee | 312 | if (debug_enabled) |
eb3dea38 MM |
313 | fprintf(stderr, "%s| %s: Starting version %s\n", LogTime(), PROGRAM, |
314 | VERSION); | |
315 | ||
316 | if ((nargs = (char **)xmalloc((nend-nstart+1)*sizeof(char *))) == NULL) { | |
317 | fprintf(stderr, "%s| %s: Error allocating memory for ntlm helper\n", LogTime(), PROGRAM); | |
318 | return 1; | |
319 | } | |
320 | memcpy(nargs,argv+nstart+1,(nend-nstart)*sizeof(char *)); | |
321 | nargs[nend-nstart]=NULL; | |
af845cee | 322 | if (debug_enabled) { |
eb3dea38 | 323 | fprintf(stderr, "%s| %s: NTLM command: ", LogTime(), PROGRAM); |
72841dbb | 324 | for (int i=0; i<nend-nstart; ++i) |
eb3dea38 MM |
325 | fprintf(stderr, "%s ", nargs[i]); |
326 | fprintf(stderr, "\n"); | |
327 | } | |
328 | if ((kargs = (char **)xmalloc((kend-kstart+1)*sizeof(char *))) == NULL) { | |
329 | fprintf(stderr, "%s| %s: Error allocating memory for kerberos helper\n", LogTime(), PROGRAM); | |
330 | return 1; | |
331 | } | |
332 | memcpy(kargs,argv+kstart+1,(kend-kstart)*sizeof(char *)); | |
333 | kargs[kend-kstart]=NULL; | |
af845cee | 334 | if (debug_enabled) { |
eb3dea38 | 335 | fprintf(stderr, "%s| %s: Kerberos command: ", LogTime(), PROGRAM); |
72841dbb | 336 | for (int i=0; i<kend-kstart; ++i) |
eb3dea38 MM |
337 | fprintf(stderr, "%s ", kargs[i]); |
338 | fprintf(stderr, "\n"); | |
339 | } | |
340 | /* | |
341 | Fork Kerberos helper and NTLM helper and manage IO to send NTLM requests | |
342 | to the right helper. squid must keep session state | |
343 | */ | |
344 | ||
3a2e0253 AJ |
345 | if (pipe(pkin) < 0) { |
346 | fprintf(stderr, "%s| %s: Could not assign streams for pkin\n", LogTime(), PROGRAM); | |
347 | return 1; | |
348 | } | |
349 | if (pipe(pkout) < 0) { | |
350 | fprintf(stderr, "%s| %s: Could not assign streams for pkout\n", LogTime(), PROGRAM); | |
351 | return 1; | |
352 | } | |
353 | ||
eb3dea38 MM |
354 | if (( fpid = vfork()) < 0 ) { |
355 | fprintf(stderr, "%s| %s: Failed first fork\n", LogTime(), PROGRAM); | |
356 | return 1; | |
357 | } | |
358 | ||
359 | if ( fpid == 0 ) { | |
360 | /* First Child for Kerberos helper */ | |
361 | ||
362 | close(pkin[1]); | |
363 | dup2(pkin[0],STDIN_FILENO); | |
364 | close(pkin[0]); | |
365 | ||
366 | close(pkout[0]); | |
367 | dup2(pkout[1],STDOUT_FILENO); | |
368 | close(pkout[1]); | |
369 | ||
370 | setbuf(stdin, NULL); | |
371 | setbuf(stdout, NULL); | |
372 | ||
373 | execv(kargs[0], kargs); | |
374 | fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, kargs[0], strerror(errno)); | |
375 | return 1; | |
eb3dea38 MM |
376 | } |
377 | ||
378 | close(pkin[0]); | |
379 | close(pkout[1]); | |
380 | ||
3a2e0253 AJ |
381 | if (pipe(pnin) < 0) { |
382 | fprintf(stderr, "%s| %s: Could not assign streams for pnin\n", LogTime(), PROGRAM); | |
383 | return 1; | |
384 | } | |
385 | if (pipe(pnout) < 0) { | |
386 | fprintf(stderr, "%s| %s: Could not assign streams for pnout\n", LogTime(), PROGRAM); | |
387 | return 1; | |
388 | } | |
eb3dea38 MM |
389 | |
390 | if (( fpid = vfork()) < 0 ) { | |
391 | fprintf(stderr, "%s| %s: Failed second fork\n", LogTime(), PROGRAM); | |
392 | return 1; | |
393 | } | |
394 | ||
395 | if ( fpid == 0 ) { | |
396 | /* Second Child for NTLM helper */ | |
397 | ||
398 | close(pnin[1]); | |
399 | dup2(pnin[0],STDIN_FILENO); | |
400 | close(pnin[0]); | |
401 | ||
402 | close(pnout[0]); | |
403 | dup2(pnout[1],STDOUT_FILENO); | |
404 | close(pnout[1]); | |
405 | ||
406 | setbuf(stdin, NULL); | |
407 | setbuf(stdout, NULL); | |
408 | ||
409 | execv(nargs[0], nargs); | |
410 | fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, nargs[0], strerror(errno)); | |
411 | return 1; | |
412 | } | |
413 | ||
414 | close(pnin[0]); | |
415 | close(pnout[1]); | |
416 | ||
af845cee AJ |
417 | FILE *FDKIN=fdopen(pkin[1],"w"); |
418 | FILE *FDKOUT=fdopen(pkout[0],"r"); | |
eb3dea38 | 419 | |
af845cee AJ |
420 | FILE *FDNIN=fdopen(pnin[1],"w"); |
421 | FILE *FDNOUT=fdopen(pnout[0],"r"); | |
eb3dea38 MM |
422 | |
423 | if (!FDKIN || !FDKOUT || !FDNIN || !FDNOUT) { | |
a86a4d3c | 424 | fprintf(stderr, "%s| %s: Could not assign streams for FDKIN/FDKOUT/FDNIN/FDNOUT\n", LogTime(), PROGRAM); |
af845cee | 425 | closeFds(FDKIN, FDKOUT, FDNIN, FDNOUT); |
eb3dea38 MM |
426 | return 1; |
427 | } | |
428 | ||
429 | setbuf(FDKIN, NULL); | |
430 | setbuf(FDKOUT, NULL); | |
431 | setbuf(FDNIN, NULL); | |
432 | setbuf(FDNOUT, NULL); | |
433 | ||
af845cee AJ |
434 | int result = processingLoop(FDKIN, FDKOUT, FDNIN, FDNOUT); |
435 | closeFds(FDKIN, FDKOUT, FDNIN, FDNOUT); | |
436 | return result; | |
eb3dea38 | 437 | } |
f53969cc | 438 |