]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/negotiate/wrapper/negotiate_wrapper.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / auth / negotiate / wrapper / negotiate_wrapper.cc
CommitLineData
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
66static const unsigned char ntlmProtocol[] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
67
68static const char *
69LogTime()
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
85void 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
95static void
96closeFds(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
108static int
109processingLoop(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
268int
269main(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