mirror of
				https://github.com/samba-team/samba.git
				synced 2025-11-04 00:23:49 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			439 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			439 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
   Unix SMB/CIFS implementation.
 | 
						|
   dump the remote SAM using rpc samsync operations
 | 
						|
 | 
						|
   Copyright (C) Guenther Deschner 2008.
 | 
						|
   Copyright (C) Michael Adam 2008
 | 
						|
 | 
						|
   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
 | 
						|
   the Free Software Foundation; either version 3 of the License, or
 | 
						|
   (at your option) any later version.
 | 
						|
 | 
						|
   This program is distributed in the hope that it will be useful,
 | 
						|
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
   GNU General Public License for more details.
 | 
						|
 | 
						|
   You should have received a copy of the GNU General Public License
 | 
						|
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
*/
 | 
						|
 | 
						|
#include "includes.h"
 | 
						|
#include "libnet/libnet.h"
 | 
						|
 | 
						|
#ifdef HAVE_KRB5
 | 
						|
 | 
						|
#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE /* Heimdal */
 | 
						|
#define KRB5_KEY_TYPE(k)	((k)->keytype)
 | 
						|
#define KRB5_KEY_LENGTH(k)	((k)->keyvalue.length)
 | 
						|
#define KRB5_KEY_DATA(k)	((k)->keyvalue.data)
 | 
						|
#else /* MIT */
 | 
						|
#define	KRB5_KEY_TYPE(k)	((k)->enctype)
 | 
						|
#define KRB5_KEY_LENGTH(k)	((k)->length)
 | 
						|
#define KRB5_KEY_DATA(k)	((k)->contents)
 | 
						|
#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
 | 
						|
 | 
						|
/****************************************************************
 | 
						|
****************************************************************/
 | 
						|
 | 
						|
static int keytab_close(struct libnet_keytab_context *ctx)
 | 
						|
{
 | 
						|
	if (!ctx) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ctx->keytab && ctx->context) {
 | 
						|
		krb5_kt_close(ctx->context, ctx->keytab);
 | 
						|
	}
 | 
						|
 | 
						|
	if (ctx->context) {
 | 
						|
		krb5_free_context(ctx->context);
 | 
						|
	}
 | 
						|
 | 
						|
	if (ctx->ads) {
 | 
						|
		ads_destroy(&ctx->ads);
 | 
						|
	}
 | 
						|
 | 
						|
	TALLOC_FREE(ctx);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/****************************************************************
 | 
						|
****************************************************************/
 | 
						|
 | 
						|
krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx,
 | 
						|
				   const char *keytab_name,
 | 
						|
				   struct libnet_keytab_context **ctx)
 | 
						|
{
 | 
						|
	krb5_error_code ret = 0;
 | 
						|
	krb5_context context = NULL;
 | 
						|
	krb5_keytab keytab = NULL;
 | 
						|
	const char *keytab_string = NULL;
 | 
						|
 | 
						|
	struct libnet_keytab_context *r;
 | 
						|
 | 
						|
	r = TALLOC_ZERO_P(mem_ctx, struct libnet_keytab_context);
 | 
						|
	if (!r) {
 | 
						|
		return ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	talloc_set_destructor(r, keytab_close);
 | 
						|
 | 
						|
	initialize_krb5_error_table();
 | 
						|
	ret = krb5_init_context(&context);
 | 
						|
	if (ret) {
 | 
						|
		DEBUG(1,("keytab_init: could not krb5_init_context: %s\n",
 | 
						|
			error_message(ret)));
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = smb_krb5_open_keytab(context, keytab_name, true, &keytab);
 | 
						|
	if (ret) {
 | 
						|
		DEBUG(1,("keytab_init: smb_krb5_open_keytab failed (%s)\n",
 | 
						|
			error_message(ret)));
 | 
						|
		krb5_free_context(context);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = smb_krb5_keytab_name(mem_ctx, context, keytab, &keytab_string);
 | 
						|
	if (ret) {
 | 
						|
		krb5_kt_close(context, keytab);
 | 
						|
		krb5_free_context(context);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	r->context = context;
 | 
						|
	r->keytab = keytab;
 | 
						|
	r->keytab_name = keytab_string;
 | 
						|
	r->clean_old_entries = false;
 | 
						|
 | 
						|
	*ctx = r;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/****************************************************************
 | 
						|
****************************************************************/
 | 
						|
 | 
						|
/**
 | 
						|
 * Remove all entries that have the given principal, kvno and enctype.
 | 
						|
 */
 | 
						|
static krb5_error_code libnet_keytab_remove_entries(krb5_context context,
 | 
						|
						    krb5_keytab keytab,
 | 
						|
						    const char *principal,
 | 
						|
						    int kvno,
 | 
						|
						    const krb5_enctype enctype,
 | 
						|
						    bool ignore_kvno)
 | 
						|
{
 | 
						|
	krb5_error_code ret;
 | 
						|
	krb5_kt_cursor cursor;
 | 
						|
	krb5_keytab_entry kt_entry;
 | 
						|
 | 
						|
	ZERO_STRUCT(kt_entry);
 | 
						|
	ZERO_STRUCT(cursor);
 | 
						|
 | 
						|
	ret = krb5_kt_start_seq_get(context, keytab, &cursor);
 | 
						|
	if (ret) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0)
 | 
						|
	{
 | 
						|
		krb5_keyblock *keyp;
 | 
						|
		char *princ_s = NULL;
 | 
						|
 | 
						|
		if (kt_entry.vno != kvno && !ignore_kvno) {
 | 
						|
			goto cont;
 | 
						|
		}
 | 
						|
 | 
						|
#if !defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) && !defined(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK)
 | 
						|
#error krb5_keytab_entry has no key or keyblock member
 | 
						|
#endif
 | 
						|
#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY               /* MIT */
 | 
						|
	keyp = &kt_entry.key;
 | 
						|
#endif
 | 
						|
#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK          /* Heimdal */
 | 
						|
	keyp = &kt_entry.keyblock;
 | 
						|
#endif
 | 
						|
 | 
						|
		if (KRB5_KEY_TYPE(keyp) != enctype) {
 | 
						|
			goto cont;
 | 
						|
		}
 | 
						|
 | 
						|
		ret = smb_krb5_unparse_name(context, kt_entry.principal,
 | 
						|
					    &princ_s);
 | 
						|
		if (ret) {
 | 
						|
			DEBUG(5, ("smb_krb5_unparse_name failed (%s)\n",
 | 
						|
				  error_message(ret)));
 | 
						|
			goto cont;
 | 
						|
		}
 | 
						|
 | 
						|
		if (strcmp(principal, princ_s) != 0) {
 | 
						|
			goto cont;
 | 
						|
		}
 | 
						|
 | 
						|
		/* match found - remove */
 | 
						|
 | 
						|
		DEBUG(10, ("found entry for principal %s, kvno %d, "
 | 
						|
			   "enctype %d - trying to remove it\n",
 | 
						|
			   princ_s, kt_entry.vno, KRB5_KEY_TYPE(keyp)));
 | 
						|
 | 
						|
		ret = krb5_kt_end_seq_get(context, keytab, &cursor);
 | 
						|
		ZERO_STRUCT(cursor);
 | 
						|
		if (ret) {
 | 
						|
			DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
 | 
						|
				  error_message(ret)));
 | 
						|
			goto cont;
 | 
						|
		}
 | 
						|
 | 
						|
		ret = krb5_kt_remove_entry(context, keytab,
 | 
						|
					   &kt_entry);
 | 
						|
		if (ret) {
 | 
						|
			DEBUG(5, ("krb5_kt_remove_entry failed (%s)\n",
 | 
						|
				  error_message(ret)));
 | 
						|
			goto cont;
 | 
						|
		}
 | 
						|
		DEBUG(10, ("removed entry for principal %s, kvno %d, "
 | 
						|
			   "enctype %d\n", princ_s, kt_entry.vno,
 | 
						|
			   KRB5_KEY_TYPE(keyp)));
 | 
						|
 | 
						|
		ret = krb5_kt_start_seq_get(context, keytab, &cursor);
 | 
						|
		if (ret) {
 | 
						|
			DEBUG(5, ("krb5_kt_start_seq_get failed (%s)\n",
 | 
						|
				  error_message(ret)));
 | 
						|
			goto cont;
 | 
						|
		}
 | 
						|
 | 
						|
cont:
 | 
						|
		smb_krb5_kt_free_entry(context, &kt_entry);
 | 
						|
		SAFE_FREE(princ_s);
 | 
						|
	}
 | 
						|
 | 
						|
	ret = krb5_kt_end_seq_get(context, keytab, &cursor);
 | 
						|
	if (ret) {
 | 
						|
		DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
 | 
						|
			  error_message(ret)));
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static krb5_error_code libnet_keytab_add_entry(krb5_context context,
 | 
						|
					       krb5_keytab keytab,
 | 
						|
					       krb5_kvno kvno,
 | 
						|
					       const char *princ_s,
 | 
						|
					       krb5_enctype enctype,
 | 
						|
					       krb5_data password)
 | 
						|
{
 | 
						|
	krb5_keyblock *keyp;
 | 
						|
	krb5_keytab_entry kt_entry;
 | 
						|
	krb5_error_code ret;
 | 
						|
 | 
						|
	/* remove duplicates first ... */
 | 
						|
	ret = libnet_keytab_remove_entries(context, keytab, princ_s, kvno,
 | 
						|
					   enctype, false);
 | 
						|
	if (ret) {
 | 
						|
		DEBUG(1, ("libnet_keytab_remove_entries failed: %s\n",
 | 
						|
			  error_message(ret)));
 | 
						|
	}
 | 
						|
 | 
						|
	ZERO_STRUCT(kt_entry);
 | 
						|
 | 
						|
	kt_entry.vno = kvno;
 | 
						|
 | 
						|
	ret = smb_krb5_parse_name(context, princ_s, &kt_entry.principal);
 | 
						|
	if (ret) {
 | 
						|
		DEBUG(1, ("smb_krb5_parse_name(%s) failed (%s)\n",
 | 
						|
			  princ_s, error_message(ret)));
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
#if !defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) && !defined(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK)
 | 
						|
#error krb5_keytab_entry has no key or keyblock member
 | 
						|
#endif
 | 
						|
#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY               /* MIT */
 | 
						|
	keyp = &kt_entry.key;
 | 
						|
#endif
 | 
						|
#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK          /* Heimdal */
 | 
						|
	keyp = &kt_entry.keyblock;
 | 
						|
#endif
 | 
						|
 | 
						|
	if (create_kerberos_key_from_string(context, kt_entry.principal,
 | 
						|
					    &password, keyp, enctype, true))
 | 
						|
	{
 | 
						|
		ret = KRB5KRB_ERR_GENERIC;
 | 
						|
		goto done;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = krb5_kt_add_entry(context, keytab, &kt_entry);
 | 
						|
	if (ret) {
 | 
						|
		DEBUG(1, ("adding entry to keytab failed (%s)\n",
 | 
						|
			  error_message(ret)));
 | 
						|
	}
 | 
						|
 | 
						|
done:
 | 
						|
	krb5_free_keyblock_contents(context, keyp);
 | 
						|
	krb5_free_principal(context, kt_entry.principal);
 | 
						|
	ZERO_STRUCT(kt_entry);
 | 
						|
	smb_krb5_kt_free_entry(context, &kt_entry);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
krb5_error_code libnet_keytab_add(struct libnet_keytab_context *ctx)
 | 
						|
{
 | 
						|
	krb5_error_code ret = 0;
 | 
						|
	uint32_t i;
 | 
						|
 | 
						|
 | 
						|
	if (ctx->clean_old_entries) {
 | 
						|
		DEBUG(0, ("cleaning old entries...\n"));
 | 
						|
		for (i=0; i < ctx->count; i++) {
 | 
						|
			struct libnet_keytab_entry *entry = &ctx->entries[i];
 | 
						|
 | 
						|
			ret = libnet_keytab_remove_entries(ctx->context,
 | 
						|
							   ctx->keytab,
 | 
						|
							   entry->principal,
 | 
						|
							   0,
 | 
						|
							   entry->enctype,
 | 
						|
							   true);
 | 
						|
			if (ret) {
 | 
						|
				DEBUG(1,("libnet_keytab_add: Failed to remove "
 | 
						|
					 "old entries for %s (enctype %u): %s\n",
 | 
						|
					 entry->principal, entry->enctype,
 | 
						|
					 error_message(ret)));
 | 
						|
				return ret;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for (i=0; i<ctx->count; i++) {
 | 
						|
 | 
						|
		struct libnet_keytab_entry *entry = &ctx->entries[i];
 | 
						|
		krb5_data password;
 | 
						|
 | 
						|
		ZERO_STRUCT(password);
 | 
						|
		password.data = (char *)entry->password.data;
 | 
						|
		password.length = entry->password.length;
 | 
						|
 | 
						|
		ret = libnet_keytab_add_entry(ctx->context,
 | 
						|
					      ctx->keytab,
 | 
						|
					      entry->kvno,
 | 
						|
					      entry->principal,
 | 
						|
					      entry->enctype,
 | 
						|
					      password);
 | 
						|
		if (ret) {
 | 
						|
			DEBUG(1,("libnet_keytab_add: "
 | 
						|
				"Failed to add entry to keytab file\n"));
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
struct libnet_keytab_entry *libnet_keytab_search(struct libnet_keytab_context *ctx,
 | 
						|
						 const char *principal,
 | 
						|
						 int kvno,
 | 
						|
						 const krb5_enctype enctype,
 | 
						|
						 TALLOC_CTX *mem_ctx)
 | 
						|
{
 | 
						|
	krb5_error_code ret = 0;
 | 
						|
	krb5_kt_cursor cursor;
 | 
						|
	krb5_keytab_entry kt_entry;
 | 
						|
	struct libnet_keytab_entry *entry = NULL;
 | 
						|
 | 
						|
	ZERO_STRUCT(kt_entry);
 | 
						|
	ZERO_STRUCT(cursor);
 | 
						|
 | 
						|
	ret = krb5_kt_start_seq_get(ctx->context, ctx->keytab, &cursor);
 | 
						|
	if (ret) {
 | 
						|
		DEBUG(10, ("krb5_kt_start_seq_get failed: %s",
 | 
						|
			  error_message(ret)));
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	while (krb5_kt_next_entry(ctx->context, ctx->keytab, &kt_entry, &cursor) == 0)
 | 
						|
	{
 | 
						|
		krb5_keyblock *keyp;
 | 
						|
		char *princ_s = NULL;
 | 
						|
 | 
						|
		if (kt_entry.vno != kvno) {
 | 
						|
			goto cont;
 | 
						|
		}
 | 
						|
 | 
						|
#if !defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) && !defined(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK)
 | 
						|
#error krb5_keytab_entry has no key or keyblock member
 | 
						|
#endif
 | 
						|
#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY               /* MIT */
 | 
						|
	keyp = &kt_entry.key;
 | 
						|
#endif
 | 
						|
#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK          /* Heimdal */
 | 
						|
	keyp = &kt_entry.keyblock;
 | 
						|
#endif
 | 
						|
 | 
						|
		if (KRB5_KEY_TYPE(keyp) != enctype) {
 | 
						|
			goto cont;
 | 
						|
		}
 | 
						|
 | 
						|
		ret = smb_krb5_unparse_name(ctx->context, kt_entry.principal,
 | 
						|
					    &princ_s);
 | 
						|
		if (ret) {
 | 
						|
			goto cont;
 | 
						|
		}
 | 
						|
 | 
						|
		if (strcmp(principal, princ_s) != 0) {
 | 
						|
			goto cont;
 | 
						|
		}
 | 
						|
 | 
						|
		entry = talloc_zero(mem_ctx, struct libnet_keytab_entry);
 | 
						|
		if (!entry) {
 | 
						|
			DEBUG(3, ("talloc failed\n"));
 | 
						|
			goto fail;
 | 
						|
		}
 | 
						|
 | 
						|
		entry->name = talloc_strdup(entry, princ_s);
 | 
						|
		if (!entry->name) {
 | 
						|
			DEBUG(3, ("talloc_strdup_failed\n"));
 | 
						|
			goto fail;
 | 
						|
		}
 | 
						|
 | 
						|
		entry->principal = talloc_strdup(entry, princ_s);
 | 
						|
		if (!entry->principal) {
 | 
						|
			DEBUG(3, ("talloc_strdup_failed\n"));
 | 
						|
			goto fail;
 | 
						|
		}
 | 
						|
 | 
						|
		entry->password = data_blob_talloc(entry, KRB5_KEY_DATA(keyp),
 | 
						|
						   KRB5_KEY_LENGTH(keyp));
 | 
						|
		if (!entry->password.data) {
 | 
						|
			DEBUG(3, ("data_blob_talloc failed\n"));
 | 
						|
			goto fail;
 | 
						|
		}
 | 
						|
 | 
						|
		DEBUG(10, ("found entry\n"));
 | 
						|
 | 
						|
		smb_krb5_kt_free_entry(ctx->context, &kt_entry);
 | 
						|
		SAFE_FREE(princ_s);
 | 
						|
		break;
 | 
						|
 | 
						|
fail:
 | 
						|
		smb_krb5_kt_free_entry(ctx->context, &kt_entry);
 | 
						|
		SAFE_FREE(princ_s);
 | 
						|
		TALLOC_FREE(entry);
 | 
						|
		break;
 | 
						|
 | 
						|
cont:
 | 
						|
		smb_krb5_kt_free_entry(ctx->context, &kt_entry);
 | 
						|
		SAFE_FREE(princ_s);
 | 
						|
		continue;
 | 
						|
	}
 | 
						|
 | 
						|
	krb5_kt_end_seq_get(ctx->context, ctx->keytab, &cursor);
 | 
						|
	return entry;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* HAVE_KRB5 */
 |