negotiate_wrapper.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
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 
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  */
32 
33 #include "squid.h"
34 #include "base64.h"
35 #include "compat/pipe.h"
36 #include "compat/unistd.h"
37 
38 #include <cerrno>
39 #include <cstring>
40 #include <cstdlib>
41 #include <ctime>
42 
43 #if !defined(HAVE_DECL_XMALLOC) || !HAVE_DECL_XMALLOC
44 #define xmalloc malloc
45 #endif
46 #if !defined(HAVE_DECL_XSTRDUP) || !HAVE_DECL_XSTRDUP
47 #define xstrdup strdup
48 #endif
49 #if !defined(HAVE_DECL_XFREE) || !HAVE_DECL_XFREE
50 #define xfree free
51 #endif
52 
53 #undef PROGRAM
54 #define PROGRAM "negotiate_wrapper"
55 #undef VERSION
56 #define VERSION "1.0.1"
57 
58 #ifndef MAX_AUTHTOKEN_LEN
59 #define MAX_AUTHTOKEN_LEN 65535
60 #endif
61 
62 static const unsigned char ntlmProtocol[] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
63 
64 static const char *
66 {
67  struct timeval now;
68  static time_t last_t = 0;
69  static char buf[128];
70 
71  gettimeofday(&now, nullptr);
72  if (now.tv_sec != last_t) {
73  time_t *tmp = (time_t *) & now.tv_sec;
74  struct tm *tm = localtime(tmp);
75  strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
76  last_t = now.tv_sec;
77  }
78  return buf;
79 }
80 
81 static void
83 {
84  fprintf(stderr, "Usage: \n");
85  fprintf(stderr, "negotiate_wrapper [-h] [-d] --ntlm ntlm helper + arguments --kerberos kerberos helper + arguments\n");
86  fprintf(stderr, "-h help\n");
87  fprintf(stderr, "-d full debug\n");
88  fprintf(stderr, "--ntlm full ntlm helper path with arguments\n");
89  fprintf(stderr, "--kerberos full kerberos helper path with arguments\n");
90 }
91 
92 static void
93 closeFds(FILE *a, FILE *b, FILE *c, FILE *d)
94 {
95  if (a)
96  fclose(a);
97  if (b)
98  fclose(b);
99  if (c)
100  fclose(c);
101  if (d)
102  fclose(d);
103 }
104 
105 static int
106 processingLoop(FILE *FDKIN, FILE *FDKOUT, FILE *FDNIN, FILE *FDNOUT)
107 {
108  char buf[MAX_AUTHTOKEN_LEN];
109  char tbuff[MAX_AUTHTOKEN_LEN];
110  char buff[MAX_AUTHTOKEN_LEN+2];
111  char *c;
112  size_t length;
113  uint8_t *token = nullptr;
114 
115  while (1) {
116  if (fgets(buf, sizeof(buf) - 1, stdin) == nullptr) {
117  xfree(token);
118  if (ferror(stdin)) {
119  if (debug_enabled)
120  fprintf(stderr,
121  "%s| %s: fgets() failed! dying..... errno=%d (%s)\n",
122  LogTime(), PROGRAM, ferror(stdin),
123  strerror(ferror(stdin)));
124 
125  fprintf(stdout, "BH input error\n");
126  return 1; /* BIIG buffer */
127  }
128  fprintf(stdout, "BH input error\n");
129  return 0;
130  }
131  c = static_cast<char*>(memchr(buf, '\n', sizeof(buf) - 1));
132  if (c) {
133  *c = '\0';
134  length = c - buf;
135  if (debug_enabled)
136  fprintf(stderr, "%s| %s: Got '%s' from squid (length: %zu).\n",
137  LogTime(), PROGRAM, buf, length);
138  } else {
139  if (debug_enabled)
140  fprintf(stderr, "%s| %s: Oversized message\n", LogTime(),
141  PROGRAM);
142  fprintf(stdout, "BH Oversized message\n");
143  continue;
144  }
145 
146  if (buf[0] == '\0') {
147  if (debug_enabled)
148  fprintf(stderr, "%s| %s: Invalid request\n", LogTime(),
149  PROGRAM);
150  fprintf(stdout, "BH Invalid request\n");
151  continue;
152  }
153  if (strlen(buf) < 2) {
154  if (debug_enabled)
155  fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(),
156  PROGRAM, buf);
157  fprintf(stdout, "BH Invalid request\n");
158  continue;
159  }
160  if (!strncmp(buf, "QQ", 2)) {
161  fprintf(stdout, "BH quit command\n");
162  xfree(token);
163  return 0;
164  }
165  if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) {
166  if (debug_enabled)
167  fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(),
168  PROGRAM, buf);
169  fprintf(stdout, "BH Invalid request\n");
170  continue;
171  }
172  if (strlen(buf) <= 3) {
173  if (debug_enabled)
174  fprintf(stderr, "%s| %s: Invalid negotiate request [%s]\n",
175  LogTime(), PROGRAM, buf);
176  fprintf(stdout, "BH Invalid negotiate request\n");
177  continue;
178  }
179  length = BASE64_DECODE_LENGTH(strlen(buf+3));
180  if (debug_enabled)
181  fprintf(stderr, "%s| %s: Decode '%s' (decoded length: %zu).\n",
182  LogTime(), PROGRAM, buf + 3, length);
183 
184  safe_free(token);
185  if (!(token = static_cast<uint8_t *>(xmalloc(length+1)))) {
186  fprintf(stderr, "%s| %s: Error allocating memory for token\n", LogTime(), PROGRAM);
187  return 1;
188  }
189 
190  struct base64_decode_ctx ctx;
191  base64_decode_init(&ctx);
192  size_t dstLen = 0;
193  if (!base64_decode_update(&ctx, &dstLen, token, strlen(buf+3), buf+3) ||
194  !base64_decode_final(&ctx)) {
195  if (debug_enabled)
196  fprintf(stderr, "%s| %s: Invalid base64 token [%s]\n", LogTime(), PROGRAM, buf+3);
197  fprintf(stdout, "BH Invalid negotiate request token\n");
198  continue;
199  }
200  assert(dstLen <= length);
201  length = dstLen;
202  token[dstLen] = '\0';
203 
204  if ((static_cast<size_t>(length) >= sizeof(ntlmProtocol) + 1) &&
205  (!memcmp(token, ntlmProtocol, sizeof ntlmProtocol))) {
206  if (debug_enabled)
207  fprintf(stderr, "%s| %s: received type %d NTLM token\n",
208  LogTime(), PROGRAM, (int) *((unsigned char *) token +
209  sizeof ntlmProtocol));
210  fprintf(FDNIN, "%s\n",buf);
211  if (fgets(tbuff, sizeof(tbuff) - 1, FDNOUT) == nullptr) {
212  xfree(token);
213  if (ferror(FDNOUT)) {
214  fprintf(stderr,
215  "fgets() failed! dying..... errno=%d (%s)\n",
216  ferror(FDNOUT), strerror(ferror(FDNOUT)));
217  return 1;
218  }
219  fprintf(stderr, "%s| %s: Error reading NTLM helper response\n",
220  LogTime(), PROGRAM);
221  return 0;
222  }
223 
224  if (!memchr(tbuff, '\n', sizeof(tbuff) - 1)) {
225  fprintf(stderr, "%s| %s: Oversized NTLM helper response\n",
226  LogTime(), PROGRAM);
227  return 0;
228  }
229 
230  /*
231  * Need to translate NTLM reply to Negotiate reply:
232  * AF user => AF blob user
233  * NA reason => NA blob reason
234  * Set blob to '='
235  */
236  if (strlen(tbuff) >= 3 && (!strncmp(tbuff,"AF ",3) || !strncmp(tbuff,"NA ",3))) {
237  strncpy(buff,tbuff,3);
238  buff[3]='=';
239  for (unsigned int i=2; i<=strlen(tbuff); ++i)
240  buff[i+2] = tbuff[i];
241  } else {
242  strcpy(buff,tbuff);
243  }
244  } else {
245  if (debug_enabled)
246  fprintf(stderr, "%s| %s: received Kerberos token\n",
247  LogTime(), PROGRAM);
248 
249  fprintf(FDKIN, "%s\n",buf);
250  if (fgets(buff, sizeof(buff) - 1, FDKOUT) == nullptr) {
251  xfree(token);
252  if (ferror(FDKOUT)) {
253  fprintf(stderr,
254  "fgets() failed! dying..... errno=%d (%s)\n",
255  ferror(FDKOUT), strerror(ferror(FDKOUT)));
256  return 1;
257  }
258  fprintf(stderr, "%s| %s: Error reading Kerberos helper response\n",
259  LogTime(), PROGRAM);
260  return 0;
261  }
262 
263  if (!memchr(buff, '\n', sizeof(buff) - 1)) {
264  fprintf(stderr, "%s| %s: Oversized Kerberos helper response\n",
265  LogTime(), PROGRAM);
266  return 0;
267  }
268  }
269  buff[sizeof(buff)-1] = '\0'; // paranoid; already terminated correctly
270  fprintf(stdout,"%s",buff);
271  if (debug_enabled)
272  fprintf(stderr, "%s| %s: Return '%s'\n",
273  LogTime(), PROGRAM, buff);
274  }
275 
276  xfree(token);
277  return 1;
278 }
279 
280 int
281 main(int argc, char *const argv[])
282 {
283  int nstart = 0, kstart = 0;
284  int nend = 0, kend = 0;
285  char **nargs, **kargs;
286  int fpid;
287  int pkin[2];
288  int pkout[2];
289  int pnin[2];
290  int pnout[2];
291 
292  setbuf(stdout, nullptr);
293  setbuf(stdin, nullptr);
294 
295  if (argc ==1 || !strncasecmp(argv[1],"-h",2)) {
296  usage();
297  exit(EXIT_SUCCESS);
298  }
299 
300  int j = 1;
301  if (!strncasecmp(argv[1],"-d",2)) {
302  debug_enabled = 1;
303  j = 2;
304  }
305 
306  for (int i=j; i<argc; ++i) {
307  if (!strncasecmp(argv[i],"--ntlm",6))
308  nstart = i;
309  if (!strncasecmp(argv[i],"--kerberos",10))
310  kstart = i;
311  }
312  if (nstart > kstart) {
313  kend = nstart-1;
314  nend = argc-1;
315  } else {
316  kend = argc-1;
317  nend = kstart-1;
318  }
319  if (nstart == 0 || kstart == 0 || kend-kstart <= 0 || nend-nstart <= 0 ) {
320  usage();
321  exit(EXIT_SUCCESS);
322  }
323 
324  if (debug_enabled)
325  fprintf(stderr, "%s| %s: Starting version %s\n", LogTime(), PROGRAM,
326  VERSION);
327 
328  if ((nargs = (char **)xmalloc((nend-nstart+1)*sizeof(char *))) == nullptr) {
329  fprintf(stderr, "%s| %s: Error allocating memory for ntlm helper\n", LogTime(), PROGRAM);
330  exit(EXIT_FAILURE);
331  }
332  memcpy(nargs,argv+nstart+1,(nend-nstart)*sizeof(char *));
333  nargs[nend-nstart]=nullptr;
334  if (debug_enabled) {
335  fprintf(stderr, "%s| %s: NTLM command: ", LogTime(), PROGRAM);
336  for (int i=0; i<nend-nstart; ++i)
337  fprintf(stderr, "%s ", nargs[i]);
338  fprintf(stderr, "\n");
339  }
340  if ((kargs = (char **)xmalloc((kend-kstart+1)*sizeof(char *))) == nullptr) {
341  fprintf(stderr, "%s| %s: Error allocating memory for kerberos helper\n", LogTime(), PROGRAM);
342  exit(EXIT_FAILURE);
343  }
344  memcpy(kargs,argv+kstart+1,(kend-kstart)*sizeof(char *));
345  kargs[kend-kstart]=nullptr;
346  if (debug_enabled) {
347  fprintf(stderr, "%s| %s: Kerberos command: ", LogTime(), PROGRAM);
348  for (int i=0; i<kend-kstart; ++i)
349  fprintf(stderr, "%s ", kargs[i]);
350  fprintf(stderr, "\n");
351  }
352  /*
353  Fork Kerberos helper and NTLM helper and manage IO to send NTLM requests
354  to the right helper. squid must keep session state
355  */
356 
357  if (pipe(pkin) < 0) {
358  fprintf(stderr, "%s| %s: Could not assign streams for pkin\n", LogTime(), PROGRAM);
359  exit(EXIT_FAILURE);
360  }
361  if (pipe(pkout) < 0) {
362  fprintf(stderr, "%s| %s: Could not assign streams for pkout\n", LogTime(), PROGRAM);
363  exit(EXIT_FAILURE);
364  }
365 
366  if (( fpid = fork()) < 0 ) {
367  fprintf(stderr, "%s| %s: Failed first fork\n", LogTime(), PROGRAM);
368  exit(EXIT_FAILURE);
369  }
370 
371  if ( fpid == 0 ) {
372  /* First Child for Kerberos helper */
373 
374  xclose(pkin[1]);
375  dup2(pkin[0],STDIN_FILENO);
376  xclose(pkin[0]);
377 
378  xclose(pkout[0]);
379  dup2(pkout[1],STDOUT_FILENO);
380  xclose(pkout[1]);
381 
382  setbuf(stdin, nullptr);
383  setbuf(stdout, nullptr);
384 
385  execv(kargs[0], kargs);
386  fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, kargs[0], strerror(errno));
387  exit(EXIT_FAILURE);
388  }
389 
390  xclose(pkin[0]);
391  xclose(pkout[1]);
392 
393  if (pipe(pnin) < 0) {
394  fprintf(stderr, "%s| %s: Could not assign streams for pnin\n", LogTime(), PROGRAM);
395  exit(EXIT_FAILURE);
396  }
397  if (pipe(pnout) < 0) {
398  fprintf(stderr, "%s| %s: Could not assign streams for pnout\n", LogTime(), PROGRAM);
399  exit(EXIT_FAILURE);
400  }
401 
402  if (( fpid = fork()) < 0 ) {
403  fprintf(stderr, "%s| %s: Failed second fork\n", LogTime(), PROGRAM);
404  exit(EXIT_FAILURE);
405  }
406 
407  if ( fpid == 0 ) {
408  /* Second Child for NTLM helper */
409 
410  xclose(pnin[1]);
411  dup2(pnin[0],STDIN_FILENO);
412  xclose(pnin[0]);
413 
414  xclose(pnout[0]);
415  dup2(pnout[1],STDOUT_FILENO);
416  xclose(pnout[1]);
417 
418  setbuf(stdin, nullptr);
419  setbuf(stdout, nullptr);
420 
421  execv(nargs[0], nargs);
422  fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, nargs[0], strerror(errno));
423  exit(EXIT_FAILURE);
424  }
425 
426  xclose(pnin[0]);
427  xclose(pnout[1]);
428 
429  FILE *FDKIN=fdopen(pkin[1],"w");
430  FILE *FDKOUT=fdopen(pkout[0],"r");
431 
432  FILE *FDNIN=fdopen(pnin[1],"w");
433  FILE *FDNOUT=fdopen(pnout[0],"r");
434 
435  if (!FDKIN || !FDKOUT || !FDNIN || !FDNOUT) {
436  fprintf(stderr, "%s| %s: Could not assign streams for FDKIN/FDKOUT/FDNIN/FDNOUT\n", LogTime(), PROGRAM);
437  closeFds(FDKIN, FDKOUT, FDNIN, FDNOUT);
438  exit(EXIT_FAILURE);
439  }
440 
441  setbuf(FDKIN, nullptr);
442  setbuf(FDKOUT, nullptr);
443  setbuf(FDNIN, nullptr);
444  setbuf(FDNOUT, nullptr);
445 
446  int result = processingLoop(FDKIN, FDKOUT, FDNIN, FDNOUT);
447  closeFds(FDKIN, FDKOUT, FDNIN, FDNOUT);
448  return result;
449 }
450 
#define xmalloc
static const char * LogTime()
void base64_decode_init(struct base64_decode_ctx *ctx)
Definition: base64.c:54
int main(int argc, char *const argv[])
static void usage()
char * strerror(int ern)
Definition: strerror.c:22
static void closeFds(FILE *a, FILE *b, FILE *c, FILE *d)
#define MAX_AUTHTOKEN_LEN
int base64_decode_final(struct base64_decode_ctx *ctx)
Definition: base64.c:159
int debug_enabled
Definition: debug.cc:13
static int processingLoop(FILE *FDKIN, FILE *FDKOUT, FILE *FDNIN, FILE *FDNOUT)
#define safe_free(x)
Definition: xalloc.h:73
#define assert(EX)
Definition: assert.h:17
int base64_decode_update(struct base64_decode_ctx *ctx, size_t *dst_length, uint8_t *dst, size_t src_length, const char *src)
Definition: base64.c:129
#define xfree
static const unsigned char ntlmProtocol[]
int xclose(int fd)
POSIX close(2) equivalent.
Definition: unistd.h:43
#define VERSION
#define PROGRAM
#define BASE64_DECODE_LENGTH(length)
Definition: base64.h:120

 

Introduction

Documentation

Support

Miscellaneous