Blame SOURCES/0001-Add-setattr-option.patch

2d8b37
From c5b0cee2976682b4fc1aeb02636cc9f2c6dbc2a5 Mon Sep 17 00:00:00 2001
2d8b37
From: Sumit Bose <sbose@redhat.com>
2d8b37
Date: Mon, 14 Jun 2021 07:54:01 +0200
2d8b37
Subject: [PATCH 1/2] Add setattr option
2d8b37
2d8b37
With the new option common LDAP attributes can be set.
2d8b37
2d8b37
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1690920
2d8b37
---
2d8b37
 doc/adcli.xml      |  34 +++++++++
2d8b37
 library/adenroll.c | 169 ++++++++++++++++++++++++++++++++++++++++++++-
2d8b37
 library/adenroll.h |   4 ++
2d8b37
 tools/computer.c   |  10 +++
2d8b37
 4 files changed, 216 insertions(+), 1 deletion(-)
2d8b37
2d8b37
diff --git a/doc/adcli.xml b/doc/adcli.xml
2d8b37
index 6c36297..8383aa7 100644
2d8b37
--- a/doc/adcli.xml
2d8b37
+++ b/doc/adcli.xml
2d8b37
@@ -374,6 +374,23 @@ Password for Administrator:
2d8b37
 			service should be accessible with a different host
2d8b37
 			name as well.</para></listitem>
2d8b37
 		</varlistentry>
2d8b37
+		<varlistentry>
2d8b37
+			<term><option>--setattr=<parameter>name=value</parameter></option></term>
2d8b37
+			<listitem><para>Add the LDAP attribute
2d8b37
+			<option><parameter>name</parameter></option> with the
2d8b37
+			given <option><parameter>value</parameter></option> to
2d8b37
+			the new LDAP host object.
2d8b37
+			This option can be used multiple times to add multiple
2d8b37
+			different attributes. Multi-value attributes are
2d8b37
+			currently not supported.</para>
2d8b37
+			<para>Please note that the account used to join the
2d8b37
+			domain must have the required privileges to add the
2d8b37
+			given attributes. Some attributes might have
2d8b37
+			constraints with respect to syntax and allowed values
2d8b37
+			which must be met as well. Attributes managed by other
2d8b37
+			adcli options cannot be set with this option.</para>
2d8b37
+			</listitem>
2d8b37
+		</varlistentry>
2d8b37
 		<varlistentry>
2d8b37
 			<term><option>--show-details</option></term>
2d8b37
 			<listitem><para>After a successful join print out information
2d8b37
@@ -543,6 +560,23 @@ $ adcli update --login-ccache=/tmp/krbcc_123
2d8b37
 			<listitem><para>Remove a service principal name from
2d8b37
 			the keytab and the AD host object.</para></listitem>
2d8b37
 		</varlistentry>
2d8b37
+		<varlistentry>
2d8b37
+			<term><option>--setattr=<parameter>name=value</parameter></option></term>
2d8b37
+			<listitem><para>Add the LDAP attribute
2d8b37
+			<option><parameter>name</parameter></option> with the
2d8b37
+			given <option><parameter>value</parameter></option> to
2d8b37
+			the LDAP host object.
2d8b37
+			This option can be used multiple times to add multiple
2d8b37
+			different attributes. Multi-value attributes are
2d8b37
+			currently not supported.</para>
2d8b37
+			<para>Please note that the account used to update the
2d8b37
+			host object must have the required privileges to modify
2d8b37
+			the given attributes. Some attributes might have
2d8b37
+			constraints with respect to syntax and allowed values
2d8b37
+			which must be met as well. Attributes managed by other
2d8b37
+			adcli options cannot be set with this option.</para>
2d8b37
+			</listitem>
2d8b37
+		</varlistentry>
2d8b37
 		<varlistentry>
2d8b37
 			<term><option>--show-details</option></term>
2d8b37
 			<listitem><para>After a successful join print out information
2d8b37
diff --git a/library/adenroll.c b/library/adenroll.c
2d8b37
index 0b1c066..dd51567 100644
2d8b37
--- a/library/adenroll.c
2d8b37
+++ b/library/adenroll.c
2d8b37
@@ -150,4 +150,5 @@ struct _adcli_enroll {
2d8b37
 	char *description;
2d8b37
+	char **setattr;
2d8b37
 };
2d8b37
 
2d8b37
 static const char *
