summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/libcli/auth/credentials.c61
-rw-r--r--source4/rpc_server/netlogon/dcerpc_netlogon.c141
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.c4
-rw-r--r--source4/rpc_server/samr/samdb.c102
-rw-r--r--source4/torture/rpc/netlogon.c2
5 files changed, 252 insertions, 58 deletions
diff --git a/source4/libcli/auth/credentials.c b/source4/libcli/auth/credentials.c
index 7d56f26b11..5fa9d5ac4a 100644
--- a/source4/libcli/auth/credentials.c
+++ b/source4/libcli/auth/credentials.c
@@ -69,8 +69,6 @@ static void creds_step(struct creds_CredentialState *creds)
{
struct netr_Credential time_cred;
- creds->sequence += 2;
-
DEBUG(5,("\tseed %08x:%08x\n",
IVAL(creds->seed.data, 0), IVAL(creds->seed.data, 4)));
@@ -98,6 +96,7 @@ static void creds_step(struct creds_CredentialState *creds)
creds->seed = time_cred;
}
+
/*
DES encrypt a 16 byte password buffer using the session key
*/
@@ -109,6 +108,16 @@ void creds_des_encrypt(struct creds_CredentialState *creds, struct netr_Password
}
/*
+ DES decrypt a 16 byte password buffer using the session key
+*/
+void creds_des_decrypt(struct creds_CredentialState *creds, struct netr_Password *pass)
+{
+ struct netr_Password tmp;
+ cred_hash3(tmp.data, pass->data, creds->session_key, 0);
+ *pass = tmp;
+}
+
+/*
ARCFOUR encrypt/decrypt a password buffer using the session key
*/
void creds_arcfour_crypt(struct creds_CredentialState *creds, char *data, size_t len)
@@ -138,13 +147,30 @@ void creds_client_init(struct creds_CredentialState *creds,
const uint8 machine_password[16],
struct netr_Credential *initial_credential)
{
- creds_init(creds, client_challenge, server_challenge, machine_password);
creds->sequence = time(NULL);
+ creds_init(creds, client_challenge, server_challenge, machine_password);
*initial_credential = creds->client;
}
/*
+ step the credentials to the next element in the chain, updating the
+ current client and server credentials and the seed
+
+ produce the next authenticator in the sequence ready to send to
+ the server
+*/
+void creds_client_authenticator(struct creds_CredentialState *creds,
+ struct netr_Authenticator *next)
+{
+ creds->sequence += 2;
+ creds_step(creds);
+
+ next->cred = creds->client;
+ next->timestamp = creds->sequence;
+}
+
+/*
check that a credentials reply from a server is correct
*/
BOOL creds_client_check(struct creds_CredentialState *creds,
@@ -158,19 +184,6 @@ BOOL creds_client_check(struct creds_CredentialState *creds,
return True;
}
-/*
- produce the next authenticator in the sequence ready to send to
- the server
-*/
-void creds_client_authenticator(struct creds_CredentialState *creds,
- struct netr_Authenticator *next)
-{
- creds_step(creds);
-
- next->cred = creds->client;
- next->timestamp = creds->sequence;
-}
-
/*****************************************************************
The above functions are common to the client and server interface
@@ -207,3 +220,19 @@ BOOL creds_server_check(const struct creds_CredentialState *creds,
return True;
}
+BOOL creds_server_step_check(struct creds_CredentialState *creds,
+ struct netr_Authenticator *received_authenticator,
+ struct netr_Authenticator *return_authenticator)
+{
+ /* Should we check that this is increasing? */
+ creds->sequence = received_authenticator->timestamp;
+ creds_step(creds);
+ if (creds_server_check(creds, &received_authenticator->cred)) {
+ return_authenticator->cred = creds->server;
+ return_authenticator->timestamp = creds->sequence;
+ return True;
+ } else {
+ ZERO_STRUCTP(return_authenticator);
+ return False;
+ }
+}
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index 87945ffef0..853a8b39e9 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -175,7 +175,7 @@ static NTSTATUS netr_ServerAuthenticateInternals(struct server_pipe_state *pipe_
account_name);
if (num_records == 0) {
- DEBUG(3,("Couldn't find user [%s] in passdb file.\n",
+ DEBUG(3,("Couldn't find user [%s] in samdb.\n",
account_name));
samdb_close(sam_ctx);
return NT_STATUS_NO_SUCH_USER;
@@ -227,7 +227,7 @@ static NTSTATUS netr_ServerAuthenticateInternals(struct server_pipe_state *pipe_
samdb_close(sam_ctx);
if (!pipe_state->creds) {
- pipe_state->creds = talloc_p(mem_ctx, struct creds_CredentialState);
+ pipe_state->creds = talloc_p(pipe_state->mem_ctx, struct creds_CredentialState);
if (!pipe_state->creds) {
return NT_STATUS_NO_MEMORY;
}
@@ -297,6 +297,18 @@ static NTSTATUS netr_ServerAuthenticate2(struct dcesrv_call_state *dce_call, TAL
r->out.negotiate_flags);
}
+static BOOL netr_creds_server_step_check(struct server_pipe_state *pipe_state,
+ struct netr_Authenticator *received_authenticator,
+ struct netr_Authenticator *return_authenticator)
+{
+ if (!pipe_state->authenticated) {
+ return False;
+ }
+ return creds_server_step_check(pipe_state->creds,
+ received_authenticator,
+ return_authenticator);
+}
+
/*
netr_ServerPasswordSet
@@ -314,10 +326,133 @@ static NTSTATUS netr_ServerAuthenticate2(struct dcesrv_call_state *dce_call, TAL
static NTSTATUS netr_ServerPasswordSet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
struct netr_ServerPasswordSet *r)
{
+ struct server_pipe_state *pipe_state = dce_call->conn->private;
- DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+ void *sam_ctx;
+ int num_records;
+ int num_records_domain;
+ int ret;
+ int i;
+ struct ldb_message **msgs;
+ struct ldb_message **msgs_domain;
+ NTSTATUS nt_status;
+ struct samr_Hash newNtHash;
+ struct ldb_message mod, *msg_set_pw = &mod;
+ const char *domain_dn;
+ struct dom_sid *domain_sid;
+
+ const char *attrs[] = {"objectSid", NULL
+ };
+
+ const char **domain_attrs = attrs;
+ ZERO_STRUCT(mod);
+
+ if (!netr_creds_server_step_check(pipe_state, &r->in.credential, &r->out.return_authenticator)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!pipe_state) {
+ DEBUG(1, ("No challange requested by client, cannot authenticate\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ sam_ctx = samdb_connect();
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+ /* pull the user attributes */
+ num_records = samdb_search(sam_ctx, mem_ctx, NULL, &msgs, attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ pipe_state->account_name);
+
+ if (num_records == 0) {
+ DEBUG(3,("Couldn't find user [%s] in samdb.\n",
+ pipe_state->account_name));
+ samdb_close(sam_ctx);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (num_records > 1) {
+ DEBUG(1,("Found %d records matching user [%s]\n", num_records,
+ pipe_state->account_name));
+ samdb_close(sam_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ domain_sid = dom_sid_parse_talloc(mem_ctx,
+ samdb_result_string(msgs[0],
+ "objectSid",
+ NULL));
+ if (!domain_sid) {
+ samdb_close(sam_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ sid_split_rid(domain_sid, NULL);
+
+ /* find the domain's DN */
+ num_records_domain = samdb_search(sam_ctx, mem_ctx, NULL,
+ &msgs_domain, domain_attrs,
+ "(&(objectSid=%s)(objectclass=domain))",
+ dom_sid_string(mem_ctx, domain_sid));
+
+ if (num_records_domain == 0) {
+ DEBUG(3,("check_sam_security: Couldn't find domain [%s] in passdb file.\n",
+ dom_sid_string(mem_ctx, domain_sid)));
+ samdb_close(sam_ctx);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (num_records_domain > 1) {
+ DEBUG(1,("Found %d records matching domain [%s]\n", num_records_domain, dom_sid_string(mem_ctx, domain_sid)));
+ samdb_close(sam_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ domain_dn = msgs_domain[0]->dn;
+
+ mod.dn = talloc_strdup(mem_ctx, msgs[0]->dn);
+ if (!mod.dn) {
+ samdb_close(sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ creds_des_decrypt(pipe_state->creds, &r->in.new_password);
+
+ memcpy(newNtHash.hash, r->in.new_password.data, sizeof(newNtHash.hash));
+
+ /* set the password - samdb needs to know both the domain and user DNs,
+ so the domain password policy can be used */
+ nt_status = samdb_set_password(sam_ctx, mem_ctx,
+ msgs[0]->dn, domain_dn,
+ msg_set_pw,
+ NULL, /* Don't have plaintext */
+ NULL, &newNtHash,
+ False /* This is not considered a password change */);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ samdb_close(sam_ctx);
+ return nt_status;
+ }
+ /* mark all the message elements as LDB_FLAG_MOD_REPLACE,
+ unless they are already marked with some other flag */
+ for (i=0;i<mod.num_elements;i++) {
+ if (mod.elements[i].flags == 0) {
+ mod.elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+ }
+
+ ret = samdb_modify(sam_ctx, mem_ctx, msg_set_pw);
+ if (ret != 0) {
+ /* we really need samdb.c to return NTSTATUS */
+
+ samdb_close(sam_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ samdb_close(sam_ctx);
+ return NT_STATUS_OK;
}
diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c
index 847b30e71c..62be2cd262 100644
--- a/source4/rpc_server/samr/dcesrv_samr.c
+++ b/source4/rpc_server/samr/dcesrv_samr.c
@@ -1687,7 +1687,9 @@ static NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
so the domain password policy can be used */
return samdb_set_password(a_state->sam_ctx, mem_ctx,
a_state->account_dn, a_state->domain_state->domain_dn,
- msg, new_pass, False /* This is a password set, not change */);
+ msg, new_pass,
+ NULL, NULL,
+ False /* This is a password set, not change */);
}
/*
diff --git a/source4/rpc_server/samr/samdb.c b/source4/rpc_server/samr/samdb.c
index 2489dae684..76de26222d 100644
--- a/source4/rpc_server/samr/samdb.c
+++ b/source4/rpc_server/samr/samdb.c
@@ -840,9 +840,12 @@ static BOOL samdb_password_complexity_ok(const char *pass)
password restrictions
*/
NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
- const char *user_dn, const char *domain_dn,
- struct ldb_message *mod, const char *new_pass,
- BOOL user_change)
+ const char *user_dn, const char *domain_dn,
+ struct ldb_message *mod,
+ const char *new_pass,
+ struct samr_Hash *lmNewHash,
+ struct samr_Hash *ntNewHash,
+ BOOL user_change)
{
const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
"ntPwdHistory", "unicodePwd",
@@ -857,15 +860,14 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
uint_t userAccountControl, badPwdCount;
struct samr_Hash *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
struct samr_Hash *new_lmPwdHistory, *new_ntPwdHistory;
- struct samr_Hash lmNewHash, ntNewHash;
- uint_t lmPwdHistory_len, ntPwdHistory_len;
+ struct samr_Hash local_lmNewHash, local_ntNewHash;
+ int lmPwdHistory_len, ntPwdHistory_len;
struct ldb_message **res;
int count;
time_t now = time(NULL);
NTTIME now_nt;
double now_double;
int i;
- BOOL lm_hash_ok;
/* we need to know the time to compute password age */
unix_to_nt_time(&now_nt, now);
@@ -897,9 +899,25 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
minPwdAge = samdb_result_double(res[0], "minPwdAge", 0);
- /* compute the new nt and lm hashes */
- lm_hash_ok = E_deshash(new_pass, lmNewHash.hash);
- E_md4hash(new_pass, ntNewHash.hash);
+ if (new_pass) {
+ /* check the various password restrictions */
+ if (minPwdLength > str_charnum(new_pass)) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* possibly check password complexity */
+ if (pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
+ !samdb_password_complexity_ok(new_pass)) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* compute the new nt and lm hashes */
+ if (E_deshash(new_pass, local_lmNewHash.hash)) {
+ lmNewHash = &local_lmNewHash;
+ }
+ E_md4hash(new_pass, local_ntNewHash.hash);
+ ntNewHash = &local_ntNewHash;
+ }
if (user_change) {
/* are all password changes disallowed? */
@@ -919,10 +937,10 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
/* check the immediately past password */
if (pwdHistoryLength > 0) {
- if (lm_hash_ok && memcmp(lmNewHash.hash, lmPwdHash.hash, 16) == 0) {
+ if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
return NT_STATUS_PASSWORD_RESTRICTION;
}
- if (memcmp(ntNewHash.hash, ntPwdHash.hash, 16) == 0) {
+ if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
return NT_STATUS_PASSWORD_RESTRICTION;
}
}
@@ -932,51 +950,45 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
if (pwdHistoryLength > 0) {
- if (unicodePwd && strcmp(unicodePwd, new_pass) == 0) {
+ if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
return NT_STATUS_PASSWORD_RESTRICTION;
}
- if (lm_hash_ok && memcmp(lmNewHash.hash, lmPwdHash.hash, 16) == 0) {
+ if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
return NT_STATUS_PASSWORD_RESTRICTION;
}
- if (memcmp(ntNewHash.hash, ntPwdHash.hash, 16) == 0) {
+ if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
return NT_STATUS_PASSWORD_RESTRICTION;
}
}
- for (i=0;lm_hash_ok && i<lmPwdHistory_len;i++) {
- if (memcmp(lmNewHash.hash, lmPwdHistory[i].hash, 16) == 0) {
+ for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
+ if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
return NT_STATUS_PASSWORD_RESTRICTION;
}
}
- for (i=0;i<ntPwdHistory_len;i++) {
- if (memcmp(ntNewHash.hash, ntPwdHistory[i].hash, 16) == 0) {
+ for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
+ if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
return NT_STATUS_PASSWORD_RESTRICTION;
}
}
}
- /* check the various password restrictions */
- if (minPwdLength > str_charnum(new_pass)) {
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
-
- /* possibly check password complexity */
- if (pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
- !samdb_password_complexity_ok(new_pass)) {
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
-
#define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
/* the password is acceptable. Start forming the new fields */
- if (lm_hash_ok) {
- CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
+ if (lmNewHash) {
+ CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", *lmNewHash));
} else {
CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
}
- CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
- if ((pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
+ if (ntNewHash) {
+ CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", *ntNewHash));
+ } else {
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
+ }
+
+ if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
(userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
"unicodePwd", new_pass));
@@ -1009,17 +1021,31 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
new_ntPwdHistory[i+1] = ntPwdHistory[i];
}
- new_lmPwdHistory[0] = lmNewHash;
- new_ntPwdHistory[0] = ntNewHash;
+
+ /* Don't store 'long' passwords in the LM history,
+ but make sure to 'expire' one password off the other end */
+ if (lmNewHash) {
+ new_lmPwdHistory[0] = *lmNewHash;
+ } else {
+ ZERO_STRUCT(new_lmPwdHistory[0]);
+ }
+ lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
+
+ if (ntNewHash) {
+ new_ntPwdHistory[0] = *ntNewHash;
+ } else {
+ ZERO_STRUCT(new_ntPwdHistory[0]);
+ }
+ ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
"lmPwdHistory",
new_lmPwdHistory,
- MIN(pwdHistoryLength, lmPwdHistory_len+1)));
+ lmPwdHistory_len));
+
CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
"ntPwdHistory",
new_ntPwdHistory,
- MIN(pwdHistoryLength, ntPwdHistory_len+1)));
-
+ ntPwdHistory_len));
return NT_STATUS_OK;
}
diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c
index 42d79e154c..b4fbcdf01d 100644
--- a/source4/torture/rpc/netlogon.c
+++ b/source4/torture/rpc/netlogon.c
@@ -1045,6 +1045,8 @@ static BOOL test_SetPassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
password = generate_random_str(mem_ctx, 8);
E_md4hash(password, r.in.new_password.data);
+ creds_des_encrypt(&creds, &r.in.new_password);
+
/* by changing the machine password twice we test the credentials
chaining fully */
printf("Testing a second ServerPasswordSet on machine account\n");