Hello community,

here is the log from the commit of package adcli.12089 for 
openSUSE:Leap:15.1:Update checked in at 2020-03-12 12:16:02
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Leap:15.1:Update/adcli.12089 (Old)
 and      /work/SRC/openSUSE:Leap:15.1:Update/.adcli.12089.new.3160 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "adcli.12089"

Thu Mar 12 12:16:02 2020 rev:1 rq:782523 version:0.8.2

Changes:
--------
New Changes file:

--- /dev/null   2020-03-10 18:28:06.918142398 +0100
+++ /work/SRC/openSUSE:Leap:15.1:Update/.adcli.12089.new.3160/adcli.changes     
2020-03-12 12:16:03.676599704 +0100
@@ -0,0 +1,46 @@
+-------------------------------------------------------------------
+Thu Feb 20 10:37:11 UTC 2020 - Samuel Cabrero <[email protected]>
+
+- Use GSS-SPNEGO if available (bsc#1162518)
+  * 0026-Use-GSS-SPNEGO-if-available.patch
+- Add option to use ldaps (bsc#1162518)
+  * 0027-add-option-use-ldaps.patch
+
+-------------------------------------------------------------------
+Thu Nov 21 18:14:44 UTC 2019 - Samuel Cabrero <[email protected]>
+
+- The userPrincipalName attribute was not added to the created
+  machine (bsc#1157491)
+- The host FQDN was not properly detected (bsc#1157491)
+- Added patches:
+  * 0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch
+  * 0002-Use-strdup-if-offsets-are-used.patch
+  * 0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch
+  * 0004-doc-explain-that-all-credential-cache-types-are-supp.patch
+  * 0005-library-add-adcli_conn_is_writeable.patch
+  * 0006-Handle-kvno-increment-for-RODCs.patch
+  * 0007-library-add-_adcli_bin_sid_to_str.patch
+  * 0008-library-add-_adcli_call_external_program.patch
+  * 0009-library-add-_adcli_ldap_parse_sid.patch
+  * 0010-library-add-lookup_domain_sid.patch
+  * 0011-library-add-adcli_conn_get_domain_sid.patch
+  * 0012-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch
+  * 0013-Add-trusted-for-delegation-option.patch
+  * 0014-Only-update-attributes-given-on-the-command-line.patch
+  * 0015-update-allow-to-add-service-names.patch
+  * 0016-Calculate-enctypes-in-a-separate-function.patch
+  * 0017-join-add-all-attributes-while-creating-computer-obje.patch
+  * 0018-util-add-_adcli_strv_remove_unsorted.patch
+  * 0019-Add-add-service-principal-and-remove-service-princip.patch
+  * 0020-Do-not-add-service-principals-twice.patch
+  * 0021-Do-not-depend-on-default_realm-in-krb5.conf.patch
+  * 0022-adutil-add-_adcli_strv_add_unique.patch
+  * 0023-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch
+  * 0024-Increment-kvno-after-password-change-with-user-creds.patch
+  * 0025-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch
+
+-------------------------------------------------------------------
+Fri Sep 21 14:28:21 UTC 2018 - [email protected]
+
+- Initial build (fate#326619, bsc#1109849)
+

New:
----
  0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch
  0002-Use-strdup-if-offsets-are-used.patch
  0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch
  0004-doc-explain-that-all-credential-cache-types-are-supp.patch
  0005-library-add-adcli_conn_is_writeable.patch
  0006-Handle-kvno-increment-for-RODCs.patch
  0007-library-add-_adcli_bin_sid_to_str.patch
  0008-library-add-_adcli_call_external_program.patch
  0009-library-add-_adcli_ldap_parse_sid.patch
  0010-library-add-lookup_domain_sid.patch
  0011-library-add-adcli_conn_get_domain_sid.patch
  0012-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch
  0013-Add-trusted-for-delegation-option.patch
  0014-Only-update-attributes-given-on-the-command-line.patch
  0015-update-allow-to-add-service-names.patch
  0016-Calculate-enctypes-in-a-separate-function.patch
  0017-join-add-all-attributes-while-creating-computer-obje.patch
  0018-util-add-_adcli_strv_remove_unsorted.patch
  0019-Add-add-service-principal-and-remove-service-princip.patch
  0020-Do-not-add-service-principals-twice.patch
  0021-Do-not-depend-on-default_realm-in-krb5.conf.patch
  0022-adutil-add-_adcli_strv_add_unique.patch
  0023-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch
  0024-Increment-kvno-after-password-change-with-user-creds.patch
  0025-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch
  0026-Use-GSS-SPNEGO-if-available.patch
  0027-add-option-use-ldaps.patch
  adcli-0.8.2.tar.gz
  adcli-0.8.2.tar.gz.sig
  adcli.changes
  adcli.spec

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ adcli.spec ++++++
#
# spec file for package adcli
#
# Copyright (c) 2018 Chris Kowalczyk <[email protected]>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
#
# Please submit bugfixes or comments via http://bugs.opensuse.org/
#

Name:           adcli
Version:        0.8.2
Release:        0
Summary:        Tool for performing actions on an Active Directory domain
License:        LGPL-2.0+
Group:          System/Libraries
URL:            http://cgit.freedesktop.org/realmd/adcli
Source0:        
http://www.freedesktop.org/software/realmd/releases/%{name}-%{version}.tar.gz
Source1:        
http://www.freedesktop.org/software/realmd/releases/%{name}-%{version}.tar.gz.sig
BuildRequires:  automake
BuildRequires:  libxslt-tools
BuildRequires:  openldap2-devel
BuildRequires:  xmlto
BuildRequires:  pkgconfig(libsasl2)
BuildRequires:  pkgconfig(mit-krb5)

BuildRoot:      %{_tmppath}/%{name}-%{version}-build

Patch1:         0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch
Patch2:         0002-Use-strdup-if-offsets-are-used.patch
Patch3:         0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch
Patch4:         0004-doc-explain-that-all-credential-cache-types-are-supp.patch
Patch5:         0005-library-add-adcli_conn_is_writeable.patch
Patch6:         0006-Handle-kvno-increment-for-RODCs.patch
Patch7:         0007-library-add-_adcli_bin_sid_to_str.patch
Patch8:         0008-library-add-_adcli_call_external_program.patch
Patch9:         0009-library-add-_adcli_ldap_parse_sid.patch
Patch10:        0010-library-add-lookup_domain_sid.patch
Patch11:        0011-library-add-adcli_conn_get_domain_sid.patch
Patch12:        0012-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch
Patch13:        0013-Add-trusted-for-delegation-option.patch
Patch14:        0014-Only-update-attributes-given-on-the-command-line.patch
Patch15:        0015-update-allow-to-add-service-names.patch
Patch16:        0016-Calculate-enctypes-in-a-separate-function.patch
Patch17:        0017-join-add-all-attributes-while-creating-computer-obje.patch
Patch18:        0018-util-add-_adcli_strv_remove_unsorted.patch
Patch19:        0019-Add-add-service-principal-and-remove-service-princip.patch
Patch20:        0020-Do-not-add-service-principals-twice.patch
Patch21:        0021-Do-not-depend-on-default_realm-in-krb5.conf.patch
Patch22:        0022-adutil-add-_adcli_strv_add_unique.patch
Patch23:        0023-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch
Patch24:        0024-Increment-kvno-after-password-change-with-user-creds.patch
Patch25:        0025-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch
Patch26:        0026-Use-GSS-SPNEGO-if-available.patch
Patch27:        0027-add-option-use-ldaps.patch

%description
Library of routines for joining a machine to Active Directory (without samba)
and managing machine accounts there. Will probably expand to cover other AD
related tasks as well.

%package doc
Summary:        Documentation for adcli
Group:          Documentation/Other
Requires:       %{name} = %{version}

%description doc
Library of routines for joining a machine to Active Directory (without samba)
and managing machine accounts there. Will probably expand to cover other AD
related tasks as well.

This package contains the documentation for adcli.

%prep
%setup -q

%autopatch -p1

%build
%configure --disable-static \
           --disable-silent-rules
make %{?_smp_mflags}

%install
%makeinstall
#Remove zero-length file.
pushd %{buildroot}/%{_datadir}/doc/%{name}
rm adcli-docs.proc
popd

%files
%defattr(-,root,root)
%doc AUTHORS ChangeLog COPYING NEWS README
%{_sbindir}/%{name}
%{_mandir}/man8/adcli.8%{?ext_man}

%files doc
%defattr(-,root,root)
%doc %{_datadir}/doc/%{name}

%changelog
++++++ 0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch ++++++
>From 8ea169d82a9c4047c254cca49ce6243cfc5941b5 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Wed, 24 Aug 2016 15:37:41 +0200
Subject: [PATCH 01/25] 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.
---
 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.16.4

++++++ 0002-Use-strdup-if-offsets-are-used.patch ++++++
>From 0fd80844d4779a1ddcb2b920b34d458ed01ca17a Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Wed, 24 Aug 2016 16:19:36 +0200
Subject: [PATCH 02/25] Use strdup() if offsets 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.16.4

++++++ 0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch ++++++
>From fa4a2371c24058877036de7f7c9ed0b85f82772c Mon Sep 17 00:00:00 2001
From: Striker Leggette <[email protected]>
Date: Wed, 1 Nov 2017 11:16:39 +0100
Subject: [PATCH 03/25] 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.16.4

++++++ 0004-doc-explain-that-all-credential-cache-types-are-supp.patch ++++++
>From 7bedf4494c66eed6e3e2f943f98cdbf87dfd1c21 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Wed, 1 Nov 2017 12:01:18 +0100
Subject: [PATCH 04/25] 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.16.4

++++++ 0005-library-add-adcli_conn_is_writeable.patch ++++++
>From f5eb8fdcfe9d66328cd152ef77a73698e6230c59 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Wed, 1 Nov 2017 16:29:19 +0100
Subject: [PATCH 05/25] library: add adcli_conn_is_writeable()

---
 library/adconn.c | 11 +++++++++++
 library/adconn.h |  2 ++
 2 files changed, 13 insertions(+)

diff --git a/library/adconn.c b/library/adconn.c
index a294dfd..0786ed4 100644
--- a/library/adconn.c
+++ b/library/adconn.c
@@ -1528,3 +1528,14 @@ adcli_conn_server_has_capability (adcli_conn *conn,
 
        return 0;
 }
+
+bool adcli_conn_is_writeable (adcli_conn *conn)
+{
+       disco_dance_if_necessary (conn);
+
+       if (conn->domain_disco == NULL) {
+               return false;
+       }
+
+       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.16.4

++++++ 0006-Handle-kvno-increment-for-RODCs.patch ++++++
>From 320f776279b99a63a12c7136ef251f7e8805c13c Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Wed, 1 Nov 2017 17:14:05 +0100
Subject: [PATCH 06/25] 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 05885d0..bb970d1 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.16.4

++++++ 0007-library-add-_adcli_bin_sid_to_str.patch ++++++
>From d708d219843c1c3895b9373e08dc33ef4a30c4bf Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Tue, 30 Jan 2018 14:37:05 +0100
Subject: [PATCH 07/25] library: add _adcli_bin_sid_to_str()

Convert a binary SID to the string representation.

https://bugs.freedesktop.org/show_bug.cgi?id=100118
https://gitlab.freedesktop.org/realmd/adcli/issues/6

Reviewed-by: Jakub Hrozek <[email protected]>
---
 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 21ccd27..a057763 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)
@@ -507,6 +584,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[])
@@ -515,6 +627,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.16.4

++++++ 0008-library-add-_adcli_call_external_program.patch ++++++
>From e70607d0842e27b5e3c71ab52345886ab0ddc838 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Tue, 30 Jan 2018 14:39:17 +0100
Subject: [PATCH 08/25] 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
https://gitlab.freedesktop.org/realmd/adcli/issues/6

Reviewed-by: Jakub Hrozek <[email protected]>
---
 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 a057763..14df42e 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"
@@ -619,6 +775,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[])
@@ -628,6 +838,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.16.4

++++++ 0009-library-add-_adcli_ldap_parse_sid.patch ++++++
>From 3c48f8c054990712faf76f12c95897e19c9c6f25 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Tue, 30 Jan 2018 14:44:45 +0100
Subject: [PATCH 09/25] 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
https://gitlab.freedesktop.org/realmd/adcli/issues/6

Reviewed-by: Jakub Hrozek <[email protected]>
---
 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.16.4

++++++ 0010-library-add-lookup_domain_sid.patch ++++++
>From d70a19b0f5f80b5c1d3b7f6a8f241b0e8605668b Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Tue, 30 Jan 2018 14:40:46 +0100
Subject: [PATCH 10/25] 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
https://gitlab.freedesktop.org/realmd/adcli/issues/6

Reviewed-by: Jakub Hrozek <[email protected]>
---
 library/adconn.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/library/adconn.c b/library/adconn.c
index 0786ed4..efba351 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.16.4

++++++ 0011-library-add-adcli_conn_get_domain_sid.patch ++++++
>From dacafea2ec5b85f4be507cf86b0dbd9c301f8ade Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Tue, 30 Jan 2018 18:23:03 +0100
Subject: [PATCH 11/25] library: add adcli_conn_get_domain_sid()

https://bugs.freedesktop.org/show_bug.cgi?id=100118
https://gitlab.freedesktop.org/realmd/adcli/issues/6

Reviewed-by: Jakub Hrozek <[email protected]>
---
 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 efba351..e2250e3 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.16.4

++++++ 0012-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch ++++++
>From 4a4a63c08d24dba2c33751febffaee99c360712f Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Tue, 30 Jan 2018 14:39:46 +0100
Subject: [PATCH 12/25] Fix memory leak in test_check_nt_time_string_lifetime

The test added with 650e5d33ef31437a049fb454ad3dc5457c56abe7 introduced
a small memory leak.

Reviewed-by: Jakub Hrozek <[email protected]>
---
 library/adutil.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/library/adutil.c b/library/adutil.c
index 14df42e..a27bd68 100644
--- a/library/adutil.c
+++ b/library/adutil.c
@@ -734,6 +734,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.16.4

++++++ 0013-Add-trusted-for-delegation-option.patch ++++++
>From 327f6bdd5c03406d7c3d1945a8d433eb45123369 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Thu, 31 May 2018 18:27:37 +0200
Subject: [PATCH 13/25] 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 c54cc1b..2cd56fb 100644
--- a/doc/adcli.xml
+++ b/doc/adcli.xml
@@ -280,6 +280,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
@@ -376,6 +383,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 bb970d1..691d993 100644
--- a/library/adenroll.c
+++ b/library/adenroll.c
@@ -59,6 +59,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;
@@ -100,6 +107,7 @@ struct _adcli_enroll {
        int keytab_enctypes_explicit;
        unsigned int computer_password_lifetime;
        int computer_password_lifetime_explicit;
+       bool trusted_for_delegation;
 };
 
 static adcli_result
@@ -533,6 +541,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);
 
        /*
@@ -966,6 +978,7 @@ retrieve_computer_account (adcli_enroll *enroll)
                "operatingSystemVersion",
                "operatingSystemServicePack",
                "pwdLastSet",
+               "userAccountControl",
                NULL,
        };
 
@@ -1144,6 +1157,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)
 {
@@ -1162,11 +1216,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) {
@@ -2304,3 +2363,20 @@ adcli_enroll_set_computer_password_lifetime 
(adcli_enroll *enroll,
 
        enroll->computer_password_lifetime_explicit = 1;
 }
+
+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 9a107ab..ccf19e7 100644
--- a/library/adenroll.h
+++ b/library/adenroll.h
@@ -108,6 +108,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 a3d0f03..5348648 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_trusted_for_delegation,
 } Option;
 
 static adcli_tool_desc common_usages[] = {
@@ -132,6 +133,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"
@@ -262,6 +265,13 @@ parse_option (Option opt,
 
                adcli_enroll_set_computer_password_lifetime (enroll, lifetime);
                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;
 
@@ -324,6 +334,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 },
                { "verbose", no_argument, NULL, opt_verbose },
@@ -423,6 +434,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 },
                { "verbose", no_argument, NULL, opt_verbose },
-- 
2.16.4

++++++ 0014-Only-update-attributes-given-on-the-command-line.patch ++++++
>From 4bffff31b08347dccf13cdf43776ac7d36e58d1f Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Fri, 1 Jun 2018 21:26:47 +0200
Subject: [PATCH 14/25] 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 691d993..1cc8ffc 100644
--- a/library/adenroll.c
+++ b/library/adenroll.c
@@ -95,8 +95,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;
@@ -108,6 +111,7 @@ struct _adcli_enroll {
        unsigned int computer_password_lifetime;
        int computer_password_lifetime_explicit;
        bool trusted_for_delegation;
+       int trusted_for_delegation_explicit;
 };
 
 static adcli_result
@@ -1207,7 +1211,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 };
@@ -1215,7 +1223,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 };
@@ -1235,12 +1243,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, };
@@ -2282,6 +2303,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 *
@@ -2299,6 +2321,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 *
@@ -2316,6 +2339,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 *
@@ -2379,4 +2403,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.16.4

++++++ 0015-update-allow-to-add-service-names.patch ++++++
>From 8629dd3a8be410d7d13e034af5f07adb26d52da4 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Mon, 4 Jun 2018 10:49:33 +0200
Subject: [PATCH 15/25] 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 1cc8ffc..6b6630f 100644
--- a/library/adenroll.c
+++ b/library/adenroll.c
@@ -300,13 +300,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;
 
@@ -314,20 +338,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;
@@ -351,6 +362,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 */
@@ -1261,7 +1273,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, };
@@ -1514,7 +1526,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;
@@ -1542,41 +1555,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;
+               }
        }
 
 