2d8b37
@@ -795,6 +796,56 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype)
2d8b37
 	return ADCLI_SUCCESS;
2d8b37
 }
2d8b37
 
2d8b37
+static LDAPMod **
2d8b37
+get_mods_for_attrs (adcli_enroll *enroll, int mod_op)
2d8b37
+{
2d8b37
+	size_t len;
2d8b37
+	size_t c;
2d8b37
+	char *end;
2d8b37
+	LDAPMod **mods = NULL;
2d8b37
+
2d8b37
+	len = _adcli_strv_len (enroll->setattr);
2d8b37
+	if (len == 0) {
2d8b37
+		return NULL;
2d8b37
+	}
2d8b37
+
2d8b37
+	mods = calloc (len + 1, sizeof (LDAPMod *));
2d8b37
+	return_val_if_fail (mods != NULL, NULL);
2d8b37
+
2d8b37
+	for (c = 0; c < len; c++) {
2d8b37
+		end = strchr (enroll->setattr[c], '=');
2d8b37
+		if (end == NULL) {
2d8b37
+			ldap_mods_free (mods, 1);
2d8b37
+			return NULL;
2d8b37
+		}
2d8b37
+
2d8b37
+		mods[c] = calloc (1, sizeof (LDAPMod));
2d8b37
+		if (mods[c] == NULL) {
2d8b37
+			ldap_mods_free (mods, 1);
2d8b37
+			return NULL;
2d8b37
+		}
2d8b37
+
2d8b37
+		mods[c]->mod_op = mod_op;
2d8b37
+		*end = '\0';
2d8b37
+		mods[c]->mod_type = strdup (enroll->setattr[c]);
2d8b37
+		*end = '=';
2d8b37
+		mods[c]->mod_values = calloc (2, sizeof (char *));
2d8b37
+		if (mods[c]->mod_type == NULL || mods[c]->mod_values == NULL) {
2d8b37
+			ldap_mods_free (mods, 1);
2d8b37
+			return NULL;
2d8b37
+		}
2d8b37
+
2d8b37
+		mods[c]->mod_values[0] = strdup (end + 1);
2d8b37
+		if (mods[c]->mod_values[0] == NULL) {
2d8b37
+			ldap_mods_free (mods, 1);
2d8b37
+			return NULL;
2d8b37
+		}
2d8b37
+	}
2d8b37
+
2d8b37
+	return mods;
2d8b37
+}
2d8b37
+
2d8b37
+
2d8b37
 static adcli_result
2d8b37
 create_computer_account (adcli_enroll *enroll,
2d8b37
                          LDAP *ldap)
