/* lsa_LookupSids3 Identical to LookupSids2, but doesn't take a policy handle */ NTSTATUS dcesrv_lsa_LookupSids3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct lsa_LookupSids3 *r) { struct lsa_LookupSids2 r2; struct lsa_OpenPolicy2 pol; NTSTATUS status; struct dcesrv_handle *h; ZERO_STRUCT(r2); /* No policy handle on the wire, so make one up here */ r2.in.handle = talloc(mem_ctx, struct policy_handle); if (!r2.in.handle) { return NT_STATUS_NO_MEMORY; } pol.out.handle = r2.in.handle; pol.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; pol.in.attr = NULL; pol.in.system_name = NULL; status = dcesrv_lsa_OpenPolicy2(dce_call, mem_ctx, &pol); if (!NT_STATUS_IS_OK(status)) { return status; } /* ensure this handle goes away at the end of this call */ DCESRV_PULL_HANDLE(h, r2.in.handle, LSA_HANDLE_POLICY); talloc_steal(mem_ctx, h); r2.in.sids = r->in.sids; r2.in.names = r->in.names; r2.in.level = r->in.level; r2.in.count = r->in.count; r2.in.lookup_options = r->in.lookup_options; r2.in.client_revision = r->in.client_revision; r2.out.count = r->out.count; r2.out.names = r->out.names; r2.out.domains = r->out.domains; status = dcesrv_lsa_LookupSids2(dce_call, mem_ctx, &r2); r->out.domains = r2.out.domains; r->out.names = r2.out.names; r->out.count = r2.out.count; return status; }
/* lsa_LookupNames3 */ NTSTATUS dcesrv_lsa_LookupNames3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct lsa_LookupNames3 *r) { enum dcerpc_transport_t transport = dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description); struct lsa_policy_state *policy_state; struct dcesrv_handle *policy_handle; if (transport != NCACN_NP && transport != NCALRPC) { DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED); } DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY); policy_state = policy_handle->data; return dcesrv_lsa_LookupNames_common(dce_call, mem_ctx, policy_state, r); }
/* lsa_LookupNames2 */ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct lsa_LookupNames2 *r) { struct lsa_policy_state *state; struct dcesrv_handle *h; int i; struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx; struct lsa_RefDomainList *domains; *r->out.domains = NULL; DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY); if (r->in.level < LSA_LOOKUP_NAMES_ALL || r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) { return NT_STATUS_INVALID_PARAMETER; } state = h->data; domains = talloc_zero(mem_ctx, struct lsa_RefDomainList); if (domains == NULL) { return NT_STATUS_NO_MEMORY; } *r->out.domains = domains; r->out.sids = talloc_zero(mem_ctx, struct lsa_TransSidArray2); if (r->out.sids == NULL) { return NT_STATUS_NO_MEMORY; } *r->out.count = 0; r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid2, r->in.num_names); if (r->out.sids->sids == NULL) { return NT_STATUS_NO_MEMORY; } for (i=0; i<r->in.num_names; i++) { const char *name = r->in.names[i].string; const char *authority_name; struct dom_sid *sid; uint32_t rtype, sid_index, rid=0; NTSTATUS status2; r->out.sids->count++; r->out.sids->sids[i].sid_type = SID_NAME_UNKNOWN; /* MS-LSAT 3.1.4.7 - rid zero is considered equivalent to sid NULL - so we should return 0 rid for unmapped entries */ r->out.sids->sids[i].rid = 0; r->out.sids->sids[i].sid_index = 0xFFFFFFFF; r->out.sids->sids[i].unknown = 0; status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx, state, mem_ctx, name, &authority_name, &sid, &rtype, &rid); if (!NT_STATUS_IS_OK(status2)) { continue; } status2 = dcesrv_lsa_authority_list(state, mem_ctx, rtype, authority_name, sid, domains, &sid_index); if (!NT_STATUS_IS_OK(status2)) { continue; } r->out.sids->sids[i].sid_type = rtype; r->out.sids->sids[i].rid = rid; r->out.sids->sids[i].sid_index = sid_index; r->out.sids->sids[i].unknown = 0; (*r->out.count)++; } if (*r->out.count == 0) { return NT_STATUS_NONE_MAPPED; } if (*r->out.count != r->in.num_names) { return STATUS_SOME_UNMAPPED; } return NT_STATUS_OK; }
/* lsa_LookupNames3 */ NTSTATUS dcesrv_lsa_LookupNames3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct lsa_LookupNames3 *r) { struct lsa_policy_state *policy_state; struct dcesrv_handle *policy_handle; int i; struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx; struct lsa_RefDomainList *domains; DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY); if (r->in.level < LSA_LOOKUP_NAMES_ALL || r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) { return NT_STATUS_INVALID_PARAMETER; } policy_state = policy_handle->data; *r->out.domains = NULL; domains = talloc_zero(mem_ctx, struct lsa_RefDomainList); if (domains == NULL) { return NT_STATUS_NO_MEMORY; } *r->out.domains = domains; r->out.sids = talloc_zero(mem_ctx, struct lsa_TransSidArray3); if (r->out.sids == NULL) { return NT_STATUS_NO_MEMORY; } *r->out.count = 0; r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid3, r->in.num_names); if (r->out.sids->sids == NULL) { return NT_STATUS_NO_MEMORY; } for (i=0; i<r->in.num_names; i++) { const char *name = r->in.names[i].string; const char *authority_name; struct dom_sid *sid; uint32_t sid_index, rid; enum lsa_SidType rtype; NTSTATUS status2; r->out.sids->count++; r->out.sids->sids[i].sid_type = SID_NAME_UNKNOWN; r->out.sids->sids[i].sid = NULL; r->out.sids->sids[i].sid_index = 0xFFFFFFFF; r->out.sids->sids[i].flags = 0; status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx, policy_state, mem_ctx, name, &authority_name, &sid, &rtype, &rid); if (!NT_STATUS_IS_OK(status2) || sid->num_auths == 0) { continue; } status2 = dcesrv_lsa_authority_list(policy_state, mem_ctx, rtype, authority_name, sid, domains, &sid_index); if (!NT_STATUS_IS_OK(status2)) { continue; } r->out.sids->sids[i].sid_type = rtype; r->out.sids->sids[i].sid = sid; r->out.sids->sids[i].sid_index = sid_index; r->out.sids->sids[i].flags = 0; (*r->out.count)++; } if (*r->out.count == 0) { return NT_STATUS_NONE_MAPPED; } if (*r->out.count != r->in.num_names) { return STATUS_SOME_UNMAPPED; } return NT_STATUS_OK; }
/* samr_ChangePasswordUser */ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct samr_ChangePasswordUser *r) { struct dcesrv_handle *h; struct samr_account_state *a_state; struct ldb_context *sam_ctx; struct ldb_message **res; int ret; struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash; struct samr_Password *lm_pwd, *nt_pwd; NTSTATUS status = NT_STATUS_OK; const char * const attrs[] = { "dBCSPwd", "unicodePwd" , NULL }; DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER); a_state = h->data; /* basic sanity checking on parameters. Do this before any database ops */ if (!r->in.lm_present || !r->in.nt_present || !r->in.old_lm_crypted || !r->in.new_lm_crypted || !r->in.old_nt_crypted || !r->in.new_nt_crypted) { /* we should really handle a change with lm not present */ return NT_STATUS_INVALID_PARAMETER_MIX; } /* Connect to a SAMDB with system privileges for fetching the old pw * hashes. */ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(dce_call->conn->dce_ctx->lp_ctx), 0); if (sam_ctx == NULL) { return NT_STATUS_INVALID_SYSTEM_SERVICE; } /* fetch the old hashes */ ret = gendb_search_dn(sam_ctx, mem_ctx, a_state->account_dn, &res, attrs); if (ret != 1) { return NT_STATUS_WRONG_PASSWORD; } status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, res[0], &lm_pwd, &nt_pwd); if (!NT_STATUS_IS_OK(status) || !nt_pwd) { return NT_STATUS_WRONG_PASSWORD; } /* decrypt and check the new lm hash */ if (lm_pwd) { D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash); D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash); if (memcmp(checkHash.hash, lm_pwd, 16) != 0) { return NT_STATUS_WRONG_PASSWORD; } } /* decrypt and check the new nt hash */ D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash); D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash); if (memcmp(checkHash.hash, nt_pwd, 16) != 0) { return NT_STATUS_WRONG_PASSWORD; } /* The NT Cross is not required by Win2k3 R2, but if present check the nt cross hash */ if (r->in.cross1_present && r->in.nt_cross && lm_pwd) { D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash); if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) { return NT_STATUS_WRONG_PASSWORD; } } /* The LM Cross is not required by Win2k3 R2, but if present check the lm cross hash */ if (r->in.cross2_present && r->in.lm_cross && lm_pwd) { D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash); if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) { return NT_STATUS_WRONG_PASSWORD; } } /* Start a SAM with user privileges for the password change */ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info, 0); if (sam_ctx == NULL) { return NT_STATUS_INVALID_SYSTEM_SERVICE; } /* Start transaction */ ret = ldb_transaction_start(sam_ctx); if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx))); return NT_STATUS_TRANSACTION_ABORTED; } /* Performs the password modification. We pass the old hashes read out * from the database since they were already checked against the user- * provided ones. */ status = samdb_set_password(sam_ctx, mem_ctx, a_state->account_dn, a_state->domain_state->domain_dn, NULL, &new_lmPwdHash, &new_ntPwdHash, lm_pwd, nt_pwd, /* this is a user password change */ NULL, NULL); if (!NT_STATUS_IS_OK(status)) { ldb_transaction_cancel(sam_ctx); return status; } /* And this confirms it in a transaction commit */ ret = ldb_transaction_commit(sam_ctx); if (ret != LDB_SUCCESS) { DEBUG(1,("Failed to commit transaction to change password on %s: %s\n", ldb_dn_get_linearized(a_state->account_dn), ldb_errstring(sam_ctx))); return NT_STATUS_TRANSACTION_ABORTED; } return NT_STATUS_OK; }
/* samr_ChangePasswordUser */ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct samr_ChangePasswordUser *r) { struct dcesrv_handle *h; struct samr_account_state *a_state; struct ldb_context *sam_ctx; struct ldb_message **res, *msg; int ret; struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash; struct samr_Password *lm_pwd, *nt_pwd; NTSTATUS status = NT_STATUS_OK; const char * const attrs[] = { "dBCSPwd", "unicodePwd" , NULL }; DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER); a_state = h->data; /* basic sanity checking on parameters. Do this before any database ops */ if (!r->in.lm_present || !r->in.nt_present || !r->in.old_lm_crypted || !r->in.new_lm_crypted || !r->in.old_nt_crypted || !r->in.new_nt_crypted) { /* we should really handle a change with lm not present */ return NT_STATUS_INVALID_PARAMETER_MIX; } /* To change a password we need to open as system */ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx)); if (sam_ctx == NULL) { return NT_STATUS_INVALID_SYSTEM_SERVICE; } ret = ldb_transaction_start(sam_ctx); if (ret) { DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx))); return NT_STATUS_TRANSACTION_ABORTED; } /* fetch the old hashes */ ret = gendb_search_dn(sam_ctx, mem_ctx, a_state->account_dn, &res, attrs); if (ret != 1) { ldb_transaction_cancel(sam_ctx); return NT_STATUS_WRONG_PASSWORD; } msg = res[0]; status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, msg, &lm_pwd, &nt_pwd); if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) { ldb_transaction_cancel(sam_ctx); return NT_STATUS_WRONG_PASSWORD; } /* decrypt and check the new lm hash */ D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash); D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash); if (memcmp(checkHash.hash, lm_pwd, 16) != 0) { ldb_transaction_cancel(sam_ctx); return NT_STATUS_WRONG_PASSWORD; } /* decrypt and check the new nt hash */ D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash); D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash); if (memcmp(checkHash.hash, nt_pwd, 16) != 0) { ldb_transaction_cancel(sam_ctx); return NT_STATUS_WRONG_PASSWORD; } /* The NT Cross is not required by Win2k3 R2, but if present check the nt cross hash */ if (r->in.cross1_present && r->in.nt_cross) { D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash); if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) { ldb_transaction_cancel(sam_ctx); return NT_STATUS_WRONG_PASSWORD; } } /* The LM Cross is not required by Win2k3 R2, but if present check the lm cross hash */ if (r->in.cross2_present && r->in.lm_cross) { D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash); if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) { ldb_transaction_cancel(sam_ctx); return NT_STATUS_WRONG_PASSWORD; } } msg = ldb_msg_new(mem_ctx); if (msg == NULL) { ldb_transaction_cancel(sam_ctx); return NT_STATUS_NO_MEMORY; } msg->dn = ldb_dn_copy(msg, a_state->account_dn); if (!msg->dn) { ldb_transaction_cancel(sam_ctx); return NT_STATUS_NO_MEMORY; } /* setup password modify mods on the user DN specified. This may fail * due to password policies. */ status = samdb_set_password(sam_ctx, mem_ctx, a_state->account_dn, a_state->domain_state->domain_dn, msg, NULL, &new_lmPwdHash, &new_ntPwdHash, true, /* this is a user password change */ NULL, NULL); if (!NT_STATUS_IS_OK(status)) { ldb_transaction_cancel(sam_ctx); return status; } /* The above call only setup the modifications, this actually * makes the write to the database. */ ret = samdb_replace(sam_ctx, mem_ctx, msg); if (ret != 0) { DEBUG(2,("Failed to modify record to change password on %s: %s\n", ldb_dn_get_linearized(a_state->account_dn), ldb_errstring(sam_ctx))); ldb_transaction_cancel(sam_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } /* And this confirms it in a transaction commit */ ret = ldb_transaction_commit(sam_ctx); if (ret != 0) { DEBUG(1,("Failed to commit transaction to change password on %s: %s\n", ldb_dn_get_linearized(a_state->account_dn), ldb_errstring(sam_ctx))); return NT_STATUS_TRANSACTION_ABORTED; } return NT_STATUS_OK; }