diff --git a/.adcli.metadata b/.adcli.metadata
new file mode 100644
index 0000000..d18f424
--- /dev/null
+++ b/.adcli.metadata
@@ -0,0 +1 @@
+d59965026153a5ee5a0d97d5a9dbd4231cff5574 SOURCES/adcli-0.8.1.tar.gz
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0493b05
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+SOURCES/adcli-0.8.1.tar.gz
diff --git a/SOURCES/0001-Add-trusted-for-delegation-option.patch b/SOURCES/0001-Add-trusted-for-delegation-option.patch
new file mode 100644
index 0000000..5e60e7c
--- /dev/null
+++ b/SOURCES/0001-Add-trusted-for-delegation-option.patch
@@ -0,0 +1,247 @@
+From 17a68288c93100e1810ad22e7fefe7357d3b2519 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 31 May 2018 18:27:37 +0200
+Subject: [PATCH 1/7] Add trusted-for-delegation option
+
+Resolves https://bugzilla.redhat.com/show_bug.cgi?id=1538730
+---
+ doc/adcli.xml      | 14 ++++++++++
+ library/adenroll.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
+ library/adenroll.h |  4 +++
+ tools/computer.c   | 12 ++++++++
+ 4 files changed, 108 insertions(+), 2 deletions(-)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index c2b7760..b246190 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -283,6 +283,13 @@ Password for Administrator:
+ 			<option>--login-type=computer</option> and providing a
+ 			password as input.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--trusted-for-delegation=<parameter>yes|no|true|false</parameter></option></term>
++			<listitem><para>Set or unset the TRUSTED_FOR_DELEGATION
++			flag in the userAccountControl attribute to allow or
++			not allow that Kerberos tickets can be forwarded to the
++			host.</para></listitem>
++		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>--show-details</option></term>
+ 			<listitem><para>After a successful join print out information
+@@ -402,6 +409,13 @@ $ adcli update --login-ccache=/tmp/krbcc_123
+ 			in days. By default the password is updated if it is
+ 			older than 30 days.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--trusted-for-delegation=<parameter>yes|no|true|false</parameter></option></term>
++			<listitem><para>Set or unset the TRUSTED_FOR_DELEGATION
++			flag in the userAccountControl attribute to allow or
++			not allow that Kerberos tickets can be forwarded to the
++			host.</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 d64c2c0..7c59078 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -63,6 +63,13 @@ static krb5_enctype v51_earlier_enctypes[] = {
+ 	0
+ };
+ 
++/* 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;
+@@ -105,6 +112,7 @@ struct _adcli_enroll {
+ 	unsigned int computer_password_lifetime;
+ 	int computer_password_lifetime_explicit;
+ 	char *samba_data_tool;
++	bool trusted_for_delegation;
+ };
+ 
+ static adcli_result
+@@ -538,6 +546,10 @@ create_computer_account (adcli_enroll *enroll,
+ 		NULL,
+ 	};
+ 
++	if (adcli_enroll_get_trusted_for_delegation (enroll)) {
++		vals_userAccountControl[0] = "593920"; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD | TRUSTED_FOR_DELEGATION */
++	}
++
+ 	ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL);
+ 
+ 	/*
+@@ -971,6 +983,7 @@ retrieve_computer_account (adcli_enroll *enroll)
+ 		"operatingSystemVersion",
+ 		"operatingSystemServicePack",
+ 		"pwdLastSet",
++		"userAccountControl",
+ 		NULL,
+ 	};
+ 
+@@ -1149,6 +1162,47 @@ update_computer_attribute (adcli_enroll *enroll,
+ 	return res;
+ }
+ 
++static char *get_user_account_control (adcli_enroll *enroll)
++{
++	uint32_t uac = 0;
++	unsigned long attr_val;
++	char *uac_str;
++	LDAP *ldap;
++	char *end;
++
++	ldap = adcli_conn_get_ldap_connection (enroll->conn);
++	return_val_if_fail (ldap != NULL, NULL);
++
++	uac_str = _adcli_ldap_parse_value (ldap, enroll->computer_attributes, "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 computer account in directory: %s, assuming 0",
++			            uac_str, enroll->computer_dn);
++		} else {
++			uac = attr_val;
++		}
++		free (uac_str);
++	}
++
++	if (uac == 0) {
++		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 (asprintf (&uac_str, "%d", uac) < 0) {
++		return_val_if_reached (NULL);
++	}
++
++	return uac_str;
++}
++
+ static void
+ update_computer_account (adcli_enroll *enroll)
+ {
+@@ -1167,11 +1221,16 @@ update_computer_account (adcli_enroll *enroll)
+ 	}
+ 
+ 	if (res == ADCLI_SUCCESS) {
+-		char *vals_userAccountControl[] = { "69632", NULL }; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD */
++		char *vals_userAccountControl[] = { NULL , NULL };
+ 		LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } };
+ 		LDAPMod *mods[] = { &userAccountControl, NULL };
+ 
+-		res |= update_computer_attribute (enroll, ldap, mods);
++		vals_userAccountControl[0] = get_user_account_control (enroll);
++		if (vals_userAccountControl[0] != NULL) {
++			res |= update_computer_attribute (enroll, ldap, mods);
++		} else {
++			_adcli_warn ("Cannot update userAccountControl");
++		}
+ 	}
+ 
+ 	if (res == ADCLI_SUCCESS) {
+@@ -2375,3 +2434,20 @@ adcli_enroll_get_samba_data_tool (adcli_enroll *enroll)
+ 	return_val_if_fail (enroll != NULL, NULL);
+ 	return enroll->samba_data_tool;
+ }
++
++bool
++adcli_enroll_get_trusted_for_delegation (adcli_enroll *enroll)
++{
++	return_val_if_fail (enroll != NULL, false);
++
++	return enroll->trusted_for_delegation;
++}
++
++void
++adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll,
++                                         bool value)
++{
++	return_if_fail (enroll != NULL);
++
++	enroll->trusted_for_delegation = value;
++}
+diff --git a/library/adenroll.h b/library/adenroll.h
+index 31ca0bc..be2ca18 100644
+--- a/library/adenroll.h
++++ b/library/adenroll.h
+@@ -109,6 +109,10 @@ unsigned int       adcli_enroll_get_computer_password_lifetime (adcli_enroll *en
+ void               adcli_enroll_set_computer_password_lifetime (adcli_enroll *enroll,
+                                                          unsigned int lifetime);
+ 
++bool               adcli_enroll_get_trusted_for_delegation (adcli_enroll *enroll);
++void               adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll,
++                                                            bool value);
++
+ krb5_kvno          adcli_enroll_get_kvno                (adcli_enroll *enroll);
+ 
+ void               adcli_enroll_set_kvno                (adcli_enroll *enroll,
+diff --git a/tools/computer.c b/tools/computer.c
+index f86548b..b905fd1 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -109,6 +109,7 @@ typedef enum {
+ 	opt_computer_password_lifetime,
+ 	opt_add_samba_data,
+ 	opt_samba_data_tool,
++	opt_trusted_for_delegation,
+ } Option;
+ 
+ static adcli_tool_desc common_usages[] = {
+@@ -135,6 +136,8 @@ static adcli_tool_desc common_usages[] = {
+ 	{ opt_os_service_pack, "the computer operating system service pack", },
+ 	{ opt_user_principal, "add an authentication principal to the account", },
+ 	{ 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_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"
+@@ -279,6 +282,13 @@ parse_option (Option opt,
+ 			adcli_enroll_set_samba_data_tool (enroll, optarg);
+ 		}
+ 		return;
++	case opt_trusted_for_delegation:
++		if (strcasecmp (optarg, "true") == 0 || strcasecmp (optarg, "yes") == 0) {
++			adcli_enroll_set_trusted_for_delegation (enroll, true);
++		} else {
++			adcli_enroll_set_trusted_for_delegation (enroll, false);
++		}
++		return;
+ 	case opt_verbose:
+ 		return;
+ 
+@@ -342,6 +352,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 },
+ 		{ "user-principal", optional_argument, NULL, opt_user_principal },
++		{ "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation },
+ 		{ "show-details", no_argument, NULL, opt_show_details },
+ 		{ "show-password", no_argument, NULL, opt_show_password },
+ 		{ "add-samba-data", no_argument, NULL, opt_add_samba_data },
+@@ -446,6 +457,7 @@ adcli_tool_computer_update (adcli_conn *conn,
+ 		{ "os-service-pack", optional_argument, NULL, opt_os_service_pack },
+ 		{ "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 },
+ 		{ "show-details", no_argument, NULL, opt_show_details },
+ 		{ "show-password", no_argument, NULL, opt_show_password },
+ 		{ "add-samba-data", no_argument, NULL, opt_add_samba_data },
+-- 
+2.14.4
+
diff --git a/SOURCES/0001-Do-not-use-arcfour-hmac-md5-when-discovering-the-sal.patch b/SOURCES/0001-Do-not-use-arcfour-hmac-md5-when-discovering-the-sal.patch
new file mode 100644
index 0000000..81d6e2c
--- /dev/null
+++ b/SOURCES/0001-Do-not-use-arcfour-hmac-md5-when-discovering-the-sal.patch
@@ -0,0 +1,60 @@
+From 158468507bb723aa62196846749c23c121d4b298 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 8 Apr 2019 10:55:39 +0200
+Subject: [PATCH] Do not use arcfour-hmac-md5 when discovering the salt
+
+Since the arcfour-hmac-md5 encryption types does not use salts it cannot
+be used to discover the right salt.
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1683745
+
+diff --git a/library/adkrb5.c b/library/adkrb5.c
+index da835d7..be3ede5 100644
+--- a/library/adkrb5.c
++++ b/library/adkrb5.c
+@@ -395,15 +395,33 @@ _adcli_krb5_keytab_discover_salt (krb5_context k5,
+ 	krb5_keytab scratch;
+ 	krb5_error_code code;
+ 	int i;
++	krb5_enctype *salt_enctypes = NULL;
++	size_t c;
++	size_t s;
+ 
+ 	/* TODO: This should be a unique name */
+ 
+ 	code = krb5_kt_resolve (k5, "MEMORY:adcli-discover-salt", &scratch);
+ 	return_val_if_fail (code == 0, code);
+ 
++	for (c = 0; enctypes[c] != 0; c++); /* count enctypes */
++	salt_enctypes = calloc (c + 1, sizeof (krb5_enctype));
++	return_val_if_fail (salt_enctypes != NULL, ENOMEM);
++
++	/* ENCTYPE_ARCFOUR_HMAC does not use salts, so it cannot be used to
++	 * discover the right salt. */
++	s = 0;
++	for (c = 0; enctypes[c] != 0; c++) {
++		if (enctypes[c] == ENCTYPE_ARCFOUR_HMAC) {
++			continue;
++		}
++
++		salt_enctypes[s++] = enctypes[c];
++	}
++
+ 	for (i = 0; salts[i].data != NULL; i++) {
+ 		code = _adcli_krb5_keytab_test_salt (k5, scratch, principal, kvno,
+-		                                     password, enctypes, &salts[i]);
++		                                     password, salt_enctypes, &salts[i]);
+ 		if (code == 0) {
+ 			*discovered = i;
+ 			break;
+@@ -412,6 +430,7 @@ _adcli_krb5_keytab_discover_salt (krb5_context k5,
+ 		}
+ 	}
+ 
++	free (salt_enctypes);
+ 	krb5_kt_close (k5, scratch);
+ 	return code;
+ }
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-Fix-for-issue-found-by-Coverity.patch b/SOURCES/0001-Fix-for-issue-found-by-Coverity.patch
new file mode 100644
index 0000000..6706ba5
--- /dev/null
+++ b/SOURCES/0001-Fix-for-issue-found-by-Coverity.patch
@@ -0,0 +1,26 @@
+From 5da6d34e2659f915e830932fd366c635801ecd91 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 12 Aug 2019 17:28:20 +0200
+Subject: [PATCH] Fix for issue found by Coverity
+
+Related to https://gitlab.freedesktop.org/realmd/adcli/issues/3
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 53cd812..524663a 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -2681,7 +2681,10 @@ adcli_enroll_get_permitted_keytab_enctypes (adcli_enroll *enroll)
+ 	for (c = 0; cur_enctypes[c] != 0; c++);
+ 
+ 	new_enctypes = calloc (c + 1, sizeof (krb5_enctype));
+-	return_val_if_fail (new_enctypes != NULL, NULL);
++	if (new_enctypes == NULL) {
++		krb5_free_enctypes (k5, permitted_enctypes);
++		return NULL;
++	}
+ 
+ 	n = 0;
+ 	for (c = 0; cur_enctypes[c] != 0; c++) {
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-Fix-for-issues-found-by-Coverity.patch b/SOURCES/0001-Fix-for-issues-found-by-Coverity.patch
new file mode 100644
index 0000000..c7e698e
--- /dev/null
+++ b/SOURCES/0001-Fix-for-issues-found-by-Coverity.patch
@@ -0,0 +1,43 @@
+From 3c93c96eb6ea2abd3869921ee4c89e1a4d9e4c44 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 14 Aug 2018 13:08:52 +0200
+Subject: [PATCH] Fix for issues found by Coverity
+
+---
+ library/adenroll.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 02bd9e3..de2242a 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -1575,7 +1575,7 @@ load_host_keytab (adcli_enroll *enroll)
+ 	}
+ 
+ 	krb5_free_context (k5);
+-	return ADCLI_SUCCESS;
++	return res;
+ }
+ 
+ typedef struct {
+@@ -1756,12 +1756,12 @@ add_principal_to_keytab (adcli_enroll *enroll,
+ 		                                       enroll->kvno, &password, enctypes, &salts[*which_salt]);
+ 
+ 		free_principal_salts (k5, salts);
++	}
+ 
+-		if (code != 0) {
+-			_adcli_err ("Couldn't add keytab entries: %s: %s",
+-			            enroll->keytab_name, krb5_get_error_message (k5, code));
+-			return ADCLI_ERR_FAIL;
+-		}
++	if (code != 0) {
++		_adcli_err ("Couldn't add keytab entries: %s: %s",
++		            enroll->keytab_name, krb5_get_error_message (k5, code));
++		return ADCLI_ERR_FAIL;
+ 	}
+ 
+ 
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch b/SOURCES/0001-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch
new file mode 100644
index 0000000..178c54f
--- /dev/null
+++ b/SOURCES/0001-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch
@@ -0,0 +1,28 @@
+From e2ae8bc7454a52e2b929f9b09ad9be21e377342b Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 30 Jan 2018 14:39:46 +0100
+Subject: [PATCH 1/9] Fix memory leak in test_check_nt_time_string_lifetime
+
+The test added with 650e5d33ef31437a049fb454ad3dc5457c56abe7 introduced
+a small memory leak.
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ library/adutil.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/library/adutil.c b/library/adutil.c
+index 21ccd27..cd40f45 100644
+--- a/library/adutil.c
++++ b/library/adutil.c
+@@ -501,6 +501,7 @@ test_check_nt_time_string_lifetime (void)
+ 			  (time (NULL) + 10 + AD_TO_UNIX_TIME_CONST) * 1000 * 1000 *10)
+ 		!= -1);
+ 	assert (!_adcli_check_nt_time_string_lifetime (time_str, 0));
++	free (time_str);
+ 
+ 	/* This test will fail some time after 2200AD as a reminder to reflect
+ 	 * why adcli is still needed. */
+-- 
+2.14.4
+
diff --git a/SOURCES/0001-Implement-adcli-testjoin.patch b/SOURCES/0001-Implement-adcli-testjoin.patch
new file mode 100644
index 0000000..d75cbf2
--- /dev/null
+++ b/SOURCES/0001-Implement-adcli-testjoin.patch
@@ -0,0 +1,181 @@
+From 6fd99ff6c5dd6ef0be8d942989b1c6dcee3102d9 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 22 Mar 2019 12:37:39 +0100
+Subject: [PATCH] Implement 'adcli testjoin'
+
+By calling adcli testjoin it will be checked if the host credentials
+stored in the keytab are still valid.
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1622583
+---
+ doc/adcli.xml    | 34 +++++++++++++++++++++++
+ tools/computer.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++
+ tools/tools.c    |  1 +
+ tools/tools.h    |  4 +++
+ 4 files changed, 111 insertions(+)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index af73433..9605b4a 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -43,6 +43,9 @@
+ 	<cmdsynopsis>
+ 		<command>adcli update</command>
+ 	</cmdsynopsis>
++	<cmdsynopsis>
++		<command>adcli testjoin</command>
++	</cmdsynopsis>
+ 	<cmdsynopsis>
+ 		<command>adcli create-user</command>
+ 		<arg choice="opt">--domain=domain.example.com</arg>
+@@ -474,6 +477,37 @@ $ adcli update --login-ccache=/tmp/krbcc_123
+ 
+ </refsect1>
+ 
++<refsect1 id='testjoin'>
++	<title>Testing if the machine account password is valid</title>
++
++	<para><command>adcli testjoin</command> uses the current credentials in
++	the keytab and tries to authenticate with the machine account to the AD
++	domain. If this works the machine account password and the join are
++	still valid. If it fails the machine account password or the whole
++	machine account have to be refreshed with
++	<command>adcli join</command> or <command>adcli update</command>.
++	</para>
++
++<programlisting>
++$ adcli testjoin
++</programlisting>
++
++	<para>Only the global options not related to authentication are
++	available, additionally you can specify the following options to
++	control how this operation is done.</para>
++
++	<variablelist>
++		<varlistentry>
++			<term><option>-K, --host-keytab=<parameter>/path/to/keytab</parameter></option></term>
++			<listitem><para>Specify the path to the host keytab where
++			current host credentials are stored and the new ones
++			will be written to.  If not specified, the default
++			location will be used, usually
++			<filename>/etc/krb5.keytab</filename>.</para></listitem>
++		</varlistentry>
++	</variablelist>
++</refsect1>
++
+ <refsect1 id='create_user'>
+ 	<title>Creating a User</title>
+ 
+diff --git a/tools/computer.c b/tools/computer.c
+index 112340e..610ed2b 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -566,6 +566,78 @@ adcli_tool_computer_update (adcli_conn *conn,
+ 	return 0;
+ }
+ 
++int
++adcli_tool_computer_testjoin (adcli_conn *conn,
++                              int argc,
++                              char *argv[])
++{
++	adcli_enroll *enroll;
++	adcli_result res;
++	const char *ktname;
++	int opt;
++
++	struct option options[] = {
++		{ "domain", required_argument, NULL, opt_domain },
++		{ "domain-controller", required_argument, NULL, opt_domain_controller },
++		{ "host-keytab", required_argument, 0, opt_host_keytab },
++		{ "verbose", no_argument, NULL, opt_verbose },
++		{ "help", no_argument, NULL, 'h' },
++		{ 0 },
++	};
++
++	static adcli_tool_desc usages[] = {
++		{ 0, "usage: adcli testjoin" },
++		{ 0 },
++	};
++
++	enroll = adcli_enroll_new (conn);
++	if (enroll == NULL)
++		errx (-1, "unexpected memory problems");
++
++	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);
++			adcli_enroll_unref (enroll);
++			return opt == 'h' ? 0 : 2;
++		default:
++			parse_option ((Option)opt, optarg, conn, enroll);
++			break;
++		}
++	}
++
++	/* Force use of a keytab to test the join/machine account password */
++	adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_COMPUTER_ACCOUNT);
++	ktname = adcli_enroll_get_keytab_name (enroll);
++	adcli_conn_set_login_keytab_name (conn, ktname ? ktname : "");
++
++	res = adcli_enroll_load (enroll);
++	if (res != ADCLI_SUCCESS) {
++		adcli_enroll_unref (enroll);
++		adcli_conn_unref (conn);
++		errx (-res, "couldn't lookup domain info from keytab: %s",
++		      adcli_get_last_error ());
++	}
++
++	res = adcli_conn_connect (conn);
++	if (res != ADCLI_SUCCESS) {
++		adcli_enroll_unref (enroll);
++		adcli_conn_unref (conn);
++		errx (-res, "couldn't connect to %s domain: %s",
++		      adcli_conn_get_domain_name (conn),
++		      adcli_get_last_error ());
++	}
++
++	printf ("Sucessfully validated join to domain %s\n",
++	        adcli_conn_get_domain_name (conn));
++
++	adcli_enroll_unref (enroll);
++
++	return 0;
++}
+ 
+ int
+ adcli_tool_computer_preset (adcli_conn *conn,
+diff --git a/tools/tools.c b/tools/tools.c
+index 915130e..c4e2851 100644
+--- a/tools/tools.c
++++ b/tools/tools.c
+@@ -55,6 +55,7 @@ struct {
+ 	{ "info", adcli_tool_info, "Print information about a domain", CONNECTION_LESS },
+ 	{ "join", adcli_tool_computer_join, "Join this machine to a domain", },
+ 	{ "update", adcli_tool_computer_update, "Update machine membership in a domain", },
++	{ "testjoin", adcli_tool_computer_testjoin, "Test if machine account password is valid", },
+ 	{ "preset-computer", adcli_tool_computer_preset, "Pre setup computers accounts", },
+ 	{ "reset-computer", adcli_tool_computer_reset, "Reset a computer account", },
+ 	{ "delete-computer", adcli_tool_computer_delete, "Delete a computer account", },
+diff --git a/tools/tools.h b/tools/tools.h
+index 6c97ccf..8cebbf9 100644
+--- a/tools/tools.h
++++ b/tools/tools.h
+@@ -70,6 +70,10 @@ int       adcli_tool_computer_update   (adcli_conn *conn,
+                                         int argc,
+                                         char *argv[]);
+ 
++int       adcli_tool_computer_testjoin (adcli_conn *conn,
++                                        int argc,
++                                        char *argv[]);
++
+ int       adcli_tool_computer_delete   (adcli_conn *conn,
+                                         int argc,
+                                         char *argv[]);
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-Increment-kvno-after-password-change-with-user-creds.patch b/SOURCES/0001-Increment-kvno-after-password-change-with-user-creds.patch
new file mode 100644
index 0000000..aeef509
--- /dev/null
+++ b/SOURCES/0001-Increment-kvno-after-password-change-with-user-creds.patch
@@ -0,0 +1,32 @@
+From 5cf1723c308e21cdbe9b98ed2aaa42cb997456fb Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 15 Mar 2019 14:31:12 +0100
+Subject: [PATCH] Increment kvno after password change with user creds
+
+Originally only the host credential part was fixed in the context of
+https://bugs.freedesktop.org/show_bug.cgi?id=91185. This patch adds the
+fix to the case when user credentials are used.
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1642546
+---
+ library/adenroll.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index e02f403..58362c2 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -1057,6 +1057,10 @@ set_password_with_user_creds (adcli_enroll *enroll)
+ #endif
+ 	} else {
+ 		_adcli_info ("Set computer password");
++		if (enroll->kvno > 0) {
++			enroll->kvno++;
++			_adcli_info ("kvno incremented to %d", enroll->kvno);
++		}
+ 		res = ADCLI_SUCCESS;
+ 	}
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-Make-adcli-info-DC-location-mechanism-more-compliant.patch b/SOURCES/0001-Make-adcli-info-DC-location-mechanism-more-compliant.patch
new file mode 100644
index 0000000..770f49c
--- /dev/null
+++ b/SOURCES/0001-Make-adcli-info-DC-location-mechanism-more-compliant.patch
@@ -0,0 +1,216 @@
+From 0a0d0f66409eb83e06b7dc50543c2f6c15a36bc4 Mon Sep 17 00:00:00 2001
+From: Alexey A Nikitin <nikitin@amazon.com>
+Date: Mon, 29 Oct 2018 20:40:36 -0700
+Subject: [PATCH] Make 'adcli info' DC location mechanism more compliant with
+ [MS-ADTS] and [MS-NRPC]
+
+AD specifications say that DC locator must attempt to find a suitable DC for the client. That means going through all of the DCs in SRV RRs one by one until one of them answers.
+
+The problem with adcli's original behavior is that it queries only five DCs from SRV, ever. This becomes a problem if for any reason there is a large number of DCs in the domain from which the client cannot get a CLDAP response.
+---
+ library/addisco.c | 146 +++++++++++++++++++++++++++++-----------------
+ 1 file changed, 94 insertions(+), 52 deletions(-)
+
+diff --git a/library/addisco.c b/library/addisco.c
+index 8cc5bf0..6e73ead 100644
+--- a/library/addisco.c
++++ b/library/addisco.c
+@@ -41,8 +41,10 @@
+ #include <string.h>
+ #include <time.h>
+ 
+-/* Number of servers to do discovery against */
+-#define DISCO_COUNT 5
++/* Number of servers to do discovery against.
++ * For AD DS maximum number of DCs is 1200.
++ */
++#define DISCO_COUNT 1200
+ 
+ /* The time period in which to do rapid requests */
+ #define DISCO_FEVER  1
+@@ -453,6 +455,51 @@ parse_disco (LDAP *ldap,
+ 	return usability;
+ }
+ 
++static int
++ldap_disco_poller (LDAP **ldap,
++                   LDAPMessage **message,
++                   adcli_disco **results,
++                   const char **addrs)
++{
++	int found = ADCLI_DISCO_UNUSABLE;
++	int close_ldap;
++	int parsed;
++	int ret = 0;
++	struct timeval tvpoll = { 0, 0 };
++
++	switch (ldap_result (*ldap, LDAP_RES_ANY, 1, &tvpoll, message)) {
++		case LDAP_RES_SEARCH_ENTRY:
++		case LDAP_RES_SEARCH_RESULT:
++			parsed = parse_disco (*ldap, *addrs, *message, results);
++			if (parsed > found)
++				found = parsed;
++			ldap_msgfree (*message);
++			close_ldap = 1;
++			break;
++		case -1:
++			ldap_get_option (*ldap, LDAP_OPT_RESULT_CODE, &ret);
++			close_ldap = 1;
++			break;
++		default:
++			ldap_msgfree (*message);
++			close_ldap = 0;
++			break;
++	}
++
++	if (ret != LDAP_SUCCESS) {
++		_adcli_ldap_handle_failure (*ldap, ADCLI_ERR_CONFIG,
++		                            "Couldn't perform discovery search");
++	}
++
++	/* Done with this connection */
++	if (close_ldap) {
++		ldap_unbind_ext_s (*ldap, NULL, NULL);
++		*ldap = NULL;
++	}
++
++	return found;
++}
++
+ static int
+ ldap_disco (const char *domain,
+             srvinfo *srv,
+@@ -477,6 +524,7 @@ ldap_disco (const char *domain,
+ 	int num, i;
+ 	int ret;
+ 	int have_any = 0;
++	struct timeval interval;
+ 
+ 	if (domain) {
+ 		value = _adcli_ldap_escape_filter (domain);
+@@ -540,7 +588,6 @@ ldap_disco (const char *domain,
+ 				version = LDAP_VERSION3;
+ 				ldap_set_option (ldap[num], LDAP_OPT_PROTOCOL_VERSION, &version);
+ 				ldap_set_option (ldap[num], LDAP_OPT_REFERRALS , 0);
+-				_adcli_info ("Sending netlogon pings to domain controller: %s", url);
+ 				addrs[num] = srv->hostname;
+ 				have_any = 1;
+ 				num++;
+@@ -555,70 +602,65 @@ ldap_disco (const char *domain,
+ 		freeaddrinfo (res);
+ 	}
+ 
+-	/* Wait for the first response. Poor mans fd watch */
+-	for (started = now = time (NULL);
+-	     have_any && found != ADCLI_DISCO_USABLE && now < started + DISCO_TIME;
+-	     now = time (NULL)) {
++	/* Initial send and short time wait */
++	interval.tv_sec = 0;
++	for (i = 0; ADCLI_DISCO_UNUSABLE == found && i < num; ++i) {
++		int parsed;
++
++		if (NULL == ldap[i])
++			continue;
+ 
+-		struct timeval tvpoll = { 0, 0 };
+-		struct timeval interval;
++		have_any = 1;
++		_adcli_info ("Sending NetLogon ping to domain controller: %s", addrs[i]);
+ 
+-		/* If in the initial period, send feverishly */
+-		if (now < started + DISCO_FEVER) {
+-			interval.tv_sec = 0;
+-			interval.tv_usec = 100 * 1000;
++		ret = ldap_search_ext (ldap[i], "", LDAP_SCOPE_BASE,
++		                       filter, attrs, 0, NULL, NULL, NULL,
++		                       -1, &msgidp);
++
++		if (ret != LDAP_SUCCESS) {
++			_adcli_ldap_handle_failure (ldap[i], ADCLI_ERR_CONFIG,
++			                            "Couldn't perform discovery search");
++			ldap_unbind_ext_s (ldap[i], NULL, NULL);
++			ldap[i] = NULL;
++		}
++
++		/* From https://msdn.microsoft.com/en-us/library/ff718294.aspx first
++		 * five DCs are given 0.4 seconds timeout, next five are given 0.2
++		 * seconds, and the rest are given 0.1 seconds
++		 */
++		if (i < 5) {
++			interval.tv_usec = 400000;
++		} else if (i < 10) {
++			interval.tv_usec = 200000;
+ 		} else {
+-			interval.tv_sec = 1;
+-			interval.tv_usec = 0;
++			interval.tv_usec = 100000;
+ 		}
++		select (0, NULL, NULL, NULL, &interval);
++
++		parsed = ldap_disco_poller (&(ldap[i]), &message, results, &(addrs[i]));
++		if (parsed > found)
++			found = parsed;
++	}
++
++	/* Wait some more until LDAP timeout (DISCO_TIME) */
++	for (started = now = time (NULL);
++	     have_any && ADCLI_DISCO_UNUSABLE == found && now < started + DISCO_TIME;
++	     now = time (NULL)) {
+ 
+ 		select (0, NULL, NULL, NULL, &interval);
+ 
+ 		have_any = 0;
+-		for (i = 0; found != ADCLI_DISCO_USABLE && i < num; i++) {
+-			int close_ldap;
++		for (i = 0; ADCLI_DISCO_UNUSABLE == found && i < num; ++i) {
+ 			int parsed;
+ 
+ 			if (ldap[i] == NULL)
+ 				continue;
+ 
+-			ret = 0;
+ 			have_any = 1;
+-			switch (ldap_result (ldap[i], LDAP_RES_ANY, 1, &tvpoll, &message)) {
+-			case LDAP_RES_SEARCH_ENTRY:
+-			case LDAP_RES_SEARCH_RESULT:
+-				parsed = parse_disco (ldap[i], addrs[i], message, results);
+-				if (parsed > found)
+-					found = parsed;
+-				ldap_msgfree (message);
+-				close_ldap = 1;
+-				break;
+-			case 0:
+-				ret = ldap_search_ext (ldap[i], "", LDAP_SCOPE_BASE,
+-				                       filter, attrs, 0, NULL, NULL, NULL,
+-				                       -1, &msgidp);
+-				close_ldap = (ret != 0);
+-				break;
+-			case -1:
+-				ldap_get_option (ldap[i], LDAP_OPT_RESULT_CODE, &ret);
+-				close_ldap = 1;
+-				break;
+-			default:
+-				ldap_msgfree (message);
+-				close_ldap = 0;
+-				break;
+-			}
+-
+-			if (ret != LDAP_SUCCESS) {
+-				_adcli_ldap_handle_failure (ldap[i], ADCLI_ERR_CONFIG,
+-				                            "Couldn't perform discovery search");
+-			}
+ 
+-			/* Done with this connection */
+-			if (close_ldap) {
+-				ldap_unbind_ext_s (ldap[i], NULL, NULL);
+-				ldap[i] = NULL;
+-			}
++			parsed = ldap_disco_poller (&(ldap[i]), &message, results, &(addrs[i]));
++			if (parsed > found)
++				found = parsed;
+ 		}
+ 	}
+ 
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch b/SOURCES/0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch
new file mode 100644
index 0000000..223ad5b
--- /dev/null
+++ b/SOURCES/0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch
@@ -0,0 +1,31 @@
+From b2d1e74852c2ea4cb6d7cb02d771aebf34c77864 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 24 Aug 2016 15:37:41 +0200
+Subject: [PATCH 1/2] Remove upper-case only check when looking for the NetBIOS
+ name
+
+It is a convention to use only upper-case letters for NetBIOS names but
+it is not enforced on the AD-side. With the new option to specify a
+random NetBIOS name it is possible to create host entries in AD with
+lower-case letters in the name. To properly determine the name from the
+keytab the upper-case check should be dropped,dc=
+---
+ library/adenroll.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index a15e4be..d1020e9 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -1309,7 +1309,7 @@ load_keytab_entry (krb5_context k5,
+ 	if (!enroll->host_fqdn_explicit && !enroll->computer_name_explicit) {
+ 
+ 		/* Automatically use the netbios name */
+-		if (!enroll->computer_name && len > 1 && _adcli_str_is_up (name) &&
++		if (!enroll->computer_name && len > 1 &&
+ 		    _adcli_str_has_suffix (name, "$") && !strchr (name, '/')) {
+ 			enroll->computer_name = name;
+ 			name[len - 1] = '\0';
+-- 
+2.7.4
+
diff --git a/SOURCES/0001-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch b/SOURCES/0001-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch
new file mode 100644
index 0000000..20518ba
--- /dev/null
+++ b/SOURCES/0001-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch
@@ -0,0 +1,32 @@
+From 05eae8a57a556412aa18d37660ebfabb74ab2890 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 31 May 2018 17:01:36 +0200
+Subject: [PATCH] adcli_conn_is_writeable: do not crash id domain_disco is
+ missing
+
+Resolves https://bugzilla.redhat.com/show_bug.cgi?id=1575554
+---
+ library/adconn.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/library/adconn.c b/library/adconn.c
+index d2fb1d5..e2250e3 100644
+--- a/library/adconn.c
++++ b/library/adconn.c
+@@ -1567,6 +1567,11 @@ adcli_conn_server_has_capability (adcli_conn *conn,
+ 
+ bool adcli_conn_is_writeable (adcli_conn *conn)
+ {
+-    disco_dance_if_necessary (conn);
+-    return ( (conn->domain_disco->flags & ADCLI_DISCO_WRITABLE) != 0);
++	disco_dance_if_necessary (conn);
++
++	if (conn->domain_disco == NULL) {
++		return false;
++	}
++
++	return ( (conn->domain_disco->flags & ADCLI_DISCO_WRITABLE) != 0);
+ }
+-- 
+2.14.4
+
diff --git a/SOURCES/0001-adenroll-make-sure-only-allowed-enctypes-are-used-in.patch b/SOURCES/0001-adenroll-make-sure-only-allowed-enctypes-are-used-in.patch
new file mode 100644
index 0000000..ad69b70
--- /dev/null
+++ b/SOURCES/0001-adenroll-make-sure-only-allowed-enctypes-are-used-in.patch
@@ -0,0 +1,80 @@
+From 341974aae7d0755fc32a0b7e2b34d8e1ef60d195 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 20 Dec 2018 21:05:35 +0100
+Subject: [PATCH 1/4] adenroll: make sure only allowed enctypes are used in
+ FIPS mode
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1717355
+---
+ library/adenroll.c | 36 +++++++++++++++++++++++++++++++++++-
+ 1 file changed, 35 insertions(+), 1 deletion(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 52aa8a8..f617f28 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -41,11 +41,19 @@
+ #include <netdb.h>
+ #include <stdio.h>
+ #include <unistd.h>
++#include <sys/stat.h>
++#include <fcntl.h>
+ 
+ #ifndef SAMBA_DATA_TOOL
+ #define SAMBA_DATA_TOOL "/usr/bin/net"
+ #endif
+ 
++static krb5_enctype v60_later_enctypes_fips[] = {
++	ENCTYPE_AES256_CTS_HMAC_SHA1_96,
++	ENCTYPE_AES128_CTS_HMAC_SHA1_96,
++	0
++};
++
+ static krb5_enctype v60_later_enctypes[] = {
+ 	ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ 	ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+@@ -2594,6 +2602,28 @@ adcli_enroll_set_keytab_name (adcli_enroll *enroll,
+ 	enroll->keytab_name_is_krb5 = 0;
+ }
+ 
++#define PROC_SYS_FIPS "/proc/sys/crypto/fips_enabled"
++
++static bool adcli_fips_enabled (void)
++{
++	int fd;
++	ssize_t len;
++	char buf[8];
++
++	fd = open (PROC_SYS_FIPS, O_RDONLY);
++	if (fd != -1) {
++		len = read (fd, buf, sizeof (buf));
++		close (fd);
++		/* Assume FIPS in enabled if PROC_SYS_FIPS contains a
++		 * non-0 value. */
++		if ( ! (len == 2 && buf[0] == '0' && buf[1] == '\n')) {
++			return true;
++		}
++	}
++
++	return false;
++}
++
+ krb5_enctype *
+ adcli_enroll_get_keytab_enctypes (adcli_enroll *enroll)
+ {
+@@ -2602,7 +2632,11 @@ adcli_enroll_get_keytab_enctypes (adcli_enroll *enroll)
+ 		return enroll->keytab_enctypes;
+ 
+ 	if (adcli_conn_server_has_capability (enroll->conn, ADCLI_CAP_V60_OID))
+-		return v60_later_enctypes;
++		if (adcli_fips_enabled ()) {
++			return v60_later_enctypes_fips;
++		} else {
++			return v60_later_enctypes;
++		}
+ 	else
+ 		return v51_earlier_enctypes;
+ }
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-adutil-add-_adcli_strv_add_unique.patch b/SOURCES/0001-adutil-add-_adcli_strv_add_unique.patch
new file mode 100644
index 0000000..f49f5f8
--- /dev/null
+++ b/SOURCES/0001-adutil-add-_adcli_strv_add_unique.patch
@@ -0,0 +1,134 @@
+From 85d127fd52a8469f9f3ce0d1130fe17e756fdd75 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 16 Nov 2018 13:32:33 +0100
+Subject: [PATCH 1/2] adutil: add _adcli_strv_add_unique
+
+_adcli_strv_add_unique checks is the new value already exists in the
+strv before adding it. Check can be done case-sensitive or not.
+
+Related to https://gitlab.freedesktop.org/realmd/adcli/issues/16
+---
+ library/adprivate.h |  5 ++++
+ library/adutil.c    | 65 ++++++++++++++++++++++++++++++++++++++-------
+ 2 files changed, 61 insertions(+), 9 deletions(-)
+
+diff --git a/library/adprivate.h b/library/adprivate.h
+index bc9df6d..0806430 100644
+--- a/library/adprivate.h
++++ b/library/adprivate.h
+@@ -111,6 +111,11 @@ char **        _adcli_strv_add               (char **strv,
+                                               char *string,
+                                               int *length) GNUC_WARN_UNUSED;
+ 
++char **        _adcli_strv_add_unique        (char **strv,
++                                              char *string,
++                                              int *length,
++                                              bool case_sensitive) GNUC_WARN_UNUSED;
++
+ void           _adcli_strv_remove_unsorted   (char **strv,
+                                               const char *string,
+                                               int *length);
+diff --git a/library/adutil.c b/library/adutil.c
+index 17d2caa..76ea158 100644
+--- a/library/adutil.c
++++ b/library/adutil.c
+@@ -221,6 +221,34 @@ _adcli_strv_add (char **strv,
+ 	return seq_push (strv, length, string);
+ }
+ 
++static int
++_adcli_strv_has_ex (char **strv,
++                    const char *str,
++                    int (* compare) (const char *match, const char*value))
++{
++	int i;
++
++	for (i = 0; strv && strv[i] != NULL; i++) {
++		if (compare (strv[i], str) == 0)
++			return 1;
++	}
++
++	return 0;
++}
++
++char **
++_adcli_strv_add_unique (char **strv,
++                        char *string,
++                        int *length,
++                        bool case_sensitive)
++{
++	if (_adcli_strv_has_ex (strv, string, case_sensitive ? strcmp : strcasecmp) == 1) {
++		return strv;
++	}
++
++	return _adcli_strv_add (strv, string, length);
++}
++
+ #define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
+ 
+ void
+@@ -241,19 +269,11 @@ _adcli_strv_remove_unsorted (char **strv,
+ 	                            (seq_compar)strcasecmp, free);
+ }
+ 
+-
+ int
+ _adcli_strv_has (char **strv,
+                  const char *str)
+ {
+-	int i;
+-
+-	for (i = 0; strv && strv[i] != NULL; i++) {
+-		if (strcmp (strv[i], str) == 0)
+-			return 1;
+-	}
+-
+-	return 0;
++	return _adcli_strv_has_ex (strv, str, strcmp);
+ }
+ 
+ void
+@@ -704,6 +724,32 @@ test_strv_add_free (void)
+ 	_adcli_strv_free (strv);
+ }
+ 
++static void
++test_strv_add_unique_free (void)
++{
++	char **strv = NULL;
++
++	strv = _adcli_strv_add_unique (strv, strdup ("one"), NULL, false);
++	strv = _adcli_strv_add_unique (strv, strdup ("one"), NULL, false);
++	strv = _adcli_strv_add_unique (strv, strdup ("two"), NULL, false);
++	strv = _adcli_strv_add_unique (strv, strdup ("two"), NULL, false);
++	strv = _adcli_strv_add_unique (strv, strdup ("tWo"), NULL, false);
++	strv = _adcli_strv_add_unique (strv, strdup ("three"), NULL, false);
++	strv = _adcli_strv_add_unique (strv, strdup ("three"), NULL, false);
++	strv = _adcli_strv_add_unique (strv, strdup ("TWO"), NULL, true);
++
++	assert_num_eq (_adcli_strv_len (strv), 4);
++
++	assert_str_eq (strv[0], "one");
++	assert_str_eq (strv[1], "two");
++	assert_str_eq (strv[2], "three");
++	assert_str_eq (strv[3], "TWO");
++	assert (strv[4] == NULL);
++
++	_adcli_strv_free (strv);
++}
++
++
+ static void
+ test_strv_dup (void)
+ {
+@@ -856,6 +902,7 @@ main (int argc,
+       char *argv[])
+ {
+ 	test_func (test_strv_add_free, "/util/strv_add_free");
++	test_func (test_strv_add_unique_free, "/util/strv_add_unique_free");
+ 	test_func (test_strv_dup, "/util/strv_dup");
+ 	test_func (test_strv_count, "/util/strv_count");
+ 	test_func (test_check_nt_time_string_lifetime, "/util/check_nt_time_string_lifetime");
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-correct-spelling-of-adcli_tool_computer_delete-descr.patch b/SOURCES/0001-correct-spelling-of-adcli_tool_computer_delete-descr.patch
new file mode 100644
index 0000000..f51933a
--- /dev/null
+++ b/SOURCES/0001-correct-spelling-of-adcli_tool_computer_delete-descr.patch
@@ -0,0 +1,26 @@
+From faa93cfbd679f193a61def8c2bbc203e507d2466 Mon Sep 17 00:00:00 2001
+From: Striker Leggette <striker@redhat.com>
+Date: Wed, 1 Nov 2017 11:16:39 +0100
+Subject: [PATCH 1/4] correct spelling of 'adcli_tool_computer_delete'
+ description
+
+---
+ tools/tools.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tools/tools.c b/tools/tools.c
+index 4b243de..915130e 100644
+--- a/tools/tools.c
++++ b/tools/tools.c
+@@ -57,7 +57,7 @@ struct {
+ 	{ "update", adcli_tool_computer_update, "Update machine membership in a domain", },
+ 	{ "preset-computer", adcli_tool_computer_preset, "Pre setup computers accounts", },
+ 	{ "reset-computer", adcli_tool_computer_reset, "Reset a computer account", },
+-	{ "delete-computer", adcli_tool_computer_delete, "Delete a computer acocunt", },
++	{ "delete-computer", adcli_tool_computer_delete, "Delete a computer account", },
+ 	{ "create-user", adcli_tool_user_create, "Create a user account", },
+ 	{ "delete-user", adcli_tool_user_delete, "Delete a user account", },
+ 	{ "create-group", adcli_tool_group_create, "Create a group", },
+-- 
+2.13.6
+
diff --git a/SOURCES/0001-create-user-add-nis-domain-option.patch b/SOURCES/0001-create-user-add-nis-domain-option.patch
new file mode 100644
index 0000000..32b0ca9
--- /dev/null
+++ b/SOURCES/0001-create-user-add-nis-domain-option.patch
@@ -0,0 +1,71 @@
+From 1457b4a7623a8ae58fb8d6a652d1cc44904b8863 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 18 Mar 2019 11:02:57 +0100
+Subject: [PATCH 1/2] create-user: add nis-domain option
+
+Related to https://gitlab.freedesktop.org/realmd/adcli/issues/2
+---
+ doc/adcli.xml | 8 ++++++++
+ tools/entry.c | 6 ++++++
+ 2 files changed, 14 insertions(+)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index 4722c3a..18620c0 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -531,6 +531,14 @@ $ adcli create-user Fry --domain=domain.example.com \
+ 			the new created user account, which should be the user's
+ 			numeric primary user id.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--nis-domain=<parameter>nis_domain</parameter></option></term>
++			<listitem><para>Set the <code>msSFU30NisDomain</code> attribute of
++			the new created user account, which should be the user's
++			NIS domain is the NIS/YP service of Active Directory's Services for Unix (SFU)
++			are used. This is needed to let the 'UNIX attributes' tab of older Active
++			Directoy versions show the set UNIX specific attributes.</para></listitem>
++		</varlistentry>
+ 	</variablelist>
+ 
+ </refsect1>
+diff --git a/tools/entry.c b/tools/entry.c
+index 7b6a200..69ce62c 100644
+--- a/tools/entry.c
++++ b/tools/entry.c
+@@ -52,6 +52,7 @@ typedef enum {
+ 	opt_unix_uid,
+ 	opt_unix_gid,
+ 	opt_unix_shell,
++	opt_nis_domain,
+ } Option;
+ 
+ static adcli_tool_desc common_usages[] = {
+@@ -62,6 +63,7 @@ static adcli_tool_desc common_usages[] = {
+ 	{ opt_unix_uid, "unix uid number" },
+ 	{ opt_unix_gid, "unix gid number" },
+ 	{ opt_unix_shell, "unix shell" },
++	{ opt_nis_domain, "NIS domain" },
+ 	{ opt_domain, "active directory domain name" },
+ 	{ opt_domain_realm, "kerberos realm for the domain" },
+ 	{ opt_domain_controller, "domain directory server to connect to" },
+@@ -159,6 +161,7 @@ adcli_tool_user_create (adcli_conn *conn,
+ 		{ "unix-uid", required_argument, NULL, opt_unix_uid },
+ 		{ "unix-gid", required_argument, NULL, opt_unix_gid },
+ 		{ "unix-shell", required_argument, NULL, opt_unix_shell },
++		{ "nis-domain", required_argument, NULL, opt_nis_domain },
+ 		{ "domain-ou", required_argument, NULL, opt_domain_ou },
+ 		{ "domain", required_argument, NULL, opt_domain },
+ 		{ "domain-realm", required_argument, NULL, opt_domain_realm },
+@@ -200,6 +203,9 @@ adcli_tool_user_create (adcli_conn *conn,
+ 		case opt_unix_shell:
+ 			adcli_attrs_add (attrs, "loginShell", optarg, NULL);
+ 			break;
++		case opt_nis_domain:
++			adcli_attrs_add (attrs, "msSFU30NisDomain", optarg, NULL);
++			break;
+ 		case opt_domain_ou:
+ 			ou = optarg;
+ 			break;
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-delete-use-keytab-data-to-determine-realm-and-NetBIO.patch b/SOURCES/0001-delete-use-keytab-data-to-determine-realm-and-NetBIO.patch
new file mode 100644
index 0000000..03ce760
--- /dev/null
+++ b/SOURCES/0001-delete-use-keytab-data-to-determine-realm-and-NetBIO.patch
@@ -0,0 +1,29 @@
+From 038af878713155a87b2e3a25f18b0dd4cf74bd39 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 25 Jul 2016 11:58:21 +0200
+Subject: [PATCH] delete: use keytab data to determine realm and NetBIOS name
+
+---
+ tools/computer.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/tools/computer.c b/tools/computer.c
+index 7ad7bf6..d8a58c9 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -732,6 +732,12 @@ adcli_tool_computer_delete (adcli_conn *conn,
+ 
+ 	adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT);
+ 
++	res = adcli_enroll_load (enroll);
++	if (res != ADCLI_SUCCESS) {
++		errx (-res, "couldn't lookup domain info from keytab: %s",
++		      adcli_get_last_error ());
++	}
++
+ 	res = adcli_conn_connect (conn);
+ 	if (res != ADCLI_SUCCESS) {
+ 		errx (-res, "couldn't connect to %s domain: %s",
+-- 
+2.7.4
+
diff --git a/SOURCES/0001-doc-Update-the-documentation-about-the-default-kerbe.patch b/SOURCES/0001-doc-Update-the-documentation-about-the-default-kerbe.patch
new file mode 100644
index 0000000..a20768f
--- /dev/null
+++ b/SOURCES/0001-doc-Update-the-documentation-about-the-default-kerbe.patch
@@ -0,0 +1,29 @@
+From 40acf5f12379c8e7c86f2fec34e1e276ede5ef47 Mon Sep 17 00:00:00 2001
+From: Stef Walter <stefw@redhat.com>
+Date: Thu, 16 Jun 2016 15:27:45 +0200
+Subject: [PATCH] doc: Update the documentation about the default kerberos
+ cache
+
+https://bugs.freedesktop.org/show_bug.cgi?id=96544
+---
+ doc/adcli.xml | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index a83e321..e18ba5d 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -120,7 +120,9 @@
+ 		<varlistentry>
+ 			<term><option>-C, --login-ccache=<parameter>/path/to/file</parameter></option></term>
+ 			<listitem><para>Use the specified kerberos credential
+-			cache to authenticate with the domain.</para></listitem>
++			cache to authenticate with the domain. If no file is specified or
++			<option>-C</option> is used, then the default kerberos credential cache will
++			be used.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>-U, --login-user=<parameter>User</parameter></option></term>
+-- 
+2.13.6
+
diff --git a/SOURCES/0001-doc-explain-how-to-force-password-reset.patch b/SOURCES/0001-doc-explain-how-to-force-password-reset.patch
new file mode 100644
index 0000000..01e694d
--- /dev/null
+++ b/SOURCES/0001-doc-explain-how-to-force-password-reset.patch
@@ -0,0 +1,27 @@
+From 9b187095edb8c914238419ed51fef6041864f4fc Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 26 Aug 2019 13:33:24 +0200
+Subject: [PATCH] doc: explain how to force password reset
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1738573
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index 094f577..4f201e0 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -330,7 +330,11 @@ Password for Administrator:
+ 			important here is currently the
+ 			<option>workgroup</option> option, see
+ 			<citerefentry><refentrytitle>smb.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+-			for details.</para></listitem>
++			for details.</para>
++			<para>Note that if the machine account password is not
++			older than 30 days, you have to pass
++			<option>--computer-password-lifetime=0</option> to
++			force the update.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>--samba-data-tool=<parameter>/path/to/net</parameter></option></term>
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-doc-fix-typos-in-the-adcli-man-page.patch b/SOURCES/0001-doc-fix-typos-in-the-adcli-man-page.patch
new file mode 100644
index 0000000..41bd97a
--- /dev/null
+++ b/SOURCES/0001-doc-fix-typos-in-the-adcli-man-page.patch
@@ -0,0 +1,203 @@
+From 1143163c367b8473dc3d0ea696eed3c30ccd634b Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 5 Jul 2018 13:06:26 +0200
+Subject: [PATCH] doc: fix typos in the adcli man page
+
+Resolves https://bugzilla.redhat.com/show_bug.cgi?id=1440533
+---
+ doc/adcli.xml | 44 ++++++++++++++++++++++----------------------
+ 1 file changed, 22 insertions(+), 22 deletions(-)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index 83b6981..97dec08 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -105,19 +105,19 @@
+ 		<varlistentry>
+ 			<term><option>-D, --domain=<parameter>domain</parameter></option></term>
+ 			<listitem><para>The domain to connect to. If a domain is
+-			not specified then the domain part of the local computer's
++			not specified, then the domain part of the local computer's
+ 			host name is used.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>-R, --domain-realm=<parameter>REALM</parameter></option></term>
+ 			<listitem><para>Kerberos realm for the domain. If not
+-			specified then the upper cased domain name is
++			specified, then the upper cased domain name is
+ 			used.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>-S, --domain-controller=<parameter>server</parameter></option></term>
+ 			<listitem><para>Connect to a specific domain controller.
+-			If not specified then an appropriate domain controller
++			If not specified, then an appropriate domain controller
+ 			is automatically discovered.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+@@ -134,7 +134,7 @@
+ 		<varlistentry>
+ 			<term><option>-U, --login-user=<parameter>User</parameter></option></term>
+ 			<listitem><para>Use the specified user account to
+-			authenticate with the domain. If not specified then
++			authenticate with the domain. If not specified, then
+ 			the name 'Administrator' will be used.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+@@ -181,7 +181,7 @@ $ adcli info --domain-controller=dc.domain.example.com
+ 	<para><command>adcli info</command> will output as much information as
+ 	it can about the domain. The information is designed to be both machine
+ 	and human readable. The command will exit with a non-zero exit code
+-	if the domain does note exist or cannot be reached.</para>
++	if the domain does not exist or cannot be reached.</para>
+ 
+ 	<para>To show domain info for a specific domain controller use the
+ 	<option>--domain-controller</option> option to specify which domain
+@@ -213,35 +213,35 @@ Password for Administrator:
+ 		<varlistentry>
+ 			<term><option>-N, --computer-name=<parameter>computer</parameter></option></term>
+ 			<listitem><para>The short non-dotted name of the computer
+-			account that will be created in the domain. If not specified
++			account that will be created in the domain. If not specified,
+ 			then the first portion of the <option>--host-fqdn</option>
+ 			is used.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>-O, --domain-ou=<parameter>OU=xxx</parameter></option></term>
+ 			<listitem><para>The full distinguished name of the OU in
+-			which to create the computer account. If not specified
++			which to create the computer account. If not specified,
+ 			then the computer account will be created in a default
+ 			location.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>-H, --host-fqdn=<parameter>host</parameter></option></term>
+ 			<listitem><para>Override the local machine's fully qualified
+-			domain name. If not specified the local machine's hostname
++			domain name. If not specified, the local machine's hostname
+ 			will be retrieved via <function>gethostname()</function>.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>-K, --host-keytab=<parameter>/path/to/keytab</parameter></option></term>
+ 			<listitem><para>Specify the path to the host keytab where
+ 			host credentials will be written after a successful join
+-			operation. If not specified the default location will be
++			operation. If not specified, the default location will be
+ 			used, usually <filename>/etc/krb5.keytab</filename>.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>--login-type=<parameter>{computer|user}</parameter></option></term>
+ 			<listitem><para>Specify the type of authentication that
+ 			will be performed before creating the machine account in
+-			the domain. If set to 'computer' then the computer must
++			the domain. If set to 'computer', then the computer must
+ 			already have a preset account in the domain. If not
+ 			specified and none of the other <option>--login-xxx</option>
+ 			arguments have been specified, then will try both
+@@ -329,7 +329,7 @@ Password for Administrator:
+ 			<term><option>--samba-data-tool=<parameter>/path/to/net</parameter></option></term>
+ 			<listitem><para>If Samba's <command>net</command>
+ 			cannot be found at
+-			<filename>&samba_data_tool;</filename> this option can
++			<filename>&samba_data_tool;</filename>, this option can
+ 			be used to specific an alternative location with the
+ 			help of an absolute path.</para></listitem>
+ 		</varlistentry>
+@@ -351,7 +351,7 @@ Password for Administrator:
+ $ adcli update
+ </programlisting>
+ 
+-	<para>If used with a credential cache other attributes of the computer
++	<para>If used with a credential cache, other attributes of the computer
+ 	account can be changed as well if the principal has sufficient
+ 	privileges.</para>
+ 
+@@ -367,20 +367,20 @@ $ adcli update --login-ccache=/tmp/krbcc_123
+ 		<varlistentry>
+ 			<term><option>-N, --computer-name=<parameter>computer</parameter></option></term>
+ 			<listitem><para>The short non-dotted name of the computer
+-			account that will be created in the domain. If not specified
++			account that will be created in the domain. If not specified,
+ 			it will be retrieved from the keytab entries.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>-H, --host-fqdn=<parameter>host</parameter></option></term>
+ 			<listitem><para>The local machine's fully qualified
+-			domain name. If not specified the local machine's hostname
++			domain name. If not specified, the local machine's hostname
+ 			will be retrieved from the keytab entries.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>-K, --host-keytab=<parameter>/path/to/keytab</parameter></option></term>
+ 			<listitem><para>Specify the path to the host keytab where
+ 			current host credentials are stored and the new ones
+-			will be written to.  If not specified the default
++			will be written to.  If not specified, the default
+ 			location will be used, usually
+ 			<filename>/etc/krb5.keytab</filename>.</para></listitem>
+ 		</varlistentry>
+@@ -462,7 +462,7 @@ $ adcli update --login-ccache=/tmp/krbcc_123
+ 			<term><option>--samba-data-tool=<parameter>/path/to/net</parameter></option></term>
+ 			<listitem><para>If Samba's <command>net</command>
+ 			cannot be found at
+-			<filename>&samba_data_tool;</filename> this option can
++			<filename>&samba_data_tool;</filename>, this option can
+ 			be used to specific an alternative location with the
+ 			help of an absolute path.</para></listitem>
+ 		</varlistentry>
+@@ -493,7 +493,7 @@ $ adcli create-user Fry --domain=domain.example.com \
+ 		<varlistentry>
+ 			<term><option>-O, --domain-ou=<parameter>OU=xxx</parameter></option></term>
+ 			<listitem><para>The full distinguished name of the OU in
+-			which to create the user account. If not specified
++			which to create the user account. If not specified,
+ 			then the computer account will be created in a default
+ 			location.</para></listitem>
+ 		</varlistentry>
+@@ -569,7 +569,7 @@ $ adcli create-group Pilots --domain=domain.example.com \
+ 		<varlistentry>
+ 			<term><option>-O, --domain-ou=<parameter>OU=xxx</parameter></option></term>
+ 			<listitem><para>The full distinguished name of the OU in
+-			which to create the group. If not specified
++			which to create the group. If not specified,
+ 			then the group will be created in a default
+ 			location.</para></listitem>
+ 		</varlistentry>
+@@ -649,14 +649,14 @@ Password for Administrator:
+ 		<varlistentry>
+ 			<term><option>-O, --domain-ou=<parameter>OU=xxx</parameter></option></term>
+ 			<listitem><para>The full distinguished name of the OU in
+-			which to create the computer accounts. If not specified
++			which to create the computer accounts. If not specified,
+ 			then the computer account will be created in a default
+ 			location.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>--one-time-password</option></term>
+ 			<listitem><para>Specify a one time password to use when
+-			presetting the computer accounts. If not specified then
++			presetting the computer accounts. If not specified, then
+ 			a default password will be used, which allows for later
+ 			automatic joins.</para></listitem>
+ 		</varlistentry>
+@@ -696,7 +696,7 @@ Password for Administrator:
+ 	<title>Reset Computer Account</title>
+ 
+ 	<para><command>adcli reset-computer</command> resets a computer account
+-	in the domain. If a the appropriate machine is currently joined to the
++	in the domain. If the appropriate machine is currently joined to the
+ 	domain, then its membership will be broken. The account must already
+ 	exist.</para>
+ 
+@@ -716,7 +716,7 @@ $ adcli reset-computer --domain=domain.example.com host2
+ 			<term><option>--login-type=<parameter>{computer|user}</parameter></option></term>
+ 			<listitem><para>Specify the type of authentication that
+ 			will be performed before creating the machine account in
+-			the domain. If set to 'computer' then the computer must
++			the domain. If set to 'computer', then the computer must
+ 			already have a preset account in the domain. If not
+ 			specified and none of the other <option>--login-xxx</option>
+ 			arguments have been specified, then will try both
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-ensure_keytab_principals-do-not-leak-memory-when-cal.patch b/SOURCES/0001-ensure_keytab_principals-do-not-leak-memory-when-cal.patch
new file mode 100644
index 0000000..dfdf745
--- /dev/null
+++ b/SOURCES/0001-ensure_keytab_principals-do-not-leak-memory-when-cal.patch
@@ -0,0 +1,72 @@
+From 3a84c2469c31967bc22c0490456f07723ef5fc86 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 20 Mar 2019 11:01:50 +0100
+Subject: [PATCH 1/4] ensure_keytab_principals: do not leak memory when called
+ twice
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1630187
+---
+ library/adenroll.c | 32 +++++++++++++++++++++-----------
+ 1 file changed, 21 insertions(+), 11 deletions(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index d1f746c..48cb4cf 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -413,6 +413,25 @@ ensure_service_principals (adcli_result res,
+ 	return res;
+ }
+ 
++static void enroll_clear_keytab_principals (adcli_enroll *enroll)
++{
++	krb5_context k5;
++	size_t c;
++
++	if (enroll->keytab_principals) {
++		k5 = adcli_conn_get_krb5_context (enroll->conn);
++		return_if_fail (k5 != NULL);
++
++		for (c = 0; enroll->keytab_principals[c] != NULL; c++)
++			krb5_free_principal (k5, enroll->keytab_principals[c]);
++
++		free (enroll->keytab_principals);
++		enroll->keytab_principals = NULL;
++	}
++
++	return;
++}
++
+ static adcli_result
+ ensure_keytab_principals (adcli_result res,
+                           adcli_enroll *enroll)
+@@ -430,6 +449,7 @@ ensure_keytab_principals (adcli_result res,
+ 	k5 = adcli_conn_get_krb5_context (enroll->conn);
+ 	return_unexpected_if_fail (k5 != NULL);
+ 
++	enroll_clear_keytab_principals (enroll);
+ 	enroll->keytab_principals = calloc (count + 3, sizeof (krb5_principal));
+ 	return_unexpected_if_fail (enroll->keytab_principals != NULL);
+ 	at = 0;
+@@ -1860,18 +1880,8 @@ static void
+ enroll_clear_state (adcli_enroll *enroll)
+ {
+ 	krb5_context k5;
+-	int i;
+-
+-	if (enroll->keytab_principals) {
+-		k5 = adcli_conn_get_krb5_context (enroll->conn);
+-		return_if_fail (k5 != NULL);
+-
+-		for (i = 0; enroll->keytab_principals[i] != NULL; i++)
+-			krb5_free_principal (k5, enroll->keytab_principals[i]);
+ 
+-		free (enroll->keytab_principals);
+-		enroll->keytab_principals = NULL;
+-	}
++	enroll_clear_keytab_principals (enroll);
+ 
+ 	if (enroll->keytab) {
+ 		k5 = adcli_conn_get_krb5_context (enroll->conn);
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-fix-typo-in-flag-value.patch b/SOURCES/0001-fix-typo-in-flag-value.patch
new file mode 100644
index 0000000..07577b7
--- /dev/null
+++ b/SOURCES/0001-fix-typo-in-flag-value.patch
@@ -0,0 +1,25 @@
+From 870ecd8f982ebb97092a93d730ad4006bd78505c Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 8 Aug 2018 12:03:01 +0200
+Subject: [PATCH 1/4] fix typo in flag value
+
+---
+ library/adenroll.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/library/adenroll.h b/library/adenroll.h
+index f87dffa..abbbfd4 100644
+--- a/library/adenroll.h
++++ b/library/adenroll.h
+@@ -30,7 +30,7 @@ typedef enum {
+ 	ADCLI_ENROLL_NO_KEYTAB = 1 << 1,
+ 	ADCLI_ENROLL_ALLOW_OVERWRITE = 1 << 2,
+ 	ADCLI_ENROLL_PASSWORD_VALID = 1 << 3,
+-	ADCLI_ENROLL_ADD_SAMBA_DATA = 1 << 3,
++	ADCLI_ENROLL_ADD_SAMBA_DATA = 1 << 4,
+ } adcli_enroll_flags;
+ 
+ typedef struct _adcli_enroll adcli_enroll;
+-- 
+2.17.1
+
diff --git a/SOURCES/0001-join-always-add-service-principals.patch b/SOURCES/0001-join-always-add-service-principals.patch
new file mode 100644
index 0000000..0281dc6
--- /dev/null
+++ b/SOURCES/0001-join-always-add-service-principals.patch
@@ -0,0 +1,86 @@
+From cd296bf24e7cc56fb8d00bad7e9a56c539894309 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 19 Mar 2019 20:44:36 +0100
+Subject: [PATCH 1/2] join: always add service principals
+
+If currently --service-name is given during the join only the service
+names given by this option are added as service principal names. As a
+result the default 'host' service principal name might be missing which
+might cause issues e.g. with SSSD and sshd.
+
+The patch makes sure the default service principals 'host' and
+'RestrictedKrbHost' are always added during join.
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1644311
+---
+ library/adenroll.c | 36 ++++++++++++++++++++++++++++++------
+ 1 file changed, 30 insertions(+), 6 deletions(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 58362c2..d1f746c 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -288,16 +288,23 @@ ensure_computer_password (adcli_result res,
+ }
+ 
+ static adcli_result
+-ensure_service_names (adcli_result res,
+-                      adcli_enroll *enroll)
++ensure_default_service_names (adcli_enroll *enroll)
+ {
+ 	int length = 0;
+ 
+-	if (res != ADCLI_SUCCESS)
+-		return res;
++	if (enroll->service_names != NULL) {
++		length = seq_count (enroll->service_names);
+ 
+-	if (enroll->service_names || enroll->service_principals)
+-		return ADCLI_SUCCESS;
++		/* Make sure there is no entry with an unexpected case. AD
++		 * would not care but since the client side is case-sensitive
++		 * we should make sure we use the expected spelling. */
++		seq_remove_unsorted (enroll->service_names,
++		                     &length, "host",
++		                     (seq_compar)strcasecmp, free);
++		seq_remove_unsorted (enroll->service_names,
++		                     &length, "RestrictedKrbHost",
++		                     (seq_compar)strcasecmp, free);
++	}
+ 
+ 	/* The default ones specified by MS */
+ 	enroll->service_names = _adcli_strv_add (enroll->service_names,
+@@ -307,6 +314,19 @@ ensure_service_names (adcli_result res,
+ 	return ADCLI_SUCCESS;
+ }
+ 
++static adcli_result
++ensure_service_names (adcli_result res,
++                      adcli_enroll *enroll)
++{
++	if (res != ADCLI_SUCCESS)
++		return res;
++
++	if (enroll->service_names || enroll->service_principals)
++		return ADCLI_SUCCESS;
++
++	return ensure_default_service_names (enroll);
++}
++
+ static adcli_result
+ add_service_names_to_service_principals (adcli_enroll *enroll)
+ {
+@@ -2039,6 +2059,10 @@ adcli_enroll_join (adcli_enroll *enroll,
+ 	if (res != ADCLI_SUCCESS)
+ 		return res;
+ 
++	res = ensure_default_service_names (enroll);
++	if (res != ADCLI_SUCCESS)
++		return res;
++
+ 	res = adcli_enroll_prepare (enroll, flags);
+ 	if (res != ADCLI_SUCCESS)
+ 		return res;
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-library-Fix-check-for-EAGAIN-or-EINTR.patch b/SOURCES/0001-library-Fix-check-for-EAGAIN-or-EINTR.patch
new file mode 100644
index 0000000..e8b7b57
--- /dev/null
+++ b/SOURCES/0001-library-Fix-check-for-EAGAIN-or-EINTR.patch
@@ -0,0 +1,26 @@
+From 836dcc4ef75d6ae46661e61be73c8b7737f85f68 Mon Sep 17 00:00:00 2001
+From: Stef Walter <stefw@redhat.com>
+Date: Thu, 14 Apr 2016 10:00:13 +0200
+Subject: [PATCH 1/3] library: Fix check for EAGAIN or EINTR
+
+https://bugs.freedesktop.org/show_bug.cgi?id=71257
+---
+ library/adutil.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/library/adutil.c b/library/adutil.c
+index 60f9d5e..21ccd27 100644
+--- a/library/adutil.c
++++ b/library/adutil.c
+@@ -377,7 +377,7 @@ _adcli_write_all (int fd,
+ 	while (len > 0) {
+ 		res = write (fd, buf, len);
+ 		if (res <= 0) {
+-			if (errno == EAGAIN && errno == EINTR)
++			if (errno == EAGAIN || errno == EINTR)
+ 				continue;
+ 			return -errno;
+ 		} else  {
+-- 
+2.5.5
+
diff --git a/SOURCES/0001-library-add-missing-strdup.patch b/SOURCES/0001-library-add-missing-strdup.patch
new file mode 100644
index 0000000..378bb3c
--- /dev/null
+++ b/SOURCES/0001-library-add-missing-strdup.patch
@@ -0,0 +1,34 @@
+From a64cce9830c2e9c26e120f671b247ee71b45c888 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 12 Apr 2019 17:31:41 +0200
+Subject: [PATCH] library: add missing strdup
+
+In add_server_side_service_principals _adcli_strv_add_unique is called
+which only adds a string to a list without copying to. Since the
+original list will be freed later the value must be copied.
+
+This issue was introduce with 972f1a2f35829ed89f5353bd204683aa9ad6a2d2
+and hence
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1630187
+---
+ library/adenroll.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 1cce86a..52aa8a8 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -1987,7 +1987,8 @@ add_server_side_service_principals (adcli_enroll *enroll)
+ 		_adcli_info ("Checking %s", spn_list[c]);
+ 		if (!_adcli_strv_has_ex (enroll->service_principals_to_remove, spn_list[c], strcasecmp)) {
+ 			enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals,
+-		                                                             spn_list[c], &length, false);
++			                                                     strdup (spn_list[c]),
++			                                                     &length, false);
+ 			assert (enroll->service_principals != NULL);
+ 			_adcli_info ("   Added %s", spn_list[c]);
+ 		}
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch b/SOURCES/0001-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch
new file mode 100644
index 0000000..c682c5a
--- /dev/null
+++ b/SOURCES/0001-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch
@@ -0,0 +1,92 @@
+From 85b835f8258a57e3b23de47a255dddd822d5bfb3 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 15 Mar 2019 17:33:44 +0100
+Subject: [PATCH] library: use getaddrinfo with AI_CANONNAME to find a FQDN
+
+Currently adcli creates service principals only with a short name if the
+hostname of the client is a short name. This would fail is
+Kerberos/GSSAPI clients will use the fully-qualified domain name (FQDN)
+to access the host.
+
+With this patch adcli tries to expand the short name by calling
+getaddrinfo with the AI_CANONNAME hint.
+
+Related to https://gitlab.freedesktop.org/realmd/adcli/issues/1
+---
+ doc/adcli.xml    |  6 +++++-
+ library/adconn.c | 30 +++++++++++++++++++++++++++++-
+ 2 files changed, 34 insertions(+), 2 deletions(-)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index 97dec08..4722c3a 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -228,7 +228,11 @@ Password for Administrator:
+ 			<term><option>-H, --host-fqdn=<parameter>host</parameter></option></term>
+ 			<listitem><para>Override the local machine's fully qualified
+ 			domain name. If not specified, the local machine's hostname
+-			will be retrieved via <function>gethostname()</function>.</para></listitem>
++			will be retrieved via <function>gethostname()</function>.
++			If <function>gethostname()</function> only returns a short name
++			<function>getaddrinfo()</function> with the AI_CANONNAME hint
++			is called to expand the name to a fully qualified domain
++			name.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>-K, --host-keytab=<parameter>/path/to/keytab</parameter></option></term>
+diff --git a/library/adconn.c b/library/adconn.c
+index e2250e3..f6c23d3 100644
+--- a/library/adconn.c
++++ b/library/adconn.c
+@@ -86,11 +86,36 @@ struct _adcli_conn_ctx {
+ 	krb5_keytab keytab;
+ };
+ 
++static char *try_to_get_fqdn (const char *host_name)
++{
++	int ret;
++	char *fqdn = NULL;
++	struct addrinfo *res;
++	struct addrinfo hints;
++
++	memset (&hints, 0, sizeof (struct addrinfo));
++	hints.ai_socktype = SOCK_DGRAM;
++	hints.ai_flags = AI_CANONNAME;
++
++	ret = getaddrinfo (host_name, NULL, &hints, &res);
++	if (ret != 0) {
++		_adcli_err ("Failed to find FQDN: %s", gai_strerror (ret));
++		return NULL;
++	}
++
++	fqdn = strdup (res->ai_canonname);
++
++	freeaddrinfo (res);
++
++	return fqdn;
++}
++
+ static adcli_result
+ ensure_host_fqdn (adcli_result res,
+                   adcli_conn *conn)
+ {
+ 	char hostname[HOST_NAME_MAX + 1];
++	char *fqdn = NULL;
+ 	int ret;
+ 
+ 	if (res != ADCLI_SUCCESS)
+@@ -107,7 +132,10 @@ ensure_host_fqdn (adcli_result res,
+ 		return ADCLI_ERR_UNEXPECTED;
+ 	}
+ 
+-	conn->host_fqdn = strdup (hostname);
++	if (strchr (hostname, '.') == NULL) {
++		fqdn = try_to_get_fqdn (hostname);
++	}
++	conn->host_fqdn = fqdn != NULL ? fqdn : strdup (hostname);
+ 	return_unexpected_if_fail (conn->host_fqdn != NULL);
+ 	return ADCLI_SUCCESS;
+ }
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-tools-remove-errx-from-computer-commands.patch b/SOURCES/0001-tools-remove-errx-from-computer-commands.patch
new file mode 100644
index 0000000..71db611
--- /dev/null
+++ b/SOURCES/0001-tools-remove-errx-from-computer-commands.patch
@@ -0,0 +1,328 @@
+From fa7926c7a9d92bc7c42c610ba6f1706c635aa901 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 15 Apr 2019 17:54:27 +0200
+Subject: [PATCH 1/7] tools: remove errx from computer commands
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596
+---
+ tools/computer.c | 166 ++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 107 insertions(+), 59 deletions(-)
+
+diff --git a/tools/computer.c b/tools/computer.c
+index bee695c..9cbbb28 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -379,8 +379,10 @@ adcli_tool_computer_join (adcli_conn *conn,
+ 	};
+ 
+ 	enroll = adcli_enroll_new (conn);
+-	if (enroll == NULL)
+-		errx (-1, "unexpected memory problems");
++	if (enroll == NULL) {
++		warnx ("unexpected memory problems");
++		return -1;
++	}
+ 
+ 	while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) {
+ 		switch (opt) {
+@@ -415,21 +417,28 @@ adcli_tool_computer_join (adcli_conn *conn,
+ 
+ 	if (argc == 1)
+ 		adcli_conn_set_domain_name (conn, argv[0]);
+-	else if (argc > 1)
+-		errx (2, "extra arguments specified");
++	else if (argc > 1) {
++		warnx ("extra arguments specified");
++		adcli_enroll_unref (enroll);
++		return 2;
++	}
+ 
+ 	res = adcli_conn_connect (conn);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "couldn't connect to %s domain: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't connect to %s domain: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_enroll_unref (enroll);
++		return -res;
+ 	}
+ 
+ 	res = adcli_enroll_join (enroll, flags);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "joining domain %s failed: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("joining domain %s failed: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_enroll_unref (enroll);
++		return -res;
+ 	}
+ 
+ 	if (details)
+@@ -486,8 +495,10 @@ adcli_tool_computer_update (adcli_conn *conn,
+ 	};
+ 
+ 	enroll = adcli_enroll_new (conn);
+-	if (enroll == NULL)
+-		errx (-1, "unexpected memory problems");
++	if (enroll == NULL) {
++		warnx ("unexpected memory problems");
++		return -1;
++	}
+ 
+ 	while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) {
+ 		switch (opt) {
+@@ -525,22 +536,28 @@ adcli_tool_computer_update (adcli_conn *conn,
+ 
+ 	res = adcli_enroll_load (enroll);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "couldn't lookup domain info from keytab: %s",
+-		      adcli_get_last_error ());
++		warnx ("couldn't lookup domain info from keytab: %s",
++		       adcli_get_last_error ());
++		adcli_enroll_unref (enroll);
++		return -res;
+ 	}
+ 
+ 	res = adcli_conn_connect (conn);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "couldn't connect to %s domain: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't connect to %s domain: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_enroll_unref (enroll);
++		return -res;
+ 	}
+ 
+ 	res = adcli_enroll_update (enroll, flags);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "updating membership with domain %s failed: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("updating membership with domain %s failed: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_enroll_unref (enroll);
++		return -res;
+ 	}
+ 
+ 	if (details)
+@@ -578,8 +595,10 @@ adcli_tool_computer_testjoin (adcli_conn *conn,
+ 	};
+ 
+ 	enroll = adcli_enroll_new (conn);
+-	if (enroll == NULL)
+-		errx (-1, "unexpected memory problems");
++	if (enroll == NULL) {
++		warnx ("unexpected memory problems");
++		return -1;
++	}
+ 
+ 	while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) {
+ 		switch (opt) {
+@@ -604,18 +623,18 @@ adcli_tool_computer_testjoin (adcli_conn *conn,
+ 	res = adcli_enroll_load (enroll);
+ 	if (res != ADCLI_SUCCESS) {
+ 		adcli_enroll_unref (enroll);
+-		adcli_conn_unref (conn);
+-		errx (-res, "couldn't lookup domain info from keytab: %s",
+-		      adcli_get_last_error ());
++		warnx ("couldn't lookup domain info from keytab: %s",
++		       adcli_get_last_error ());
++		return -res;
+ 	}
+ 
+ 	res = adcli_conn_connect (conn);
+ 	if (res != ADCLI_SUCCESS) {
+ 		adcli_enroll_unref (enroll);
+-		adcli_conn_unref (conn);
+-		errx (-res, "couldn't connect to %s domain: %s",
++		warnx ("couldn't connect to %s domain: %s",
+ 		      adcli_conn_get_domain_name (conn),
+ 		      adcli_get_last_error ());
++		return -res;
+ 	}
+ 
+ 	printf ("Sucessfully validated join to domain %s\n",
+@@ -665,8 +684,10 @@ adcli_tool_computer_preset (adcli_conn *conn,
+ 	};
+ 
+ 	enroll = adcli_enroll_new (conn);
+-	if (enroll == NULL)
+-		errx (-1, "unexpected memory problems");
++	if (enroll == NULL) {
++		warnx ("unexpected memory problems");
++		return -1;
++	}
+ 	flags = ADCLI_ENROLL_NO_KEYTAB;
+ 
+ 	while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) {
+@@ -694,17 +715,22 @@ adcli_tool_computer_preset (adcli_conn *conn,
+ 	argc -= optind;
+ 	argv += optind;
+ 
+-	if (argc < 1)
+-		errx (EUSAGE, "specify one or more host names of computer accounts to preset");
++	if (argc < 1) {
++		warnx ("specify one or more host names of computer accounts to preset");
++		adcli_enroll_unref (enroll);
++		return EUSAGE;
++	}
+ 
+ 	adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT);
+ 	reset_password = (adcli_enroll_get_computer_password (enroll) == NULL);
+ 
+ 	res = adcli_conn_connect (conn);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "couldn't connect to %s domain: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't connect to %s domain: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_enroll_unref (enroll);
++		return -res;
+ 	}
+ 
+ 	for (i = 0; i < argc; i++) {
+@@ -715,9 +741,11 @@ adcli_tool_computer_preset (adcli_conn *conn,
+ 
+ 		res = adcli_enroll_join (enroll, flags);
+ 		if (res != ADCLI_SUCCESS) {
+-			errx (-res, "presetting %s in %s domain failed: %s", argv[i],
+-			      adcli_conn_get_domain_name (conn),
+-			      adcli_get_last_error ());
++			warnx ("presetting %s in %s domain failed: %s", argv[i],
++			       adcli_conn_get_domain_name (conn),
++			       adcli_get_last_error ());
++			adcli_enroll_unref (enroll);
++			return -res;
+ 		}
+ 
+ 		printf ("computer-name: %s\n", adcli_enroll_get_computer_name (enroll));
+@@ -758,8 +786,10 @@ adcli_tool_computer_reset (adcli_conn *conn,
+ 	};
+ 
+ 	enroll = adcli_enroll_new (conn);
+-	if (enroll == NULL)
+-		errx (-1, "unexpected memory problems");
++	if (enroll == NULL) {
++		warnx ("unexpected memory problems");
++		return -1;
++	}
+ 
+ 	while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) {
+ 		switch (opt) {
+@@ -779,14 +809,19 @@ adcli_tool_computer_reset (adcli_conn *conn,
+ 	argc -= optind;
+ 	argv += optind;
+ 
+-	if (argc != 1)
+-		errx (EUSAGE, "specify one host name of computer account to reset");
++	if (argc != 1) {
++		warnx ("specify one host name of computer account to reset");
++		adcli_enroll_unref (enroll);
++		return EUSAGE;
++	}
+ 
+ 	res = adcli_conn_connect (conn);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "couldn't connect to %s domain: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't connect to %s domain: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_enroll_unref (enroll);
++		return -res;
+ 	}
+ 
+ 	parse_fqdn_or_name (enroll, argv[0]);
+@@ -794,9 +829,11 @@ adcli_tool_computer_reset (adcli_conn *conn,
+ 
+ 	res = adcli_enroll_password (enroll, 0);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "resetting %s in %s domain failed: %s", argv[0],
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("resetting %s in %s domain failed: %s", argv[0],
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_enroll_unref (enroll);
++		return -res;
+ 	}
+ 
+ 	adcli_enroll_unref (enroll);
+@@ -832,8 +869,10 @@ adcli_tool_computer_delete (adcli_conn *conn,
+ 	};
+ 
+ 	enroll = adcli_enroll_new (conn);
+-	if (enroll == NULL)
+-		errx (-1, "unexpected memory problems");
++	if (enroll == NULL) {
++		warnx ("unexpected memory problems");
++		return -1;
++	}
+ 
+ 	while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) {
+ 		switch (opt) {
+@@ -853,22 +892,29 @@ adcli_tool_computer_delete (adcli_conn *conn,
+ 	argc -= optind;
+ 	argv += optind;
+ 
+-	if (argc > 1)
+-		errx (EUSAGE, "specify one host name of computer account to delete");
++	if (argc > 1) {
++		warnx ("specify one host name of computer account to delete");
++		adcli_enroll_unref (enroll);
++		return EUSAGE;
++	}
+ 
+ 	adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT);
+ 
+ 	res = adcli_enroll_load (enroll);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "couldn't lookup domain info from keytab: %s",
+-		      adcli_get_last_error ());
++		warnx ("couldn't lookup domain info from keytab: %s",
++		       adcli_get_last_error ());
++		adcli_enroll_unref (enroll);
++		return -res;
+ 	}
+ 
+ 	res = adcli_conn_connect (conn);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "couldn't connect to %s domain: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't connect to %s domain: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_enroll_unref (enroll);
++		return -res;
+ 	}
+ 
+ 	if (argc == 1)
+@@ -876,9 +922,11 @@ adcli_tool_computer_delete (adcli_conn *conn,
+ 
+ 	res = adcli_enroll_delete (enroll, 0);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "deleting %s in %s domain failed: %s", argv[0],
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("deleting %s in %s domain failed: %s", argv[0],
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_enroll_unref (enroll);
++		return -res;
+ 	}
+ 
+ 	adcli_enroll_unref (enroll);
+-- 
+2.20.1
+
diff --git a/SOURCES/0002-Only-update-attributes-given-on-the-command-line.patch b/SOURCES/0002-Only-update-attributes-given-on-the-command-line.patch
new file mode 100644
index 0000000..d69ff8a
--- /dev/null
+++ b/SOURCES/0002-Only-update-attributes-given-on-the-command-line.patch
@@ -0,0 +1,126 @@
+From 5eca1f995ced1ce4ddead4471ac7ac9037bedb73 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 1 Jun 2018 21:26:47 +0200
+Subject: [PATCH 2/7] Only update attributes given on the command line
+
+When updating attributes of the LDAP computer object we only want to
+update attributes which are related to options given on the command
+line. Otherwise a simple call of 'adcli update' to check if the machine
+account password needs an update might unexpectedly reset other
+attributes as well.
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1547013
+           https://bugzilla.redhat.com/show_bug.cgi?id=1545568
+           https://bugzilla.redhat.com/show_bug.cgi?id=1538730
+---
+ library/adenroll.c | 35 ++++++++++++++++++++++++++++++-----
+ 1 file changed, 30 insertions(+), 5 deletions(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 7c59078..2be6796 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -99,8 +99,11 @@ struct _adcli_enroll {
+ 	int user_princpal_generate;
+ 
+ 	char *os_name;
++	int os_name_explicit;
+ 	char *os_version;
++	int os_version_explicit;
+ 	char *os_service_pack;
++	int os_service_pack_explicit;
+ 
+ 	krb5_kvno kvno;
+ 	char *keytab_name;
+@@ -113,6 +116,7 @@ struct _adcli_enroll {
+ 	int computer_password_lifetime_explicit;
+ 	char *samba_data_tool;
+ 	bool trusted_for_delegation;
++	int trusted_for_delegation_explicit;
+ };
+ 
+ static adcli_result
+@@ -1212,7 +1216,11 @@ update_computer_account (adcli_enroll *enroll)
+ 	ldap = adcli_conn_get_ldap_connection (enroll->conn);
+ 	return_if_fail (ldap != NULL);
+ 
+-	{
++	/* Only update attributes which are explicitly given on the command
++	 * line. Otherwise 'adcli update' must be always called with the same
++	 * set of options to make sure existing attributes are not deleted or
++	 * overwritten with different values. */
++	if (enroll->host_fqdn_explicit) {
+ 		char *vals_dNSHostName[] = { enroll->host_fqdn, NULL };
+ 		LDAPMod dNSHostName = { LDAP_MOD_REPLACE, "dNSHostName", { vals_dNSHostName, } };
+ 		LDAPMod *mods[] = { &dNSHostName, NULL };
+@@ -1220,7 +1228,7 @@ update_computer_account (adcli_enroll *enroll)
+ 		res |= update_computer_attribute (enroll, ldap, mods);
+ 	}
+ 
+-	if (res == ADCLI_SUCCESS) {
++	if (res == ADCLI_SUCCESS && enroll->trusted_for_delegation_explicit) {
+ 		char *vals_userAccountControl[] = { NULL , NULL };
+ 		LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } };
+ 		LDAPMod *mods[] = { &userAccountControl, NULL };
+@@ -1240,12 +1248,25 @@ update_computer_account (adcli_enroll *enroll)
+ 		LDAPMod operatingSystemVersion = { LDAP_MOD_REPLACE, "operatingSystemVersion", { vals_operatingSystemVersion, } };
+ 		char *vals_operatingSystemServicePack[] = { enroll->os_service_pack, NULL };
+ 		LDAPMod operatingSystemServicePack = { LDAP_MOD_REPLACE, "operatingSystemServicePack", { vals_operatingSystemServicePack, } };
+-		LDAPMod *mods[] = { &operatingSystem, &operatingSystemVersion, &operatingSystemServicePack, NULL };
++		LDAPMod *mods[] = { NULL, NULL, NULL, NULL };
++		size_t c = 0;
+ 
+-		res |= update_computer_attribute (enroll, ldap, mods);
++		if (enroll->os_name_explicit) {
++			mods[c++] = &operatingSystem;
++		}
++		if (enroll->os_version_explicit) {
++			mods[c++] = &operatingSystemVersion;
++		}
++		if (enroll->os_service_pack_explicit) {
++			mods[c++] = &operatingSystemServicePack;
++		}
++
++		if (c != 0) {
++			res |= update_computer_attribute (enroll, ldap, mods);
++		}
+ 	}
+ 
+-	if (res == ADCLI_SUCCESS) {
++	if (res == ADCLI_SUCCESS && !enroll->user_princpal_generate) {
+ 		char *vals_userPrincipalName[] = { enroll->user_principal, NULL };
+ 		LDAPMod userPrincipalName = { LDAP_MOD_REPLACE, "userPrincipalName", { vals_userPrincipalName, }, };
+ 		LDAPMod *mods[] = { &userPrincipalName, NULL, };
+@@ -2337,6 +2358,7 @@ adcli_enroll_set_os_name (adcli_enroll *enroll,
+ 	if (value && value[0] == '\0')
+ 		value = NULL;
+ 	_adcli_str_set (&enroll->os_name, value);
++	enroll->os_name_explicit = 1;
+ }
+ 
+ const char *
+@@ -2354,6 +2376,7 @@ adcli_enroll_set_os_version (adcli_enroll *enroll,
+ 	if (value && value[0] == '\0')
+ 		value = NULL;
+ 	_adcli_str_set (&enroll->os_version, value);
++	enroll->os_version_explicit = 1;
+ }
+ 
+ const char *
+@@ -2371,6 +2394,7 @@ adcli_enroll_set_os_service_pack (adcli_enroll *enroll,
+ 	if (value && value[0] == '\0')
+ 		value = NULL;
+ 	_adcli_str_set (&enroll->os_service_pack, value);
++	enroll->os_service_pack_explicit = 1;
+ }
+ 
+ const char *
+@@ -2450,4 +2474,5 @@ adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll,
+ 	return_if_fail (enroll != NULL);
+ 
+ 	enroll->trusted_for_delegation = value;
++	enroll->trusted_for_delegation_explicit = 1;
+ }
+-- 
+2.14.4
+
diff --git a/SOURCES/0002-Patch-to-adcli.xml-to-fix-documentation-error.patch b/SOURCES/0002-Patch-to-adcli.xml-to-fix-documentation-error.patch
new file mode 100644
index 0000000..69734f3
--- /dev/null
+++ b/SOURCES/0002-Patch-to-adcli.xml-to-fix-documentation-error.patch
@@ -0,0 +1,36 @@
+From 83230282a3b8999d83afb04563589b38420cfa0e Mon Sep 17 00:00:00 2001
+From: Gerard Weatherby <GWeatherby@uchc.edu>
+Date: Thu, 28 Jan 2016 22:27:00 +0000
+Subject: [PATCH 2/3] Patch to adcli.xml to fix documentation error
+
+https://bugs.freedesktop.org/show_bug.cgi?id=93913
+---
+ doc/adcli.xml | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index d8f30a3..a83e321 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -428,14 +428,14 @@ $ adcli create-user Fry --domain=domain.example.com \
+ 		<varlistentry>
+ 			<term><option>--unix-shell=<parameter>/bin/shell</parameter></option></term>
+ 			<listitem><para>Set the <code>loginShell</code> attribute of
+-			the new created user account, which should be the user's
+-			numeric primary user id.</para></listitem>
++			the new created user account, which should be a path to
++			a valid shell.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>--unix-uid=<parameter>111</parameter></option></term>
+ 			<listitem><para>Set the <code>uidNumber</code> attribute of
+-			the new created user account, which should be a path to
+-			a valid shell.</para></listitem>
++			the new created user account, which should be the user's
++			numeric primary user id.</para></listitem>
+ 		</varlistentry>
+ 	</variablelist>
+ 
+-- 
+2.5.5
+
diff --git a/SOURCES/0002-Use-strdup-if-offset-are-used.patch b/SOURCES/0002-Use-strdup-if-offset-are-used.patch
new file mode 100644
index 0000000..5b1c2ed
--- /dev/null
+++ b/SOURCES/0002-Use-strdup-if-offset-are-used.patch
@@ -0,0 +1,31 @@
+From 0b24b7ece288641f1d5d6844d1a42ff30b16511a Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 24 Aug 2016 16:19:36 +0200
+Subject: [PATCH 2/2] Use strdup() if offset are used
+
+Strings with an offset to the original starting point must be copied
+because otherwise they cannot be properly freed later.
+---
+ library/adenroll.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index d1020e9..05885d0 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -1318,9 +1318,9 @@ load_keytab_entry (krb5_context k5,
+ 
+ 		} else if (!enroll->host_fqdn && _adcli_str_has_prefix (name, "host/") && strchr (name, '.')) {
+ 			/* Skip host/ prefix */
+-			enroll->host_fqdn = name + 5;
+-			_adcli_info ("Found host qualified name in keytab: %s", name);
+-			name = NULL;
++			enroll->host_fqdn = strdup (name + 5);
++			return_val_if_fail (enroll->host_fqdn != NULL, FALSE);
++			_adcli_info ("Found host qualified name in keytab: %s", enroll->host_fqdn);
+ 		}
+ 	}
+ 
+-- 
+2.7.4
+
diff --git a/SOURCES/0002-_adcli_call_external_program-silence-noisy-debug-mes.patch b/SOURCES/0002-_adcli_call_external_program-silence-noisy-debug-mes.patch
new file mode 100644
index 0000000..76fe1dc
--- /dev/null
+++ b/SOURCES/0002-_adcli_call_external_program-silence-noisy-debug-mes.patch
@@ -0,0 +1,25 @@
+From 8cc4ef1cae7d4d753f2cf9aeb8021dd96cb75d36 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 8 Aug 2018 12:17:18 +0200
+Subject: [PATCH 2/4] _adcli_call_external_program: silence noisy debug message
+
+---
+ library/adutil.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/library/adutil.c b/library/adutil.c
+index 6334b52..17d2caa 100644
+--- a/library/adutil.c
++++ b/library/adutil.c
+@@ -672,7 +672,7 @@ done:
+ 		if (wret == -1) {
+ 			_adcli_err ("No sure what happend to net command.");
+ 		} else {
+-			if (WIFEXITED (status)) {
++			if (WIFEXITED (status) && WEXITSTATUS (status) != 0) {
+ 				_adcli_err ("net command failed with %d.",
+ 				            WEXITSTATUS (status));
+ 			}
+-- 
+2.17.1
+
diff --git a/SOURCES/0002-adconn-add-adcli_conn_set_krb5_context.patch b/SOURCES/0002-adconn-add-adcli_conn_set_krb5_context.patch
new file mode 100644
index 0000000..769a022
--- /dev/null
+++ b/SOURCES/0002-adconn-add-adcli_conn_set_krb5_context.patch
@@ -0,0 +1,52 @@
+From 2fc259a88be618871cea8ff8b8a13bd3e040aea4 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 13 Jun 2019 17:23:47 +0200
+Subject: [PATCH 2/4] adconn: add adcli_conn_set_krb5_context
+
+Related to https://gitlab.freedesktop.org/realmd/adcli/issues/3
+---
+ library/adconn.c | 13 +++++++++++++
+ library/adconn.h |  3 +++
+ 2 files changed, 16 insertions(+)
+
+diff --git a/library/adconn.c b/library/adconn.c
+index f6c23d3..bcaced8 100644
+--- a/library/adconn.c
++++ b/library/adconn.c
+@@ -1406,6 +1406,19 @@ adcli_conn_get_krb5_context (adcli_conn *conn)
+ 	return conn->k5;
+ }
+ 
++void
++adcli_conn_set_krb5_context (adcli_conn *conn,
++                             krb5_context k5)
++{
++	return_if_fail (conn != NULL);
++
++	if (conn->k5 != NULL) {
++		krb5_free_context (conn->k5);
++	}
++
++	conn->k5 = k5;
++}
++
+ const char *
+ adcli_conn_get_login_user (adcli_conn *conn)
+ {
+diff --git a/library/adconn.h b/library/adconn.h
+index 13cfd32..1ad5715 100644
+--- a/library/adconn.h
++++ b/library/adconn.h
+@@ -97,6 +97,9 @@ LDAP *              adcli_conn_get_ldap_connection   (adcli_conn *conn);
+ 
+ krb5_context        adcli_conn_get_krb5_context      (adcli_conn *conn);
+ 
++void                adcli_conn_set_krb5_context      (adcli_conn *conn,
++                                                      krb5_context k5);
++
+ const char *        adcli_conn_get_computer_name     (adcli_conn *conn);
+ 
+ void                adcli_conn_set_computer_name     (adcli_conn *conn,
+-- 
+2.21.0
+
diff --git a/SOURCES/0002-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch b/SOURCES/0002-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch
new file mode 100644
index 0000000..e4a6bc5
--- /dev/null
+++ b/SOURCES/0002-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch
@@ -0,0 +1,83 @@
+From 0c027538f398b3823bedbfbf5f388ad97784a0ec Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 16 Nov 2018 13:32:59 +0100
+Subject: [PATCH 2/2] adenroll: use _adcli_strv_add_unique for service
+ principals
+
+Check if service principals is already in the list before adding it.
+
+Related to https://gitlab.freedesktop.org/realmd/adcli/issues/16
+---
+ library/adenroll.c | 31 ++++++++-----------------------
+ 1 file changed, 8 insertions(+), 23 deletions(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index de2242a..e02f403 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -313,7 +313,6 @@ add_service_names_to_service_principals (adcli_enroll *enroll)
+ 	char *name;
+ 	int length = 0;
+ 	int i;
+-	size_t c;
+ 
+ 	if (enroll->service_principals != NULL) {
+ 		length = seq_count (enroll->service_principals);
+@@ -322,28 +321,14 @@ add_service_names_to_service_principals (adcli_enroll *enroll)
+ 	for (i = 0; enroll->service_names[i] != NULL; i++) {
+ 		if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0)
+ 			return_unexpected_if_reached ();
+-		for (c = 0; enroll->service_principals != NULL && enroll->service_principals[c] != NULL; c++) {
+-			if (strcmp (name, enroll->service_principals[c]) == 0) {
+-				break;
+-			}
+-		}
+-		if (enroll->service_principals == NULL || enroll->service_principals[c] == NULL) {
+-			enroll->service_principals = _adcli_strv_add (enroll->service_principals,
+-				                                      name, &length);
+-		}
++		enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals,
++		                                                     name, &length, false);
+ 
+ 		if (enroll->host_fqdn) {
+ 			if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0)
+ 				return_unexpected_if_reached ();
+-			for (c = 0; enroll->service_principals != NULL && enroll->service_principals[c] != NULL; c++) {
+-				if (strcmp (name, enroll->service_principals[c]) == 0) {
+-					break;
+-				}
+-			}
+-			if (enroll->service_principals == NULL || enroll->service_principals[c] == NULL) {
+-				enroll->service_principals = _adcli_strv_add (enroll->service_principals,
+-					                                      name, &length);
+-			}
++			enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals,
++			                                                     name, &length, false);
+ 		}
+ 	}
+ 
+@@ -364,9 +349,9 @@ add_and_remove_service_principals (adcli_enroll *enroll)
+ 	list = adcli_enroll_get_service_principals_to_add (enroll);
+ 	if (list != NULL) {
+ 		for (c = 0; list[c] != NULL; c++) {
+-			enroll->service_principals = _adcli_strv_add (enroll->service_principals,
+-			                                              strdup (list[c]),
+-			                                              &length);
++			enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals,
++			                                                     strdup (list[c]),
++			                                                     &length, false);
+ 			if (enroll->service_principals == NULL) {
+ 				return ADCLI_ERR_UNEXPECTED;
+ 			}
+@@ -1525,7 +1510,7 @@ load_keytab_entry (krb5_context k5,
+ 			value = strdup (name);
+ 			return_val_if_fail (value != NULL, FALSE);
+ 			_adcli_info ("Found service principal in keytab: %s", value);
+-			enroll->service_principals = _adcli_strv_add (enroll->service_principals, value, NULL);
++			enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals, value, NULL, false);
+ 		}
+ 	}
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0002-create-user-try-to-find-NIS-domain-if-needed.patch b/SOURCES/0002-create-user-try-to-find-NIS-domain-if-needed.patch
new file mode 100644
index 0000000..0ecdc90
--- /dev/null
+++ b/SOURCES/0002-create-user-try-to-find-NIS-domain-if-needed.patch
@@ -0,0 +1,147 @@
+From 408880a11879b1a57a450e25c77ef2e310bdffd5 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 18 Mar 2019 16:45:54 +0100
+Subject: [PATCH 2/2] create-user: try to find NIS domain if needed
+
+Related to https://gitlab.freedesktop.org/realmd/adcli/issues/2
+---
+ doc/adcli.xml     |  4 +++-
+ library/adentry.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
+ library/adentry.h |  2 ++
+ tools/entry.c     | 16 ++++++++++++++++
+ 4 files changed, 65 insertions(+), 1 deletion(-)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index 18620c0..af73433 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -537,7 +537,9 @@ $ adcli create-user Fry --domain=domain.example.com \
+ 			the new created user account, which should be the user's
+ 			NIS domain is the NIS/YP service of Active Directory's Services for Unix (SFU)
+ 			are used. This is needed to let the 'UNIX attributes' tab of older Active
+-			Directoy versions show the set UNIX specific attributes.</para></listitem>
++			Directoy versions show the set UNIX specific attributes. If not specified
++			adcli will try to determine the NIS domain automatically if needed.
++			</para></listitem>
+ 		</varlistentry>
+ 	</variablelist>
+ 
+diff --git a/library/adentry.c b/library/adentry.c
+index 9b9e1c6..1cc0518 100644
+--- a/library/adentry.c
++++ b/library/adentry.c
+@@ -484,3 +484,47 @@ adcli_entry_new_group (adcli_conn *conn,
+ 	return_val_if_fail (sam_name != NULL, NULL);
+ 	return entry_new (conn, "group", group_entry_builder, sam_name);
+ }
++
++adcli_result
++adcli_get_nis_domain (adcli_entry *entry,
++                      adcli_attrs *attrs)
++{
++	LDAP *ldap;
++	const char *ldap_attrs[] = { "cn", NULL };
++	LDAPMessage *results;
++	LDAPMessage *ldap_entry;
++	char *base;
++	const char *filter = "objectClass=msSFU30DomainInfo";
++	char *cn;
++	int ret;
++
++	ldap = adcli_conn_get_ldap_connection (entry->conn);
++	return_unexpected_if_fail (ldap != NULL);
++
++	if (asprintf (&base, "CN=ypservers,CN=ypServ30,CN=RpcServices,CN=System,%s",
++	              adcli_conn_get_default_naming_context (entry->conn)) < 0) {
++		return_unexpected_if_reached ();
++	}
++
++	ret = ldap_search_ext_s (ldap, base, LDAP_SCOPE_SUB, filter, (char **)ldap_attrs,
++	                         0, NULL, NULL, NULL, -1, &results);
++
++	free (base);
++
++	if (ret != LDAP_SUCCESS) {
++		/* No NIS domain available */
++		ldap_msgfree (results);
++		return ADCLI_SUCCESS;
++	}
++
++	ldap_entry = ldap_first_entry (ldap, results);
++	if (ldap_entry != NULL) {
++		cn = _adcli_ldap_parse_value (ldap, ldap_entry, "cn");
++		return_unexpected_if_fail (cn != NULL);
++
++		adcli_attrs_add (attrs, "msSFU30NisDomain", cn, NULL);
++	}
++	ldap_msgfree (results);
++
++	return ADCLI_SUCCESS;
++}
+diff --git a/library/adentry.h b/library/adentry.h
+index eb8bc00..ae90689 100644
+--- a/library/adentry.h
++++ b/library/adentry.h
+@@ -58,4 +58,6 @@ const char *       adcli_entry_get_sam_name             (adcli_entry *entry);
+ 
+ const char *       adcli_entry_get_dn                   (adcli_entry *entry);
+ 
++adcli_result       adcli_get_nis_domain                 (adcli_entry *entry,
++                                                         adcli_attrs *attrs);
+ #endif /* ADENTRY_H_ */
+diff --git a/tools/entry.c b/tools/entry.c
+index 69ce62c..de56586 100644
+--- a/tools/entry.c
++++ b/tools/entry.c
+@@ -153,6 +153,8 @@ adcli_tool_user_create (adcli_conn *conn,
+ 	adcli_attrs *attrs;
+ 	const char *ou = NULL;
+ 	int opt;
++	bool has_unix_attr = false;
++	bool has_nis_domain = false;
+ 
+ 	struct option options[] = {
+ 		{ "display-name", required_argument, NULL, opt_display_name },
+@@ -193,18 +195,23 @@ adcli_tool_user_create (adcli_conn *conn,
+ 			break;
+ 		case opt_unix_home:
+ 			adcli_attrs_add (attrs, "unixHomeDirectory", optarg, NULL);
++			has_unix_attr = true;
+ 			break;
+ 		case opt_unix_uid:
+ 			adcli_attrs_add (attrs, "uidNumber", optarg, NULL);
++			has_unix_attr = true;
+ 			break;
+ 		case opt_unix_gid:
+ 			adcli_attrs_add (attrs, "gidNumber", optarg, NULL);
++			has_unix_attr = true;
+ 			break;
+ 		case opt_unix_shell:
+ 			adcli_attrs_add (attrs, "loginShell", optarg, NULL);
++			has_unix_attr = true;
+ 			break;
+ 		case opt_nis_domain:
+ 			adcli_attrs_add (attrs, "msSFU30NisDomain", optarg, NULL);
++			has_nis_domain = true;
+ 			break;
+ 		case opt_domain_ou:
+ 			ou = optarg;
+@@ -242,6 +249,15 @@ adcli_tool_user_create (adcli_conn *conn,
+ 		      adcli_get_last_error ());
+ 	}
+ 
++	if (has_unix_attr && !has_nis_domain) {
++		res = adcli_get_nis_domain (entry, attrs);
++		if (res != ADCLI_SUCCESS) {
++			adcli_entry_unref (entry);
++			adcli_attrs_free (attrs);
++			errx (-res, "couldn't get NIS domain");
++		}
++	}
++
+ 	res = adcli_entry_create (entry, attrs);
+ 	if (res != ADCLI_SUCCESS) {
+ 		errx (-res, "creating user %s in domain %s failed: %s",
+-- 
+2.20.1
+
diff --git a/SOURCES/0002-doc-explain-that-all-credential-cache-types-are-supp.patch b/SOURCES/0002-doc-explain-that-all-credential-cache-types-are-supp.patch
new file mode 100644
index 0000000..39167a9
--- /dev/null
+++ b/SOURCES/0002-doc-explain-that-all-credential-cache-types-are-supp.patch
@@ -0,0 +1,37 @@
+From 7e71dd109601d3be09cdda37cd4bf99ba867ce19 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 1 Nov 2017 12:01:18 +0100
+Subject: [PATCH 2/4] doc: explain that all credential cache types are
+ supported
+
+---
+ doc/adcli.xml | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index e18ba5d..c54cc1b 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -118,11 +118,15 @@
+ 			is automatically discovered.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+-			<term><option>-C, --login-ccache=<parameter>/path/to/file</parameter></option></term>
++			<term><option>-C, --login-ccache=<parameter>ccache_name</parameter></option></term>
+ 			<listitem><para>Use the specified kerberos credential
+-			cache to authenticate with the domain. If no file is specified or
+-			<option>-C</option> is used, then the default kerberos credential cache will
+-			be used.</para></listitem>
++                        cache to authenticate with the domain. If no credential
++                        cache is specified, the default kerberos credential
++                        cache will be used. Credential caches of type FILE can
++                        be given with the path to the file. For other
++                        credential cache types, e.g. DIR, KEYRING or KCM, the
++                        type must be specified explicitly together with a
++                        suitable identifier.</para></listitem>
+ 		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>-U, --login-user=<parameter>User</parameter></option></term>
+-- 
+2.13.6
+
diff --git a/SOURCES/0002-library-add-_adcli_bin_sid_to_str.patch b/SOURCES/0002-library-add-_adcli_bin_sid_to_str.patch
new file mode 100644
index 0000000..4fad98c
--- /dev/null
+++ b/SOURCES/0002-library-add-_adcli_bin_sid_to_str.patch
@@ -0,0 +1,178 @@
+From 0d0c1bf9721ba523d8c3ac584bdbb9a8ffdddee7 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 30 Jan 2018 14:37:05 +0100
+Subject: [PATCH 2/9] library: add _adcli_bin_sid_to_str()
+
+Convert a binary SID to the string representation.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=100118
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ library/adprivate.h |   4 ++
+ library/adutil.c    | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 117 insertions(+)
+
+diff --git a/library/adprivate.h b/library/adprivate.h
+index fc146af..e99f9fc 100644
+--- a/library/adprivate.h
++++ b/library/adprivate.h
+@@ -31,6 +31,7 @@
+ #include <limits.h>
+ #include <stdlib.h>
+ #include <stdio.h>
++#include <stdint.h>
+ 
+ #include <ldap.h>
+ 
+@@ -132,6 +133,9 @@ int            _adcli_str_has_prefix         (const char *str,
+ int            _adcli_str_has_suffix         (const char *str,
+ 		                              const char *suffix);
+ 
++char *          _adcli_bin_sid_to_str        (const uint8_t *data,
++                                              size_t len);
++
+ char *         _adcli_str_dupn               (void *data,
+                                               size_t len);
+ 
+diff --git a/library/adutil.c b/library/adutil.c
+index cd40f45..829cdd9 100644
+--- a/library/adutil.c
++++ b/library/adutil.c
+@@ -293,6 +293,83 @@ _adcli_strv_set (char ***field,
+ 	*field = newval;
+ }
+ 
++char *
++_adcli_bin_sid_to_str (const uint8_t *data,
++                       size_t len)
++{
++	uint8_t sid_rev_num;
++	int8_t num_auths;
++	uint8_t id_auth[6];
++	uint32_t id_auth_val;
++	uint32_t sub_auths[15];
++	uint32_t val;
++	size_t p = 0;
++	size_t c;
++	int nc;
++	char *sid_buf;
++	size_t sid_buf_len;
++
++	if (data == NULL || len < 8) {
++		return NULL;
++	}
++
++	sid_rev_num = (uint8_t) data [p];
++	p++;
++
++	num_auths = (int8_t) data[p];
++	p++;
++
++	if (num_auths > 15 || len < 8 + (num_auths * sizeof (uint32_t))) {
++		return NULL;
++	}
++
++	for (c = 0; c < 6; c++) {
++		id_auth[c] = (uint8_t) data[p];
++		p++;
++	}
++
++	/* Only 32bits are used for the string representation */
++	id_auth_val = (id_auth[2] << 24) +
++	              (id_auth[3] << 16) +
++	              (id_auth[4] << 8) +
++	              (id_auth[5]);
++
++	for (c = 0; c < num_auths; c++) {
++		memcpy (&val, data + p, sizeof (uint32_t));
++		sub_auths[c] = le32toh (val);
++
++		p += sizeof (uint32_t);
++	}
++
++	sid_buf_len = 17 + (num_auths * 11);
++	sid_buf = calloc (1, sid_buf_len);
++	if (sid_buf == NULL) {
++		return NULL;
++	}
++
++	nc = snprintf (sid_buf, sid_buf_len, "S-%u-%lu", sid_rev_num,
++	              (unsigned long) id_auth_val);
++	if (nc < 0 || nc >= sid_buf_len) {
++		free (sid_buf);
++		return NULL;
++	}
++
++	p = 0;
++	for (c = 0; c < num_auths; c++) {
++		p += nc;
++		sid_buf_len -= nc;
++
++		nc = snprintf (sid_buf + p, sid_buf_len, "-%lu",
++		               (unsigned long) sub_auths[c]);
++		if (nc < 0 || nc >= sid_buf_len) {
++			free (sid_buf);
++			return NULL;
++		}
++	}
++
++	return sid_buf;
++}
++
+ char *
+ _adcli_str_dupn (void *data,
+                  size_t len)
+@@ -508,6 +585,41 @@ test_check_nt_time_string_lifetime (void)
+ 	assert (_adcli_check_nt_time_string_lifetime ("130645404000000000", 100000));
+ }
+ 
++static void
++test_bin_sid_to_str (void)
++{
++	uint8_t sid1[] = { 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
++	                   0x15, 0x00, 0x00, 0x00, 0xF8, 0x12, 0x13, 0xDC,
++	                   0x47, 0xF3, 0x1C, 0x76, 0x47, 0x2F, 0x2E, 0xD7,
++	                   0x51, 0x04, 0x00, 0x00 };
++
++	uint8_t sid2[] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
++	                   0x15, 0x00, 0x00, 0x00, 0xF8, 0x12, 0x13, 0xDC,
++	                   0x47, 0xF3, 0x1C, 0x76, 0x47, 0x2F, 0x2E, 0xD7};
++
++	uint8_t sid3[] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
++	                   0x15, 0x00, 0x00, 0x00, 0x29, 0xC9, 0x4F, 0xD9,
++	                   0xC2, 0x3C, 0xC3, 0x78, 0x36, 0x55, 0x87, 0xF8};
++
++
++	char *str;
++
++	str = _adcli_bin_sid_to_str (sid1, sizeof (sid1));
++	assert (str != NULL);
++	assert (strcmp (str, "S-1-5-21-3692237560-1981608775-3610128199-1105") == 0);
++	free (str);
++
++	str = _adcli_bin_sid_to_str (sid2, sizeof (sid2));
++	assert (str != NULL);
++	assert (strcmp (str, "S-1-5-21-3692237560-1981608775-3610128199") == 0);
++	free (str);
++
++	str = _adcli_bin_sid_to_str (sid3, sizeof (sid2));
++	assert (str != NULL);
++	assert (strcmp (str, "S-1-5-21-3645884713-2026060994-4169618742") == 0);
++	free (str);
++}
++
+ int
+ main (int argc,
+       char *argv[])
+@@ -516,6 +628,7 @@ main (int argc,
+ 	test_func (test_strv_dup, "/util/strv_dup");
+ 	test_func (test_strv_count, "/util/strv_count");
+ 	test_func (test_check_nt_time_string_lifetime, "/util/check_nt_time_string_lifetime");
++	test_func (test_bin_sid_to_str, "/util/bin_sid_to_str");
+ 	return test_run (argc, argv);
+ }
+ 
+-- 
+2.14.4
+
diff --git a/SOURCES/0002-library-make-_adcli_strv_has_ex-public.patch b/SOURCES/0002-library-make-_adcli_strv_has_ex-public.patch
new file mode 100644
index 0000000..db18951
--- /dev/null
+++ b/SOURCES/0002-library-make-_adcli_strv_has_ex-public.patch
@@ -0,0 +1,42 @@
+From e1b45e66bc185f5db4c252e1f3fb1b4400b4538e Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 22 Mar 2019 10:36:38 +0100
+Subject: [PATCH 2/4] library: make _adcli_strv_has_ex public
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1630187
+---
+ library/adprivate.h | 4 ++++
+ library/adutil.c    | 2 +-
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/library/adprivate.h b/library/adprivate.h
+index 0806430..55e6234 100644
+--- a/library/adprivate.h
++++ b/library/adprivate.h
+@@ -125,6 +125,10 @@ void           _adcli_strv_free              (char **strv);
+ int            _adcli_strv_has               (char **strv,
+                                               const char *str);
+ 
++int            _adcli_strv_has_ex            (char **strv,
++                                              const char *str,
++                                              int (* compare) (const char *match, const char*value));
++
+ char **        _adcli_strv_dup               (char **strv) GNUC_WARN_UNUSED;
+ 
+ char *         _adcli_strv_join              (char **strv,
+diff --git a/library/adutil.c b/library/adutil.c
+index 76ea158..9b0c47f 100644
+--- a/library/adutil.c
++++ b/library/adutil.c
+@@ -221,7 +221,7 @@ _adcli_strv_add (char **strv,
+ 	return seq_push (strv, length, string);
+ }
+ 
+-static int
++int
+ _adcli_strv_has_ex (char **strv,
+                     const char *str,
+                     int (* compare) (const char *match, const char*value))
+-- 
+2.20.1
+
diff --git a/SOURCES/0002-library-return-error-if-no-matching-key-was-found.patch b/SOURCES/0002-library-return-error-if-no-matching-key-was-found.patch
new file mode 100644
index 0000000..f9c68d6
--- /dev/null
+++ b/SOURCES/0002-library-return-error-if-no-matching-key-was-found.patch
@@ -0,0 +1,35 @@
+From 4987a21f4839ab7ea50e932c72df05075efb89b3 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 21 Mar 2019 15:05:33 +0100
+Subject: [PATCH 2/2] library: return error if no matching key was found
+
+To avoid a misleading debug message indicating success a proper erro
+code should be returned the no matching key was found when trying to
+copy an keytab entry for a new principal.
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1644311
+---
+ library/adkrb5.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/library/adkrb5.c b/library/adkrb5.c
+index 033c181..7f77373 100644
+--- a/library/adkrb5.c
++++ b/library/adkrb5.c
+@@ -298,11 +298,10 @@ _adcli_krb5_keytab_copy_entries (krb5_context k5,
+ 
+ 		code = _adcli_krb5_get_keyblock (k5, keytab, &entry.key,
+ 		                                 match_enctype_and_kvno, &closure);
+-		if (code != 0) {
+-			return code;
++		if (code != 0 || closure.matched == 0) {
++			return code != 0 ? code : ENOKEY;
+ 		}
+ 
+-
+ 		entry.principal = principal;
+ 		entry.vno = kvno;
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0002-tools-remove-errx-from-user-and-group-commands.patch b/SOURCES/0002-tools-remove-errx-from-user-and-group-commands.patch
new file mode 100644
index 0000000..36e5567
--- /dev/null
+++ b/SOURCES/0002-tools-remove-errx-from-user-and-group-commands.patch
@@ -0,0 +1,398 @@
+From cac0fa9df8888245399f2db187e05e31f93d1471 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 15 Apr 2019 17:56:37 +0200
+Subject: [PATCH 2/7] tools: remove errx from user and group commands
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596
+---
+ tools/entry.c | 232 +++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 154 insertions(+), 78 deletions(-)
+
+diff --git a/tools/entry.c b/tools/entry.c
+index de56586..97ec6e7 100644
+--- a/tools/entry.c
++++ b/tools/entry.c
+@@ -232,21 +232,30 @@ adcli_tool_user_create (adcli_conn *conn,
+ 	argc -= optind;
+ 	argv += optind;
+ 
+-	if (argc != 1)
+-		errx (2, "specify one user name to create");
++	if (argc != 1) {
++		warnx ("specify one user name to create");
++		adcli_attrs_free (attrs);
++		return 2;
++	}
+ 
+ 	entry = adcli_entry_new_user (conn, argv[0]);
+-	if (entry == NULL)
+-		errx (-1, "unexpected memory problems");
++	if (entry == NULL) {
++		warnx ("unexpected memory problems");
++		adcli_attrs_free (attrs);
++		return -1;
++	}
+ 	adcli_entry_set_domain_ou (entry, ou);
+ 
+ 	adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT);
+ 
+ 	res = adcli_conn_connect (conn);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "couldn't connect to %s domain: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't connect to %s domain: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_entry_unref (entry);
++		adcli_attrs_free (attrs);
++		return -res;
+ 	}
+ 
+ 	if (has_unix_attr && !has_nis_domain) {
+@@ -254,16 +263,20 @@ adcli_tool_user_create (adcli_conn *conn,
+ 		if (res != ADCLI_SUCCESS) {
+ 			adcli_entry_unref (entry);
+ 			adcli_attrs_free (attrs);
+-			errx (-res, "couldn't get NIS domain");
++			warnx ("couldn't get NIS domain");
++			return -res;
+ 		}
+ 	}
+ 
+ 	res = adcli_entry_create (entry, attrs);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "creating user %s in domain %s failed: %s",
+-		      adcli_entry_get_sam_name (entry),
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("creating 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);
++		adcli_attrs_free (attrs);
++		return -res;
+ 	}
+ 
+ 	adcli_entry_unref (entry);
+@@ -317,28 +330,36 @@ adcli_tool_user_delete (adcli_conn *conn,
+ 	argc -= optind;
+ 	argv += optind;
+ 
+-	if (argc != 1)
+-		errx (2, "specify one user name to delete");
++	if (argc != 1) {
++		warnx ("specify one user name to delete");
++		return 2;
++	}
+ 
+ 	entry = adcli_entry_new_user (conn, argv[0]);
+-	if (entry == NULL)
+-		errx (-1, "unexpected memory problems");
++	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) {
+-		errx (-res, "couldn't connect to %s domain: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't connect to %s domain: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_entry_unref (entry);
++		return -res;
+ 	}
+ 
+ 	res = adcli_entry_delete (entry);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "deleting user %s in domain %s failed: %s",
+-		      adcli_entry_get_sam_name (entry),
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("deleting 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);
+@@ -404,29 +425,41 @@ adcli_tool_group_create (adcli_conn *conn,
+ 	argc -= optind;
+ 	argv += optind;
+ 
+-	if (argc != 1)
+-		errx (2, "specify one group to create");
++	if (argc != 1) {
++		warnx ("specify one group to create");
++		adcli_attrs_free (attrs);
++		return 2;
++	}
+ 
+ 	entry = adcli_entry_new_group (conn, argv[0]);
+-	if (entry == NULL)
+-		errx (-1, "unexpected memory problems");
++	if (entry == NULL) {
++		warnx ("unexpected memory problems");
++		adcli_attrs_free (attrs);
++		return -1;
++	}
+ 	adcli_entry_set_domain_ou (entry, ou);
+ 
+ 	adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT);
+ 
+ 	res = adcli_conn_connect (conn);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "couldn't connect to domain %s: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't connect to domain %s: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_entry_unref (entry);
++		adcli_attrs_free (attrs);
++		return -res;
+ 	}
+ 
+ 	res = adcli_entry_create (entry, attrs);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "creating group %s in domain %s failed: %s",
+-		      adcli_entry_get_sam_name (entry),
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("creating group %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);
++		adcli_attrs_free (attrs);
++		return -res;
+ 	}
+ 
+ 	adcli_entry_unref (entry);
+@@ -480,28 +513,36 @@ adcli_tool_group_delete (adcli_conn *conn,
+ 	argc -= optind;
+ 	argv += optind;
+ 
+-	if (argc != 1)
+-		errx (2, "specify one group name to delete");
++	if (argc != 1) {
++		warnx ("specify one group name to delete");
++		return 2;
++	}
+ 
+ 	entry = adcli_entry_new_group (conn, argv[0]);
+-	if (entry == NULL)
+-		errx (-1, "unexpected memory problems");
++	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) {
+-		errx (-res, "couldn't connect to %s domain: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't connect to %s domain: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_entry_unref (entry);
++		return -res;
+ 	}
+ 
+ 	res = adcli_entry_delete (entry);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "deleting group %s in domain %s failed: %s",
+-		      adcli_entry_get_sam_name (entry),
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("deleting group %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);
+@@ -509,7 +550,7 @@ adcli_tool_group_delete (adcli_conn *conn,
+ 	return 0;
+ }
+ 
+-static void
++static int
+ expand_user_dn_as_member (adcli_conn *conn,
+                           adcli_attrs *attrs,
+                           const char *user,
+@@ -523,16 +564,19 @@ expand_user_dn_as_member (adcli_conn *conn,
+ 
+ 	res = adcli_entry_load (entry);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "couldn't lookup user %s in domain %s: %s",
+-		      user, adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't lookup user %s in domain %s: %s",
++		       user, adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_entry_unref (entry);
++		return -res;
+ 	}
+ 
+ 	dn = adcli_entry_get_dn (entry);
+ 	if (dn == NULL) {
+-		errx (-ADCLI_ERR_CONFIG,
+-		      "couldn't found user %s in domain %s",
+-		      user, adcli_conn_get_domain_name (conn));
++		warnx ("couldn't found user %s in domain %s",
++		       user, adcli_conn_get_domain_name (conn));
++		adcli_entry_unref (entry);
++		return -ADCLI_ERR_CONFIG;
+ 	}
+ 
+ 	if (adding)
+@@ -541,6 +585,8 @@ expand_user_dn_as_member (adcli_conn *conn,
+ 		adcli_attrs_delete1 (attrs, "member", dn);
+ 
+ 	adcli_entry_unref (entry);
++
++	return ADCLI_SUCCESS;
+ }
+ 
+ int
+@@ -590,33 +636,48 @@ adcli_tool_member_add (adcli_conn *conn,
+ 	argc -= optind;
+ 	argv += optind;
+ 
+-	if (argc < 2)
+-		errx (2, "specify a group name and a user to add");
++	if (argc < 2) {
++		warnx ("specify a group name and a user to add");
++		return 2;
++	}
+ 
+ 	entry = adcli_entry_new_group (conn, argv[0]);
+-	if (entry == NULL)
+-		errx (-1, "unexpected memory problems");
++	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) {
+-		errx (-res, "couldn't connect to %s domain: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't connect to %s domain: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_entry_unref (entry);
++		return -res;
+ 	}
+ 
+ 	attrs = adcli_attrs_new ();
+ 
+-	for (i = 1; i < argc; i++)
+-		expand_user_dn_as_member (conn, attrs, argv[i], 1);
++	for (i = 1; i < argc; i++) {
++		res = expand_user_dn_as_member (conn, attrs, argv[i], 1);
++		if (res != ADCLI_SUCCESS) {
++			adcli_attrs_free (attrs);
++			adcli_entry_unref (entry);
++			return res;
++		}
++	}
+ 
+ 	res = adcli_entry_modify (entry, attrs);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "adding member(s) to group %s in domain %s failed: %s",
+-		      adcli_entry_get_sam_name (entry),
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("adding member(s) to group %s in domain %s failed: %s",
++		       adcli_entry_get_sam_name (entry),
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_attrs_free (attrs);
++		adcli_entry_unref (entry);
++		return -res;
+ 	}
+ 
+ 	adcli_attrs_free (attrs);
+@@ -672,33 +733,48 @@ adcli_tool_member_remove (adcli_conn *conn,
+ 	argc -= optind;
+ 	argv += optind;
+ 
+-	if (argc < 2)
+-		errx (2, "specify a group name and a user to remove");
++	if (argc < 2) {
++		warnx ("specify a group name and a user to remove");
++		return 2;
++	}
+ 
+ 	entry = adcli_entry_new_group (conn, argv[0]);
+-	if (entry == NULL)
+-		errx (-1, "unexpected memory problems");
++	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) {
+-		errx (-res, "couldn't connect to %s domain: %s",
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("couldn't connect to %s domain: %s",
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_entry_unref (entry);
++		return -res;
+ 	}
+ 
+ 	attrs = adcli_attrs_new ();
+ 
+-	for (i = 1; i < argc; i++)
+-		expand_user_dn_as_member (conn, attrs, argv[i], 0);
++	for (i = 1; i < argc; i++) {
++		res = expand_user_dn_as_member (conn, attrs, argv[i], 0);
++		if (res != ADCLI_SUCCESS) {
++			adcli_attrs_free (attrs);
++			adcli_entry_unref (entry);
++			return res;
++		}
++	}
+ 
+ 	res = adcli_entry_modify (entry, attrs);
+ 	if (res != ADCLI_SUCCESS) {
+-		errx (-res, "adding member(s) to group %s in domain %s failed: %s",
+-		      adcli_entry_get_sam_name (entry),
+-		      adcli_conn_get_domain_name (conn),
+-		      adcli_get_last_error ());
++		warnx ("adding member(s) to group %s in domain %s failed: %s",
++		       adcli_entry_get_sam_name (entry),
++		       adcli_conn_get_domain_name (conn),
++		       adcli_get_last_error ());
++		adcli_attrs_free (attrs);
++		adcli_entry_unref (entry);
++		return -res;
+ 	}
+ 
+ 	adcli_attrs_free (attrs);
+-- 
+2.20.1
+
diff --git a/SOURCES/0003-Do-not-add-service-principals-twice.patch b/SOURCES/0003-Do-not-add-service-principals-twice.patch
new file mode 100644
index 0000000..5d0bfcf
--- /dev/null
+++ b/SOURCES/0003-Do-not-add-service-principals-twice.patch
@@ -0,0 +1,57 @@
+From f05adc23d5cc9f1dfa5638e31949dcd81d632df9 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 13 Aug 2018 17:32:24 +0200
+Subject: [PATCH 3/4] Do not add service principals twice
+
+---
+ library/adenroll.c | 23 +++++++++++++++++++----
+ 1 file changed, 19 insertions(+), 4 deletions(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index c4ba537..bb50365 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -313,6 +313,7 @@ add_service_names_to_service_principals (adcli_enroll *enroll)
+ 	char *name;
+ 	int length = 0;
+ 	int i;
++	size_t c;
+ 
+ 	if (enroll->service_principals != NULL) {
+ 		length = seq_count (enroll->service_principals);
+@@ -321,14 +322,28 @@ add_service_names_to_service_principals (adcli_enroll *enroll)
+ 	for (i = 0; enroll->service_names[i] != NULL; i++) {
+ 		if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0)
+ 			return_unexpected_if_reached ();
+-		enroll->service_principals = _adcli_strv_add (enroll->service_principals,
+-			                                      name, &length);
++		for (c = 0; enroll->service_principals != NULL && enroll->service_principals[c] != NULL; c++) {
++			if (strcmp (name, enroll->service_principals[c]) == 0) {
++				break;
++			}
++		}
++		if (enroll->service_principals == NULL || enroll->service_principals[c] == NULL) {
++			enroll->service_principals = _adcli_strv_add (enroll->service_principals,
++				                                      name, &length);
++		}
+ 
+ 		if (enroll->host_fqdn) {
+ 			if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0)
+ 				return_unexpected_if_reached ();
+-			enroll->service_principals = _adcli_strv_add (enroll->service_principals,
+-				                                      name, &length);
++			for (c = 0; enroll->service_principals != NULL && enroll->service_principals[c] != NULL; c++) {
++				if (strcmp (name, enroll->service_principals[c]) == 0) {
++					break;
++				}
++			}
++			if (enroll->service_principals == NULL || enroll->service_principals[c] == NULL) {
++				enroll->service_principals = _adcli_strv_add (enroll->service_principals,
++					                                      name, &length);
++			}
+ 		}
+ 	}
+ 
+-- 
+2.17.1
+
diff --git a/SOURCES/0003-Remove-n-or-r-n-from-stdin-password.patch b/SOURCES/0003-Remove-n-or-r-n-from-stdin-password.patch
new file mode 100644
index 0000000..5d4bc27
--- /dev/null
+++ b/SOURCES/0003-Remove-n-or-r-n-from-stdin-password.patch
@@ -0,0 +1,31 @@
+From c334771f214e6f0cc2f13edf1032389e7bf74684 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 13 May 2016 18:05:37 +0200
+Subject: [PATCH 3/3] Remove \n or \r\n from stdin password
+
+https://bugs.freedesktop.org/show_bug.cgi?id=78448
+---
+ tools/tools.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/tools/tools.c b/tools/tools.c
+index 3e3435e..4b243de 100644
+--- a/tools/tools.c
++++ b/tools/tools.c
+@@ -250,6 +250,13 @@ adcli_read_password_func (adcli_login_type login_type,
+ 
+ 		} else if (res == 0) {
+ 			buffer[offset] = '\0';
++			/* remove new line character */
++			if (offset > 0 && buffer[offset - 1] == '\n') {
++				buffer[offset - 1] = '\0';
++				if (offset > 1 && buffer[offset - 2] == '\r') {
++					buffer[offset - 2] = '\0';
++				}
++			}
+ 			return buffer;
+ 
+ 		} else {
+-- 
+2.5.5
+
diff --git a/SOURCES/0003-adenroll-add-adcli_enroll_get_permitted_keytab_encty.patch b/SOURCES/0003-adenroll-add-adcli_enroll_get_permitted_keytab_encty.patch
new file mode 100644
index 0000000..f810c13
--- /dev/null
+++ b/SOURCES/0003-adenroll-add-adcli_enroll_get_permitted_keytab_encty.patch
@@ -0,0 +1,196 @@
+From 0c09070e8beec734e3f0c70e14b0a04788077b73 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 13 Jun 2019 17:25:52 +0200
+Subject: [PATCH 3/4] adenroll: add adcli_enroll_get_permitted_keytab_enctypes
+ with tests
+
+The new call does not only return the current encryption types set in AD
+or a default list but filters them with the list of permitted encryption
+types on the client. This makes sure the client can create and use the
+keys.
+
+Related to https://gitlab.freedesktop.org/realmd/adcli/issues/3
+---
+ library/Makefile.am |   5 ++
+ library/adenroll.c  | 124 ++++++++++++++++++++++++++++++++++++++++++++
+ library/adenroll.h  |   2 +
+ 3 files changed, 131 insertions(+)
+
+diff --git a/library/Makefile.am b/library/Makefile.am
+index 39e8fd1..4829555 100644
+--- a/library/Makefile.am
++++ b/library/Makefile.am
+@@ -40,6 +40,7 @@ check_PROGRAMS = \
+ 	test-util \
+ 	test-ldap \
+ 	test-attrs \
++	test-adenroll \
+ 	$(NULL)
+ 
+ test_seq_SOURCES = seq.c test.c test.h
+@@ -56,6 +57,10 @@ test_attrs_SOURCES = adattrs.c $(test_ldap_SOURCES)
+ test_attrs_CFLAGS = -DATTRS_TESTS
+ test_attrs_LDADD = $(test_ldap_LDADD)
+ 
++test_adenroll_SOURCES = adenroll.c $(test_ldap_SOURCES)
++test_adenroll_CFLAGS = -DADENROLL_TESTS
++test_adenroll_LDADD = $(KRB5_LIBS)
++
+ TESTS = $(check_PROGRAMS)
+ 
+ MEMCHECK_ENV = $(TEST_RUNNER) valgrind --error-exitcode=80 --quiet --trace-children=yes
+diff --git a/library/adenroll.c b/library/adenroll.c
+index f617f28..95c07cd 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -2641,6 +2641,50 @@ adcli_enroll_get_keytab_enctypes (adcli_enroll *enroll)
+ 		return v51_earlier_enctypes;
+ }
+ 
++krb5_enctype *
++adcli_enroll_get_permitted_keytab_enctypes (adcli_enroll *enroll)
++{
++	krb5_enctype *cur_enctypes;
++	krb5_enctype *permitted_enctypes;
++	krb5_enctype *new_enctypes;
++	krb5_error_code code;
++	krb5_context k5;
++	size_t c;
++	size_t p;
++	size_t n;
++
++	return_val_if_fail (enroll != NULL, NULL);
++	cur_enctypes = adcli_enroll_get_keytab_enctypes (enroll);
++
++	k5 = adcli_conn_get_krb5_context (enroll->conn);
++	return_val_if_fail (k5 != NULL, NULL);
++
++	code = krb5_get_permitted_enctypes (k5, &permitted_enctypes);
++	return_val_if_fail (code == 0, NULL);
++
++	for (c = 0; cur_enctypes[c] != 0; c++);
++
++	new_enctypes = calloc (c + 1, sizeof (krb5_enctype));
++	return_val_if_fail (new_enctypes != NULL, NULL);
++
++	n = 0;
++	for (c = 0; cur_enctypes[c] != 0; c++) {
++		for (p = 0; permitted_enctypes[p] != 0; p++) {
++			if (cur_enctypes[c] == permitted_enctypes[p]) {
++				new_enctypes[n++] = cur_enctypes[c];
++				break;
++			}
++		}
++		if (permitted_enctypes[p] == 0) {
++			_adcli_info ("Encryption type [%d] not permitted.", cur_enctypes[c]);
++		}
++	}
++
++	krb5_free_enctypes (k5, permitted_enctypes);
++
++	return new_enctypes;
++}
++
+ void
+ adcli_enroll_set_keytab_enctypes (adcli_enroll *enroll,
+                                   krb5_enctype *value)
+@@ -2833,3 +2877,83 @@ adcli_enroll_add_service_principal_to_remove (adcli_enroll *enroll,
+ 							    strdup (value), NULL);
+ 	return_if_fail (enroll->service_principals_to_remove != NULL);
+ }
++
++#ifdef ADENROLL_TESTS
++
++#include "test.h"
++
++static void
++test_adcli_enroll_get_permitted_keytab_enctypes (void)
++{
++	krb5_enctype *enctypes;
++	krb5_error_code code;
++	krb5_enctype *permitted_enctypes;
++	krb5_enctype check_enctypes[3] = { 0 };
++	adcli_conn *conn;
++	adcli_enroll *enroll;
++	adcli_result res;
++	krb5_context k5;
++	size_t c;
++
++	conn = adcli_conn_new ("test.dom");
++	assert_ptr_not_null (conn);
++
++	enroll = adcli_enroll_new (conn);
++	assert_ptr_not_null (enroll);
++
++	enctypes = adcli_enroll_get_permitted_keytab_enctypes (NULL);
++	assert_ptr_eq (enctypes, NULL);
++
++	/* krb5 context missing */
++	enctypes = adcli_enroll_get_permitted_keytab_enctypes (enroll);
++	assert_ptr_eq (enctypes, NULL);
++
++	/* check that all permitted enctypes can pass */
++	res = _adcli_krb5_init_context (&k5);
++	assert_num_eq (res, ADCLI_SUCCESS);
++
++	adcli_conn_set_krb5_context (conn, k5);
++
++	code = krb5_get_permitted_enctypes (k5, &permitted_enctypes);
++	assert_num_eq (code, 0);
++	assert_ptr_not_null (permitted_enctypes);
++	assert_num_cmp (permitted_enctypes[0], !=, 0);
++
++	adcli_enroll_set_keytab_enctypes (enroll, permitted_enctypes);
++
++	enctypes = adcli_enroll_get_permitted_keytab_enctypes (enroll);
++	assert_ptr_not_null (enctypes);
++	for (c = 0; permitted_enctypes[c] != 0; c++) {
++		assert_num_eq (enctypes[c], permitted_enctypes[c]);
++	}
++	assert_num_eq (enctypes[c], 0);
++	krb5_free_enctypes (k5, enctypes);
++
++	/* check that ENCTYPE_UNKNOWN is filtered out */
++	check_enctypes[0] = permitted_enctypes[0];
++	check_enctypes[1] = ENCTYPE_UNKNOWN;
++	check_enctypes[2] = 0;
++	adcli_enroll_set_keytab_enctypes (enroll, check_enctypes);
++
++	enctypes = adcli_enroll_get_permitted_keytab_enctypes (enroll);
++	assert_ptr_not_null (enctypes);
++	assert_num_eq (enctypes[0], permitted_enctypes[0]);
++	assert_num_eq (enctypes[1], 0);
++	krb5_free_enctypes (k5, enctypes);
++
++	krb5_free_enctypes (k5, permitted_enctypes);
++
++	adcli_enroll_unref (enroll);
++	adcli_conn_unref (conn);
++}
++
++int
++main (int argc,
++      char *argv[])
++{
++	test_func (test_adcli_enroll_get_permitted_keytab_enctypes,
++	           "/attrs/adcli_enroll_get_permitted_keytab_enctypes");
++	return test_run (argc, argv);
++}
++
++#endif /* ADENROLL_TESTS */
+diff --git a/library/adenroll.h b/library/adenroll.h
+index abbbfd4..1d5d00d 100644
+--- a/library/adenroll.h
++++ b/library/adenroll.h
+@@ -138,6 +138,8 @@ krb5_enctype *     adcli_enroll_get_keytab_enctypes     (adcli_enroll *enroll);
+ void               adcli_enroll_set_keytab_enctypes     (adcli_enroll *enroll,
+                                                          krb5_enctype *enctypes);
+ 
++krb5_enctype *     adcli_enroll_get_permitted_keytab_enctypes (adcli_enroll *enroll);
++
+ const char *       adcli_enroll_get_os_name             (adcli_enroll *enroll);
+ 
+ void               adcli_enroll_set_os_name             (adcli_enroll *enroll,
+-- 
+2.21.0
+
diff --git a/SOURCES/0003-library-_adcli_krb5_build_principal-allow-principals.patch b/SOURCES/0003-library-_adcli_krb5_build_principal-allow-principals.patch
new file mode 100644
index 0000000..217636c
--- /dev/null
+++ b/SOURCES/0003-library-_adcli_krb5_build_principal-allow-principals.patch
@@ -0,0 +1,42 @@
+From 10a4dbb5978b6f05cf75f820d97da908e735ace8 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 22 Mar 2019 10:37:11 +0100
+Subject: [PATCH 3/4] library: _adcli_krb5_build_principal allow principals as
+ names
+
+Make _adcli_krb5_build_principal a bit more robust by checking if the
+given name already contains a realm suffix.
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1630187
+---
+ library/adkrb5.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/library/adkrb5.c b/library/adkrb5.c
+index 7f77373..da835d7 100644
+--- a/library/adkrb5.c
++++ b/library/adkrb5.c
+@@ -41,12 +41,16 @@ _adcli_krb5_build_principal (krb5_context k5,
+                              krb5_principal *principal)
+ {
+ 	krb5_error_code code;
+-	char *name;
++	char *name = NULL;
+ 
+-	if (asprintf (&name, "%s@%s", user, realm) < 0)
+-		return_val_if_reached (ENOMEM);
++	/* Use user if user contains a @-character and add @realm otherwise */
++	if (strchr (user, '@') == NULL) {
++		if (asprintf (&name, "%s@%s", user, realm) < 0) {
++			return_val_if_reached (ENOMEM);
++		}
++	}
+ 
+-	code = krb5_parse_name (k5, name, principal);
++	code = krb5_parse_name (k5, name != NULL ? name : user, principal);
+ 	return_val_if_fail (code == 0, code);
+ 
+ 	free (name);
+-- 
+2.20.1
+
diff --git a/SOURCES/0003-library-add-_adcli_call_external_program.patch b/SOURCES/0003-library-add-_adcli_call_external_program.patch
new file mode 100644
index 0000000..9553e0d
--- /dev/null
+++ b/SOURCES/0003-library-add-_adcli_call_external_program.patch
@@ -0,0 +1,317 @@
+From 8abf673c508db9d8bbdaaa5bbee50dedd3a95faf Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 30 Jan 2018 14:39:17 +0100
+Subject: [PATCH 3/9] library: add _adcli_call_external_program()
+
+Allow adcli to call an external program given by an absolute path name
+and an array of options. stdin and stdout can be used if needed.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=100118
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ configure.ac        |  28 +++++++
+ library/adprivate.h |   6 ++
+ library/adutil.c    | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 245 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index 221d8ae..fe86638 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -263,6 +263,34 @@ AC_SUBST(LCOV)
+ AC_SUBST(GCOV)
+ AC_SUBST(GENHTML)
+ 
++AC_PATH_PROG(BIN_CAT, cat, no)
++if test "$BIN_CAT" = "no" ; then
++	AC_MSG_ERROR([cat is not available])
++else
++	AC_DEFINE_UNQUOTED(BIN_CAT, "$BIN_CAT", [path to cat, used in unit test])
++fi
++
++AC_PATH_PROG(BIN_TAC, tac, no)
++if test "$BIN_TAC" = "no" ; then
++	AC_MSG_ERROR([tac is not available])
++else
++	AC_DEFINE_UNQUOTED(BIN_TAC, "$BIN_TAC", [path to tac, used in unit test])
++fi
++
++AC_PATH_PROG(BIN_REV, rev, no)
++if test "$BIN_REV" = "no" ; then
++	AC_MSG_ERROR([rev is not available])
++else
++	AC_DEFINE_UNQUOTED(BIN_REV, "$BIN_REV", [path to rev, used in unit test])
++fi
++
++AC_PATH_PROG(BIN_ECHO, echo, no)
++if test "$BIN_ECHO" = "no" ; then
++	AC_MSG_ERROR([echo is not available])
++else
++	AC_DEFINE_UNQUOTED(BIN_ECHO, "$BIN_ECHO", [path to echo, used in unit test])
++fi
++
+ # ---------------------------------------------------------------------
+ 
+ ADCLI_LT_RELEASE=$ADCLI_CURRENT:$ADCLI_REVISION:$ADCLI_AGE
+diff --git a/library/adprivate.h b/library/adprivate.h
+index e99f9fc..7257c93 100644
+--- a/library/adprivate.h
++++ b/library/adprivate.h
+@@ -285,4 +285,10 @@ struct _adcli_attrs {
+ 
+ bool             _adcli_check_nt_time_string_lifetime (const char *nt_time_string, unsigned int lifetime);
+ 
++adcli_result     _adcli_call_external_program     (const char *binary,
++                                                   char * const *argv,
++                                                   const char *stdin_data,
++                                                   uint8_t **stdout_data,
++                                                   size_t *stdout_data_len);
++
+ #endif /* ADPRIVATE_H_ */
+diff --git a/library/adutil.c b/library/adutil.c
+index 829cdd9..a27bd68 100644
+--- a/library/adutil.c
++++ b/library/adutil.c
+@@ -36,6 +36,7 @@
+ #include <unistd.h>
+ #include <stdint.h>
+ #include <time.h>
++#include <sys/wait.h>
+ 
+ static adcli_message_func message_func = NULL;
+ static char last_error[2048] = { 0, };
+@@ -506,6 +507,161 @@ _adcli_check_nt_time_string_lifetime (const char *nt_time_string,
+ 	return false;
+ }
+ 
++adcli_result
++_adcli_call_external_program (const char *binary, char * const *argv,
++                              const char *stdin_data,
++                              uint8_t **stdout_data, size_t *stdout_data_len)
++{
++	int ret;
++	int pipefd_to_child[2] = { -1, -1};
++	int pipefd_from_child[2] = { -1, -1};
++	pid_t child_pid = 0;
++	int err;
++	size_t len;
++	ssize_t rlen;
++	pid_t wret;
++	int status;
++	uint8_t read_buf[4096];
++	uint8_t *out;
++
++	errno = 0;
++	ret = access (binary, X_OK);
++	if (ret != 0) {
++		err = errno;
++		_adcli_err ("Cannot run [%s]: [%d][%s].", binary, err,
++		                                          strerror (err));
++		ret = ADCLI_ERR_FAIL;
++		goto done;
++	}
++
++	ret = pipe (pipefd_from_child);
++	if (ret == -1) {
++		err = errno;
++		_adcli_err ("pipe failed [%d][%s].", err, strerror (err));
++		ret = ADCLI_ERR_FAIL;
++		goto done;
++	}
++
++	ret = pipe (pipefd_to_child);
++	if (ret == -1) {
++		err = errno;
++		_adcli_err ("pipe failed [%d][%s].", err, strerror (err));
++		ret = ADCLI_ERR_FAIL;
++		goto done;
++	}
++
++	child_pid = fork ();
++
++	if (child_pid == 0) { /* child */
++		close (pipefd_to_child[1]);
++		ret = dup2 (pipefd_to_child[0], STDIN_FILENO);
++		if (ret == -1) {
++			err = errno;
++			_adcli_err ("dup2 failed [%d][%s].", err,
++			                                     strerror (err));
++			exit (EXIT_FAILURE);
++		}
++
++		close (pipefd_from_child[0]);
++		ret = dup2 (pipefd_from_child[1], STDOUT_FILENO);
++		if (ret == -1) {
++			err = errno;
++			_adcli_err ("dup2 failed [%d][%s].", err,
++			                                     strerror (err));
++			exit (EXIT_FAILURE);
++		}
++
++		execv (binary, argv);
++		_adcli_err ("Failed to run %s.", binary);
++		ret = ADCLI_ERR_FAIL;
++		goto done;
++	} else if (child_pid > 0) { /* parent */
++
++		if (stdin_data != NULL) {
++			len = strlen (stdin_data);
++			ret = write (pipefd_to_child[1], stdin_data, len);
++			if (ret != len) {
++				_adcli_err ("Failed to send computer account password "
++				            "to net command.");
++				ret = ADCLI_ERR_FAIL;
++				goto done;
++			}
++		}
++
++		close (pipefd_to_child[0]);
++		pipefd_to_child[0] = -1;
++		close (pipefd_to_child[1]);
++		pipefd_to_child[0] = -1;
++
++		if (stdout_data != NULL || stdout_data_len != NULL) {
++			rlen = read (pipefd_from_child[0], read_buf, sizeof (read_buf));
++			if (rlen < 0) {
++				ret = errno;
++				_adcli_err ("Failed to read from child [%d][%s].\n",
++				            ret, strerror (ret));
++				ret = ADCLI_ERR_FAIL;
++				goto done;
++			}
++
++			out = malloc (sizeof(uint8_t) * rlen);
++			if (out == NULL) {
++				_adcli_err ("Failed to allocate memory "
++				            "for child output.");
++				ret = ADCLI_ERR_FAIL;
++				goto done;
++			} else {
++				memcpy (out, read_buf, rlen);
++			}
++
++			if (stdout_data != NULL) {
++				*stdout_data = out;
++			} else {
++				free (out);
++			}
++
++			if (stdout_data_len != NULL) {
++				*stdout_data_len = rlen;
++			}
++		}
++
++	} else {
++		_adcli_err ("Cannot run net command.");
++		ret = ADCLI_ERR_FAIL;
++		goto done;
++	}
++
++	ret = ADCLI_SUCCESS;
++
++done:
++	if (pipefd_from_child[0] != -1) {
++		close (pipefd_from_child[0]);
++	}
++	if (pipefd_from_child[1] != -1) {
++		close (pipefd_from_child[1]);
++	}
++	if (pipefd_to_child[0] != -1) {
++		close (pipefd_to_child[0]);
++	}
++	if (pipefd_to_child[1] != -1) {
++		close (pipefd_to_child[1]);
++	}
++
++	if (child_pid > 0) {
++		wret = waitpid (child_pid, &status, 0);
++		if (wret == -1) {
++			_adcli_err ("No sure what happend to net command.");
++		} else {
++			if (WIFEXITED (status)) {
++				_adcli_err ("net command failed with %d.",
++				            WEXITSTATUS (status));
++			}
++		}
++	}
++
++	return ret;
++}
++
++
+ #ifdef UTIL_TESTS
+ 
+ #include "test.h"
+@@ -620,6 +776,60 @@ test_bin_sid_to_str (void)
+ 	free (str);
+ }
+ 
++static void
++test_call_external_program (void)
++{
++	adcli_result res;
++	char *argv[] = { NULL, NULL, NULL };
++	uint8_t *stdout_data;
++	size_t stdout_data_len;
++
++	argv[0] = "/does/not/exists";
++	res = _adcli_call_external_program (argv[0], argv, NULL, NULL, NULL);
++	assert (res == ADCLI_ERR_FAIL);
++
++#ifdef BIN_CAT
++	argv[0] = BIN_CAT;
++	res = _adcli_call_external_program (argv[0], argv, "Hello",
++	                                    &stdout_data, &stdout_data_len);
++	assert (res == ADCLI_SUCCESS);
++	assert (strncmp ("Hello", (char *) stdout_data, stdout_data_len) == 0);
++	free (stdout_data);
++
++	res = _adcli_call_external_program (argv[0], argv, "Hello",
++	                                    NULL, NULL);
++	assert (res == ADCLI_SUCCESS);
++#endif
++
++#ifdef BIN_REV
++	argv[0] = BIN_REV;
++	res = _adcli_call_external_program (argv[0], argv, "Hello\n",
++	                                    &stdout_data, &stdout_data_len);
++	assert (res == ADCLI_SUCCESS);
++	assert (strncmp ("olleH\n", (char *) stdout_data, stdout_data_len) == 0);
++	free (stdout_data);
++#endif
++
++#ifdef BIN_TAC
++	argv[0] = BIN_TAC;
++	res = _adcli_call_external_program (argv[0], argv, "Hello\nWorld\n",
++	                                    &stdout_data, &stdout_data_len);
++	assert (res == ADCLI_SUCCESS);
++	assert (strncmp ("World\nHello\n", (char *) stdout_data, stdout_data_len) == 0);
++	free (stdout_data);
++#endif
++
++#ifdef BIN_ECHO
++	argv[0] = BIN_ECHO;
++	argv[1] = "Hello";
++	res = _adcli_call_external_program (argv[0], argv, NULL,
++	                                    &stdout_data, &stdout_data_len);
++	assert (res == ADCLI_SUCCESS);
++	assert (strncmp ("Hello\n", (char *) stdout_data, stdout_data_len) == 0);
++	free (stdout_data);
++#endif
++}
++
+ int
+ main (int argc,
+       char *argv[])
+@@ -629,6 +839,7 @@ main (int argc,
+ 	test_func (test_strv_count, "/util/strv_count");
+ 	test_func (test_check_nt_time_string_lifetime, "/util/check_nt_time_string_lifetime");
+ 	test_func (test_bin_sid_to_str, "/util/bin_sid_to_str");
++	test_func (test_call_external_program, "/util/call_external_program");
+ 	return test_run (argc, argv);
+ }
+ 
+-- 
+2.14.4
+
diff --git a/SOURCES/0003-library-add-adcli_conn_is_writeable.patch b/SOURCES/0003-library-add-adcli_conn_is_writeable.patch
new file mode 100644
index 0000000..d674fda
--- /dev/null
+++ b/SOURCES/0003-library-add-adcli_conn_is_writeable.patch
@@ -0,0 +1,38 @@
+From e4de685cb3f924e0af54159d49ef3174ff94e7af Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 1 Nov 2017 16:29:19 +0100
+Subject: [PATCH 3/4] library: add adcli_conn_is_writeable()
+
+---
+ library/adconn.c | 6 ++++++
+ library/adconn.h | 2 ++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/library/adconn.c b/library/adconn.c
+index a294dfd..67bdfd9 100644
+--- a/library/adconn.c
++++ b/library/adconn.c
+@@ -1528,3 +1528,9 @@ adcli_conn_server_has_capability (adcli_conn *conn,
+ 
+ 	return 0;
+ }
++
++bool adcli_conn_is_writeable (adcli_conn *conn)
++{
++    disco_dance_if_necessary (conn);
++    return ( (conn->domain_disco->flags & ADCLI_DISCO_WRITABLE) != 0);
++}
+diff --git a/library/adconn.h b/library/adconn.h
+index a0cb1f8..ed1cc58 100644
+--- a/library/adconn.h
++++ b/library/adconn.h
+@@ -144,4 +144,6 @@ void                adcli_conn_set_krb5_conf_dir     (adcli_conn *conn,
+ int                 adcli_conn_server_has_capability (adcli_conn *conn,
+                                                       const char *capability);
+ 
++bool                adcli_conn_is_writeable          (adcli_conn *conn);
++
+ #endif /* ADCONN_H_ */
+-- 
+2.13.6
+
diff --git a/SOURCES/0003-tools-remove-errx-from-info-commands.patch b/SOURCES/0003-tools-remove-errx-from-info-commands.patch
new file mode 100644
index 0000000..c53cf50
--- /dev/null
+++ b/SOURCES/0003-tools-remove-errx-from-info-commands.patch
@@ -0,0 +1,53 @@
+From 4794812cc98c8783921f534d20dae8b44f3826d2 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 15 Apr 2019 17:57:37 +0200
+Subject: [PATCH 3/7] tools: remove errx from info commands
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596
+---
+ tools/info.c | 21 ++++++++++++++-------
+ 1 file changed, 14 insertions(+), 7 deletions(-)
+
+diff --git a/tools/info.c b/tools/info.c
+index e7e20ad..c63e0ff 100644
+--- a/tools/info.c
++++ b/tools/info.c
+@@ -162,21 +162,28 @@ adcli_tool_info (adcli_conn *unused,
+ 
+ 	if (argc == 1)
+ 		domain = argv[0];
+-	else if (argc != 0)
+-		errx (2, "specify one user name to create");
++	else if (argc != 0) {
++		warnx ("specify one user name to create");
++		return 2;
++	}
+ 
+ 	if (server) {
+ 		adcli_disco_host (server, &disco);
+-		if (disco == NULL)
+-			errx (1, "couldn't discover domain controller: %s", server);
++		if (disco == NULL) {
++			warnx ("couldn't discover domain controller: %s", server);
++			return 1;
++		}
+ 		for_host = 1;
+ 	} else if (domain) {
+ 		adcli_disco_domain (domain, &disco);
+-		if (disco == NULL)
+-			errx (1, "couldn't discover domain: %s", domain);
++		if (disco == NULL) {
++			warnx ("couldn't discover domain: %s", domain);
++			return 1;
++		}
+ 		for_host = 0;
+ 	} else {
+-		errx (2, "specify a domain to discover");
++		warnx ("specify a domain to discover");
++		return 2;
+ 	}
+ 
+ 	print_info (disco, for_host);
+-- 
+2.20.1
+
diff --git a/SOURCES/0003-update-allow-to-add-service-names.patch b/SOURCES/0003-update-allow-to-add-service-names.patch
new file mode 100644
index 0000000..9eb1d68
--- /dev/null
+++ b/SOURCES/0003-update-allow-to-add-service-names.patch
@@ -0,0 +1,387 @@
+From c53a51a61d7ac20900836b1bb005bf272c08a849 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 4 Jun 2018 10:49:33 +0200
+Subject: [PATCH 3/7] update: allow to add service names
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1547013
+           https://bugzilla.redhat.com/show_bug.cgi?id=1545568
+---
+ library/adenroll.c  | 136 +++++++++++++++++++++++++++++++++-------------------
+ library/adkrb5.c    | 113 +++++++++++++++++++++++++++++++++++++++++++
+ library/adprivate.h |   6 +++
+ 3 files changed, 206 insertions(+), 49 deletions(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 2be6796..1221e89 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -305,13 +305,37 @@ ensure_service_names (adcli_result res,
+ }
+ 
+ static adcli_result
+-ensure_service_principals (adcli_result res,
+-                           adcli_enroll *enroll)
++add_service_names_to_service_principals (adcli_enroll *enroll)
+ {
+ 	char *name;
+ 	int length = 0;
+ 	int i;
+ 
++	if (enroll->service_principals != NULL) {
++		length = seq_count (enroll->service_principals);
++	}
++
++	for (i = 0; enroll->service_names[i] != NULL; i++) {
++		if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0)
++			return_unexpected_if_reached ();
++		enroll->service_principals = _adcli_strv_add (enroll->service_principals,
++			                                      name, &length);
++
++		if (enroll->host_fqdn) {
++			if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0)
++				return_unexpected_if_reached ();
++			enroll->service_principals = _adcli_strv_add (enroll->service_principals,
++				                                      name, &length);
++		}
++	}
++
++	return ADCLI_SUCCESS;
++}
++
++static adcli_result
++ensure_service_principals (adcli_result res,
++                           adcli_enroll *enroll)
++{
+ 	if (res != ADCLI_SUCCESS)
+ 		return res;
+ 
+@@ -319,20 +343,7 @@ ensure_service_principals (adcli_result res,
+ 
+ 	if (!enroll->service_principals) {
+ 		assert (enroll->service_names != NULL);
+-
+-		for (i = 0; enroll->service_names[i] != NULL; i++) {
+-			if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0)
+-				return_unexpected_if_reached ();
+-			enroll->service_principals = _adcli_strv_add (enroll->service_principals,
+-			                                              name, &length);
+-
+-			if (enroll->host_fqdn) {
+-				if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0)
+-					return_unexpected_if_reached ();
+-				enroll->service_principals = _adcli_strv_add (enroll->service_principals,
+-				                                              name, &length);
+-			}
+-		}
++		return add_service_names_to_service_principals (enroll);
+ 	}
+ 
+ 	return ADCLI_SUCCESS;
+@@ -356,6 +367,7 @@ ensure_keytab_principals (adcli_result res,
+ 	return_unexpected_if_fail (k5 != NULL);
+ 
+ 	enroll->keytab_principals = calloc (count + 3, sizeof (krb5_principal));
++	return_unexpected_if_fail (enroll->keytab_principals != NULL);
+ 	at = 0;
+ 
+ 	/* First add the principal for the computer account name */
+@@ -1266,7 +1278,7 @@ update_computer_account (adcli_enroll *enroll)
+ 		}
+ 	}
+ 
+-	if (res == ADCLI_SUCCESS && !enroll->user_princpal_generate) {
++	if (res == ADCLI_SUCCESS && enroll->user_principal != NULL && !enroll->user_princpal_generate) {
+ 		char *vals_userPrincipalName[] = { enroll->user_principal, NULL };
+ 		LDAPMod userPrincipalName = { LDAP_MOD_REPLACE, "userPrincipalName", { vals_userPrincipalName, }, };
+ 		LDAPMod *mods[] = { &userPrincipalName, NULL, };
+@@ -1519,7 +1531,8 @@ add_principal_to_keytab (adcli_enroll *enroll,
+                          krb5_context k5,
+                          krb5_principal principal,
+                          const char *principal_name,
+-                         int *which_salt)
++                         int *which_salt,
++                         adcli_enroll_flags flags)
+ {
+ 	match_principal_kvno closure;
+ 	krb5_data password;
+@@ -1547,41 +1560,47 @@ add_principal_to_keytab (adcli_enroll *enroll,
+ 		             enroll->keytab_name);
+ 	}
+ 
+-	password.data = enroll->computer_password;
+-	password.length = strlen (enroll->computer_password);
+-
+ 	enctypes = adcli_enroll_get_keytab_enctypes (enroll);
+ 
+-	/*
+-	 * So we need to discover which salt to use. As a side effect we are
+-	 * also testing that our account works.
+-	 */
++	if (flags & ADCLI_ENROLL_PASSWORD_VALID) {
++		code = _adcli_krb5_keytab_copy_entries (k5, enroll->keytab, principal,
++		                                        enroll->kvno, enctypes);
++	} else {
+ 
+-	salts = build_principal_salts (enroll, k5, principal);
+-	return_unexpected_if_fail (salts != NULL);
++		password.data = enroll->computer_password;
++		password.length = strlen (enroll->computer_password);
+ 
+-	if (*which_salt < 0) {
+-		code = _adcli_krb5_keytab_discover_salt (k5, principal, enroll->kvno, &password,
+-		                                         enctypes, salts, which_salt);
+-		if (code != 0) {
+-			_adcli_warn ("Couldn't authenticate with keytab while discovering which salt to use: %s: %s",
+-			             principal_name, krb5_get_error_message (k5, code));
+-			*which_salt = DEFAULT_SALT;
+-		} else {
+-			assert (*which_salt >= 0);
+-			_adcli_info ("Discovered which keytab salt to use");
++		/*
++		 * So we need to discover which salt to use. As a side effect we are
++		 * also testing that our account works.
++		 */
++
++		salts = build_principal_salts (enroll, k5, principal);
++		return_unexpected_if_fail (salts != NULL);
++
++		if (*which_salt < 0) {
++			code = _adcli_krb5_keytab_discover_salt (k5, principal, enroll->kvno, &password,
++			                                         enctypes, salts, which_salt);
++			if (code != 0) {
++				_adcli_warn ("Couldn't authenticate with keytab while discovering which salt to use: %s: %s",
++				             principal_name, krb5_get_error_message (k5, code));
++				*which_salt = DEFAULT_SALT;
++			} else {
++				assert (*which_salt >= 0);
++				_adcli_info ("Discovered which keytab salt to use");
++			}
+ 		}
+-	}
+ 
+-	code = _adcli_krb5_keytab_add_entries (k5, enroll->keytab, principal,
+-	                                       enroll->kvno, &password, enctypes, &salts[*which_salt]);
++		code = _adcli_krb5_keytab_add_entries (k5, enroll->keytab, principal,
++		                                       enroll->kvno, &password, enctypes, &salts[*which_salt]);
+ 
+-	free_principal_salts (k5, salts);
++		free_principal_salts (k5, salts);
+ 
+-	if (code != 0) {
+-		_adcli_err ("Couldn't add keytab entries: %s: %s",
+-		            enroll->keytab_name, krb5_get_error_message (k5, code));
+-		return ADCLI_ERR_FAIL;
++		if (code != 0) {
++			_adcli_err ("Couldn't add keytab entries: %s: %s",
++			            enroll->keytab_name, krb5_get_error_message (k5, code));
++			return ADCLI_ERR_FAIL;
++		}
+ 	}
+ 
+ 
+@@ -1591,7 +1610,8 @@ add_principal_to_keytab (adcli_enroll *enroll,
+ }
+ 
+ static adcli_result
+-update_keytab_for_principals (adcli_enroll *enroll)
++update_keytab_for_principals (adcli_enroll *enroll,
++                              adcli_enroll_flags flags)
+ {
+ 	krb5_context k5;
+ 	adcli_result res;
+@@ -1608,7 +1628,7 @@ update_keytab_for_principals (adcli_enroll *enroll)
+ 		if (krb5_unparse_name (k5, enroll->keytab_principals[i], &name) != 0)
+ 			name = "";
+ 		res = add_principal_to_keytab (enroll, k5, enroll->keytab_principals[i],
+-		                               name, &which_salt);
++		                               name, &which_salt, flags);
+ 		krb5_free_unparsed_name (k5, name);
+ 
+ 		if (res != ADCLI_SUCCESS)
+@@ -1807,6 +1827,20 @@ enroll_join_or_update_tasks (adcli_enroll *enroll,
+ 	/* We ignore failures of setting these fields */
+ 	update_and_calculate_enctypes (enroll);
+ 	update_computer_account (enroll);
++
++	/* service_names is only set from input on the command line, so no
++	 * additional check for explicit is needed here */
++	if (enroll->service_names != NULL) {
++		res = add_service_names_to_service_principals (enroll);
++		if (res != ADCLI_SUCCESS) {
++			return res;
++		}
++		res = ensure_keytab_principals (res, enroll);
++		if (res != ADCLI_SUCCESS) {
++			return res;
++		}
++	}
++
+ 	update_service_principals (enroll);
+ 
+ 	if ( (flags & ADCLI_ENROLL_ADD_SAMBA_DATA) && ! (flags & ADCLI_ENROLL_PASSWORD_VALID)) {
+@@ -1826,7 +1860,7 @@ enroll_join_or_update_tasks (adcli_enroll *enroll,
+ 	 * that we use for salting.
+ 	 */
+ 
+-	return update_keytab_for_principals (enroll);
++	return update_keytab_for_principals (enroll, flags);
+ }
+ 
+ adcli_result
+@@ -1927,7 +1961,11 @@ adcli_enroll_update (adcli_enroll *enroll,
+ 
+ 	if (_adcli_check_nt_time_string_lifetime (value,
+ 	                adcli_enroll_get_computer_password_lifetime (enroll))) {
+-		flags |= ADCLI_ENROLL_NO_KEYTAB;
++		/* Do not update keytab if neither new service principals have
++                 * to be added nor the user principal has to be changed. */
++		if (enroll->service_names == NULL && (enroll->user_principal == NULL || enroll->user_princpal_generate)) {
++			flags |= ADCLI_ENROLL_NO_KEYTAB;
++		}
+ 		flags |= ADCLI_ENROLL_PASSWORD_VALID;
+ 	}
+ 	free (value);
+diff --git a/library/adkrb5.c b/library/adkrb5.c
+index b0e903e..033c181 100644
+--- a/library/adkrb5.c
++++ b/library/adkrb5.c
+@@ -204,6 +204,119 @@ _adcli_krb5_open_keytab (krb5_context k5,
+ 	return ADCLI_SUCCESS;
+ }
+ 
++typedef struct {
++	krb5_kvno kvno;
++	krb5_enctype enctype;
++	int matched;
++} match_enctype_kvno;
++
++static krb5_boolean
++match_enctype_and_kvno (krb5_context k5,
++                        krb5_keytab_entry *entry,
++                        void *data)
++{
++	krb5_boolean similar = FALSE;
++	match_enctype_kvno *closure = data;
++	krb5_error_code code;
++
++	assert (closure->enctype);
++
++	code = krb5_c_enctype_compare (k5, closure->enctype, entry->key.enctype,
++	                               &similar);
++
++	if (code == 0 && entry->vno == closure->kvno && similar) {
++		closure->matched = 1;
++		return 1;
++	}
++
++	return 0;
++}
++
++static krb5_error_code
++_adcli_krb5_get_keyblock (krb5_context k5,
++                          krb5_keytab keytab,
++                          krb5_keyblock *keyblock,
++                          krb5_boolean (* match_func) (krb5_context,
++                                                       krb5_keytab_entry *,
++                                                       void *),
++                          void *match_data)
++{
++	krb5_kt_cursor cursor;
++	krb5_keytab_entry entry;
++	krb5_error_code code;
++
++	code = krb5_kt_start_seq_get (k5, keytab, &cursor);
++	if (code == KRB5_KT_END || code == ENOENT)
++		return 0;
++	else if (code != 0)
++		return code;
++
++	for (;;) {
++		code = krb5_kt_next_entry (k5, keytab, &entry, &cursor);
++		if (code != 0)
++			break;
++
++		/* See if we should remove this entry */
++		if (!match_func (k5, &entry, match_data)) {
++			krb5_free_keytab_entry_contents (k5, &entry);
++			continue;
++		}
++
++		code = krb5_copy_keyblock_contents (k5, &entry.key, keyblock);
++		krb5_free_keytab_entry_contents (k5, &entry);
++		break;
++
++
++	}
++
++	if (code == KRB5_KT_END)
++		code = 0;
++
++	krb5_kt_end_seq_get (k5, keytab, &cursor);
++	return code;
++}
++
++krb5_error_code
++_adcli_krb5_keytab_copy_entries (krb5_context k5,
++                                 krb5_keytab keytab,
++                                 krb5_principal principal,
++                                 krb5_kvno kvno,
++                                 krb5_enctype *enctypes)
++{
++	krb5_keytab_entry entry;
++	krb5_error_code code;
++	int i;
++	match_enctype_kvno closure;
++
++	for (i = 0; enctypes[i] != 0; i++) {
++
++		closure.kvno = kvno;
++		closure.enctype = enctypes[i];
++		closure.matched = 0;
++
++		memset (&entry, 0, sizeof (entry));
++
++		code = _adcli_krb5_get_keyblock (k5, keytab, &entry.key,
++		                                 match_enctype_and_kvno, &closure);
++		if (code != 0) {
++			return code;
++		}
++
++
++		entry.principal = principal;
++		entry.vno = kvno;
++
++		code = krb5_kt_add_entry (k5, keytab, &entry);
++
++		entry.principal = NULL;
++		krb5_free_keytab_entry_contents (k5, &entry);
++
++		if (code != 0)
++			return code;
++	}
++
++	return 0;
++}
+ 
+ krb5_error_code
+ _adcli_krb5_keytab_add_entries (krb5_context k5,
+diff --git a/library/adprivate.h b/library/adprivate.h
+index 83a88f6..7485249 100644
+--- a/library/adprivate.h
++++ b/library/adprivate.h
+@@ -282,6 +282,12 @@ krb5_enctype *   _adcli_krb5_parse_enctypes       (const char *value);
+ 
+ char *           _adcli_krb5_format_enctypes      (krb5_enctype *enctypes);
+ 
++krb5_error_code  _adcli_krb5_keytab_copy_entries  (krb5_context k5,
++                                                   krb5_keytab keytab,
++                                                   krb5_principal principal,
++                                                   krb5_kvno kvno,
++                                                   krb5_enctype *enctypes);
++
+ struct _adcli_attrs {
+ 	LDAPMod **mods;
+ 	int len;
+-- 
+2.14.4
+
diff --git a/SOURCES/0004-Calculate-enctypes-in-a-separate-function.patch b/SOURCES/0004-Calculate-enctypes-in-a-separate-function.patch
new file mode 100644
index 0000000..fd34daf
--- /dev/null
+++ b/SOURCES/0004-Calculate-enctypes-in-a-separate-function.patch
@@ -0,0 +1,181 @@
+From 8d36c3e07a6efc65aadd218c28e2f864db15b7fd Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 6 Jun 2018 16:31:32 +0200
+Subject: [PATCH 4/7] Calculate enctypes in a separate function
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1542354
+---
+ library/adenroll.c | 137 +++++++++++++++++++++++++++++++----------------------
+ 1 file changed, 81 insertions(+), 56 deletions(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 1221e89..1ed94f2 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -542,6 +542,83 @@ calculate_computer_account (adcli_enroll *enroll,
+ 	return ADCLI_SUCCESS;
+ }
+ 
++static adcli_result
++calculate_enctypes (adcli_enroll *enroll, char **enctype)
++{
++	char *value = NULL;
++	krb5_enctype *read_enctypes;
++	char *new_value = NULL;
++	int is_2008_or_later;
++	LDAP *ldap;
++
++	*enctype = NULL;
++	/*
++	 * Because we're using a keytab we want the server to be aware of the
++	 * encryption types supported on the client, because we can't dynamically
++	 * use a new one that's thrown at us.
++	 *
++	 * If the encryption types are not explicitly set by the caller of this
++	 * library, then see if the account already has some encryption types
++	 * marked on it.
++	 *
++	 * If not, write our default set to the account.
++	 *
++	 * Note that Windows 2003 and earlier have a standard set of encryption
++	 * types, and no msDS-supportedEncryptionTypes attribute.
++	 */
++
++	ldap = adcli_conn_get_ldap_connection (enroll->conn);
++	return_unexpected_if_fail (ldap != NULL);
++
++	is_2008_or_later = adcli_conn_server_has_capability (enroll->conn, ADCLI_CAP_V60_OID);
++
++	/* In 2008 or later, use the msDS-supportedEncryptionTypes attribute */
++	if (is_2008_or_later) {
++		value = _adcli_ldap_parse_value (ldap, enroll->computer_attributes,
++		                                 "msDS-supportedEncryptionTypes");
++
++		if (!enroll->keytab_enctypes_explicit && value != NULL) {
++			read_enctypes = _adcli_krb5_parse_enctypes (value);
++			if (read_enctypes == NULL) {
++				_adcli_warn ("Invalid or unsupported encryption types are set on "
++				             "the computer account (%s).", value);
++			} else {
++				free (enroll->keytab_enctypes);
++				enroll->keytab_enctypes = read_enctypes;
++			}
++		}
++
++	/* In 2003 or earlier, standard set of enc types */
++	} else {
++		value = _adcli_krb5_format_enctypes (v51_earlier_enctypes);
++	}
++
++	new_value = _adcli_krb5_format_enctypes (adcli_enroll_get_keytab_enctypes (enroll));
++	if (new_value == NULL) {
++		free (value);
++		_adcli_warn ("The encryption types desired are not available in active directory");
++		return ADCLI_ERR_CONFIG;
++	}
++
++	/* If we already have this value, then don't need to update */
++	if (value && strcmp (new_value, value) == 0) {
++		free (value);
++		free (new_value);
++		return ADCLI_SUCCESS;
++	}
++	free (value);
++
++	if (!is_2008_or_later) {
++		free (new_value);
++		_adcli_warn ("Server does not support setting encryption types");
++		return ADCLI_SUCCESS;
++	}
++
++	*enctype = new_value;
++	return ADCLI_SUCCESS;
++}
++
++
+ static adcli_result
+ create_computer_account (adcli_enroll *enroll,
+                          LDAP *ldap)
+@@ -1053,75 +1130,23 @@ retrieve_computer_account (adcli_enroll *enroll)
+ static adcli_result
+ update_and_calculate_enctypes (adcli_enroll *enroll)
+ {
+-	char *value = NULL;
+-	krb5_enctype *read_enctypes;
+ 	char *vals_supportedEncryptionTypes[] = { NULL, NULL };
+ 	LDAPMod mod = { LDAP_MOD_REPLACE, "msDS-supportedEncryptionTypes", { vals_supportedEncryptionTypes, } };
+ 	LDAPMod *mods[2] = { &mod, NULL };
+-	int is_2008_or_later;
+ 	char *new_value;
+ 	LDAP *ldap;
+ 	int ret;
+ 
+-	/*
+-	 * Because we're using a keytab we want the server to be aware of the
+-	 * encryption types supported on the client, because we can't dynamically
+-	 * use a new one that's thrown at us.
+-	 *
+-	 * If the encryption types are not explicitly set by the caller of this
+-	 * library, then see if the account already has some encryption types
+-	 * marked on it.
+-	 *
+-	 * If not, write our default set to the account.
+-	 *
+-	 * Note that Windows 2003 and earlier have a standard set of encryption
+-	 * types, and no msDS-supportedEncryptionTypes attribute.
+-	 */
+-
+ 	ldap = adcli_conn_get_ldap_connection (enroll->conn);
+ 	return_unexpected_if_fail (ldap != NULL);
+ 
+-	is_2008_or_later = adcli_conn_server_has_capability (enroll->conn, ADCLI_CAP_V60_OID);
+-
+-	/* In 2008 or later, use the msDS-supportedEncryptionTypes attribute */
+-	if (is_2008_or_later) {
+-		value = _adcli_ldap_parse_value (ldap, enroll->computer_attributes,
+-		                                 "msDS-supportedEncryptionTypes");
+-
+-		if (!enroll->keytab_enctypes_explicit && value != NULL) {
+-			read_enctypes = _adcli_krb5_parse_enctypes (value);
+-			if (read_enctypes == NULL) {
+-				_adcli_warn ("Invalid or unsupported encryption types are set on "
+-				             "the computer account (%s).", value);
+-			} else {
+-				free (enroll->keytab_enctypes);
+-				enroll->keytab_enctypes = read_enctypes;
+-			}
+-		}
+-
+-	/* In 2003 or earlier, standard set of enc types */
+-	} else {
+-		value = _adcli_krb5_format_enctypes (v51_earlier_enctypes);
+-	}
+-
+-	new_value = _adcli_krb5_format_enctypes (adcli_enroll_get_keytab_enctypes (enroll));
+-	if (new_value == NULL) {
+-		free (value);
+-		_adcli_warn ("The encryption types desired are not available in active directory");
+-		return ADCLI_ERR_CONFIG;
+-	}
+-
+-	/* If we already have this value, then don't need to update */
+-	if (value && strcmp (new_value, value) == 0) {
+-		free (value);
++	ret = calculate_enctypes (enroll, &new_value);
++	if (ret != ADCLI_SUCCESS) {
+ 		free (new_value);
+-		return ADCLI_SUCCESS;
++		return ret;
+ 	}
+-	free (value);
+ 
+-	if (!is_2008_or_later) {
+-		free (new_value);
+-		_adcli_warn ("Server does not support setting encryption types");
++	if (new_value == NULL) {
+ 		return ADCLI_SUCCESS;
+ 	}
+ 
+-- 
+2.14.4
+
diff --git a/SOURCES/0004-Do-not-depend-on-default_realm-in-krb5.conf.patch b/SOURCES/0004-Do-not-depend-on-default_realm-in-krb5.conf.patch
new file mode 100644
index 0000000..d12f335
--- /dev/null
+++ b/SOURCES/0004-Do-not-depend-on-default_realm-in-krb5.conf.patch
@@ -0,0 +1,27 @@
+From 8f726817b9ff643a382fa12ea9ff489cd5ab9068 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 13 Aug 2018 18:24:58 +0200
+Subject: [PATCH 4/4] Do not depend on default_realm in krb5.conf
+
+---
+ library/adenroll.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index bb50365..02bd9e3 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -1661,7 +1661,9 @@ remove_principal_from_keytab (adcli_enroll *enroll,
+ 	krb5_principal principal;
+ 	match_principal_kvno closure;
+ 
+-	code = krb5_parse_name (k5, principal_name, &principal);
++	code = _adcli_krb5_build_principal (k5, principal_name,
++	                                    adcli_conn_get_domain_realm (enroll->conn),
++	                                    &principal);
+ 	if (code != 0) {
+ 		_adcli_err ("Couldn't parse principal: %s: %s",
+ 		            principal_name, krb5_get_error_message (k5, code));
+-- 
+2.17.1
+
diff --git a/SOURCES/0004-Handle-kvno-increment-for-RODCs.patch b/SOURCES/0004-Handle-kvno-increment-for-RODCs.patch
new file mode 100644
index 0000000..3d4977e
--- /dev/null
+++ b/SOURCES/0004-Handle-kvno-increment-for-RODCs.patch
@@ -0,0 +1,67 @@
+From 108d3fd58e16428316dae4a4c0601633d2903a4b Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 1 Nov 2017 17:14:05 +0100
+Subject: [PATCH 4/4] Handle kvno increment for RODCs
+
+Since the actual password change does not happen on the read-only domain
+controller (RODC) the kvno change has to be replicated back which might
+take some time. So we check the kvno before and after the change if we
+are connected to a RODC and increment the kvno if needed.
+---
+ library/adenroll.c | 31 +++++++++++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index a15e4be..40c3920 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -1633,8 +1633,30 @@ enroll_join_or_update_tasks (adcli_enroll *enroll,
+ 		             adcli_enroll_flags flags)
+ {
+ 	adcli_result res;
++	krb5_kvno old_kvno = -1;
+ 
+ 	if (!(flags & ADCLI_ENROLL_PASSWORD_VALID)) {
++
++		/* Handle kvno changes for read-only domain controllers
++		 * (RODC). Since the actual password change does not happen on
++		 * the RODC the kvno change has to be replicated back which
++		 * might take some time. So we check the kvno before and after
++		 * the change if we are connected to a RODC and increment the
++		 * kvno if needed. */
++		if (!adcli_conn_is_writeable (enroll->conn)) {
++			if (enroll->computer_attributes == NULL) {
++				res = retrieve_computer_account (enroll);
++				if (res != ADCLI_SUCCESS)
++					return res;
++			}
++			old_kvno = adcli_enroll_get_kvno (enroll);
++			_adcli_info ("Found old kvno '%d'", old_kvno);
++
++			ldap_msgfree (enroll->computer_attributes);
++			enroll->computer_attributes = NULL;
++			adcli_enroll_set_kvno (enroll, 0);
++		}
++
+ 		res = set_computer_password (enroll);
+ 		if (res != ADCLI_SUCCESS)
+ 			return res;
+@@ -1651,6 +1673,15 @@ enroll_join_or_update_tasks (adcli_enroll *enroll,
+ 			return res;
+ 	}
+ 
++	/* Handle kvno changes for read-only domain controllers (RODC) */
++	if (!adcli_conn_is_writeable (enroll->conn) && old_kvno != -1 &&
++	    adcli_enroll_get_kvno (enroll) != 0 &&
++	    adcli_enroll_get_kvno (enroll) == old_kvno) {
++		enroll->kvno++;
++		_adcli_info ("No kvno change detected on read-only DC,  kvno "
++		             "will be incremented by 1 to '%d'", enroll->kvno);
++	}
++
+ 	/* We ignore failures of setting these fields */
+ 	update_and_calculate_enctypes (enroll);
+ 	update_computer_account (enroll);
+-- 
+2.13.6
+
diff --git a/SOURCES/0004-adenroll-use-only-enctypes-permitted-by-Kerberos-con.patch b/SOURCES/0004-adenroll-use-only-enctypes-permitted-by-Kerberos-con.patch
new file mode 100644
index 0000000..a496e32
--- /dev/null
+++ b/SOURCES/0004-adenroll-use-only-enctypes-permitted-by-Kerberos-con.patch
@@ -0,0 +1,103 @@
+From cc3ef52884a48863a81acbfc741735fe09cd85f7 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 13 Jun 2019 18:27:49 +0200
+Subject: [PATCH 4/4] adenroll: use only enctypes permitted by Kerberos config
+
+Realted to https://gitlab.freedesktop.org/realmd/adcli/issues/3
+---
+ doc/adcli.xml      | 10 ++++++++++
+ library/adenroll.c | 22 +++++++++++++++++++---
+ 2 files changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index 9605b4a..094f577 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -342,6 +342,11 @@ Password for Administrator:
+ 		</varlistentry>
+ 	</variablelist>
+ 
++	<para>If supported on the AD side the
++	<option>msDS-supportedEncryptionTypes</option> attribute will be set as
++	well. Either the current value or the default list of AD's supported
++	encryption types filtered by the permitted encryption types of the
++	client's Kerberos configuration are written.</para>
+ </refsect1>
+ 
+ <refsect1 id='updating'>
+@@ -475,6 +480,11 @@ $ adcli update --login-ccache=/tmp/krbcc_123
+ 		</varlistentry>
+ 	</variablelist>
+ 
++	<para>If supported on the AD side the
++	<option>msDS-supportedEncryptionTypes</option> attribute will be set as
++	well. Either the current value or the default list of AD's supported
++	encryption types filtered by the permitted encryption types of the
++	client's Kerberos configuration are written.</para>
+ </refsect1>
+ 
+ <refsect1 id='testjoin'>
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 95c07cd..53cd812 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -639,6 +639,7 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype)
+ {
+ 	char *value = NULL;
+ 	krb5_enctype *read_enctypes;
++	krb5_enctype *new_enctypes;
+ 	char *new_value = NULL;
+ 	int is_2008_or_later;
+ 	LDAP *ldap;
+@@ -685,7 +686,14 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype)
+ 		value = _adcli_krb5_format_enctypes (v51_earlier_enctypes);
+ 	}
+ 
+-	new_value = _adcli_krb5_format_enctypes (adcli_enroll_get_keytab_enctypes (enroll));
++	new_enctypes = adcli_enroll_get_permitted_keytab_enctypes (enroll);
++	if (new_enctypes == NULL) {
++		_adcli_warn ("No permitted encryption type found.");
++		return ADCLI_ERR_UNEXPECTED;
++	}
++
++	new_value = _adcli_krb5_format_enctypes (new_enctypes);
++	krb5_free_enctypes (adcli_conn_get_krb5_context (enroll->conn), new_enctypes);
+ 	if (new_value == NULL) {
+ 		free (value);
+ 		_adcli_warn ("The encryption types desired are not available in active directory");
+@@ -1758,7 +1766,11 @@ add_principal_to_keytab (adcli_enroll *enroll,
+ 		             enroll->keytab_name);
+ 	}
+ 
+-	enctypes = adcli_enroll_get_keytab_enctypes (enroll);
++	enctypes = adcli_enroll_get_permitted_keytab_enctypes (enroll);
++	if (enctypes == NULL) {
++		_adcli_warn ("No permitted encryption type found.");
++		return ADCLI_ERR_UNEXPECTED;
++	}
+ 
+ 	if (flags & ADCLI_ENROLL_PASSWORD_VALID) {
+ 		code = _adcli_krb5_keytab_copy_entries (k5, enroll->keytab, principal,
+@@ -1774,7 +1786,10 @@ add_principal_to_keytab (adcli_enroll *enroll,
+ 		 */
+ 
+ 		salts = build_principal_salts (enroll, k5, principal);
+-		return_unexpected_if_fail (salts != NULL);
++		if (salts == NULL) {
++			krb5_free_enctypes (k5, enctypes);
++			return ADCLI_ERR_UNEXPECTED;
++		}
+ 
+ 		if (*which_salt < 0) {
+ 			code = _adcli_krb5_keytab_discover_salt (k5, principal, enroll->kvno, &password,
+@@ -1794,6 +1809,7 @@ add_principal_to_keytab (adcli_enroll *enroll,
+ 
+ 		free_principal_salts (k5, salts);
+ 	}
++	krb5_free_enctypes (k5, enctypes);
+ 
+ 	if (code != 0) {
+ 		_adcli_err ("Couldn't add keytab entries: %s: %s",
+-- 
+2.21.0
+
diff --git a/SOURCES/0004-library-add-_adcli_ldap_parse_sid.patch b/SOURCES/0004-library-add-_adcli_ldap_parse_sid.patch
new file mode 100644
index 0000000..e3cfb75
--- /dev/null
+++ b/SOURCES/0004-library-add-_adcli_ldap_parse_sid.patch
@@ -0,0 +1,69 @@
+From fc48976e49ebffb6db8b640eebcc3015e55a886f Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 30 Jan 2018 14:44:45 +0100
+Subject: [PATCH 4/9] library: add _adcli_ldap_parse_sid()
+
+Get a binary SID from a LDAP message and return it in the string
+representation.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=100118
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ library/adldap.c    | 24 ++++++++++++++++++++++++
+ library/adprivate.h |  4 ++++
+ 2 files changed, 28 insertions(+)
+
+diff --git a/library/adldap.c b/library/adldap.c
+index 7c7a01b..07dc373 100644
+--- a/library/adldap.c
++++ b/library/adldap.c
+@@ -67,6 +67,30 @@ _adcli_ldap_handle_failure (LDAP *ldap,
+ 	return defres;
+ }
+ 
++char *
++_adcli_ldap_parse_sid (LDAP *ldap,
++                         LDAPMessage *results,
++                         const char *attr_name)
++{
++	LDAPMessage *entry;
++	struct berval **bvs;
++	char *val = NULL;
++
++	entry = ldap_first_entry (ldap, results);
++	if (entry != NULL) {
++		bvs = ldap_get_values_len (ldap, entry, attr_name);
++		if (bvs != NULL) {
++			if (bvs[0]) {
++				val = _adcli_bin_sid_to_str ( (uint8_t *) bvs[0]->bv_val,
++				                              bvs[0]->bv_len);
++				return_val_if_fail (val != NULL, NULL);
++			}
++			ldap_value_free_len (bvs);
++		}
++	}
++
++	return val;
++}
+ 
+ char *
+ _adcli_ldap_parse_value (LDAP *ldap,
+diff --git a/library/adprivate.h b/library/adprivate.h
+index 7257c93..83a88f6 100644
+--- a/library/adprivate.h
++++ b/library/adprivate.h
+@@ -174,6 +174,10 @@ adcli_result  _adcli_ldap_handle_failure     (LDAP *ldap,
+                                               const char *desc,
+                                               ...) GNUC_PRINTF(3, 4);
+ 
++char *         _adcli_ldap_parse_sid         (LDAP *ldap,
++                                              LDAPMessage *results,
++                                              const char *attr_name);
++
+ char *        _adcli_ldap_parse_value        (LDAP *ldap,
+                                               LDAPMessage *results,
+                                               const char *attr_name);
+-- 
+2.14.4
+
diff --git a/SOURCES/0004-library-make-sure-server-side-SPNs-are-preserved.patch b/SOURCES/0004-library-make-sure-server-side-SPNs-are-preserved.patch
new file mode 100644
index 0000000..07cc8fb
--- /dev/null
+++ b/SOURCES/0004-library-make-sure-server-side-SPNs-are-preserved.patch
@@ -0,0 +1,82 @@
+From 972f1a2f35829ed89f5353bd204683aa9ad6a2d2 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 22 Mar 2019 10:37:57 +0100
+Subject: [PATCH 4/4] library: make sure server side SPNs are preserved
+
+adcli should not delete service principal names (SPNs) unexpectedly. If
+a SPN was added on the server while presetting a host or updating an
+existing entry and upcoming adcli join or update should preserver this
+change.
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1630187
+---
+ library/adenroll.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 46 insertions(+)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 48cb4cf..1cce86a 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -1961,6 +1961,47 @@ adcli_enroll_prepare (adcli_enroll *enroll,
+ 	return res;
+ }
+ 
++static adcli_result
++add_server_side_service_principals (adcli_enroll *enroll)
++{
++	char **spn_list;
++	LDAP *ldap;
++	size_t c;
++	int length = 0;
++	adcli_result res;
++
++	ldap = adcli_conn_get_ldap_connection (enroll->conn);
++	assert (ldap != NULL);
++
++	spn_list = _adcli_ldap_parse_values (ldap, enroll->computer_attributes,
++	                                     "servicePrincipalName");
++	if (spn_list == NULL) {
++		return ADCLI_SUCCESS;
++	}
++
++	if (enroll->service_principals != NULL) {
++		length = seq_count (enroll->service_principals);
++	}
++
++	for (c = 0; spn_list[c] != NULL; c++) {
++		_adcli_info ("Checking %s", spn_list[c]);
++		if (!_adcli_strv_has_ex (enroll->service_principals_to_remove, spn_list[c], strcasecmp)) {
++			enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals,
++		                                                             spn_list[c], &length, false);
++			assert (enroll->service_principals != NULL);
++			_adcli_info ("   Added %s", spn_list[c]);
++		}
++	}
++	_adcli_strv_free (spn_list);
++
++	res = ensure_keytab_principals (ADCLI_SUCCESS, enroll);
++	if (res != ADCLI_SUCCESS) {
++		return res;
++	}
++
++	return ADCLI_SUCCESS;
++}
++
+ static adcli_result
+ enroll_join_or_update_tasks (adcli_enroll *enroll,
+ 		             adcli_enroll_flags flags)
+@@ -2019,6 +2060,11 @@ enroll_join_or_update_tasks (adcli_enroll *enroll,
+ 	update_and_calculate_enctypes (enroll);
+ 	update_computer_account (enroll);
+ 
++	res = add_server_side_service_principals (enroll);
++	if (res != ADCLI_SUCCESS) {
++		return res;
++	}
++
+ 	/* service_names is only set from input on the command line, so no
+ 	 * additional check for explicit is needed here */
+ 	if (enroll->service_names != NULL) {
+-- 
+2.20.1
+
diff --git a/SOURCES/0004-tools-remove-errx-from-adcli_read_password_func.patch b/SOURCES/0004-tools-remove-errx-from-adcli_read_password_func.patch
new file mode 100644
index 0000000..1b53d7d
--- /dev/null
+++ b/SOURCES/0004-tools-remove-errx-from-adcli_read_password_func.patch
@@ -0,0 +1,42 @@
+From 251d7d0c71226afb8e51f7bc5794a7a3164f5a20 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 15 Apr 2019 17:59:17 +0200
+Subject: [PATCH 4/7] tools: remove errx from adcli_read_password_func
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596
+---
+ tools/tools.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/tools/tools.c b/tools/tools.c
+index c4e2851..bdf6d38 100644
+--- a/tools/tools.c
++++ b/tools/tools.c
+@@ -247,7 +247,9 @@ adcli_read_password_func (adcli_login_type login_type,
+ 		if (res < 0) {
+ 			if (errno == EAGAIN || errno == EINTR)
+ 				continue;
+-			err (EFAIL, "couldn't read password from stdin");
++			warn ("couldn't read password from stdin");
++			free (buffer);
++			return NULL;
+ 
+ 		} else if (res == 0) {
+ 			buffer[offset] = '\0';
+@@ -261,8 +263,11 @@ adcli_read_password_func (adcli_login_type login_type,
+ 			return buffer;
+ 
+ 		} else {
+-			if (memchr (buffer + offset, 0, res))
+-				errx (EUSAGE, "unsupported null character present in password");
++			if (memchr (buffer + offset, 0, res)) {
++				warnx ("unsupported null character present in password");
++				free (buffer);
++				return NULL;
++			}
+ 			offset += res;
+ 		}
+ 	}
+-- 
+2.20.1
+
diff --git a/SOURCES/0005-join-add-all-attributes-while-creating-computer-obje.patch b/SOURCES/0005-join-add-all-attributes-while-creating-computer-obje.patch
new file mode 100644
index 0000000..2aeb04a
--- /dev/null
+++ b/SOURCES/0005-join-add-all-attributes-while-creating-computer-obje.patch
@@ -0,0 +1,110 @@
+From e9198751411d11785ac6e7e533d63911b6e51326 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 11 Jun 2018 09:44:49 +0200
+Subject: [PATCH 5/7] join: add all attributes while creating computer object
+
+It is possible to create special accounts which can only join a computer
+to a domain but is not allowed to do any further operations which the
+computer object. As a result if such an account is used during the join
+only the ldapadd operation is permitted but not any later ldapmodify
+operation. To create the computer object correctly in this case all
+attributes must be added while the object is created and not later.
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1542354
+---
+ library/adenroll.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 47 insertions(+), 5 deletions(-)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 1ed94f2..3f8d017 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -573,7 +573,7 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype)
+ 	is_2008_or_later = adcli_conn_server_has_capability (enroll->conn, ADCLI_CAP_V60_OID);
+ 
+ 	/* In 2008 or later, use the msDS-supportedEncryptionTypes attribute */
+-	if (is_2008_or_later) {
++	if (is_2008_or_later && enroll->computer_attributes != NULL) {
+ 		value = _adcli_ldap_parse_value (ldap, enroll->computer_attributes,
+ 		                                 "msDS-supportedEncryptionTypes");
+ 
+@@ -618,7 +618,6 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype)
+ 	return ADCLI_SUCCESS;
+ }
+ 
+-
+ static adcli_result
+ create_computer_account (adcli_enroll *enroll,
+                          LDAP *ldap)
+@@ -628,22 +627,65 @@ create_computer_account (adcli_enroll *enroll,
+ 	char *vals_sAMAccountName[] = { enroll->computer_sam, NULL };
+ 	LDAPMod sAMAccountName = { LDAP_MOD_ADD, "sAMAccountName", { vals_sAMAccountName, } };
+ 	char *vals_userAccountControl[] = { "69632", NULL }; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD */
+-	LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } };
++	LDAPMod userAccountControl = { LDAP_MOD_ADD, "userAccountControl", { vals_userAccountControl, } };
++	char *vals_supportedEncryptionTypes[] = { NULL, NULL };
++	LDAPMod encTypes = { LDAP_MOD_ADD, "msDS-supportedEncryptionTypes", { vals_supportedEncryptionTypes, } };
++	char *vals_dNSHostName[] = { enroll->host_fqdn, NULL };
++	LDAPMod dNSHostName = { LDAP_MOD_ADD, "dNSHostName", { vals_dNSHostName, } };
++	char *vals_operatingSystem[] = { enroll->os_name, NULL };
++	LDAPMod operatingSystem = { LDAP_MOD_ADD, "operatingSystem", { vals_operatingSystem, } };
++	char *vals_operatingSystemVersion[] = { enroll->os_version, NULL };
++	LDAPMod operatingSystemVersion = { LDAP_MOD_ADD, "operatingSystemVersion", { vals_operatingSystemVersion, } };
++	char *vals_operatingSystemServicePack[] = { enroll->os_service_pack, NULL };
++	LDAPMod operatingSystemServicePack = { LDAP_MOD_ADD, "operatingSystemServicePack", { vals_operatingSystemServicePack, } };
++	char *vals_userPrincipalName[] = { enroll->user_principal, NULL };
++	LDAPMod userPrincipalName = { LDAP_MOD_ADD, "userPrincipalName", { vals_userPrincipalName, }, };
++	LDAPMod servicePrincipalName = { LDAP_MOD_ADD, "servicePrincipalName", { enroll->service_principals, } };
++
++	char *val = NULL;
+ 
+ 	int ret;
++	size_t c;
++	size_t m;
+ 
+-	LDAPMod *mods[] = {
++	LDAPMod *all_mods[] = {
+ 		&objectClass,
+ 		&sAMAccountName,
+ 		&userAccountControl,
+-		NULL,
++		&encTypes,
++		&dNSHostName,
++		&operatingSystem,
++		&operatingSystemVersion,
++		&operatingSystemServicePack,
++		&userPrincipalName,
++		&servicePrincipalName,
++		NULL
+ 	};
+ 
++	size_t mods_count = sizeof (all_mods) / sizeof (LDAPMod *);
++	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 */
+ 	}
+ 
++	ret = calculate_enctypes (enroll, &val);
++	if (ret != ADCLI_SUCCESS) {
++		return ret;
++	}
++	vals_supportedEncryptionTypes[0] = val;
++
++	m = 0;
++	for (c = 0; c < mods_count - 1; c++) {
++		/* Skip empty LDAP sttributes */
++		if (all_mods[c]->mod_vals.modv_strvals[0] != NULL) {
++			mods[m++] = all_mods[c];
++		}
++	}
++	mods[m] = NULL;
++
+ 	ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL);
++	free (val);
+ 
+ 	/*
+ 	 * Hand to head. This is really dumb... AD returns
+-- 
+2.14.4
+
diff --git a/SOURCES/0005-library-add-lookup_domain_sid.patch b/SOURCES/0005-library-add-lookup_domain_sid.patch
new file mode 100644
index 0000000..89e68e7
--- /dev/null
+++ b/SOURCES/0005-library-add-lookup_domain_sid.patch
@@ -0,0 +1,71 @@
+From 43fe15cc27ebf5f33234c30067a295ef0bd864bb Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 30 Jan 2018 14:40:46 +0100
+Subject: [PATCH 5/9] library: add lookup_domain_sid()
+
+Read the domain SID from the default naming context AD object and store
+it in adcli_conn.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=100118
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ library/adconn.c | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/library/adconn.c b/library/adconn.c
+index 67bdfd9..6b84b88 100644
+--- a/library/adconn.c
++++ b/library/adconn.c
+@@ -72,6 +72,7 @@ struct _adcli_conn_ctx {
+ 	char *domain_controller;
+ 	char *canonical_host;
+ 	char *domain_short;
++	char *domain_sid;
+ 	adcli_disco *domain_disco;
+ 	char *default_naming_context;
+ 	char *configuration_naming_context;
+@@ -1068,6 +1069,32 @@ lookup_short_name (adcli_conn *conn)
+ 	}
+ }
+ 
++static void
++lookup_domain_sid (adcli_conn *conn)
++{
++	char *attrs[] = { "objectSid", NULL, };
++	LDAPMessage *results;
++	int ret;
++
++	free (conn->domain_sid);
++	conn->domain_sid = NULL;
++
++	ret = ldap_search_ext_s (conn->ldap, conn->default_naming_context, LDAP_SCOPE_BASE,
++	                         NULL, attrs, 0, NULL, NULL, NULL, -1, &results);
++	if (ret == LDAP_SUCCESS) {
++		conn->domain_sid = _adcli_ldap_parse_sid (conn->ldap, results, "objectSid");
++		ldap_msgfree (results);
++
++		if (conn->domain_sid)
++			_adcli_info ("Looked up domain SID: %s", conn->domain_sid);
++		else
++			_adcli_err ("No domain SID found");
++	} else {
++		_adcli_ldap_handle_failure (conn->ldap, ADCLI_ERR_DIRECTORY,
++		                            "Couldn't lookup domain SID");
++	}
++}
++
+ static void
+ conn_clear_state (adcli_conn *conn)
+ {
+@@ -1148,6 +1175,7 @@ adcli_conn_connect (adcli_conn *conn)
+ 		return res;
+ 
+ 	lookup_short_name (conn);
++	lookup_domain_sid (conn);
+ 	return ADCLI_SUCCESS;
+ }
+ 
+-- 
+2.14.4
+
diff --git a/SOURCES/0005-tools-remove-errx-from-setup_krb5_conf_directory.patch b/SOURCES/0005-tools-remove-errx-from-setup_krb5_conf_directory.patch
new file mode 100644
index 0000000..8fd9197
--- /dev/null
+++ b/SOURCES/0005-tools-remove-errx-from-setup_krb5_conf_directory.patch
@@ -0,0 +1,63 @@
+From b8f5d995d30c17eb8bec3ac5e0777ea94f5b76c3 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 15 Apr 2019 18:00:52 +0200
+Subject: [PATCH 5/7] tools: remove errx from setup_krb5_conf_directory
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596
+---
+ tools/tools.c | 38 ++++++++++++++++++++++++--------------
+ 1 file changed, 24 insertions(+), 14 deletions(-)
+
+diff --git a/tools/tools.c b/tools/tools.c
+index bdf6d38..fc9fa9a 100644
+--- a/tools/tools.c
++++ b/tools/tools.c
+@@ -327,21 +327,31 @@ setup_krb5_conf_directory (adcli_conn *conn)
+ 	}
+ 
+ 	if (asprintf (&directory, "%s%sadcli-krb5-XXXXXX", parent,
+-	              (parent[0] && parent[strlen(parent) - 1] == '/') ? "" : "/") < 0)
+-		errx (1, "unexpected: out of memory");
+-
+-	if (mkdtemp (directory) == NULL) {
+-		errn = errno;
++	              (parent[0] && parent[strlen(parent) - 1] == '/') ? "" : "/") < 0) {
++		warnx ("unexpected: out of memory");
++		directory = NULL; /* content is undefined */
+ 		failed = 1;
+-		warnx ("couldn't create temporary directory in: %s: %s",
+-		       parent, strerror (errn));
+-	} else {
+-		if (asprintf (&filename, "%s/krb5.conf", directory) < 0 ||
+-		    asprintf (&snippets, "%s/krb5.d", directory) < 0 ||
+-		    asprintf (&contents, "includedir %s\n%s%s\n", snippets,
+-		              krb5_conf ? "include " : "",
+-		              krb5_conf ? krb5_conf : "") < 0)
+-			errx (1, "unexpected: out of memory");
++	}
++
++	if (!failed) {
++		if (mkdtemp (directory) == NULL) {
++			errn = errno;
++			failed = 1;
++			warnx ("couldn't create temporary directory in: %s: %s",
++			       parent, strerror (errn));
++		} else {
++			if (asprintf (&filename, "%s/krb5.conf", directory) < 0 ||
++			    asprintf (&snippets, "%s/krb5.d", directory) < 0 ||
++			    asprintf (&contents, "includedir %s\n%s%s\n", snippets,
++			              krb5_conf ? "include " : "",
++			              krb5_conf ? krb5_conf : "") < 0) {
++				warnx ("unexpected: out of memory");
++				filename = NULL; /* content is undefined */
++				snippets = NULL; /* content is undefined */
++				contents = NULL; /* content is undefined */
++				failed = 1;
++			}
++		}
+ 	}
+ 
+ 	if (!failed) {
+-- 
+2.20.1
+
diff --git a/SOURCES/0006-library-add-adcli_conn_get_domain_sid.patch b/SOURCES/0006-library-add-adcli_conn_get_domain_sid.patch
new file mode 100644
index 0000000..a390fd9
--- /dev/null
+++ b/SOURCES/0006-library-add-adcli_conn_get_domain_sid.patch
@@ -0,0 +1,61 @@
+From f2d54486b911974e396255a5e27003abbf7268ab Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 30 Jan 2018 18:23:03 +0100
+Subject: [PATCH 6/9] library: add adcli_conn_get_domain_sid()
+
+https://bugs.freedesktop.org/show_bug.cgi?id=100118
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ library/adconn.c | 8 ++++++++
+ library/adconn.h | 2 ++
+ tools/computer.c | 1 +
+ 3 files changed, 11 insertions(+)
+
+diff --git a/library/adconn.c b/library/adconn.c
+index 6b84b88..d2fb1d5 100644
+--- a/library/adconn.c
++++ b/library/adconn.c
+@@ -1355,6 +1355,14 @@ adcli_conn_get_domain_short (adcli_conn *conn)
+ 	return conn->domain_short;
+ }
+ 
++const char *
++adcli_conn_get_domain_sid (adcli_conn *conn)
++{
++	return_val_if_fail (conn != NULL, NULL);
++	return conn->domain_sid;
++}
++
++
+ LDAP *
+ adcli_conn_get_ldap_connection (adcli_conn *conn)
+ {
+diff --git a/library/adconn.h b/library/adconn.h
+index ed1cc58..13cfd32 100644
+--- a/library/adconn.h
++++ b/library/adconn.h
+@@ -91,6 +91,8 @@ void                adcli_conn_set_domain_controller (adcli_conn *conn,
+ 
+ const char *        adcli_conn_get_domain_short      (adcli_conn *conn);
+ 
++const char *        adcli_conn_get_domain_sid        (adcli_conn *conn);
++
+ LDAP *              adcli_conn_get_ldap_connection   (adcli_conn *conn);
+ 
+ krb5_context        adcli_conn_get_krb5_context      (adcli_conn *conn);
+diff --git a/tools/computer.c b/tools/computer.c
+index d8a58c9..a3d0f03 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -43,6 +43,7 @@ dump_details (adcli_conn *conn,
+ 	printf ("domain-realm = %s\n", adcli_conn_get_domain_realm (conn));
+ 	printf ("domain-controller = %s\n", adcli_conn_get_domain_controller (conn));
+ 	printf ("domain-short = %s\n", adcli_conn_get_domain_short (conn));
++	printf ("domain-SID = %s\n", adcli_conn_get_domain_sid (conn));
+ 	printf ("naming-context = %s\n", adcli_conn_get_default_naming_context (conn));
+ 	printf ("domain-ou = %s\n", adcli_enroll_get_domain_ou (enroll));
+ 
+-- 
+2.14.4
+
diff --git a/SOURCES/0006-tools-entry-remove-errx-from-parse_option.patch b/SOURCES/0006-tools-entry-remove-errx-from-parse_option.patch
new file mode 100644
index 0000000..17bb9c4
--- /dev/null
+++ b/SOURCES/0006-tools-entry-remove-errx-from-parse_option.patch
@@ -0,0 +1,175 @@
+From d9912e19e48ec482351b9c384140ad71922ec5c0 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 8 Apr 2019 17:22:00 +0200
+Subject: [PATCH 6/7] tools: entry - remove errx from parse_option
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596
+---
+ tools/entry.c | 70 ++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 47 insertions(+), 23 deletions(-)
+
+diff --git a/tools/entry.c b/tools/entry.c
+index 97ec6e7..f361845 100644
+--- a/tools/entry.c
++++ b/tools/entry.c
+@@ -81,7 +81,7 @@ static adcli_tool_desc common_usages[] = {
+ 	{ 0 },
+ };
+ 
+-static void
++static int
+ parse_option (Option opt,
+               const char *optarg,
+               adcli_conn *conn)
+@@ -93,54 +93,58 @@ parse_option (Option opt,
+ 	switch (opt) {
+ 	case opt_login_ccache:
+ 		adcli_conn_set_login_ccache_name (conn, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_login_user:
+ 		adcli_conn_set_login_user (conn, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_domain:
+ 		adcli_conn_set_domain_name (conn, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_domain_realm:
+ 		adcli_conn_set_domain_realm (conn, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_domain_controller:
+ 		adcli_conn_set_domain_controller (conn, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_no_password:
+ 		if (stdin_password || prompt_password) {
+-			errx (EUSAGE, "cannot use --no-password argument with %s",
+-			      stdin_password ? "--stdin-password" : "--prompt-password");
++			warnx ("cannot use --no-password argument with %s",
++			       stdin_password ? "--stdin-password" : "--prompt-password");
++			return EUSAGE;
+ 		} else {
+ 			adcli_conn_set_password_func (conn, NULL, NULL, NULL);
+ 			no_password = 1;
+ 		}
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_prompt_password:
+ 		if (stdin_password || no_password) {
+-			errx (EUSAGE, "cannot use --prompt-password argument with %s",
+-			      stdin_password ? "--stdin-password" : "--no-password");
++			warnx ("cannot use --prompt-password argument with %s",
++			       stdin_password ? "--stdin-password" : "--no-password");
++			return EUSAGE;
+ 		} else {
+ 			adcli_conn_set_password_func (conn, adcli_prompt_password_func, NULL, NULL);
+ 			prompt_password = 1;
+ 		}
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_stdin_password:
+ 		if (prompt_password || no_password) {
+-			errx (EUSAGE, "cannot use --stdin-password argument with %s",
+-			      prompt_password ? "--prompt-password" : "--no-password");
++			warnx ("cannot use --stdin-password argument with %s",
++			       prompt_password ? "--prompt-password" : "--no-password");
++			return EUSAGE;
+ 		} else {
+ 			adcli_conn_set_password_func (conn, adcli_read_password_func, NULL, NULL);
+ 			stdin_password = 1;
+ 		}
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_verbose:
+-		return;
++		return ADCLI_SUCCESS;
+ 	default:
+ 		assert (0 && "not reached");
+ 		break;
+ 	}
+ 
+-	errx (EUSAGE, "failure to parse option '%c'", opt);
++	warnx ("failure to parse option '%c'", opt);
++	return EUSAGE;
+ }
+ 
+ int
+@@ -224,7 +228,11 @@ adcli_tool_user_create (adcli_conn *conn,
+ 			adcli_attrs_free (attrs);
+ 			return opt == 'h' ? 0 : 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn);
++			res = parse_option ((Option)opt, optarg, conn);
++			if (res != ADCLI_SUCCESS) {
++				adcli_attrs_free (attrs);
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+@@ -322,7 +330,10 @@ adcli_tool_user_delete (adcli_conn *conn,
+ 			adcli_tool_usage (options, common_usages);
+ 			return opt == 'h' ? 0 : 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn);
++			res = parse_option ((Option)opt, optarg, conn);
++			if (res != ADCLI_SUCCESS) {
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+@@ -417,7 +428,11 @@ adcli_tool_group_create (adcli_conn *conn,
+ 			adcli_attrs_free (attrs);
+ 			return opt == 'h' ? 0 : 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn);
++			res = parse_option ((Option)opt, optarg, conn);
++			if (res != ADCLI_SUCCESS) {
++				adcli_attrs_free (attrs);
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+@@ -505,7 +520,10 @@ adcli_tool_group_delete (adcli_conn *conn,
+ 			adcli_tool_usage (options, common_usages);
+ 			return opt == 'h' ? 0 : 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn);
++			res = parse_option ((Option)opt, optarg, conn);
++			if (res != ADCLI_SUCCESS) {
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+@@ -628,7 +646,10 @@ adcli_tool_member_add (adcli_conn *conn,
+ 			adcli_tool_usage (options, common_usages);
+ 			return opt == 'h' ? 0 : 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn);
++			res = parse_option ((Option)opt, optarg, conn);
++			if (res != ADCLI_SUCCESS) {
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+@@ -725,7 +746,10 @@ adcli_tool_member_remove (adcli_conn *conn,
+ 			adcli_tool_usage (options, common_usages);
+ 			return opt == 'h' ? 0 : 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn);
++			res = parse_option ((Option)opt, optarg, conn);
++			if (res != ADCLI_SUCCESS) {
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+-- 
+2.20.1
+
diff --git a/SOURCES/0006-util-add-_adcli_strv_remove_unsorted.patch b/SOURCES/0006-util-add-_adcli_strv_remove_unsorted.patch
new file mode 100644
index 0000000..eb5f96b
--- /dev/null
+++ b/SOURCES/0006-util-add-_adcli_strv_remove_unsorted.patch
@@ -0,0 +1,288 @@
+From 3c97263990648f808010220a080f4f9a4e846dac Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 14 Jun 2018 16:48:22 +0200
+Subject: [PATCH 6/7] util: add _adcli_strv_remove_unsorted
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1547014
+---
+ library/adprivate.h |   4 ++
+ library/adutil.c    |  21 ++++++++
+ library/seq.c       | 149 +++++++++++++++++++++++++++++++++++++++++++++++++---
+ library/seq.h       |  12 +++++
+ 4 files changed, 179 insertions(+), 7 deletions(-)
+
+diff --git a/library/adprivate.h b/library/adprivate.h
+index 7485249..bc9df6d 100644
+--- a/library/adprivate.h
++++ b/library/adprivate.h
+@@ -111,6 +111,10 @@ char **        _adcli_strv_add               (char **strv,
+                                               char *string,
+                                               int *length) GNUC_WARN_UNUSED;
+ 
++void           _adcli_strv_remove_unsorted   (char **strv,
++                                              const char *string,
++                                              int *length);
++
+ void           _adcli_strv_free              (char **strv);
+ 
+ int            _adcli_strv_has               (char **strv,
+diff --git a/library/adutil.c b/library/adutil.c
+index a27bd68..6334b52 100644
+--- a/library/adutil.c
++++ b/library/adutil.c
+@@ -221,6 +221,27 @@ _adcli_strv_add (char **strv,
+ 	return seq_push (strv, length, string);
+ }
+ 
++#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
++
++void
++_adcli_strv_remove_unsorted (char **strv,
++                             const char *string,
++                             int *length)
++{
++	int len;
++
++	return_if_fail (string != NULL);
++
++	if (!length) {
++		len = seq_count (strv);
++		length = &len;
++	}
++
++	return seq_remove_unsorted (strv, length, discard_const (string),
++	                            (seq_compar)strcasecmp, free);
++}
++
++
+ int
+ _adcli_strv_has (char **strv,
+                  const char *str)
+diff --git a/library/seq.c b/library/seq.c
+index 627dcaf..8e7475d 100644
+--- a/library/seq.c
++++ b/library/seq.c
+@@ -111,6 +111,24 @@ seq_push (seq_voidp sequence,
+ 	return seq;
+ }
+ 
++static int
++linear_search (void **seq,
++               int low,
++               int high,
++               void *match,
++               seq_compar compar)
++{
++	int at;
++
++	for (at = low; at < high; at++) {
++		if (compar (match, seq[at]) == 0) {
++			break;
++		}
++	}
++
++	return at;
++}
++
+ static int
+ binary_search (void **seq,
+                int low,
+@@ -171,12 +189,13 @@ seq_insert (seq_voidp sequence,
+ 	return seq;
+ }
+ 
+-void
+-seq_remove (seq_voidp sequence,
+-            int *length,
+-            void *match,
+-            seq_compar compar,
+-            seq_destroy destroy)
++static void
++seq_remove_int (seq_voidp sequence,
++                int *length,
++                void *match,
++                seq_search search,
++                seq_compar compar,
++                seq_destroy destroy)
+ {
+ 	void **seq = sequence;
+ 	int at;
+@@ -187,7 +206,7 @@ seq_remove (seq_voidp sequence,
+ 	assert (match != NULL);
+ 
+ 	len = *length;
+-	at = binary_search (seq, 0, len, match, compar);
++	at = search (seq, 0, len, match, compar);
+ 
+ 	/* We have a matching value */
+ 	if (at < len && compar (match, seq[at]) == 0) {
+@@ -201,6 +220,26 @@ seq_remove (seq_voidp sequence,
+ 	*length = len;
+ }
+ 
++void
++seq_remove (seq_voidp sequence,
++            int *length,
++            void *match,
++            seq_compar compar,
++            seq_destroy destroy)
++{
++	return seq_remove_int (sequence, length, match, binary_search, compar, destroy);
++}
++
++void
++seq_remove_unsorted (seq_voidp sequence,
++                     int *length,
++                     void *match,
++                     seq_compar compar,
++                     seq_destroy destroy)
++{
++	return seq_remove_int (sequence, length, match, linear_search, compar, destroy);
++}
++
+ void
+ seq_filter (seq_voidp sequence,
+             int *length,
+@@ -430,6 +469,99 @@ test_remove (void)
+ 	seq_free (seq, NULL);
+ }
+ 
++static void
++test_remove_unsorted (void)
++{
++	void **seq = NULL;
++	int len = 0;
++
++	seq = seq_push (seq, &len, "3");
++	seq = seq_push (seq, &len, "5");
++	seq = seq_push (seq, &len, "1");
++	seq = seq_push (seq, &len, "4");
++	seq = seq_push (seq, &len, "2");
++
++	assert_str_eq (seq[0], "3");
++	assert_str_eq (seq[1], "5");
++	assert_str_eq (seq[2], "1");
++	assert_str_eq (seq[3], "4");
++	assert_str_eq (seq[4], "2");
++	assert (seq[5] == NULL);
++	assert_num_eq (len, 5);
++
++	seq_remove_unsorted (seq, &len, "3", (seq_compar)strcmp, NULL);
++	seq_remove_unsorted (seq, &len, "2", (seq_compar)strcmp, NULL);
++
++	assert_str_eq (seq[0], "5");
++	assert_str_eq (seq[1], "1");
++	assert_str_eq (seq[2], "4");
++	assert (seq[3] == NULL);
++	assert_num_eq (len, 3);
++
++	seq_free (seq, NULL);
++}
++
++static void
++test_remove_first (void)
++{
++	void **seq = NULL;
++	int len = 0;
++
++	seq = seq_insert (seq, &len, "3", (seq_compar)strcmp, NULL);
++	seq = seq_insert (seq, &len, "5", (seq_compar)strcmp, NULL);
++	seq = seq_insert (seq, &len, "1", (seq_compar)strcmp, NULL);
++	seq = seq_insert (seq, &len, "4", (seq_compar)strcmp, NULL);
++	seq = seq_insert (seq, &len, "2", (seq_compar)strcmp, NULL);
++
++	assert_str_eq (seq[0], "1");
++	assert_str_eq (seq[1], "2");
++	assert_str_eq (seq[2], "3");
++	assert_str_eq (seq[3], "4");
++	assert_str_eq (seq[4], "5");
++	assert (seq[5] == NULL);
++	assert_num_eq (len, 5);
++
++	seq_remove (seq, &len, "1", (seq_compar)strcmp, NULL);
++
++	assert_str_eq (seq[0], "2");
++	assert_str_eq (seq[1], "3");
++	assert_str_eq (seq[2], "4");
++	assert_str_eq (seq[3], "5");
++	assert (seq[4] == NULL);
++	assert_num_eq (len, 4);
++
++	seq_free (seq, NULL);
++}
++
++static void
++test_remove_last (void)
++{
++	void **seq = NULL;
++	int len = 0;
++
++	seq = seq_insert (seq, &len, "3", (seq_compar)strcmp, NULL);
++	seq = seq_insert (seq, &len, "1", (seq_compar)strcmp, NULL);
++	seq = seq_insert (seq, &len, "4", (seq_compar)strcmp, NULL);
++	seq = seq_insert (seq, &len, "2", (seq_compar)strcmp, NULL);
++
++	assert_str_eq (seq[0], "1");
++	assert_str_eq (seq[1], "2");
++	assert_str_eq (seq[2], "3");
++	assert_str_eq (seq[3], "4");
++	assert (seq[4] == NULL);
++	assert_num_eq (len, 4);
++
++	seq_remove (seq, &len, "4", (seq_compar)strcmp, NULL);
++
++	assert_str_eq (seq[0], "1");
++	assert_str_eq (seq[1], "2");
++	assert_str_eq (seq[2], "3");
++	assert (seq[3] == NULL);
++	assert_num_eq (len, 3);
++
++	seq_free (seq, NULL);
++}
++
+ static int
+ compar_even (void *match,
+              void *value)
+@@ -631,6 +763,9 @@ main (int argc,
+ 	test_func (test_insert, "/seq/insert");
+ 	test_func (test_insert_destroys, "/seq/insert_destroys");
+ 	test_func (test_remove, "/seq/remove");
++	test_func (test_remove_unsorted, "/seq/remove_unsorted");
++	test_func (test_remove_first, "/seq/remove_first");
++	test_func (test_remove_last, "/seq/remove_last");
+ 	test_func (test_remove_destroys, "/seq/remove_destroys");
+ 	test_func (test_filter, "/seq/filter");
+ 	test_func (test_filter_null, "/seq/filter_null");
+diff --git a/library/seq.h b/library/seq.h
+index 694965b..5d48848 100644
+--- a/library/seq.h
++++ b/library/seq.h
+@@ -44,6 +44,12 @@ typedef void *     (* seq_copy)               (void *value);
+ 
+ typedef void       (* seq_destroy)            (void *value);
+ 
++typedef int        (* seq_search)             (void **seq,
++                                               int low,
++                                               int high,
++                                               void *match,
++                                               seq_compar compar);
++
+ seq_voidp          seq_push                   (seq_voidp seq,
+                                                int *length,
+                                                void *value) WARN_UNUSED;
+@@ -62,6 +68,12 @@ void               seq_remove                 (seq_voidp seq,
+                                                seq_compar compar,
+                                                seq_destroy destroy);
+ 
++void               seq_remove_unsorted        (seq_voidp seq,
++                                               int *length,
++                                               void *match,
++                                               seq_compar compar,
++                                               seq_destroy destroy);
++
+ seq_voidp          seq_lookup                 (seq_voidp seq,
+                                                int *length,
+                                                void *match,
+-- 
+2.14.4
+
diff --git a/SOURCES/0007-Add-add-service-principal-and-remove-service-princip.patch b/SOURCES/0007-Add-add-service-principal-and-remove-service-princip.patch
new file mode 100644
index 0000000..f665593
--- /dev/null
+++ b/SOURCES/0007-Add-add-service-principal-and-remove-service-princip.patch
@@ -0,0 +1,360 @@
+From 767e905e096d9b4f2361a5f307dfb9e622e847aa Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 14 Jun 2018 16:49:26 +0200
+Subject: [PATCH 7/7] Add add-service-principal and remove-service-principal
+ options
+
+Currently it is only possible to specific a service name for service
+principals but not to set the full service principal. This is e.g.
+needed if there is a service running on a host which should be reachable
+by a different DNS name as well.
+
+With this patch service principal can be added and removed by specifying
+the full name.
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1547014
+---
+ doc/adcli.xml      |  21 ++++++++
+ library/adenroll.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++--
+ library/adenroll.h |   8 +++
+ library/adldap.c   |  16 ++++--
+ tools/computer.c   |  13 +++++
+ 5 files changed, 189 insertions(+), 8 deletions(-)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index b246190..83b6981 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -290,6 +290,14 @@ Password for Administrator:
+ 			not allow that Kerberos tickets can be forwarded to the
+ 			host.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--add-service-principal=<parameter>service/hostname</parameter></option></term>
++			<listitem><para>Add a service principal name. In
++			contrast to the <option>--service-name</option> the
++			hostname part can be specified as well in case the
++			service should be accessible with a different host
++			name as well.</para></listitem>
++		</varlistentry>
+ 		<varlistentry>
+ 			<term><option>--show-details</option></term>
+ 			<listitem><para>After a successful join print out information
+@@ -416,6 +424,19 @@ $ adcli update --login-ccache=/tmp/krbcc_123
+ 			not allow that Kerberos tickets can be forwarded to the
+ 			host.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--add-service-principal=<parameter>service/hostname</parameter></option></term>
++			<listitem><para>Add a service principal name. In
++			contrast to the <option>--service-name</option> the
++			hostname part can be specified as well in case the
++			service should be accessible with a different host
++			name as well.</para></listitem>
++		</varlistentry>
++		<varlistentry>
++			<term><option>--remove-service-principal=<parameter>service/hostname</parameter></option></term>
++			<listitem><para>Remove a service principal name from
++			the keytab and the AD host object.</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 3f8d017..1ecd371 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -95,6 +95,9 @@ struct _adcli_enroll {
+ 	char **service_principals;
+ 	int service_principals_explicit;
+ 
++	char **service_principals_to_add;
++	char **service_principals_to_remove;
++
+ 	char *user_principal;
+ 	int user_princpal_generate;
+ 
+@@ -332,6 +335,43 @@ add_service_names_to_service_principals (adcli_enroll *enroll)
+ 	return ADCLI_SUCCESS;
+ }
+ 
++static adcli_result
++add_and_remove_service_principals (adcli_enroll *enroll)
++{
++	int length = 0;
++	size_t c;
++	const char **list;
++
++	if (enroll->service_principals != NULL) {
++		length = seq_count (enroll->service_principals);
++	}
++
++	list = adcli_enroll_get_service_principals_to_add (enroll);
++	if (list != NULL) {
++		for (c = 0; list[c] != NULL; c++) {
++			enroll->service_principals = _adcli_strv_add (enroll->service_principals,
++			                                              strdup (list[c]),
++			                                              &length);
++			if (enroll->service_principals == NULL) {
++				return ADCLI_ERR_UNEXPECTED;
++			}
++		}
++	}
++
++	list = adcli_enroll_get_service_principals_to_remove (enroll);
++	if (list != NULL) {
++		for (c = 0; list[c] != NULL; c++) {
++			/* enroll->service_principals typically refects the
++			 * order of the principal in the keytabm so it is not
++			 * ordered. */
++			_adcli_strv_remove_unsorted (enroll->service_principals,
++			                             list[c], &length);
++		}
++	}
++
++	return ADCLI_SUCCESS;
++}
++
+ static adcli_result
+ ensure_service_principals (adcli_result res,
+                            adcli_enroll *enroll)
+@@ -343,10 +383,14 @@ ensure_service_principals (adcli_result res,
+ 
+ 	if (!enroll->service_principals) {
+ 		assert (enroll->service_names != NULL);
+-		return add_service_names_to_service_principals (enroll);
++		res = add_service_names_to_service_principals (enroll);
+ 	}
+ 
+-	return ADCLI_SUCCESS;
++	if (res == ADCLI_SUCCESS) {
++		res = add_and_remove_service_principals (enroll);
++	}
++
++	return res;
+ }
+ 
+ static adcli_result
+@@ -1593,6 +1637,39 @@ free_principal_salts (krb5_context k5,
+ 	free (salts);
+ }
+ 
++static adcli_result
++remove_principal_from_keytab (adcli_enroll *enroll,
++                              krb5_context k5,
++                              const char *principal_name)
++{
++	krb5_error_code code;
++	krb5_principal principal;
++	match_principal_kvno closure;
++
++	code = krb5_parse_name (k5, principal_name, &principal);
++	if (code != 0) {
++		_adcli_err ("Couldn't parse principal: %s: %s",
++		            principal_name, krb5_get_error_message (k5, code));
++		return ADCLI_ERR_FAIL;
++	}
++
++	closure.kvno = enroll->kvno;
++	closure.principal = principal;
++	closure.matched = 0;
++
++	code = _adcli_krb5_keytab_clear (k5, enroll->keytab,
++	                                 match_principal_and_kvno, &closure);
++	krb5_free_principal (k5, principal);
++
++	if (code != 0) {
++		_adcli_err ("Couldn't update keytab: %s: %s",
++		            enroll->keytab_name, krb5_get_error_message (k5, code));
++		return ADCLI_ERR_FAIL;
++	}
++
++	return ADCLI_SUCCESS;
++}
++
+ static adcli_result
+ add_principal_to_keytab (adcli_enroll *enroll,
+                          krb5_context k5,
+@@ -1702,6 +1779,17 @@ update_keytab_for_principals (adcli_enroll *enroll,
+ 			return res;
+ 	}
+ 
++	if (enroll->service_principals_to_remove != NULL) {
++		for (i = 0; enroll->service_principals_to_remove[i] != NULL; i++) {
++			res = remove_principal_from_keytab (enroll, k5,
++			                                    enroll->service_principals_to_remove[i]);
++			if (res != ADCLI_SUCCESS) {
++				_adcli_warn ("Failed to remove %s from keytab.",
++				             enroll->service_principals_to_remove[i]);
++			}
++		}
++	}
++
+ 	return ADCLI_SUCCESS;
+ }
+ 
+@@ -2029,8 +2117,11 @@ adcli_enroll_update (adcli_enroll *enroll,
+ 	if (_adcli_check_nt_time_string_lifetime (value,
+ 	                adcli_enroll_get_computer_password_lifetime (enroll))) {
+ 		/* Do not update keytab if neither new service principals have
+-                 * to be added nor the user principal has to be changed. */
+-		if (enroll->service_names == NULL && (enroll->user_principal == NULL || enroll->user_princpal_generate)) {
++                 * to be added or deleted nor the user principal has to be changed. */
++		if (enroll->service_names == NULL
++		              && (enroll->user_principal == NULL || enroll->user_princpal_generate)
++		              && enroll->service_principals_to_add == NULL
++		              && enroll->service_principals_to_remove == NULL) {
+ 			flags |= ADCLI_ENROLL_NO_KEYTAB;
+ 		}
+ 		flags |= ADCLI_ENROLL_PASSWORD_VALID;
+@@ -2581,3 +2672,43 @@ adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll,
+ 	enroll->trusted_for_delegation = value;
+ 	enroll->trusted_for_delegation_explicit = 1;
+ }
++
++const char **
++adcli_enroll_get_service_principals_to_add (adcli_enroll *enroll)
++{
++	return_val_if_fail (enroll != NULL, NULL);
++
++	return (const char **)enroll->service_principals_to_add;
++}
++
++void
++adcli_enroll_add_service_principal_to_add (adcli_enroll *enroll,
++                                           const char *value)
++{
++	return_if_fail (enroll != NULL);
++	return_if_fail (value != NULL);
++
++	enroll->service_principals_to_add = _adcli_strv_add (enroll->service_principals_to_add,
++							    strdup (value), NULL);
++	return_if_fail (enroll->service_principals_to_add != NULL);
++}
++
++const char **
++adcli_enroll_get_service_principals_to_remove (adcli_enroll *enroll)
++{
++	return_val_if_fail (enroll != NULL, NULL);
++
++	return (const char **)enroll->service_principals_to_remove;
++}
++
++void
++adcli_enroll_add_service_principal_to_remove (adcli_enroll *enroll,
++                                              const char *value)
++{
++	return_if_fail (enroll != NULL);
++	return_if_fail (value != NULL);
++
++	enroll->service_principals_to_remove = _adcli_strv_add (enroll->service_principals_to_remove,
++							    strdup (value), NULL);
++	return_if_fail (enroll->service_principals_to_remove != NULL);
++}
+diff --git a/library/adenroll.h b/library/adenroll.h
+index be2ca18..f87dffa 100644
+--- a/library/adenroll.h
++++ b/library/adenroll.h
+@@ -98,6 +98,14 @@ const char **      adcli_enroll_get_service_principals  (adcli_enroll *enroll);
+ void               adcli_enroll_set_service_principals  (adcli_enroll *enroll,
+                                                          const char **value);
+ 
++const char **      adcli_enroll_get_service_principals_to_add (adcli_enroll *enroll);
++void               adcli_enroll_add_service_principal_to_add (adcli_enroll *enroll,
++                                                              const char *value);
++
++const char **      adcli_enroll_get_service_principals_to_remove (adcli_enroll *enroll);
++void               adcli_enroll_add_service_principal_to_remove (adcli_enroll *enroll,
++                                                                 const char *value);
++
+ const char *       adcli_enroll_get_user_principal      (adcli_enroll *enroll);
+ 
+ void               adcli_enroll_set_user_principal      (adcli_enroll *enroll,
+diff --git a/library/adldap.c b/library/adldap.c
+index 07dc373..d93efb7 100644
+--- a/library/adldap.c
++++ b/library/adldap.c
+@@ -210,16 +210,24 @@ _adcli_ldap_have_in_mod (LDAPMod *mod,
+ 	struct berval *vals;
+ 	struct berval **pvals;
+ 	int count = 0;
++	int count_have = 0;
+ 	int i;
+ 	int ret;
+ 
+-	/* Already in berval format, just compare */
+-	if (mod->mod_op & LDAP_MOD_BVALUES)
+-		return _adcli_ldap_have_vals (mod->mod_vals.modv_bvals, have);
+-
+ 	/* Count number of values */
+ 	for (i = 0; mod->mod_vals.modv_strvals[i] != 0; i++)
+ 		count++;
++	for (i = 0; have[i] != 0; i++)
++		count_have++;
++
++	/* If numbers different something has to be added or removed */
++	if (count != count_have) {
++		return 0;
++	}
++
++	/* Already in berval format, just compare */
++	if (mod->mod_op & LDAP_MOD_BVALUES)
++		return _adcli_ldap_have_vals (mod->mod_vals.modv_bvals, have);
+ 
+ 	vals = malloc (sizeof (struct berval) * (count + 1));
+ 	pvals = malloc (sizeof (struct berval *) * (count + 1));
+diff --git a/tools/computer.c b/tools/computer.c
+index b905fd1..377d449 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -110,6 +110,8 @@ typedef enum {
+ 	opt_add_samba_data,
+ 	opt_samba_data_tool,
+ 	opt_trusted_for_delegation,
++	opt_add_service_principal,
++	opt_remove_service_principal,
+ } Option;
+ 
+ static adcli_tool_desc common_usages[] = {
+@@ -138,6 +140,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_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_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"
+@@ -289,6 +293,12 @@ parse_option (Option opt,
+ 			adcli_enroll_set_trusted_for_delegation (enroll, false);
+ 		}
+ 		return;
++	case opt_add_service_principal:
++		adcli_enroll_add_service_principal_to_add (enroll, optarg);
++		return;
++	case opt_remove_service_principal:
++		adcli_enroll_add_service_principal_to_remove (enroll, optarg);
++		return;
+ 	case opt_verbose:
+ 		return;
+ 
+@@ -353,6 +363,7 @@ adcli_tool_computer_join (adcli_conn *conn,
+ 		{ "os-service-pack", optional_argument, NULL, opt_os_service_pack },
+ 		{ "user-principal", optional_argument, NULL, opt_user_principal },
+ 		{ "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation },
++		{ "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 },
+ 		{ "add-samba-data", no_argument, NULL, opt_add_samba_data },
+@@ -458,6 +469,8 @@ 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 },
++		{ "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 },
+ 		{ "show-password", no_argument, NULL, opt_show_password },
+ 		{ "add-samba-data", no_argument, NULL, opt_add_samba_data },
+-- 
+2.14.4
+
diff --git a/SOURCES/0007-tools-add-option-add-samba-data.patch b/SOURCES/0007-tools-add-option-add-samba-data.patch
new file mode 100644
index 0000000..fee1643
--- /dev/null
+++ b/SOURCES/0007-tools-add-option-add-samba-data.patch
@@ -0,0 +1,142 @@
+From b4fccc5b43bb4d24bbd652bec5bff715dd43b883 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 30 Jan 2018 14:46:00 +0100
+Subject: [PATCH 7/9] tools: add option --add-samba-data
+
+https://bugs.freedesktop.org/show_bug.cgi?id=100118
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ doc/adcli.xml      | 30 ++++++++++++++++++++++++++++++
+ library/adenroll.h |  1 +
+ tools/computer.c   | 12 ++++++++++++
+ 3 files changed, 43 insertions(+)
+
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index c54cc1b..fbc6c63 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -292,6 +292,21 @@ Password for Administrator:
+ 			machine account password. This is output in a format that should
+ 			be both human and machine readable.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--add-samba-data</option></term>
++			<listitem><para>After a successful join add the domain
++			SID and the machine account password to the Samba
++			specific databases by calling Samba's
++			<command>net</command> utility.</para>
++
++			<para>Please note that Samba's <command>net</command>
++			requires some settings in <filename>smb.conf</filename>
++			to create the database entries correctly. Most
++			important here is currently the
++			<option>workgroup</option> option, see
++			<citerefentry><refentrytitle>smb.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
++			for details.</para></listitem>
++		</varlistentry>
+ 	</variablelist>
+ 
+ </refsect1>
+@@ -382,6 +397,21 @@ $ adcli update --login-ccache=/tmp/krbcc_123
+ 			about join operation. This is output in a format that should
+ 			be both human and machine readable.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--add-samba-data</option></term>
++			<listitem><para>After a successful join add the domain
++			SID and the machine account password to the Samba
++			specific databases by calling Samba's
++			<command>net</command> utility.</para>
++
++			<para>Please note that Samba's <command>net</command>
++			requires some settings in <filename>smb.conf</filename>
++			to create the database entries correctly. Most
++			important here is currently the
++			<option>workgroup</option> option, see
++			<citerefentry><refentrytitle>smb.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
++			for details.</para></listitem>
++		</varlistentry>
+ 	</variablelist>
+ 
+ </refsect1>
+diff --git a/library/adenroll.h b/library/adenroll.h
+index 9a107ab..32c9764 100644
+--- a/library/adenroll.h
++++ b/library/adenroll.h
+@@ -30,6 +30,7 @@ typedef enum {
+ 	ADCLI_ENROLL_NO_KEYTAB = 1 << 1,
+ 	ADCLI_ENROLL_ALLOW_OVERWRITE = 1 << 2,
+ 	ADCLI_ENROLL_PASSWORD_VALID = 1 << 3,
++	ADCLI_ENROLL_ADD_SAMBA_DATA = 1 << 3,
+ } adcli_enroll_flags;
+ 
+ typedef struct _adcli_enroll adcli_enroll;
+diff --git a/tools/computer.c b/tools/computer.c
+index a3d0f03..fc646f2 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -106,6 +106,7 @@ typedef enum {
+ 	opt_os_service_pack,
+ 	opt_user_principal,
+ 	opt_computer_password_lifetime,
++	opt_add_samba_data,
+ } Option;
+ 
+ static adcli_tool_desc common_usages[] = {
+@@ -142,6 +143,8 @@ static adcli_tool_desc common_usages[] = {
+ 	                     "a successful join" },
+ 	{ opt_show_password, "show computer account password after after a\n"
+ 	                     "successful join" },
++	{ opt_add_samba_data, "add domain SID and computer account password\n"
++	                      "to the Samba specific configuration database" },
+ 	{ opt_verbose, "show verbose progress and failure messages", },
+ 	{ 0 },
+ };
+@@ -269,6 +272,7 @@ parse_option (Option opt,
+ 	case opt_show_details:
+ 	case opt_show_password:
+ 	case opt_one_time_password:
++	case opt_add_samba_data:
+ 		assert (0 && "not reached");
+ 		break;
+ 	}
+@@ -326,6 +330,7 @@ adcli_tool_computer_join (adcli_conn *conn,
+ 		{ "user-principal", optional_argument, NULL, opt_user_principal },
+ 		{ "show-details", no_argument, NULL, opt_show_details },
+ 		{ "show-password", no_argument, NULL, opt_show_password },
++		{ "add-samba-data", no_argument, NULL, opt_add_samba_data },
+ 		{ "verbose", no_argument, NULL, opt_verbose },
+ 		{ "help", no_argument, NULL, 'h' },
+ 		{ 0 },
+@@ -352,6 +357,9 @@ adcli_tool_computer_join (adcli_conn *conn,
+ 		case opt_show_password:
+ 			show_password = 1;
+ 			break;
++		case opt_add_samba_data:
++			flags |= ADCLI_ENROLL_ADD_SAMBA_DATA;
++			break;
+ 		case 'h':
+ 		case '?':
+ 		case ':':
+@@ -425,6 +433,7 @@ adcli_tool_computer_update (adcli_conn *conn,
+ 		{ "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime },
+ 		{ "show-details", no_argument, NULL, opt_show_details },
+ 		{ "show-password", no_argument, NULL, opt_show_password },
++		{ "add-samba-data", no_argument, NULL, opt_add_samba_data },
+ 		{ "verbose", no_argument, NULL, opt_verbose },
+ 		{ "help", no_argument, NULL, 'h' },
+ 		{ 0 },
+@@ -447,6 +456,9 @@ adcli_tool_computer_update (adcli_conn *conn,
+ 		case opt_show_password:
+ 			show_password = 1;
+ 			break;
++		case opt_add_samba_data:
++			flags |= ADCLI_ENROLL_ADD_SAMBA_DATA;
++			break;
+ 		case 'h':
+ 		case '?':
+ 		case ':':
+-- 
+2.14.4
+
diff --git a/SOURCES/0007-tools-computer-remove-errx-from-parse_option.patch b/SOURCES/0007-tools-computer-remove-errx-from-parse_option.patch
new file mode 100644
index 0000000..065fc9f
--- /dev/null
+++ b/SOURCES/0007-tools-computer-remove-errx-from-parse_option.patch
@@ -0,0 +1,294 @@
+From f127ddef23a532cd9763190527bf79b4e47fa2ab Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 8 Apr 2019 17:33:17 +0200
+Subject: [PATCH 7/7] tools: computer - remove errx from parse_option
+
+Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596
+---
+ tools/computer.c | 128 +++++++++++++++++++++++++++++------------------
+ 1 file changed, 80 insertions(+), 48 deletions(-)
+
+diff --git a/tools/computer.c b/tools/computer.c
+index 9cbbb28..ac8a203 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -159,7 +159,7 @@ static adcli_tool_desc common_usages[] = {
+ 	{ 0 },
+ };
+ 
+-static void
++static int
+ parse_option (Option opt,
+               const char *optarg,
+               adcli_conn *conn,
+@@ -175,132 +175,139 @@ parse_option (Option opt,
+ 	switch (opt) {
+ 	case opt_login_ccache:
+ 		adcli_conn_set_login_ccache_name (conn, optarg ? optarg : "");
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_login_user:
+ 		if (adcli_conn_get_allowed_login_types (conn) & ADCLI_LOGIN_USER_ACCOUNT) {
+ 			adcli_conn_set_login_user (conn, optarg);
+ 			adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT);
+ 		} else {
+-			errx (EUSAGE, "cannot set --user if --login-type not set to 'user'");
++			warnx ("cannot set --user if --login-type not set to 'user'");
++			return EUSAGE;
+ 		}
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_login_type:
+ 		if (optarg && strcmp (optarg, "computer") == 0) {
+-			if (adcli_conn_get_login_user (conn) != NULL)
+-				errx (EUSAGE, "cannot set --login-type to 'computer' if --user is set");
+-			else
++			if (adcli_conn_get_login_user (conn) != NULL) {
++				warnx ("cannot set --login-type to 'computer' if --user is set");
++				return EUSAGE;
++			} else
+ 				adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_COMPUTER_ACCOUNT);
+ 		} else if (optarg && strcmp (optarg, "user") == 0) {
+ 			adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT);
+ 
+ 		} else {
+-			errx (EUSAGE, "unknown login type '%s'", optarg);
++			warnx ("unknown login type '%s'", optarg);
++			return EUSAGE;
+ 		}
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_host_fqdn:
+ 		adcli_conn_set_host_fqdn (conn, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_host_keytab:
+ 		adcli_enroll_set_keytab_name (enroll, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_computer_name:
+ 		adcli_conn_set_computer_name (conn, optarg);
+ 		adcli_enroll_set_computer_name (enroll, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_domain:
+ 		adcli_conn_set_domain_name (conn, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_domain_realm:
+ 		adcli_conn_set_domain_realm (conn, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_domain_controller:
+ 		adcli_conn_set_domain_controller (conn, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_domain_ou:
+ 		adcli_enroll_set_domain_ou (enroll, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_service_name:
+ 		adcli_enroll_add_service_name (enroll, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_no_password:
+ 		if (stdin_password || prompt_password) {
+-			errx (EUSAGE, "cannot use --no-password argument with %s",
+-			      stdin_password ? "--stdin-password" : "--prompt-password");
++			warnx ("cannot use --no-password argument with %s",
++			       stdin_password ? "--stdin-password" : "--prompt-password");
++			return EUSAGE;
+ 		} else {
+ 			adcli_conn_set_password_func (conn, NULL, NULL, NULL);
+ 			no_password = 1;
+ 		}
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_prompt_password:
+ 		if (stdin_password || no_password) {
+-			errx (EUSAGE, "cannot use --prompt-password argument with %s",
+-			      stdin_password ? "--stdin-password" : "--no-password");
++			warnx ("cannot use --prompt-password argument with %s",
++			       stdin_password ? "--stdin-password" : "--no-password");
++			return EUSAGE;
+ 		} else {
+ 			adcli_conn_set_password_func (conn, adcli_prompt_password_func, NULL, NULL);
+ 			prompt_password = 1;
+ 		}
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_stdin_password:
+ 		if (prompt_password || no_password) {
+-			errx (EUSAGE, "cannot use --stdin-password argument with %s",
+-			      prompt_password ? "--prompt-password" : "--no-password");
++			warnx ("cannot use --stdin-password argument with %s",
++			       prompt_password ? "--prompt-password" : "--no-password");
++			return EUSAGE;
+ 		} else {
+ 			adcli_conn_set_password_func (conn, adcli_read_password_func, NULL, NULL);
+ 			stdin_password = 1;
+ 		}
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_os_name:
+ 		adcli_enroll_set_os_name (enroll, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_os_version:
+ 		adcli_enroll_set_os_version (enroll, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_os_service_pack:
+ 		adcli_enroll_set_os_service_pack (enroll, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_user_principal:
+ 		if (optarg && optarg[0])
+ 			adcli_enroll_set_user_principal (enroll, optarg);
+ 		else
+ 			adcli_enroll_auto_user_principal (enroll);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_computer_password_lifetime:
+ 		errno = 0;
+ 		lifetime = strtoul (optarg, &endptr, 10);
+ 		if (errno != 0 || *endptr != '\0' || endptr == optarg) {
+-			errx (EUSAGE,
+-			      "failure to parse value '%s' of option 'computer-password-lifetime'; "
+-			      "expecting non-negative integer indicating the lifetime in days",
+-			      optarg);
++			warnx ("failure to parse value '%s' of option 'computer-password-lifetime'; "
++			       "expecting non-negative integer indicating the lifetime in days",
++			       optarg);
++			return EUSAGE;
+ 		}
+ 
+ 		adcli_enroll_set_computer_password_lifetime (enroll, lifetime);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_samba_data_tool:
+ 		errno = 0;
+ 		ret = access (optarg, X_OK);
+ 		if (ret != 0) {
+ 			ret = errno;
+-			errx (EUSAGE, "Failed to access tool to add Samba data: %s", strerror (ret));
++			warnx ("Failed to access tool to add Samba data: %s", strerror (ret));
++			return EUSAGE;
+ 		} else {
+ 			adcli_enroll_set_samba_data_tool (enroll, optarg);
+ 		}
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_trusted_for_delegation:
+ 		if (strcasecmp (optarg, "true") == 0 || strcasecmp (optarg, "yes") == 0) {
+ 			adcli_enroll_set_trusted_for_delegation (enroll, true);
+ 		} else {
+ 			adcli_enroll_set_trusted_for_delegation (enroll, false);
+ 		}
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_add_service_principal:
+ 		adcli_enroll_add_service_principal_to_add (enroll, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_remove_service_principal:
+ 		adcli_enroll_add_service_principal_to_remove (enroll, optarg);
+-		return;
++		return ADCLI_SUCCESS;
+ 	case opt_verbose:
+-		return;
++		return ADCLI_SUCCESS;
+ 
+ 	/* Should be handled by caller */
+ 	case opt_show_details:
+@@ -311,7 +318,8 @@ parse_option (Option opt,
+ 		break;
+ 	}
+ 
+-	errx (EUSAGE, "failure to parse option '%c'", opt);
++	warnx ("failure to parse option '%c'", opt);
++	return EUSAGE;
+ }
+ 
+ static void
+@@ -407,7 +415,11 @@ adcli_tool_computer_join (adcli_conn *conn,
+ 			adcli_enroll_unref (enroll);
+ 			return opt == 'h' ? 0 : 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn, enroll);
++			res = parse_option ((Option)opt, optarg, conn, enroll);
++			if (res != ADCLI_SUCCESS) {
++				adcli_enroll_unref (enroll);
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+@@ -519,7 +531,11 @@ adcli_tool_computer_update (adcli_conn *conn,
+ 			adcli_enroll_unref (enroll);
+ 			return opt == 'h' ? 0 : 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn, enroll);
++			res = parse_option ((Option)opt, optarg, conn, enroll);
++			if (res != ADCLI_SUCCESS) {
++				adcli_enroll_unref (enroll);
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+@@ -610,7 +626,11 @@ adcli_tool_computer_testjoin (adcli_conn *conn,
+ 			adcli_enroll_unref (enroll);
+ 			return opt == 'h' ? 0 : 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn, enroll);
++			res = parse_option ((Option)opt, optarg, conn, enroll);
++			if (res != ADCLI_SUCCESS) {
++				adcli_enroll_unref (enroll);
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+@@ -707,7 +727,11 @@ adcli_tool_computer_preset (adcli_conn *conn,
+ 			adcli_enroll_unref (enroll);
+ 			return 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn, enroll);
++			res = parse_option ((Option)opt, optarg, conn, enroll);
++			if (res != ADCLI_SUCCESS) {
++				adcli_enroll_unref (enroll);
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+@@ -801,7 +825,11 @@ adcli_tool_computer_reset (adcli_conn *conn,
+ 			adcli_enroll_unref (enroll);
+ 			return opt == 'h' ? 0 : 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn, enroll);
++			res = parse_option ((Option)opt, optarg, conn, enroll);
++			if (res != ADCLI_SUCCESS) {
++				adcli_enroll_unref (enroll);
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+@@ -884,7 +912,11 @@ adcli_tool_computer_delete (adcli_conn *conn,
+ 			adcli_enroll_unref (enroll);
+ 			return opt == 'h' ? 0 : 2;
+ 		default:
+-			parse_option ((Option)opt, optarg, conn, enroll);
++			res = parse_option ((Option)opt, optarg, conn, enroll);
++			if (res != ADCLI_SUCCESS) {
++				adcli_enroll_unref (enroll);
++				return res;
++			}
+ 			break;
+ 		}
+ 	}
+-- 
+2.20.1
+
diff --git a/SOURCES/0008-tools-store-Samba-data-if-requested.patch b/SOURCES/0008-tools-store-Samba-data-if-requested.patch
new file mode 100644
index 0000000..c7d3227
--- /dev/null
+++ b/SOURCES/0008-tools-store-Samba-data-if-requested.patch
@@ -0,0 +1,75 @@
+From 4d46d295458286e4a268f37ea900e58177ac903c Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 30 Jan 2018 18:24:15 +0100
+Subject: [PATCH 8/9] tools: store Samba data if requested
+
+Use Samba's net utility to add the machine account password and the
+domain SID to the Samba configuration.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=100118
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ library/adenroll.c | 39 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 40c3920..6b1aae5 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -1533,6 +1533,36 @@ update_keytab_for_principals (adcli_enroll *enroll)
+ 	return ADCLI_SUCCESS;
+ }
+ 
++static adcli_result
++update_samba_data (adcli_enroll *enroll)
++{
++	int ret;
++	char *argv_pw[] = { "/usr/bin/net", "changesecretpw", "-i", "-f", NULL };
++	char *argv_sid[] = { "/usr/bin/net", "setdomainsid", NULL, NULL };
++
++	_adcli_info ("Trying to set Samba secret.\n");
++	ret = _adcli_call_external_program (argv_pw[0], argv_pw,
++	                                    enroll->computer_password, NULL, NULL);
++	if (ret != ADCLI_SUCCESS) {
++		_adcli_err ("Failed to set Samba computer account password.\n");
++	}
++
++	argv_sid[2] = (char *) adcli_conn_get_domain_sid (enroll->conn);
++	if (argv_sid[2] == NULL) {
++		_adcli_err ("Domain SID not available.\n");
++	} else {
++		_adcli_info ("Trying to set domain SID %s for Samba.\n",
++		             argv_sid[2]);
++		ret = _adcli_call_external_program (argv_sid[0], argv_sid,
++		                                    NULL, NULL, NULL);
++		if (ret != ADCLI_SUCCESS) {
++			_adcli_err ("Failed to set Samba domain SID.\n");
++		}
++	}
++
++	return ret;
++}
++
+ static void
+ enroll_clear_state (adcli_enroll *enroll)
+ {
+@@ -1687,6 +1717,15 @@ enroll_join_or_update_tasks (adcli_enroll *enroll,
+ 	update_computer_account (enroll);
+ 	update_service_principals (enroll);
+ 
++	if ( (flags & ADCLI_ENROLL_ADD_SAMBA_DATA) && ! (flags & ADCLI_ENROLL_PASSWORD_VALID)) {
++		res = update_samba_data (enroll);
++		if (res != ADCLI_SUCCESS) {
++			_adcli_info ("Failed to add Samba specific data, smbd "
++			             "or winbindd might not work as "
++			             "expected.\n");
++		}
++	}
++
+ 	if (flags & ADCLI_ENROLL_NO_KEYTAB)
+ 		return ADCLI_SUCCESS;
+ 
+-- 
+2.14.4
+
diff --git a/SOURCES/0009-make-Samba-data-tool-configurable.patch b/SOURCES/0009-make-Samba-data-tool-configurable.patch
new file mode 100644
index 0000000..209b591
--- /dev/null
+++ b/SOURCES/0009-make-Samba-data-tool-configurable.patch
@@ -0,0 +1,292 @@
+From c0784106a839c2686e01c75472bae2e7d9071c8b Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 1 Feb 2018 14:26:22 +0100
+Subject: [PATCH 9/9] make Samba data tool configurable
+
+Allow to specify an alternative path to Samba's net utility at configure
+time and at run time.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=100118
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ configure.ac                    | 13 ++++++++++++
+ doc/adcli.xml                   | 21 ++++++++++++++++++-
+ doc/samba_data_tool_path.xml.in |  1 +
+ library/adenroll.c              | 46 ++++++++++++++++++++++++++++++++++-------
+ library/adenroll.h              |  5 +++++
+ tools/computer.c                | 16 ++++++++++++++
+ 7 files changed, 95 insertions(+), 8 deletions(-)
+ create mode 100644 doc/samba_data_tool_path.xml.in
+
+diff --git a/configure.ac b/configure.ac
+index fe86638..68877c7 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -291,6 +291,18 @@ else
+ 	AC_DEFINE_UNQUOTED(BIN_ECHO, "$BIN_ECHO", [path to echo, used in unit test])
+ fi
+ 
++AC_MSG_CHECKING([where is Samba's net utility])
++AC_ARG_WITH([samba_data_tool],
++              AC_HELP_STRING([--with-samba-data-tool=/path],
++              [Path to Samba's net utility]),
++              [],
++              [with_samba_data_tool=/usr/bin/net])
++AC_MSG_RESULT([$with_samba_data_tool])
++
++AC_DEFINE_UNQUOTED(SAMBA_DATA_TOOL, "$with_samba_data_tool",
++                   [Path to Samba's net utility])
++
++AC_SUBST(SAMBA_DATA_TOOL, [$with_samba_data_tool])
+ # ---------------------------------------------------------------------
+ 
+ ADCLI_LT_RELEASE=$ADCLI_CURRENT:$ADCLI_REVISION:$ADCLI_AGE
+@@ -300,6 +312,7 @@ AC_CONFIG_FILES([Makefile
+ 	build/Makefile
+ 	doc/Makefile
+ 	doc/version.xml
++	doc/samba_data_tool_path.xml
+ 	library/Makefile
+ 	tools/Makefile
+ ])
+diff --git a/doc/adcli.xml b/doc/adcli.xml
+index fbc6c63..c2b7760 100644
+--- a/doc/adcli.xml
++++ b/doc/adcli.xml
+@@ -1,6 +1,9 @@
+ <?xml version='1.0'?>
+ <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+-	"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
++	"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
++[
++	<!ENTITY samba_data_tool SYSTEM "samba_data_tool_path.xml">
++]>
+ 
+ <refentry id="adcli">
+ 
+@@ -307,6 +310,14 @@ Password for Administrator:
+ 			<citerefentry><refentrytitle>smb.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ 			for details.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--samba-data-tool=<parameter>/path/to/net</parameter></option></term>
++			<listitem><para>If Samba's <command>net</command>
++			cannot be found at
++			<filename>&samba_data_tool;</filename> this option can
++			be used to specific an alternative location with the
++			help of an absolute path.</para></listitem>
++		</varlistentry>
+ 	</variablelist>
+ 
+ </refsect1>
+@@ -412,6 +423,14 @@ $ adcli update --login-ccache=/tmp/krbcc_123
+ 			<citerefentry><refentrytitle>smb.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ 			for details.</para></listitem>
+ 		</varlistentry>
++		<varlistentry>
++			<term><option>--samba-data-tool=<parameter>/path/to/net</parameter></option></term>
++			<listitem><para>If Samba's <command>net</command>
++			cannot be found at
++			<filename>&samba_data_tool;</filename> this option can
++			be used to specific an alternative location with the
++			help of an absolute path.</para></listitem>
++		</varlistentry>
+ 	</variablelist>
+ 
+ </refsect1>
+diff --git a/doc/samba_data_tool_path.xml.in b/doc/samba_data_tool_path.xml.in
+new file mode 100644
+index 0000000..a667c57
+--- /dev/null
++++ b/doc/samba_data_tool_path.xml.in
+@@ -0,0 +1 @@
++@SAMBA_DATA_TOOL@
+diff --git a/library/adenroll.c b/library/adenroll.c
+index 6b1aae5..d64c2c0 100644
+--- a/library/adenroll.c
++++ b/library/adenroll.c
+@@ -42,6 +42,10 @@
+ #include <stdio.h>
+ #include <unistd.h>
+ 
++#ifndef SAMBA_DATA_TOOL
++#define SAMBA_DATA_TOOL "/usr/bin/net"
++#endif
++
+ static krb5_enctype v60_later_enctypes[] = {
+ 	ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ 	ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+@@ -100,6 +104,7 @@ struct _adcli_enroll {
+ 	int keytab_enctypes_explicit;
+ 	unsigned int computer_password_lifetime;
+ 	int computer_password_lifetime_explicit;
++	char *samba_data_tool;
+ };
+ 
+ static adcli_result
+@@ -1537,26 +1542,33 @@ static adcli_result
+ update_samba_data (adcli_enroll *enroll)
+ {
+ 	int ret;
+-	char *argv_pw[] = { "/usr/bin/net", "changesecretpw", "-i", "-f", NULL };
+-	char *argv_sid[] = { "/usr/bin/net", "setdomainsid", NULL, NULL };
++	char *argv_pw[] = { NULL, "changesecretpw", "-i", "-f", NULL };
++	char *argv_sid[] = { NULL, "setdomainsid", NULL, NULL };
++
++	argv_pw[0] = (char *) adcli_enroll_get_samba_data_tool (enroll);
++	if (argv_pw[0] ==NULL) {
++		_adcli_err ("Samba data tool not available.");
++		return ADCLI_ERR_FAIL;
++	}
++	argv_sid[0] = argv_pw[0];
+ 
+-	_adcli_info ("Trying to set Samba secret.\n");
++	_adcli_info ("Trying to set Samba secret.");
+ 	ret = _adcli_call_external_program (argv_pw[0], argv_pw,
+ 	                                    enroll->computer_password, NULL, NULL);
+ 	if (ret != ADCLI_SUCCESS) {
+-		_adcli_err ("Failed to set Samba computer account password.\n");
++		_adcli_err ("Failed to set Samba computer account password.");
+ 	}
+ 
+ 	argv_sid[2] = (char *) adcli_conn_get_domain_sid (enroll->conn);
+ 	if (argv_sid[2] == NULL) {
+-		_adcli_err ("Domain SID not available.\n");
++		_adcli_err ("Domain SID not available.");
+ 	} else {
+-		_adcli_info ("Trying to set domain SID %s for Samba.\n",
++		_adcli_info ("Trying to set domain SID %s for Samba.",
+ 		             argv_sid[2]);
+ 		ret = _adcli_call_external_program (argv_sid[0], argv_sid,
+ 		                                    NULL, NULL, NULL);
+ 		if (ret != ADCLI_SUCCESS) {
+-			_adcli_err ("Failed to set Samba domain SID.\n");
++			_adcli_err ("Failed to set Samba domain SID.");
+ 		}
+ 	}
+ 
+@@ -1951,6 +1963,9 @@ adcli_enroll_new (adcli_conn *conn)
+ 	enroll->os_name = strdup (value);
+ 	return_val_if_fail (enroll->os_name != NULL, NULL);
+ 
++	enroll->samba_data_tool = strdup (SAMBA_DATA_TOOL);
++	return_val_if_fail (enroll->samba_data_tool != NULL, NULL);
++
+ 	return enroll;
+ }
+ 
+@@ -1978,6 +1993,7 @@ enroll_free (adcli_enroll *enroll)
+ 	free (enroll->os_name);
+ 	free (enroll->os_version);
+ 	free (enroll->os_service_pack);
++	free (enroll->samba_data_tool);
+ 
+ 	free (enroll->user_principal);
+ 	_adcli_strv_free (enroll->service_names);
+@@ -2343,3 +2359,19 @@ adcli_enroll_set_computer_password_lifetime (adcli_enroll *enroll,
+ 
+ 	enroll->computer_password_lifetime_explicit = 1;
+ }
++
++void
++adcli_enroll_set_samba_data_tool (adcli_enroll *enroll, const char *value)
++{
++	return_if_fail (enroll != NULL);
++	if (value != NULL && value[0] != '\0') {
++		_adcli_str_set (&enroll->samba_data_tool, value);
++	}
++}
++
++const char *
++adcli_enroll_get_samba_data_tool (adcli_enroll *enroll)
++{
++	return_val_if_fail (enroll != NULL, NULL);
++	return enroll->samba_data_tool;
++}
+diff --git a/library/adenroll.h b/library/adenroll.h
+index 32c9764..31ca0bc 100644
+--- a/library/adenroll.h
++++ b/library/adenroll.h
+@@ -141,4 +141,9 @@ const char *       adcli_enroll_get_os_service_pack     (adcli_enroll *enroll);
+ void               adcli_enroll_set_os_service_pack     (adcli_enroll *enroll,
+                                                          const char *value);
+ 
++void               adcli_enroll_set_samba_data_tool     (adcli_enroll *enroll,
++                                                         const char *value);
++
++const char *       adcli_enroll_get_samba_data_tool     (adcli_enroll *enroll);
++
+ #endif /* ADENROLL_H_ */
+diff --git a/tools/computer.c b/tools/computer.c
+index fc646f2..f86548b 100644
+--- a/tools/computer.c
++++ b/tools/computer.c
+@@ -30,6 +30,7 @@
+ #include <err.h>
+ #include <stdio.h>
+ #include <errno.h>
++#include <unistd.h>
+ 
+ static void
+ dump_details (adcli_conn *conn,
+@@ -107,6 +108,7 @@ typedef enum {
+ 	opt_user_principal,
+ 	opt_computer_password_lifetime,
+ 	opt_add_samba_data,
++	opt_samba_data_tool,
+ } Option;
+ 
+ static adcli_tool_desc common_usages[] = {
+@@ -145,6 +147,7 @@ static adcli_tool_desc common_usages[] = {
+ 	                     "successful join" },
+ 	{ opt_add_samba_data, "add domain SID and computer account password\n"
+ 	                      "to the Samba specific configuration database" },
++	{ opt_samba_data_tool, "Absolute path to the tool used for add-samba-data" },
+ 	{ opt_verbose, "show verbose progress and failure messages", },
+ 	{ 0 },
+ };
+@@ -160,6 +163,7 @@ parse_option (Option opt,
+ 	static int stdin_password = 0;
+ 	char *endptr;
+ 	unsigned int lifetime;
++	int ret;
+ 
+ 	switch (opt) {
+ 	case opt_login_ccache:
+@@ -265,6 +269,16 @@ parse_option (Option opt,
+ 
+ 		adcli_enroll_set_computer_password_lifetime (enroll, lifetime);
+ 		return;
++	case opt_samba_data_tool:
++		errno = 0;
++		ret = access (optarg, X_OK);
++		if (ret != 0) {
++			ret = errno;
++			errx (EUSAGE, "Failed to access tool to add Samba data: %s", strerror (ret));
++		} else {
++			adcli_enroll_set_samba_data_tool (enroll, optarg);
++		}
++		return;
+ 	case opt_verbose:
+ 		return;
+ 
+@@ -331,6 +345,7 @@ adcli_tool_computer_join (adcli_conn *conn,
+ 		{ "show-details", no_argument, NULL, opt_show_details },
+ 		{ "show-password", no_argument, NULL, opt_show_password },
+ 		{ "add-samba-data", no_argument, NULL, opt_add_samba_data },
++		{ "samba-data-tool", no_argument, NULL, opt_samba_data_tool },
+ 		{ "verbose", no_argument, NULL, opt_verbose },
+ 		{ "help", no_argument, NULL, 'h' },
+ 		{ 0 },
+@@ -434,6 +449,7 @@ adcli_tool_computer_update (adcli_conn *conn,
+ 		{ "show-details", no_argument, NULL, opt_show_details },
+ 		{ "show-password", no_argument, NULL, opt_show_password },
+ 		{ "add-samba-data", no_argument, NULL, opt_add_samba_data },
++		{ "samba-data-tool", no_argument, NULL, opt_samba_data_tool },
+ 		{ "verbose", no_argument, NULL, opt_verbose },
+ 		{ "help", no_argument, NULL, 'h' },
+ 		{ 0 },
+-- 
+2.14.4
+
diff --git a/SPECS/adcli.spec b/SPECS/adcli.spec
new file mode 100644
index 0000000..b961f61
--- /dev/null
+++ b/SPECS/adcli.spec
@@ -0,0 +1,295 @@
+Name:           adcli
+Version:        0.8.1
+Release:        12%{?dist}
+Summary:        Active Directory enrollment
+License:        LGPLv2+
+URL:            http://cgit.freedesktop.org/realmd/adcli
+Source0:        http://www.freedesktop.org/software/realmd/releases/adcli-%{version}.tar.gz
+Patch1:         0001-library-Fix-check-for-EAGAIN-or-EINTR.patch
+Patch2:         0002-Patch-to-adcli.xml-to-fix-documentation-error.patch
+Patch3:         0003-Remove-n-or-r-n-from-stdin-password.patch
+Patch4:         0001-delete-use-keytab-data-to-determine-realm-and-NetBIO.patch
+Patch5:         0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch
+Patch6:         0002-Use-strdup-if-offset-are-used.patch
+Patch7:         0001-correct-spelling-of-adcli_tool_computer_delete-descr.patch
+Patch8:         0001-doc-Update-the-documentation-about-the-default-kerbe.patch
+Patch9:         0002-doc-explain-that-all-credential-cache-types-are-supp.patch
+Patch10:        0003-library-add-adcli_conn_is_writeable.patch
+Patch11:        0004-Handle-kvno-increment-for-RODCs.patch
+
+Patch12:        0001-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch
+Patch13:        0002-library-add-_adcli_bin_sid_to_str.patch
+Patch14:        0003-library-add-_adcli_call_external_program.patch
+Patch15:        0004-library-add-_adcli_ldap_parse_sid.patch
+Patch16:        0005-library-add-lookup_domain_sid.patch
+Patch17:        0006-library-add-adcli_conn_get_domain_sid.patch
+Patch18:        0007-tools-add-option-add-samba-data.patch
+Patch19:        0008-tools-store-Samba-data-if-requested.patch
+Patch20:        0009-make-Samba-data-tool-configurable.patch
+
+Patch21:        0001-Add-trusted-for-delegation-option.patch
+Patch22:        0002-Only-update-attributes-given-on-the-command-line.patch
+Patch23:        0003-update-allow-to-add-service-names.patch
+Patch24:        0004-Calculate-enctypes-in-a-separate-function.patch
+Patch25:        0005-join-add-all-attributes-while-creating-computer-obje.patch
+Patch26:        0006-util-add-_adcli_strv_remove_unsorted.patch
+Patch27:        0007-Add-add-service-principal-and-remove-service-princip.patch
+Patch28:        0001-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch
+
+# Additional fixes for rhbz#1593240
+Patch29:        0001-fix-typo-in-flag-value.patch
+Patch30:        0002-_adcli_call_external_program-silence-noisy-debug-mes.patch
+
+# rhbz#1608212
+Patch31:        0003-Do-not-add-service-principals-twice.patch
+
+# Additional fixed for rhbz#1547014
+Patch32:        0004-Do-not-depend-on-default_realm-in-krb5.conf.patch
+
+# rhbz#1649868
+Patch33:        0001-adutil-add-_adcli_strv_add_unique.patch
+Patch34:        0002-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch
+
+# Patch35 is replaced by Patch49 - Patch55
+
+# rhbz#1642546 - adcli exports kerberos ticket with old kvno
+Patch36:        0001-Increment-kvno-after-password-change-with-user-creds.patch
+
+# rhbz#1595911 - [RFE] Have `adcli join` work without FQDN in `hostname` output
+#                with some additional man page fixes from rhbz#1440533
+Patch37:        0001-doc-fix-typos-in-the-adcli-man-page.patch
+Patch38:        0001-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch
+
+# rhbz#1644311 - Improve handling of service principals
+Patch39:        0001-join-always-add-service-principals.patch
+Patch40:        0002-library-return-error-if-no-matching-key-was-found.patch
+
+# rhbz#1337489 - [RFE] adcli command with --unix-* options doesn't update
+#                values in UnixAttributes Tab for user
+Patch41:        0001-create-user-add-nis-domain-option.patch
+Patch42:        0002-create-user-try-to-find-NIS-domain-if-needed.patch
+
+# rhbz#1630187 - [RFE] adcli join should preserve SPN added by adcli preset-computer
+Patch43:        0001-ensure_keytab_principals-do-not-leak-memory-when-cal.patch
+Patch44:        0002-library-make-_adcli_strv_has_ex-public.patch
+Patch45:        0003-library-_adcli_krb5_build_principal-allow-principals.patch
+Patch46:        0004-library-make-sure-server-side-SPNs-are-preserved.patch
+
+# rhbz#1622583 - [RFE] Need an option for adcli command which will show domain join status.
+Patch47:        0001-Implement-adcli-testjoin.patch
+
+# rhbz#1630187 - [RFE] adcli join should preserve SPN added by adcli preset-computer - additional patch
+Patch48:        0001-library-add-missing-strdup.patch
+
+# rhbz#1588596 - many adcli-krb5-????? directories are created /tmp
+Patch49:        0001-tools-remove-errx-from-computer-commands.patch
+Patch50:        0002-tools-remove-errx-from-user-and-group-commands.patch
+Patch51:        0003-tools-remove-errx-from-info-commands.patch
+Patch52:        0004-tools-remove-errx-from-adcli_read_password_func.patch
+Patch53:        0005-tools-remove-errx-from-setup_krb5_conf_directory.patch
+Patch54:        0006-tools-entry-remove-errx-from-parse_option.patch
+Patch55:        0007-tools-computer-remove-errx-from-parse_option.patch
+
+# rhbz#1665162 - adcli is failing with "Couldn't add keytab entries: FILE:/etc/krb5.keytab: Cannot allocate memory" (edit)
+Patch56:        0001-Fix-for-issues-found-by-Coverity.patch
+Patch57:        0001-adenroll-make-sure-only-allowed-enctypes-are-used-in.patch
+Patch58:        0002-adconn-add-adcli_conn_set_krb5_context.patch
+Patch59:        0003-adenroll-add-adcli_enroll_get_permitted_keytab_encty.patch
+Patch60:        0004-adenroll-use-only-enctypes-permitted-by-Kerberos-con.patch
+
+# Coverity fix related to fixes for rhbz#1665162
+Patch61:        0001-Fix-for-issue-found-by-Coverity.patch
+
+# rhbz#1683745 - Issue is that with arcfour-hmac as first encryption type
+Patch62:        0001-Do-not-use-arcfour-hmac-md5-when-discovering-the-sal.patch
+
+# rhbz#1738573 - adcli update --add-samba-data does not work as expected
+Patch63:        0001-doc-explain-how-to-force-password-reset.patch
+
+# rhbz#1685138 - adcli info should send netlogin pings to all domain controllers, not only a subset
+Patch64:        0001-Make-adcli-info-DC-location-mechanism-more-compliant.patch
+
+BuildRequires:  intltool pkgconfig
+BuildRequires:  libtool
+BuildRequires:  gettext-devel
+BuildRequires:  krb5-devel
+BuildRequires:  openldap-devel
+BuildRequires:  libxslt
+BuildRequires:  xmlto
+
+Requires:       cyrus-sasl-gssapi
+
+# adcli no longer has a library of development files
+# the adcli tool itself is to be used by callers
+Obsoletes:      adcli-devel < 0.5
+
+%description
+adcli is a library and tool for joining an Active Directory domain using
+standard LDAP and Kerberos calls.
+
+%define _hardened_build 1
+
+%prep
+%autosetup -p1
+
+%build
+autoreconf --force --install --verbose
+%configure --disable-static --disable-silent-rules
+make %{?_smp_mflags}
+
+%check
+make check
+
+%install
+make install DESTDIR=%{buildroot}
+find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%clean
+
+%files
+%{_sbindir}/adcli
+%doc AUTHORS COPYING ChangeLog NEWS README
+%doc %{_datadir}/doc/adcli
+%doc %{_mandir}/*/*
+
+%changelog
+* Wed Sep 04 2019 Sumit Bose <sbose@redhat.com> - 0.8.1-12
+- adcli info should send netlogin pings to all domain controllers, not only a subset [#1685138]
+
+* Tue Aug 27 2019 Sumit Bose <sbose@redhat.com> - 0.8.1-11
+- Fixes and improvements for RHEL-7.8
+- Issue is that with arcfour-hmac as first encryption type in the config ... [#1683745]
+- adcli update --add-samba-data does not work as expected [#1738573]
+
+* Mon Aug 12 2019 Sumit Bose <sbose@redhat.com> - 0.8.1-10
+- adcli is failing with "Couldn't add keytab entries: FILE:/etc/krb5.keytab:
+  Cannot allocate memory" [1665162]
+
+* Thu May 02 2019 Sumit Bose <sbose@redhat.com> - 0.8.1-9
+- Fixes for RHEL-7.7 updates
+- additional patch for [RFE] adcli join should preserve SPN added by adcli
+  preset-computer [#1630187]
+- new patches for many adcli-krb5-????? directories are created /tmp [#1588596]
+
+* Fri Mar 22 2019 Sumit Bose <sbose@redhat.com> - 0.8.1-8
+- Various updates for RHEL-7.7
+- many adcli-krb5-????? directories are created /tmp [#1588596]
+- adcli exports kerberos ticket with old kvno [#1642546]
+- [RFE] Have `adcli join` work without FQDN in `hostname` output [#1595911]
+- Improve handling of service principals [#1644311]
+- [RFE] adcli command with --unix-* options doesn't update
+  values in UnixAttributes Tab for user [#1337489]
+- [RFE] adcli join should preserve SPN added by adcli preset-computer [#1630187]
+- [RFE] Need an option for adcli command which will show domain join status. [#1622583]
+
+* Wed Jan 16 2019 Sumit Bose <sbose@redhat.com> - 0.8.1-7
+- use autosetup macro to simplify patch handling
+- fixed rpmlint warnings in the spec file
+- join failed if hostname is not FQDN [#1649868]
+
+* Tue Aug 14 2018 Sumit Bose <sbose@redhat.com> - 0.8.1-6
+- Couldn't set service principals on computer account [#1608212]
+- additional fix #1547014 and #1593240
+
+* Tue Jun 19 2018 Sumit Bose <sbose@redhat.com> - 0.8.1-5
+- enable "Trust this computer for delegation to any service (Kerberos only)" [#1538730]
+- realm join fails with Insufficient permissions [#1542354]
+- adcli update option cannot add principals for computer object [#1545568]
+- adcli refuses to add service principals [#1547013]
+- [RFE] Support adding SPN of a different host [#1547014]
+- adcli segfaults during AD join RHEL 7.5 [#1575554]
+
+* Thu Nov 02 2017 Sumit Bose <sbose@redhat.com> - 0.8.1-4
+- adcli doesn't update kvno while joining system to AD domain (RODC) [#1471021]
+- adcli_tool_computer_delete description spelling [#1450179]
+- adcli man page should not only mention FILE type credential caches [#1423871]
+
+* Wed Aug 24 2016 Sumit Bose <sbose@redhat.com> - 0.8.1-3
+- fix crash when name is specified on the command line and detect names with
+  lower case characters [#1359773]
+
+* Mon Jul 25 2016 Sumit Bose <sbose@redhat.com> - 0.8.1-2
+- delete: use keytab data to determine realm and NetBIOS name [#1359773]
+* Thu May 19 2016 Sumit Bose <sbose@redhat.com> - 0.8.1-1
+- Update to upstream release 0.8.1
+- Rebase adcli in RHEL-7.3 to version 0.8.0 [#1292530]
+- Support Host Keytab renewal [#1288485]
+- realmd not joining AD so ssh gssapi-with-mic works [#1061371]
+- technically wrong length checks in binary parsers  [#1027905]
+- avoid alloca in _adcli_ldap_have_in_mod [#1027889]
+- [RFE] adcli --stdin-password should be able to strip newline character from
+  the input [#1134330]
+
+* Thu Jan 30 2014 Stef Walter <stefw@redhat.com> - 0.7.5-4
+- Fix incorrect ownership of manual page directory [#1057563]
+
+* Tue Jan 28 2014 Daniel Mach <dmach@redhat.com> - 0.7.5-3
+- Mass rebuild 2014-01-24
+
+* Fri Dec 27 2013 Daniel Mach <dmach@redhat.com> - 0.7.5-2
+- Mass rebuild 2013-12-27
+
+* Fri Sep 13 2013 Stef Walter <stefw@redhat.com> - 0.7.5-1
+- Update to upstream point release 0.7.5
+- Workaround for discovery via IPv6 address [#1004442]
+- Correctly put IPv6 addresses in temporary krb5.conf
+
+* Mon Sep 09 2013 Stef Walter <stefw@redhat.com> - 0.7.4-1
+- Update to upstream point release 0.7.4
+- Correctly handle truncating long host names [#1001667]
+- Try to contact all available addresses for discovery [#1004442]
+- Build fixes [#1004823]
+
+* Wed Aug 07 2013 Stef Walter <stefw@redhat.com> - 0.7.3-1
+- Update to upstream point release 0.7.3
+- Don't try to set encryption types on Windows 2003
+
+* Mon Jul 22 2013 Stef Walter <stefw@redhat.com> - 0.7.2-1
+- Update to upstream point release 0.7.2
+- Part of fix for bug [#967008]
+
+* Tue Jun 11 2013 Stef Walter <stefw@redhat.com> - 0.7.1-3
+- Run 'make check' when building the package
+
+* Mon May 13 2013 Stef Walter <stefw@redhat.com> - 0.7.1-2
+- Bump version to get around botched update
+
+* Mon May 13 2013 Stef Walter <stefw@redhat.com> - 0.7.1-1
+- Update to upstream 0.7.1 release
+- Fix problems with salt discovery [#961399]
+
+* Mon May 06 2013 Stef Walter <stefw@redhat.com> - 0.7-1
+- Work around broken krb5 with empty passwords [#960001]
+- Fix memory corruption issue [#959999]
+- Update to 0.7, fixing various bugs
+
+* Mon Apr 29 2013 Stef Walter <stefw@redhat.com> - 0.6-1
+- Update to 0.6, fixing various bugs
+
+* Wed Apr 10 2013 Stef walter <stefw@redhat.com> - 0.5-2
+- Add appropriate Obsoletes line for libadcli removal
+
+* Wed Apr 10 2013 Stef Walter <stefw@redhat.com> - 0.5-1
+- Update to upstream 0.5 version
+- No more libadcli, and thus no adcli-devel
+- Many new adcli commands
+- Documentation
+
+* Wed Feb 13 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.4-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
+
+* Mon Nov 12 2012 Stef Walter <stefw@redhat.com> - 0.4-1
+- Update for 0.4 version, fixing various bugs
+
+* Sat Oct 20 2012 Stef Walter <stefw@redhat.com> - 0.3-1
+- Update for 0.3 version
+
+* Tue Sep 4 2012 Stef Walter <stefw@redhat.com> - 0.2-1
+- Update for 0.2 version
+
+* Wed Aug 15 2012 Stef Walter <stefw@redhat.com> - 0.1-1
+- Initial 0.1 package