2d8b37
@@ -828,6 +879,7 @@ create_computer_account (adcli_enroll *enroll,
2d8b37
 	size_t m;
2d8b37
 	uint32_t uac = UAC_WORKSTATION_TRUST_ACCOUNT | UAC_DONT_EXPIRE_PASSWORD ;
2d8b37
 	char *uac_str = NULL;
2d8b37
+	LDAPMod **extra_mods = NULL;
2d8b37
 
2d8b37
 	LDAPMod *all_mods[] = {
2d8b37
 		&objectClass,
2d8b37
@@ -845,7 +897,7 @@ create_computer_account (adcli_enroll *enroll,
2d8b37
 	};
2d8b37
 
2d8b37
 	size_t mods_count = sizeof (all_mods) / sizeof (LDAPMod *);
2d8b37
-	LDAPMod *mods[mods_count];
2d8b37
+	LDAPMod **mods;
2d8b37
 
2d8b37
 	if (adcli_enroll_get_trusted_for_delegation (enroll)) {
2d8b37
 		uac |= UAC_TRUSTED_FOR_DELEGATION;
2d8b37
@@ -868,6 +920,17 @@ create_computer_account (adcli_enroll *enroll,
2d8b37
 	}
2d8b37
 	vals_supportedEncryptionTypes[0] = val;
2d8b37
 
2d8b37
+	if (enroll->setattr != NULL) {
2d8b37
+		extra_mods = get_mods_for_attrs (enroll, LDAP_MOD_ADD);
2d8b37
+		if (extra_mods == NULL) {
2d8b37
+			_adcli_err ("Failed to add setattr attributes, "
2d8b37
+			            "just using defaults");
2d8b37
+		}
2d8b37
+	}
2d8b37
+
2d8b37
+	mods = calloc (mods_count + seq_count (extra_mods) + 1, sizeof (LDAPMod *));
2d8b37
+	return_val_if_fail (mods != NULL, ADCLI_ERR_UNEXPECTED);
2d8b37
+
2d8b37
 	m = 0;
2d8b37
 	for (c = 0; c < mods_count - 1; c++) {
2d8b37
 		/* Skip empty LDAP sttributes */
2d8b37
@@ -875,9 +938,15 @@ create_computer_account (adcli_enroll *enroll,
2d8b37
 			mods[m++] = all_mods[c];
2d8b37
 		}
2d8b37
 	}
2d8b37
+
2d8b37
+	for (c = 0; c < seq_count (extra_mods); c++) {
2d8b37
+		mods[m++] = extra_mods[c];
2d8b37
+	}
2d8b37
 	mods[m] = NULL;
2d8b37
 
2d8b37
 	ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL);
2d8b37
+	ldap_mods_free (extra_mods, 1);
2d8b37
+	free (mods);
2d8b37
 	free (uac_str);
2d8b37
 	free (val);
2d8b37
 
2d8b37
@@ -1698,6 +1767,14 @@ update_computer_account (adcli_enroll *enroll)
2d8b37
 		res |= update_computer_attribute (enroll, ldap, mods);
2d8b37
 	}
2d8b37
 
2d8b37
+	if (res == ADCLI_SUCCESS && enroll->setattr != NULL) {
2d8b37
+		LDAPMod **mods = get_mods_for_attrs (enroll, LDAP_MOD_REPLACE);
2d8b37
+		if (mods != NULL) {
2d8b37
+			res |= update_computer_attribute (enroll, ldap, mods);
2d8b37
+			ldap_mods_free (mods, 1);
2d8b37
+		}
2d8b37
+	}
2d8b37
+
2d8b37
 	if (res != 0)
2d8b37
 		_adcli_info ("Updated existing computer account: %s", enroll->computer_dn);
2d8b37
 }
2d8b37
@@ -2751,6 +2828,7 @@ enroll_free (adcli_enroll *enroll)
2d8b37
 	free (enroll->user_principal);
2d8b37
 	_adcli_strv_free (enroll->service_names);
2d8b37
 	_adcli_strv_free (enroll->service_principals);
2d8b37
+	_adcli_strv_free (enroll->setattr);
2d8b37
 	_adcli_password_free (enroll->computer_password);
2d8b37
 
2d8b37
 	adcli_enroll_set_keytab_name (enroll, NULL);
2d8b37
@@ -3332,6 +3410,72 @@ adcli_enroll_add_service_principal_to_remove (adcli_enroll *enroll,
2d8b37
 	return_if_fail (enroll->service_principals_to_remove != NULL);
2d8b37
 }
2d8b37
 
