s3:libads: Fix creating machine account using LDAP
[samba.git] / source3 / libads / ldap.c
index 793e97efdacdba10df227f8a19a74474df074004..d7f5515a095d653daf62f7b6bbba8115bcaf54f2 100644 (file)
@@ -1366,19 +1366,42 @@ char *ads_parent_dn(const char *dn)
 {
        ADS_STATUS status;
        char *expr;
-       const char *attrs[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL};
+       const char *attrs[] = {
+               /* This is how Windows checks for machine accounts */
+               "objectClass",
+               "SamAccountName",
+               "userAccountControl",
+               "DnsHostName",
+               "ServicePrincipalName",
+               "unicodePwd",
+
+               /* Additional attributes Samba checks */
+               "msDS-SupportedEncryptionTypes",
+               "nTSecurityDescriptor",
+
+               NULL
+       };
+       TALLOC_CTX *frame = talloc_stackframe();
 
        *res = NULL;
 
        /* the easiest way to find a machine account anywhere in the tree
           is to look for hostname$ */
-       if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
-               DEBUG(1, ("asprintf failed!\n"));
-               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
+       if (expr == NULL) {
+               status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+               goto done;
        }
 
        status = ads_search(ads, res, expr, attrs);
-       SAFE_FREE(expr);
+       if (ADS_ERR_OK(status)) {
+               if (ads_count_replies(ads, *res) != 1) {
+                       status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
+               }
+       }
+
+done:
+       TALLOC_FREE(frame);
        return status;
 }
 
@@ -1493,7 +1516,6 @@ ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
                               name, (const void **) vals);
 }
 
-#if 0
 /**
  * Add a single ber-encoded value to a mod list
  * @param ctx An initialized TALLOC_CTX
@@ -1514,7 +1536,6 @@ static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
        return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
                               name, (const void **) values);
 }
-#endif
 
 static void ads_print_error(int ret, LDAP *ld)
 {
@@ -1863,11 +1884,11 @@ ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machin
        char *dn_string = NULL;
 
        ret = ads_find_machine_acct(ads, &res, machine_name);
-       if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
+       if (!ADS_ERR_OK(ret)) {
                DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
                DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
                ads_msgfree(ads, res);
-               return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
+               return ret;
        }
 
        DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
@@ -2023,12 +2044,12 @@ ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
        const char **servicePrincipalName = spns;
 
        ret = ads_find_machine_acct(ads, &res, machine_name);
-       if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
+       if (!ADS_ERR_OK(ret)) {
                DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
                        machine_name));
                DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
                ads_msgfree(ads, res);
-               return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
+               return ret;
        }
 
        DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
@@ -2088,42 +2109,49 @@ ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
 
 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
                                   const char *machine_name,
+                                  const char *machine_password,
                                   const char *org_unit,
-                                  uint32_t etype_list)
+                                  uint32_t etype_list,
+                                  const char *dns_domain_name)
 {
        ADS_STATUS ret;
-       char *samAccountName, *controlstr;
-       TALLOC_CTX *ctx;
+       char *samAccountName = NULL;
+       char *controlstr = NULL;
+       TALLOC_CTX *ctx = NULL;
        ADS_MODLIST mods;
        char *machine_escaped = NULL;
-       char *new_dn;
-       const char *objectClass[] = {"top", "person", "organizationalPerson",
-                                    "user", "computer", NULL};
+       char *dns_hostname = NULL;
+       char *new_dn = NULL;
+       char *utf8_pw = NULL;
+       size_t utf8_pw_len = 0;
+       char *utf16_pw = NULL;
+       size_t utf16_pw_len = 0;
+       struct berval machine_pw_val;
+       bool ok;
+       const char **spn_array = NULL;
+       size_t num_spns = 0;
+       const char *spn_prefix[] = {
+               "HOST",
+               "RestrictedKrbHost",
+       };
+       size_t i;
        LDAPMessage *res = NULL;
-       uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
-                               UF_DONT_EXPIRE_PASSWD |\
-                               UF_ACCOUNTDISABLE );
-       uint32_t func_level = 0;
-
-       ret = ads_domain_func_level(ads, &func_level);
-       if (!ADS_ERR_OK(ret)) {
-               return ret;
-       }
+       uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
 
-       if (!(ctx = talloc_init("ads_add_machine_acct")))
+       ctx = talloc_init("ads_add_machine_acct");
+       if (ctx == NULL) {
                return ADS_ERROR(LDAP_NO_MEMORY);
-
-       ret = ADS_ERROR(LDAP_NO_MEMORY);
+       }
 
        machine_escaped = escape_rdn_val_string_alloc(machine_name);
-       if (!machine_escaped) {
+       if (machine_escaped == NULL) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
                goto done;
        }
 
+       /* Check if the machine account already exists. */
        ret = ads_find_machine_acct(ads, &res, machine_escaped);
