Blame SOURCES/0003-update-allow-to-add-service-names.patch

a302cd
From c53a51a61d7ac20900836b1bb005bf272c08a849 Mon Sep 17 00:00:00 2001
a302cd
From: Sumit Bose <sbose@redhat.com>
a302cd
Date: Mon, 4 Jun 2018 10:49:33 +0200
a302cd
Subject: [PATCH 3/7] update: allow to add service names
a302cd
a302cd
Related to https://bugzilla.redhat.com/show_bug.cgi?id=1547013
a302cd
           https://bugzilla.redhat.com/show_bug.cgi?id=1545568
a302cd
---
a302cd
 library/adenroll.c  | 136 +++++++++++++++++++++++++++++++++-------------------
a302cd
 library/adkrb5.c    | 113 +++++++++++++++++++++++++++++++++++++++++++
a302cd
 library/adprivate.h |   6 +++
a302cd
 3 files changed, 206 insertions(+), 49 deletions(-)
a302cd
a302cd
diff --git a/library/adenroll.c b/library/adenroll.c
a302cd
index 2be6796..1221e89 100644
a302cd
--- a/library/adenroll.c
a302cd
+++ b/library/adenroll.c
a302cd
@@ -305,13 +305,37 @@ ensure_service_names (adcli_result res,
a302cd
 }
a302cd
 
a302cd
 static adcli_result
a302cd
-ensure_service_principals (adcli_result res,
a302cd
-                           adcli_enroll *enroll)
a302cd
+add_service_names_to_service_principals (adcli_enroll *enroll)
a302cd
 {
a302cd
 	char *name;
a302cd
 	int length = 0;
a302cd
 	int i;
a302cd
 
a302cd
+	if (enroll->service_principals != NULL) {
a302cd
+		length = seq_count (enroll->service_principals);
a302cd
+	}
a302cd
+
a302cd
+	for (i = 0; enroll->service_names[i] != NULL; i++) {
a302cd
+		if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0)
a302cd
+			return_unexpected_if_reached ();
a302cd
+		enroll->service_principals = _adcli_strv_add (enroll->service_principals,
a302cd
+			                                      name, &length);
a302cd
+
a302cd
+		if (enroll->host_fqdn) {
a302cd
+			if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0)
a302cd
+				return_unexpected_if_reached ();
a302cd
+			enroll->service_principals = _adcli_strv_add (enroll->service_principals,
a302cd
+				                                      name, &length);
a302cd
+		}
a302cd
+	}
a302cd
+
a302cd
+	return ADCLI_SUCCESS;
a302cd
+}
a302cd
+
a302cd
+static adcli_result
a302cd
+ensure_service_principals (adcli_result res,
a302cd
+                           adcli_enroll *enroll)
a302cd
+{
a302cd
 	if (res != ADCLI_SUCCESS)
a302cd
 		return res;
a302cd
 
a302cd
@@ -319,20 +343,7 @@ ensure_service_principals (adcli_result res,
a302cd
 
a302cd
 	if (!enroll->service_principals) {
a302cd
 		assert (enroll->service_names != NULL);
a302cd
-
a302cd
-		for (i = 0; enroll->service_names[i] != NULL; i++) {
a302cd
-			if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0)
a302cd
-				return_unexpected_if_reached ();
a302cd
-			enroll->service_principals = _adcli_strv_add (enroll->service_principals,
a302cd
-			                                              name, &length);
a302cd
-
a302cd
-			if (enroll->host_fqdn) {
a302cd
-				if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0)
a302cd
-					return_unexpected_if_reached ();
a302cd
-				enroll->service_principals = _adcli_strv_add (enroll->service_principals,
a302cd
-				                                              name, &length);
a302cd
-			}
a302cd
-		}
a302cd
+		return add_service_names_to_service_principals (enroll);
a302cd
 	}
a302cd
 
a302cd
 	return ADCLI_SUCCESS;
