From 989367d020de4c5e03936f70449681cda9d1910c Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Sat, 19 Jun 2021 04:19:36 +0000 Subject: [PATCH] import adcli-0.8.2-12.el8 --- SOURCES/0001-Add-setattr-option.patch | 384 ++++++++++++++++++ ...dont-expire-password-option-and-join.patch | 27 ++ ...endor-error-message-configure-option.patch | 60 +++ ...001-coverity-add-missing-NULL-checks.patch | 44 ++ ...C-flags-to-a-more-common-header-file.patch | 50 +++ SOURCES/0002-Add-delattr-option.patch | 191 +++++++++ ...0002-Add-dont-expire-password-option.patch | 241 +++++++++++ ...entry_attrs-with-userAccountControl-.patch | 60 +++ ...03-entry-add-passwd-user-sub-command.patch | 366 +++++++++++++++++ SPECS/adcli.spec | 38 +- 10 files changed, 1459 insertions(+), 2 deletions(-) create mode 100644 SOURCES/0001-Add-setattr-option.patch create mode 100644 SOURCES/0001-Fix-for-dont-expire-password-option-and-join.patch create mode 100644 SOURCES/0001-build-add-with-vendor-error-message-configure-option.patch create mode 100644 SOURCES/0001-coverity-add-missing-NULL-checks.patch create mode 100644 SOURCES/0001-library-move-UAC-flags-to-a-more-common-header-file.patch create mode 100644 SOURCES/0002-Add-delattr-option.patch create mode 100644 SOURCES/0002-Add-dont-expire-password-option.patch create mode 100644 SOURCES/0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch create mode 100644 SOURCES/0003-entry-add-passwd-user-sub-command.patch diff --git a/SOURCES/0001-Add-setattr-option.patch b/SOURCES/0001-Add-setattr-option.patch new file mode 100644 index 0000000..abc40bb --- /dev/null +++ b/SOURCES/0001-Add-setattr-option.patch @@ -0,0 +1,384 @@ +From c5b0cee2976682b4fc1aeb02636cc9f2c6dbc2a5 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +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/SOURCES/0001-Fix-for-dont-expire-password-option-and-join.patch b/SOURCES/0001-Fix-for-dont-expire-password-option-and-join.patch new file mode 100644 index 0000000..9740f31 --- /dev/null +++ b/SOURCES/0001-Fix-for-dont-expire-password-option-and-join.patch @@ -0,0 +1,27 @@ +From 924465af7a4f37390bfdfdb4971e88421f52f3d9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 3 Jun 2021 15:03:20 +0200 +Subject: [PATCH] Fix for dont-expire-password option and join + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1769644 +--- + library/adenroll.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index f3d606e..5b0dcd5 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -856,7 +856,8 @@ create_computer_account (adcli_enroll *enroll, + uac |= UAC_TRUSTED_FOR_DELEGATION; + } + +- if (!adcli_enroll_get_dont_expire_password (enroll)) { ++ if (enroll->dont_expire_password_explicit ++ && !adcli_enroll_get_dont_expire_password (enroll)) { + uac &= ~(UAC_DONT_EXPIRE_PASSWORD); + } + +-- +2.31.1 + diff --git a/SOURCES/0001-build-add-with-vendor-error-message-configure-option.patch b/SOURCES/0001-build-add-with-vendor-error-message-configure-option.patch new file mode 100644 index 0000000..75235ee --- /dev/null +++ b/SOURCES/0001-build-add-with-vendor-error-message-configure-option.patch @@ -0,0 +1,60 @@ +From 0353d704879f20983184f8bded4f16538d72f7cc Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 10 Mar 2021 18:12:09 +0100 +Subject: [PATCH] build: add --with-vendor-error-message configure option + +With the new configure option --with-vendor-error-message a packager or +a distribution can add a message if adcli returns with an error. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1889386 +--- + configure.ac | 15 +++++++++++++++ + tools/tools.c | 6 ++++++ + 2 files changed, 21 insertions(+) + +diff --git a/configure.ac b/configure.ac +index baa0d3b..7dfba97 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -123,6 +123,21 @@ if test "$sasl_invalid" = "yes"; then + AC_MSG_ERROR([Couldn't find Cyrus SASL headers]) + fi + ++# -------------------------------------------------------------------- ++# Vendor error message ++ ++AC_ARG_WITH([vendor-error-message], ++ [AS_HELP_STRING([--with-vendor-error-message=ARG], ++ [Add a vendor specific error message shown if a adcli command fails] ++ )], ++ [AS_IF([test "x$withval" != "x"], ++ [AC_DEFINE_UNQUOTED([VENDOR_MSG], ++ ["$withval"], ++ [Vendor specific error message])], ++ [AC_MSG_ERROR([--with-vendor-error-message requires an argument])] ++ )], ++ []) ++ + # -------------------------------------------------------------------- + # Documentation options + +diff --git a/tools/tools.c b/tools/tools.c +index d0dcf98..84bbba9 100644 +--- a/tools/tools.c ++++ b/tools/tools.c +@@ -538,6 +538,12 @@ main (int argc, + + if (conn) + adcli_conn_unref (conn); ++#ifdef VENDOR_MSG ++ if (ret != 0) { ++ fprintf (stderr, VENDOR_MSG"\n"); ++ } ++#endif ++ + return ret; + } + +-- +2.30.2 + diff --git a/SOURCES/0001-coverity-add-missing-NULL-checks.patch b/SOURCES/0001-coverity-add-missing-NULL-checks.patch new file mode 100644 index 0000000..2f41992 --- /dev/null +++ b/SOURCES/0001-coverity-add-missing-NULL-checks.patch @@ -0,0 +1,44 @@ +From 13fe79c0a78028ccfe8e3d4e5ee16cfb9e143924 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 2 Jun 2021 13:39:31 +0200 +Subject: [PATCH 1/2] coverity: add missing NULL checks + +--- + library/adenroll.c | 2 ++ + library/adldap.c | 7 +++++++ + 2 files changed, 9 insertions(+) + +diff --git a/library/adenroll.c b/library/adenroll.c +index f693e58..c726093 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -3046,6 +3046,8 @@ adcli_enroll_set_keytab_enctypes (adcli_enroll *enroll, + krb5_enctype *newval = NULL; + int len; + ++ return_if_fail (enroll != NULL); ++ + if (value) { + for (len = 0; value[len] != 0; len++); + newval = malloc (sizeof (krb5_enctype) * (len + 1)); +diff --git a/library/adldap.c b/library/adldap.c +index d93efb7..b86014c 100644 +--- a/library/adldap.c ++++ b/library/adldap.c +@@ -231,6 +231,13 @@ _adcli_ldap_have_in_mod (LDAPMod *mod, + + vals = malloc (sizeof (struct berval) * (count + 1)); + pvals = malloc (sizeof (struct berval *) * (count + 1)); ++ if (vals == NULL || pvals == NULL) { ++ _adcli_err ("Memory allocation failed, assuming attribute must be updated."); ++ free (vals); ++ free (pvals); ++ return 0; ++ } ++ + for (i = 0; i < count; i++) { + vals[i].bv_val = mod->mod_vals.modv_strvals[i]; + vals[i].bv_len = strlen (vals[i].bv_val); +-- +2.31.1 + diff --git a/SOURCES/0001-library-move-UAC-flags-to-a-more-common-header-file.patch b/SOURCES/0001-library-move-UAC-flags-to-a-more-common-header-file.patch new file mode 100644 index 0000000..0129517 --- /dev/null +++ b/SOURCES/0001-library-move-UAC-flags-to-a-more-common-header-file.patch @@ -0,0 +1,50 @@ +From a7a40ce4f47fe40305624b6d86c135b7d27c387d Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 11 Jun 2021 12:44:36 +0200 +Subject: [PATCH 1/3] library: move UAC flags to a more common header file + +--- + library/adenroll.c | 8 -------- + library/adprivate.h | 8 ++++++++ + 2 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index f00d179..0b1c066 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -93,13 +93,6 @@ static char *default_ad_ldap_attrs[] = { + NULL, + }; + +-/* Some constants for the userAccountControl AD LDAP attribute, see e.g. +- * https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro +- * for details. */ +-#define UAC_WORKSTATION_TRUST_ACCOUNT 0x1000 +-#define UAC_DONT_EXPIRE_PASSWORD 0x10000 +-#define UAC_TRUSTED_FOR_DELEGATION 0x80000 +- + struct _adcli_enroll { + int refs; + adcli_conn *conn; +diff --git a/library/adprivate.h b/library/adprivate.h +index 55e6234..822f919 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -39,6 +39,14 @@ + #define HOST_NAME_MAX 255 + #endif + ++/* Some constants for the userAccountControl AD LDAP attribute, see e.g. ++ * https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro ++ * for details. */ ++#define UAC_ACCOUNTDISABLE 0x0002 ++#define UAC_WORKSTATION_TRUST_ACCOUNT 0x1000 ++#define UAC_DONT_EXPIRE_PASSWORD 0x10000 ++#define UAC_TRUSTED_FOR_DELEGATION 0x80000 ++ + /* Utilities */ + + #if !defined(__cplusplus) && (__GNUC__ > 2) +-- +2.31.1 + diff --git a/SOURCES/0002-Add-delattr-option.patch b/SOURCES/0002-Add-delattr-option.patch new file mode 100644 index 0000000..92cf623 --- /dev/null +++ b/SOURCES/0002-Add-delattr-option.patch @@ -0,0 +1,191 @@ +From cd5b6cdcf3e6bfc5776f2865f460f608421dfa3f Mon Sep 17 00:00:00 2001 +From: Sumit Bose +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/SOURCES/0002-Add-dont-expire-password-option.patch b/SOURCES/0002-Add-dont-expire-password-option.patch new file mode 100644 index 0000000..0ae8a90 --- /dev/null +++ b/SOURCES/0002-Add-dont-expire-password-option.patch @@ -0,0 +1,241 @@ +From 74b52a30c2b142118b7f26f78c014e7bee825e84 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +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/SOURCES/0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch b/SOURCES/0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch new file mode 100644 index 0000000..58d91ec --- /dev/null +++ b/SOURCES/0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch @@ -0,0 +1,60 @@ +From 7148ab196d0a96ede9b5ef463b0481d0fe372b21 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 11 Jun 2021 12:46:03 +0200 +Subject: [PATCH 2/3] adcli_entry: add entry_attrs with userAccountControl + attribute + +--- + library/adentry.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/library/adentry.c b/library/adentry.c +index 1cc0518..13dcaf8 100644 +--- a/library/adentry.c ++++ b/library/adentry.c +@@ -42,6 +42,7 @@ struct _adcli_entry { + char *entry_dn; + char *domain_ou; + char *entry_container; ++ LDAPMessage *entry_attrs; + }; + + static adcli_entry * +@@ -63,6 +64,7 @@ entry_new (adcli_conn *conn, + + entry->builder = builder; + entry->object_class = object_class; ++ entry->entry_attrs = NULL; + return entry; + } + +@@ -82,6 +84,7 @@ entry_free (adcli_entry *entry) + free (entry->entry_container); + free (entry->entry_dn); + free (entry->domain_ou); ++ ldap_msgfree (entry->entry_attrs); + adcli_conn_unref (entry->conn); + free (entry); + } +@@ -102,7 +105,7 @@ static adcli_result + update_entry_from_domain (adcli_entry *entry, + LDAP *ldap) + { +- const char *attrs[] = { "1.1", NULL }; ++ const char *attrs[] = { "userAccountControl", NULL }; + LDAPMessage *results; + LDAPMessage *first; + const char *base; +@@ -139,7 +142,8 @@ update_entry_from_domain (adcli_entry *entry, + return_unexpected_if_fail (entry->entry_dn != NULL); + } + +- ldap_msgfree (results); ++ ldap_msgfree (entry->entry_attrs); ++ entry->entry_attrs = results; + return ADCLI_SUCCESS; + } + +-- +2.31.1 + diff --git a/SOURCES/0003-entry-add-passwd-user-sub-command.patch b/SOURCES/0003-entry-add-passwd-user-sub-command.patch new file mode 100644 index 0000000..413d7ab --- /dev/null +++ b/SOURCES/0003-entry-add-passwd-user-sub-command.patch @@ -0,0 +1,366 @@ +From 6a673b236dfdfdf9c73cc3d2ccf3949eb1a5ddd0 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +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/SPECS/adcli.spec b/SPECS/adcli.spec index 6378940..3fc795c 100644 --- a/SPECS/adcli.spec +++ b/SPECS/adcli.spec @@ -1,6 +1,6 @@ Name: adcli Version: 0.8.2 -Release: 9%{?dist} +Release: 12%{?dist} Summary: Active Directory enrollment License: LGPLv2+ URL: http://cgit.freedesktop.org/realmd/adcli @@ -150,6 +150,24 @@ Patch80: 0007-service-account-add-random-suffix-to-account-name.patch # rhbz#1906303 - Typo in CREATE A SERVICE ACCOUNT section of man page of adcli Patch81: 0001-service-account-fix-typo-in-the-man-page-entry.patch +# rhbz#1889386 - [RFE] Adcli and Realm Error Code Optimization Request +Patch82: 0001-build-add-with-vendor-error-message-configure-option.patch + +# rhbz#1769644 - [RFE] adcli should allow to modify DONT_EXPIRE_PASSWORD attribute +Patch83: 0001-coverity-add-missing-NULL-checks.patch +Patch84: 0002-Add-dont-expire-password-option.patch +Patch85: 0001-Fix-for-dont-expire-password-option-and-join.patch + +# rhbz#1952828 - [RFE] Allow adcli to create AD user with password as well as +# set or reset existing user password +Patch86: 0001-library-move-UAC-flags-to-a-more-common-header-file.patch +Patch87: 0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch +Patch88: 0003-entry-add-passwd-user-sub-command.patch + +# rhbz#1690920 - [RFE] add option to populate "managed by" computer attribute +Patch89: 0001-Add-setattr-option.patch +Patch90: 0002-Add-delattr-option.patch + BuildRequires: gcc BuildRequires: intltool pkgconfig BuildRequires: libtool @@ -176,7 +194,11 @@ standard LDAP and Kerberos calls. %build autoreconf --force --install --verbose -%configure --disable-static --disable-silent-rules +%configure --disable-static --disable-silent-rules \ +%if 0%{?rhel} + --with-vendor-error-message='Please check\n https://red.ht/support_rhel_ad \nto get help for common issues.' \ +%endif + %{nil} make %{?_smp_mflags} %check @@ -210,6 +232,18 @@ documentation. %doc %{_datadir}/doc/adcli/* %changelog +* Mon Jun 14 2021 Sumit Bose - 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]