s4:auth Move BUILTIN group addition into session.c
authorAndrew Bartlett <abartlet@samba.org>
Tue, 13 Apr 2010 12:11:26 +0000 (22:11 +1000)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 19 Apr 2010 11:32:49 +0000 (21:32 +1000)
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

source4/auth/auth.h
source4/auth/sam.c
source4/auth/session.c
source4/dsdb/samdb/samdb.c

index 915d10397baf7e6e9143569dae2faa444c154825..39847049283f89dee42ae6f8b27e47fc8bc2acbf 100644 (file)
@@ -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,
index 201185cf4de0129365054ae6c0673b7fcda59183..a011afb5a1dc6910f019c09e205523cf313c0d33 100644 (file)
@@ -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);
-
-       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_ERR_NO_SUCH_OBJECT) {
+               talloc_free(tmp_ctx);
+               return NT_STATUS_OK;
+       }
+               
+       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;
index 03d1c91054be77d76165f7c5105d3704e0a255d2..eec2bf5444772339b16860bcdafa53aadaac23d8 100644 (file)
@@ -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;
 }
index 51e3f73a7e9c1d5e2a9871c786803394a283dda0..9e4156407e060eb2c9e67a5d37261f5b0106d1ca 100644 (file)
@@ -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++;
        }