@@ -1586,7 +1605,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;
@@ -1603,7 +1623,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)
@@ -1765,6 +1785,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_NO_KEYTAB)
@@ -1775,7 +1809,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
@@ -1876,7 +1910,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.16.4

++++++ 0016-Calculate-enctypes-in-a-separate-function.patch ++++++
>From 6939dff54597f761902158e73e0880cc53f6477c Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Wed, 6 Jun 2018 16:31:32 +0200
Subject: [PATCH 16/25] 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 6b6630f..923a811 100644
--- a/library/adenroll.c
+++ b/library/adenroll.c
@@ -537,6 +537,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)
@@ -1048,75 +1125,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.16.4

++++++ 0017-join-add-all-attributes-while-creating-computer-obje.patch ++++++
>From 07ea3d55f318b7feeef08ee1c160112310828dd2 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Mon, 11 Jun 2018 09:44:49 +0200
Subject: [PATCH 17/25] 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 923a811..deaef0f 100644
--- a/library/adenroll.c
+++ b/library/adenroll.c
@@ -568,7 +568,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");
 
@@ -613,7 +613,6 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype)
        return ADCLI_SUCCESS;
 }
 
-
 static adcli_result
 create_computer_account (adcli_enroll *enroll,
                          LDAP *ldap)
@@ -623,22 +622,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.16.4

