diff options
-rw-r--r-- | doc/adcli.xml | 22 | ||||
-rw-r--r-- | library/adenroll.c | 148 | ||||
-rw-r--r-- | library/adenroll.h | 4 | ||||
-rw-r--r-- | tools/computer.c | 13 |
4 files changed, 175 insertions, 12 deletions
diff --git a/doc/adcli.xml b/doc/adcli.xml index 324ce06..93e1520 100644 --- a/doc/adcli.xml +++ b/doc/adcli.xml @@ -426,6 +426,17 @@ Password for Administrator: be used to specific an alternative location with the help of an absolute path.</para></listitem> </varlistentry> + <varlistentry> + <term><option>--ldap-passwd</option></term> + <listitem><para>Use LDAP add/mod operations to set the + machine account password instead of Kerberos. This + might help in some situations where Kerberos fails or + is unreliable. But please note that 'Change password' + or 'Reset password' permissions or similar might be + needed to make the LDAP operation work. Additionally + there will be no read-only domain controller (RODC) + support as there is with Kerberos.</para></listitem> + </varlistentry> </variablelist> <para>If supported on the AD side the @@ -621,6 +632,17 @@ $ adcli update --login-ccache=/tmp/krbcc_123 be used to specific an alternative location with the help of an absolute path.</para></listitem> </varlistentry> + <varlistentry> + <term><option>--ldap-passwd</option></term> + <listitem><para>Use LDAP add/mod operations to set the + machine account password instead of Kerberos. This + might help in some situations where Kerberos fails or + is unreliable. But please note that 'Change password' + or 'Rest password' permissions or similar might be + needed to make the LDAP operation work. Additionally + there will be no read-only domain controller (RODC) + support as there is with Kerberos.</para></listitem> + </varlistentry> </variablelist> <para>If supported on the AD side the diff --git a/library/adenroll.c b/library/adenroll.c index ef60e2c..123fb79 100644 --- a/library/adenroll.c +++ b/library/adenroll.c @@ -43,6 +43,8 @@ #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> +#include <iconv.h> +#include <lber.h> #ifndef SAMBA_DATA_TOOL #define SAMBA_DATA_TOOL "/usr/bin/net" @@ -880,9 +882,72 @@ get_del_mods_for_attrs (adcli_enroll *enroll, int mod_op) return mods; } +static struct berval *get_unicode_pwd (char *pwd) +{ + iconv_t cd; + size_t s; + char *in = NULL; + char *in_ptr; + size_t in_size; + size_t len; + char *out = NULL; + char *out_ptr; + size_t out_size; + struct berval *bv = NULL; + + if (pwd == NULL) { + return NULL; + } + + if (asprintf (&in, "\"%s\"",pwd) < 0) { + return NULL; + } + in_ptr = in; + len = in_size = strlen (in); + + out_size = 2*in_size; + out = malloc (out_size * sizeof (char)); + out_ptr = out; + + cd = iconv_open ("UTF-16LE", "UTF-8"); + if (cd == (iconv_t) -1 ) { + goto done; + } + + s = iconv (cd, &in_ptr, &in_size, &out_ptr, &out_size); + if (s == (size_t) -1 || out_size != 0) { + goto done; + } + + s = iconv (cd, NULL, NULL, &out_ptr, &out_size); + if (s == (size_t) -1) { + goto done; + } + + if (iconv_close (cd) != 0) { + goto done; + } + + bv = malloc (sizeof(struct berval)); + if (bv == NULL) { + goto done; + } + + bv->bv_len = 2*len; + bv->bv_val = out; + +done: + free (in); + if (bv == NULL) { + free (out); + } + + return bv; +} + static adcli_result create_computer_account (adcli_enroll *enroll, - LDAP *ldap) + LDAP *ldap, int ldap_passwd) { char *vals_objectClass[] = { enroll->is_service ? "msDS-ManagedServiceAccount" : "computer", NULL }; LDAPMod objectClass = { LDAP_MOD_ADD, "objectClass", { vals_objectClass, } }; @@ -905,6 +970,8 @@ create_computer_account (adcli_enroll *enroll, LDAPMod servicePrincipalName = { LDAP_MOD_ADD, "servicePrincipalName", { enroll->service_principals, } }; char *vals_description[] = { enroll->description, NULL }; LDAPMod description = { LDAP_MOD_ADD, "description", { vals_description, }, }; + struct berval *vals_unicodePwd[] = { NULL, NULL }; + LDAPMod unicodePwd = { LDAP_MOD_ADD | LDAP_MOD_BVALUES, "unicodePwd", { NULL, } }; char *val = NULL; @@ -927,12 +994,23 @@ create_computer_account (adcli_enroll *enroll, &userPrincipalName, &servicePrincipalName, &description, + &unicodePwd, NULL }; size_t mods_count = sizeof (all_mods) / sizeof (LDAPMod *); LDAPMod **mods; + if (ldap_passwd) { + _adcli_info ("Trying to set %s password with LDAP", s_or_c (enroll)); + + vals_unicodePwd[0] = get_unicode_pwd (enroll->computer_password); + if (vals_unicodePwd[0] == NULL) { + return ADCLI_ERR_FAIL; + } + unicodePwd.mod_vals.modv_bvals = vals_unicodePwd; + } + if (adcli_enroll_get_trusted_for_delegation (enroll)) { uac |= UAC_TRUSTED_FOR_DELEGATION; } @@ -943,6 +1021,7 @@ create_computer_account (adcli_enroll *enroll, } if (asprintf (&uac_str, "%d", uac) < 0) { + ber_bvfree (vals_unicodePwd[0]); return_val_if_reached (ADCLI_ERR_UNEXPECTED); } vals_userAccountControl[0] = uac_str; @@ -950,6 +1029,7 @@ create_computer_account (adcli_enroll *enroll, ret = calculate_enctypes (enroll, &val); if (ret != ADCLI_SUCCESS) { free (uac_str); + ber_bvfree (vals_unicodePwd[0]); return ret; } vals_supportedEncryptionTypes[0] = val; @@ -979,6 +1059,7 @@ create_computer_account (adcli_enroll *enroll, mods[m] = NULL; ret = ldap_add_ext_s (ldap, enroll->computer_dn, mods, NULL, NULL); + ber_bvfree (vals_unicodePwd[0]); ldap_mods_free (extra_mods, 1); free (mods); free (uac_str); @@ -1290,7 +1371,7 @@ get_service_account_name_from_ldap (adcli_enroll *enroll, LDAPMessage *results) static adcli_result locate_or_create_computer_account (adcli_enroll *enroll, - int allow_overwrite) + int allow_overwrite, int ldap_passwd) { LDAPMessage *results = NULL; LDAPMessage *entry = NULL; @@ -1352,7 +1433,7 @@ locate_or_create_computer_account (adcli_enroll *enroll, res = validate_computer_account (enroll, allow_overwrite, entry != NULL); if (res == ADCLI_SUCCESS && entry == NULL) - res = create_computer_account (enroll, ldap); + res = create_computer_account (enroll, ldap, ldap_passwd); /* Service account already exists, just continue and update the * password */ @@ -1367,6 +1448,47 @@ locate_or_create_computer_account (adcli_enroll *enroll, } static adcli_result +set_password_with_ldap (adcli_enroll *enroll) +{ + LDAP *ldap; + int ret; + struct berval *vals_unicodePwd[] = { NULL, NULL }; + LDAPMod unicodePwd = { LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, "unicodePwd", { NULL, } }; + + LDAPMod *all_mods[] = { + &unicodePwd, + NULL + }; + + ldap = adcli_conn_get_ldap_connection (enroll->conn); + return_unexpected_if_fail (ldap != NULL); + + vals_unicodePwd[0] = get_unicode_pwd (enroll->computer_password); + return_unexpected_if_fail (vals_unicodePwd[0] != NULL); + unicodePwd.mod_vals.modv_bvals = vals_unicodePwd; + + _adcli_info ("Trying to set %s password with LDAP", s_or_c (enroll)); + + ret = ldap_modify_ext_s (ldap, enroll->computer_dn, all_mods, NULL, NULL); + ber_bvfree (vals_unicodePwd[0]); + + if (ret == LDAP_INSUFFICIENT_ACCESS || ret == LDAP_OBJECT_CLASS_VIOLATION || + ret == LDAP_UNWILLING_TO_PERFORM || ret == LDAP_CONSTRAINT_VIOLATION) { + return _adcli_ldap_handle_failure (ldap, ADCLI_ERR_CREDENTIALS, + "Insufficient permissions to set password for: %s", + enroll->computer_dn); + + } else if (ret != LDAP_SUCCESS) { + return _adcli_ldap_handle_failure (ldap, ADCLI_ERR_DIRECTORY, + "Couldn't set password for: %s", + enroll->computer_dn); + } + + _adcli_info ("Set password for: %s", enroll->computer_dn); + return ADCLI_SUCCESS; +} + +static adcli_result set_password_with_user_creds (adcli_enroll *enroll) { krb5_error_code code; @@ -1390,6 +1512,8 @@ set_password_with_user_creds (adcli_enroll *enroll) memset (&result_string, 0, sizeof (result_string)); memset (&result_code_string, 0, sizeof (result_code_string)); + _adcli_info ("Trying to set %s password with Kerberos", s_or_c (enroll)); + code = krb5_set_password_using_ccache (k5, ccache, enroll->computer_password, enroll->computer_principal, &result_code, &result_code_string, &result_string); @@ -1452,6 +1576,8 @@ set_password_with_computer_creds (adcli_enroll *enroll) k5 = adcli_conn_get_krb5_context (enroll->conn); return_unexpected_if_fail (k5 != NULL); + _adcli_info ("Trying to change %s password with Kerberos", s_or_c (enroll)); + code = _adcli_kinit_computer_creds (enroll->conn, "kadmin/changepw", NULL, &creds); if (code != 0) { _adcli_err ("Couldn't get change password ticket for %s account: %s: %s", @@ -1506,8 +1632,12 @@ set_password_with_computer_creds (adcli_enroll *enroll) } static adcli_result -set_computer_password (adcli_enroll *enroll) +set_computer_password (adcli_enroll *enroll, int ldap_passwd) { + if (ldap_passwd) { + return set_password_with_ldap (enroll); + } + if (adcli_conn_get_login_type (enroll->conn) == ADCLI_LOGIN_COMPUTER_ACCOUNT) return set_password_with_computer_creds (enroll); else @@ -2434,7 +2564,7 @@ enroll_join_or_update_tasks (adcli_enroll *enroll, adcli_enroll_set_kvno (enroll, 0); } - res = set_computer_password (enroll); + res = set_computer_password (enroll, flags & ADCLI_ENROLL_LDAP_PASSWD); if (res != ADCLI_SUCCESS) return res; } @@ -2587,7 +2717,8 @@ adcli_enroll_join (adcli_enroll *enroll, return res; /* This is where it really happens */ - res = locate_or_create_computer_account (enroll, flags & ADCLI_ENROLL_ALLOW_OVERWRITE); + res = locate_or_create_computer_account (enroll, flags & ADCLI_ENROLL_ALLOW_OVERWRITE, + flags & ADCLI_ENROLL_LDAP_PASSWD); if (res != ADCLI_SUCCESS) return res; @@ -2769,8 +2900,7 @@ adcli_enroll_delete (adcli_enroll *enroll, } adcli_result -adcli_enroll_password (adcli_enroll *enroll, - adcli_enroll_flags password_flags) +adcli_enroll_password (adcli_enroll *enroll) { adcli_result res = ADCLI_SUCCESS; LDAP *ldap; @@ -2809,7 +2939,7 @@ adcli_enroll_password (adcli_enroll *enroll, } } - return set_computer_password (enroll); + return set_computer_password (enroll, 0); } adcli_enroll * diff --git a/library/adenroll.h b/library/adenroll.h index e3ada33..da2adc5 100644 --- a/library/adenroll.h +++ b/library/adenroll.h @@ -31,6 +31,7 @@ typedef enum { ADCLI_ENROLL_ALLOW_OVERWRITE = 1 << 2, ADCLI_ENROLL_PASSWORD_VALID = 1 << 3, ADCLI_ENROLL_ADD_SAMBA_DATA = 1 << 4, + ADCLI_ENROLL_LDAP_PASSWD = 1 << 5, } adcli_enroll_flags; typedef struct _adcli_enroll adcli_enroll; @@ -54,8 +55,7 @@ adcli_result adcli_enroll_show_computer_attribute (adcli_enroll *enroll); adcli_result adcli_enroll_delete (adcli_enroll *enroll, adcli_enroll_flags delete_flags); -adcli_result adcli_enroll_password (adcli_enroll *enroll, - adcli_enroll_flags password_flags); +adcli_result adcli_enroll_password (adcli_enroll *enroll); adcli_enroll * adcli_enroll_new (adcli_conn *conn); diff --git a/tools/computer.c b/tools/computer.c index dffeecb..58ade1f 100644 --- a/tools/computer.c +++ b/tools/computer.c @@ -118,6 +118,7 @@ typedef enum { opt_delattr, opt_use_ldaps, opt_account_disable, + opt_ldap_passwd, } Option; static adcli_tool_desc common_usages[] = { @@ -169,6 +170,7 @@ static adcli_tool_desc common_usages[] = { { 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_ldap_passwd, "Use LDAP add/mod operation to set/change password" }, { opt_verbose, "show verbose progress and failure messages", }, { 0 }, }; @@ -360,6 +362,7 @@ parse_option (Option opt, case opt_show_password: case opt_one_time_password: case opt_add_samba_data: + case opt_ldap_passwd: assert (0 && "not reached"); break; } @@ -426,6 +429,7 @@ adcli_tool_computer_join (adcli_conn *conn, { "show-password", no_argument, NULL, opt_show_password }, { "add-samba-data", no_argument, NULL, opt_add_samba_data }, { "samba-data-tool", required_argument, 0, opt_samba_data_tool }, + { "ldap-passwd", no_argument, NULL, opt_ldap_passwd }, { "verbose", no_argument, NULL, opt_verbose }, { "help", no_argument, NULL, 'h' }, { 0 }, @@ -457,6 +461,9 @@ adcli_tool_computer_join (adcli_conn *conn, case opt_add_samba_data: flags |= ADCLI_ENROLL_ADD_SAMBA_DATA; break; + case opt_ldap_passwd: + flags |= ADCLI_ENROLL_LDAP_PASSWD; + break; case 'h': case '?': case ':': @@ -554,6 +561,7 @@ adcli_tool_computer_update (adcli_conn *conn, { "show-password", no_argument, NULL, opt_show_password }, { "add-samba-data", no_argument, NULL, opt_add_samba_data }, { "samba-data-tool", required_argument, 0, opt_samba_data_tool }, + { "ldap-passwd", no_argument, NULL, opt_ldap_passwd }, { "verbose", no_argument, NULL, opt_verbose }, { "help", no_argument, NULL, 'h' }, { 0 }, @@ -581,6 +589,9 @@ adcli_tool_computer_update (adcli_conn *conn, case opt_add_samba_data: flags |= ADCLI_ENROLL_ADD_SAMBA_DATA; break; + case opt_ldap_passwd: + flags |= ADCLI_ENROLL_LDAP_PASSWD; + break; case 'h': case '?': case ':': @@ -916,7 +927,7 @@ adcli_tool_computer_reset (adcli_conn *conn, parse_fqdn_or_name (enroll, argv[0]); adcli_enroll_reset_computer_password (enroll); - res = adcli_enroll_password (enroll, 0); + res = adcli_enroll_password (enroll); if (res != ADCLI_SUCCESS) { warnx ("resetting %s in %s domain failed: %s", argv[0], adcli_conn_get_domain_name (conn), |