-       if (ADS_ERR_OK(ret) && ads_count_replies(ads, res) == 1) {
-               DBG_DEBUG("Host account for %s already exists.\n",
-                               machine_escaped);
+       if (ADS_ERR_OK(ret)) {
                ret = ADS_ERROR_LDAP(LDAP_ALREADY_EXISTS);
                ads_msgfree(ads, res);
                goto done;
@@ -2131,36 +2159,117 @@ ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
        ads_msgfree(ads, res);
 
        new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
-       samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
-
-       if ( !new_dn || !samAccountName ) {
+       if (new_dn == NULL) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
                goto done;
        }
 
-       if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
+       /* Create machine account */
+
+       samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
+       if (samAccountName == NULL) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
                goto done;
        }
 
-       if (!(mods = ads_init_mods(ctx))) {
+       dns_hostname = talloc_asprintf(ctx,
+                                      "%s.%s",
+                                      machine_name,
+                                      dns_domain_name);
+       if (dns_hostname == NULL) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
                goto done;
        }
 
-       ads_mod_str(ctx, &mods, "cn", machine_name);
-       ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
-       ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
-       ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
+       /* Add dns_hostname SPNs */
+       for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
+               char *spn = talloc_asprintf(ctx,
+                                           "%s/%s",
+                                           spn_prefix[i],
+                                           dns_hostname);
+               if (spn == NULL) {
+                       ret = ADS_ERROR(LDAP_NO_MEMORY);
+                       goto done;
+               }
+
+               ok = add_string_to_array(spn_array,
+                                        spn,
+                                        &spn_array,
+                                        &num_spns);
+               if (!ok) {
+                       ret = ADS_ERROR(LDAP_NO_MEMORY);
+                       goto done;
+               }
+       }
 
-       if (func_level >= DS_DOMAIN_FUNCTION_2008) {
-               const char *etype_list_str;
+       /* Add machine_name SPNs */
+       for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
+               char *spn = talloc_asprintf(ctx,
+                                           "%s/%s",
+                                           spn_prefix[i],
+                                           machine_name);
+               if (spn == NULL) {
+                       ret = ADS_ERROR(LDAP_NO_MEMORY);
+                       goto done;
+               }
 
-               etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
-               if (etype_list_str == NULL) {
+               ok = add_string_to_array(spn_array,
+                                        spn,
+                                        &spn_array,
+                                        &num_spns);
+               if (!ok) {
+                       ret = ADS_ERROR(LDAP_NO_MEMORY);
                        goto done;
                }
-               ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
-                           etype_list_str);
        }
 
+       /* Make sure to NULL terminate the array */
+       spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
+       if (spn_array == NULL) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+       spn_array[num_spns] = NULL;
+
+       controlstr = talloc_asprintf(ctx, "%u", acct_control);
+       if (controlstr == NULL) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
+               goto done;
+       }
+
+       utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
+       if (utf8_pw == NULL) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
+               goto done;
+       }
+       utf8_pw_len = strlen(utf8_pw);
+
+       ok = convert_string_talloc(ctx,
+                                  CH_UTF8, CH_UTF16MUNGED,
+                                  utf8_pw, utf8_pw_len,
+                                  (void *)&utf16_pw, &utf16_pw_len);
+       if (!ok) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
+               goto done;
+       }
+
+       machine_pw_val = (struct berval) {
+               .bv_val = utf16_pw,
+               .bv_len = utf16_pw_len,
+       };
+
+       mods = ads_init_mods(ctx);
+       if (mods == NULL) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
+               goto done;
+       }
+
+       ads_mod_str(ctx, &mods, "objectClass", "Computer");
+       ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
+       ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
+       ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
+       ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
+       ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
+
        ret = ads_gen_add(ads, new_dn, mods);
 
 done:
@@ -3672,14 +3781,15 @@ ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
        TALLOC_FREE(hostnameDN);
 
        status = ads_find_machine_acct(ads, &res, host);
-       if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
+       if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
+           (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
                DEBUG(3, ("Failed to remove host account.\n"));
                SAFE_FREE(host);
                return status;
        }
 
        SAFE_FREE(host);
-       return status;
+       return ADS_SUCCESS;
 }
 
 /**