++++++ 0018-util-add-_adcli_strv_remove_unsorted.patch ++++++
>From db2878d7b06c6618e5585eb7ce8f96ce932d5348 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Thu, 14 Jun 2018 16:48:22 +0200
Subject: [PATCH 18/25] 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.16.4

++++++ 0019-Add-add-service-principal-and-remove-service-princip.patch ++++++
>From 2f77e6338346c738eb2d75404d8db39aab4c8977 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Thu, 14 Jun 2018 16:49:26 +0200
Subject: [PATCH 19/25] 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 2cd56fb..7003e5f 100644
--- a/doc/adcli.xml
+++ b/doc/adcli.xml
@@ -287,6 +287,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
@@ -390,6 +398,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 deaef0f..df1f551 100644
--- a/library/adenroll.c
+++ b/library/adenroll.c
@@ -91,6 +91,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;
 
@@ -327,6 +330,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)
@@ -338,10 +378,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
@@ -1588,6 +1632,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,
@@ -1697,6 +1774,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;
 }
 
@@ -1978,8 +2066,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;
@@ -2510,3 +2601,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 ccf19e7..5a24c42 100644
--- a/library/adenroll.h
+++ b/library/adenroll.h
@@ -97,6 +97,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 5348648..7a0c6f5 100644
--- a/tools/computer.c
+++ b/tools/computer.c
@@ -107,6 +107,8 @@ typedef enum {
        opt_user_principal,
        opt_computer_password_lifetime,
        opt_trusted_for_delegation,
+       opt_add_service_principal,
+       opt_remove_service_principal,
 } Option;
 
 static adcli_tool_desc common_usages[] = {
@@ -135,6 +137,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"
@@ -272,6 +276,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;
 
@@ -335,6 +345,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 },
                { "verbose", no_argument, NULL, opt_verbose },
