From 32a0a2a0ec9517bda32229a93024d0462ad6720c Mon Sep 17 00:00:00 2001 From: James Antill Date: Thu, 26 May 2022 00:48:51 -0400 Subject: [PATCH] Auto sync2gitlab import of adcli-0.8.2-12.el8.src.rpm --- .gitignore | 1 + 0001-Add-setattr-option.patch | 384 +++++++++++ ...ur-hmac-md5-when-discovering-the-sal.patch | 63 ++ ...dont-expire-password-option-and-join.patch | 27 + 0001-Fix-for-issue-found-by-Coverity.patch | 29 + 0001-Fix-for-issues-found-by-Coverity.patch | 43 ++ 0001-Implement-adcli-testjoin.patch | 181 +++++ ...fter-password-change-with-user-creds.patch | 32 + ...DC-location-mechanism-more-compliant.patch | 216 ++++++ ...e-only-check-when-looking-for-the-Ne.patch | 31 + 0001-Use-GSS-SPNEGO-if-available.patch | 124 ++++ ...re-only-allowed-enctypes-are-used-in.patch | 80 +++ 0001-adutil-add-_adcli_strv_add_unique.patch | 134 ++++ ...endor-error-message-configure-option.patch | 60 ++ 0001-coverity-add-missing-NULL-checks.patch | 44 ++ 0001-create-user-add-nis-domain-option.patch | 71 ++ ...do-not-exit-if-keytab-cannot-be-read.patch | 32 + 0001-discovery-fix.patch | 27 + ...samba_data_tool_path.xml-.in-to-EXTR.patch | 25 + ...-explain-how-to-force-password-reset.patch | 30 + ...-doc-explain-required-AD-permissions.patch | 242 +++++++ 0001-enroll-add-is_service-member.patch | 66 ++ ...incipals-do-not-leak-memory-when-cal.patch | 72 ++ 0001-fix-typo-in-flag-value.patch | 25 + 0001-join-always-add-service-principals.patch | 86 +++ ...in-update-set-dNSHostName-if-not-set.patch | 59 ++ 0001-library-add-missing-strdup.patch | 34 + ...C-flags-to-a-more-common-header-file.patch | 50 ++ ...ddrinfo-with-AI_CANONNAME-to-find-a-.patch | 92 +++ ...onal-parameter-of-login-ccache-bette.patch | 44 ++ ...g-of-optional-credential-cache-more-.patch | 41 ++ 0001-man-move-note-to-the-right-section.patch | 48 ++ ...count-fix-typo-in-the-man-page-entry.patch | 35 + ...g-use-ldaps-option-to-update-and-tes.patch | 36 + 0001-tools-add-show-computer-command.patch | 338 +++++++++ ...-tools-disable-SSSD-s-locator-plugin.patch | 41 ++ ...ix-typo-in-show-password-help-output.patch | 26 + ...s-remove-errx-from-computer-commands.patch | 328 +++++++++ 0002-Add-delattr-option.patch | 191 ++++++ 0002-Add-dont-expire-password-option.patch | 241 +++++++ 0002-Use-strdup-if-offset-are-used.patch | 31 + ...rnal_program-silence-noisy-debug-mes.patch | 25 + ...entry_attrs-with-userAccountControl-.patch | 60 ++ ...conn-add-adcli_conn_set_krb5_context.patch | 52 ++ ...escription-option-to-join-and-update.patch | 183 +++++ 0002-add-option-use-ldaps.patch | 378 ++++++++++ ...cli_strv_add_unique-for-service-prin.patch | 83 +++ ...-computer-add-create-msa-sub-command.patch | 646 ++++++++++++++++++ ...ser-try-to-find-NIS-domain-if-needed.patch | 147 ++++ ...brary-make-_adcli_strv_has_ex-public.patch | 42 ++ ...n-error-if-no-matching-key-was-found.patch | 35 + ...ve-errx-from-user-and-group-commands.patch | 398 +++++++++++ ...-Do-not-add-service-principals-twice.patch | 57 ++ ...li_enroll_get_permitted_keytab_encty.patch | 196 ++++++ ...-of-adcli_tool_computer_delete-descr.patch | 26 + ...omputer-or-service-in-debug-messages.patch | 358 ++++++++++ 0003-entry-add-passwd-user-sub-command.patch | 366 ++++++++++ ...rb5_build_principal-allow-principals.patch | 42 ++ ...tools-remove-errx-from-info-commands.patch | 53 ++ ...depend-on-default_realm-in-krb5.conf.patch | 27 + ...y-enctypes-permitted-by-Kerberos-con.patch | 103 +++ ...-all-credential-cache-types-are-supp.patch | 37 + ...l-more-filters-for-random-characters.patch | 77 +++ ...-sure-server-side-SPNs-are-preserved.patch | 82 +++ ...e-errx-from-adcli_read_password_func.patch | 42 ++ ...i_enroll_add_keytab_for_service_acco.patch | 91 +++ ...-library-add-adcli_conn_is_writeable.patch | 38 ++ ...-errx-from-setup_krb5_conf_directory.patch | 63 ++ 0006-Handle-kvno-increment-for-RODCs.patch | 67 ++ ...low-fqdn-for-locate_computer_account.patch | 129 ++++ ...-entry-remove-errx-from-parse_option.patch | 175 +++++ ...in-test_check_nt_time_string_lifetim.patch | 28 + ...nt-add-random-suffix-to-account-name.patch | 122 ++++ ...mputer-remove-errx-from-parse_option.patch | 294 ++++++++ 0008-library-add-_adcli_bin_sid_to_str.patch | 178 +++++ ...ary-add-_adcli_call_external_program.patch | 317 +++++++++ 0010-library-add-_adcli_ldap_parse_sid.patch | 69 ++ 0011-library-add-lookup_domain_sid.patch | 71 ++ ...ibrary-add-adcli_conn_get_domain_sid.patch | 61 ++ 0013-tools-add-option-add-samba-data.patch | 142 ++++ ...-tools-store-Samba-data-if-requested.patch | 75 ++ 0015-make-Samba-data-tool-configurable.patch | 292 ++++++++ 0016-Add-trusted-for-delegation-option.patch | 247 +++++++ ...attributes-given-on-the-command-line.patch | 126 ++++ 0018-update-allow-to-add-service-names.patch | 387 +++++++++++ ...late-enctypes-in-a-separate-function.patch | 181 +++++ ...ributes-while-creating-computer-obje.patch | 110 +++ ...util-add-_adcli_strv_remove_unsorted.patch | 288 ++++++++ ...principal-and-remove-service-princip.patch | 360 ++++++++++ ...iteable-do-not-crash-id-domain_disco.patch | 32 + ...-doc-fix-typos-in-the-adcli-man-page.patch | 203 ++++++ EMPTY | 1 - adcli.spec | 395 +++++++++++ sources | 1 + 94 files changed, 11817 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 0001-Add-setattr-option.patch create mode 100644 0001-Do-not-use-arcfour-hmac-md5-when-discovering-the-sal.patch create mode 100644 0001-Fix-for-dont-expire-password-option-and-join.patch create mode 100644 0001-Fix-for-issue-found-by-Coverity.patch create mode 100644 0001-Fix-for-issues-found-by-Coverity.patch create mode 100644 0001-Implement-adcli-testjoin.patch create mode 100644 0001-Increment-kvno-after-password-change-with-user-creds.patch create mode 100644 0001-Make-adcli-info-DC-location-mechanism-more-compliant.patch create mode 100644 0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch create mode 100644 0001-Use-GSS-SPNEGO-if-available.patch create mode 100644 0001-adenroll-make-sure-only-allowed-enctypes-are-used-in.patch create mode 100644 0001-adutil-add-_adcli_strv_add_unique.patch create mode 100644 0001-build-add-with-vendor-error-message-configure-option.patch create mode 100644 0001-coverity-add-missing-NULL-checks.patch create mode 100644 0001-create-user-add-nis-domain-option.patch create mode 100644 0001-delete-do-not-exit-if-keytab-cannot-be-read.patch create mode 100644 0001-discovery-fix.patch create mode 100644 0001-doc-add-missing-samba_data_tool_path.xml-.in-to-EXTR.patch create mode 100644 0001-doc-explain-how-to-force-password-reset.patch create mode 100644 0001-doc-explain-required-AD-permissions.patch create mode 100644 0001-enroll-add-is_service-member.patch create mode 100644 0001-ensure_keytab_principals-do-not-leak-memory-when-cal.patch create mode 100644 0001-fix-typo-in-flag-value.patch create mode 100644 0001-join-always-add-service-principals.patch create mode 100644 0001-join-update-set-dNSHostName-if-not-set.patch create mode 100644 0001-library-add-missing-strdup.patch create mode 100644 0001-library-move-UAC-flags-to-a-more-common-header-file.patch create mode 100644 0001-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch create mode 100644 0001-man-explain-optional-parameter-of-login-ccache-bette.patch create mode 100644 0001-man-make-handling-of-optional-credential-cache-more-.patch create mode 100644 0001-man-move-note-to-the-right-section.patch create mode 100644 0001-service-account-fix-typo-in-the-man-page-entry.patch create mode 100644 0001-tools-add-missing-use-ldaps-option-to-update-and-tes.patch create mode 100644 0001-tools-add-show-computer-command.patch create mode 100644 0001-tools-disable-SSSD-s-locator-plugin.patch create mode 100644 0001-tools-fix-typo-in-show-password-help-output.patch create mode 100644 0001-tools-remove-errx-from-computer-commands.patch create mode 100644 0002-Add-delattr-option.patch create mode 100644 0002-Add-dont-expire-password-option.patch create mode 100644 0002-Use-strdup-if-offset-are-used.patch create mode 100644 0002-_adcli_call_external_program-silence-noisy-debug-mes.patch create mode 100644 0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch create mode 100644 0002-adconn-add-adcli_conn_set_krb5_context.patch create mode 100644 0002-add-description-option-to-join-and-update.patch create mode 100644 0002-add-option-use-ldaps.patch create mode 100644 0002-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch create mode 100644 0002-computer-add-create-msa-sub-command.patch create mode 100644 0002-create-user-try-to-find-NIS-domain-if-needed.patch create mode 100644 0002-library-make-_adcli_strv_has_ex-public.patch create mode 100644 0002-library-return-error-if-no-matching-key-was-found.patch create mode 100644 0002-tools-remove-errx-from-user-and-group-commands.patch create mode 100644 0003-Do-not-add-service-principals-twice.patch create mode 100644 0003-adenroll-add-adcli_enroll_get_permitted_keytab_encty.patch create mode 100644 0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch create mode 100644 0003-enroll-use-computer-or-service-in-debug-messages.patch create mode 100644 0003-entry-add-passwd-user-sub-command.patch create mode 100644 0003-library-_adcli_krb5_build_principal-allow-principals.patch create mode 100644 0003-tools-remove-errx-from-info-commands.patch create mode 100644 0004-Do-not-depend-on-default_realm-in-krb5.conf.patch create mode 100644 0004-adenroll-use-only-enctypes-permitted-by-Kerberos-con.patch create mode 100644 0004-doc-explain-that-all-credential-cache-types-are-supp.patch create mode 100644 0004-enroll-more-filters-for-random-characters.patch create mode 100644 0004-library-make-sure-server-side-SPNs-are-preserved.patch create mode 100644 0004-tools-remove-errx-from-adcli_read_password_func.patch create mode 100644 0005-enroll-make-adcli_enroll_add_keytab_for_service_acco.patch create mode 100644 0005-library-add-adcli_conn_is_writeable.patch create mode 100644 0005-tools-remove-errx-from-setup_krb5_conf_directory.patch create mode 100644 0006-Handle-kvno-increment-for-RODCs.patch create mode 100644 0006-enroll-allow-fqdn-for-locate_computer_account.patch create mode 100644 0006-tools-entry-remove-errx-from-parse_option.patch create mode 100644 0007-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch create mode 100644 0007-service-account-add-random-suffix-to-account-name.patch create mode 100644 0007-tools-computer-remove-errx-from-parse_option.patch create mode 100644 0008-library-add-_adcli_bin_sid_to_str.patch create mode 100644 0009-library-add-_adcli_call_external_program.patch create mode 100644 0010-library-add-_adcli_ldap_parse_sid.patch create mode 100644 0011-library-add-lookup_domain_sid.patch create mode 100644 0012-library-add-adcli_conn_get_domain_sid.patch create mode 100644 0013-tools-add-option-add-samba-data.patch create mode 100644 0014-tools-store-Samba-data-if-requested.patch create mode 100644 0015-make-Samba-data-tool-configurable.patch create mode 100644 0016-Add-trusted-for-delegation-option.patch create mode 100644 0017-Only-update-attributes-given-on-the-command-line.patch create mode 100644 0018-update-allow-to-add-service-names.patch create mode 100644 0019-Calculate-enctypes-in-a-separate-function.patch create mode 100644 0020-join-add-all-attributes-while-creating-computer-obje.patch create mode 100644 0021-util-add-_adcli_strv_remove_unsorted.patch create mode 100644 0022-Add-add-service-principal-and-remove-service-princip.patch create mode 100644 0023-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch create mode 100644 0024-doc-fix-typos-in-the-adcli-man-page.patch delete mode 100644 EMPTY create mode 100644 adcli.spec create mode 100644 sources diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a3c2601 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/adcli-0.8.2.tar.gz diff --git a/0001-Add-setattr-option.patch b/0001-Add-setattr-option.patch new file mode 100644 index 0000000..abc40bb --- /dev/null +++ b/0001-Add-setattr-option.patch @@ -0,0 +1,384 @@ +From c5b0cee2976682b4fc1aeb02636cc9f2c6dbc2a5 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 14 Jun 2021 07:54:01 +0200 +Subject: [PATCH 1/2] Add setattr option + +With the new option common LDAP attributes can be set. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1690920 +--- + doc/adcli.xml | 34 +++++++++ + library/adenroll.c | 169 ++++++++++++++++++++++++++++++++++++++++++++- + library/adenroll.h | 4 ++ + tools/computer.c | 10 +++ + 4 files changed, 216 insertions(+), 1 deletion(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 6c36297..8383aa7 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -374,6 +374,23 @@ Password for Administrator: + service should be accessible with a different host + name as well. + ++ ++ ++ Add the LDAP attribute ++ with the ++ given to ++ the new LDAP host object. ++ This option can be used multiple times to add multiple ++ different attributes. Multi-value attributes are ++ currently not supported. ++ Please note that the account used to join the ++ domain must have the required privileges to add the ++ given attributes. Some attributes might have ++ constraints with respect to syntax and allowed values ++ which must be met as well. Attributes managed by other ++ adcli options cannot be set with this option. ++ ++ + + + After a successful join print out information +@@ -543,6 +560,23 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + Remove a service principal name from + the keytab and the AD host object. + ++ ++ ++ Add the LDAP attribute ++ with the ++ given to ++ the LDAP host object. ++ This option can be used multiple times to add multiple ++ different attributes. Multi-value attributes are ++ currently not supported. ++ Please note that the account used to update the ++ host object must have the required privileges to modify ++ the given attributes. Some attributes might have ++ constraints with respect to syntax and allowed values ++ which must be met as well. Attributes managed by other ++ adcli options cannot be set with this option. ++ ++ + + + After a successful join print out information +diff --git a/library/adenroll.c b/library/adenroll.c +index 0b1c066..dd51567 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -150,4 +150,5 @@ struct _adcli_enroll { + char *description; ++ char **setattr; + }; + + static const char * +@@ -795,6 +796,56 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype) + return ADCLI_SUCCESS; + } + ++static LDAPMod ** ++get_mods_for_attrs (adcli_enroll *enroll, int mod_op) ++{ ++ size_t len; ++ size_t c; ++ char *end; ++ LDAPMod **mods = NULL; ++ ++ len = _adcli_strv_len (enroll->setattr); ++ if (len == 0) { ++ return NULL; ++ } ++ ++ mods = calloc (len + 1, sizeof (LDAPMod *)); ++ return_val_if_fail (mods != NULL, NULL); ++ ++ for (c = 0; c < len; c++) { ++ end = strchr (enroll->setattr[c], '='); ++ if (end == NULL) { ++ ldap_mods_free (mods, 1); ++ return NULL; ++ } ++ ++ mods[c] = calloc (1, sizeof (LDAPMod)); ++ if (mods[c] == NULL) { ++ ldap_mods_free (mods, 1); ++ return NULL; ++ } ++ ++ mods[c]->mod_op = mod_op; ++ *end = '\0'; ++ mods[c]->mod_type = strdup (enroll->setattr[c]); ++ *end = '='; ++ mods[c]->mod_values = calloc (2, sizeof (char *)); ++ if (mods[c]->mod_type == NULL || mods[c]->mod_values == NULL) { ++ ldap_mods_free (mods, 1); ++ return NULL; ++ } ++ ++ mods[c]->mod_values[0] = strdup (end + 1); ++ if (mods[c]->mod_values[0] == NULL) { ++ ldap_mods_free (mods, 1); ++ return NULL; ++ } ++ } ++ ++ return mods; ++} ++ ++ + static adcli_result + create_computer_account (adcli_enroll *enroll, + LDAP *ldap) +@@ -828,6 +879,7 @@ create_computer_account (adcli_enroll *enroll, + size_t m; + uint32_t uac = UAC_WORKSTATION_TRUST_ACCOUNT | UAC_DONT_EXPIRE_PASSWORD ; + char *uac_str = NULL; ++ LDAPMod **extra_mods = NULL; + + LDAPMod *all_mods[] = { + &objectClass, +@@ -845,7 +897,7 @@ create_computer_account (adcli_enroll *enroll, + }; + + size_t mods_count = sizeof (all_mods) / sizeof (LDAPMod *); +- LDAPMod *mods[mods_count]; ++ LDAPMod **mods; + + if (adcli_enroll_get_trusted_for_delegation (enroll)) { + uac |= UAC_TRUSTED_FOR_DELEGATION; +@@ -868,6 +920,17 @@ create_computer_account (adcli_enroll *enroll, + } + vals_supportedEncryptionTypes[0] = val; + ++ if (enroll->setattr != NULL) { ++ extra_mods = get_mods_for_attrs (enroll, LDAP_MOD_ADD); ++ if (extra_mods == NULL) { ++ _adcli_err ("Failed to add setattr attributes, " ++ "just using defaults"); ++ } ++ } ++ ++ mods = calloc (mods_count + seq_count (extra_mods) + 1, sizeof (LDAPMod *)); ++ return_val_if_fail (mods != NULL, ADCLI_ERR_UNEXPECTED); ++ + m = 0; + for (c = 0; c < mods_count - 1; c++) { + /* Skip empty LDAP sttributes */ +@@ -875,9 +938,15 @@ create_computer_account (adcli_enroll *enroll, + mods[m++] = all_mods[c]; + } + } ++ ++ for (c = 0; c < seq_count (extra_mods); c++) { ++ mods[m++] = extra_mods[c]; ++ } + mods[m] = NULL; + + ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL); ++ ldap_mods_free (extra_mods, 1); ++ free (mods); + free (uac_str); + free (val); + +@@ -1698,6 +1767,14 @@ update_computer_account (adcli_enroll *enroll) + res |= update_computer_attribute (enroll, ldap, mods); + } + ++ if (res == ADCLI_SUCCESS && enroll->setattr != NULL) { ++ LDAPMod **mods = get_mods_for_attrs (enroll, LDAP_MOD_REPLACE); ++ if (mods != NULL) { ++ res |= update_computer_attribute (enroll, ldap, mods); ++ ldap_mods_free (mods, 1); ++ } ++ } ++ + if (res != 0) + _adcli_info ("Updated existing computer account: %s", enroll->computer_dn); + } +@@ -2751,6 +2828,7 @@ enroll_free (adcli_enroll *enroll) + free (enroll->user_principal); + _adcli_strv_free (enroll->service_names); + _adcli_strv_free (enroll->service_principals); ++ _adcli_strv_free (enroll->setattr); + _adcli_password_free (enroll->computer_password); + + adcli_enroll_set_keytab_name (enroll, NULL); +@@ -3332,6 +3410,72 @@ adcli_enroll_add_service_principal_to_remove (adcli_enroll *enroll, + return_if_fail (enroll->service_principals_to_remove != NULL); + } + ++static int comp_attr_name (const char *s1, const char *s2) ++{ ++ size_t c = 0; ++ ++ /* empty strings cannot contain an attribute name */ ++ if (s1 == NULL || s2 == NULL || *s1 == '\0' || *s2 == '\0') { ++ return 1; ++ } ++ ++ for (c = 0 ; s1[c] != '\0' && s2[c] != '\0'; c++) { ++ if (s1[c] == '=' && s2[c] == '=') { ++ return 0; ++ } else if (tolower (s1[c]) != tolower (s2[c])) { ++ return 1; ++ } ++ } ++ ++ return 1; ++} ++ ++adcli_result ++adcli_enroll_add_setattr (adcli_enroll *enroll, const char *value) ++{ ++ char *delim; ++ ++ return_val_if_fail (enroll != NULL, ADCLI_ERR_CONFIG); ++ return_val_if_fail (value != NULL, ADCLI_ERR_CONFIG); ++ ++ delim = strchr (value, '='); ++ if (delim == NULL) { ++ _adcli_err ("Missing '=' in setattr option [%s]", value); ++ return ADCLI_ERR_CONFIG; ++ } ++ ++ if (*(delim + 1) == '\0') { ++ _adcli_err ("Missing value in setattr option [%s]", value); ++ return ADCLI_ERR_CONFIG; ++ } ++ ++ *delim = '\0'; ++ if (_adcli_strv_has_ex (default_ad_ldap_attrs, value, strcasecmp) == 1) { ++ _adcli_err ("Attribute [%s] cannot be set with setattr", value); ++ return ADCLI_ERR_CONFIG; ++ } ++ *delim = '='; ++ ++ if (_adcli_strv_has_ex (enroll->setattr, value, comp_attr_name) == 1) { ++ _adcli_err ("Attribute [%s] already set", value); ++ return ADCLI_ERR_CONFIG; ++ } ++ ++ enroll->setattr = _adcli_strv_add (enroll->setattr, strdup (value), ++ NULL); ++ return_val_if_fail (enroll->setattr != NULL, ADCLI_ERR_CONFIG); ++ ++ return ADCLI_SUCCESS; ++} ++ ++const char ** ++adcli_enroll_get_setattr (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, NULL); ++ return (const char **) enroll->setattr; ++} ++ ++ + #ifdef ADENROLL_TESTS + + #include "test.h" +@@ -3401,12 +3545,35 @@ test_adcli_enroll_get_permitted_keytab_enctypes (void) + adcli_conn_unref (conn); + } + ++static void ++test_comp_attr_name (void) ++{ ++ assert_num_eq (1, comp_attr_name (NULL ,NULL)); ++ assert_num_eq (1, comp_attr_name ("" ,NULL)); ++ assert_num_eq (1, comp_attr_name ("" ,"")); ++ assert_num_eq (1, comp_attr_name (NULL ,"")); ++ assert_num_eq (1, comp_attr_name (NULL ,"abc=xyz")); ++ assert_num_eq (1, comp_attr_name ("" ,"abc=xyz")); ++ assert_num_eq (1, comp_attr_name ("abc=xyz", NULL)); ++ assert_num_eq (1, comp_attr_name ("abc=xyz", "")); ++ assert_num_eq (1, comp_attr_name ("abc=xyz", "ab=xyz")); ++ assert_num_eq (1, comp_attr_name ("ab=xyz", "abc=xyz")); ++ assert_num_eq (1, comp_attr_name ("abcxyz", "abc=xyz")); ++ assert_num_eq (1, comp_attr_name ("abc=xyz", "abcxyz")); ++ assert_num_eq (1, comp_attr_name ("abc=xyz", "a")); ++ assert_num_eq (1, comp_attr_name ("a", "abc=xyz")); ++ ++ assert_num_eq (0, comp_attr_name ("abc=xyz", "abc=xyz")); ++ assert_num_eq (0, comp_attr_name ("abc=xyz", "abc=123")); ++} ++ + int + main (int argc, + char *argv[]) + { + test_func (test_adcli_enroll_get_permitted_keytab_enctypes, + "/attrs/adcli_enroll_get_permitted_keytab_enctypes"); ++ test_func (test_comp_attr_name, "/attrs/comp_attr_name"); + return test_run (argc, argv); + } + +diff --git a/library/adenroll.h b/library/adenroll.h +index 34dc683..862bb60 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -138,6 +138,10 @@ const char * adcli_enroll_get_desciption (adcli_enroll *enroll); + void adcli_enroll_set_description (adcli_enroll *enroll, + const char *value); + ++const char ** adcli_enroll_get_setattr (adcli_enroll *enroll); ++adcli_result adcli_enroll_add_setattr (adcli_enroll *enroll, ++ const char *value); ++ + bool adcli_enroll_get_is_service (adcli_enroll *enroll); + void adcli_enroll_set_is_service (adcli_enroll *enroll, + bool value); +diff --git a/tools/computer.c b/tools/computer.c +index 16a1983..af38894 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -114,6 +114,7 @@ typedef enum { + opt_add_service_principal, + opt_remove_service_principal, + opt_description, ++ opt_setattr, + opt_use_ldaps, + } Option; + +@@ -152,6 +153,7 @@ static adcli_tool_desc common_usages[] = { + { opt_add_service_principal, "add the given service principal to the account\n" }, + { opt_remove_service_principal, "remove the given service principal from the account\n" }, + { opt_description, "add a description to the account\n" }, ++ { opt_setattr, "add an attribute with a value\n" }, + { opt_no_password, "don't prompt for or read a password" }, + { opt_prompt_password, "prompt for a password if necessary" }, + { opt_stdin_password, "read a password from stdin (until EOF) if\n" +@@ -333,6 +335,12 @@ parse_option (Option opt, + case opt_description: + adcli_enroll_set_description (enroll, optarg); + return ADCLI_SUCCESS; ++ case opt_setattr: ++ ret = adcli_enroll_add_setattr (enroll, optarg); ++ if (ret != ADCLI_SUCCESS) { ++ warnx ("parsing setattr option failed"); ++ } ++ return ret; + case opt_use_ldaps: + adcli_conn_set_use_ldaps (conn, true); + return ADCLI_SUCCESS; +@@ -401,6 +409,7 @@ adcli_tool_computer_join (adcli_conn *conn, + { "os-version", required_argument, NULL, opt_os_version }, + { "os-service-pack", optional_argument, NULL, opt_os_service_pack }, + { "description", optional_argument, NULL, opt_description }, ++ { "setattr", required_argument, NULL, opt_setattr }, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, + { "dont-expire-password", required_argument, NULL, opt_dont_expire_password }, +@@ -524,6 +533,7 @@ adcli_tool_computer_update (adcli_conn *conn, + { "os-version", required_argument, NULL, opt_os_version }, + { "os-service-pack", optional_argument, NULL, opt_os_service_pack }, + { "description", optional_argument, NULL, opt_description }, ++ { "setattr", required_argument, NULL, opt_setattr }, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime }, + { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, +-- +2.31.1 + diff --git a/0001-Do-not-use-arcfour-hmac-md5-when-discovering-the-sal.patch b/0001-Do-not-use-arcfour-hmac-md5-when-discovering-the-sal.patch new file mode 100644 index 0000000..4c2323c --- /dev/null +++ b/0001-Do-not-use-arcfour-hmac-md5-when-discovering-the-sal.patch @@ -0,0 +1,63 @@ +From 158468507bb723aa62196846749c23c121d4b298 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 8 Apr 2019 10:55:39 +0200 +Subject: [PATCH] Do not use arcfour-hmac-md5 when discovering the salt + +Since the arcfour-hmac-md5 encryption types does not use salts it cannot +be used to discover the right salt. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1683745 +--- + library/adkrb5.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/library/adkrb5.c b/library/adkrb5.c +index da835d7..be3ede5 100644 +--- a/library/adkrb5.c ++++ b/library/adkrb5.c +@@ -395,15 +395,33 @@ _adcli_krb5_keytab_discover_salt (krb5_context k5, + krb5_keytab scratch; + krb5_error_code code; + int i; ++ krb5_enctype *salt_enctypes = NULL; ++ size_t c; ++ size_t s; + + /* TODO: This should be a unique name */ + + code = krb5_kt_resolve (k5, "MEMORY:adcli-discover-salt", &scratch); + return_val_if_fail (code == 0, code); + ++ for (c = 0; enctypes[c] != 0; c++); /* count enctypes */ ++ salt_enctypes = calloc (c + 1, sizeof (krb5_enctype)); ++ return_val_if_fail (salt_enctypes != NULL, ENOMEM); ++ ++ /* ENCTYPE_ARCFOUR_HMAC does not use salts, so it cannot be used to ++ * discover the right salt. */ ++ s = 0; ++ for (c = 0; enctypes[c] != 0; c++) { ++ if (enctypes[c] == ENCTYPE_ARCFOUR_HMAC) { ++ continue; ++ } ++ ++ salt_enctypes[s++] = enctypes[c]; ++ } ++ + for (i = 0; salts[i].data != NULL; i++) { + code = _adcli_krb5_keytab_test_salt (k5, scratch, principal, kvno, +- password, enctypes, &salts[i]); ++ password, salt_enctypes, &salts[i]); + if (code == 0) { + *discovered = i; + break; +@@ -412,6 +430,7 @@ _adcli_krb5_keytab_discover_salt (krb5_context k5, + } + } + ++ free (salt_enctypes); + krb5_kt_close (k5, scratch); + return code; + } +-- +2.21.0 + diff --git a/0001-Fix-for-dont-expire-password-option-and-join.patch b/0001-Fix-for-dont-expire-password-option-and-join.patch new file mode 100644 index 0000000..9740f31 --- /dev/null +++ b/0001-Fix-for-dont-expire-password-option-and-join.patch @@ -0,0 +1,27 @@ +From 924465af7a4f37390bfdfdb4971e88421f52f3d9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 3 Jun 2021 15:03:20 +0200 +Subject: [PATCH] Fix for dont-expire-password option and join + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1769644 +--- + library/adenroll.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index f3d606e..5b0dcd5 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -856,7 +856,8 @@ create_computer_account (adcli_enroll *enroll, + uac |= UAC_TRUSTED_FOR_DELEGATION; + } + +- if (!adcli_enroll_get_dont_expire_password (enroll)) { ++ if (enroll->dont_expire_password_explicit ++ && !adcli_enroll_get_dont_expire_password (enroll)) { + uac &= ~(UAC_DONT_EXPIRE_PASSWORD); + } + +-- +2.31.1 + diff --git a/0001-Fix-for-issue-found-by-Coverity.patch b/0001-Fix-for-issue-found-by-Coverity.patch new file mode 100644 index 0000000..b5159c8 --- /dev/null +++ b/0001-Fix-for-issue-found-by-Coverity.patch @@ -0,0 +1,29 @@ +From 5da6d34e2659f915e830932fd366c635801ecd91 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 12 Aug 2019 17:28:20 +0200 +Subject: [PATCH] Fix for issue found by Coverity + +Related to https://gitlab.freedesktop.org/realmd/adcli/issues/3 +--- + library/adenroll.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 53cd812..524663a 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -2681,7 +2681,10 @@ adcli_enroll_get_permitted_keytab_enctypes (adcli_enroll *enroll) + for (c = 0; cur_enctypes[c] != 0; c++); + + new_enctypes = calloc (c + 1, sizeof (krb5_enctype)); +- return_val_if_fail (new_enctypes != NULL, NULL); ++ if (new_enctypes == NULL) { ++ krb5_free_enctypes (k5, permitted_enctypes); ++ return NULL; ++ } + + n = 0; + for (c = 0; cur_enctypes[c] != 0; c++) { +-- +2.21.0 + diff --git a/0001-Fix-for-issues-found-by-Coverity.patch b/0001-Fix-for-issues-found-by-Coverity.patch new file mode 100644 index 0000000..c7e698e --- /dev/null +++ b/0001-Fix-for-issues-found-by-Coverity.patch @@ -0,0 +1,43 @@ +From 3c93c96eb6ea2abd3869921ee4c89e1a4d9e4c44 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 14 Aug 2018 13:08:52 +0200 +Subject: [PATCH] Fix for issues found by Coverity + +--- + library/adenroll.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 02bd9e3..de2242a 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1575,7 +1575,7 @@ load_host_keytab (adcli_enroll *enroll) + } + + krb5_free_context (k5); +- return ADCLI_SUCCESS; ++ return res; + } + + typedef struct { +@@ -1756,12 +1756,12 @@ add_principal_to_keytab (adcli_enroll *enroll, + enroll->kvno, &password, enctypes, &salts[*which_salt]); + + free_principal_salts (k5, salts); ++ } + +- if (code != 0) { +- _adcli_err ("Couldn't add keytab entries: %s: %s", +- enroll->keytab_name, krb5_get_error_message (k5, code)); +- return ADCLI_ERR_FAIL; +- } ++ if (code != 0) { ++ _adcli_err ("Couldn't add keytab entries: %s: %s", ++ enroll->keytab_name, krb5_get_error_message (k5, code)); ++ return ADCLI_ERR_FAIL; + } + + +-- +2.21.0 + diff --git a/0001-Implement-adcli-testjoin.patch b/0001-Implement-adcli-testjoin.patch new file mode 100644 index 0000000..d75cbf2 --- /dev/null +++ b/0001-Implement-adcli-testjoin.patch @@ -0,0 +1,181 @@ +From 6fd99ff6c5dd6ef0be8d942989b1c6dcee3102d9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 22 Mar 2019 12:37:39 +0100 +Subject: [PATCH] Implement 'adcli testjoin' + +By calling adcli testjoin it will be checked if the host credentials +stored in the keytab are still valid. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1622583 +--- + doc/adcli.xml | 34 +++++++++++++++++++++++ + tools/computer.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ + tools/tools.c | 1 + + tools/tools.h | 4 +++ + 4 files changed, 111 insertions(+) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index af73433..9605b4a 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -43,6 +43,9 @@ + + adcli update + ++ ++ adcli testjoin ++ + + adcli create-user + --domain=domain.example.com +@@ -474,6 +477,37 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + + + ++ ++ Testing if the machine account password is valid ++ ++ adcli testjoin uses the current credentials in ++ the keytab and tries to authenticate with the machine account to the AD ++ domain. If this works the machine account password and the join are ++ still valid. If it fails the machine account password or the whole ++ machine account have to be refreshed with ++ adcli join or adcli update. ++ ++ ++ ++$ adcli testjoin ++ ++ ++ Only the global options not related to authentication are ++ available, additionally you can specify the following options to ++ control how this operation is done. ++ ++ ++ ++ ++ Specify the path to the host keytab where ++ current host credentials are stored and the new ones ++ will be written to. If not specified, the default ++ location will be used, usually ++ /etc/krb5.keytab. ++ ++ ++ ++ + + Creating a User + +diff --git a/tools/computer.c b/tools/computer.c +index 112340e..610ed2b 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -566,6 +566,78 @@ adcli_tool_computer_update (adcli_conn *conn, + return 0; + } + ++int ++adcli_tool_computer_testjoin (adcli_conn *conn, ++ int argc, ++ char *argv[]) ++{ ++ adcli_enroll *enroll; ++ adcli_result res; ++ const char *ktname; ++ int opt; ++ ++ struct option options[] = { ++ { "domain", required_argument, NULL, opt_domain }, ++ { "domain-controller", required_argument, NULL, opt_domain_controller }, ++ { "host-keytab", required_argument, 0, opt_host_keytab }, ++ { "verbose", no_argument, NULL, opt_verbose }, ++ { "help", no_argument, NULL, 'h' }, ++ { 0 }, ++ }; ++ ++ static adcli_tool_desc usages[] = { ++ { 0, "usage: adcli testjoin" }, ++ { 0 }, ++ }; ++ ++ enroll = adcli_enroll_new (conn); ++ if (enroll == NULL) ++ errx (-1, "unexpected memory problems"); ++ ++ while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) { ++ switch (opt) { ++ case 'h': ++ case '?': ++ case ':': ++ adcli_tool_usage (options, usages); ++ adcli_tool_usage (options, common_usages); ++ adcli_enroll_unref (enroll); ++ return opt == 'h' ? 0 : 2; ++ default: ++ parse_option ((Option)opt, optarg, conn, enroll); ++ break; ++ } ++ } ++ ++ /* Force use of a keytab to test the join/machine account password */ ++ adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_COMPUTER_ACCOUNT); ++ ktname = adcli_enroll_get_keytab_name (enroll); ++ adcli_conn_set_login_keytab_name (conn, ktname ? ktname : ""); ++ ++ res = adcli_enroll_load (enroll); ++ if (res != ADCLI_SUCCESS) { ++ adcli_enroll_unref (enroll); ++ adcli_conn_unref (conn); ++ errx (-res, "couldn't lookup domain info from keytab: %s", ++ adcli_get_last_error ()); ++ } ++ ++ res = adcli_conn_connect (conn); ++ if (res != ADCLI_SUCCESS) { ++ adcli_enroll_unref (enroll); ++ adcli_conn_unref (conn); ++ errx (-res, "couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ } ++ ++ printf ("Sucessfully validated join to domain %s\n", ++ adcli_conn_get_domain_name (conn)); ++ ++ adcli_enroll_unref (enroll); ++ ++ return 0; ++} + + int + adcli_tool_computer_preset (adcli_conn *conn, +diff --git a/tools/tools.c b/tools/tools.c +index 915130e..c4e2851 100644 +--- a/tools/tools.c ++++ b/tools/tools.c +@@ -55,6 +55,7 @@ struct { + { "info", adcli_tool_info, "Print information about a domain", CONNECTION_LESS }, + { "join", adcli_tool_computer_join, "Join this machine to a domain", }, + { "update", adcli_tool_computer_update, "Update machine membership in a domain", }, ++ { "testjoin", adcli_tool_computer_testjoin, "Test if machine account password is valid", }, + { "preset-computer", adcli_tool_computer_preset, "Pre setup computers accounts", }, + { "reset-computer", adcli_tool_computer_reset, "Reset a computer account", }, + { "delete-computer", adcli_tool_computer_delete, "Delete a computer account", }, +diff --git a/tools/tools.h b/tools/tools.h +index 6c97ccf..8cebbf9 100644 +--- a/tools/tools.h ++++ b/tools/tools.h +@@ -70,6 +70,10 @@ int adcli_tool_computer_update (adcli_conn *conn, + int argc, + char *argv[]); + ++int adcli_tool_computer_testjoin (adcli_conn *conn, ++ int argc, ++ char *argv[]); ++ + int adcli_tool_computer_delete (adcli_conn *conn, + int argc, + char *argv[]); +-- +2.20.1 + diff --git a/0001-Increment-kvno-after-password-change-with-user-creds.patch b/0001-Increment-kvno-after-password-change-with-user-creds.patch new file mode 100644 index 0000000..aeef509 --- /dev/null +++ b/0001-Increment-kvno-after-password-change-with-user-creds.patch @@ -0,0 +1,32 @@ +From 5cf1723c308e21cdbe9b98ed2aaa42cb997456fb Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 15 Mar 2019 14:31:12 +0100 +Subject: [PATCH] Increment kvno after password change with user creds + +Originally only the host credential part was fixed in the context of +https://bugs.freedesktop.org/show_bug.cgi?id=91185. This patch adds the +fix to the case when user credentials are used. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1642546 +--- + library/adenroll.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/library/adenroll.c b/library/adenroll.c +index e02f403..58362c2 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1057,6 +1057,10 @@ set_password_with_user_creds (adcli_enroll *enroll) + #endif + } else { + _adcli_info ("Set computer password"); ++ if (enroll->kvno > 0) { ++ enroll->kvno++; ++ _adcli_info ("kvno incremented to %d", enroll->kvno); ++ } + res = ADCLI_SUCCESS; + } + +-- +2.20.1 + diff --git a/0001-Make-adcli-info-DC-location-mechanism-more-compliant.patch b/0001-Make-adcli-info-DC-location-mechanism-more-compliant.patch new file mode 100644 index 0000000..f37f2f1 --- /dev/null +++ b/0001-Make-adcli-info-DC-location-mechanism-more-compliant.patch @@ -0,0 +1,216 @@ +From 0a0d0f66409eb83e06b7dc50543c2f6c15a36bc4 Mon Sep 17 00:00:00 2001 +From: Alexey A Nikitin +Date: Mon, 29 Oct 2018 20:40:36 -0700 +Subject: [PATCH] Make 'adcli info' DC location mechanism more compliant with + [MS-ADTS] and [MS-NRPC] + +AD specifications say that DC locator must attempt to find a suitable DC for the client. That means going through all of the DCs in SRV RRs one by one until one of them answers. + +The problem with adcli's original behavior is that it queries only five DCs from SRV, ever. This becomes a problem if for any reason there is a large number of DCs in the domain from which the client cannot get a CLDAP response. +--- + library/addisco.c | 146 +++++++++++++++++++++++++++++----------------- + 1 file changed, 94 insertions(+), 52 deletions(-) + +diff --git a/library/addisco.c b/library/addisco.c +index 8cc5bf0..6e73ead 100644 +--- a/library/addisco.c ++++ b/library/addisco.c +@@ -41,8 +41,10 @@ + #include + #include + +-/* Number of servers to do discovery against */ +-#define DISCO_COUNT 5 ++/* Number of servers to do discovery against. ++ * For AD DS maximum number of DCs is 1200. ++ */ ++#define DISCO_COUNT 1200 + + /* The time period in which to do rapid requests */ + #define DISCO_FEVER 1 +@@ -453,6 +455,51 @@ parse_disco (LDAP *ldap, + return usability; + } + ++static int ++ldap_disco_poller (LDAP **ldap, ++ LDAPMessage **message, ++ adcli_disco **results, ++ const char **addrs) ++{ ++ int found = ADCLI_DISCO_UNUSABLE; ++ int close_ldap; ++ int parsed; ++ int ret = 0; ++ struct timeval tvpoll = { 0, 0 }; ++ ++ switch (ldap_result (*ldap, LDAP_RES_ANY, 1, &tvpoll, message)) { ++ case LDAP_RES_SEARCH_ENTRY: ++ case LDAP_RES_SEARCH_RESULT: ++ parsed = parse_disco (*ldap, *addrs, *message, results); ++ if (parsed > found) ++ found = parsed; ++ ldap_msgfree (*message); ++ close_ldap = 1; ++ break; ++ case -1: ++ ldap_get_option (*ldap, LDAP_OPT_RESULT_CODE, &ret); ++ close_ldap = 1; ++ break; ++ default: ++ ldap_msgfree (*message); ++ close_ldap = 0; ++ break; ++ } ++ ++ if (ret != LDAP_SUCCESS) { ++ _adcli_ldap_handle_failure (*ldap, ADCLI_ERR_CONFIG, ++ "Couldn't perform discovery search"); ++ } ++ ++ /* Done with this connection */ ++ if (close_ldap) { ++ ldap_unbind_ext_s (*ldap, NULL, NULL); ++ *ldap = NULL; ++ } ++ ++ return found; ++} ++ + static int + ldap_disco (const char *domain, + srvinfo *srv, +@@ -477,6 +524,7 @@ ldap_disco (const char *domain, + int num, i; + int ret; + int have_any = 0; ++ struct timeval interval; + + if (domain) { + value = _adcli_ldap_escape_filter (domain); +@@ -540,7 +588,6 @@ ldap_disco (const char *domain, + version = LDAP_VERSION3; + ldap_set_option (ldap[num], LDAP_OPT_PROTOCOL_VERSION, &version); + ldap_set_option (ldap[num], LDAP_OPT_REFERRALS , 0); +- _adcli_info ("Sending netlogon pings to domain controller: %s", url); + addrs[num] = srv->hostname; + have_any = 1; + num++; +@@ -555,70 +602,65 @@ ldap_disco (const char *domain, + freeaddrinfo (res); + } + +- /* Wait for the first response. Poor mans fd watch */ +- for (started = now = time (NULL); +- have_any && found != ADCLI_DISCO_USABLE && now < started + DISCO_TIME; +- now = time (NULL)) { ++ /* Initial send and short time wait */ ++ interval.tv_sec = 0; ++ for (i = 0; ADCLI_DISCO_UNUSABLE == found && i < num; ++i) { ++ int parsed; ++ ++ if (NULL == ldap[i]) ++ continue; + +- struct timeval tvpoll = { 0, 0 }; +- struct timeval interval; ++ have_any = 1; ++ _adcli_info ("Sending NetLogon ping to domain controller: %s", addrs[i]); + +- /* If in the initial period, send feverishly */ +- if (now < started + DISCO_FEVER) { +- interval.tv_sec = 0; +- interval.tv_usec = 100 * 1000; ++ ret = ldap_search_ext (ldap[i], "", LDAP_SCOPE_BASE, ++ filter, attrs, 0, NULL, NULL, NULL, ++ -1, &msgidp); ++ ++ if (ret != LDAP_SUCCESS) { ++ _adcli_ldap_handle_failure (ldap[i], ADCLI_ERR_CONFIG, ++ "Couldn't perform discovery search"); ++ ldap_unbind_ext_s (ldap[i], NULL, NULL); ++ ldap[i] = NULL; ++ } ++ ++ /* From https://msdn.microsoft.com/en-us/library/ff718294.aspx first ++ * five DCs are given 0.4 seconds timeout, next five are given 0.2 ++ * seconds, and the rest are given 0.1 seconds ++ */ ++ if (i < 5) { ++ interval.tv_usec = 400000; ++ } else if (i < 10) { ++ interval.tv_usec = 200000; + } else { +- interval.tv_sec = 1; +- interval.tv_usec = 0; ++ interval.tv_usec = 100000; + } ++ select (0, NULL, NULL, NULL, &interval); ++ ++ parsed = ldap_disco_poller (&(ldap[i]), &message, results, &(addrs[i])); ++ if (parsed > found) ++ found = parsed; ++ } ++ ++ /* Wait some more until LDAP timeout (DISCO_TIME) */ ++ for (started = now = time (NULL); ++ have_any && ADCLI_DISCO_UNUSABLE == found && now < started + DISCO_TIME; ++ now = time (NULL)) { + + select (0, NULL, NULL, NULL, &interval); + + have_any = 0; +- for (i = 0; found != ADCLI_DISCO_USABLE && i < num; i++) { +- int close_ldap; ++ for (i = 0; ADCLI_DISCO_UNUSABLE == found && i < num; ++i) { + int parsed; + + if (ldap[i] == NULL) + continue; + +- ret = 0; + have_any = 1; +- switch (ldap_result (ldap[i], LDAP_RES_ANY, 1, &tvpoll, &message)) { +- case LDAP_RES_SEARCH_ENTRY: +- case LDAP_RES_SEARCH_RESULT: +- parsed = parse_disco (ldap[i], addrs[i], message, results); +- if (parsed > found) +- found = parsed; +- ldap_msgfree (message); +- close_ldap = 1; +- break; +- case 0: +- ret = ldap_search_ext (ldap[i], "", LDAP_SCOPE_BASE, +- filter, attrs, 0, NULL, NULL, NULL, +- -1, &msgidp); +- close_ldap = (ret != 0); +- break; +- case -1: +- ldap_get_option (ldap[i], LDAP_OPT_RESULT_CODE, &ret); +- close_ldap = 1; +- break; +- default: +- ldap_msgfree (message); +- close_ldap = 0; +- break; +- } +- +- if (ret != LDAP_SUCCESS) { +- _adcli_ldap_handle_failure (ldap[i], ADCLI_ERR_CONFIG, +- "Couldn't perform discovery search"); +- } + +- /* Done with this connection */ +- if (close_ldap) { +- ldap_unbind_ext_s (ldap[i], NULL, NULL); +- ldap[i] = NULL; +- } ++ parsed = ldap_disco_poller (&(ldap[i]), &message, results, &(addrs[i])); ++ if (parsed > found) ++ found = parsed; + } + } + +-- +2.26.2 + diff --git a/0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch b/0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch new file mode 100644 index 0000000..964c6bf --- /dev/null +++ b/0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch @@ -0,0 +1,31 @@ +From d8eb0f5704f34cb7d411cd275d32c63ead297b8d Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 24 Aug 2016 15:37:41 +0200 +Subject: [PATCH 01/23] Remove upper-case only check when looking for the + NetBIOS name + +It is a convention to use only upper-case letters for NetBIOS names but +it is not enforced on the AD-side. With the new option to specify a +random NetBIOS name it is possible to create host entries in AD with +lower-case letters in the name. To properly determine the name from the +keytab the upper-case check should be dropped,dc= +--- + library/adenroll.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index a15e4be..d1020e9 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1309,7 +1309,7 @@ load_keytab_entry (krb5_context k5, + if (!enroll->host_fqdn_explicit && !enroll->computer_name_explicit) { + + /* Automatically use the netbios name */ +- if (!enroll->computer_name && len > 1 && _adcli_str_is_up (name) && ++ if (!enroll->computer_name && len > 1 && + _adcli_str_has_suffix (name, "$") && !strchr (name, '/')) { + enroll->computer_name = name; + name[len - 1] = '\0'; +-- +2.14.4 + diff --git a/0001-Use-GSS-SPNEGO-if-available.patch b/0001-Use-GSS-SPNEGO-if-available.patch new file mode 100644 index 0000000..bae8b22 --- /dev/null +++ b/0001-Use-GSS-SPNEGO-if-available.patch @@ -0,0 +1,124 @@ +From a6f795ba3d6048b32d7863468688bf7f42b2cafd Mon Sep 17 00:00:00 2001 +From: Sumit Bose +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 +--- + 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 bcaced8..ffb54f9 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); +@@ -1606,6 +1619,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 1ad5715..37ebdd9 100644 +--- a/library/adconn.h ++++ b/library/adconn.h +@@ -149,6 +149,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.21.0 + diff --git a/0001-adenroll-make-sure-only-allowed-enctypes-are-used-in.patch b/0001-adenroll-make-sure-only-allowed-enctypes-are-used-in.patch new file mode 100644 index 0000000..ad69b70 --- /dev/null +++ b/0001-adenroll-make-sure-only-allowed-enctypes-are-used-in.patch @@ -0,0 +1,80 @@ +From 341974aae7d0755fc32a0b7e2b34d8e1ef60d195 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 20 Dec 2018 21:05:35 +0100 +Subject: [PATCH 1/4] adenroll: make sure only allowed enctypes are used in + FIPS mode + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1717355 +--- + library/adenroll.c | 36 +++++++++++++++++++++++++++++++++++- + 1 file changed, 35 insertions(+), 1 deletion(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 52aa8a8..f617f28 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -41,11 +41,19 @@ + #include + #include + #include ++#include ++#include + + #ifndef SAMBA_DATA_TOOL + #define SAMBA_DATA_TOOL "/usr/bin/net" + #endif + ++static krb5_enctype v60_later_enctypes_fips[] = { ++ ENCTYPE_AES256_CTS_HMAC_SHA1_96, ++ ENCTYPE_AES128_CTS_HMAC_SHA1_96, ++ 0 ++}; ++ + static krb5_enctype v60_later_enctypes[] = { + ENCTYPE_AES256_CTS_HMAC_SHA1_96, + ENCTYPE_AES128_CTS_HMAC_SHA1_96, +@@ -2594,6 +2602,28 @@ adcli_enroll_set_keytab_name (adcli_enroll *enroll, + enroll->keytab_name_is_krb5 = 0; + } + ++#define PROC_SYS_FIPS "/proc/sys/crypto/fips_enabled" ++ ++static bool adcli_fips_enabled (void) ++{ ++ int fd; ++ ssize_t len; ++ char buf[8]; ++ ++ fd = open (PROC_SYS_FIPS, O_RDONLY); ++ if (fd != -1) { ++ len = read (fd, buf, sizeof (buf)); ++ close (fd); ++ /* Assume FIPS in enabled if PROC_SYS_FIPS contains a ++ * non-0 value. */ ++ if ( ! (len == 2 && buf[0] == '0' && buf[1] == '\n')) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ + krb5_enctype * + adcli_enroll_get_keytab_enctypes (adcli_enroll *enroll) + { +@@ -2602,7 +2632,11 @@ adcli_enroll_get_keytab_enctypes (adcli_enroll *enroll) + return enroll->keytab_enctypes; + + if (adcli_conn_server_has_capability (enroll->conn, ADCLI_CAP_V60_OID)) +- return v60_later_enctypes; ++ if (adcli_fips_enabled ()) { ++ return v60_later_enctypes_fips; ++ } else { ++ return v60_later_enctypes; ++ } + else + return v51_earlier_enctypes; + } +-- +2.21.0 + diff --git a/0001-adutil-add-_adcli_strv_add_unique.patch b/0001-adutil-add-_adcli_strv_add_unique.patch new file mode 100644 index 0000000..f49f5f8 --- /dev/null +++ b/0001-adutil-add-_adcli_strv_add_unique.patch @@ -0,0 +1,134 @@ +From 85d127fd52a8469f9f3ce0d1130fe17e756fdd75 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 16 Nov 2018 13:32:33 +0100 +Subject: [PATCH 1/2] adutil: add _adcli_strv_add_unique + +_adcli_strv_add_unique checks is the new value already exists in the +strv before adding it. Check can be done case-sensitive or not. + +Related to https://gitlab.freedesktop.org/realmd/adcli/issues/16 +--- + library/adprivate.h | 5 ++++ + library/adutil.c | 65 ++++++++++++++++++++++++++++++++++++++------- + 2 files changed, 61 insertions(+), 9 deletions(-) + +diff --git a/library/adprivate.h b/library/adprivate.h +index bc9df6d..0806430 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -111,6 +111,11 @@ char ** _adcli_strv_add (char **strv, + char *string, + int *length) GNUC_WARN_UNUSED; + ++char ** _adcli_strv_add_unique (char **strv, ++ char *string, ++ int *length, ++ bool case_sensitive) GNUC_WARN_UNUSED; ++ + void _adcli_strv_remove_unsorted (char **strv, + const char *string, + int *length); +diff --git a/library/adutil.c b/library/adutil.c +index 17d2caa..76ea158 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -221,6 +221,34 @@ _adcli_strv_add (char **strv, + return seq_push (strv, length, string); + } + ++static int ++_adcli_strv_has_ex (char **strv, ++ const char *str, ++ int (* compare) (const char *match, const char*value)) ++{ ++ int i; ++ ++ for (i = 0; strv && strv[i] != NULL; i++) { ++ if (compare (strv[i], str) == 0) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++char ** ++_adcli_strv_add_unique (char **strv, ++ char *string, ++ int *length, ++ bool case_sensitive) ++{ ++ if (_adcli_strv_has_ex (strv, string, case_sensitive ? strcmp : strcasecmp) == 1) { ++ return strv; ++ } ++ ++ return _adcli_strv_add (strv, string, length); ++} ++ + #define discard_const(ptr) ((void *)((uintptr_t)(ptr))) + + void +@@ -241,19 +269,11 @@ _adcli_strv_remove_unsorted (char **strv, + (seq_compar)strcasecmp, free); + } + +- + int + _adcli_strv_has (char **strv, + const char *str) + { +- int i; +- +- for (i = 0; strv && strv[i] != NULL; i++) { +- if (strcmp (strv[i], str) == 0) +- return 1; +- } +- +- return 0; ++ return _adcli_strv_has_ex (strv, str, strcmp); + } + + void +@@ -704,6 +724,32 @@ test_strv_add_free (void) + _adcli_strv_free (strv); + } + ++static void ++test_strv_add_unique_free (void) ++{ ++ char **strv = NULL; ++ ++ strv = _adcli_strv_add_unique (strv, strdup ("one"), NULL, false); ++ strv = _adcli_strv_add_unique (strv, strdup ("one"), NULL, false); ++ strv = _adcli_strv_add_unique (strv, strdup ("two"), NULL, false); ++ strv = _adcli_strv_add_unique (strv, strdup ("two"), NULL, false); ++ strv = _adcli_strv_add_unique (strv, strdup ("tWo"), NULL, false); ++ strv = _adcli_strv_add_unique (strv, strdup ("three"), NULL, false); ++ strv = _adcli_strv_add_unique (strv, strdup ("three"), NULL, false); ++ strv = _adcli_strv_add_unique (strv, strdup ("TWO"), NULL, true); ++ ++ assert_num_eq (_adcli_strv_len (strv), 4); ++ ++ assert_str_eq (strv[0], "one"); ++ assert_str_eq (strv[1], "two"); ++ assert_str_eq (strv[2], "three"); ++ assert_str_eq (strv[3], "TWO"); ++ assert (strv[4] == NULL); ++ ++ _adcli_strv_free (strv); ++} ++ ++ + static void + test_strv_dup (void) + { +@@ -856,6 +902,7 @@ main (int argc, + char *argv[]) + { + test_func (test_strv_add_free, "/util/strv_add_free"); ++ test_func (test_strv_add_unique_free, "/util/strv_add_unique_free"); + test_func (test_strv_dup, "/util/strv_dup"); + test_func (test_strv_count, "/util/strv_count"); + test_func (test_check_nt_time_string_lifetime, "/util/check_nt_time_string_lifetime"); +-- +2.20.1 + diff --git a/0001-build-add-with-vendor-error-message-configure-option.patch b/0001-build-add-with-vendor-error-message-configure-option.patch new file mode 100644 index 0000000..75235ee --- /dev/null +++ b/0001-build-add-with-vendor-error-message-configure-option.patch @@ -0,0 +1,60 @@ +From 0353d704879f20983184f8bded4f16538d72f7cc Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 10 Mar 2021 18:12:09 +0100 +Subject: [PATCH] build: add --with-vendor-error-message configure option + +With the new configure option --with-vendor-error-message a packager or +a distribution can add a message if adcli returns with an error. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1889386 +--- + configure.ac | 15 +++++++++++++++ + tools/tools.c | 6 ++++++ + 2 files changed, 21 insertions(+) + +diff --git a/configure.ac b/configure.ac +index baa0d3b..7dfba97 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -123,6 +123,21 @@ if test "$sasl_invalid" = "yes"; then + AC_MSG_ERROR([Couldn't find Cyrus SASL headers]) + fi + ++# -------------------------------------------------------------------- ++# Vendor error message ++ ++AC_ARG_WITH([vendor-error-message], ++ [AS_HELP_STRING([--with-vendor-error-message=ARG], ++ [Add a vendor specific error message shown if a adcli command fails] ++ )], ++ [AS_IF([test "x$withval" != "x"], ++ [AC_DEFINE_UNQUOTED([VENDOR_MSG], ++ ["$withval"], ++ [Vendor specific error message])], ++ [AC_MSG_ERROR([--with-vendor-error-message requires an argument])] ++ )], ++ []) ++ + # -------------------------------------------------------------------- + # Documentation options + +diff --git a/tools/tools.c b/tools/tools.c +index d0dcf98..84bbba9 100644 +--- a/tools/tools.c ++++ b/tools/tools.c +@@ -538,6 +538,12 @@ main (int argc, + + if (conn) + adcli_conn_unref (conn); ++#ifdef VENDOR_MSG ++ if (ret != 0) { ++ fprintf (stderr, VENDOR_MSG"\n"); ++ } ++#endif ++ + return ret; + } + +-- +2.30.2 + diff --git a/0001-coverity-add-missing-NULL-checks.patch b/0001-coverity-add-missing-NULL-checks.patch new file mode 100644 index 0000000..2f41992 --- /dev/null +++ b/0001-coverity-add-missing-NULL-checks.patch @@ -0,0 +1,44 @@ +From 13fe79c0a78028ccfe8e3d4e5ee16cfb9e143924 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 2 Jun 2021 13:39:31 +0200 +Subject: [PATCH 1/2] coverity: add missing NULL checks + +--- + library/adenroll.c | 2 ++ + library/adldap.c | 7 +++++++ + 2 files changed, 9 insertions(+) + +diff --git a/library/adenroll.c b/library/adenroll.c +index f693e58..c726093 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -3046,6 +3046,8 @@ adcli_enroll_set_keytab_enctypes (adcli_enroll *enroll, + krb5_enctype *newval = NULL; + int len; + ++ return_if_fail (enroll != NULL); ++ + if (value) { + for (len = 0; value[len] != 0; len++); + newval = malloc (sizeof (krb5_enctype) * (len + 1)); +diff --git a/library/adldap.c b/library/adldap.c +index d93efb7..b86014c 100644 +--- a/library/adldap.c ++++ b/library/adldap.c +@@ -231,6 +231,13 @@ _adcli_ldap_have_in_mod (LDAPMod *mod, + + vals = malloc (sizeof (struct berval) * (count + 1)); + pvals = malloc (sizeof (struct berval *) * (count + 1)); ++ if (vals == NULL || pvals == NULL) { ++ _adcli_err ("Memory allocation failed, assuming attribute must be updated."); ++ free (vals); ++ free (pvals); ++ return 0; ++ } ++ + for (i = 0; i < count; i++) { + vals[i].bv_val = mod->mod_vals.modv_strvals[i]; + vals[i].bv_len = strlen (vals[i].bv_val); +-- +2.31.1 + diff --git a/0001-create-user-add-nis-domain-option.patch b/0001-create-user-add-nis-domain-option.patch new file mode 100644 index 0000000..32b0ca9 --- /dev/null +++ b/0001-create-user-add-nis-domain-option.patch @@ -0,0 +1,71 @@ +From 1457b4a7623a8ae58fb8d6a652d1cc44904b8863 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 18 Mar 2019 11:02:57 +0100 +Subject: [PATCH 1/2] create-user: add nis-domain option + +Related to https://gitlab.freedesktop.org/realmd/adcli/issues/2 +--- + doc/adcli.xml | 8 ++++++++ + tools/entry.c | 6 ++++++ + 2 files changed, 14 insertions(+) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 4722c3a..18620c0 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -531,6 +531,14 @@ $ adcli create-user Fry --domain=domain.example.com \ + the new created user account, which should be the user's + numeric primary user id. + ++ ++ ++ Set the msSFU30NisDomain attribute of ++ the new created user account, which should be the user's ++ NIS domain is the NIS/YP service of Active Directory's Services for Unix (SFU) ++ are used. This is needed to let the 'UNIX attributes' tab of older Active ++ Directoy versions show the set UNIX specific attributes. ++ + + + +diff --git a/tools/entry.c b/tools/entry.c +index 7b6a200..69ce62c 100644 +--- a/tools/entry.c ++++ b/tools/entry.c +@@ -52,6 +52,7 @@ typedef enum { + opt_unix_uid, + opt_unix_gid, + opt_unix_shell, ++ opt_nis_domain, + } Option; + + static adcli_tool_desc common_usages[] = { +@@ -62,6 +63,7 @@ static adcli_tool_desc common_usages[] = { + { opt_unix_uid, "unix uid number" }, + { opt_unix_gid, "unix gid number" }, + { opt_unix_shell, "unix shell" }, ++ { opt_nis_domain, "NIS domain" }, + { opt_domain, "active directory domain name" }, + { opt_domain_realm, "kerberos realm for the domain" }, + { opt_domain_controller, "domain directory server to connect to" }, +@@ -159,6 +161,7 @@ adcli_tool_user_create (adcli_conn *conn, + { "unix-uid", required_argument, NULL, opt_unix_uid }, + { "unix-gid", required_argument, NULL, opt_unix_gid }, + { "unix-shell", required_argument, NULL, opt_unix_shell }, ++ { "nis-domain", required_argument, NULL, opt_nis_domain }, + { "domain-ou", required_argument, NULL, opt_domain_ou }, + { "domain", required_argument, NULL, opt_domain }, + { "domain-realm", required_argument, NULL, opt_domain_realm }, +@@ -200,6 +203,9 @@ adcli_tool_user_create (adcli_conn *conn, + case opt_unix_shell: + adcli_attrs_add (attrs, "loginShell", optarg, NULL); + break; ++ case opt_nis_domain: ++ adcli_attrs_add (attrs, "msSFU30NisDomain", optarg, NULL); ++ break; + case opt_domain_ou: + ou = optarg; + break; +-- +2.20.1 + diff --git a/0001-delete-do-not-exit-if-keytab-cannot-be-read.patch b/0001-delete-do-not-exit-if-keytab-cannot-be-read.patch new file mode 100644 index 0000000..15aaf07 --- /dev/null +++ b/0001-delete-do-not-exit-if-keytab-cannot-be-read.patch @@ -0,0 +1,32 @@ +From 40d3be22f6e518e4354aa7c3d0278291fcbed32f Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 5 Jun 2020 17:06:58 +0200 +Subject: [PATCH] delete: do not exit if keytab cannot be read + +Reading the keytab is not required when deleting a host object in AD. It +is only needed in the case where the host was added with a manual set +NetBIOS name (--computer-name option) which does not match the short +hostname and no computer name was given at the delete-computer command +line. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1840752 +--- + tools/computer.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/tools/computer.c b/tools/computer.c +index 292c4d8..a90c4b2 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -952,8 +952,6 @@ adcli_tool_computer_delete (adcli_conn *conn, + if (res != ADCLI_SUCCESS) { + warnx ("couldn't lookup domain info from keytab: %s", + adcli_get_last_error ()); +- adcli_enroll_unref (enroll); +- return -res; + } + + res = adcli_conn_connect (conn); +-- +2.26.2 + diff --git a/0001-discovery-fix.patch b/0001-discovery-fix.patch new file mode 100644 index 0000000..7c1018d --- /dev/null +++ b/0001-discovery-fix.patch @@ -0,0 +1,27 @@ +From 08bac0946de29f3e5de90743ce6dfc7118d4ad20 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 11 Feb 2020 17:42:03 +0100 +Subject: [PATCH] discovery fix + +Do not continue processing on closed connection. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1802258 +--- + library/addisco.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/library/addisco.c b/library/addisco.c +index 6e73ead..f3b3546 100644 +--- a/library/addisco.c ++++ b/library/addisco.c +@@ -622,6 +622,7 @@ ldap_disco (const char *domain, + "Couldn't perform discovery search"); + ldap_unbind_ext_s (ldap[i], NULL, NULL); + ldap[i] = NULL; ++ continue; + } + + /* From https://msdn.microsoft.com/en-us/library/ff718294.aspx first +-- +2.26.2 + diff --git a/0001-doc-add-missing-samba_data_tool_path.xml-.in-to-EXTR.patch b/0001-doc-add-missing-samba_data_tool_path.xml-.in-to-EXTR.patch new file mode 100644 index 0000000..7fc04d3 --- /dev/null +++ b/0001-doc-add-missing-samba_data_tool_path.xml-.in-to-EXTR.patch @@ -0,0 +1,25 @@ +From 2edc26afda17db1a92703deb16658e9de9f79e14 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 3 Sep 2019 14:39:37 +0200 +Subject: [PATCH] doc: add missing samba_data_tool_path.xml(.in) to EXTRA_DIST + +--- + doc/Makefile.am | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/doc/Makefile.am b/doc/Makefile.am +index 3a53843..4490688 100644 +--- a/doc/Makefile.am ++++ b/doc/Makefile.am +@@ -31,6 +31,8 @@ EXTRA_DIST = \ + static \ + version.xml.in \ + version.xml \ ++ samba_data_tool_path.xml.in \ ++ samba_data_tool_path.xml \ + $(NULL) + + CLEANFILES = \ +-- +2.28.0 + diff --git a/0001-doc-explain-how-to-force-password-reset.patch b/0001-doc-explain-how-to-force-password-reset.patch new file mode 100644 index 0000000..f3d25f4 --- /dev/null +++ b/0001-doc-explain-how-to-force-password-reset.patch @@ -0,0 +1,30 @@ +From 9b187095edb8c914238419ed51fef6041864f4fc Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 26 Aug 2019 13:33:24 +0200 +Subject: [PATCH] doc: explain how to force password reset + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1738573 +--- + doc/adcli.xml | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 094f577..4f201e0 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -330,7 +330,11 @@ Password for Administrator: + important here is currently the + option, see + smb.conf5 +- for details. ++ for details. ++ Note that if the machine account password is not ++ older than 30 days, you have to pass ++ to ++ force the update. + + + +-- +2.21.0 + diff --git a/0001-doc-explain-required-AD-permissions.patch b/0001-doc-explain-required-AD-permissions.patch new file mode 100644 index 0000000..fe0826a --- /dev/null +++ b/0001-doc-explain-required-AD-permissions.patch @@ -0,0 +1,242 @@ +From fa5c5fb4f8e7bcadf3e5a3798bd060720fd35eaa Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 20 Oct 2020 13:34:41 +0200 +Subject: [PATCH] doc: explain required AD permissions + +When using a restricted account with adcli some operations might fail +because the account might not have all required permissions. The man +page is extended and now explains which permissions are needed under +given circumstances. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1852080 +Resolves: https://gitlab.freedesktop.org/realmd/adcli/-/issues/20 +--- + doc/Makefile.am | 10 ++++ + doc/adcli.xml | 132 +++++++++++++++++++++++++++++++++++++++++++++ + library/adenroll.c | 30 ++++++----- + 3 files changed, 160 insertions(+), 12 deletions(-) + +diff --git a/doc/Makefile.am b/doc/Makefile.am +index 4490688..50fb777 100644 +--- a/doc/Makefile.am ++++ b/doc/Makefile.am +@@ -33,14 +33,17 @@ EXTRA_DIST = \ + version.xml \ + samba_data_tool_path.xml.in \ + samba_data_tool_path.xml \ ++ permissions.xml \ + $(NULL) + + CLEANFILES = \ + $(man8_MANS) \ ++ permissions.xml \ + $(NULL) + + XSLTPROC_FLAGS = \ + --nonet \ ++ --xinclude \ + --stringparam man.output.quietly 1 \ + --stringparam funcsynopsis.style ansi \ + --stringparam man.th.extra1.suppress 1 \ +@@ -50,6 +53,13 @@ XSLTPROC_FLAGS = \ + XSLTPROC_MAN = \ + $(XSLTPROC) $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl + ++permissions.xml: ../library/adenroll.c adcli.xml ++ echo "" > $@ ++ grep '".*".*/\* :ADPermissions: ' $< | sed -e 's#.*"\(.*\)".*/\* :ADPermissions: \(.*\)\*/$$#\1\2#' | sed -e 's#\*##g' >> $@ ++ echo "" >> $@ ++ ++$(man8_MANS): permissions.xml ++ + .xml.8: + $(AM_V_GEN) $(XSLTPROC_MAN) $< + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 1437679..cc44fd8 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -885,6 +885,138 @@ Password for Administrator: + + + ++ ++ Delegated Permissions ++ It is common practice in AD to not use an account from the Domain ++ Administrators group to join a machine to a domain but use a dedicated ++ account which only has permissions to join a machine to one or more OUs ++ in the Active Directory tree. Giving the needed permissions to a single ++ account or a group in Active Directory is called Delegation. A typical ++ example on how to configured Delegation can be found in the Delegation ++ section of the blog post ++ Who can add workstation to the domain. ++ ++ ++ When using an account with delegated permissions with adcli ++ basically the same applies as well. However some aspects are explained ++ here in a bit more details to better illustrate different concepts of ++ Active Directory and to make it more easy to debug permissions issues ++ during the join. Please note that the following is not specific to ++ adcli but applies to all applications which would like to modify ++ certain properties or objects in Active Directory with an account with ++ limited permissions. ++ ++ First, as said in the blog post it is sufficient to have ++ "Create computer object" permissions to join a ++ computer to a domain. But this would only work as expected if the ++ computer object does not exist in Active Directory before the join. ++ Because only when a new object is created Active Directory does not ++ apply additional permission checks on the attributes of the new ++ computer object. This means the delegated user can add any kind of ++ attribute with any value to a new computer object also long as they ++ meet general constraints like e.g. that the attribute must be defined ++ in the schema and is allowed in a objectclass of the object, the value ++ must match the syntax defined in the schema or that the ++ must be unique in the domain. ++ ++ If you want to use the account with delegated permission to ++ remove computer objects in Active Directory (adcli delete-computer) you ++ should of course make sure that the account has ++ "Delete computer object" permissions. ++ ++ If the computer object already exists the ++ "Create computer object" permission does not apply ++ anymore since now an existing object must be modified. Now permissions ++ on the individual attributes are needed. e.g. ++ "Read and write Account Restrictions" or ++ "Reset Password". For some attributes Active ++ Directory has two types of permissions the plain ++ "Read and Write" permissions and the ++ "Validated Write" permissions. For the latter case ++ there are two specific permissions relevant for adcli, namely ++ ++ Validated write to DNS host name ++ Validated write to service principal name ++ ++ Details about the validation of the values can be found in the ++ "Validated Writes" section of ++ [MS-ADTS], especially ++ dNSHostName ++ and ++ servicePrincipalName. ++ To cut it short for "Validated write to DNS host name" ++ the domain part of the fully-qualified hostname must either match the ++ domain name of the domain you want to join to or must be listed in the ++ attribute. And for ++ "Validated write to service principal name" the ++ hostname part of the service principal name must match the name stored ++ in or some other attributes which are ++ not handled by adcli. This also means that ++ cannot be empty or only contain a short ++ name if the service principal name should contain a fully-qualified ++ name. ++ ++ To summarize, if you only have validated write permissions you ++ should make sure the domain part of the hostname matches the domain you ++ want to join or use the with a matching ++ name. ++ ++ The plain read write permissions do not run additional ++ validations but the attribute values must still be in agreement with ++ the general constraints mentioned above. If the computer object already ++ exists adcli might need the following permissions which are also needed ++ by Windows clients to modify existing attributes: ++ ++ Reset Password ++ Read and write Account Restrictions ++ Read and (validated) write to DNS host name ++ Read and (validated) write to service principal name ++ ++ additionally adcli needs ++ ++ Read and write msDS-supportedEncryptionTypes ++ ++ This is added for security reasons to avoid that Active Directory ++ stores Kerberos keys with (potentially weaker) encryption types than ++ the client supports since Active Directory is often configured to still ++ support older (weaker) encryption types for compatibility reasons. ++ ++ ++ All other attributes are only set or modified on demand, i.e. ++ adcli must be called with an option the would set or modify the given ++ attribute. In the following the attributes adcli can modify together ++ with the required permissions are listed: ++ ++ ++ ++ For the management of users and groups (adcli create-user, ++ adcli delete-user, adcli create-group, adcli delete-group) the same ++ applies only for different types of objects, i.e. users and groups. ++ Since currently adcli only supports the creation and the removal of ++ user and group objects it is sufficient to have the ++ "Create/Delete User objects" and ++ "Create/Delete Group objects" permissions. ++ ++ If you want to manage group members as well (adcli add-member, ++ adcli remove-member) "Read/Write Members" permissions ++ are needed as well. ++ ++ Depending on the version of Active Directory the ++ "Delegation of Control Wizard" might offer some ++ shortcuts for common task like e.g. ++ ++ Create, delete and manage user accounts ++ Create, delete and manage groups ++ Modify the membership of a group ++ ++ The first 2 shortcuts will provided full access to user and group ++ objects which, as explained above, is more than currently is needed. ++ After using those shortcut it is a good idea to verify in the ++ "Security" tab in the "Properties" ++ of the related Active Directory container that the assigned permissions ++ meet the expectations. ++ ++ + + Bugs + +diff --git a/library/adenroll.c b/library/adenroll.c +index e745295..98e9786 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -71,19 +71,25 @@ static krb5_enctype v51_earlier_enctypes[] = { + 0 + }; + ++/* The following list containst all attributes handled by adcli, some are ++ * read-only and the others can be written as well. To properly document the ++ * required permissions each attribute which adcli tries to modify should have ++ * a comment starting with ':ADPermissions:' and the related permissions in AD ++ * on the same line. Multiple permissions can be seperated with a '*'. For all ++ * other attribute a suitable comment is very welcome. */ + static char *default_ad_ldap_attrs[] = { +- "sAMAccountName", +- "userPrincipalName", +- "msDS-KeyVersionNumber", +- "msDS-supportedEncryptionTypes", +- "dNSHostName", +- "servicePrincipalName", +- "operatingSystem", +- "operatingSystemVersion", +- "operatingSystemServicePack", +- "pwdLastSet", +- "userAccountControl", +- "description", ++ "sAMAccountName", /* Only set during creation */ ++ "userPrincipalName", /* :ADPermissions: Read/Write userPrincipal Name */ ++ "msDS-KeyVersionNumber", /* Manages by AD */ ++ "msDS-supportedEncryptionTypes", /* :ADPermissions: Read/Write msDS-SupportedEncryptionTypes */ ++ "dNSHostName", /* :ADPermissions: Read/Write dNSHostName * Read and write DNS host name attributes * Validated write to DNS host name */ ++ "servicePrincipalName", /* :ADPermissions: Read/Write servicePrincipalName * Validated write to service principal name */ ++ "operatingSystem", /* :ADPermissions: Read/Write Operating System */ ++ "operatingSystemVersion", /* :ADPermissions: Read/Write Operating System Version */ ++ "operatingSystemServicePack", /* :ADPermissions: Read/Write operatingSystemServicePack */ ++ "pwdLastSet", /* Managed by AD */ ++ "userAccountControl", /* :ADPermissions: Read/Write userAccountControl */ ++ "description", /* :ADPermissions: Read/Write Description */ + NULL, + }; + +-- +2.28.0 + diff --git a/0001-enroll-add-is_service-member.patch b/0001-enroll-add-is_service-member.patch new file mode 100644 index 0000000..0a5129a --- /dev/null +++ b/0001-enroll-add-is_service-member.patch @@ -0,0 +1,66 @@ +From 4e4dbf8d2b437808863f8be85e7f30865d88c7fc Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 23 Oct 2020 16:46:43 +0200 +Subject: [PATCH 1/7] enroll: add is_service member + +Add helpers to indicate a managed service account. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1854112 +--- + library/adenroll.c | 17 +++++++++++++++++ + library/adenroll.h | 4 ++++ + 2 files changed, 21 insertions(+) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 98e9786..5ae1f7b 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -103,6 +103,8 @@ static char *default_ad_ldap_attrs[] = { + struct _adcli_enroll { + int refs; + adcli_conn *conn; ++ bool is_service; ++ bool is_service_explicit; + + char *host_fqdn; + int host_fqdn_explicit; +@@ -2942,6 +2944,21 @@ adcli_enroll_get_desciption (adcli_enroll *enroll) + return enroll->description; + } + ++void ++adcli_enroll_set_is_service (adcli_enroll *enroll, bool value) ++{ ++ return_if_fail (enroll != NULL); ++ ++ enroll->is_service = value; ++ enroll->is_service_explicit = true; ++} ++ ++bool ++adcli_enroll_get_is_service (adcli_enroll *enroll) ++{ ++ return enroll->is_service; ++} ++ + const char ** + adcli_enroll_get_service_principals_to_add (adcli_enroll *enroll) + { +diff --git a/library/adenroll.h b/library/adenroll.h +index 0606169..7765ed4 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -130,6 +130,10 @@ const char * adcli_enroll_get_desciption (adcli_enroll *enroll); + void adcli_enroll_set_description (adcli_enroll *enroll, + const char *value); + ++bool adcli_enroll_get_is_service (adcli_enroll *enroll); ++void adcli_enroll_set_is_service (adcli_enroll *enroll, ++ bool value); ++ + krb5_kvno adcli_enroll_get_kvno (adcli_enroll *enroll); + + void adcli_enroll_set_kvno (adcli_enroll *enroll, +-- +2.28.0 + diff --git a/0001-ensure_keytab_principals-do-not-leak-memory-when-cal.patch b/0001-ensure_keytab_principals-do-not-leak-memory-when-cal.patch new file mode 100644 index 0000000..dfdf745 --- /dev/null +++ b/0001-ensure_keytab_principals-do-not-leak-memory-when-cal.patch @@ -0,0 +1,72 @@ +From 3a84c2469c31967bc22c0490456f07723ef5fc86 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 20 Mar 2019 11:01:50 +0100 +Subject: [PATCH 1/4] ensure_keytab_principals: do not leak memory when called + twice + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1630187 +--- + library/adenroll.c | 32 +++++++++++++++++++++----------- + 1 file changed, 21 insertions(+), 11 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index d1f746c..48cb4cf 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -413,6 +413,25 @@ ensure_service_principals (adcli_result res, + return res; + } + ++static void enroll_clear_keytab_principals (adcli_enroll *enroll) ++{ ++ krb5_context k5; ++ size_t c; ++ ++ if (enroll->keytab_principals) { ++ k5 = adcli_conn_get_krb5_context (enroll->conn); ++ return_if_fail (k5 != NULL); ++ ++ for (c = 0; enroll->keytab_principals[c] != NULL; c++) ++ krb5_free_principal (k5, enroll->keytab_principals[c]); ++ ++ free (enroll->keytab_principals); ++ enroll->keytab_principals = NULL; ++ } ++ ++ return; ++} ++ + static adcli_result + ensure_keytab_principals (adcli_result res, + adcli_enroll *enroll) +@@ -430,6 +449,7 @@ ensure_keytab_principals (adcli_result res, + k5 = adcli_conn_get_krb5_context (enroll->conn); + return_unexpected_if_fail (k5 != NULL); + ++ enroll_clear_keytab_principals (enroll); + enroll->keytab_principals = calloc (count + 3, sizeof (krb5_principal)); + return_unexpected_if_fail (enroll->keytab_principals != NULL); + at = 0; +@@ -1860,18 +1880,8 @@ static void + enroll_clear_state (adcli_enroll *enroll) + { + krb5_context k5; +- int i; +- +- if (enroll->keytab_principals) { +- k5 = adcli_conn_get_krb5_context (enroll->conn); +- return_if_fail (k5 != NULL); +- +- for (i = 0; enroll->keytab_principals[i] != NULL; i++) +- krb5_free_principal (k5, enroll->keytab_principals[i]); + +- free (enroll->keytab_principals); +- enroll->keytab_principals = NULL; +- } ++ enroll_clear_keytab_principals (enroll); + + if (enroll->keytab) { + k5 = adcli_conn_get_krb5_context (enroll->conn); +-- +2.20.1 + diff --git a/0001-fix-typo-in-flag-value.patch b/0001-fix-typo-in-flag-value.patch new file mode 100644 index 0000000..07577b7 --- /dev/null +++ b/0001-fix-typo-in-flag-value.patch @@ -0,0 +1,25 @@ +From 870ecd8f982ebb97092a93d730ad4006bd78505c Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 8 Aug 2018 12:03:01 +0200 +Subject: [PATCH 1/4] fix typo in flag value + +--- + library/adenroll.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/library/adenroll.h b/library/adenroll.h +index f87dffa..abbbfd4 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -30,7 +30,7 @@ typedef enum { + ADCLI_ENROLL_NO_KEYTAB = 1 << 1, + ADCLI_ENROLL_ALLOW_OVERWRITE = 1 << 2, + ADCLI_ENROLL_PASSWORD_VALID = 1 << 3, +- ADCLI_ENROLL_ADD_SAMBA_DATA = 1 << 3, ++ ADCLI_ENROLL_ADD_SAMBA_DATA = 1 << 4, + } adcli_enroll_flags; + + typedef struct _adcli_enroll adcli_enroll; +-- +2.17.1 + diff --git a/0001-join-always-add-service-principals.patch b/0001-join-always-add-service-principals.patch new file mode 100644 index 0000000..0281dc6 --- /dev/null +++ b/0001-join-always-add-service-principals.patch @@ -0,0 +1,86 @@ +From cd296bf24e7cc56fb8d00bad7e9a56c539894309 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 19 Mar 2019 20:44:36 +0100 +Subject: [PATCH 1/2] join: always add service principals + +If currently --service-name is given during the join only the service +names given by this option are added as service principal names. As a +result the default 'host' service principal name might be missing which +might cause issues e.g. with SSSD and sshd. + +The patch makes sure the default service principals 'host' and +'RestrictedKrbHost' are always added during join. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1644311 +--- + library/adenroll.c | 36 ++++++++++++++++++++++++++++++------ + 1 file changed, 30 insertions(+), 6 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 58362c2..d1f746c 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -288,16 +288,23 @@ ensure_computer_password (adcli_result res, + } + + static adcli_result +-ensure_service_names (adcli_result res, +- adcli_enroll *enroll) ++ensure_default_service_names (adcli_enroll *enroll) + { + int length = 0; + +- if (res != ADCLI_SUCCESS) +- return res; ++ if (enroll->service_names != NULL) { ++ length = seq_count (enroll->service_names); + +- if (enroll->service_names || enroll->service_principals) +- return ADCLI_SUCCESS; ++ /* Make sure there is no entry with an unexpected case. AD ++ * would not care but since the client side is case-sensitive ++ * we should make sure we use the expected spelling. */ ++ seq_remove_unsorted (enroll->service_names, ++ &length, "host", ++ (seq_compar)strcasecmp, free); ++ seq_remove_unsorted (enroll->service_names, ++ &length, "RestrictedKrbHost", ++ (seq_compar)strcasecmp, free); ++ } + + /* The default ones specified by MS */ + enroll->service_names = _adcli_strv_add (enroll->service_names, +@@ -307,6 +314,19 @@ ensure_service_names (adcli_result res, + return ADCLI_SUCCESS; + } + ++static adcli_result ++ensure_service_names (adcli_result res, ++ adcli_enroll *enroll) ++{ ++ if (res != ADCLI_SUCCESS) ++ return res; ++ ++ if (enroll->service_names || enroll->service_principals) ++ return ADCLI_SUCCESS; ++ ++ return ensure_default_service_names (enroll); ++} ++ + static adcli_result + add_service_names_to_service_principals (adcli_enroll *enroll) + { +@@ -2039,6 +2059,10 @@ adcli_enroll_join (adcli_enroll *enroll, + if (res != ADCLI_SUCCESS) + return res; + ++ res = ensure_default_service_names (enroll); ++ if (res != ADCLI_SUCCESS) ++ return res; ++ + res = adcli_enroll_prepare (enroll, flags); + if (res != ADCLI_SUCCESS) + return res; +-- +2.20.1 + diff --git a/0001-join-update-set-dNSHostName-if-not-set.patch b/0001-join-update-set-dNSHostName-if-not-set.patch new file mode 100644 index 0000000..a27653e --- /dev/null +++ b/0001-join-update-set-dNSHostName-if-not-set.patch @@ -0,0 +1,59 @@ +From beb7abfacc0010987d2cd8ab70f7c373d309eed9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 15 Oct 2020 18:01:12 +0200 +Subject: [PATCH] join/update: set dNSHostName if not set + +If during a join or update an existing AD computer object does not have +the dNSHostName attribute set it will be set with the current hostname. +This is important for cases where the user doing the join or update only +has "Validated write to service principal name" for the computer object. +The validated write with fully-qualified names can only be successful if +dNSHostName is set, see [MS-ADTS] section 3.1.1.5.3.1.1.4 "Validated +Writes - servicePrincipalName" for details. + +Resolves https://bugzilla.redhat.com/show_bug.cgi?id=1734764 +--- + library/adenroll.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 246f658..e745295 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1403,21 +1403,29 @@ update_computer_account (adcli_enroll *enroll) + { + int res = 0; + LDAP *ldap; ++ char *value = NULL; + + 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) { ++ * line or not set in the existing AD object. 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->computer_attributes != NULL) { ++ value = _adcli_ldap_parse_value (ldap, ++ enroll->computer_attributes, ++ "dNSHostName"); ++ } ++ if (enroll->host_fqdn_explicit || value == NULL ) { + char *vals_dNSHostName[] = { enroll->host_fqdn, NULL }; + LDAPMod dNSHostName = { LDAP_MOD_REPLACE, "dNSHostName", { vals_dNSHostName, } }; + LDAPMod *mods[] = { &dNSHostName, NULL }; + + res |= update_computer_attribute (enroll, ldap, mods); + } ++ free (value); + + if (res == ADCLI_SUCCESS && enroll->trusted_for_delegation_explicit) { + char *vals_userAccountControl[] = { NULL , NULL }; +-- +2.28.0 + diff --git a/0001-library-add-missing-strdup.patch b/0001-library-add-missing-strdup.patch new file mode 100644 index 0000000..378bb3c --- /dev/null +++ b/0001-library-add-missing-strdup.patch @@ -0,0 +1,34 @@ +From a64cce9830c2e9c26e120f671b247ee71b45c888 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 12 Apr 2019 17:31:41 +0200 +Subject: [PATCH] library: add missing strdup + +In add_server_side_service_principals _adcli_strv_add_unique is called +which only adds a string to a list without copying to. Since the +original list will be freed later the value must be copied. + +This issue was introduce with 972f1a2f35829ed89f5353bd204683aa9ad6a2d2 +and hence + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1630187 +--- + library/adenroll.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 1cce86a..52aa8a8 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1987,7 +1987,8 @@ add_server_side_service_principals (adcli_enroll *enroll) + _adcli_info ("Checking %s", spn_list[c]); + if (!_adcli_strv_has_ex (enroll->service_principals_to_remove, spn_list[c], strcasecmp)) { + enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals, +- spn_list[c], &length, false); ++ strdup (spn_list[c]), ++ &length, false); + assert (enroll->service_principals != NULL); + _adcli_info (" Added %s", spn_list[c]); + } +-- +2.20.1 + diff --git a/0001-library-move-UAC-flags-to-a-more-common-header-file.patch b/0001-library-move-UAC-flags-to-a-more-common-header-file.patch new file mode 100644 index 0000000..0129517 --- /dev/null +++ b/0001-library-move-UAC-flags-to-a-more-common-header-file.patch @@ -0,0 +1,50 @@ +From a7a40ce4f47fe40305624b6d86c135b7d27c387d Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 11 Jun 2021 12:44:36 +0200 +Subject: [PATCH 1/3] library: move UAC flags to a more common header file + +--- + library/adenroll.c | 8 -------- + library/adprivate.h | 8 ++++++++ + 2 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index f00d179..0b1c066 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -93,13 +93,6 @@ static char *default_ad_ldap_attrs[] = { + NULL, + }; + +-/* Some constants for the userAccountControl AD LDAP attribute, see e.g. +- * https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro +- * for details. */ +-#define UAC_WORKSTATION_TRUST_ACCOUNT 0x1000 +-#define UAC_DONT_EXPIRE_PASSWORD 0x10000 +-#define UAC_TRUSTED_FOR_DELEGATION 0x80000 +- + struct _adcli_enroll { + int refs; + adcli_conn *conn; +diff --git a/library/adprivate.h b/library/adprivate.h +index 55e6234..822f919 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -39,6 +39,14 @@ + #define HOST_NAME_MAX 255 + #endif + ++/* Some constants for the userAccountControl AD LDAP attribute, see e.g. ++ * https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro ++ * for details. */ ++#define UAC_ACCOUNTDISABLE 0x0002 ++#define UAC_WORKSTATION_TRUST_ACCOUNT 0x1000 ++#define UAC_DONT_EXPIRE_PASSWORD 0x10000 ++#define UAC_TRUSTED_FOR_DELEGATION 0x80000 ++ + /* Utilities */ + + #if !defined(__cplusplus) && (__GNUC__ > 2) +-- +2.31.1 + diff --git a/0001-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch b/0001-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch new file mode 100644 index 0000000..c682c5a --- /dev/null +++ b/0001-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch @@ -0,0 +1,92 @@ +From 85b835f8258a57e3b23de47a255dddd822d5bfb3 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 15 Mar 2019 17:33:44 +0100 +Subject: [PATCH] library: use getaddrinfo with AI_CANONNAME to find a FQDN + +Currently adcli creates service principals only with a short name if the +hostname of the client is a short name. This would fail is +Kerberos/GSSAPI clients will use the fully-qualified domain name (FQDN) +to access the host. + +With this patch adcli tries to expand the short name by calling +getaddrinfo with the AI_CANONNAME hint. + +Related to https://gitlab.freedesktop.org/realmd/adcli/issues/1 +--- + doc/adcli.xml | 6 +++++- + library/adconn.c | 30 +++++++++++++++++++++++++++++- + 2 files changed, 34 insertions(+), 2 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 97dec08..4722c3a 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -228,7 +228,11 @@ Password for Administrator: + + Override the local machine's fully qualified + domain name. If not specified, the local machine's hostname +- will be retrieved via gethostname(). ++ will be retrieved via gethostname(). ++ If gethostname() only returns a short name ++ getaddrinfo() with the AI_CANONNAME hint ++ is called to expand the name to a fully qualified domain ++ name. + + + +diff --git a/library/adconn.c b/library/adconn.c +index e2250e3..f6c23d3 100644 +--- a/library/adconn.c ++++ b/library/adconn.c +@@ -86,11 +86,36 @@ struct _adcli_conn_ctx { + krb5_keytab keytab; + }; + ++static char *try_to_get_fqdn (const char *host_name) ++{ ++ int ret; ++ char *fqdn = NULL; ++ struct addrinfo *res; ++ struct addrinfo hints; ++ ++ memset (&hints, 0, sizeof (struct addrinfo)); ++ hints.ai_socktype = SOCK_DGRAM; ++ hints.ai_flags = AI_CANONNAME; ++ ++ ret = getaddrinfo (host_name, NULL, &hints, &res); ++ if (ret != 0) { ++ _adcli_err ("Failed to find FQDN: %s", gai_strerror (ret)); ++ return NULL; ++ } ++ ++ fqdn = strdup (res->ai_canonname); ++ ++ freeaddrinfo (res); ++ ++ return fqdn; ++} ++ + static adcli_result + ensure_host_fqdn (adcli_result res, + adcli_conn *conn) + { + char hostname[HOST_NAME_MAX + 1]; ++ char *fqdn = NULL; + int ret; + + if (res != ADCLI_SUCCESS) +@@ -107,7 +132,10 @@ ensure_host_fqdn (adcli_result res, + return ADCLI_ERR_UNEXPECTED; + } + +- conn->host_fqdn = strdup (hostname); ++ if (strchr (hostname, '.') == NULL) { ++ fqdn = try_to_get_fqdn (hostname); ++ } ++ conn->host_fqdn = fqdn != NULL ? fqdn : strdup (hostname); + return_unexpected_if_fail (conn->host_fqdn != NULL); + return ADCLI_SUCCESS; + } +-- +2.20.1 + diff --git a/0001-man-explain-optional-parameter-of-login-ccache-bette.patch b/0001-man-explain-optional-parameter-of-login-ccache-bette.patch new file mode 100644 index 0000000..191fa3e --- /dev/null +++ b/0001-man-explain-optional-parameter-of-login-ccache-bette.patch @@ -0,0 +1,44 @@ +From 93a39bd12db11dd407676f428cfbc30406a88c36 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 15 Jun 2020 15:57:47 +0200 +Subject: [PATCH] man: explain optional parameter of login-ccache better + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1791545 +--- + doc/adcli.xml | 20 +++++++++++++------- + 1 file changed, 13 insertions(+), 7 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index acced25..ecf8726 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -155,13 +155,19 @@ $ LDAPTLS_CACERT=/path/to/ad_dc_ca_cert.pem adcli join --use-ldaps -D domain.exa + + + Use the specified kerberos credential +- 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. ++ 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. ++ Please note that since the ++ ccache_name is optional the ++ =(equal) sign is mandatory. If = is missing the ++ parameter is treated as optionless extra argument. How ++ this is handled depends on the specific sub-command. ++ + + + +-- +2.26.2 + diff --git a/0001-man-make-handling-of-optional-credential-cache-more-.patch b/0001-man-make-handling-of-optional-credential-cache-more-.patch new file mode 100644 index 0000000..3d5955a --- /dev/null +++ b/0001-man-make-handling-of-optional-credential-cache-more-.patch @@ -0,0 +1,41 @@ +From 88fbb7e2395dec20b37697a213a097909870c21f Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 13 Aug 2020 17:10:01 +0200 +Subject: [PATCH] man: make handling of optional credential cache more clear + +The optional Kerberos credential cache can only be used with the long +option name --login-ccache and not with the short version -C. To make +this more clear each option get its own entry. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1791545 +--- + doc/adcli.xml | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index ecf8726..1437679 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -153,10 +153,16 @@ $ LDAPTLS_CACERT=/path/to/ad_dc_ca_cert.pem adcli join --use-ldaps -D domain.exa + + + +- +- Use the specified kerberos credential ++ ++ Use the default Kerberos credential ++ cache to authenticate with the domain. ++ ++ ++ ++ ++ Use the specified Kerberos credential + cache to authenticate with the domain. If no credential +- cache is specified, the default kerberos 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 +-- +2.26.2 + diff --git a/0001-man-move-note-to-the-right-section.patch b/0001-man-move-note-to-the-right-section.patch new file mode 100644 index 0000000..307dfc9 --- /dev/null +++ b/0001-man-move-note-to-the-right-section.patch @@ -0,0 +1,48 @@ +From d2d3879bdfcea70757a8b0527882e79e8b5c6e70 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 27 Nov 2019 18:26:44 +0100 +Subject: [PATCH] man: move note to the right section + +Unfortunately the note about the password lifetime was added to the join +section. This patch move it to the update section where it belongs to. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1738573 + https://bugzilla.redhat.com/show_bug.cgi?id=1745931 + https://bugzilla.redhat.com/show_bug.cgi?id=1774622 +--- + doc/adcli.xml | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 4f201e0..9faf96a 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -330,11 +330,7 @@ Password for Administrator: + important here is currently the + option, see + smb.conf5 +- for details. +- Note that if the machine account password is not +- older than 30 days, you have to pass +- to +- force the update. ++ for details. + + + +@@ -472,7 +468,11 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + important here is currently the + option, see + smb.conf5 +- for details. ++ for details. ++ Note that if the machine account password is not ++ older than 30 days, you have to pass ++ to ++ force the update. + + + +-- +2.21.0 + diff --git a/0001-service-account-fix-typo-in-the-man-page-entry.patch b/0001-service-account-fix-typo-in-the-man-page-entry.patch new file mode 100644 index 0000000..b476a93 --- /dev/null +++ b/0001-service-account-fix-typo-in-the-man-page-entry.patch @@ -0,0 +1,35 @@ +From 637cc53953ef61c90530ae5eaf26eb4911336465 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 10 Dec 2020 18:29:18 +0100 +Subject: [PATCH] service-account: fix typo in the man page entry + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1906303 +--- + doc/adcli.xml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 14921f9..a64687a 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -943,7 +943,7 @@ Password for Administrator: + of the managed service account as a suffix. On most systems it would be + /etc/krb5.keytab with a suffix of + 'domain.example.com', e.g. +- /etc/krb5.keytad.domain.example.com. ++ /etc/krb5.keytab.domain.example.com. + + adcli create-msa can be called multiple + times to reset the password of the managed service account. To identify +@@ -955,7 +955,7 @@ Password for Administrator: + + The managed service account password can be updated with + +-$ adcli update --domain=domain.example.com --host-keytab=/etc/krb5.keytad.domain.example.com ++$ adcli update --domain=domain.example.com --host-keytab=/etc/krb5.keytab.domain.example.com + + and the managed service account can be deleted with + +-- +2.29.2 + diff --git a/0001-tools-add-missing-use-ldaps-option-to-update-and-tes.patch b/0001-tools-add-missing-use-ldaps-option-to-update-and-tes.patch new file mode 100644 index 0000000..a62fbe6 --- /dev/null +++ b/0001-tools-add-missing-use-ldaps-option-to-update-and-tes.patch @@ -0,0 +1,36 @@ +From 76ca1e6737742208d83e016d43a3379e378f8d90 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 14 Oct 2020 17:44:10 +0200 +Subject: [PATCH] tools: add missing use-ldaps option to update and testjoin + +When adding the use-ldaps option the update and testjoin sub-commands +were forgotten. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1883467 +--- + tools/computer.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tools/computer.c b/tools/computer.c +index 24ea258..5a97d8b 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -491,6 +491,7 @@ adcli_tool_computer_update (adcli_conn *conn, + struct option options[] = { + { "domain", required_argument, NULL, opt_domain }, + { "domain-controller", required_argument, NULL, opt_domain_controller }, ++ { "use-ldaps", no_argument, 0, opt_use_ldaps }, + { "host-fqdn", required_argument, 0, opt_host_fqdn }, + { "computer-name", required_argument, 0, opt_computer_name }, + { "host-keytab", required_argument, 0, opt_host_keytab }, +@@ -612,6 +613,7 @@ adcli_tool_computer_testjoin (adcli_conn *conn, + struct option options[] = { + { "domain", required_argument, NULL, opt_domain }, + { "domain-controller", required_argument, NULL, opt_domain_controller }, ++ { "use-ldaps", no_argument, 0, opt_use_ldaps }, + { "host-keytab", required_argument, 0, opt_host_keytab }, + { "verbose", no_argument, NULL, opt_verbose }, + { "help", no_argument, NULL, 'h' }, +-- +2.28.0 + diff --git a/0001-tools-add-show-computer-command.patch b/0001-tools-add-show-computer-command.patch new file mode 100644 index 0000000..1decaf6 --- /dev/null +++ b/0001-tools-add-show-computer-command.patch @@ -0,0 +1,338 @@ +From 0a169bd9b2687293f74bb57694eb82f9769610c9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 27 Nov 2019 12:34:45 +0100 +Subject: [PATCH 1/2] tools: add show-computer command + +The show-computer command prints the LDAP attributes of the related +computer object from AD. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1737342 +--- + doc/adcli.xml | 28 ++++++++++++++ + library/adenroll.c | 78 +++++++++++++++++++++++++++++--------- + library/adenroll.h | 5 +++ + tools/computer.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++ + tools/tools.c | 1 + + tools/tools.h | 4 ++ + 6 files changed, 191 insertions(+), 18 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 9faf96a..1f93186 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -93,6 +93,11 @@ + --domain=domain.example.com + computer + ++ ++ adcli show-computer ++ --domain=domain.example.com ++ computer ++ + + + +@@ -811,6 +816,29 @@ Password for Administrator: + + + ++ ++ Show Computer Account Attributes ++ ++ adcli show-computer show the computer account ++ attributes stored in AD. The account must already exist. ++ ++ ++$ adcli show-computer --domain=domain.example.com host2 ++Password for Administrator: ++ ++ ++ If the computer name contains a dot, then it is ++ treated as fully qualified host name, otherwise it is treated ++ as short computer name. ++ ++ If no computer name is specified, then the host name of the ++ computer adcli is running on is used, as returned by ++ gethostname(). ++ ++ The various global options can be used. ++ ++ ++ + + Bugs + +diff --git a/library/adenroll.c b/library/adenroll.c +index 524663a..8d2adeb 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -71,6 +71,21 @@ static krb5_enctype v51_earlier_enctypes[] = { + 0 + }; + ++static char *default_ad_ldap_attrs[] = { ++ "sAMAccountName", ++ "userPrincipalName", ++ "msDS-KeyVersionNumber", ++ "msDS-supportedEncryptionTypes", ++ "dNSHostName", ++ "servicePrincipalName", ++ "operatingSystem", ++ "operatingSystemVersion", ++ "operatingSystemServicePack", ++ "pwdLastSet", ++ "userAccountControl", ++ NULL, ++}; ++ + /* Some constants for the userAccountControl AD LDAP attribute, see e.g. + * https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro + * for details. */ +@@ -1213,19 +1228,6 @@ retrieve_computer_account (adcli_enroll *enroll) + char *end; + int ret; + +- char *attrs[] = { +- "msDS-KeyVersionNumber", +- "msDS-supportedEncryptionTypes", +- "dNSHostName", +- "servicePrincipalName", +- "operatingSystem", +- "operatingSystemVersion", +- "operatingSystemServicePack", +- "pwdLastSet", +- "userAccountControl", +- NULL, +- }; +- + assert (enroll->computer_dn != NULL); + assert (enroll->computer_attributes == NULL); + +@@ -1233,7 +1235,8 @@ retrieve_computer_account (adcli_enroll *enroll) + assert (ldap != NULL); + + ret = ldap_search_ext_s (ldap, enroll->computer_dn, LDAP_SCOPE_BASE, +- "(objectClass=*)", attrs, 0, NULL, NULL, NULL, -1, ++ "(objectClass=*)", default_ad_ldap_attrs, ++ 0, NULL, NULL, NULL, -1, + &enroll->computer_attributes); + + if (ret != LDAP_SUCCESS) { +@@ -2179,12 +2182,11 @@ adcli_enroll_load (adcli_enroll *enroll) + } + + adcli_result +-adcli_enroll_update (adcli_enroll *enroll, +- adcli_enroll_flags flags) ++adcli_enroll_read_computer_account (adcli_enroll *enroll, ++ adcli_enroll_flags flags) + { + adcli_result res = ADCLI_SUCCESS; + LDAP *ldap; +- char *value; + + return_unexpected_if_fail (enroll != NULL); + +@@ -2214,7 +2216,18 @@ adcli_enroll_update (adcli_enroll *enroll, + } + + /* Get information about the computer account */ +- res = retrieve_computer_account (enroll); ++ return retrieve_computer_account (enroll); ++} ++ ++adcli_result ++adcli_enroll_update (adcli_enroll *enroll, ++ adcli_enroll_flags flags) ++{ ++ adcli_result res = ADCLI_SUCCESS; ++ LDAP *ldap; ++ char *value; ++ ++ res = adcli_enroll_read_computer_account (enroll, flags); + if (res != ADCLI_SUCCESS) + return res; + +@@ -2242,6 +2255,35 @@ adcli_enroll_update (adcli_enroll *enroll, + return enroll_join_or_update_tasks (enroll, flags); + } + ++adcli_result ++adcli_enroll_show_computer_attribute (adcli_enroll *enroll) ++{ ++ LDAP *ldap; ++ size_t c; ++ char **vals; ++ size_t v; ++ ++ ldap = adcli_conn_get_ldap_connection (enroll->conn); ++ assert (ldap != NULL); ++ ++ for (c = 0; default_ad_ldap_attrs[c] != NULL; c++) { ++ vals = _adcli_ldap_parse_values (ldap, ++ enroll->computer_attributes, ++ default_ad_ldap_attrs[c]); ++ printf ("%s:\n", default_ad_ldap_attrs[c]); ++ if (vals == NULL) { ++ printf (" - not set -\n"); ++ } else { ++ for (v = 0; vals[v] != NULL; v++) { ++ printf (" %s\n", vals[v]); ++ } ++ } ++ _adcli_strv_free (vals); ++ } ++ ++ return ADCLI_SUCCESS; ++} ++ + adcli_result + adcli_enroll_delete (adcli_enroll *enroll, + adcli_enroll_flags delete_flags) +diff --git a/library/adenroll.h b/library/adenroll.h +index 1d5d00d..11eb517 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -46,6 +46,11 @@ adcli_result adcli_enroll_join (adcli_enroll *enroll, + adcli_result adcli_enroll_update (adcli_enroll *enroll, + adcli_enroll_flags flags); + ++adcli_result adcli_enroll_read_computer_account (adcli_enroll *enroll, ++ adcli_enroll_flags flags); ++ ++adcli_result adcli_enroll_show_computer_attribute (adcli_enroll *enroll); ++ + adcli_result adcli_enroll_delete (adcli_enroll *enroll, + adcli_enroll_flags delete_flags); + +diff --git a/tools/computer.c b/tools/computer.c +index ac8a203..c8b96a4 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -964,3 +964,96 @@ adcli_tool_computer_delete (adcli_conn *conn, + adcli_enroll_unref (enroll); + return 0; + } ++ ++int ++adcli_tool_computer_show (adcli_conn *conn, ++ int argc, ++ char *argv[]) ++{ ++ adcli_enroll *enroll; ++ adcli_result res; ++ int opt; ++ ++ struct option options[] = { ++ { "domain", required_argument, NULL, opt_domain }, ++ { "domain-realm", required_argument, NULL, opt_domain_realm }, ++ { "domain-controller", required_argument, NULL, opt_domain_controller }, ++ { "login-user", required_argument, NULL, opt_login_user }, ++ { "login-ccache", optional_argument, NULL, opt_login_ccache }, ++ { "login-type", required_argument, NULL, opt_login_type }, ++ { "no-password", no_argument, 0, opt_no_password }, ++ { "stdin-password", no_argument, 0, opt_stdin_password }, ++ { "prompt-password", no_argument, 0, opt_prompt_password }, ++ { "verbose", no_argument, NULL, opt_verbose }, ++ { "help", no_argument, NULL, 'h' }, ++ { 0 }, ++ }; ++ ++ static adcli_tool_desc usages[] = { ++ { 0, "usage: adcli show-computer --domain=xxxx host1.example.com" }, ++ { 0 }, ++ }; ++ ++ enroll = adcli_enroll_new (conn); ++ if (enroll == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } ++ ++ while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) { ++ switch (opt) { ++ case 'h': ++ case '?': ++ case ':': ++ adcli_tool_usage (options, usages); ++ adcli_tool_usage (options, common_usages); ++ adcli_enroll_unref (enroll); ++ return opt == 'h' ? 0 : 2; ++ default: ++ res = parse_option ((Option)opt, optarg, conn, enroll); ++ if (res != ADCLI_SUCCESS) { ++ adcli_enroll_unref (enroll); ++ return res; ++ } ++ break; ++ } ++ } ++ ++ argc -= optind; ++ argv += optind; ++ ++ res = adcli_conn_connect (conn); ++ if (res != ADCLI_SUCCESS) { ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; ++ } ++ ++ if (argc == 1) { ++ parse_fqdn_or_name (enroll, argv[0]); ++ } ++ ++ res = adcli_enroll_read_computer_account (enroll, 0); ++ if (res != ADCLI_SUCCESS) { ++ warnx ("couldn't read data for %s: %s", ++ adcli_enroll_get_host_fqdn (enroll) != NULL ++ ? adcli_enroll_get_host_fqdn (enroll) ++ : adcli_enroll_get_computer_name (enroll), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; ++ } ++ ++ res = adcli_enroll_show_computer_attribute (enroll); ++ if (res != ADCLI_SUCCESS) { ++ warnx ("couldn't print data for %s: %s", ++ argv[0], adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; ++ } ++ ++ adcli_enroll_unref (enroll); ++ return 0; ++} +diff --git a/tools/tools.c b/tools/tools.c +index fc9fa9a..9d422f2 100644 +--- a/tools/tools.c ++++ b/tools/tools.c +@@ -59,6 +59,7 @@ struct { + { "preset-computer", adcli_tool_computer_preset, "Pre setup computers accounts", }, + { "reset-computer", adcli_tool_computer_reset, "Reset a computer account", }, + { "delete-computer", adcli_tool_computer_delete, "Delete a computer account", }, ++ { "show-computer", adcli_tool_computer_show, "Show computer account attributes stored in AD", }, + { "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", }, +diff --git a/tools/tools.h b/tools/tools.h +index 8cebbf9..3702875 100644 +--- a/tools/tools.h ++++ b/tools/tools.h +@@ -78,6 +78,10 @@ int adcli_tool_computer_delete (adcli_conn *conn, + int argc, + char *argv[]); + ++int adcli_tool_computer_show (adcli_conn *conn, ++ int argc, ++ char *argv[]); ++ + int adcli_tool_user_create (adcli_conn *conn, + int argc, + char *argv[]); +-- +2.21.0 + diff --git a/0001-tools-disable-SSSD-s-locator-plugin.patch b/0001-tools-disable-SSSD-s-locator-plugin.patch new file mode 100644 index 0000000..07d791e --- /dev/null +++ b/0001-tools-disable-SSSD-s-locator-plugin.patch @@ -0,0 +1,41 @@ +From 50d580c58dab5928cadfc6ca82aedccee58eaced Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 5 Jun 2020 17:28:28 +0200 +Subject: [PATCH] tools: disable SSSD's locator plugin + +MIT's libkrb5 checks available locator plugins first before checking the +config file. This might cause issues when the locator plugin returns a +different DC than the one used for the LDAP connection if some data must +be replicated. + +This patch sets the SSSD_KRB5_LOCATOR_DISABLE environment variable to +'true' to disable SSSD's locator plugin for adcli. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1762633 +--- + tools/tools.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tools/tools.c b/tools/tools.c +index 9d422f2..1b6d879 100644 +--- a/tools/tools.c ++++ b/tools/tools.c +@@ -296,6 +296,7 @@ cleanup_krb5_conf_directory (void) + } + + unsetenv ("KRB5_CONFIG"); ++ unsetenv ("SSSD_KRB5_LOCATOR_DISABLE"); + } + + static void +@@ -394,6 +395,7 @@ setup_krb5_conf_directory (adcli_conn *conn) + adcli_krb5_conf_filename = filename; + adcli_krb5_d_directory = snippets; + setenv ("KRB5_CONFIG", adcli_krb5_conf_filename, 1); ++ setenv ("SSSD_KRB5_LOCATOR_DISABLE", "true", 1); + + } else { + free (filename); +-- +2.26.2 + diff --git a/0001-tools-fix-typo-in-show-password-help-output.patch b/0001-tools-fix-typo-in-show-password-help-output.patch new file mode 100644 index 0000000..d82d49c --- /dev/null +++ b/0001-tools-fix-typo-in-show-password-help-output.patch @@ -0,0 +1,26 @@ +From d70075c597e7ebc1683d407409c45b04110676a0 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 15 Jun 2020 15:41:53 +0200 +Subject: [PATCH] tools: fix typo in show-password help output + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1791611 +--- + tools/computer.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/computer.c b/tools/computer.c +index a90c4b2..24ea258 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -154,7 +154,7 @@ static adcli_tool_desc common_usages[] = { + "accounts" }, + { opt_show_details, "show information about joining the domain after\n" + "a successful join" }, +- { opt_show_password, "show computer account password after after a\n" ++ { opt_show_password, "show computer account password after a\n" + "successful join" }, + { opt_add_samba_data, "add domain SID and computer account password\n" + "to the Samba specific configuration database" }, +-- +2.26.2 + diff --git a/0001-tools-remove-errx-from-computer-commands.patch b/0001-tools-remove-errx-from-computer-commands.patch new file mode 100644 index 0000000..71db611 --- /dev/null +++ b/0001-tools-remove-errx-from-computer-commands.patch @@ -0,0 +1,328 @@ +From fa7926c7a9d92bc7c42c610ba6f1706c635aa901 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 15 Apr 2019 17:54:27 +0200 +Subject: [PATCH 1/7] tools: remove errx from computer commands + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596 +--- + tools/computer.c | 166 ++++++++++++++++++++++++++++++----------------- + 1 file changed, 107 insertions(+), 59 deletions(-) + +diff --git a/tools/computer.c b/tools/computer.c +index bee695c..9cbbb28 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -379,8 +379,10 @@ adcli_tool_computer_join (adcli_conn *conn, + }; + + enroll = adcli_enroll_new (conn); +- if (enroll == NULL) +- errx (-1, "unexpected memory problems"); ++ if (enroll == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } + + while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) { + switch (opt) { +@@ -415,21 +417,28 @@ adcli_tool_computer_join (adcli_conn *conn, + + if (argc == 1) + adcli_conn_set_domain_name (conn, argv[0]); +- else if (argc > 1) +- errx (2, "extra arguments specified"); ++ else if (argc > 1) { ++ warnx ("extra arguments specified"); ++ adcli_enroll_unref (enroll); ++ return 2; ++ } + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't connect to %s domain: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + res = adcli_enroll_join (enroll, flags); + if (res != ADCLI_SUCCESS) { +- errx (-res, "joining domain %s failed: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("joining domain %s failed: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + if (details) +@@ -486,8 +495,10 @@ adcli_tool_computer_update (adcli_conn *conn, + }; + + enroll = adcli_enroll_new (conn); +- if (enroll == NULL) +- errx (-1, "unexpected memory problems"); ++ if (enroll == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } + + while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) { + switch (opt) { +@@ -525,22 +536,28 @@ adcli_tool_computer_update (adcli_conn *conn, + + res = adcli_enroll_load (enroll); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't lookup domain info from keytab: %s", +- adcli_get_last_error ()); ++ warnx ("couldn't lookup domain info from keytab: %s", ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't connect to %s domain: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + res = adcli_enroll_update (enroll, flags); + if (res != ADCLI_SUCCESS) { +- errx (-res, "updating membership with domain %s failed: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("updating membership with domain %s failed: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + if (details) +@@ -578,8 +595,10 @@ adcli_tool_computer_testjoin (adcli_conn *conn, + }; + + enroll = adcli_enroll_new (conn); +- if (enroll == NULL) +- errx (-1, "unexpected memory problems"); ++ if (enroll == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } + + while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) { + switch (opt) { +@@ -604,18 +623,18 @@ adcli_tool_computer_testjoin (adcli_conn *conn, + res = adcli_enroll_load (enroll); + if (res != ADCLI_SUCCESS) { + adcli_enroll_unref (enroll); +- adcli_conn_unref (conn); +- errx (-res, "couldn't lookup domain info from keytab: %s", +- adcli_get_last_error ()); ++ warnx ("couldn't lookup domain info from keytab: %s", ++ adcli_get_last_error ()); ++ return -res; + } + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { + adcli_enroll_unref (enroll); +- adcli_conn_unref (conn); +- errx (-res, "couldn't connect to %s domain: %s", ++ warnx ("couldn't connect to %s domain: %s", + adcli_conn_get_domain_name (conn), + adcli_get_last_error ()); ++ return -res; + } + + printf ("Sucessfully validated join to domain %s\n", +@@ -665,8 +684,10 @@ adcli_tool_computer_preset (adcli_conn *conn, + }; + + enroll = adcli_enroll_new (conn); +- if (enroll == NULL) +- errx (-1, "unexpected memory problems"); ++ if (enroll == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } + flags = ADCLI_ENROLL_NO_KEYTAB; + + while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) { +@@ -694,17 +715,22 @@ adcli_tool_computer_preset (adcli_conn *conn, + argc -= optind; + argv += optind; + +- if (argc < 1) +- errx (EUSAGE, "specify one or more host names of computer accounts to preset"); ++ if (argc < 1) { ++ warnx ("specify one or more host names of computer accounts to preset"); ++ adcli_enroll_unref (enroll); ++ return EUSAGE; ++ } + + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); + reset_password = (adcli_enroll_get_computer_password (enroll) == NULL); + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't connect to %s domain: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + for (i = 0; i < argc; i++) { +@@ -715,9 +741,11 @@ adcli_tool_computer_preset (adcli_conn *conn, + + res = adcli_enroll_join (enroll, flags); + if (res != ADCLI_SUCCESS) { +- errx (-res, "presetting %s in %s domain failed: %s", argv[i], +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("presetting %s in %s domain failed: %s", argv[i], ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + printf ("computer-name: %s\n", adcli_enroll_get_computer_name (enroll)); +@@ -758,8 +786,10 @@ adcli_tool_computer_reset (adcli_conn *conn, + }; + + enroll = adcli_enroll_new (conn); +- if (enroll == NULL) +- errx (-1, "unexpected memory problems"); ++ if (enroll == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } + + while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) { + switch (opt) { +@@ -779,14 +809,19 @@ adcli_tool_computer_reset (adcli_conn *conn, + argc -= optind; + argv += optind; + +- if (argc != 1) +- errx (EUSAGE, "specify one host name of computer account to reset"); ++ if (argc != 1) { ++ warnx ("specify one host name of computer account to reset"); ++ adcli_enroll_unref (enroll); ++ return EUSAGE; ++ } + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't connect to %s domain: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + parse_fqdn_or_name (enroll, argv[0]); +@@ -794,9 +829,11 @@ adcli_tool_computer_reset (adcli_conn *conn, + + res = adcli_enroll_password (enroll, 0); + if (res != ADCLI_SUCCESS) { +- errx (-res, "resetting %s in %s domain failed: %s", argv[0], +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("resetting %s in %s domain failed: %s", argv[0], ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + adcli_enroll_unref (enroll); +@@ -832,8 +869,10 @@ adcli_tool_computer_delete (adcli_conn *conn, + }; + + enroll = adcli_enroll_new (conn); +- if (enroll == NULL) +- errx (-1, "unexpected memory problems"); ++ if (enroll == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } + + while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) { + switch (opt) { +@@ -853,22 +892,29 @@ adcli_tool_computer_delete (adcli_conn *conn, + argc -= optind; + argv += optind; + +- if (argc > 1) +- errx (EUSAGE, "specify one host name of computer account to delete"); ++ if (argc > 1) { ++ warnx ("specify one host name of computer account to delete"); ++ adcli_enroll_unref (enroll); ++ return EUSAGE; ++ } + + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); + + res = adcli_enroll_load (enroll); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't lookup domain info from keytab: %s", +- adcli_get_last_error ()); ++ warnx ("couldn't lookup domain info from keytab: %s", ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't connect to %s domain: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + if (argc == 1) +@@ -876,9 +922,11 @@ adcli_tool_computer_delete (adcli_conn *conn, + + res = adcli_enroll_delete (enroll, 0); + if (res != ADCLI_SUCCESS) { +- errx (-res, "deleting %s in %s domain failed: %s", argv[0], +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("deleting %s in %s domain failed: %s", argv[0], ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; + } + + adcli_enroll_unref (enroll); +-- +2.20.1 + diff --git a/0002-Add-delattr-option.patch b/0002-Add-delattr-option.patch new file mode 100644 index 0000000..92cf623 --- /dev/null +++ b/0002-Add-delattr-option.patch @@ -0,0 +1,191 @@ +From cd5b6cdcf3e6bfc5776f2865f460f608421dfa3f Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 14 Jun 2021 08:42:21 +0200 +Subject: [PATCH 2/2] Add delattr option + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1690920 +--- + doc/adcli.xml | 11 ++++++++ + library/adenroll.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ + library/adenroll.h | 4 +++ + tools/computer.c | 9 +++++++ + 4 files changed, 90 insertions(+) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 8383aa7..bcf4857 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -577,6 +577,17 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + adcli options cannot be set with this option. + + ++ ++ ++ Remove the LDAP attribute ++ from the ++ LDAP host object. This option can be used multiple ++ times to remove multiple different attributes. ++ Please note that the account used to update the ++ host object must have the required privileges to delete ++ the given attributes. Attributes managed by other adcli ++ options cannot be removed. ++ + + + After a successful join print out information +diff --git a/library/adenroll.c b/library/adenroll.c +index dd51567..9a06d52 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -151,5 +151,6 @@ struct _adcli_enroll { + char *description; + char **setattr; ++ char **delattr; + }; + + static const char * +@@ -845,6 +846,39 @@ get_mods_for_attrs (adcli_enroll *enroll, int mod_op) + return mods; + } + ++static LDAPMod ** ++get_del_mods_for_attrs (adcli_enroll *enroll, int mod_op) ++{ ++ size_t len; ++ size_t c; ++ LDAPMod **mods = NULL; ++ ++ len = _adcli_strv_len (enroll->delattr); ++ if (len == 0) { ++ return NULL; ++ } ++ ++ mods = calloc (len + 1, sizeof (LDAPMod *)); ++ return_val_if_fail (mods != NULL, NULL); ++ ++ for (c = 0; c < len; c++) { ++ mods[c] = calloc (1, sizeof (LDAPMod)); ++ if (mods[c] == NULL) { ++ ldap_mods_free (mods, 1); ++ return NULL; ++ } ++ ++ mods[c]->mod_op = mod_op; ++ mods[c]->mod_type = strdup (enroll->delattr[c]); ++ mods[c]->mod_values = NULL; ++ if (mods[c]->mod_type == NULL) { ++ ldap_mods_free (mods, 1); ++ return NULL; ++ } ++ } ++ ++ return mods; ++} + + static adcli_result + create_computer_account (adcli_enroll *enroll, +@@ -1775,6 +1809,14 @@ update_computer_account (adcli_enroll *enroll) + } + } + ++ if (res == ADCLI_SUCCESS && enroll->delattr != NULL) { ++ LDAPMod **mods = get_del_mods_for_attrs (enroll, LDAP_MOD_DELETE); ++ if (mods != NULL) { ++ res |= update_computer_attribute (enroll, ldap, mods); ++ ldap_mods_free (mods, 1); ++ } ++ } ++ + if (res != 0) + _adcli_info ("Updated existing computer account: %s", enroll->computer_dn); + } +@@ -3475,6 +3517,30 @@ adcli_enroll_get_setattr (adcli_enroll *enroll) + return (const char **) enroll->setattr; + } + ++adcli_result ++adcli_enroll_add_delattr (adcli_enroll *enroll, const char *value) ++{ ++ return_val_if_fail (enroll != NULL, ADCLI_ERR_CONFIG); ++ return_val_if_fail (value != NULL, ADCLI_ERR_CONFIG); ++ ++ if (_adcli_strv_has_ex (default_ad_ldap_attrs, value, strcasecmp) == 1) { ++ _adcli_err ("Attribute [%s] cannot be removed with delattr", value); ++ return ADCLI_ERR_CONFIG; ++ } ++ ++ enroll->delattr = _adcli_strv_add (enroll->delattr, strdup (value), ++ NULL); ++ return_val_if_fail (enroll->delattr != NULL, ADCLI_ERR_CONFIG); ++ ++ return ADCLI_SUCCESS; ++} ++ ++const char ** ++adcli_enroll_get_delattr (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, NULL); ++ return (const char **) enroll->delattr; ++} + + #ifdef ADENROLL_TESTS + +diff --git a/library/adenroll.h b/library/adenroll.h +index 862bb60..e3ada33 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -142,6 +142,10 @@ const char ** adcli_enroll_get_setattr (adcli_enroll *enroll); + adcli_result adcli_enroll_add_setattr (adcli_enroll *enroll, + const char *value); + ++const char ** adcli_enroll_get_delattr (adcli_enroll *enroll); ++adcli_result adcli_enroll_add_delattr (adcli_enroll *enroll, ++ const char *value); ++ + bool adcli_enroll_get_is_service (adcli_enroll *enroll); + void adcli_enroll_set_is_service (adcli_enroll *enroll, + bool value); +diff --git a/tools/computer.c b/tools/computer.c +index af38894..dffeecb 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -115,6 +115,7 @@ typedef enum { + opt_remove_service_principal, + opt_description, + opt_setattr, ++ opt_delattr, + opt_use_ldaps, + } Option; + +@@ -154,6 +155,7 @@ static adcli_tool_desc common_usages[] = { + { opt_remove_service_principal, "remove the given service principal from the account\n" }, + { opt_description, "add a description to the account\n" }, + { opt_setattr, "add an attribute with a value\n" }, ++ { opt_delattr, "remove an attribute\n" }, + { opt_no_password, "don't prompt for or read a password" }, + { opt_prompt_password, "prompt for a password if necessary" }, + { opt_stdin_password, "read a password from stdin (until EOF) if\n" +@@ -341,6 +343,12 @@ parse_option (Option opt, + warnx ("parsing setattr option failed"); + } + return ret; ++ case opt_delattr: ++ ret = adcli_enroll_add_delattr (enroll, optarg); ++ if (ret != ADCLI_SUCCESS) { ++ warnx ("parsing delattr option failed"); ++ } ++ return ret; + case opt_use_ldaps: + adcli_conn_set_use_ldaps (conn, true); + return ADCLI_SUCCESS; +@@ -534,6 +542,7 @@ adcli_tool_computer_update (adcli_conn *conn, + { "os-service-pack", optional_argument, NULL, opt_os_service_pack }, + { "description", optional_argument, NULL, opt_description }, + { "setattr", required_argument, NULL, opt_setattr }, ++ { "delattr", required_argument, NULL, opt_delattr }, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime }, + { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, +-- +2.31.1 + diff --git a/0002-Add-dont-expire-password-option.patch b/0002-Add-dont-expire-password-option.patch new file mode 100644 index 0000000..0ae8a90 --- /dev/null +++ b/0002-Add-dont-expire-password-option.patch @@ -0,0 +1,241 @@ +From 74b52a30c2b142118b7f26f78c014e7bee825e84 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 2 Jun 2021 17:24:07 +0200 +Subject: [PATCH 2/2] Add dont-expire-password option + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1769644 +--- + doc/adcli.xml | 28 ++++++++++++++++++++++ + library/adenroll.c | 58 +++++++++++++++++++++++++++++++++++++++++----- + library/adenroll.h | 4 ++++ + tools/computer.c | 12 ++++++++++ + 4 files changed, 96 insertions(+), 6 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index a64687a..7c2e126 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -347,6 +347,20 @@ Password for Administrator: + not allow that Kerberos tickets can be forwarded to the + host. + ++ ++ ++ Set or unset the DONT_EXPIRE_PASSWORD ++ flag in the userAccountControl attribute to indicate if ++ the machine account password should expire or not. By ++ default adcli will set this flag while joining the ++ domain which corresponds to the default behavior of ++ Windows clients. ++ Please note that if the password will expire ++ (--dont-expire-password=false) a renewal mechanism has ++ to be enabled on the client to not loose the ++ connectivity to AD if the password expires. ++ ++ + + + Add a service principal name. In +@@ -491,6 +505,20 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + not allow that Kerberos tickets can be forwarded to the + host. + ++ ++ ++ Set or unset the DONT_EXPIRE_PASSWORD ++ flag in the userAccountControl attribute to indicate if ++ the machine account password should expire or not. By ++ default adcli will set this flag while joining the ++ domain which corresponds to the default behavior of ++ Windows clients. ++ Please note that if the password will expire ++ (--dont-expire-password=false) a renewal mechanism has ++ to be enabled on the client to not loose the ++ connectivity to AD if the password expires. ++ ++ + + + Add a service principal name. In +diff --git a/library/adenroll.c b/library/adenroll.c +index c726093..01f149c 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -152,6 +152,8 @@ struct _adcli_enroll { + char *samba_data_tool; + bool trusted_for_delegation; + int trusted_for_delegation_explicit; ++ bool dont_expire_password; ++ int dont_expire_password_explicit; + char *description; + }; + +@@ -829,6 +831,8 @@ create_computer_account (adcli_enroll *enroll, + int ret; + size_t c; + size_t m; ++ uint32_t uac = UAC_WORKSTATION_TRUST_ACCOUNT | UAC_DONT_EXPIRE_PASSWORD ; ++ char *uac_str = NULL; + + LDAPMod *all_mods[] = { + &objectClass, +@@ -849,11 +853,21 @@ create_computer_account (adcli_enroll *enroll, + LDAPMod *mods[mods_count]; + + if (adcli_enroll_get_trusted_for_delegation (enroll)) { +- vals_userAccountControl[0] = "593920"; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD | TRUSTED_FOR_DELEGATION */ ++ uac |= UAC_TRUSTED_FOR_DELEGATION; ++ } ++ ++ if (!adcli_enroll_get_dont_expire_password (enroll)) { ++ uac &= ~(UAC_DONT_EXPIRE_PASSWORD); ++ } ++ ++ if (asprintf (&uac_str, "%d", uac) < 0) { ++ return_val_if_reached (ADCLI_ERR_UNEXPECTED); + } ++ vals_userAccountControl[0] = uac_str; + + ret = calculate_enctypes (enroll, &val); + if (ret != ADCLI_SUCCESS) { ++ free (uac_str); + return ret; + } + vals_supportedEncryptionTypes[0] = val; +@@ -868,6 +882,7 @@ create_computer_account (adcli_enroll *enroll, + mods[m] = NULL; + + ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL); ++ free (uac_str); + free (val); + + /* +@@ -1566,10 +1581,20 @@ static char *get_user_account_control (adcli_enroll *enroll) + uac = UAC_WORKSTATION_TRUST_ACCOUNT | UAC_DONT_EXPIRE_PASSWORD; + } + +- if (adcli_enroll_get_trusted_for_delegation (enroll)) { +- uac |= UAC_TRUSTED_FOR_DELEGATION; +- } else { +- uac &= ~(UAC_TRUSTED_FOR_DELEGATION); ++ if (enroll->trusted_for_delegation_explicit) { ++ if (adcli_enroll_get_trusted_for_delegation (enroll)) { ++ uac |= UAC_TRUSTED_FOR_DELEGATION; ++ } else { ++ uac &= ~(UAC_TRUSTED_FOR_DELEGATION); ++ } ++ } ++ ++ if (enroll->dont_expire_password_explicit) { ++ if (adcli_enroll_get_dont_expire_password (enroll)) { ++ uac |= UAC_DONT_EXPIRE_PASSWORD; ++ } else { ++ uac &= ~(UAC_DONT_EXPIRE_PASSWORD); ++ } + } + + if (asprintf (&uac_str, "%d", uac) < 0) { +@@ -1613,7 +1640,8 @@ update_computer_account (adcli_enroll *enroll) + } + free (value); + +- if (res == ADCLI_SUCCESS && enroll->trusted_for_delegation_explicit) { ++ if (res == ADCLI_SUCCESS && (enroll->trusted_for_delegation_explicit || ++ enroll->dont_expire_password_explicit)) { + char *vals_userAccountControl[] = { NULL , NULL }; + LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } }; + LDAPMod *mods[] = { &userAccountControl, NULL }; +@@ -3194,6 +3222,24 @@ adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, + enroll->trusted_for_delegation_explicit = 1; + } + ++bool ++adcli_enroll_get_dont_expire_password (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, false); ++ ++ return enroll->dont_expire_password; ++} ++ ++void ++adcli_enroll_set_dont_expire_password (adcli_enroll *enroll, ++ bool value) ++{ ++ return_if_fail (enroll != NULL); ++ ++ enroll->dont_expire_password = value; ++ enroll->dont_expire_password_explicit = 1; ++} ++ + void + adcli_enroll_set_description (adcli_enroll *enroll, const char *value) + { +diff --git a/library/adenroll.h b/library/adenroll.h +index 11a30c8..5190eb6 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -126,6 +126,10 @@ bool adcli_enroll_get_trusted_for_delegation (adcli_enroll *enroll + void adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, + bool value); + ++bool adcli_enroll_get_dont_expire_password (adcli_enroll *enroll); ++void adcli_enroll_set_dont_expire_password (adcli_enroll *enroll, ++ bool value); ++ + const char * adcli_enroll_get_desciption (adcli_enroll *enroll); + void adcli_enroll_set_description (adcli_enroll *enroll, + const char *value); +diff --git a/tools/computer.c b/tools/computer.c +index 98a0472..954066a 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -110,6 +110,7 @@ typedef enum { + opt_add_samba_data, + opt_samba_data_tool, + opt_trusted_for_delegation, ++ opt_dont_expire_password, + opt_add_service_principal, + opt_remove_service_principal, + opt_description, +@@ -143,6 +144,8 @@ static adcli_tool_desc common_usages[] = { + { opt_computer_password_lifetime, "lifetime of the host accounts password in days", }, + { opt_trusted_for_delegation, "set/unset the TRUSTED_FOR_DELEGATION flag\n" + "in the userAccountControl attribute", }, ++ { opt_dont_expire_password, "set/unset the DONT_EXPIRE_PASSWORD flag\n" ++ "in the userAccountControl attribute", }, + { opt_add_service_principal, "add the given service principal to the account\n" }, + { opt_remove_service_principal, "remove the given service principal from the account\n" }, + { opt_description, "add a description to the account\n" }, +@@ -304,6 +307,13 @@ parse_option (Option opt, + adcli_enroll_set_trusted_for_delegation (enroll, false); + } + return ADCLI_SUCCESS; ++ case opt_dont_expire_password: ++ if (strcasecmp (optarg, "true") == 0 || strcasecmp (optarg, "yes") == 0) { ++ adcli_enroll_set_dont_expire_password (enroll, true); ++ } else { ++ adcli_enroll_set_dont_expire_password (enroll, false); ++ } ++ return ADCLI_SUCCESS; + case opt_add_service_principal: + adcli_enroll_add_service_principal_to_add (enroll, optarg); + return ADCLI_SUCCESS; +@@ -383,6 +393,7 @@ adcli_tool_computer_join (adcli_conn *conn, + { "description", optional_argument, NULL, opt_description }, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, ++ { "dont-expire-password", required_argument, NULL, opt_dont_expire_password }, + { "add-service-principal", required_argument, NULL, opt_add_service_principal }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, +@@ -504,6 +515,7 @@ adcli_tool_computer_update (adcli_conn *conn, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime }, + { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, ++ { "dont-expire-password", required_argument, NULL, opt_dont_expire_password }, + { "add-service-principal", required_argument, NULL, opt_add_service_principal }, + { "remove-service-principal", required_argument, NULL, opt_remove_service_principal }, + { "show-details", no_argument, NULL, opt_show_details }, +-- +2.31.1 + diff --git a/0002-Use-strdup-if-offset-are-used.patch b/0002-Use-strdup-if-offset-are-used.patch new file mode 100644 index 0000000..47ea1ad --- /dev/null +++ b/0002-Use-strdup-if-offset-are-used.patch @@ -0,0 +1,31 @@ +From 4ba49015ca1ad98c03a209a11862f8e00d00fbd0 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 24 Aug 2016 16:19:36 +0200 +Subject: [PATCH 02/23] Use strdup() if offset are used + +Strings with an offset to the original starting point must be copied +because otherwise they cannot be properly freed later. +--- + library/adenroll.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index d1020e9..05885d0 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1318,9 +1318,9 @@ load_keytab_entry (krb5_context k5, + + } else if (!enroll->host_fqdn && _adcli_str_has_prefix (name, "host/") && strchr (name, '.')) { + /* Skip host/ prefix */ +- enroll->host_fqdn = name + 5; +- _adcli_info ("Found host qualified name in keytab: %s", name); +- name = NULL; ++ enroll->host_fqdn = strdup (name + 5); ++ return_val_if_fail (enroll->host_fqdn != NULL, FALSE); ++ _adcli_info ("Found host qualified name in keytab: %s", enroll->host_fqdn); + } + } + +-- +2.14.4 + diff --git a/0002-_adcli_call_external_program-silence-noisy-debug-mes.patch b/0002-_adcli_call_external_program-silence-noisy-debug-mes.patch new file mode 100644 index 0000000..76fe1dc --- /dev/null +++ b/0002-_adcli_call_external_program-silence-noisy-debug-mes.patch @@ -0,0 +1,25 @@ +From 8cc4ef1cae7d4d753f2cf9aeb8021dd96cb75d36 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 8 Aug 2018 12:17:18 +0200 +Subject: [PATCH 2/4] _adcli_call_external_program: silence noisy debug message + +--- + library/adutil.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/library/adutil.c b/library/adutil.c +index 6334b52..17d2caa 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -672,7 +672,7 @@ done: + if (wret == -1) { + _adcli_err ("No sure what happend to net command."); + } else { +- if (WIFEXITED (status)) { ++ if (WIFEXITED (status) && WEXITSTATUS (status) != 0) { + _adcli_err ("net command failed with %d.", + WEXITSTATUS (status)); + } +-- +2.17.1 + diff --git a/0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch b/0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch new file mode 100644 index 0000000..58d91ec --- /dev/null +++ b/0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch @@ -0,0 +1,60 @@ +From 7148ab196d0a96ede9b5ef463b0481d0fe372b21 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 11 Jun 2021 12:46:03 +0200 +Subject: [PATCH 2/3] adcli_entry: add entry_attrs with userAccountControl + attribute + +--- + library/adentry.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/library/adentry.c b/library/adentry.c +index 1cc0518..13dcaf8 100644 +--- a/library/adentry.c ++++ b/library/adentry.c +@@ -42,6 +42,7 @@ struct _adcli_entry { + char *entry_dn; + char *domain_ou; + char *entry_container; ++ LDAPMessage *entry_attrs; + }; + + static adcli_entry * +@@ -63,6 +64,7 @@ entry_new (adcli_conn *conn, + + entry->builder = builder; + entry->object_class = object_class; ++ entry->entry_attrs = NULL; + return entry; + } + +@@ -82,6 +84,7 @@ entry_free (adcli_entry *entry) + free (entry->entry_container); + free (entry->entry_dn); + free (entry->domain_ou); ++ ldap_msgfree (entry->entry_attrs); + adcli_conn_unref (entry->conn); + free (entry); + } +@@ -102,7 +105,7 @@ static adcli_result + update_entry_from_domain (adcli_entry *entry, + LDAP *ldap) + { +- const char *attrs[] = { "1.1", NULL }; ++ const char *attrs[] = { "userAccountControl", NULL }; + LDAPMessage *results; + LDAPMessage *first; + const char *base; +@@ -139,7 +142,8 @@ update_entry_from_domain (adcli_entry *entry, + return_unexpected_if_fail (entry->entry_dn != NULL); + } + +- ldap_msgfree (results); ++ ldap_msgfree (entry->entry_attrs); ++ entry->entry_attrs = results; + return ADCLI_SUCCESS; + } + +-- +2.31.1 + diff --git a/0002-adconn-add-adcli_conn_set_krb5_context.patch b/0002-adconn-add-adcli_conn_set_krb5_context.patch new file mode 100644 index 0000000..769a022 --- /dev/null +++ b/0002-adconn-add-adcli_conn_set_krb5_context.patch @@ -0,0 +1,52 @@ +From 2fc259a88be618871cea8ff8b8a13bd3e040aea4 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 13 Jun 2019 17:23:47 +0200 +Subject: [PATCH 2/4] adconn: add adcli_conn_set_krb5_context + +Related to https://gitlab.freedesktop.org/realmd/adcli/issues/3 +--- + library/adconn.c | 13 +++++++++++++ + library/adconn.h | 3 +++ + 2 files changed, 16 insertions(+) + +diff --git a/library/adconn.c b/library/adconn.c +index f6c23d3..bcaced8 100644 +--- a/library/adconn.c ++++ b/library/adconn.c +@@ -1406,6 +1406,19 @@ adcli_conn_get_krb5_context (adcli_conn *conn) + return conn->k5; + } + ++void ++adcli_conn_set_krb5_context (adcli_conn *conn, ++ krb5_context k5) ++{ ++ return_if_fail (conn != NULL); ++ ++ if (conn->k5 != NULL) { ++ krb5_free_context (conn->k5); ++ } ++ ++ conn->k5 = k5; ++} ++ + const char * + adcli_conn_get_login_user (adcli_conn *conn) + { +diff --git a/library/adconn.h b/library/adconn.h +index 13cfd32..1ad5715 100644 +--- a/library/adconn.h ++++ b/library/adconn.h +@@ -97,6 +97,9 @@ LDAP * adcli_conn_get_ldap_connection (adcli_conn *conn); + + krb5_context adcli_conn_get_krb5_context (adcli_conn *conn); + ++void adcli_conn_set_krb5_context (adcli_conn *conn, ++ krb5_context k5); ++ + const char * adcli_conn_get_computer_name (adcli_conn *conn); + + void adcli_conn_set_computer_name (adcli_conn *conn, +-- +2.21.0 + diff --git a/0002-add-description-option-to-join-and-update.patch b/0002-add-description-option-to-join-and-update.patch new file mode 100644 index 0000000..a36dfc9 --- /dev/null +++ b/0002-add-description-option-to-join-and-update.patch @@ -0,0 +1,183 @@ +From 3937a2a7db90611aa7a93248233b0c5d31e85a3e Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 27 Nov 2019 14:48:32 +0100 +Subject: [PATCH 2/2] add description option to join and update + +This new option allows to set the description LDAP attribute for the AD +computer object. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1737342 +--- + doc/adcli.xml | 10 ++++++++++ + library/adenroll.c | 29 +++++++++++++++++++++++++++++ + library/adenroll.h | 4 ++++ + tools/computer.c | 7 +++++++ + 4 files changed, 50 insertions(+) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 1f93186..dd30435 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -275,6 +275,11 @@ Password for Administrator: + Set the operating system version on the computer + account. Not set by default. + ++ ++ ++ Set the description attribute on the computer ++ account. Not set by default. ++ + + + Additional service name for a kerberos +@@ -416,6 +421,11 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + Set the operating system version on the computer + account. Not set by default. + ++ ++ ++ Set the description attribute on the computer ++ account. Not set by default. ++ + + + Additional service name for a Kerberos +diff --git a/library/adenroll.c b/library/adenroll.c +index 8d2adeb..246f658 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -83,6 +83,7 @@ static char *default_ad_ldap_attrs[] = { + "operatingSystemServicePack", + "pwdLastSet", + "userAccountControl", ++ "description", + NULL, + }; + +@@ -143,6 +144,7 @@ struct _adcli_enroll { + char *samba_data_tool; + bool trusted_for_delegation; + int trusted_for_delegation_explicit; ++ char *description; + }; + + static adcli_result +@@ -756,6 +758,8 @@ create_computer_account (adcli_enroll *enroll, + 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 *vals_description[] = { enroll->description, NULL }; ++ LDAPMod description = { LDAP_MOD_ADD, "description", { vals_description, }, }; + + char *val = NULL; + +@@ -774,6 +778,7 @@ create_computer_account (adcli_enroll *enroll, + &operatingSystemServicePack, + &userPrincipalName, + &servicePrincipalName, ++ &description, + NULL + }; + +@@ -1460,6 +1465,14 @@ update_computer_account (adcli_enroll *enroll) + res |= update_computer_attribute (enroll, ldap, mods); + } + ++ if (res == ADCLI_SUCCESS && enroll->description != NULL) { ++ char *vals_description[] = { enroll->description, NULL }; ++ LDAPMod description = { LDAP_MOD_REPLACE, "description", { vals_description, }, }; ++ LDAPMod *mods[] = { &description, NULL, }; ++ ++ res |= update_computer_attribute (enroll, ldap, mods); ++ } ++ + if (res != 0) + _adcli_info ("Updated existing computer account: %s", enroll->computer_dn); + } +@@ -2899,6 +2912,22 @@ adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, + enroll->trusted_for_delegation_explicit = 1; + } + ++void ++adcli_enroll_set_description (adcli_enroll *enroll, const char *value) ++{ ++ return_if_fail (enroll != NULL); ++ if (value != NULL && value[0] != '\0') { ++ _adcli_str_set (&enroll->description, value); ++ } ++} ++ ++const char * ++adcli_enroll_get_desciption (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, NULL); ++ return enroll->description; ++} ++ + const char ** + adcli_enroll_get_service_principals_to_add (adcli_enroll *enroll) + { +diff --git a/library/adenroll.h b/library/adenroll.h +index 11eb517..0606169 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -126,6 +126,10 @@ bool adcli_enroll_get_trusted_for_delegation (adcli_enroll *enroll + void adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, + bool value); + ++const char * adcli_enroll_get_desciption (adcli_enroll *enroll); ++void adcli_enroll_set_description (adcli_enroll *enroll, ++ const char *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 c8b96a4..840e334 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -112,6 +112,7 @@ typedef enum { + opt_trusted_for_delegation, + opt_add_service_principal, + opt_remove_service_principal, ++ opt_description, + } Option; + + static adcli_tool_desc common_usages[] = { +@@ -142,6 +143,7 @@ static adcli_tool_desc common_usages[] = { + "in the userAccountControl attribute", }, + { opt_add_service_principal, "add the given service principal to the account\n" }, + { opt_remove_service_principal, "remove the given service principal from the account\n" }, ++ { opt_description, "add a description to the account\n" }, + { 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" +@@ -306,6 +308,9 @@ parse_option (Option opt, + case opt_remove_service_principal: + adcli_enroll_add_service_principal_to_remove (enroll, optarg); + return ADCLI_SUCCESS; ++ case opt_description: ++ adcli_enroll_set_description (enroll, optarg); ++ return ADCLI_SUCCESS; + case opt_verbose: + return ADCLI_SUCCESS; + +@@ -369,6 +374,7 @@ adcli_tool_computer_join (adcli_conn *conn, + { "os-name", required_argument, NULL, opt_os_name }, + { "os-version", required_argument, NULL, opt_os_version }, + { "os-service-pack", optional_argument, NULL, opt_os_service_pack }, ++ { "description", optional_argument, NULL, opt_description }, + { "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 }, +@@ -487,6 +493,7 @@ adcli_tool_computer_update (adcli_conn *conn, + { "os-name", required_argument, NULL, opt_os_name }, + { "os-version", required_argument, NULL, opt_os_version }, + { "os-service-pack", optional_argument, NULL, opt_os_service_pack }, ++ { "description", optional_argument, NULL, opt_description }, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime }, + { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, +-- +2.21.0 + diff --git a/0002-add-option-use-ldaps.patch b/0002-add-option-use-ldaps.patch new file mode 100644 index 0000000..ab34272 --- /dev/null +++ b/0002-add-option-use-ldaps.patch @@ -0,0 +1,378 @@ +From 85097245b57f190337225dbdbf6e33b58616c092 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +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 +--- + doc/adcli.xml | 24 +++++++++++++++ + library/adconn.c | 79 ++++++++++++++++++++++++++++++++++++++++++------ + library/adconn.h | 4 +++ + tools/computer.c | 10 ++++++ + tools/entry.c | 11 +++++++ + 5 files changed, 119 insertions(+), 9 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index dd30435..acced25 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -128,6 +128,30 @@ + If not specified, then an appropriate domain controller + is automatically discovered. + ++ ++ ++ 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. ++ 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. /etc/openldap/ldap.conf. ++ As an alternative it can be specified with the help of ++ an environment variable, e.g. ++ ++$ LDAPTLS_CACERT=/path/to/ad_dc_ca_cert.pem adcli join --use-ldaps -D domain.example.com ++... ++ ++ Please see ++ ldap.conf ++ 5 for details. ++ ++ + + + Use the specified kerberos credential +diff --git a/library/adconn.c b/library/adconn.c +index ffb54f9..7bab852 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 37ebdd9..1d5faa8 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 840e334..292c4d8 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -113,12 +113,14 @@ typedef enum { + opt_add_service_principal, + opt_remove_service_principal, + opt_description, ++ 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" }, +@@ -311,6 +313,9 @@ parse_option (Option opt, + case opt_description: + adcli_enroll_set_description (enroll, optarg); + return ADCLI_SUCCESS; ++ case opt_use_ldaps: ++ adcli_conn_set_use_ldaps (conn, true); ++ return ADCLI_SUCCESS; + case opt_verbose: + return ADCLI_SUCCESS; + +@@ -357,6 +362,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 }, +@@ -688,6 +694,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 }, +@@ -800,6 +807,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 }, +@@ -888,6 +896,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 }, +@@ -985,6 +994,7 @@ adcli_tool_computer_show (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 }, +diff --git a/tools/entry.c b/tools/entry.c +index f361845..05e4313 100644 +--- a/tools/entry.c ++++ b/tools/entry.c +@@ -53,6 +53,7 @@ typedef enum { + opt_unix_gid, + opt_unix_shell, + opt_nis_domain, ++ opt_use_ldaps, + } Option; + + static adcli_tool_desc common_usages[] = { +@@ -67,6 +68,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" +@@ -136,6 +138,9 @@ parse_option (Option opt, + stdin_password = 1; + } + return ADCLI_SUCCESS; ++ case opt_use_ldaps: ++ adcli_conn_set_use_ldaps (conn, true); ++ return ADCLI_SUCCESS; + case opt_verbose: + return ADCLI_SUCCESS; + default: +@@ -172,6 +177,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 }, +@@ -306,6 +312,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 }, +@@ -394,6 +401,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 }, +@@ -496,6 +504,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 }, +@@ -622,6 +631,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 }, +@@ -722,6 +732,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.21.0 + diff --git a/0002-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch b/0002-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch new file mode 100644 index 0000000..e4a6bc5 --- /dev/null +++ b/0002-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch @@ -0,0 +1,83 @@ +From 0c027538f398b3823bedbfbf5f388ad97784a0ec Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 16 Nov 2018 13:32:59 +0100 +Subject: [PATCH 2/2] adenroll: use _adcli_strv_add_unique for service + principals + +Check if service principals is already in the list before adding it. + +Related to https://gitlab.freedesktop.org/realmd/adcli/issues/16 +--- + library/adenroll.c | 31 ++++++++----------------------- + 1 file changed, 8 insertions(+), 23 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index de2242a..e02f403 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -313,7 +313,6 @@ add_service_names_to_service_principals (adcli_enroll *enroll) + char *name; + int length = 0; + int i; +- size_t c; + + if (enroll->service_principals != NULL) { + length = seq_count (enroll->service_principals); +@@ -322,28 +321,14 @@ add_service_names_to_service_principals (adcli_enroll *enroll) + for (i = 0; enroll->service_names[i] != NULL; i++) { + if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0) + return_unexpected_if_reached (); +- for (c = 0; enroll->service_principals != NULL && enroll->service_principals[c] != NULL; c++) { +- if (strcmp (name, enroll->service_principals[c]) == 0) { +- break; +- } +- } +- if (enroll->service_principals == NULL || enroll->service_principals[c] == NULL) { +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, +- name, &length); +- } ++ enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals, ++ name, &length, false); + + if (enroll->host_fqdn) { + if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0) + return_unexpected_if_reached (); +- for (c = 0; enroll->service_principals != NULL && enroll->service_principals[c] != NULL; c++) { +- if (strcmp (name, enroll->service_principals[c]) == 0) { +- break; +- } +- } +- if (enroll->service_principals == NULL || enroll->service_principals[c] == NULL) { +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, +- name, &length); +- } ++ enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals, ++ name, &length, false); + } + } + +@@ -364,9 +349,9 @@ add_and_remove_service_principals (adcli_enroll *enroll) + list = adcli_enroll_get_service_principals_to_add (enroll); + if (list != NULL) { + for (c = 0; list[c] != NULL; c++) { +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, +- strdup (list[c]), +- &length); ++ enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals, ++ strdup (list[c]), ++ &length, false); + if (enroll->service_principals == NULL) { + return ADCLI_ERR_UNEXPECTED; + } +@@ -1525,7 +1510,7 @@ load_keytab_entry (krb5_context k5, + value = strdup (name); + return_val_if_fail (value != NULL, FALSE); + _adcli_info ("Found service principal in keytab: %s", value); +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, value, NULL); ++ enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals, value, NULL, false); + } + } + +-- +2.20.1 + diff --git a/0002-computer-add-create-msa-sub-command.patch b/0002-computer-add-create-msa-sub-command.patch new file mode 100644 index 0000000..7aaae8a --- /dev/null +++ b/0002-computer-add-create-msa-sub-command.patch @@ -0,0 +1,646 @@ +From 41379f7ad6a9442dd55cc43d832427911e86db31 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 23 Oct 2020 16:53:43 +0200 +Subject: [PATCH 2/7] computer: add create-msa sub-command + +Add new sub-command to create a managed service account in AD. This can +be used if LDAP access to AD is needed but the host is already joined to +a different domain. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1854112 +--- + doc/adcli.xml | 140 ++++++++++++++++++++++++++++++++++++++ + library/adenroll.c | 164 ++++++++++++++++++++++++++++++++++++++------- + tools/computer.c | 125 ++++++++++++++++++++++++++++++++++ + tools/tools.c | 1 + + tools/tools.h | 4 ++ + 5 files changed, 409 insertions(+), 25 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index cc44fd8..14921f9 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -98,6 +98,10 @@ + --domain=domain.example.com + computer + ++ ++ adcli create-msa ++ --domain=domain.example.com ++ + + + +@@ -885,6 +889,142 @@ Password for Administrator: + + + ++ ++ Create a managed service account ++ ++ adcli create-msa creates a managed service ++ account (MSA) in the given Active Directory domain. This is useful if a ++ computer should not fully join the Active Directory domain but LDAP ++ access is needed. A typical use case is that the computer is already ++ joined an Active Directory domain and needs access to another Active ++ Directory domain in the same or a trusted forest where the host ++ credentials from the joined Active Directory domain are ++ not valid, e.g. there is only a one-way trust. ++ ++ ++$ adcli create-msa --domain=domain.example.com ++Password for Administrator: ++ ++ ++ The managed service account, as maintained by adcli, cannot have ++ additional service principals names (SPNs) associated with it. An SPN ++ is defined within the context of a Kerberos service which is tied to a ++ machine account in Active Directory. Since a machine can be joined to a ++ single Active Directory domain, managed service account in a different ++ Active Directory domain will not have the SPNs that otherwise are part ++ of another Active Directory domain's machine. ++ ++ Since it is expected that a client will most probably join to the ++ Active Directory domain matching its DNS domain the managed service ++ account will be needed for a different Active directory domain and as a ++ result the Active Directory domain name is a mandatory option. If ++ called with no other options adcli create-msa ++ will use the short hostname with an additional random suffix as ++ computer name to avoid name collisions. ++ ++ LDAP attribute sAMAccountName has a limit of 20 characters. ++ However, machine account's NetBIOS name must be at most 16 characters ++ long, including a trailing '$' sign. Since it is not expected that the ++ managed service accounts created by adcli will be used on the NetBIOS ++ level the remaining 4 characters can be used to add uniqueness. Managed ++ service account names will have a suffix of 3 random characters from ++ number and upper- and lowercase ASCII ranges appended to the chosen ++ short host name, using '!' as a separator. For a host with the ++ shortname 'myhost', a managed service account will have a common name ++ (CN attribute) 'myhost!A2c' and a NetBIOS name ++ (sAMAccountName attribute) will be 'myhost!A2c$'. A corresponding ++ Kerberos principal in the Active Directory domain where the managed ++ service account was created would be ++ 'myhost!A2c$@DOMAIN.EXAMPLE.COM'. ++ ++ A keytab for the managed service account is stored into a file ++ specified with -K option. If it is not specified, the file is named ++ after the default keytab file, with lowercase Active Directory domain ++ of the managed service account as a suffix. On most systems it would be ++ /etc/krb5.keytab with a suffix of ++ 'domain.example.com', e.g. ++ /etc/krb5.keytad.domain.example.com. ++ ++ adcli create-msa can be called multiple ++ times to reset the password of the managed service account. To identify ++ the right account with the random component in the name the ++ corresponding principal is read from the keytab. If the keytab got ++ deleted adcli will try to identify an existing ++ managed service account with the help of the fully-qualified name, if ++ this fails a new managed service account will be created. ++ ++ The managed service account password can be updated with ++ ++$ adcli update --domain=domain.example.com --host-keytab=/etc/krb5.keytad.domain.example.com ++ ++ and the managed service account can be deleted with ++ ++$ adcli delete-computer --domain=domain.example.com 'myhost!A2c' ++ ++ ++ ++ In addition to the global options, you can specify the following ++ options to control how this operation is done. ++ ++ ++ ++ ++ The short non-dotted name of the managed ++ service account that will be created in the Active ++ Directory domain. The long option name ++ is ++ kept to underline the similarity with the same option ++ of the other sub-commands. If not specified, ++ then the first portion of the ++ or its default is used with a random suffix. ++ ++ ++ ++ The full distinguished name of the OU in ++ which to create the managed service account. If not ++ specified, then the managed service account will be ++ created in a default location. ++ ++ ++ ++ Override the local machine's fully ++ qualified DNS domain name. If not specified, the local ++ machine's hostname will be retrieved via ++ gethostname(). ++ If gethostname() only returns a short name ++ getaddrinfo() with the AI_CANONNAME hint ++ is called to expand the name to a fully qualified DNS ++ domain name. ++ ++ ++ ++ Specify the path to the host keytab where ++ credentials of the managed service account will be ++ written after a successful creation. If not specified, ++ the default location will be used, usually ++ /etc/krb5.keytab with ++ the lower-cased Active Directory domain name added as a ++ suffix e.g. ++ /etc/krb5.keytab.domain.example.com. ++ ++ ++ ++ ++ After a successful creation print out ++ information about the created object. This is output in ++ a format that should be both human and machine ++ readable. ++ ++ ++ ++ After a successful creation print out ++ the managed service account password. This is output in ++ a format that should be both human and machine ++ readable. ++ ++ ++ ++ + + Delegated Permissions + It is common practice in AD to not use an account from the Domain +diff --git a/library/adenroll.c b/library/adenroll.c +index 5ae1f7b..dbfda36 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -155,6 +155,20 @@ struct _adcli_enroll { + char *description; + }; + ++static void ++check_if_service (adcli_enroll *enroll, ++ LDAP *ldap, ++ LDAPMessage *results) ++{ ++ char **objectclasses = NULL; ++ ++ objectclasses = _adcli_ldap_parse_values (ldap, results, "objectClass"); ++ enroll->is_service = _adcli_strv_has_ex (objectclasses, ++ "msDS-ManagedServiceAccount", ++ strcasecmp) == 1 ? true : false; ++ _adcli_strv_free (objectclasses); ++} ++ + static adcli_result + ensure_host_fqdn (adcli_result res, + adcli_enroll *enroll) +@@ -471,13 +485,15 @@ ensure_keytab_principals (adcli_result res, + { + krb5_context k5; + krb5_error_code code; +- int count; ++ int count = 0; + int at, i; + + /* Prepare the principals we're going to add to the keytab */ + +- return_unexpected_if_fail (enroll->service_principals); +- count = _adcli_strv_len (enroll->service_principals); ++ if (!enroll->is_service) { ++ return_unexpected_if_fail (enroll->service_principals); ++ count = _adcli_strv_len (enroll->service_principals); ++ } + + k5 = adcli_conn_get_krb5_context (enroll->conn); + return_unexpected_if_fail (k5 != NULL); +@@ -556,8 +572,12 @@ static adcli_result + lookup_computer_container (adcli_enroll *enroll, + LDAP *ldap) + { +- char *attrs[] = { "wellKnownObjects", NULL }; +- char *prefix = "B:32:AA312825768811D1ADED00C04FD8D5CD:"; ++ char *attrs[] = { enroll->is_service ? "otherWellKnownObjects" ++ : "wellKnownObjects", NULL }; ++ const char *prefix = enroll->is_service ? "B:32:1EB93889E40C45DF9F0C64D23BBB6237:" ++ : "B:32:AA312825768811D1ADED00C04FD8D5CD:"; ++ const char *filter = enroll->is_service ? "(&(objectClass=container)(cn=Managed Service Accounts))" ++ : "(&(objectClass=container)(cn=Computers))"; + int prefix_len; + LDAPMessage *results; + const char *base; +@@ -586,7 +606,7 @@ lookup_computer_container (adcli_enroll *enroll, + "Couldn't lookup computer container: %s", base); + } + +- values = _adcli_ldap_parse_values (ldap, results, "wellKnownObjects"); ++ values = _adcli_ldap_parse_values (ldap, results, attrs[0]); + ldap_msgfree (results); + + prefix_len = strlen (prefix); +@@ -604,8 +624,7 @@ lookup_computer_container (adcli_enroll *enroll, + + /* Try harder */ + if (!enroll->computer_container) { +- ret = ldap_search_ext_s (ldap, base, LDAP_SCOPE_BASE, +- "(&(objectClass=container)(cn=Computers))", ++ ret = ldap_search_ext_s (ldap, base, LDAP_SCOPE_BASE, filter, + attrs, 0, NULL, NULL, NULL, -1, &results); + if (ret == LDAP_SUCCESS) { + enroll->computer_container = _adcli_ldap_parse_dn (ldap, results); +@@ -747,7 +766,7 @@ static adcli_result + create_computer_account (adcli_enroll *enroll, + LDAP *ldap) + { +- char *vals_objectClass[] = { "computer", NULL }; ++ char *vals_objectClass[] = { enroll->is_service ? "msDS-ManagedServiceAccount" : "computer", NULL }; + LDAPMod objectClass = { LDAP_MOD_ADD, "objectClass", { vals_objectClass, } }; + char *vals_sAMAccountName[] = { enroll->computer_sam, NULL }; + LDAPMod sAMAccountName = { LDAP_MOD_ADD, "sAMAccountName", { vals_sAMAccountName, } }; +@@ -806,7 +825,7 @@ create_computer_account (adcli_enroll *enroll, + m = 0; + for (c = 0; c < mods_count - 1; c++) { + /* Skip empty LDAP sttributes */ +- if (all_mods[c]->mod_vals.modv_strvals[0] != NULL) { ++ if (all_mods[c]->mod_vals.modv_strvals != NULL && all_mods[c]->mod_vals.modv_strvals[0] != NULL) { + mods[m++] = all_mods[c]; + } + } +@@ -936,7 +955,7 @@ locate_computer_account (adcli_enroll *enroll, + LDAPMessage **rresults, + LDAPMessage **rentry) + { +- char *attrs[] = { "1.1", NULL }; ++ char *attrs[] = { "objectClass", NULL }; + LDAPMessage *results = NULL; + LDAPMessage *entry = NULL; + const char *base; +@@ -948,7 +967,9 @@ locate_computer_account (adcli_enroll *enroll, + /* If we don't yet know our computer dn, then try and find it */ + value = _adcli_ldap_escape_filter (enroll->computer_sam); + return_unexpected_if_fail (value != NULL); +- if (asprintf (&filter, "(&(objectClass=computer)(sAMAccountName=%s))", value) < 0) ++ if (asprintf (&filter, "(&(objectClass=%s)(sAMAccountName=%s))", ++ enroll->is_service ? "msDS-ManagedServiceAccount" : "computer", ++ value) < 0) + return_unexpected_if_reached (); + free (value); + +@@ -962,8 +983,11 @@ locate_computer_account (adcli_enroll *enroll, + if (ret == LDAP_SUCCESS) { + entry = ldap_first_entry (ldap, results); + +- /* If we found a computer account, make note of dn */ ++ /* If we found a computer/service account, make note of dn */ + if (entry) { ++ if (!enroll->is_service_explicit) { ++ check_if_service ( enroll, ldap, results); ++ } + dn = ldap_get_dn (ldap, entry); + free (enroll->computer_dn); + enroll->computer_dn = strdup (dn); +@@ -1003,7 +1027,7 @@ load_computer_account (adcli_enroll *enroll, + LDAPMessage **rresults, + LDAPMessage **rentry) + { +- char *attrs[] = { "1.1", NULL }; ++ char *attrs[] = { "objectClass", NULL }; + LDAPMessage *results = NULL; + LDAPMessage *entry = NULL; + int ret; +@@ -1081,6 +1105,12 @@ locate_or_create_computer_account (adcli_enroll *enroll, + if (res == ADCLI_SUCCESS && entry == NULL) + res = create_computer_account (enroll, ldap); + ++ /* Service account already exists, just continue and update the ++ * password */ ++ if (enroll->is_service && entry != NULL) { ++ res = ADCLI_SUCCESS; ++ } ++ + if (results) + ldap_msgfree (results); + +@@ -1413,6 +1443,11 @@ update_computer_account (adcli_enroll *enroll) + LDAP *ldap; + char *value = NULL; + ++ /* No updates for service accounts */ ++ if (enroll->is_service) { ++ return; ++ } ++ + ldap = adcli_conn_get_ldap_connection (enroll->conn); + return_if_fail (ldap != NULL); + +@@ -1501,6 +1536,11 @@ update_service_principals (adcli_enroll *enroll) + LDAP *ldap; + int ret; + ++ /* No updates for service accounts */ ++ if (enroll->is_service) { ++ return ADCLI_SUCCESS; ++ } ++ + ldap = adcli_conn_get_ldap_connection (enroll->conn); + return_unexpected_if_fail (ldap != NULL); + +@@ -1614,6 +1654,8 @@ load_keytab_entry (krb5_context k5, + enroll->computer_name = name; + name[len - 1] = '\0'; + _adcli_info ("Found computer name in keytab: %s", name); ++ adcli_conn_set_computer_name (enroll->conn, ++ enroll->computer_name); + name = NULL; + + } else if (!enroll->host_fqdn && _adcli_str_has_prefix (name, "host/") && strchr (name, '.')) { +@@ -2002,17 +2044,25 @@ adcli_enroll_prepare (adcli_enroll *enroll, + + adcli_clear_last_error (); + +- /* Basic discovery and figuring out enroll params */ +- res = ensure_host_fqdn (res, enroll); +- res = ensure_computer_name (res, enroll); +- res = ensure_computer_sam (res, enroll); +- res = ensure_user_principal (res, enroll); +- res = ensure_computer_password (res, enroll); +- if (!(flags & ADCLI_ENROLL_NO_KEYTAB)) ++ if (enroll->is_service) { ++ /* Ensure basic params for service accounts */ ++ res = ensure_computer_sam (res, enroll); ++ res = ensure_computer_password (res, enroll); + res = ensure_host_keytab (res, enroll); +- res = ensure_service_names (res, enroll); +- res = ensure_service_principals (res, enroll); +- res = ensure_keytab_principals (res, enroll); ++ res = ensure_keytab_principals (res, enroll); ++ } else { ++ /* Basic discovery and figuring out enroll params */ ++ res = ensure_host_fqdn (res, enroll); ++ res = ensure_computer_name (res, enroll); ++ res = ensure_computer_sam (res, enroll); ++ res = ensure_user_principal (res, enroll); ++ res = ensure_computer_password (res, enroll); ++ if (!(flags & ADCLI_ENROLL_NO_KEYTAB)) ++ res = ensure_host_keytab (res, enroll); ++ res = ensure_service_names (res, enroll); ++ res = ensure_service_principals (res, enroll); ++ res = ensure_keytab_principals (res, enroll); ++ } + + return res; + } +@@ -2157,6 +2207,58 @@ enroll_join_or_update_tasks (adcli_enroll *enroll, + return update_keytab_for_principals (enroll, flags); + } + ++static adcli_result ++adcli_enroll_add_description_for_service_account (adcli_enroll *enroll) ++{ ++ const char *fqdn; ++ char *desc; ++ ++ fqdn = adcli_conn_get_host_fqdn (enroll->conn); ++ return_unexpected_if_fail (fqdn != NULL); ++ if (asprintf (&desc, "Please do not edit, Service account for %s, " ++ "managed by adcli.", fqdn) < 0) { ++ return_unexpected_if_reached (); ++ } ++ ++ adcli_enroll_set_description (enroll, desc); ++ free (desc); ++ ++ return ADCLI_SUCCESS; ++} ++ ++static adcli_result ++adcli_enroll_add_keytab_for_service_account (adcli_enroll *enroll) ++{ ++ krb5_context k5; ++ krb5_error_code code; ++ char def_keytab_name[MAX_KEYTAB_NAME_LEN]; ++ char *lc_dom_name; ++ int ret; ++ ++ if (adcli_enroll_get_keytab_name (enroll) == NULL) { ++ k5 = adcli_conn_get_krb5_context (enroll->conn); ++ return_unexpected_if_fail (k5 != NULL); ++ ++ code = krb5_kt_default_name (k5, def_keytab_name, ++ sizeof (def_keytab_name)); ++ return_unexpected_if_fail (code == 0); ++ ++ lc_dom_name = strdup (adcli_conn_get_domain_name (enroll->conn)); ++ return_unexpected_if_fail (lc_dom_name != NULL); ++ _adcli_str_down (lc_dom_name); ++ ++ ++ ret = asprintf (&enroll->keytab_name, "%s.%s", def_keytab_name, ++ lc_dom_name); ++ free (lc_dom_name); ++ return_unexpected_if_fail (ret > 0); ++ } ++ ++ _adcli_info ("Using service account keytab: %s", enroll->keytab_name); ++ ++ return ADCLI_SUCCESS; ++} ++ + adcli_result + adcli_enroll_join (adcli_enroll *enroll, + adcli_enroll_flags flags) +@@ -2172,7 +2274,14 @@ adcli_enroll_join (adcli_enroll *enroll, + if (res != ADCLI_SUCCESS) + return res; + +- res = ensure_default_service_names (enroll); ++ if (enroll->is_service) { ++ res = adcli_enroll_add_description_for_service_account (enroll); ++ if (res == ADCLI_SUCCESS) { ++ res = adcli_enroll_add_keytab_for_service_account (enroll); ++ } ++ } else { ++ res = ensure_default_service_names (enroll); ++ } + if (res != ADCLI_SUCCESS) + return res; + +@@ -2281,6 +2390,11 @@ adcli_enroll_update (adcli_enroll *enroll, + } + free (value); + ++ /* We only support password changes for service accounts */ ++ if (enroll->is_service && (flags & ADCLI_ENROLL_PASSWORD_VALID)) { ++ return ADCLI_SUCCESS; ++ } ++ + return enroll_join_or_update_tasks (enroll, flags); + } + +diff --git a/tools/computer.c b/tools/computer.c +index 5a97d8b..63fd374 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -1074,3 +1074,128 @@ adcli_tool_computer_show (adcli_conn *conn, + adcli_enroll_unref (enroll); + return 0; + } ++ ++int ++adcli_tool_computer_managed_service_account (adcli_conn *conn, ++ int argc, ++ char *argv[]) ++{ ++ adcli_enroll *enroll; ++ adcli_result res; ++ int show_password = 0; ++ int details = 0; ++ int opt; ++ ++ struct option options[] = { ++ { "domain", required_argument, NULL, opt_domain }, ++ { "domain-realm", required_argument, NULL, opt_domain_realm }, ++ { "domain-controller", required_argument, NULL, opt_domain_controller }, ++ { "use-ldaps", no_argument, 0, opt_use_ldaps }, ++ { "login-user", required_argument, NULL, opt_login_user }, ++ { "login-ccache", optional_argument, NULL, opt_login_ccache }, ++ { "host-fqdn", required_argument, 0, opt_host_fqdn }, ++ { "computer-name", required_argument, 0, opt_computer_name }, ++ { "host-keytab", required_argument, 0, opt_host_keytab }, ++ { "no-password", no_argument, 0, opt_no_password }, ++ { "stdin-password", no_argument, 0, opt_stdin_password }, ++ { "prompt-password", no_argument, 0, opt_prompt_password }, ++ { "domain-ou", required_argument, NULL, opt_domain_ou }, ++ { "show-details", no_argument, NULL, opt_show_details }, ++ { "show-password", no_argument, NULL, opt_show_password }, ++ { "verbose", no_argument, NULL, opt_verbose }, ++ { "help", no_argument, NULL, 'h' }, ++ { 0 }, ++ }; ++ ++ static adcli_tool_desc usages[] = { ++ { 0, "usage: adcli create-msa --domain=xxxx" }, ++ { 0 }, ++ }; ++ ++ enroll = adcli_enroll_new (conn); ++ if (enroll == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } ++ ++ while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) { ++ switch (opt) { ++ case opt_one_time_password: ++ adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_COMPUTER_ACCOUNT); ++ adcli_conn_set_computer_password (conn, optarg); ++ break; ++ case opt_show_details: ++ details = 1; ++ break; ++ case opt_show_password: ++ show_password = 1; ++ break; ++ case 'h': ++ case '?': ++ case ':': ++ adcli_tool_usage (options, usages); ++ adcli_tool_usage (options, common_usages); ++ adcli_enroll_unref (enroll); ++ return opt == 'h' ? 0 : 2; ++ default: ++ res = parse_option ((Option)opt, optarg, conn, enroll); ++ if (res != ADCLI_SUCCESS) { ++ adcli_enroll_unref (enroll); ++ return res; ++ } ++ break; ++ } ++ } ++ ++ argc -= optind; ++ argv += optind; ++ ++ if (argc == 1) ++ adcli_conn_set_domain_name (conn, argv[0]); ++ else if (argc > 1) { ++ warnx ("extra arguments specified"); ++ adcli_enroll_unref (enroll); ++ return 2; ++ } ++ ++ if (adcli_conn_get_domain_name (conn) == NULL) { ++ warnx ("domain name is required"); ++ adcli_enroll_unref (enroll); ++ return 2; ++ } ++ ++ adcli_enroll_set_is_service (enroll, true); ++ adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); ++ ++ res = adcli_enroll_load (enroll); ++ if (res != ADCLI_SUCCESS) { ++ /* ignored */ ++ } ++ ++ res = adcli_conn_connect (conn); ++ if (res != ADCLI_SUCCESS) { ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; ++ } ++ ++ res = adcli_enroll_join (enroll, 0); ++ if (res != ADCLI_SUCCESS) { ++ warnx ("Adding service account for %s failed: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_enroll_unref (enroll); ++ return -res; ++ } ++ ++ if (details) ++ dump_details (conn, enroll, show_password); ++ else if (show_password) ++ dump_password (conn, enroll); ++ ++ adcli_enroll_unref (enroll); ++ ++ return 0; ++} +diff --git a/tools/tools.c b/tools/tools.c +index 1b6d879..d0dcf98 100644 +--- a/tools/tools.c ++++ b/tools/tools.c +@@ -60,6 +60,7 @@ struct { + { "reset-computer", adcli_tool_computer_reset, "Reset a computer account", }, + { "delete-computer", adcli_tool_computer_delete, "Delete a computer account", }, + { "show-computer", adcli_tool_computer_show, "Show computer account attributes stored in AD", }, ++ { "create-msa", adcli_tool_computer_managed_service_account, "Create a managed service account in the given AD domain", }, + { "create-user", adcli_tool_user_create, "Create a user account", }, + { "delete-user", adcli_tool_user_delete, "Delete a user account", }, + { "create-group", adcli_tool_group_create, "Create a group", }, +diff --git a/tools/tools.h b/tools/tools.h +index 3702875..82d5e4e 100644 +--- a/tools/tools.h ++++ b/tools/tools.h +@@ -82,6 +82,10 @@ int adcli_tool_computer_show (adcli_conn *conn, + int argc, + char *argv[]); + ++int adcli_tool_computer_managed_service_account (adcli_conn *conn, ++ int argc, ++ char *argv[]); ++ + int adcli_tool_user_create (adcli_conn *conn, + int argc, + char *argv[]); +-- +2.28.0 + diff --git a/0002-create-user-try-to-find-NIS-domain-if-needed.patch b/0002-create-user-try-to-find-NIS-domain-if-needed.patch new file mode 100644 index 0000000..0ecdc90 --- /dev/null +++ b/0002-create-user-try-to-find-NIS-domain-if-needed.patch @@ -0,0 +1,147 @@ +From 408880a11879b1a57a450e25c77ef2e310bdffd5 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 18 Mar 2019 16:45:54 +0100 +Subject: [PATCH 2/2] create-user: try to find NIS domain if needed + +Related to https://gitlab.freedesktop.org/realmd/adcli/issues/2 +--- + doc/adcli.xml | 4 +++- + library/adentry.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ + library/adentry.h | 2 ++ + tools/entry.c | 16 ++++++++++++++++ + 4 files changed, 65 insertions(+), 1 deletion(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 18620c0..af73433 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -537,7 +537,9 @@ $ adcli create-user Fry --domain=domain.example.com \ + the new created user account, which should be the user's + NIS domain is the NIS/YP service of Active Directory's Services for Unix (SFU) + are used. This is needed to let the 'UNIX attributes' tab of older Active +- Directoy versions show the set UNIX specific attributes. ++ Directoy versions show the set UNIX specific attributes. If not specified ++ adcli will try to determine the NIS domain automatically if needed. ++ + + + +diff --git a/library/adentry.c b/library/adentry.c +index 9b9e1c6..1cc0518 100644 +--- a/library/adentry.c ++++ b/library/adentry.c +@@ -484,3 +484,47 @@ adcli_entry_new_group (adcli_conn *conn, + return_val_if_fail (sam_name != NULL, NULL); + return entry_new (conn, "group", group_entry_builder, sam_name); + } ++ ++adcli_result ++adcli_get_nis_domain (adcli_entry *entry, ++ adcli_attrs *attrs) ++{ ++ LDAP *ldap; ++ const char *ldap_attrs[] = { "cn", NULL }; ++ LDAPMessage *results; ++ LDAPMessage *ldap_entry; ++ char *base; ++ const char *filter = "objectClass=msSFU30DomainInfo"; ++ char *cn; ++ int ret; ++ ++ ldap = adcli_conn_get_ldap_connection (entry->conn); ++ return_unexpected_if_fail (ldap != NULL); ++ ++ if (asprintf (&base, "CN=ypservers,CN=ypServ30,CN=RpcServices,CN=System,%s", ++ adcli_conn_get_default_naming_context (entry->conn)) < 0) { ++ return_unexpected_if_reached (); ++ } ++ ++ ret = ldap_search_ext_s (ldap, base, LDAP_SCOPE_SUB, filter, (char **)ldap_attrs, ++ 0, NULL, NULL, NULL, -1, &results); ++ ++ free (base); ++ ++ if (ret != LDAP_SUCCESS) { ++ /* No NIS domain available */ ++ ldap_msgfree (results); ++ return ADCLI_SUCCESS; ++ } ++ ++ ldap_entry = ldap_first_entry (ldap, results); ++ if (ldap_entry != NULL) { ++ cn = _adcli_ldap_parse_value (ldap, ldap_entry, "cn"); ++ return_unexpected_if_fail (cn != NULL); ++ ++ adcli_attrs_add (attrs, "msSFU30NisDomain", cn, NULL); ++ } ++ ldap_msgfree (results); ++ ++ return ADCLI_SUCCESS; ++} +diff --git a/library/adentry.h b/library/adentry.h +index eb8bc00..ae90689 100644 +--- a/library/adentry.h ++++ b/library/adentry.h +@@ -58,4 +58,6 @@ const char * adcli_entry_get_sam_name (adcli_entry *entry); + + const char * adcli_entry_get_dn (adcli_entry *entry); + ++adcli_result adcli_get_nis_domain (adcli_entry *entry, ++ adcli_attrs *attrs); + #endif /* ADENTRY_H_ */ +diff --git a/tools/entry.c b/tools/entry.c +index 69ce62c..de56586 100644 +--- a/tools/entry.c ++++ b/tools/entry.c +@@ -153,6 +153,8 @@ adcli_tool_user_create (adcli_conn *conn, + adcli_attrs *attrs; + const char *ou = NULL; + int opt; ++ bool has_unix_attr = false; ++ bool has_nis_domain = false; + + struct option options[] = { + { "display-name", required_argument, NULL, opt_display_name }, +@@ -193,18 +195,23 @@ adcli_tool_user_create (adcli_conn *conn, + break; + case opt_unix_home: + adcli_attrs_add (attrs, "unixHomeDirectory", optarg, NULL); ++ has_unix_attr = true; + break; + case opt_unix_uid: + adcli_attrs_add (attrs, "uidNumber", optarg, NULL); ++ has_unix_attr = true; + break; + case opt_unix_gid: + adcli_attrs_add (attrs, "gidNumber", optarg, NULL); ++ has_unix_attr = true; + break; + case opt_unix_shell: + adcli_attrs_add (attrs, "loginShell", optarg, NULL); ++ has_unix_attr = true; + break; + case opt_nis_domain: + adcli_attrs_add (attrs, "msSFU30NisDomain", optarg, NULL); ++ has_nis_domain = true; + break; + case opt_domain_ou: + ou = optarg; +@@ -242,6 +249,15 @@ adcli_tool_user_create (adcli_conn *conn, + adcli_get_last_error ()); + } + ++ if (has_unix_attr && !has_nis_domain) { ++ res = adcli_get_nis_domain (entry, attrs); ++ if (res != ADCLI_SUCCESS) { ++ adcli_entry_unref (entry); ++ adcli_attrs_free (attrs); ++ errx (-res, "couldn't get NIS domain"); ++ } ++ } ++ + res = adcli_entry_create (entry, attrs); + if (res != ADCLI_SUCCESS) { + errx (-res, "creating user %s in domain %s failed: %s", +-- +2.20.1 + diff --git a/0002-library-make-_adcli_strv_has_ex-public.patch b/0002-library-make-_adcli_strv_has_ex-public.patch new file mode 100644 index 0000000..db18951 --- /dev/null +++ b/0002-library-make-_adcli_strv_has_ex-public.patch @@ -0,0 +1,42 @@ +From e1b45e66bc185f5db4c252e1f3fb1b4400b4538e Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 22 Mar 2019 10:36:38 +0100 +Subject: [PATCH 2/4] library: make _adcli_strv_has_ex public + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1630187 +--- + library/adprivate.h | 4 ++++ + library/adutil.c | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/library/adprivate.h b/library/adprivate.h +index 0806430..55e6234 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -125,6 +125,10 @@ void _adcli_strv_free (char **strv); + int _adcli_strv_has (char **strv, + const char *str); + ++int _adcli_strv_has_ex (char **strv, ++ const char *str, ++ int (* compare) (const char *match, const char*value)); ++ + char ** _adcli_strv_dup (char **strv) GNUC_WARN_UNUSED; + + char * _adcli_strv_join (char **strv, +diff --git a/library/adutil.c b/library/adutil.c +index 76ea158..9b0c47f 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -221,7 +221,7 @@ _adcli_strv_add (char **strv, + return seq_push (strv, length, string); + } + +-static int ++int + _adcli_strv_has_ex (char **strv, + const char *str, + int (* compare) (const char *match, const char*value)) +-- +2.20.1 + diff --git a/0002-library-return-error-if-no-matching-key-was-found.patch b/0002-library-return-error-if-no-matching-key-was-found.patch new file mode 100644 index 0000000..f9c68d6 --- /dev/null +++ b/0002-library-return-error-if-no-matching-key-was-found.patch @@ -0,0 +1,35 @@ +From 4987a21f4839ab7ea50e932c72df05075efb89b3 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 21 Mar 2019 15:05:33 +0100 +Subject: [PATCH 2/2] library: return error if no matching key was found + +To avoid a misleading debug message indicating success a proper erro +code should be returned the no matching key was found when trying to +copy an keytab entry for a new principal. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1644311 +--- + library/adkrb5.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/library/adkrb5.c b/library/adkrb5.c +index 033c181..7f77373 100644 +--- a/library/adkrb5.c ++++ b/library/adkrb5.c +@@ -298,11 +298,10 @@ _adcli_krb5_keytab_copy_entries (krb5_context k5, + + code = _adcli_krb5_get_keyblock (k5, keytab, &entry.key, + match_enctype_and_kvno, &closure); +- if (code != 0) { +- return code; ++ if (code != 0 || closure.matched == 0) { ++ return code != 0 ? code : ENOKEY; + } + +- + entry.principal = principal; + entry.vno = kvno; + +-- +2.20.1 + diff --git a/0002-tools-remove-errx-from-user-and-group-commands.patch b/0002-tools-remove-errx-from-user-and-group-commands.patch new file mode 100644 index 0000000..36e5567 --- /dev/null +++ b/0002-tools-remove-errx-from-user-and-group-commands.patch @@ -0,0 +1,398 @@ +From cac0fa9df8888245399f2db187e05e31f93d1471 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 15 Apr 2019 17:56:37 +0200 +Subject: [PATCH 2/7] tools: remove errx from user and group commands + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596 +--- + tools/entry.c | 232 +++++++++++++++++++++++++++++++++----------------- + 1 file changed, 154 insertions(+), 78 deletions(-) + +diff --git a/tools/entry.c b/tools/entry.c +index de56586..97ec6e7 100644 +--- a/tools/entry.c ++++ b/tools/entry.c +@@ -232,21 +232,30 @@ adcli_tool_user_create (adcli_conn *conn, + argc -= optind; + argv += optind; + +- if (argc != 1) +- errx (2, "specify one user name to create"); ++ if (argc != 1) { ++ warnx ("specify one user name to create"); ++ adcli_attrs_free (attrs); ++ return 2; ++ } + + entry = adcli_entry_new_user (conn, argv[0]); +- if (entry == NULL) +- errx (-1, "unexpected memory problems"); ++ if (entry == NULL) { ++ warnx ("unexpected memory problems"); ++ adcli_attrs_free (attrs); ++ return -1; ++ } + adcli_entry_set_domain_ou (entry, ou); + + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't connect to %s domain: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ adcli_attrs_free (attrs); ++ return -res; + } + + if (has_unix_attr && !has_nis_domain) { +@@ -254,16 +263,20 @@ adcli_tool_user_create (adcli_conn *conn, + if (res != ADCLI_SUCCESS) { + adcli_entry_unref (entry); + adcli_attrs_free (attrs); +- errx (-res, "couldn't get NIS domain"); ++ warnx ("couldn't get NIS domain"); ++ return -res; + } + } + + res = adcli_entry_create (entry, attrs); + if (res != ADCLI_SUCCESS) { +- errx (-res, "creating user %s in domain %s failed: %s", +- adcli_entry_get_sam_name (entry), +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("creating user %s in domain %s failed: %s", ++ adcli_entry_get_sam_name (entry), ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ adcli_attrs_free (attrs); ++ return -res; + } + + adcli_entry_unref (entry); +@@ -317,28 +330,36 @@ adcli_tool_user_delete (adcli_conn *conn, + argc -= optind; + argv += optind; + +- if (argc != 1) +- errx (2, "specify one user name to delete"); ++ if (argc != 1) { ++ warnx ("specify one user name to delete"); ++ return 2; ++ } + + entry = adcli_entry_new_user (conn, argv[0]); +- if (entry == NULL) +- errx (-1, "unexpected memory problems"); ++ if (entry == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } + + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't connect to %s domain: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ return -res; + } + + res = adcli_entry_delete (entry); + if (res != ADCLI_SUCCESS) { +- errx (-res, "deleting user %s in domain %s failed: %s", +- adcli_entry_get_sam_name (entry), +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("deleting user %s in domain %s failed: %s", ++ adcli_entry_get_sam_name (entry), ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ return -res; + } + + adcli_entry_unref (entry); +@@ -404,29 +425,41 @@ adcli_tool_group_create (adcli_conn *conn, + argc -= optind; + argv += optind; + +- if (argc != 1) +- errx (2, "specify one group to create"); ++ if (argc != 1) { ++ warnx ("specify one group to create"); ++ adcli_attrs_free (attrs); ++ return 2; ++ } + + entry = adcli_entry_new_group (conn, argv[0]); +- if (entry == NULL) +- errx (-1, "unexpected memory problems"); ++ if (entry == NULL) { ++ warnx ("unexpected memory problems"); ++ adcli_attrs_free (attrs); ++ return -1; ++ } + adcli_entry_set_domain_ou (entry, ou); + + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't connect to domain %s: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't connect to domain %s: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ adcli_attrs_free (attrs); ++ return -res; + } + + res = adcli_entry_create (entry, attrs); + if (res != ADCLI_SUCCESS) { +- errx (-res, "creating group %s in domain %s failed: %s", +- adcli_entry_get_sam_name (entry), +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("creating group %s in domain %s failed: %s", ++ adcli_entry_get_sam_name (entry), ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ adcli_attrs_free (attrs); ++ return -res; + } + + adcli_entry_unref (entry); +@@ -480,28 +513,36 @@ adcli_tool_group_delete (adcli_conn *conn, + argc -= optind; + argv += optind; + +- if (argc != 1) +- errx (2, "specify one group name to delete"); ++ if (argc != 1) { ++ warnx ("specify one group name to delete"); ++ return 2; ++ } + + entry = adcli_entry_new_group (conn, argv[0]); +- if (entry == NULL) +- errx (-1, "unexpected memory problems"); ++ if (entry == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } + + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't connect to %s domain: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ return -res; + } + + res = adcli_entry_delete (entry); + if (res != ADCLI_SUCCESS) { +- errx (-res, "deleting group %s in domain %s failed: %s", +- adcli_entry_get_sam_name (entry), +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("deleting group %s in domain %s failed: %s", ++ adcli_entry_get_sam_name (entry), ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ return -res; + } + + adcli_entry_unref (entry); +@@ -509,7 +550,7 @@ adcli_tool_group_delete (adcli_conn *conn, + return 0; + } + +-static void ++static int + expand_user_dn_as_member (adcli_conn *conn, + adcli_attrs *attrs, + const char *user, +@@ -523,16 +564,19 @@ expand_user_dn_as_member (adcli_conn *conn, + + res = adcli_entry_load (entry); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't lookup user %s in domain %s: %s", +- user, adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't lookup user %s in domain %s: %s", ++ user, adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ return -res; + } + + dn = adcli_entry_get_dn (entry); + if (dn == NULL) { +- errx (-ADCLI_ERR_CONFIG, +- "couldn't found user %s in domain %s", +- user, adcli_conn_get_domain_name (conn)); ++ warnx ("couldn't found user %s in domain %s", ++ user, adcli_conn_get_domain_name (conn)); ++ adcli_entry_unref (entry); ++ return -ADCLI_ERR_CONFIG; + } + + if (adding) +@@ -541,6 +585,8 @@ expand_user_dn_as_member (adcli_conn *conn, + adcli_attrs_delete1 (attrs, "member", dn); + + adcli_entry_unref (entry); ++ ++ return ADCLI_SUCCESS; + } + + int +@@ -590,33 +636,48 @@ adcli_tool_member_add (adcli_conn *conn, + argc -= optind; + argv += optind; + +- if (argc < 2) +- errx (2, "specify a group name and a user to add"); ++ if (argc < 2) { ++ warnx ("specify a group name and a user to add"); ++ return 2; ++ } + + entry = adcli_entry_new_group (conn, argv[0]); +- if (entry == NULL) +- errx (-1, "unexpected memory problems"); ++ if (entry == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } + + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't connect to %s domain: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ return -res; + } + + attrs = adcli_attrs_new (); + +- for (i = 1; i < argc; i++) +- expand_user_dn_as_member (conn, attrs, argv[i], 1); ++ for (i = 1; i < argc; i++) { ++ res = expand_user_dn_as_member (conn, attrs, argv[i], 1); ++ if (res != ADCLI_SUCCESS) { ++ adcli_attrs_free (attrs); ++ adcli_entry_unref (entry); ++ return res; ++ } ++ } + + res = adcli_entry_modify (entry, attrs); + if (res != ADCLI_SUCCESS) { +- errx (-res, "adding member(s) to group %s in domain %s failed: %s", +- adcli_entry_get_sam_name (entry), +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("adding member(s) to group %s in domain %s failed: %s", ++ adcli_entry_get_sam_name (entry), ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_attrs_free (attrs); ++ adcli_entry_unref (entry); ++ return -res; + } + + adcli_attrs_free (attrs); +@@ -672,33 +733,48 @@ adcli_tool_member_remove (adcli_conn *conn, + argc -= optind; + argv += optind; + +- if (argc < 2) +- errx (2, "specify a group name and a user to remove"); ++ if (argc < 2) { ++ warnx ("specify a group name and a user to remove"); ++ return 2; ++ } + + entry = adcli_entry_new_group (conn, argv[0]); +- if (entry == NULL) +- errx (-1, "unexpected memory problems"); ++ if (entry == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } + + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); + + res = adcli_conn_connect (conn); + if (res != ADCLI_SUCCESS) { +- errx (-res, "couldn't connect to %s domain: %s", +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ return -res; + } + + attrs = adcli_attrs_new (); + +- for (i = 1; i < argc; i++) +- expand_user_dn_as_member (conn, attrs, argv[i], 0); ++ for (i = 1; i < argc; i++) { ++ res = expand_user_dn_as_member (conn, attrs, argv[i], 0); ++ if (res != ADCLI_SUCCESS) { ++ adcli_attrs_free (attrs); ++ adcli_entry_unref (entry); ++ return res; ++ } ++ } + + res = adcli_entry_modify (entry, attrs); + if (res != ADCLI_SUCCESS) { +- errx (-res, "adding member(s) to group %s in domain %s failed: %s", +- adcli_entry_get_sam_name (entry), +- adcli_conn_get_domain_name (conn), +- adcli_get_last_error ()); ++ warnx ("adding member(s) to group %s in domain %s failed: %s", ++ adcli_entry_get_sam_name (entry), ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_attrs_free (attrs); ++ adcli_entry_unref (entry); ++ return -res; + } + + adcli_attrs_free (attrs); +-- +2.20.1 + diff --git a/0003-Do-not-add-service-principals-twice.patch b/0003-Do-not-add-service-principals-twice.patch new file mode 100644 index 0000000..5d0bfcf --- /dev/null +++ b/0003-Do-not-add-service-principals-twice.patch @@ -0,0 +1,57 @@ +From f05adc23d5cc9f1dfa5638e31949dcd81d632df9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 13 Aug 2018 17:32:24 +0200 +Subject: [PATCH 3/4] Do not add service principals twice + +--- + library/adenroll.c | 23 +++++++++++++++++++---- + 1 file changed, 19 insertions(+), 4 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index c4ba537..bb50365 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -313,6 +313,7 @@ add_service_names_to_service_principals (adcli_enroll *enroll) + char *name; + int length = 0; + int i; ++ size_t c; + + if (enroll->service_principals != NULL) { + length = seq_count (enroll->service_principals); +@@ -321,14 +322,28 @@ add_service_names_to_service_principals (adcli_enroll *enroll) + for (i = 0; enroll->service_names[i] != NULL; i++) { + if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0) + return_unexpected_if_reached (); +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, +- name, &length); ++ for (c = 0; enroll->service_principals != NULL && enroll->service_principals[c] != NULL; c++) { ++ if (strcmp (name, enroll->service_principals[c]) == 0) { ++ break; ++ } ++ } ++ if (enroll->service_principals == NULL || enroll->service_principals[c] == NULL) { ++ enroll->service_principals = _adcli_strv_add (enroll->service_principals, ++ name, &length); ++ } + + if (enroll->host_fqdn) { + if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0) + return_unexpected_if_reached (); +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, +- name, &length); ++ for (c = 0; enroll->service_principals != NULL && enroll->service_principals[c] != NULL; c++) { ++ if (strcmp (name, enroll->service_principals[c]) == 0) { ++ break; ++ } ++ } ++ if (enroll->service_principals == NULL || enroll->service_principals[c] == NULL) { ++ enroll->service_principals = _adcli_strv_add (enroll->service_principals, ++ name, &length); ++ } + } + } + +-- +2.17.1 + diff --git a/0003-adenroll-add-adcli_enroll_get_permitted_keytab_encty.patch b/0003-adenroll-add-adcli_enroll_get_permitted_keytab_encty.patch new file mode 100644 index 0000000..f810c13 --- /dev/null +++ b/0003-adenroll-add-adcli_enroll_get_permitted_keytab_encty.patch @@ -0,0 +1,196 @@ +From 0c09070e8beec734e3f0c70e14b0a04788077b73 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 13 Jun 2019 17:25:52 +0200 +Subject: [PATCH 3/4] adenroll: add adcli_enroll_get_permitted_keytab_enctypes + with tests + +The new call does not only return the current encryption types set in AD +or a default list but filters them with the list of permitted encryption +types on the client. This makes sure the client can create and use the +keys. + +Related to https://gitlab.freedesktop.org/realmd/adcli/issues/3 +--- + library/Makefile.am | 5 ++ + library/adenroll.c | 124 ++++++++++++++++++++++++++++++++++++++++++++ + library/adenroll.h | 2 + + 3 files changed, 131 insertions(+) + +diff --git a/library/Makefile.am b/library/Makefile.am +index 39e8fd1..4829555 100644 +--- a/library/Makefile.am ++++ b/library/Makefile.am +@@ -40,6 +40,7 @@ check_PROGRAMS = \ + test-util \ + test-ldap \ + test-attrs \ ++ test-adenroll \ + $(NULL) + + test_seq_SOURCES = seq.c test.c test.h +@@ -56,6 +57,10 @@ test_attrs_SOURCES = adattrs.c $(test_ldap_SOURCES) + test_attrs_CFLAGS = -DATTRS_TESTS + test_attrs_LDADD = $(test_ldap_LDADD) + ++test_adenroll_SOURCES = adenroll.c $(test_ldap_SOURCES) ++test_adenroll_CFLAGS = -DADENROLL_TESTS ++test_adenroll_LDADD = $(KRB5_LIBS) ++ + TESTS = $(check_PROGRAMS) + + MEMCHECK_ENV = $(TEST_RUNNER) valgrind --error-exitcode=80 --quiet --trace-children=yes +diff --git a/library/adenroll.c b/library/adenroll.c +index f617f28..95c07cd 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -2641,6 +2641,50 @@ adcli_enroll_get_keytab_enctypes (adcli_enroll *enroll) + return v51_earlier_enctypes; + } + ++krb5_enctype * ++adcli_enroll_get_permitted_keytab_enctypes (adcli_enroll *enroll) ++{ ++ krb5_enctype *cur_enctypes; ++ krb5_enctype *permitted_enctypes; ++ krb5_enctype *new_enctypes; ++ krb5_error_code code; ++ krb5_context k5; ++ size_t c; ++ size_t p; ++ size_t n; ++ ++ return_val_if_fail (enroll != NULL, NULL); ++ cur_enctypes = adcli_enroll_get_keytab_enctypes (enroll); ++ ++ k5 = adcli_conn_get_krb5_context (enroll->conn); ++ return_val_if_fail (k5 != NULL, NULL); ++ ++ code = krb5_get_permitted_enctypes (k5, &permitted_enctypes); ++ return_val_if_fail (code == 0, NULL); ++ ++ for (c = 0; cur_enctypes[c] != 0; c++); ++ ++ new_enctypes = calloc (c + 1, sizeof (krb5_enctype)); ++ return_val_if_fail (new_enctypes != NULL, NULL); ++ ++ n = 0; ++ for (c = 0; cur_enctypes[c] != 0; c++) { ++ for (p = 0; permitted_enctypes[p] != 0; p++) { ++ if (cur_enctypes[c] == permitted_enctypes[p]) { ++ new_enctypes[n++] = cur_enctypes[c]; ++ break; ++ } ++ } ++ if (permitted_enctypes[p] == 0) { ++ _adcli_info ("Encryption type [%d] not permitted.", cur_enctypes[c]); ++ } ++ } ++ ++ krb5_free_enctypes (k5, permitted_enctypes); ++ ++ return new_enctypes; ++} ++ + void + adcli_enroll_set_keytab_enctypes (adcli_enroll *enroll, + krb5_enctype *value) +@@ -2833,3 +2877,83 @@ adcli_enroll_add_service_principal_to_remove (adcli_enroll *enroll, + strdup (value), NULL); + return_if_fail (enroll->service_principals_to_remove != NULL); + } ++ ++#ifdef ADENROLL_TESTS ++ ++#include "test.h" ++ ++static void ++test_adcli_enroll_get_permitted_keytab_enctypes (void) ++{ ++ krb5_enctype *enctypes; ++ krb5_error_code code; ++ krb5_enctype *permitted_enctypes; ++ krb5_enctype check_enctypes[3] = { 0 }; ++ adcli_conn *conn; ++ adcli_enroll *enroll; ++ adcli_result res; ++ krb5_context k5; ++ size_t c; ++ ++ conn = adcli_conn_new ("test.dom"); ++ assert_ptr_not_null (conn); ++ ++ enroll = adcli_enroll_new (conn); ++ assert_ptr_not_null (enroll); ++ ++ enctypes = adcli_enroll_get_permitted_keytab_enctypes (NULL); ++ assert_ptr_eq (enctypes, NULL); ++ ++ /* krb5 context missing */ ++ enctypes = adcli_enroll_get_permitted_keytab_enctypes (enroll); ++ assert_ptr_eq (enctypes, NULL); ++ ++ /* check that all permitted enctypes can pass */ ++ res = _adcli_krb5_init_context (&k5); ++ assert_num_eq (res, ADCLI_SUCCESS); ++ ++ adcli_conn_set_krb5_context (conn, k5); ++ ++ code = krb5_get_permitted_enctypes (k5, &permitted_enctypes); ++ assert_num_eq (code, 0); ++ assert_ptr_not_null (permitted_enctypes); ++ assert_num_cmp (permitted_enctypes[0], !=, 0); ++ ++ adcli_enroll_set_keytab_enctypes (enroll, permitted_enctypes); ++ ++ enctypes = adcli_enroll_get_permitted_keytab_enctypes (enroll); ++ assert_ptr_not_null (enctypes); ++ for (c = 0; permitted_enctypes[c] != 0; c++) { ++ assert_num_eq (enctypes[c], permitted_enctypes[c]); ++ } ++ assert_num_eq (enctypes[c], 0); ++ krb5_free_enctypes (k5, enctypes); ++ ++ /* check that ENCTYPE_UNKNOWN is filtered out */ ++ check_enctypes[0] = permitted_enctypes[0]; ++ check_enctypes[1] = ENCTYPE_UNKNOWN; ++ check_enctypes[2] = 0; ++ adcli_enroll_set_keytab_enctypes (enroll, check_enctypes); ++ ++ enctypes = adcli_enroll_get_permitted_keytab_enctypes (enroll); ++ assert_ptr_not_null (enctypes); ++ assert_num_eq (enctypes[0], permitted_enctypes[0]); ++ assert_num_eq (enctypes[1], 0); ++ krb5_free_enctypes (k5, enctypes); ++ ++ krb5_free_enctypes (k5, permitted_enctypes); ++ ++ adcli_enroll_unref (enroll); ++ adcli_conn_unref (conn); ++} ++ ++int ++main (int argc, ++ char *argv[]) ++{ ++ test_func (test_adcli_enroll_get_permitted_keytab_enctypes, ++ "/attrs/adcli_enroll_get_permitted_keytab_enctypes"); ++ return test_run (argc, argv); ++} ++ ++#endif /* ADENROLL_TESTS */ +diff --git a/library/adenroll.h b/library/adenroll.h +index abbbfd4..1d5d00d 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -138,6 +138,8 @@ krb5_enctype * adcli_enroll_get_keytab_enctypes (adcli_enroll *enroll); + void adcli_enroll_set_keytab_enctypes (adcli_enroll *enroll, + krb5_enctype *enctypes); + ++krb5_enctype * adcli_enroll_get_permitted_keytab_enctypes (adcli_enroll *enroll); ++ + const char * adcli_enroll_get_os_name (adcli_enroll *enroll); + + void adcli_enroll_set_os_name (adcli_enroll *enroll, +-- +2.21.0 + diff --git a/0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch b/0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch new file mode 100644 index 0000000..59fb6fe --- /dev/null +++ b/0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch @@ -0,0 +1,26 @@ +From 85fa595baf689e85c0d897c5eef73fdf1ecc1581 Mon Sep 17 00:00:00 2001 +From: Striker Leggette +Date: Wed, 1 Nov 2017 11:16:39 +0100 +Subject: [PATCH 03/23] 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.14.4 + diff --git a/0003-enroll-use-computer-or-service-in-debug-messages.patch b/0003-enroll-use-computer-or-service-in-debug-messages.patch new file mode 100644 index 0000000..4e5aea5 --- /dev/null +++ b/0003-enroll-use-computer-or-service-in-debug-messages.patch @@ -0,0 +1,358 @@ +From eea6a8071b5e5df74808903bb15b30acf820ce3f Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 23 Oct 2020 16:55:11 +0200 +Subject: [PATCH 3/7] enroll: use 'computer' or 'service' in debug messages + +Use proper account type in debug messages. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1854112 +--- + library/adenroll.c | 115 ++++++++++++++++++++++++++++----------------- + 1 file changed, 72 insertions(+), 43 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index dbfda36..9cdc79b 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -155,6 +155,12 @@ struct _adcli_enroll { + char *description; + }; + ++static const char * ++s_or_c (adcli_enroll *enroll) ++{ ++ return enroll->is_service ? "service" : "computer"; ++} ++ + static void + check_if_service (adcli_enroll *enroll, + LDAP *ldap, +@@ -203,13 +209,15 @@ ensure_computer_name (adcli_result res, + return res; + + if (enroll->computer_name) { +- _adcli_info ("Enrolling computer name: %s", ++ _adcli_info ("Enrolling %s name: %s", ++ s_or_c (enroll), + enroll->computer_name); + return ADCLI_SUCCESS; + } + + if (!enroll->host_fqdn) { +- _adcli_err ("No host name from which to determine the computer name"); ++ _adcli_err ("No host name from which to determine the %s name", ++ s_or_c (enroll)); + return ADCLI_ERR_CONFIG; + } + +@@ -603,7 +611,8 @@ lookup_computer_container (adcli_enroll *enroll, + + } else if (ret != LDAP_SUCCESS) { + return _adcli_ldap_handle_failure (ldap, ADCLI_ERR_DIRECTORY, +- "Couldn't lookup computer container: %s", base); ++ "Couldn't lookup %s container: %s", ++ s_or_c (enroll), base); + } + + values = _adcli_ldap_parse_values (ldap, results, attrs[0]); +@@ -614,8 +623,8 @@ lookup_computer_container (adcli_enroll *enroll, + if (strncmp (values[i], prefix, prefix_len) == 0) { + enroll->computer_container = strdup (values[i] + prefix_len); + return_unexpected_if_fail (enroll->computer_container != NULL); +- _adcli_info ("Found well known computer container at: %s", +- enroll->computer_container); ++ _adcli_info ("Found well known %s container at: %s", ++ s_or_c (enroll), enroll->computer_container); + break; + } + } +@@ -629,8 +638,9 @@ lookup_computer_container (adcli_enroll *enroll, + if (ret == LDAP_SUCCESS) { + enroll->computer_container = _adcli_ldap_parse_dn (ldap, results); + if (enroll->computer_container) { +- _adcli_info ("Well known computer container not " ++ _adcli_info ("Well known %s container not " + "found, but found suitable one at: %s", ++ s_or_c (enroll), + enroll->computer_container); + } + } +@@ -646,7 +656,8 @@ lookup_computer_container (adcli_enroll *enroll, + } + + if (!enroll->computer_container) { +- _adcli_err ("Couldn't find location to create computer accounts"); ++ _adcli_err ("Couldn't find location to create %s accounts", ++ s_or_c (enroll)); + return ADCLI_ERR_DIRECTORY; + } + +@@ -674,7 +685,8 @@ calculate_computer_account (adcli_enroll *enroll, + if (asprintf (&enroll->computer_dn, "CN=%s,%s", enroll->computer_name, enroll->computer_container) < 0) + return_unexpected_if_reached (); + +- _adcli_info ("Calculated computer account: %s", enroll->computer_dn); ++ _adcli_info ("Calculated %s account: %s", ++ s_or_c (enroll), enroll->computer_dn); + return ADCLI_SUCCESS; + } + +@@ -861,7 +873,8 @@ create_computer_account (adcli_enroll *enroll, + enroll->computer_dn); + } + +- _adcli_info ("Created computer account: %s", enroll->computer_dn); ++ _adcli_info ("Created %s account: %s", s_or_c (enroll), ++ enroll->computer_dn); + return ADCLI_SUCCESS; + } + +@@ -908,17 +921,17 @@ validate_computer_account (adcli_enroll *enroll, + assert (enroll->computer_dn != NULL); + + if (already_exists && !allow_overwrite) { +- _adcli_err ("The computer account %s already exists", +- enroll->computer_name); ++ _adcli_err ("The %s account %s already exists", ++ s_or_c (enroll), enroll->computer_name); + return ADCLI_ERR_CONFIG; + } + + /* Do we have an explicitly requested ou? */ + if (enroll->domain_ou && enroll->domain_ou_explicit && already_exists) { + if (!_adcli_ldap_dn_has_ancestor (enroll->computer_dn, enroll->domain_ou)) { +- _adcli_err ("The computer account %s already exists, " ++ _adcli_err ("The %s account %s already exists, " + "but is not in the desired organizational unit.", +- enroll->computer_name); ++ s_or_c (enroll), enroll->computer_name); + return ADCLI_ERR_CONFIG; + } + } +@@ -943,7 +956,8 @@ delete_computer_account (adcli_enroll *enroll, + "Couldn't delete computer account: %s", + enroll->computer_dn); + } else { +- _adcli_info ("Deleted computer account at: %s", enroll->computer_dn); ++ _adcli_info ("Deleted %s account at: %s", s_or_c (enroll), ++ enroll->computer_dn); + } + + return ADCLI_SUCCESS; +@@ -992,20 +1006,21 @@ locate_computer_account (adcli_enroll *enroll, + free (enroll->computer_dn); + enroll->computer_dn = strdup (dn); + return_unexpected_if_fail (enroll->computer_dn != NULL); +- _adcli_info ("Found computer account for %s at: %s", +- enroll->computer_sam, dn); ++ _adcli_info ("Found %s account for %s at: %s", ++ s_or_c (enroll), enroll->computer_sam, dn); + ldap_memfree (dn); + + } else { + ldap_msgfree (results); + results = NULL; +- _adcli_info ("Computer account for %s does not exist", +- enroll->computer_sam); ++ _adcli_info ("A %s account for %s does not exist", ++ s_or_c (enroll), enroll->computer_sam); + } + + } else { + return _adcli_ldap_handle_failure (ldap, ADCLI_ERR_DIRECTORY, +- "Couldn't lookup computer account: %s", ++ "Couldn't lookup %s account: %s", ++ s_or_c (enroll), + enroll->computer_sam); + } + +@@ -1039,7 +1054,9 @@ load_computer_account (adcli_enroll *enroll, + if (ret == LDAP_SUCCESS) { + entry = ldap_first_entry (ldap, results); + if (entry) { +- _adcli_info ("Found computer account for %s at: %s", ++ check_if_service (enroll, ldap, results); ++ _adcli_info ("Found %s account for %s at: %s", ++ s_or_c (enroll), + enroll->computer_sam, enroll->computer_dn); + } + +@@ -1146,7 +1163,8 @@ set_password_with_user_creds (adcli_enroll *enroll) + &result_code_string, &result_string); + + if (code != 0) { +- _adcli_err ("Couldn't set password for computer account: %s: %s", ++ _adcli_err ("Couldn't set password for %s account: %s: %s", ++ s_or_c (enroll), + enroll->computer_sam, krb5_get_error_message (k5, code)); + /* TODO: Parse out these values */ + res = ADCLI_ERR_DIRECTORY; +@@ -1160,7 +1178,8 @@ set_password_with_user_creds (adcli_enroll *enroll) + if (result_string.length) + message = _adcli_str_dupn (result_string.data, result_string.length); + #endif +- _adcli_err ("Cannot set computer password: %.*s%s%s", ++ _adcli_err ("Cannot set %s password: %.*s%s%s", ++ s_or_c (enroll), + (int)result_code_string.length, result_code_string.data, + message ? ": " : "", message ? message : ""); + res = ADCLI_ERR_CREDENTIALS; +@@ -1170,7 +1189,7 @@ set_password_with_user_creds (adcli_enroll *enroll) + free (message); + #endif + } else { +- _adcli_info ("Set computer password"); ++ _adcli_info ("Set %s password", s_or_c (enroll)); + if (enroll->kvno > 0) { + enroll->kvno++; + _adcli_info ("kvno incremented to %d", enroll->kvno); +@@ -1203,7 +1222,8 @@ set_password_with_computer_creds (adcli_enroll *enroll) + + code = _adcli_kinit_computer_creds (enroll->conn, "kadmin/changepw", NULL, &creds); + if (code != 0) { +- _adcli_err ("Couldn't get change password ticket for computer account: %s: %s", ++ _adcli_err ("Couldn't get change password ticket for %s account: %s: %s", ++ s_or_c (enroll), + enroll->computer_sam, krb5_get_error_message (k5, code)); + return ADCLI_ERR_DIRECTORY; + } +@@ -1214,7 +1234,8 @@ set_password_with_computer_creds (adcli_enroll *enroll) + krb5_free_cred_contents (k5, &creds); + + if (code != 0) { +- _adcli_err ("Couldn't change password for computer account: %s: %s", ++ _adcli_err ("Couldn't change password for %s account: %s: %s", ++ s_or_c (enroll), + enroll->computer_sam, krb5_get_error_message (k5, code)); + /* TODO: Parse out these values */ + res = ADCLI_ERR_DIRECTORY; +@@ -1284,7 +1305,8 @@ retrieve_computer_account (adcli_enroll *enroll) + + if (ret != LDAP_SUCCESS) { + return _adcli_ldap_handle_failure (ldap, ADCLI_ERR_DIRECTORY, +- "Couldn't retrieve computer account info: %s", ++ "Couldn't retrieve %s account info: %s", ++ s_or_c (enroll), + enroll->computer_dn); + } + +@@ -1294,15 +1316,15 @@ retrieve_computer_account (adcli_enroll *enroll) + if (value != NULL) { + kvno = strtoul (value, &end, 10); + if (end == NULL || *end != '\0') { +- _adcli_err ("Invalid kvno '%s' for computer account in directory: %s", +- value, enroll->computer_dn); ++ _adcli_err ("Invalid kvno '%s' for %s account in directory: %s", ++ value, s_or_c (enroll), enroll->computer_dn); + res = ADCLI_ERR_DIRECTORY; + + } else { + enroll->kvno = kvno; + +- _adcli_info ("Retrieved kvno '%s' for computer account in directory: %s", +- value, enroll->computer_dn); ++ _adcli_info ("Retrieved kvno '%s' for %s account in directory: %s", ++ value, s_or_c (enroll), enroll->computer_dn); + } + + free (value); +@@ -1311,8 +1333,8 @@ retrieve_computer_account (adcli_enroll *enroll) + /* Apparently old AD didn't have this attribute, use zero */ + enroll->kvno = 0; + +- _adcli_info ("No kvno found for computer account in directory: %s", +- enroll->computer_dn); ++ _adcli_info ("No kvno found for %s account in directory: %s", ++ s_or_c (enroll), enroll->computer_dn); + } + } + +@@ -1353,12 +1375,14 @@ update_and_calculate_enctypes (adcli_enroll *enroll) + + if (ret == LDAP_INSUFFICIENT_ACCESS) { + return _adcli_ldap_handle_failure (ldap, ADCLI_ERR_CREDENTIALS, +- "Insufficient permissions to set encryption types on computer account: %s", ++ "Insufficient permissions to set encryption types on %s account: %s", ++ s_or_c (enroll), + enroll->computer_dn); + + } else if (ret != LDAP_SUCCESS) { + return _adcli_ldap_handle_failure (ldap, ADCLI_ERR_DIRECTORY, +- "Couldn't set encryption types on computer account: %s", ++ "Couldn't set encryption types on %s account: %s", ++ s_or_c (enroll), + enroll->computer_dn); + } + +@@ -1381,13 +1405,14 @@ update_computer_attribute (adcli_enroll *enroll, + string = _adcli_ldap_mods_to_string (mods); + return_unexpected_if_fail (string != NULL); + +- _adcli_info ("Modifying computer account: %s", string); ++ _adcli_info ("Modifying %s account: %s", s_or_c (enroll), string); + + ret = ldap_modify_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL); + + if (ret != LDAP_SUCCESS) { +- _adcli_warn ("Couldn't set %s on computer account: %s: %s", +- string, enroll->computer_dn, ldap_err2string (ret)); ++ _adcli_warn ("Couldn't set %s on %s account: %s: %s", ++ string, s_or_c (enroll), enroll->computer_dn, ++ ldap_err2string (ret)); + res = ADCLI_ERR_DIRECTORY; + } + +@@ -1411,8 +1436,8 @@ static char *get_user_account_control (adcli_enroll *enroll) + + 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); ++ _adcli_warn ("Invalid userAccountControl '%s' for %s account in directory: %s, assuming 0", ++ uac_str, s_or_c (enroll), enroll->computer_dn); + } else { + uac = attr_val; + } +@@ -1653,7 +1678,8 @@ load_keytab_entry (krb5_context k5, + _adcli_str_has_suffix (name, "$") && !strchr (name, '/')) { + enroll->computer_name = name; + name[len - 1] = '\0'; +- _adcli_info ("Found computer name in keytab: %s", name); ++ _adcli_info ("Found %s name in keytab: %s", ++ s_or_c (enroll), name); + adcli_conn_set_computer_name (enroll->conn, + enroll->computer_name); + name = NULL; +@@ -2348,7 +2374,8 @@ adcli_enroll_read_computer_account (adcli_enroll *enroll, + if (res != ADCLI_SUCCESS) + return res; + if (!enroll->computer_dn) { +- _adcli_err ("No computer account for %s exists", enroll->computer_sam); ++ _adcli_err ("No %s account for %s exists", ++ s_or_c (enroll), enroll->computer_sam); + return ADCLI_ERR_CONFIG; + } + } +@@ -2460,7 +2487,8 @@ adcli_enroll_delete (adcli_enroll *enroll, + if (res != ADCLI_SUCCESS) + return res; + if (!enroll->computer_dn) { +- _adcli_err ("No computer account for %s exists", ++ _adcli_err ("No %s account for %s exists", ++ s_or_c (enroll), + enroll->computer_sam); + return ADCLI_ERR_CONFIG; + } +@@ -2503,7 +2531,8 @@ adcli_enroll_password (adcli_enroll *enroll, + if (res != ADCLI_SUCCESS) + return res; + if (!enroll->computer_dn) { +- _adcli_err ("No computer account for %s exists", ++ _adcli_err ("No %s account for %s exists", ++ s_or_c (enroll), + enroll->computer_sam); + return ADCLI_ERR_CONFIG; + } +-- +2.28.0 + diff --git a/0003-entry-add-passwd-user-sub-command.patch b/0003-entry-add-passwd-user-sub-command.patch new file mode 100644 index 0000000..413d7ab --- /dev/null +++ b/0003-entry-add-passwd-user-sub-command.patch @@ -0,0 +1,366 @@ +From 6a673b236dfdfdf9c73cc3d2ccf3949eb1a5ddd0 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 11 Jun 2021 12:47:37 +0200 +Subject: [PATCH 3/3] entry: add passwd-user sub-command + +The new command allows to set or reset a user password with the help of +an account privileged to set the password. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1952828 +--- + doc/adcli.xml | 20 +++++++ + library/adentry.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++ + library/adentry.h | 3 + + tools/entry.c | 99 +++++++++++++++++++++++++++++++++ + tools/tools.c | 1 + + tools/tools.h | 4 ++ + 6 files changed, 265 insertions(+) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 1ed5d3f..6c36297 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -56,6 +56,11 @@ + --domain=domain.example.com + user + ++ ++ adcli passwd-user ++ --domain=domain.example.com ++ user ++ + + adcli create-group + --domain=domain.example.com +@@ -696,6 +701,21 @@ $ adcli delete-user Fry --domain=domain.example.com + + + ++ ++ (Re)setting the password of a User with an Administrative Account ++ ++ adcli passwd-user sets or resets the password ++ of user account. The administrative account used for this operation ++ must have privileges to set a password. ++ ++ ++$ adcli passwd-user Fry --domain=domain.example.com ++ ++ ++ The various global options can be used. ++ ++ ++ + + + Creating a Group +diff --git a/library/adentry.c b/library/adentry.c +index 13dcaf8..0d9b9af 100644 +--- a/library/adentry.c ++++ b/library/adentry.c +@@ -409,6 +409,144 @@ adcli_entry_delete (adcli_entry *entry) + return ADCLI_SUCCESS; + } + ++static adcli_result ++adcli_entry_ensure_enabled (adcli_entry *entry) ++{ ++ adcli_result res; ++ LDAP *ldap; ++ adcli_attrs *attrs; ++ uint32_t uac = 0; ++ char *uac_str; ++ unsigned long attr_val; ++ char *end; ++ ++ return_unexpected_if_fail (entry->entry_attrs != NULL); ++ ++ ldap = adcli_conn_get_ldap_connection (entry->conn); ++ return_unexpected_if_fail (ldap != NULL); ++ ++ uac_str = _adcli_ldap_parse_value (ldap, entry->entry_attrs, ++ "userAccountControl"); ++ if (uac_str != NULL) { ++ attr_val = strtoul (uac_str, &end, 10); ++ if (*end != '\0' || attr_val > UINT32_MAX) { ++ _adcli_warn ("Invalid userAccountControl '%s' for %s account in directory: %s, assuming 0", ++ uac_str, entry->object_class, entry->entry_dn); ++ } else { ++ uac = attr_val; ++ } ++ free (uac_str); ++ } ++ if (uac & UAC_ACCOUNTDISABLE) { ++ uac &= ~(UAC_ACCOUNTDISABLE); ++ ++ if (asprintf (&uac_str, "%d", uac) < 0) { ++ _adcli_warn ("Cannot enable %s entry %s after password (re)set", ++ entry->object_class, entry->entry_dn); ++ return ADCLI_ERR_UNEXPECTED; ++ } ++ ++ attrs = adcli_attrs_new (); ++ adcli_attrs_replace (attrs, "userAccountControl", uac_str, ++ NULL); ++ res = adcli_entry_modify (entry, attrs); ++ if (res == ADCLI_SUCCESS) { ++ _adcli_info ("Enabled %s entry %s after password (re)set", ++ entry->object_class, entry->entry_dn); ++ } else { ++ _adcli_warn ("Failed to enable %s entry %s after password (re)set", ++ entry->object_class, entry->entry_dn); ++ } ++ free (uac_str); ++ adcli_attrs_free (attrs); ++ } else { ++ res = ADCLI_SUCCESS; ++ } ++ ++ return res; ++} ++ ++adcli_result ++adcli_entry_set_passwd (adcli_entry *entry, const char *user_pwd) ++{ ++ adcli_result res; ++ LDAP *ldap; ++ krb5_error_code code; ++ krb5_context k5; ++ krb5_ccache ccache; ++ krb5_data result_string = { 0, }; ++ krb5_data result_code_string = { 0, }; ++ int result_code; ++ char *message; ++ krb5_principal user_principal; ++ ++ ldap = adcli_conn_get_ldap_connection (entry->conn); ++ return_unexpected_if_fail (ldap != NULL); ++ ++ /* Find the user */ ++ res = update_entry_from_domain (entry, ldap); ++ if (res != ADCLI_SUCCESS) ++ return res; ++ ++ if (!entry->entry_dn) { ++ _adcli_err ("Cannot find the %s entry %s in the domain", ++ entry->object_class, entry->sam_name); ++ return ADCLI_ERR_CONFIG; ++ } ++ ++ k5 = adcli_conn_get_krb5_context (entry->conn); ++ return_unexpected_if_fail (k5 != NULL); ++ ++ code = _adcli_krb5_build_principal (k5, entry->sam_name, ++ adcli_conn_get_domain_realm (entry->conn), ++ &user_principal); ++ return_unexpected_if_fail (code == 0); ++ ++ ccache = adcli_conn_get_login_ccache (entry->conn); ++ return_unexpected_if_fail (ccache != NULL); ++ ++ memset (&result_string, 0, sizeof (result_string)); ++ memset (&result_code_string, 0, sizeof (result_code_string)); ++ ++ code = krb5_set_password_using_ccache (k5, ccache, user_pwd, ++ user_principal, &result_code, ++ &result_code_string, &result_string); ++ ++ if (code != 0) { ++ _adcli_err ("Couldn't set password for %s account: %s: %s", ++ entry->object_class, ++ entry->sam_name, krb5_get_error_message (k5, code)); ++ /* TODO: Parse out these values */ ++ res = ADCLI_ERR_DIRECTORY; ++ ++ } else if (result_code != 0) { ++#ifdef HAVE_KRB5_CHPW_MESSAGE ++ if (krb5_chpw_message (k5, &result_string, &message) != 0) ++ message = NULL; ++#else ++ message = NULL; ++ if (result_string.length) ++ message = _adcli_str_dupn (result_string.data, result_string.length); ++#endif ++ _adcli_err ("Cannot set %s password: %.*s%s%s", ++ entry->object_class, ++ (int)result_code_string.length, result_code_string.data, ++ message ? ": " : "", message ? message : ""); ++ res = ADCLI_ERR_CREDENTIALS; ++#ifdef HAVE_KRB5_CHPW_MESSAGE ++ krb5_free_string (k5, message); ++#else ++ free (message); ++#endif ++ } else { ++ _adcli_info ("Password (re)setted for %s: %s", entry->object_class, entry->entry_dn); ++ ++ res = adcli_entry_ensure_enabled (entry); ++ } ++ ++ return res; ++} ++ + const char * + adcli_entry_get_sam_name (adcli_entry *entry) + { +diff --git a/library/adentry.h b/library/adentry.h +index ae90689..f2382b1 100644 +--- a/library/adentry.h ++++ b/library/adentry.h +@@ -49,6 +49,9 @@ adcli_result adcli_entry_modify (adcli_entry *entry, + + adcli_result adcli_entry_delete (adcli_entry *entry); + ++adcli_result adcli_entry_set_passwd (adcli_entry *entry, ++ const char *user_pwd); ++ + const char * adcli_entry_get_domain_ou (adcli_entry *entry); + + void adcli_entry_set_domain_ou (adcli_entry *entry, +diff --git a/tools/entry.c b/tools/entry.c +index 05e4313..52d2546 100644 +--- a/tools/entry.c ++++ b/tools/entry.c +@@ -24,6 +24,7 @@ + #include "config.h" + + #include "adcli.h" ++#include "adprivate.h" + #include "adattrs.h" + #include "tools.h" + +@@ -385,6 +386,104 @@ adcli_tool_user_delete (adcli_conn *conn, + return 0; + } + ++int ++adcli_tool_user_passwd (adcli_conn *conn, ++ int argc, ++ char *argv[]) ++{ ++ adcli_result res; ++ adcli_entry *entry; ++ int opt; ++ char *user_pwd = NULL; ++ ++ struct option options[] = { ++ { "domain", required_argument, NULL, opt_domain }, ++ { "domain-realm", required_argument, NULL, opt_domain_realm }, ++ { "domain-controller", required_argument, NULL, opt_domain_controller }, ++ { "use-ldaps", no_argument, 0, opt_use_ldaps }, ++ { "login-user", required_argument, NULL, opt_login_user }, ++ { "login-ccache", optional_argument, NULL, opt_login_ccache }, ++ { "no-password", no_argument, 0, opt_no_password }, ++ { "stdin-password", no_argument, 0, opt_stdin_password }, ++ { "prompt-password", no_argument, 0, opt_prompt_password }, ++ { "verbose", no_argument, NULL, opt_verbose }, ++ { "help", no_argument, NULL, 'h' }, ++ { 0 }, ++ }; ++ ++ static adcli_tool_desc usages[] = { ++ { 0, "usage: adcli passwd-user --domain=xxxx user" }, ++ { 0 }, ++ }; ++ ++ while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) { ++ switch (opt) { ++ case 'h': ++ case '?': ++ case ':': ++ adcli_tool_usage (options, usages); ++ adcli_tool_usage (options, common_usages); ++ return opt == 'h' ? 0 : 2; ++ default: ++ res = parse_option ((Option)opt, optarg, conn); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } ++ break; ++ } ++ } ++ ++ argc -= optind; ++ argv += optind; ++ ++ if (argc != 1) { ++ warnx ("specify one user name to (re)set password"); ++ return 2; ++ } ++ ++ entry = adcli_entry_new_user (conn, argv[0]); ++ if (entry == NULL) { ++ warnx ("unexpected memory problems"); ++ return -1; ++ } ++ ++ adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); ++ ++ res = adcli_conn_connect (conn); ++ if (res != ADCLI_SUCCESS) { ++ warnx ("couldn't connect to %s domain: %s", ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ return -res; ++ } ++ ++ user_pwd = adcli_prompt_password_func (ADCLI_LOGIN_USER_ACCOUNT, ++ adcli_entry_get_sam_name(entry), ++ 0, NULL); ++ if (user_pwd == NULL || *user_pwd == '\0') { ++ warnx ("missing password"); ++ _adcli_password_free (user_pwd); ++ adcli_entry_unref (entry); ++ return 2; ++ } ++ ++ res = adcli_entry_set_passwd (entry, user_pwd); ++ _adcli_password_free (user_pwd); ++ if (res != ADCLI_SUCCESS) { ++ warnx ("(re)setting password for user %s in domain %s failed: %s", ++ adcli_entry_get_sam_name (entry), ++ adcli_conn_get_domain_name (conn), ++ adcli_get_last_error ()); ++ adcli_entry_unref (entry); ++ return -res; ++ } ++ ++ adcli_entry_unref (entry); ++ ++ return 0; ++} ++ + int + adcli_tool_group_create (adcli_conn *conn, + int argc, +diff --git a/tools/tools.c b/tools/tools.c +index 84bbba9..a14b9ca 100644 +--- a/tools/tools.c ++++ b/tools/tools.c +@@ -63,6 +63,7 @@ struct { + { "create-msa", adcli_tool_computer_managed_service_account, "Create a managed service account in the given AD domain", }, + { "create-user", adcli_tool_user_create, "Create a user account", }, + { "delete-user", adcli_tool_user_delete, "Delete a user account", }, ++ { "passwd-user", adcli_tool_user_passwd, "(Re)set a user password", }, + { "create-group", adcli_tool_group_create, "Create a group", }, + { "delete-group", adcli_tool_group_delete, "Delete a group", }, + { "add-member", adcli_tool_member_add, "Add users to a group", }, +diff --git a/tools/tools.h b/tools/tools.h +index 82d5e4e..d38aa32 100644 +--- a/tools/tools.h ++++ b/tools/tools.h +@@ -94,6 +94,10 @@ int adcli_tool_user_delete (adcli_conn *conn, + int argc, + char *argv[]); + ++int adcli_tool_user_passwd (adcli_conn *conn, ++ int argc, ++ char *argv[]); ++ + int adcli_tool_group_create (adcli_conn *conn, + int argc, + char *argv[]); +-- +2.31.1 + diff --git a/0003-library-_adcli_krb5_build_principal-allow-principals.patch b/0003-library-_adcli_krb5_build_principal-allow-principals.patch new file mode 100644 index 0000000..217636c --- /dev/null +++ b/0003-library-_adcli_krb5_build_principal-allow-principals.patch @@ -0,0 +1,42 @@ +From 10a4dbb5978b6f05cf75f820d97da908e735ace8 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 22 Mar 2019 10:37:11 +0100 +Subject: [PATCH 3/4] library: _adcli_krb5_build_principal allow principals as + names + +Make _adcli_krb5_build_principal a bit more robust by checking if the +given name already contains a realm suffix. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1630187 +--- + library/adkrb5.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/library/adkrb5.c b/library/adkrb5.c +index 7f77373..da835d7 100644 +--- a/library/adkrb5.c ++++ b/library/adkrb5.c +@@ -41,12 +41,16 @@ _adcli_krb5_build_principal (krb5_context k5, + krb5_principal *principal) + { + krb5_error_code code; +- char *name; ++ char *name = NULL; + +- if (asprintf (&name, "%s@%s", user, realm) < 0) +- return_val_if_reached (ENOMEM); ++ /* Use user if user contains a @-character and add @realm otherwise */ ++ if (strchr (user, '@') == NULL) { ++ if (asprintf (&name, "%s@%s", user, realm) < 0) { ++ return_val_if_reached (ENOMEM); ++ } ++ } + +- code = krb5_parse_name (k5, name, principal); ++ code = krb5_parse_name (k5, name != NULL ? name : user, principal); + return_val_if_fail (code == 0, code); + + free (name); +-- +2.20.1 + diff --git a/0003-tools-remove-errx-from-info-commands.patch b/0003-tools-remove-errx-from-info-commands.patch new file mode 100644 index 0000000..c53cf50 --- /dev/null +++ b/0003-tools-remove-errx-from-info-commands.patch @@ -0,0 +1,53 @@ +From 4794812cc98c8783921f534d20dae8b44f3826d2 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 15 Apr 2019 17:57:37 +0200 +Subject: [PATCH 3/7] tools: remove errx from info commands + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596 +--- + tools/info.c | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +diff --git a/tools/info.c b/tools/info.c +index e7e20ad..c63e0ff 100644 +--- a/tools/info.c ++++ b/tools/info.c +@@ -162,21 +162,28 @@ adcli_tool_info (adcli_conn *unused, + + if (argc == 1) + domain = argv[0]; +- else if (argc != 0) +- errx (2, "specify one user name to create"); ++ else if (argc != 0) { ++ warnx ("specify one user name to create"); ++ return 2; ++ } + + if (server) { + adcli_disco_host (server, &disco); +- if (disco == NULL) +- errx (1, "couldn't discover domain controller: %s", server); ++ if (disco == NULL) { ++ warnx ("couldn't discover domain controller: %s", server); ++ return 1; ++ } + for_host = 1; + } else if (domain) { + adcli_disco_domain (domain, &disco); +- if (disco == NULL) +- errx (1, "couldn't discover domain: %s", domain); ++ if (disco == NULL) { ++ warnx ("couldn't discover domain: %s", domain); ++ return 1; ++ } + for_host = 0; + } else { +- errx (2, "specify a domain to discover"); ++ warnx ("specify a domain to discover"); ++ return 2; + } + + print_info (disco, for_host); +-- +2.20.1 + diff --git a/0004-Do-not-depend-on-default_realm-in-krb5.conf.patch b/0004-Do-not-depend-on-default_realm-in-krb5.conf.patch new file mode 100644 index 0000000..d12f335 --- /dev/null +++ b/0004-Do-not-depend-on-default_realm-in-krb5.conf.patch @@ -0,0 +1,27 @@ +From 8f726817b9ff643a382fa12ea9ff489cd5ab9068 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 13 Aug 2018 18:24:58 +0200 +Subject: [PATCH 4/4] Do not depend on default_realm in krb5.conf + +--- + library/adenroll.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index bb50365..02bd9e3 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1661,7 +1661,9 @@ remove_principal_from_keytab (adcli_enroll *enroll, + krb5_principal principal; + match_principal_kvno closure; + +- code = krb5_parse_name (k5, principal_name, &principal); ++ code = _adcli_krb5_build_principal (k5, principal_name, ++ adcli_conn_get_domain_realm (enroll->conn), ++ &principal); + if (code != 0) { + _adcli_err ("Couldn't parse principal: %s: %s", + principal_name, krb5_get_error_message (k5, code)); +-- +2.17.1 + diff --git a/0004-adenroll-use-only-enctypes-permitted-by-Kerberos-con.patch b/0004-adenroll-use-only-enctypes-permitted-by-Kerberos-con.patch new file mode 100644 index 0000000..a496e32 --- /dev/null +++ b/0004-adenroll-use-only-enctypes-permitted-by-Kerberos-con.patch @@ -0,0 +1,103 @@ +From cc3ef52884a48863a81acbfc741735fe09cd85f7 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 13 Jun 2019 18:27:49 +0200 +Subject: [PATCH 4/4] adenroll: use only enctypes permitted by Kerberos config + +Realted to https://gitlab.freedesktop.org/realmd/adcli/issues/3 +--- + doc/adcli.xml | 10 ++++++++++ + library/adenroll.c | 22 +++++++++++++++++++--- + 2 files changed, 29 insertions(+), 3 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 9605b4a..094f577 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -342,6 +342,11 @@ Password for Administrator: + + + ++ If supported on the AD side the ++ attribute will be set as ++ well. Either the current value or the default list of AD's supported ++ encryption types filtered by the permitted encryption types of the ++ client's Kerberos configuration are written. + + + +@@ -475,6 +480,11 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + + + ++ If supported on the AD side the ++ attribute will be set as ++ well. Either the current value or the default list of AD's supported ++ encryption types filtered by the permitted encryption types of the ++ client's Kerberos configuration are written. + + + +diff --git a/library/adenroll.c b/library/adenroll.c +index 95c07cd..53cd812 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -639,6 +639,7 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype) + { + char *value = NULL; + krb5_enctype *read_enctypes; ++ krb5_enctype *new_enctypes; + char *new_value = NULL; + int is_2008_or_later; + LDAP *ldap; +@@ -685,7 +686,14 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype) + value = _adcli_krb5_format_enctypes (v51_earlier_enctypes); + } + +- new_value = _adcli_krb5_format_enctypes (adcli_enroll_get_keytab_enctypes (enroll)); ++ new_enctypes = adcli_enroll_get_permitted_keytab_enctypes (enroll); ++ if (new_enctypes == NULL) { ++ _adcli_warn ("No permitted encryption type found."); ++ return ADCLI_ERR_UNEXPECTED; ++ } ++ ++ new_value = _adcli_krb5_format_enctypes (new_enctypes); ++ krb5_free_enctypes (adcli_conn_get_krb5_context (enroll->conn), new_enctypes); + if (new_value == NULL) { + free (value); + _adcli_warn ("The encryption types desired are not available in active directory"); +@@ -1758,7 +1766,11 @@ add_principal_to_keytab (adcli_enroll *enroll, + enroll->keytab_name); + } + +- enctypes = adcli_enroll_get_keytab_enctypes (enroll); ++ enctypes = adcli_enroll_get_permitted_keytab_enctypes (enroll); ++ if (enctypes == NULL) { ++ _adcli_warn ("No permitted encryption type found."); ++ return ADCLI_ERR_UNEXPECTED; ++ } + + if (flags & ADCLI_ENROLL_PASSWORD_VALID) { + code = _adcli_krb5_keytab_copy_entries (k5, enroll->keytab, principal, +@@ -1774,7 +1786,10 @@ add_principal_to_keytab (adcli_enroll *enroll, + */ + + salts = build_principal_salts (enroll, k5, principal); +- return_unexpected_if_fail (salts != NULL); ++ if (salts == NULL) { ++ krb5_free_enctypes (k5, enctypes); ++ return ADCLI_ERR_UNEXPECTED; ++ } + + if (*which_salt < 0) { + code = _adcli_krb5_keytab_discover_salt (k5, principal, enroll->kvno, &password, +@@ -1794,6 +1809,7 @@ add_principal_to_keytab (adcli_enroll *enroll, + + free_principal_salts (k5, salts); + } ++ krb5_free_enctypes (k5, enctypes); + + if (code != 0) { + _adcli_err ("Couldn't add keytab entries: %s: %s", +-- +2.21.0 + diff --git a/0004-doc-explain-that-all-credential-cache-types-are-supp.patch b/0004-doc-explain-that-all-credential-cache-types-are-supp.patch new file mode 100644 index 0000000..e0dbc67 --- /dev/null +++ b/0004-doc-explain-that-all-credential-cache-types-are-supp.patch @@ -0,0 +1,37 @@ +From c3ec5121c1e79344ce615612ab3b576bc4745acb Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 1 Nov 2017 12:01:18 +0100 +Subject: [PATCH 04/23] 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. + + +- ++ + Use the specified kerberos credential +- cache to authenticate with the domain. If no file is specified or +- is used, then the default kerberos credential cache will +- be used. ++ 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. + + + +-- +2.14.4 + diff --git a/0004-enroll-more-filters-for-random-characters.patch b/0004-enroll-more-filters-for-random-characters.patch new file mode 100644 index 0000000..911e229 --- /dev/null +++ b/0004-enroll-more-filters-for-random-characters.patch @@ -0,0 +1,77 @@ +From 2750f536ac6746756335eec8332060d2365a4126 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 27 Oct 2020 14:44:07 +0100 +Subject: [PATCH 4/7] enroll: more filters for random characters + +Make handling of random strings more flexible. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1854112 +--- + library/adenroll.c | 30 +++++++++++++++++++++++++++--- + 1 file changed, 27 insertions(+), 3 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 9cdc79b..44383cc 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -259,6 +259,29 @@ ensure_computer_sam (adcli_result res, + return ADCLI_SUCCESS; + } + ++typedef int (rand_filter) (char *password, int length); ++ ++static int ++filter_sam_chars (char *password, ++ int length) ++{ ++ int i, j; ++ ++ /* ++ * There are a couple of restrictions for characters in the ++ * sAMAccountName attribute value, for our purpose (random suffix) ++ * letters and numbers are sufficient. ++ */ ++ for (i = 0, j = 0; i < length; i++) { ++ if (password[i] >= 48 && password[i] <= 122 && ++ isalnum (password[i])) ++ password[j++] = password[i]; ++ } ++ ++ /* return the number of valid characters remaining */ ++ return j; ++} ++ + static int + filter_password_chars (char *password, + int length) +@@ -283,7 +306,8 @@ filter_password_chars (char *password, + + static char * + generate_host_password (adcli_enroll *enroll, +- size_t length) ++ size_t length, ++ rand_filter *filter) + { + char *password; + krb5_context k5; +@@ -305,7 +329,7 @@ generate_host_password (adcli_enroll *enroll, + code = krb5_c_random_make_octets (k5, &buffer); + return_val_if_fail (code == 0, NULL); + +- at += filter_password_chars (buffer.data, buffer.length); ++ at += filter (buffer.data, buffer.length); + assert (at <= length); + } + +@@ -333,7 +357,7 @@ ensure_computer_password (adcli_result res, + _adcli_info ("Using default reset computer password"); + + } else { +- enroll->computer_password = generate_host_password (enroll, length); ++ enroll->computer_password = generate_host_password (enroll, length, filter_password_chars); + return_unexpected_if_fail (enroll->computer_password != NULL); + _adcli_info ("Generated %d character computer password", length); + } +-- +2.28.0 + diff --git a/0004-library-make-sure-server-side-SPNs-are-preserved.patch b/0004-library-make-sure-server-side-SPNs-are-preserved.patch new file mode 100644 index 0000000..07cc8fb --- /dev/null +++ b/0004-library-make-sure-server-side-SPNs-are-preserved.patch @@ -0,0 +1,82 @@ +From 972f1a2f35829ed89f5353bd204683aa9ad6a2d2 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 22 Mar 2019 10:37:57 +0100 +Subject: [PATCH 4/4] library: make sure server side SPNs are preserved + +adcli should not delete service principal names (SPNs) unexpectedly. If +a SPN was added on the server while presetting a host or updating an +existing entry and upcoming adcli join or update should preserver this +change. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1630187 +--- + library/adenroll.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 46 insertions(+) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 48cb4cf..1cce86a 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1961,6 +1961,47 @@ adcli_enroll_prepare (adcli_enroll *enroll, + return res; + } + ++static adcli_result ++add_server_side_service_principals (adcli_enroll *enroll) ++{ ++ char **spn_list; ++ LDAP *ldap; ++ size_t c; ++ int length = 0; ++ adcli_result res; ++ ++ ldap = adcli_conn_get_ldap_connection (enroll->conn); ++ assert (ldap != NULL); ++ ++ spn_list = _adcli_ldap_parse_values (ldap, enroll->computer_attributes, ++ "servicePrincipalName"); ++ if (spn_list == NULL) { ++ return ADCLI_SUCCESS; ++ } ++ ++ if (enroll->service_principals != NULL) { ++ length = seq_count (enroll->service_principals); ++ } ++ ++ for (c = 0; spn_list[c] != NULL; c++) { ++ _adcli_info ("Checking %s", spn_list[c]); ++ if (!_adcli_strv_has_ex (enroll->service_principals_to_remove, spn_list[c], strcasecmp)) { ++ enroll->service_principals = _adcli_strv_add_unique (enroll->service_principals, ++ spn_list[c], &length, false); ++ assert (enroll->service_principals != NULL); ++ _adcli_info (" Added %s", spn_list[c]); ++ } ++ } ++ _adcli_strv_free (spn_list); ++ ++ res = ensure_keytab_principals (ADCLI_SUCCESS, enroll); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } ++ ++ return ADCLI_SUCCESS; ++} ++ + static adcli_result + enroll_join_or_update_tasks (adcli_enroll *enroll, + adcli_enroll_flags flags) +@@ -2019,6 +2060,11 @@ enroll_join_or_update_tasks (adcli_enroll *enroll, + update_and_calculate_enctypes (enroll); + update_computer_account (enroll); + ++ res = add_server_side_service_principals (enroll); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } ++ + /* service_names is only set from input on the command line, so no + * additional check for explicit is needed here */ + if (enroll->service_names != NULL) { +-- +2.20.1 + diff --git a/0004-tools-remove-errx-from-adcli_read_password_func.patch b/0004-tools-remove-errx-from-adcli_read_password_func.patch new file mode 100644 index 0000000..1b53d7d --- /dev/null +++ b/0004-tools-remove-errx-from-adcli_read_password_func.patch @@ -0,0 +1,42 @@ +From 251d7d0c71226afb8e51f7bc5794a7a3164f5a20 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 15 Apr 2019 17:59:17 +0200 +Subject: [PATCH 4/7] tools: remove errx from adcli_read_password_func + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596 +--- + tools/tools.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/tools/tools.c b/tools/tools.c +index c4e2851..bdf6d38 100644 +--- a/tools/tools.c ++++ b/tools/tools.c +@@ -247,7 +247,9 @@ adcli_read_password_func (adcli_login_type login_type, + if (res < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; +- err (EFAIL, "couldn't read password from stdin"); ++ warn ("couldn't read password from stdin"); ++ free (buffer); ++ return NULL; + + } else if (res == 0) { + buffer[offset] = '\0'; +@@ -261,8 +263,11 @@ adcli_read_password_func (adcli_login_type login_type, + return buffer; + + } else { +- if (memchr (buffer + offset, 0, res)) +- errx (EUSAGE, "unsupported null character present in password"); ++ if (memchr (buffer + offset, 0, res)) { ++ warnx ("unsupported null character present in password"); ++ free (buffer); ++ return NULL; ++ } + offset += res; + } + } +-- +2.20.1 + diff --git a/0005-enroll-make-adcli_enroll_add_keytab_for_service_acco.patch b/0005-enroll-make-adcli_enroll_add_keytab_for_service_acco.patch new file mode 100644 index 0000000..4da2d9d --- /dev/null +++ b/0005-enroll-make-adcli_enroll_add_keytab_for_service_acco.patch @@ -0,0 +1,91 @@ +From 81c98e367ba4bc8d77668acd31e462ad31cf12be Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 27 Oct 2020 14:47:31 +0100 +Subject: [PATCH 5/7] enroll: make adcli_enroll_add_keytab_for_service_account + public + +Determine keytab name more early to catch errors more early. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1854112 +--- + library/adenroll.c | 13 +++++++------ + library/adenroll.h | 2 ++ + tools/computer.c | 6 ++++++ + 3 files changed, 15 insertions(+), 6 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 44383cc..05bb085 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -2276,9 +2276,10 @@ adcli_enroll_add_description_for_service_account (adcli_enroll *enroll) + return ADCLI_SUCCESS; + } + +-static adcli_result ++adcli_result + adcli_enroll_add_keytab_for_service_account (adcli_enroll *enroll) + { ++ adcli_result res; + krb5_context k5; + krb5_error_code code; + char def_keytab_name[MAX_KEYTAB_NAME_LEN]; +@@ -2286,11 +2287,14 @@ adcli_enroll_add_keytab_for_service_account (adcli_enroll *enroll) + int ret; + + if (adcli_enroll_get_keytab_name (enroll) == NULL) { +- k5 = adcli_conn_get_krb5_context (enroll->conn); +- return_unexpected_if_fail (k5 != NULL); ++ res = _adcli_krb5_init_context (&k5); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } + + code = krb5_kt_default_name (k5, def_keytab_name, + sizeof (def_keytab_name)); ++ krb5_free_context (k5); + return_unexpected_if_fail (code == 0); + + lc_dom_name = strdup (adcli_conn_get_domain_name (enroll->conn)); +@@ -2326,9 +2330,6 @@ adcli_enroll_join (adcli_enroll *enroll, + + if (enroll->is_service) { + res = adcli_enroll_add_description_for_service_account (enroll); +- if (res == ADCLI_SUCCESS) { +- res = adcli_enroll_add_keytab_for_service_account (enroll); +- } + } else { + res = ensure_default_service_names (enroll); + } +diff --git a/library/adenroll.h b/library/adenroll.h +index 7765ed4..11a30c8 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -146,6 +146,8 @@ const char * adcli_enroll_get_keytab_name (adcli_enroll *enroll); + void adcli_enroll_set_keytab_name (adcli_enroll *enroll, + const char *value); + ++adcli_result adcli_enroll_add_keytab_for_service_account (adcli_enroll *enroll); ++ + krb5_enctype * adcli_enroll_get_keytab_enctypes (adcli_enroll *enroll); + + void adcli_enroll_set_keytab_enctypes (adcli_enroll *enroll, +diff --git a/tools/computer.c b/tools/computer.c +index 63fd374..98a0472 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -1166,6 +1166,12 @@ adcli_tool_computer_managed_service_account (adcli_conn *conn, + + adcli_enroll_set_is_service (enroll, true); + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); ++ res = adcli_enroll_add_keytab_for_service_account (enroll); ++ if (res != ADCLI_SUCCESS) { ++ warnx ("Failed to set domain specific keytab name"); ++ adcli_enroll_unref (enroll); ++ return 2; ++ } + + res = adcli_enroll_load (enroll); + if (res != ADCLI_SUCCESS) { +-- +2.28.0 + diff --git a/0005-library-add-adcli_conn_is_writeable.patch b/0005-library-add-adcli_conn_is_writeable.patch new file mode 100644 index 0000000..832bb0f --- /dev/null +++ b/0005-library-add-adcli_conn_is_writeable.patch @@ -0,0 +1,38 @@ +From d2cdc54b0e51436c30ffaf19b0530aa446440367 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 1 Nov 2017 16:29:19 +0100 +Subject: [PATCH 05/23] library: add adcli_conn_is_writeable() + +--- + library/adconn.c | 6 ++++++ + library/adconn.h | 2 ++ + 2 files changed, 8 insertions(+) + +diff --git a/library/adconn.c b/library/adconn.c +index a294dfd..67bdfd9 100644 +--- a/library/adconn.c ++++ b/library/adconn.c +@@ -1528,3 +1528,9 @@ adcli_conn_server_has_capability (adcli_conn *conn, + + return 0; + } ++ ++bool adcli_conn_is_writeable (adcli_conn *conn) ++{ ++ disco_dance_if_necessary (conn); ++ return ( (conn->domain_disco->flags & ADCLI_DISCO_WRITABLE) != 0); ++} +diff --git a/library/adconn.h b/library/adconn.h +index a0cb1f8..ed1cc58 100644 +--- a/library/adconn.h ++++ b/library/adconn.h +@@ -144,4 +144,6 @@ void adcli_conn_set_krb5_conf_dir (adcli_conn *conn, + int adcli_conn_server_has_capability (adcli_conn *conn, + const char *capability); + ++bool adcli_conn_is_writeable (adcli_conn *conn); ++ + #endif /* ADCONN_H_ */ +-- +2.14.4 + diff --git a/0005-tools-remove-errx-from-setup_krb5_conf_directory.patch b/0005-tools-remove-errx-from-setup_krb5_conf_directory.patch new file mode 100644 index 0000000..8fd9197 --- /dev/null +++ b/0005-tools-remove-errx-from-setup_krb5_conf_directory.patch @@ -0,0 +1,63 @@ +From b8f5d995d30c17eb8bec3ac5e0777ea94f5b76c3 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 15 Apr 2019 18:00:52 +0200 +Subject: [PATCH 5/7] tools: remove errx from setup_krb5_conf_directory + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596 +--- + tools/tools.c | 38 ++++++++++++++++++++++++-------------- + 1 file changed, 24 insertions(+), 14 deletions(-) + +diff --git a/tools/tools.c b/tools/tools.c +index bdf6d38..fc9fa9a 100644 +--- a/tools/tools.c ++++ b/tools/tools.c +@@ -327,21 +327,31 @@ setup_krb5_conf_directory (adcli_conn *conn) + } + + if (asprintf (&directory, "%s%sadcli-krb5-XXXXXX", parent, +- (parent[0] && parent[strlen(parent) - 1] == '/') ? "" : "/") < 0) +- errx (1, "unexpected: out of memory"); +- +- if (mkdtemp (directory) == NULL) { +- errn = errno; ++ (parent[0] && parent[strlen(parent) - 1] == '/') ? "" : "/") < 0) { ++ warnx ("unexpected: out of memory"); ++ directory = NULL; /* content is undefined */ + failed = 1; +- warnx ("couldn't create temporary directory in: %s: %s", +- parent, strerror (errn)); +- } else { +- if (asprintf (&filename, "%s/krb5.conf", directory) < 0 || +- asprintf (&snippets, "%s/krb5.d", directory) < 0 || +- asprintf (&contents, "includedir %s\n%s%s\n", snippets, +- krb5_conf ? "include " : "", +- krb5_conf ? krb5_conf : "") < 0) +- errx (1, "unexpected: out of memory"); ++ } ++ ++ if (!failed) { ++ if (mkdtemp (directory) == NULL) { ++ errn = errno; ++ failed = 1; ++ warnx ("couldn't create temporary directory in: %s: %s", ++ parent, strerror (errn)); ++ } else { ++ if (asprintf (&filename, "%s/krb5.conf", directory) < 0 || ++ asprintf (&snippets, "%s/krb5.d", directory) < 0 || ++ asprintf (&contents, "includedir %s\n%s%s\n", snippets, ++ krb5_conf ? "include " : "", ++ krb5_conf ? krb5_conf : "") < 0) { ++ warnx ("unexpected: out of memory"); ++ filename = NULL; /* content is undefined */ ++ snippets = NULL; /* content is undefined */ ++ contents = NULL; /* content is undefined */ ++ failed = 1; ++ } ++ } + } + + if (!failed) { +-- +2.20.1 + diff --git a/0006-Handle-kvno-increment-for-RODCs.patch b/0006-Handle-kvno-increment-for-RODCs.patch new file mode 100644 index 0000000..90f05a4 --- /dev/null +++ b/0006-Handle-kvno-increment-for-RODCs.patch @@ -0,0 +1,67 @@ +From 6b60f4c08d811e4bc3a68d1a4770c2ce5619c890 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 1 Nov 2017 17:14:05 +0100 +Subject: [PATCH 06/23] 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.14.4 + diff --git a/0006-enroll-allow-fqdn-for-locate_computer_account.patch b/0006-enroll-allow-fqdn-for-locate_computer_account.patch new file mode 100644 index 0000000..9f6094a --- /dev/null +++ b/0006-enroll-allow-fqdn-for-locate_computer_account.patch @@ -0,0 +1,129 @@ +From 2a695dfe09cafeee3a648d3b969c364f8d3f494f Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 27 Oct 2020 14:49:55 +0100 +Subject: [PATCH 6/7] enroll: allow fqdn for locate_computer_account + +Make it possible to find existing manages service account by the +fully-qualified name. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1854112 +--- + library/adenroll.c | 45 +++++++++++++++++++++++++++++++-------------- + 1 file changed, 31 insertions(+), 14 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 05bb085..98cd5fa 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -990,10 +990,11 @@ delete_computer_account (adcli_enroll *enroll, + static adcli_result + locate_computer_account (adcli_enroll *enroll, + LDAP *ldap, ++ bool use_fqdn, + LDAPMessage **rresults, + LDAPMessage **rentry) + { +- char *attrs[] = { "objectClass", NULL }; ++ char *attrs[] = { "objectClass", "CN", NULL }; + LDAPMessage *results = NULL; + LDAPMessage *entry = NULL; + const char *base; +@@ -1003,12 +1004,22 @@ locate_computer_account (adcli_enroll *enroll, + int ret = 0; + + /* If we don't yet know our computer dn, then try and find it */ +- value = _adcli_ldap_escape_filter (enroll->computer_sam); +- return_unexpected_if_fail (value != NULL); +- if (asprintf (&filter, "(&(objectClass=%s)(sAMAccountName=%s))", +- enroll->is_service ? "msDS-ManagedServiceAccount" : "computer", +- value) < 0) +- return_unexpected_if_reached (); ++ if (use_fqdn) { ++ return_unexpected_if_fail (enroll->host_fqdn != NULL); ++ value = _adcli_ldap_escape_filter (enroll->host_fqdn); ++ return_unexpected_if_fail (value != NULL); ++ if (asprintf (&filter, "(&(objectClass=%s)(dNSHostName=%s))", ++ enroll->is_service ? "msDS-ManagedServiceAccount" : "computer", ++ value) < 0) ++ return_unexpected_if_reached (); ++ } else { ++ value = _adcli_ldap_escape_filter (enroll->computer_sam); ++ return_unexpected_if_fail (value != NULL); ++ if (asprintf (&filter, "(&(objectClass=%s)(sAMAccountName=%s))", ++ enroll->is_service ? "msDS-ManagedServiceAccount" : "computer", ++ value) < 0) ++ return_unexpected_if_reached (); ++ } + free (value); + + base = adcli_conn_get_default_naming_context (enroll->conn); +@@ -1031,21 +1042,26 @@ locate_computer_account (adcli_enroll *enroll, + enroll->computer_dn = strdup (dn); + return_unexpected_if_fail (enroll->computer_dn != NULL); + _adcli_info ("Found %s account for %s at: %s", +- s_or_c (enroll), enroll->computer_sam, dn); ++ s_or_c (enroll), ++ use_fqdn ? enroll->host_fqdn ++ : enroll->computer_sam, dn); + ldap_memfree (dn); + + } else { + ldap_msgfree (results); + results = NULL; + _adcli_info ("A %s account for %s does not exist", +- s_or_c (enroll), enroll->computer_sam); ++ s_or_c (enroll), ++ use_fqdn ? enroll->host_fqdn ++ : enroll->computer_sam); + } + + } else { + return _adcli_ldap_handle_failure (ldap, ADCLI_ERR_DIRECTORY, + "Couldn't lookup %s account: %s", + s_or_c (enroll), +- enroll->computer_sam); ++ use_fqdn ? enroll->host_fqdn ++ :enroll->computer_sam); + } + + if (rresults) +@@ -1120,7 +1136,8 @@ locate_or_create_computer_account (adcli_enroll *enroll, + + /* Try to find the computer account */ + if (!enroll->computer_dn) { +- res = locate_computer_account (enroll, ldap, &results, &entry); ++ res = locate_computer_account (enroll, ldap, false, ++ &results, &entry); + if (res != ADCLI_SUCCESS) + return res; + searched = 1; +@@ -2395,7 +2412,7 @@ adcli_enroll_read_computer_account (adcli_enroll *enroll, + + /* Find the computer dn */ + if (!enroll->computer_dn) { +- res = locate_computer_account (enroll, ldap, NULL, NULL); ++ res = locate_computer_account (enroll, ldap, false, NULL, NULL); + if (res != ADCLI_SUCCESS) + return res; + if (!enroll->computer_dn) { +@@ -2508,7 +2525,7 @@ adcli_enroll_delete (adcli_enroll *enroll, + + /* Find the computer dn */ + if (!enroll->computer_dn) { +- res = locate_computer_account (enroll, ldap, NULL, NULL); ++ res = locate_computer_account (enroll, ldap, false, NULL, NULL); + if (res != ADCLI_SUCCESS) + return res; + if (!enroll->computer_dn) { +@@ -2552,7 +2569,7 @@ adcli_enroll_password (adcli_enroll *enroll, + + /* Find the computer dn */ + if (!enroll->computer_dn) { +- res = locate_computer_account (enroll, ldap, NULL, NULL); ++ res = locate_computer_account (enroll, ldap, false, NULL, NULL); + if (res != ADCLI_SUCCESS) + return res; + if (!enroll->computer_dn) { +-- +2.28.0 + diff --git a/0006-tools-entry-remove-errx-from-parse_option.patch b/0006-tools-entry-remove-errx-from-parse_option.patch new file mode 100644 index 0000000..17bb9c4 --- /dev/null +++ b/0006-tools-entry-remove-errx-from-parse_option.patch @@ -0,0 +1,175 @@ +From d9912e19e48ec482351b9c384140ad71922ec5c0 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 8 Apr 2019 17:22:00 +0200 +Subject: [PATCH 6/7] tools: entry - remove errx from parse_option + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596 +--- + tools/entry.c | 70 ++++++++++++++++++++++++++++++++++----------------- + 1 file changed, 47 insertions(+), 23 deletions(-) + +diff --git a/tools/entry.c b/tools/entry.c +index 97ec6e7..f361845 100644 +--- a/tools/entry.c ++++ b/tools/entry.c +@@ -81,7 +81,7 @@ static adcli_tool_desc common_usages[] = { + { 0 }, + }; + +-static void ++static int + parse_option (Option opt, + const char *optarg, + adcli_conn *conn) +@@ -93,54 +93,58 @@ parse_option (Option opt, + switch (opt) { + case opt_login_ccache: + adcli_conn_set_login_ccache_name (conn, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_login_user: + adcli_conn_set_login_user (conn, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_domain: + adcli_conn_set_domain_name (conn, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_domain_realm: + adcli_conn_set_domain_realm (conn, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_domain_controller: + adcli_conn_set_domain_controller (conn, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_no_password: + if (stdin_password || prompt_password) { +- errx (EUSAGE, "cannot use --no-password argument with %s", +- stdin_password ? "--stdin-password" : "--prompt-password"); ++ warnx ("cannot use --no-password argument with %s", ++ stdin_password ? "--stdin-password" : "--prompt-password"); ++ return EUSAGE; + } else { + adcli_conn_set_password_func (conn, NULL, NULL, NULL); + no_password = 1; + } +- return; ++ return ADCLI_SUCCESS; + case opt_prompt_password: + if (stdin_password || no_password) { +- errx (EUSAGE, "cannot use --prompt-password argument with %s", +- stdin_password ? "--stdin-password" : "--no-password"); ++ warnx ("cannot use --prompt-password argument with %s", ++ stdin_password ? "--stdin-password" : "--no-password"); ++ return EUSAGE; + } else { + adcli_conn_set_password_func (conn, adcli_prompt_password_func, NULL, NULL); + prompt_password = 1; + } +- return; ++ return ADCLI_SUCCESS; + case opt_stdin_password: + if (prompt_password || no_password) { +- errx (EUSAGE, "cannot use --stdin-password argument with %s", +- prompt_password ? "--prompt-password" : "--no-password"); ++ warnx ("cannot use --stdin-password argument with %s", ++ prompt_password ? "--prompt-password" : "--no-password"); ++ return EUSAGE; + } else { + adcli_conn_set_password_func (conn, adcli_read_password_func, NULL, NULL); + stdin_password = 1; + } +- return; ++ return ADCLI_SUCCESS; + case opt_verbose: +- return; ++ return ADCLI_SUCCESS; + default: + assert (0 && "not reached"); + break; + } + +- errx (EUSAGE, "failure to parse option '%c'", opt); ++ warnx ("failure to parse option '%c'", opt); ++ return EUSAGE; + } + + int +@@ -224,7 +228,11 @@ adcli_tool_user_create (adcli_conn *conn, + adcli_attrs_free (attrs); + return opt == 'h' ? 0 : 2; + default: +- parse_option ((Option)opt, optarg, conn); ++ res = parse_option ((Option)opt, optarg, conn); ++ if (res != ADCLI_SUCCESS) { ++ adcli_attrs_free (attrs); ++ return res; ++ } + break; + } + } +@@ -322,7 +330,10 @@ adcli_tool_user_delete (adcli_conn *conn, + adcli_tool_usage (options, common_usages); + return opt == 'h' ? 0 : 2; + default: +- parse_option ((Option)opt, optarg, conn); ++ res = parse_option ((Option)opt, optarg, conn); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } + break; + } + } +@@ -417,7 +428,11 @@ adcli_tool_group_create (adcli_conn *conn, + adcli_attrs_free (attrs); + return opt == 'h' ? 0 : 2; + default: +- parse_option ((Option)opt, optarg, conn); ++ res = parse_option ((Option)opt, optarg, conn); ++ if (res != ADCLI_SUCCESS) { ++ adcli_attrs_free (attrs); ++ return res; ++ } + break; + } + } +@@ -505,7 +520,10 @@ adcli_tool_group_delete (adcli_conn *conn, + adcli_tool_usage (options, common_usages); + return opt == 'h' ? 0 : 2; + default: +- parse_option ((Option)opt, optarg, conn); ++ res = parse_option ((Option)opt, optarg, conn); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } + break; + } + } +@@ -628,7 +646,10 @@ adcli_tool_member_add (adcli_conn *conn, + adcli_tool_usage (options, common_usages); + return opt == 'h' ? 0 : 2; + default: +- parse_option ((Option)opt, optarg, conn); ++ res = parse_option ((Option)opt, optarg, conn); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } + break; + } + } +@@ -725,7 +746,10 @@ adcli_tool_member_remove (adcli_conn *conn, + adcli_tool_usage (options, common_usages); + return opt == 'h' ? 0 : 2; + default: +- parse_option ((Option)opt, optarg, conn); ++ res = parse_option ((Option)opt, optarg, conn); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } + break; + } + } +-- +2.20.1 + diff --git a/0007-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch b/0007-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch new file mode 100644 index 0000000..18c8c9c --- /dev/null +++ b/0007-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch @@ -0,0 +1,28 @@ +From 3d312a6c89a88be444fb5ed768fbaa6155bf1cc9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:39:46 +0100 +Subject: [PATCH 07/23] Fix memory leak in test_check_nt_time_string_lifetime + +The test added with 650e5d33ef31437a049fb454ad3dc5457c56abe7 introduced +a small memory leak. + +Reviewed-by: Jakub Hrozek +--- + library/adutil.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/library/adutil.c b/library/adutil.c +index 21ccd27..cd40f45 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -501,6 +501,7 @@ test_check_nt_time_string_lifetime (void) + (time (NULL) + 10 + AD_TO_UNIX_TIME_CONST) * 1000 * 1000 *10) + != -1); + assert (!_adcli_check_nt_time_string_lifetime (time_str, 0)); ++ free (time_str); + + /* This test will fail some time after 2200AD as a reminder to reflect + * why adcli is still needed. */ +-- +2.14.4 + diff --git a/0007-service-account-add-random-suffix-to-account-name.patch b/0007-service-account-add-random-suffix-to-account-name.patch new file mode 100644 index 0000000..d2635ca --- /dev/null +++ b/0007-service-account-add-random-suffix-to-account-name.patch @@ -0,0 +1,122 @@ +From 6b94f9712378b8f1fa1bc530c64cb987abb0c43b Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 27 Oct 2020 15:23:04 +0100 +Subject: [PATCH 7/7] service-account: add random suffix to account name + +Add a random component to the default managed service account name to +avoid name collisions. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1854112 +--- + library/adenroll.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 79 insertions(+) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 98cd5fa..f693e58 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1121,6 +1121,59 @@ load_computer_account (adcli_enroll *enroll, + return ADCLI_SUCCESS; + } + ++static adcli_result ++refresh_service_account_name_sam_and_princ (adcli_enroll *enroll, ++ const char *name) ++{ ++ adcli_result res; ++ ++ adcli_enroll_set_computer_name (enroll, name); ++ res = ensure_computer_sam (ADCLI_SUCCESS, enroll); ++ res = ensure_keytab_principals (res, enroll); ++ ++ return res; ++} ++ ++static adcli_result ++calculate_random_service_account_name (adcli_enroll *enroll) ++{ ++ char *suffix; ++ char *new_name; ++ int ret; ++ adcli_result res; ++ ++ suffix = generate_host_password (enroll, 3, filter_sam_chars); ++ return_unexpected_if_fail (suffix != NULL); ++ ++ ret = asprintf (&new_name, "%s!%s", enroll->computer_name, suffix); ++ free (suffix); ++ return_unexpected_if_fail (ret > 0); ++ ++ res = refresh_service_account_name_sam_and_princ (enroll, new_name); ++ free (new_name); ++ ++ return res; ++} ++ ++static adcli_result ++get_service_account_name_from_ldap (adcli_enroll *enroll, LDAPMessage *results) ++{ ++ LDAP *ldap; ++ char *cn; ++ adcli_result res; ++ ++ ldap = adcli_conn_get_ldap_connection (enroll->conn); ++ assert (ldap != NULL); ++ ++ cn = _adcli_ldap_parse_value (ldap, results, "CN"); ++ return_unexpected_if_fail (cn != NULL); ++ ++ res = refresh_service_account_name_sam_and_princ (enroll, cn); ++ free (cn); ++ ++ return res; ++} ++ + static adcli_result + locate_or_create_computer_account (adcli_enroll *enroll, + int allow_overwrite) +@@ -1143,8 +1196,32 @@ locate_or_create_computer_account (adcli_enroll *enroll, + searched = 1; + } + ++ /* Try with fqdn for service accounts */ ++ if (!enroll->computer_dn && enroll->is_service ++ && enroll->host_fqdn != NULL) { ++ res = locate_computer_account (enroll, ldap, true, ++ &results, &entry); ++ if (res != ADCLI_SUCCESS) ++ return res; ++ searched = 1; ++ ++ if (results != NULL) { ++ res = get_service_account_name_from_ldap (enroll, ++ results); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } ++ } ++ } ++ + /* Next try and come up with where we think it should be */ + if (enroll->computer_dn == NULL) { ++ if (enroll->is_service && !enroll->computer_name_explicit) { ++ res = calculate_random_service_account_name (enroll); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } ++ } + res = calculate_computer_account (enroll, ldap); + if (res != ADCLI_SUCCESS) + return res; +@@ -2113,6 +2190,8 @@ adcli_enroll_prepare (adcli_enroll *enroll, + + if (enroll->is_service) { + /* Ensure basic params for service accounts */ ++ res = ensure_host_fqdn (res, enroll); ++ res = ensure_computer_name (res, enroll); + res = ensure_computer_sam (res, enroll); + res = ensure_computer_password (res, enroll); + res = ensure_host_keytab (res, enroll); +-- +2.28.0 + diff --git a/0007-tools-computer-remove-errx-from-parse_option.patch b/0007-tools-computer-remove-errx-from-parse_option.patch new file mode 100644 index 0000000..065fc9f --- /dev/null +++ b/0007-tools-computer-remove-errx-from-parse_option.patch @@ -0,0 +1,294 @@ +From f127ddef23a532cd9763190527bf79b4e47fa2ab Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 8 Apr 2019 17:33:17 +0200 +Subject: [PATCH 7/7] tools: computer - remove errx from parse_option + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1588596 +--- + tools/computer.c | 128 +++++++++++++++++++++++++++++------------------ + 1 file changed, 80 insertions(+), 48 deletions(-) + +diff --git a/tools/computer.c b/tools/computer.c +index 9cbbb28..ac8a203 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -159,7 +159,7 @@ static adcli_tool_desc common_usages[] = { + { 0 }, + }; + +-static void ++static int + parse_option (Option opt, + const char *optarg, + adcli_conn *conn, +@@ -175,132 +175,139 @@ parse_option (Option opt, + switch (opt) { + case opt_login_ccache: + adcli_conn_set_login_ccache_name (conn, optarg ? optarg : ""); +- return; ++ return ADCLI_SUCCESS; + case opt_login_user: + if (adcli_conn_get_allowed_login_types (conn) & ADCLI_LOGIN_USER_ACCOUNT) { + adcli_conn_set_login_user (conn, optarg); + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); + } else { +- errx (EUSAGE, "cannot set --user if --login-type not set to 'user'"); ++ warnx ("cannot set --user if --login-type not set to 'user'"); ++ return EUSAGE; + } +- return; ++ return ADCLI_SUCCESS; + case opt_login_type: + if (optarg && strcmp (optarg, "computer") == 0) { +- if (adcli_conn_get_login_user (conn) != NULL) +- errx (EUSAGE, "cannot set --login-type to 'computer' if --user is set"); +- else ++ if (adcli_conn_get_login_user (conn) != NULL) { ++ warnx ("cannot set --login-type to 'computer' if --user is set"); ++ return EUSAGE; ++ } else + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_COMPUTER_ACCOUNT); + } else if (optarg && strcmp (optarg, "user") == 0) { + adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT); + + } else { +- errx (EUSAGE, "unknown login type '%s'", optarg); ++ warnx ("unknown login type '%s'", optarg); ++ return EUSAGE; + } +- return; ++ return ADCLI_SUCCESS; + case opt_host_fqdn: + adcli_conn_set_host_fqdn (conn, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_host_keytab: + adcli_enroll_set_keytab_name (enroll, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_computer_name: + adcli_conn_set_computer_name (conn, optarg); + adcli_enroll_set_computer_name (enroll, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_domain: + adcli_conn_set_domain_name (conn, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_domain_realm: + adcli_conn_set_domain_realm (conn, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_domain_controller: + adcli_conn_set_domain_controller (conn, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_domain_ou: + adcli_enroll_set_domain_ou (enroll, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_service_name: + adcli_enroll_add_service_name (enroll, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_no_password: + if (stdin_password || prompt_password) { +- errx (EUSAGE, "cannot use --no-password argument with %s", +- stdin_password ? "--stdin-password" : "--prompt-password"); ++ warnx ("cannot use --no-password argument with %s", ++ stdin_password ? "--stdin-password" : "--prompt-password"); ++ return EUSAGE; + } else { + adcli_conn_set_password_func (conn, NULL, NULL, NULL); + no_password = 1; + } +- return; ++ return ADCLI_SUCCESS; + case opt_prompt_password: + if (stdin_password || no_password) { +- errx (EUSAGE, "cannot use --prompt-password argument with %s", +- stdin_password ? "--stdin-password" : "--no-password"); ++ warnx ("cannot use --prompt-password argument with %s", ++ stdin_password ? "--stdin-password" : "--no-password"); ++ return EUSAGE; + } else { + adcli_conn_set_password_func (conn, adcli_prompt_password_func, NULL, NULL); + prompt_password = 1; + } +- return; ++ return ADCLI_SUCCESS; + case opt_stdin_password: + if (prompt_password || no_password) { +- errx (EUSAGE, "cannot use --stdin-password argument with %s", +- prompt_password ? "--prompt-password" : "--no-password"); ++ warnx ("cannot use --stdin-password argument with %s", ++ prompt_password ? "--prompt-password" : "--no-password"); ++ return EUSAGE; + } else { + adcli_conn_set_password_func (conn, adcli_read_password_func, NULL, NULL); + stdin_password = 1; + } +- return; ++ return ADCLI_SUCCESS; + case opt_os_name: + adcli_enroll_set_os_name (enroll, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_os_version: + adcli_enroll_set_os_version (enroll, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_os_service_pack: + adcli_enroll_set_os_service_pack (enroll, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_user_principal: + if (optarg && optarg[0]) + adcli_enroll_set_user_principal (enroll, optarg); + else + adcli_enroll_auto_user_principal (enroll); +- return; ++ return ADCLI_SUCCESS; + case opt_computer_password_lifetime: + errno = 0; + lifetime = strtoul (optarg, &endptr, 10); + if (errno != 0 || *endptr != '\0' || endptr == optarg) { +- errx (EUSAGE, +- "failure to parse value '%s' of option 'computer-password-lifetime'; " +- "expecting non-negative integer indicating the lifetime in days", +- optarg); ++ warnx ("failure to parse value '%s' of option 'computer-password-lifetime'; " ++ "expecting non-negative integer indicating the lifetime in days", ++ optarg); ++ return EUSAGE; + } + + adcli_enroll_set_computer_password_lifetime (enroll, lifetime); +- return; ++ return ADCLI_SUCCESS; + case opt_samba_data_tool: + errno = 0; + ret = access (optarg, X_OK); + if (ret != 0) { + ret = errno; +- errx (EUSAGE, "Failed to access tool to add Samba data: %s", strerror (ret)); ++ warnx ("Failed to access tool to add Samba data: %s", strerror (ret)); ++ return EUSAGE; + } else { + adcli_enroll_set_samba_data_tool (enroll, optarg); + } +- return; ++ return ADCLI_SUCCESS; + case opt_trusted_for_delegation: + if (strcasecmp (optarg, "true") == 0 || strcasecmp (optarg, "yes") == 0) { + adcli_enroll_set_trusted_for_delegation (enroll, true); + } else { + adcli_enroll_set_trusted_for_delegation (enroll, false); + } +- return; ++ return ADCLI_SUCCESS; + case opt_add_service_principal: + adcli_enroll_add_service_principal_to_add (enroll, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_remove_service_principal: + adcli_enroll_add_service_principal_to_remove (enroll, optarg); +- return; ++ return ADCLI_SUCCESS; + case opt_verbose: +- return; ++ return ADCLI_SUCCESS; + + /* Should be handled by caller */ + case opt_show_details: +@@ -311,7 +318,8 @@ parse_option (Option opt, + break; + } + +- errx (EUSAGE, "failure to parse option '%c'", opt); ++ warnx ("failure to parse option '%c'", opt); ++ return EUSAGE; + } + + static void +@@ -407,7 +415,11 @@ adcli_tool_computer_join (adcli_conn *conn, + adcli_enroll_unref (enroll); + return opt == 'h' ? 0 : 2; + default: +- parse_option ((Option)opt, optarg, conn, enroll); ++ res = parse_option ((Option)opt, optarg, conn, enroll); ++ if (res != ADCLI_SUCCESS) { ++ adcli_enroll_unref (enroll); ++ return res; ++ } + break; + } + } +@@ -519,7 +531,11 @@ adcli_tool_computer_update (adcli_conn *conn, + adcli_enroll_unref (enroll); + return opt == 'h' ? 0 : 2; + default: +- parse_option ((Option)opt, optarg, conn, enroll); ++ res = parse_option ((Option)opt, optarg, conn, enroll); ++ if (res != ADCLI_SUCCESS) { ++ adcli_enroll_unref (enroll); ++ return res; ++ } + break; + } + } +@@ -610,7 +626,11 @@ adcli_tool_computer_testjoin (adcli_conn *conn, + adcli_enroll_unref (enroll); + return opt == 'h' ? 0 : 2; + default: +- parse_option ((Option)opt, optarg, conn, enroll); ++ res = parse_option ((Option)opt, optarg, conn, enroll); ++ if (res != ADCLI_SUCCESS) { ++ adcli_enroll_unref (enroll); ++ return res; ++ } + break; + } + } +@@ -707,7 +727,11 @@ adcli_tool_computer_preset (adcli_conn *conn, + adcli_enroll_unref (enroll); + return 2; + default: +- parse_option ((Option)opt, optarg, conn, enroll); ++ res = parse_option ((Option)opt, optarg, conn, enroll); ++ if (res != ADCLI_SUCCESS) { ++ adcli_enroll_unref (enroll); ++ return res; ++ } + break; + } + } +@@ -801,7 +825,11 @@ adcli_tool_computer_reset (adcli_conn *conn, + adcli_enroll_unref (enroll); + return opt == 'h' ? 0 : 2; + default: +- parse_option ((Option)opt, optarg, conn, enroll); ++ res = parse_option ((Option)opt, optarg, conn, enroll); ++ if (res != ADCLI_SUCCESS) { ++ adcli_enroll_unref (enroll); ++ return res; ++ } + break; + } + } +@@ -884,7 +912,11 @@ adcli_tool_computer_delete (adcli_conn *conn, + adcli_enroll_unref (enroll); + return opt == 'h' ? 0 : 2; + default: +- parse_option ((Option)opt, optarg, conn, enroll); ++ res = parse_option ((Option)opt, optarg, conn, enroll); ++ if (res != ADCLI_SUCCESS) { ++ adcli_enroll_unref (enroll); ++ return res; ++ } + break; + } + } +-- +2.20.1 + diff --git a/0008-library-add-_adcli_bin_sid_to_str.patch b/0008-library-add-_adcli_bin_sid_to_str.patch new file mode 100644 index 0000000..c7d1c14 --- /dev/null +++ b/0008-library-add-_adcli_bin_sid_to_str.patch @@ -0,0 +1,178 @@ +From f28edf4e887cf8616fa21dacc2b0f0d31f5f92fb Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:37:05 +0100 +Subject: [PATCH 08/23] library: add _adcli_bin_sid_to_str() + +Convert a binary SID to the string representation. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + 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 + #include + #include ++#include + + #include + +@@ -132,6 +133,9 @@ int _adcli_str_has_prefix (const char *str, + int _adcli_str_has_suffix (const char *str, + const char *suffix); + ++char * _adcli_bin_sid_to_str (const uint8_t *data, ++ size_t len); ++ + char * _adcli_str_dupn (void *data, + size_t len); + +diff --git a/library/adutil.c b/library/adutil.c +index cd40f45..829cdd9 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -293,6 +293,83 @@ _adcli_strv_set (char ***field, + *field = newval; + } + ++char * ++_adcli_bin_sid_to_str (const uint8_t *data, ++ size_t len) ++{ ++ uint8_t sid_rev_num; ++ int8_t num_auths; ++ uint8_t id_auth[6]; ++ uint32_t id_auth_val; ++ uint32_t sub_auths[15]; ++ uint32_t val; ++ size_t p = 0; ++ size_t c; ++ int nc; ++ char *sid_buf; ++ size_t sid_buf_len; ++ ++ if (data == NULL || len < 8) { ++ return NULL; ++ } ++ ++ sid_rev_num = (uint8_t) data [p]; ++ p++; ++ ++ num_auths = (int8_t) data[p]; ++ p++; ++ ++ if (num_auths > 15 || len < 8 + (num_auths * sizeof (uint32_t))) { ++ return NULL; ++ } ++ ++ for (c = 0; c < 6; c++) { ++ id_auth[c] = (uint8_t) data[p]; ++ p++; ++ } ++ ++ /* Only 32bits are used for the string representation */ ++ id_auth_val = (id_auth[2] << 24) + ++ (id_auth[3] << 16) + ++ (id_auth[4] << 8) + ++ (id_auth[5]); ++ ++ for (c = 0; c < num_auths; c++) { ++ memcpy (&val, data + p, sizeof (uint32_t)); ++ sub_auths[c] = le32toh (val); ++ ++ p += sizeof (uint32_t); ++ } ++ ++ sid_buf_len = 17 + (num_auths * 11); ++ sid_buf = calloc (1, sid_buf_len); ++ if (sid_buf == NULL) { ++ return NULL; ++ } ++ ++ nc = snprintf (sid_buf, sid_buf_len, "S-%u-%lu", sid_rev_num, ++ (unsigned long) id_auth_val); ++ if (nc < 0 || nc >= sid_buf_len) { ++ free (sid_buf); ++ return NULL; ++ } ++ ++ p = 0; ++ for (c = 0; c < num_auths; c++) { ++ p += nc; ++ sid_buf_len -= nc; ++ ++ nc = snprintf (sid_buf + p, sid_buf_len, "-%lu", ++ (unsigned long) sub_auths[c]); ++ if (nc < 0 || nc >= sid_buf_len) { ++ free (sid_buf); ++ return NULL; ++ } ++ } ++ ++ return sid_buf; ++} ++ + char * + _adcli_str_dupn (void *data, + size_t len) +@@ -508,6 +585,41 @@ test_check_nt_time_string_lifetime (void) + assert (_adcli_check_nt_time_string_lifetime ("130645404000000000", 100000)); + } + ++static void ++test_bin_sid_to_str (void) ++{ ++ uint8_t sid1[] = { 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, ++ 0x15, 0x00, 0x00, 0x00, 0xF8, 0x12, 0x13, 0xDC, ++ 0x47, 0xF3, 0x1C, 0x76, 0x47, 0x2F, 0x2E, 0xD7, ++ 0x51, 0x04, 0x00, 0x00 }; ++ ++ uint8_t sid2[] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, ++ 0x15, 0x00, 0x00, 0x00, 0xF8, 0x12, 0x13, 0xDC, ++ 0x47, 0xF3, 0x1C, 0x76, 0x47, 0x2F, 0x2E, 0xD7}; ++ ++ uint8_t sid3[] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, ++ 0x15, 0x00, 0x00, 0x00, 0x29, 0xC9, 0x4F, 0xD9, ++ 0xC2, 0x3C, 0xC3, 0x78, 0x36, 0x55, 0x87, 0xF8}; ++ ++ ++ char *str; ++ ++ str = _adcli_bin_sid_to_str (sid1, sizeof (sid1)); ++ assert (str != NULL); ++ assert (strcmp (str, "S-1-5-21-3692237560-1981608775-3610128199-1105") == 0); ++ free (str); ++ ++ str = _adcli_bin_sid_to_str (sid2, sizeof (sid2)); ++ assert (str != NULL); ++ assert (strcmp (str, "S-1-5-21-3692237560-1981608775-3610128199") == 0); ++ free (str); ++ ++ str = _adcli_bin_sid_to_str (sid3, sizeof (sid2)); ++ assert (str != NULL); ++ assert (strcmp (str, "S-1-5-21-3645884713-2026060994-4169618742") == 0); ++ free (str); ++} ++ + int + main (int argc, + char *argv[]) +@@ -516,6 +628,7 @@ main (int argc, + test_func (test_strv_dup, "/util/strv_dup"); + test_func (test_strv_count, "/util/strv_count"); + test_func (test_check_nt_time_string_lifetime, "/util/check_nt_time_string_lifetime"); ++ test_func (test_bin_sid_to_str, "/util/bin_sid_to_str"); + return test_run (argc, argv); + } + +-- +2.14.4 + diff --git a/0009-library-add-_adcli_call_external_program.patch b/0009-library-add-_adcli_call_external_program.patch new file mode 100644 index 0000000..f215de6 --- /dev/null +++ b/0009-library-add-_adcli_call_external_program.patch @@ -0,0 +1,317 @@ +From 63576f12524f521c0cf08d42b279654885135a90 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:39:17 +0100 +Subject: [PATCH 09/23] library: add _adcli_call_external_program() + +Allow adcli to call an external program given by an absolute path name +and an array of options. stdin and stdout can be used if needed. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + configure.ac | 28 +++++++ + library/adprivate.h | 6 ++ + library/adutil.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 245 insertions(+) + +diff --git a/configure.ac b/configure.ac +index 221d8ae..fe86638 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -263,6 +263,34 @@ AC_SUBST(LCOV) + AC_SUBST(GCOV) + AC_SUBST(GENHTML) + ++AC_PATH_PROG(BIN_CAT, cat, no) ++if test "$BIN_CAT" = "no" ; then ++ AC_MSG_ERROR([cat is not available]) ++else ++ AC_DEFINE_UNQUOTED(BIN_CAT, "$BIN_CAT", [path to cat, used in unit test]) ++fi ++ ++AC_PATH_PROG(BIN_TAC, tac, no) ++if test "$BIN_TAC" = "no" ; then ++ AC_MSG_ERROR([tac is not available]) ++else ++ AC_DEFINE_UNQUOTED(BIN_TAC, "$BIN_TAC", [path to tac, used in unit test]) ++fi ++ ++AC_PATH_PROG(BIN_REV, rev, no) ++if test "$BIN_REV" = "no" ; then ++ AC_MSG_ERROR([rev is not available]) ++else ++ AC_DEFINE_UNQUOTED(BIN_REV, "$BIN_REV", [path to rev, used in unit test]) ++fi ++ ++AC_PATH_PROG(BIN_ECHO, echo, no) ++if test "$BIN_ECHO" = "no" ; then ++ AC_MSG_ERROR([echo is not available]) ++else ++ AC_DEFINE_UNQUOTED(BIN_ECHO, "$BIN_ECHO", [path to echo, used in unit test]) ++fi ++ + # --------------------------------------------------------------------- + + ADCLI_LT_RELEASE=$ADCLI_CURRENT:$ADCLI_REVISION:$ADCLI_AGE +diff --git a/library/adprivate.h b/library/adprivate.h +index e99f9fc..7257c93 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -285,4 +285,10 @@ struct _adcli_attrs { + + bool _adcli_check_nt_time_string_lifetime (const char *nt_time_string, unsigned int lifetime); + ++adcli_result _adcli_call_external_program (const char *binary, ++ char * const *argv, ++ const char *stdin_data, ++ uint8_t **stdout_data, ++ size_t *stdout_data_len); ++ + #endif /* ADPRIVATE_H_ */ +diff --git a/library/adutil.c b/library/adutil.c +index 829cdd9..a27bd68 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + + static adcli_message_func message_func = NULL; + static char last_error[2048] = { 0, }; +@@ -506,6 +507,161 @@ _adcli_check_nt_time_string_lifetime (const char *nt_time_string, + return false; + } + ++adcli_result ++_adcli_call_external_program (const char *binary, char * const *argv, ++ const char *stdin_data, ++ uint8_t **stdout_data, size_t *stdout_data_len) ++{ ++ int ret; ++ int pipefd_to_child[2] = { -1, -1}; ++ int pipefd_from_child[2] = { -1, -1}; ++ pid_t child_pid = 0; ++ int err; ++ size_t len; ++ ssize_t rlen; ++ pid_t wret; ++ int status; ++ uint8_t read_buf[4096]; ++ uint8_t *out; ++ ++ errno = 0; ++ ret = access (binary, X_OK); ++ if (ret != 0) { ++ err = errno; ++ _adcli_err ("Cannot run [%s]: [%d][%s].", binary, err, ++ strerror (err)); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ ++ ret = pipe (pipefd_from_child); ++ if (ret == -1) { ++ err = errno; ++ _adcli_err ("pipe failed [%d][%s].", err, strerror (err)); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ ++ ret = pipe (pipefd_to_child); ++ if (ret == -1) { ++ err = errno; ++ _adcli_err ("pipe failed [%d][%s].", err, strerror (err)); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ ++ child_pid = fork (); ++ ++ if (child_pid == 0) { /* child */ ++ close (pipefd_to_child[1]); ++ ret = dup2 (pipefd_to_child[0], STDIN_FILENO); ++ if (ret == -1) { ++ err = errno; ++ _adcli_err ("dup2 failed [%d][%s].", err, ++ strerror (err)); ++ exit (EXIT_FAILURE); ++ } ++ ++ close (pipefd_from_child[0]); ++ ret = dup2 (pipefd_from_child[1], STDOUT_FILENO); ++ if (ret == -1) { ++ err = errno; ++ _adcli_err ("dup2 failed [%d][%s].", err, ++ strerror (err)); ++ exit (EXIT_FAILURE); ++ } ++ ++ execv (binary, argv); ++ _adcli_err ("Failed to run %s.", binary); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } else if (child_pid > 0) { /* parent */ ++ ++ if (stdin_data != NULL) { ++ len = strlen (stdin_data); ++ ret = write (pipefd_to_child[1], stdin_data, len); ++ if (ret != len) { ++ _adcli_err ("Failed to send computer account password " ++ "to net command."); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ } ++ ++ close (pipefd_to_child[0]); ++ pipefd_to_child[0] = -1; ++ close (pipefd_to_child[1]); ++ pipefd_to_child[0] = -1; ++ ++ if (stdout_data != NULL || stdout_data_len != NULL) { ++ rlen = read (pipefd_from_child[0], read_buf, sizeof (read_buf)); ++ if (rlen < 0) { ++ ret = errno; ++ _adcli_err ("Failed to read from child [%d][%s].\n", ++ ret, strerror (ret)); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ ++ out = malloc (sizeof(uint8_t) * rlen); ++ if (out == NULL) { ++ _adcli_err ("Failed to allocate memory " ++ "for child output."); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } else { ++ memcpy (out, read_buf, rlen); ++ } ++ ++ if (stdout_data != NULL) { ++ *stdout_data = out; ++ } else { ++ free (out); ++ } ++ ++ if (stdout_data_len != NULL) { ++ *stdout_data_len = rlen; ++ } ++ } ++ ++ } else { ++ _adcli_err ("Cannot run net command."); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ ++ ret = ADCLI_SUCCESS; ++ ++done: ++ if (pipefd_from_child[0] != -1) { ++ close (pipefd_from_child[0]); ++ } ++ if (pipefd_from_child[1] != -1) { ++ close (pipefd_from_child[1]); ++ } ++ if (pipefd_to_child[0] != -1) { ++ close (pipefd_to_child[0]); ++ } ++ if (pipefd_to_child[1] != -1) { ++ close (pipefd_to_child[1]); ++ } ++ ++ if (child_pid > 0) { ++ wret = waitpid (child_pid, &status, 0); ++ if (wret == -1) { ++ _adcli_err ("No sure what happend to net command."); ++ } else { ++ if (WIFEXITED (status)) { ++ _adcli_err ("net command failed with %d.", ++ WEXITSTATUS (status)); ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++ + #ifdef UTIL_TESTS + + #include "test.h" +@@ -620,6 +776,60 @@ test_bin_sid_to_str (void) + free (str); + } + ++static void ++test_call_external_program (void) ++{ ++ adcli_result res; ++ char *argv[] = { NULL, NULL, NULL }; ++ uint8_t *stdout_data; ++ size_t stdout_data_len; ++ ++ argv[0] = "/does/not/exists"; ++ res = _adcli_call_external_program (argv[0], argv, NULL, NULL, NULL); ++ assert (res == ADCLI_ERR_FAIL); ++ ++#ifdef BIN_CAT ++ argv[0] = BIN_CAT; ++ res = _adcli_call_external_program (argv[0], argv, "Hello", ++ &stdout_data, &stdout_data_len); ++ assert (res == ADCLI_SUCCESS); ++ assert (strncmp ("Hello", (char *) stdout_data, stdout_data_len) == 0); ++ free (stdout_data); ++ ++ res = _adcli_call_external_program (argv[0], argv, "Hello", ++ NULL, NULL); ++ assert (res == ADCLI_SUCCESS); ++#endif ++ ++#ifdef BIN_REV ++ argv[0] = BIN_REV; ++ res = _adcli_call_external_program (argv[0], argv, "Hello\n", ++ &stdout_data, &stdout_data_len); ++ assert (res == ADCLI_SUCCESS); ++ assert (strncmp ("olleH\n", (char *) stdout_data, stdout_data_len) == 0); ++ free (stdout_data); ++#endif ++ ++#ifdef BIN_TAC ++ argv[0] = BIN_TAC; ++ res = _adcli_call_external_program (argv[0], argv, "Hello\nWorld\n", ++ &stdout_data, &stdout_data_len); ++ assert (res == ADCLI_SUCCESS); ++ assert (strncmp ("World\nHello\n", (char *) stdout_data, stdout_data_len) == 0); ++ free (stdout_data); ++#endif ++ ++#ifdef BIN_ECHO ++ argv[0] = BIN_ECHO; ++ argv[1] = "Hello"; ++ res = _adcli_call_external_program (argv[0], argv, NULL, ++ &stdout_data, &stdout_data_len); ++ assert (res == ADCLI_SUCCESS); ++ assert (strncmp ("Hello\n", (char *) stdout_data, stdout_data_len) == 0); ++ free (stdout_data); ++#endif ++} ++ + int + main (int argc, + char *argv[]) +@@ -629,6 +839,7 @@ main (int argc, + test_func (test_strv_count, "/util/strv_count"); + test_func (test_check_nt_time_string_lifetime, "/util/check_nt_time_string_lifetime"); + test_func (test_bin_sid_to_str, "/util/bin_sid_to_str"); ++ test_func (test_call_external_program, "/util/call_external_program"); + return test_run (argc, argv); + } + +-- +2.14.4 + diff --git a/0010-library-add-_adcli_ldap_parse_sid.patch b/0010-library-add-_adcli_ldap_parse_sid.patch new file mode 100644 index 0000000..6e4d719 --- /dev/null +++ b/0010-library-add-_adcli_ldap_parse_sid.patch @@ -0,0 +1,69 @@ +From bab08d90162c9146c1b4e8373f4b08209b84768c Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:44:45 +0100 +Subject: [PATCH 10/23] library: add _adcli_ldap_parse_sid() + +Get a binary SID from a LDAP message and return it in the string +representation. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + library/adldap.c | 24 ++++++++++++++++++++++++ + library/adprivate.h | 4 ++++ + 2 files changed, 28 insertions(+) + +diff --git a/library/adldap.c b/library/adldap.c +index 7c7a01b..07dc373 100644 +--- a/library/adldap.c ++++ b/library/adldap.c +@@ -67,6 +67,30 @@ _adcli_ldap_handle_failure (LDAP *ldap, + return defres; + } + ++char * ++_adcli_ldap_parse_sid (LDAP *ldap, ++ LDAPMessage *results, ++ const char *attr_name) ++{ ++ LDAPMessage *entry; ++ struct berval **bvs; ++ char *val = NULL; ++ ++ entry = ldap_first_entry (ldap, results); ++ if (entry != NULL) { ++ bvs = ldap_get_values_len (ldap, entry, attr_name); ++ if (bvs != NULL) { ++ if (bvs[0]) { ++ val = _adcli_bin_sid_to_str ( (uint8_t *) bvs[0]->bv_val, ++ bvs[0]->bv_len); ++ return_val_if_fail (val != NULL, NULL); ++ } ++ ldap_value_free_len (bvs); ++ } ++ } ++ ++ return val; ++} + + char * + _adcli_ldap_parse_value (LDAP *ldap, +diff --git a/library/adprivate.h b/library/adprivate.h +index 7257c93..83a88f6 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -174,6 +174,10 @@ adcli_result _adcli_ldap_handle_failure (LDAP *ldap, + const char *desc, + ...) GNUC_PRINTF(3, 4); + ++char * _adcli_ldap_parse_sid (LDAP *ldap, ++ LDAPMessage *results, ++ const char *attr_name); ++ + char * _adcli_ldap_parse_value (LDAP *ldap, + LDAPMessage *results, + const char *attr_name); +-- +2.14.4 + diff --git a/0011-library-add-lookup_domain_sid.patch b/0011-library-add-lookup_domain_sid.patch new file mode 100644 index 0000000..4534269 --- /dev/null +++ b/0011-library-add-lookup_domain_sid.patch @@ -0,0 +1,71 @@ +From 3fa854b1439c039a2250cb24efadae6a66b0e9da Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:40:46 +0100 +Subject: [PATCH 11/23] library: add lookup_domain_sid() + +Read the domain SID from the default naming context AD object and store +it in adcli_conn. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + library/adconn.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/library/adconn.c b/library/adconn.c +index 67bdfd9..6b84b88 100644 +--- a/library/adconn.c ++++ b/library/adconn.c +@@ -72,6 +72,7 @@ struct _adcli_conn_ctx { + char *domain_controller; + char *canonical_host; + char *domain_short; ++ char *domain_sid; + adcli_disco *domain_disco; + char *default_naming_context; + char *configuration_naming_context; +@@ -1068,6 +1069,32 @@ lookup_short_name (adcli_conn *conn) + } + } + ++static void ++lookup_domain_sid (adcli_conn *conn) ++{ ++ char *attrs[] = { "objectSid", NULL, }; ++ LDAPMessage *results; ++ int ret; ++ ++ free (conn->domain_sid); ++ conn->domain_sid = NULL; ++ ++ ret = ldap_search_ext_s (conn->ldap, conn->default_naming_context, LDAP_SCOPE_BASE, ++ NULL, attrs, 0, NULL, NULL, NULL, -1, &results); ++ if (ret == LDAP_SUCCESS) { ++ conn->domain_sid = _adcli_ldap_parse_sid (conn->ldap, results, "objectSid"); ++ ldap_msgfree (results); ++ ++ if (conn->domain_sid) ++ _adcli_info ("Looked up domain SID: %s", conn->domain_sid); ++ else ++ _adcli_err ("No domain SID found"); ++ } else { ++ _adcli_ldap_handle_failure (conn->ldap, ADCLI_ERR_DIRECTORY, ++ "Couldn't lookup domain SID"); ++ } ++} ++ + static void + conn_clear_state (adcli_conn *conn) + { +@@ -1148,6 +1175,7 @@ adcli_conn_connect (adcli_conn *conn) + return res; + + lookup_short_name (conn); ++ lookup_domain_sid (conn); + return ADCLI_SUCCESS; + } + +-- +2.14.4 + diff --git a/0012-library-add-adcli_conn_get_domain_sid.patch b/0012-library-add-adcli_conn_get_domain_sid.patch new file mode 100644 index 0000000..c05aaef --- /dev/null +++ b/0012-library-add-adcli_conn_get_domain_sid.patch @@ -0,0 +1,61 @@ +From f98c4f92091f6a68f390078f73be3bb6ca6e6550 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 18:23:03 +0100 +Subject: [PATCH 12/23] library: add adcli_conn_get_domain_sid() + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + library/adconn.c | 8 ++++++++ + library/adconn.h | 2 ++ + tools/computer.c | 1 + + 3 files changed, 11 insertions(+) + +diff --git a/library/adconn.c b/library/adconn.c +index 6b84b88..d2fb1d5 100644 +--- a/library/adconn.c ++++ b/library/adconn.c +@@ -1355,6 +1355,14 @@ adcli_conn_get_domain_short (adcli_conn *conn) + return conn->domain_short; + } + ++const char * ++adcli_conn_get_domain_sid (adcli_conn *conn) ++{ ++ return_val_if_fail (conn != NULL, NULL); ++ return conn->domain_sid; ++} ++ ++ + LDAP * + adcli_conn_get_ldap_connection (adcli_conn *conn) + { +diff --git a/library/adconn.h b/library/adconn.h +index ed1cc58..13cfd32 100644 +--- a/library/adconn.h ++++ b/library/adconn.h +@@ -91,6 +91,8 @@ void adcli_conn_set_domain_controller (adcli_conn *conn, + + const char * adcli_conn_get_domain_short (adcli_conn *conn); + ++const char * adcli_conn_get_domain_sid (adcli_conn *conn); ++ + LDAP * adcli_conn_get_ldap_connection (adcli_conn *conn); + + krb5_context adcli_conn_get_krb5_context (adcli_conn *conn); +diff --git a/tools/computer.c b/tools/computer.c +index d8a58c9..a3d0f03 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -43,6 +43,7 @@ dump_details (adcli_conn *conn, + printf ("domain-realm = %s\n", adcli_conn_get_domain_realm (conn)); + printf ("domain-controller = %s\n", adcli_conn_get_domain_controller (conn)); + printf ("domain-short = %s\n", adcli_conn_get_domain_short (conn)); ++ printf ("domain-SID = %s\n", adcli_conn_get_domain_sid (conn)); + printf ("naming-context = %s\n", adcli_conn_get_default_naming_context (conn)); + printf ("domain-ou = %s\n", adcli_enroll_get_domain_ou (enroll)); + +-- +2.14.4 + diff --git a/0013-tools-add-option-add-samba-data.patch b/0013-tools-add-option-add-samba-data.patch new file mode 100644 index 0000000..6e5c872 --- /dev/null +++ b/0013-tools-add-option-add-samba-data.patch @@ -0,0 +1,142 @@ +From d362a0799618b576918f5c5d0625565484670ba2 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:46:00 +0100 +Subject: [PATCH 13/23] tools: add option --add-samba-data + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + doc/adcli.xml | 30 ++++++++++++++++++++++++++++++ + library/adenroll.h | 1 + + tools/computer.c | 12 ++++++++++++ + 3 files changed, 43 insertions(+) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index c54cc1b..fbc6c63 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -292,6 +292,21 @@ Password for Administrator: + machine account password. This is output in a format that should + be both human and machine readable. + ++ ++ ++ After a successful join add the domain ++ SID and the machine account password to the Samba ++ specific databases by calling Samba's ++ net utility. ++ ++ Please note that Samba's net ++ requires some settings in smb.conf ++ to create the database entries correctly. Most ++ important here is currently the ++ option, see ++ smb.conf5 ++ for details. ++ + + + +@@ -382,6 +397,21 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + about join operation. This is output in a format that should + be both human and machine readable. + ++ ++ ++ After a successful join add the domain ++ SID and the machine account password to the Samba ++ specific databases by calling Samba's ++ net utility. ++ ++ Please note that Samba's net ++ requires some settings in smb.conf ++ to create the database entries correctly. Most ++ important here is currently the ++ option, see ++ smb.conf5 ++ for details. ++ + + + +diff --git a/library/adenroll.h b/library/adenroll.h +index 9a107ab..32c9764 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -30,6 +30,7 @@ typedef enum { + ADCLI_ENROLL_NO_KEYTAB = 1 << 1, + ADCLI_ENROLL_ALLOW_OVERWRITE = 1 << 2, + ADCLI_ENROLL_PASSWORD_VALID = 1 << 3, ++ ADCLI_ENROLL_ADD_SAMBA_DATA = 1 << 3, + } adcli_enroll_flags; + + typedef struct _adcli_enroll adcli_enroll; +diff --git a/tools/computer.c b/tools/computer.c +index a3d0f03..fc646f2 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -106,6 +106,7 @@ typedef enum { + opt_os_service_pack, + opt_user_principal, + opt_computer_password_lifetime, ++ opt_add_samba_data, + } Option; + + static adcli_tool_desc common_usages[] = { +@@ -142,6 +143,8 @@ static adcli_tool_desc common_usages[] = { + "a successful join" }, + { opt_show_password, "show computer account password after after a\n" + "successful join" }, ++ { opt_add_samba_data, "add domain SID and computer account password\n" ++ "to the Samba specific configuration database" }, + { opt_verbose, "show verbose progress and failure messages", }, + { 0 }, + }; +@@ -269,6 +272,7 @@ parse_option (Option opt, + case opt_show_details: + case opt_show_password: + case opt_one_time_password: ++ case opt_add_samba_data: + assert (0 && "not reached"); + break; + } +@@ -326,6 +330,7 @@ adcli_tool_computer_join (adcli_conn *conn, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, ++ { "add-samba-data", no_argument, NULL, opt_add_samba_data }, + { "verbose", no_argument, NULL, opt_verbose }, + { "help", no_argument, NULL, 'h' }, + { 0 }, +@@ -352,6 +357,9 @@ adcli_tool_computer_join (adcli_conn *conn, + case opt_show_password: + show_password = 1; + break; ++ case opt_add_samba_data: ++ flags |= ADCLI_ENROLL_ADD_SAMBA_DATA; ++ break; + case 'h': + case '?': + case ':': +@@ -425,6 +433,7 @@ adcli_tool_computer_update (adcli_conn *conn, + { "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, ++ { "add-samba-data", no_argument, NULL, opt_add_samba_data }, + { "verbose", no_argument, NULL, opt_verbose }, + { "help", no_argument, NULL, 'h' }, + { 0 }, +@@ -447,6 +456,9 @@ adcli_tool_computer_update (adcli_conn *conn, + case opt_show_password: + show_password = 1; + break; ++ case opt_add_samba_data: ++ flags |= ADCLI_ENROLL_ADD_SAMBA_DATA; ++ break; + case 'h': + case '?': + case ':': +-- +2.14.4 + diff --git a/0014-tools-store-Samba-data-if-requested.patch b/0014-tools-store-Samba-data-if-requested.patch new file mode 100644 index 0000000..33f8bff --- /dev/null +++ b/0014-tools-store-Samba-data-if-requested.patch @@ -0,0 +1,75 @@ +From c090131e4f912f6f6c4f79eb40fbe500eb31c171 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 18:24:15 +0100 +Subject: [PATCH 14/23] tools: store Samba data if requested + +Use Samba's net utility to add the machine account password and the +domain SID to the Samba configuration. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + library/adenroll.c | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/library/adenroll.c b/library/adenroll.c +index bb970d1..20731cd 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1533,6 +1533,36 @@ update_keytab_for_principals (adcli_enroll *enroll) + return ADCLI_SUCCESS; + } + ++static adcli_result ++update_samba_data (adcli_enroll *enroll) ++{ ++ int ret; ++ char *argv_pw[] = { "/usr/bin/net", "changesecretpw", "-i", "-f", NULL }; ++ char *argv_sid[] = { "/usr/bin/net", "setdomainsid", NULL, NULL }; ++ ++ _adcli_info ("Trying to set Samba secret.\n"); ++ ret = _adcli_call_external_program (argv_pw[0], argv_pw, ++ enroll->computer_password, NULL, NULL); ++ if (ret != ADCLI_SUCCESS) { ++ _adcli_err ("Failed to set Samba computer account password.\n"); ++ } ++ ++ argv_sid[2] = (char *) adcli_conn_get_domain_sid (enroll->conn); ++ if (argv_sid[2] == NULL) { ++ _adcli_err ("Domain SID not available.\n"); ++ } else { ++ _adcli_info ("Trying to set domain SID %s for Samba.\n", ++ argv_sid[2]); ++ ret = _adcli_call_external_program (argv_sid[0], argv_sid, ++ NULL, NULL, NULL); ++ if (ret != ADCLI_SUCCESS) { ++ _adcli_err ("Failed to set Samba domain SID.\n"); ++ } ++ } ++ ++ return ret; ++} ++ + static void + enroll_clear_state (adcli_enroll *enroll) + { +@@ -1687,6 +1717,15 @@ enroll_join_or_update_tasks (adcli_enroll *enroll, + update_computer_account (enroll); + update_service_principals (enroll); + ++ if ( (flags & ADCLI_ENROLL_ADD_SAMBA_DATA) && ! (flags & ADCLI_ENROLL_PASSWORD_VALID)) { ++ res = update_samba_data (enroll); ++ if (res != ADCLI_SUCCESS) { ++ _adcli_info ("Failed to add Samba specific data, smbd " ++ "or winbindd might not work as " ++ "expected.\n"); ++ } ++ } ++ + if (flags & ADCLI_ENROLL_NO_KEYTAB) + return ADCLI_SUCCESS; + +-- +2.14.4 + diff --git a/0015-make-Samba-data-tool-configurable.patch b/0015-make-Samba-data-tool-configurable.patch new file mode 100644 index 0000000..714cae5 --- /dev/null +++ b/0015-make-Samba-data-tool-configurable.patch @@ -0,0 +1,292 @@ +From 9b73f79a2436760b8278377014bf78a144a427ae Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 1 Feb 2018 14:26:22 +0100 +Subject: [PATCH 15/23] make Samba data tool configurable + +Allow to specify an alternative path to Samba's net utility at configure +time and at run time. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + configure.ac | 13 ++++++++++++ + doc/adcli.xml | 21 ++++++++++++++++++- + doc/samba_data_tool_path.xml.in | 1 + + library/adenroll.c | 46 ++++++++++++++++++++++++++++++++++------- + library/adenroll.h | 5 +++++ + tools/computer.c | 16 ++++++++++++++ + 7 files changed, 95 insertions(+), 8 deletions(-) + create mode 100644 doc/samba_data_tool_path.xml.in + +diff --git a/configure.ac b/configure.ac +index fe86638..68877c7 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -291,6 +291,18 @@ else + AC_DEFINE_UNQUOTED(BIN_ECHO, "$BIN_ECHO", [path to echo, used in unit test]) + fi + ++AC_MSG_CHECKING([where is Samba's net utility]) ++AC_ARG_WITH([samba_data_tool], ++ AC_HELP_STRING([--with-samba-data-tool=/path], ++ [Path to Samba's net utility]), ++ [], ++ [with_samba_data_tool=/usr/bin/net]) ++AC_MSG_RESULT([$with_samba_data_tool]) ++ ++AC_DEFINE_UNQUOTED(SAMBA_DATA_TOOL, "$with_samba_data_tool", ++ [Path to Samba's net utility]) ++ ++AC_SUBST(SAMBA_DATA_TOOL, [$with_samba_data_tool]) + # --------------------------------------------------------------------- + + ADCLI_LT_RELEASE=$ADCLI_CURRENT:$ADCLI_REVISION:$ADCLI_AGE +@@ -300,6 +312,7 @@ AC_CONFIG_FILES([Makefile + build/Makefile + doc/Makefile + doc/version.xml ++ doc/samba_data_tool_path.xml + library/Makefile + tools/Makefile + ]) +diff --git a/doc/adcli.xml b/doc/adcli.xml +index fbc6c63..c2b7760 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -1,6 +1,9 @@ + + ++ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" ++[ ++ ++]> + + + +@@ -307,6 +310,14 @@ Password for Administrator: + smb.conf5 + for details. + ++ ++ ++ If Samba's net ++ cannot be found at ++ &samba_data_tool; this option can ++ be used to specific an alternative location with the ++ help of an absolute path. ++ + + + +@@ -412,6 +423,14 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + smb.conf5 + for details. + ++ ++ ++ If Samba's net ++ cannot be found at ++ &samba_data_tool; this option can ++ be used to specific an alternative location with the ++ help of an absolute path. ++ + + + +diff --git a/doc/samba_data_tool_path.xml.in b/doc/samba_data_tool_path.xml.in +new file mode 100644 +index 0000000..a667c57 +--- /dev/null ++++ b/doc/samba_data_tool_path.xml.in +@@ -0,0 +1 @@ ++@SAMBA_DATA_TOOL@ +diff --git a/library/adenroll.c b/library/adenroll.c +index 20731cd..a693049 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -42,6 +42,10 @@ + #include + #include + ++#ifndef SAMBA_DATA_TOOL ++#define SAMBA_DATA_TOOL "/usr/bin/net" ++#endif ++ + static krb5_enctype v60_later_enctypes[] = { + ENCTYPE_AES256_CTS_HMAC_SHA1_96, + ENCTYPE_AES128_CTS_HMAC_SHA1_96, +@@ -100,6 +104,7 @@ struct _adcli_enroll { + int keytab_enctypes_explicit; + unsigned int computer_password_lifetime; + int computer_password_lifetime_explicit; ++ char *samba_data_tool; + }; + + static adcli_result +@@ -1537,26 +1542,33 @@ static adcli_result + update_samba_data (adcli_enroll *enroll) + { + int ret; +- char *argv_pw[] = { "/usr/bin/net", "changesecretpw", "-i", "-f", NULL }; +- char *argv_sid[] = { "/usr/bin/net", "setdomainsid", NULL, NULL }; ++ char *argv_pw[] = { NULL, "changesecretpw", "-i", "-f", NULL }; ++ char *argv_sid[] = { NULL, "setdomainsid", NULL, NULL }; ++ ++ argv_pw[0] = (char *) adcli_enroll_get_samba_data_tool (enroll); ++ if (argv_pw[0] ==NULL) { ++ _adcli_err ("Samba data tool not available."); ++ return ADCLI_ERR_FAIL; ++ } ++ argv_sid[0] = argv_pw[0]; + +- _adcli_info ("Trying to set Samba secret.\n"); ++ _adcli_info ("Trying to set Samba secret."); + ret = _adcli_call_external_program (argv_pw[0], argv_pw, + enroll->computer_password, NULL, NULL); + if (ret != ADCLI_SUCCESS) { +- _adcli_err ("Failed to set Samba computer account password.\n"); ++ _adcli_err ("Failed to set Samba computer account password."); + } + + argv_sid[2] = (char *) adcli_conn_get_domain_sid (enroll->conn); + if (argv_sid[2] == NULL) { +- _adcli_err ("Domain SID not available.\n"); ++ _adcli_err ("Domain SID not available."); + } else { +- _adcli_info ("Trying to set domain SID %s for Samba.\n", ++ _adcli_info ("Trying to set domain SID %s for Samba.", + argv_sid[2]); + ret = _adcli_call_external_program (argv_sid[0], argv_sid, + NULL, NULL, NULL); + if (ret != ADCLI_SUCCESS) { +- _adcli_err ("Failed to set Samba domain SID.\n"); ++ _adcli_err ("Failed to set Samba domain SID."); + } + } + +@@ -1951,6 +1963,9 @@ adcli_enroll_new (adcli_conn *conn) + enroll->os_name = strdup (value); + return_val_if_fail (enroll->os_name != NULL, NULL); + ++ enroll->samba_data_tool = strdup (SAMBA_DATA_TOOL); ++ return_val_if_fail (enroll->samba_data_tool != NULL, NULL); ++ + return enroll; + } + +@@ -1978,6 +1993,7 @@ enroll_free (adcli_enroll *enroll) + free (enroll->os_name); + free (enroll->os_version); + free (enroll->os_service_pack); ++ free (enroll->samba_data_tool); + + free (enroll->user_principal); + _adcli_strv_free (enroll->service_names); +@@ -2343,3 +2359,19 @@ adcli_enroll_set_computer_password_lifetime (adcli_enroll *enroll, + + enroll->computer_password_lifetime_explicit = 1; + } ++ ++void ++adcli_enroll_set_samba_data_tool (adcli_enroll *enroll, const char *value) ++{ ++ return_if_fail (enroll != NULL); ++ if (value != NULL && value[0] != '\0') { ++ _adcli_str_set (&enroll->samba_data_tool, value); ++ } ++} ++ ++const char * ++adcli_enroll_get_samba_data_tool (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, NULL); ++ return enroll->samba_data_tool; ++} +diff --git a/library/adenroll.h b/library/adenroll.h +index 32c9764..31ca0bc 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -141,4 +141,9 @@ const char * adcli_enroll_get_os_service_pack (adcli_enroll *enroll); + void adcli_enroll_set_os_service_pack (adcli_enroll *enroll, + const char *value); + ++void adcli_enroll_set_samba_data_tool (adcli_enroll *enroll, ++ const char *value); ++ ++const char * adcli_enroll_get_samba_data_tool (adcli_enroll *enroll); ++ + #endif /* ADENROLL_H_ */ +diff --git a/tools/computer.c b/tools/computer.c +index fc646f2..f86548b 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + + static void + dump_details (adcli_conn *conn, +@@ -107,6 +108,7 @@ typedef enum { + opt_user_principal, + opt_computer_password_lifetime, + opt_add_samba_data, ++ opt_samba_data_tool, + } Option; + + static adcli_tool_desc common_usages[] = { +@@ -145,6 +147,7 @@ static adcli_tool_desc common_usages[] = { + "successful join" }, + { opt_add_samba_data, "add domain SID and computer account password\n" + "to the Samba specific configuration database" }, ++ { opt_samba_data_tool, "Absolute path to the tool used for add-samba-data" }, + { opt_verbose, "show verbose progress and failure messages", }, + { 0 }, + }; +@@ -160,6 +163,7 @@ parse_option (Option opt, + static int stdin_password = 0; + char *endptr; + unsigned int lifetime; ++ int ret; + + switch (opt) { + case opt_login_ccache: +@@ -265,6 +269,16 @@ parse_option (Option opt, + + adcli_enroll_set_computer_password_lifetime (enroll, lifetime); + return; ++ case opt_samba_data_tool: ++ errno = 0; ++ ret = access (optarg, X_OK); ++ if (ret != 0) { ++ ret = errno; ++ errx (EUSAGE, "Failed to access tool to add Samba data: %s", strerror (ret)); ++ } else { ++ adcli_enroll_set_samba_data_tool (enroll, optarg); ++ } ++ return; + case opt_verbose: + return; + +@@ -331,6 +345,7 @@ adcli_tool_computer_join (adcli_conn *conn, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, ++ { "samba-data-tool", no_argument, NULL, opt_samba_data_tool }, + { "verbose", no_argument, NULL, opt_verbose }, + { "help", no_argument, NULL, 'h' }, + { 0 }, +@@ -434,6 +449,7 @@ adcli_tool_computer_update (adcli_conn *conn, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, ++ { "samba-data-tool", no_argument, NULL, opt_samba_data_tool }, + { "verbose", no_argument, NULL, opt_verbose }, + { "help", no_argument, NULL, 'h' }, + { 0 }, +-- +2.14.4 + diff --git a/0016-Add-trusted-for-delegation-option.patch b/0016-Add-trusted-for-delegation-option.patch new file mode 100644 index 0000000..122f5a2 --- /dev/null +++ b/0016-Add-trusted-for-delegation-option.patch @@ -0,0 +1,247 @@ +From f306f2f20c1d35fac63d27147824f039f7ef2d67 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 31 May 2018 18:27:37 +0200 +Subject: [PATCH 16/23] Add trusted-for-delegation option + +Resolves https://bugzilla.redhat.com/show_bug.cgi?id=1538730 +--- + doc/adcli.xml | 14 ++++++++++ + library/adenroll.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- + library/adenroll.h | 4 +++ + tools/computer.c | 12 ++++++++ + 4 files changed, 108 insertions(+), 2 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index c2b7760..b246190 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -283,6 +283,13 @@ Password for Administrator: + and providing a + password as input. + ++ ++ ++ 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. ++ + + + After a successful join print out information +@@ -402,6 +409,13 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + in days. By default the password is updated if it is + older than 30 days. + ++ ++ ++ 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. ++ + + + After a successful join print out information +diff --git a/library/adenroll.c b/library/adenroll.c +index a693049..eca3c37 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -63,6 +63,13 @@ static krb5_enctype v51_earlier_enctypes[] = { + 0 + }; + ++/* Some constants for the userAccountControl AD LDAP attribute, see e.g. ++ * https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro ++ * for details. */ ++#define UAC_WORKSTATION_TRUST_ACCOUNT 0x1000 ++#define UAC_DONT_EXPIRE_PASSWORD 0x10000 ++#define UAC_TRUSTED_FOR_DELEGATION 0x80000 ++ + struct _adcli_enroll { + int refs; + adcli_conn *conn; +@@ -105,6 +112,7 @@ struct _adcli_enroll { + unsigned int computer_password_lifetime; + int computer_password_lifetime_explicit; + char *samba_data_tool; ++ bool trusted_for_delegation; + }; + + static adcli_result +@@ -538,6 +546,10 @@ create_computer_account (adcli_enroll *enroll, + NULL, + }; + ++ if (adcli_enroll_get_trusted_for_delegation (enroll)) { ++ vals_userAccountControl[0] = "593920"; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD | TRUSTED_FOR_DELEGATION */ ++ } ++ + ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL); + + /* +@@ -971,6 +983,7 @@ retrieve_computer_account (adcli_enroll *enroll) + "operatingSystemVersion", + "operatingSystemServicePack", + "pwdLastSet", ++ "userAccountControl", + NULL, + }; + +@@ -1149,6 +1162,47 @@ update_computer_attribute (adcli_enroll *enroll, + return res; + } + ++static char *get_user_account_control (adcli_enroll *enroll) ++{ ++ uint32_t uac = 0; ++ unsigned long attr_val; ++ char *uac_str; ++ LDAP *ldap; ++ char *end; ++ ++ ldap = adcli_conn_get_ldap_connection (enroll->conn); ++ return_val_if_fail (ldap != NULL, NULL); ++ ++ uac_str = _adcli_ldap_parse_value (ldap, enroll->computer_attributes, "userAccountControl"); ++ if (uac_str != NULL) { ++ ++ attr_val = strtoul (uac_str, &end, 10); ++ if (*end != '\0' || attr_val > UINT32_MAX) { ++ _adcli_warn ("Invalid userAccountControl '%s' for computer account in directory: %s, assuming 0", ++ uac_str, enroll->computer_dn); ++ } else { ++ uac = attr_val; ++ } ++ free (uac_str); ++ } ++ ++ if (uac == 0) { ++ uac = UAC_WORKSTATION_TRUST_ACCOUNT | UAC_DONT_EXPIRE_PASSWORD; ++ } ++ ++ if (adcli_enroll_get_trusted_for_delegation (enroll)) { ++ uac |= UAC_TRUSTED_FOR_DELEGATION; ++ } else { ++ uac &= ~(UAC_TRUSTED_FOR_DELEGATION); ++ } ++ ++ if (asprintf (&uac_str, "%d", uac) < 0) { ++ return_val_if_reached (NULL); ++ } ++ ++ return uac_str; ++} ++ + static void + update_computer_account (adcli_enroll *enroll) + { +@@ -1167,11 +1221,16 @@ update_computer_account (adcli_enroll *enroll) + } + + if (res == ADCLI_SUCCESS) { +- char *vals_userAccountControl[] = { "69632", NULL }; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD */ ++ char *vals_userAccountControl[] = { NULL , NULL }; + LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } }; + LDAPMod *mods[] = { &userAccountControl, NULL }; + +- res |= update_computer_attribute (enroll, ldap, mods); ++ vals_userAccountControl[0] = get_user_account_control (enroll); ++ if (vals_userAccountControl[0] != NULL) { ++ res |= update_computer_attribute (enroll, ldap, mods); ++ } else { ++ _adcli_warn ("Cannot update userAccountControl"); ++ } + } + + if (res == ADCLI_SUCCESS) { +@@ -2375,3 +2434,20 @@ adcli_enroll_get_samba_data_tool (adcli_enroll *enroll) + return_val_if_fail (enroll != NULL, NULL); + return enroll->samba_data_tool; + } ++ ++bool ++adcli_enroll_get_trusted_for_delegation (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, false); ++ ++ return enroll->trusted_for_delegation; ++} ++ ++void ++adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, ++ bool value) ++{ ++ return_if_fail (enroll != NULL); ++ ++ enroll->trusted_for_delegation = value; ++} +diff --git a/library/adenroll.h b/library/adenroll.h +index 31ca0bc..be2ca18 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -109,6 +109,10 @@ unsigned int adcli_enroll_get_computer_password_lifetime (adcli_enroll *en + void adcli_enroll_set_computer_password_lifetime (adcli_enroll *enroll, + unsigned int lifetime); + ++bool adcli_enroll_get_trusted_for_delegation (adcli_enroll *enroll); ++void adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, ++ bool value); ++ + krb5_kvno adcli_enroll_get_kvno (adcli_enroll *enroll); + + void adcli_enroll_set_kvno (adcli_enroll *enroll, +diff --git a/tools/computer.c b/tools/computer.c +index f86548b..b905fd1 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -109,6 +109,7 @@ typedef enum { + opt_computer_password_lifetime, + opt_add_samba_data, + opt_samba_data_tool, ++ opt_trusted_for_delegation, + } Option; + + static adcli_tool_desc common_usages[] = { +@@ -135,6 +136,8 @@ static adcli_tool_desc common_usages[] = { + { opt_os_service_pack, "the computer operating system service pack", }, + { opt_user_principal, "add an authentication principal to the account", }, + { opt_computer_password_lifetime, "lifetime of the host accounts password in days", }, ++ { opt_trusted_for_delegation, "set/unset the TRUSTED_FOR_DELEGATION flag\n" ++ "in the userAccountControl attribute", }, + { opt_no_password, "don't prompt for or read a password" }, + { opt_prompt_password, "prompt for a password if necessary" }, + { opt_stdin_password, "read a password from stdin (until EOF) if\n" +@@ -279,6 +282,13 @@ parse_option (Option opt, + adcli_enroll_set_samba_data_tool (enroll, optarg); + } + return; ++ case opt_trusted_for_delegation: ++ if (strcasecmp (optarg, "true") == 0 || strcasecmp (optarg, "yes") == 0) { ++ adcli_enroll_set_trusted_for_delegation (enroll, true); ++ } else { ++ adcli_enroll_set_trusted_for_delegation (enroll, false); ++ } ++ return; + case opt_verbose: + return; + +@@ -342,6 +352,7 @@ adcli_tool_computer_join (adcli_conn *conn, + { "os-version", required_argument, NULL, opt_os_version }, + { "os-service-pack", optional_argument, NULL, opt_os_service_pack }, + { "user-principal", optional_argument, NULL, opt_user_principal }, ++ { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, +@@ -446,6 +457,7 @@ adcli_tool_computer_update (adcli_conn *conn, + { "os-service-pack", optional_argument, NULL, opt_os_service_pack }, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime }, ++ { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, +-- +2.14.4 + diff --git a/0017-Only-update-attributes-given-on-the-command-line.patch b/0017-Only-update-attributes-given-on-the-command-line.patch new file mode 100644 index 0000000..2ee6a6f --- /dev/null +++ b/0017-Only-update-attributes-given-on-the-command-line.patch @@ -0,0 +1,126 @@ +From 27c7dde2c0e84c3bb610d1aadb0fd8faff70d3fa Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 1 Jun 2018 21:26:47 +0200 +Subject: [PATCH 17/23] 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 eca3c37..ee845ef 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -99,8 +99,11 @@ struct _adcli_enroll { + int user_princpal_generate; + + char *os_name; ++ int os_name_explicit; + char *os_version; ++ int os_version_explicit; + char *os_service_pack; ++ int os_service_pack_explicit; + + krb5_kvno kvno; + char *keytab_name; +@@ -113,6 +116,7 @@ struct _adcli_enroll { + int computer_password_lifetime_explicit; + char *samba_data_tool; + bool trusted_for_delegation; ++ int trusted_for_delegation_explicit; + }; + + static adcli_result +@@ -1212,7 +1216,11 @@ update_computer_account (adcli_enroll *enroll) + ldap = adcli_conn_get_ldap_connection (enroll->conn); + return_if_fail (ldap != NULL); + +- { ++ /* Only update attributes which are explicitly given on the command ++ * line. Otherwise 'adcli update' must be always called with the same ++ * set of options to make sure existing attributes are not deleted or ++ * overwritten with different values. */ ++ if (enroll->host_fqdn_explicit) { + char *vals_dNSHostName[] = { enroll->host_fqdn, NULL }; + LDAPMod dNSHostName = { LDAP_MOD_REPLACE, "dNSHostName", { vals_dNSHostName, } }; + LDAPMod *mods[] = { &dNSHostName, NULL }; +@@ -1220,7 +1228,7 @@ update_computer_account (adcli_enroll *enroll) + res |= update_computer_attribute (enroll, ldap, mods); + } + +- if (res == ADCLI_SUCCESS) { ++ if (res == ADCLI_SUCCESS && enroll->trusted_for_delegation_explicit) { + char *vals_userAccountControl[] = { NULL , NULL }; + LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } }; + LDAPMod *mods[] = { &userAccountControl, NULL }; +@@ -1240,12 +1248,25 @@ update_computer_account (adcli_enroll *enroll) + LDAPMod operatingSystemVersion = { LDAP_MOD_REPLACE, "operatingSystemVersion", { vals_operatingSystemVersion, } }; + char *vals_operatingSystemServicePack[] = { enroll->os_service_pack, NULL }; + LDAPMod operatingSystemServicePack = { LDAP_MOD_REPLACE, "operatingSystemServicePack", { vals_operatingSystemServicePack, } }; +- LDAPMod *mods[] = { &operatingSystem, &operatingSystemVersion, &operatingSystemServicePack, NULL }; ++ LDAPMod *mods[] = { NULL, NULL, NULL, NULL }; ++ size_t c = 0; + +- res |= update_computer_attribute (enroll, ldap, mods); ++ if (enroll->os_name_explicit) { ++ mods[c++] = &operatingSystem; ++ } ++ if (enroll->os_version_explicit) { ++ mods[c++] = &operatingSystemVersion; ++ } ++ if (enroll->os_service_pack_explicit) { ++ mods[c++] = &operatingSystemServicePack; ++ } ++ ++ if (c != 0) { ++ res |= update_computer_attribute (enroll, ldap, mods); ++ } + } + +- if (res == ADCLI_SUCCESS) { ++ if (res == ADCLI_SUCCESS && !enroll->user_princpal_generate) { + char *vals_userPrincipalName[] = { enroll->user_principal, NULL }; + LDAPMod userPrincipalName = { LDAP_MOD_REPLACE, "userPrincipalName", { vals_userPrincipalName, }, }; + LDAPMod *mods[] = { &userPrincipalName, NULL, }; +@@ -2337,6 +2358,7 @@ adcli_enroll_set_os_name (adcli_enroll *enroll, + if (value && value[0] == '\0') + value = NULL; + _adcli_str_set (&enroll->os_name, value); ++ enroll->os_name_explicit = 1; + } + + const char * +@@ -2354,6 +2376,7 @@ adcli_enroll_set_os_version (adcli_enroll *enroll, + if (value && value[0] == '\0') + value = NULL; + _adcli_str_set (&enroll->os_version, value); ++ enroll->os_version_explicit = 1; + } + + const char * +@@ -2371,6 +2394,7 @@ adcli_enroll_set_os_service_pack (adcli_enroll *enroll, + if (value && value[0] == '\0') + value = NULL; + _adcli_str_set (&enroll->os_service_pack, value); ++ enroll->os_service_pack_explicit = 1; + } + + const char * +@@ -2450,4 +2474,5 @@ adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, + return_if_fail (enroll != NULL); + + enroll->trusted_for_delegation = value; ++ enroll->trusted_for_delegation_explicit = 1; + } +-- +2.14.4 + diff --git a/0018-update-allow-to-add-service-names.patch b/0018-update-allow-to-add-service-names.patch new file mode 100644 index 0000000..2387ef2 --- /dev/null +++ b/0018-update-allow-to-add-service-names.patch @@ -0,0 +1,387 @@ +From 54c3d176326f719ffefded17bb797bc9e6c7f3c0 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 4 Jun 2018 10:49:33 +0200 +Subject: [PATCH 18/23] 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 ee845ef..6fdc773 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -305,13 +305,37 @@ ensure_service_names (adcli_result res, + } + + static adcli_result +-ensure_service_principals (adcli_result res, +- adcli_enroll *enroll) ++add_service_names_to_service_principals (adcli_enroll *enroll) + { + char *name; + int length = 0; + int i; + ++ if (enroll->service_principals != NULL) { ++ length = seq_count (enroll->service_principals); ++ } ++ ++ for (i = 0; enroll->service_names[i] != NULL; i++) { ++ if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0) ++ return_unexpected_if_reached (); ++ enroll->service_principals = _adcli_strv_add (enroll->service_principals, ++ name, &length); ++ ++ if (enroll->host_fqdn) { ++ if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0) ++ return_unexpected_if_reached (); ++ enroll->service_principals = _adcli_strv_add (enroll->service_principals, ++ name, &length); ++ } ++ } ++ ++ return ADCLI_SUCCESS; ++} ++ ++static adcli_result ++ensure_service_principals (adcli_result res, ++ adcli_enroll *enroll) ++{ + if (res != ADCLI_SUCCESS) + return res; + +@@ -319,20 +343,7 @@ ensure_service_principals (adcli_result res, + + if (!enroll->service_principals) { + assert (enroll->service_names != NULL); +- +- for (i = 0; enroll->service_names[i] != NULL; i++) { +- if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0) +- return_unexpected_if_reached (); +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, +- name, &length); +- +- if (enroll->host_fqdn) { +- if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0) +- return_unexpected_if_reached (); +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, +- name, &length); +- } +- } ++ return add_service_names_to_service_principals (enroll); + } + + return ADCLI_SUCCESS; +@@ -356,6 +367,7 @@ ensure_keytab_principals (adcli_result res, + return_unexpected_if_fail (k5 != NULL); + + enroll->keytab_principals = calloc (count + 3, sizeof (krb5_principal)); ++ return_unexpected_if_fail (enroll->keytab_principals != NULL); + at = 0; + + /* First add the principal for the computer account name */ +@@ -1266,7 +1278,7 @@ update_computer_account (adcli_enroll *enroll) + } + } + +- if (res == ADCLI_SUCCESS && !enroll->user_princpal_generate) { ++ if (res == ADCLI_SUCCESS && enroll->user_principal != NULL && !enroll->user_princpal_generate) { + char *vals_userPrincipalName[] = { enroll->user_principal, NULL }; + LDAPMod userPrincipalName = { LDAP_MOD_REPLACE, "userPrincipalName", { vals_userPrincipalName, }, }; + LDAPMod *mods[] = { &userPrincipalName, NULL, }; +@@ -1519,7 +1531,8 @@ add_principal_to_keytab (adcli_enroll *enroll, + krb5_context k5, + krb5_principal principal, + const char *principal_name, +- int *which_salt) ++ int *which_salt, ++ adcli_enroll_flags flags) + { + match_principal_kvno closure; + krb5_data password; +@@ -1547,41 +1560,47 @@ add_principal_to_keytab (adcli_enroll *enroll, + enroll->keytab_name); + } + +- password.data = enroll->computer_password; +- password.length = strlen (enroll->computer_password); +- + enctypes = adcli_enroll_get_keytab_enctypes (enroll); + +- /* +- * So we need to discover which salt to use. As a side effect we are +- * also testing that our account works. +- */ ++ if (flags & ADCLI_ENROLL_PASSWORD_VALID) { ++ code = _adcli_krb5_keytab_copy_entries (k5, enroll->keytab, principal, ++ enroll->kvno, enctypes); ++ } else { + +- salts = build_principal_salts (enroll, k5, principal); +- return_unexpected_if_fail (salts != NULL); ++ password.data = enroll->computer_password; ++ password.length = strlen (enroll->computer_password); + +- if (*which_salt < 0) { +- code = _adcli_krb5_keytab_discover_salt (k5, principal, enroll->kvno, &password, +- enctypes, salts, which_salt); +- if (code != 0) { +- _adcli_warn ("Couldn't authenticate with keytab while discovering which salt to use: %s: %s", +- principal_name, krb5_get_error_message (k5, code)); +- *which_salt = DEFAULT_SALT; +- } else { +- assert (*which_salt >= 0); +- _adcli_info ("Discovered which keytab salt to use"); ++ /* ++ * So we need to discover which salt to use. As a side effect we are ++ * also testing that our account works. ++ */ ++ ++ salts = build_principal_salts (enroll, k5, principal); ++ return_unexpected_if_fail (salts != NULL); ++ ++ if (*which_salt < 0) { ++ code = _adcli_krb5_keytab_discover_salt (k5, principal, enroll->kvno, &password, ++ enctypes, salts, which_salt); ++ if (code != 0) { ++ _adcli_warn ("Couldn't authenticate with keytab while discovering which salt to use: %s: %s", ++ principal_name, krb5_get_error_message (k5, code)); ++ *which_salt = DEFAULT_SALT; ++ } else { ++ assert (*which_salt >= 0); ++ _adcli_info ("Discovered which keytab salt to use"); ++ } + } +- } + +- code = _adcli_krb5_keytab_add_entries (k5, enroll->keytab, principal, +- enroll->kvno, &password, enctypes, &salts[*which_salt]); ++ code = _adcli_krb5_keytab_add_entries (k5, enroll->keytab, principal, ++ enroll->kvno, &password, enctypes, &salts[*which_salt]); + +- free_principal_salts (k5, salts); ++ free_principal_salts (k5, salts); + +- if (code != 0) { +- _adcli_err ("Couldn't add keytab entries: %s: %s", +- enroll->keytab_name, krb5_get_error_message (k5, code)); +- return ADCLI_ERR_FAIL; ++ if (code != 0) { ++ _adcli_err ("Couldn't add keytab entries: %s: %s", ++ enroll->keytab_name, krb5_get_error_message (k5, code)); ++ return ADCLI_ERR_FAIL; ++ } + } + + +@@ -1591,7 +1610,8 @@ add_principal_to_keytab (adcli_enroll *enroll, + } + + static adcli_result +-update_keytab_for_principals (adcli_enroll *enroll) ++update_keytab_for_principals (adcli_enroll *enroll, ++ adcli_enroll_flags flags) + { + krb5_context k5; + adcli_result res; +@@ -1608,7 +1628,7 @@ update_keytab_for_principals (adcli_enroll *enroll) + if (krb5_unparse_name (k5, enroll->keytab_principals[i], &name) != 0) + name = ""; + res = add_principal_to_keytab (enroll, k5, enroll->keytab_principals[i], +- name, &which_salt); ++ name, &which_salt, flags); + krb5_free_unparsed_name (k5, name); + + if (res != ADCLI_SUCCESS) +@@ -1807,6 +1827,20 @@ enroll_join_or_update_tasks (adcli_enroll *enroll, + /* We ignore failures of setting these fields */ + update_and_calculate_enctypes (enroll); + update_computer_account (enroll); ++ ++ /* service_names is only set from input on the command line, so no ++ * additional check for explicit is needed here */ ++ if (enroll->service_names != NULL) { ++ res = add_service_names_to_service_principals (enroll); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } ++ res = ensure_keytab_principals (res, enroll); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } ++ } ++ + update_service_principals (enroll); + + if ( (flags & ADCLI_ENROLL_ADD_SAMBA_DATA) && ! (flags & ADCLI_ENROLL_PASSWORD_VALID)) { +@@ -1826,7 +1860,7 @@ enroll_join_or_update_tasks (adcli_enroll *enroll, + * that we use for salting. + */ + +- return update_keytab_for_principals (enroll); ++ return update_keytab_for_principals (enroll, flags); + } + + adcli_result +@@ -1927,7 +1961,11 @@ adcli_enroll_update (adcli_enroll *enroll, + + if (_adcli_check_nt_time_string_lifetime (value, + adcli_enroll_get_computer_password_lifetime (enroll))) { +- flags |= ADCLI_ENROLL_NO_KEYTAB; ++ /* Do not update keytab if neither new service principals have ++ * to be added nor the user principal has to be changed. */ ++ if (enroll->service_names == NULL && (enroll->user_principal == NULL || enroll->user_princpal_generate)) { ++ flags |= ADCLI_ENROLL_NO_KEYTAB; ++ } + flags |= ADCLI_ENROLL_PASSWORD_VALID; + } + free (value); +diff --git a/library/adkrb5.c b/library/adkrb5.c +index b0e903e..033c181 100644 +--- a/library/adkrb5.c ++++ b/library/adkrb5.c +@@ -204,6 +204,119 @@ _adcli_krb5_open_keytab (krb5_context k5, + return ADCLI_SUCCESS; + } + ++typedef struct { ++ krb5_kvno kvno; ++ krb5_enctype enctype; ++ int matched; ++} match_enctype_kvno; ++ ++static krb5_boolean ++match_enctype_and_kvno (krb5_context k5, ++ krb5_keytab_entry *entry, ++ void *data) ++{ ++ krb5_boolean similar = FALSE; ++ match_enctype_kvno *closure = data; ++ krb5_error_code code; ++ ++ assert (closure->enctype); ++ ++ code = krb5_c_enctype_compare (k5, closure->enctype, entry->key.enctype, ++ &similar); ++ ++ if (code == 0 && entry->vno == closure->kvno && similar) { ++ closure->matched = 1; ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static krb5_error_code ++_adcli_krb5_get_keyblock (krb5_context k5, ++ krb5_keytab keytab, ++ krb5_keyblock *keyblock, ++ krb5_boolean (* match_func) (krb5_context, ++ krb5_keytab_entry *, ++ void *), ++ void *match_data) ++{ ++ krb5_kt_cursor cursor; ++ krb5_keytab_entry entry; ++ krb5_error_code code; ++ ++ code = krb5_kt_start_seq_get (k5, keytab, &cursor); ++ if (code == KRB5_KT_END || code == ENOENT) ++ return 0; ++ else if (code != 0) ++ return code; ++ ++ for (;;) { ++ code = krb5_kt_next_entry (k5, keytab, &entry, &cursor); ++ if (code != 0) ++ break; ++ ++ /* See if we should remove this entry */ ++ if (!match_func (k5, &entry, match_data)) { ++ krb5_free_keytab_entry_contents (k5, &entry); ++ continue; ++ } ++ ++ code = krb5_copy_keyblock_contents (k5, &entry.key, keyblock); ++ krb5_free_keytab_entry_contents (k5, &entry); ++ break; ++ ++ ++ } ++ ++ if (code == KRB5_KT_END) ++ code = 0; ++ ++ krb5_kt_end_seq_get (k5, keytab, &cursor); ++ return code; ++} ++ ++krb5_error_code ++_adcli_krb5_keytab_copy_entries (krb5_context k5, ++ krb5_keytab keytab, ++ krb5_principal principal, ++ krb5_kvno kvno, ++ krb5_enctype *enctypes) ++{ ++ krb5_keytab_entry entry; ++ krb5_error_code code; ++ int i; ++ match_enctype_kvno closure; ++ ++ for (i = 0; enctypes[i] != 0; i++) { ++ ++ closure.kvno = kvno; ++ closure.enctype = enctypes[i]; ++ closure.matched = 0; ++ ++ memset (&entry, 0, sizeof (entry)); ++ ++ code = _adcli_krb5_get_keyblock (k5, keytab, &entry.key, ++ match_enctype_and_kvno, &closure); ++ if (code != 0) { ++ return code; ++ } ++ ++ ++ entry.principal = principal; ++ entry.vno = kvno; ++ ++ code = krb5_kt_add_entry (k5, keytab, &entry); ++ ++ entry.principal = NULL; ++ krb5_free_keytab_entry_contents (k5, &entry); ++ ++ if (code != 0) ++ return code; ++ } ++ ++ return 0; ++} + + krb5_error_code + _adcli_krb5_keytab_add_entries (krb5_context k5, +diff --git a/library/adprivate.h b/library/adprivate.h +index 83a88f6..7485249 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -282,6 +282,12 @@ krb5_enctype * _adcli_krb5_parse_enctypes (const char *value); + + char * _adcli_krb5_format_enctypes (krb5_enctype *enctypes); + ++krb5_error_code _adcli_krb5_keytab_copy_entries (krb5_context k5, ++ krb5_keytab keytab, ++ krb5_principal principal, ++ krb5_kvno kvno, ++ krb5_enctype *enctypes); ++ + struct _adcli_attrs { + LDAPMod **mods; + int len; +-- +2.14.4 + diff --git a/0019-Calculate-enctypes-in-a-separate-function.patch b/0019-Calculate-enctypes-in-a-separate-function.patch new file mode 100644 index 0000000..4a09f33 --- /dev/null +++ b/0019-Calculate-enctypes-in-a-separate-function.patch @@ -0,0 +1,181 @@ +From 9ad1164405e7b4decb7c4ad96fe5ab27d6e53366 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 6 Jun 2018 16:31:32 +0200 +Subject: [PATCH 19/23] 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 6fdc773..75ac1e4 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -542,6 +542,83 @@ calculate_computer_account (adcli_enroll *enroll, + return ADCLI_SUCCESS; + } + ++static adcli_result ++calculate_enctypes (adcli_enroll *enroll, char **enctype) ++{ ++ char *value = NULL; ++ krb5_enctype *read_enctypes; ++ char *new_value = NULL; ++ int is_2008_or_later; ++ LDAP *ldap; ++ ++ *enctype = NULL; ++ /* ++ * Because we're using a keytab we want the server to be aware of the ++ * encryption types supported on the client, because we can't dynamically ++ * use a new one that's thrown at us. ++ * ++ * If the encryption types are not explicitly set by the caller of this ++ * library, then see if the account already has some encryption types ++ * marked on it. ++ * ++ * If not, write our default set to the account. ++ * ++ * Note that Windows 2003 and earlier have a standard set of encryption ++ * types, and no msDS-supportedEncryptionTypes attribute. ++ */ ++ ++ ldap = adcli_conn_get_ldap_connection (enroll->conn); ++ return_unexpected_if_fail (ldap != NULL); ++ ++ is_2008_or_later = adcli_conn_server_has_capability (enroll->conn, ADCLI_CAP_V60_OID); ++ ++ /* In 2008 or later, use the msDS-supportedEncryptionTypes attribute */ ++ if (is_2008_or_later) { ++ value = _adcli_ldap_parse_value (ldap, enroll->computer_attributes, ++ "msDS-supportedEncryptionTypes"); ++ ++ if (!enroll->keytab_enctypes_explicit && value != NULL) { ++ read_enctypes = _adcli_krb5_parse_enctypes (value); ++ if (read_enctypes == NULL) { ++ _adcli_warn ("Invalid or unsupported encryption types are set on " ++ "the computer account (%s).", value); ++ } else { ++ free (enroll->keytab_enctypes); ++ enroll->keytab_enctypes = read_enctypes; ++ } ++ } ++ ++ /* In 2003 or earlier, standard set of enc types */ ++ } else { ++ value = _adcli_krb5_format_enctypes (v51_earlier_enctypes); ++ } ++ ++ new_value = _adcli_krb5_format_enctypes (adcli_enroll_get_keytab_enctypes (enroll)); ++ if (new_value == NULL) { ++ free (value); ++ _adcli_warn ("The encryption types desired are not available in active directory"); ++ return ADCLI_ERR_CONFIG; ++ } ++ ++ /* If we already have this value, then don't need to update */ ++ if (value && strcmp (new_value, value) == 0) { ++ free (value); ++ free (new_value); ++ return ADCLI_SUCCESS; ++ } ++ free (value); ++ ++ if (!is_2008_or_later) { ++ free (new_value); ++ _adcli_warn ("Server does not support setting encryption types"); ++ return ADCLI_SUCCESS; ++ } ++ ++ *enctype = new_value; ++ return ADCLI_SUCCESS; ++} ++ ++ + static adcli_result + create_computer_account (adcli_enroll *enroll, + LDAP *ldap) +@@ -1053,75 +1130,23 @@ retrieve_computer_account (adcli_enroll *enroll) + static adcli_result + update_and_calculate_enctypes (adcli_enroll *enroll) + { +- char *value = NULL; +- krb5_enctype *read_enctypes; + char *vals_supportedEncryptionTypes[] = { NULL, NULL }; + LDAPMod mod = { LDAP_MOD_REPLACE, "msDS-supportedEncryptionTypes", { vals_supportedEncryptionTypes, } }; + LDAPMod *mods[2] = { &mod, NULL }; +- int is_2008_or_later; + char *new_value; + LDAP *ldap; + int ret; + +- /* +- * Because we're using a keytab we want the server to be aware of the +- * encryption types supported on the client, because we can't dynamically +- * use a new one that's thrown at us. +- * +- * If the encryption types are not explicitly set by the caller of this +- * library, then see if the account already has some encryption types +- * marked on it. +- * +- * If not, write our default set to the account. +- * +- * Note that Windows 2003 and earlier have a standard set of encryption +- * types, and no msDS-supportedEncryptionTypes attribute. +- */ +- + ldap = adcli_conn_get_ldap_connection (enroll->conn); + return_unexpected_if_fail (ldap != NULL); + +- is_2008_or_later = adcli_conn_server_has_capability (enroll->conn, ADCLI_CAP_V60_OID); +- +- /* In 2008 or later, use the msDS-supportedEncryptionTypes attribute */ +- if (is_2008_or_later) { +- value = _adcli_ldap_parse_value (ldap, enroll->computer_attributes, +- "msDS-supportedEncryptionTypes"); +- +- if (!enroll->keytab_enctypes_explicit && value != NULL) { +- read_enctypes = _adcli_krb5_parse_enctypes (value); +- if (read_enctypes == NULL) { +- _adcli_warn ("Invalid or unsupported encryption types are set on " +- "the computer account (%s).", value); +- } else { +- free (enroll->keytab_enctypes); +- enroll->keytab_enctypes = read_enctypes; +- } +- } +- +- /* In 2003 or earlier, standard set of enc types */ +- } else { +- value = _adcli_krb5_format_enctypes (v51_earlier_enctypes); +- } +- +- new_value = _adcli_krb5_format_enctypes (adcli_enroll_get_keytab_enctypes (enroll)); +- if (new_value == NULL) { +- free (value); +- _adcli_warn ("The encryption types desired are not available in active directory"); +- return ADCLI_ERR_CONFIG; +- } +- +- /* If we already have this value, then don't need to update */ +- if (value && strcmp (new_value, value) == 0) { +- free (value); ++ ret = calculate_enctypes (enroll, &new_value); ++ if (ret != ADCLI_SUCCESS) { + free (new_value); +- return ADCLI_SUCCESS; ++ return ret; + } +- free (value); + +- if (!is_2008_or_later) { +- free (new_value); +- _adcli_warn ("Server does not support setting encryption types"); ++ if (new_value == NULL) { + return ADCLI_SUCCESS; + } + +-- +2.14.4 + diff --git a/0020-join-add-all-attributes-while-creating-computer-obje.patch b/0020-join-add-all-attributes-while-creating-computer-obje.patch new file mode 100644 index 0000000..7c76b1c --- /dev/null +++ b/0020-join-add-all-attributes-while-creating-computer-obje.patch @@ -0,0 +1,110 @@ +From cbe33b3e6d0d3415e4642d71942380d1793311f1 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 11 Jun 2018 09:44:49 +0200 +Subject: [PATCH 20/23] 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 75ac1e4..b508caf 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -573,7 +573,7 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype) + is_2008_or_later = adcli_conn_server_has_capability (enroll->conn, ADCLI_CAP_V60_OID); + + /* In 2008 or later, use the msDS-supportedEncryptionTypes attribute */ +- if (is_2008_or_later) { ++ if (is_2008_or_later && enroll->computer_attributes != NULL) { + value = _adcli_ldap_parse_value (ldap, enroll->computer_attributes, + "msDS-supportedEncryptionTypes"); + +@@ -618,7 +618,6 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype) + return ADCLI_SUCCESS; + } + +- + static adcli_result + create_computer_account (adcli_enroll *enroll, + LDAP *ldap) +@@ -628,22 +627,65 @@ create_computer_account (adcli_enroll *enroll, + char *vals_sAMAccountName[] = { enroll->computer_sam, NULL }; + LDAPMod sAMAccountName = { LDAP_MOD_ADD, "sAMAccountName", { vals_sAMAccountName, } }; + char *vals_userAccountControl[] = { "69632", NULL }; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD */ +- LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } }; ++ LDAPMod userAccountControl = { LDAP_MOD_ADD, "userAccountControl", { vals_userAccountControl, } }; ++ char *vals_supportedEncryptionTypes[] = { NULL, NULL }; ++ LDAPMod encTypes = { LDAP_MOD_ADD, "msDS-supportedEncryptionTypes", { vals_supportedEncryptionTypes, } }; ++ char *vals_dNSHostName[] = { enroll->host_fqdn, NULL }; ++ LDAPMod dNSHostName = { LDAP_MOD_ADD, "dNSHostName", { vals_dNSHostName, } }; ++ char *vals_operatingSystem[] = { enroll->os_name, NULL }; ++ LDAPMod operatingSystem = { LDAP_MOD_ADD, "operatingSystem", { vals_operatingSystem, } }; ++ char *vals_operatingSystemVersion[] = { enroll->os_version, NULL }; ++ LDAPMod operatingSystemVersion = { LDAP_MOD_ADD, "operatingSystemVersion", { vals_operatingSystemVersion, } }; ++ char *vals_operatingSystemServicePack[] = { enroll->os_service_pack, NULL }; ++ LDAPMod operatingSystemServicePack = { LDAP_MOD_ADD, "operatingSystemServicePack", { vals_operatingSystemServicePack, } }; ++ char *vals_userPrincipalName[] = { enroll->user_principal, NULL }; ++ LDAPMod userPrincipalName = { LDAP_MOD_ADD, "userPrincipalName", { vals_userPrincipalName, }, }; ++ LDAPMod servicePrincipalName = { LDAP_MOD_ADD, "servicePrincipalName", { enroll->service_principals, } }; ++ ++ char *val = NULL; + + int ret; ++ size_t c; ++ size_t m; + +- LDAPMod *mods[] = { ++ LDAPMod *all_mods[] = { + &objectClass, + &sAMAccountName, + &userAccountControl, +- NULL, ++ &encTypes, ++ &dNSHostName, ++ &operatingSystem, ++ &operatingSystemVersion, ++ &operatingSystemServicePack, ++ &userPrincipalName, ++ &servicePrincipalName, ++ NULL + }; + ++ size_t mods_count = sizeof (all_mods) / sizeof (LDAPMod *); ++ LDAPMod *mods[mods_count]; ++ + if (adcli_enroll_get_trusted_for_delegation (enroll)) { + vals_userAccountControl[0] = "593920"; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD | TRUSTED_FOR_DELEGATION */ + } + ++ ret = calculate_enctypes (enroll, &val); ++ if (ret != ADCLI_SUCCESS) { ++ return ret; ++ } ++ vals_supportedEncryptionTypes[0] = val; ++ ++ m = 0; ++ for (c = 0; c < mods_count - 1; c++) { ++ /* Skip empty LDAP sttributes */ ++ if (all_mods[c]->mod_vals.modv_strvals[0] != NULL) { ++ mods[m++] = all_mods[c]; ++ } ++ } ++ mods[m] = NULL; ++ + ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL); ++ free (val); + + /* + * Hand to head. This is really dumb... AD returns +-- +2.14.4 + diff --git a/0021-util-add-_adcli_strv_remove_unsorted.patch b/0021-util-add-_adcli_strv_remove_unsorted.patch new file mode 100644 index 0000000..f9dea0f --- /dev/null +++ b/0021-util-add-_adcli_strv_remove_unsorted.patch @@ -0,0 +1,288 @@ +From 4208646609da9b25b70c21f5f39c92fabbd59dfc Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 14 Jun 2018 16:48:22 +0200 +Subject: [PATCH 21/23] util: add _adcli_strv_remove_unsorted + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1547014 +--- + library/adprivate.h | 4 ++ + library/adutil.c | 21 ++++++++ + library/seq.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++--- + library/seq.h | 12 +++++ + 4 files changed, 179 insertions(+), 7 deletions(-) + +diff --git a/library/adprivate.h b/library/adprivate.h +index 7485249..bc9df6d 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -111,6 +111,10 @@ char ** _adcli_strv_add (char **strv, + char *string, + int *length) GNUC_WARN_UNUSED; + ++void _adcli_strv_remove_unsorted (char **strv, ++ const char *string, ++ int *length); ++ + void _adcli_strv_free (char **strv); + + int _adcli_strv_has (char **strv, +diff --git a/library/adutil.c b/library/adutil.c +index a27bd68..6334b52 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -221,6 +221,27 @@ _adcli_strv_add (char **strv, + return seq_push (strv, length, string); + } + ++#define discard_const(ptr) ((void *)((uintptr_t)(ptr))) ++ ++void ++_adcli_strv_remove_unsorted (char **strv, ++ const char *string, ++ int *length) ++{ ++ int len; ++ ++ return_if_fail (string != NULL); ++ ++ if (!length) { ++ len = seq_count (strv); ++ length = &len; ++ } ++ ++ return seq_remove_unsorted (strv, length, discard_const (string), ++ (seq_compar)strcasecmp, free); ++} ++ ++ + int + _adcli_strv_has (char **strv, + const char *str) +diff --git a/library/seq.c b/library/seq.c +index 627dcaf..8e7475d 100644 +--- a/library/seq.c ++++ b/library/seq.c +@@ -111,6 +111,24 @@ seq_push (seq_voidp sequence, + return seq; + } + ++static int ++linear_search (void **seq, ++ int low, ++ int high, ++ void *match, ++ seq_compar compar) ++{ ++ int at; ++ ++ for (at = low; at < high; at++) { ++ if (compar (match, seq[at]) == 0) { ++ break; ++ } ++ } ++ ++ return at; ++} ++ + static int + binary_search (void **seq, + int low, +@@ -171,12 +189,13 @@ seq_insert (seq_voidp sequence, + return seq; + } + +-void +-seq_remove (seq_voidp sequence, +- int *length, +- void *match, +- seq_compar compar, +- seq_destroy destroy) ++static void ++seq_remove_int (seq_voidp sequence, ++ int *length, ++ void *match, ++ seq_search search, ++ seq_compar compar, ++ seq_destroy destroy) + { + void **seq = sequence; + int at; +@@ -187,7 +206,7 @@ seq_remove (seq_voidp sequence, + assert (match != NULL); + + len = *length; +- at = binary_search (seq, 0, len, match, compar); ++ at = search (seq, 0, len, match, compar); + + /* We have a matching value */ + if (at < len && compar (match, seq[at]) == 0) { +@@ -201,6 +220,26 @@ seq_remove (seq_voidp sequence, + *length = len; + } + ++void ++seq_remove (seq_voidp sequence, ++ int *length, ++ void *match, ++ seq_compar compar, ++ seq_destroy destroy) ++{ ++ return seq_remove_int (sequence, length, match, binary_search, compar, destroy); ++} ++ ++void ++seq_remove_unsorted (seq_voidp sequence, ++ int *length, ++ void *match, ++ seq_compar compar, ++ seq_destroy destroy) ++{ ++ return seq_remove_int (sequence, length, match, linear_search, compar, destroy); ++} ++ + void + seq_filter (seq_voidp sequence, + int *length, +@@ -430,6 +469,99 @@ test_remove (void) + seq_free (seq, NULL); + } + ++static void ++test_remove_unsorted (void) ++{ ++ void **seq = NULL; ++ int len = 0; ++ ++ seq = seq_push (seq, &len, "3"); ++ seq = seq_push (seq, &len, "5"); ++ seq = seq_push (seq, &len, "1"); ++ seq = seq_push (seq, &len, "4"); ++ seq = seq_push (seq, &len, "2"); ++ ++ assert_str_eq (seq[0], "3"); ++ assert_str_eq (seq[1], "5"); ++ assert_str_eq (seq[2], "1"); ++ assert_str_eq (seq[3], "4"); ++ assert_str_eq (seq[4], "2"); ++ assert (seq[5] == NULL); ++ assert_num_eq (len, 5); ++ ++ seq_remove_unsorted (seq, &len, "3", (seq_compar)strcmp, NULL); ++ seq_remove_unsorted (seq, &len, "2", (seq_compar)strcmp, NULL); ++ ++ assert_str_eq (seq[0], "5"); ++ assert_str_eq (seq[1], "1"); ++ assert_str_eq (seq[2], "4"); ++ assert (seq[3] == NULL); ++ assert_num_eq (len, 3); ++ ++ seq_free (seq, NULL); ++} ++ ++static void ++test_remove_first (void) ++{ ++ void **seq = NULL; ++ int len = 0; ++ ++ seq = seq_insert (seq, &len, "3", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "5", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "1", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "4", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "2", (seq_compar)strcmp, NULL); ++ ++ assert_str_eq (seq[0], "1"); ++ assert_str_eq (seq[1], "2"); ++ assert_str_eq (seq[2], "3"); ++ assert_str_eq (seq[3], "4"); ++ assert_str_eq (seq[4], "5"); ++ assert (seq[5] == NULL); ++ assert_num_eq (len, 5); ++ ++ seq_remove (seq, &len, "1", (seq_compar)strcmp, NULL); ++ ++ assert_str_eq (seq[0], "2"); ++ assert_str_eq (seq[1], "3"); ++ assert_str_eq (seq[2], "4"); ++ assert_str_eq (seq[3], "5"); ++ assert (seq[4] == NULL); ++ assert_num_eq (len, 4); ++ ++ seq_free (seq, NULL); ++} ++ ++static void ++test_remove_last (void) ++{ ++ void **seq = NULL; ++ int len = 0; ++ ++ seq = seq_insert (seq, &len, "3", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "1", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "4", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "2", (seq_compar)strcmp, NULL); ++ ++ assert_str_eq (seq[0], "1"); ++ assert_str_eq (seq[1], "2"); ++ assert_str_eq (seq[2], "3"); ++ assert_str_eq (seq[3], "4"); ++ assert (seq[4] == NULL); ++ assert_num_eq (len, 4); ++ ++ seq_remove (seq, &len, "4", (seq_compar)strcmp, NULL); ++ ++ assert_str_eq (seq[0], "1"); ++ assert_str_eq (seq[1], "2"); ++ assert_str_eq (seq[2], "3"); ++ assert (seq[3] == NULL); ++ assert_num_eq (len, 3); ++ ++ seq_free (seq, NULL); ++} ++ + static int + compar_even (void *match, + void *value) +@@ -631,6 +763,9 @@ main (int argc, + test_func (test_insert, "/seq/insert"); + test_func (test_insert_destroys, "/seq/insert_destroys"); + test_func (test_remove, "/seq/remove"); ++ test_func (test_remove_unsorted, "/seq/remove_unsorted"); ++ test_func (test_remove_first, "/seq/remove_first"); ++ test_func (test_remove_last, "/seq/remove_last"); + test_func (test_remove_destroys, "/seq/remove_destroys"); + test_func (test_filter, "/seq/filter"); + test_func (test_filter_null, "/seq/filter_null"); +diff --git a/library/seq.h b/library/seq.h +index 694965b..5d48848 100644 +--- a/library/seq.h ++++ b/library/seq.h +@@ -44,6 +44,12 @@ typedef void * (* seq_copy) (void *value); + + typedef void (* seq_destroy) (void *value); + ++typedef int (* seq_search) (void **seq, ++ int low, ++ int high, ++ void *match, ++ seq_compar compar); ++ + seq_voidp seq_push (seq_voidp seq, + int *length, + void *value) WARN_UNUSED; +@@ -62,6 +68,12 @@ void seq_remove (seq_voidp seq, + seq_compar compar, + seq_destroy destroy); + ++void seq_remove_unsorted (seq_voidp seq, ++ int *length, ++ void *match, ++ seq_compar compar, ++ seq_destroy destroy); ++ + seq_voidp seq_lookup (seq_voidp seq, + int *length, + void *match, +-- +2.14.4 + diff --git a/0022-Add-add-service-principal-and-remove-service-princip.patch b/0022-Add-add-service-principal-and-remove-service-princip.patch new file mode 100644 index 0000000..a92414a --- /dev/null +++ b/0022-Add-add-service-principal-and-remove-service-princip.patch @@ -0,0 +1,360 @@ +From ee71c4c0614a504b4472bf64a24fc3c18c6b9987 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 14 Jun 2018 16:49:26 +0200 +Subject: [PATCH 22/23] Add add-service-principal and remove-service-principal + options + +Currently it is only possible to specific a service name for service +principals but not to set the full service principal. This is e.g. +needed if there is a service running on a host which should be reachable +by a different DNS name as well. + +With this patch service principal can be added and removed by specifying +the full name. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1547014 +--- + doc/adcli.xml | 21 ++++++++ + library/adenroll.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++-- + library/adenroll.h | 8 +++ + library/adldap.c | 16 ++++-- + tools/computer.c | 13 +++++ + 5 files changed, 189 insertions(+), 8 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index b246190..83b6981 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -290,6 +290,14 @@ Password for Administrator: + not allow that Kerberos tickets can be forwarded to the + host. + ++ ++ ++ Add a service principal name. In ++ contrast to the the ++ hostname part can be specified as well in case the ++ service should be accessible with a different host ++ name as well. ++ + + + After a successful join print out information +@@ -416,6 +424,19 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + not allow that Kerberos tickets can be forwarded to the + host. + ++ ++ ++ Add a service principal name. In ++ contrast to the the ++ hostname part can be specified as well in case the ++ service should be accessible with a different host ++ name as well. ++ ++ ++ ++ Remove a service principal name from ++ the keytab and the AD host object. ++ + + + After a successful join print out information +diff --git a/library/adenroll.c b/library/adenroll.c +index b508caf..c4ba537 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -95,6 +95,9 @@ struct _adcli_enroll { + char **service_principals; + int service_principals_explicit; + ++ char **service_principals_to_add; ++ char **service_principals_to_remove; ++ + char *user_principal; + int user_princpal_generate; + +@@ -332,6 +335,43 @@ add_service_names_to_service_principals (adcli_enroll *enroll) + return ADCLI_SUCCESS; + } + ++static adcli_result ++add_and_remove_service_principals (adcli_enroll *enroll) ++{ ++ int length = 0; ++ size_t c; ++ const char **list; ++ ++ if (enroll->service_principals != NULL) { ++ length = seq_count (enroll->service_principals); ++ } ++ ++ list = adcli_enroll_get_service_principals_to_add (enroll); ++ if (list != NULL) { ++ for (c = 0; list[c] != NULL; c++) { ++ enroll->service_principals = _adcli_strv_add (enroll->service_principals, ++ strdup (list[c]), ++ &length); ++ if (enroll->service_principals == NULL) { ++ return ADCLI_ERR_UNEXPECTED; ++ } ++ } ++ } ++ ++ list = adcli_enroll_get_service_principals_to_remove (enroll); ++ if (list != NULL) { ++ for (c = 0; list[c] != NULL; c++) { ++ /* enroll->service_principals typically refects the ++ * order of the principal in the keytabm so it is not ++ * ordered. */ ++ _adcli_strv_remove_unsorted (enroll->service_principals, ++ list[c], &length); ++ } ++ } ++ ++ return ADCLI_SUCCESS; ++} ++ + static adcli_result + ensure_service_principals (adcli_result res, + adcli_enroll *enroll) +@@ -343,10 +383,14 @@ ensure_service_principals (adcli_result res, + + if (!enroll->service_principals) { + assert (enroll->service_names != NULL); +- return add_service_names_to_service_principals (enroll); ++ res = add_service_names_to_service_principals (enroll); + } + +- return ADCLI_SUCCESS; ++ if (res == ADCLI_SUCCESS) { ++ res = add_and_remove_service_principals (enroll); ++ } ++ ++ return res; + } + + static adcli_result +@@ -1593,6 +1637,39 @@ free_principal_salts (krb5_context k5, + free (salts); + } + ++static adcli_result ++remove_principal_from_keytab (adcli_enroll *enroll, ++ krb5_context k5, ++ const char *principal_name) ++{ ++ krb5_error_code code; ++ krb5_principal principal; ++ match_principal_kvno closure; ++ ++ code = krb5_parse_name (k5, principal_name, &principal); ++ if (code != 0) { ++ _adcli_err ("Couldn't parse principal: %s: %s", ++ principal_name, krb5_get_error_message (k5, code)); ++ return ADCLI_ERR_FAIL; ++ } ++ ++ closure.kvno = enroll->kvno; ++ closure.principal = principal; ++ closure.matched = 0; ++ ++ code = _adcli_krb5_keytab_clear (k5, enroll->keytab, ++ match_principal_and_kvno, &closure); ++ krb5_free_principal (k5, principal); ++ ++ if (code != 0) { ++ _adcli_err ("Couldn't update keytab: %s: %s", ++ enroll->keytab_name, krb5_get_error_message (k5, code)); ++ return ADCLI_ERR_FAIL; ++ } ++ ++ return ADCLI_SUCCESS; ++} ++ + static adcli_result + add_principal_to_keytab (adcli_enroll *enroll, + krb5_context k5, +@@ -1702,6 +1779,17 @@ update_keytab_for_principals (adcli_enroll *enroll, + return res; + } + ++ if (enroll->service_principals_to_remove != NULL) { ++ for (i = 0; enroll->service_principals_to_remove[i] != NULL; i++) { ++ res = remove_principal_from_keytab (enroll, k5, ++ enroll->service_principals_to_remove[i]); ++ if (res != ADCLI_SUCCESS) { ++ _adcli_warn ("Failed to remove %s from keytab.", ++ enroll->service_principals_to_remove[i]); ++ } ++ } ++ } ++ + return ADCLI_SUCCESS; + } + +@@ -2029,8 +2117,11 @@ adcli_enroll_update (adcli_enroll *enroll, + if (_adcli_check_nt_time_string_lifetime (value, + adcli_enroll_get_computer_password_lifetime (enroll))) { + /* Do not update keytab if neither new service principals have +- * to be added nor the user principal has to be changed. */ +- if (enroll->service_names == NULL && (enroll->user_principal == NULL || enroll->user_princpal_generate)) { ++ * to be added or deleted nor the user principal has to be changed. */ ++ if (enroll->service_names == NULL ++ && (enroll->user_principal == NULL || enroll->user_princpal_generate) ++ && enroll->service_principals_to_add == NULL ++ && enroll->service_principals_to_remove == NULL) { + flags |= ADCLI_ENROLL_NO_KEYTAB; + } + flags |= ADCLI_ENROLL_PASSWORD_VALID; +@@ -2581,3 +2672,43 @@ adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, + enroll->trusted_for_delegation = value; + enroll->trusted_for_delegation_explicit = 1; + } ++ ++const char ** ++adcli_enroll_get_service_principals_to_add (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, NULL); ++ ++ return (const char **)enroll->service_principals_to_add; ++} ++ ++void ++adcli_enroll_add_service_principal_to_add (adcli_enroll *enroll, ++ const char *value) ++{ ++ return_if_fail (enroll != NULL); ++ return_if_fail (value != NULL); ++ ++ enroll->service_principals_to_add = _adcli_strv_add (enroll->service_principals_to_add, ++ strdup (value), NULL); ++ return_if_fail (enroll->service_principals_to_add != NULL); ++} ++ ++const char ** ++adcli_enroll_get_service_principals_to_remove (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, NULL); ++ ++ return (const char **)enroll->service_principals_to_remove; ++} ++ ++void ++adcli_enroll_add_service_principal_to_remove (adcli_enroll *enroll, ++ const char *value) ++{ ++ return_if_fail (enroll != NULL); ++ return_if_fail (value != NULL); ++ ++ enroll->service_principals_to_remove = _adcli_strv_add (enroll->service_principals_to_remove, ++ strdup (value), NULL); ++ return_if_fail (enroll->service_principals_to_remove != NULL); ++} +diff --git a/library/adenroll.h b/library/adenroll.h +index be2ca18..f87dffa 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -98,6 +98,14 @@ const char ** adcli_enroll_get_service_principals (adcli_enroll *enroll); + void adcli_enroll_set_service_principals (adcli_enroll *enroll, + const char **value); + ++const char ** adcli_enroll_get_service_principals_to_add (adcli_enroll *enroll); ++void adcli_enroll_add_service_principal_to_add (adcli_enroll *enroll, ++ const char *value); ++ ++const char ** adcli_enroll_get_service_principals_to_remove (adcli_enroll *enroll); ++void adcli_enroll_add_service_principal_to_remove (adcli_enroll *enroll, ++ const char *value); ++ + const char * adcli_enroll_get_user_principal (adcli_enroll *enroll); + + void adcli_enroll_set_user_principal (adcli_enroll *enroll, +diff --git a/library/adldap.c b/library/adldap.c +index 07dc373..d93efb7 100644 +--- a/library/adldap.c ++++ b/library/adldap.c +@@ -210,16 +210,24 @@ _adcli_ldap_have_in_mod (LDAPMod *mod, + struct berval *vals; + struct berval **pvals; + int count = 0; ++ int count_have = 0; + int i; + int ret; + +- /* Already in berval format, just compare */ +- if (mod->mod_op & LDAP_MOD_BVALUES) +- return _adcli_ldap_have_vals (mod->mod_vals.modv_bvals, have); +- + /* Count number of values */ + for (i = 0; mod->mod_vals.modv_strvals[i] != 0; i++) + count++; ++ for (i = 0; have[i] != 0; i++) ++ count_have++; ++ ++ /* If numbers different something has to be added or removed */ ++ if (count != count_have) { ++ return 0; ++ } ++ ++ /* Already in berval format, just compare */ ++ if (mod->mod_op & LDAP_MOD_BVALUES) ++ return _adcli_ldap_have_vals (mod->mod_vals.modv_bvals, have); + + vals = malloc (sizeof (struct berval) * (count + 1)); + pvals = malloc (sizeof (struct berval *) * (count + 1)); +diff --git a/tools/computer.c b/tools/computer.c +index b905fd1..377d449 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -110,6 +110,8 @@ typedef enum { + opt_add_samba_data, + opt_samba_data_tool, + opt_trusted_for_delegation, ++ opt_add_service_principal, ++ opt_remove_service_principal, + } Option; + + static adcli_tool_desc common_usages[] = { +@@ -138,6 +140,8 @@ static adcli_tool_desc common_usages[] = { + { opt_computer_password_lifetime, "lifetime of the host accounts password in days", }, + { opt_trusted_for_delegation, "set/unset the TRUSTED_FOR_DELEGATION flag\n" + "in the userAccountControl attribute", }, ++ { opt_add_service_principal, "add the given service principal to the account\n" }, ++ { opt_remove_service_principal, "remove the given service principal from the account\n" }, + { opt_no_password, "don't prompt for or read a password" }, + { opt_prompt_password, "prompt for a password if necessary" }, + { opt_stdin_password, "read a password from stdin (until EOF) if\n" +@@ -289,6 +293,12 @@ parse_option (Option opt, + adcli_enroll_set_trusted_for_delegation (enroll, false); + } + return; ++ case opt_add_service_principal: ++ adcli_enroll_add_service_principal_to_add (enroll, optarg); ++ return; ++ case opt_remove_service_principal: ++ adcli_enroll_add_service_principal_to_remove (enroll, optarg); ++ return; + case opt_verbose: + return; + +@@ -353,6 +363,7 @@ adcli_tool_computer_join (adcli_conn *conn, + { "os-service-pack", optional_argument, NULL, opt_os_service_pack }, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, ++ { "add-service-principal", required_argument, NULL, opt_add_service_principal }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, +@@ -458,6 +469,8 @@ adcli_tool_computer_update (adcli_conn *conn, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime }, + { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, ++ { "add-service-principal", required_argument, NULL, opt_add_service_principal }, ++ { "remove-service-principal", required_argument, NULL, opt_remove_service_principal }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, +-- +2.14.4 + diff --git a/0023-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch b/0023-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch new file mode 100644 index 0000000..7b775c3 --- /dev/null +++ b/0023-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch @@ -0,0 +1,32 @@ +From 026cfacabfad58ae2cebcdf6cd82d905023ea289 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 31 May 2018 17:01:36 +0200 +Subject: [PATCH 23/23] adcli_conn_is_writeable: do not crash id domain_disco + is missing + +Resolves https://bugzilla.redhat.com/show_bug.cgi?id=1575554 +--- + library/adconn.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/library/adconn.c b/library/adconn.c +index d2fb1d5..e2250e3 100644 +--- a/library/adconn.c ++++ b/library/adconn.c +@@ -1567,6 +1567,11 @@ adcli_conn_server_has_capability (adcli_conn *conn, + + bool adcli_conn_is_writeable (adcli_conn *conn) + { +- disco_dance_if_necessary (conn); +- return ( (conn->domain_disco->flags & ADCLI_DISCO_WRITABLE) != 0); ++ disco_dance_if_necessary (conn); ++ ++ if (conn->domain_disco == NULL) { ++ return false; ++ } ++ ++ return ( (conn->domain_disco->flags & ADCLI_DISCO_WRITABLE) != 0); + } +-- +2.14.4 + diff --git a/0024-doc-fix-typos-in-the-adcli-man-page.patch b/0024-doc-fix-typos-in-the-adcli-man-page.patch new file mode 100644 index 0000000..1827c95 --- /dev/null +++ b/0024-doc-fix-typos-in-the-adcli-man-page.patch @@ -0,0 +1,203 @@ +From 1e57862cf5d8f4f774868b3599e4a34c525ae348 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 5 Jul 2018 13:06:26 +0200 +Subject: [PATCH 24/24] doc: fix typos in the adcli man page + +Resolves https://bugzilla.redhat.com/show_bug.cgi?id=1440533 +--- + doc/adcli.xml | 44 ++++++++++++++++++++++---------------------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 83b6981..97dec08 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -105,19 +105,19 @@ + + + The domain to connect to. If a domain is +- not specified then the domain part of the local computer's ++ not specified, then the domain part of the local computer's + host name is used. + + + + Kerberos realm for the domain. If not +- specified then the upper cased domain name is ++ specified, then the upper cased domain name is + used. + + + + Connect to a specific domain controller. +- If not specified then an appropriate domain controller ++ If not specified, then an appropriate domain controller + is automatically discovered. + + +@@ -134,7 +134,7 @@ + + + Use the specified user account to +- authenticate with the domain. If not specified then ++ authenticate with the domain. If not specified, then + the name 'Administrator' will be used. + + +@@ -181,7 +181,7 @@ $ adcli info --domain-controller=dc.domain.example.com + adcli info will output as much information as + it can about the domain. The information is designed to be both machine + and human readable. The command will exit with a non-zero exit code +- if the domain does note exist or cannot be reached. ++ if the domain does not exist or cannot be reached. + + To show domain info for a specific domain controller use the + option to specify which domain +@@ -213,35 +213,35 @@ Password for Administrator: + + + The short non-dotted name of the computer +- account that will be created in the domain. If not specified ++ account that will be created in the domain. If not specified, + then the first portion of the + is used. + + + + The full distinguished name of the OU in +- which to create the computer account. If not specified ++ which to create the computer account. If not specified, + then the computer account will be created in a default + location. + + + + Override the local machine's fully qualified +- domain name. If not specified the local machine's hostname ++ domain name. If not specified, the local machine's hostname + will be retrieved via gethostname(). + + + + Specify the path to the host keytab where + host credentials will be written after a successful join +- operation. If not specified the default location will be ++ operation. If not specified, the default location will be + used, usually /etc/krb5.keytab. + + + + Specify the type of authentication that + will be performed before creating the machine account in +- the domain. If set to 'computer' then the computer must ++ the domain. If set to 'computer', then the computer must + already have a preset account in the domain. If not + specified and none of the other + arguments have been specified, then will try both +@@ -329,7 +329,7 @@ Password for Administrator: + + If Samba's net + cannot be found at +- &samba_data_tool; this option can ++ &samba_data_tool;, this option can + be used to specific an alternative location with the + help of an absolute path. + +@@ -351,7 +351,7 @@ Password for Administrator: + $ adcli update + + +- If used with a credential cache other attributes of the computer ++ If used with a credential cache, other attributes of the computer + account can be changed as well if the principal has sufficient + privileges. + +@@ -367,20 +367,20 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + + + The short non-dotted name of the computer +- account that will be created in the domain. If not specified ++ account that will be created in the domain. If not specified, + it will be retrieved from the keytab entries. + + + + The local machine's fully qualified +- domain name. If not specified the local machine's hostname ++ domain name. If not specified, the local machine's hostname + will be retrieved from the keytab entries. + + + + Specify the path to the host keytab where + current host credentials are stored and the new ones +- will be written to. If not specified the default ++ will be written to. If not specified, the default + location will be used, usually + /etc/krb5.keytab. + +@@ -462,7 +462,7 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + + If Samba's net + cannot be found at +- &samba_data_tool; this option can ++ &samba_data_tool;, this option can + be used to specific an alternative location with the + help of an absolute path. + +@@ -493,7 +493,7 @@ $ adcli create-user Fry --domain=domain.example.com \ + + + The full distinguished name of the OU in +- which to create the user account. If not specified ++ which to create the user account. If not specified, + then the computer account will be created in a default + location. + +@@ -569,7 +569,7 @@ $ adcli create-group Pilots --domain=domain.example.com \ + + + The full distinguished name of the OU in +- which to create the group. If not specified ++ which to create the group. If not specified, + then the group will be created in a default + location. + +@@ -649,14 +649,14 @@ Password for Administrator: + + + The full distinguished name of the OU in +- which to create the computer accounts. If not specified ++ which to create the computer accounts. If not specified, + then the computer account will be created in a default + location. + + + + Specify a one time password to use when +- presetting the computer accounts. If not specified then ++ presetting the computer accounts. If not specified, then + a default password will be used, which allows for later + automatic joins. + +@@ -696,7 +696,7 @@ Password for Administrator: + Reset Computer Account + + adcli reset-computer resets a computer account +- in the domain. If a the appropriate machine is currently joined to the ++ in the domain. If the appropriate machine is currently joined to the + domain, then its membership will be broken. The account must already + exist. + +@@ -716,7 +716,7 @@ $ adcli reset-computer --domain=domain.example.com host2 + + Specify the type of authentication that + will be performed before creating the machine account in +- the domain. If set to 'computer' then the computer must ++ the domain. If set to 'computer', then the computer must + already have a preset account in the domain. If not + specified and none of the other + arguments have been specified, then will try both +-- +2.14.4 + diff --git a/EMPTY b/EMPTY deleted file mode 100644 index 0519ecb..0000000 --- a/EMPTY +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/adcli.spec b/adcli.spec new file mode 100644 index 0000000..3fc795c --- /dev/null +++ b/adcli.spec @@ -0,0 +1,395 @@ +Name: adcli +Version: 0.8.2 +Release: 12%{?dist} +Summary: Active Directory enrollment +License: LGPLv2+ +URL: http://cgit.freedesktop.org/realmd/adcli +Source0: http://www.freedesktop.org/software/realmd/releases/adcli-%{version}.tar.gz + +Patch1: 0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch +Patch2: 0002-Use-strdup-if-offset-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-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch +Patch8: 0008-library-add-_adcli_bin_sid_to_str.patch +Patch9: 0009-library-add-_adcli_call_external_program.patch +Patch10: 0010-library-add-_adcli_ldap_parse_sid.patch +Patch11: 0011-library-add-lookup_domain_sid.patch +Patch12: 0012-library-add-adcli_conn_get_domain_sid.patch +Patch13: 0013-tools-add-option-add-samba-data.patch +Patch14: 0014-tools-store-Samba-data-if-requested.patch +Patch15: 0015-make-Samba-data-tool-configurable.patch +Patch16: 0016-Add-trusted-for-delegation-option.patch +Patch17: 0017-Only-update-attributes-given-on-the-command-line.patch +Patch18: 0018-update-allow-to-add-service-names.patch +Patch19: 0019-Calculate-enctypes-in-a-separate-function.patch +Patch20: 0020-join-add-all-attributes-while-creating-computer-obje.patch +Patch21: 0021-util-add-_adcli_strv_remove_unsorted.patch +Patch22: 0022-Add-add-service-principal-and-remove-service-princip.patch +Patch23: 0023-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch +Patch24: 0024-doc-fix-typos-in-the-adcli-man-page.patch + +Patch25: 0001-fix-typo-in-flag-value.patch +Patch26: 0002-_adcli_call_external_program-silence-noisy-debug-mes.patch +Patch27: 0003-Do-not-add-service-principals-twice.patch +Patch28: 0004-Do-not-depend-on-default_realm-in-krb5.conf.patch + +# rhbz#1677194 - Realm cannot join domain when hostname is not FQDN +Patch29: 0001-adutil-add-_adcli_strv_add_unique.patch +Patch30: 0002-adenroll-use-_adcli_strv_add_unique-for-service-prin.patch + +# Forward port of RHEL-7.7 ticket rhbz#1642546 - adcli exports kerberos ticket +# with old kvno +Patch31: 0001-Increment-kvno-after-password-change-with-user-creds.patch + +# Forward port of RHEL-7.7 ticket rhbz#1595911 - [RFE] Have `adcli join` work +# without FQDN in `hostname` output +Patch32: 0001-library-use-getaddrinfo-with-AI_CANONNAME-to-find-a-.patch + +# Forward port of RHEL-7.7 ticket rhbz#1644311 - Improve handling of service +# principals +Patch33: 0001-join-always-add-service-principals.patch +Patch34: 0002-library-return-error-if-no-matching-key-was-found.patch + +# Forward port of RHEL-7.7 ticket rhbz#1337489 - [RFE] adcli command with +# --unix-* options doesn't update values in UnixAttributes Tab for user +Patch35: 0001-create-user-add-nis-domain-option.patch +Patch36: 0002-create-user-try-to-find-NIS-domain-if-needed.patch + +# Forward port of RHEL-7.7 ticket rhbz#1630187 - [RFE] adcli join should +# preserve SPN added by adcli preset-computer +Patch37: 0001-ensure_keytab_principals-do-not-leak-memory-when-cal.patch +Patch38: 0002-library-make-_adcli_strv_has_ex-public.patch +Patch39: 0003-library-_adcli_krb5_build_principal-allow-principals.patch +Patch40: 0004-library-make-sure-server-side-SPNs-are-preserved.patch + +# Forward port of RHEL-7.7 ticket rhbz#1622583 - [RFE] Need an option for adcli +# command which will show domain join status. +Patch41: 0001-Implement-adcli-testjoin.patch + +# Forward port of RHEL-7.7 ticket rhbz#1630187 - [RFE] adcli join should +# preserve SPN added by adcli preset-computer - additional patch +Patch42: 0001-library-add-missing-strdup.patch + +# Forward port of RHEL-7.7 ticket rhbz#1588596 - many adcli-krb5-????? +# directories are created /tmp +Patch43: 0001-tools-remove-errx-from-computer-commands.patch +Patch44: 0002-tools-remove-errx-from-user-and-group-commands.patch +Patch45: 0003-tools-remove-errx-from-info-commands.patch +Patch46: 0004-tools-remove-errx-from-adcli_read_password_func.patch +Patch47: 0005-tools-remove-errx-from-setup_krb5_conf_directory.patch +Patch48: 0006-tools-entry-remove-errx-from-parse_option.patch +Patch49: 0007-tools-computer-remove-errx-from-parse_option.patch + +# rhbz#1717355 - `adcli join` fails in FIPS enabled environment +Patch50: 0001-Fix-for-issues-found-by-Coverity.patch +Patch51: 0001-adenroll-make-sure-only-allowed-enctypes-are-used-in.patch +Patch52: 0002-adconn-add-adcli_conn_set_krb5_context.patch +Patch53: 0003-adenroll-add-adcli_enroll_get_permitted_keytab_encty.patch +Patch54: 0004-adenroll-use-only-enctypes-permitted-by-Kerberos-con.patch + +# rhbz#1745931 - adcli update --add-samba-data does not work as expected +Patch55: 0001-doc-explain-how-to-force-password-reset.patch +Patch56: 0001-man-move-note-to-the-right-section.patch + +# rhbz#1745932 - Issue is that with arcfour-hmac as first encryption type +Patch57: 0001-Do-not-use-arcfour-hmac-md5-when-discovering-the-sal.patch + +Patch58: 0001-Fix-for-issue-found-by-Coverity.patch + +# rhbz#1737342 - [RFE] enhancement adcli to set description attribute and to +# show all AD attributes +Patch59: 0001-tools-add-show-computer-command.patch +Patch60: 0002-add-description-option-to-join-and-update.patch + +Patch61: 0001-Use-GSS-SPNEGO-if-available.patch +Patch62: 0002-add-option-use-ldaps.patch + +# rhbz#1806260 - [abrt] [faf] adcli: raise(): /usr/sbin/adcli killed by 6 +Patch63: 0001-Make-adcli-info-DC-location-mechanism-more-compliant.patch +Patch64: 0001-discovery-fix.patch + +# rhbz#1846882 - No longer able to delete computer from AD using adcli +Patch65: 0001-delete-do-not-exit-if-keytab-cannot-be-read.patch + +# rhbz#1846878 - adcli: presetting $computer in $domain domain failed: Cannot +# set computer password: Authentication error +Patch66: 0001-tools-disable-SSSD-s-locator-plugin.patch + +# rhbz#1791611 - Typo in adcli update --help option +Patch67: 0001-tools-fix-typo-in-show-password-help-output.patch + +# rhbz#1791545 - Manpage and help does not explain the use of "-C" option +Patch68: 0001-man-explain-optional-parameter-of-login-ccache-bette.patch +Patch69: 0001-man-make-handling-of-optional-credential-cache-more-.patch + +# rhbz#1883467 - Add --use-ldaps option to adcli update as well +Patch70: 0001-tools-add-missing-use-ldaps-option-to-update-and-tes.patch + +# rhbz#1734764 - Cannot join a pre-staged Computer Account on AD in Custom OU +# using Delegated user +Patch71: 0001-join-update-set-dNSHostName-if-not-set.patch + +# rhbz#1852080 - missing documentation for required AD rights for adcli join +# and net join +Patch72: 0001-doc-add-missing-samba_data_tool_path.xml-.in-to-EXTR.patch +Patch73: 0001-doc-explain-required-AD-permissions.patch + +# rhbz#1854112 - [RFE] Add new mode to just create an AD account to be able to +# connect to LDAP +Patch74: 0001-enroll-add-is_service-member.patch +Patch75: 0002-computer-add-create-msa-sub-command.patch +Patch76: 0003-enroll-use-computer-or-service-in-debug-messages.patch +Patch77: 0004-enroll-more-filters-for-random-characters.patch +Patch78: 0005-enroll-make-adcli_enroll_add_keytab_for_service_acco.patch +Patch79: 0006-enroll-allow-fqdn-for-locate_computer_account.patch +Patch80: 0007-service-account-add-random-suffix-to-account-name.patch + +# rhbz#1906303 - Typo in CREATE A SERVICE ACCOUNT section of man page of adcli +Patch81: 0001-service-account-fix-typo-in-the-man-page-entry.patch + +# rhbz#1889386 - [RFE] Adcli and Realm Error Code Optimization Request +Patch82: 0001-build-add-with-vendor-error-message-configure-option.patch + +# rhbz#1769644 - [RFE] adcli should allow to modify DONT_EXPIRE_PASSWORD attribute +Patch83: 0001-coverity-add-missing-NULL-checks.patch +Patch84: 0002-Add-dont-expire-password-option.patch +Patch85: 0001-Fix-for-dont-expire-password-option-and-join.patch + +# rhbz#1952828 - [RFE] Allow adcli to create AD user with password as well as +# set or reset existing user password +Patch86: 0001-library-move-UAC-flags-to-a-more-common-header-file.patch +Patch87: 0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch +Patch88: 0003-entry-add-passwd-user-sub-command.patch + +# rhbz#1690920 - [RFE] add option to populate "managed by" computer attribute +Patch89: 0001-Add-setattr-option.patch +Patch90: 0002-Add-delattr-option.patch + +BuildRequires: gcc +BuildRequires: intltool pkgconfig +BuildRequires: libtool +BuildRequires: gettext-devel +BuildRequires: krb5-devel +BuildRequires: openldap-devel +BuildRequires: libxslt +BuildRequires: xmlto + +Requires: cyrus-sasl-gssapi + +# adcli no longer has a library of development files +# the adcli tool itself is to be used by callers +Obsoletes: adcli-devel < 0.5 + +%description +adcli is a tool for joining an Active Directory domain using +standard LDAP and Kerberos calls. + +%define _hardened_build 1 + +%prep +%autosetup -p1 + +%build +autoreconf --force --install --verbose +%configure --disable-static --disable-silent-rules \ +%if 0%{?rhel} + --with-vendor-error-message='Please check\n https://red.ht/support_rhel_ad \nto get help for common issues.' \ +%endif + %{nil} +make %{?_smp_mflags} + +%check +make check + +%install +make install DESTDIR=%{buildroot} +find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%clean + +%files +%{_sbindir}/adcli +%doc AUTHORS COPYING ChangeLog NEWS README +%doc %{_mandir}/*/* + +%package doc +Summary: adcli documentation +BuildArch: noarch + +%description doc +adcli is a tool for joining an Active Directory domain using +standard LDAP and Kerberos calls. This package contains its +documentation. + +%files doc +%doc %{_datadir}/doc/adcli/* + +%changelog +* Mon Jun 14 2021 Sumit Bose - 0.8.2-12 +- [RFE] Allow adcli to create AD user with password as well as set or reset + existing user password [#1952828] +- [RFE] add option to populate "managed by" computer attribute [#1690920] + +* Thu Jun 03 2021 Sumit Bose - 0.8.2-11 +- Add missing patch for [#1769644] + +* Thu Jun 03 2021 Sumit Bose - 0.8.2-10 +- [RFE] Adcli and Realm Error Code Optimization Request [#1889386] +- [RFE] adcli should allow to modify DONT_EXPIRE_PASSWORD attribute [#1769644] + +* Fri Dec 11 2020 Sumit Bose - 0.8.2-9 +- Typo in CREATE A SERVICE ACCOUNT section of man page of adcli [#1906303] + +* Wed Nov 11 2020 Sumit Bose - 0.8.2-8 +- Add --use-ldaps option to adcli update as well [#1883467] +- Cannot join a pre-staged Computer Account on AD in Custom OU using Delegated + user [#1734764] +- missing documentation for required AD rights for adcli join and net + join [#1852080] +- [RFE] Add new mode to just create an AD account to be able to connect to + LDAP [#1854112] + +* Thu Aug 13 2020 Sumit Bose - 0.8.2-7 +- Improve "-C" option description in man page even more [#1791545] + +* Mon Jun 15 2020 Sumit Bose - 0.8.2-6 +- [abrt] [faf] adcli: raise(): /usr/sbin/adcli killed by 6 [#1806260] +- No longer able to delete computer from AD using adcli [#1846882] +- adcli: presetting $computer in $domain domain failed: Cannot set computer + password: Authentication error [#1846878] +- Typo in adcli update --help option [#1791611] +- Manpage and help does not explain the use of "-C" option [#1791545] + +* Wed Jan 29 2020 Sumit Bose - 0.8.2-5 +- adcli should be able to Force LDAPS over 636 with AD Access Provider w.r.t + sssd [#1762420] + +* Thu Nov 28 2019 Sumit Bose - 0.8.2-4 +- adcli update --add-samba-data does not work as expected [#1745931] +- Issue is that with arcfour-hmac as first encryption type [#1745932] +- [RFE] enhancement adcli to set description attribute and to show all AD + attributes [#1737342] + +* Fri Jun 14 2019 Sumit Bose - 0.8.2-3 +- use autosetup macro to simplify patch handling +- fixed rpmlint warnings in the spec file +- join failed if hostname is not FQDN [#1677194] +- adcli join fails in FIPS enabled environment [#1717355] +- forward port of RHEL-7.7 fixes and enhancements + +* Tue Oct 09 2018 Sumit Bose - 0.8.2-2 +- Do not add service principals twice and related fixes +- Resolves: rhbz#1631734 + +* Thu Jul 05 2018 Sumit Bose - 0.8.2-1 +- Update to upstream release 0.8.2 +- various other fixes and improvements from the latest Fedora update + +* Wed Feb 07 2018 Fedora Release Engineering - 0.8.0-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Wed Aug 02 2017 Fedora Release Engineering - 0.8.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 0.8.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Feb 10 2017 Fedora Release Engineering - 0.8.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Feb 03 2016 Fedora Release Engineering - 0.8.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Thu Dec 17 2015 Sumit Bose - 0.8.0-1 +- Update to upstream release 0.8.0 + +* Mon Oct 19 2015 Stef Walter - 0.7.6-1 +- Fix issue with keytab use with sshd +- Resolves: rhbz#1267319 +- Put documentation in a subpackage + +* Tue Jun 16 2015 Fedora Release Engineering - 0.7.5-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Fri Aug 15 2014 Fedora Release Engineering - 0.7.5-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Sat Jun 07 2014 Fedora Release Engineering - 0.7.5-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Thu Jan 30 2014 Stef Walter - 0.7.5-2 +- Fix incorrect ownership of manual page directory + +* Fri Sep 13 2013 Stef Walter - 0.7.5-1 +- Update to upstream point release 0.7.5 +- Workaround for discovery via IPv6 address +- Correctly put IPv6 addresses in temporary krb5.conf + +* Mon Sep 09 2013 Stef Walter - 0.7.4-1 +- Update to upstream point release 0.7.4 +- Correctly handle truncating long host names +- Try to contact all available addresses for discovery +- Build fixes + +* Wed Aug 07 2013 Stef Walter - 0.7.3-1 +- Update to upstream point release 0.7.3 +- Don't try to set encryption types on Windows 2003 + +* Sat Aug 03 2013 Fedora Release Engineering - 0.7.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Mon Jul 22 2013 Stef Walter - 0.7.2-1 +- Update to upstream point release 0.7.2 +- Part of fix for bug [#961244] + +* Mon Jul 15 2013 Stef Walter - 0.7.1-4 +- Build with verbose output logging + +* Tue Jun 11 2013 Stef Walter - 0.7.1-3 +- Run 'make check' when building the package + +* Mon May 13 2013 Stef Walter - 0.7.1-2 +- Bump version to get around botched update + +* Mon May 13 2013 Stef Walter - 0.7.1-1 +- Update to upstream 0.7.1 release +- Fix problems with salt discovery [#961399] + +* Mon May 06 2013 Stef Walter - 0.7-1 +- Work around broken krb5 with empty passwords [#960001] +- Fix memory corruption issue [#959999] +- Update to 0.7, fixing various bugs + +* Mon Apr 29 2013 Stef Walter - 0.6-1 +- Update to 0.6, fixing various bugs + +* Wed Apr 10 2013 Stef walter - 0.5-2 +- Add appropriate Obsoletes line for libadcli removal + +* Wed Apr 10 2013 Stef Walter - 0.5-1 +- Update to upstream 0.5 version +- No more libadcli, and thus no adcli-devel +- Many new adcli commands +- Documentation + +* Wed Feb 13 2013 Fedora Release Engineering - 0.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Mon Nov 12 2012 Stef Walter - 0.4-1 +- Update for 0.4 version, fixing various bugs + +* Sat Oct 20 2012 Stef Walter - 0.3-1 +- Update for 0.3 version + +* Tue Sep 4 2012 Stef Walter - 0.2-1 +- Update for 0.2 version + +* Wed Aug 15 2012 Stef Walter - 0.1-1 +- Initial 0.1 package diff --git a/sources b/sources new file mode 100644 index 0000000..54767e3 --- /dev/null +++ b/sources @@ -0,0 +1 @@ +SHA512 (adcli-0.8.2.tar.gz) = a46e3f4b3c5434557a75cfe1c44c8bc7e9e7c7e240fa3a903e0095ef58505c2bcc66e80aa7b9a6bcf3284aed1d9af4068037c57cd5bd9f68a0bde34f429c44e9