diff options
author | Andrew Bartlett <abartlet@samba.org> | 2004-05-16 21:30:48 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 12:53:48 -0500 |
commit | 92dd542aa01f2c3b64ca104696c731919f4d7ec7 (patch) | |
tree | 1a1e2489c32f2b8c6b4eec31a7f8dc019ebda2ed /source4 | |
parent | 8b84f643bd50c83230b723eb35b0edafe5670fca (diff) | |
download | samba-92dd542aa01f2c3b64ca104696c731919f4d7ec7.tar.gz samba-92dd542aa01f2c3b64ca104696c731919f4d7ec7.tar.bz2 samba-92dd542aa01f2c3b64ca104696c731919f4d7ec7.zip |
r754: Implement the SetPassword operation on the netlogon pipe.
This involves allowing the password set code in samdb to take an
already hashed password, and some fixes to our torture code.
Andrew Bartlett
(This used to be commit f9f581b5804a20785df06cde157b23c952edc2ce)
Diffstat (limited to 'source4')
-rw-r--r-- | source4/libcli/auth/credentials.c | 61 | ||||
-rw-r--r-- | source4/rpc_server/netlogon/dcerpc_netlogon.c | 141 | ||||
-rw-r--r-- | source4/rpc_server/samr/dcesrv_samr.c | 4 | ||||
-rw-r--r-- | source4/rpc_server/samr/samdb.c | 102 | ||||
-rw-r--r-- | source4/torture/rpc/netlogon.c | 2 |
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"); |