@@ -435,6 +446,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 },
                { "verbose", no_argument, NULL, opt_verbose },
-- 
2.16.4

++++++ 0020-Do-not-add-service-principals-twice.patch ++++++
>From 85332e1818129ead6d9973b85918c54a04e3706e Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Mon, 13 Aug 2018 17:32:24 +0200
Subject: [PATCH 20/25] 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 df1f551..f942ea2 100644
--- a/library/adenroll.c
+++ b/library/adenroll.c
@@ -308,6 +308,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);
@@ -316,14 +317,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.16.4

++++++ 0021-Do-not-depend-on-default_realm-in-krb5.conf.patch ++++++
>From 058f1d18d0ce47c3bbfa090a3da03e5bdb111276 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Mon, 13 Aug 2018 18:24:58 +0200
Subject: [PATCH 21/25] 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 f942ea2..d23870a 100644
--- a/library/adenroll.c
+++ b/library/adenroll.c
@@ -1656,7 +1656,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.16.4

++++++ 0022-adutil-add-_adcli_strv_add_unique.patch ++++++
>From 4ee9d518c66b2f229a74d55b5491f51483fb5d29 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Fri, 16 Nov 2018 13:32:33 +0100
Subject: [PATCH 22/25] 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 6334b52..08da731 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.16.4

++++++ 0023-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch ++++++
>From 371cdbe4b003c8b70cd6a4060ef5637b341eee0f Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Fri, 16 Nov 2018 13:32:59 +0100
Subject: [PATCH 23/25] 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 d23870a..53bd440 100644
--- a/library/adenroll.c
+++ b/library/adenroll.c
@@ -308,7 +308,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);
@@ -317,28 +316,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);
                }
        }
 
@@ -359,9 +344,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;
                        }
@@ -1520,7 +1505,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.16.4

++++++ 0024-Increment-kvno-after-password-change-with-user-creds.patch ++++++
>From dd288de866d25058f06eb45084e7e80dadebae14 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Fri, 15 Mar 2019 14:31:12 +0100
Subject: [PATCH 24/25] 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 53bd440..6a17d92 100644
--- a/library/adenroll.c
+++ b/library/adenroll.c
@@ -1052,6 +1052,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.16.4

