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