diff options
Diffstat (limited to 'source4/rpc_server')
-rw-r--r-- | source4/rpc_server/common/common.h | 11 | ||||
-rw-r--r-- | source4/rpc_server/samr/dcesrv_samr.c | 410 | ||||
-rw-r--r-- | source4/rpc_server/samr/samdb.c | 302 |
3 files changed, 624 insertions, 99 deletions
diff --git a/source4/rpc_server/common/common.h b/source4/rpc_server/common/common.h index 17b76840af..afc238f6b6 100644 --- a/source4/rpc_server/common/common.h +++ b/source4/rpc_server/common/common.h @@ -34,3 +34,14 @@ /* a useful macro for checking the validity of a dcerpc policy handle and giving the right fault code if invalid */ #define DCESRV_CHECK_HANDLE(h) do {if (!(h)) DCESRV_FAULT(DCERPC_FAULT_CONTEXT_MISMATCH); } while (0) + +/* this checks for a valid policy handle, and gives a fault if an + invalid handle or NT_STATUS_INVALID_HANDLE if the handle is of the + wrong type */ +#define DCESRV_PULL_HANDLE(h, inhandle, t) do { \ + (h) = dcesrv_handle_fetch(dce_call->conn, (inhandle), DCESRV_HANDLE_ANY); \ + DCESRV_CHECK_HANDLE(h); \ + if ((t) != DCESRV_HANDLE_ANY && (h)->wire_handle.handle_type != (t)) { \ + return NT_STATUS_INVALID_HANDLE; \ + } \ +} while (0) diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c index 0b443a7078..bea65dceef 100644 --- a/source4/rpc_server/samr/dcesrv_samr.c +++ b/source4/rpc_server/samr/dcesrv_samr.c @@ -56,18 +56,20 @@ struct samr_domain_state { uint32 access_mask; const char *domain_sid; const char *domain_name; + const char *basedn; }; /* - state associated with a open user handle + state associated with a open account handle */ -struct samr_user_state { +struct samr_account_state { struct samr_domain_state *domain_state; void *sam_ctx; TALLOC_CTX *mem_ctx; uint32 access_mask; - const char *user_sid; - const char *user_name; + const char *account_sid; + const char *account_name; + const char *basedn; }; @@ -147,10 +149,11 @@ static NTSTATUS samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem static NTSTATUS samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct samr_Close *r) { - struct dcesrv_handle *h = dcesrv_handle_fetch(dce_call->conn, - r->in.handle, - DCESRV_HANDLE_ANY); - DCESRV_CHECK_HANDLE(h); + struct dcesrv_handle *h; + + *r->out.handle = *r->in.handle; + + DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY); /* this causes the callback samr_XXX_destroy() to be called by the handle destroy code which destroys the state associated @@ -209,20 +212,18 @@ static NTSTATUS samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX struct dom_sid2 *sid; const char *sidstr; - h = dcesrv_handle_fetch(dce_call->conn, r->in.handle, SAMR_HANDLE_CONNECT); + r->out.sid = NULL; - DCESRV_CHECK_HANDLE(h); + DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_CONNECT); state = h->data; - r->out.sid = NULL; - if (r->in.domain->name == NULL) { return NT_STATUS_INVALID_PARAMETER; } sidstr = samdb_search_string(state->sam_ctx, - mem_ctx, "objectSid", + mem_ctx, NULL, "objectSid", "(&(name=%s)(objectclass=domain))", r->in.domain->name); if (sidstr == NULL) { @@ -253,21 +254,19 @@ static NTSTATUS samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX struct samr_connect_state *state; struct dcesrv_handle *h; struct samr_SamArray *array; - char **domains; + const char **domains; int count, i, start_i; - h = dcesrv_handle_fetch(dce_call->conn, r->in.handle, SAMR_HANDLE_CONNECT); - - DCESRV_CHECK_HANDLE(h); - - state = h->data; - *r->out.resume_handle = 0; r->out.sam = NULL; r->out.num_entries = 0; + DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_CONNECT); + + state = h->data; + count = samdb_search_string_multiple(state->sam_ctx, - mem_ctx, &domains, + mem_ctx, NULL, &domains, "name", "(objectclass=domain)"); if (count == -1) { DEBUG(1,("samdb: no domains found in EnumDomains\n")); @@ -310,11 +309,11 @@ static NTSTATUS samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX /* - destroy an open domain context + close an open domain context */ -static void samr_Domain_destroy(struct dcesrv_connection *conn, struct dcesrv_handle *h) +static void samr_Domain_close(struct dcesrv_connection *conn, + struct samr_domain_state *state) { - struct samr_domain_state *state = h->data; state->reference_count--; if (state->reference_count == 0) { samr_Connect_close(state->connect_state); @@ -322,6 +321,15 @@ static void samr_Domain_destroy(struct dcesrv_connection *conn, struct dcesrv_ha } } +/* + destroy an open domain context +*/ +static void samr_Domain_destroy(struct dcesrv_connection *conn, struct dcesrv_handle *h) +{ + struct samr_domain_state *state = h->data; + samr_Domain_close(conn, state); +} + /* samr_OpenDomain */ @@ -329,25 +337,38 @@ static NTSTATUS samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX * struct samr_OpenDomain *r) { struct dcesrv_handle *h_conn, *h_domain; - char *sidstr, *domain_name; + const char *sidstr, *domain_name; struct samr_connect_state *c_state; struct samr_domain_state *state; TALLOC_CTX *mem_ctx2; + const char * const attrs[2] = { "name", NULL}; + struct ldb_message **msgs; + int ret; + + ZERO_STRUCTP(r->out.domain_handle); - h_conn = dcesrv_handle_fetch(dce_call->conn, r->in.handle, SAMR_HANDLE_CONNECT); - DCESRV_CHECK_HANDLE(h_conn); + DCESRV_PULL_HANDLE(h_conn, r->in.handle, SAMR_HANDLE_CONNECT); c_state = h_conn->data; + if (r->in.sid == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + sidstr = dom_sid_string(mem_ctx, r->in.sid); if (sidstr == NULL) { return NT_STATUS_INVALID_PARAMETER; } - domain_name = samdb_search_string(c_state->sam_ctx, - mem_ctx, "name", - "(&(objectSid=%s)(objectclass=domain))", - sidstr); + ret = samdb_search(c_state->sam_ctx, + mem_ctx, NULL, &msgs, attrs, + "(&(objectSid=%s)(objectclass=domain))", + sidstr); + if (ret != 1) { + return NT_STATUS_NO_SUCH_DOMAIN; + } + + domain_name = ldb_msg_find_string(msgs[0], "name", NULL); if (domain_name == NULL) { return NT_STATUS_NO_SUCH_DOMAIN; } @@ -367,8 +388,13 @@ static NTSTATUS samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX * state->connect_state = c_state; state->sam_ctx = c_state->sam_ctx; state->mem_ctx = mem_ctx2; - state->domain_sid = talloc_steal(mem_ctx, mem_ctx2, sidstr); - state->domain_name = talloc_steal(mem_ctx, mem_ctx2, domain_name); + state->domain_sid = talloc_strdup(mem_ctx2, sidstr); + state->domain_name = talloc_strdup(mem_ctx2, domain_name); + state->basedn = talloc_strdup(mem_ctx2, msgs[0]->dn); + if (!state->domain_sid || !state->domain_name || !state->basedn) { + talloc_destroy(mem_ctx2); + return NT_STATUS_NO_MEMORY; + } state->access_mask = r->in.access_mask; h_domain = dcesrv_handle_new(dce_call->conn, SAMR_HANDLE_DOMAIN); @@ -426,43 +452,140 @@ static NTSTATUS samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC } +/* + destroy an open account context +*/ +static void samr_Account_destroy(struct dcesrv_connection *conn, struct dcesrv_handle *h) +{ + struct samr_account_state *state = h->data; + samr_Domain_close(conn, state->domain_state); + talloc_destroy(state->mem_ctx); +} + + /* samr_CreateUser2 + + TODO: This should do some form of locking, especially around the rid allocation */ static NTSTATUS samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct samr_CreateUser2 *r) { struct samr_domain_state *d_state; - struct samr_user_state *state; - struct dcesrv_handle *h = dcesrv_handle_fetch(dce_call->conn, - r->in.handle, - SAMR_HANDLE_DOMAIN); + struct samr_account_state *state; + struct dcesrv_handle *h; const char *name; + struct ldb_message msg; + uint32 rid; + const char *username, *sidstr; + time_t now = time(NULL); + TALLOC_CTX *mem_ctx2; + struct dcesrv_handle *u_handle; + int ret; + NTSTATUS status; + + ZERO_STRUCTP(r->out.acct_handle); + *r->out.access_granted = 0; + *r->out.rid = 0; - DCESRV_CHECK_HANDLE(h); + DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_DOMAIN); d_state = h->data; - if (r->in.username->name == NULL) { + username = r->in.username->name; + + if (username == NULL) { return NT_STATUS_INVALID_PARAMETER; } /* check if the user already exists */ - name = samdb_search_string(d_state->sam_ctx, mem_ctx, "name", - "(&(name=%s)(objectclass=user))", - r->in.username->name); + name = samdb_search_string(d_state->sam_ctx, mem_ctx, d_state->basedn, + "name", "(&(name=%s)(objectclass=user))", username); if (name != NULL) { return NT_STATUS_USER_EXISTS; } - /* read the default user template */ + ZERO_STRUCT(msg); + + /* pull in all the template attributes */ + ret = samdb_copy_template(d_state->sam_ctx, mem_ctx, &msg, + "(&(name=TemplateUser)(objectclass=userTemplate))"); + if (ret != 0) { + DEBUG(1,("Failed to load TemplateUser from samdb\n")); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + /* allocate a rid */ - /* create a ldb_message for the user */ + status = samdb_allocate_next_id(d_state->sam_ctx, mem_ctx, + d_state->basedn, "nextRid", &rid); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* and the users SID */ + sidstr = talloc_asprintf(mem_ctx, "%s-%u", d_state->domain_sid, rid); + if (!sidstr) { + return NT_STATUS_NO_MEMORY; + } + + /* add core elements to the ldb_message for the user */ + msg.dn = talloc_asprintf(mem_ctx, "CN=%s,CN=Users,%s", username, d_state->basedn); + if (!msg.dn) { + return NT_STATUS_NO_MEMORY; + } + samdb_msg_add_string(d_state->sam_ctx, mem_ctx, &msg, "name", username); + samdb_msg_add_string(d_state->sam_ctx, mem_ctx, &msg, "cn", username); + samdb_msg_add_string(d_state->sam_ctx, mem_ctx, &msg, "sAMAccountName", username); + samdb_msg_add_string(d_state->sam_ctx, mem_ctx, &msg, "objectClass", "user"); + samdb_msg_add_string(d_state->sam_ctx, mem_ctx, &msg, "objectSid", sidstr); + samdb_msg_set_ldaptime(d_state->sam_ctx, mem_ctx, &msg, "whenCreated", now); + samdb_msg_set_ldaptime(d_state->sam_ctx, mem_ctx, &msg, "whenChanged", now); + /* create the user */ + ret = samdb_add(d_state->sam_ctx, mem_ctx, &msg); + if (ret != 0) { + DEBUG(1,("Failed to create user record %s\n", msg.dn)); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + /* create user state and new policy handle */ - + mem_ctx2 = talloc_init("CreateUser(%s)", username); + if (!mem_ctx2) { + return NT_STATUS_NO_MEMORY; + } - DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR); + state = talloc_p(mem_ctx2, struct samr_account_state); + if (!state) { + return NT_STATUS_NO_MEMORY; + } + state->mem_ctx = mem_ctx2; + state->sam_ctx = d_state->sam_ctx; + state->access_mask = r->in.access_mask; + state->domain_state = d_state; + state->basedn = talloc_steal(mem_ctx, mem_ctx2, msg.dn); + state->account_sid = talloc_strdup(mem_ctx2, sidstr); + state->account_name = talloc_strdup(mem_ctx2, username); + if (!state->account_name || !state->account_sid) { + return NT_STATUS_NO_MEMORY; + } + + /* create the policy handle */ + u_handle = dcesrv_handle_new(dce_call->conn, SAMR_HANDLE_USER); + if (!u_handle) { + return NT_STATUS_NO_MEMORY; + } + + u_handle->data = state; + u_handle->destroy = samr_Account_destroy; + + /* the domain state is in use one more time */ + d_state->reference_count++; + + *r->out.acct_handle = u_handle->wire_handle; + *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */ + *r->out.rid = rid; + + return NT_STATUS_OK; } @@ -473,7 +596,7 @@ static NTSTATUS samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX * struct samr_CreateUser *r) { struct samr_CreateUser2 r2; - uint32 access_granted, rid; + uint32 access_granted; /* a simple wrapper around samr_CreateUser2 works nicely */ @@ -483,7 +606,7 @@ static NTSTATUS samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX * r2.in.access_mask = r->in.access_mask; r2.out.acct_handle = r->out.acct_handle; r2.out.access_granted = &access_granted; - r2.out.rid = &rid; + r2.out.rid = r->out.rid; return samr_CreateUser2(dce_call, mem_ctx, &r2); } @@ -533,9 +656,90 @@ static NTSTATUS samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALL samr_LookupNames */ static NTSTATUS samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct samr_LookupNames *r) + struct samr_LookupNames *r) { - DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR); + struct dcesrv_handle *h; + struct samr_domain_state *state; + int i; + NTSTATUS status = NT_STATUS_OK; + const char * const attrs[] = { "sAMAccountType", "objectSid", NULL }; + int count; + + ZERO_STRUCT(r->out.rids); + ZERO_STRUCT(r->out.types); + + DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_DOMAIN); + + state = h->data; + + if (r->in.num_names == 0) { + return NT_STATUS_OK; + } + + r->out.rids.ids = talloc_array_p(mem_ctx, uint32, r->in.num_names); + r->out.types.ids = talloc_array_p(mem_ctx, uint32, r->in.num_names); + if (!r->out.rids.ids || !r->out.types.ids) { + return NT_STATUS_NO_MEMORY; + } + r->out.rids.count = r->in.num_names; + r->out.types.count = r->in.num_names; + + for (i=0;i<r->in.num_names;i++) { + struct ldb_message **res; + struct dom_sid2 *sid; + const char *sidstr; + uint32 atype, rtype; + + r->out.rids.ids[i] = 0; + r->out.types.ids[i] = SID_NAME_UNKNOWN; + + count = samdb_search(state->sam_ctx, mem_ctx, state->basedn, &res, attrs, + "sAMAccountName=%s", r->in.names[i].name); + if (count != 1) { + status = STATUS_SOME_UNMAPPED; + continue; + } + + sidstr = samdb_result_string(res[0], "objectSid", NULL); + if (sidstr == NULL) { + status = STATUS_SOME_UNMAPPED; + continue; + } + + sid = dom_sid_parse_talloc(mem_ctx, sidstr); + if (sid == NULL) { + status = STATUS_SOME_UNMAPPED; + continue; + } + + atype = samdb_result_uint(res[0], "sAMAccountType", 0); + if (atype == 0) { + status = STATUS_SOME_UNMAPPED; + continue; + } + + switch (atype & 0xF0000000) { + case ATYPE_ACCOUNT: + rtype = SID_NAME_USER; + break; + case ATYPE_GLOBAL_GROUP: + rtype = SID_NAME_DOM_GRP; + break; + case ATYPE_LOCAL_GROUP: + rtype = SID_NAME_ALIAS; + break; + default: + DEBUG(1,("Unknown sAMAccountType 0x%08x\n", atype)); + status = STATUS_SOME_UNMAPPED; + continue; + } + + r->out.rids.ids[i] = sid->sub_auths[sid->num_auths-1]; + r->out.types.ids[i] = rtype; + } + + + return status; } @@ -703,9 +907,86 @@ static NTSTATUS samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLO samr_OpenUser */ static NTSTATUS samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct samr_OpenUser *r) + struct samr_OpenUser *r) { - DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR); + struct samr_domain_state *d_state; + struct samr_account_state *state; + struct dcesrv_handle *h; + const char *username, *sidstr; + TALLOC_CTX *mem_ctx2; + struct ldb_message **msgs; + struct dcesrv_handle *u_handle; + const char * const attrs[2] = { "sAMAccountName", NULL }; + int ret; + + ZERO_STRUCTP(r->out.acct_handle); + + DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_DOMAIN); + + d_state = h->data; + + /* form the users SID */ + sidstr = talloc_asprintf(mem_ctx, "%s-%u", d_state->domain_sid, r->in.rid); + if (!sidstr) { + return NT_STATUS_NO_MEMORY; + } + + /* search for the user record */ + ret = samdb_search(d_state->sam_ctx, + mem_ctx, d_state->basedn, &msgs, attrs, + "(&(objectSid=%s)(objectclass=user))", + sidstr); + if (ret == 0) { + return NT_STATUS_NO_SUCH_USER; + } + if (ret != 1) { + DEBUG(1,("Found %d records matching sid %s\n", ret, sidstr)); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + username = samdb_result_string(msgs[0], "sAMAccountName", NULL); + if (username == NULL) { + DEBUG(1,("sAMAccountName field missing for sid %s\n", sidstr)); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + /* create user state and new policy handle */ + mem_ctx2 = talloc_init("OpenUser(%u)", r->in.rid); + if (!mem_ctx2) { + return NT_STATUS_NO_MEMORY; + } + + state = talloc_p(mem_ctx2, struct samr_account_state); + if (!state) { + return NT_STATUS_NO_MEMORY; + } + state->mem_ctx = mem_ctx2; + state->sam_ctx = d_state->sam_ctx; + state->access_mask = r->in.access_mask; + state->domain_state = d_state; + state->basedn = talloc_steal(mem_ctx, mem_ctx2, msgs[0]->dn); + state->account_sid = talloc_strdup(mem_ctx2, sidstr); + state->account_name = talloc_strdup(mem_ctx2, username); + if (!state->account_name || !state->account_sid) { + return NT_STATUS_NO_MEMORY; + } + + /* create the policy handle */ + u_handle = dcesrv_handle_new(dce_call->conn, SAMR_HANDLE_USER); + if (!u_handle) { + return NT_STATUS_NO_MEMORY; + } + + u_handle->data = state; + u_handle->destroy = samr_Account_destroy; + + /* the domain state is in use one more time */ + d_state->reference_count++; + + *r->out.acct_handle = u_handle->wire_handle; + + return NT_STATUS_OK; + } @@ -713,9 +994,26 @@ static NTSTATUS samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *me samr_DeleteUser */ static NTSTATUS samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct samr_DeleteUser *r) + struct samr_DeleteUser *r) { - DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR); + struct dcesrv_handle *h; + struct samr_account_state *state; + int ret; + + *r->out.handle = *r->in.handle; + + DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_USER); + + state = h->data; + + ret = samdb_delete(state->sam_ctx, mem_ctx, state->basedn); + if (ret != 0) { + return NT_STATUS_UNSUCCESSFUL; + } + + ZERO_STRUCTP(r->out.handle); + + return NT_STATUS_OK; } @@ -919,7 +1217,7 @@ static NTSTATUS samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX { struct ldb_message **msgs; int ret; - char * const attrs[] = {"minPwdLength", "pwdProperties", NULL }; + const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL }; void *sam_ctx; if (r->in.name == NULL || r->in.name->name == NULL) { @@ -932,7 +1230,7 @@ static NTSTATUS samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX } ret = samdb_search(sam_ctx, - mem_ctx, &msgs, attrs, + mem_ctx, NULL, &msgs, attrs, "(&(name=%s)(objectclass=domain))", r->in.name->name); if (ret <= 0) { diff --git a/source4/rpc_server/samr/samdb.c b/source4/rpc_server/samr/samdb.c index d384c6ada7..f382dcd136 100644 --- a/source4/rpc_server/samr/samdb.c +++ b/source4/rpc_server/samr/samdb.c @@ -101,8 +101,9 @@ static void *samdb_alloc(void *context, void *ptr, size_t size) */ int samdb_search_v(void *ctx, TALLOC_CTX *mem_ctx, + const char *basedn, struct ldb_message ***res, - char * const *attrs, + const char * const *attrs, const char *format, va_list ap) { @@ -117,7 +118,7 @@ int samdb_search_v(void *ctx, ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx); - count = ldb_search(sam_ctx->ldb, NULL, LDB_SCOPE_SUBTREE, expr, attrs, res); + count = ldb_search(sam_ctx->ldb, basedn, LDB_SCOPE_SUBTREE, expr, attrs, res); free(expr); @@ -130,15 +131,16 @@ int samdb_search_v(void *ctx, */ int samdb_search(void *ctx, TALLOC_CTX *mem_ctx, + const char *basedn, struct ldb_message ***res, - char * const *attrs, + const char * const *attrs, const char *format, ...) { va_list ap; int count; va_start(ap, format); - count = samdb_search_v(ctx, mem_ctx, res, attrs, format, ap); + count = samdb_search_v(ctx, mem_ctx, basedn, res, attrs, format, ap); va_end(ap); return count; @@ -154,50 +156,52 @@ int samdb_search_free(void *ctx, ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx); return ldb_search_free(sam_ctx->ldb, res); } - /* search the sam for a single string attribute in exactly 1 record */ -char *samdb_search_string(void *ctx, - TALLOC_CTX *mem_ctx, - const char *attr_name, - const char *format, ...) +const char *samdb_search_string_v(void *ctx, + TALLOC_CTX *mem_ctx, + const char *basedn, + const char *attr_name, + const char *format, va_list ap) { - va_list ap; int count; - char * const attrs[2] = { attr_name, NULL }; + const char * const attrs[2] = { attr_name, NULL }; struct ldb_message **res = NULL; - char *str = NULL; - va_start(ap, format); - count = samdb_search_v(ctx, mem_ctx, &res, attrs, format, ap); - va_end(ap); - - if (count == 0) { - return NULL; + count = samdb_search_v(ctx, mem_ctx, basedn, &res, attrs, format, ap); + if (count > 1) { + DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n", + attr_name, format, count)); } - - /* make sure its single valued */ - if (count != 1 || - res[0]->num_elements != 1 || - res[0]->elements[0].num_values != 1 || - res[0]->elements[0].values[0].data == NULL) { - DEBUG(1,("samdb: search for %s %s not single valued\n", - attr_name, format)); + if (count != 1) { samdb_search_free(ctx, mem_ctx, res); return NULL; } - str = talloc_strndup(mem_ctx, - res[0]->elements[0].values[0].data, - res[0]->elements[0].values[0].length); + return samdb_result_string(res[0], attr_name, NULL); +} + + +/* + search the sam for a single string attribute in exactly 1 record +*/ +const char *samdb_search_string(void *ctx, + TALLOC_CTX *mem_ctx, + const char *basedn, + const char *attr_name, + const char *format, ...) +{ + va_list ap; + const char *str; - samdb_search_free(ctx, mem_ctx, res); + va_start(ap, format); + str = samdb_search_string_v(ctx, mem_ctx, basedn, attr_name, format, ap); + va_end(ap); return str; } - /* search the sam for multipe records each giving a single string attribute @@ -205,17 +209,18 @@ char *samdb_search_string(void *ctx, */ int samdb_search_string_multiple(void *ctx, TALLOC_CTX *mem_ctx, - char ***strs, + const char *basedn, + const char ***strs, const char *attr_name, const char *format, ...) { va_list ap; int count, i; - char * const attrs[2] = { attr_name, NULL }; + const char * const attrs[2] = { attr_name, NULL }; struct ldb_message **res = NULL; va_start(ap, format); - count = samdb_search_v(ctx, mem_ctx, &res, attrs, format, ap); + count = samdb_search_v(ctx, mem_ctx, basedn, &res, attrs, format, ap); va_end(ap); if (count <= 0) { @@ -224,9 +229,7 @@ int samdb_search_string_multiple(void *ctx, /* make sure its single valued */ for (i=0;i<count;i++) { - if (res[i]->num_elements != 1 || - res[i]->elements[0].num_values != 1 || - res[i]->elements[0].values[0].data == NULL) { + if (res[i]->num_elements != 1) { DEBUG(1,("samdb: search for %s %s not single valued\n", attr_name, format)); samdb_search_free(ctx, mem_ctx, res); @@ -234,21 +237,17 @@ int samdb_search_string_multiple(void *ctx, } } - *strs = talloc_array_p(mem_ctx, char *, count+1); + *strs = talloc_array_p(mem_ctx, const char *, count+1); if (! *strs) { samdb_search_free(ctx, mem_ctx, res); return -1; } for (i=0;i<count;i++) { - (*strs)[i] = talloc_strndup(mem_ctx, - res[i]->elements[0].values[0].data, - res[i]->elements[0].values[0].length); + (*strs)[i] = samdb_result_string(res[i], attr_name, NULL); } (*strs)[count] = NULL; - samdb_search_free(ctx, mem_ctx, res); - return count; } @@ -259,3 +258,220 @@ uint_t samdb_result_uint(struct ldb_message *msg, const char *attr, uint_t defau { return ldb_msg_find_uint(msg, attr, default_value); } + +/* + pull a string from a result set. +*/ +const char *samdb_result_string(struct ldb_message *msg, const char *attr, char *default_value) +{ + return ldb_msg_find_string(msg, attr, default_value); +} + + +/* + copy from a template record to a message +*/ +int samdb_copy_template(void *ctx, TALLOC_CTX *mem_ctx, + struct ldb_message *msg, const char *expression) +{ + struct ldb_message **res, *t; + int ret, i, j; + + + /* pull the template record */ + ret = samdb_search(ctx, mem_ctx, NULL, &res, NULL, expression); + if (ret != 1) { + DEBUG(1,("samdb: ERROR: template '%s' matched %d records\n", + expression, ret)); + return -1; + } + t = res[0]; + + for (i=0;i<t->num_elements;i++) { + struct ldb_message_element *el = &t->elements[i]; + /* some elements should not be copied from the template */ + if (strcasecmp(el->name, "cn") == 0 || + strcasecmp(el->name, "name") == 0 || + strcasecmp(el->name, "sAMAccountName") == 0) { + continue; + } + for (j=0;j<el->num_values;j++) { + if (strcasecmp(el->name, "objectClass") == 0 && + strcasecmp((char *)el->values[j].data, "userTemplate") == 0) { + continue; + } + samdb_msg_add_string(ctx, mem_ctx, msg, el->name, + (char *)el->values[j].data); + } + } + + return 0; +} + + +/* + allocate a new id, attempting to do it atomically + return 0 on failure, the id on success +*/ +static NTSTATUS _samdb_allocate_next_id(void *ctx, TALLOC_CTX *mem_ctx, const char *dn, + const char *attr, uint32 *id) +{ + struct samdb_context *sam_ctx = ctx; + struct ldb_message msg; + int ret; + const char *str; + struct ldb_val vals[2]; + struct ldb_message_element els[2]; + + str = samdb_search_string(ctx, mem_ctx, NULL, attr, "dn=%s", dn); + if (!str) { + DEBUG(1,("id not found at %s %s\n", dn, attr)); + return NT_STATUS_OBJECT_NAME_INVALID; + } + + *id = strtol(str, NULL, 0); + if ((*id)+1 == 0) { + /* out of IDs ! */ + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + + /* we do a delete and add as a single operation. That prevents + a race */ + ZERO_STRUCT(msg); + msg.dn = talloc_strdup(mem_ctx, dn); + if (!msg.dn) { + return NT_STATUS_NO_MEMORY; + } + msg.num_elements = 2; + msg.elements = els; + + els[0].num_values = 1; + els[0].values = &vals[0]; + els[0].flags = LDB_FLAG_MOD_DELETE; + els[0].name = talloc_strdup(mem_ctx, attr); + if (!els[0].name) { + return NT_STATUS_NO_MEMORY; + } + + els[1].num_values = 1; + els[1].values = &vals[1]; + els[1].flags = LDB_FLAG_MOD_ADD; + els[1].name = els[0].name; + + vals[0].data = talloc_asprintf(mem_ctx, "%u", *id); + if (!vals[0].data) { + return NT_STATUS_NO_MEMORY; + } + vals[0].length = strlen(vals[0].data); + + vals[1].data = talloc_asprintf(mem_ctx, "%u", (*id)+1); + if (!vals[1].data) { + return NT_STATUS_NO_MEMORY; + } + vals[1].length = strlen(vals[1].data); + + ret = ldb_modify(sam_ctx->ldb, &msg); + if (ret != 0) { + return NT_STATUS_UNEXPECTED_IO_ERROR; + } + + (*id)++; + + return NT_STATUS_OK; +} + +/* + allocate a new id, attempting to do it atomically + return 0 on failure, the id on success +*/ +NTSTATUS samdb_allocate_next_id(void *ctx, TALLOC_CTX *mem_ctx, const char *dn, const char *attr, + uint32 *id) +{ + int tries = 10; + NTSTATUS status; + + /* we need to try multiple times to cope with two account + creations at the same time */ + while (tries--) { + status = _samdb_allocate_next_id(ctx, mem_ctx, dn, attr, id); + if (!NT_STATUS_EQUAL(NT_STATUS_UNEXPECTED_IO_ERROR, status)) { + break; + } + } + + if (NT_STATUS_EQUAL(NT_STATUS_UNEXPECTED_IO_ERROR, status)) { + DEBUG(1,("Failed to increment id %s at %s\n", attr, dn)); + } + + return status; +} + + +/* + add a string element to a message +*/ +int samdb_msg_add_string(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg, + const char *attr_name, const char *str) +{ + struct samdb_context *sam_ctx = ctx; + char *s = talloc_strdup(mem_ctx, str); + char *a = talloc_strdup(mem_ctx, attr_name); + if (s == NULL || a == NULL) { + return -1; + } + ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx); + return ldb_msg_add_string(sam_ctx->ldb, msg, a, s); +} + +/* + set a string element in a message +*/ +int samdb_msg_set_string(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg, + const char *attr_name, const char *str) +{ + struct samdb_context *sam_ctx = ctx; + struct ldb_message_element *el; + + ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx); + + el = ldb_msg_find_element(msg, attr_name); + if (el) { + el->num_values = 0; + } + return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, str); +} + +/* + set a ldaptime element in a message +*/ +int samdb_msg_set_ldaptime(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg, + const char *attr_name, time_t t) +{ + char *str = ldap_timestring(mem_ctx, t); + if (!str) { + return -1; + } + return samdb_msg_set_string(ctx, mem_ctx, msg, attr_name, str); +} + +/* + add a record +*/ +int samdb_add(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_add(sam_ctx->ldb, msg); +} + +/* + delete a record +*/ +int samdb_delete(void *ctx, TALLOC_CTX *mem_ctx, const char *dn) +{ + struct samdb_context *sam_ctx = ctx; + + ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx); + return ldb_delete(sam_ctx->ldb, dn); +} |