++++++ 0025-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch ++++++
>From fd5b6bd9fd2c79b438349bada2c1a80f44daae0f Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Fri, 15 Mar 2019 17:33:44 +0100
Subject: [PATCH 25/25] 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 7003e5f..2fe9309 100644
--- a/doc/adcli.xml
+++ b/doc/adcli.xml
@@ -225,7 +225,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.16.4

++++++ 0026-Use-GSS-SPNEGO-if-available.patch ++++++
>From 89fde01eaffd84630d0088d49b18b084aa807b65 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Fri, 11 Oct 2019 16:39:25 +0200
Subject: [PATCH 1/2] Use GSS-SPNEGO if available

Currently adcli uses the GSSAPI SASL mechanism for LDAP authentication
and to establish encryption. While this works in general it does not
handle some of the more advanced features which can be required by AD
DCs.

The GSS-SPNEGO mechanism can handle them and is used with this patch by
adcli if the AD DC indicates that it supports it.

Related to https://bugzilla.redhat.com/show_bug.cgi?id=1762420

(cherry picked from commit a6f795ba3d6048b32d7863468688bf7f42b2cafd)
---
 library/adconn.c | 35 ++++++++++++++++++++++++++++++++++-
 library/adconn.h |  3 +++
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/library/adconn.c b/library/adconn.c
index f6c23d3..a3f4548 100644
--- a/library/adconn.c
+++ b/library/adconn.c
@@ -77,6 +77,7 @@ struct _adcli_conn_ctx {
        char *default_naming_context;
        char *configuration_naming_context;
        char **supported_capabilities;
+       char **supported_sasl_mechs;
 
        /* Connect state */
        LDAP *ldap;
@@ -845,6 +846,7 @@ connect_and_lookup_naming (adcli_conn *conn,
                "defaultNamingContext",
                "configurationNamingContext",
                "supportedCapabilities",
+               "supportedSASLMechanisms",
                NULL
        };
 
@@ -897,6 +899,11 @@ connect_and_lookup_naming (adcli_conn *conn,
                                                                         
"supportedCapabilities");
        }
 