a302cd
@@ -356,6 +367,7 @@ ensure_keytab_principals (adcli_result res,
a302cd
 	return_unexpected_if_fail (k5 != NULL);
a302cd
 
a302cd
 	enroll->keytab_principals = calloc (count + 3, sizeof (krb5_principal));
a302cd
+	return_unexpected_if_fail (enroll->keytab_principals != NULL);
a302cd
 	at = 0;
a302cd
 
a302cd
 	/* First add the principal for the computer account name */
a302cd
@@ -1266,7 +1278,7 @@ update_computer_account (adcli_enroll *enroll)
a302cd
 		}
a302cd
 	}
a302cd
 
a302cd
-	if (res == ADCLI_SUCCESS && !enroll->user_princpal_generate) {
a302cd
+	if (res == ADCLI_SUCCESS && enroll->user_principal != NULL && !enroll->user_princpal_generate) {
a302cd
 		char *vals_userPrincipalName[] = { enroll->user_principal, NULL };
a302cd
 		LDAPMod userPrincipalName = { LDAP_MOD_REPLACE, "userPrincipalName", { vals_userPrincipalName, }, };
a302cd
 		LDAPMod *mods[] = { &userPrincipalName, NULL, };
a302cd
@@ -1519,7 +1531,8 @@ add_principal_to_keytab (adcli_enroll *enroll,
a302cd
                          krb5_context k5,
a302cd
                          krb5_principal principal,
a302cd
                          const char *principal_name,
a302cd
-                         int *which_salt)
a302cd
+                         int *which_salt,
a302cd
+                         adcli_enroll_flags flags)
a302cd
 {
a302cd
 	match_principal_kvno closure;
a302cd
 	krb5_data password;
a302cd
@@ -1547,41 +1560,47 @@ add_principal_to_keytab (adcli_enroll *enroll,
a302cd
 		             enroll->keytab_name);
a302cd
 	}
a302cd
 
a302cd
-	password.data = enroll->computer_password;
a302cd
-	password.length = strlen (enroll->computer_password);
a302cd
-
a302cd
 	enctypes = adcli_enroll_get_keytab_enctypes (enroll);
a302cd
 
a302cd
-	/*
a302cd
-	 * So we need to discover which salt to use. As a side effect we are
a302cd
-	 * also testing that our account works.
a302cd
-	 */
a302cd
+	if (flags & ADCLI_ENROLL_PASSWORD_VALID) {
a302cd
+		code = _adcli_krb5_keytab_copy_entries (k5, enroll->keytab, principal,
a302cd
+		                                        enroll->kvno, enctypes);
a302cd
+	} else {
a302cd
 
a302cd
-	salts = build_principal_salts (enroll, k5, principal);
a302cd
-	return_unexpected_if_fail (salts != NULL);
a302cd
+		password.data = enroll->computer_password;
a302cd
+		password.length = strlen (enroll->computer_password);
a302cd
 
a302cd
-	if (*which_salt < 0) {
a302cd
-		code = _adcli_krb5_keytab_discover_salt (k5, principal, enroll->kvno, &password,
a302cd
-		                                         enctypes, salts, which_salt);
a302cd
-		if (code != 0) {
a302cd
-			_adcli_warn ("Couldn't authenticate with keytab while discovering which salt to use: %s: %s",
a302cd
-			             principal_name, krb5_get_error_message (k5, code));
a302cd
-			*which_salt = DEFAULT_SALT;
a302cd
-		} else {
a302cd
-			assert (*which_salt >= 0);
a302cd
-			_adcli_info ("Discovered which keytab salt to use");
a302cd
+		/*
a302cd
+		 * So we need to discover which salt to use. As a side effect we are
a302cd
+		 * also testing that our account works.
a302cd
+		 */
a302cd
+
a302cd
+		salts = build_principal_salts (enroll, k5, principal);
a302cd
+		return_unexpected_if_fail (salts != NULL);
a302cd
+
a302cd
+		if (*which_salt < 0) {
a302cd
+			code = _adcli_krb5_keytab_discover_salt (k5, principal, enroll->kvno, &password,
a302cd
+			                                         enctypes, salts, which_salt);
a302cd
+			if (code != 0) {
a302cd
+				_adcli_warn ("Couldn't authenticate with keytab while discovering which salt to use: %s: %s",
a302cd
+				             principal_name, krb5_get_error_message (k5, code));
a302cd
+				*which_salt = DEFAULT_SALT;
a302cd
+			} else {
a302cd
+				assert (*which_salt >= 0);
a302cd
+				_adcli_info ("Discovered which keytab salt to use");
a302cd
+			}
a302cd
 		}
a302cd
-	}
a302cd
 
a302cd
-	code = _adcli_krb5_keytab_add_entries (k5, enroll->keytab, principal,
a302cd
-	                                       enroll->kvno, &password, enctypes, &salts[*which_salt]);
a302cd
+		code = _adcli_krb5_keytab_add_entries (k5, enroll->keytab, principal,
a302cd
+		                                       enroll->kvno, &password, enctypes, &salts[*which_salt]);
a302cd
 
a302cd
-	free_principal_salts (k5, salts);
a302cd
+		free_principal_salts (k5, salts);
a302cd
 
a302cd
-	if (code != 0) {
a302cd
-		_adcli_err ("Couldn't add keytab entries: %s: %s",
a302cd
-		            enroll->keytab_name, krb5_get_error_message (k5, code));
a302cd
-		return ADCLI_ERR_FAIL;
a302cd
+		if (code != 0) {
a302cd
+			_adcli_err ("Couldn't add keytab entries: %s: %s",
a302cd
+			            enroll->keytab_name, krb5_get_error_message (k5, code));
a302cd
+			return ADCLI_ERR_FAIL;
a302cd
+		}
a302cd
 	}
a302cd
 
a302cd
 
a302cd
@@ -1591,7 +1610,8 @@ add_principal_to_keytab (adcli_enroll *enroll,
a302cd
 }
a302cd
 
a302cd
 static adcli_result
a302cd
-update_keytab_for_principals (adcli_enroll *enroll)
a302cd
+update_keytab_for_principals (adcli_enroll *enroll,
a302cd
+                              adcli_enroll_flags flags)
a302cd
 {
a302cd
 	krb5_context k5;
a302cd
 	adcli_result res;
a302cd
@@ -1608,7 +1628,7 @@ update_keytab_for_principals (adcli_enroll *enroll)
a302cd
 		if (krb5_unparse_name (k5, enroll->keytab_principals[i], &name) != 0)
a302cd
 			name = "";
a302cd
 		res = add_principal_to_keytab (enroll, k5, enroll->keytab_principals[i],
a302cd
-		                               name, &which_salt);
a302cd
+		                               name, &which_salt, flags);
a302cd
 		krb5_free_unparsed_name (k5, name);
a302cd
 
a302cd
 		if (res != ADCLI_SUCCESS)
a302cd
@@ -1807,6 +1827,20 @@ enroll_join_or_update_tasks (adcli_enroll *enroll,
a302cd
 	/* We ignore failures of setting these fields */
a302cd
 	update_and_calculate_enctypes (enroll);
a302cd
 	update_computer_account (enroll);
a302cd
+
a302cd
+	/* service_names is only set from input on the command line, so no
a302cd
+	 * additional check for explicit is needed here */
a302cd
+	if (enroll->service_names != NULL) {
a302cd
+		res = add_service_names_to_service_principals (enroll);
a302cd
+		if (res != ADCLI_SUCCESS) {
a302cd
+			return res;
a302cd
+		}
a302cd
+		res = ensure_keytab_principals (res, enroll);
a302cd
+		if (res != ADCLI_SUCCESS) {
a302cd
+			return res;
a302cd
+		}
a302cd
+	}
a302cd
+
a302cd
 	update_service_principals (enroll);
a302cd
 
a302cd
 	if ( (flags & ADCLI_ENROLL_ADD_SAMBA_DATA) && ! (flags & ADCLI_ENROLL_PASSWORD_VALID)) {
a302cd
@@ -1826,7 +1860,7 @@ enroll_join_or_update_tasks (adcli_enroll *enroll,
a302cd
 	 * that we use for salting.
a302cd
 	 */
a302cd
 
a302cd
-	return update_keytab_for_principals (enroll);
a302cd
+	return update_keytab_for_principals (enroll, flags);
a302cd
 }
a302cd
 
a302cd
 adcli_result
a302cd
@@ -1927,7 +1961,11 @@ adcli_enroll_update (adcli_enroll *enroll,
a302cd
 
a302cd
 	if (_adcli_check_nt_time_string_lifetime (value,
a302cd
 	                adcli_enroll_get_computer_password_lifetime (enroll))) {
a302cd
-		flags |= ADCLI_ENROLL_NO_KEYTAB;
a302cd
+		/* Do not update keytab if neither new service principals have
a302cd
+                 * to be added nor the user principal has to be changed. */
a302cd
+		if (enroll->service_names == NULL && (enroll->user_principal == NULL || enroll->user_princpal_generate)) {
a302cd
+			flags |= ADCLI_ENROLL_NO_KEYTAB;
a302cd
+		}
a302cd
 		flags |= ADCLI_ENROLL_PASSWORD_VALID;
a302cd
 	}
a302cd
 	free (value);
a302cd
diff --git a/library/adkrb5.c b/library/adkrb5.c
a302cd
index b0e903e..033c181 100644
a302cd
--- a/library/adkrb5.c
a302cd
+++ b/library/adkrb5.c
a302cd
@@ -204,6 +204,119 @@ _adcli_krb5_open_keytab (krb5_context k5,
a302cd
 	return ADCLI_SUCCESS;
a302cd
 }
a302cd
 
a302cd
+typedef struct {
a302cd
+	krb5_kvno kvno;
a302cd
+	krb5_enctype enctype;
a302cd
+	int matched;
a302cd
+} match_enctype_kvno;
a302cd
+
a302cd
+static krb5_boolean
a302cd
+match_enctype_and_kvno (krb5_context k5,
a302cd
+                        krb5_keytab_entry *entry,
a302cd
+                        void *data)
a302cd
+{
a302cd
+	krb5_boolean similar = FALSE;
a302cd
+	match_enctype_kvno *closure = data;
a302cd
+	krb5_error_code code;
a302cd
+
a302cd
+	assert (closure->enctype);
a302cd
+
a302cd
+	code = krb5_c_enctype_compare (k5, closure->enctype, entry->key.enctype,
a302cd
+	                               &similar);
a302cd
+
a302cd
+	if (code == 0 && entry->vno == closure->kvno && similar) {
a302cd
+		closure->matched = 1;
a302cd
+		return 1;
a302cd
+	}
a302cd
+
a302cd
+	return 0;
a302cd
+}
a302cd
+
a302cd
+static krb5_error_code
a302cd
+_adcli_krb5_get_keyblock (krb5_context k5,
a302cd
+                          krb5_keytab keytab,
a302cd
+                          krb5_keyblock *keyblock,
a302cd
+                          krb5_boolean (* match_func) (krb5_context,
a302cd
+                                                       krb5_keytab_entry *,
a302cd
+                                                       void *),
a302cd
+                          void *match_data)
a302cd
+{
a302cd
+	krb5_kt_cursor cursor;
a302cd
+	krb5_keytab_entry entry;
a302cd
+	krb5_error_code code;
a302cd
+
a302cd
+	code = krb5_kt_start_seq_get (k5, keytab, &cursor);
a302cd
+	if (code == KRB5_KT_END || code == ENOENT)
a302cd
+		return 0;
a302cd
+	else if (code != 0)
a302cd
+		return code;
a302cd
+
a302cd
+	for (;;) {
a302cd
+		code = krb5_kt_next_entry (k5, keytab, &entry, &cursor);
a302cd
+		if (code != 0)
a302cd
+			break;
a302cd
+
a302cd
+		/* See if we should remove this entry */
a302cd
+		if (!match_func (k5, &entry, match_data)) {
a302cd
+			krb5_free_keytab_entry_contents (k5, &entry);
a302cd
+			continue;
a302cd
+		}
a302cd
+
a302cd
+		code = krb5_copy_keyblock_contents (k5, &entry.key, keyblock);
a302cd
+		krb5_free_keytab_entry_contents (k5, &entry);
a302cd
+		break;
a302cd
+
a302cd
+
a302cd
+	}
a302cd
+
a302cd
+	if (code == KRB5_KT_END)
a302cd
+		code = 0;
a302cd
+
a302cd
+	krb5_kt_end_seq_get (k5, keytab, &cursor);
a302cd
+	return code;
a302cd
+}
a302cd
+
a302cd
+krb5_error_code
a302cd
+_adcli_krb5_keytab_copy_entries (krb5_context k5,
a302cd
+                                 krb5_keytab keytab,
a302cd
+                                 krb5_principal principal,
a302cd
+                                 krb5_kvno kvno,
a302cd
+                                 krb5_enctype *enctypes)
a302cd
+{
a302cd
+	krb5_keytab_entry entry;
a302cd
+	krb5_error_code code;
a302cd
+	int i;
a302cd
+	match_enctype_kvno closure;
a302cd
+
a302cd
+	for (i = 0; enctypes[i] != 0; i++) {
a302cd
+
a302cd
+		closure.kvno = kvno;
a302cd
+		closure.enctype = enctypes[i];
a302cd
+		closure.matched = 0;
a302cd
+
a302cd
+		memset (&entry, 0, sizeof (entry));
a302cd
+
a302cd
+		code = _adcli_krb5_get_keyblock (k5, keytab, &entry.key,
a302cd
+		                                 match_enctype_and_kvno, &closure);
a302cd
+		if (code != 0) {
a302cd
+			return code;
a302cd
+		}
a302cd
+
a302cd
+
a302cd
+		entry.principal = principal;
a302cd
+		entry.vno = kvno;
a302cd
+
a302cd
+		code = krb5_kt_add_entry (k5, keytab, &entry);
a302cd
+
a302cd
+		entry.principal = NULL;
a302cd
+		krb5_free_keytab_entry_contents (k5, &entry);
a302cd
+
a302cd
+		if (code != 0)
a302cd
+			return code;
a302cd
+	}
a302cd
+
a302cd
+	return 0;
a302cd
+}
a302cd
 
a302cd
 krb5_error_code
a302cd
 _adcli_krb5_keytab_add_entries (krb5_context k5,
a302cd
diff --git a/library/adprivate.h b/library/adprivate.h
a302cd
index 83a88f6..7485249 100644
a302cd
--- a/library/adprivate.h
a302cd
+++ b/library/adprivate.h
a302cd
@@ -282,6 +282,12 @@ krb5_enctype *   _adcli_krb5_parse_enctypes       (const char *value);
a302cd
 
a302cd
 char *           _adcli_krb5_format_enctypes      (krb5_enctype *enctypes);
a302cd
 
a302cd
+krb5_error_code  _adcli_krb5_keytab_copy_entries  (krb5_context k5,
a302cd
+                                                   krb5_keytab keytab,
a302cd
+                                                   krb5_principal principal,
a302cd
+                                                   krb5_kvno kvno,
a302cd
+                                                   krb5_enctype *enctypes);
a302cd
+
a302cd
 struct _adcli_attrs {
a302cd
 	LDAPMod **mods;
a302cd
 	int len;
a302cd
-- 
a302cd
2.14.4
a302cd