diff --git a/SOURCES/0001-Add-setattr-option.patch b/SOURCES/0001-Add-setattr-option.patch
new file mode 100644
index 0000000000000000000000000000000000000000..abc40bb102a83eb64e067887a730ddbf39d53682
--- /dev/null
+++ b/SOURCES/0001-Add-setattr-option.patch
@@ -0,0 +1,384 @@
+From c5b0cee2976682b4fc1aeb02636cc9f2c6dbc2a5 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 14 Jun 2021 07:54:01 +0200
+Subject: [PATCH 1/2] Add setattr option
+
+With the new option common LDAP attributes can be set.
+
+Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1690920
+---
+ doc/adcli.xml      |  34 +++++++++
+ library/adenroll.c | 169 ++++++++++++++++++++++++++++++++++++++++++++-
+ library/adenroll.h |   4 ++
+ tools/computer.c   |  10 +++
+ 4 files changed, 216 insertions(+), 1 deletion(-)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index 6c36297..8383aa7 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -374,6 +374,23 @@ Password for Administrator:
+ 			service should be accessible with a different host
+ 			name as well.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--setattr=<parameter>name=value</parameter></option></term>
++			<listitem><para>Add the LDAP attribute
++			<option><parameter>name</parameter></option> with the
++			given <option><parameter>value</parameter></option> to
++			the new LDAP host object.
++			This option can be used multiple times to add multiple
++			different attributes. Multi-value attributes are
++			currently not supported.</para>
++			<para>Please note that the account used to join the
++			domain must have the required privileges to add the
++			given attributes. Some attributes might have
++			constraints with respect to syntax and allowed values
++			which must be met as well. Attributes managed by other
++			adcli options cannot be set with this option.</para>
++			</listitem>
++		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>--show-details</option></term>
+ 			<listitem><para>After a successful join print out information
+@@ -543,6 +560,23 @@ $ adcli update --login-ccache=/tmp/krbcc_123
+ 			<listitem><para>Remove a service principal name from
+ 			the keytab and the AD host object.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--setattr=<parameter>name=value</parameter></option></term>
++			<listitem><para>Add the LDAP attribute
++			<option><parameter>name</parameter></option> with the
++			given <option><parameter>value</parameter></option> to
++			the LDAP host object.
++			This option can be used multiple times to add multiple
++			different attributes. Multi-value attributes are
++			currently not supported.</para>
++			<para>Please note that the account used to update the
++			host object must have the required privileges to modify
++			the given attributes. Some attributes might have
++			constraints with respect to syntax and allowed values
++			which must be met as well. Attributes managed by other
++			adcli options cannot be set with this option.</para>
++			</listitem>
++		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>--show-details</option></term>
+ 			<listitem><para>After a successful join print out information
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 0b1c066..dd51567 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -150,4 +150,5 @@ struct _adcli_enroll {
+ 	char *description;
++	char **setattr;
+ };
+ 
+ static const char *
+@@ -795,6 +796,56 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype)
+ 	return ADCLI_SUCCESS;
+ }
+ 
++static LDAPMod **
++get_mods_for_attrs (adcli_enroll *enroll, int mod_op)
++{
++	size_t len;
++	size_t c;
++	char *end;
++	LDAPMod **mods = NULL;
++
++	len = _adcli_strv_len (enroll->setattr);
++	if (len == 0) {
++		return NULL;
++	}
++
++	mods = calloc (len + 1, sizeof (LDAPMod *));
++	return_val_if_fail (mods != NULL, NULL);
++
++	for (c = 0; c < len; c++) {
++		end = strchr (enroll->setattr[c], '=');
++		if (end == NULL) {
++			ldap_mods_free (mods, 1);
++			return NULL;
++		}
++
++		mods[c] = calloc (1, sizeof (LDAPMod));
++		if (mods[c] == NULL) {
++			ldap_mods_free (mods, 1);
++			return NULL;
++		}
++
++		mods[c]->mod_op = mod_op;
++		*end = '\0';
++		mods[c]->mod_type = strdup (enroll->setattr[c]);
++		*end = '=';
++		mods[c]->mod_values = calloc (2, sizeof (char *));
++		if (mods[c]->mod_type == NULL || mods[c]->mod_values == NULL) {
++			ldap_mods_free (mods, 1);
++			return NULL;
++		}
++
++		mods[c]->mod_values[0] = strdup (end + 1);
++		if (mods[c]->mod_values[0] == NULL) {
++			ldap_mods_free (mods, 1);
++			return NULL;
++		}
++	}
++
++	return mods;
++}
++
++
+ static adcli_result
+ create_computer_account (adcli_enroll *enroll,
+                          LDAP *ldap)
+@@ -828,6 +879,7 @@ create_computer_account (adcli_enroll *enroll,
+ 	size_t m;
+ 	uint32_t uac = UAC_WORKSTATION_TRUST_ACCOUNT | UAC_DONT_EXPIRE_PASSWORD ;
+ 	char *uac_str = NULL;
++	LDAPMod **extra_mods = NULL;
+ 
+ 	LDAPMod *all_mods[] = {
+ 		&objectClass,
+@@ -845,7 +897,7 @@ create_computer_account (adcli_enroll *enroll,
+ 	};
+ 
+ 	size_t mods_count = sizeof (all_mods) / sizeof (LDAPMod *);
+-	LDAPMod *mods[mods_count];
++	LDAPMod **mods;
+ 
+ 	if (adcli_enroll_get_trusted_for_delegation (enroll)) {
+ 		uac |= UAC_TRUSTED_FOR_DELEGATION;
+@@ -868,6 +920,17 @@ create_computer_account (adcli_enroll *enroll,
+ 	}
+ 	vals_supportedEncryptionTypes[0] = val;
+ 
++	if (enroll->setattr != NULL) {
++		extra_mods = get_mods_for_attrs (enroll, LDAP_MOD_ADD);
++		if (extra_mods == NULL) {
++			_adcli_err ("Failed to add setattr attributes, "
++			            "just using defaults");
++		}
++	}
++
++	mods = calloc (mods_count + seq_count (extra_mods) + 1, sizeof (LDAPMod *));
++	return_val_if_fail (mods != NULL, ADCLI_ERR_UNEXPECTED);
++
+ 	m = 0;
+ 	for (c = 0; c < mods_count - 1; c++) {
+ 		/* Skip empty LDAP sttributes */
+@@ -875,9 +938,15 @@ create_computer_account (adcli_enroll *enroll,
+ 			mods[m++] = all_mods[c];
+ 		}
+ 	}
++
++	for (c = 0; c < seq_count (extra_mods); c++) {
++		mods[m++] = extra_mods[c];
++	}
+ 	mods[m] = NULL;
+ 
+ 	ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL);
++	ldap_mods_free (extra_mods, 1);
++	free (mods);
+ 	free (uac_str);
+ 	free (val);
+ 
+@@ -1698,6 +1767,14 @@ update_computer_account (adcli_enroll *enroll)
+ 		res |= update_computer_attribute (enroll, ldap, mods);
+ 	}
+ 
++	if (res == ADCLI_SUCCESS && enroll->setattr != NULL) {
++		LDAPMod **mods = get_mods_for_attrs (enroll, LDAP_MOD_REPLACE);
++		if (mods != NULL) {
++			res |= update_computer_attribute (enroll, ldap, mods);
++			ldap_mods_free (mods, 1);
++		}
++	}
++
+ 	if (res != 0)
+ 		_adcli_info ("Updated existing computer account: %s", enroll->computer_dn);
+ }
+@@ -2751,6 +2828,7 @@ enroll_free (adcli_enroll *enroll)
+ 	free (enroll->user_principal);
+ 	_adcli_strv_free (enroll->service_names);
+ 	_adcli_strv_free (enroll->service_principals);
++	_adcli_strv_free (enroll->setattr);
+ 	_adcli_password_free (enroll->computer_password);
+ 
+ 	adcli_enroll_set_keytab_name (enroll, NULL);
+@@ -3332,6 +3410,72 @@ adcli_enroll_add_service_principal_to_remove (adcli_enroll *enroll,
+ 	return_if_fail (enroll->service_principals_to_remove != NULL);
+ }
+ 
++static int comp_attr_name (const char *s1, const char *s2)
++{
++	size_t c = 0;
++
++	/* empty strings cannot contain an attribute name */
++	if (s1 == NULL || s2 == NULL || *s1 == '\0' || *s2 == '\0') {
++		return 1;
++	}
++
++	for (c = 0 ; s1[c] != '\0' && s2[c] != '\0'; c++) {
++		if (s1[c] == '=' && s2[c] == '=') {
++			return 0;
++		} else if (tolower (s1[c]) != tolower (s2[c])) {
++			return 1;
++		}
++	}
++
++	return 1;
++}
++
++adcli_result
++adcli_enroll_add_setattr (adcli_enroll *enroll, const char *value)
++{
++	char *delim;
++
++	return_val_if_fail (enroll != NULL, ADCLI_ERR_CONFIG);
++	return_val_if_fail (value != NULL, ADCLI_ERR_CONFIG);
++
++	delim = strchr (value, '=');
++	if (delim == NULL) {
++		_adcli_err ("Missing '=' in setattr option [%s]", value);
++		return ADCLI_ERR_CONFIG;
++	}
++
++	if (*(delim + 1) == '\0') {
++		_adcli_err ("Missing value in setattr option [%s]", value);
++		return ADCLI_ERR_CONFIG;
++	}
++
++	*delim = '\0';
++	if (_adcli_strv_has_ex (default_ad_ldap_attrs, value, strcasecmp) == 1) {
++		_adcli_err ("Attribute [%s] cannot be set with setattr", value);
++		return ADCLI_ERR_CONFIG;
++	}
++	*delim = '=';
++
++	if (_adcli_strv_has_ex (enroll->setattr, value, comp_attr_name) == 1) {
++		_adcli_err ("Attribute [%s] already set", value);
++		return ADCLI_ERR_CONFIG;
++	}
++
++	enroll->setattr = _adcli_strv_add (enroll->setattr, strdup (value),
++	                                   NULL);
++	return_val_if_fail (enroll->setattr != NULL, ADCLI_ERR_CONFIG);
++
++	return ADCLI_SUCCESS;
++}
++
++const char **
++adcli_enroll_get_setattr (adcli_enroll *enroll)
++{
++	return_val_if_fail (enroll != NULL, NULL);
++	return (const char **) enroll->setattr;
++}
++
++
+ #ifdef ADENROLL_TESTS
+ 
+ #include "test.h"
+@@ -3401,12 +3545,35 @@ test_adcli_enroll_get_permitted_keytab_enctypes (void)
+ 	adcli_conn_unref (conn);
+ }
+ 
++static void
++test_comp_attr_name (void)
++{
++	assert_num_eq (1, comp_attr_name (NULL ,NULL));
++	assert_num_eq (1, comp_attr_name ("" ,NULL));
++	assert_num_eq (1, comp_attr_name ("" ,""));
++	assert_num_eq (1, comp_attr_name (NULL ,""));
++	assert_num_eq (1, comp_attr_name (NULL ,"abc=xyz"));
++	assert_num_eq (1, comp_attr_name ("" ,"abc=xyz"));
++	assert_num_eq (1, comp_attr_name ("abc=xyz", NULL));
++	assert_num_eq (1, comp_attr_name ("abc=xyz", ""));
++	assert_num_eq (1, comp_attr_name ("abc=xyz", "ab=xyz"));
++	assert_num_eq (1, comp_attr_name ("ab=xyz", "abc=xyz"));
++	assert_num_eq (1, comp_attr_name ("abcxyz", "abc=xyz"));
++	assert_num_eq (1, comp_attr_name ("abc=xyz", "abcxyz"));
++	assert_num_eq (1, comp_attr_name ("abc=xyz", "a"));
++	assert_num_eq (1, comp_attr_name ("a", "abc=xyz"));
++
++	assert_num_eq (0, comp_attr_name ("abc=xyz", "abc=xyz"));
++	assert_num_eq (0, comp_attr_name ("abc=xyz", "abc=123"));
++}
++
+ int
+ main (int argc,
+       char *argv[])
+ {
+ 	test_func (test_adcli_enroll_get_permitted_keytab_enctypes,
+ 	           "/attrs/adcli_enroll_get_permitted_keytab_enctypes");
++	test_func (test_comp_attr_name, "/attrs/comp_attr_name");
+ 	return test_run (argc, argv);
+ }
+ 
+diff --git a/library/adenroll.h b/library/adenroll.h
+index 34dc683..862bb60 100644
+--- a/library/adenroll.h
++++ b/library/adenroll.h
+@@ -138,6 +138,10 @@ const char *       adcli_enroll_get_desciption          (adcli_enroll *enroll);
+ void               adcli_enroll_set_description         (adcli_enroll *enroll,
+                                                          const char *value);
+ 
++const char **      adcli_enroll_get_setattr             (adcli_enroll *enroll);
++adcli_result       adcli_enroll_add_setattr             (adcli_enroll *enroll,
++                                                         const char *value);
++
+ bool               adcli_enroll_get_is_service          (adcli_enroll *enroll);
+ void               adcli_enroll_set_is_service          (adcli_enroll *enroll,
+                                                          bool value);
+diff --git a/tools/computer.c b/tools/computer.c
+index 16a1983..af38894 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -114,6 +114,7 @@ typedef enum {
+ 	opt_add_service_principal,
+ 	opt_remove_service_principal,
+ 	opt_description,
++	opt_setattr,
+ 	opt_use_ldaps,
+ } Option;
+ 
+@@ -152,6 +153,7 @@ static adcli_tool_desc common_usages[] = {
+ 	{ opt_add_service_principal, "add the given service principal to the account\n" },
+ 	{ opt_remove_service_principal, "remove the given service principal from the account\n" },
+ 	{ opt_description, "add a description to the account\n" },
++	{ opt_setattr, "add an attribute with a value\n" },
+ 	{ opt_no_password, "don't prompt for or read a password" },
+ 	{ opt_prompt_password, "prompt for a password if necessary" },
+ 	{ opt_stdin_password, "read a password from stdin (until EOF) if\n"
+@@ -333,6 +335,12 @@ parse_option (Option opt,
+ 	case opt_description:
+ 		adcli_enroll_set_description (enroll, optarg);
+ 		return ADCLI_SUCCESS;
++	case opt_setattr:
++		ret =  adcli_enroll_add_setattr (enroll, optarg);
++		if (ret != ADCLI_SUCCESS) {
++			warnx ("parsing setattr option failed");
++		}
++		return ret;
+ 	case opt_use_ldaps:
+ 		adcli_conn_set_use_ldaps (conn, true);
+ 		return ADCLI_SUCCESS;
+@@ -401,6 +409,7 @@ adcli_tool_computer_join (adcli_conn *conn,
+ 		{ "os-version", required_argument, NULL, opt_os_version },
+ 		{ "os-service-pack", optional_argument, NULL, opt_os_service_pack },
+ 		{ "description", optional_argument, NULL, opt_description },
++		{ "setattr", required_argument, NULL, opt_setattr },
+ 		{ "user-principal", optional_argument, NULL, opt_user_principal },
+ 		{ "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation },
+ 		{ "dont-expire-password", required_argument, NULL, opt_dont_expire_password },
+@@ -524,6 +533,7 @@ adcli_tool_computer_update (adcli_conn *conn,
+ 		{ "os-version", required_argument, NULL, opt_os_version },
+ 		{ "os-service-pack", optional_argument, NULL, opt_os_service_pack },
+ 		{ "description", optional_argument, NULL, opt_description },
++		{ "setattr", required_argument, NULL, opt_setattr },
+ 		{ "user-principal", optional_argument, NULL, opt_user_principal },
+ 		{ "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime },
+ 		{ "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation },
+-- 
+2.31.1
+
diff --git a/SOURCES/0001-Fix-for-dont-expire-password-option-and-join.patch b/SOURCES/0001-Fix-for-dont-expire-password-option-and-join.patch
new file mode 100644
index 0000000000000000000000000000000000000000..9740f3194a178604db5c0c902b72f5cd0c8b7ed9
--- /dev/null
+++ b/SOURCES/0001-Fix-for-dont-expire-password-option-and-join.patch
@@ -0,0 +1,27 @@
+From 924465af7a4f37390bfdfdb4971e88421f52f3d9 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 3 Jun 2021 15:03:20 +0200
+Subject: [PATCH] Fix for dont-expire-password option and join
+
+Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1769644
+---
+ library/adenroll.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index f3d606e..5b0dcd5 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -856,7 +856,8 @@ create_computer_account (adcli_enroll *enroll,
+ 		uac |= UAC_TRUSTED_FOR_DELEGATION;
+ 	}
+ 
+-	if (!adcli_enroll_get_dont_expire_password (enroll)) {
++	if (enroll->dont_expire_password_explicit
++		       && !adcli_enroll_get_dont_expire_password (enroll)) {
+ 		uac &= ~(UAC_DONT_EXPIRE_PASSWORD);
+ 	}
+ 
+-- 
+2.31.1
+
diff --git a/SOURCES/0001-build-add-with-vendor-error-message-configure-option.patch b/SOURCES/0001-build-add-with-vendor-error-message-configure-option.patch
new file mode 100644
index 0000000000000000000000000000000000000000..75235eef192d6b17a769a7bb832089b5fd352d8a
--- /dev/null
+++ b/SOURCES/0001-build-add-with-vendor-error-message-configure-option.patch
@@ -0,0 +1,60 @@
+From 0353d704879f20983184f8bded4f16538d72f7cc Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 10 Mar 2021 18:12:09 +0100
+Subject: [PATCH] build: add --with-vendor-error-message configure option
+
+With the new configure option --with-vendor-error-message a packager or
+a distribution can add a message if adcli returns with an error.
+
+Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1889386
+---
+ configure.ac  | 15 +++++++++++++++
+ tools/tools.c |  6 ++++++
+ 2 files changed, 21 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index baa0d3b..7dfba97 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -123,6 +123,21 @@ if test "$sasl_invalid" = "yes"; then
+ 	AC_MSG_ERROR([Couldn't find Cyrus SASL headers])
+ fi
+ 
++# --------------------------------------------------------------------
++# Vendor error message
++
++AC_ARG_WITH([vendor-error-message],
++              [AS_HELP_STRING([--with-vendor-error-message=ARG],
++                            [Add a vendor specific error message shown if a adcli command fails]
++                           )],
++              [AS_IF([test "x$withval" != "x"],
++                     [AC_DEFINE_UNQUOTED([VENDOR_MSG],
++                                         ["$withval"],
++                                         [Vendor specific error message])],
++                     [AC_MSG_ERROR([--with-vendor-error-message requires an argument])]
++                    )],
++              [])
++
+ # --------------------------------------------------------------------
+ # Documentation options
+ 
+diff --git a/tools/tools.c b/tools/tools.c
+index d0dcf98..84bbba9 100644
+--- a/tools/tools.c
++++ b/tools/tools.c
+@@ -538,6 +538,12 @@ main (int argc,
+ 
+ 		if (conn)
+ 			adcli_conn_unref (conn);
++#ifdef VENDOR_MSG
++		if (ret != 0) {
++			fprintf (stderr, VENDOR_MSG"\n");
++		}
++#endif
++
+ 		return ret;
+ 	}
+ 
+-- 
+2.30.2
+
diff --git a/SOURCES/0001-coverity-add-missing-NULL-checks.patch b/SOURCES/0001-coverity-add-missing-NULL-checks.patch
new file mode 100644
index 0000000000000000000000000000000000000000..2f419920a9348baa024d49e0fce12a5ee4d000f9
--- /dev/null
+++ b/SOURCES/0001-coverity-add-missing-NULL-checks.patch
@@ -0,0 +1,44 @@
+From 13fe79c0a78028ccfe8e3d4e5ee16cfb9e143924 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 2 Jun 2021 13:39:31 +0200
+Subject: [PATCH 1/2] coverity: add missing NULL checks
+
+---
+ library/adenroll.c | 2 ++
+ library/adldap.c   | 7 +++++++
+ 2 files changed, 9 insertions(+)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index f693e58..c726093 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -3046,6 +3046,8 @@ adcli_enroll_set_keytab_enctypes (adcli_enroll *enroll,
+ 	krb5_enctype *newval = NULL;
+ 	int len;
+ 
++	return_if_fail (enroll != NULL);
++
+ 	if (value) {
+ 		for (len = 0; value[len] != 0; len++);
+ 		newval = malloc (sizeof (krb5_enctype) * (len + 1));
+diff --git a/library/adldap.c b/library/adldap.c
+index d93efb7..b86014c 100644
+--- a/library/adldap.c
++++ b/library/adldap.c
+@@ -231,6 +231,13 @@ _adcli_ldap_have_in_mod (LDAPMod *mod,
+ 
+ 	vals = malloc (sizeof (struct berval) * (count + 1));
+ 	pvals = malloc (sizeof (struct berval *) * (count + 1));
++	if (vals == NULL || pvals == NULL) {
++		_adcli_err ("Memory allocation failed, assuming attribute must be updated.");
++		free (vals);
++		free (pvals);
++		return 0;
++	}
++
+ 	for (i = 0; i < count; i++) {
+ 		vals[i].bv_val = mod->mod_vals.modv_strvals[i];
+ 		vals[i].bv_len = strlen (vals[i].bv_val);
+-- 
+2.31.1
+
diff --git a/SOURCES/0001-library-move-UAC-flags-to-a-more-common-header-file.patch b/SOURCES/0001-library-move-UAC-flags-to-a-more-common-header-file.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0129517fc4a01c2ce6fedcc0170840d0c0413396
--- /dev/null
+++ b/SOURCES/0001-library-move-UAC-flags-to-a-more-common-header-file.patch
@@ -0,0 +1,50 @@
+From a7a40ce4f47fe40305624b6d86c135b7d27c387d Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 11 Jun 2021 12:44:36 +0200
+Subject: [PATCH 1/3] library: move UAC flags to a more common header file
+
+---
+ library/adenroll.c  | 8 --------
+ library/adprivate.h | 8 ++++++++
+ 2 files changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index f00d179..0b1c066 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -93,13 +93,6 @@ static char *default_ad_ldap_attrs[] =  {
+ 	NULL,
+ };
+ 
+-/* Some constants for the userAccountControl AD LDAP attribute, see e.g.
+- * https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro
+- * for details. */
+-#define UAC_WORKSTATION_TRUST_ACCOUNT  0x1000
+-#define UAC_DONT_EXPIRE_PASSWORD      0x10000
+-#define UAC_TRUSTED_FOR_DELEGATION    0x80000
+-
+ struct _adcli_enroll {
+ 	int refs;
+ 	adcli_conn *conn;
+diff --git a/library/adprivate.h b/library/adprivate.h
+index 55e6234..822f919 100644
+--- a/library/adprivate.h
++++ b/library/adprivate.h
+@@ -39,6 +39,14 @@
+ #define HOST_NAME_MAX 255
+ #endif
+ 
++/* Some constants for the userAccountControl AD LDAP attribute, see e.g.
++ * https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro
++ * for details. */
++#define UAC_ACCOUNTDISABLE             0x0002
++#define UAC_WORKSTATION_TRUST_ACCOUNT  0x1000
++#define UAC_DONT_EXPIRE_PASSWORD      0x10000
++#define UAC_TRUSTED_FOR_DELEGATION    0x80000
++
+ /* Utilities */
+ 
+ #if !defined(__cplusplus) && (__GNUC__ > 2)
+-- 
+2.31.1
+
diff --git a/SOURCES/0002-Add-delattr-option.patch b/SOURCES/0002-Add-delattr-option.patch
new file mode 100644
index 0000000000000000000000000000000000000000..92cf6237f82e6fcdf05530472ca31ca7cec6f7bb
--- /dev/null
+++ b/SOURCES/0002-Add-delattr-option.patch
@@ -0,0 +1,191 @@
+From cd5b6cdcf3e6bfc5776f2865f460f608421dfa3f Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 14 Jun 2021 08:42:21 +0200
+Subject: [PATCH 2/2] Add delattr option
+
+Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1690920
+---
+ doc/adcli.xml      | 11 ++++++++
+ library/adenroll.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++
+ library/adenroll.h |  4 +++
+ tools/computer.c   |  9 +++++++
+ 4 files changed, 90 insertions(+)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index 8383aa7..bcf4857 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -577,6 +577,17 @@ $ adcli update --login-ccache=/tmp/krbcc_123
+ 			adcli options cannot be set with this option.</para>
+ 			</listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--delattr=<parameter>name</parameter></option></term>
++			<listitem><para>Remove the LDAP attribute
++			<option><parameter>name</parameter></option> from the
++			LDAP host object. This option can be used multiple
++			times to remove multiple different attributes.</para>
++			<para>Please note that the account used to update the
++			host object must have the required privileges to delete
++			the given attributes. Attributes managed by other adcli
++			options cannot be removed.</para></listitem>
++		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>--show-details</option></term>
+ 			<listitem><para>After a successful join print out information
+diff --git a/library/adenroll.c b/library/adenroll.c
+index dd51567..9a06d52 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -151,5 +151,6 @@ struct _adcli_enroll {
+ 	char *description;
+ 	char **setattr;
++	char **delattr;
+ };
+ 
+ static const char *
+@@ -845,6 +846,39 @@ get_mods_for_attrs (adcli_enroll *enroll, int mod_op)
+ 	return mods;
+ }
+ 
++static LDAPMod **
++get_del_mods_for_attrs (adcli_enroll *enroll, int mod_op)
++{
++	size_t len;
++	size_t c;
++	LDAPMod **mods = NULL;
++
++	len = _adcli_strv_len (enroll->delattr);
++	if (len == 0) {
++		return NULL;
++	}
++
++	mods = calloc (len + 1, sizeof (LDAPMod *));
++	return_val_if_fail (mods != NULL, NULL);
++
++	for (c = 0; c < len; c++) {
++		mods[c] = calloc (1, sizeof (LDAPMod));
++		if (mods[c] == NULL) {
++			ldap_mods_free (mods, 1);
++			return NULL;
++		}
++
++		mods[c]->mod_op = mod_op;
++		mods[c]->mod_type = strdup (enroll->delattr[c]);
++		mods[c]->mod_values = NULL;
++		if (mods[c]->mod_type == NULL) {
++			ldap_mods_free (mods, 1);
++			return NULL;
++		}
++	}
++
++	return mods;
++}
+ 
+ static adcli_result
+ create_computer_account (adcli_enroll *enroll,
+@@ -1775,6 +1809,14 @@ update_computer_account (adcli_enroll *enroll)
+ 		}
+ 	}
+ 
++	if (res == ADCLI_SUCCESS && enroll->delattr != NULL) {
++		LDAPMod **mods = get_del_mods_for_attrs (enroll, LDAP_MOD_DELETE);
++		if (mods != NULL) {
++			res |= update_computer_attribute (enroll, ldap, mods);
++			ldap_mods_free (mods, 1);
++		}
++	}
++
+ 	if (res != 0)
+ 		_adcli_info ("Updated existing computer account: %s", enroll->computer_dn);
+ }
+@@ -3475,6 +3517,30 @@ adcli_enroll_get_setattr (adcli_enroll *enroll)
+ 	return (const char **) enroll->setattr;
+ }
+ 
++adcli_result
++adcli_enroll_add_delattr (adcli_enroll *enroll, const char *value)
++{
++	return_val_if_fail (enroll != NULL, ADCLI_ERR_CONFIG);
++	return_val_if_fail (value != NULL, ADCLI_ERR_CONFIG);
++
++	if (_adcli_strv_has_ex (default_ad_ldap_attrs, value, strcasecmp) == 1) {
++		_adcli_err ("Attribute [%s] cannot be removed with delattr", value);
++		return ADCLI_ERR_CONFIG;
++	}
++
++	enroll->delattr = _adcli_strv_add (enroll->delattr, strdup (value),
++	                                   NULL);
++	return_val_if_fail (enroll->delattr != NULL, ADCLI_ERR_CONFIG);
++
++	return ADCLI_SUCCESS;
++}
++
++const char **
++adcli_enroll_get_delattr (adcli_enroll *enroll)
++{
++	return_val_if_fail (enroll != NULL, NULL);
++	return (const char **) enroll->delattr;
++}
+ 
+ #ifdef ADENROLL_TESTS
+ 
+diff --git a/library/adenroll.h b/library/adenroll.h
+index 862bb60..e3ada33 100644
+--- a/library/adenroll.h
++++ b/library/adenroll.h
+@@ -142,6 +142,10 @@ const char **      adcli_enroll_get_setattr             (adcli_enroll *enroll);
+ adcli_result       adcli_enroll_add_setattr             (adcli_enroll *enroll,
+                                                          const char *value);
+ 
++const char **      adcli_enroll_get_delattr             (adcli_enroll *enroll);
++adcli_result       adcli_enroll_add_delattr             (adcli_enroll *enroll,
++                                                         const char *value);
++
+ bool               adcli_enroll_get_is_service          (adcli_enroll *enroll);
+ void               adcli_enroll_set_is_service          (adcli_enroll *enroll,
+                                                          bool value);
+diff --git a/tools/computer.c b/tools/computer.c
+index af38894..dffeecb 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -115,6 +115,7 @@ typedef enum {
+ 	opt_remove_service_principal,
+ 	opt_description,
+ 	opt_setattr,
++	opt_delattr,
+ 	opt_use_ldaps,
+ } Option;
+ 
+@@ -154,6 +155,7 @@ static adcli_tool_desc common_usages[] = {
+ 	{ opt_remove_service_principal, "remove the given service principal from the account\n" },
+ 	{ opt_description, "add a description to the account\n" },
+ 	{ opt_setattr, "add an attribute with a value\n" },
++	{ opt_delattr, "remove an attribute\n" },
+ 	{ opt_no_password, "don't prompt for or read a password" },
+ 	{ opt_prompt_password, "prompt for a password if necessary" },
+ 	{ opt_stdin_password, "read a password from stdin (until EOF) if\n"
+@@ -341,6 +343,12 @@ parse_option (Option opt,
+ 			warnx ("parsing setattr option failed");
+ 		}
+ 		return ret;
++	case opt_delattr:
++		ret = adcli_enroll_add_delattr (enroll, optarg);
++		if (ret != ADCLI_SUCCESS) {
++			warnx ("parsing delattr option failed");
++		}
++		return ret;
+ 	case opt_use_ldaps:
+ 		adcli_conn_set_use_ldaps (conn, true);
+ 		return ADCLI_SUCCESS;
+@@ -534,6 +542,7 @@ adcli_tool_computer_update (adcli_conn *conn,
+ 		{ "os-service-pack", optional_argument, NULL, opt_os_service_pack },
+ 		{ "description", optional_argument, NULL, opt_description },
+ 		{ "setattr", required_argument, NULL, opt_setattr },
++		{ "delattr", required_argument, NULL, opt_delattr },
+ 		{ "user-principal", optional_argument, NULL, opt_user_principal },
+ 		{ "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime },
+ 		{ "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation },
+-- 
+2.31.1
+
diff --git a/SOURCES/0002-Add-dont-expire-password-option.patch b/SOURCES/0002-Add-dont-expire-password-option.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0ae8a90b7ce28c6472c6113674aaa4bee36349db
--- /dev/null
+++ b/SOURCES/0002-Add-dont-expire-password-option.patch
@@ -0,0 +1,241 @@
+From 74b52a30c2b142118b7f26f78c014e7bee825e84 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 2 Jun 2021 17:24:07 +0200
+Subject: [PATCH 2/2] Add dont-expire-password option
+
+Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1769644
+---
+ doc/adcli.xml      | 28 ++++++++++++++++++++++
+ library/adenroll.c | 58 +++++++++++++++++++++++++++++++++++++++++-----
+ library/adenroll.h |  4 ++++
+ tools/computer.c   | 12 ++++++++++
+ 4 files changed, 96 insertions(+), 6 deletions(-)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index a64687a..7c2e126 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -347,6 +347,20 @@ Password for Administrator:
+ 			not allow that Kerberos tickets can be forwarded to the
+ 			host.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--dont-expire-password=<parameter>yes|no|true|false</parameter></option></term>
++			<listitem><para>Set or unset the DONT_EXPIRE_PASSWORD
++			flag in the userAccountControl attribute to indicate if
++			the machine account password should expire or not. By
++			default adcli will set this flag while joining the
++			domain which corresponds to the default behavior of
++			Windows clients.</para>
++			<para>Please note that if the password will expire
++			(--dont-expire-password=false) a renewal mechanism has
++			to be enabled on the client to not loose the
++			connectivity to AD if the password expires.</para>
++			</listitem>
++		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>--add-service-principal=<parameter>service/hostname</parameter></option></term>
+ 			<listitem><para>Add a service principal name. In
+@@ -491,6 +505,20 @@ $ adcli update --login-ccache=/tmp/krbcc_123
+ 			not allow that Kerberos tickets can be forwarded to the
+ 			host.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--dont-expire-password=<parameter>yes|no|true|false</parameter></option></term>
++			<listitem><para>Set or unset the DONT_EXPIRE_PASSWORD
++			flag in the userAccountControl attribute to indicate if
++			the machine account password should expire or not. By
++			default adcli will set this flag while joining the
++			domain which corresponds to the default behavior of
++			Windows clients.</para>
++			<para>Please note that if the password will expire
++			(--dont-expire-password=false) a renewal mechanism has
++			to be enabled on the client to not loose the
++			connectivity to AD if the password expires.</para>
++			</listitem>
++		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>--add-service-principal=<parameter>service/hostname</parameter></option></term>
+ 			<listitem><para>Add a service principal name. In
+diff --git a/library/adenroll.c b/library/adenroll.c
+index c726093..01f149c 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -152,6 +152,8 @@ struct _adcli_enroll {
+ 	char *samba_data_tool;
+ 	bool trusted_for_delegation;
+ 	int trusted_for_delegation_explicit;
++	bool dont_expire_password;
++	int dont_expire_password_explicit;
+ 	char *description;
+ };
+ 
+@@ -829,6 +831,8 @@ create_computer_account (adcli_enroll *enroll,
+ 	int ret;
+ 	size_t c;
+ 	size_t m;
++	uint32_t uac = UAC_WORKSTATION_TRUST_ACCOUNT | UAC_DONT_EXPIRE_PASSWORD ;
++	char *uac_str = NULL;
+ 
+ 	LDAPMod *all_mods[] = {
+ 		&objectClass,
+@@ -849,11 +853,21 @@ create_computer_account (adcli_enroll *enroll,
+ 	LDAPMod *mods[mods_count];
+ 
+ 	if (adcli_enroll_get_trusted_for_delegation (enroll)) {
+-		vals_userAccountControl[0] = "593920"; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD | TRUSTED_FOR_DELEGATION */
++		uac |= UAC_TRUSTED_FOR_DELEGATION;
++	}
++
++	if (!adcli_enroll_get_dont_expire_password (enroll)) {
++		uac &= ~(UAC_DONT_EXPIRE_PASSWORD);
++	}
++
++	if (asprintf (&uac_str, "%d", uac) < 0) {
++		return_val_if_reached (ADCLI_ERR_UNEXPECTED);
+ 	}
++	vals_userAccountControl[0] = uac_str;
+ 
+ 	ret = calculate_enctypes (enroll, &val);
+ 	if (ret != ADCLI_SUCCESS) {
++		free (uac_str);
+ 		return ret;
+ 	}
+ 	vals_supportedEncryptionTypes[0] = val;
+@@ -868,6 +882,7 @@ create_computer_account (adcli_enroll *enroll,
+ 	mods[m] = NULL;
+ 
+ 	ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL);
++	free (uac_str);
+ 	free (val);
+ 
+ 	/*
+@@ -1566,10 +1581,20 @@ static char *get_user_account_control (adcli_enroll *enroll)
+ 		uac = UAC_WORKSTATION_TRUST_ACCOUNT | UAC_DONT_EXPIRE_PASSWORD;
+ 	}
+ 
+-	if (adcli_enroll_get_trusted_for_delegation (enroll)) {
+-		uac |= UAC_TRUSTED_FOR_DELEGATION;
+-	} else {
+-		uac &= ~(UAC_TRUSTED_FOR_DELEGATION);
++	if (enroll->trusted_for_delegation_explicit) {
++		if (adcli_enroll_get_trusted_for_delegation (enroll)) {
++			uac |= UAC_TRUSTED_FOR_DELEGATION;
++		} else {
++			uac &= ~(UAC_TRUSTED_FOR_DELEGATION);
++		}
++	}
++
++	if (enroll->dont_expire_password_explicit) {
++		if (adcli_enroll_get_dont_expire_password (enroll)) {
++			uac |= UAC_DONT_EXPIRE_PASSWORD;
++		} else {
++			uac &= ~(UAC_DONT_EXPIRE_PASSWORD);
++		}
+ 	}
+ 
+ 	if (asprintf (&uac_str, "%d", uac) < 0) {
+@@ -1613,7 +1640,8 @@ update_computer_account (adcli_enroll *enroll)
+ 	}
+ 	free (value);
+ 
+-	if (res == ADCLI_SUCCESS && enroll->trusted_for_delegation_explicit) {
++	if (res == ADCLI_SUCCESS && (enroll->trusted_for_delegation_explicit ||
++	                             enroll->dont_expire_password_explicit)) {
+ 		char *vals_userAccountControl[] = { NULL , NULL };
+ 		LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } };
+ 		LDAPMod *mods[] = { &userAccountControl, NULL };
+@@ -3194,6 +3222,24 @@ adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll,
+ 	enroll->trusted_for_delegation_explicit = 1;
+ }
+ 
++bool
++adcli_enroll_get_dont_expire_password (adcli_enroll *enroll)
++{
++	return_val_if_fail (enroll != NULL, false);
++
++	return enroll->dont_expire_password;
++}
++
++void
++adcli_enroll_set_dont_expire_password (adcli_enroll *enroll,
++                                       bool value)
++{
++	return_if_fail (enroll != NULL);
++
++	enroll->dont_expire_password = value;
++	enroll->dont_expire_password_explicit = 1;
++}
++
+ void
+ adcli_enroll_set_description (adcli_enroll *enroll, const char *value)
+ {
+diff --git a/library/adenroll.h b/library/adenroll.h
+index 11a30c8..5190eb6 100644
+--- a/library/adenroll.h
++++ b/library/adenroll.h
+@@ -126,6 +126,10 @@ bool               adcli_enroll_get_trusted_for_delegation (adcli_enroll *enroll
+ void               adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll,
+                                                             bool value);
+ 
++bool               adcli_enroll_get_dont_expire_password (adcli_enroll *enroll);
++void               adcli_enroll_set_dont_expire_password (adcli_enroll *enroll,
++                                                          bool value);
++
+ const char *       adcli_enroll_get_desciption          (adcli_enroll *enroll);
+ void               adcli_enroll_set_description         (adcli_enroll *enroll,
+                                                          const char *value);
+diff --git a/tools/computer.c b/tools/computer.c
+index 98a0472..954066a 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -110,6 +110,7 @@ typedef enum {
+ 	opt_add_samba_data,
+ 	opt_samba_data_tool,
+ 	opt_trusted_for_delegation,
++	opt_dont_expire_password,
+ 	opt_add_service_principal,
+ 	opt_remove_service_principal,
+ 	opt_description,
+@@ -143,6 +144,8 @@ static adcli_tool_desc common_usages[] = {
+ 	{ opt_computer_password_lifetime, "lifetime of the host accounts password in days", },
+ 	{ opt_trusted_for_delegation, "set/unset the TRUSTED_FOR_DELEGATION flag\n"
+ 	                              "in the userAccountControl attribute", },
++	{ opt_dont_expire_password, "set/unset the DONT_EXPIRE_PASSWORD flag\n"
++	                            "in the userAccountControl attribute", },
+ 	{ opt_add_service_principal, "add the given service principal to the account\n" },
+ 	{ opt_remove_service_principal, "remove the given service principal from the account\n" },
+ 	{ opt_description, "add a description to the account\n" },
+@@ -304,6 +307,13 @@ parse_option (Option opt,
+ 			adcli_enroll_set_trusted_for_delegation (enroll, false);
+ 		}
+ 		return ADCLI_SUCCESS;
++	case opt_dont_expire_password:
++		if (strcasecmp (optarg, "true") == 0 || strcasecmp (optarg, "yes") == 0) {
++			adcli_enroll_set_dont_expire_password (enroll, true);
++		} else {
++			adcli_enroll_set_dont_expire_password (enroll, false);
++		}
++		return ADCLI_SUCCESS;
+ 	case opt_add_service_principal:
+ 		adcli_enroll_add_service_principal_to_add (enroll, optarg);
+ 		return ADCLI_SUCCESS;
+@@ -383,6 +393,7 @@ adcli_tool_computer_join (adcli_conn *conn,
+ 		{ "description", optional_argument, NULL, opt_description },
+ 		{ "user-principal", optional_argument, NULL, opt_user_principal },
+ 		{ "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation },
++		{ "dont-expire-password", required_argument, NULL, opt_dont_expire_password },
+ 		{ "add-service-principal", required_argument, NULL, opt_add_service_principal },
+ 		{ "show-details", no_argument, NULL, opt_show_details },
+ 		{ "show-password", no_argument, NULL, opt_show_password },
+@@ -504,6 +515,7 @@ adcli_tool_computer_update (adcli_conn *conn,
+ 		{ "user-principal", optional_argument, NULL, opt_user_principal },
+ 		{ "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime },
+ 		{ "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation },
++		{ "dont-expire-password", required_argument, NULL, opt_dont_expire_password },
+ 		{ "add-service-principal", required_argument, NULL, opt_add_service_principal },
+ 		{ "remove-service-principal", required_argument, NULL, opt_remove_service_principal },
+ 		{ "show-details", no_argument, NULL, opt_show_details },
+-- 
+2.31.1
+
diff --git a/SOURCES/0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch b/SOURCES/0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch
new file mode 100644
index 0000000000000000000000000000000000000000..58d91ec698c961affbe6b1d136882c4180b9f143
--- /dev/null
+++ b/SOURCES/0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch
@@ -0,0 +1,60 @@
+From 7148ab196d0a96ede9b5ef463b0481d0fe372b21 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 11 Jun 2021 12:46:03 +0200
+Subject: [PATCH 2/3] adcli_entry: add entry_attrs with userAccountControl
+ attribute
+
+---
+ library/adentry.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/library/adentry.c b/library/adentry.c
+index 1cc0518..13dcaf8 100644
+--- a/library/adentry.c
++++ b/library/adentry.c
+@@ -42,6 +42,7 @@ struct _adcli_entry {
+ 	char *entry_dn;
+ 	char *domain_ou;
+ 	char *entry_container;
++	LDAPMessage *entry_attrs;
+ };
+ 
+ static adcli_entry *
+@@ -63,6 +64,7 @@ entry_new (adcli_conn *conn,
+ 
+ 	entry->builder = builder;
+ 	entry->object_class = object_class;
++	entry->entry_attrs = NULL;
+ 	return entry;
+ }
+ 
+@@ -82,6 +84,7 @@ entry_free (adcli_entry *entry)
+ 	free (entry->entry_container);
+ 	free (entry->entry_dn);
+ 	free (entry->domain_ou);
++	ldap_msgfree (entry->entry_attrs);
+ 	adcli_conn_unref (entry->conn);
+ 	free (entry);
+ }
+@@ -102,7 +105,7 @@ static adcli_result
+ update_entry_from_domain (adcli_entry *entry,
+                           LDAP *ldap)
+ {
+-	const char *attrs[] = { "1.1", NULL };
++	const char *attrs[] = { "userAccountControl", NULL };
+ 	LDAPMessage *results;
+ 	LDAPMessage *first;
+ 	const char *base;
+@@ -139,7 +142,8 @@ update_entry_from_domain (adcli_entry *entry,
+ 		return_unexpected_if_fail (entry->entry_dn != NULL);
+ 	}
+ 
+-	ldap_msgfree (results);
++	ldap_msgfree (entry->entry_attrs);
++	entry->entry_attrs = results;
+ 	return ADCLI_SUCCESS;
+ }
+ 
+-- 
+2.31.1
+
diff --git a/SOURCES/0003-entry-add-passwd-user-sub-command.patch b/SOURCES/0003-entry-add-passwd-user-sub-command.patch
new file mode 100644
index 0000000000000000000000000000000000000000..413d7ab677d243524fa169b5cc2533536bb4eef2
--- /dev/null
+++ b/SOURCES/0003-entry-add-passwd-user-sub-command.patch
@@ -0,0 +1,366 @@
+From 6a673b236dfdfdf9c73cc3d2ccf3949eb1a5ddd0 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 11 Jun 2021 12:47:37 +0200
+Subject: [PATCH 3/3] entry: add passwd-user sub-command
+
+The new command allows to set or reset a user password with the help of
+an account privileged to set the password.
+
+Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1952828
+---
+ doc/adcli.xml     |  20 +++++++
+ library/adentry.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++
+ library/adentry.h |   3 +
+ tools/entry.c     |  99 +++++++++++++++++++++++++++++++++
+ tools/tools.c     |   1 +
+ tools/tools.h     |   4 ++
+ 6 files changed, 265 insertions(+)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index 1ed5d3f..6c36297 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -56,6 +56,11 @@
+ 		<arg choice="opt">--domain=domain.example.com</arg>
+ 		<arg choice="plain">user</arg>
+ 	</cmdsynopsis>
++	<cmdsynopsis>
++		<command>adcli passwd-user</command>
++		<arg choice="opt">--domain=domain.example.com</arg>
++		<arg choice="plain">user</arg>
++	</cmdsynopsis>
+ 	<cmdsynopsis>
+ 		<command>adcli create-group</command>
+ 		<arg choice="opt">--domain=domain.example.com</arg>
+@@ -696,6 +701,21 @@ $ adcli delete-user Fry --domain=domain.example.com
+ 
+ </refsect1>
+ 
++<refsect1 id='passwd_user'>
++	<title>(Re)setting the password of a User with an Administrative Account</title>
++
++	<para><command>adcli passwd-user</command> sets or resets the password
++	of user account. The administrative account used for this operation
++	must have privileges to set a password.</para>
++
++<programlisting>
++$ adcli passwd-user Fry --domain=domain.example.com
++</programlisting>
++
++	<para>The various global options can be used.</para>
++
++</refsect1>
++
+ 
+ <refsect1 id='create_group'>
+ 	<title>Creating a Group</title>
+diff --git a/library/adentry.c b/library/adentry.c
+index 13dcaf8..0d9b9af 100644
+--- a/library/adentry.c
++++ b/library/adentry.c
+@@ -409,6 +409,144 @@ adcli_entry_delete (adcli_entry *entry)
+ 	return ADCLI_SUCCESS;
+ }
+ 
++static adcli_result
++adcli_entry_ensure_enabled (adcli_entry *entry)
++{
++	adcli_result res;
++	LDAP *ldap;
++	adcli_attrs *attrs;
++	uint32_t uac = 0;
++	char *uac_str;
++	unsigned long attr_val;
++	char *end;
++
++	return_unexpected_if_fail (entry->entry_attrs != NULL);
++
++	ldap = adcli_conn_get_ldap_connection (entry->conn);
++	return_unexpected_if_fail (ldap != NULL);
++
++	uac_str = _adcli_ldap_parse_value (ldap, entry->entry_attrs,
++	                                   "userAccountControl");
++	if (uac_str != NULL) {
++		attr_val = strtoul (uac_str, &end, 10);
++		if (*end != '\0' || attr_val > UINT32_MAX) {
++			_adcli_warn ("Invalid userAccountControl '%s' for %s account in directory: %s, assuming 0",
++			            uac_str, entry->object_class, entry->entry_dn);
++		} else {
++			uac = attr_val;
++		}
++		free (uac_str);
++	}
++	if (uac & UAC_ACCOUNTDISABLE) {
++		uac &= ~(UAC_ACCOUNTDISABLE);
++
++		if (asprintf (&uac_str, "%d", uac) < 0) {
++			_adcli_warn ("Cannot enable %s entry %s after password (re)set",
++			             entry->object_class, entry->entry_dn);
++			return ADCLI_ERR_UNEXPECTED;
++		}
++
++		attrs = adcli_attrs_new ();
++		adcli_attrs_replace (attrs, "userAccountControl", uac_str,
++		                     NULL);
++		res = adcli_entry_modify (entry, attrs);
++		if (res == ADCLI_SUCCESS) {
++			_adcli_info ("Enabled %s entry %s after password (re)set",
++			             entry->object_class, entry->entry_dn);
++		} else {
++			_adcli_warn ("Failed to enable %s entry %s after password (re)set",
++			             entry->object_class, entry->entry_dn);
++		}
++		free (uac_str);
++		adcli_attrs_free (attrs);
++	} else {
++		res = ADCLI_SUCCESS;
++	}
++
++	return res;
++}
++
++adcli_result
++adcli_entry_set_passwd (adcli_entry *entry, const char *user_pwd)
++{
++	adcli_result res;
++	LDAP *ldap;
++	krb5_error_code code;
++	krb5_context k5;
++	krb5_ccache ccache;
++	krb5_data result_string = { 0, };
++	krb5_data result_code_string = { 0, };
++	int result_code;
++	char *message;
++	krb5_principal user_principal;
++
++	ldap = adcli_conn_get_ldap_connection (entry->conn);
++	return_unexpected_if_fail (ldap != NULL);
++
++	/* Find the user */
++	res = update_entry_from_domain (entry, ldap);
++	if (res != ADCLI_SUCCESS)
++		return res;
++
++	if (!entry->entry_dn) {
++		_adcli_err ("Cannot find the %s entry %s in the domain",
++		            entry->object_class, entry->sam_name);
++		return ADCLI_ERR_CONFIG;
++	}
++
++	k5 = adcli_conn_get_krb5_context (entry->conn);
++	return_unexpected_if_fail (k5 != NULL);
++
++	code = _adcli_krb5_build_principal (k5, entry->sam_name,
++	                                    adcli_conn_get_domain_realm (entry->conn),
++	                                    &user_principal);
++	return_unexpected_if_fail (code == 0);
++
++	ccache = adcli_conn_get_login_ccache (entry->conn);
++	return_unexpected_if_fail (ccache != NULL);
++
++	memset (&result_string, 0, sizeof (result_string));
++	memset (&result_code_string, 0, sizeof (result_code_string));
++
++	code = krb5_set_password_using_ccache (k5, ccache, user_pwd,
++	                                       user_principal, &result_code,
++	                                       &result_code_string, &result_string);
++
++	if (code != 0) {
++		_adcli_err ("Couldn't set password for %s account: %s: %s",
++		            entry->object_class,
++		            entry->sam_name, krb5_get_error_message (k5, code));
++		/* TODO: Parse out these values */
++		res = ADCLI_ERR_DIRECTORY;
++
++	} else if (result_code != 0) {
++#ifdef HAVE_KRB5_CHPW_MESSAGE
++		if (krb5_chpw_message (k5, &result_string, &message) != 0)
++			message = NULL;
++#else
++		message = NULL;
++		if (result_string.length)
++			message = _adcli_str_dupn (result_string.data, result_string.length);
++#endif
++		_adcli_err ("Cannot set %s password: %.*s%s%s",
++		            entry->object_class,
++		            (int)result_code_string.length, result_code_string.data,
++		            message ? ": " : "", message ? message : "");
++		res = ADCLI_ERR_CREDENTIALS;
++#ifdef HAVE_KRB5_CHPW_MESSAGE
++		krb5_free_string (k5, message);
++#else
++		free (message);
++#endif
++	} else {
++		_adcli_info ("Password (re)setted for %s: %s", entry->object_class, entry->entry_dn);
++
++		res = adcli_entry_ensure_enabled (entry);
++	}
++
++	return res;
++}
++
+ const char *
+ adcli_entry_get_sam_name (adcli_entry *entry)
+ {
+diff --git a/library/adentry.h b/library/adentry.h
+index ae90689..f2382b1 100644
+--- a/library/adentry.h
++++ b/library/adentry.h
+@@ -49,6 +49,9 @@ adcli_result       adcli_entry_modify                   (adcli_entry *entry,
+ 
+ adcli_result       adcli_entry_delete                   (adcli_entry *entry);
+ 
++adcli_result       adcli_entry_set_passwd               (adcli_entry *entry,
++                                                         const char *user_pwd);
++
+ const char *       adcli_entry_get_domain_ou            (adcli_entry *entry);
+ 
+ void               adcli_entry_set_domain_ou            (adcli_entry *entry,
+diff --git a/tools/entry.c b/tools/entry.c
+index 05e4313..52d2546 100644
+--- a/tools/entry.c
++++ b/tools/entry.c
+@@ -24,6 +24,7 @@
+ #include "config.h"
+ 
+ #include "adcli.h"
++#include "adprivate.h"
+ #include "adattrs.h"
+ #include "tools.h"
+ 
+@@ -385,6 +386,104 @@ adcli_tool_user_delete (adcli_conn *conn,
+ 	return 0;
+ }
+ 
++int
++adcli_tool_user_passwd (adcli_conn *conn,
++                        int argc,
++                        char *argv[])
++{
++	adcli_result res;
++	adcli_entry *entry;
++	int opt;
++	char *user_pwd = NULL;
++
++	struct option options[] = {
++		{ "domain", required_argument, NULL, opt_domain },
++		{ "domain-realm", required_argument, NULL, opt_domain_realm },
++		{ "domain-controller", required_argument, NULL, opt_domain_controller },
++		{ "use-ldaps", no_argument, 0, opt_use_ldaps },
++		{ "login-user", required_argument, NULL, opt_login_user },
++		{ "login-ccache", optional_argument, NULL, opt_login_ccache },
++		{ "no-password", no_argument, 0, opt_no_password },
++		{ "stdin-password", no_argument, 0, opt_stdin_password },
++		{ "prompt-password", no_argument, 0, opt_prompt_password },
++		{ "verbose", no_argument, NULL, opt_verbose },
++		{ "help", no_argument, NULL, 'h' },
++		{ 0 },
++	};
++
++	static adcli_tool_desc usages[] = {
++		{ 0, "usage: adcli passwd-user --domain=xxxx user" },
++		{ 0 },
++	};
++
++	while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) {
++		switch (opt) {
++		case 'h':
++		case '?':
++		case ':':
++			adcli_tool_usage (options, usages);
++			adcli_tool_usage (options, common_usages);
++			return opt == 'h' ? 0 : 2;
++		default:
++			res = parse_option ((Option)opt, optarg, conn);
++			if (res != ADCLI_SUCCESS) {
++				return res;
++			}
++			break;
++		}
++	}
++
++	argc -= optind;
++	argv += optind;
++
++	if (argc != 1) {
++		warnx ("specify one user name to (re)set password");
++		return 2;
++	}
++
++	entry = adcli_entry_new_user (conn, argv[0]);
++	if (entry == NULL) {
++		warnx ("unexpected memory problems");
++		return -1;
++	}
++
++	adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT);
++
++	res = adcli_conn_connect (conn);
++	if (res != ADCLI_SUCCESS) {
++		warnx ("couldn't connect to %s domain: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_entry_unref (entry);
++		return -res;
++	}
++
++	user_pwd = adcli_prompt_password_func (ADCLI_LOGIN_USER_ACCOUNT,
++	                                       adcli_entry_get_sam_name(entry),
++	                                       0, NULL);
++	if (user_pwd == NULL || *user_pwd == '\0') {
++		warnx ("missing password");
++		_adcli_password_free (user_pwd);
++		adcli_entry_unref (entry);
++		return 2;
++	}
++
++	res = adcli_entry_set_passwd (entry, user_pwd);
++	_adcli_password_free (user_pwd);
++	if (res != ADCLI_SUCCESS) {
++		warnx ("(re)setting password for user %s in domain %s failed: %s",
++		       adcli_entry_get_sam_name (entry),
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_entry_unref (entry);
++		return -res;
++	}
++
++	adcli_entry_unref (entry);
++
++	return 0;
++}
++
+ int
+ adcli_tool_group_create (adcli_conn *conn,
+                          int argc,
+diff --git a/tools/tools.c b/tools/tools.c
+index 84bbba9..a14b9ca 100644
+--- a/tools/tools.c
++++ b/tools/tools.c
+@@ -63,6 +63,7 @@ struct {
+ 	{ "create-msa", adcli_tool_computer_managed_service_account, "Create a managed service account in the given AD domain", },
+ 	{ "create-user", adcli_tool_user_create, "Create a user account", },
+ 	{ "delete-user", adcli_tool_user_delete, "Delete a user account", },
++	{ "passwd-user", adcli_tool_user_passwd, "(Re)set a user password", },
+ 	{ "create-group", adcli_tool_group_create, "Create a group", },
+ 	{ "delete-group", adcli_tool_group_delete, "Delete a group", },
+ 	{ "add-member", adcli_tool_member_add, "Add users to a group", },
+diff --git a/tools/tools.h b/tools/tools.h
+index 82d5e4e..d38aa32 100644
+--- a/tools/tools.h
++++ b/tools/tools.h
+@@ -94,6 +94,10 @@ int       adcli_tool_user_delete       (adcli_conn *conn,
+                                         int argc,
+                                         char *argv[]);
+ 
++int       adcli_tool_user_passwd       (adcli_conn *conn,
++                                        int argc,
++                                        char *argv[]);
++
+ int       adcli_tool_group_create      (adcli_conn *conn,
+                                         int argc,
+                                         char *argv[]);
+-- 
+2.31.1
+
diff --git a/SPECS/adcli.spec b/SPECS/adcli.spec
index 63789409f45aafe700fd25f56b6ff1e6fe3a27da..3fc795c69d488895b506b2bc3c7433e13ec3febc 100644
--- a/SPECS/adcli.spec
+++ b/SPECS/adcli.spec
@@ -1,6 +1,6 @@
 Name:		adcli
 Version:	0.8.2
-Release:	9%{?dist}
+Release:	12%{?dist}
 Summary:	Active Directory enrollment
 License:	LGPLv2+
 URL:		http://cgit.freedesktop.org/realmd/adcli
@@ -150,6 +150,24 @@ Patch80:	0007-service-account-add-random-suffix-to-account-name.patch
 # rhbz#1906303 - Typo in CREATE A SERVICE ACCOUNT section of man page of adcli
 Patch81:	0001-service-account-fix-typo-in-the-man-page-entry.patch
 
+# rhbz#1889386 - [RFE] Adcli and Realm Error Code Optimization Request
+Patch82:	0001-build-add-with-vendor-error-message-configure-option.patch
+
+# rhbz#1769644 - [RFE] adcli should allow to modify DONT_EXPIRE_PASSWORD attribute
+Patch83:	0001-coverity-add-missing-NULL-checks.patch
+Patch84:	0002-Add-dont-expire-password-option.patch
+Patch85:	0001-Fix-for-dont-expire-password-option-and-join.patch
+
+# rhbz#1952828 - [RFE] Allow adcli to create AD user with password as well as
+# set or reset existing user password
+Patch86:	0001-library-move-UAC-flags-to-a-more-common-header-file.patch
+Patch87:	0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch
+Patch88:	0003-entry-add-passwd-user-sub-command.patch
+
+# rhbz#1690920 - [RFE] add option to populate "managed by" computer attribute
+Patch89:	0001-Add-setattr-option.patch
+Patch90:	0002-Add-delattr-option.patch
+
 BuildRequires:	gcc
 BuildRequires:	intltool pkgconfig
 BuildRequires:	libtool
@@ -176,7 +194,11 @@ standard LDAP and Kerberos calls.
 
 %build
 autoreconf --force --install --verbose
-%configure --disable-static --disable-silent-rules
+%configure --disable-static --disable-silent-rules \
+%if 0%{?rhel}
+    --with-vendor-error-message='Please check\n    https://red.ht/support_rhel_ad \nto get help for common issues.' \
+%endif
+    %{nil}
 make %{?_smp_mflags}
 
 %check
@@ -210,6 +232,18 @@ documentation.
 %doc %{_datadir}/doc/adcli/*
 
 %changelog
+* Mon Jun 14 2021 Sumit Bose <sbose@redhat.com> - 0.8.2-12
+- [RFE] Allow adcli to create AD user with password as well as set or reset
+  existing user password [#1952828]
+- [RFE] add option to populate "managed by" computer attribute [#1690920]
+
+* Thu Jun 03 2021 Sumit Bose <sbose@redhat.com> - 0.8.2-11
+- Add missing patch for [#1769644]
+
+* Thu Jun 03 2021 Sumit Bose <sbose@redhat.com> - 0.8.2-10
+- [RFE] Adcli and Realm Error Code Optimization Request [#1889386]
+- [RFE] adcli should allow to modify DONT_EXPIRE_PASSWORD attribute [#1769644]
+
 * Fri Dec 11 2020 Sumit Bose <sbose@redhat,com> - 0.8.2-9
 - Typo in CREATE A SERVICE ACCOUNT section of man page of adcli [#1906303]