Squid Web Cache master
Loading...
Searching...
No Matches
negotiate_kerberos_auth.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) 2007 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 * As a special exemption, M Moeller gives permission to link this program
31 * with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
32 * the resulting executable, without including the source code for
33 * the Libraries in the source distribution.
34 *
35 * -----------------------------------------------------------------------------
36 */
37
38#include "squid.h"
39#include "rfc1738.h"
40
41#if HAVE_GSSAPI
42
43#include "compat/unistd.h"
44#include "negotiate_kerberos.h"
45
46#if HAVE_SYS_STAT_H
47#include "sys/stat.h"
48#endif
49
50#if HAVE_KRB5_MEMORY_KEYTAB
51typedef struct _krb5_kt_list {
52 struct _krb5_kt_list *next;
53 krb5_keytab_entry *entry;
54} *krb5_kt_list;
55krb5_kt_list ktlist = nullptr;
56
57krb5_keytab memory_keytab;
58
59krb5_error_code krb5_free_kt_list(krb5_context context, krb5_kt_list kt_list);
60krb5_error_code krb5_write_keytab(krb5_context context,
61 krb5_kt_list kt_list,
62 char *name);
63krb5_error_code krb5_read_keytab(krb5_context context,
64 char *name,
65 krb5_kt_list *kt_list);
66#endif /* HAVE_KRB5_MEMORY_KEYTAB */
67
68int
69check_k5_err(krb5_context context, const char *function, krb5_error_code code)
70{
71
72 if (code && code != KRB5_KT_END) {
73 const char *errmsg;
74 errmsg = krb5_get_error_message(context, code);
75 debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, errmsg);
76 fprintf(stderr, "%s| %s: ERROR: %s: %s\n", LogTime(), PROGRAM, function, errmsg);
77#if HAVE_KRB5_FREE_ERROR_MESSAGE
78 krb5_free_error_message(context, errmsg);
79#elif HAVE_KRB5_FREE_ERROR_STRING
80 krb5_free_error_string(context, (char *)errmsg);
81#else
82 xfree(errmsg);
83#endif
84 }
85 return code;
86}
87
88char *
89gethost_name(void)
90{
91 /*
92 * char hostname[sysconf(_SC_HOST_NAME_MAX)];
93 */
94 char hostname[1024];
95 struct addrinfo *hres = nullptr, *hres_list;
96 int rc;
97
98 rc = xgethostname(hostname, sizeof(hostname)-1);
99 if (rc) {
100 debug((char *) "%s| %s: ERROR: resolving hostname '%s' failed\n", LogTime(), PROGRAM, hostname);
101 fprintf(stderr, "%s| %s: ERROR: resolving hostname '%s' failed\n",
102 LogTime(), PROGRAM, hostname);
103 return nullptr;
104 }
105 rc = getaddrinfo(hostname, nullptr, nullptr, &hres);
106 if (rc != 0 || hres == nullptr ) {
107 debug((char *) "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
108 LogTime(), PROGRAM, gai_strerror(rc));
109 fprintf(stderr,
110 "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
111 LogTime(), PROGRAM, gai_strerror(rc));
112 return nullptr;
113 }
114 hres_list = hres;
115 while (hres_list) {
116 hres_list = hres_list->ai_next;
117 }
118 rc = getnameinfo(hres->ai_addr, hres->ai_addrlen, hostname,
119 sizeof(hostname), nullptr, 0, 0);
120 if (rc != 0) {
121 debug((char *) "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
122 LogTime(), PROGRAM, gai_strerror(rc));
123 fprintf(stderr,
124 "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
125 LogTime(), PROGRAM, gai_strerror(rc));
126 freeaddrinfo(hres);
127 return nullptr;
128 }
129 freeaddrinfo(hres);
130 hostname[sizeof(hostname)-1] = '\0';
131 return (xstrdup(hostname));
132}
133
134int
135check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
136 const char *function, int log, int sout)
137{
138 if (GSS_ERROR(major_status)) {
139 OM_uint32 maj_stat, min_stat;
140 OM_uint32 msg_ctx = 0;
141 gss_buffer_desc status_string;
142 char buf[1024];
143 size_t len;
144
145 len = 0;
146 msg_ctx = 0;
147 do {
148 /* convert major status code (GSS-API error) to text */
149 maj_stat = gss_display_status(&min_stat, major_status,
150 GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
151 if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
152 if (sizeof(buf) > len + status_string.length + 1) {
153 snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
154 len += status_string.length;
155 }
156 } else
157 msg_ctx = 0;
158 gss_release_buffer(&min_stat, &status_string);
159 } while (msg_ctx);
160 if (sizeof(buf) > len + 2) {
161 snprintf(buf + len, (sizeof(buf) - len), "%s", ". ");
162 len += 2;
163 }
164 msg_ctx = 0;
165 do {
166 /* convert minor status code (underlying routine error) to text */
167 maj_stat = gss_display_status(&min_stat, minor_status,
168 GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
169 if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
170 if (sizeof(buf) > len + status_string.length) {
171 snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
172 len += status_string.length;
173 }
174 } else
175 msg_ctx = 0;
176 gss_release_buffer(&min_stat, &status_string);
177 } while (msg_ctx);
178 debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, buf);
179 if (sout)
180 fprintf(stdout, "BH %s failed: %s\n", function, buf);
181 if (log)
182 fprintf(stderr, "%s| %s: INFO: User not authenticated\n", LogTime(),
183 PROGRAM);
184 return (1);
185 }
186 return (0);
187}
188
189#if HAVE_KRB5_MEMORY_KEYTAB
190/*
191 * Free a kt_list
192 */
193krb5_error_code krb5_free_kt_list(krb5_context context, krb5_kt_list list)
194{
195 krb5_kt_list lp = list;
196
197 while (lp) {
198#if HAVE_LIBHEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY )
199 krb5_error_code retval = krb5_kt_free_entry(context, lp->entry);
200#else
201 krb5_error_code retval = krb5_free_keytab_entry_contents(context, lp->entry);
202#endif
203 safe_free(lp->entry);
204 if (check_k5_err(context, "krb5_kt_free_entry", retval))
205 return retval;
206 krb5_kt_list prev = lp;
207 lp = lp->next;
208 xfree(prev);
209 }
210 return 0;
211}
212/*
213 * Read in a keytab and append it to list. If list starts as NULL,
214 * allocate a new one if necessary.
215 */
216krb5_error_code krb5_read_keytab(krb5_context context, char *name, krb5_kt_list *list)
217{
218 krb5_kt_list lp = nullptr, tail = nullptr, back = nullptr;
219 krb5_keytab kt;
220 krb5_keytab_entry *entry;
221 krb5_kt_cursor cursor;
222 krb5_error_code retval = 0;
223
224 if (*list) {
225 /* point lp at the tail of the list */
226 for (lp = *list; lp->next; lp = lp->next);
227 back = lp;
228 }
229 retval = krb5_kt_resolve(context, name, &kt);
230 if (check_k5_err(context, "krb5_kt_resolve", retval))
231 return retval;
232 retval = krb5_kt_start_seq_get(context, kt, &cursor);
233 if (check_k5_err(context, "krb5_kt_start_seq_get", retval))
234 goto close_kt;
235 for (;;) {
236 entry = (krb5_keytab_entry *)xcalloc(1, sizeof (krb5_keytab_entry));
237 if (!entry) {
238 retval = ENOMEM;
239 debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
240 LogTime(), PROGRAM, strerror(retval));
241 fprintf(stderr, "%s| %s: ERROR: krb5_read_keytab: %s\n",
242 LogTime(), PROGRAM, strerror(retval));
243 break;
244 }
245 memset(entry, 0, sizeof (*entry));
246 retval = krb5_kt_next_entry(context, kt, entry, &cursor);
247 if (check_k5_err(context, "krb5_kt_next_entry", retval))
248 break;
249
250 if (!lp) { /* if list is empty, start one */
251 lp = (krb5_kt_list)xmalloc(sizeof (*lp));
252 if (!lp) {
253 retval = ENOMEM;
254 debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
255 LogTime(), PROGRAM, strerror(retval));
256 fprintf(stderr, "%s| %s: ERROR: krb5_read_keytab: %s\n",
257 LogTime(), PROGRAM, strerror(retval));
258 break;
259 }
260 } else {
261 lp->next = (krb5_kt_list)xmalloc(sizeof (*lp));
262 if (!lp->next) {
263 retval = ENOMEM;
264 debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
265 LogTime(), PROGRAM, strerror(retval));
266 fprintf(stderr, "%s| %s: ERROR: krb5_read_keytab: %s\n",
267 LogTime(), PROGRAM, strerror(retval));
268 break;
269 }
270 lp = lp->next;
271 }
272 if (!tail)
273 tail = lp;
274 lp->next = nullptr;
275 lp->entry = entry;
276 }
277 xfree(entry);
278 if (retval) {
279 if (retval == KRB5_KT_END)
280 retval = 0;
281 else {
282 krb5_free_kt_list(context, tail);
283 tail = nullptr;
284 if (back)
285 back->next = nullptr;
286 }
287 }
288 if (!*list)
289 *list = tail;
290 krb5_kt_end_seq_get(context, kt, &cursor);
291close_kt:
292 krb5_kt_close(context, kt);
293 return retval;
294}
295
296/*
297 * Takes a kt_list and writes it to the named keytab.
298 */
299krb5_error_code krb5_write_keytab(krb5_context context, krb5_kt_list list, char *name)
300{
301 char ktname[MAXPATHLEN+sizeof("MEMORY:")+1];
302 krb5_error_code retval = 0;
303
304 snprintf(ktname, sizeof(ktname), "%s", name);
305 retval = krb5_kt_resolve(context, ktname, &memory_keytab);
306 if (retval)
307 return retval;
308 for (krb5_kt_list lp = list; lp; lp = lp->next) {
309 retval = krb5_kt_add_entry(context, memory_keytab, lp->entry);
310 if (retval)
311 break;
312 }
313 /*
314 * krb5_kt_close(context, kt);
315 */
316 return retval;
317}
318#endif /* HAVE_KRB5_MEMORY_KEYTAB */
319
320int
321main(int argc, char *const argv[])
322{
323 char buf[MAX_AUTHTOKEN_LEN];
324 char *c, *p;
325 char *user = nullptr;
326 char *rfc_user = nullptr;
327#if HAVE_PAC_SUPPORT
328 char ad_groups[MAX_PAC_GROUP_SIZE];
329 char *ag=nullptr;
330 krb5_pac pac;
331#if HAVE_LIBHEIMDAL_KRB5
332 gss_buffer_desc data_set = GSS_C_EMPTY_BUFFER;
333#else
334 gss_buffer_desc type_id = GSS_C_EMPTY_BUFFER;
335#endif
336#endif
337 krb5_context context = nullptr;
338 krb5_error_code ret;
339 long length = 0;
340 static int err = 0;
341 int opt, log = 0, norealm = 0;
342 OM_uint32 ret_flags = 0, spnego_flag = 0;
343 char *service_name = (char *) "HTTP", *host_name = nullptr;
344 char *token = nullptr;
345 char *service_principal = nullptr;
346 char *keytab_name = nullptr;
347 char *keytab_name_env = nullptr;
348 char default_keytab[MAXPATHLEN] = {};
349#if HAVE_KRB5_MEMORY_KEYTAB
350 char *memory_keytab_name = nullptr;
351#endif
352 char *rcache_type = nullptr;
353 char *rcache_dir = nullptr;
354 OM_uint32 major_status, minor_status;
355 gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
356 gss_name_t client_name = GSS_C_NO_NAME;
357 gss_name_t server_name = GSS_C_NO_NAME;
358 gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
359 gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
360 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
361 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
362 const unsigned char *kerberosToken = nullptr;
363 const unsigned char *spnegoToken = nullptr;
364 size_t spnegoTokenLength = 0;
365
366 setbuf(stdout, nullptr);
367 setbuf(stdin, nullptr);
368
369 while (-1 != (opt = getopt(argc, argv, "dirs:k:c:t:"))) {
370 switch (opt) {
371 case 'd':
372 debug_enabled = 1;
373 break;
374 case 'i':
375 log = 1;
376 break;
377 case 'r':
378 norealm = 1;
379 break;
380 case 'k':
381#if HAVE_SYS_STAT_H
382 struct stat fstat;
383 char *ktp;
384#endif
385 if (optarg)
386 keytab_name = xstrdup(optarg);
387 else {
388 fprintf(stderr, "ERROR: keytab file not given\n");
389 exit(EXIT_FAILURE);
390 }
391 /*
392 * Some sanity checks
393 */
394#if HAVE_SYS_STAT_H
395 if ((ktp=strchr(keytab_name,':')))
396 ktp++;
397 else
398 ktp=keytab_name;
399 if (stat((const char*)ktp, &fstat)) {
400 if (ENOENT == errno)
401 fprintf(stderr, "ERROR: keytab file %s does not exist\n",keytab_name);
402 else
403 fprintf(stderr, "ERROR: Error %s during stat of keytab file %s\n",strerror(errno),keytab_name);
404 exit(EXIT_FAILURE);
405 } else if (!S_ISREG(fstat.st_mode)) {
406 fprintf(stderr, "ERROR: keytab file %s is not a file\n",keytab_name);
407 exit(EXIT_FAILURE);
408 }
409#endif
410#if HAVE_UNISTD_H
411 if (access(ktp, R_OK)) {
412 fprintf(stderr, "ERROR: keytab file %s is not accessible\n",keytab_name);
413 exit(EXIT_FAILURE);
414 }
415#endif
416 break;
417 case 'c':
418#if HAVE_SYS_STAT_H
419 struct stat dstat;
420#endif
421 if (optarg)
422 rcache_dir = xstrdup(optarg);
423 else {
424 fprintf(stderr, "ERROR: replay cache directory not given\n");
425 exit(EXIT_FAILURE);
426 }
427 /*
428 * Some sanity checks
429 */
430#if HAVE_SYS_STAT_H
431 if (stat((const char*)rcache_dir, &dstat)) {
432 if (ENOENT == errno)
433 fprintf(stderr, "ERROR: replay cache directory %s does not exist\n",rcache_dir);
434 else
435 fprintf(stderr, "ERROR: Error %s during stat of replay cache directory %s\n",strerror(errno),rcache_dir);
436 exit(EXIT_FAILURE);
437 } else if (!S_ISDIR(dstat.st_mode)) {
438 fprintf(stderr, "ERROR: replay cache directory %s is not a directory\n",rcache_dir);
439 exit(EXIT_FAILURE);
440 }
441#endif
442#if HAVE_UNISTD_H
443 if (access(rcache_dir, W_OK)) {
444 fprintf(stderr, "ERROR: replay cache directory %s is not accessible\n",rcache_dir);
445 exit(EXIT_FAILURE);
446 }
447#endif
448 break;
449 case 't':
450 if (optarg)
451 rcache_type = xstrdup(optarg);
452 else {
453 fprintf(stderr, "ERROR: replay cache type not given\n");
454 exit(EXIT_FAILURE);
455 }
456 break;
457 case 's':
458 if (optarg)
459 service_principal = xstrdup(optarg);
460 else {
461 fprintf(stderr, "ERROR: service principal not given\n");
462 exit(EXIT_FAILURE);
463 }
464 break;
465 default:
466 fprintf(stderr, "Usage: \n");
467 fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-k keytab] [-c rcdir] [-t rctype]\n");
468 fprintf(stderr, "-d full debug\n");
469 fprintf(stderr, "-i informational messages\n");
470 fprintf(stderr, "-r remove realm from username\n");
471 fprintf(stderr, "-s service principal name\n");
472 fprintf(stderr, "-k keytab name\n");
473 fprintf(stderr, "-c replay cache directory\n");
474 fprintf(stderr, "-t replay cache type\n");
475 fprintf(stderr,
476 "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
477 fprintf(stderr, "default SPN is HTTP/fqdn@DEFAULT_REALM\n");
478 exit(EXIT_SUCCESS);
479 }
480 }
481
482 debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, SQUID_KERB_AUTH_VERSION);
483 if (service_principal && strcasecmp(service_principal, "GSS_C_NO_NAME")) {
484 if (!strstr(service_principal,"HTTP/")) {
485 debug((char *) "%s| %s: WARN: service_principal %s does not start with HTTP/\n",
486 LogTime(), PROGRAM, service_principal);
487 }
488 service.value = service_principal;
489 service.length = strlen((char *) service.value);
490 } else {
491 host_name = gethost_name();
492 if (!host_name) {
493 fprintf(stderr,
494 "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
495 LogTime(), PROGRAM);
496 fprintf(stdout, "BH hostname error\n");
497 exit(EXIT_FAILURE);
498 }
499 service.value = xmalloc(strlen(service_name) + strlen(host_name) + 2);
500 snprintf((char *) service.value, strlen(service_name) + strlen(host_name) + 2,
501 "%s@%s", service_name, host_name);
502 service.length = strlen((char *) service.value);
503 xfree(host_name);
504 }
505
506 if (rcache_type) {
507 (void)setenv("KRB5RCACHETYPE", rcache_type, 1);
508 debug((char *) "%s| %s: INFO: Setting replay cache type to %s\n",
509 LogTime(), PROGRAM, rcache_type);
510 }
511
512 if (rcache_dir) {
513 (void)setenv("KRB5RCACHEDIR", rcache_dir, 1);
514 debug((char *) "%s| %s: INFO: Setting replay cache directory to %s\n",
515 LogTime(), PROGRAM, rcache_dir);
516 }
517
518 if (keytab_name) {
519 (void)setenv("KRB5_KTNAME", keytab_name, 1);
520 } else {
521 keytab_name_env = getenv("KRB5_KTNAME");
522 if (!keytab_name_env) {
523 ret = krb5_init_context(&context);
524 if (!check_k5_err(context, "krb5_init_context", ret)) {
525 krb5_kt_default_name(context, default_keytab, MAXPATHLEN);
526 }
527 keytab_name = xstrdup(default_keytab);
528 krb5_free_context(context);
529 } else
530 keytab_name = xstrdup(keytab_name_env);
531 }
532 debug((char *) "%s| %s: INFO: Setting keytab to %s\n", LogTime(), PROGRAM, keytab_name);
533#if HAVE_KRB5_MEMORY_KEYTAB
534 ret = krb5_init_context(&context);
535 if (!check_k5_err(context, "krb5_init_context", ret)) {
536 memory_keytab_name = (char *)xmalloc(strlen("MEMORY:negotiate_kerberos_auth_")+16);
537 snprintf(memory_keytab_name, strlen("MEMORY:negotiate_kerberos_auth_")+16,
538 "MEMORY:negotiate_kerberos_auth_%d", (unsigned int) getpid());
539 ret = krb5_read_keytab(context, keytab_name, &ktlist);
540 if (check_k5_err(context, "krb5_read_keytab", ret)) {
541 debug((char *) "%s| %s: ERROR: Reading keytab %s into list failed\n",
542 LogTime(), PROGRAM, keytab_name);
543 } else {
544 ret = krb5_write_keytab(context, ktlist, memory_keytab_name);
545 if (check_k5_err(context, "krb5_write_keytab", ret)) {
546 debug((char *) "%s| %s: ERROR: Writing list into keytab %s\n",
547 LogTime(), PROGRAM, memory_keytab_name);
548 } else {
549 (void)setenv("KRB5_KTNAME", memory_keytab_name, 1);
550 xfree(keytab_name);
551 keytab_name = xstrdup(memory_keytab_name);
552 debug((char *) "%s| %s: INFO: Changed keytab to %s\n",
553 LogTime(), PROGRAM, memory_keytab_name);
554 }
555 }
556 ret = krb5_free_kt_list(context,ktlist);
557 if (check_k5_err(context, "krb5_free_kt_list", ret)) {
558 debug((char *) "%s| %s: ERROR: Freeing list failed\n",
559 LogTime(), PROGRAM);
560 }
561 }
562 krb5_free_context(context);
563#endif
564#ifdef HAVE_HEIMDAL_KERBEROS
565 gsskrb5_register_acceptor_identity(keytab_name);
566#endif
567 while (1) {
568 if (fgets(buf, sizeof(buf) - 1, stdin) == nullptr) {
569 if (ferror(stdin)) {
570 debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
571 LogTime(), PROGRAM, ferror(stdin),
572 strerror(ferror(stdin)));
573
574 fprintf(stdout, "BH input error\n");
575 exit(EXIT_FAILURE); /* BIIG buffer */
576 }
577 fprintf(stdout, "BH input error\n");
578 exit(EXIT_SUCCESS);
579 }
580 c = (char *) memchr(buf, '\n', sizeof(buf) - 1);
581 if (c) {
582 *c = '\0';
583 length = c - buf;
584 } else {
585 err = 1;
586 }
587 if (err) {
588 debug((char *) "%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM);
589 fprintf(stdout, "BH Oversized message\n");
590 err = 0;
591 continue;
592 }
593 debug((char *) "%s| %s: DEBUG: Got '%s' from squid (length: %ld).\n", LogTime(), PROGRAM, buf, length);
594
595 if (buf[0] == '\0') {
596 debug((char *) "%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM);
597 fprintf(stdout, "BH Invalid request\n");
598 continue;
599 }
600 if (strlen(buf) < 2) {
601 debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
602 fprintf(stdout, "BH Invalid request\n");
603 continue;
604 }
605 if (!strncmp(buf, "QQ", 2)) {
606 gss_release_buffer(&minor_status, &input_token);
607 gss_release_buffer(&minor_status, &output_token);
608 gss_release_buffer(&minor_status, &service);
609 gss_release_cred(&minor_status, &server_creds);
610 if (server_name)
611 gss_release_name(&minor_status, &server_name);
612 if (client_name)
613 gss_release_name(&minor_status, &client_name);
614 if (gss_context != GSS_C_NO_CONTEXT)
615 gss_delete_sec_context(&minor_status, &gss_context, nullptr);
616 if (kerberosToken) {
617 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
618 if (!spnego_flag)
619 xfree(kerberosToken);
620 }
621 if (spnego_flag) {
622 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
623 xfree(spnegoToken);
624 }
625 xfree(token);
626 xfree(rcache_type);
627 xfree(rcache_dir);
628 xfree(keytab_name);
629#if HAVE_KRB5_MEMORY_KEYTAB
630 krb5_kt_close(context, memory_keytab);
631 xfree(memory_keytab_name);
632#endif
633 xfree(rfc_user);
634 fprintf(stdout, "BH quit command\n");
635 exit(EXIT_SUCCESS);
636 }
637 if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) {
638 debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
639 fprintf(stdout, "BH Invalid request\n");
640 continue;
641 }
642 if (!strncmp(buf, "YR", 2)) {
643 if (gss_context != GSS_C_NO_CONTEXT)
644 gss_delete_sec_context(&minor_status, &gss_context, nullptr);
645 gss_context = GSS_C_NO_CONTEXT;
646 }
647 if (strlen(buf) <= 3) {
648 debug((char *) "%s| %s: ERROR: Invalid negotiate request [%s]\n", LogTime(), PROGRAM, buf);
649 fprintf(stdout, "BH Invalid negotiate request\n");
650 continue;
651 }
652 const char *b64Token = buf+3;
653 const size_t srcLen = strlen(buf+3);
654 input_token.length = BASE64_DECODE_LENGTH(srcLen);
655 debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length estimate: %d).\n",
656 LogTime(), PROGRAM, b64Token, (int) input_token.length);
657 input_token.value = xmalloc(input_token.length);
658
659 struct base64_decode_ctx ctx;
660 base64_decode_init(&ctx);
661 size_t dstLen = 0;
662 if (!base64_decode_update(&ctx, &dstLen, static_cast<uint8_t*>(input_token.value), srcLen, b64Token) ||
663 !base64_decode_final(&ctx)) {
664 debug((char *) "%s| %s: ERROR: Invalid base64 token [%s]\n", LogTime(), PROGRAM, b64Token);
665 fprintf(stdout, "BH Invalid negotiate request token\n");
666 continue;
667 }
668 input_token.length = dstLen;
669
670 if ((input_token.length >= sizeof ntlmProtocol + 1) &&
671 (!memcmp(input_token.value, ntlmProtocol, sizeof ntlmProtocol))) {
672 debug((char *) "%s| %s: WARNING: received type %d NTLM token\n",
673 LogTime(), PROGRAM,
674 (int) *((unsigned char *) input_token.value +
675 sizeof ntlmProtocol));
676 fprintf(stdout, "BH received type %d NTLM token\n",
677 (int) *((unsigned char *) input_token.value +
678 sizeof ntlmProtocol));
679 goto cleanup;
680 }
681 if (service_principal) {
682 if (strcasecmp(service_principal, "GSS_C_NO_NAME")) {
683 major_status = gss_import_name(&minor_status, &service,
684 (gss_OID) GSS_C_NULL_OID, &server_name);
685
686 } else {
687 server_name = GSS_C_NO_NAME;
688 major_status = GSS_S_COMPLETE;
689 minor_status = 0;
690 }
691 } else {
692 major_status = gss_import_name(&minor_status, &service,
693 gss_nt_service_name, &server_name);
694 }
695
696 if (check_gss_err(major_status, minor_status, "gss_import_name()", log, 1))
697 goto cleanup;
698
699 major_status =
700 gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
701 GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, nullptr, nullptr);
702 if (check_gss_err(major_status, minor_status, "gss_acquire_cred()", log, 1))
703 goto cleanup;
704
705 major_status = gss_accept_sec_context(&minor_status,
706 &gss_context,
707 server_creds,
708 &input_token,
709 GSS_C_NO_CHANNEL_BINDINGS,
710 &client_name, nullptr, &output_token, &ret_flags, nullptr, nullptr);
711
712 if (output_token.length) {
713 spnegoToken = (const unsigned char *) output_token.value;
714 spnegoTokenLength = output_token.length;
715 token = (char *) xmalloc((size_t)base64_encode_len(spnegoTokenLength));
716 if (token == nullptr) {
717 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
718 fprintf(stdout, "BH Not enough memory\n");
719 goto cleanup;
720 }
721 struct base64_encode_ctx tokCtx;
722 base64_encode_init(&tokCtx);
723 size_t blen = base64_encode_update(&tokCtx, token, spnegoTokenLength, reinterpret_cast<const uint8_t*>(spnegoToken));
724 blen += base64_encode_final(&tokCtx, token+blen);
725 token[blen] = '\0';
726
727 if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log, 1))
728 goto cleanup;
729 if (major_status & GSS_S_CONTINUE_NEEDED) {
730 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
731 fprintf(stdout, "TT token=%s\n", token);
732 goto cleanup;
733 }
734 gss_release_buffer(&minor_status, &output_token);
735 major_status =
736 gss_display_name(&minor_status, client_name, &output_token,
737 nullptr);
738
739 if (check_gss_err(major_status, minor_status, "gss_display_name()", log, 1))
740 goto cleanup;
741 user = (char *) xmalloc(output_token.length + 1);
742 if (user == nullptr) {
743 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
744 fprintf(stdout, "BH Not enough memory\n");
745 goto cleanup;
746 }
747 memcpy(user, output_token.value, output_token.length);
748 user[output_token.length] = '\0';
749 if (norealm && (p = strchr(user, '@')) != nullptr) {
750 *p = '\0';
751 }
752
753#if HAVE_PAC_SUPPORT
754 ret = krb5_init_context(&context);
755 if (!check_k5_err(context, "krb5_init_context", ret)) {
756#if HAVE_LIBHEIMDAL_KRB5
757#define ADWIN2KPAC 128
758 major_status = gsskrb5_extract_authz_data_from_sec_context(&minor_status,
759 gss_context, ADWIN2KPAC, &data_set);
760 if (!check_gss_err(major_status, minor_status,
761 "gsskrb5_extract_authz_data_from_sec_context()", log, 0)) {
762 ret = krb5_pac_parse(context, data_set.value, data_set.length, &pac);
763 gss_release_buffer(&minor_status, &data_set);
764 if (!check_k5_err(context, "krb5_pac_parse", ret)) {
765 ag = get_ad_groups((char *)&ad_groups, context, pac);
766 krb5_pac_free(context, pac);
767 }
768 krb5_free_context(context);
769 }
770#else
771 type_id.value = (void *)"mspac";
772 type_id.length = strlen((char *)type_id.value);
773#define KRB5PACLOGONINFO 1
774 major_status = gss_map_name_to_any(&minor_status, client_name, KRB5PACLOGONINFO, &type_id, (gss_any_t *)&pac);
775 if (!check_gss_err(major_status, minor_status, "gss_map_name_to_any()", log, 0)) {
776 ag = get_ad_groups((char *)&ad_groups,context, pac);
777 }
778 (void)gss_release_any_name_mapping(&minor_status, client_name, &type_id, (gss_any_t *)&pac);
779 krb5_free_context(context);
780#endif
781 }
782 if (ag) {
783 debug((char *) "%s| %s: DEBUG: Groups %s\n", LogTime(), PROGRAM, ag);
784 }
785#endif
786 rfc_user = rfc1738_escape(user);
787#if HAVE_PAC_SUPPORT
788 fprintf(stdout, "OK token=%s user=%s %s\n", token, rfc_user, ag?ag:"group=");
789#else
790 fprintf(stdout, "OK token=%s user=%s\n", token, rfc_user);
791#endif
792 debug((char *) "%s| %s: DEBUG: OK token=%s user=%s\n", LogTime(), PROGRAM, token, rfc_user);
793 if (log)
794 fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
795 PROGRAM, rfc_user);
796 goto cleanup;
797 } else {
798 if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log, 1))
799 goto cleanup;
800 if (major_status & GSS_S_CONTINUE_NEEDED) {
801 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
802 // XXX: where to get the server token for delivery to client? token is nullptr here.
803 fprintf(stdout, "ERR\n");
804 goto cleanup;
805 }
806 gss_release_buffer(&minor_status, &output_token);
807 major_status =
808 gss_display_name(&minor_status, client_name, &output_token,
809 nullptr);
810
811 if (check_gss_err(major_status, minor_status, "gss_display_name()", log, 1))
812 goto cleanup;
813 /*
814 * Return dummy token AA. May need an extra return tag then AF
815 */
816 user = (char *) xmalloc(output_token.length + 1);
817 if (user == nullptr) {
818 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
819 fprintf(stdout, "BH Not enough memory\n");
820 goto cleanup;
821 }
822 memcpy(user, output_token.value, output_token.length);
823 user[output_token.length] = '\0';
824 if (norealm && (p = strchr(user, '@')) != nullptr) {
825 *p = '\0';
826 }
827 rfc_user = rfc1738_escape(user);
828#if HAVE_PAC_SUPPORT
829 fprintf(stdout, "OK token=%s user=%s %s\n", "AA==", rfc_user, ag?ag:"group=");
830#else
831 fprintf(stdout, "OK token=%s user=%s\n", "AA==", rfc_user);
832#endif
833 debug((char *) "%s| %s: DEBUG: OK token=%s user=%s\n", LogTime(), PROGRAM, "AA==", rfc_user);
834 if (log)
835 fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
836 PROGRAM, rfc_user);
837 }
838cleanup:
839 gss_release_buffer(&minor_status, &input_token);
840 gss_release_buffer(&minor_status, &output_token);
841 gss_release_cred(&minor_status, &server_creds);
842 if (server_name)
843 gss_release_name(&minor_status, &server_name);
844 if (client_name)
845 gss_release_name(&minor_status, &client_name);
846 if (kerberosToken) {
847 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
848 if (!spnego_flag)
849 safe_free(kerberosToken);
850 }
851 if (spnego_flag) {
852 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
853 safe_free(spnegoToken);
854 }
855 safe_free(token);
856 safe_free(user);
857 continue;
858 }
859 return EXIT_SUCCESS;
860}
861#else
862#include <cstdlib>
863#ifndef MAX_AUTHTOKEN_LEN
864#define MAX_AUTHTOKEN_LEN 65535
865#endif
866int
867main(int argc, char *const argv[])
868{
869 setbuf(stdout, nullptr);
870 setbuf(stdin, nullptr);
871 char buf[MAX_AUTHTOKEN_LEN];
872 while (1) {
873 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
874 fprintf(stdout, "BH input error\n");
875 exit(EXIT_SUCCESS);
876 }
877 fprintf(stdout, "BH Kerberos authentication not supported\n");
878 }
879 return EXIT_SUCCESS;
880}
881#endif /* HAVE_GSSAPI */
882
void log(char *format,...)
#define PROGRAM
Definition support.h:169
const char * LogTime(void)
void base64_encode_init(struct base64_encode_ctx *ctx)
Definition base64.c:232
size_t base64_encode_update(struct base64_encode_ctx *ctx, char *dst, size_t length, const uint8_t *src)
Definition base64.c:265
void base64_decode_init(struct base64_decode_ctx *ctx)
Definition base64.c:54
size_t base64_encode_final(struct base64_encode_ctx *ctx, char *dst)
Definition base64.c:308
#define base64_encode_len(length)
Definition base64.h:169
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
int base64_decode_final(struct base64_decode_ctx *ctx)
Definition base64.c:159
#define BASE64_DECODE_LENGTH(length)
Definition base64.h:120
int debug_enabled
Definition debug.cc:13
void debug(const char *format,...)
Definition debug.cc:19
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition getopt.c:62
char * optarg
Definition getopt.c:51
int main()
#define gss_nt_service_name
char * gethost_name(void)
#define SQUID_KERB_AUTH_VERSION
int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status, const char *function, int log, int sout)
int check_k5_err(krb5_context context, const char *msg, krb5_error_code code)
static const unsigned char ntlmProtocol[]
#define MAX_AUTHTOKEN_LEN
#define xfree
#define xstrdup
#define xmalloc
#define rfc1738_escape(x)
Definition rfc1738.h:52
#define MAXPATHLEN
Definition stdio.h:62
char * strerror(int ern)
Definition strerror.c:22
SBuf service_name(APP_SHORTNAME)
#define NULL
Definition types.h:145
int xgethostname(char *name, size_t nameLength)
POSIX gethostname(2) equivalent.
Definition unistd.h:49
void * xcalloc(size_t n, size_t sz)
Definition xalloc.cc:71
#define safe_free(x)
Definition xalloc.h:73