]>
Commit | Line | Data |
---|---|---|
ca02e0ec | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 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 | ||
aee3523a | 75 | gettimeofday(&now, nullptr); |
eb3dea38 | 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 | ||
8b082ed9 FC |
85 | static void |
86 | usage() | |
eb3dea38 MM |
87 | { |
88 | fprintf(stderr, "Usage: \n"); | |
89 | fprintf(stderr, "negotiate_wrapper [-h] [-d] --ntlm ntlm helper + arguments --kerberos kerberos helper + arguments\n"); | |
90 | fprintf(stderr, "-h help\n"); | |
91 | fprintf(stderr, "-d full debug\n"); | |
92 | fprintf(stderr, "--ntlm full ntlm helper path with arguments\n"); | |
93 | fprintf(stderr, "--kerberos full kerberos helper path with arguments\n"); | |
94 | } | |
95 | ||
af845cee AJ |
96 | static void |
97 | closeFds(FILE *a, FILE *b, FILE *c, FILE *d) | |
98 | { | |
e0702229 | 99 | if (a) |
af845cee | 100 | fclose(a); |
e0702229 | 101 | if (b) |
af845cee | 102 | fclose(b); |
e0702229 | 103 | if (c) |
af845cee | 104 | fclose(c); |
e0702229 | 105 | if (d) |
af845cee AJ |
106 | fclose(d); |
107 | } | |
108 | ||
109 | static int | |
110 | processingLoop(FILE *FDKIN, FILE *FDKOUT, FILE *FDNIN, FILE *FDNOUT) | |
eb3dea38 MM |
111 | { |
112 | char buf[MAX_AUTHTOKEN_LEN]; | |
113 | char tbuff[MAX_AUTHTOKEN_LEN]; | |
114 | char buff[MAX_AUTHTOKEN_LEN+2]; | |
a86a4d3c | 115 | char *c; |
04b008df | 116 | size_t length; |
aee3523a | 117 | uint8_t *token = nullptr; |
af845cee AJ |
118 | |
119 | while (1) { | |
aee3523a | 120 | if (fgets(buf, sizeof(buf) - 1, stdin) == nullptr) { |
e3a5c24c | 121 | xfree(token); |
af845cee AJ |
122 | if (ferror(stdin)) { |
123 | if (debug_enabled) | |
124 | fprintf(stderr, | |
125 | "%s| %s: fgets() failed! dying..... errno=%d (%s)\n", | |
126 | LogTime(), PROGRAM, ferror(stdin), | |
127 | strerror(ferror(stdin))); | |
128 | ||
129 | fprintf(stdout, "BH input error\n"); | |
130 | return 1; /* BIIG buffer */ | |
131 | } | |
132 | fprintf(stdout, "BH input error\n"); | |
133 | return 0; | |
134 | } | |
135 | c = static_cast<char*>(memchr(buf, '\n', sizeof(buf) - 1)); | |
136 | if (c) { | |
137 | *c = '\0'; | |
138 | length = c - buf; | |
139 | if (debug_enabled) | |
04b008df | 140 | fprintf(stderr, "%s| %s: Got '%s' from squid (length: %" PRIuSIZE ").\n", |
af845cee AJ |
141 | LogTime(), PROGRAM, buf, length); |
142 | } else { | |
143 | if (debug_enabled) | |
144 | fprintf(stderr, "%s| %s: Oversized message\n", LogTime(), | |
145 | PROGRAM); | |
146 | fprintf(stdout, "BH Oversized message\n"); | |
147 | continue; | |
148 | } | |
149 | ||
150 | if (buf[0] == '\0') { | |
151 | if (debug_enabled) | |
152 | fprintf(stderr, "%s| %s: Invalid request\n", LogTime(), | |
153 | PROGRAM); | |
154 | fprintf(stdout, "BH Invalid request\n"); | |
155 | continue; | |
156 | } | |
157 | if (strlen(buf) < 2) { | |
158 | if (debug_enabled) | |
159 | fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(), | |
160 | PROGRAM, buf); | |
161 | fprintf(stdout, "BH Invalid request\n"); | |
162 | continue; | |
163 | } | |
164 | if (!strncmp(buf, "QQ", 2)) { | |
165 | fprintf(stdout, "BH quit command\n"); | |
92c2f0bd | 166 | xfree(token); |
af845cee AJ |
167 | return 0; |
168 | } | |
169 | if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) { | |
170 | if (debug_enabled) | |
171 | fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(), | |
172 | PROGRAM, buf); | |
173 | fprintf(stdout, "BH Invalid request\n"); | |
174 | continue; | |
175 | } | |
176 | if (strlen(buf) <= 3) { | |
177 | if (debug_enabled) | |
178 | fprintf(stderr, "%s| %s: Invalid negotiate request [%s]\n", | |
179 | LogTime(), PROGRAM, buf); | |
180 | fprintf(stdout, "BH Invalid negotiate request\n"); | |
181 | continue; | |
182 | } | |
183 | length = BASE64_DECODE_LENGTH(strlen(buf+3)); | |
184 | if (debug_enabled) | |
04b008df AJ |
185 | fprintf(stderr, "%s| %s: Decode '%s' (decoded length: %" PRIuSIZE ").\n", |
186 | LogTime(), PROGRAM, buf + 3, length); | |
af845cee | 187 | |
e3a5c24c | 188 | safe_free(token); |
04b008df | 189 | if (!(token = static_cast<uint8_t *>(xmalloc(length+1)))) { |
af845cee AJ |
190 | fprintf(stderr, "%s| %s: Error allocating memory for token\n", LogTime(), PROGRAM); |
191 | return 1; | |
192 | } | |
193 | ||
194 | struct base64_decode_ctx ctx; | |
195 | base64_decode_init(&ctx); | |
196 | size_t dstLen = 0; | |
1d11e9b3 | 197 | if (!base64_decode_update(&ctx, &dstLen, token, strlen(buf+3), buf+3) || |
af845cee AJ |
198 | !base64_decode_final(&ctx)) { |
199 | if (debug_enabled) | |
200 | fprintf(stderr, "%s| %s: Invalid base64 token [%s]\n", LogTime(), PROGRAM, buf+3); | |
201 | fprintf(stdout, "BH Invalid negotiate request token\n"); | |
202 | continue; | |
203 | } | |
04b008df | 204 | assert(dstLen <= length); |
af845cee AJ |
205 | length = dstLen; |
206 | token[dstLen] = '\0'; | |
207 | ||
208 | if ((static_cast<size_t>(length) >= sizeof(ntlmProtocol) + 1) && | |
209 | (!memcmp(token, ntlmProtocol, sizeof ntlmProtocol))) { | |
af845cee AJ |
210 | if (debug_enabled) |
211 | fprintf(stderr, "%s| %s: received type %d NTLM token\n", | |
212 | LogTime(), PROGRAM, (int) *((unsigned char *) token + | |
213 | sizeof ntlmProtocol)); | |
214 | fprintf(FDNIN, "%s\n",buf); | |
aee3523a | 215 | if (fgets(tbuff, sizeof(tbuff) - 1, FDNOUT) == nullptr) { |
e3a5c24c | 216 | xfree(token); |
af845cee AJ |
217 | if (ferror(FDNOUT)) { |
218 | fprintf(stderr, | |
219 | "fgets() failed! dying..... errno=%d (%s)\n", | |
220 | ferror(FDNOUT), strerror(ferror(FDNOUT))); | |
221 | return 1; | |
222 | } | |
223 | fprintf(stderr, "%s| %s: Error reading NTLM helper response\n", | |
224 | LogTime(), PROGRAM); | |
225 | return 0; | |
226 | } | |
227 | /* | |
e3a5c24c AJ |
228 | * Need to translate NTLM reply to Negotiate reply: |
229 | * AF user => AF blob user | |
230 | * NA reason => NA blob reason | |
231 | * Set blob to '=' | |
232 | */ | |
af845cee AJ |
233 | if (strlen(tbuff) >= 3 && (!strncmp(tbuff,"AF ",3) || !strncmp(tbuff,"NA ",3))) { |
234 | strncpy(buff,tbuff,3); | |
235 | buff[3]='='; | |
236 | for (unsigned int i=2; i<=strlen(tbuff); ++i) | |
237 | buff[i+2] = tbuff[i]; | |
238 | } else { | |
239 | strcpy(buff,tbuff); | |
240 | } | |
241 | } else { | |
af845cee AJ |
242 | if (debug_enabled) |
243 | fprintf(stderr, "%s| %s: received Kerberos token\n", | |
244 | LogTime(), PROGRAM); | |
245 | ||
246 | fprintf(FDKIN, "%s\n",buf); | |
aee3523a | 247 | if (fgets(buff, sizeof(buff) - 1, FDKOUT) == nullptr) { |
e3a5c24c | 248 | xfree(token); |
af845cee AJ |
249 | if (ferror(FDKOUT)) { |
250 | fprintf(stderr, | |
251 | "fgets() failed! dying..... errno=%d (%s)\n", | |
252 | ferror(FDKOUT), strerror(ferror(FDKOUT))); | |
253 | return 1; | |
254 | } | |
255 | fprintf(stderr, "%s| %s: Error reading Kerberos helper response\n", | |
256 | LogTime(), PROGRAM); | |
257 | return 0; | |
258 | } | |
259 | } | |
265d1cc5 | 260 | buff[sizeof(buff)-1] = '\0'; // paranoid; already terminated correctly |
af845cee AJ |
261 | fprintf(stdout,"%s",buff); |
262 | if (debug_enabled) | |
263 | fprintf(stderr, "%s| %s: Return '%s'\n", | |
264 | LogTime(), PROGRAM, buff); | |
265 | } | |
266 | ||
e3a5c24c | 267 | xfree(token); |
af845cee AJ |
268 | return 1; |
269 | } | |
270 | ||
271 | int | |
272 | main(int argc, char *const argv[]) | |
273 | { | |
eb3dea38 MM |
274 | int nstart = 0, kstart = 0; |
275 | int nend = 0, kend = 0; | |
eb3dea38 | 276 | char **nargs, **kargs; |
eb3dea38 | 277 | int fpid; |
eb3dea38 MM |
278 | int pkin[2]; |
279 | int pkout[2]; | |
280 | int pnin[2]; | |
281 | int pnout[2]; | |
282 | ||
aee3523a AR |
283 | setbuf(stdout, nullptr); |
284 | setbuf(stdin, nullptr); | |
eb3dea38 MM |
285 | |
286 | if (argc ==1 || !strncasecmp(argv[1],"-h",2)) { | |
287 | usage(); | |
24885773 | 288 | exit(EXIT_SUCCESS); |
eb3dea38 MM |
289 | } |
290 | ||
72841dbb | 291 | int j = 1; |
eb3dea38 | 292 | if (!strncasecmp(argv[1],"-d",2)) { |
af845cee | 293 | debug_enabled = 1; |
eb3dea38 MM |
294 | j = 2; |
295 | } | |
296 | ||
72841dbb | 297 | for (int i=j; i<argc; ++i) { |
eb3dea38 MM |
298 | if (!strncasecmp(argv[i],"--ntlm",6)) |
299 | nstart = i; | |
300 | if (!strncasecmp(argv[i],"--kerberos",10)) | |
301 | kstart = i; | |
302 | } | |
303 | if (nstart > kstart) { | |
304 | kend = nstart-1; | |
305 | nend = argc-1; | |
306 | } else { | |
307 | kend = argc-1; | |
308 | nend = kstart-1; | |
309 | } | |
310 | if (nstart == 0 || kstart == 0 || kend-kstart <= 0 || nend-nstart <= 0 ) { | |
311 | usage(); | |
24885773 | 312 | exit(EXIT_SUCCESS); |
eb3dea38 MM |
313 | } |
314 | ||
af845cee | 315 | if (debug_enabled) |
eb3dea38 MM |
316 | fprintf(stderr, "%s| %s: Starting version %s\n", LogTime(), PROGRAM, |
317 | VERSION); | |
318 | ||
aee3523a | 319 | if ((nargs = (char **)xmalloc((nend-nstart+1)*sizeof(char *))) == nullptr) { |
eb3dea38 | 320 | fprintf(stderr, "%s| %s: Error allocating memory for ntlm helper\n", LogTime(), PROGRAM); |
24885773 | 321 | exit(EXIT_FAILURE); |
eb3dea38 MM |
322 | } |
323 | memcpy(nargs,argv+nstart+1,(nend-nstart)*sizeof(char *)); | |
aee3523a | 324 | nargs[nend-nstart]=nullptr; |
af845cee | 325 | if (debug_enabled) { |
eb3dea38 | 326 | fprintf(stderr, "%s| %s: NTLM command: ", LogTime(), PROGRAM); |
72841dbb | 327 | for (int i=0; i<nend-nstart; ++i) |
eb3dea38 MM |
328 | fprintf(stderr, "%s ", nargs[i]); |
329 | fprintf(stderr, "\n"); | |
330 | } | |
aee3523a | 331 | if ((kargs = (char **)xmalloc((kend-kstart+1)*sizeof(char *))) == nullptr) { |
eb3dea38 | 332 | fprintf(stderr, "%s| %s: Error allocating memory for kerberos helper\n", LogTime(), PROGRAM); |
24885773 | 333 | exit(EXIT_FAILURE); |
eb3dea38 MM |
334 | } |
335 | memcpy(kargs,argv+kstart+1,(kend-kstart)*sizeof(char *)); | |
aee3523a | 336 | kargs[kend-kstart]=nullptr; |
af845cee | 337 | if (debug_enabled) { |
eb3dea38 | 338 | fprintf(stderr, "%s| %s: Kerberos command: ", LogTime(), PROGRAM); |
72841dbb | 339 | for (int i=0; i<kend-kstart; ++i) |
eb3dea38 MM |
340 | fprintf(stderr, "%s ", kargs[i]); |
341 | fprintf(stderr, "\n"); | |
342 | } | |
343 | /* | |
344 | Fork Kerberos helper and NTLM helper and manage IO to send NTLM requests | |
345 | to the right helper. squid must keep session state | |
346 | */ | |
347 | ||
3a2e0253 AJ |
348 | if (pipe(pkin) < 0) { |
349 | fprintf(stderr, "%s| %s: Could not assign streams for pkin\n", LogTime(), PROGRAM); | |
24885773 | 350 | exit(EXIT_FAILURE); |
3a2e0253 AJ |
351 | } |
352 | if (pipe(pkout) < 0) { | |
353 | fprintf(stderr, "%s| %s: Could not assign streams for pkout\n", LogTime(), PROGRAM); | |
24885773 | 354 | exit(EXIT_FAILURE); |
3a2e0253 AJ |
355 | } |
356 | ||
eb3dea38 MM |
357 | if (( fpid = vfork()) < 0 ) { |
358 | fprintf(stderr, "%s| %s: Failed first fork\n", LogTime(), PROGRAM); | |
24885773 | 359 | exit(EXIT_FAILURE); |
eb3dea38 MM |
360 | } |
361 | ||
362 | if ( fpid == 0 ) { | |
363 | /* First Child for Kerberos helper */ | |
364 | ||
365 | close(pkin[1]); | |
366 | dup2(pkin[0],STDIN_FILENO); | |
367 | close(pkin[0]); | |
368 | ||
369 | close(pkout[0]); | |
370 | dup2(pkout[1],STDOUT_FILENO); | |
371 | close(pkout[1]); | |
372 | ||
aee3523a AR |
373 | setbuf(stdin, nullptr); |
374 | setbuf(stdout, nullptr); | |
eb3dea38 MM |
375 | |
376 | execv(kargs[0], kargs); | |
377 | fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, kargs[0], strerror(errno)); | |
24885773 | 378 | exit(EXIT_FAILURE); |
eb3dea38 MM |
379 | } |
380 | ||
381 | close(pkin[0]); | |
382 | close(pkout[1]); | |
383 | ||
3a2e0253 AJ |
384 | if (pipe(pnin) < 0) { |
385 | fprintf(stderr, "%s| %s: Could not assign streams for pnin\n", LogTime(), PROGRAM); | |
24885773 | 386 | exit(EXIT_FAILURE); |
3a2e0253 AJ |
387 | } |
388 | if (pipe(pnout) < 0) { | |
389 | fprintf(stderr, "%s| %s: Could not assign streams for pnout\n", LogTime(), PROGRAM); | |
24885773 | 390 | exit(EXIT_FAILURE); |
3a2e0253 | 391 | } |
eb3dea38 MM |
392 | |
393 | if (( fpid = vfork()) < 0 ) { | |
394 | fprintf(stderr, "%s| %s: Failed second fork\n", LogTime(), PROGRAM); | |
24885773 | 395 | exit(EXIT_FAILURE); |
eb3dea38 MM |
396 | } |
397 | ||
398 | if ( fpid == 0 ) { | |
399 | /* Second Child for NTLM helper */ | |
400 | ||
401 | close(pnin[1]); | |
402 | dup2(pnin[0],STDIN_FILENO); | |
403 | close(pnin[0]); | |
404 | ||
405 | close(pnout[0]); | |
406 | dup2(pnout[1],STDOUT_FILENO); | |
407 | close(pnout[1]); | |
408 | ||
aee3523a AR |
409 | setbuf(stdin, nullptr); |
410 | setbuf(stdout, nullptr); | |
eb3dea38 MM |
411 | |
412 | execv(nargs[0], nargs); | |
413 | fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, nargs[0], strerror(errno)); | |
24885773 | 414 | exit(EXIT_FAILURE); |
eb3dea38 MM |
415 | } |
416 | ||
417 | close(pnin[0]); | |
418 | close(pnout[1]); | |
419 | ||
af845cee AJ |
420 | FILE *FDKIN=fdopen(pkin[1],"w"); |
421 | FILE *FDKOUT=fdopen(pkout[0],"r"); | |
eb3dea38 | 422 | |
af845cee AJ |
423 | FILE *FDNIN=fdopen(pnin[1],"w"); |
424 | FILE *FDNOUT=fdopen(pnout[0],"r"); | |
eb3dea38 MM |
425 | |
426 | if (!FDKIN || !FDKOUT || !FDNIN || !FDNOUT) { | |
a86a4d3c | 427 | fprintf(stderr, "%s| %s: Could not assign streams for FDKIN/FDKOUT/FDNIN/FDNOUT\n", LogTime(), PROGRAM); |
af845cee | 428 | closeFds(FDKIN, FDKOUT, FDNIN, FDNOUT); |
24885773 | 429 | exit(EXIT_FAILURE); |
eb3dea38 MM |
430 | } |
431 | ||
aee3523a AR |
432 | setbuf(FDKIN, nullptr); |
433 | setbuf(FDKOUT, nullptr); | |
434 | setbuf(FDNIN, nullptr); | |
435 | setbuf(FDNOUT, nullptr); | |
eb3dea38 | 436 | |
af845cee AJ |
437 | int result = processingLoop(FDKIN, FDKOUT, FDNIN, FDNOUT); |
438 | closeFds(FDKIN, FDKOUT, FDNIN, FDNOUT); | |
439 | return result; | |
eb3dea38 | 440 | } |
f53969cc | 441 |