]>
Commit | Line | Data |
---|---|---|
ca02e0ec | 1 | /* |
bde978a6 | 2 | * Copyright (C) 1996-2015 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"); | |
165 | return 0; | |
166 | } | |
167 | if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) { | |
168 | if (debug_enabled) | |
169 | fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(), | |
170 | PROGRAM, buf); | |
171 | fprintf(stdout, "BH Invalid request\n"); | |
172 | continue; | |
173 | } | |
174 | if (strlen(buf) <= 3) { | |
175 | if (debug_enabled) | |
176 | fprintf(stderr, "%s| %s: Invalid negotiate request [%s]\n", | |
177 | LogTime(), PROGRAM, buf); | |
178 | fprintf(stdout, "BH Invalid negotiate request\n"); | |
179 | continue; | |
180 | } | |
181 | length = BASE64_DECODE_LENGTH(strlen(buf+3)); | |
182 | if (debug_enabled) | |
183 | fprintf(stderr, "%s| %s: Decode '%s' (decoded length: %d).\n", | |
184 | LogTime(), PROGRAM, buf + 3, (int) length); | |
185 | ||
e3a5c24c AJ |
186 | safe_free(token); |
187 | if (!(token = static_cast<uint8_t *>(xmalloc(length)))) { | |
af845cee AJ |
188 | fprintf(stderr, "%s| %s: Error allocating memory for token\n", LogTime(), PROGRAM); |
189 | return 1; | |
190 | } | |
191 | ||
192 | struct base64_decode_ctx ctx; | |
193 | base64_decode_init(&ctx); | |
194 | size_t dstLen = 0; | |
195 | if (!base64_decode_update(&ctx, &dstLen, token, strlen(buf+3), reinterpret_cast<const uint8_t*>(buf+3)) || | |
196 | !base64_decode_final(&ctx)) { | |
197 | if (debug_enabled) | |
198 | fprintf(stderr, "%s| %s: Invalid base64 token [%s]\n", LogTime(), PROGRAM, buf+3); | |
199 | fprintf(stdout, "BH Invalid negotiate request token\n"); | |
200 | continue; | |
201 | } | |
202 | length = dstLen; | |
203 | token[dstLen] = '\0'; | |
204 | ||
205 | if ((static_cast<size_t>(length) >= sizeof(ntlmProtocol) + 1) && | |
206 | (!memcmp(token, ntlmProtocol, sizeof ntlmProtocol))) { | |
af845cee AJ |
207 | if (debug_enabled) |
208 | fprintf(stderr, "%s| %s: received type %d NTLM token\n", | |
209 | LogTime(), PROGRAM, (int) *((unsigned char *) token + | |
210 | sizeof ntlmProtocol)); | |
211 | fprintf(FDNIN, "%s\n",buf); | |
212 | if (fgets(tbuff, sizeof(tbuff) - 1, FDNOUT) == NULL) { | |
e3a5c24c | 213 | xfree(token); |
af845cee AJ |
214 | if (ferror(FDNOUT)) { |
215 | fprintf(stderr, | |
216 | "fgets() failed! dying..... errno=%d (%s)\n", | |
217 | ferror(FDNOUT), strerror(ferror(FDNOUT))); | |
218 | return 1; | |
219 | } | |
220 | fprintf(stderr, "%s| %s: Error reading NTLM helper response\n", | |
221 | LogTime(), PROGRAM); | |
222 | return 0; | |
223 | } | |
224 | /* | |
e3a5c24c AJ |
225 | * Need to translate NTLM reply to Negotiate reply: |
226 | * AF user => AF blob user | |
227 | * NA reason => NA blob reason | |
228 | * Set blob to '=' | |
229 | */ | |
af845cee AJ |
230 | if (strlen(tbuff) >= 3 && (!strncmp(tbuff,"AF ",3) || !strncmp(tbuff,"NA ",3))) { |
231 | strncpy(buff,tbuff,3); | |
232 | buff[3]='='; | |
233 | for (unsigned int i=2; i<=strlen(tbuff); ++i) | |
234 | buff[i+2] = tbuff[i]; | |
235 | } else { | |
236 | strcpy(buff,tbuff); | |
237 | } | |
238 | } else { | |
af845cee AJ |
239 | if (debug_enabled) |
240 | fprintf(stderr, "%s| %s: received Kerberos token\n", | |
241 | LogTime(), PROGRAM); | |
242 | ||
243 | fprintf(FDKIN, "%s\n",buf); | |
244 | if (fgets(buff, sizeof(buff) - 1, FDKOUT) == NULL) { | |
e3a5c24c | 245 | xfree(token); |
af845cee AJ |
246 | if (ferror(FDKOUT)) { |
247 | fprintf(stderr, | |
248 | "fgets() failed! dying..... errno=%d (%s)\n", | |
249 | ferror(FDKOUT), strerror(ferror(FDKOUT))); | |
250 | return 1; | |
251 | } | |
252 | fprintf(stderr, "%s| %s: Error reading Kerberos helper response\n", | |
253 | LogTime(), PROGRAM); | |
254 | return 0; | |
255 | } | |
256 | } | |
257 | fprintf(stdout,"%s",buff); | |
258 | if (debug_enabled) | |
259 | fprintf(stderr, "%s| %s: Return '%s'\n", | |
260 | LogTime(), PROGRAM, buff); | |
261 | } | |
262 | ||
e3a5c24c | 263 | xfree(token); |
af845cee AJ |
264 | return 1; |
265 | } | |
266 | ||
267 | int | |
268 | main(int argc, char *const argv[]) | |
269 | { | |
eb3dea38 MM |
270 | int nstart = 0, kstart = 0; |
271 | int nend = 0, kend = 0; | |
eb3dea38 | 272 | char **nargs, **kargs; |
eb3dea38 | 273 | int fpid; |
eb3dea38 MM |
274 | int pkin[2]; |
275 | int pkout[2]; | |
276 | int pnin[2]; | |
277 | int pnout[2]; | |
278 | ||
279 | setbuf(stdout, NULL); | |
280 | setbuf(stdin, NULL); | |
281 | ||
282 | if (argc ==1 || !strncasecmp(argv[1],"-h",2)) { | |
283 | usage(); | |
284 | return 0; | |
285 | } | |
286 | ||
72841dbb | 287 | int j = 1; |
eb3dea38 | 288 | if (!strncasecmp(argv[1],"-d",2)) { |
af845cee | 289 | debug_enabled = 1; |
eb3dea38 MM |
290 | j = 2; |
291 | } | |
292 | ||
72841dbb | 293 | for (int i=j; i<argc; ++i) { |
eb3dea38 MM |
294 | if (!strncasecmp(argv[i],"--ntlm",6)) |
295 | nstart = i; | |
296 | if (!strncasecmp(argv[i],"--kerberos",10)) | |
297 | kstart = i; | |
298 | } | |
299 | if (nstart > kstart) { | |
300 | kend = nstart-1; | |
301 | nend = argc-1; | |
302 | } else { | |
303 | kend = argc-1; | |
304 | nend = kstart-1; | |
305 | } | |
306 | if (nstart == 0 || kstart == 0 || kend-kstart <= 0 || nend-nstart <= 0 ) { | |
307 | usage(); | |
308 | return 0; | |
309 | } | |
310 | ||
af845cee | 311 | if (debug_enabled) |
eb3dea38 MM |
312 | fprintf(stderr, "%s| %s: Starting version %s\n", LogTime(), PROGRAM, |
313 | VERSION); | |
314 | ||
315 | if ((nargs = (char **)xmalloc((nend-nstart+1)*sizeof(char *))) == NULL) { | |
316 | fprintf(stderr, "%s| %s: Error allocating memory for ntlm helper\n", LogTime(), PROGRAM); | |
317 | return 1; | |
318 | } | |
319 | memcpy(nargs,argv+nstart+1,(nend-nstart)*sizeof(char *)); | |
320 | nargs[nend-nstart]=NULL; | |
af845cee | 321 | if (debug_enabled) { |
eb3dea38 | 322 | fprintf(stderr, "%s| %s: NTLM command: ", LogTime(), PROGRAM); |
72841dbb | 323 | for (int i=0; i<nend-nstart; ++i) |
eb3dea38 MM |
324 | fprintf(stderr, "%s ", nargs[i]); |
325 | fprintf(stderr, "\n"); | |
326 | } | |
327 | if ((kargs = (char **)xmalloc((kend-kstart+1)*sizeof(char *))) == NULL) { | |
328 | fprintf(stderr, "%s| %s: Error allocating memory for kerberos helper\n", LogTime(), PROGRAM); | |
329 | return 1; | |
330 | } | |
331 | memcpy(kargs,argv+kstart+1,(kend-kstart)*sizeof(char *)); | |
332 | kargs[kend-kstart]=NULL; | |
af845cee | 333 | if (debug_enabled) { |
eb3dea38 | 334 | fprintf(stderr, "%s| %s: Kerberos command: ", LogTime(), PROGRAM); |
72841dbb | 335 | for (int i=0; i<kend-kstart; ++i) |
eb3dea38 MM |
336 | fprintf(stderr, "%s ", kargs[i]); |
337 | fprintf(stderr, "\n"); | |
338 | } | |
339 | /* | |
340 | Fork Kerberos helper and NTLM helper and manage IO to send NTLM requests | |
341 | to the right helper. squid must keep session state | |
342 | */ | |
343 | ||
3a2e0253 AJ |
344 | if (pipe(pkin) < 0) { |
345 | fprintf(stderr, "%s| %s: Could not assign streams for pkin\n", LogTime(), PROGRAM); | |
346 | return 1; | |
347 | } | |
348 | if (pipe(pkout) < 0) { | |
349 | fprintf(stderr, "%s| %s: Could not assign streams for pkout\n", LogTime(), PROGRAM); | |
350 | return 1; | |
351 | } | |
352 | ||
eb3dea38 MM |
353 | if (( fpid = vfork()) < 0 ) { |
354 | fprintf(stderr, "%s| %s: Failed first fork\n", LogTime(), PROGRAM); | |
355 | return 1; | |
356 | } | |
357 | ||
358 | if ( fpid == 0 ) { | |
359 | /* First Child for Kerberos helper */ | |
360 | ||
361 | close(pkin[1]); | |
362 | dup2(pkin[0],STDIN_FILENO); | |
363 | close(pkin[0]); | |
364 | ||
365 | close(pkout[0]); | |
366 | dup2(pkout[1],STDOUT_FILENO); | |
367 | close(pkout[1]); | |
368 | ||
369 | setbuf(stdin, NULL); | |
370 | setbuf(stdout, NULL); | |
371 | ||
372 | execv(kargs[0], kargs); | |
373 | fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, kargs[0], strerror(errno)); | |
374 | return 1; | |
eb3dea38 MM |
375 | } |
376 | ||
377 | close(pkin[0]); | |
378 | close(pkout[1]); | |
379 | ||
3a2e0253 AJ |
380 | if (pipe(pnin) < 0) { |
381 | fprintf(stderr, "%s| %s: Could not assign streams for pnin\n", LogTime(), PROGRAM); | |
382 | return 1; | |
383 | } | |
384 | if (pipe(pnout) < 0) { | |
385 | fprintf(stderr, "%s| %s: Could not assign streams for pnout\n", LogTime(), PROGRAM); | |
386 | return 1; | |
387 | } | |
eb3dea38 MM |
388 | |
389 | if (( fpid = vfork()) < 0 ) { | |
390 | fprintf(stderr, "%s| %s: Failed second fork\n", LogTime(), PROGRAM); | |
391 | return 1; | |
392 | } | |
393 | ||
394 | if ( fpid == 0 ) { | |
395 | /* Second Child for NTLM helper */ | |
396 | ||
397 | close(pnin[1]); | |
398 | dup2(pnin[0],STDIN_FILENO); | |
399 | close(pnin[0]); | |
400 | ||
401 | close(pnout[0]); | |
402 | dup2(pnout[1],STDOUT_FILENO); | |
403 | close(pnout[1]); | |
404 | ||
405 | setbuf(stdin, NULL); | |
406 | setbuf(stdout, NULL); | |
407 | ||
408 | execv(nargs[0], nargs); | |
409 | fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, nargs[0], strerror(errno)); | |
410 | return 1; | |
411 | } | |
412 | ||
413 | close(pnin[0]); | |
414 | close(pnout[1]); | |
415 | ||
af845cee AJ |
416 | FILE *FDKIN=fdopen(pkin[1],"w"); |
417 | FILE *FDKOUT=fdopen(pkout[0],"r"); | |
eb3dea38 | 418 | |
af845cee AJ |
419 | FILE *FDNIN=fdopen(pnin[1],"w"); |
420 | FILE *FDNOUT=fdopen(pnout[0],"r"); | |
eb3dea38 MM |
421 | |
422 | if (!FDKIN || !FDKOUT || !FDNIN || !FDNOUT) { | |
a86a4d3c | 423 | fprintf(stderr, "%s| %s: Could not assign streams for FDKIN/FDKOUT/FDNIN/FDNOUT\n", LogTime(), PROGRAM); |
af845cee | 424 | closeFds(FDKIN, FDKOUT, FDNIN, FDNOUT); |
eb3dea38 MM |
425 | return 1; |
426 | } | |
427 | ||
428 | setbuf(FDKIN, NULL); | |
429 | setbuf(FDKOUT, NULL); | |
430 | setbuf(FDNIN, NULL); | |
431 | setbuf(FDNOUT, NULL); | |
432 | ||
af845cee AJ |
433 | int result = processingLoop(FDKIN, FDKOUT, FDNIN, FDNOUT); |
434 | closeFds(FDKIN, FDKOUT, FDNIN, FDNOUT); | |
435 | return result; | |
eb3dea38 | 436 | } |
f53969cc | 437 |