2d8b37
+static int comp_attr_name (const char *s1, const char *s2)
2d8b37
+{
2d8b37
+	size_t c = 0;
2d8b37
+
2d8b37
+	/* empty strings cannot contain an attribute name */
2d8b37
+	if (s1 == NULL || s2 == NULL || *s1 == '\0' || *s2 == '\0') {
2d8b37
+		return 1;
2d8b37
+	}
2d8b37
+
2d8b37
+	for (c = 0 ; s1[c] != '\0' && s2[c] != '\0'; c++) {
2d8b37
+		if (s1[c] == '=' && s2[c] == '=') {
2d8b37
+			return 0;
2d8b37
+		} else if (tolower (s1[c]) != tolower (s2[c])) {
2d8b37
+			return 1;
2d8b37
+		}
2d8b37
+	}
2d8b37
+
2d8b37
+	return 1;
2d8b37
+}
2d8b37
+
2d8b37
+adcli_result
2d8b37
+adcli_enroll_add_setattr (adcli_enroll *enroll, const char *value)
2d8b37
+{
2d8b37
+	char *delim;
2d8b37
+
2d8b37
+	return_val_if_fail (enroll != NULL, ADCLI_ERR_CONFIG);
2d8b37
+	return_val_if_fail (value != NULL, ADCLI_ERR_CONFIG);
2d8b37
+
2d8b37
+	delim = strchr (value, '=');
2d8b37
+	if (delim == NULL) {
2d8b37
+		_adcli_err ("Missing '=' in setattr option [%s]", value);
2d8b37
+		return ADCLI_ERR_CONFIG;
2d8b37
+	}
2d8b37
+
2d8b37
+	if (*(delim + 1) == '\0') {
2d8b37
+		_adcli_err ("Missing value in setattr option [%s]", value);
2d8b37
+		return ADCLI_ERR_CONFIG;
2d8b37
+	}
2d8b37
+
2d8b37
+	*delim = '\0';
2d8b37
+	if (_adcli_strv_has_ex (default_ad_ldap_attrs, value, strcasecmp) == 1) {
2d8b37
+		_adcli_err ("Attribute [%s] cannot be set with setattr", value);
2d8b37
+		return ADCLI_ERR_CONFIG;
2d8b37
+	}
2d8b37
+	*delim = '=';
2d8b37
+
2d8b37
+	if (_adcli_strv_has_ex (enroll->setattr, value, comp_attr_name) == 1) {
2d8b37
+		_adcli_err ("Attribute [%s] already set", value);
2d8b37
+		return ADCLI_ERR_CONFIG;
2d8b37
+	}
2d8b37
+
2d8b37
+	enroll->setattr = _adcli_strv_add (enroll->setattr, strdup (value),
2d8b37
+	                                   NULL);
2d8b37
+	return_val_if_fail (enroll->setattr != NULL, ADCLI_ERR_CONFIG);
2d8b37
+
2d8b37
+	return ADCLI_SUCCESS;
2d8b37
+}
2d8b37
+
2d8b37
+const char **
2d8b37
+adcli_enroll_get_setattr (adcli_enroll *enroll)
2d8b37
+{
2d8b37
+	return_val_if_fail (enroll != NULL, NULL);
2d8b37
+	return (const char **) enroll->setattr;
2d8b37
+}
2d8b37
+
2d8b37
+
2d8b37
 #ifdef ADENROLL_TESTS
2d8b37
 
2d8b37
 #include "test.h"
2d8b37
@@ -3401,12 +3545,35 @@ test_adcli_enroll_get_permitted_keytab_enctypes (void)
2d8b37
 	adcli_conn_unref (conn);
2d8b37
 }
2d8b37
 
2d8b37
+static void
2d8b37
+test_comp_attr_name (void)
2d8b37
+{
2d8b37
+	assert_num_eq (1, comp_attr_name (NULL ,NULL));
2d8b37
+	assert_num_eq (1, comp_attr_name ("" ,NULL));
2d8b37
+	assert_num_eq (1, comp_attr_name ("" ,""));
2d8b37
+	assert_num_eq (1, comp_attr_name (NULL ,""));
2d8b37
+	assert_num_eq (1, comp_attr_name (NULL ,"abc=xyz"));
2d8b37
+	assert_num_eq (1, comp_attr_name ("" ,"abc=xyz"));
2d8b37
+	assert_num_eq (1, comp_attr_name ("abc=xyz", NULL));
2d8b37
+	assert_num_eq (1, comp_attr_name ("abc=xyz", ""));
2d8b37
+	assert_num_eq (1, comp_attr_name ("abc=xyz", "ab=xyz"));
2d8b37
+	assert_num_eq (1, comp_attr_name ("ab=xyz", "abc=xyz"));
2d8b37
+	assert_num_eq (1, comp_attr_name ("abcxyz", "abc=xyz"));
2d8b37
+	assert_num_eq (1, comp_attr_name ("abc=xyz", "abcxyz"));
2d8b37
+	assert_num_eq (1, comp_attr_name ("abc=xyz", "a"));
2d8b37
+	assert_num_eq (1, comp_attr_name ("a", "abc=xyz"));
2d8b37
+
2d8b37
+	assert_num_eq (0, comp_attr_name ("abc=xyz", "abc=xyz"));
2d8b37
+	assert_num_eq (0, comp_attr_name ("abc=xyz", "abc=123"));
2d8b37
+}
2d8b37
+
2d8b37
 int
2d8b37
 main (int argc,
2d8b37
       char *argv[])
2d8b37
 {
2d8b37
 	test_func (test_adcli_enroll_get_permitted_keytab_enctypes,
2d8b37
 	           "/attrs/adcli_enroll_get_permitted_keytab_enctypes");
2d8b37
+	test_func (test_comp_attr_name, "/attrs/comp_attr_name");
2d8b37
 	return test_run (argc, argv);
2d8b37
 }
2d8b37
 
