Memory leak or programing error ?
Markus Moeller
huaraz at moeller.plus.com
Sun Aug 23 11:35:28 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
export KRB5RCACHETYPE=none
Thank you
Markus
More information about the Kerberos
mailing list