]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/negotiate_auth/wrapper/negotiate_wrapper.cc
SourceFormat Enforcement
[thirdparty/squid.git] / helpers / negotiate_auth / wrapper / negotiate_wrapper.cc
1 /*
2 * Copyright (C) 1996-2014 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
36 #include <cerrno>
37 #include <cstring>
38 #include <cstdlib>
39 #include <ctime>
40 #if HAVE_NETDB_H
41 #include <netdb.h>
42 #endif
43 #if HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
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 {
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) {
77 time_t *tmp = (time_t *) & now.tv_sec;
78 struct tm *tm = localtime(tmp);
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
95 int
96 main(int argc, char *const argv[])
97 {
98 char buf[MAX_AUTHTOKEN_LEN];
99 char tbuff[MAX_AUTHTOKEN_LEN];
100 char buff[MAX_AUTHTOKEN_LEN+2];
101 char *c;
102 int debug = 0;
103 int length;
104 int nstart = 0, kstart = 0;
105 int nend = 0, kend = 0;
106 char *token;
107 char **nargs, **kargs;
108 int fpid;
109 FILE *FDKIN,*FDKOUT;
110 FILE *FDNIN,*FDNOUT;
111 int pkin[2];
112 int pkout[2];
113 int pnin[2];
114 int pnout[2];
115
116 setbuf(stdout, NULL);
117 setbuf(stdin, NULL);
118
119 if (argc ==1 || !strncasecmp(argv[1],"-h",2)) {
120 usage();
121 return 0;
122 }
123
124 int j = 1;
125 if (!strncasecmp(argv[1],"-d",2)) {
126 debug = 1;
127 j = 2;
128 }
129
130 for (int i=j; i<argc; ++i) {
131 if (!strncasecmp(argv[i],"--ntlm",6))
132 nstart = i;
133 if (!strncasecmp(argv[i],"--kerberos",10))
134 kstart = i;
135 }
136 if (nstart > kstart) {
137 kend = nstart-1;
138 nend = argc-1;
139 } else {
140 kend = argc-1;
141 nend = kstart-1;
142 }
143 if (nstart == 0 || kstart == 0 || kend-kstart <= 0 || nend-nstart <= 0 ) {
144 usage();
145 return 0;
146 }
147
148 if (debug)
149 fprintf(stderr, "%s| %s: Starting version %s\n", LogTime(), PROGRAM,
150 VERSION);
151
152 if ((nargs = (char **)xmalloc((nend-nstart+1)*sizeof(char *))) == NULL) {
153 fprintf(stderr, "%s| %s: Error allocating memory for ntlm helper\n", LogTime(), PROGRAM);
154 return 1;
155 }
156 memcpy(nargs,argv+nstart+1,(nend-nstart)*sizeof(char *));
157 nargs[nend-nstart]=NULL;
158 if (debug) {
159 fprintf(stderr, "%s| %s: NTLM command: ", LogTime(), PROGRAM);
160 for (int i=0; i<nend-nstart; ++i)
161 fprintf(stderr, "%s ", nargs[i]);
162 fprintf(stderr, "\n");
163 }
164 if ((kargs = (char **)xmalloc((kend-kstart+1)*sizeof(char *))) == NULL) {
165 fprintf(stderr, "%s| %s: Error allocating memory for kerberos helper\n", LogTime(), PROGRAM);
166 return 1;
167 }
168 memcpy(kargs,argv+kstart+1,(kend-kstart)*sizeof(char *));
169 kargs[kend-kstart]=NULL;
170 if (debug) {
171 fprintf(stderr, "%s| %s: Kerberos command: ", LogTime(), PROGRAM);
172 for (int i=0; i<kend-kstart; ++i)
173 fprintf(stderr, "%s ", kargs[i]);
174 fprintf(stderr, "\n");
175 }
176 /*
177 Fork Kerberos helper and NTLM helper and manage IO to send NTLM requests
178 to the right helper. squid must keep session state
179 */
180
181 if (pipe(pkin) < 0) {
182 fprintf(stderr, "%s| %s: Could not assign streams for pkin\n", LogTime(), PROGRAM);
183 return 1;
184 }
185 if (pipe(pkout) < 0) {
186 fprintf(stderr, "%s| %s: Could not assign streams for pkout\n", LogTime(), PROGRAM);
187 return 1;
188 }
189
190 if (( fpid = vfork()) < 0 ) {
191 fprintf(stderr, "%s| %s: Failed first fork\n", LogTime(), PROGRAM);
192 return 1;
193 }
194
195 if ( fpid == 0 ) {
196 /* First Child for Kerberos helper */
197
198 close(pkin[1]);
199 dup2(pkin[0],STDIN_FILENO);
200 close(pkin[0]);
201
202 close(pkout[0]);
203 dup2(pkout[1],STDOUT_FILENO);
204 close(pkout[1]);
205
206 setbuf(stdin, NULL);
207 setbuf(stdout, NULL);
208
209 execv(kargs[0], kargs);
210 fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, kargs[0], strerror(errno));
211 return 1;
212
213 }
214
215 close(pkin[0]);
216 close(pkout[1]);
217
218 if (pipe(pnin) < 0) {
219 fprintf(stderr, "%s| %s: Could not assign streams for pnin\n", LogTime(), PROGRAM);
220 return 1;
221 }
222 if (pipe(pnout) < 0) {
223 fprintf(stderr, "%s| %s: Could not assign streams for pnout\n", LogTime(), PROGRAM);
224 return 1;
225 }
226
227 if (( fpid = vfork()) < 0 ) {
228 fprintf(stderr, "%s| %s: Failed second fork\n", LogTime(), PROGRAM);
229 return 1;
230 }
231
232 if ( fpid == 0 ) {
233 /* Second Child for NTLM helper */
234
235 close(pnin[1]);
236 dup2(pnin[0],STDIN_FILENO);
237 close(pnin[0]);
238
239 close(pnout[0]);
240 dup2(pnout[1],STDOUT_FILENO);
241 close(pnout[1]);
242
243 setbuf(stdin, NULL);
244 setbuf(stdout, NULL);
245
246 execv(nargs[0], nargs);
247 fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, nargs[0], strerror(errno));
248 return 1;
249 }
250
251 close(pnin[0]);
252 close(pnout[1]);
253
254 FDKIN=fdopen(pkin[1],"w");
255 FDKOUT=fdopen(pkout[0],"r");
256
257 FDNIN=fdopen(pnin[1],"w");
258 FDNOUT=fdopen(pnout[0],"r");
259
260 if (!FDKIN || !FDKOUT || !FDNIN || !FDNOUT) {
261 fprintf(stderr, "%s| %s: Could not assign streams for FDKIN/FDKOUT/FDNIN/FDNOUT\n", LogTime(), PROGRAM);
262 return 1;
263 }
264
265 setbuf(FDKIN, NULL);
266 setbuf(FDKOUT, NULL);
267 setbuf(FDNIN, NULL);
268 setbuf(FDNOUT, NULL);
269
270 while (1) {
271 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
272 if (ferror(stdin)) {
273 if (debug)
274 fprintf(stderr,
275 "%s| %s: fgets() failed! dying..... errno=%d (%s)\n",
276 LogTime(), PROGRAM, ferror(stdin),
277 strerror(ferror(stdin)));
278
279 fprintf(stdout, "BH input error\n");
280 return 1; /* BIIG buffer */
281 }
282 fprintf(stdout, "BH input error\n");
283 return 0;
284 }
285 c = static_cast<char*>(memchr(buf, '\n', sizeof(buf) - 1));
286 if (c) {
287 *c = '\0';
288 length = c - buf;
289 if (debug)
290 fprintf(stderr, "%s| %s: Got '%s' from squid (length: %d).\n",
291 LogTime(), PROGRAM, buf, length);
292 } else {
293 if (debug)
294 fprintf(stderr, "%s| %s: Oversized message\n", LogTime(),
295 PROGRAM);
296 fprintf(stdout, "BH Oversized message\n");
297 continue;
298 }
299
300 if (buf[0] == '\0') {
301 if (debug)
302 fprintf(stderr, "%s| %s: Invalid request\n", LogTime(),
303 PROGRAM);
304 fprintf(stdout, "BH Invalid request\n");
305 continue;
306 }
307 if (strlen(buf) < 2) {
308 if (debug)
309 fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(),
310 PROGRAM, buf);
311 fprintf(stdout, "BH Invalid request\n");
312 continue;
313 }
314 if (!strncmp(buf, "QQ", 2)) {
315 fprintf(stdout, "BH quit command\n");
316 return 0;
317 }
318 if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) {
319 if (debug)
320 fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(),
321 PROGRAM, buf);
322 fprintf(stdout, "BH Invalid request\n");
323 continue;
324 }
325 if (strlen(buf) <= 3) {
326 if (debug)
327 fprintf(stderr, "%s| %s: Invalid negotiate request [%s]\n",
328 LogTime(), PROGRAM, buf);
329 fprintf(stdout, "BH Invalid negotiate request\n");
330 continue;
331 }
332 length = base64_decode_len(buf+3);
333 if (debug)
334 fprintf(stderr, "%s| %s: Decode '%s' (decoded length: %d).\n",
335 LogTime(), PROGRAM, buf + 3, (int) length);
336
337 if ((token = (char *)xmalloc(length)) == NULL) {
338 fprintf(stderr, "%s| %s: Error allocating memory for token\n", LogTime(), PROGRAM);
339 return 1;
340 }
341
342 length = base64_decode(token, length, buf+3);
343
344 if ((static_cast<size_t>(length) >= sizeof(ntlmProtocol) + 1) &&
345 (!memcmp(token, ntlmProtocol, sizeof ntlmProtocol))) {
346 free(token);
347 if (debug)
348 fprintf(stderr, "%s| %s: received type %d NTLM token\n",
349 LogTime(), PROGRAM, (int) *((unsigned char *) token +
350 sizeof ntlmProtocol));
351 fprintf(FDNIN, "%s\n",buf);
352 if (fgets(tbuff, sizeof(tbuff) - 1, FDNOUT) == NULL) {
353 if (ferror(FDNOUT)) {
354 fprintf(stderr,
355 "fgets() failed! dying..... errno=%d (%s)\n",
356 ferror(FDNOUT), strerror(ferror(FDNOUT)));
357 return 1;
358 }
359 fprintf(stderr, "%s| %s: Error reading NTLM helper response\n",
360 LogTime(), PROGRAM);
361 return 0;
362 }
363 /*
364 Need to translate NTLM reply to Negotiate reply
365 AF user => AF blob user
366 NA reason => NA blob reason
367 Set blob to '='
368 */
369 if (strlen(tbuff) >= 3 && (!strncmp(tbuff,"AF ",3) || !strncmp(tbuff,"NA ",3))) {
370 strncpy(buff,tbuff,3);
371 buff[3]='=';
372 for (unsigned int i=2; i<=strlen(tbuff); ++i)
373 buff[i+2] = tbuff[i];
374 } else {
375 strcpy(buff,tbuff);
376 }
377 } else {
378 free(token);
379 if (debug)
380 fprintf(stderr, "%s| %s: received Kerberos token\n",
381 LogTime(), PROGRAM);
382
383 fprintf(FDKIN, "%s\n",buf);
384 if (fgets(buff, sizeof(buff) - 1, FDKOUT) == NULL) {
385 if (ferror(FDKOUT)) {
386 fprintf(stderr,
387 "fgets() failed! dying..... errno=%d (%s)\n",
388 ferror(FDKOUT), strerror(ferror(FDKOUT)));
389 return 1;
390 }
391 fprintf(stderr, "%s| %s: Error reading Kerberos helper response\n",
392 LogTime(), PROGRAM);
393 return 0;
394 }
395 }
396 fprintf(stdout,"%s",buff);
397 if (debug)
398 fprintf(stderr, "%s| %s: Return '%s'\n",
399 LogTime(), PROGRAM, buff);
400 }
401
402 return 1;
403 }
404