From 35ffc464541366ba1da0a332afae203b68512010 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 8 May 2004 14:42:45 +0000 Subject: r587: added server code for samr_EnumDomainUsers, and started adding samr_SetUserInfo and samr_QueryUserInfo (This used to be commit e0db9659a85b59e52fbe033a94b411d6c64d9f9c) --- source4/lib/time.c | 18 +++ source4/librpc/idl/samr.idl | 4 + source4/rpc_server/samr/dcesrv_samr.c | 231 ++++++++++++++++++++++++++++++++-- source4/rpc_server/samr/samdb.c | 50 +++++++- 4 files changed, 293 insertions(+), 10 deletions(-) diff --git a/source4/lib/time.c b/source4/lib/time.c index 0bc5fcd3fc..65b85b2180 100644 --- a/source4/lib/time.c +++ b/source4/lib/time.c @@ -428,3 +428,21 @@ NTTIME pull_nttime(void *base, uint16 offset) return ret; } + +/* + parse a nttime as a integer in a string and return a NTTIME +*/ +NTTIME nttime_from_string(const char *s) +{ + double t = 0; + const double t32 = 4294967296.0; + NTTIME ret; + /* i wish we could rely on 64 bit systems and sscanf %llu */ + if (sscanf(s, "%lf", &t) != 1) { + ret.low = 0; + ret.high = 0; + } + ret.high = t / t32; + ret.low = t - (ret.high*t32); + return ret; +} diff --git a/source4/librpc/idl/samr.idl b/source4/librpc/idl/samr.idl index eceaf784ec..2110cd9330 100644 --- a/source4/librpc/idl/samr.idl +++ b/source4/librpc/idl/samr.idl @@ -262,6 +262,10 @@ /************************/ /* Function 0x0d */ + + /* w2k3 treats max_size as max_users*54 and sets the + resume_handle as the rid of the last user sent + */ NTSTATUS samr_EnumDomainUsers( [in,ref] policy_handle *handle, [in,out,ref] uint32 *resume_handle, diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c index bea65dceef..20460ba172 100644 --- a/source4/rpc_server/samr/dcesrv_samr.c +++ b/source4/rpc_server/samr/dcesrv_samr.c @@ -500,7 +500,7 @@ static NTSTATUS samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX /* check if the user already exists */ name = samdb_search_string(d_state->sam_ctx, mem_ctx, d_state->basedn, - "name", "(&(name=%s)(objectclass=user))", username); + "name", "(&(sAMAccountName=%s)(objectclass=user))", username); if (name != NULL) { return NT_STATUS_USER_EXISTS; } @@ -611,14 +611,88 @@ static NTSTATUS samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX * return samr_CreateUser2(dce_call, mem_ctx, &r2); } +/* + comparison function for sorting SamEntry array +*/ +static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2) +{ + return e1->idx - e2->idx; +} /* samr_EnumDomainUsers */ static NTSTATUS samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct samr_EnumDomainUsers *r) + struct samr_EnumDomainUsers *r) { - DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR); + struct dcesrv_handle *h; + struct samr_domain_state *state; + struct ldb_message **res; + int count, i, first; + struct samr_SamEntry *entries; + const char * const attrs[3] = { "objectSid", "sAMAccountName", NULL }; + + *r->out.resume_handle = 0; + r->out.sam = NULL; + r->out.num_entries = 0; + + DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_DOMAIN); + + state = h->data; + + /* search for all users in this domain. This could possibly be cached and + resumed based on resume_key */ + count = samdb_search(state->sam_ctx, mem_ctx, state->basedn, &res, attrs, + "objectclass=user"); + if (count == -1) { + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + if (count == 0) { + return NT_STATUS_OK; + } + + /* convert to SamEntry format */ + entries = talloc_array_p(mem_ctx, struct samr_SamEntry, count); + if (!entries) { + return NT_STATUS_NO_MEMORY; + } + for (i=0;iin.resume_handle; + first++) ; + + if (first == count) { + return NT_STATUS_OK; + } + + /* return the rest, limit by max_size. Note that we + use the w2k3 element size value of 54 */ + r->out.num_entries = count - first; + r->out.num_entries = MIN(r->out.num_entries, 1+(r->in.max_size/54)); + + r->out.sam = talloc_p(mem_ctx, struct samr_SamArray); + if (!r->out.sam) { + return NT_STATUS_NO_MEMORY; + } + + r->out.sam->entries = entries+first; + r->out.sam->count = r->out.num_entries; + + if (r->out.num_entries < count - first) { + *r->out.resume_handle = entries[first+r->out.num_entries-1].idx; + return STATUS_MORE_ENTRIES; + } + + return NT_STATUS_OK; } @@ -1016,24 +1090,155 @@ static NTSTATUS samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX * return NT_STATUS_OK; } +/* these query macros make samr_QueryUserInfo a bit easier to read */ +#define QUERY_STRING(msg, field, attr) \ + r->out.info->field = samdb_result_string(msg, attr, ""); +#define QUERY_UINT(msg, field, attr) \ + r->out.info->field = samdb_result_uint(msg, attr, 0); +#define QUERY_RID(msg, field, attr) \ + r->out.info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0); +#define QUERY_NTTIME(msg, field, attr) \ + r->out.info->field = samdb_result_nttime(msg, attr, 0); /* samr_QueryUserInfo */ static NTSTATUS samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct samr_QueryUserInfo *r) + struct samr_QueryUserInfo *r) { - DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR); + struct dcesrv_handle *h; + struct samr_account_state *state; + struct ldb_message *msg, **res; + int ret; + + r->out.info = NULL; + + DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_USER); + + state = h->data; + + /* pull all the user attributes */ + ret = samdb_search(state->sam_ctx, mem_ctx, NULL, &res, NULL, + "dn=%s", state->basedn); + if (ret != 1) { + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + msg = res[0]; + + /* allocate the info structure */ + r->out.info = talloc_p(mem_ctx, union samr_UserInfo); + if (r->out.info == NULL) { + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(r->out.info); + + /* fill in the reply */ + switch (r->in.level) { + case 1: + QUERY_STRING(msg, info1.username.name, "sAMAccountName"); + QUERY_STRING(msg, info1.full_name.name, "displayName"); + QUERY_UINT (msg, info1.primary_gid, "primaryGroupID"); + QUERY_STRING(msg, info1.description.name, "description"); + QUERY_STRING(msg, info1.comment.name, "comment"); + break; + + case 2: + QUERY_STRING(msg, info2.comment.name, "comment"); + QUERY_UINT (msg, info2.country_code, "countryCode"); + QUERY_UINT (msg, info2.code_page, "codePage"); + break; + + case 3: + QUERY_STRING(msg, info3.username.name, "sAMAccountName"); + QUERY_STRING(msg, info3.full_name.name, "displayName"); + QUERY_RID (msg, info3.Rid, "objectSid"); + QUERY_UINT (msg, info3.primary_gid, "primaryGroupID"); + QUERY_STRING(msg, info3.home_directory.name, "homeDirectory"); + QUERY_STRING(msg, info3.home_drive.name, "homeDrive"); + QUERY_STRING(msg, info3.logon_script.name, "scriptPath"); + QUERY_STRING(msg, info3.profile.name, "profilePath"); + QUERY_STRING(msg, info3.workstations.name, "userWorkstations"); + QUERY_NTTIME(msg, info3.last_logon, "lastLogon"); + QUERY_NTTIME(msg, info3.last_logoff, "lastLogoff"); + QUERY_NTTIME(msg, info3.last_pwd_change, "pwdLastSet"); +/* + QUERY_APASSC(msg, info2.allow_pwd_change, "pwdLastSet"); + QUERY_LHOURS(msg, info2.logon_hours, "logonHours"); + QUERY_UINT (msg, info2.bad_pwd_count, "badPwdCount"); + QUERY_UINT (msg, info2.num_logons, "logonCount"); + QUERY_AFLAGS(msg, info2.acct_flags, "userAccountControl"); +*/ + break; + + default: + r->out.info = NULL; + return NT_STATUS_INVALID_INFO_CLASS; + } + + return NT_STATUS_OK; } +/* these are used to make the SetUserInfo code easier to follow */ +#define SET_STRING(mod, field, attr) do { \ + if (r->in.info->field == NULL) return NT_STATUS_INVALID_PARAMETER; \ + if (samdb_msg_add_string(state->sam_ctx, mem_ctx, mod, attr, r->in.info->field) != 0) { \ + return NT_STATUS_NO_MEMORY; \ + } \ +} while (0) + +#define SET_UINT(mod, field, attr) do { \ + if (samdb_msg_add_uint(state->sam_ctx, mem_ctx, mod, attr, r->in.info->field) != 0) { \ + return NT_STATUS_NO_MEMORY; \ + } \ +} while (0) + /* samr_SetUserInfo */ static NTSTATUS samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct samr_SetUserInfo *r) + struct samr_SetUserInfo *r) { - DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR); + struct dcesrv_handle *h; + struct samr_account_state *state; + struct ldb_message mod; + int i, ret; + + DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_USER); + + state = h->data; + + ZERO_STRUCT(mod); + mod.dn = talloc_strdup(mem_ctx, state->basedn); + if (!mod.dn) { + return NT_STATUS_NO_MEMORY; + } + + switch (r->in.level) { + case 2: + SET_STRING(&mod, info2.comment.name, "description"); + SET_UINT (&mod, info2.country_code, "countryCode"); + SET_UINT (&mod, info2.code_page, "codePage"); + break; + + default: + /* many info classes are not valid for SetUserInfo */ + return NT_STATUS_INVALID_INFO_CLASS; + } + + /* mark all the message elements as LDB_FLAG_MOD_REPLACE */ + for (i=0;isam_ctx, mem_ctx, &mod); + if (ret != 0) { + /* we really need samdb.c to return NTSTATUS */ + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; } @@ -1271,11 +1476,19 @@ static NTSTATUS samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *me /* samr_SetUserInfo2 + + just an alias for samr_SetUserInfo */ static NTSTATUS samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct samr_SetUserInfo2 *r) + struct samr_SetUserInfo2 *r) { - DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR); + struct samr_SetUserInfo r2; + + r2.in.handle = r->in.handle; + r2.in.level = r->in.level; + r2.in.info = r->in.info; + + return samr_SetUserInfo(dce_call, mem_ctx, &r2); } diff --git a/source4/rpc_server/samr/samdb.c b/source4/rpc_server/samr/samdb.c index f382dcd136..fc82303f13 100644 --- a/source4/rpc_server/samr/samdb.c +++ b/source4/rpc_server/samr/samdb.c @@ -262,11 +262,38 @@ uint_t samdb_result_uint(struct ldb_message *msg, const char *attr, uint_t defau /* pull a string from a result set. */ -const char *samdb_result_string(struct ldb_message *msg, const char *attr, char *default_value) +const char *samdb_result_string(struct ldb_message *msg, const char *attr, + const char *default_value) { return ldb_msg_find_string(msg, attr, default_value); } +/* + pull a rid from a objectSid in a result set. +*/ +uint32 samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, struct ldb_message *msg, + const char *attr, uint32 default_value) +{ + struct dom_sid *sid; + const char *sidstr = ldb_msg_find_string(msg, attr, NULL); + if (!sidstr) return default_value; + + sid = dom_sid_parse_talloc(mem_ctx, sidstr); + if (!sid) return default_value; + + return sid->sub_auths[sid->num_auths-1]; +} + +/* + pull a rid from a objectSid in a result set. +*/ +NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, + const char *default_value) +{ + const char *str = ldb_msg_find_string(msg, attr, default_value); + return nttime_from_string(str); +} + /* copy from a template record to a message @@ -423,6 +450,16 @@ int samdb_msg_add_string(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg return ldb_msg_add_string(sam_ctx->ldb, msg, a, s); } +/* + add a uint_t element to a message +*/ +int samdb_msg_add_uint(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg, + const char *attr_name, uint_t v) +{ + const char *s = talloc_asprintf(mem_ctx, "%u", v); + return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, s); +} + /* set a string element in a message */ @@ -475,3 +512,14 @@ int samdb_delete(void *ctx, TALLOC_CTX *mem_ctx, const char *dn) ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx); return ldb_delete(sam_ctx->ldb, dn); } + +/* + modify a record +*/ +int samdb_modify(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg) +{ + struct samdb_context *sam_ctx = ctx; + + ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx); + return ldb_modify(sam_ctx->ldb, msg); +} -- cgit