Memory leak or programing error

Markus Moeller huaraz at moeller.plus.com
Sun Aug 23 17:41:55 EDT 2009


I am working on an application to do gssapi authentication and noticed
increased memory usage. I created the following test application:

/*
 */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>


#ifndef HEIMDAL
#define HEIMDAL 0
#endif
#if HEIMDAL
#define HAVE_HEIMDAL_KERBEROS 1
#define HAVE_GSSAPI_GSSAPI_H 1
#else
#define HAVE_MIT_KERBEROS 1
#define HAVE_GSSAPI_GSSAPI_GENERIC_H 1
#define HAVE_GSSAPI_GSSAPI_H 1
#define HAVE_GSSAPI_GSSAPI_KRB5_H 1
#endif

#ifdef HAVE_HEIMDAL_KERBEROS
#ifdef HAVE_GSSAPI_GSSAPI_H
#include <gssapi/gssapi.h>
#elif defined(HAVE_GSSAPI_H)
#include <gssapi.h>
#else
#error "GSSAPI header required"
#endif
#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
#else
#ifdef HAVE_SEAM_KERBEROS
#ifdef HAVE_GSSAPI_GSSAPI_H
#include <gssapi/gssapi.h>
#elif defined(HAVE_GSSAPI_H)
#include <gssapi.h>
#else
#error "GSSAPI header required"
#endif
#ifdef HAVE_GSSAPI_GSSAPI_EXT_H
#include <gssapi/gssapi_ext.h>
#endif
#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
#else /*MIT*/
#ifdef HAVE_GSSAPI_GSSAPI_H
#include <gssapi/gssapi.h>
#elif defined(HAVE_GSSAPI_H)
#include <gssapi.h>
#else
#error "GSSAPI header required"
#endif
#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H
#include <gssapi/gssapi_krb5.h>
#endif
#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
#include <gssapi/gssapi_generic.h>
#endif
#endif
#endif

#include "base64.h"

#ifndef MAX_AUTHTOKEN_LEN
#define MAX_AUTHTOKEN_LEN   65535
#endif

static const unsigned char ntlmProtocol [] = {'N', 'T', 'L', 'M', 'S', 'S',
'P', 0};

