summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2010-04-13 22:11:26 +1000
committerAndrew Bartlett <abartlet@samba.org>2010-05-20 17:39:09 +1000
commit5f9024c8a4350792e67e1d8dbe8e45ff5732bd66 (patch)
tree37bd039a17555bfa92231cfe63a352743df9a2d5 /source4
parent564b4c7443b256e002b7ac173d4c5e8870980de3 (diff)
downloadsamba-5f9024c8a4350792e67e1d8dbe8e45ff5732bd66.tar.gz
samba-5f9024c8a4350792e67e1d8dbe8e45ff5732bd66.tar.bz2
samba-5f9024c8a4350792e67e1d8dbe8e45ff5732bd66.zip
s4:auth Move BUILTIN group addition into session.c
The group list in the PAC does not include 'enterprise DCs' and BUILTIN groups, so we should generate it on each server, not in the list we pass around in the PAC or SamLogon reply. Andrew Bartlett
Diffstat (limited to 'source4')
-rw-r--r--source4/auth/auth.h8
-rw-r--r--source4/auth/sam.c182
-rw-r--r--source4/auth/session.c147
-rw-r--r--source4/dsdb/samdb/samdb.c11
4 files changed, 264 insertions, 84 deletions
diff --git a/source4/auth/auth.h b/source4/auth/auth.h
index 915d10397b..3984704928 100644
--- a/source4/auth/auth.h
+++ b/source4/auth/auth.h
@@ -244,13 +244,11 @@ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
const char *name_for_logs,
bool allow_domain_trust,
bool password_change);
-struct auth_session_info *system_session(struct loadparm_context *lp_ctx);
NTSTATUS authsam_expand_nested_groups(struct ldb_context *sam_ctx,
- const struct dom_sid *sid,
- const bool only_childs,
- TALLOC_CTX *res_sids_ctx,
- struct dom_sid ***res_sids,
+ struct ldb_val *dn_val, const bool only_childs, const char *filter,
+ TALLOC_CTX *res_sids_ctx, struct dom_sid ***res_sids,
unsigned int *num_res_sids);
+struct auth_session_info *system_session(struct loadparm_context *lp_ctx);
NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
const char *netbios_name,
const char *domain_name,
diff --git a/source4/auth/sam.c b/source4/auth/sam.c
index 201185cf4d..244ebc732e 100644
--- a/source4/auth/sam.c
+++ b/source4/auth/sam.c
@@ -294,72 +294,94 @@ static bool sids_contains_sid(const struct dom_sid **sids,
* "false".
* The "only_childs" flag is particularly useful if you have a user SID and
* want to include all his groups (referenced with "memberOf") without his SID
- * itself.
+ * itself, or considering if that SID matches the filter
*
* At the beginning "res_sids" should reference to a NULL pointer.
*/
-_PUBLIC_ NTSTATUS authsam_expand_nested_groups(struct ldb_context *sam_ctx,
- const struct dom_sid *sid, const bool only_childs,
- TALLOC_CTX *res_sids_ctx, struct dom_sid ***res_sids,
- unsigned int *num_res_sids)
+NTSTATUS authsam_expand_nested_groups(struct ldb_context *sam_ctx,
+ struct ldb_val *dn_val, const bool only_childs, const char *filter,
+ TALLOC_CTX *res_sids_ctx, struct dom_sid ***res_sids,
+ unsigned int *num_res_sids)
{
const char * const attrs[] = { "memberOf", NULL };
unsigned int i;
int ret;
bool already_there;
- struct ldb_dn *tmp_dn;
- struct dom_sid *tmp_sid;
+ struct ldb_dn *dn;
+ struct dom_sid *sid;
TALLOC_CTX *tmp_ctx;
- struct ldb_message **res;
+ struct ldb_result *res;
NTSTATUS status;
+ const struct ldb_val *v;
+ const struct ldb_message_element *el;
+ enum ndr_err_code ndr_err;
if (*res_sids == NULL) {
*num_res_sids = 0;
}
- if (sid == NULL) {
- return NT_STATUS_OK;
+ tmp_ctx = talloc_new(res_sids_ctx);
+
+ sid = talloc(tmp_ctx, struct dom_sid);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sid, tmp_ctx);
+
+ dn = ldb_dn_from_ldb_val(tmp_ctx, sam_ctx, dn_val);
+ if (dn == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
+ v = ldb_dn_get_extended_component(dn, "SID");
+ ndr_err = ndr_pull_struct_blob(v, sid, NULL, sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* This is an O(n^2) linear search */
already_there = sids_contains_sid((const struct dom_sid**) *res_sids,
- *num_res_sids, sid);
+ *num_res_sids, sid);
if (already_there) {
return NT_STATUS_OK;
}
- if (!only_childs) {
- tmp_sid = dom_sid_dup(res_sids_ctx, sid);
- NT_STATUS_HAVE_NO_MEMORY(tmp_sid);
- *res_sids = talloc_realloc(res_sids_ctx, *res_sids,
- struct dom_sid *, *num_res_sids + 1);
- NT_STATUS_HAVE_NO_MEMORY(*res_sids);
- (*res_sids)[*num_res_sids] = tmp_sid;
- ++(*num_res_sids);
+ if (only_childs) {
+ ret = dsdb_search(sam_ctx, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, NULL);
+ } else {
+ ret = dsdb_search(sam_ctx, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, "%s", filter);
}
- tmp_ctx = talloc_new(res_sids_ctx);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
- ret = gendb_search(sam_ctx, tmp_ctx, NULL, &res, attrs,
- "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
- if (ret != 1) {
+ if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- if (res[0]->num_elements == 0) {
- talloc_free(res);
+ /* We may get back 0 results, if the SID didn't match the filter - such as it wasn't a domain group, for example */
+ if (res->count != 1) {
talloc_free(tmp_ctx);
return NT_STATUS_OK;
}
- for (i = 0; i < res[0]->elements[0].num_values; i++) {
- tmp_dn = ldb_dn_from_ldb_val(tmp_ctx, sam_ctx,
- &res[0]->elements[0].values[i]);
- tmp_sid = samdb_search_dom_sid(sam_ctx, tmp_ctx, tmp_dn,
- "objectSid", NULL);
+ /* We only apply this test once we know the SID matches the filter */
+ if (!only_childs) {
+ *res_sids = talloc_realloc(res_sids_ctx, *res_sids,
+ struct dom_sid *, *num_res_sids + 1);
+ NT_STATUS_HAVE_NO_MEMORY(*res_sids);
+ (*res_sids)[*num_res_sids] = talloc_steal(*res_sids, sid);
+ ++(*num_res_sids);
+ }
- status = authsam_expand_nested_groups(sam_ctx, tmp_sid,
- false, res_sids_ctx, res_sids, num_res_sids);
+ el = ldb_msg_find_element(res->msgs[0], "memberOf");
+
+ for (i = 0; el && i < el->num_values; i++) {
+ status = authsam_expand_nested_groups(sam_ctx, &el->values[i],
+ false, filter, res_sids_ctx, res_sids, num_res_sids);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(res);
talloc_free(tmp_ctx);
@@ -385,72 +407,87 @@ _PUBLIC_ NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx,
{
NTSTATUS status;
struct auth_serversupplied_info *server_info;
- const char *str;
- struct dom_sid *tmp_sid;
+ const char *str, *filter;
/* SIDs for the account and his primary group */
struct dom_sid *account_sid;
struct dom_sid *primary_group_sid;
+ const char *primary_group_string;
+ const char *primary_group_dn;
+ DATA_BLOB primary_group_blob;
/* SID structures for the expanded group memberships */
- struct dom_sid **groupSIDs = NULL, **groupSIDs_2 = NULL;
- unsigned int num_groupSIDs = 0, num_groupSIDs_2 = 0, i;
- uint32_t userAccountControl;
+ struct dom_sid **groupSIDs = NULL;
+ unsigned int num_groupSIDs = 0, i;
+ struct dom_sid *domain_sid;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message_element *el;
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
NT_STATUS_HAVE_NO_MEMORY(server_info);
+ tmp_ctx = talloc_new(server_info);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(server_info, server_info);
+
account_sid = samdb_result_dom_sid(server_info, msg, "objectSid");
NT_STATUS_HAVE_NO_MEMORY_AND_FREE(account_sid, server_info);
- primary_group_sid = dom_sid_add_rid(server_info,
- samdb_domain_sid(sam_ctx),
- samdb_result_uint(msg, "primaryGroupID", ~0));
- NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_sid, server_info);
-
- /* Expands the primary group */
- status = authsam_expand_nested_groups(sam_ctx, primary_group_sid, false,
- server_info, &groupSIDs, &num_groupSIDs);
+ status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(server_info);
return status;
}
- /* Expands the additional groups */
- status = authsam_expand_nested_groups(sam_ctx, account_sid, true,
- server_info, &groupSIDs_2, &num_groupSIDs_2);
+ primary_group_sid = dom_sid_add_rid(server_info,
+ domain_sid,
+ samdb_result_uint(msg, "primaryGroupID", ~0));
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_sid, server_info);
+
+ /* Filter out builtin groups from this token. We will search
+ * for builtin groups later, and not include them in the PAC
+ * on SamLogon validation info */
+ filter = talloc_asprintf(tmp_ctx, "(&(objectClass=group)(!(groupType:1.2.840.113556.1.4.803:=%u))(groupType:1.2.840.113556.1.4.803:=%u))", GROUP_TYPE_BUILTIN_LOCAL_GROUP, GROUP_TYPE_SECURITY_ENABLED);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(filter, server_info);
+
+ primary_group_string = dom_sid_string(tmp_ctx, primary_group_sid);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_string, server_info);
+
+ primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_dn, server_info);
+
+ primary_group_blob = data_blob_string_const(primary_group_dn);
+
+ /* Expands the primary group - this function takes in
+ * memberOf-like values, so we fake one up with the
+ * <SID=S-...> format of DN and then let it expand
+ * them, as long as they meet the filter - so only
+ * domain groups, not builtin groups
+ *
+ * The primary group is still treated specially, so we set the
+ * 'only childs' flag to true
+ */
+ status = authsam_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
+ server_info, &groupSIDs, &num_groupSIDs);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(server_info);
return status;
}
- /* Merge the two expanded structures (groupSIDs, groupSIDs_2) */
- for (i = 0; i < num_groupSIDs_2; i++)
- if (!sids_contains_sid((const struct dom_sid **) groupSIDs,
- num_groupSIDs, groupSIDs_2[i])) {
- tmp_sid = dom_sid_dup(server_info, groupSIDs_2[i]);
- NT_STATUS_HAVE_NO_MEMORY_AND_FREE(tmp_sid, server_info);
- groupSIDs = talloc_realloc(server_info, groupSIDs,
- struct dom_sid *, num_groupSIDs + 1);
- NT_STATUS_HAVE_NO_MEMORY_AND_FREE(groupSIDs,
- server_info);
- groupSIDs[num_groupSIDs] = tmp_sid;
- ++num_groupSIDs;
+ /* Expands the additional groups */
+ el = ldb_msg_find_element(msg, "memberOf");
+ for (i = 0; el && i < el->num_values; i++) {
+ /* This function takes in memberOf values and expands
+ * them, as long as they meet the filter - so only
+ * domain groups, not builtin groups */
+ status = authsam_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
+ server_info, &groupSIDs, &num_groupSIDs);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(server_info);
+ return status;
}
- talloc_free(groupSIDs_2);
+ }
server_info->account_sid = account_sid;
server_info->primary_group_sid = primary_group_sid;
- /* DCs also get SID_NT_ENTERPRISE_DCS */
- userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
- if (userAccountControl & UF_SERVER_TRUST_ACCOUNT) {
- groupSIDs = talloc_realloc(server_info, groupSIDs, struct dom_sid *,
- num_groupSIDs+1);
- NT_STATUS_HAVE_NO_MEMORY_AND_FREE(groupSIDs, server_info);
- groupSIDs[num_groupSIDs] = dom_sid_parse_talloc(groupSIDs, SID_NT_ENTERPRISE_DCS);
- NT_STATUS_HAVE_NO_MEMORY_AND_FREE(groupSIDs[num_groupSIDs], server_info);
- num_groupSIDs++;
- }
-
server_info->domain_groups = groupSIDs;
server_info->n_domain_groups = num_groupSIDs;
@@ -523,6 +560,7 @@ _PUBLIC_ NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx,
server_info->authenticated = true;
+ talloc_free(tmp_ctx);
*_server_info = server_info;
return NT_STATUS_OK;
diff --git a/source4/auth/session.c b/source4/auth/session.c
index 03d1c91054..7817195727 100644
--- a/source4/auth/session.c
+++ b/source4/auth/session.c
@@ -49,9 +49,71 @@ _PUBLIC_ NTSTATUS auth_generate_session_info(TALLOC_CTX *mem_ctx,
{
struct auth_session_info *session_info;
NTSTATUS nt_status;
+ unsigned int i, num_groupSIDs = 0;
+ const char *account_sid_string;
+ const char *account_sid_dn;
+ DATA_BLOB account_sid_blob;
+ const char *primary_group_string;
+ const char *primary_group_dn;
+ DATA_BLOB primary_group_blob;
- session_info = talloc(mem_ctx, struct auth_session_info);
- NT_STATUS_HAVE_NO_MEMORY(session_info);
+ const char *filter;
+
+ struct dom_sid **groupSIDs = NULL;
+ const struct dom_sid *dom_sid;
+ bool is_enterprise_dc = false;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ if (!auth_context->sam_ctx) {
+ DEBUG(0, ("No SAM available, cannot determine local groups\n"));
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* For now, we don't have trusted domains, so we do a very
+ * simple check to see that the user's SID is in *this*
+ * domain, and then trust the user account control. When we
+ * get trusted domains, we should check it's a trusted domain
+ * in this forest. This elaborate check is to try and avoid a
+ * nasty security bug if we forget about this later... */
+
+ if (server_info->acct_flags & ACB_SVRTRUST) {
+ dom_sid = samdb_domain_sid(auth_context->sam_ctx);
+ if (dom_sid) {
+ if (dom_sid_in_domain(dom_sid, server_info->account_sid)) {
+ is_enterprise_dc = true;
+ } else {
+ DEBUG(2, ("DC %s is not in our domain. "
+ "It will not have Enterprise Domain Controllers membership on this server",
+ server_info->account_name));
+ }
+ } else {
+ DEBUG(2, ("Could not obtain local domain SID, "
+ "so can not determine if DC %s is a DC of this domain. "
+ "It will not have Enterprise Domain Controllers membership",
+ server_info->account_name));
+ }
+ }
+
+ groupSIDs = talloc_array(tmp_ctx, struct dom_sid *, server_info->n_domain_groups);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(groupSIDs, tmp_ctx);
+ if (!groupSIDs) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ num_groupSIDs = server_info->n_domain_groups;
+
+ for (i=0; i < server_info->n_domain_groups; i++) {
+ groupSIDs[i] = server_info->domain_groups[i];
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u))",
+ GROUP_TYPE_BUILTIN_LOCAL_GROUP);
+
+ session_info = talloc(tmp_ctx, struct auth_session_info);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(session_info, tmp_ctx);
session_info->server_info = talloc_reference(session_info, server_info);
@@ -59,19 +121,94 @@ _PUBLIC_ NTSTATUS auth_generate_session_info(TALLOC_CTX *mem_ctx,
* key from the auth subsystem */
session_info->session_key = server_info->user_session_key;
+ /* Search for each group in the token */
+
+ /* Expands the account SID - this function takes in
+ * memberOf-like values, so we fake one up with the
+ * <SID=S-...> format of DN and then let it expand
+ * them, as long as they meet the filter - so only
+ * builtin groups
+ *
+ * We already have the primary group in the token, so set
+ * 'only childs' flag to true
+ */
+ account_sid_string = dom_sid_string(tmp_ctx, server_info->account_sid);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(account_sid_string, server_info);
+
+ account_sid_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", account_sid_string);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(account_sid_dn, server_info);
+
+ account_sid_blob = data_blob_string_const(account_sid_dn);
+
+ nt_status = authsam_expand_nested_groups(auth_context->sam_ctx, &account_sid_blob, true, filter,
+ tmp_ctx, &groupSIDs, &num_groupSIDs);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ /* Expands the primary group - this function takes in
+ * memberOf-like values, so we fake one up with the
+ * <SID=S-...> format of DN and then let it expand
+ * them, as long as they meet the filter - so only
+ * builtin groups
+ *
+ * We already have the primary group in the token, so set
+ * 'only childs' flag to true
+ */
+ primary_group_string = dom_sid_string(tmp_ctx, server_info->primary_group_sid);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_string, server_info);
+
+ primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_dn, server_info);
+
+ primary_group_blob = data_blob_string_const(primary_group_dn);
+
+ nt_status = authsam_expand_nested_groups(auth_context->sam_ctx, &primary_group_blob, true, filter,
+ tmp_ctx, &groupSIDs, &num_groupSIDs);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ for (i = 0; i < server_info->n_domain_groups; i++) {
+ const char *group_string;
+ const char *group_dn;
+ DATA_BLOB group_blob;
+ group_string = dom_sid_string(tmp_ctx, server_info->domain_groups[i]);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(group_string, server_info);
+
+ group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", group_string);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(group_dn, server_info);
+
+ group_blob = data_blob_string_const(group_dn);
+
+ /* This function takes in memberOf values and expands
+ * them, as long as they meet the filter - so only
+ * builtin groups */
+ nt_status = authsam_expand_nested_groups(auth_context->sam_ctx, &group_blob, true, filter,
+ tmp_ctx, &groupSIDs, &num_groupSIDs);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+ }
+
nt_status = security_token_create(session_info,
auth_context->event_ctx,
auth_context->lp_ctx,
server_info->account_sid,
server_info->primary_group_sid,
- server_info->n_domain_groups,
- server_info->domain_groups,
+ num_groupSIDs,
+ groupSIDs,
server_info->authenticated,
+ is_enterprise_dc,
&session_info->security_token);
- NT_STATUS_NOT_OK_RETURN(nt_status);
+ NT_STATUS_NOT_OK_RETURN_AND_FREE(nt_status, tmp_ctx);
session_info->credentials = NULL;
+ talloc_steal(mem_ctx, session_info);
*_session_info = session_info;
return NT_STATUS_OK;
}
diff --git a/source4/dsdb/samdb/samdb.c b/source4/dsdb/samdb/samdb.c
index 51e3f73a7e..9e4156407e 100644
--- a/source4/dsdb/samdb/samdb.c
+++ b/source4/dsdb/samdb/samdb.c
@@ -147,6 +147,7 @@ NTSTATUS security_token_create(TALLOC_CTX *mem_ctx,
unsigned int n_groupSIDs,
struct dom_sid **groupSIDs,
bool is_authenticated,
+ bool is_dc,
struct security_token **token)
{
struct security_token *ptoken;
@@ -156,7 +157,7 @@ NTSTATUS security_token_create(TALLOC_CTX *mem_ctx,
ptoken = security_token_initialise(mem_ctx);
NT_STATUS_HAVE_NO_MEMORY(ptoken);
- ptoken->sids = talloc_array(ptoken, struct dom_sid *, n_groupSIDs + 5);
+ ptoken->sids = talloc_array(ptoken, struct dom_sid *, n_groupSIDs + 6);
NT_STATUS_HAVE_NO_MEMORY(ptoken->sids);
ptoken->user_sid = talloc_reference(ptoken, user_sid);
@@ -178,7 +179,13 @@ NTSTATUS security_token_create(TALLOC_CTX *mem_ctx,
ptoken->num_sids = 4;
if (is_authenticated) {
- ptoken->sids[4] = dom_sid_parse_talloc(ptoken->sids, SID_NT_AUTHENTICATED_USERS);
+ ptoken->sids[ptoken->num_sids] = dom_sid_parse_talloc(ptoken->sids, SID_NT_AUTHENTICATED_USERS);
+ NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[4]);
+ ptoken->num_sids++;
+ }
+
+ if (is_dc) {
+ ptoken->sids[ptoken->num_sids] = dom_sid_parse_talloc(ptoken->sids, SID_NT_ENTERPRISE_DCS);
NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[4]);
ptoken->num_sids++;
}