summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2011-08-11 15:47:01 +1000
committerAndrew Bartlett <abartlet@samba.org>2011-08-13 12:30:49 +1000
commit00364e4e0d3d091e1b8cc9dd2b1b30eb84f2f085 (patch)
tree41e2dded174f9bccdc112814e3b9859a4aa0d348
parent2993113a56032be89272a626a7ef4c436d73080a (diff)
downloadsamba-00364e4e0d3d091e1b8cc9dd2b1b30eb84f2f085.tar.gz
samba-00364e4e0d3d091e1b8cc9dd2b1b30eb84f2f085.tar.bz2
samba-00364e4e0d3d091e1b8cc9dd2b1b30eb84f2f085.zip
s3-passdb Add support for pdb_add_sam_account() and password hashes to pdb_samba4
This will help when using this as part of the Samba3 passdb -> Samba4 ldb database upgrade script. Andrew Bartlett
-rw-r--r--source3/passdb/pdb_samba4.c291
1 files changed, 222 insertions, 69 deletions
diff --git a/source3/passdb/pdb_samba4.c b/source3/passdb/pdb_samba4.c
index aa3352a658..35316d71d4 100644
--- a/source3/passdb/pdb_samba4.c
+++ b/source3/passdb/pdb_samba4.c
@@ -328,115 +328,224 @@ static bool pdb_samba4_add_time(struct ldb_message *msg,
return ldb_msg_add_fmt(msg, attrib, "%llu", (unsigned long long) nt_time);
}
-/* Like in pdb_ldap(), this will need to be a function pointer when we
- * start to support 'adds' for migrations from samba3 passdb backends
- * to samba4 */
-static bool update_required(struct samu *sam, enum pdb_elements element)
-{
- return (IS_SAM_CHANGED(sam, element));
-}
-
-static bool pdb_samba4_init_samba4_from_sam(struct pdb_samba4_state *state,
- struct ldb_message *existing,
- TALLOC_CTX *mem_ctx,
- struct ldb_message **pmods,
- struct samu *sam)
+static int pdb_samba4_replace_by_sam(struct pdb_samba4_state *state,
+ bool (*need_update)(const struct samu *,
+ enum pdb_elements),
+ struct ldb_dn *dn,
+ struct samu *sam)
{
int ret = LDB_SUCCESS;
const char *pw;
struct ldb_message *msg;
-
+ struct ldb_request *req;
+ unsigned int j;
/* TODO: All fields :-) */
- msg = ldb_msg_new(mem_ctx);
+ msg = ldb_msg_new(talloc_tos());
if (!msg) {
return false;
}
- msg->dn = existing->dn;
+ msg->dn = dn;
+
+ /* build modify request */
+ ret = ldb_build_mod_req(&req, state->ldb, talloc_tos(), msg, NULL, NULL,
+ NULL, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return ret;
+ }
pw = pdb_get_plaintext_passwd(sam);
- if (update_required(sam, PDB_PLAINTEXT_PW)) {
+ if (need_update(sam, PDB_PLAINTEXT_PW)) {
if (pw == NULL) {
- ret = LDB_ERR_OPERATIONS_ERROR;
- goto fail;
+ return LDB_ERR_OPERATIONS_ERROR;
}
ret |= ldb_msg_add_string(msg, "clearTextPassword", pw);
+ } else {
+ bool changed_lm_pw = false;
+ bool changed_nt_pw = false;
+ bool changed_history = false;
+ if (need_update(sam, PDB_LMPASSWD)) {
+ struct ldb_val val;
+ val.data = pdb_get_lanman_passwd(sam);
+ if (!val.data) {
+ samdb_msg_add_delete(state->ldb, msg, msg,
+ "dBCSPwd");
+ } else {
+ val.length = LM_HASH_LEN;
+ ret |= ldb_msg_add_value(msg, "dBCSPwd", &val, NULL);
+ }
+ changed_lm_pw = true;
+ }
+ if (need_update(sam, PDB_NTPASSWD)) {
+ struct ldb_val val;
+ val.data = pdb_get_lanman_passwd(sam);
+ if (!val.data) {
+ samdb_msg_add_delete(state->ldb, msg, msg,
+ "unicodePwd");
+ } else {
+ val.length = NT_HASH_LEN;
+ ret |= ldb_msg_add_value(msg, "unicodePwd", &val, NULL);
+ }
+ changed_nt_pw = true;
+ }
+
+ /* Try to ensure we don't get out of sync */
+ if (changed_lm_pw && !changed_nt_pw) {
+ samdb_msg_add_delete(state->ldb, msg, msg,
+ "unicodePwd");
+ } else if (changed_nt_pw && !changed_lm_pw) {
+ samdb_msg_add_delete(state->ldb, msg, msg,
+ "dBCSPwd");
+ }
+ if (changed_lm_pw || changed_nt_pw) {
+ samdb_msg_add_delete(state->ldb, msg, msg,
+ "supplementalCredentials");
+
+ }
+
+ /* If we set a plaintext password, the system will
+ * force the pwdLastSet to now(), and it isn't worth
+ * working around this for the real world use cases of
+ * pdb_samba4 */
+ if (need_update(sam, PDB_PASSLASTSET)) {
+ ret |= pdb_samba4_add_time(msg, "pwdLastSet",
+ pdb_get_pass_last_set_time(sam));
+ }
+
+ if (need_update(sam, PDB_PWHISTORY)) {
+ uint32_t current_hist_len;
+ const uint8_t *history = pdb_get_pw_history(sam, &current_hist_len);
+
+ bool invalid_history = false;
+ struct samr_Password *history_hashes = talloc_array(talloc_tos(), struct samr_Password,
+ current_hist_len);
+ if (!history) {
+ invalid_history = true;
+ } else {
+ unsigned int i;
+ static const uint8_t zeros[16];
+ /* Parse the history into the correct format */
+ for (i = 0; i < current_hist_len; i++) {
+ if (memcmp(&history[i*PW_HISTORY_ENTRY_LEN], zeros, 16) != 0) {
+ /* If the history is in the old format, with a salted hash, then we can't migrate it to AD format */
+ invalid_history = true;
+ break;
+ }
+ /* Copy out the 2nd 16 bytes of the 32 byte password history, containing the NT hash */
+ memcpy(history_hashes[i].hash,
+ &history[(i*PW_HISTORY_ENTRY_LEN) + PW_HISTORY_SALT_LEN],
+ sizeof(history_hashes[i].hash));
+ }
+ }
+ if (invalid_history) {
+ ret |= samdb_msg_add_delete(state->ldb, msg, msg,
+ "ntPwdHistory");
+
+ ret |= samdb_msg_add_delete(state->ldb, msg, msg,
+ "lmPwdHistory");
+ } else {
+ ret |= samdb_msg_add_hashes(state->ldb, msg, msg,
+ "ntPwdHistory",
+ history_hashes,
+ current_hist_len);
+ }
+ changed_history = true;
+ }
+ if (changed_lm_pw || changed_nt_pw || changed_history) {
+ ret |= ldb_request_add_control(req,
+ DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID,
+ true, NULL);
+ }
}
- if (update_required(sam, PDB_FULLNAME)) {
+ /* PDB_USERSID is only allowed on ADD, handled in caller */
+ if (need_update(sam, PDB_GROUPSID)) {
+ const struct dom_sid *sid = pdb_get_group_sid(sam);
+ uint32_t rid;
+ NTSTATUS status = dom_sid_split_rid(NULL, sid, NULL, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (!dom_sid_in_domain(samdb_domain_sid(state->ldb), sid)) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ ret |= samdb_msg_add_uint(state->ldb, msg, msg, "primaryGroupID", rid);
+ }
+ if (need_update(sam, PDB_FULLNAME)) {
ret |= ldb_msg_add_string(msg, "displayName", pdb_get_fullname(sam));
}
- if (update_required(sam, PDB_SMBHOME)) {
+ if (need_update(sam, PDB_SMBHOME)) {
ret |= ldb_msg_add_string(msg, "homeDirectory",
pdb_get_homedir(sam));
}
- if (update_required(sam, PDB_PROFILE)) {
+ if (need_update(sam, PDB_PROFILE)) {
ret |= ldb_msg_add_string(msg, "profilePath",
pdb_get_profile_path(sam));
}
- if (update_required(sam, PDB_DRIVE)) {
+ if (need_update(sam, PDB_DRIVE)) {
ret |= ldb_msg_add_string(msg, "homeDrive",
pdb_get_dir_drive(sam));
}
- if (update_required(sam, PDB_LOGONSCRIPT)) {
+ if (need_update(sam, PDB_LOGONSCRIPT)) {
ret |= ldb_msg_add_string(msg, "scriptPath",
pdb_get_logon_script(sam));
}
- if (update_required(sam, PDB_KICKOFFTIME)) {
+ if (need_update(sam, PDB_KICKOFFTIME)) {
ret |= pdb_samba4_add_time(msg, "accountExpires",
pdb_get_kickoff_time(sam));
}
- if (update_required(sam, PDB_USERNAME)) {
+ if (need_update(sam, PDB_USERNAME)) {
ret |= ldb_msg_add_string(msg, "samAccountName",
pdb_get_username(sam));
}
- if (update_required(sam, PDB_HOURSLEN) || update_required(sam, PDB_HOURS)) {
+ if (need_update(sam, PDB_HOURSLEN) || need_update(sam, PDB_HOURS)) {
struct ldb_val hours = data_blob_const(pdb_get_hours(sam), pdb_get_hours_len(sam));
ret |= ldb_msg_add_value(msg, "logonHours",
&hours, NULL);
}
- if (update_required(sam, PDB_ACCTCTRL)) {
- ret |= ldb_msg_add_fmt(msg, "userAccountControl",
- "%d", ds_acb2uf(pdb_get_acct_ctrl(sam)));
+ if (need_update(sam, PDB_ACCTCTRL)) {
+ ret |= samdb_msg_add_acct_flags(state->ldb, msg, msg,
+ "userAccountControl", pdb_get_acct_ctrl(sam));
}
- if (update_required(sam, PDB_COMMENT)) {
+ if (need_update(sam, PDB_COMMENT)) {
ret |= ldb_msg_add_string(msg, "comment",
pdb_get_comment(sam));
}
- if (update_required(sam, PDB_ACCTDESC)) {
+ if (need_update(sam, PDB_ACCTDESC)) {
ret |= ldb_msg_add_string(msg, "description",
pdb_get_acct_desc(sam));
}
- if (update_required(sam, PDB_WORKSTATIONS)) {
+ if (need_update(sam, PDB_WORKSTATIONS)) {
ret |= ldb_msg_add_string(msg, "userWorkstations",
pdb_get_workstations(sam));
}
/* This will need work, it is actually a UTF8 'string' with internal NULLs, to handle TS parameters */
- if (update_required(sam, PDB_MUNGEDDIAL)) {
+ if (need_update(sam, PDB_MUNGEDDIAL)) {
ret |= ldb_msg_add_string(msg, "userParameters",
pdb_get_munged_dial(sam));
}
- if (update_required(sam, PDB_COUNTRY_CODE)) {
+ if (need_update(sam, PDB_COUNTRY_CODE)) {
ret |= ldb_msg_add_fmt(msg, "countryCode",
"%i", (int)pdb_get_country_code(sam));
}
- if (update_required(sam, PDB_CODE_PAGE)) {
+ if (need_update(sam, PDB_CODE_PAGE)) {
ret |= ldb_msg_add_fmt(msg, "codePage",
"%i", (int)pdb_get_code_page(sam));
}
@@ -445,28 +554,46 @@ static bool pdb_samba4_init_samba4_from_sam(struct pdb_samba4_state *state,
PDB_LOGONTIME,
PDB_LOGOFFTIME,
PDB_BAD_PASSWORD_TIME,
- PDB_CANCHANGETIME,
- PDB_MUSTCHANGETIME,
+ PDB_CANCHANGETIME, - these are calculated per policy, not stored
+ PDB_MUSTCHANGETIME, - these are calculated per policy, not stored
PDB_DOMAIN,
- PDB_NTUSERNAME,
+ PDB_NTUSERNAME, - this makes no sense, and never really did
PDB_LOGONDIVS,
- PDB_USERSID,
- PDB_GROUPSID,
- PDB_PASSLASTSET,
+ PDB_USERSID, - Handled in pdb_samba4_add_sam_account()
PDB_FIELDS_PRESENT,
PDB_BAD_PASSWORD_COUNT,
PDB_LOGON_COUNT,
PDB_UNKNOWN6,
- PDB_LMPASSWD,
- PDB_NTPASSWD,
PDB_PWHISTORY,
PDB_BACKEND_PRIVATE_DATA,
*/
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
- *pmods = msg;
-fail:
- return ret == LDB_SUCCESS;
+ if (msg->num_elements == 0) {
+ /* Nothing to do, just return success */
+ return LDB_SUCCESS;
+ }
+
+ /* mark everything here as a replace */
+ for (j=0;j<msg->num_elements;j++) {
+ msg->elements[j].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_request(state->ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to modify account record %s to set user attributes: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(state->ldb)));
+ }
+
+ return ret;
}
static NTSTATUS pdb_samba4_getsamupriv(struct pdb_samba4_state *state,
@@ -626,7 +753,50 @@ static NTSTATUS pdb_samba4_delete_user(struct pdb_methods *m,
static NTSTATUS pdb_samba4_add_sam_account(struct pdb_methods *m,
struct samu *sampass)
{
- return NT_STATUS_NOT_IMPLEMENTED;
+ int ret;
+ NTSTATUS status;
+ struct ldb_dn *dn;
+ struct pdb_samba4_state *state = talloc_get_type_abort(
+ m->private_data, struct pdb_samba4_state);
+ uint32_t acb_flags = pdb_get_acct_ctrl(sampass);
+ const char *username = pdb_get_username(sampass);
+ const struct dom_sid *user_sid = pdb_get_user_sid(sampass);
+ TALLOC_CTX *tframe = talloc_stackframe();
+
+ acb_flags &= (ACB_NORMAL|ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST);
+
+ ret = ldb_transaction_start(state->ldb);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tframe);
+ return NT_STATUS_LOCK_NOT_GRANTED;
+ }
+
+ status = dsdb_add_user(state->ldb, talloc_tos(), username,
+ acb_flags, user_sid, NULL, &dn);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(state->ldb);
+ talloc_free(tframe);
+ return status;
+ }
+
+ ret = pdb_samba4_replace_by_sam(state, pdb_element_is_set_or_changed,
+ dn, sampass);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(state->ldb);
+ talloc_free(tframe);
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ ret = ldb_transaction_commit(state->ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to commit transaction to add and modify account record %s: %s\n",
+ ldb_dn_get_linearized(dn),
+ ldb_errstring(state->ldb)));
+ talloc_free(tframe);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ talloc_free(tframe);
+ return NT_STATUS_OK;
}
/*
@@ -642,28 +812,11 @@ static NTSTATUS pdb_samba4_update_sam_account(struct pdb_methods *m,
m->private_data, struct pdb_samba4_state);
struct ldb_message *msg = pdb_samba4_get_samu_private(
m, sam);
- struct ldb_message *replace_msg;
- int rc;
-
- if (!pdb_samba4_init_samba4_from_sam(state, msg, talloc_tos(),
- &replace_msg, sam)) {
- return NT_STATUS_NO_MEMORY;
- }
-
- if (replace_msg->num_elements == 0) {
- /* Nothing to do, just return success */
- return NT_STATUS_OK;
- }
-
- rc = dsdb_replace(state->ldb, replace_msg, 0);
- TALLOC_FREE(replace_msg);
- if (rc != LDB_SUCCESS) {
- DEBUG(10, ("dsdb_replace for %s failed: %s\n", ldb_dn_get_linearized(replace_msg->dn),
- ldb_errstring(state->ldb)));
- return NT_STATUS_LDAP(rc);
- }
+ int ret;
- return NT_STATUS_OK;
+ ret = pdb_samba4_replace_by_sam(state, pdb_element_is_changed, msg->dn,
+ sam);
+ return dsdb_ldb_err_to_ntstatus(ret);
}
static NTSTATUS pdb_samba4_delete_sam_account(struct pdb_methods *m,