From 7ddab3d4d1ed2ed03d405c6eeb3ac9cadd141225 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: May 07 2019 05:17:23 +0000 Subject: import adcli-0.8.2-2.el8 --- diff --git a/.adcli.metadata b/.adcli.metadata new file mode 100644 index 0000000..fda023a --- /dev/null +++ b/.adcli.metadata @@ -0,0 +1 @@ +f3f9537502eba38a22889fbab6e1100c32175ca2 SOURCES/adcli-0.8.2.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d651b5a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/adcli-0.8.2.tar.gz diff --git a/SOURCES/0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch b/SOURCES/0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch new file mode 100644 index 0000000..964c6bf --- /dev/null +++ b/SOURCES/0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch @@ -0,0 +1,31 @@ +From d8eb0f5704f34cb7d411cd275d32c63ead297b8d Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 24 Aug 2016 15:37:41 +0200 +Subject: [PATCH 01/23] Remove upper-case only check when looking for the + NetBIOS name + +It is a convention to use only upper-case letters for NetBIOS names but +it is not enforced on the AD-side. With the new option to specify a +random NetBIOS name it is possible to create host entries in AD with +lower-case letters in the name. To properly determine the name from the +keytab the upper-case check should be dropped,dc= +--- + library/adenroll.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index a15e4be..d1020e9 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1309,7 +1309,7 @@ load_keytab_entry (krb5_context k5, + if (!enroll->host_fqdn_explicit && !enroll->computer_name_explicit) { + + /* Automatically use the netbios name */ +- if (!enroll->computer_name && len > 1 && _adcli_str_is_up (name) && ++ if (!enroll->computer_name && len > 1 && + _adcli_str_has_suffix (name, "$") && !strchr (name, '/')) { + enroll->computer_name = name; + name[len - 1] = '\0'; +-- +2.14.4 + diff --git a/SOURCES/0001-fix-typo-in-flag-value.patch b/SOURCES/0001-fix-typo-in-flag-value.patch new file mode 100644 index 0000000..07577b7 --- /dev/null +++ b/SOURCES/0001-fix-typo-in-flag-value.patch @@ -0,0 +1,25 @@ +From 870ecd8f982ebb97092a93d730ad4006bd78505c Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 8 Aug 2018 12:03:01 +0200 +Subject: [PATCH 1/4] fix typo in flag value + +--- + library/adenroll.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/library/adenroll.h b/library/adenroll.h +index f87dffa..abbbfd4 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -30,7 +30,7 @@ typedef enum { + ADCLI_ENROLL_NO_KEYTAB = 1 << 1, + ADCLI_ENROLL_ALLOW_OVERWRITE = 1 << 2, + ADCLI_ENROLL_PASSWORD_VALID = 1 << 3, +- ADCLI_ENROLL_ADD_SAMBA_DATA = 1 << 3, ++ ADCLI_ENROLL_ADD_SAMBA_DATA = 1 << 4, + } adcli_enroll_flags; + + typedef struct _adcli_enroll adcli_enroll; +-- +2.17.1 + diff --git a/SOURCES/0002-Use-strdup-if-offset-are-used.patch b/SOURCES/0002-Use-strdup-if-offset-are-used.patch new file mode 100644 index 0000000..47ea1ad --- /dev/null +++ b/SOURCES/0002-Use-strdup-if-offset-are-used.patch @@ -0,0 +1,31 @@ +From 4ba49015ca1ad98c03a209a11862f8e00d00fbd0 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 24 Aug 2016 16:19:36 +0200 +Subject: [PATCH 02/23] Use strdup() if offset are used + +Strings with an offset to the original starting point must be copied +because otherwise they cannot be properly freed later. +--- + library/adenroll.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index d1020e9..05885d0 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1318,9 +1318,9 @@ load_keytab_entry (krb5_context k5, + + } else if (!enroll->host_fqdn && _adcli_str_has_prefix (name, "host/") && strchr (name, '.')) { + /* Skip host/ prefix */ +- enroll->host_fqdn = name + 5; +- _adcli_info ("Found host qualified name in keytab: %s", name); +- name = NULL; ++ enroll->host_fqdn = strdup (name + 5); ++ return_val_if_fail (enroll->host_fqdn != NULL, FALSE); ++ _adcli_info ("Found host qualified name in keytab: %s", enroll->host_fqdn); + } + } + +-- +2.14.4 + diff --git a/SOURCES/0002-_adcli_call_external_program-silence-noisy-debug-mes.patch b/SOURCES/0002-_adcli_call_external_program-silence-noisy-debug-mes.patch new file mode 100644 index 0000000..76fe1dc --- /dev/null +++ b/SOURCES/0002-_adcli_call_external_program-silence-noisy-debug-mes.patch @@ -0,0 +1,25 @@ +From 8cc4ef1cae7d4d753f2cf9aeb8021dd96cb75d36 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 8 Aug 2018 12:17:18 +0200 +Subject: [PATCH 2/4] _adcli_call_external_program: silence noisy debug message + +--- + library/adutil.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/library/adutil.c b/library/adutil.c +index 6334b52..17d2caa 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -672,7 +672,7 @@ done: + if (wret == -1) { + _adcli_err ("No sure what happend to net command."); + } else { +- if (WIFEXITED (status)) { ++ if (WIFEXITED (status) && WEXITSTATUS (status) != 0) { + _adcli_err ("net command failed with %d.", + WEXITSTATUS (status)); + } +-- +2.17.1 + diff --git a/SOURCES/0003-Do-not-add-service-principals-twice.patch b/SOURCES/0003-Do-not-add-service-principals-twice.patch new file mode 100644 index 0000000..5d0bfcf --- /dev/null +++ b/SOURCES/0003-Do-not-add-service-principals-twice.patch @@ -0,0 +1,57 @@ +From f05adc23d5cc9f1dfa5638e31949dcd81d632df9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 13 Aug 2018 17:32:24 +0200 +Subject: [PATCH 3/4] Do not add service principals twice + +--- + library/adenroll.c | 23 +++++++++++++++++++---- + 1 file changed, 19 insertions(+), 4 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index c4ba537..bb50365 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -313,6 +313,7 @@ add_service_names_to_service_principals (adcli_enroll *enroll) + char *name; + int length = 0; + int i; ++ size_t c; + + if (enroll->service_principals != NULL) { + length = seq_count (enroll->service_principals); +@@ -321,14 +322,28 @@ add_service_names_to_service_principals (adcli_enroll *enroll) + for (i = 0; enroll->service_names[i] != NULL; i++) { + if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0) + return_unexpected_if_reached (); +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, +- name, &length); ++ for (c = 0; enroll->service_principals != NULL && enroll->service_principals[c] != NULL; c++) { ++ if (strcmp (name, enroll->service_principals[c]) == 0) { ++ break; ++ } ++ } ++ if (enroll->service_principals == NULL || enroll->service_principals[c] == NULL) { ++ enroll->service_principals = _adcli_strv_add (enroll->service_principals, ++ name, &length); ++ } + + if (enroll->host_fqdn) { + if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0) + return_unexpected_if_reached (); +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, +- name, &length); ++ for (c = 0; enroll->service_principals != NULL && enroll->service_principals[c] != NULL; c++) { ++ if (strcmp (name, enroll->service_principals[c]) == 0) { ++ break; ++ } ++ } ++ if (enroll->service_principals == NULL || enroll->service_principals[c] == NULL) { ++ enroll->service_principals = _adcli_strv_add (enroll->service_principals, ++ name, &length); ++ } + } + } + +-- +2.17.1 + diff --git a/SOURCES/0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch b/SOURCES/0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch new file mode 100644 index 0000000..59fb6fe --- /dev/null +++ b/SOURCES/0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch @@ -0,0 +1,26 @@ +From 85fa595baf689e85c0d897c5eef73fdf1ecc1581 Mon Sep 17 00:00:00 2001 +From: Striker Leggette +Date: Wed, 1 Nov 2017 11:16:39 +0100 +Subject: [PATCH 03/23] correct spelling of 'adcli_tool_computer_delete' + description + +--- + tools/tools.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/tools.c b/tools/tools.c +index 4b243de..915130e 100644 +--- a/tools/tools.c ++++ b/tools/tools.c +@@ -57,7 +57,7 @@ struct { + { "update", adcli_tool_computer_update, "Update machine membership in a domain", }, + { "preset-computer", adcli_tool_computer_preset, "Pre setup computers accounts", }, + { "reset-computer", adcli_tool_computer_reset, "Reset a computer account", }, +- { "delete-computer", adcli_tool_computer_delete, "Delete a computer acocunt", }, ++ { "delete-computer", adcli_tool_computer_delete, "Delete a computer account", }, + { "create-user", adcli_tool_user_create, "Create a user account", }, + { "delete-user", adcli_tool_user_delete, "Delete a user account", }, + { "create-group", adcli_tool_group_create, "Create a group", }, +-- +2.14.4 + diff --git a/SOURCES/0004-Do-not-depend-on-default_realm-in-krb5.conf.patch b/SOURCES/0004-Do-not-depend-on-default_realm-in-krb5.conf.patch new file mode 100644 index 0000000..d12f335 --- /dev/null +++ b/SOURCES/0004-Do-not-depend-on-default_realm-in-krb5.conf.patch @@ -0,0 +1,27 @@ +From 8f726817b9ff643a382fa12ea9ff489cd5ab9068 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 13 Aug 2018 18:24:58 +0200 +Subject: [PATCH 4/4] Do not depend on default_realm in krb5.conf + +--- + library/adenroll.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index bb50365..02bd9e3 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1661,7 +1661,9 @@ remove_principal_from_keytab (adcli_enroll *enroll, + krb5_principal principal; + match_principal_kvno closure; + +- code = krb5_parse_name (k5, principal_name, &principal); ++ code = _adcli_krb5_build_principal (k5, principal_name, ++ adcli_conn_get_domain_realm (enroll->conn), ++ &principal); + if (code != 0) { + _adcli_err ("Couldn't parse principal: %s: %s", + principal_name, krb5_get_error_message (k5, code)); +-- +2.17.1 + diff --git a/SOURCES/0004-doc-explain-that-all-credential-cache-types-are-supp.patch b/SOURCES/0004-doc-explain-that-all-credential-cache-types-are-supp.patch new file mode 100644 index 0000000..e0dbc67 --- /dev/null +++ b/SOURCES/0004-doc-explain-that-all-credential-cache-types-are-supp.patch @@ -0,0 +1,37 @@ +From c3ec5121c1e79344ce615612ab3b576bc4745acb Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 1 Nov 2017 12:01:18 +0100 +Subject: [PATCH 04/23] doc: explain that all credential cache types are + supported + +--- + doc/adcli.xml | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index e18ba5d..c54cc1b 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -118,11 +118,15 @@ + is automatically discovered. + + +- ++ + Use the specified kerberos credential +- cache to authenticate with the domain. If no file is specified or +- is used, then the default kerberos credential cache will +- be used. ++ cache to authenticate with the domain. If no credential ++ cache is specified, the default kerberos credential ++ cache will be used. Credential caches of type FILE can ++ be given with the path to the file. For other ++ credential cache types, e.g. DIR, KEYRING or KCM, the ++ type must be specified explicitly together with a ++ suitable identifier. + + + +-- +2.14.4 + diff --git a/SOURCES/0005-library-add-adcli_conn_is_writeable.patch b/SOURCES/0005-library-add-adcli_conn_is_writeable.patch new file mode 100644 index 0000000..832bb0f --- /dev/null +++ b/SOURCES/0005-library-add-adcli_conn_is_writeable.patch @@ -0,0 +1,38 @@ +From d2cdc54b0e51436c30ffaf19b0530aa446440367 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 1 Nov 2017 16:29:19 +0100 +Subject: [PATCH 05/23] library: add adcli_conn_is_writeable() + +--- + library/adconn.c | 6 ++++++ + library/adconn.h | 2 ++ + 2 files changed, 8 insertions(+) + +diff --git a/library/adconn.c b/library/adconn.c +index a294dfd..67bdfd9 100644 +--- a/library/adconn.c ++++ b/library/adconn.c +@@ -1528,3 +1528,9 @@ adcli_conn_server_has_capability (adcli_conn *conn, + + return 0; + } ++ ++bool adcli_conn_is_writeable (adcli_conn *conn) ++{ ++ disco_dance_if_necessary (conn); ++ return ( (conn->domain_disco->flags & ADCLI_DISCO_WRITABLE) != 0); ++} +diff --git a/library/adconn.h b/library/adconn.h +index a0cb1f8..ed1cc58 100644 +--- a/library/adconn.h ++++ b/library/adconn.h +@@ -144,4 +144,6 @@ void adcli_conn_set_krb5_conf_dir (adcli_conn *conn, + int adcli_conn_server_has_capability (adcli_conn *conn, + const char *capability); + ++bool adcli_conn_is_writeable (adcli_conn *conn); ++ + #endif /* ADCONN_H_ */ +-- +2.14.4 + diff --git a/SOURCES/0006-Handle-kvno-increment-for-RODCs.patch b/SOURCES/0006-Handle-kvno-increment-for-RODCs.patch new file mode 100644 index 0000000..90f05a4 --- /dev/null +++ b/SOURCES/0006-Handle-kvno-increment-for-RODCs.patch @@ -0,0 +1,67 @@ +From 6b60f4c08d811e4bc3a68d1a4770c2ce5619c890 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 1 Nov 2017 17:14:05 +0100 +Subject: [PATCH 06/23] Handle kvno increment for RODCs + +Since the actual password change does not happen on the read-only domain +controller (RODC) the kvno change has to be replicated back which might +take some time. So we check the kvno before and after the change if we +are connected to a RODC and increment the kvno if needed. +--- + library/adenroll.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 05885d0..bb970d1 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1633,8 +1633,30 @@ enroll_join_or_update_tasks (adcli_enroll *enroll, + adcli_enroll_flags flags) + { + adcli_result res; ++ krb5_kvno old_kvno = -1; + + if (!(flags & ADCLI_ENROLL_PASSWORD_VALID)) { ++ ++ /* Handle kvno changes for read-only domain controllers ++ * (RODC). Since the actual password change does not happen on ++ * the RODC the kvno change has to be replicated back which ++ * might take some time. So we check the kvno before and after ++ * the change if we are connected to a RODC and increment the ++ * kvno if needed. */ ++ if (!adcli_conn_is_writeable (enroll->conn)) { ++ if (enroll->computer_attributes == NULL) { ++ res = retrieve_computer_account (enroll); ++ if (res != ADCLI_SUCCESS) ++ return res; ++ } ++ old_kvno = adcli_enroll_get_kvno (enroll); ++ _adcli_info ("Found old kvno '%d'", old_kvno); ++ ++ ldap_msgfree (enroll->computer_attributes); ++ enroll->computer_attributes = NULL; ++ adcli_enroll_set_kvno (enroll, 0); ++ } ++ + res = set_computer_password (enroll); + if (res != ADCLI_SUCCESS) + return res; +@@ -1651,6 +1673,15 @@ enroll_join_or_update_tasks (adcli_enroll *enroll, + return res; + } + ++ /* Handle kvno changes for read-only domain controllers (RODC) */ ++ if (!adcli_conn_is_writeable (enroll->conn) && old_kvno != -1 && ++ adcli_enroll_get_kvno (enroll) != 0 && ++ adcli_enroll_get_kvno (enroll) == old_kvno) { ++ enroll->kvno++; ++ _adcli_info ("No kvno change detected on read-only DC, kvno " ++ "will be incremented by 1 to '%d'", enroll->kvno); ++ } ++ + /* We ignore failures of setting these fields */ + update_and_calculate_enctypes (enroll); + update_computer_account (enroll); +-- +2.14.4 + diff --git a/SOURCES/0007-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch b/SOURCES/0007-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch new file mode 100644 index 0000000..18c8c9c --- /dev/null +++ b/SOURCES/0007-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch @@ -0,0 +1,28 @@ +From 3d312a6c89a88be444fb5ed768fbaa6155bf1cc9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:39:46 +0100 +Subject: [PATCH 07/23] Fix memory leak in test_check_nt_time_string_lifetime + +The test added with 650e5d33ef31437a049fb454ad3dc5457c56abe7 introduced +a small memory leak. + +Reviewed-by: Jakub Hrozek +--- + library/adutil.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/library/adutil.c b/library/adutil.c +index 21ccd27..cd40f45 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -501,6 +501,7 @@ test_check_nt_time_string_lifetime (void) + (time (NULL) + 10 + AD_TO_UNIX_TIME_CONST) * 1000 * 1000 *10) + != -1); + assert (!_adcli_check_nt_time_string_lifetime (time_str, 0)); ++ free (time_str); + + /* This test will fail some time after 2200AD as a reminder to reflect + * why adcli is still needed. */ +-- +2.14.4 + diff --git a/SOURCES/0008-library-add-_adcli_bin_sid_to_str.patch b/SOURCES/0008-library-add-_adcli_bin_sid_to_str.patch new file mode 100644 index 0000000..c7d1c14 --- /dev/null +++ b/SOURCES/0008-library-add-_adcli_bin_sid_to_str.patch @@ -0,0 +1,178 @@ +From f28edf4e887cf8616fa21dacc2b0f0d31f5f92fb Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:37:05 +0100 +Subject: [PATCH 08/23] library: add _adcli_bin_sid_to_str() + +Convert a binary SID to the string representation. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + library/adprivate.h | 4 ++ + library/adutil.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 117 insertions(+) + +diff --git a/library/adprivate.h b/library/adprivate.h +index fc146af..e99f9fc 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + #include + +@@ -132,6 +133,9 @@ int _adcli_str_has_prefix (const char *str, + int _adcli_str_has_suffix (const char *str, + const char *suffix); + ++char * _adcli_bin_sid_to_str (const uint8_t *data, ++ size_t len); ++ + char * _adcli_str_dupn (void *data, + size_t len); + +diff --git a/library/adutil.c b/library/adutil.c +index cd40f45..829cdd9 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -293,6 +293,83 @@ _adcli_strv_set (char ***field, + *field = newval; + } + ++char * ++_adcli_bin_sid_to_str (const uint8_t *data, ++ size_t len) ++{ ++ uint8_t sid_rev_num; ++ int8_t num_auths; ++ uint8_t id_auth[6]; ++ uint32_t id_auth_val; ++ uint32_t sub_auths[15]; ++ uint32_t val; ++ size_t p = 0; ++ size_t c; ++ int nc; ++ char *sid_buf; ++ size_t sid_buf_len; ++ ++ if (data == NULL || len < 8) { ++ return NULL; ++ } ++ ++ sid_rev_num = (uint8_t) data [p]; ++ p++; ++ ++ num_auths = (int8_t) data[p]; ++ p++; ++ ++ if (num_auths > 15 || len < 8 + (num_auths * sizeof (uint32_t))) { ++ return NULL; ++ } ++ ++ for (c = 0; c < 6; c++) { ++ id_auth[c] = (uint8_t) data[p]; ++ p++; ++ } ++ ++ /* Only 32bits are used for the string representation */ ++ id_auth_val = (id_auth[2] << 24) + ++ (id_auth[3] << 16) + ++ (id_auth[4] << 8) + ++ (id_auth[5]); ++ ++ for (c = 0; c < num_auths; c++) { ++ memcpy (&val, data + p, sizeof (uint32_t)); ++ sub_auths[c] = le32toh (val); ++ ++ p += sizeof (uint32_t); ++ } ++ ++ sid_buf_len = 17 + (num_auths * 11); ++ sid_buf = calloc (1, sid_buf_len); ++ if (sid_buf == NULL) { ++ return NULL; ++ } ++ ++ nc = snprintf (sid_buf, sid_buf_len, "S-%u-%lu", sid_rev_num, ++ (unsigned long) id_auth_val); ++ if (nc < 0 || nc >= sid_buf_len) { ++ free (sid_buf); ++ return NULL; ++ } ++ ++ p = 0; ++ for (c = 0; c < num_auths; c++) { ++ p += nc; ++ sid_buf_len -= nc; ++ ++ nc = snprintf (sid_buf + p, sid_buf_len, "-%lu", ++ (unsigned long) sub_auths[c]); ++ if (nc < 0 || nc >= sid_buf_len) { ++ free (sid_buf); ++ return NULL; ++ } ++ } ++ ++ return sid_buf; ++} ++ + char * + _adcli_str_dupn (void *data, + size_t len) +@@ -508,6 +585,41 @@ test_check_nt_time_string_lifetime (void) + assert (_adcli_check_nt_time_string_lifetime ("130645404000000000", 100000)); + } + ++static void ++test_bin_sid_to_str (void) ++{ ++ uint8_t sid1[] = { 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, ++ 0x15, 0x00, 0x00, 0x00, 0xF8, 0x12, 0x13, 0xDC, ++ 0x47, 0xF3, 0x1C, 0x76, 0x47, 0x2F, 0x2E, 0xD7, ++ 0x51, 0x04, 0x00, 0x00 }; ++ ++ uint8_t sid2[] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, ++ 0x15, 0x00, 0x00, 0x00, 0xF8, 0x12, 0x13, 0xDC, ++ 0x47, 0xF3, 0x1C, 0x76, 0x47, 0x2F, 0x2E, 0xD7}; ++ ++ uint8_t sid3[] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, ++ 0x15, 0x00, 0x00, 0x00, 0x29, 0xC9, 0x4F, 0xD9, ++ 0xC2, 0x3C, 0xC3, 0x78, 0x36, 0x55, 0x87, 0xF8}; ++ ++ ++ char *str; ++ ++ str = _adcli_bin_sid_to_str (sid1, sizeof (sid1)); ++ assert (str != NULL); ++ assert (strcmp (str, "S-1-5-21-3692237560-1981608775-3610128199-1105") == 0); ++ free (str); ++ ++ str = _adcli_bin_sid_to_str (sid2, sizeof (sid2)); ++ assert (str != NULL); ++ assert (strcmp (str, "S-1-5-21-3692237560-1981608775-3610128199") == 0); ++ free (str); ++ ++ str = _adcli_bin_sid_to_str (sid3, sizeof (sid2)); ++ assert (str != NULL); ++ assert (strcmp (str, "S-1-5-21-3645884713-2026060994-4169618742") == 0); ++ free (str); ++} ++ + int + main (int argc, + char *argv[]) +@@ -516,6 +628,7 @@ main (int argc, + test_func (test_strv_dup, "/util/strv_dup"); + test_func (test_strv_count, "/util/strv_count"); + test_func (test_check_nt_time_string_lifetime, "/util/check_nt_time_string_lifetime"); ++ test_func (test_bin_sid_to_str, "/util/bin_sid_to_str"); + return test_run (argc, argv); + } + +-- +2.14.4 + diff --git a/SOURCES/0009-library-add-_adcli_call_external_program.patch b/SOURCES/0009-library-add-_adcli_call_external_program.patch new file mode 100644 index 0000000..f215de6 --- /dev/null +++ b/SOURCES/0009-library-add-_adcli_call_external_program.patch @@ -0,0 +1,317 @@ +From 63576f12524f521c0cf08d42b279654885135a90 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:39:17 +0100 +Subject: [PATCH 09/23] library: add _adcli_call_external_program() + +Allow adcli to call an external program given by an absolute path name +and an array of options. stdin and stdout can be used if needed. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + configure.ac | 28 +++++++ + library/adprivate.h | 6 ++ + library/adutil.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 245 insertions(+) + +diff --git a/configure.ac b/configure.ac +index 221d8ae..fe86638 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -263,6 +263,34 @@ AC_SUBST(LCOV) + AC_SUBST(GCOV) + AC_SUBST(GENHTML) + ++AC_PATH_PROG(BIN_CAT, cat, no) ++if test "$BIN_CAT" = "no" ; then ++ AC_MSG_ERROR([cat is not available]) ++else ++ AC_DEFINE_UNQUOTED(BIN_CAT, "$BIN_CAT", [path to cat, used in unit test]) ++fi ++ ++AC_PATH_PROG(BIN_TAC, tac, no) ++if test "$BIN_TAC" = "no" ; then ++ AC_MSG_ERROR([tac is not available]) ++else ++ AC_DEFINE_UNQUOTED(BIN_TAC, "$BIN_TAC", [path to tac, used in unit test]) ++fi ++ ++AC_PATH_PROG(BIN_REV, rev, no) ++if test "$BIN_REV" = "no" ; then ++ AC_MSG_ERROR([rev is not available]) ++else ++ AC_DEFINE_UNQUOTED(BIN_REV, "$BIN_REV", [path to rev, used in unit test]) ++fi ++ ++AC_PATH_PROG(BIN_ECHO, echo, no) ++if test "$BIN_ECHO" = "no" ; then ++ AC_MSG_ERROR([echo is not available]) ++else ++ AC_DEFINE_UNQUOTED(BIN_ECHO, "$BIN_ECHO", [path to echo, used in unit test]) ++fi ++ + # --------------------------------------------------------------------- + + ADCLI_LT_RELEASE=$ADCLI_CURRENT:$ADCLI_REVISION:$ADCLI_AGE +diff --git a/library/adprivate.h b/library/adprivate.h +index e99f9fc..7257c93 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -285,4 +285,10 @@ struct _adcli_attrs { + + bool _adcli_check_nt_time_string_lifetime (const char *nt_time_string, unsigned int lifetime); + ++adcli_result _adcli_call_external_program (const char *binary, ++ char * const *argv, ++ const char *stdin_data, ++ uint8_t **stdout_data, ++ size_t *stdout_data_len); ++ + #endif /* ADPRIVATE_H_ */ +diff --git a/library/adutil.c b/library/adutil.c +index 829cdd9..a27bd68 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + + static adcli_message_func message_func = NULL; + static char last_error[2048] = { 0, }; +@@ -506,6 +507,161 @@ _adcli_check_nt_time_string_lifetime (const char *nt_time_string, + return false; + } + ++adcli_result ++_adcli_call_external_program (const char *binary, char * const *argv, ++ const char *stdin_data, ++ uint8_t **stdout_data, size_t *stdout_data_len) ++{ ++ int ret; ++ int pipefd_to_child[2] = { -1, -1}; ++ int pipefd_from_child[2] = { -1, -1}; ++ pid_t child_pid = 0; ++ int err; ++ size_t len; ++ ssize_t rlen; ++ pid_t wret; ++ int status; ++ uint8_t read_buf[4096]; ++ uint8_t *out; ++ ++ errno = 0; ++ ret = access (binary, X_OK); ++ if (ret != 0) { ++ err = errno; ++ _adcli_err ("Cannot run [%s]: [%d][%s].", binary, err, ++ strerror (err)); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ ++ ret = pipe (pipefd_from_child); ++ if (ret == -1) { ++ err = errno; ++ _adcli_err ("pipe failed [%d][%s].", err, strerror (err)); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ ++ ret = pipe (pipefd_to_child); ++ if (ret == -1) { ++ err = errno; ++ _adcli_err ("pipe failed [%d][%s].", err, strerror (err)); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ ++ child_pid = fork (); ++ ++ if (child_pid == 0) { /* child */ ++ close (pipefd_to_child[1]); ++ ret = dup2 (pipefd_to_child[0], STDIN_FILENO); ++ if (ret == -1) { ++ err = errno; ++ _adcli_err ("dup2 failed [%d][%s].", err, ++ strerror (err)); ++ exit (EXIT_FAILURE); ++ } ++ ++ close (pipefd_from_child[0]); ++ ret = dup2 (pipefd_from_child[1], STDOUT_FILENO); ++ if (ret == -1) { ++ err = errno; ++ _adcli_err ("dup2 failed [%d][%s].", err, ++ strerror (err)); ++ exit (EXIT_FAILURE); ++ } ++ ++ execv (binary, argv); ++ _adcli_err ("Failed to run %s.", binary); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } else if (child_pid > 0) { /* parent */ ++ ++ if (stdin_data != NULL) { ++ len = strlen (stdin_data); ++ ret = write (pipefd_to_child[1], stdin_data, len); ++ if (ret != len) { ++ _adcli_err ("Failed to send computer account password " ++ "to net command."); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ } ++ ++ close (pipefd_to_child[0]); ++ pipefd_to_child[0] = -1; ++ close (pipefd_to_child[1]); ++ pipefd_to_child[0] = -1; ++ ++ if (stdout_data != NULL || stdout_data_len != NULL) { ++ rlen = read (pipefd_from_child[0], read_buf, sizeof (read_buf)); ++ if (rlen < 0) { ++ ret = errno; ++ _adcli_err ("Failed to read from child [%d][%s].\n", ++ ret, strerror (ret)); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ ++ out = malloc (sizeof(uint8_t) * rlen); ++ if (out == NULL) { ++ _adcli_err ("Failed to allocate memory " ++ "for child output."); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } else { ++ memcpy (out, read_buf, rlen); ++ } ++ ++ if (stdout_data != NULL) { ++ *stdout_data = out; ++ } else { ++ free (out); ++ } ++ ++ if (stdout_data_len != NULL) { ++ *stdout_data_len = rlen; ++ } ++ } ++ ++ } else { ++ _adcli_err ("Cannot run net command."); ++ ret = ADCLI_ERR_FAIL; ++ goto done; ++ } ++ ++ ret = ADCLI_SUCCESS; ++ ++done: ++ if (pipefd_from_child[0] != -1) { ++ close (pipefd_from_child[0]); ++ } ++ if (pipefd_from_child[1] != -1) { ++ close (pipefd_from_child[1]); ++ } ++ if (pipefd_to_child[0] != -1) { ++ close (pipefd_to_child[0]); ++ } ++ if (pipefd_to_child[1] != -1) { ++ close (pipefd_to_child[1]); ++ } ++ ++ if (child_pid > 0) { ++ wret = waitpid (child_pid, &status, 0); ++ if (wret == -1) { ++ _adcli_err ("No sure what happend to net command."); ++ } else { ++ if (WIFEXITED (status)) { ++ _adcli_err ("net command failed with %d.", ++ WEXITSTATUS (status)); ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++ + #ifdef UTIL_TESTS + + #include "test.h" +@@ -620,6 +776,60 @@ test_bin_sid_to_str (void) + free (str); + } + ++static void ++test_call_external_program (void) ++{ ++ adcli_result res; ++ char *argv[] = { NULL, NULL, NULL }; ++ uint8_t *stdout_data; ++ size_t stdout_data_len; ++ ++ argv[0] = "/does/not/exists"; ++ res = _adcli_call_external_program (argv[0], argv, NULL, NULL, NULL); ++ assert (res == ADCLI_ERR_FAIL); ++ ++#ifdef BIN_CAT ++ argv[0] = BIN_CAT; ++ res = _adcli_call_external_program (argv[0], argv, "Hello", ++ &stdout_data, &stdout_data_len); ++ assert (res == ADCLI_SUCCESS); ++ assert (strncmp ("Hello", (char *) stdout_data, stdout_data_len) == 0); ++ free (stdout_data); ++ ++ res = _adcli_call_external_program (argv[0], argv, "Hello", ++ NULL, NULL); ++ assert (res == ADCLI_SUCCESS); ++#endif ++ ++#ifdef BIN_REV ++ argv[0] = BIN_REV; ++ res = _adcli_call_external_program (argv[0], argv, "Hello\n", ++ &stdout_data, &stdout_data_len); ++ assert (res == ADCLI_SUCCESS); ++ assert (strncmp ("olleH\n", (char *) stdout_data, stdout_data_len) == 0); ++ free (stdout_data); ++#endif ++ ++#ifdef BIN_TAC ++ argv[0] = BIN_TAC; ++ res = _adcli_call_external_program (argv[0], argv, "Hello\nWorld\n", ++ &stdout_data, &stdout_data_len); ++ assert (res == ADCLI_SUCCESS); ++ assert (strncmp ("World\nHello\n", (char *) stdout_data, stdout_data_len) == 0); ++ free (stdout_data); ++#endif ++ ++#ifdef BIN_ECHO ++ argv[0] = BIN_ECHO; ++ argv[1] = "Hello"; ++ res = _adcli_call_external_program (argv[0], argv, NULL, ++ &stdout_data, &stdout_data_len); ++ assert (res == ADCLI_SUCCESS); ++ assert (strncmp ("Hello\n", (char *) stdout_data, stdout_data_len) == 0); ++ free (stdout_data); ++#endif ++} ++ + int + main (int argc, + char *argv[]) +@@ -629,6 +839,7 @@ main (int argc, + test_func (test_strv_count, "/util/strv_count"); + test_func (test_check_nt_time_string_lifetime, "/util/check_nt_time_string_lifetime"); + test_func (test_bin_sid_to_str, "/util/bin_sid_to_str"); ++ test_func (test_call_external_program, "/util/call_external_program"); + return test_run (argc, argv); + } + +-- +2.14.4 + diff --git a/SOURCES/0010-library-add-_adcli_ldap_parse_sid.patch b/SOURCES/0010-library-add-_adcli_ldap_parse_sid.patch new file mode 100644 index 0000000..6e4d719 --- /dev/null +++ b/SOURCES/0010-library-add-_adcli_ldap_parse_sid.patch @@ -0,0 +1,69 @@ +From bab08d90162c9146c1b4e8373f4b08209b84768c Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:44:45 +0100 +Subject: [PATCH 10/23] library: add _adcli_ldap_parse_sid() + +Get a binary SID from a LDAP message and return it in the string +representation. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + library/adldap.c | 24 ++++++++++++++++++++++++ + library/adprivate.h | 4 ++++ + 2 files changed, 28 insertions(+) + +diff --git a/library/adldap.c b/library/adldap.c +index 7c7a01b..07dc373 100644 +--- a/library/adldap.c ++++ b/library/adldap.c +@@ -67,6 +67,30 @@ _adcli_ldap_handle_failure (LDAP *ldap, + return defres; + } + ++char * ++_adcli_ldap_parse_sid (LDAP *ldap, ++ LDAPMessage *results, ++ const char *attr_name) ++{ ++ LDAPMessage *entry; ++ struct berval **bvs; ++ char *val = NULL; ++ ++ entry = ldap_first_entry (ldap, results); ++ if (entry != NULL) { ++ bvs = ldap_get_values_len (ldap, entry, attr_name); ++ if (bvs != NULL) { ++ if (bvs[0]) { ++ val = _adcli_bin_sid_to_str ( (uint8_t *) bvs[0]->bv_val, ++ bvs[0]->bv_len); ++ return_val_if_fail (val != NULL, NULL); ++ } ++ ldap_value_free_len (bvs); ++ } ++ } ++ ++ return val; ++} + + char * + _adcli_ldap_parse_value (LDAP *ldap, +diff --git a/library/adprivate.h b/library/adprivate.h +index 7257c93..83a88f6 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -174,6 +174,10 @@ adcli_result _adcli_ldap_handle_failure (LDAP *ldap, + const char *desc, + ...) GNUC_PRINTF(3, 4); + ++char * _adcli_ldap_parse_sid (LDAP *ldap, ++ LDAPMessage *results, ++ const char *attr_name); ++ + char * _adcli_ldap_parse_value (LDAP *ldap, + LDAPMessage *results, + const char *attr_name); +-- +2.14.4 + diff --git a/SOURCES/0011-library-add-lookup_domain_sid.patch b/SOURCES/0011-library-add-lookup_domain_sid.patch new file mode 100644 index 0000000..4534269 --- /dev/null +++ b/SOURCES/0011-library-add-lookup_domain_sid.patch @@ -0,0 +1,71 @@ +From 3fa854b1439c039a2250cb24efadae6a66b0e9da Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:40:46 +0100 +Subject: [PATCH 11/23] library: add lookup_domain_sid() + +Read the domain SID from the default naming context AD object and store +it in adcli_conn. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + library/adconn.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/library/adconn.c b/library/adconn.c +index 67bdfd9..6b84b88 100644 +--- a/library/adconn.c ++++ b/library/adconn.c +@@ -72,6 +72,7 @@ struct _adcli_conn_ctx { + char *domain_controller; + char *canonical_host; + char *domain_short; ++ char *domain_sid; + adcli_disco *domain_disco; + char *default_naming_context; + char *configuration_naming_context; +@@ -1068,6 +1069,32 @@ lookup_short_name (adcli_conn *conn) + } + } + ++static void ++lookup_domain_sid (adcli_conn *conn) ++{ ++ char *attrs[] = { "objectSid", NULL, }; ++ LDAPMessage *results; ++ int ret; ++ ++ free (conn->domain_sid); ++ conn->domain_sid = NULL; ++ ++ ret = ldap_search_ext_s (conn->ldap, conn->default_naming_context, LDAP_SCOPE_BASE, ++ NULL, attrs, 0, NULL, NULL, NULL, -1, &results); ++ if (ret == LDAP_SUCCESS) { ++ conn->domain_sid = _adcli_ldap_parse_sid (conn->ldap, results, "objectSid"); ++ ldap_msgfree (results); ++ ++ if (conn->domain_sid) ++ _adcli_info ("Looked up domain SID: %s", conn->domain_sid); ++ else ++ _adcli_err ("No domain SID found"); ++ } else { ++ _adcli_ldap_handle_failure (conn->ldap, ADCLI_ERR_DIRECTORY, ++ "Couldn't lookup domain SID"); ++ } ++} ++ + static void + conn_clear_state (adcli_conn *conn) + { +@@ -1148,6 +1175,7 @@ adcli_conn_connect (adcli_conn *conn) + return res; + + lookup_short_name (conn); ++ lookup_domain_sid (conn); + return ADCLI_SUCCESS; + } + +-- +2.14.4 + diff --git a/SOURCES/0012-library-add-adcli_conn_get_domain_sid.patch b/SOURCES/0012-library-add-adcli_conn_get_domain_sid.patch new file mode 100644 index 0000000..c05aaef --- /dev/null +++ b/SOURCES/0012-library-add-adcli_conn_get_domain_sid.patch @@ -0,0 +1,61 @@ +From f98c4f92091f6a68f390078f73be3bb6ca6e6550 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 18:23:03 +0100 +Subject: [PATCH 12/23] library: add adcli_conn_get_domain_sid() + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + library/adconn.c | 8 ++++++++ + library/adconn.h | 2 ++ + tools/computer.c | 1 + + 3 files changed, 11 insertions(+) + +diff --git a/library/adconn.c b/library/adconn.c +index 6b84b88..d2fb1d5 100644 +--- a/library/adconn.c ++++ b/library/adconn.c +@@ -1355,6 +1355,14 @@ adcli_conn_get_domain_short (adcli_conn *conn) + return conn->domain_short; + } + ++const char * ++adcli_conn_get_domain_sid (adcli_conn *conn) ++{ ++ return_val_if_fail (conn != NULL, NULL); ++ return conn->domain_sid; ++} ++ ++ + LDAP * + adcli_conn_get_ldap_connection (adcli_conn *conn) + { +diff --git a/library/adconn.h b/library/adconn.h +index ed1cc58..13cfd32 100644 +--- a/library/adconn.h ++++ b/library/adconn.h +@@ -91,6 +91,8 @@ void adcli_conn_set_domain_controller (adcli_conn *conn, + + const char * adcli_conn_get_domain_short (adcli_conn *conn); + ++const char * adcli_conn_get_domain_sid (adcli_conn *conn); ++ + LDAP * adcli_conn_get_ldap_connection (adcli_conn *conn); + + krb5_context adcli_conn_get_krb5_context (adcli_conn *conn); +diff --git a/tools/computer.c b/tools/computer.c +index d8a58c9..a3d0f03 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -43,6 +43,7 @@ dump_details (adcli_conn *conn, + printf ("domain-realm = %s\n", adcli_conn_get_domain_realm (conn)); + printf ("domain-controller = %s\n", adcli_conn_get_domain_controller (conn)); + printf ("domain-short = %s\n", adcli_conn_get_domain_short (conn)); ++ printf ("domain-SID = %s\n", adcli_conn_get_domain_sid (conn)); + printf ("naming-context = %s\n", adcli_conn_get_default_naming_context (conn)); + printf ("domain-ou = %s\n", adcli_enroll_get_domain_ou (enroll)); + +-- +2.14.4 + diff --git a/SOURCES/0013-tools-add-option-add-samba-data.patch b/SOURCES/0013-tools-add-option-add-samba-data.patch new file mode 100644 index 0000000..6e5c872 --- /dev/null +++ b/SOURCES/0013-tools-add-option-add-samba-data.patch @@ -0,0 +1,142 @@ +From d362a0799618b576918f5c5d0625565484670ba2 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 14:46:00 +0100 +Subject: [PATCH 13/23] tools: add option --add-samba-data + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + doc/adcli.xml | 30 ++++++++++++++++++++++++++++++ + library/adenroll.h | 1 + + tools/computer.c | 12 ++++++++++++ + 3 files changed, 43 insertions(+) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index c54cc1b..fbc6c63 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -292,6 +292,21 @@ Password for Administrator: + machine account password. This is output in a format that should + be both human and machine readable. + ++ ++ ++ After a successful join add the domain ++ SID and the machine account password to the Samba ++ specific databases by calling Samba's ++ net utility. ++ ++ Please note that Samba's net ++ requires some settings in smb.conf ++ to create the database entries correctly. Most ++ important here is currently the ++ option, see ++ smb.conf5 ++ for details. ++ + + + +@@ -382,6 +397,21 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + about join operation. This is output in a format that should + be both human and machine readable. + ++ ++ ++ After a successful join add the domain ++ SID and the machine account password to the Samba ++ specific databases by calling Samba's ++ net utility. ++ ++ Please note that Samba's net ++ requires some settings in smb.conf ++ to create the database entries correctly. Most ++ important here is currently the ++ option, see ++ smb.conf5 ++ for details. ++ + + + +diff --git a/library/adenroll.h b/library/adenroll.h +index 9a107ab..32c9764 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -30,6 +30,7 @@ typedef enum { + ADCLI_ENROLL_NO_KEYTAB = 1 << 1, + ADCLI_ENROLL_ALLOW_OVERWRITE = 1 << 2, + ADCLI_ENROLL_PASSWORD_VALID = 1 << 3, ++ ADCLI_ENROLL_ADD_SAMBA_DATA = 1 << 3, + } adcli_enroll_flags; + + typedef struct _adcli_enroll adcli_enroll; +diff --git a/tools/computer.c b/tools/computer.c +index a3d0f03..fc646f2 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -106,6 +106,7 @@ typedef enum { + opt_os_service_pack, + opt_user_principal, + opt_computer_password_lifetime, ++ opt_add_samba_data, + } Option; + + static adcli_tool_desc common_usages[] = { +@@ -142,6 +143,8 @@ static adcli_tool_desc common_usages[] = { + "a successful join" }, + { opt_show_password, "show computer account password after after a\n" + "successful join" }, ++ { opt_add_samba_data, "add domain SID and computer account password\n" ++ "to the Samba specific configuration database" }, + { opt_verbose, "show verbose progress and failure messages", }, + { 0 }, + }; +@@ -269,6 +272,7 @@ parse_option (Option opt, + case opt_show_details: + case opt_show_password: + case opt_one_time_password: ++ case opt_add_samba_data: + assert (0 && "not reached"); + break; + } +@@ -326,6 +330,7 @@ adcli_tool_computer_join (adcli_conn *conn, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, ++ { "add-samba-data", no_argument, NULL, opt_add_samba_data }, + { "verbose", no_argument, NULL, opt_verbose }, + { "help", no_argument, NULL, 'h' }, + { 0 }, +@@ -352,6 +357,9 @@ adcli_tool_computer_join (adcli_conn *conn, + case opt_show_password: + show_password = 1; + break; ++ case opt_add_samba_data: ++ flags |= ADCLI_ENROLL_ADD_SAMBA_DATA; ++ break; + case 'h': + case '?': + case ':': +@@ -425,6 +433,7 @@ adcli_tool_computer_update (adcli_conn *conn, + { "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, ++ { "add-samba-data", no_argument, NULL, opt_add_samba_data }, + { "verbose", no_argument, NULL, opt_verbose }, + { "help", no_argument, NULL, 'h' }, + { 0 }, +@@ -447,6 +456,9 @@ adcli_tool_computer_update (adcli_conn *conn, + case opt_show_password: + show_password = 1; + break; ++ case opt_add_samba_data: ++ flags |= ADCLI_ENROLL_ADD_SAMBA_DATA; ++ break; + case 'h': + case '?': + case ':': +-- +2.14.4 + diff --git a/SOURCES/0014-tools-store-Samba-data-if-requested.patch b/SOURCES/0014-tools-store-Samba-data-if-requested.patch new file mode 100644 index 0000000..33f8bff --- /dev/null +++ b/SOURCES/0014-tools-store-Samba-data-if-requested.patch @@ -0,0 +1,75 @@ +From c090131e4f912f6f6c4f79eb40fbe500eb31c171 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 30 Jan 2018 18:24:15 +0100 +Subject: [PATCH 14/23] tools: store Samba data if requested + +Use Samba's net utility to add the machine account password and the +domain SID to the Samba configuration. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + library/adenroll.c | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/library/adenroll.c b/library/adenroll.c +index bb970d1..20731cd 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -1533,6 +1533,36 @@ update_keytab_for_principals (adcli_enroll *enroll) + return ADCLI_SUCCESS; + } + ++static adcli_result ++update_samba_data (adcli_enroll *enroll) ++{ ++ int ret; ++ char *argv_pw[] = { "/usr/bin/net", "changesecretpw", "-i", "-f", NULL }; ++ char *argv_sid[] = { "/usr/bin/net", "setdomainsid", NULL, NULL }; ++ ++ _adcli_info ("Trying to set Samba secret.\n"); ++ ret = _adcli_call_external_program (argv_pw[0], argv_pw, ++ enroll->computer_password, NULL, NULL); ++ if (ret != ADCLI_SUCCESS) { ++ _adcli_err ("Failed to set Samba computer account password.\n"); ++ } ++ ++ argv_sid[2] = (char *) adcli_conn_get_domain_sid (enroll->conn); ++ if (argv_sid[2] == NULL) { ++ _adcli_err ("Domain SID not available.\n"); ++ } else { ++ _adcli_info ("Trying to set domain SID %s for Samba.\n", ++ argv_sid[2]); ++ ret = _adcli_call_external_program (argv_sid[0], argv_sid, ++ NULL, NULL, NULL); ++ if (ret != ADCLI_SUCCESS) { ++ _adcli_err ("Failed to set Samba domain SID.\n"); ++ } ++ } ++ ++ return ret; ++} ++ + static void + enroll_clear_state (adcli_enroll *enroll) + { +@@ -1687,6 +1717,15 @@ enroll_join_or_update_tasks (adcli_enroll *enroll, + update_computer_account (enroll); + update_service_principals (enroll); + ++ if ( (flags & ADCLI_ENROLL_ADD_SAMBA_DATA) && ! (flags & ADCLI_ENROLL_PASSWORD_VALID)) { ++ res = update_samba_data (enroll); ++ if (res != ADCLI_SUCCESS) { ++ _adcli_info ("Failed to add Samba specific data, smbd " ++ "or winbindd might not work as " ++ "expected.\n"); ++ } ++ } ++ + if (flags & ADCLI_ENROLL_NO_KEYTAB) + return ADCLI_SUCCESS; + +-- +2.14.4 + diff --git a/SOURCES/0015-make-Samba-data-tool-configurable.patch b/SOURCES/0015-make-Samba-data-tool-configurable.patch new file mode 100644 index 0000000..714cae5 --- /dev/null +++ b/SOURCES/0015-make-Samba-data-tool-configurable.patch @@ -0,0 +1,292 @@ +From 9b73f79a2436760b8278377014bf78a144a427ae Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 1 Feb 2018 14:26:22 +0100 +Subject: [PATCH 15/23] make Samba data tool configurable + +Allow to specify an alternative path to Samba's net utility at configure +time and at run time. + +https://bugs.freedesktop.org/show_bug.cgi?id=100118 + +Reviewed-by: Jakub Hrozek +--- + configure.ac | 13 ++++++++++++ + doc/adcli.xml | 21 ++++++++++++++++++- + doc/samba_data_tool_path.xml.in | 1 + + library/adenroll.c | 46 ++++++++++++++++++++++++++++++++++------- + library/adenroll.h | 5 +++++ + tools/computer.c | 16 ++++++++++++++ + 7 files changed, 95 insertions(+), 8 deletions(-) + create mode 100644 doc/samba_data_tool_path.xml.in + +diff --git a/configure.ac b/configure.ac +index fe86638..68877c7 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -291,6 +291,18 @@ else + AC_DEFINE_UNQUOTED(BIN_ECHO, "$BIN_ECHO", [path to echo, used in unit test]) + fi + ++AC_MSG_CHECKING([where is Samba's net utility]) ++AC_ARG_WITH([samba_data_tool], ++ AC_HELP_STRING([--with-samba-data-tool=/path], ++ [Path to Samba's net utility]), ++ [], ++ [with_samba_data_tool=/usr/bin/net]) ++AC_MSG_RESULT([$with_samba_data_tool]) ++ ++AC_DEFINE_UNQUOTED(SAMBA_DATA_TOOL, "$with_samba_data_tool", ++ [Path to Samba's net utility]) ++ ++AC_SUBST(SAMBA_DATA_TOOL, [$with_samba_data_tool]) + # --------------------------------------------------------------------- + + ADCLI_LT_RELEASE=$ADCLI_CURRENT:$ADCLI_REVISION:$ADCLI_AGE +@@ -300,6 +312,7 @@ AC_CONFIG_FILES([Makefile + build/Makefile + doc/Makefile + doc/version.xml ++ doc/samba_data_tool_path.xml + library/Makefile + tools/Makefile + ]) +diff --git a/doc/adcli.xml b/doc/adcli.xml +index fbc6c63..c2b7760 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -1,6 +1,9 @@ + + ++ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" ++[ ++ ++]> + + + +@@ -307,6 +310,14 @@ Password for Administrator: + smb.conf5 + for details. + ++ ++ ++ If Samba's net ++ cannot be found at ++ &samba_data_tool; this option can ++ be used to specific an alternative location with the ++ help of an absolute path. ++ + + + +@@ -412,6 +423,14 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + smb.conf5 + for details. + ++ ++ ++ If Samba's net ++ cannot be found at ++ &samba_data_tool; this option can ++ be used to specific an alternative location with the ++ help of an absolute path. ++ + + + +diff --git a/doc/samba_data_tool_path.xml.in b/doc/samba_data_tool_path.xml.in +new file mode 100644 +index 0000000..a667c57 +--- /dev/null ++++ b/doc/samba_data_tool_path.xml.in +@@ -0,0 +1 @@ ++@SAMBA_DATA_TOOL@ +diff --git a/library/adenroll.c b/library/adenroll.c +index 20731cd..a693049 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -42,6 +42,10 @@ + #include + #include + ++#ifndef SAMBA_DATA_TOOL ++#define SAMBA_DATA_TOOL "/usr/bin/net" ++#endif ++ + static krb5_enctype v60_later_enctypes[] = { + ENCTYPE_AES256_CTS_HMAC_SHA1_96, + ENCTYPE_AES128_CTS_HMAC_SHA1_96, +@@ -100,6 +104,7 @@ struct _adcli_enroll { + int keytab_enctypes_explicit; + unsigned int computer_password_lifetime; + int computer_password_lifetime_explicit; ++ char *samba_data_tool; + }; + + static adcli_result +@@ -1537,26 +1542,33 @@ static adcli_result + update_samba_data (adcli_enroll *enroll) + { + int ret; +- char *argv_pw[] = { "/usr/bin/net", "changesecretpw", "-i", "-f", NULL }; +- char *argv_sid[] = { "/usr/bin/net", "setdomainsid", NULL, NULL }; ++ char *argv_pw[] = { NULL, "changesecretpw", "-i", "-f", NULL }; ++ char *argv_sid[] = { NULL, "setdomainsid", NULL, NULL }; ++ ++ argv_pw[0] = (char *) adcli_enroll_get_samba_data_tool (enroll); ++ if (argv_pw[0] ==NULL) { ++ _adcli_err ("Samba data tool not available."); ++ return ADCLI_ERR_FAIL; ++ } ++ argv_sid[0] = argv_pw[0]; + +- _adcli_info ("Trying to set Samba secret.\n"); ++ _adcli_info ("Trying to set Samba secret."); + ret = _adcli_call_external_program (argv_pw[0], argv_pw, + enroll->computer_password, NULL, NULL); + if (ret != ADCLI_SUCCESS) { +- _adcli_err ("Failed to set Samba computer account password.\n"); ++ _adcli_err ("Failed to set Samba computer account password."); + } + + argv_sid[2] = (char *) adcli_conn_get_domain_sid (enroll->conn); + if (argv_sid[2] == NULL) { +- _adcli_err ("Domain SID not available.\n"); ++ _adcli_err ("Domain SID not available."); + } else { +- _adcli_info ("Trying to set domain SID %s for Samba.\n", ++ _adcli_info ("Trying to set domain SID %s for Samba.", + argv_sid[2]); + ret = _adcli_call_external_program (argv_sid[0], argv_sid, + NULL, NULL, NULL); + if (ret != ADCLI_SUCCESS) { +- _adcli_err ("Failed to set Samba domain SID.\n"); ++ _adcli_err ("Failed to set Samba domain SID."); + } + } + +@@ -1951,6 +1963,9 @@ adcli_enroll_new (adcli_conn *conn) + enroll->os_name = strdup (value); + return_val_if_fail (enroll->os_name != NULL, NULL); + ++ enroll->samba_data_tool = strdup (SAMBA_DATA_TOOL); ++ return_val_if_fail (enroll->samba_data_tool != NULL, NULL); ++ + return enroll; + } + +@@ -1978,6 +1993,7 @@ enroll_free (adcli_enroll *enroll) + free (enroll->os_name); + free (enroll->os_version); + free (enroll->os_service_pack); ++ free (enroll->samba_data_tool); + + free (enroll->user_principal); + _adcli_strv_free (enroll->service_names); +@@ -2343,3 +2359,19 @@ adcli_enroll_set_computer_password_lifetime (adcli_enroll *enroll, + + enroll->computer_password_lifetime_explicit = 1; + } ++ ++void ++adcli_enroll_set_samba_data_tool (adcli_enroll *enroll, const char *value) ++{ ++ return_if_fail (enroll != NULL); ++ if (value != NULL && value[0] != '\0') { ++ _adcli_str_set (&enroll->samba_data_tool, value); ++ } ++} ++ ++const char * ++adcli_enroll_get_samba_data_tool (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, NULL); ++ return enroll->samba_data_tool; ++} +diff --git a/library/adenroll.h b/library/adenroll.h +index 32c9764..31ca0bc 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -141,4 +141,9 @@ const char * adcli_enroll_get_os_service_pack (adcli_enroll *enroll); + void adcli_enroll_set_os_service_pack (adcli_enroll *enroll, + const char *value); + ++void adcli_enroll_set_samba_data_tool (adcli_enroll *enroll, ++ const char *value); ++ ++const char * adcli_enroll_get_samba_data_tool (adcli_enroll *enroll); ++ + #endif /* ADENROLL_H_ */ +diff --git a/tools/computer.c b/tools/computer.c +index fc646f2..f86548b 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + + static void + dump_details (adcli_conn *conn, +@@ -107,6 +108,7 @@ typedef enum { + opt_user_principal, + opt_computer_password_lifetime, + opt_add_samba_data, ++ opt_samba_data_tool, + } Option; + + static adcli_tool_desc common_usages[] = { +@@ -145,6 +147,7 @@ static adcli_tool_desc common_usages[] = { + "successful join" }, + { opt_add_samba_data, "add domain SID and computer account password\n" + "to the Samba specific configuration database" }, ++ { opt_samba_data_tool, "Absolute path to the tool used for add-samba-data" }, + { opt_verbose, "show verbose progress and failure messages", }, + { 0 }, + }; +@@ -160,6 +163,7 @@ parse_option (Option opt, + static int stdin_password = 0; + char *endptr; + unsigned int lifetime; ++ int ret; + + switch (opt) { + case opt_login_ccache: +@@ -265,6 +269,16 @@ parse_option (Option opt, + + adcli_enroll_set_computer_password_lifetime (enroll, lifetime); + return; ++ case opt_samba_data_tool: ++ errno = 0; ++ ret = access (optarg, X_OK); ++ if (ret != 0) { ++ ret = errno; ++ errx (EUSAGE, "Failed to access tool to add Samba data: %s", strerror (ret)); ++ } else { ++ adcli_enroll_set_samba_data_tool (enroll, optarg); ++ } ++ return; + case opt_verbose: + return; + +@@ -331,6 +345,7 @@ adcli_tool_computer_join (adcli_conn *conn, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, ++ { "samba-data-tool", no_argument, NULL, opt_samba_data_tool }, + { "verbose", no_argument, NULL, opt_verbose }, + { "help", no_argument, NULL, 'h' }, + { 0 }, +@@ -434,6 +449,7 @@ adcli_tool_computer_update (adcli_conn *conn, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, ++ { "samba-data-tool", no_argument, NULL, opt_samba_data_tool }, + { "verbose", no_argument, NULL, opt_verbose }, + { "help", no_argument, NULL, 'h' }, + { 0 }, +-- +2.14.4 + diff --git a/SOURCES/0016-Add-trusted-for-delegation-option.patch b/SOURCES/0016-Add-trusted-for-delegation-option.patch new file mode 100644 index 0000000..122f5a2 --- /dev/null +++ b/SOURCES/0016-Add-trusted-for-delegation-option.patch @@ -0,0 +1,247 @@ +From f306f2f20c1d35fac63d27147824f039f7ef2d67 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 31 May 2018 18:27:37 +0200 +Subject: [PATCH 16/23] Add trusted-for-delegation option + +Resolves https://bugzilla.redhat.com/show_bug.cgi?id=1538730 +--- + doc/adcli.xml | 14 ++++++++++ + library/adenroll.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- + library/adenroll.h | 4 +++ + tools/computer.c | 12 ++++++++ + 4 files changed, 108 insertions(+), 2 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index c2b7760..b246190 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -283,6 +283,13 @@ Password for Administrator: + and providing a + password as input. + ++ ++ ++ Set or unset the TRUSTED_FOR_DELEGATION ++ flag in the userAccountControl attribute to allow or ++ not allow that Kerberos tickets can be forwarded to the ++ host. ++ + + + After a successful join print out information +@@ -402,6 +409,13 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + in days. By default the password is updated if it is + older than 30 days. + ++ ++ ++ Set or unset the TRUSTED_FOR_DELEGATION ++ flag in the userAccountControl attribute to allow or ++ not allow that Kerberos tickets can be forwarded to the ++ host. ++ + + + After a successful join print out information +diff --git a/library/adenroll.c b/library/adenroll.c +index a693049..eca3c37 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -63,6 +63,13 @@ static krb5_enctype v51_earlier_enctypes[] = { + 0 + }; + ++/* Some constants for the userAccountControl AD LDAP attribute, see e.g. ++ * https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro ++ * for details. */ ++#define UAC_WORKSTATION_TRUST_ACCOUNT 0x1000 ++#define UAC_DONT_EXPIRE_PASSWORD 0x10000 ++#define UAC_TRUSTED_FOR_DELEGATION 0x80000 ++ + struct _adcli_enroll { + int refs; + adcli_conn *conn; +@@ -105,6 +112,7 @@ struct _adcli_enroll { + unsigned int computer_password_lifetime; + int computer_password_lifetime_explicit; + char *samba_data_tool; ++ bool trusted_for_delegation; + }; + + static adcli_result +@@ -538,6 +546,10 @@ create_computer_account (adcli_enroll *enroll, + NULL, + }; + ++ if (adcli_enroll_get_trusted_for_delegation (enroll)) { ++ vals_userAccountControl[0] = "593920"; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD | TRUSTED_FOR_DELEGATION */ ++ } ++ + ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL); + + /* +@@ -971,6 +983,7 @@ retrieve_computer_account (adcli_enroll *enroll) + "operatingSystemVersion", + "operatingSystemServicePack", + "pwdLastSet", ++ "userAccountControl", + NULL, + }; + +@@ -1149,6 +1162,47 @@ update_computer_attribute (adcli_enroll *enroll, + return res; + } + ++static char *get_user_account_control (adcli_enroll *enroll) ++{ ++ uint32_t uac = 0; ++ unsigned long attr_val; ++ char *uac_str; ++ LDAP *ldap; ++ char *end; ++ ++ ldap = adcli_conn_get_ldap_connection (enroll->conn); ++ return_val_if_fail (ldap != NULL, NULL); ++ ++ uac_str = _adcli_ldap_parse_value (ldap, enroll->computer_attributes, "userAccountControl"); ++ if (uac_str != NULL) { ++ ++ attr_val = strtoul (uac_str, &end, 10); ++ if (*end != '\0' || attr_val > UINT32_MAX) { ++ _adcli_warn ("Invalid userAccountControl '%s' for computer account in directory: %s, assuming 0", ++ uac_str, enroll->computer_dn); ++ } else { ++ uac = attr_val; ++ } ++ free (uac_str); ++ } ++ ++ if (uac == 0) { ++ uac = UAC_WORKSTATION_TRUST_ACCOUNT | UAC_DONT_EXPIRE_PASSWORD; ++ } ++ ++ if (adcli_enroll_get_trusted_for_delegation (enroll)) { ++ uac |= UAC_TRUSTED_FOR_DELEGATION; ++ } else { ++ uac &= ~(UAC_TRUSTED_FOR_DELEGATION); ++ } ++ ++ if (asprintf (&uac_str, "%d", uac) < 0) { ++ return_val_if_reached (NULL); ++ } ++ ++ return uac_str; ++} ++ + static void + update_computer_account (adcli_enroll *enroll) + { +@@ -1167,11 +1221,16 @@ update_computer_account (adcli_enroll *enroll) + } + + if (res == ADCLI_SUCCESS) { +- char *vals_userAccountControl[] = { "69632", NULL }; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD */ ++ char *vals_userAccountControl[] = { NULL , NULL }; + LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } }; + LDAPMod *mods[] = { &userAccountControl, NULL }; + +- res |= update_computer_attribute (enroll, ldap, mods); ++ vals_userAccountControl[0] = get_user_account_control (enroll); ++ if (vals_userAccountControl[0] != NULL) { ++ res |= update_computer_attribute (enroll, ldap, mods); ++ } else { ++ _adcli_warn ("Cannot update userAccountControl"); ++ } + } + + if (res == ADCLI_SUCCESS) { +@@ -2375,3 +2434,20 @@ adcli_enroll_get_samba_data_tool (adcli_enroll *enroll) + return_val_if_fail (enroll != NULL, NULL); + return enroll->samba_data_tool; + } ++ ++bool ++adcli_enroll_get_trusted_for_delegation (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, false); ++ ++ return enroll->trusted_for_delegation; ++} ++ ++void ++adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, ++ bool value) ++{ ++ return_if_fail (enroll != NULL); ++ ++ enroll->trusted_for_delegation = value; ++} +diff --git a/library/adenroll.h b/library/adenroll.h +index 31ca0bc..be2ca18 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -109,6 +109,10 @@ unsigned int adcli_enroll_get_computer_password_lifetime (adcli_enroll *en + void adcli_enroll_set_computer_password_lifetime (adcli_enroll *enroll, + unsigned int lifetime); + ++bool adcli_enroll_get_trusted_for_delegation (adcli_enroll *enroll); ++void adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, ++ bool value); ++ + krb5_kvno adcli_enroll_get_kvno (adcli_enroll *enroll); + + void adcli_enroll_set_kvno (adcli_enroll *enroll, +diff --git a/tools/computer.c b/tools/computer.c +index f86548b..b905fd1 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -109,6 +109,7 @@ typedef enum { + opt_computer_password_lifetime, + opt_add_samba_data, + opt_samba_data_tool, ++ opt_trusted_for_delegation, + } Option; + + static adcli_tool_desc common_usages[] = { +@@ -135,6 +136,8 @@ static adcli_tool_desc common_usages[] = { + { opt_os_service_pack, "the computer operating system service pack", }, + { opt_user_principal, "add an authentication principal to the account", }, + { opt_computer_password_lifetime, "lifetime of the host accounts password in days", }, ++ { opt_trusted_for_delegation, "set/unset the TRUSTED_FOR_DELEGATION flag\n" ++ "in the userAccountControl attribute", }, + { opt_no_password, "don't prompt for or read a password" }, + { opt_prompt_password, "prompt for a password if necessary" }, + { opt_stdin_password, "read a password from stdin (until EOF) if\n" +@@ -279,6 +282,13 @@ parse_option (Option opt, + adcli_enroll_set_samba_data_tool (enroll, optarg); + } + return; ++ case opt_trusted_for_delegation: ++ if (strcasecmp (optarg, "true") == 0 || strcasecmp (optarg, "yes") == 0) { ++ adcli_enroll_set_trusted_for_delegation (enroll, true); ++ } else { ++ adcli_enroll_set_trusted_for_delegation (enroll, false); ++ } ++ return; + case opt_verbose: + return; + +@@ -342,6 +352,7 @@ adcli_tool_computer_join (adcli_conn *conn, + { "os-version", required_argument, NULL, opt_os_version }, + { "os-service-pack", optional_argument, NULL, opt_os_service_pack }, + { "user-principal", optional_argument, NULL, opt_user_principal }, ++ { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, +@@ -446,6 +457,7 @@ adcli_tool_computer_update (adcli_conn *conn, + { "os-service-pack", optional_argument, NULL, opt_os_service_pack }, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime }, ++ { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, +-- +2.14.4 + diff --git a/SOURCES/0017-Only-update-attributes-given-on-the-command-line.patch b/SOURCES/0017-Only-update-attributes-given-on-the-command-line.patch new file mode 100644 index 0000000..2ee6a6f --- /dev/null +++ b/SOURCES/0017-Only-update-attributes-given-on-the-command-line.patch @@ -0,0 +1,126 @@ +From 27c7dde2c0e84c3bb610d1aadb0fd8faff70d3fa Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 1 Jun 2018 21:26:47 +0200 +Subject: [PATCH 17/23] Only update attributes given on the command line + +When updating attributes of the LDAP computer object we only want to +update attributes which are related to options given on the command +line. Otherwise a simple call of 'adcli update' to check if the machine +account password needs an update might unexpectedly reset other +attributes as well. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1547013 + https://bugzilla.redhat.com/show_bug.cgi?id=1545568 + https://bugzilla.redhat.com/show_bug.cgi?id=1538730 +--- + library/adenroll.c | 35 ++++++++++++++++++++++++++++++----- + 1 file changed, 30 insertions(+), 5 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index eca3c37..ee845ef 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -99,8 +99,11 @@ struct _adcli_enroll { + int user_princpal_generate; + + char *os_name; ++ int os_name_explicit; + char *os_version; ++ int os_version_explicit; + char *os_service_pack; ++ int os_service_pack_explicit; + + krb5_kvno kvno; + char *keytab_name; +@@ -113,6 +116,7 @@ struct _adcli_enroll { + int computer_password_lifetime_explicit; + char *samba_data_tool; + bool trusted_for_delegation; ++ int trusted_for_delegation_explicit; + }; + + static adcli_result +@@ -1212,7 +1216,11 @@ update_computer_account (adcli_enroll *enroll) + ldap = adcli_conn_get_ldap_connection (enroll->conn); + return_if_fail (ldap != NULL); + +- { ++ /* Only update attributes which are explicitly given on the command ++ * line. Otherwise 'adcli update' must be always called with the same ++ * set of options to make sure existing attributes are not deleted or ++ * overwritten with different values. */ ++ if (enroll->host_fqdn_explicit) { + char *vals_dNSHostName[] = { enroll->host_fqdn, NULL }; + LDAPMod dNSHostName = { LDAP_MOD_REPLACE, "dNSHostName", { vals_dNSHostName, } }; + LDAPMod *mods[] = { &dNSHostName, NULL }; +@@ -1220,7 +1228,7 @@ update_computer_account (adcli_enroll *enroll) + res |= update_computer_attribute (enroll, ldap, mods); + } + +- if (res == ADCLI_SUCCESS) { ++ if (res == ADCLI_SUCCESS && enroll->trusted_for_delegation_explicit) { + char *vals_userAccountControl[] = { NULL , NULL }; + LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } }; + LDAPMod *mods[] = { &userAccountControl, NULL }; +@@ -1240,12 +1248,25 @@ update_computer_account (adcli_enroll *enroll) + LDAPMod operatingSystemVersion = { LDAP_MOD_REPLACE, "operatingSystemVersion", { vals_operatingSystemVersion, } }; + char *vals_operatingSystemServicePack[] = { enroll->os_service_pack, NULL }; + LDAPMod operatingSystemServicePack = { LDAP_MOD_REPLACE, "operatingSystemServicePack", { vals_operatingSystemServicePack, } }; +- LDAPMod *mods[] = { &operatingSystem, &operatingSystemVersion, &operatingSystemServicePack, NULL }; ++ LDAPMod *mods[] = { NULL, NULL, NULL, NULL }; ++ size_t c = 0; + +- res |= update_computer_attribute (enroll, ldap, mods); ++ if (enroll->os_name_explicit) { ++ mods[c++] = &operatingSystem; ++ } ++ if (enroll->os_version_explicit) { ++ mods[c++] = &operatingSystemVersion; ++ } ++ if (enroll->os_service_pack_explicit) { ++ mods[c++] = &operatingSystemServicePack; ++ } ++ ++ if (c != 0) { ++ res |= update_computer_attribute (enroll, ldap, mods); ++ } + } + +- if (res == ADCLI_SUCCESS) { ++ if (res == ADCLI_SUCCESS && !enroll->user_princpal_generate) { + char *vals_userPrincipalName[] = { enroll->user_principal, NULL }; + LDAPMod userPrincipalName = { LDAP_MOD_REPLACE, "userPrincipalName", { vals_userPrincipalName, }, }; + LDAPMod *mods[] = { &userPrincipalName, NULL, }; +@@ -2337,6 +2358,7 @@ adcli_enroll_set_os_name (adcli_enroll *enroll, + if (value && value[0] == '\0') + value = NULL; + _adcli_str_set (&enroll->os_name, value); ++ enroll->os_name_explicit = 1; + } + + const char * +@@ -2354,6 +2376,7 @@ adcli_enroll_set_os_version (adcli_enroll *enroll, + if (value && value[0] == '\0') + value = NULL; + _adcli_str_set (&enroll->os_version, value); ++ enroll->os_version_explicit = 1; + } + + const char * +@@ -2371,6 +2394,7 @@ adcli_enroll_set_os_service_pack (adcli_enroll *enroll, + if (value && value[0] == '\0') + value = NULL; + _adcli_str_set (&enroll->os_service_pack, value); ++ enroll->os_service_pack_explicit = 1; + } + + const char * +@@ -2450,4 +2474,5 @@ adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, + return_if_fail (enroll != NULL); + + enroll->trusted_for_delegation = value; ++ enroll->trusted_for_delegation_explicit = 1; + } +-- +2.14.4 + diff --git a/SOURCES/0018-update-allow-to-add-service-names.patch b/SOURCES/0018-update-allow-to-add-service-names.patch new file mode 100644 index 0000000..2387ef2 --- /dev/null +++ b/SOURCES/0018-update-allow-to-add-service-names.patch @@ -0,0 +1,387 @@ +From 54c3d176326f719ffefded17bb797bc9e6c7f3c0 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 4 Jun 2018 10:49:33 +0200 +Subject: [PATCH 18/23] update: allow to add service names + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1547013 + https://bugzilla.redhat.com/show_bug.cgi?id=1545568 +--- + library/adenroll.c | 136 +++++++++++++++++++++++++++++++++------------------- + library/adkrb5.c | 113 +++++++++++++++++++++++++++++++++++++++++++ + library/adprivate.h | 6 +++ + 3 files changed, 206 insertions(+), 49 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index ee845ef..6fdc773 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -305,13 +305,37 @@ ensure_service_names (adcli_result res, + } + + static adcli_result +-ensure_service_principals (adcli_result res, +- adcli_enroll *enroll) ++add_service_names_to_service_principals (adcli_enroll *enroll) + { + char *name; + int length = 0; + int i; + ++ if (enroll->service_principals != NULL) { ++ length = seq_count (enroll->service_principals); ++ } ++ ++ for (i = 0; enroll->service_names[i] != NULL; i++) { ++ if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0) ++ return_unexpected_if_reached (); ++ enroll->service_principals = _adcli_strv_add (enroll->service_principals, ++ name, &length); ++ ++ if (enroll->host_fqdn) { ++ if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0) ++ return_unexpected_if_reached (); ++ enroll->service_principals = _adcli_strv_add (enroll->service_principals, ++ name, &length); ++ } ++ } ++ ++ return ADCLI_SUCCESS; ++} ++ ++static adcli_result ++ensure_service_principals (adcli_result res, ++ adcli_enroll *enroll) ++{ + if (res != ADCLI_SUCCESS) + return res; + +@@ -319,20 +343,7 @@ ensure_service_principals (adcli_result res, + + if (!enroll->service_principals) { + assert (enroll->service_names != NULL); +- +- for (i = 0; enroll->service_names[i] != NULL; i++) { +- if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->computer_name) < 0) +- return_unexpected_if_reached (); +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, +- name, &length); +- +- if (enroll->host_fqdn) { +- if (asprintf (&name, "%s/%s", enroll->service_names[i], enroll->host_fqdn) < 0) +- return_unexpected_if_reached (); +- enroll->service_principals = _adcli_strv_add (enroll->service_principals, +- name, &length); +- } +- } ++ return add_service_names_to_service_principals (enroll); + } + + return ADCLI_SUCCESS; +@@ -356,6 +367,7 @@ ensure_keytab_principals (adcli_result res, + return_unexpected_if_fail (k5 != NULL); + + enroll->keytab_principals = calloc (count + 3, sizeof (krb5_principal)); ++ return_unexpected_if_fail (enroll->keytab_principals != NULL); + at = 0; + + /* First add the principal for the computer account name */ +@@ -1266,7 +1278,7 @@ update_computer_account (adcli_enroll *enroll) + } + } + +- if (res == ADCLI_SUCCESS && !enroll->user_princpal_generate) { ++ if (res == ADCLI_SUCCESS && enroll->user_principal != NULL && !enroll->user_princpal_generate) { + char *vals_userPrincipalName[] = { enroll->user_principal, NULL }; + LDAPMod userPrincipalName = { LDAP_MOD_REPLACE, "userPrincipalName", { vals_userPrincipalName, }, }; + LDAPMod *mods[] = { &userPrincipalName, NULL, }; +@@ -1519,7 +1531,8 @@ add_principal_to_keytab (adcli_enroll *enroll, + krb5_context k5, + krb5_principal principal, + const char *principal_name, +- int *which_salt) ++ int *which_salt, ++ adcli_enroll_flags flags) + { + match_principal_kvno closure; + krb5_data password; +@@ -1547,41 +1560,47 @@ add_principal_to_keytab (adcli_enroll *enroll, + enroll->keytab_name); + } + +- password.data = enroll->computer_password; +- password.length = strlen (enroll->computer_password); +- + enctypes = adcli_enroll_get_keytab_enctypes (enroll); + +- /* +- * So we need to discover which salt to use. As a side effect we are +- * also testing that our account works. +- */ ++ if (flags & ADCLI_ENROLL_PASSWORD_VALID) { ++ code = _adcli_krb5_keytab_copy_entries (k5, enroll->keytab, principal, ++ enroll->kvno, enctypes); ++ } else { + +- salts = build_principal_salts (enroll, k5, principal); +- return_unexpected_if_fail (salts != NULL); ++ password.data = enroll->computer_password; ++ password.length = strlen (enroll->computer_password); + +- if (*which_salt < 0) { +- code = _adcli_krb5_keytab_discover_salt (k5, principal, enroll->kvno, &password, +- enctypes, salts, which_salt); +- if (code != 0) { +- _adcli_warn ("Couldn't authenticate with keytab while discovering which salt to use: %s: %s", +- principal_name, krb5_get_error_message (k5, code)); +- *which_salt = DEFAULT_SALT; +- } else { +- assert (*which_salt >= 0); +- _adcli_info ("Discovered which keytab salt to use"); ++ /* ++ * So we need to discover which salt to use. As a side effect we are ++ * also testing that our account works. ++ */ ++ ++ salts = build_principal_salts (enroll, k5, principal); ++ return_unexpected_if_fail (salts != NULL); ++ ++ if (*which_salt < 0) { ++ code = _adcli_krb5_keytab_discover_salt (k5, principal, enroll->kvno, &password, ++ enctypes, salts, which_salt); ++ if (code != 0) { ++ _adcli_warn ("Couldn't authenticate with keytab while discovering which salt to use: %s: %s", ++ principal_name, krb5_get_error_message (k5, code)); ++ *which_salt = DEFAULT_SALT; ++ } else { ++ assert (*which_salt >= 0); ++ _adcli_info ("Discovered which keytab salt to use"); ++ } + } +- } + +- code = _adcli_krb5_keytab_add_entries (k5, enroll->keytab, principal, +- enroll->kvno, &password, enctypes, &salts[*which_salt]); ++ code = _adcli_krb5_keytab_add_entries (k5, enroll->keytab, principal, ++ enroll->kvno, &password, enctypes, &salts[*which_salt]); + +- free_principal_salts (k5, salts); ++ free_principal_salts (k5, salts); + +- if (code != 0) { +- _adcli_err ("Couldn't add keytab entries: %s: %s", +- enroll->keytab_name, krb5_get_error_message (k5, code)); +- return ADCLI_ERR_FAIL; ++ if (code != 0) { ++ _adcli_err ("Couldn't add keytab entries: %s: %s", ++ enroll->keytab_name, krb5_get_error_message (k5, code)); ++ return ADCLI_ERR_FAIL; ++ } + } + + +@@ -1591,7 +1610,8 @@ add_principal_to_keytab (adcli_enroll *enroll, + } + + static adcli_result +-update_keytab_for_principals (adcli_enroll *enroll) ++update_keytab_for_principals (adcli_enroll *enroll, ++ adcli_enroll_flags flags) + { + krb5_context k5; + adcli_result res; +@@ -1608,7 +1628,7 @@ update_keytab_for_principals (adcli_enroll *enroll) + if (krb5_unparse_name (k5, enroll->keytab_principals[i], &name) != 0) + name = ""; + res = add_principal_to_keytab (enroll, k5, enroll->keytab_principals[i], +- name, &which_salt); ++ name, &which_salt, flags); + krb5_free_unparsed_name (k5, name); + + if (res != ADCLI_SUCCESS) +@@ -1807,6 +1827,20 @@ enroll_join_or_update_tasks (adcli_enroll *enroll, + /* We ignore failures of setting these fields */ + update_and_calculate_enctypes (enroll); + update_computer_account (enroll); ++ ++ /* service_names is only set from input on the command line, so no ++ * additional check for explicit is needed here */ ++ if (enroll->service_names != NULL) { ++ res = add_service_names_to_service_principals (enroll); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } ++ res = ensure_keytab_principals (res, enroll); ++ if (res != ADCLI_SUCCESS) { ++ return res; ++ } ++ } ++ + update_service_principals (enroll); + + if ( (flags & ADCLI_ENROLL_ADD_SAMBA_DATA) && ! (flags & ADCLI_ENROLL_PASSWORD_VALID)) { +@@ -1826,7 +1860,7 @@ enroll_join_or_update_tasks (adcli_enroll *enroll, + * that we use for salting. + */ + +- return update_keytab_for_principals (enroll); ++ return update_keytab_for_principals (enroll, flags); + } + + adcli_result +@@ -1927,7 +1961,11 @@ adcli_enroll_update (adcli_enroll *enroll, + + if (_adcli_check_nt_time_string_lifetime (value, + adcli_enroll_get_computer_password_lifetime (enroll))) { +- flags |= ADCLI_ENROLL_NO_KEYTAB; ++ /* Do not update keytab if neither new service principals have ++ * to be added nor the user principal has to be changed. */ ++ if (enroll->service_names == NULL && (enroll->user_principal == NULL || enroll->user_princpal_generate)) { ++ flags |= ADCLI_ENROLL_NO_KEYTAB; ++ } + flags |= ADCLI_ENROLL_PASSWORD_VALID; + } + free (value); +diff --git a/library/adkrb5.c b/library/adkrb5.c +index b0e903e..033c181 100644 +--- a/library/adkrb5.c ++++ b/library/adkrb5.c +@@ -204,6 +204,119 @@ _adcli_krb5_open_keytab (krb5_context k5, + return ADCLI_SUCCESS; + } + ++typedef struct { ++ krb5_kvno kvno; ++ krb5_enctype enctype; ++ int matched; ++} match_enctype_kvno; ++ ++static krb5_boolean ++match_enctype_and_kvno (krb5_context k5, ++ krb5_keytab_entry *entry, ++ void *data) ++{ ++ krb5_boolean similar = FALSE; ++ match_enctype_kvno *closure = data; ++ krb5_error_code code; ++ ++ assert (closure->enctype); ++ ++ code = krb5_c_enctype_compare (k5, closure->enctype, entry->key.enctype, ++ &similar); ++ ++ if (code == 0 && entry->vno == closure->kvno && similar) { ++ closure->matched = 1; ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static krb5_error_code ++_adcli_krb5_get_keyblock (krb5_context k5, ++ krb5_keytab keytab, ++ krb5_keyblock *keyblock, ++ krb5_boolean (* match_func) (krb5_context, ++ krb5_keytab_entry *, ++ void *), ++ void *match_data) ++{ ++ krb5_kt_cursor cursor; ++ krb5_keytab_entry entry; ++ krb5_error_code code; ++ ++ code = krb5_kt_start_seq_get (k5, keytab, &cursor); ++ if (code == KRB5_KT_END || code == ENOENT) ++ return 0; ++ else if (code != 0) ++ return code; ++ ++ for (;;) { ++ code = krb5_kt_next_entry (k5, keytab, &entry, &cursor); ++ if (code != 0) ++ break; ++ ++ /* See if we should remove this entry */ ++ if (!match_func (k5, &entry, match_data)) { ++ krb5_free_keytab_entry_contents (k5, &entry); ++ continue; ++ } ++ ++ code = krb5_copy_keyblock_contents (k5, &entry.key, keyblock); ++ krb5_free_keytab_entry_contents (k5, &entry); ++ break; ++ ++ ++ } ++ ++ if (code == KRB5_KT_END) ++ code = 0; ++ ++ krb5_kt_end_seq_get (k5, keytab, &cursor); ++ return code; ++} ++ ++krb5_error_code ++_adcli_krb5_keytab_copy_entries (krb5_context k5, ++ krb5_keytab keytab, ++ krb5_principal principal, ++ krb5_kvno kvno, ++ krb5_enctype *enctypes) ++{ ++ krb5_keytab_entry entry; ++ krb5_error_code code; ++ int i; ++ match_enctype_kvno closure; ++ ++ for (i = 0; enctypes[i] != 0; i++) { ++ ++ closure.kvno = kvno; ++ closure.enctype = enctypes[i]; ++ closure.matched = 0; ++ ++ memset (&entry, 0, sizeof (entry)); ++ ++ code = _adcli_krb5_get_keyblock (k5, keytab, &entry.key, ++ match_enctype_and_kvno, &closure); ++ if (code != 0) { ++ return code; ++ } ++ ++ ++ entry.principal = principal; ++ entry.vno = kvno; ++ ++ code = krb5_kt_add_entry (k5, keytab, &entry); ++ ++ entry.principal = NULL; ++ krb5_free_keytab_entry_contents (k5, &entry); ++ ++ if (code != 0) ++ return code; ++ } ++ ++ return 0; ++} + + krb5_error_code + _adcli_krb5_keytab_add_entries (krb5_context k5, +diff --git a/library/adprivate.h b/library/adprivate.h +index 83a88f6..7485249 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -282,6 +282,12 @@ krb5_enctype * _adcli_krb5_parse_enctypes (const char *value); + + char * _adcli_krb5_format_enctypes (krb5_enctype *enctypes); + ++krb5_error_code _adcli_krb5_keytab_copy_entries (krb5_context k5, ++ krb5_keytab keytab, ++ krb5_principal principal, ++ krb5_kvno kvno, ++ krb5_enctype *enctypes); ++ + struct _adcli_attrs { + LDAPMod **mods; + int len; +-- +2.14.4 + diff --git a/SOURCES/0019-Calculate-enctypes-in-a-separate-function.patch b/SOURCES/0019-Calculate-enctypes-in-a-separate-function.patch new file mode 100644 index 0000000..4a09f33 --- /dev/null +++ b/SOURCES/0019-Calculate-enctypes-in-a-separate-function.patch @@ -0,0 +1,181 @@ +From 9ad1164405e7b4decb7c4ad96fe5ab27d6e53366 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 6 Jun 2018 16:31:32 +0200 +Subject: [PATCH 19/23] Calculate enctypes in a separate function + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1542354 +--- + library/adenroll.c | 137 +++++++++++++++++++++++++++++++---------------------- + 1 file changed, 81 insertions(+), 56 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 6fdc773..75ac1e4 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -542,6 +542,83 @@ calculate_computer_account (adcli_enroll *enroll, + return ADCLI_SUCCESS; + } + ++static adcli_result ++calculate_enctypes (adcli_enroll *enroll, char **enctype) ++{ ++ char *value = NULL; ++ krb5_enctype *read_enctypes; ++ char *new_value = NULL; ++ int is_2008_or_later; ++ LDAP *ldap; ++ ++ *enctype = NULL; ++ /* ++ * Because we're using a keytab we want the server to be aware of the ++ * encryption types supported on the client, because we can't dynamically ++ * use a new one that's thrown at us. ++ * ++ * If the encryption types are not explicitly set by the caller of this ++ * library, then see if the account already has some encryption types ++ * marked on it. ++ * ++ * If not, write our default set to the account. ++ * ++ * Note that Windows 2003 and earlier have a standard set of encryption ++ * types, and no msDS-supportedEncryptionTypes attribute. ++ */ ++ ++ ldap = adcli_conn_get_ldap_connection (enroll->conn); ++ return_unexpected_if_fail (ldap != NULL); ++ ++ is_2008_or_later = adcli_conn_server_has_capability (enroll->conn, ADCLI_CAP_V60_OID); ++ ++ /* In 2008 or later, use the msDS-supportedEncryptionTypes attribute */ ++ if (is_2008_or_later) { ++ value = _adcli_ldap_parse_value (ldap, enroll->computer_attributes, ++ "msDS-supportedEncryptionTypes"); ++ ++ if (!enroll->keytab_enctypes_explicit && value != NULL) { ++ read_enctypes = _adcli_krb5_parse_enctypes (value); ++ if (read_enctypes == NULL) { ++ _adcli_warn ("Invalid or unsupported encryption types are set on " ++ "the computer account (%s).", value); ++ } else { ++ free (enroll->keytab_enctypes); ++ enroll->keytab_enctypes = read_enctypes; ++ } ++ } ++ ++ /* In 2003 or earlier, standard set of enc types */ ++ } else { ++ value = _adcli_krb5_format_enctypes (v51_earlier_enctypes); ++ } ++ ++ new_value = _adcli_krb5_format_enctypes (adcli_enroll_get_keytab_enctypes (enroll)); ++ if (new_value == NULL) { ++ free (value); ++ _adcli_warn ("The encryption types desired are not available in active directory"); ++ return ADCLI_ERR_CONFIG; ++ } ++ ++ /* If we already have this value, then don't need to update */ ++ if (value && strcmp (new_value, value) == 0) { ++ free (value); ++ free (new_value); ++ return ADCLI_SUCCESS; ++ } ++ free (value); ++ ++ if (!is_2008_or_later) { ++ free (new_value); ++ _adcli_warn ("Server does not support setting encryption types"); ++ return ADCLI_SUCCESS; ++ } ++ ++ *enctype = new_value; ++ return ADCLI_SUCCESS; ++} ++ ++ + static adcli_result + create_computer_account (adcli_enroll *enroll, + LDAP *ldap) +@@ -1053,75 +1130,23 @@ retrieve_computer_account (adcli_enroll *enroll) + static adcli_result + update_and_calculate_enctypes (adcli_enroll *enroll) + { +- char *value = NULL; +- krb5_enctype *read_enctypes; + char *vals_supportedEncryptionTypes[] = { NULL, NULL }; + LDAPMod mod = { LDAP_MOD_REPLACE, "msDS-supportedEncryptionTypes", { vals_supportedEncryptionTypes, } }; + LDAPMod *mods[2] = { &mod, NULL }; +- int is_2008_or_later; + char *new_value; + LDAP *ldap; + int ret; + +- /* +- * Because we're using a keytab we want the server to be aware of the +- * encryption types supported on the client, because we can't dynamically +- * use a new one that's thrown at us. +- * +- * If the encryption types are not explicitly set by the caller of this +- * library, then see if the account already has some encryption types +- * marked on it. +- * +- * If not, write our default set to the account. +- * +- * Note that Windows 2003 and earlier have a standard set of encryption +- * types, and no msDS-supportedEncryptionTypes attribute. +- */ +- + ldap = adcli_conn_get_ldap_connection (enroll->conn); + return_unexpected_if_fail (ldap != NULL); + +- is_2008_or_later = adcli_conn_server_has_capability (enroll->conn, ADCLI_CAP_V60_OID); +- +- /* In 2008 or later, use the msDS-supportedEncryptionTypes attribute */ +- if (is_2008_or_later) { +- value = _adcli_ldap_parse_value (ldap, enroll->computer_attributes, +- "msDS-supportedEncryptionTypes"); +- +- if (!enroll->keytab_enctypes_explicit && value != NULL) { +- read_enctypes = _adcli_krb5_parse_enctypes (value); +- if (read_enctypes == NULL) { +- _adcli_warn ("Invalid or unsupported encryption types are set on " +- "the computer account (%s).", value); +- } else { +- free (enroll->keytab_enctypes); +- enroll->keytab_enctypes = read_enctypes; +- } +- } +- +- /* In 2003 or earlier, standard set of enc types */ +- } else { +- value = _adcli_krb5_format_enctypes (v51_earlier_enctypes); +- } +- +- new_value = _adcli_krb5_format_enctypes (adcli_enroll_get_keytab_enctypes (enroll)); +- if (new_value == NULL) { +- free (value); +- _adcli_warn ("The encryption types desired are not available in active directory"); +- return ADCLI_ERR_CONFIG; +- } +- +- /* If we already have this value, then don't need to update */ +- if (value && strcmp (new_value, value) == 0) { +- free (value); ++ ret = calculate_enctypes (enroll, &new_value); ++ if (ret != ADCLI_SUCCESS) { + free (new_value); +- return ADCLI_SUCCESS; ++ return ret; + } +- free (value); + +- if (!is_2008_or_later) { +- free (new_value); +- _adcli_warn ("Server does not support setting encryption types"); ++ if (new_value == NULL) { + return ADCLI_SUCCESS; + } + +-- +2.14.4 + diff --git a/SOURCES/0020-join-add-all-attributes-while-creating-computer-obje.patch b/SOURCES/0020-join-add-all-attributes-while-creating-computer-obje.patch new file mode 100644 index 0000000..7c76b1c --- /dev/null +++ b/SOURCES/0020-join-add-all-attributes-while-creating-computer-obje.patch @@ -0,0 +1,110 @@ +From cbe33b3e6d0d3415e4642d71942380d1793311f1 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 11 Jun 2018 09:44:49 +0200 +Subject: [PATCH 20/23] join: add all attributes while creating computer object + +It is possible to create special accounts which can only join a computer +to a domain but is not allowed to do any further operations which the +computer object. As a result if such an account is used during the join +only the ldapadd operation is permitted but not any later ldapmodify +operation. To create the computer object correctly in this case all +attributes must be added while the object is created and not later. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1542354 +--- + library/adenroll.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 47 insertions(+), 5 deletions(-) + +diff --git a/library/adenroll.c b/library/adenroll.c +index 75ac1e4..b508caf 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -573,7 +573,7 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype) + is_2008_or_later = adcli_conn_server_has_capability (enroll->conn, ADCLI_CAP_V60_OID); + + /* In 2008 or later, use the msDS-supportedEncryptionTypes attribute */ +- if (is_2008_or_later) { ++ if (is_2008_or_later && enroll->computer_attributes != NULL) { + value = _adcli_ldap_parse_value (ldap, enroll->computer_attributes, + "msDS-supportedEncryptionTypes"); + +@@ -618,7 +618,6 @@ calculate_enctypes (adcli_enroll *enroll, char **enctype) + return ADCLI_SUCCESS; + } + +- + static adcli_result + create_computer_account (adcli_enroll *enroll, + LDAP *ldap) +@@ -628,22 +627,65 @@ create_computer_account (adcli_enroll *enroll, + char *vals_sAMAccountName[] = { enroll->computer_sam, NULL }; + LDAPMod sAMAccountName = { LDAP_MOD_ADD, "sAMAccountName", { vals_sAMAccountName, } }; + char *vals_userAccountControl[] = { "69632", NULL }; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD */ +- LDAPMod userAccountControl = { LDAP_MOD_REPLACE, "userAccountControl", { vals_userAccountControl, } }; ++ LDAPMod userAccountControl = { LDAP_MOD_ADD, "userAccountControl", { vals_userAccountControl, } }; ++ char *vals_supportedEncryptionTypes[] = { NULL, NULL }; ++ LDAPMod encTypes = { LDAP_MOD_ADD, "msDS-supportedEncryptionTypes", { vals_supportedEncryptionTypes, } }; ++ char *vals_dNSHostName[] = { enroll->host_fqdn, NULL }; ++ LDAPMod dNSHostName = { LDAP_MOD_ADD, "dNSHostName", { vals_dNSHostName, } }; ++ char *vals_operatingSystem[] = { enroll->os_name, NULL }; ++ LDAPMod operatingSystem = { LDAP_MOD_ADD, "operatingSystem", { vals_operatingSystem, } }; ++ char *vals_operatingSystemVersion[] = { enroll->os_version, NULL }; ++ LDAPMod operatingSystemVersion = { LDAP_MOD_ADD, "operatingSystemVersion", { vals_operatingSystemVersion, } }; ++ char *vals_operatingSystemServicePack[] = { enroll->os_service_pack, NULL }; ++ LDAPMod operatingSystemServicePack = { LDAP_MOD_ADD, "operatingSystemServicePack", { vals_operatingSystemServicePack, } }; ++ char *vals_userPrincipalName[] = { enroll->user_principal, NULL }; ++ LDAPMod userPrincipalName = { LDAP_MOD_ADD, "userPrincipalName", { vals_userPrincipalName, }, }; ++ LDAPMod servicePrincipalName = { LDAP_MOD_ADD, "servicePrincipalName", { enroll->service_principals, } }; ++ ++ char *val = NULL; + + int ret; ++ size_t c; ++ size_t m; + +- LDAPMod *mods[] = { ++ LDAPMod *all_mods[] = { + &objectClass, + &sAMAccountName, + &userAccountControl, +- NULL, ++ &encTypes, ++ &dNSHostName, ++ &operatingSystem, ++ &operatingSystemVersion, ++ &operatingSystemServicePack, ++ &userPrincipalName, ++ &servicePrincipalName, ++ NULL + }; + ++ size_t mods_count = sizeof (all_mods) / sizeof (LDAPMod *); ++ LDAPMod *mods[mods_count]; ++ + if (adcli_enroll_get_trusted_for_delegation (enroll)) { + vals_userAccountControl[0] = "593920"; /* WORKSTATION_TRUST_ACCOUNT | DONT_EXPIRE_PASSWD | TRUSTED_FOR_DELEGATION */ + } + ++ ret = calculate_enctypes (enroll, &val); ++ if (ret != ADCLI_SUCCESS) { ++ return ret; ++ } ++ vals_supportedEncryptionTypes[0] = val; ++ ++ m = 0; ++ for (c = 0; c < mods_count - 1; c++) { ++ /* Skip empty LDAP sttributes */ ++ if (all_mods[c]->mod_vals.modv_strvals[0] != NULL) { ++ mods[m++] = all_mods[c]; ++ } ++ } ++ mods[m] = NULL; ++ + ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL); ++ free (val); + + /* + * Hand to head. This is really dumb... AD returns +-- +2.14.4 + diff --git a/SOURCES/0021-util-add-_adcli_strv_remove_unsorted.patch b/SOURCES/0021-util-add-_adcli_strv_remove_unsorted.patch new file mode 100644 index 0000000..f9dea0f --- /dev/null +++ b/SOURCES/0021-util-add-_adcli_strv_remove_unsorted.patch @@ -0,0 +1,288 @@ +From 4208646609da9b25b70c21f5f39c92fabbd59dfc Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 14 Jun 2018 16:48:22 +0200 +Subject: [PATCH 21/23] util: add _adcli_strv_remove_unsorted + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1547014 +--- + library/adprivate.h | 4 ++ + library/adutil.c | 21 ++++++++ + library/seq.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++--- + library/seq.h | 12 +++++ + 4 files changed, 179 insertions(+), 7 deletions(-) + +diff --git a/library/adprivate.h b/library/adprivate.h +index 7485249..bc9df6d 100644 +--- a/library/adprivate.h ++++ b/library/adprivate.h +@@ -111,6 +111,10 @@ char ** _adcli_strv_add (char **strv, + char *string, + int *length) GNUC_WARN_UNUSED; + ++void _adcli_strv_remove_unsorted (char **strv, ++ const char *string, ++ int *length); ++ + void _adcli_strv_free (char **strv); + + int _adcli_strv_has (char **strv, +diff --git a/library/adutil.c b/library/adutil.c +index a27bd68..6334b52 100644 +--- a/library/adutil.c ++++ b/library/adutil.c +@@ -221,6 +221,27 @@ _adcli_strv_add (char **strv, + return seq_push (strv, length, string); + } + ++#define discard_const(ptr) ((void *)((uintptr_t)(ptr))) ++ ++void ++_adcli_strv_remove_unsorted (char **strv, ++ const char *string, ++ int *length) ++{ ++ int len; ++ ++ return_if_fail (string != NULL); ++ ++ if (!length) { ++ len = seq_count (strv); ++ length = &len; ++ } ++ ++ return seq_remove_unsorted (strv, length, discard_const (string), ++ (seq_compar)strcasecmp, free); ++} ++ ++ + int + _adcli_strv_has (char **strv, + const char *str) +diff --git a/library/seq.c b/library/seq.c +index 627dcaf..8e7475d 100644 +--- a/library/seq.c ++++ b/library/seq.c +@@ -111,6 +111,24 @@ seq_push (seq_voidp sequence, + return seq; + } + ++static int ++linear_search (void **seq, ++ int low, ++ int high, ++ void *match, ++ seq_compar compar) ++{ ++ int at; ++ ++ for (at = low; at < high; at++) { ++ if (compar (match, seq[at]) == 0) { ++ break; ++ } ++ } ++ ++ return at; ++} ++ + static int + binary_search (void **seq, + int low, +@@ -171,12 +189,13 @@ seq_insert (seq_voidp sequence, + return seq; + } + +-void +-seq_remove (seq_voidp sequence, +- int *length, +- void *match, +- seq_compar compar, +- seq_destroy destroy) ++static void ++seq_remove_int (seq_voidp sequence, ++ int *length, ++ void *match, ++ seq_search search, ++ seq_compar compar, ++ seq_destroy destroy) + { + void **seq = sequence; + int at; +@@ -187,7 +206,7 @@ seq_remove (seq_voidp sequence, + assert (match != NULL); + + len = *length; +- at = binary_search (seq, 0, len, match, compar); ++ at = search (seq, 0, len, match, compar); + + /* We have a matching value */ + if (at < len && compar (match, seq[at]) == 0) { +@@ -201,6 +220,26 @@ seq_remove (seq_voidp sequence, + *length = len; + } + ++void ++seq_remove (seq_voidp sequence, ++ int *length, ++ void *match, ++ seq_compar compar, ++ seq_destroy destroy) ++{ ++ return seq_remove_int (sequence, length, match, binary_search, compar, destroy); ++} ++ ++void ++seq_remove_unsorted (seq_voidp sequence, ++ int *length, ++ void *match, ++ seq_compar compar, ++ seq_destroy destroy) ++{ ++ return seq_remove_int (sequence, length, match, linear_search, compar, destroy); ++} ++ + void + seq_filter (seq_voidp sequence, + int *length, +@@ -430,6 +469,99 @@ test_remove (void) + seq_free (seq, NULL); + } + ++static void ++test_remove_unsorted (void) ++{ ++ void **seq = NULL; ++ int len = 0; ++ ++ seq = seq_push (seq, &len, "3"); ++ seq = seq_push (seq, &len, "5"); ++ seq = seq_push (seq, &len, "1"); ++ seq = seq_push (seq, &len, "4"); ++ seq = seq_push (seq, &len, "2"); ++ ++ assert_str_eq (seq[0], "3"); ++ assert_str_eq (seq[1], "5"); ++ assert_str_eq (seq[2], "1"); ++ assert_str_eq (seq[3], "4"); ++ assert_str_eq (seq[4], "2"); ++ assert (seq[5] == NULL); ++ assert_num_eq (len, 5); ++ ++ seq_remove_unsorted (seq, &len, "3", (seq_compar)strcmp, NULL); ++ seq_remove_unsorted (seq, &len, "2", (seq_compar)strcmp, NULL); ++ ++ assert_str_eq (seq[0], "5"); ++ assert_str_eq (seq[1], "1"); ++ assert_str_eq (seq[2], "4"); ++ assert (seq[3] == NULL); ++ assert_num_eq (len, 3); ++ ++ seq_free (seq, NULL); ++} ++ ++static void ++test_remove_first (void) ++{ ++ void **seq = NULL; ++ int len = 0; ++ ++ seq = seq_insert (seq, &len, "3", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "5", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "1", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "4", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "2", (seq_compar)strcmp, NULL); ++ ++ assert_str_eq (seq[0], "1"); ++ assert_str_eq (seq[1], "2"); ++ assert_str_eq (seq[2], "3"); ++ assert_str_eq (seq[3], "4"); ++ assert_str_eq (seq[4], "5"); ++ assert (seq[5] == NULL); ++ assert_num_eq (len, 5); ++ ++ seq_remove (seq, &len, "1", (seq_compar)strcmp, NULL); ++ ++ assert_str_eq (seq[0], "2"); ++ assert_str_eq (seq[1], "3"); ++ assert_str_eq (seq[2], "4"); ++ assert_str_eq (seq[3], "5"); ++ assert (seq[4] == NULL); ++ assert_num_eq (len, 4); ++ ++ seq_free (seq, NULL); ++} ++ ++static void ++test_remove_last (void) ++{ ++ void **seq = NULL; ++ int len = 0; ++ ++ seq = seq_insert (seq, &len, "3", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "1", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "4", (seq_compar)strcmp, NULL); ++ seq = seq_insert (seq, &len, "2", (seq_compar)strcmp, NULL); ++ ++ assert_str_eq (seq[0], "1"); ++ assert_str_eq (seq[1], "2"); ++ assert_str_eq (seq[2], "3"); ++ assert_str_eq (seq[3], "4"); ++ assert (seq[4] == NULL); ++ assert_num_eq (len, 4); ++ ++ seq_remove (seq, &len, "4", (seq_compar)strcmp, NULL); ++ ++ assert_str_eq (seq[0], "1"); ++ assert_str_eq (seq[1], "2"); ++ assert_str_eq (seq[2], "3"); ++ assert (seq[3] == NULL); ++ assert_num_eq (len, 3); ++ ++ seq_free (seq, NULL); ++} ++ + static int + compar_even (void *match, + void *value) +@@ -631,6 +763,9 @@ main (int argc, + test_func (test_insert, "/seq/insert"); + test_func (test_insert_destroys, "/seq/insert_destroys"); + test_func (test_remove, "/seq/remove"); ++ test_func (test_remove_unsorted, "/seq/remove_unsorted"); ++ test_func (test_remove_first, "/seq/remove_first"); ++ test_func (test_remove_last, "/seq/remove_last"); + test_func (test_remove_destroys, "/seq/remove_destroys"); + test_func (test_filter, "/seq/filter"); + test_func (test_filter_null, "/seq/filter_null"); +diff --git a/library/seq.h b/library/seq.h +index 694965b..5d48848 100644 +--- a/library/seq.h ++++ b/library/seq.h +@@ -44,6 +44,12 @@ typedef void * (* seq_copy) (void *value); + + typedef void (* seq_destroy) (void *value); + ++typedef int (* seq_search) (void **seq, ++ int low, ++ int high, ++ void *match, ++ seq_compar compar); ++ + seq_voidp seq_push (seq_voidp seq, + int *length, + void *value) WARN_UNUSED; +@@ -62,6 +68,12 @@ void seq_remove (seq_voidp seq, + seq_compar compar, + seq_destroy destroy); + ++void seq_remove_unsorted (seq_voidp seq, ++ int *length, ++ void *match, ++ seq_compar compar, ++ seq_destroy destroy); ++ + seq_voidp seq_lookup (seq_voidp seq, + int *length, + void *match, +-- +2.14.4 + diff --git a/SOURCES/0022-Add-add-service-principal-and-remove-service-princip.patch b/SOURCES/0022-Add-add-service-principal-and-remove-service-princip.patch new file mode 100644 index 0000000..a92414a --- /dev/null +++ b/SOURCES/0022-Add-add-service-principal-and-remove-service-princip.patch @@ -0,0 +1,360 @@ +From ee71c4c0614a504b4472bf64a24fc3c18c6b9987 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 14 Jun 2018 16:49:26 +0200 +Subject: [PATCH 22/23] Add add-service-principal and remove-service-principal + options + +Currently it is only possible to specific a service name for service +principals but not to set the full service principal. This is e.g. +needed if there is a service running on a host which should be reachable +by a different DNS name as well. + +With this patch service principal can be added and removed by specifying +the full name. + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1547014 +--- + doc/adcli.xml | 21 ++++++++ + library/adenroll.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++-- + library/adenroll.h | 8 +++ + library/adldap.c | 16 ++++-- + tools/computer.c | 13 +++++ + 5 files changed, 189 insertions(+), 8 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index b246190..83b6981 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -290,6 +290,14 @@ Password for Administrator: + not allow that Kerberos tickets can be forwarded to the + host. + ++ ++ ++ Add a service principal name. In ++ contrast to the the ++ hostname part can be specified as well in case the ++ service should be accessible with a different host ++ name as well. ++ + + + After a successful join print out information +@@ -416,6 +424,19 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + not allow that Kerberos tickets can be forwarded to the + host. + ++ ++ ++ Add a service principal name. In ++ contrast to the the ++ hostname part can be specified as well in case the ++ service should be accessible with a different host ++ name as well. ++ ++ ++ ++ Remove a service principal name from ++ the keytab and the AD host object. ++ + + + After a successful join print out information +diff --git a/library/adenroll.c b/library/adenroll.c +index b508caf..c4ba537 100644 +--- a/library/adenroll.c ++++ b/library/adenroll.c +@@ -95,6 +95,9 @@ struct _adcli_enroll { + char **service_principals; + int service_principals_explicit; + ++ char **service_principals_to_add; ++ char **service_principals_to_remove; ++ + char *user_principal; + int user_princpal_generate; + +@@ -332,6 +335,43 @@ add_service_names_to_service_principals (adcli_enroll *enroll) + return ADCLI_SUCCESS; + } + ++static adcli_result ++add_and_remove_service_principals (adcli_enroll *enroll) ++{ ++ int length = 0; ++ size_t c; ++ const char **list; ++ ++ if (enroll->service_principals != NULL) { ++ length = seq_count (enroll->service_principals); ++ } ++ ++ list = adcli_enroll_get_service_principals_to_add (enroll); ++ if (list != NULL) { ++ for (c = 0; list[c] != NULL; c++) { ++ enroll->service_principals = _adcli_strv_add (enroll->service_principals, ++ strdup (list[c]), ++ &length); ++ if (enroll->service_principals == NULL) { ++ return ADCLI_ERR_UNEXPECTED; ++ } ++ } ++ } ++ ++ list = adcli_enroll_get_service_principals_to_remove (enroll); ++ if (list != NULL) { ++ for (c = 0; list[c] != NULL; c++) { ++ /* enroll->service_principals typically refects the ++ * order of the principal in the keytabm so it is not ++ * ordered. */ ++ _adcli_strv_remove_unsorted (enroll->service_principals, ++ list[c], &length); ++ } ++ } ++ ++ return ADCLI_SUCCESS; ++} ++ + static adcli_result + ensure_service_principals (adcli_result res, + adcli_enroll *enroll) +@@ -343,10 +383,14 @@ ensure_service_principals (adcli_result res, + + if (!enroll->service_principals) { + assert (enroll->service_names != NULL); +- return add_service_names_to_service_principals (enroll); ++ res = add_service_names_to_service_principals (enroll); + } + +- return ADCLI_SUCCESS; ++ if (res == ADCLI_SUCCESS) { ++ res = add_and_remove_service_principals (enroll); ++ } ++ ++ return res; + } + + static adcli_result +@@ -1593,6 +1637,39 @@ free_principal_salts (krb5_context k5, + free (salts); + } + ++static adcli_result ++remove_principal_from_keytab (adcli_enroll *enroll, ++ krb5_context k5, ++ const char *principal_name) ++{ ++ krb5_error_code code; ++ krb5_principal principal; ++ match_principal_kvno closure; ++ ++ code = krb5_parse_name (k5, principal_name, &principal); ++ if (code != 0) { ++ _adcli_err ("Couldn't parse principal: %s: %s", ++ principal_name, krb5_get_error_message (k5, code)); ++ return ADCLI_ERR_FAIL; ++ } ++ ++ closure.kvno = enroll->kvno; ++ closure.principal = principal; ++ closure.matched = 0; ++ ++ code = _adcli_krb5_keytab_clear (k5, enroll->keytab, ++ match_principal_and_kvno, &closure); ++ krb5_free_principal (k5, principal); ++ ++ if (code != 0) { ++ _adcli_err ("Couldn't update keytab: %s: %s", ++ enroll->keytab_name, krb5_get_error_message (k5, code)); ++ return ADCLI_ERR_FAIL; ++ } ++ ++ return ADCLI_SUCCESS; ++} ++ + static adcli_result + add_principal_to_keytab (adcli_enroll *enroll, + krb5_context k5, +@@ -1702,6 +1779,17 @@ update_keytab_for_principals (adcli_enroll *enroll, + return res; + } + ++ if (enroll->service_principals_to_remove != NULL) { ++ for (i = 0; enroll->service_principals_to_remove[i] != NULL; i++) { ++ res = remove_principal_from_keytab (enroll, k5, ++ enroll->service_principals_to_remove[i]); ++ if (res != ADCLI_SUCCESS) { ++ _adcli_warn ("Failed to remove %s from keytab.", ++ enroll->service_principals_to_remove[i]); ++ } ++ } ++ } ++ + return ADCLI_SUCCESS; + } + +@@ -2029,8 +2117,11 @@ adcli_enroll_update (adcli_enroll *enroll, + if (_adcli_check_nt_time_string_lifetime (value, + adcli_enroll_get_computer_password_lifetime (enroll))) { + /* Do not update keytab if neither new service principals have +- * to be added nor the user principal has to be changed. */ +- if (enroll->service_names == NULL && (enroll->user_principal == NULL || enroll->user_princpal_generate)) { ++ * to be added or deleted nor the user principal has to be changed. */ ++ if (enroll->service_names == NULL ++ && (enroll->user_principal == NULL || enroll->user_princpal_generate) ++ && enroll->service_principals_to_add == NULL ++ && enroll->service_principals_to_remove == NULL) { + flags |= ADCLI_ENROLL_NO_KEYTAB; + } + flags |= ADCLI_ENROLL_PASSWORD_VALID; +@@ -2581,3 +2672,43 @@ adcli_enroll_set_trusted_for_delegation (adcli_enroll *enroll, + enroll->trusted_for_delegation = value; + enroll->trusted_for_delegation_explicit = 1; + } ++ ++const char ** ++adcli_enroll_get_service_principals_to_add (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, NULL); ++ ++ return (const char **)enroll->service_principals_to_add; ++} ++ ++void ++adcli_enroll_add_service_principal_to_add (adcli_enroll *enroll, ++ const char *value) ++{ ++ return_if_fail (enroll != NULL); ++ return_if_fail (value != NULL); ++ ++ enroll->service_principals_to_add = _adcli_strv_add (enroll->service_principals_to_add, ++ strdup (value), NULL); ++ return_if_fail (enroll->service_principals_to_add != NULL); ++} ++ ++const char ** ++adcli_enroll_get_service_principals_to_remove (adcli_enroll *enroll) ++{ ++ return_val_if_fail (enroll != NULL, NULL); ++ ++ return (const char **)enroll->service_principals_to_remove; ++} ++ ++void ++adcli_enroll_add_service_principal_to_remove (adcli_enroll *enroll, ++ const char *value) ++{ ++ return_if_fail (enroll != NULL); ++ return_if_fail (value != NULL); ++ ++ enroll->service_principals_to_remove = _adcli_strv_add (enroll->service_principals_to_remove, ++ strdup (value), NULL); ++ return_if_fail (enroll->service_principals_to_remove != NULL); ++} +diff --git a/library/adenroll.h b/library/adenroll.h +index be2ca18..f87dffa 100644 +--- a/library/adenroll.h ++++ b/library/adenroll.h +@@ -98,6 +98,14 @@ const char ** adcli_enroll_get_service_principals (adcli_enroll *enroll); + void adcli_enroll_set_service_principals (adcli_enroll *enroll, + const char **value); + ++const char ** adcli_enroll_get_service_principals_to_add (adcli_enroll *enroll); ++void adcli_enroll_add_service_principal_to_add (adcli_enroll *enroll, ++ const char *value); ++ ++const char ** adcli_enroll_get_service_principals_to_remove (adcli_enroll *enroll); ++void adcli_enroll_add_service_principal_to_remove (adcli_enroll *enroll, ++ const char *value); ++ + const char * adcli_enroll_get_user_principal (adcli_enroll *enroll); + + void adcli_enroll_set_user_principal (adcli_enroll *enroll, +diff --git a/library/adldap.c b/library/adldap.c +index 07dc373..d93efb7 100644 +--- a/library/adldap.c ++++ b/library/adldap.c +@@ -210,16 +210,24 @@ _adcli_ldap_have_in_mod (LDAPMod *mod, + struct berval *vals; + struct berval **pvals; + int count = 0; ++ int count_have = 0; + int i; + int ret; + +- /* Already in berval format, just compare */ +- if (mod->mod_op & LDAP_MOD_BVALUES) +- return _adcli_ldap_have_vals (mod->mod_vals.modv_bvals, have); +- + /* Count number of values */ + for (i = 0; mod->mod_vals.modv_strvals[i] != 0; i++) + count++; ++ for (i = 0; have[i] != 0; i++) ++ count_have++; ++ ++ /* If numbers different something has to be added or removed */ ++ if (count != count_have) { ++ return 0; ++ } ++ ++ /* Already in berval format, just compare */ ++ if (mod->mod_op & LDAP_MOD_BVALUES) ++ return _adcli_ldap_have_vals (mod->mod_vals.modv_bvals, have); + + vals = malloc (sizeof (struct berval) * (count + 1)); + pvals = malloc (sizeof (struct berval *) * (count + 1)); +diff --git a/tools/computer.c b/tools/computer.c +index b905fd1..377d449 100644 +--- a/tools/computer.c ++++ b/tools/computer.c +@@ -110,6 +110,8 @@ typedef enum { + opt_add_samba_data, + opt_samba_data_tool, + opt_trusted_for_delegation, ++ opt_add_service_principal, ++ opt_remove_service_principal, + } Option; + + static adcli_tool_desc common_usages[] = { +@@ -138,6 +140,8 @@ static adcli_tool_desc common_usages[] = { + { opt_computer_password_lifetime, "lifetime of the host accounts password in days", }, + { opt_trusted_for_delegation, "set/unset the TRUSTED_FOR_DELEGATION flag\n" + "in the userAccountControl attribute", }, ++ { opt_add_service_principal, "add the given service principal to the account\n" }, ++ { opt_remove_service_principal, "remove the given service principal from the account\n" }, + { opt_no_password, "don't prompt for or read a password" }, + { opt_prompt_password, "prompt for a password if necessary" }, + { opt_stdin_password, "read a password from stdin (until EOF) if\n" +@@ -289,6 +293,12 @@ parse_option (Option opt, + adcli_enroll_set_trusted_for_delegation (enroll, false); + } + return; ++ case opt_add_service_principal: ++ adcli_enroll_add_service_principal_to_add (enroll, optarg); ++ return; ++ case opt_remove_service_principal: ++ adcli_enroll_add_service_principal_to_remove (enroll, optarg); ++ return; + case opt_verbose: + return; + +@@ -353,6 +363,7 @@ adcli_tool_computer_join (adcli_conn *conn, + { "os-service-pack", optional_argument, NULL, opt_os_service_pack }, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, ++ { "add-service-principal", required_argument, NULL, opt_add_service_principal }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, +@@ -458,6 +469,8 @@ adcli_tool_computer_update (adcli_conn *conn, + { "user-principal", optional_argument, NULL, opt_user_principal }, + { "computer-password-lifetime", optional_argument, NULL, opt_computer_password_lifetime }, + { "trusted-for-delegation", required_argument, NULL, opt_trusted_for_delegation }, ++ { "add-service-principal", required_argument, NULL, opt_add_service_principal }, ++ { "remove-service-principal", required_argument, NULL, opt_remove_service_principal }, + { "show-details", no_argument, NULL, opt_show_details }, + { "show-password", no_argument, NULL, opt_show_password }, + { "add-samba-data", no_argument, NULL, opt_add_samba_data }, +-- +2.14.4 + diff --git a/SOURCES/0023-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch b/SOURCES/0023-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch new file mode 100644 index 0000000..7b775c3 --- /dev/null +++ b/SOURCES/0023-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch @@ -0,0 +1,32 @@ +From 026cfacabfad58ae2cebcdf6cd82d905023ea289 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 31 May 2018 17:01:36 +0200 +Subject: [PATCH 23/23] adcli_conn_is_writeable: do not crash id domain_disco + is missing + +Resolves https://bugzilla.redhat.com/show_bug.cgi?id=1575554 +--- + library/adconn.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/library/adconn.c b/library/adconn.c +index d2fb1d5..e2250e3 100644 +--- a/library/adconn.c ++++ b/library/adconn.c +@@ -1567,6 +1567,11 @@ adcli_conn_server_has_capability (adcli_conn *conn, + + bool adcli_conn_is_writeable (adcli_conn *conn) + { +- disco_dance_if_necessary (conn); +- return ( (conn->domain_disco->flags & ADCLI_DISCO_WRITABLE) != 0); ++ disco_dance_if_necessary (conn); ++ ++ if (conn->domain_disco == NULL) { ++ return false; ++ } ++ ++ return ( (conn->domain_disco->flags & ADCLI_DISCO_WRITABLE) != 0); + } +-- +2.14.4 + diff --git a/SOURCES/0024-doc-fix-typos-in-the-adcli-man-page.patch b/SOURCES/0024-doc-fix-typos-in-the-adcli-man-page.patch new file mode 100644 index 0000000..1827c95 --- /dev/null +++ b/SOURCES/0024-doc-fix-typos-in-the-adcli-man-page.patch @@ -0,0 +1,203 @@ +From 1e57862cf5d8f4f774868b3599e4a34c525ae348 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 5 Jul 2018 13:06:26 +0200 +Subject: [PATCH 24/24] doc: fix typos in the adcli man page + +Resolves https://bugzilla.redhat.com/show_bug.cgi?id=1440533 +--- + doc/adcli.xml | 44 ++++++++++++++++++++++---------------------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/doc/adcli.xml b/doc/adcli.xml +index 83b6981..97dec08 100644 +--- a/doc/adcli.xml ++++ b/doc/adcli.xml +@@ -105,19 +105,19 @@ + + + The domain to connect to. If a domain is +- not specified then the domain part of the local computer's ++ not specified, then the domain part of the local computer's + host name is used. + + + + Kerberos realm for the domain. If not +- specified then the upper cased domain name is ++ specified, then the upper cased domain name is + used. + + + + Connect to a specific domain controller. +- If not specified then an appropriate domain controller ++ If not specified, then an appropriate domain controller + is automatically discovered. + + +@@ -134,7 +134,7 @@ + + + Use the specified user account to +- authenticate with the domain. If not specified then ++ authenticate with the domain. If not specified, then + the name 'Administrator' will be used. + + +@@ -181,7 +181,7 @@ $ adcli info --domain-controller=dc.domain.example.com + adcli info will output as much information as + it can about the domain. The information is designed to be both machine + and human readable. The command will exit with a non-zero exit code +- if the domain does note exist or cannot be reached. ++ if the domain does not exist or cannot be reached. + + To show domain info for a specific domain controller use the + option to specify which domain +@@ -213,35 +213,35 @@ Password for Administrator: + + + The short non-dotted name of the computer +- account that will be created in the domain. If not specified ++ account that will be created in the domain. If not specified, + then the first portion of the + is used. + + + + The full distinguished name of the OU in +- which to create the computer account. If not specified ++ which to create the computer account. If not specified, + then the computer account will be created in a default + location. + + + + Override the local machine's fully qualified +- domain name. If not specified the local machine's hostname ++ domain name. If not specified, the local machine's hostname + will be retrieved via gethostname(). + + + + Specify the path to the host keytab where + host credentials will be written after a successful join +- operation. If not specified the default location will be ++ operation. If not specified, the default location will be + used, usually /etc/krb5.keytab. + + + + Specify the type of authentication that + will be performed before creating the machine account in +- the domain. If set to 'computer' then the computer must ++ the domain. If set to 'computer', then the computer must + already have a preset account in the domain. If not + specified and none of the other + arguments have been specified, then will try both +@@ -329,7 +329,7 @@ Password for Administrator: + + If Samba's net + cannot be found at +- &samba_data_tool; this option can ++ &samba_data_tool;, this option can + be used to specific an alternative location with the + help of an absolute path. + +@@ -351,7 +351,7 @@ Password for Administrator: + $ adcli update + + +- If used with a credential cache other attributes of the computer ++ If used with a credential cache, other attributes of the computer + account can be changed as well if the principal has sufficient + privileges. + +@@ -367,20 +367,20 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + + + The short non-dotted name of the computer +- account that will be created in the domain. If not specified ++ account that will be created in the domain. If not specified, + it will be retrieved from the keytab entries. + + + + The local machine's fully qualified +- domain name. If not specified the local machine's hostname ++ domain name. If not specified, the local machine's hostname + will be retrieved from the keytab entries. + + + + Specify the path to the host keytab where + current host credentials are stored and the new ones +- will be written to. If not specified the default ++ will be written to. If not specified, the default + location will be used, usually + /etc/krb5.keytab. + +@@ -462,7 +462,7 @@ $ adcli update --login-ccache=/tmp/krbcc_123 + + If Samba's net + cannot be found at +- &samba_data_tool; this option can ++ &samba_data_tool;, this option can + be used to specific an alternative location with the + help of an absolute path. + +@@ -493,7 +493,7 @@ $ adcli create-user Fry --domain=domain.example.com \ + + + The full distinguished name of the OU in +- which to create the user account. If not specified ++ which to create the user account. If not specified, + then the computer account will be created in a default + location. + +@@ -569,7 +569,7 @@ $ adcli create-group Pilots --domain=domain.example.com \ + + + The full distinguished name of the OU in +- which to create the group. If not specified ++ which to create the group. If not specified, + then the group will be created in a default + location. + +@@ -649,14 +649,14 @@ Password for Administrator: + + + The full distinguished name of the OU in +- which to create the computer accounts. If not specified ++ which to create the computer accounts. If not specified, + then the computer account will be created in a default + location. + + + + Specify a one time password to use when +- presetting the computer accounts. If not specified then ++ presetting the computer accounts. If not specified, then + a default password will be used, which allows for later + automatic joins. + +@@ -696,7 +696,7 @@ Password for Administrator: + Reset Computer Account + + adcli reset-computer resets a computer account +- in the domain. If a the appropriate machine is currently joined to the ++ in the domain. If the appropriate machine is currently joined to the + domain, then its membership will be broken. The account must already + exist. + +@@ -716,7 +716,7 @@ $ adcli reset-computer --domain=domain.example.com host2 + + Specify the type of authentication that + will be performed before creating the machine account in +- the domain. If set to 'computer' then the computer must ++ the domain. If set to 'computer', then the computer must + already have a preset account in the domain. If not + specified and none of the other + arguments have been specified, then will try both +-- +2.14.4 + diff --git a/SPECS/adcli.spec b/SPECS/adcli.spec new file mode 100644 index 0000000..c60909d --- /dev/null +++ b/SPECS/adcli.spec @@ -0,0 +1,235 @@ +Name: adcli +Version: 0.8.2 +Release: 2%{?dist} +Summary: Active Directory enrollment +License: LGPLv2+ +URL: http://cgit.freedesktop.org/realmd/adcli +Source0: http://www.freedesktop.org/software/realmd/releases/adcli-%{version}.tar.gz + +Patch1: 0001-Remove-upper-case-only-check-when-looking-for-the-Ne.patch +Patch2: 0002-Use-strdup-if-offset-are-used.patch +Patch3: 0003-correct-spelling-of-adcli_tool_computer_delete-descr.patch +Patch4: 0004-doc-explain-that-all-credential-cache-types-are-supp.patch +Patch5: 0005-library-add-adcli_conn_is_writeable.patch +Patch6: 0006-Handle-kvno-increment-for-RODCs.patch +Patch7: 0007-Fix-memory-leak-in-test_check_nt_time_string_lifetim.patch +Patch8: 0008-library-add-_adcli_bin_sid_to_str.patch +Patch9: 0009-library-add-_adcli_call_external_program.patch +Patch10: 0010-library-add-_adcli_ldap_parse_sid.patch +Patch11: 0011-library-add-lookup_domain_sid.patch +Patch12: 0012-library-add-adcli_conn_get_domain_sid.patch +Patch13: 0013-tools-add-option-add-samba-data.patch +Patch14: 0014-tools-store-Samba-data-if-requested.patch +Patch15: 0015-make-Samba-data-tool-configurable.patch +Patch16: 0016-Add-trusted-for-delegation-option.patch +Patch17: 0017-Only-update-attributes-given-on-the-command-line.patch +Patch18: 0018-update-allow-to-add-service-names.patch +Patch19: 0019-Calculate-enctypes-in-a-separate-function.patch +Patch20: 0020-join-add-all-attributes-while-creating-computer-obje.patch +Patch21: 0021-util-add-_adcli_strv_remove_unsorted.patch +Patch22: 0022-Add-add-service-principal-and-remove-service-princip.patch +Patch23: 0023-adcli_conn_is_writeable-do-not-crash-id-domain_disco.patch +Patch24: 0024-doc-fix-typos-in-the-adcli-man-page.patch + +Patch25: 0001-fix-typo-in-flag-value.patch +Patch26: 0002-_adcli_call_external_program-silence-noisy-debug-mes.patch +Patch27: 0003-Do-not-add-service-principals-twice.patch +Patch28: 0004-Do-not-depend-on-default_realm-in-krb5.conf.patch + +BuildRequires: gcc +BuildRequires: intltool pkgconfig +BuildRequires: libtool +BuildRequires: gettext-devel +BuildRequires: krb5-devel +BuildRequires: openldap-devel +BuildRequires: libxslt +BuildRequires: xmlto + +Requires: cyrus-sasl-gssapi + +# adcli no longer has a library of development files +# the adcli tool itself is to be used by callers +Obsoletes: adcli-devel < 0.5 + +%description +adcli is a tool for joining an Active Directory domain using +standard LDAP and Kerberos calls. + +%define _hardened_build 1 + +%prep +%setup -q +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 + +%build +autoreconf --force --install --verbose +%configure --disable-static --disable-silent-rules +make %{?_smp_mflags} + +%check +make check + +%install +make install DESTDIR=%{buildroot} +find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%clean + +%files +%{_sbindir}/adcli +%doc AUTHORS COPYING ChangeLog NEWS README +%doc %{_mandir}/*/* + +%package doc +Summary: adcli documentation +BuildArch: noarch + +%description doc +adcli is a tool for joining an Active Directory domain using +standard LDAP and Kerberos calls. This package contains its +documentation. + +%files doc +%doc %{_datadir}/doc/adcli/* + +%changelog +* Tue Oct 09 2018 Sumit Bose - 0.8.2-2 +- Do not add service principals twice and related fixes +- Resolves: rhbz#1631734 + +* Thu Jul 05 2018 Sumit Bose - 0.8.2-1 +- Update to upstream release 0.8.2 +- various other fixes and improvements from the latest Fedora update + +* Wed Feb 07 2018 Fedora Release Engineering - 0.8.0-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Wed Aug 02 2017 Fedora Release Engineering - 0.8.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 0.8.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Feb 10 2017 Fedora Release Engineering - 0.8.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Feb 03 2016 Fedora Release Engineering - 0.8.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Thu Dec 17 2015 Sumit Bose - 0.8.0-1 +- Update to upstream release 0.8.0 + +* Mon Oct 19 2015 Stef Walter - 0.7.6-1 +- Fix issue with keytab use with sshd +- Resolves: rhbz#1267319 +- Put documentation in a subpackage + +* Tue Jun 16 2015 Fedora Release Engineering - 0.7.5-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Fri Aug 15 2014 Fedora Release Engineering - 0.7.5-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Sat Jun 07 2014 Fedora Release Engineering - 0.7.5-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Thu Jan 30 2014 Stef Walter - 0.7.5-2 +- Fix incorrect ownership of manual page directory + +* Fri Sep 13 2013 Stef Walter - 0.7.5-1 +- Update to upstream point release 0.7.5 +- Workaround for discovery via IPv6 address +- Correctly put IPv6 addresses in temporary krb5.conf + +* Mon Sep 09 2013 Stef Walter - 0.7.4-1 +- Update to upstream point release 0.7.4 +- Correctly handle truncating long host names +- Try to contact all available addresses for discovery +- Build fixes + +* Wed Aug 07 2013 Stef Walter - 0.7.3-1 +- Update to upstream point release 0.7.3 +- Don't try to set encryption types on Windows 2003 + +* Sat Aug 03 2013 Fedora Release Engineering - 0.7.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Mon Jul 22 2013 Stef Walter - 0.7.2-1 +- Update to upstream point release 0.7.2 +- Part of fix for bug [#961244] + +* Mon Jul 15 2013 Stef Walter - 0.7.1-4 +- Build with verbose output logging + +* Tue Jun 11 2013 Stef Walter - 0.7.1-3 +- Run 'make check' when building the package + +* Mon May 13 2013 Stef Walter - 0.7.1-2 +- Bump version to get around botched update + +* Mon May 13 2013 Stef Walter - 0.7.1-1 +- Update to upstream 0.7.1 release +- Fix problems with salt discovery [#961399] + +* Mon May 06 2013 Stef Walter - 0.7-1 +- Work around broken krb5 with empty passwords [#960001] +- Fix memory corruption issue [#959999] +- Update to 0.7, fixing various bugs + +* Mon Apr 29 2013 Stef Walter - 0.6-1 +- Update to 0.6, fixing various bugs + +* Wed Apr 10 2013 Stef walter - 0.5-2 +- Add appropriate Obsoletes line for libadcli removal + +* Wed Apr 10 2013 Stef Walter - 0.5-1 +- Update to upstream 0.5 version +- No more libadcli, and thus no adcli-devel +- Many new adcli commands +- Documentation + +* Wed Feb 13 2013 Fedora Release Engineering - 0.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Mon Nov 12 2012 Stef Walter - 0.4-1 +- Update for 0.4 version, fixing various bugs + +* Sat Oct 20 2012 Stef Walter - 0.3-1 +- Update for 0.3 version + +* Tue Sep 4 2012 Stef Walter - 0.2-1 +- Update for 0.2 version + +* Wed Aug 15 2012 Stef Walter - 0.1-1 +- Initial 0.1 package