summaryrefslogtreecommitdiff
path: root/source4/rpc_server
diff options
context:
space:
mode:
Diffstat (limited to 'source4/rpc_server')
-rw-r--r--source4/rpc_server/echo/rpc_echo.c9
-rw-r--r--source4/rpc_server/netlogon/dcerpc_netlogon.c3
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.c4
-rw-r--r--source4/rpc_server/samr/samdb.c105
-rw-r--r--source4/rpc_server/samr/samr_password.c224
5 files changed, 290 insertions, 55 deletions
diff --git a/source4/rpc_server/echo/rpc_echo.c b/source4/rpc_server/echo/rpc_echo.c
index 25ac28a664..c4e1256841 100644
--- a/source4/rpc_server/echo/rpc_echo.c
+++ b/source4/rpc_server/echo/rpc_echo.c
@@ -84,13 +84,11 @@ static NTSTATUS echo_TestCall2(struct dcesrv_call_state *dce_call, TALLOC_CTX *m
r->out.info->info3.v = 30;
break;
case 4:
- r->out.info->info4.v.low = 40;
- r->out.info->info4.v.high = 0;
+ r->out.info->info4.v = 40;
break;
case 5:
r->out.info->info5.v1 = 50;
- r->out.info->info5.v2.low = 60;
- r->out.info->info5.v2.high = 0;
+ r->out.info->info5.v2 = 60;
break;
case 6:
r->out.info->info6.v1 = 70;
@@ -98,8 +96,7 @@ static NTSTATUS echo_TestCall2(struct dcesrv_call_state *dce_call, TALLOC_CTX *m
break;
case 7:
r->out.info->info7.v1 = 80;
- r->out.info->info7.info4.v.low = 90;
- r->out.info->info7.info4.v.high = 0;
+ r->out.info->info7.info4.v = 90;
break;
default:
return NT_STATUS_INVALID_LEVEL;
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index 1dab39ebc1..d4e60771ba 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -423,7 +423,8 @@ static NTSTATUS netr_ServerPasswordSet(struct dcesrv_call_state *dce_call, TALLO
msg_set_pw,
NULL, /* Don't have plaintext */
NULL, &newNtHash,
- False /* This is not considered a password change */);
+ False /* This is not considered a password change */,
+ NULL);
if (!NT_STATUS_IS_OK(nt_status)) {
samdb_close(sam_ctx);
diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c
index 0db3a4ff6a..7592c479fa 100644
--- a/source4/rpc_server/samr/dcesrv_samr.c
+++ b/source4/rpc_server/samr/dcesrv_samr.c
@@ -1715,8 +1715,8 @@ static NTSTATUS samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX
/* the set password levels are handled separately */
case 24:
- status = samr_set_password(a_state->sam_ctx,
- dce_call,
+ status = samr_set_password(dce_call,
+ a_state->sam_ctx,
a_state->account_dn,
a_state->domain_state->domain_dn,
mem_ctx, msg,
diff --git a/source4/rpc_server/samr/samdb.c b/source4/rpc_server/samr/samdb.c
index c7a2f770c0..1d5b90a22d 100644
--- a/source4/rpc_server/samr/samdb.c
+++ b/source4/rpc_server/samr/samdb.c
@@ -233,6 +233,32 @@ uint_t samdb_search_uint(void *ctx,
}
/*
+ search the sam for a single signed 64 bit integer attribute in exactly 1 record
+*/
+int64_t samdb_search_int64(void *ctx,
+ TALLOC_CTX *mem_ctx,
+ int64_t default_value,
+ const char *basedn,
+ const char *attr_name,
+ const char *format, ...)
+{
+ va_list ap;
+ int count;
+ struct ldb_message **res;
+ const char * const attrs[2] = { attr_name, NULL };
+
+ va_start(ap, format);
+ count = samdb_search_v(ctx, mem_ctx, basedn, &res, attrs, format, ap);
+ va_end(ap);
+
+ if (count != 1) {
+ return default_value;
+ }
+
+ return samdb_result_int64(res[0], attr_name, default_value);
+}
+
+/*
search the sam for multipe records each giving a single string attribute
return the number of matches, or -1 on error
*/
@@ -289,6 +315,14 @@ uint_t samdb_result_uint(struct ldb_message *msg, const char *attr, uint_t defau
}
/*
+ pull a (signed) int64 from a result set.
+*/
+int64_t samdb_result_int64(struct ldb_message *msg, const char *attr, int64_t default_value)
+{
+ return ldb_msg_find_int64(msg, attr, default_value);
+}
+
+/*
pull a string from a result set.
*/
const char *samdb_result_string(struct ldb_message *msg, const char *attr,
@@ -350,11 +384,11 @@ NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, const char
}
/*
- pull a double (really a large integer) from a result set.
+ pull a uint64_t from a result set.
*/
-double samdb_result_double(struct ldb_message *msg, const char *attr, double default_value)
+uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
{
- return ldb_msg_find_double(msg, attr, default_value);
+ return ldb_msg_find_uint64(msg, attr, default_value);
}
@@ -365,17 +399,20 @@ double samdb_result_double(struct ldb_message *msg, const char *attr, double def
NTTIME samdb_result_allow_pwd_change(void *ctx, TALLOC_CTX *mem_ctx,
const char *domain_dn, struct ldb_message *msg, const char *attr)
{
- double attr_time = samdb_result_double(msg, attr, 0);
- if (attr_time > 0) {
- const char *minPwdAge = samdb_search_string(ctx, mem_ctx, NULL, "minPwdAge",
- "dn=%s", domain_dn);
- if (minPwdAge) {
- /* yes, this is a -= not a += as minPwdAge is stored as the negative
- of the number of 100-nano-seconds */
- attr_time -= strtod(minPwdAge, NULL);
- }
+ uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
+ int64_t minPwdAge;
+
+ if (attr_time == 0) {
+ return 0;
}
- return nttime_from_double_nt(attr_time);
+
+ minPwdAge = samdb_search_int64(ctx, mem_ctx, 0, "minPwdAge", "dn=%s", domain_dn);
+
+ /* yes, this is a -= not a += as minPwdAge is stored as the negative
+ of the number of 100-nano-seconds */
+ attr_time -= minPwdAge;
+
+ return attr_time;
}
/*
@@ -385,17 +422,21 @@ NTTIME samdb_result_allow_pwd_change(void *ctx, TALLOC_CTX *mem_ctx,
NTTIME samdb_result_force_pwd_change(void *ctx, TALLOC_CTX *mem_ctx,
const char *domain_dn, struct ldb_message *msg, const char *attr)
{
- double attr_time = samdb_result_double(msg, attr, 0);
- if (attr_time > 0) {
- const char *maxPwdAge = samdb_search_string(ctx, mem_ctx, NULL, "maxPwdAge",
- "dn=%s", domain_dn);
- if (!maxPwdAge || strcmp(maxPwdAge, "0") == 0) {
- attr_time = 0;
- } else {
- attr_time -= strtod(maxPwdAge, NULL);
- }
+ uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
+ int64_t maxPwdAge;
+
+ if (attr_time == 0) {
+ return 0;
}
- return nttime_from_double_nt(attr_time);
+
+ maxPwdAge = samdb_search_int64(ctx, mem_ctx, 0, "maxPwdAge", "dn=%s", domain_dn);
+ if (maxPwdAge == 0) {
+ attr_time = 0;
+ } else {
+ attr_time -= maxPwdAge;
+ }
+
+ return attr_time;
}
/*
@@ -721,12 +762,22 @@ int samdb_msg_add_uint(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
}
/*
- add a double element to a message (actually a large integer)
+ add a (signed) int64_t element to a message
+*/
+int samdb_msg_add_int64(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, int64_t v)
+{
+ const char *s = talloc_asprintf(mem_ctx, "%lld", v);
+ return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, s);
+}
+
+/*
+ add a uint64_t element to a message
*/
-int samdb_msg_add_double(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
- const char *attr_name, double v)
+int samdb_msg_add_uint64(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, uint64_t v)
{
- const char *s = talloc_asprintf(mem_ctx, "%.0f", v);
+ const char *s = talloc_asprintf(mem_ctx, "%llu", v);
return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, s);
}
diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c
index 5b99e04fab..812e0c69b9 100644
--- a/source4/rpc_server/samr/samr_password.c
+++ b/source4/rpc_server/samr/samr_password.c
@@ -109,7 +109,8 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX
status = samdb_set_password(a_state->sam_ctx, mem_ctx,
a_state->account_dn, a_state->domain_state->domain_dn,
- &mod, NULL, &new_lmPwdHash, &new_ntPwdHash, True);
+ &mod, NULL, &new_lmPwdHash, &new_ntPwdHash,
+ True, NULL);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
@@ -210,7 +211,7 @@ NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_
user_dn, domain_dn,
&mod, new_pass,
NULL, NULL,
- True);
+ True, NULL);
if (!NT_STATUS_IS_OK(status)) {
samdb_close(sam_ctx);
return status;
@@ -241,10 +242,158 @@ NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX
/*
samr_ChangePasswordUser3
*/
-NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
struct samr_ChangePasswordUser3 *r)
-{
- DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+{
+ NTSTATUS status;
+ char new_pass[512];
+ uint32 new_pass_len;
+ void *sam_ctx = NULL;
+ const char *user_dn, *domain_dn = NULL;
+ int ret;
+ struct ldb_message **res, mod;
+ const char * const attrs[] = { "objectSid", "ntPwdHash", NULL };
+ const char * const dom_attrs[] = { "minPwdLength", "pwdHistoryLength",
+ "pwdProperties", "minPwdAge", "maxPwdAge",
+ NULL };
+ const char *domain_sid;
+ struct samr_Hash *ntPwdHash;
+ struct samr_DomInfo1 *dominfo;
+ struct samr_ChangeReject *reject;
+ uint32 reason = 0;
+
+ ZERO_STRUCT(r->out);
+
+ if (r->in.nt_password == NULL ||
+ r->in.nt_verifier == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto failed;
+ }
+
+ /* this call doesn't take a policy handle, so we need to open
+ the sam db from scratch */
+ sam_ctx = samdb_connect();
+ if (sam_ctx == NULL) {
+ status = NT_STATUS_INVALID_SYSTEM_SERVICE;
+ goto failed;
+ }
+
+ /* we need the users dn and the domain dn (derived from the
+ user SID). We also need the current lm and nt password hashes
+ in order to decrypt the incoming passwords */
+ ret = samdb_search(sam_ctx,
+ mem_ctx, NULL, &res, attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ r->in.account->name);
+ if (ret != 1) {
+ status = NT_STATUS_NO_SUCH_USER;
+ goto failed;
+ }
+
+ user_dn = res[0]->dn;
+
+ ret = samdb_result_hashes(mem_ctx, res[0], "ntPwdHash", &ntPwdHash);
+ if (ret != 1) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+
+ /* decrypt the password we have been given */
+ SamOEMhash(r->in.nt_password->data, ntPwdHash->hash, 516);
+
+ if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
+ &new_pass_len, STR_UNICODE)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+
+ /* work out the domain dn */
+ domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
+ if (domain_sid == NULL) {
+ status = NT_STATUS_NO_SUCH_DOMAIN;
+ goto failed;
+ }
+
+ domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
+ "(objectSid=%s)", domain_sid);
+ if (!domain_dn) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+
+
+ ZERO_STRUCT(mod);
+ mod.dn = talloc_strdup(mem_ctx, user_dn);
+ if (!mod.dn) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ /* set the password - samdb needs to know both the domain and user DNs,
+ so the domain password policy can be used */
+ status = samdb_set_password(sam_ctx, mem_ctx,
+ user_dn, domain_dn,
+ &mod, new_pass,
+ NULL, NULL,
+ True, &reason);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* modify the samdb record */
+ ret = samdb_replace(sam_ctx, mem_ctx, &mod);
+ if (ret != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto failed;
+ }
+
+ samdb_close(sam_ctx);
+ return NT_STATUS_OK;
+
+failed:
+ if (sam_ctx) {
+ samdb_close(sam_ctx);
+ }
+
+ /* on failure we need to fill in the reject reasons */
+ dominfo = talloc_p(mem_ctx, struct samr_DomInfo1);
+ if (dominfo == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ reject = talloc_p(mem_ctx, struct samr_ChangeReject);
+ if (reject == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(dominfo);
+ ZERO_STRUCTP(reject);
+
+ reject->reason = reason;
+
+ r->out.dominfo = dominfo;
+ r->out.reject = reject;
+
+ if (!domain_dn) {
+ return status;
+ }
+
+ ret = samdb_search(sam_ctx,
+ mem_ctx, NULL, &res, dom_attrs,
+ "dn=%s", domain_dn);
+ if (ret != 1) {
+ status = NT_STATUS_NO_SUCH_USER;
+ goto failed;
+ }
+
+ dominfo->min_pwd_len = samdb_result_uint(res[0], "minPwdLength", 0);
+ dominfo->password_properties = samdb_result_uint(res[0], "pwdProperties", 0);
+ dominfo->password_history = samdb_result_uint(res[0], "pwdHistoryLength", 0);
+ dominfo->max_password_age = samdb_result_int64(res[0], "maxPwdAge", 0);
+ dominfo->min_password_age = samdb_result_int64(res[0], "minPwdAge", 0);
+
+ return status;
}
@@ -269,12 +418,13 @@ BOOL samdb_password_complexity_ok(const char *pass)
changes (as is needed by some of the set user info levels)
*/
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,
- struct samr_Hash *lmNewHash,
- struct samr_Hash *ntNewHash,
- 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,
+ uint32 *reject_reason)
{
const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
"ntPwdHistory", "unicodePwd",
@@ -284,7 +434,8 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
"maxPwdAge", "minPwdAge",
"minPwdLength", "pwdLastSet", NULL };
const char *unicodePwd;
- double minPwdAge, pwdLastSet;
+ NTTIME pwdLastSet;
+ int64_t minPwdAge;
uint_t minPwdLength, pwdProperties, pwdHistoryLength;
uint_t userAccountControl, badPwdCount;
struct samr_Hash *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
@@ -295,12 +446,10 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
int count;
time_t now = time(NULL);
NTTIME now_nt;
- double now_double;
int i;
/* we need to know the time to compute password age */
unix_to_nt_time(&now_nt, now);
- now_double = nttime_to_double_nt(now_nt);
/* pull all the user parameters */
count = samdb_search(ctx, mem_ctx, NULL, &res, user_attrs, "dn=%s", user_dn);
@@ -316,7 +465,7 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
"ntPwdHistory", &ntPwdHistory);
lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
- pwdLastSet = samdb_result_double(res[0], "pwdLastSet", 0);
+ pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
/* pull the domain parameters */
count = samdb_search(ctx, mem_ctx, NULL, &res, domain_attrs, "dn=%s", domain_dn);
@@ -326,17 +475,23 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
- minPwdAge = samdb_result_double(res[0], "minPwdAge", 0);
+ minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
if (new_pass) {
/* check the various password restrictions */
if (minPwdLength > str_charnum(new_pass)) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_TOO_SHORT;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
/* possibly check password complexity */
if (pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
!samdb_password_complexity_ok(new_pass)) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
@@ -351,25 +506,40 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
if (user_change) {
/* are all password changes disallowed? */
if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_OTHER;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
/* can this user change password? */
if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_OTHER;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
/* yes, this is a minus. The ages are in negative 100nsec units! */
- if (pwdLastSet - minPwdAge > now_double) {
+ if (pwdLastSet - minPwdAge > now_nt) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_OTHER;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
/* check the immediately past password */
if (pwdHistoryLength > 0) {
if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
}
@@ -380,23 +550,38 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
if (pwdHistoryLength > 0) {
if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
}
for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
}
for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
return NT_STATUS_PASSWORD_RESTRICTION;
}
}
@@ -425,7 +610,7 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
}
- CHECK_RET(samdb_msg_add_double(ctx, mem_ctx, mod, "pwdLastSet", now_double));
+ CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
if (pwdHistoryLength == 0) {
CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
@@ -509,6 +694,7 @@ NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
account_dn, domain_dn,
msg, new_pass,
NULL, NULL,
- False /* This is a password set, not change */);
+ False /* This is a password set, not change */,
+ NULL);
}