]> git.ipfire.org Git - thirdparty/squid.git/blame - helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc
SourceFormat Enforcement
[thirdparty/squid.git] / helpers / negotiate_auth / kerberos / negotiate_kerberos_auth.cc
CommitLineData
3e5d7cdf 1/*
2 * -----------------------------------------------------------------------------
3 *
4 * Author: Markus Moeller (markus_moeller at compuserve.com)
5 *
6 * Copyright (C) 2007 Markus Moeller. All rights reserved.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21 *
ba4fe07c
AJ
22 * As a special exemption, M Moeller gives permission to link this program
23 * with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
24 * the resulting executable, without including the source code for
25 * the Libraries in the source distribution.
26 *
3e5d7cdf 27 * -----------------------------------------------------------------------------
28 */
29/*
30 * Hosted at http://sourceforge.net/projects/squidkerbauth
31 */
f7f3304a 32#include "squid.h"
27bc2077
AJ
33#include "compat/getaddrinfo.h"
34#include "compat/getnameinfo.h"
d1f95b42 35
3ad12bda 36#if HAVE_GSSAPI
e9658d82
AJ
37
38#if HAVE_STRING_H
3e5d7cdf 39#include <string.h>
e9658d82
AJ
40#endif
41#if HAVE_STDOI_H
3e5d7cdf 42#include <stdio.h>
e9658d82 43#endif
e9658d82 44#if HAVE_NETDB_H
3e5d7cdf 45#include <netdb.h>
e9658d82
AJ
46#endif
47#if HAVE_UNISTD_H
3e5d7cdf 48#include <unistd.h>
e9658d82
AJ
49#endif
50#if HAVE_TIME_H
3e5d7cdf 51#include <time.h>
e9658d82 52#endif
3e5d7cdf 53
3ad12bda 54#include "util.h"
3e5d7cdf 55#include "base64.h"
3e5d7cdf 56
3ad12bda
AJ
57#if HAVE_GSSAPI_GSSAPI_H
58#include <gssapi/gssapi.h>
59#elif HAVE_GSSAPI_H
60#include <gssapi.h>
3a665e67 61#endif
75a8c92e
AJ
62
63#if !HAVE_HEIMDAL_KERBEROS
3ad12bda
AJ
64#if HAVE_GSSAPI_GSSAPI_KRB5_H
65#include <gssapi/gssapi_krb5.h>
75a8c92e 66#endif
3ad12bda
AJ
67#if HAVE_GSSAPI_GSSAPI_GENERIC_H
68#include <gssapi/gssapi_generic.h>
75a8c92e
AJ
69#endif
70#if HAVE_GSSAPI_GSSAPI_EXT_H
71#include <gssapi/gssapi_ext.h>
72#endif
73#endif
74
6989c6dc
AJ
75#ifndef gss_nt_service_name
76#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
77#endif
3e5d7cdf 78
e9658d82 79#define PROGRAM "negotiate_kerberos_auth"
ba4fe07c
AJ
80
81#ifndef MAX_AUTHTOKEN_LEN
82#define MAX_AUTHTOKEN_LEN 65535
3e5d7cdf 83#endif
3ad12bda 84#ifndef SQUID_KERB_AUTH_VERSION
08885c7f 85#define SQUID_KERB_AUTH_VERSION "3.0.4sq"
3ad12bda 86#endif
3e5d7cdf 87
3ad12bda 88int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
2e881a6f 89 const char *function, int log);
3e5d7cdf 90char *gethost_name(void);
91static const char *LogTime(void);
92
2e881a6f 93static const unsigned char ntlmProtocol[] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
3e5d7cdf 94
3ad12bda
AJ
95static const char *
96LogTime()
3e5d7cdf 97{
98 struct tm *tm;
99 struct timeval now;
100 static time_t last_t = 0;
101 static char buf[128];
102
103 gettimeofday(&now, NULL);
104 if (now.tv_sec != last_t) {
2e881a6f
A
105 tm = localtime((time_t *) & now.tv_sec);
106 strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
107 last_t = now.tv_sec;
3e5d7cdf 108 }
109 return buf;
110}
111
3ad12bda
AJ
112char *
113gethost_name(void)
26ac0430 114{
e6aa9181 115 /*
b1218840
AJ
116 * char hostname[sysconf(_SC_HOST_NAME_MAX)];
117 */
3d62cc61 118 char hostname[1024];
3ad12bda
AJ
119 struct addrinfo *hres = NULL, *hres_list;
120 int rc, count;
26ac0430 121
3ad12bda 122 rc = gethostname(hostname, sysconf(_SC_HOST_NAME_MAX));
26ac0430 123 if (rc) {
2e881a6f
A
124 fprintf(stderr, "%s| %s: ERROR: resolving hostname '%s' failed\n",
125 LogTime(), PROGRAM, hostname);
126 return NULL;
26ac0430 127 }
27bc2077 128 rc = getaddrinfo(hostname, NULL, NULL, &hres);
26ac0430 129 if (rc != 0) {
2e881a6f
A
130 fprintf(stderr,
131 "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
132 LogTime(), PROGRAM, gai_strerror(rc));
133 return NULL;
26ac0430 134 }
3ad12bda
AJ
135 hres_list = hres;
136 count = 0;
26ac0430 137 while (hres_list) {
755494da 138 ++count;
2e881a6f 139 hres_list = hres_list->ai_next;
26ac0430 140 }
27bc2077 141 rc = getnameinfo(hres->ai_addr, hres->ai_addrlen, hostname,
2e881a6f 142 sizeof(hostname), NULL, 0, 0);
26ac0430 143 if (rc != 0) {
2e881a6f
A
144 fprintf(stderr,
145 "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
146 LogTime(), PROGRAM, gai_strerror(rc));
147 freeaddrinfo(hres);
148 return NULL;
3e5d7cdf 149 }
27bc2077 150 freeaddrinfo(hres);
3ad12bda
AJ
151 hostname[sysconf(_SC_HOST_NAME_MAX) - 1] = '\0';
152 return (xstrdup(hostname));
3e5d7cdf 153}
154
3ad12bda
AJ
155int
156check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
2e881a6f 157 const char *function, int log)
26ac0430
AJ
158{
159 if (GSS_ERROR(major_status)) {
2e881a6f
A
160 OM_uint32 maj_stat, min_stat;
161 OM_uint32 msg_ctx = 0;
162 gss_buffer_desc status_string;
163 char buf[1024];
164 size_t len;
165
166 len = 0;
167 msg_ctx = 0;
08885c7f 168 do {
2e881a6f
A
169 /* convert major status code (GSS-API error) to text */
170 maj_stat = gss_display_status(&min_stat, major_status,
171 GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
08885c7f 172 if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
2e881a6f
A
173 if (sizeof(buf) > len + status_string.length + 1) {
174 snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
175 len += status_string.length;
176 }
08885c7f
MM
177 } else
178 msg_ctx = 0;
2e881a6f 179 gss_release_buffer(&min_stat, &status_string);
08885c7f 180 } while (msg_ctx);
2e881a6f
A
181 if (sizeof(buf) > len + 2) {
182 snprintf(buf + len, (sizeof(buf) - len), "%s", ". ");
183 len += 2;
184 }
185 msg_ctx = 0;
08885c7f 186 do {
2e881a6f
A
187 /* convert minor status code (underlying routine error) to text */
188 maj_stat = gss_display_status(&min_stat, minor_status,
189 GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
08885c7f 190 if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
2e881a6f
A
191 if (sizeof(buf) > len + status_string.length) {
192 snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
193 len += status_string.length;
194 }
08885c7f
MM
195 } else
196 msg_ctx = 0;
2e881a6f 197 gss_release_buffer(&min_stat, &status_string);
08885c7f 198 } while (msg_ctx);
2e881a6f
A
199 debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, buf);
200 fprintf(stdout, "BH %s failed: %s\n", function, buf);
201 if (log)
202 fprintf(stderr, "%s| %s: INFO: User not authenticated\n", LogTime(),
203 PROGRAM);
204 return (1);
3e5d7cdf 205 }
3ad12bda 206 return (0);
3e5d7cdf 207}
208
3ad12bda
AJ
209int
210main(int argc, char *const argv[])
3e5d7cdf 211{
26ac0430 212 char buf[MAX_AUTHTOKEN_LEN];
3ad12bda
AJ
213 char *c, *p;
214 char *user = NULL;
215 int length = 0;
216 static int err = 0;
e673ba3a 217 int opt, log = 0, norealm = 0;
3ad12bda
AJ
218 OM_uint32 ret_flags = 0, spnego_flag = 0;
219 char *service_name = (char *) "HTTP", *host_name = NULL;
26ac0430
AJ
220 char *token = NULL;
221 char *service_principal = NULL;
222 OM_uint32 major_status, minor_status;
3ad12bda
AJ
223 gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
224 gss_name_t client_name = GSS_C_NO_NAME;
225 gss_name_t server_name = GSS_C_NO_NAME;
226 gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
227 gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
228 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
229 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
230 const unsigned char *kerberosToken = NULL;
3ad12bda
AJ
231 const unsigned char *spnegoToken = NULL;
232 size_t spnegoTokenLength = 0;
26ac0430 233
3ad12bda
AJ
234 setbuf(stdout, NULL);
235 setbuf(stdin, NULL);
26ac0430 236
6989c6dc 237 while (-1 != (opt = getopt(argc, argv, "dirs:h"))) {
2e881a6f
A
238 switch (opt) {
239 case 'd':
240 debug_enabled = 1;
241 break;
242 case 'i':
243 log = 1;
244 break;
245 case 'r':
246 norealm = 1;
247 break;
248 case 's':
249 service_principal = xstrdup(optarg);
250 break;
251 case 'h':
252 fprintf(stderr, "Usage: \n");
253 fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-h]\n");
254 fprintf(stderr, "-d full debug\n");
255 fprintf(stderr, "-i informational messages\n");
256 fprintf(stderr, "-r remove realm from username\n");
257 fprintf(stderr, "-s service principal name\n");
258 fprintf(stderr, "-h help\n");
259 fprintf(stderr,
260 "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
261 fprintf(stderr, "default SPN is HTTP/fqdn@DEFAULT_REALM\n");
262 exit(0);
263 default:
264 fprintf(stderr, "%s| %s: WARNING: unknown option: -%c.\n", LogTime(),
265 PROGRAM, opt);
266 }
3e5d7cdf 267 }
268
b1218840 269 debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, SQUID_KERB_AUTH_VERSION);
3ad12bda 270 if (service_principal && strcasecmp(service_principal, "GSS_C_NO_NAME")) {
2e881a6f
A
271 service.value = service_principal;
272 service.length = strlen((char *) service.value);
26ac0430 273 } else {
2e881a6f
A
274 host_name = gethost_name();
275 if (!host_name) {
276 fprintf(stderr,
277 "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
278 LogTime(), PROGRAM);
279 fprintf(stdout, "BH hostname error\n");
280 exit(-1);
281 }
282 service.value = xmalloc(strlen(service_name) + strlen(host_name) + 2);
283 snprintf((char *) service.value, strlen(service_name) + strlen(host_name) + 2,
284 "%s@%s", service_name, host_name);
285 service.length = strlen((char *) service.value);
3e5d7cdf 286 }
287
26ac0430 288 while (1) {
2e881a6f
A
289 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
290 if (ferror(stdin)) {
291 debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
292 LogTime(), PROGRAM, ferror(stdin),
293 strerror(ferror(stdin)));
294
295 fprintf(stdout, "BH input error\n");
296 exit(1); /* BIIG buffer */
297 }
298 fprintf(stdout, "BH input error\n");
299 exit(0);
300 }
301 c = (char *) memchr(buf, '\n', sizeof(buf) - 1);
302 if (c) {
303 *c = '\0';
304 length = c - buf;
305 } else {
306 err = 1;
307 }
308 if (err) {
309 debug((char *) "%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM);
310 fprintf(stdout, "BH Oversized message\n");
311 err = 0;
312 continue;
313 }
314 debug((char *) "%s| %s: DEBUG: Got '%s' from squid (length: %d).\n", LogTime(), PROGRAM, buf, length);
315
316 if (buf[0] == '\0') {
317 debug((char *) "%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM);
318 fprintf(stdout, "BH Invalid request\n");
319 continue;
320 }
321 if (strlen(buf) < 2) {
322 debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
323 fprintf(stdout, "BH Invalid request\n");
324 continue;
325 }
326 if (!strncmp(buf, "QQ", 2)) {
327 gss_release_buffer(&minor_status, &input_token);
328 gss_release_buffer(&minor_status, &output_token);
329 gss_release_buffer(&minor_status, &service);
330 gss_release_cred(&minor_status, &server_creds);
331 if (server_name)
332 gss_release_name(&minor_status, &server_name);
333 if (client_name)
334 gss_release_name(&minor_status, &client_name);
335 if (gss_context != GSS_C_NO_CONTEXT)
336 gss_delete_sec_context(&minor_status, &gss_context, NULL);
337 if (kerberosToken) {
338 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
339 if (!spnego_flag)
340 xfree((char *) kerberosToken);
341 kerberosToken = NULL;
342 }
343 if (spnego_flag) {
344 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
345 if (spnegoToken)
346 xfree((char *) spnegoToken);
347 spnegoToken = NULL;
348 }
349 if (token) {
350 xfree(token);
351 token = NULL;
352 }
353 if (host_name) {
354 xfree(host_name);
355 host_name = NULL;
356 }
357 fprintf(stdout, "BH quit command\n");
358 exit(0);
359 }
360 if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) {
361 debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
362 fprintf(stdout, "BH Invalid request\n");
363 continue;
364 }
365 if (!strncmp(buf, "YR", 2)) {
366 if (gss_context != GSS_C_NO_CONTEXT)
367 gss_delete_sec_context(&minor_status, &gss_context, NULL);
368 gss_context = GSS_C_NO_CONTEXT;
369 }
370 if (strlen(buf) <= 3) {
371 debug((char *) "%s| %s: ERROR: Invalid negotiate request [%s]\n", LogTime(), PROGRAM, buf);
372 fprintf(stdout, "BH Invalid negotiate request\n");
373 continue;
374 }
8bdd0cec 375 input_token.length = base64_decode_len(buf+3);
2e881a6f
A
376 debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length: %d).\n",
377 LogTime(), PROGRAM, buf + 3, (int) input_token.length);
378 input_token.value = xmalloc(input_token.length);
379
8bdd0cec 380 input_token.length = base64_decode((char *) input_token.value, input_token.length, buf+3);
2e881a6f
A
381
382 if ((input_token.length >= sizeof ntlmProtocol + 1) &&
383 (!memcmp(input_token.value, ntlmProtocol, sizeof ntlmProtocol))) {
384 debug((char *) "%s| %s: WARNING: received type %d NTLM token\n",
385 LogTime(), PROGRAM,
386 (int) *((unsigned char *) input_token.value +
387 sizeof ntlmProtocol));
388 fprintf(stdout, "BH received type %d NTLM token\n",
389 (int) *((unsigned char *) input_token.value +
390 sizeof ntlmProtocol));
391 goto cleanup;
392 }
393 if (service_principal) {
394 if (strcasecmp(service_principal, "GSS_C_NO_NAME")) {
395 major_status = gss_import_name(&minor_status, &service,
396 (gss_OID) GSS_C_NULL_OID, &server_name);
397
398 } else {
399 server_name = GSS_C_NO_NAME;
400 major_status = GSS_S_COMPLETE;
401 }
402 } else {
403 major_status = gss_import_name(&minor_status, &service,
404 gss_nt_service_name, &server_name);
405 }
406
407 if (check_gss_err(major_status, minor_status, "gss_import_name()", log))
408 goto cleanup;
409
410 major_status =
411 gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
412 GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL);
413 if (check_gss_err(major_status, minor_status, "gss_acquire_cred()", log))
414 goto cleanup;
415
416 major_status = gss_accept_sec_context(&minor_status,
417 &gss_context,
418 server_creds,
419 &input_token,
420 GSS_C_NO_CHANNEL_BINDINGS,
421 &client_name, NULL, &output_token, &ret_flags, NULL, NULL);
422
2e881a6f
A
423 if (output_token.length) {
424 spnegoToken = (const unsigned char *) output_token.value;
425 spnegoTokenLength = output_token.length;
8bdd0cec 426 token = (char *) xmalloc(base64_encode_len(spnegoTokenLength));
2e881a6f
A
427 if (token == NULL) {
428 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
429 fprintf(stdout, "BH Not enough memory\n");
430 goto cleanup;
431 }
8bdd0cec
AJ
432 base64_encode_str(token, base64_encode_len(spnegoTokenLength),
433 (const char *) spnegoToken, spnegoTokenLength);
2e881a6f
A
434
435 if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log))
436 goto cleanup;
437 if (major_status & GSS_S_CONTINUE_NEEDED) {
438 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
439 fprintf(stdout, "TT %s\n", token);
440 goto cleanup;
441 }
442 gss_release_buffer(&minor_status, &output_token);
443 major_status =
444 gss_display_name(&minor_status, client_name, &output_token,
445 NULL);
446
447 if (check_gss_err(major_status, minor_status, "gss_display_name()", log))
448 goto cleanup;
449 user = (char *) xmalloc(output_token.length + 1);
450 if (user == NULL) {
451 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
452 fprintf(stdout, "BH Not enough memory\n");
453 goto cleanup;
454 }
455 memcpy(user, output_token.value, output_token.length);
456 user[output_token.length] = '\0';
457 if (norealm && (p = strchr(user, '@')) != NULL) {
458 *p = '\0';
459 }
460 fprintf(stdout, "AF %s %s\n", token, user);
461 debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, token, user);
462 if (log)
463 fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
464 PROGRAM, user);
465 goto cleanup;
466 } else {
467 if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log))
468 goto cleanup;
469 if (major_status & GSS_S_CONTINUE_NEEDED) {
470 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
471 fprintf(stdout, "NA %s\n", token);
472 goto cleanup;
473 }
474 gss_release_buffer(&minor_status, &output_token);
475 major_status =
476 gss_display_name(&minor_status, client_name, &output_token,
477 NULL);
478
479 if (check_gss_err(major_status, minor_status, "gss_display_name()", log))
480 goto cleanup;
481 /*
482 * Return dummy token AA. May need an extra return tag then AF
483 */
484 user = (char *) xmalloc(output_token.length + 1);
485 if (user == NULL) {
486 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
487 fprintf(stdout, "BH Not enough memory\n");
488 goto cleanup;
489 }
490 memcpy(user, output_token.value, output_token.length);
491 user[output_token.length] = '\0';
492 if (norealm && (p = strchr(user, '@')) != NULL) {
493 *p = '\0';
494 }
495 fprintf(stdout, "AF %s %s\n", "AA==", user);
496 debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, "AA==", user);
497 if (log)
498 fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
499 PROGRAM, user);
500
501 }
502cleanup:
503 gss_release_buffer(&minor_status, &input_token);
504 gss_release_buffer(&minor_status, &output_token);
505 gss_release_cred(&minor_status, &server_creds);
506 if (server_name)
507 gss_release_name(&minor_status, &server_name);
508 if (client_name)
509 gss_release_name(&minor_status, &client_name);
510 if (kerberosToken) {
511 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
512 if (!spnego_flag)
513 xfree((char *) kerberosToken);
514 kerberosToken = NULL;
515 }
516 if (spnego_flag) {
517 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
518 if (spnegoToken)
519 xfree((char *) spnegoToken);
520 spnegoToken = NULL;
521 }
522 if (token) {
523 xfree(token);
524 token = NULL;
525 }
526 if (user) {
527 xfree(user);
528 user = NULL;
529 }
530 continue;
3e5d7cdf 531 }
3e5d7cdf 532}
6989c6dc
AJ
533#else
534#include <stdio.h>
535#include <stdlib.h>
536#ifndef MAX_AUTHTOKEN_LEN
537#define MAX_AUTHTOKEN_LEN 65535
538#endif
539int
540main(int argc, char *const argv[])
541{
542 setbuf(stdout, NULL);
543 setbuf(stdin, NULL);
544 char buf[MAX_AUTHTOKEN_LEN];
545 while (1) {
2e881a6f
A
546 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
547 fprintf(stdout, "BH input error\n");
548 exit(0);
549 }
550 fprintf(stdout, "BH Kerberos authentication not supported\n");
6989c6dc
AJ
551 }
552}
3ad12bda 553#endif /* HAVE_GSSAPI */