]> git.ipfire.org Git - thirdparty/squid.git/blame - helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc
Boilerplate: update copyright blurbs on Squid helpers
[thirdparty/squid.git] / helpers / negotiate_auth / kerberos / negotiate_kerberos_auth.cc
CommitLineData
ca02e0ec
AJ
1/*
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
3e5d7cdf 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 *
ba4fe07c
AJ
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 *
3e5d7cdf 35 * -----------------------------------------------------------------------------
36 */
ca02e0ec 37
f7f3304a 38#include "squid.h"
27bc2077
AJ
39#include "compat/getaddrinfo.h"
40#include "compat/getnameinfo.h"
602d9612 41#include "rfc1738.h"
d1f95b42 42
3ad12bda 43#if HAVE_GSSAPI
e9658d82 44
4ebcf1ce 45#include "negotiate_kerberos.h"
3e5d7cdf 46
3ad12bda
AJ
47char *
48gethost_name(void)
26ac0430 49{
e6aa9181 50 /*
b1218840
AJ
51 * char hostname[sysconf(_SC_HOST_NAME_MAX)];
52 */
3d62cc61 53 char hostname[1024];
3ad12bda
AJ
54 struct addrinfo *hres = NULL, *hres_list;
55 int rc, count;
26ac0430 56
bc30ad64 57 rc = gethostname(hostname, sizeof(hostname)-1);
26ac0430 58 if (rc) {
2e881a6f
A
59 fprintf(stderr, "%s| %s: ERROR: resolving hostname '%s' failed\n",
60 LogTime(), PROGRAM, hostname);
61 return NULL;
26ac0430 62 }
27bc2077 63 rc = getaddrinfo(hostname, NULL, NULL, &hres);
26ac0430 64 if (rc != 0) {
2e881a6f
A
65 fprintf(stderr,
66 "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
67 LogTime(), PROGRAM, gai_strerror(rc));
68 return NULL;
26ac0430 69 }
3ad12bda
AJ
70 hres_list = hres;
71 count = 0;
26ac0430 72 while (hres_list) {
755494da 73 ++count;
2e881a6f 74 hres_list = hres_list->ai_next;
26ac0430 75 }
27bc2077 76 rc = getnameinfo(hres->ai_addr, hres->ai_addrlen, hostname,
2e881a6f 77 sizeof(hostname), NULL, 0, 0);
26ac0430 78 if (rc != 0) {
2e881a6f
A
79 fprintf(stderr,
80 "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
81 LogTime(), PROGRAM, gai_strerror(rc));
82 freeaddrinfo(hres);
83 return NULL;
3e5d7cdf 84 }
27bc2077 85 freeaddrinfo(hres);
bc30ad64 86 hostname[sizeof(hostname)-1] = '\0';
3ad12bda 87 return (xstrdup(hostname));
3e5d7cdf 88}
89
3ad12bda
AJ
90int
91check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
4ebcf1ce 92 const char *function, int log, int sout)
26ac0430
AJ
93{
94 if (GSS_ERROR(major_status)) {
2e881a6f
A
95 OM_uint32 maj_stat, min_stat;
96 OM_uint32 msg_ctx = 0;
97 gss_buffer_desc status_string;
98 char buf[1024];
99 size_t len;
100
101 len = 0;
102 msg_ctx = 0;
08885c7f 103 do {
2e881a6f
A
104 /* convert major status code (GSS-API error) to text */
105 maj_stat = gss_display_status(&min_stat, major_status,
106 GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
08885c7f 107 if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
2e881a6f
A
108 if (sizeof(buf) > len + status_string.length + 1) {
109 snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
110 len += status_string.length;
111 }
08885c7f
MM
112 } else
113 msg_ctx = 0;
2e881a6f 114 gss_release_buffer(&min_stat, &status_string);
08885c7f 115 } while (msg_ctx);
2e881a6f
A
116 if (sizeof(buf) > len + 2) {
117 snprintf(buf + len, (sizeof(buf) - len), "%s", ". ");
118 len += 2;
119 }
120 msg_ctx = 0;
08885c7f 121 do {
2e881a6f
A
122 /* convert minor status code (underlying routine error) to text */
123 maj_stat = gss_display_status(&min_stat, minor_status,
124 GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
08885c7f 125 if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
2e881a6f
A
126 if (sizeof(buf) > len + status_string.length) {
127 snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
128 len += status_string.length;
129 }
08885c7f
MM
130 } else
131 msg_ctx = 0;
2e881a6f 132 gss_release_buffer(&min_stat, &status_string);
08885c7f 133 } while (msg_ctx);
2e881a6f 134 debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, buf);
4ebcf1ce
MM
135 if (sout)
136 fprintf(stdout, "BH %s failed: %s\n", function, buf);
2e881a6f
A
137 if (log)
138 fprintf(stderr, "%s| %s: INFO: User not authenticated\n", LogTime(),
139 PROGRAM);
140 return (1);
3e5d7cdf 141 }
3ad12bda 142 return (0);
3e5d7cdf 143}
144
3ad12bda
AJ
145int
146main(int argc, char *const argv[])
3e5d7cdf 147{
26ac0430 148 char buf[MAX_AUTHTOKEN_LEN];
3ad12bda
AJ
149 char *c, *p;
150 char *user = NULL;
4ebcf1ce
MM
151 char *rfc_user = NULL;
152#if HAVE_PAC_SUPPORT
153 char ad_groups[MAX_PAC_GROUP_SIZE];
154 char *ag=NULL;
155 krb5_context context = NULL;
156 krb5_error_code ret;
157 krb5_pac pac;
1a22a39e 158#if USE_HEIMDAL_KRB5
4ebcf1ce
MM
159 gss_buffer_desc data_set = GSS_C_EMPTY_BUFFER;
160#else
161 gss_buffer_desc type_id = GSS_C_EMPTY_BUFFER;
162#endif
163#endif
164 long length = 0;
3ad12bda 165 static int err = 0;
e673ba3a 166 int opt, log = 0, norealm = 0;
3ad12bda
AJ
167 OM_uint32 ret_flags = 0, spnego_flag = 0;
168 char *service_name = (char *) "HTTP", *host_name = NULL;
26ac0430
AJ
169 char *token = NULL;
170 char *service_principal = NULL;
171 OM_uint32 major_status, minor_status;
3ad12bda
AJ
172 gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
173 gss_name_t client_name = GSS_C_NO_NAME;
174 gss_name_t server_name = GSS_C_NO_NAME;
175 gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
176 gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
177 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
178 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
179 const unsigned char *kerberosToken = NULL;
3ad12bda
AJ
180 const unsigned char *spnegoToken = NULL;
181 size_t spnegoTokenLength = 0;
26ac0430 182
3ad12bda
AJ
183 setbuf(stdout, NULL);
184 setbuf(stdin, NULL);
26ac0430 185
6989c6dc 186 while (-1 != (opt = getopt(argc, argv, "dirs:h"))) {
2e881a6f
A
187 switch (opt) {
188 case 'd':
189 debug_enabled = 1;
190 break;
191 case 'i':
192 log = 1;
193 break;
194 case 'r':
195 norealm = 1;
196 break;
197 case 's':
198 service_principal = xstrdup(optarg);
199 break;
200 case 'h':
201 fprintf(stderr, "Usage: \n");
202 fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-h]\n");
203 fprintf(stderr, "-d full debug\n");
204 fprintf(stderr, "-i informational messages\n");
205 fprintf(stderr, "-r remove realm from username\n");
206 fprintf(stderr, "-s service principal name\n");
207 fprintf(stderr, "-h help\n");
208 fprintf(stderr,
209 "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
210 fprintf(stderr, "default SPN is HTTP/fqdn@DEFAULT_REALM\n");
211 exit(0);
212 default:
213 fprintf(stderr, "%s| %s: WARNING: unknown option: -%c.\n", LogTime(),
214 PROGRAM, opt);
215 }
3e5d7cdf 216 }
217
b1218840 218 debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, SQUID_KERB_AUTH_VERSION);
3ad12bda 219 if (service_principal && strcasecmp(service_principal, "GSS_C_NO_NAME")) {
2e881a6f
A
220 service.value = service_principal;
221 service.length = strlen((char *) service.value);
26ac0430 222 } else {
2e881a6f
A
223 host_name = gethost_name();
224 if (!host_name) {
225 fprintf(stderr,
226 "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
227 LogTime(), PROGRAM);
228 fprintf(stdout, "BH hostname error\n");
229 exit(-1);
230 }
231 service.value = xmalloc(strlen(service_name) + strlen(host_name) + 2);
232 snprintf((char *) service.value, strlen(service_name) + strlen(host_name) + 2,
233 "%s@%s", service_name, host_name);
234 service.length = strlen((char *) service.value);
4ebcf1ce 235 xfree(host_name);
3e5d7cdf 236 }
237
26ac0430 238 while (1) {
2e881a6f
A
239 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
240 if (ferror(stdin)) {
241 debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
242 LogTime(), PROGRAM, ferror(stdin),
243 strerror(ferror(stdin)));
244
245 fprintf(stdout, "BH input error\n");
246 exit(1); /* BIIG buffer */
247 }
248 fprintf(stdout, "BH input error\n");
249 exit(0);
250 }
251 c = (char *) memchr(buf, '\n', sizeof(buf) - 1);
252 if (c) {
253 *c = '\0';
254 length = c - buf;
255 } else {
256 err = 1;
257 }
258 if (err) {
259 debug((char *) "%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM);
260 fprintf(stdout, "BH Oversized message\n");
261 err = 0;
262 continue;
263 }
4ebcf1ce 264 debug((char *) "%s| %s: DEBUG: Got '%s' from squid (length: %ld).\n", LogTime(), PROGRAM, buf, length);
2e881a6f
A
265
266 if (buf[0] == '\0') {
267 debug((char *) "%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM);
268 fprintf(stdout, "BH Invalid request\n");
269 continue;
270 }
271 if (strlen(buf) < 2) {
272 debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
273 fprintf(stdout, "BH Invalid request\n");
274 continue;
275 }
276 if (!strncmp(buf, "QQ", 2)) {
277 gss_release_buffer(&minor_status, &input_token);
278 gss_release_buffer(&minor_status, &output_token);
279 gss_release_buffer(&minor_status, &service);
280 gss_release_cred(&minor_status, &server_creds);
281 if (server_name)
282 gss_release_name(&minor_status, &server_name);
283 if (client_name)
284 gss_release_name(&minor_status, &client_name);
285 if (gss_context != GSS_C_NO_CONTEXT)
286 gss_delete_sec_context(&minor_status, &gss_context, NULL);
287 if (kerberosToken) {
288 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
289 if (!spnego_flag)
4ebcf1ce 290 xfree(kerberosToken);
2e881a6f
A
291 }
292 if (spnego_flag) {
293 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
4ebcf1ce 294 xfree(spnegoToken);
2e881a6f 295 }
4ebcf1ce 296 xfree(token);
2e881a6f
A
297 fprintf(stdout, "BH quit command\n");
298 exit(0);
299 }
300 if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) {
301 debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
302 fprintf(stdout, "BH Invalid request\n");
303 continue;
304 }
305 if (!strncmp(buf, "YR", 2)) {
306 if (gss_context != GSS_C_NO_CONTEXT)
307 gss_delete_sec_context(&minor_status, &gss_context, NULL);
308 gss_context = GSS_C_NO_CONTEXT;
309 }
310 if (strlen(buf) <= 3) {
311 debug((char *) "%s| %s: ERROR: Invalid negotiate request [%s]\n", LogTime(), PROGRAM, buf);
312 fprintf(stdout, "BH Invalid negotiate request\n");
313 continue;
314 }
4ebcf1ce 315 input_token.length = (size_t)base64_decode_len(buf+3);
2e881a6f
A
316 debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length: %d).\n",
317 LogTime(), PROGRAM, buf + 3, (int) input_token.length);
318 input_token.value = xmalloc(input_token.length);
319
4ebcf1ce 320 input_token.length = (size_t)base64_decode((char *) input_token.value, (unsigned int)input_token.length, buf+3);
2e881a6f
A
321
322 if ((input_token.length >= sizeof ntlmProtocol + 1) &&
323 (!memcmp(input_token.value, ntlmProtocol, sizeof ntlmProtocol))) {
324 debug((char *) "%s| %s: WARNING: received type %d NTLM token\n",
325 LogTime(), PROGRAM,
326 (int) *((unsigned char *) input_token.value +
327 sizeof ntlmProtocol));
328 fprintf(stdout, "BH received type %d NTLM token\n",
329 (int) *((unsigned char *) input_token.value +
330 sizeof ntlmProtocol));
331 goto cleanup;
332 }
333 if (service_principal) {
334 if (strcasecmp(service_principal, "GSS_C_NO_NAME")) {
335 major_status = gss_import_name(&minor_status, &service,
336 (gss_OID) GSS_C_NULL_OID, &server_name);
337
338 } else {
339 server_name = GSS_C_NO_NAME;
340 major_status = GSS_S_COMPLETE;
4ebcf1ce 341 minor_status = 0;
2e881a6f
A
342 }
343 } else {
344 major_status = gss_import_name(&minor_status, &service,
345 gss_nt_service_name, &server_name);
346 }
347
4ebcf1ce 348 if (check_gss_err(major_status, minor_status, "gss_import_name()", log, 1))
2e881a6f
A
349 goto cleanup;
350
351 major_status =
352 gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
353 GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL);
4ebcf1ce 354 if (check_gss_err(major_status, minor_status, "gss_acquire_cred()", log, 1))
2e881a6f
A
355 goto cleanup;
356
357 major_status = gss_accept_sec_context(&minor_status,
358 &gss_context,
359 server_creds,
360 &input_token,
361 GSS_C_NO_CHANNEL_BINDINGS,
362 &client_name, NULL, &output_token, &ret_flags, NULL, NULL);
363
2e881a6f
A
364 if (output_token.length) {
365 spnegoToken = (const unsigned char *) output_token.value;
366 spnegoTokenLength = output_token.length;
4ebcf1ce 367 token = (char *) xmalloc((size_t)base64_encode_len((int)spnegoTokenLength));
2e881a6f
A
368 if (token == NULL) {
369 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
370 fprintf(stdout, "BH Not enough memory\n");
371 goto cleanup;
372 }
4ebcf1ce
MM
373 base64_encode_str(token, base64_encode_len((int)spnegoTokenLength),
374 (const char *) spnegoToken, (int)spnegoTokenLength);
2e881a6f 375
4ebcf1ce 376 if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log, 1))
2e881a6f
A
377 goto cleanup;
378 if (major_status & GSS_S_CONTINUE_NEEDED) {
379 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
380 fprintf(stdout, "TT %s\n", token);
381 goto cleanup;
382 }
383 gss_release_buffer(&minor_status, &output_token);
384 major_status =
385 gss_display_name(&minor_status, client_name, &output_token,
386 NULL);
387
4ebcf1ce 388 if (check_gss_err(major_status, minor_status, "gss_display_name()", log, 1))
2e881a6f
A
389 goto cleanup;
390 user = (char *) xmalloc(output_token.length + 1);
391 if (user == NULL) {
392 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
393 fprintf(stdout, "BH Not enough memory\n");
394 goto cleanup;
395 }
396 memcpy(user, output_token.value, output_token.length);
397 user[output_token.length] = '\0';
398 if (norealm && (p = strchr(user, '@')) != NULL) {
399 *p = '\0';
400 }
4ebcf1ce
MM
401
402#if HAVE_PAC_SUPPORT
403 ret = krb5_init_context(&context);
404 if (!check_k5_err(context, "krb5_init_context", ret)) {
1a22a39e 405#if USE_HEIMDAL_KRB5
4ebcf1ce
MM
406#define ADWIN2KPAC 128
407 major_status = gsskrb5_extract_authz_data_from_sec_context(&minor_status,
408 gss_context, ADWIN2KPAC, &data_set);
409 if (!check_gss_err(major_status, minor_status,
410 "gsskrb5_extract_authz_data_from_sec_context()", log, 0)) {
411 ret = krb5_pac_parse(context, data_set.value, data_set.length, &pac);
412 gss_release_buffer(&minor_status, &data_set);
413 if (!check_k5_err(context, "krb5_pac_parse", ret)) {
414 ag = get_ad_groups((char *)&ad_groups, context, pac);
415 krb5_pac_free(context, pac);
416 }
417 krb5_free_context(context);
418 }
419#else
420 type_id.value = (void *)"mspac";
421 type_id.length = strlen((char *)type_id.value);
422#define KRB5PACLOGONINFO 1
423 major_status = gss_map_name_to_any(&minor_status, client_name, KRB5PACLOGONINFO, &type_id, (gss_any_t *)&pac);
424 if (!check_gss_err(major_status, minor_status, "gss_map_name_to_any()", log, 0)) {
425 ag = get_ad_groups((char *)&ad_groups,context, pac);
426 }
427 (void)gss_release_any_name_mapping(&minor_status, client_name, &type_id, (gss_any_t *)&pac);
428 krb5_free_context(context);
429#endif
430 }
431 if (ag) {
432 debug((char *) "%s| %s: DEBUG: Groups %s\n", LogTime(), PROGRAM, ag);
433 }
434#endif
2e881a6f 435 fprintf(stdout, "AF %s %s\n", token, user);
4ebcf1ce
MM
436 rfc_user = rfc1738_escape(user);
437 debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, token, rfc_user);
2e881a6f
A
438 if (log)
439 fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
8288b253 440 PROGRAM, rfc1738_escape(user));
2e881a6f
A
441 goto cleanup;
442 } else {
4ebcf1ce 443 if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log, 1))
2e881a6f
A
444 goto cleanup;
445 if (major_status & GSS_S_CONTINUE_NEEDED) {
446 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
447 fprintf(stdout, "NA %s\n", token);
448 goto cleanup;
449 }
450 gss_release_buffer(&minor_status, &output_token);
451 major_status =
452 gss_display_name(&minor_status, client_name, &output_token,
453 NULL);
454
4ebcf1ce 455 if (check_gss_err(major_status, minor_status, "gss_display_name()", log, 1))
2e881a6f
A
456 goto cleanup;
457 /*
458 * Return dummy token AA. May need an extra return tag then AF
459 */
460 user = (char *) xmalloc(output_token.length + 1);
461 if (user == NULL) {
462 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
463 fprintf(stdout, "BH Not enough memory\n");
464 goto cleanup;
465 }
466 memcpy(user, output_token.value, output_token.length);
467 user[output_token.length] = '\0';
468 if (norealm && (p = strchr(user, '@')) != NULL) {
469 *p = '\0';
470 }
471 fprintf(stdout, "AF %s %s\n", "AA==", user);
8288b253 472 debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, "AA==", rfc1738_escape(user));
2e881a6f
A
473 if (log)
474 fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
8288b253 475 PROGRAM, rfc1738_escape(user));
2e881a6f
A
476
477 }
478cleanup:
479 gss_release_buffer(&minor_status, &input_token);
480 gss_release_buffer(&minor_status, &output_token);
481 gss_release_cred(&minor_status, &server_creds);
482 if (server_name)
483 gss_release_name(&minor_status, &server_name);
484 if (client_name)
485 gss_release_name(&minor_status, &client_name);
486 if (kerberosToken) {
487 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
488 if (!spnego_flag)
4ebcf1ce 489 safe_free(kerberosToken);
2e881a6f
A
490 }
491 if (spnego_flag) {
492 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
4ebcf1ce 493 safe_free(spnegoToken);
2e881a6f 494 }
4ebcf1ce
MM
495 safe_free(token);
496 safe_free(user);
2e881a6f 497 continue;
3e5d7cdf 498 }
3e5d7cdf 499}
6989c6dc 500#else
074d6a40 501#include <cstdlib>
6989c6dc
AJ
502#ifndef MAX_AUTHTOKEN_LEN
503#define MAX_AUTHTOKEN_LEN 65535
504#endif
505int
506main(int argc, char *const argv[])
507{
508 setbuf(stdout, NULL);
509 setbuf(stdin, NULL);
510 char buf[MAX_AUTHTOKEN_LEN];
511 while (1) {
2e881a6f
A
512 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
513 fprintf(stdout, "BH input error\n");
514 exit(0);
515 }
516 fprintf(stdout, "BH Kerberos authentication not supported\n");
6989c6dc
AJ
517 }
518}
3ad12bda 519#endif /* HAVE_GSSAPI */