]>
Commit | Line | Data |
---|---|---|
ca02e0ec | 1 | /* |
f70aedc4 | 2 | * Copyright (C) 1996-2021 The Squid Software Foundation and contributors |
ca02e0ec AJ |
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" |
602d9612 | 39 | #include "rfc1738.h" |
d1f95b42 | 40 | |
3ad12bda | 41 | #if HAVE_GSSAPI |
e9658d82 | 42 | |
4ebcf1ce | 43 | #include "negotiate_kerberos.h" |
3e5d7cdf | 44 | |
685277d8 MM |
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 | ||
c8df5896 | 59 | krb5_keytab memory_keytab; |
60 | ||
685277d8 MM |
61 | krb5_error_code krb5_free_kt_list(krb5_context context, krb5_kt_list kt_list); |
62 | krb5_error_code krb5_write_keytab(krb5_context context, | |
63 | krb5_kt_list kt_list, | |
64 | char *name); | |
65 | krb5_error_code krb5_read_keytab(krb5_context context, | |
66 | char *name, | |
67 | krb5_kt_list *kt_list); | |
68 | #endif /* HAVE_KRB5_MEMORY_KEYTAB */ | |
69 | ||
685277d8 MM |
70 | int |
71 | check_k5_err(krb5_context context, const char *function, krb5_error_code code) | |
72 | { | |
73 | ||
1aa0fb05 | 74 | if (code && code != KRB5_KT_END) { |
685277d8 MM |
75 | const char *errmsg; |
76 | errmsg = krb5_get_error_message(context, code); | |
77 | debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, errmsg); | |
78 | fprintf(stderr, "%s| %s: ERROR: %s: %s\n", LogTime(), PROGRAM, function, errmsg); | |
79 | #if HAVE_KRB5_FREE_ERROR_MESSAGE | |
80 | krb5_free_error_message(context, errmsg); | |
81 | #elif HAVE_KRB5_FREE_ERROR_STRING | |
82 | krb5_free_error_string(context, (char *)errmsg); | |
83 | #else | |
84 | xfree(errmsg); | |
85 | #endif | |
86 | } | |
87 | return code; | |
88 | } | |
685277d8 | 89 | |
3ad12bda AJ |
90 | char * |
91 | gethost_name(void) | |
26ac0430 | 92 | { |
e6aa9181 | 93 | /* |
b1218840 AJ |
94 | * char hostname[sysconf(_SC_HOST_NAME_MAX)]; |
95 | */ | |
3d62cc61 | 96 | char hostname[1024]; |
3ad12bda AJ |
97 | struct addrinfo *hres = NULL, *hres_list; |
98 | int rc, count; | |
26ac0430 | 99 | |
bc30ad64 | 100 | rc = gethostname(hostname, sizeof(hostname)-1); |
26ac0430 | 101 | if (rc) { |
685277d8 | 102 | debug((char *) "%s| %s: ERROR: resolving hostname '%s' failed\n", LogTime(), PROGRAM, hostname); |
2e881a6f A |
103 | fprintf(stderr, "%s| %s: ERROR: resolving hostname '%s' failed\n", |
104 | LogTime(), PROGRAM, hostname); | |
105 | return NULL; | |
26ac0430 | 106 | } |
27bc2077 | 107 | rc = getaddrinfo(hostname, NULL, NULL, &hres); |
4616aac3 | 108 | if (rc != 0 || hres == NULL ) { |
685277d8 MM |
109 | debug((char *) "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n", |
110 | LogTime(), PROGRAM, gai_strerror(rc)); | |
2e881a6f A |
111 | fprintf(stderr, |
112 | "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n", | |
113 | LogTime(), PROGRAM, gai_strerror(rc)); | |
114 | return NULL; | |
26ac0430 | 115 | } |
3ad12bda AJ |
116 | hres_list = hres; |
117 | count = 0; | |
26ac0430 | 118 | while (hres_list) { |
755494da | 119 | ++count; |
2e881a6f | 120 | hres_list = hres_list->ai_next; |
26ac0430 | 121 | } |
27bc2077 | 122 | rc = getnameinfo(hres->ai_addr, hres->ai_addrlen, hostname, |
2e881a6f | 123 | sizeof(hostname), NULL, 0, 0); |
26ac0430 | 124 | if (rc != 0) { |
685277d8 MM |
125 | debug((char *) "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n", |
126 | LogTime(), PROGRAM, gai_strerror(rc)); | |
2e881a6f A |
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; | |
3e5d7cdf | 132 | } |
27bc2077 | 133 | freeaddrinfo(hres); |
bc30ad64 | 134 | hostname[sizeof(hostname)-1] = '\0'; |
3ad12bda | 135 | return (xstrdup(hostname)); |
3e5d7cdf | 136 | } |
137 | ||
3ad12bda AJ |
138 | int |
139 | check_gss_err(OM_uint32 major_status, OM_uint32 minor_status, | |
4ebcf1ce | 140 | const char *function, int log, int sout) |
26ac0430 AJ |
141 | { |
142 | if (GSS_ERROR(major_status)) { | |
2e881a6f A |
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; | |
08885c7f | 151 | do { |
2e881a6f A |
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); | |
08885c7f | 155 | if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) { |
2e881a6f A |
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 | } | |
08885c7f MM |
160 | } else |
161 | msg_ctx = 0; | |
2e881a6f | 162 | gss_release_buffer(&min_stat, &status_string); |
08885c7f | 163 | } while (msg_ctx); |
2e881a6f A |
164 | if (sizeof(buf) > len + 2) { |
165 | snprintf(buf + len, (sizeof(buf) - len), "%s", ". "); | |
166 | len += 2; | |
167 | } | |
168 | msg_ctx = 0; | |
08885c7f | 169 | do { |
2e881a6f A |
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); | |
08885c7f | 173 | if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) { |
2e881a6f A |
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 | } | |
08885c7f MM |
178 | } else |
179 | msg_ctx = 0; | |
2e881a6f | 180 | gss_release_buffer(&min_stat, &status_string); |
08885c7f | 181 | } while (msg_ctx); |
2e881a6f | 182 | debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, buf); |
4ebcf1ce MM |
183 | if (sout) |
184 | fprintf(stdout, "BH %s failed: %s\n", function, buf); | |
2e881a6f A |
185 | if (log) |
186 | fprintf(stderr, "%s| %s: INFO: User not authenticated\n", LogTime(), | |
187 | PROGRAM); | |
188 | return (1); | |
3e5d7cdf | 189 | } |
3ad12bda | 190 | return (0); |
3e5d7cdf | 191 | } |
192 | ||
685277d8 MM |
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 | { | |
685277d8 MM |
305 | char ktname[MAXPATHLEN+sizeof("MEMORY:")+1]; |
306 | krb5_error_code retval = 0; | |
307 | ||
308 | snprintf(ktname, sizeof(ktname), "%s", name); | |
c8df5896 | 309 | retval = krb5_kt_resolve(context, ktname, &memory_keytab); |
685277d8 MM |
310 | if (retval) |
311 | return retval; | |
312 | for (krb5_kt_list lp = list; lp; lp = lp->next) { | |
c8df5896 | 313 | retval = krb5_kt_add_entry(context, memory_keytab, lp->entry); |
685277d8 MM |
314 | if (retval) |
315 | break; | |
316 | } | |
317 | /* | |
318 | * krb5_kt_close(context, kt); | |
319 | */ | |
320 | return retval; | |
321 | } | |
322 | #endif /* HAVE_KRB5_MEMORY_KEYTAB */ | |
323 | ||
3ad12bda AJ |
324 | int |
325 | main(int argc, char *const argv[]) | |
3e5d7cdf | 326 | { |
26ac0430 | 327 | char buf[MAX_AUTHTOKEN_LEN]; |
3ad12bda AJ |
328 | char *c, *p; |
329 | char *user = NULL; | |
4ebcf1ce MM |
330 | char *rfc_user = NULL; |
331 | #if HAVE_PAC_SUPPORT | |
332 | char ad_groups[MAX_PAC_GROUP_SIZE]; | |
333 | char *ag=NULL; | |
4ebcf1ce | 334 | krb5_pac pac; |
1a22a39e | 335 | #if USE_HEIMDAL_KRB5 |
4ebcf1ce MM |
336 | gss_buffer_desc data_set = GSS_C_EMPTY_BUFFER; |
337 | #else | |
338 | gss_buffer_desc type_id = GSS_C_EMPTY_BUFFER; | |
339 | #endif | |
685277d8 | 340 | #endif |
685277d8 MM |
341 | krb5_context context = NULL; |
342 | krb5_error_code ret; | |
4ebcf1ce | 343 | long length = 0; |
3ad12bda | 344 | static int err = 0; |
e673ba3a | 345 | int opt, log = 0, norealm = 0; |
3ad12bda AJ |
346 | OM_uint32 ret_flags = 0, spnego_flag = 0; |
347 | char *service_name = (char *) "HTTP", *host_name = NULL; | |
26ac0430 AJ |
348 | char *token = NULL; |
349 | char *service_principal = NULL; | |
685277d8 MM |
350 | char *keytab_name = NULL; |
351 | char *keytab_name_env = NULL; | |
4616aac3 | 352 | char default_keytab[MAXPATHLEN]; |
685277d8 MM |
353 | #if HAVE_KRB5_MEMORY_KEYTAB |
354 | char *memory_keytab_name = NULL; | |
c8df5896 | 355 | char *memory_keytab_name_env = NULL; |
685277d8 MM |
356 | #endif |
357 | char *rcache_type = NULL; | |
358 | char *rcache_type_env = NULL; | |
359 | char *rcache_dir = NULL; | |
360 | char *rcache_dir_env = NULL; | |
26ac0430 | 361 | OM_uint32 major_status, minor_status; |
3ad12bda AJ |
362 | gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; |
363 | gss_name_t client_name = GSS_C_NO_NAME; | |
364 | gss_name_t server_name = GSS_C_NO_NAME; | |
365 | gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL; | |
366 | gss_buffer_desc service = GSS_C_EMPTY_BUFFER; | |
367 | gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; | |
368 | gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; | |
369 | const unsigned char *kerberosToken = NULL; | |
3ad12bda AJ |
370 | const unsigned char *spnegoToken = NULL; |
371 | size_t spnegoTokenLength = 0; | |
26ac0430 | 372 | |
3ad12bda AJ |
373 | setbuf(stdout, NULL); |
374 | setbuf(stdin, NULL); | |
26ac0430 | 375 | |
685277d8 | 376 | while (-1 != (opt = getopt(argc, argv, "dirs:k:c:t:"))) { |
2e881a6f A |
377 | switch (opt) { |
378 | case 'd': | |
379 | debug_enabled = 1; | |
380 | break; | |
381 | case 'i': | |
382 | log = 1; | |
383 | break; | |
384 | case 'r': | |
385 | norealm = 1; | |
386 | break; | |
685277d8 MM |
387 | case 'k': |
388 | #if HAVE_SYS_STAT_H | |
389 | struct stat fstat; | |
390 | char *ktp; | |
391 | #endif | |
f53969cc | 392 | if (optarg) |
685277d8 | 393 | keytab_name = xstrdup(optarg); |
f53969cc | 394 | else { |
685277d8 | 395 | fprintf(stderr, "ERROR: keytab file not given\n"); |
24885773 | 396 | exit(EXIT_FAILURE); |
f53969cc | 397 | } |
685277d8 MM |
398 | /* |
399 | * Some sanity checks | |
400 | */ | |
401 | #if HAVE_SYS_STAT_H | |
402 | if ((ktp=strchr(keytab_name,':'))) | |
403 | ktp++; | |
404 | else | |
405 | ktp=keytab_name; | |
406 | if (stat((const char*)ktp, &fstat)) { | |
407 | if (ENOENT == errno) | |
408 | fprintf(stderr, "ERROR: keytab file %s does not exist\n",keytab_name); | |
409 | else | |
410 | fprintf(stderr, "ERROR: Error %s during stat of keytab file %s\n",strerror(errno),keytab_name); | |
24885773 | 411 | exit(EXIT_FAILURE); |
685277d8 MM |
412 | } else if (!S_ISREG(fstat.st_mode)) { |
413 | fprintf(stderr, "ERROR: keytab file %s is not a file\n",keytab_name); | |
24885773 | 414 | exit(EXIT_FAILURE); |
685277d8 MM |
415 | } |
416 | #endif | |
417 | #if HAVE_UNISTD_H | |
418 | if (access(ktp, R_OK)) { | |
419 | fprintf(stderr, "ERROR: keytab file %s is not accessible\n",keytab_name); | |
24885773 | 420 | exit(EXIT_FAILURE); |
685277d8 MM |
421 | } |
422 | #endif | |
423 | break; | |
424 | case 'c': | |
425 | #if HAVE_SYS_STAT_H | |
426 | struct stat dstat; | |
427 | #endif | |
f53969cc | 428 | if (optarg) |
685277d8 | 429 | rcache_dir = xstrdup(optarg); |
f53969cc | 430 | else { |
685277d8 | 431 | fprintf(stderr, "ERROR: replay cache directory not given\n"); |
24885773 | 432 | exit(EXIT_FAILURE); |
f53969cc | 433 | } |
685277d8 MM |
434 | /* |
435 | * Some sanity checks | |
436 | */ | |
437 | #if HAVE_SYS_STAT_H | |
438 | if (stat((const char*)rcache_dir, &dstat)) { | |
439 | if (ENOENT == errno) | |
440 | fprintf(stderr, "ERROR: replay cache directory %s does not exist\n",rcache_dir); | |
441 | else | |
442 | fprintf(stderr, "ERROR: Error %s during stat of replay cache directory %s\n",strerror(errno),rcache_dir); | |
24885773 | 443 | exit(EXIT_FAILURE); |
685277d8 MM |
444 | } else if (!S_ISDIR(dstat.st_mode)) { |
445 | fprintf(stderr, "ERROR: replay cache directory %s is not a directory\n",rcache_dir); | |
24885773 | 446 | exit(EXIT_FAILURE); |
685277d8 MM |
447 | } |
448 | #endif | |
449 | #if HAVE_UNISTD_H | |
450 | if (access(rcache_dir, W_OK)) { | |
451 | fprintf(stderr, "ERROR: replay cache directory %s is not accessible\n",rcache_dir); | |
24885773 | 452 | exit(EXIT_FAILURE); |
685277d8 MM |
453 | } |
454 | #endif | |
455 | break; | |
456 | case 't': | |
f53969cc | 457 | if (optarg) |
685277d8 | 458 | rcache_type = xstrdup(optarg); |
f53969cc | 459 | else { |
685277d8 | 460 | fprintf(stderr, "ERROR: replay cache type not given\n"); |
24885773 | 461 | exit(EXIT_FAILURE); |
f53969cc | 462 | } |
685277d8 | 463 | break; |
2e881a6f | 464 | case 's': |
f53969cc | 465 | if (optarg) |
685277d8 | 466 | service_principal = xstrdup(optarg); |
f53969cc | 467 | else { |
685277d8 | 468 | fprintf(stderr, "ERROR: service principal not given\n"); |
24885773 | 469 | exit(EXIT_FAILURE); |
f53969cc | 470 | } |
2e881a6f | 471 | break; |
685277d8 | 472 | default: |
2e881a6f | 473 | fprintf(stderr, "Usage: \n"); |
685277d8 | 474 | fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-k keytab] [-c rcdir] [-t rctype]\n"); |
2e881a6f A |
475 | fprintf(stderr, "-d full debug\n"); |
476 | fprintf(stderr, "-i informational messages\n"); | |
477 | fprintf(stderr, "-r remove realm from username\n"); | |
478 | fprintf(stderr, "-s service principal name\n"); | |
685277d8 MM |
479 | fprintf(stderr, "-k keytab name\n"); |
480 | fprintf(stderr, "-c replay cache directory\n"); | |
481 | fprintf(stderr, "-t replay cache type\n"); | |
2e881a6f A |
482 | fprintf(stderr, |
483 | "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n"); | |
484 | fprintf(stderr, "default SPN is HTTP/fqdn@DEFAULT_REALM\n"); | |
24885773 | 485 | exit(EXIT_SUCCESS); |
2e881a6f | 486 | } |
3e5d7cdf | 487 | } |
488 | ||
b1218840 | 489 | debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, SQUID_KERB_AUTH_VERSION); |
3ad12bda | 490 | if (service_principal && strcasecmp(service_principal, "GSS_C_NO_NAME")) { |
685277d8 MM |
491 | if (!strstr(service_principal,"HTTP/")) { |
492 | debug((char *) "%s| %s: WARN: service_principal %s does not start with HTTP/\n", | |
493 | LogTime(), PROGRAM, service_principal); | |
494 | } | |
2e881a6f A |
495 | service.value = service_principal; |
496 | service.length = strlen((char *) service.value); | |
26ac0430 | 497 | } else { |
2e881a6f A |
498 | host_name = gethost_name(); |
499 | if (!host_name) { | |
500 | fprintf(stderr, | |
501 | "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n", | |
502 | LogTime(), PROGRAM); | |
503 | fprintf(stdout, "BH hostname error\n"); | |
24885773 | 504 | exit(EXIT_FAILURE); |
2e881a6f A |
505 | } |
506 | service.value = xmalloc(strlen(service_name) + strlen(host_name) + 2); | |
507 | snprintf((char *) service.value, strlen(service_name) + strlen(host_name) + 2, | |
508 | "%s@%s", service_name, host_name); | |
509 | service.length = strlen((char *) service.value); | |
4ebcf1ce | 510 | xfree(host_name); |
3e5d7cdf | 511 | } |
512 | ||
685277d8 MM |
513 | if (rcache_type) { |
514 | rcache_type_env = (char *) xmalloc(strlen("KRB5RCACHETYPE=")+strlen(rcache_type)+1); | |
515 | strcpy(rcache_type_env, "KRB5RCACHETYPE="); | |
516 | strcat(rcache_type_env, rcache_type); | |
517 | putenv(rcache_type_env); | |
518 | debug((char *) "%s| %s: INFO: Setting replay cache type to %s\n", | |
519 | LogTime(), PROGRAM, rcache_type); | |
520 | } | |
521 | ||
522 | if (rcache_dir) { | |
523 | rcache_dir_env = (char *) xmalloc(strlen("KRB5RCACHEDIR=")+strlen(rcache_dir)+1); | |
524 | strcpy(rcache_dir_env, "KRB5RCACHEDIR="); | |
525 | strcat(rcache_dir_env, rcache_dir); | |
526 | putenv(rcache_dir_env); | |
527 | debug((char *) "%s| %s: INFO: Setting replay cache directory to %s\n", | |
528 | LogTime(), PROGRAM, rcache_dir); | |
529 | } | |
530 | ||
531 | if (keytab_name) { | |
532 | keytab_name_env = (char *) xmalloc(strlen("KRB5_KTNAME=")+strlen(keytab_name)+1); | |
533 | strcpy(keytab_name_env, "KRB5_KTNAME="); | |
534 | strcat(keytab_name_env, keytab_name); | |
535 | putenv(keytab_name_env); | |
536 | } else { | |
537 | keytab_name_env = getenv("KRB5_KTNAME"); | |
4616aac3 MM |
538 | if (!keytab_name_env) { |
539 | ret = krb5_init_context(&context); | |
540 | if (!check_k5_err(context, "krb5_init_context", ret)) { | |
541 | krb5_kt_default_name(context, default_keytab, MAXPATHLEN); | |
542 | } | |
39c178ae | 543 | keytab_name = xstrdup(default_keytab); |
4616aac3 MM |
544 | krb5_free_context(context); |
545 | } else | |
685277d8 MM |
546 | keytab_name = xstrdup(keytab_name_env); |
547 | } | |
548 | debug((char *) "%s| %s: INFO: Setting keytab to %s\n", LogTime(), PROGRAM, keytab_name); | |
549 | #if HAVE_KRB5_MEMORY_KEYTAB | |
550 | ret = krb5_init_context(&context); | |
551 | if (!check_k5_err(context, "krb5_init_context", ret)) { | |
552 | memory_keytab_name = (char *)xmalloc(strlen("MEMORY:negotiate_kerberos_auth_")+16); | |
553 | snprintf(memory_keytab_name, strlen("MEMORY:negotiate_kerberos_auth_")+16, | |
554 | "MEMORY:negotiate_kerberos_auth_%d", (unsigned int) getpid()); | |
555 | ret = krb5_read_keytab(context, keytab_name, &ktlist); | |
556 | if (check_k5_err(context, "krb5_read_keytab", ret)) { | |
557 | debug((char *) "%s| %s: ERROR: Reading keytab %s into list failed\n", | |
558 | LogTime(), PROGRAM, keytab_name); | |
559 | } else { | |
560 | ret = krb5_write_keytab(context, ktlist, memory_keytab_name); | |
561 | if (check_k5_err(context, "krb5_write_keytab", ret)) { | |
562 | debug((char *) "%s| %s: ERROR: Writing list into keytab %s\n", | |
563 | LogTime(), PROGRAM, memory_keytab_name); | |
564 | } else { | |
c8df5896 | 565 | memory_keytab_name_env = (char *) xmalloc(strlen("KRB5_KTNAME=")+strlen(memory_keytab_name)+1); |
566 | strcpy(memory_keytab_name_env, "KRB5_KTNAME="); | |
567 | strcat(memory_keytab_name_env, memory_keytab_name); | |
568 | putenv(memory_keytab_name_env); | |
685277d8 MM |
569 | xfree(keytab_name); |
570 | keytab_name = xstrdup(memory_keytab_name); | |
571 | debug((char *) "%s| %s: INFO: Changed keytab to %s\n", | |
572 | LogTime(), PROGRAM, memory_keytab_name); | |
573 | } | |
574 | } | |
1aa0fb05 MM |
575 | ret = krb5_free_kt_list(context,ktlist); |
576 | if (check_k5_err(context, "krb5_free_kt_list", ret)) { | |
577 | debug((char *) "%s| %s: ERROR: Freeing list failed\n", | |
578 | LogTime(), PROGRAM); | |
579 | } | |
685277d8 MM |
580 | } |
581 | krb5_free_context(context); | |
582 | #endif | |
583 | #ifdef HAVE_HEIMDAL_KERBEROS | |
584 | gsskrb5_register_acceptor_identity(keytab_name); | |
585 | #endif | |
26ac0430 | 586 | while (1) { |
2e881a6f A |
587 | if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) { |
588 | if (ferror(stdin)) { | |
589 | debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n", | |
590 | LogTime(), PROGRAM, ferror(stdin), | |
591 | strerror(ferror(stdin))); | |
592 | ||
593 | fprintf(stdout, "BH input error\n"); | |
24885773 | 594 | exit(EXIT_FAILURE); /* BIIG buffer */ |
2e881a6f A |
595 | } |
596 | fprintf(stdout, "BH input error\n"); | |
24885773 | 597 | exit(EXIT_SUCCESS); |
2e881a6f A |
598 | } |
599 | c = (char *) memchr(buf, '\n', sizeof(buf) - 1); | |
600 | if (c) { | |
601 | *c = '\0'; | |
602 | length = c - buf; | |
603 | } else { | |
604 | err = 1; | |
605 | } | |
606 | if (err) { | |
607 | debug((char *) "%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM); | |
608 | fprintf(stdout, "BH Oversized message\n"); | |
609 | err = 0; | |
610 | continue; | |
611 | } | |
4ebcf1ce | 612 | debug((char *) "%s| %s: DEBUG: Got '%s' from squid (length: %ld).\n", LogTime(), PROGRAM, buf, length); |
2e881a6f A |
613 | |
614 | if (buf[0] == '\0') { | |
615 | debug((char *) "%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM); | |
616 | fprintf(stdout, "BH Invalid request\n"); | |
617 | continue; | |
618 | } | |
619 | if (strlen(buf) < 2) { | |
620 | debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf); | |
621 | fprintf(stdout, "BH Invalid request\n"); | |
622 | continue; | |
623 | } | |
624 | if (!strncmp(buf, "QQ", 2)) { | |
625 | gss_release_buffer(&minor_status, &input_token); | |
626 | gss_release_buffer(&minor_status, &output_token); | |
627 | gss_release_buffer(&minor_status, &service); | |
628 | gss_release_cred(&minor_status, &server_creds); | |
629 | if (server_name) | |
630 | gss_release_name(&minor_status, &server_name); | |
631 | if (client_name) | |
632 | gss_release_name(&minor_status, &client_name); | |
633 | if (gss_context != GSS_C_NO_CONTEXT) | |
634 | gss_delete_sec_context(&minor_status, &gss_context, NULL); | |
635 | if (kerberosToken) { | |
636 | /* Allocated by parseNegTokenInit, but no matching free function exists.. */ | |
637 | if (!spnego_flag) | |
4ebcf1ce | 638 | xfree(kerberosToken); |
2e881a6f A |
639 | } |
640 | if (spnego_flag) { | |
641 | /* Allocated by makeNegTokenTarg, but no matching free function exists.. */ | |
4ebcf1ce | 642 | xfree(spnegoToken); |
2e881a6f | 643 | } |
4ebcf1ce | 644 | xfree(token); |
c8df5896 | 645 | xfree(rcache_type); |
646 | xfree(rcache_type_env); | |
647 | xfree(rcache_dir); | |
648 | xfree(rcache_dir_env); | |
649 | xfree(keytab_name); | |
650 | xfree(keytab_name_env); | |
651 | #if HAVE_KRB5_MEMORY_KEYTAB | |
652 | krb5_kt_close(context, memory_keytab); | |
653 | xfree(memory_keytab_name); | |
654 | xfree(memory_keytab_name_env); | |
655 | #endif | |
656 | xfree(rfc_user); | |
2e881a6f | 657 | fprintf(stdout, "BH quit command\n"); |
24885773 | 658 | exit(EXIT_SUCCESS); |
2e881a6f A |
659 | } |
660 | if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) { | |
661 | debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf); | |
662 | fprintf(stdout, "BH Invalid request\n"); | |
663 | continue; | |
664 | } | |
665 | if (!strncmp(buf, "YR", 2)) { | |
666 | if (gss_context != GSS_C_NO_CONTEXT) | |
667 | gss_delete_sec_context(&minor_status, &gss_context, NULL); | |
668 | gss_context = GSS_C_NO_CONTEXT; | |
669 | } | |
670 | if (strlen(buf) <= 3) { | |
671 | debug((char *) "%s| %s: ERROR: Invalid negotiate request [%s]\n", LogTime(), PROGRAM, buf); | |
672 | fprintf(stdout, "BH Invalid negotiate request\n"); | |
673 | continue; | |
674 | } | |
1d11e9b3 | 675 | const char *b64Token = buf+3; |
40f220c3 | 676 | const size_t srcLen = strlen(buf+3); |
18db38a8 MM |
677 | input_token.length = BASE64_DECODE_LENGTH(srcLen); |
678 | debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length estimate: %d).\n", | |
aadbbd7d | 679 | LogTime(), PROGRAM, b64Token, (int) input_token.length); |
2e881a6f A |
680 | input_token.value = xmalloc(input_token.length); |
681 | ||
aadbbd7d AJ |
682 | struct base64_decode_ctx ctx; |
683 | base64_decode_init(&ctx); | |
684 | size_t dstLen = 0; | |
18db38a8 | 685 | if (!base64_decode_update(&ctx, &dstLen, static_cast<uint8_t*>(input_token.value), srcLen, b64Token) || |
aadbbd7d AJ |
686 | !base64_decode_final(&ctx)) { |
687 | debug((char *) "%s| %s: ERROR: Invalid base64 token [%s]\n", LogTime(), PROGRAM, b64Token); | |
688 | fprintf(stdout, "BH Invalid negotiate request token\n"); | |
689 | continue; | |
690 | } | |
691 | input_token.length = dstLen; | |
2e881a6f A |
692 | |
693 | if ((input_token.length >= sizeof ntlmProtocol + 1) && | |
694 | (!memcmp(input_token.value, ntlmProtocol, sizeof ntlmProtocol))) { | |
695 | debug((char *) "%s| %s: WARNING: received type %d NTLM token\n", | |
696 | LogTime(), PROGRAM, | |
697 | (int) *((unsigned char *) input_token.value + | |
698 | sizeof ntlmProtocol)); | |
699 | fprintf(stdout, "BH received type %d NTLM token\n", | |
700 | (int) *((unsigned char *) input_token.value + | |
701 | sizeof ntlmProtocol)); | |
702 | goto cleanup; | |
703 | } | |
704 | if (service_principal) { | |
705 | if (strcasecmp(service_principal, "GSS_C_NO_NAME")) { | |
706 | major_status = gss_import_name(&minor_status, &service, | |
707 | (gss_OID) GSS_C_NULL_OID, &server_name); | |
708 | ||
709 | } else { | |
710 | server_name = GSS_C_NO_NAME; | |
711 | major_status = GSS_S_COMPLETE; | |
4ebcf1ce | 712 | minor_status = 0; |
2e881a6f A |
713 | } |
714 | } else { | |
715 | major_status = gss_import_name(&minor_status, &service, | |
716 | gss_nt_service_name, &server_name); | |
717 | } | |
718 | ||
4ebcf1ce | 719 | if (check_gss_err(major_status, minor_status, "gss_import_name()", log, 1)) |
2e881a6f A |
720 | goto cleanup; |
721 | ||
722 | major_status = | |
723 | gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE, | |
724 | GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL); | |
4ebcf1ce | 725 | if (check_gss_err(major_status, minor_status, "gss_acquire_cred()", log, 1)) |
2e881a6f A |
726 | goto cleanup; |
727 | ||
728 | major_status = gss_accept_sec_context(&minor_status, | |
729 | &gss_context, | |
730 | server_creds, | |
731 | &input_token, | |
732 | GSS_C_NO_CHANNEL_BINDINGS, | |
733 | &client_name, NULL, &output_token, &ret_flags, NULL, NULL); | |
734 | ||
2e881a6f A |
735 | if (output_token.length) { |
736 | spnegoToken = (const unsigned char *) output_token.value; | |
737 | spnegoTokenLength = output_token.length; | |
aadbbd7d | 738 | token = (char *) xmalloc((size_t)base64_encode_len(spnegoTokenLength)); |
2e881a6f A |
739 | if (token == NULL) { |
740 | debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM); | |
741 | fprintf(stdout, "BH Not enough memory\n"); | |
742 | goto cleanup; | |
743 | } | |
aadbbd7d AJ |
744 | struct base64_encode_ctx tokCtx; |
745 | base64_encode_init(&tokCtx); | |
1d11e9b3 AJ |
746 | size_t blen = base64_encode_update(&tokCtx, token, spnegoTokenLength, reinterpret_cast<const uint8_t*>(spnegoToken)); |
747 | blen += base64_encode_final(&tokCtx, token+blen); | |
aadbbd7d | 748 | token[blen] = '\0'; |
2e881a6f | 749 | |
4ebcf1ce | 750 | if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log, 1)) |
2e881a6f A |
751 | goto cleanup; |
752 | if (major_status & GSS_S_CONTINUE_NEEDED) { | |
753 | debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM); | |
1c19010b | 754 | fprintf(stdout, "TT token=%s\n", token); |
2e881a6f A |
755 | goto cleanup; |
756 | } | |
757 | gss_release_buffer(&minor_status, &output_token); | |
758 | major_status = | |
759 | gss_display_name(&minor_status, client_name, &output_token, | |
760 | NULL); | |
761 | ||
4ebcf1ce | 762 | if (check_gss_err(major_status, minor_status, "gss_display_name()", log, 1)) |
2e881a6f A |
763 | goto cleanup; |
764 | user = (char *) xmalloc(output_token.length + 1); | |
765 | if (user == NULL) { | |
766 | debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM); | |
767 | fprintf(stdout, "BH Not enough memory\n"); | |
768 | goto cleanup; | |
769 | } | |
770 | memcpy(user, output_token.value, output_token.length); | |
771 | user[output_token.length] = '\0'; | |
772 | if (norealm && (p = strchr(user, '@')) != NULL) { | |
773 | *p = '\0'; | |
774 | } | |
4ebcf1ce MM |
775 | |
776 | #if HAVE_PAC_SUPPORT | |
777 | ret = krb5_init_context(&context); | |
778 | if (!check_k5_err(context, "krb5_init_context", ret)) { | |
1a22a39e | 779 | #if USE_HEIMDAL_KRB5 |
4ebcf1ce MM |
780 | #define ADWIN2KPAC 128 |
781 | major_status = gsskrb5_extract_authz_data_from_sec_context(&minor_status, | |
782 | gss_context, ADWIN2KPAC, &data_set); | |
783 | if (!check_gss_err(major_status, minor_status, | |
784 | "gsskrb5_extract_authz_data_from_sec_context()", log, 0)) { | |
785 | ret = krb5_pac_parse(context, data_set.value, data_set.length, &pac); | |
786 | gss_release_buffer(&minor_status, &data_set); | |
787 | if (!check_k5_err(context, "krb5_pac_parse", ret)) { | |
788 | ag = get_ad_groups((char *)&ad_groups, context, pac); | |
789 | krb5_pac_free(context, pac); | |
790 | } | |
791 | krb5_free_context(context); | |
792 | } | |
793 | #else | |
794 | type_id.value = (void *)"mspac"; | |
795 | type_id.length = strlen((char *)type_id.value); | |
796 | #define KRB5PACLOGONINFO 1 | |
797 | major_status = gss_map_name_to_any(&minor_status, client_name, KRB5PACLOGONINFO, &type_id, (gss_any_t *)&pac); | |
798 | if (!check_gss_err(major_status, minor_status, "gss_map_name_to_any()", log, 0)) { | |
799 | ag = get_ad_groups((char *)&ad_groups,context, pac); | |
800 | } | |
801 | (void)gss_release_any_name_mapping(&minor_status, client_name, &type_id, (gss_any_t *)&pac); | |
802 | krb5_free_context(context); | |
803 | #endif | |
804 | } | |
805 | if (ag) { | |
806 | debug((char *) "%s| %s: DEBUG: Groups %s\n", LogTime(), PROGRAM, ag); | |
807 | } | |
808 | #endif | |
4ebcf1ce | 809 | rfc_user = rfc1738_escape(user); |
2eb6054f | 810 | #if HAVE_PAC_SUPPORT |
1c19010b | 811 | fprintf(stdout, "OK token=%s user=%s %s\n", token, rfc_user, ag?ag:"group="); |
2eb6054f | 812 | #else |
1c19010b | 813 | fprintf(stdout, "OK token=%s user=%s\n", token, rfc_user); |
2eb6054f | 814 | #endif |
1c19010b | 815 | debug((char *) "%s| %s: DEBUG: OK token=%s user=%s\n", LogTime(), PROGRAM, token, rfc_user); |
2e881a6f A |
816 | if (log) |
817 | fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(), | |
2eb6054f | 818 | PROGRAM, rfc_user); |
2e881a6f A |
819 | goto cleanup; |
820 | } else { | |
4ebcf1ce | 821 | if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log, 1)) |
2e881a6f A |
822 | goto cleanup; |
823 | if (major_status & GSS_S_CONTINUE_NEEDED) { | |
824 | debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM); | |
4f3c41b3 AJ |
825 | // XXX: where to get the server token for delivery to client? token is nullptr here. |
826 | fprintf(stdout, "ERR\n"); | |
2e881a6f A |
827 | goto cleanup; |
828 | } | |
829 | gss_release_buffer(&minor_status, &output_token); | |
830 | major_status = | |
831 | gss_display_name(&minor_status, client_name, &output_token, | |
832 | NULL); | |
833 | ||
4ebcf1ce | 834 | if (check_gss_err(major_status, minor_status, "gss_display_name()", log, 1)) |
2e881a6f A |
835 | goto cleanup; |
836 | /* | |
837 | * Return dummy token AA. May need an extra return tag then AF | |
838 | */ | |
839 | user = (char *) xmalloc(output_token.length + 1); | |
840 | if (user == NULL) { | |
841 | debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM); | |
842 | fprintf(stdout, "BH Not enough memory\n"); | |
843 | goto cleanup; | |
844 | } | |
845 | memcpy(user, output_token.value, output_token.length); | |
846 | user[output_token.length] = '\0'; | |
847 | if (norealm && (p = strchr(user, '@')) != NULL) { | |
848 | *p = '\0'; | |
849 | } | |
2eb6054f MM |
850 | rfc_user = rfc1738_escape(user); |
851 | #if HAVE_PAC_SUPPORT | |
1c19010b | 852 | fprintf(stdout, "OK token=%s user=%s %s\n", "AA==", rfc_user, ag?ag:"group="); |
2eb6054f | 853 | #else |
1c19010b | 854 | fprintf(stdout, "OK token=%s user=%s\n", "AA==", rfc_user); |
2eb6054f | 855 | #endif |
1c19010b | 856 | debug((char *) "%s| %s: DEBUG: OK token=%s user=%s\n", LogTime(), PROGRAM, "AA==", rfc_user); |
2e881a6f A |
857 | if (log) |
858 | fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(), | |
2eb6054f | 859 | PROGRAM, rfc_user); |
2e881a6f A |
860 | } |
861 | cleanup: | |
862 | gss_release_buffer(&minor_status, &input_token); | |
863 | gss_release_buffer(&minor_status, &output_token); | |
864 | gss_release_cred(&minor_status, &server_creds); | |
865 | if (server_name) | |
866 | gss_release_name(&minor_status, &server_name); | |
867 | if (client_name) | |
868 | gss_release_name(&minor_status, &client_name); | |
869 | if (kerberosToken) { | |
870 | /* Allocated by parseNegTokenInit, but no matching free function exists.. */ | |
871 | if (!spnego_flag) | |
4ebcf1ce | 872 | safe_free(kerberosToken); |
2e881a6f A |
873 | } |
874 | if (spnego_flag) { | |
875 | /* Allocated by makeNegTokenTarg, but no matching free function exists.. */ | |
4ebcf1ce | 876 | safe_free(spnegoToken); |
2e881a6f | 877 | } |
4ebcf1ce MM |
878 | safe_free(token); |
879 | safe_free(user); | |
2e881a6f | 880 | continue; |
3e5d7cdf | 881 | } |
24885773 | 882 | return EXIT_SUCCESS; |
3e5d7cdf | 883 | } |
6989c6dc | 884 | #else |
074d6a40 | 885 | #include <cstdlib> |
6989c6dc AJ |
886 | #ifndef MAX_AUTHTOKEN_LEN |
887 | #define MAX_AUTHTOKEN_LEN 65535 | |
888 | #endif | |
889 | int | |
890 | main(int argc, char *const argv[]) | |
891 | { | |
892 | setbuf(stdout, NULL); | |
893 | setbuf(stdin, NULL); | |
894 | char buf[MAX_AUTHTOKEN_LEN]; | |
895 | while (1) { | |
2e881a6f A |
896 | if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) { |
897 | fprintf(stdout, "BH input error\n"); | |
24885773 | 898 | exit(EXIT_SUCCESS); |
2e881a6f A |
899 | } |
900 | fprintf(stdout, "BH Kerberos authentication not supported\n"); | |
6989c6dc | 901 | } |
24885773 | 902 | return EXIT_SUCCESS; |
6989c6dc | 903 | } |
3ad12bda | 904 | #endif /* HAVE_GSSAPI */ |
f53969cc | 905 |