commit a9898996841253df27cc79e4aeb0c555410be8d2 Author: MSVSphere Packaging Team Date: Wed Mar 15 15:38:20 2023 +0300 import adcli-0.9.1-7.el9 diff --git a/.adcli.metadata b/.adcli.metadata new file mode 100644 index 0000000..877b146 --- /dev/null +++ b/.adcli.metadata @@ -0,0 +1 @@ +37e86e0447e9961bb1897ddecfc20445f646ee0f SOURCES/adcli-0.9.1.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..403f165 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/adcli-0.9.1.tar.gz 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..92c22f4 --- /dev/null +++ b/SOURCES/0001-Fix-for-dont-expire-password-option-and-join.patch @@ -0,0 +1,27 @@ +From 0d8482d4ed83677424f6c9428672d225bfdfe4d9 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 7653f89..f00d179 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -859,7 +859,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-configure-check-for-ns_get16-and-ns_get32-as-well.patch b/SOURCES/0001-configure-check-for-ns_get16-and-ns_get32-as-well.patch new file mode 100644 index 0000000..22f8a6a --- /dev/null +++ b/SOURCES/0001-configure-check-for-ns_get16-and-ns_get32-as-well.patch @@ -0,0 +1,38 @@ +From e841ba7513f3f8b6393183d2dea9adcbf7ba2e44 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 28 Jul 2021 12:55:16 +0200 +Subject: [PATCH] configure: check for ns_get16 and ns_get32 as well + +With newer versions of glibc res_query() might ba already available in +glibc with ns_get16() and ns_get32() still requires libresolv. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1984891 +--- + configure.ac | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/configure.ac b/configure.ac +index c6ff31d..fc6e790 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -98,13 +98,15 @@ AC_SUBST(LDAP_CFLAGS) + # ------------------------------------------------------------------- + # resolv + +-AC_MSG_CHECKING(for which library has res_query) ++AC_MSG_CHECKING([for which library has res_query, ns_get16 and ns_get32]) + for lib in "" "-lresolv"; do + saved_LIBS="$LIBS" + LIBS="$LIBS $lib" + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([#include ], +- [res_query (0, 0, 0, 0, 0)]) ++ [res_query (0, 0, 0, 0, 0); ++ ns_get32 (NULL); ++ ns_get16 (NULL);]) + ], + [ AC_MSG_RESULT(${lib:-libc}); have_res_query="yes"; break; ], + [ LIBS="$saved_LIBS" ]) +-- +2.31.1 + diff --git a/SOURCES/0001-configure-update-some-macros-for-autoconf-2.71.patch b/SOURCES/0001-configure-update-some-macros-for-autoconf-2.71.patch new file mode 100644 index 0000000..e9f0bc6 --- /dev/null +++ b/SOURCES/0001-configure-update-some-macros-for-autoconf-2.71.patch @@ -0,0 +1,84 @@ +From a8492d71a6db8565544444eef11de8c733c95ef8 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 6 Apr 2021 19:32:07 +0200 +Subject: [PATCH] configure: update some macros for autoconf-2.71 + +--- + configure.ac | 10 +++++----- + library/Makefile.am | 2 +- + tools/Makefile.am | 2 +- + 3 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 7dfba97..c6ff31d 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1,4 +1,4 @@ +-AC_PREREQ(2.61) ++AC_PREREQ([2.61]) + + AC_INIT([adcli], + [0.9.1], +@@ -33,7 +33,7 @@ LT_INIT([dlopen disable-static]) + AC_PROG_CC + AC_PROG_CPP + AM_PROG_CC_C_O +-AM_PROG_LIBTOOL ++LT_INIT + + # ------------------------------------------------------------------- + # Kerberos +@@ -143,7 +143,7 @@ AC_ARG_WITH([vendor-error-message], + + AC_MSG_CHECKING([whether to build documentation]) + AC_ARG_ENABLE(doc, +- AC_HELP_STRING([--enable-doc], ++ AS_HELP_STRING([--enable-doc], + [Disable building documentation]) + ) + +@@ -180,7 +180,7 @@ doc_status=$enable_doc + + AC_MSG_CHECKING([for debug mode]) + AC_ARG_ENABLE(debug, +- AC_HELP_STRING([--enable-debug=no/default/yes], ++ AS_HELP_STRING([--enable-debug=no/default/yes], + [Turn on or off debugging])) + + if test "$enable_debug" != "no"; then +@@ -308,7 +308,7 @@ fi + + AC_MSG_CHECKING([where is Samba's net utility]) + AC_ARG_WITH([samba_data_tool], +- AC_HELP_STRING([--with-samba-data-tool=/path], ++ AS_HELP_STRING([--with-samba-data-tool=/path], + [Path to Samba's net utility]), + [], + [with_samba_data_tool=/usr/bin/net]) +diff --git a/library/Makefile.am b/library/Makefile.am +index 4829555..e046606 100644 +--- a/library/Makefile.am ++++ b/library/Makefile.am +@@ -1,6 +1,6 @@ + include $(top_srcdir)/Makefile.decl + +-INCLUDES = \ ++AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -DADCLI_UNSTABLE_API \ + -DHOST_TRIPLET=\"$(host_triplet)\" \ +diff --git a/tools/Makefile.am b/tools/Makefile.am +index 1cdf451..71ec14d 100644 +--- a/tools/Makefile.am ++++ b/tools/Makefile.am +@@ -1,6 +1,6 @@ + include $(top_srcdir)/Makefile.decl + +-INCLUDES = \ ++AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/library \ + -DKRB5_CONFIG=\""$(sysconfdir)/krb5.conf"\" \ +-- +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..cb6c77b --- /dev/null +++ b/SOURCES/0001-coverity-add-missing-NULL-checks.patch @@ -0,0 +1,44 @@ +From 3c652910d05616ee12c710e2071fc884dde4eaea 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 2b830a4..0f3e8b9 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -3060,6 +3060,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..aea2a99 --- /dev/null +++ b/SOURCES/0001-library-move-UAC-flags-to-a-more-common-header-file.patch @@ -0,0 +1,51 @@ +From a7a40ce4f47fe40305624b6d86c135b7d27c387d Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 11 Jun 2021 12:44:36 +0200 +Subject: [PATCH 1/5] 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,14 +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_ACCOUNTDISABLE 0x0002 +-#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-dont-expire-password-option.patch b/SOURCES/0002-Add-dont-expire-password-option.patch new file mode 100644 index 0000000..5189f40 --- /dev/null +++ b/SOURCES/0002-Add-dont-expire-password-option.patch @@ -0,0 +1,229 @@ +From a78116ba0e608050f391223bad3834d48c9adf1b 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 | 44 +++++++++++++++++++++++++++++++++++++++++++- + library/adenroll.h | 4 ++++ + tools/computer.c | 12 ++++++++++++ + 4 files changed, 87 insertions(+), 1 deletion(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 8ec48d4..1ed5d3f 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. ++ ++ + + + Set or unset the ACCOUNTDISABLE +diff --git a/library/adenroll.c b/library/adenroll.c +index 0f3e8b9..7653f89 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -153,6 +153,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; + bool account_disable; + int account_disable_explicit; + char *description; +@@ -832,6 +834,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, +@@ -852,11 +856,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; +@@ -871,6 +885,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); + + /* +@@ -1577,6 +1592,14 @@ static char *get_user_account_control (adcli_enroll *enroll) + } + } + ++ 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 (enroll->account_disable_explicit) { + if (adcli_enroll_get_account_disable (enroll)) { + uac |= UAC_ACCOUNTDISABLE; +@@ -1627,6 +1650,7 @@ update_computer_account (adcli_enroll *enroll) + free (value); + + if (res == ADCLI_SUCCESS && (enroll->trusted_for_delegation_explicit || ++ enroll->dont_expire_password_explicit || + enroll->account_disable_explicit)) { + char *vals_userAccountControl[] = { NULL , NULL }; + LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } }; +@@ -3208,6 +3232,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; ++} ++ + bool + adcli_enroll_get_account_disable (adcli_enroll *enroll) + { +diff --git a/library/adenroll.h b/library/adenroll.h +index 8b1c1c7..34dc683 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); ++ + bool adcli_enroll_get_account_disable (adcli_enroll *enroll); + void adcli_enroll_set_account_disable (adcli_enroll *enroll, + bool value); +diff --git a/tools/computer.c b/tools/computer.c +index 6e309d9..16a1983 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, +@@ -144,6 +145,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_account_disable, "set/unset the ACCOUNTDISABLE flag\n" + "in the userAccountControl attribute", }, + { opt_add_service_principal, "add the given service principal to the account\n" }, +@@ -307,6 +310,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_account_disable: + if (strcasecmp (optarg, "true") == 0 || strcasecmp (optarg, "yes") == 0) { + adcli_enroll_set_account_disable (enroll, true); +@@ -393,6 +403,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 }, +@@ -516,6 +527,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 }, + { "account-disable", required_argument, NULL, opt_account_disable }, + { "add-service-principal", required_argument, NULL, opt_add_service_principal }, + { "remove-service-principal", required_argument, NULL, opt_remove_service_principal }, +-- +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..cb92099 --- /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/5] 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..7ad5111 --- /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/5] 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/SOURCES/0004-Add-setattr-option.patch b/SOURCES/0004-Add-setattr-option.patch new file mode 100644 index 0000000..7976e7c --- /dev/null +++ b/SOURCES/0004-Add-setattr-option.patch @@ -0,0 +1,386 @@ +From c5b0cee2976682b4fc1aeb02636cc9f2c6dbc2a5 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 14 Jun 2021 07:54:01 +0200 +Subject: [PATCH 4/5] 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,6 +150,7 @@ struct _adcli_enroll { + bool account_disable; + int account_disable_explicit; + 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, + opt_account_disable, + } 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/0005-Add-delattr-option.patch b/SOURCES/0005-Add-delattr-option.patch new file mode 100644 index 0000000..ee72cfe --- /dev/null +++ b/SOURCES/0005-Add-delattr-option.patch @@ -0,0 +1,192 @@ +From cd5b6cdcf3e6bfc5776f2865f460f608421dfa3f Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 14 Jun 2021 08:42:21 +0200 +Subject: [PATCH 5/5] 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,6 +151,7 @@ struct _adcli_enroll { + int account_disable_explicit; + 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, + opt_account_disable, + } 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/SPECS/adcli.spec b/SPECS/adcli.spec new file mode 100644 index 0000000..a70e02f --- /dev/null +++ b/SPECS/adcli.spec @@ -0,0 +1,287 @@ +Name: adcli +Version: 0.9.1 +Release: 7%{?dist} +Summary: Active Directory enrollment +License: LGPLv2+ +URL: https://gitlab.freedesktop.org/realmd/adcli +Source0: https://gitlab.freedesktop.org/sbose/adcli/uploads/30880d967e79cee789194435e70fbf30/adcli-%{version}.tar.gz + +Patch1: 0001-build-add-with-vendor-error-message-configure-option.patch + +# rhbz#1977167 - [RFE] adcli should allow to modify DONT_EXPIRE_PASSWORD +# attribute +Patch2: 0001-configure-update-some-macros-for-autoconf-2.71.patch +Patch3: 0001-coverity-add-missing-NULL-checks.patch +Patch4: 0002-Add-dont-expire-password-option.patch +Patch5: 0001-Fix-for-dont-expire-password-option-and-join.patch + +# rhbz#1977168 - [RFE] Allow adcli to create AD user with password as well as +# set or reset existing user password +Patch6: 0001-library-move-UAC-flags-to-a-more-common-header-file.patch +Patch7: 0002-adcli_entry-add-entry_attrs-with-userAccountControl-.patch +Patch8: 0003-entry-add-passwd-user-sub-command.patch + +# rhbz#1977165 - [RFE] add option to populate "managed by" computer attribute +Patch9: 0004-Add-setattr-option.patch +Patch10: 0005-Add-delattr-option.patch + +# rhbz#1984892 - adcli: FTBFS because of libresolv changes in glibc 2.34 +Patch11: 0001-configure-check-for-ns_get16-and-ns_get32-as-well.patch + +BuildRequires: gcc +BuildRequires: intltool pkgconfig +BuildRequires: libtool +BuildRequires: gettext-devel +BuildRequires: krb5-devel +BuildRequires: openldap-devel +BuildRequires: libxslt +BuildRequires: xmlto +BuildRequires: make + +Requires: cyrus-sasl-gssapi +Conflicts: adcli-doc < %{version}-%{release} + +# 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 {} ';' + +%ldconfig_scriptlets + +%files +%{_sbindir}/adcli +%doc AUTHORS COPYING ChangeLog NEWS README +%doc %{_mandir}/*/* + +%package doc +Summary: adcli documentation +BuildArch: noarch +Conflicts: adcli < %{version}-%{release} + +%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 +* Wed Mar 15 2023 MSVSphere Packaging Team - 0.9.1-7 +- Rebuilt for MSVSphere 9.1. + +* Mon Aug 09 2021 Mohan Boddu - 0.9.1-7 +- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags + Related: rhbz#1991688 + +* Wed Jul 28 2021 Sumit Bose - 0.9.1-6 +- Add ns_get16() and ns_get32() to configure check + Resolves: rhbz#1984892 + +* Wed Jun 30 2021 Sumit Bose - 0.9.1-5 +- Sync with upstream/Fedora/RHEL-8.5 + Resolves: rhbz#1977168, rhbz#1977167, rhbz#1977165 + +* Thu Apr 15 2021 Mohan Boddu - 0.9.1-4 +- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 + +* Mon Mar 29 2021 Sumit Bose - 0.9.1-3 +- Add vendor error message + Resolves: rhbz#1889386 + +* Sat Feb 20 2021 Sumit Bose - 0.9.1-2 +- Add Conflicts to avoid update/downgrade issues + +* Sat Feb 20 2021 Sumit Bose - 0.9.1-1 +- Update to upstream release 0.9.1 + +* Mon Jan 25 2021 Fedora Release Engineering - 0.9.0-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Fri Nov 13 2020 Sumit Bose - 0.9.0-6 +- Include the latest upstream patches with use-ldaps fixes, man page + improvements and a new sub-command to create managed service accounts + +* Thu Aug 13 2020 Sumit Bose - 0.9.0-5 +- man page and help output fixes + +* Fri Jul 31 2020 Fedora Release Engineering - 0.9.0-4 +- Second attempt - Rebuilt for + https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Mon Jul 27 2020 Fedora Release Engineering - 0.9.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Mon Jun 08 2020 Sumit Bose - 0.9.0-2 +- Include the latest upstream patches + +* Wed Mar 18 2020 Sumit Bose - 0.9.0-1 +- Update to upstream release 0.9.0 and latest patches + +* Tue Jan 28 2020 Fedora Release Engineering - 0.8.2-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Mon Aug 26 2019 Sumit Bose - 0.8.2-8 +- various fixes and improvements + Resolves: rhbz#1683745, rhbz#1738573 + +* Wed Jul 24 2019 Fedora Release Engineering - 0.8.2-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Fri Jul 5 2019 Jakub Hrozek - 0.8.2-6 +- Resolves: rhbz#1727144 - adcli join fails with new krb5-libs; adcli + needs to backport patches to only use permitted + enctypes from upstream + +* Tue Apr 30 2019 Sumit Bose - 0.8.2-5 +- addition patch for rhbz#1630187 and new ones for rhbz#1588596 + Resolves: rhbz#1630187, rhbz#1588596 + +* Fri Mar 22 2019 Sumit Bose - 0.8.2-4 +- various fixes and improvements + Resolves: rhbz#1593240, rhbz#1608212, rhbz#1547014, rhbz#1547014, + rhbz#1649868, rhbz#1588596, rhbz#1642546, rhbz#1595911, + rhbz#1644311, rhbz#1337489, rhbz#1630187, rhbz#1622583 + +* Thu Jan 31 2019 Fedora Release Engineering - 0.8.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Thu Jul 12 2018 Fedora Release Engineering - 0.8.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Thu Jul 05 2018 Sumit Bose - 0.8.0-1 +- Update to upstream release 0.8.2 +- various other fixes and improvements +- add option to enable "Trust this computer for delegation" + Resolves: rhbz#988349 +- fix typos in the adcli man page + Resolves: rhbz#1440533 + +* Wed Mar 07 2018 Sumit Bose - 0.8.0-7 +- Added BuildRequires gcc + +* 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