+       if (conn->supported_sasl_mechs == NULL) {
+               conn->supported_sasl_mechs = _adcli_ldap_parse_values (ldap, 
results,
+                                                                      
"supportedSASLMechanisms");
+       }
+
        ldap_msgfree (results);
 
        if (conn->default_naming_context == NULL) {
@@ -1022,6 +1029,7 @@ authenticate_to_directory (adcli_conn *conn)
        OM_uint32 minor;
        ber_len_t ssf;
        int ret;
+       const char *mech = "GSSAPI";
 
        if (conn->ldap_authenticated)
                return ADCLI_SUCCESS;
@@ -1038,7 +1046,11 @@ authenticate_to_directory (adcli_conn *conn)
        ret = ldap_set_option (conn->ldap, LDAP_OPT_X_SASL_SSF_MIN, &ssf);
        return_unexpected_if_fail (ret == 0);
 
-       ret = ldap_sasl_interactive_bind_s (conn->ldap, NULL, "GSSAPI", NULL, 
NULL,
+       if (adcli_conn_server_has_sasl_mech (conn, "GSS-SPNEGO")) {
+               mech =  "GSS-SPNEGO";
+       }
+
+       ret = ldap_sasl_interactive_bind_s (conn->ldap, NULL, mech, NULL, NULL,
                                            LDAP_SASL_QUIET, sasl_interact, 
NULL);
 
        /* Clear the credential cache GSSAPI to use (for this thread) */
@@ -1231,6 +1243,7 @@ conn_free (adcli_conn *conn)
        free (conn->default_naming_context);
        free (conn->configuration_naming_context);
        _adcli_strv_free (conn->supported_capabilities);
+       _adcli_strv_free (conn->supported_sasl_mechs);
 
        free (conn->computer_name);
        free (conn->host_fqdn);
@@ -1593,6 +1606,26 @@ adcli_conn_server_has_capability (adcli_conn *conn,
        return 0;
 }
 
+bool
+adcli_conn_server_has_sasl_mech (adcli_conn *conn,
+                                 const char *mech)
+{
+       int i;
+
+       return_val_if_fail (conn != NULL, false);
+       return_val_if_fail (mech != NULL, false);
+
+       if (!conn->supported_sasl_mechs)
+               return false;
+
+       for (i = 0; conn->supported_sasl_mechs[i] != NULL; i++) {
+               if (strcasecmp (mech, conn->supported_sasl_mechs[i]) == 0)
+                       return true;
+       }
+
+       return false;
+}
+
 bool adcli_conn_is_writeable (adcli_conn *conn)
 {
        disco_dance_if_necessary (conn);
diff --git a/library/adconn.h b/library/adconn.h
index 13cfd32..8e88045 100644
--- a/library/adconn.h
+++ b/library/adconn.h
@@ -146,6 +146,9 @@ 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_server_has_sasl_mech  (adcli_conn *conn,
+                                                      const char *mech);
+
 bool                adcli_conn_is_writeable          (adcli_conn *conn);
 
 #endif /* ADCONN_H_ */
-- 
2.25.0

++++++ 0027-add-option-use-ldaps.patch ++++++
>From 41db84aac101a54c4b0ef78119ccf9905f0691fe Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Thu, 19 Dec 2019 07:22:33 +0100
Subject: [PATCH 2/2] add option use-ldaps

In general using the LDAP port with GSS-SPNEGO should satifiy all
requirements an AD DC should have for authentication on an encrypted
LDAP connection.

But if e.g. the LDAP port is blocked by a firewall using the LDAPS port
with TLS encryption might be an alternative. For this use case the
--use-ldaps option is added.

Related to https://bugzilla.redhat.com/show_bug.cgi?id=1762420

(cherry picked from commit 85097245b57f190337225dbdbf6e33b58616c092)
---
 doc/adcli.xml    | 24 +++++++++++++++
 library/adconn.c | 79 ++++++++++++++++++++++++++++++++++++++++++------
 library/adconn.h |  4 +++
 tools/computer.c |  9 ++++++
 tools/entry.c    | 11 +++++++
 5 files changed, 118 insertions(+), 9 deletions(-)

diff --git a/doc/adcli.xml b/doc/adcli.xml
index 2fe9309..f257c88 100644
--- a/doc/adcli.xml
+++ b/doc/adcli.xml
@@ -117,6 +117,30 @@
                        If not specified then an appropriate domain controller
                        is automatically discovered.</para></listitem>
                </varlistentry>
+               <varlistentry>
+                       <term><option>--use-ldaps</option></term>
+                       <listitem><para>Connect to the domain controller
+                       with LDAPS. By default the LDAP port is used and SASL
+                       GSS-SPNEGO or GSSAPI is used for authentication and to
+                       establish encryption. This should satisfy all
+                       requirements set on the server side and LDAPS should
+                       only be used if the LDAP port is not accessible due to
+                       firewalls or other reasons.</para>
+                       <para> Please note that the place where CA certificates
+                       can be found to validate the AD DC certificates
+                       must be configured in the OpenLDAP configuration
+                       file, e.g. <filename>/etc/openldap/ldap.conf</filename>.
+                       As an alternative it can be specified with the help of
+                       an environment variable, e.g.
+<programlisting>
+$ LDAPTLS_CACERT=/path/to/ad_dc_ca_cert.pem adcli join --use-ldaps -D 
domain.example.com
+...
+</programlisting>
+                       Please see
+                       <citerefentry><refentrytitle>ldap.conf</refentrytitle>
+                       <manvolnum>5</manvolnum></citerefentry> for details.
+                       </para></listitem>
+               </varlistentry>
                <varlistentry>
                        <term><option>-C, 
--login-ccache=<parameter>ccache_name</parameter></option></term>
                        <listitem><para>Use the specified kerberos credential
diff --git a/library/adconn.c b/library/adconn.c
index a3f4548..8a55776 100644
--- a/library/adconn.c
+++ b/library/adconn.c
@@ -70,6 +70,7 @@ struct _adcli_conn_ctx {
        char *domain_name;
        char *domain_realm;
        char *domain_controller;
+       bool use_ldaps;
        char *canonical_host;
        char *domain_short;
        char *domain_sid;
@@ -773,7 +774,8 @@ int ldap_init_fd (ber_socket_t fd, int proto, LDAP_CONST 
char *url, struct ldap
 
 static LDAP *
 connect_to_address (const char *host,
-                    const char *canonical_host)
+                    const char *canonical_host,
+                    bool use_ldaps)
 {
        struct addrinfo *res = NULL;
        struct addrinfo *ai;
@@ -783,6 +785,16 @@ connect_to_address (const char *host,
        char *url;
        int sock;
        int rc;
+       int opt_rc;
+       const char *port = "389";
+       const char *proto = "ldap";
+       const char *errmsg = NULL;
+
+       if (use_ldaps) {
+               port = "636";
+               proto = "ldaps";
+               _adcli_info ("Using LDAPS to connect to %s", host);
+       }
 
        memset (&hints, '\0', sizeof(hints));
 #ifdef AI_ADDRCONFIG
@@ -794,7 +806,7 @@ connect_to_address (const char *host,
        if (!canonical_host)
                canonical_host = host;
 
-       rc = getaddrinfo (host, "389", &hints, &res);
+       rc = getaddrinfo (host, port, &hints, &res);
        if (rc != 0) {
                _adcli_err ("Couldn't resolve host name: %s: %s", host, 
gai_strerror (rc));
                return NULL;
@@ -810,7 +822,7 @@ connect_to_address (const char *host,
                        close (sock);
                } else {
                        error = 0;
-                       if (asprintf (&url, "ldap://%s";, canonical_host) < 0)
+                       if (asprintf (&url, "%s://%s", proto, canonical_host) < 
0)
                                return_val_if_reached (NULL);
                        rc = ldap_init_fd (sock, 1, url, &ldap);
                        free (url);
@@ -820,6 +832,25 @@ connect_to_address (const char *host,
                                            ldap_err2string (rc));
                                break;
                        }
+
+                       if (use_ldaps) {
+                               rc = ldap_install_tls (ldap);
+                               if (rc != LDAP_SUCCESS) {
+                                       opt_rc = ldap_get_option (ldap,
+                                                                 
LDAP_OPT_DIAGNOSTIC_MESSAGE,
+                                                                 (void *) 
&errmsg);
+                                       if (opt_rc != LDAP_SUCCESS) {
+                                               errmsg = NULL;
+                                       }
+                                       _adcli_err ("Couldn't initialize TLS 
[%s]: %s",
+                                                   ldap_err2string (rc),
+                                                   errmsg == NULL ? "- no 
details -"
+                                                                  : errmsg);
+                                       ldap_unbind_ext_s (ldap, NULL, NULL);
+                                       ldap = NULL;
+                                       break;
+                               }
+                       }
                }
        }
 
@@ -856,7 +887,8 @@ connect_and_lookup_naming (adcli_conn *conn,
        if (!canonical_host)
                canonical_host = disco->host_addr;
 
-       ldap = connect_to_address (disco->host_addr, canonical_host);
+       ldap = connect_to_address (disco->host_addr, canonical_host,
+                                  adcli_conn_get_use_ldaps (conn));
        if (ldap == NULL)
                return ADCLI_ERR_DIRECTORY;
 
@@ -1041,14 +1073,28 @@ authenticate_to_directory (adcli_conn *conn)
        status = gss_krb5_ccache_name (&minor, conn->login_ccache_name, NULL);
        return_unexpected_if_fail (status == 0);
 
-       /* Clumsily tell ldap + cyrus-sasl that we want encryption */
-       ssf = 1;
-       ret = ldap_set_option (conn->ldap, LDAP_OPT_X_SASL_SSF_MIN, &ssf);
-       return_unexpected_if_fail (ret == 0);
+       if (adcli_conn_get_use_ldaps (conn)) {
+               /* do not use SASL encryption on LDAPS connection */
+               ssf = 0;
+               ret = ldap_set_option (conn->ldap, LDAP_OPT_X_SASL_SSF_MIN, 
&ssf);
+               return_unexpected_if_fail (ret == 0);
+               ret = ldap_set_option (conn->ldap, LDAP_OPT_X_SASL_SSF_MAX, 
&ssf);
+               return_unexpected_if_fail (ret == 0);
+       } else {
+               /* Clumsily tell ldap + cyrus-sasl that we want encryption */
+               ssf = 1;
+               ret = ldap_set_option (conn->ldap, LDAP_OPT_X_SASL_SSF_MIN, 
&ssf);
+               return_unexpected_if_fail (ret == 0);
+       }
 
-       if (adcli_conn_server_has_sasl_mech (conn, "GSS-SPNEGO")) {
+       /* There are issues with cryrus-sasl and GSS-SPNEGO with TLS even if
+        * ssf_max is set to 0. To be on the safe side GSS-SPNEGO is only used
+        * without LDAPS. */
+       if (adcli_conn_server_has_sasl_mech (conn, "GSS-SPNEGO")
+                            && !adcli_conn_get_use_ldaps (conn)) {
                mech =  "GSS-SPNEGO";
        }
+       _adcli_info ("Using %s for SASL bind", mech);
 
        ret = ldap_sasl_interactive_bind_s (conn->ldap, NULL, mech, NULL, NULL,
                                            LDAP_SASL_QUIET, sasl_interact, 
NULL);
@@ -1230,6 +1276,7 @@ adcli_conn_new (const char *domain_name)
        conn->refs = 1;
        conn->logins_allowed = ADCLI_LOGIN_COMPUTER_ACCOUNT | 
ADCLI_LOGIN_USER_ACCOUNT;
        adcli_conn_set_domain_name (conn, domain_name);
+       adcli_conn_set_use_ldaps (conn, false);
        return conn;
 }
 
@@ -1389,6 +1436,20 @@ adcli_conn_set_domain_controller (adcli_conn *conn,
        no_more_disco (conn);
 }
 
+bool
+adcli_conn_get_use_ldaps (adcli_conn *conn)
+{
+       return_val_if_fail (conn != NULL, NULL);
+       return conn->use_ldaps;
+}
+
+void
+adcli_conn_set_use_ldaps (adcli_conn *conn, bool value)
+{
+       return_if_fail (conn != NULL);
+       conn->use_ldaps = value;
+}
+
 const char *
 adcli_conn_get_domain_short (adcli_conn *conn)
 {
diff --git a/library/adconn.h b/library/adconn.h
index 8e88045..3e287b1 100644
--- a/library/adconn.h
+++ b/library/adconn.h
@@ -89,6 +89,10 @@ const char *        adcli_conn_get_domain_controller 
(adcli_conn *conn);
 void                adcli_conn_set_domain_controller (adcli_conn *conn,
                                                       const char *value);
 
+bool                adcli_conn_get_use_ldaps         (adcli_conn *conn);
+void                adcli_conn_set_use_ldaps         (adcli_conn *conn,
+                                                      bool value);
+
 const char *        adcli_conn_get_domain_short      (adcli_conn *conn);
 
 const char *        adcli_conn_get_domain_sid        (adcli_conn *conn);
diff --git a/tools/computer.c b/tools/computer.c
index 7a0c6f5..bb77963 100644
--- a/tools/computer.c
+++ b/tools/computer.c
@@ -109,12 +109,14 @@ typedef enum {
        opt_trusted_for_delegation,
        opt_add_service_principal,
        opt_remove_service_principal,
+       opt_use_ldaps,
 } Option;
 
 static adcli_tool_desc common_usages[] = {
        { opt_domain, "active directory domain name" },
        { opt_domain_realm, "kerberos realm for the domain" },
        { opt_domain_controller, "domain controller to connect to" },
+       { opt_use_ldaps, "use LDAPS port for communication" },
        { opt_host_fqdn, "override the fully qualified domain name of the\n"
                         "local machine" },
        { opt_host_keytab, "filename for the host kerberos keytab" },
@@ -282,6 +284,9 @@ parse_option (Option opt,
        case opt_remove_service_principal:
                adcli_enroll_add_service_principal_to_remove (enroll, optarg);
                return;
+       case opt_use_ldaps:
+               adcli_conn_set_use_ldaps (conn, true);
+               return;
        case opt_verbose:
                return;
 
@@ -326,6 +331,7 @@ adcli_tool_computer_join (adcli_conn *conn,
                { "domain-realm", required_argument, NULL, opt_domain_realm },
                { "domain-controller", required_argument, NULL, 
opt_domain_controller },
                { "domain-server", required_argument, NULL, 
opt_domain_controller }, /* compat */
+               { "use-ldaps", no_argument, 0, opt_use_ldaps },
                { "login-user", required_argument, NULL, opt_login_user },
                { "user", required_argument, NULL, opt_login_user }, /* compat 
*/
                { "login-ccache", optional_argument, NULL, opt_login_ccache },
@@ -542,6 +548,7 @@ adcli_tool_computer_preset (adcli_conn *conn,
                { "domain", required_argument, NULL, opt_domain },
                { "domain-realm", required_argument, NULL, opt_domain_realm },
                { "domain-controller", required_argument, NULL, 
opt_domain_controller },
+               { "use-ldaps", no_argument, 0, opt_use_ldaps },
                { "domain-ou", required_argument, NULL, opt_domain_ou },
                { "login-user", required_argument, NULL, opt_login_user },
                { "login-ccache", optional_argument, NULL, opt_login_ccache },
@@ -641,6 +648,7 @@ adcli_tool_computer_reset (adcli_conn *conn,
                { "domain", required_argument, NULL, opt_domain },
                { "domain-realm", required_argument, NULL, opt_domain_realm },
                { "domain-controller", required_argument, NULL, 
opt_domain_controller },
+               { "use-ldaps", no_argument, 0, opt_use_ldaps },
                { "login-user", required_argument, NULL, opt_login_user },
                { "login-ccache", optional_argument, NULL, opt_login_ccache },
                { "login-type", required_argument, NULL, opt_login_type },
@@ -716,6 +724,7 @@ adcli_tool_computer_delete (adcli_conn *conn,
                { "domain", required_argument, NULL, opt_domain },
                { "domain-realm", required_argument, NULL, opt_domain_realm },
                { "domain-controller", required_argument, NULL, 
opt_domain_controller },
+               { "use-ldaps", no_argument, 0, opt_use_ldaps },
                { "login-user", required_argument, NULL, opt_login_user },
                { "login-ccache", optional_argument, NULL, opt_login_ccache },
                { "no-password", no_argument, 0, opt_no_password },
diff --git a/tools/entry.c b/tools/entry.c
index 7b6a200..737fefe 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_use_ldaps,
 } Option;
 
 static adcli_tool_desc common_usages[] = {
@@ -65,6 +66,7 @@ static adcli_tool_desc common_usages[] = {
        { opt_domain, "active directory domain name" },
        { opt_domain_realm, "kerberos realm for the domain" },
        { opt_domain_controller, "domain directory server to connect to" },
+       { opt_use_ldaps, "use LDAPS port for communication" },
        { opt_login_ccache, "kerberos credential cache file which contains\n"
                            "ticket to used to connect to the domain" },
        { opt_login_user, "user (usually administrative) login name of\n"
@@ -131,6 +133,9 @@ parse_option (Option opt,
                        stdin_password = 1;
                }
                return;
+       case opt_use_ldaps:
+               adcli_conn_set_use_ldaps (conn, true);
+               return;
        case opt_verbose:
                return;
        default:
@@ -163,6 +168,7 @@ adcli_tool_user_create (adcli_conn *conn,
                { "domain", required_argument, NULL, opt_domain },
                { "domain-realm", required_argument, NULL, opt_domain_realm },
                { "domain-controller", required_argument, NULL, 
opt_domain_controller },
+               { "use-ldaps", no_argument, 0, opt_use_ldaps },
                { "login-user", required_argument, NULL, opt_login_user },
                { "login-ccache", optional_argument, NULL, opt_login_ccache },
                { "no-password", no_argument, 0, opt_no_password },
@@ -263,6 +269,7 @@ adcli_tool_user_delete (adcli_conn *conn,
                { "domain", required_argument, NULL, opt_domain },
                { "domain-realm", required_argument, NULL, opt_domain_realm },
                { "domain-controller", required_argument, NULL, 
opt_domain_controller },
+               { "use-ldaps", no_argument, 0, opt_use_ldaps },
                { "login-user", required_argument, NULL, opt_login_user },
                { "login-ccache", optional_argument, NULL, opt_login_ccache },
                { "no-password", no_argument, 0, opt_no_password },
@@ -340,6 +347,7 @@ adcli_tool_group_create (adcli_conn *conn,
                { "domain", required_argument, NULL, opt_domain },
                { "domain-realm", required_argument, NULL, opt_domain_realm },
                { "domain-controller", required_argument, NULL, 
opt_domain_controller },
+               { "use-ldaps", no_argument, 0, opt_use_ldaps },
                { "domain-ou", required_argument, NULL, opt_domain_ou },
                { "login-user", required_argument, NULL, opt_login_user },
                { "login-ccache", optional_argument, NULL, opt_login_ccache },
@@ -426,6 +434,7 @@ adcli_tool_group_delete (adcli_conn *conn,
                { "domain", required_argument, NULL, opt_domain },
                { "domain-realm", required_argument, NULL, opt_domain_realm },
                { "domain-controller", required_argument, NULL, 
opt_domain_controller },
+               { "use-ldaps", no_argument, 0, opt_use_ldaps },
                { "login-user", required_argument, NULL, opt_login_user },
                { "login-ccache", optional_argument, NULL, opt_login_ccache },
                { "no-password", no_argument, 0, opt_no_password },
@@ -536,6 +545,7 @@ adcli_tool_member_add (adcli_conn *conn,
                { "domain", required_argument, NULL, opt_domain },
                { "domain-realm", required_argument, NULL, opt_domain_realm },
                { "domain-controller", required_argument, NULL, 
opt_domain_controller },
+               { "use-ldaps", no_argument, 0, opt_use_ldaps },
                { "login-user", required_argument, NULL, opt_login_user },
                { "login-ccache", optional_argument, NULL, opt_login_ccache },
                { "no-password", no_argument, 0, opt_no_password },
@@ -618,6 +628,7 @@ adcli_tool_member_remove (adcli_conn *conn,
                { "domain", required_argument, NULL, opt_domain },
                { "domain-realm", required_argument, NULL, opt_domain_realm },
                { "domain-controller", required_argument, NULL, 
opt_domain_controller },
+               { "use-ldaps", no_argument, 0, opt_use_ldaps },
                { "login-user", required_argument, NULL, opt_login_user },
                { "login-ccache", optional_argument, NULL, opt_login_ccache },
                { "no-password", no_argument, 0, opt_no_password },
-- 
2.25.0


Reply via email to