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