BOOL check_lanman_password(char *user, uchar *pass1, uchar *pass2, struct smb_passwd **psmbpw) { static uchar null_pw[16]; uchar unenc_new_pw[16]; uchar unenc_old_pw[16]; struct smb_passwd *smbpw; *psmbpw = NULL; become_root(0); smbpw = getsmbpwnam(user); unbecome_root(0); if (smbpw == NULL) { DEBUG(0,("check_lanman_password: getsmbpwnam returned NULL\n")); return False; } if (smbpw->acct_ctrl & ACB_DISABLED) { DEBUG(0,("check_lanman_password: account %s disabled.\n", user)); return False; } if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ)) { uchar no_pw[14]; memset(no_pw, '\0', 14); E_P16(no_pw, null_pw); smbpw->smb_passwd = null_pw; } else if (smbpw->smb_passwd == NULL) { DEBUG(0,("check_lanman_password: no lanman password !\n")); return False; } /* Get the new lanman hash. */ D_P16(smbpw->smb_passwd, pass2, unenc_new_pw); /* Use this to get the old lanman hash. */ D_P16(unenc_new_pw, pass1, unenc_old_pw); /* Check that the two old passwords match. */ if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16)) { DEBUG(0,("check_lanman_password: old password doesn't match.\n")); return False; } *psmbpw = smbpw; return True; }
BOOL change_lanman_password(struct smb_passwd *smbpw, uchar *pass1, uchar *pass2) { static uchar null_pw[16]; uchar unenc_new_pw[16]; BOOL ret; if (smbpw == NULL) { DEBUG(0,("change_lanman_password: no smb password entry.\n")); return False; } if (smbpw->acct_ctrl & ACB_DISABLED) { DEBUG(0,("change_lanman_password: account %s disabled.\n", smbpw->smb_name)); return False; } if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ)) { uchar no_pw[14]; memset(no_pw, '\0', 14); E_P16(no_pw, null_pw); smbpw->smb_passwd = null_pw; } else if (smbpw->smb_passwd == NULL) { DEBUG(0,("change_lanman_password: no lanman password !\n")); return False; } /* Get the new lanman hash. */ D_P16(smbpw->smb_passwd, pass2, unenc_new_pw); smbpw->smb_passwd = unenc_new_pw; smbpw->smb_nt_passwd = NULL; /* We lose the NT hash. Sorry. */ /* Now write it into the file. */ become_root(0); ret = mod_smbpwd_entry(smbpw,False); unbecome_root(0); return ret; }
/*********************************************************** Code to check the OEM hashed password. this function ignores the 516 byte nt OEM hashed password but does use the lm OEM password to check the nt hashed-hash. ************************************************************/ BOOL check_oem_password(char *user, uchar *lmdata, uchar *lmhash, uchar *ntdata, uchar *nthash, struct smb_passwd **psmbpw, char *new_passwd, int new_passwd_size) { static uchar null_pw[16]; static uchar null_ntpw[16]; struct smb_passwd *smbpw = NULL; int new_pw_len; uchar new_ntp16[16]; uchar unenc_old_ntpw[16]; uchar new_p16[16]; uchar unenc_old_pw[16]; char no_pw[2]; BOOL nt_pass_set = (ntdata != NULL && nthash != NULL); become_root(False); *psmbpw = smbpw = getsmbpwnam(user); unbecome_root(False); if (smbpw == NULL) { DEBUG(0,("check_oem_password: getsmbpwnam returned NULL\n")); return False; } if (smbpw->acct_ctrl & ACB_DISABLED) { DEBUG(0,("check_lanman_password: account %s disabled.\n", user)); return False; } /* construct a null password (in case one is needed */ no_pw[0] = 0; no_pw[1] = 0; nt_lm_owf_gen(no_pw, null_ntpw, null_pw); /* check for null passwords */ if (smbpw->smb_passwd == NULL) { if (smbpw->acct_ctrl & ACB_PWNOTREQ) { smbpw->smb_passwd = null_pw; } else { DEBUG(0,("check_oem_password: no lanman password !\n")); return False; } } if (smbpw->smb_nt_passwd == NULL && nt_pass_set) { if (smbpw->acct_ctrl & ACB_PWNOTREQ) { smbpw->smb_nt_passwd = null_pw; } else { DEBUG(0,("check_oem_password: no ntlm password !\n")); return False; } } /* * Call the hash function to get the new password. */ SamOEMhash( (uchar *)lmdata, (uchar *)smbpw->smb_passwd, True); /* * The length of the new password is in the last 4 bytes of * the data buffer. */ new_pw_len = IVAL(lmdata, 512); if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) { DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len)); return False; } if (nt_pass_set) { /* * nt passwords are in unicode */ int uni_pw_len = new_pw_len; char *pw; new_pw_len /= 2; pw = dos_unistrn2((uint16*)(&lmdata[512-uni_pw_len]), new_pw_len); memcpy(new_passwd, pw, new_pw_len+1); } else { memcpy(new_passwd, &lmdata[512-new_pw_len], new_pw_len); new_passwd[new_pw_len] = '\0'; } /* * To ensure we got the correct new password, hash it and * use it as a key to test the passed old password. */ nt_lm_owf_gen(new_passwd, new_ntp16, new_p16); if (!nt_pass_set) { /* * Now use new_p16 as the key to see if the old * password matches. */ D_P16(new_p16 , lmhash, unenc_old_pw); if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16)) { DEBUG(0,("check_oem_password: old lm password doesn't match.\n")); return False; } #ifdef DEBUG_PASSWORD DEBUG(100,("check_oem_password: password %s ok\n", new_passwd)); #endif return True; } /* * Now use new_p16 as the key to see if the old * password matches. */ D_P16(new_ntp16, lmhash, unenc_old_pw); D_P16(new_ntp16, nthash, unenc_old_ntpw); if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16)) { DEBUG(0,("check_oem_password: old lm password doesn't match.\n")); return False; } if (memcmp(smbpw->smb_nt_passwd, unenc_old_ntpw, 16)) { DEBUG(0,("check_oem_password: old nt password doesn't match.\n")); return False; } #ifdef DEBUG_PASSWORD DEBUG(100,("check_oem_password: password %s ok\n", new_passwd)); #endif return True; }
/* 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; }