From 0ed08d9398d0ee8d9fdbc0f387415adc32ba675e Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 8 May 2004 00:02:31 +0000 Subject: r578: initial server side implementation of samr_CreateUser(), samr_CreateUser2(), samr_LookupNames(), samr_OpenUser(), and samr_DeleteUser() this uses a user template in the SAM db, of objectclass "userTemplate" and dn CN=TemplateUser,CN=Templates,$BASEDN. Using a template allows an admin to add any default user attributes that they might want to the user template and all new users will receive those attributes. (This used to be commit 10b6e0011b5952c98432dc2d4b2058ac89a9cc2d) --- source4/rpc_server/samr/dcesrv_samr.c | 410 +++++++++++++++++++++++++++++----- source4/rpc_server/samr/samdb.c | 302 +++++++++++++++++++++---- 2 files changed, 613 insertions(+), 99 deletions(-) (limited to 'source4/rpc_server/samr') 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;iin.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;inum_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;ielements[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;inum_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;jnum_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); +} -- cgit