2d8b37
diff --git a/library/adenroll.h b/library/adenroll.h
2d8b37
index 34dc683..862bb60 100644
2d8b37
--- a/library/adenroll.h
2d8b37
+++ b/library/adenroll.h
2d8b37
@@ -138,6 +138,10 @@ const char *       adcli_enroll_get_desciption          (adcli_enroll *enroll);
2d8b37
 void               adcli_enroll_set_description         (adcli_enroll *enroll,
2d8b37
                                                          const char *value);
2d8b37
 
2d8b37
+const char **      adcli_enroll_get_setattr             (adcli_enroll *enroll);
2d8b37
+adcli_result       adcli_enroll_add_setattr             (adcli_enroll *enroll,
2d8b37
+                                                         const char *value);
2d8b37
+
2d8b37
 bool               adcli_enroll_get_is_service          (adcli_enroll *enroll);
2d8b37
 void               adcli_enroll_set_is_service          (adcli_enroll *enroll,
2d8b37
                                                          bool value);
2d8b37
diff --git a/tools/computer.c b/tools/computer.c
2d8b37
index 16a1983..af38894 100644
2d8b37
--- a/tools/computer.c
2d8b37
+++ b/tools/computer.c
2d8b37
@@ -114,6 +114,7 @@ typedef enum {
2d8b37
 	opt_add_service_principal,
2d8b37
 	opt_remove_service_principal,
2d8b37
 	opt_description,
2d8b37
+	opt_setattr,
2d8b37
 	opt_use_ldaps,
2d8b37
 } Option;
2d8b37
 
2d8b37
@@ -152,6 +153,7 @@ static adcli_tool_desc common_usages[] = {
2d8b37
 	{ opt_add_service_principal, "add the given service principal to the account\n" },
2d8b37
 	{ opt_remove_service_principal, "remove the given service principal from the account\n" },
2d8b37
 	{ opt_description, "add a description to the account\n" },
2d8b37
+	{ opt_setattr, "add an attribute with a value\n" },
2d8b37
 	{ opt_no_password, "don't prompt for or read a password" },
2d8b37
 	{ opt_prompt_password, "prompt for a password if necessary" },
2d8b37
 	{ opt_stdin_password, "read a password from stdin (until EOF) if\n"
2d8b37
@@ -333,6 +335,12 @@ parse_option (Option opt,
2d8b37
 	case opt_description:
2d8b37
 		adcli_enroll_set_description (enroll, optarg);
2d8b37
 		return ADCLI_SUCCESS;
2d8b37
+	case opt_setattr:
2d8b37
+		ret =  adcli_enroll_add_setattr (enroll, optarg);
2d8b37
+		if (ret != ADCLI_SUCCESS) {
2d8b37
+			warnx ("parsing setattr option failed");
2d8b37
+		}
2d8b37
+		return ret;
2d8b37
 	case opt_use_ldaps:
2d8b37
 		adcli_conn_set_use_ldaps (conn, true);
2d8b37
 		return ADCLI_SUCCESS;
2d8b37
@@ -401,6 +409,7 @@ adcli_tool_computer_join (adcli_conn *conn,
2d8b37
 		{ "os-version", required_argument, NULL, opt_os_version },
2d8b37
 		{ "os-service-pack", optional_argument, NULL, opt_os_service_pack },
2d8b37
 		{ "description", optional_argument, NULL, opt_description },
2d8b37
+		{ "setattr", required_argument, NULL, opt_setattr },
2d8b37
 		{ "user-principal", optional_argument, NULL, opt_user_principal },
2d8b37
 		{ "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation },
2d8b37
 		{ "dont-expire-password", required_argument, NULL, opt_dont_expire_password },
2d8b37
@@ -524,6 +533,7 @@ adcli_tool_computer_update (adcli_conn *conn,
2d8b37
 		{ "os-version", required_argument, NULL, opt_os_version },
2d8b37
 		{ "os-service-pack", optional_argument, NULL, opt_os_service_pack },
2d8b37
 		{ "description", optional_argument, NULL, opt_description },
2d8b37
+		{ "setattr", required_argument, NULL, opt_setattr },
2d8b37
 		{ "user-principal", optional_argument, NULL, opt_user_principal },
2d8b37
 		{ "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime },
2d8b37
 		{ "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation },
2d8b37
-- 
2d8b37
2.31.1
2d8b37