50 #if HAVE_KRB5_MEMORY_KEYTAB 
   51 typedef struct _krb5_kt_list {
 
   52     struct _krb5_kt_list *next;
 
   53     krb5_keytab_entry *entry;
 
   55 krb5_kt_list ktlist = 
nullptr;
 
   57 krb5_keytab memory_keytab;
 
   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,
 
   63 krb5_error_code krb5_read_keytab(krb5_context context,
 
   65                                  krb5_kt_list *kt_list);
 
   69 check_k5_err(krb5_context context, 
const char *
function, krb5_error_code code)
 
   72     if (code && code != KRB5_KT_END) {
 
   74         errmsg = krb5_get_error_message(context, code);
 
   76         fprintf(stderr, 
"%s| %s: ERROR: %s: %s\n", 
LogTime(), 
PROGRAM, 
function, errmsg);
 
   77 #if HAVE_KRB5_FREE_ERROR_MESSAGE 
   78         krb5_free_error_message(context, errmsg);
 
   79 #elif HAVE_KRB5_FREE_ERROR_STRING 
   80         krb5_free_error_string(context, (
char *)errmsg);
 
   95     struct addrinfo *hres = 
nullptr, *hres_list;
 
  100         debug((
char *) 
"%s| %s: ERROR: resolving hostname '%s' failed\n", 
LogTime(), 
PROGRAM, hostname);
 
  101         fprintf(stderr, 
"%s| %s: ERROR: resolving hostname '%s' failed\n",
 
  105     rc = getaddrinfo(hostname, 
nullptr, 
nullptr, &hres);
 
  106     if (rc != 0 || hres == 
nullptr ) {
 
  107         debug((
char *) 
"%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
 
  110                 "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
 
  116         hres_list = hres_list->ai_next;
 
  118     rc = getnameinfo(hres->ai_addr, hres->ai_addrlen, hostname,
 
  119                      sizeof(hostname), 
nullptr, 0, 0);
 
  121         debug((
char *) 
"%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
 
  124                 "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
 
  130     hostname[
sizeof(hostname)-1] = 
'\0';
 
  135 check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
 
  136               const char *
function, 
int log, 
int sout)
 
  138     if (GSS_ERROR(major_status)) {
 
  139         OM_uint32 maj_stat, min_stat;
 
  140         OM_uint32 msg_ctx = 0;
 
  141         gss_buffer_desc status_string;
 
  149             maj_stat = gss_display_status(&min_stat, major_status,
 
  150                                           GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
 
  151             if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
 
  152                 if (
sizeof(buf) > len + status_string.length + 1) {
 
  153                     snprintf(buf + len, (
sizeof(buf) - len), 
"%s", (
char *) status_string.value);
 
  154                     len += status_string.length;
 
  158             gss_release_buffer(&min_stat, &status_string);
 
  160         if (
sizeof(buf) > len + 2) {
 
  161             snprintf(buf + len, (
sizeof(buf) - len), 
"%s", 
". ");
 
  167             maj_stat = gss_display_status(&min_stat, minor_status,
 
  168                                           GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
 
  169             if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
 
  170                 if (
sizeof(buf) > len + status_string.length) {
 
  171                     snprintf(buf + len, (
sizeof(buf) - len), 
"%s", (
char *) status_string.value);
 
  172                     len += status_string.length;
 
  176             gss_release_buffer(&min_stat, &status_string);
 
  180             fprintf(stdout, 
"BH %s failed: %s\n", 
function, buf);
 
  182             fprintf(stderr, 
"%s| %s: INFO: User not authenticated\n", 
LogTime(),
 
  189 #if HAVE_KRB5_MEMORY_KEYTAB 
  193 krb5_error_code krb5_free_kt_list(krb5_context context, krb5_kt_list list)
 
  195     krb5_kt_list lp = list;
 
  198 #if HAVE_LIBHEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY ) 
  199         krb5_error_code  retval = krb5_kt_free_entry(context, lp->entry);
 
  201         krb5_error_code  retval = krb5_free_keytab_entry_contents(context, lp->entry);
 
  204         if (
check_k5_err(context, 
"krb5_kt_free_entry", retval))
 
  206         krb5_kt_list prev = lp;
 
  216 krb5_error_code krb5_read_keytab(krb5_context context, 
char *name, krb5_kt_list *list)
 
  218     krb5_kt_list lp = 
nullptr, tail = 
nullptr, back = 
nullptr;
 
  220     krb5_keytab_entry *entry;
 
  221     krb5_kt_cursor cursor;
 
  222     krb5_error_code retval = 0;
 
  226         for (lp = *list; lp->next; lp = lp->next);
 
  229     retval = krb5_kt_resolve(context, name, &kt);
 
  232     retval = krb5_kt_start_seq_get(context, kt, &cursor);
 
  233     if (
check_k5_err(context, 
"krb5_kt_start_seq_get", retval))
 
  236         entry = (krb5_keytab_entry *)
xcalloc(1, 
sizeof (krb5_keytab_entry));
 
  239             debug((
char *) 
"%s| %s: ERROR: krb5_read_keytab failed: %s\n",
 
  241             fprintf(stderr, 
"%s| %s: ERROR: krb5_read_keytab: %s\n",
 
  245         memset(entry, 0, 
sizeof (*entry));
 
  246         retval = krb5_kt_next_entry(context, kt, entry, &cursor);
 
  247         if (
check_k5_err(context, 
"krb5_kt_next_entry", retval))
 
  251             lp = (krb5_kt_list)
xmalloc(
sizeof (*lp));
 
  254                 debug((
char *) 
"%s| %s: ERROR: krb5_read_keytab failed: %s\n",
 
  256                 fprintf(stderr, 
"%s| %s: ERROR: krb5_read_keytab: %s\n",
 
  261             lp->next = (krb5_kt_list)
xmalloc(
sizeof (*lp));
 
  264                 debug((
char *) 
"%s| %s: ERROR: krb5_read_keytab failed: %s\n",
 
  266                 fprintf(stderr, 
"%s| %s: ERROR: krb5_read_keytab: %s\n",
 
  279         if (retval == KRB5_KT_END)
 
  282             krb5_free_kt_list(context, tail);
 
  285                 back->next = 
nullptr;
 
  290     krb5_kt_end_seq_get(context, kt, &cursor);
 
  292     krb5_kt_close(context, kt);
 
  299 krb5_error_code krb5_write_keytab(krb5_context context, krb5_kt_list list, 
char *name)
 
  302     krb5_error_code retval = 0;
 
  304     snprintf(ktname, 
sizeof(ktname), 
"%s", name);
 
  305     retval = krb5_kt_resolve(context, ktname, &memory_keytab);
 
  308     for (krb5_kt_list lp = list; lp; lp = lp->next) {
 
  309         retval = krb5_kt_add_entry(context, memory_keytab, lp->entry);
 
  321 main(
int argc, 
char *
const argv[])
 
  325     char *user = 
nullptr;
 
  326     char *rfc_user = 
nullptr;
 
  328     char ad_groups[MAX_PAC_GROUP_SIZE];
 
  331 #if HAVE_LIBHEIMDAL_KRB5 
  332     gss_buffer_desc data_set = GSS_C_EMPTY_BUFFER;
 
  334     gss_buffer_desc type_id = GSS_C_EMPTY_BUFFER;
 
  337     krb5_context context = 
nullptr;
 
  341     int opt, 
log = 0, norealm = 0;
 
  342     OM_uint32 ret_flags = 0, spnego_flag = 0;
 
  343     char *
service_name = (
char *) 
"HTTP", *host_name = 
nullptr;
 
  344     char *token = 
nullptr;
 
  345     char *service_principal = 
nullptr;
 
  346     char *keytab_name = 
nullptr;
 
  347     char *keytab_name_env = 
nullptr;
 
  349 #if HAVE_KRB5_MEMORY_KEYTAB 
  350     char *memory_keytab_name = 
nullptr;
 
  352     char *rcache_type = 
nullptr;
 
  353     char *rcache_dir = 
nullptr;
 
  354     OM_uint32 major_status, minor_status;
 
  355     gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
 
  356     gss_name_t client_name = GSS_C_NO_NAME;
 
  357     gss_name_t server_name = GSS_C_NO_NAME;
 
  358     gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
 
  359     gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
 
  360     gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
 
  361     gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
 
  362     const unsigned char *kerberosToken = 
nullptr;
 
  363     const unsigned char *spnegoToken = 
nullptr;
 
  364     size_t spnegoTokenLength = 0;
 
  366     setbuf(stdout, 
nullptr);
 
  367     setbuf(stdin, 
nullptr);
 
  369     while (-1 != (opt = 
getopt(argc, argv, 
"dirs:k:c:t:"))) {
 
  388                 fprintf(stderr, 
"ERROR: keytab file not given\n");
 
  395             if ((ktp=strchr(keytab_name,
':')))
 
  399             if (stat((
const char*)ktp, &fstat)) {
 
  401                     fprintf(stderr, 
"ERROR: keytab file %s does not exist\n",keytab_name);
 
  403                     fprintf(stderr, 
"ERROR: Error %s during stat of keytab file %s\n",
strerror(errno),keytab_name);
 
  405             } 
else if (!S_ISREG(fstat.st_mode)) {
 
  406                 fprintf(stderr, 
"ERROR: keytab file %s is not a file\n",keytab_name);
 
  411             if (access(ktp, R_OK)) {
 
  412                 fprintf(stderr, 
"ERROR: keytab file %s is not accessible\n",keytab_name);
 
  424                 fprintf(stderr, 
"ERROR: replay cache directory not given\n");
 
  431             if (stat((
const char*)rcache_dir, &dstat)) {
 
  433                     fprintf(stderr, 
"ERROR: replay cache directory %s does not exist\n",rcache_dir);
 
  435                     fprintf(stderr, 
"ERROR: Error %s during stat of replay cache directory %s\n",
strerror(errno),rcache_dir);
 
  437             } 
else if (!S_ISDIR(dstat.st_mode)) {
 
  438                 fprintf(stderr, 
"ERROR: replay cache directory %s is not a directory\n",rcache_dir);
 
  443             if (access(rcache_dir, W_OK)) {
 
  444                 fprintf(stderr, 
"ERROR: replay cache directory %s is not accessible\n",rcache_dir);
 
  453                 fprintf(stderr, 
"ERROR: replay cache type not given\n");
 
  461                 fprintf(stderr, 
"ERROR: service principal not given\n");
 
  466             fprintf(stderr, 
"Usage: \n");
 
  467             fprintf(stderr, 
"squid_kerb_auth [-d] [-i] [-s SPN] [-k keytab] [-c rcdir] [-t rctype]\n");
 
  468             fprintf(stderr, 
"-d full debug\n");
 
  469             fprintf(stderr, 
"-i informational messages\n");
 
  470             fprintf(stderr, 
"-r remove realm from username\n");
 
  471             fprintf(stderr, 
"-s service principal name\n");
 
  472             fprintf(stderr, 
"-k keytab name\n");
 
  473             fprintf(stderr, 
"-c replay cache directory\n");
 
  474             fprintf(stderr, 
"-t replay cache type\n");
 
  476                     "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
 
  477             fprintf(stderr, 
"default SPN is HTTP/fqdn@DEFAULT_REALM\n");
 
  483     if (service_principal && strcasecmp(service_principal, 
"GSS_C_NO_NAME")) {
 
  484         if (!strstr(service_principal,
"HTTP/")) {
 
  485             debug((
char *) 
"%s| %s: WARN: service_principal %s does not start with HTTP/\n",
 
  488         service.value = service_principal;
 
  489         service.length = strlen((
char *) service.value);
 
  494                     "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
 
  496             fprintf(stdout, 
"BH hostname error\n");
 
  500         snprintf((
char *) service.value, strlen(
service_name) + strlen(host_name) + 2,
 
  502         service.length = strlen((
char *) service.value);
 
  507         (void)setenv(
"KRB5RCACHETYPE", rcache_type, 1);
 
  508         debug((
char *) 
"%s| %s: INFO: Setting replay cache type to %s\n",
 
  513         (void)setenv(
"KRB5RCACHEDIR", rcache_dir, 1);
 
  514         debug((
char *) 
"%s| %s: INFO: Setting replay cache directory to %s\n",
 
  519         (void)setenv(
"KRB5_KTNAME", keytab_name, 1);
 
  521         keytab_name_env = getenv(
"KRB5_KTNAME");
 
  522         if (!keytab_name_env) {
 
  523             ret = krb5_init_context(&context);
 
  525                 krb5_kt_default_name(context, default_keytab, 
MAXPATHLEN);
 
  527             keytab_name = 
xstrdup(default_keytab);
 
  528             krb5_free_context(context);
 
  530             keytab_name = 
xstrdup(keytab_name_env);
 
  533 #if HAVE_KRB5_MEMORY_KEYTAB 
  534     ret = krb5_init_context(&context);
 
  536         memory_keytab_name = (
char *)
xmalloc(strlen(
"MEMORY:negotiate_kerberos_auth_")+16);
 
  537         snprintf(memory_keytab_name, strlen(
"MEMORY:negotiate_kerberos_auth_")+16,
 
  538                  "MEMORY:negotiate_kerberos_auth_%d", (
unsigned int) getpid());
 
  539         ret = krb5_read_keytab(context, keytab_name, &ktlist);
 
  541             debug((
char *) 
"%s| %s: ERROR: Reading keytab %s into list failed\n",
 
  544             ret = krb5_write_keytab(context, ktlist, memory_keytab_name);
 
  546                 debug((
char *) 
"%s| %s: ERROR: Writing list into keytab %s\n",
 
  549                 (void)setenv(
"KRB5_KTNAME", memory_keytab_name, 1);
 
  551                 keytab_name = 
xstrdup(memory_keytab_name);
 
  552                 debug((
char *) 
"%s| %s: INFO: Changed keytab to %s\n",
 
  556         ret = krb5_free_kt_list(context,ktlist);
 
  558             debug((
char *) 
"%s| %s: ERROR: Freeing list failed\n",
 
  562     krb5_free_context(context);
 
  564 #ifdef HAVE_HEIMDAL_KERBEROS 
  565     gsskrb5_register_acceptor_identity(keytab_name);
 
  568         if (fgets(buf, 
sizeof(buf) - 1, stdin) == 
nullptr) {
 
  570                 debug((
char *) 
"%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
 
  574                 fprintf(stdout, 
"BH input error\n");
 
  577             fprintf(stdout, 
"BH input error\n");
 
  580         c = (
char *) memchr(buf, 
'\n', 
sizeof(buf) - 1);
 
  589             fprintf(stdout, 
"BH Oversized message\n");
 
  593         debug((
char *) 
"%s| %s: DEBUG: Got '%s' from squid (length: %ld).\n", 
LogTime(), 
PROGRAM, buf, length);
 
  595         if (buf[0] == 
'\0') {
 
  597             fprintf(stdout, 
"BH Invalid request\n");
 
  600         if (strlen(buf) < 2) {
 
  602             fprintf(stdout, 
"BH Invalid request\n");
 
  605         if (!strncmp(buf, 
"QQ", 2)) {
 
  606             gss_release_buffer(&minor_status, &input_token);
 
  607             gss_release_buffer(&minor_status, &output_token);
 
  608             gss_release_buffer(&minor_status, &service);
 
  609             gss_release_cred(&minor_status, &server_creds);
 
  611                 gss_release_name(&minor_status, &server_name);
 
  613                 gss_release_name(&minor_status, &client_name);
 
  614             if (gss_context != GSS_C_NO_CONTEXT)
 
  615                 gss_delete_sec_context(&minor_status, &gss_context, 
nullptr);
 
  619                     xfree(kerberosToken);
 
  629 #if HAVE_KRB5_MEMORY_KEYTAB 
  630             krb5_kt_close(context, memory_keytab);
 
  631             xfree(memory_keytab_name);
 
  634             fprintf(stdout, 
"BH quit command\n");
 
  637         if (strncmp(buf, 
"YR", 2) && strncmp(buf, 
"KK", 2)) {
 
  639             fprintf(stdout, 
"BH Invalid request\n");
 
  642         if (!strncmp(buf, 
"YR", 2)) {
 
  643             if (gss_context != GSS_C_NO_CONTEXT)
 
  644                 gss_delete_sec_context(&minor_status, &gss_context, 
nullptr);
 
  645             gss_context = GSS_C_NO_CONTEXT;
 
  647         if (strlen(buf) <= 3) {
 
  648             debug((
char *) 
"%s| %s: ERROR: Invalid negotiate request [%s]\n", 
LogTime(), 
PROGRAM, buf);
 
  649             fprintf(stdout, 
"BH Invalid negotiate request\n");
 
  652         const char *b64Token = buf+3;
 
  653         const size_t srcLen = strlen(buf+3);
 
  655         debug((
char *) 
"%s| %s: DEBUG: Decode '%s' (decoded length estimate: %d).\n",
 
  657         input_token.value = 
xmalloc(input_token.length);
 
  662         if (!
base64_decode_update(&ctx, &dstLen, 
static_cast<uint8_t*
>(input_token.value), srcLen, b64Token) ||
 
  664             debug((
char *) 
"%s| %s: ERROR: Invalid base64 token [%s]\n", 
LogTime(), 
PROGRAM, b64Token);
 
  665             fprintf(stdout, 
"BH Invalid negotiate request token\n");
 
  668         input_token.length = dstLen;
 
  672             debug((
char *) 
"%s| %s: WARNING: received type %d NTLM token\n",
 
  674                   (
int) *((
unsigned char *) input_token.value +
 
  676             fprintf(stdout, 
"BH received type %d NTLM token\n",
 
  677                     (
int) *((
unsigned char *) input_token.value +
 
  681         if (service_principal) {
 
  682             if (strcasecmp(service_principal, 
"GSS_C_NO_NAME")) {
 
  683                 major_status = gss_import_name(&minor_status, &service,
 
  684                                                (gss_OID) GSS_C_NULL_OID, &server_name);
 
  687                 server_name = GSS_C_NO_NAME;
 
  688                 major_status = GSS_S_COMPLETE;
 
  692             major_status = gss_import_name(&minor_status, &service,
 
  700             gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
 
  701                              GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, 
nullptr, 
nullptr);
 
  702         if (
check_gss_err(major_status, minor_status, 
"gss_acquire_cred()", 
log, 1))
 
  705         major_status = gss_accept_sec_context(&minor_status,
 
  709                                               GSS_C_NO_CHANNEL_BINDINGS,
 
  710                                               &client_name, 
nullptr, &output_token, &ret_flags, 
nullptr, 
nullptr);
 
  712         if (output_token.length) {
 
  713             spnegoToken = (
const unsigned char *) output_token.value;
 
  714             spnegoTokenLength = output_token.length;
 
  716             if (token == 
nullptr) {
 
  718                 fprintf(stdout, 
"BH Not enough memory\n");
 
  723             size_t blen = 
base64_encode_update(&tokCtx, token, spnegoTokenLength, 
reinterpret_cast<const uint8_t*
>(spnegoToken));
 
  727             if (
check_gss_err(major_status, minor_status, 
"gss_accept_sec_context()", 
log, 1))
 
  729             if (major_status & GSS_S_CONTINUE_NEEDED) {
 
  731                 fprintf(stdout, 
"TT token=%s\n", token);
 
  734             gss_release_buffer(&minor_status, &output_token);
 
  736                 gss_display_name(&minor_status, client_name, &output_token,
 
  739             if (
check_gss_err(major_status, minor_status, 
"gss_display_name()", 
log, 1))
 
  741             user = (
char *) 
xmalloc(output_token.length + 1);
 
  742             if (user == 
nullptr) {
 
  744                 fprintf(stdout, 
"BH Not enough memory\n");
 
  747             memcpy(user, output_token.value, output_token.length);
 
  748             user[output_token.length] = 
'\0';
 
  749             if (norealm && (p = strchr(user, 
'@')) != 
nullptr) {
 
  754             ret = krb5_init_context(&context);
 
  756 #if HAVE_LIBHEIMDAL_KRB5 
  757 #define ADWIN2KPAC 128 
  758                 major_status = gsskrb5_extract_authz_data_from_sec_context(&minor_status,
 
  759                                gss_context, ADWIN2KPAC, &data_set);
 
  761                                    "gsskrb5_extract_authz_data_from_sec_context()", 
log, 0)) {
 
  762                     ret = krb5_pac_parse(context, data_set.value, data_set.length, &pac);
 
  763                     gss_release_buffer(&minor_status, &data_set);
 
  765                         ag = get_ad_groups((
char *)&ad_groups, context, pac);
 
  766                         krb5_pac_free(context, pac);
 
  768                     krb5_free_context(context);
 
  771                 type_id.value = (
void *)
"mspac";
 
  772                 type_id.length = strlen((
char *)type_id.value);
 
  773 #define KRB5PACLOGONINFO        1 
  774                 major_status = gss_map_name_to_any(&minor_status, client_name, KRB5PACLOGONINFO, &type_id, (gss_any_t *)&pac);
 
  775                 if (!
check_gss_err(major_status, minor_status, 
"gss_map_name_to_any()", 
log, 0)) {
 
  776                     ag = get_ad_groups((
char *)&ad_groups,context, pac);
 
  778                 (void)gss_release_any_name_mapping(&minor_status, client_name, &type_id, (gss_any_t *)&pac);
 
  779                 krb5_free_context(context);
 
  788             fprintf(stdout, 
"OK token=%s user=%s %s\n", token, rfc_user, ag?ag:
"group=");
 
  790             fprintf(stdout, 
"OK token=%s user=%s\n", token, rfc_user);
 
  792             debug((
char *) 
"%s| %s: DEBUG: OK token=%s user=%s\n", 
LogTime(), 
PROGRAM, token, rfc_user);
 
  794                 fprintf(stderr, 
"%s| %s: INFO: User %s authenticated\n", 
LogTime(),
 
  798             if (
check_gss_err(major_status, minor_status, 
"gss_accept_sec_context()", 
log, 1))
 
  800             if (major_status & GSS_S_CONTINUE_NEEDED) {
 
  803                 fprintf(stdout, 
"ERR\n");
 
  806             gss_release_buffer(&minor_status, &output_token);
 
  808                 gss_display_name(&minor_status, client_name, &output_token,
 
  811             if (
check_gss_err(major_status, minor_status, 
"gss_display_name()", 
log, 1))
 
  816             user = (
char *) 
xmalloc(output_token.length + 1);
 
  817             if (user == 
nullptr) {
 
  819                 fprintf(stdout, 
"BH Not enough memory\n");
 
  822             memcpy(user, output_token.value, output_token.length);
 
  823             user[output_token.length] = 
'\0';
 
  824             if (norealm && (p = strchr(user, 
'@')) != 
nullptr) {
 
  829             fprintf(stdout, 
"OK token=%s user=%s %s\n", 
"AA==", rfc_user, ag?ag:
"group=");
 
  831             fprintf(stdout, 
"OK token=%s user=%s\n", 
"AA==", rfc_user);
 
  833             debug((
char *) 
"%s| %s: DEBUG: OK token=%s user=%s\n", 
LogTime(), 
PROGRAM, 
"AA==", rfc_user);
 
  835                 fprintf(stderr, 
"%s| %s: INFO: User %s authenticated\n", 
LogTime(),
 
  839         gss_release_buffer(&minor_status, &input_token);
 
  840         gss_release_buffer(&minor_status, &output_token);
 
  841         gss_release_cred(&minor_status, &server_creds);
 
  843             gss_release_name(&minor_status, &server_name);
 
  845             gss_release_name(&minor_status, &client_name);
 
  863 #ifndef MAX_AUTHTOKEN_LEN 
  864 #define MAX_AUTHTOKEN_LEN   65535 
  867 main(
int argc, 
char *
const argv[])
 
  869     setbuf(stdout, 
nullptr);
 
  870     setbuf(stdin, 
nullptr);
 
  873         if (fgets(buf, 
sizeof(buf) - 1, stdin) == 
NULL) {
 
  874             fprintf(stdout, 
"BH input error\n");
 
  877         fprintf(stdout, 
"BH Kerberos authentication not supported\n");