int main(int argc, char * const argv[])
{
  char buf[MAX_AUTHTOKEN_LEN];
  char *c;
  int length=0;
  static int err=0;
  OM_uint32 ret_flags=0;
  char *service_name=(char *)"HTTP";
  char *host_name=(char *)"opensuse11.suse.home";
  char *token = NULL;
  OM_uint32 major_status, minor_status;
  gss_ctx_id_t          gss_context = GSS_C_NO_CONTEXT;
  gss_name_t            client_name = GSS_C_NO_NAME;
  gss_name_t            server_name = GSS_C_NO_NAME;
  gss_cred_id_t         server_creds = GSS_C_NO_CREDENTIAL;
  gss_buffer_desc       service = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc       input_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc       output_token = GSS_C_EMPTY_BUFFER;

  setbuf(stdout,NULL);
  setbuf(stdin,NULL);

  if ( !host_name ) {
      fprintf(stderr, "Local hostname could not be determined. Please
specify the service principal\n");
      exit(-1);
    }
  service.value = malloc(strlen(service_name)+strlen(host_name)+2);
  snprintf(service.value,strlen(service_name)+strlen(host_name)+2,"%s@%s",service_name,host_name);
  service.length = strlen((char *)service.value);
  fprintf(stderr, "Use service %.*s \n",service.length,service.value);

  while (1) {
    if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
      if (ferror(stdin)) {
        fprintf(stderr, "fgets() failed! dying..... errno=%d (%s)\n",
ferror(stdin), strerror(ferror(stdin)));
        exit(1);    /* BIIG buffer */
      }
      exit(0);
    }

    c=memchr(buf,'\n',sizeof(buf)-1);
    if (c) {
      *c = '\0';
      length = c-buf;
    } else {
      err = 1;
    }
    if (err) {
      fprintf(stderr, "Oversized message\n");
      err = 0;
      continue;
    }

    if (buf[0] == '\0') {
      fprintf(stderr, "Invalid request\n");
      continue;
    }

    if (!strncmp(buf,"QQ",2)){
      gss_release_buffer(&minor_status, &input_token);
      gss_release_buffer(&minor_status, &output_token);
      gss_delete_sec_context(&minor_status, &gss_context, &output_token);
      gss_release_buffer(&minor_status, &output_token);
      gss_context = GSS_C_NO_CONTEXT;
      gss_release_cred(&minor_status, &server_creds);
      if (server_name)
        gss_release_name(&minor_status, &server_name);
      if (client_name)
        gss_release_name(&minor_status, &client_name);
      if (token) {
        free(token);
        token=NULL;
      }
      fprintf(stderr, "Quit\n");
      exit (1);
    }

    input_token.length = base64_decode_len(buf);
    input_token.value = malloc(input_token.length);

    base64_decode(input_token.value,buf,input_token.length);


    if ((input_token.length >= sizeof ntlmProtocol + 1) &&
        (!memcmp (input_token.value, ntlmProtocol, sizeof ntlmProtocol))) {
      fprintf(stderr, "received type %d NTLM token\n", (int) *((unsigned
char *)input_token.value + sizeof ntlmProtocol));
      goto cleanup;
    }

    major_status = gss_import_name(&minor_status, &service,
                                     gss_nt_service_name, &server_name);

    if (GSS_ERROR(major_status)) {
      fprintf(stderr, "gss_import_name error\n");
      goto cleanup;
    }

    major_status = gss_acquire_cred(&minor_status, server_name,
GSS_C_INDEFINITE,
                                    GSS_C_NO_OID_SET, GSS_C_ACCEPT,
&server_creds,
                                    NULL, NULL);
    if (GSS_ERROR(major_status)) {
      fprintf(stderr, "gss_acquire_cred error\n");
      goto cleanup;
    }

    major_status = gss_accept_sec_context(&minor_status,
                                          &gss_context,
                                          server_creds,
                                          &input_token,
                                          GSS_C_NO_CHANNEL_BINDINGS,
                                          &client_name,
                                          NULL,
                                          &output_token,
                                          &ret_flags,
                                          NULL,
                                          NULL);


    if (output_token.length) {
      token = malloc(base64_encode_len(output_token.length));
      if (token == NULL) {
        fprintf(stderr, "Not enough memory\n");
        goto cleanup;
      }

      base64_encode(token,(const char
*)output_token.value,base64_encode_len(output_token.length),output_token.length);

      if (GSS_ERROR(major_status)) {
        fprintf(stderr, "gss_accept_sec_context error\n");
        goto cleanup;
      }
      if (major_status & GSS_S_CONTINUE_NEEDED) {
        fprintf(stderr, "continuation needed\n");
        goto cleanup;
      }
      gss_release_buffer(&minor_status, &output_token);
      major_status = gss_display_name(&minor_status, client_name,
&output_token, NULL);

      if (GSS_ERROR(major_status)) {
        fprintf(stderr, "gss_display_name error\n");
        goto cleanup;
      }
      fprintf(stderr, "User %s authenticated\n", (char
*)output_token.value);
      goto cleanup;
    } else {
      if (GSS_ERROR(major_status)) {
        fprintf(stderr, "gss_accept_sec_context error\n");
        goto cleanup;
      }
      if (major_status & GSS_S_CONTINUE_NEEDED) {
        fprintf(stderr, "continuation needed\n");
        goto cleanup;
      }
      gss_release_buffer(&minor_status, &output_token);
      major_status = gss_display_name(&minor_status, client_name,
&output_token, NULL);

      if (GSS_ERROR(major_status)) {
        fprintf(stderr, "gss_display_name error\n");
        goto cleanup;
      }
      fprintf(stderr, "User %s authenticated\n", (char
*)output_token.value);
    }
cleanup:
    gss_release_buffer(&minor_status, &input_token);
    gss_release_buffer(&minor_status, &output_token);
    gss_delete_sec_context(&minor_status, &gss_context, &output_token);
    gss_release_buffer(&minor_status, &output_token);
    gss_context = GSS_C_NO_CONTEXT;
    gss_release_cred(&minor_status, &server_creds);
    if (server_name)
      gss_release_name(&minor_status, &server_name);
    if (client_name)
      gss_release_name(&minor_status, &client_name);
    if (token) {
      free(token);
      token=NULL;
    }
    continue;
  }
}

It works fine with MIT 1.6.3 for successful authentications, but when I get 
errors in gss_accept_sec_context I get memory leaks when using spnego tokens 
as input.

Find attached my valgrind output for 20 runs. In the failure case it means 
the first is successful and the others are replays.

I also see problems when I don't use a replay cache by using even with no gss_accept_sec_context failure. 

export KRB5RCACHETYPE=none


Thank you
Markus



More information about the Kerberos mailing list