Commit 920034ec authored by Jeremy Allison's avatar Jeremy Allison Committed by Gerald (Jerry) Carter
Browse files

r1182: Partial re-write of keytab code to clean up, remove memory leaks etc. Work in progress !

It seems the krb5 interfaces are so horrible it's impossible to write good error checking
code :-(.
Jeremy.
(This used to be commit 03f8c8bc07c9d8a378a34c271dcc088d17adb342)
parent a986d45e
Loading
Loading
Loading
Loading
+77 −47
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
   Copyright (C) Guenther Deschner 2003
   Copyright (C) Rakesh Patel 2004
   Copyright (C) Dan Perry 2004
   Copyright (C) Jeremy Allison 2004

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -28,37 +29,52 @@

#ifdef HAVE_KRB5

/**********************************************************************
 Converts a name to a fully qalified domain name.
***********************************************************************/

/*
void name_to_fqdn(fstring fqdn, const char *name)
{
	struct hostent *hp = sys_gethostbyname(name);
	if ( hp && hp->h_name && *hp->h_name ) {
		DEBUG(10,("name_to_fqdn: lookup for %s -> %s.\n", name, hp->h_name));
		fstrcpy(fqdn,hp->h_name);
	} else {
		DEBUG(10,("name_to_fqdn: lookup for %s failed.\n", name));
		fstrcpy(fqdn, name);
	}
}

/**********************************************************************
  Adds a single service principal, i.e. 'host' to the system keytab
*/
***********************************************************************/

int ads_keytab_add_entry(const char *srvPrinc, ADS_STRUCT *ads)
{
	krb5_error_code ret;
	krb5_context context;
	krb5_keytab keytab;
	krb5_kt_cursor cursor;
	krb5_keytab_entry entry;
	krb5_principal princ;
	krb5_error_code ret = 0;
	krb5_context context = NULL;
	krb5_keytab keytab = NULL;
	krb5_kt_cursor cursor = NULL;
	krb5_keytab_entry kt_entry;
	krb5_principal princ = NULL;
	krb5_data password;
	krb5_enctype *enctypes = NULL;
	krb5_kvno kvno;
	krb5_keyblock *key;
	krb5_keyblock *key = NULL;

	char *principal = NULL;
	char *princ_s = NULL;
	char *password_s = NULL;
	char keytab_name[MAX_KEYTAB_NAME_LEN];          /* This MAX_NAME_LEN is a constant defined in krb5.h */
	fstring myname;
	fstring my_fqdn;
	int i;
	int retval = 0;
	char *ktprinc = NULL;
	struct hostent *hp;

	ZERO_STRUCT(kt_entry);
	initialize_krb5_error_table();
	ret = krb5_init_context(&context);
	if (ret) {
		DEBUG(1,("could not krb5_init_context: %s\n",error_message(ret)));
		DEBUG(1,("ads_keytab_add_entry: could not krb5_init_context: %s\n",error_message(ret)));
		return -1;
	}
#ifdef HAVE_WRFILE_KEYTAB       /* MIT */
@@ -69,30 +85,31 @@ int ads_keytab_add_entry(const char *srvPrinc, ADS_STRUCT *ads)
	ret = krb5_kt_default_name(context, (char *) &keytab_name[0], MAX_KEYTAB_NAME_LEN - 2);
#endif
	if (ret) {
		DEBUG(1,("krb5_kt_default_name failed (%s)\n", error_message(ret)));
		DEBUG(1,("ads_keytab_add_entry: krb5_kt_default_name failed (%s)\n", error_message(ret)));
		goto out;
	}
	DEBUG(1,("Using default system keytab: %s\n", (char *) &keytab_name));
	DEBUG(2,("ads_keytab_add_entry: Using default system keytab: %s\n", (char *) &keytab_name));
	ret = krb5_kt_resolve(context, (char *) &keytab_name, &keytab);
	if (ret) {
		DEBUG(1,("krb5_kt_resolve failed (%s)\n", error_message(ret)));
		return ret;
		DEBUG(1,("ads_keytab_add_entry: krb5_kt_resolve failed (%s)\n", error_message(ret)));
		goto out;
	}

	ret = get_kerberos_allowed_etypes(context,&enctypes);
	if (ret) {
		DEBUG(1,("get_kerberos_allowed_etypes failed (%s)\n",error_message(ret)));
		DEBUG(1,("ads_keytab_add_entry: get_kerberos_allowed_etypes failed (%s)\n",error_message(ret)));
		goto out;
	}

	/* retrieve the password */
	if (!secrets_init()) {
		DEBUG(1,("secrets_init failed\n"));
		DEBUG(1,("ads_keytab_add_entry: secrets_init failed\n"));
		ret = -1;
		goto out;
	}
	password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
	if (!password_s) {
		DEBUG(1,("failed to fetch machine password\n"));
		DEBUG(1,("ads_keytab_add_entry: failed to fetch machine password\n"));
		ret = -1;
		goto out;
	}
@@ -100,23 +117,19 @@ int ads_keytab_add_entry(const char *srvPrinc, ADS_STRUCT *ads)
	password.length = strlen(password_s);

	/* Construct our principal */
	fstrcpy(myname, global_myname());
	hp = gethostbyname(myname);
	if ( hp->h_name && strlen(hp->h_name) > 0 ) {
		fstrcpy(myname,hp->h_name);
	}
	strlower_m(myname);
	asprintf(&princ_s, "%s/%s@%s", srvPrinc, myname, lp_realm());
	name_to_fqdn(my_fqdn, global_myname());
	strlower_m(my_fqdn);
	asprintf(&princ_s, "%s/%s@%s", srvPrinc, my_fqdn, lp_realm());

	ret = krb5_parse_name(context, princ_s, &princ);
	if (ret) {
		DEBUG(1,("krb5_parse_name(%s) failed (%s)\n", princ_s, error_message(ret)));
		DEBUG(1,("ads_keytab_add_entry: krb5_parse_name(%s) failed (%s)\n", princ_s, error_message(ret)));
		goto out;
	}

	kvno = (krb5_kvno) ads_get_kvno(ads, global_myname());
	if (kvno == -1) {       /* -1 indicates failure, everything else is OK */
		DEBUG(1,("ads_get_kvno failed to determine the system's kvno.\n"));
		DEBUG(1,("ads_keytab_add_entry: ads_get_kvno failed to determine the system's kvno.\n"));
		ret = -1;
		goto out;
	}
@@ -124,10 +137,14 @@ int ads_keytab_add_entry(const char *srvPrinc, ADS_STRUCT *ads)
	/* Seek and delete old keytab entries */
	ret = krb5_kt_start_seq_get(context, keytab, &cursor);
	if (ret != KRB5_KT_END && ret != ENOENT ) {
		DEBUG(1,("Will try to delete old keytab entries\n"));
		while(!krb5_kt_next_entry(context, keytab, &entry, &cursor)) {
		DEBUG(3,("ads_keytab_add_entry: Will try to delete old keytab entries\n"));
		while(!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {

			krb5_unparse_name(context, entry.principal, &ktprinc);
			ret = krb5_unparse_name(context, entry.principal, &ktprinc);
			if (ret) {
				DEBUG(1,("ads_keytab_add_entry: krb5_unparse_name failed (%s)\n", error_message(ret)));
				goto out;
			}

			/*---------------------------------------------------------------------------
			 * Save the entries with kvno - 1.   This is what microsoft does
@@ -138,12 +155,14 @@ int ads_keytab_add_entry(const char *srvPrinc, ADS_STRUCT *ads)
			 * with the new kvno.
			 */

			HERE

#ifdef HAVE_KRB5_KT_COMPARE
			if (krb5_kt_compare(context, &entry, princ, 0, 0) == True && entry.vno != kvno - 1) {
			if (krb5_kt_compare(context, &kt_entry, princ, 0, 0) == True && kt_entry.vno != kvno - 1) {
#else
			if (strcmp(ktprinc, princ_s) == 0 && entry.vno != kvno - 1) {
			if (strcmp(ktprinc, princ_s) == 0 && kt_entry.vno != kvno - 1) {
#endif
				free(ktprinc);
				SAFE_FREE(ktprinc);
				DEBUG(1,("Found old entry for principal: %s (kvno %d) - trying to remove it.\n",
					princ_s, entry.vno));
				ret = krb5_kt_end_seq_get(context, keytab, &cursor);
@@ -168,7 +187,7 @@ int ads_keytab_add_entry(const char *srvPrinc, ADS_STRUCT *ads)
				}
				continue;
			} else {
				free(ktprinc);
				SAFE_FREE(ktprinc);
			}

			ret = krb5_kt_free_entry(context, &entry);
@@ -231,22 +250,33 @@ int ads_keytab_add_entry(const char *srvPrinc, ADS_STRUCT *ads)

out:

	krb5_kt_close(context, keytab);
	SAFE_FREE(principal);
	SAFE_FREE(password_s);
	SAFE_FREE(princ_s);

	{
		krb5_keytab_entry zero_kt_entry;
		ZERO_STRUCT(zero_kt_entry);
		if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
			krb5_kt_free_entry(context, &kt_entry);
		}
	}
	if (princ) {
		krb5_free_principal(context, princ);
	}
	if (enctypes) {
		free(enctypes);
		free_kerberos_etypes(context, enctypes);
	}
	if (principal) {
		free(principal);
	if (cursor && keytab) {
		krb5_kt_end_seq_get(context, keytab, &cursor);	
	}
	if (password_s) {
		free(password_s);
	if (keytab) {
		krb5_kt_close(context, keytab);
	}
	if (princ_s) {
		free(princ_s);
	if (context) {
		krb5_free_context(context);
	}

	return ret;
	return (int)ret;
}