Move the uid2sid cache to the parent winbind process
authorVolker Lendecke <vl@samba.org>
Mon, 7 Jul 2008 20:09:39 +0000 (22:09 +0200)
committerVolker Lendecke <vl@samba.org>
Tue, 12 Aug 2008 09:28:28 +0000 (11:28 +0200)
(This used to be commit 6e885aeabba2265a06b726f567cb14dde12c8ccb)

source3/winbindd/idmap_cache.c
source3/winbindd/winbindd.h
source3/winbindd/winbindd_sid.c

index f7e1d4e6d180edc3bfed246a36b956b4c2adca14..027ce3b6195387382534f34870063aa84d28aa4e 100644 (file)
@@ -521,3 +521,83 @@ done:
        return ret;
 }
 
+bool idmap_cache_find_sid2uid(const struct dom_sid *sid, uid_t *puid,
+                             bool *expired)
+{
+       fstring sidstr;
+       char *key;
+       char *value;
+       char *endptr;
+       time_t timeout;
+       uid_t uid;
+       bool ret;
+
+       key = talloc_asprintf(talloc_tos(), "IDMAP/SID2UID/%s",
+                             sid_to_fstring(sidstr, sid));
+       if (key == NULL) {
+               return false;
+       }
+       ret = gencache_get(key, &value, &timeout);
+       TALLOC_FREE(key);
+       if (!ret) {
+               return false;
+       }
+       uid = strtol(value, &endptr, 10);
+       ret = (*endptr == '\0');
+       SAFE_FREE(value);
+       if (ret) {
+               *puid = uid;
+               *expired = (timeout <= time(NULL));
+       }
+       return ret;
+}
+
+bool idmap_cache_find_uid2sid(uid_t uid, struct dom_sid *sid, bool *expired)
+{
+       char *key;
+       char *value;
+       time_t timeout;
+       bool ret;
+
+       key = talloc_asprintf(talloc_tos(), "IDMAP/UID2SID/%d", (int)uid);
+       if (key == NULL) {
+               return false;
+       }
+       ret = gencache_get(key, &value, &timeout);
+       TALLOC_FREE(key);
+       if (!ret) {
+               return false;
+       }
+       ZERO_STRUCTP(sid);
+       ret = string_to_sid(sid, value);
+       SAFE_FREE(value);
+       if (ret) {
+               *expired = (timeout <= time(NULL));
+       }
+       return ret;
+}
+
+void idmap_cache_set_sid2uid(const struct dom_sid *sid, uid_t uid)
+{
+       time_t now = time(NULL);
+       time_t timeout;
+       fstring sidstr, key, value;
+
+       if (!is_null_sid(sid)) {
+               fstr_sprintf(key, "IDMAP/SID2UID/%s",
+                            sid_to_fstring(sidstr, sid));
+               fstr_sprintf(value, "%d", (int)uid);
+               timeout = (uid == -1)
+                       ? lp_idmap_negative_cache_time()
+                       : lp_idmap_cache_time();
+               gencache_set(key, value, now + timeout);
+       }
+       if (uid != -1) {
+               fstr_sprintf(key, "IDMAP/UID2SID/%d", (int)uid);
+               sid_to_fstring(value, sid);
+               timeout = is_null_sid(sid)
+                       ? lp_idmap_negative_cache_time()
+                       : lp_idmap_cache_time();
+               gencache_set(key, value, now + timeout);
+       }
+}
index b2fe8b67e7ca58cb199fd09ee639af024a3505c1..f6f269ee6d6a3eb4732b34fb6f4cc8b202071a76 100644 (file)
@@ -379,4 +379,6 @@ enum ent_type {
 #define IS_DOMAIN_OFFLINE(x) ( lp_winbind_offline_logon() && \
                               ( get_global_winbindd_state_offline() \
                                 || !(x)->online ) )
+#define IS_DOMAIN_ONLINE(x) (!IS_DOMAIN_OFFLINE(x))
+
 #endif /* _WINBINDD_H */
index 0e8e6ca00bf9ffa50df770543fc205928a6f9827..ce34dadf4c2a69f1498823babb4162c0f77a49c8 100644 (file)
@@ -159,14 +159,19 @@ static void sid2uid_recv(void *private_data, bool success, uid_t uid)
 {
        struct winbindd_cli_state *state =
                talloc_get_type_abort(private_data, struct winbindd_cli_state);
+       struct dom_sid sid;
+
+       string_to_sid(&sid, state->request.data.sid);
 
        if (!success) {
                DEBUG(5, ("Could not convert sid %s\n",
                          state->request.data.sid));
+               idmap_cache_set_sid2uid(&sid, -1);
                request_error(state);
                return;
        }
 
+       idmap_cache_set_sid2uid(&sid, uid);
        state->response.data.uid = uid;
        request_ok(state);
 }
@@ -180,34 +185,40 @@ static void sid2uid_lookupsid_recv( void *private_data, bool success,
                talloc_get_type_abort(private_data, struct winbindd_cli_state);
        DOM_SID sid;
 
+       if (!string_to_sid(&sid, state->request.data.sid)) {
+               DEBUG(1, ("sid2uid_lookupsid_recv: Could not get convert sid "
+                         "%s from string\n", state->request.data.sid));
+               request_error(state);
+               return;
+       }
+
        if (!success) {
                DEBUG(5, ("sid2uid_lookupsid_recv Could not convert get sid type for %s\n",
                          state->request.data.sid));
-               request_error(state);
-               return;
+               goto fail;
        }
 
        if ( (type!=SID_NAME_USER) && (type!=SID_NAME_COMPUTER) ) {
                DEBUG(5,("sid2uid_lookupsid_recv: Sid %s is not a user or a computer.\n", 
                         state->request.data.sid));
-               request_error(state);
-               return;         
+               goto fail;
        }
 
-       if (!string_to_sid(&sid, state->request.data.sid)) {
-               DEBUG(1, ("sid2uid_lookupsid_recv: Could not get convert sid %s from string\n",
-                         state->request.data.sid));
-               request_error(state);
-               return;
-       }
-       
        /* always use the async interface (may block) */
        winbindd_sid2uid_async(state->mem_ctx, &sid, sid2uid_recv, state);
+       return;
+
+ fail:
+       idmap_cache_set_sid2uid(&sid, -1);
+       request_error(state);
+       return;
 }
 
 void winbindd_sid_to_uid(struct winbindd_cli_state *state)
 {
        DOM_SID sid;
+       uid_t uid;
+       bool expired;
 
        /* Ensure null termination */
        state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
@@ -222,10 +233,29 @@ void winbindd_sid_to_uid(struct winbindd_cli_state *state)
                return;
        }
 
+       if (idmap_cache_find_sid2uid(&sid, &uid, &expired)) {
+               DEBUG(10, ("idmap_cache_find_sid2uid found %d%s\n",
+                          (int)uid, expired ? " (expired)": ""));
+               if (expired && IS_DOMAIN_ONLINE(find_our_domain())) {
+                       DEBUG(10, ("revalidating expired entry\n"));
+                       goto backend;
+               }
+               if (uid == -1) {
+                       DEBUG(10, ("Returning negative cache entry\n"));
+                       request_error(state);
+                       return;
+               }
+               DEBUG(10, ("Returning positive cache entry\n"));
+               state->response.data.uid = uid;
+               request_ok(state);
+               return;
+       }
+
        /* Validate the SID as a user.  Hopefully this will hit cache.
           Needed to prevent DoS by exhausting the uid allocation
           range from random SIDs. */
 
+ backend:
        winbindd_lookupsid_async( state->mem_ctx, &sid, sid2uid_lookupsid_recv, state );
 }
 
@@ -411,30 +441,59 @@ void winbindd_set_hwm(struct winbindd_cli_state *state)
 
 /* Convert a uid to a sid */
 
-static void uid2sid_recv(void *private_data, bool success, const char *sid)
+static void uid2sid_recv(void *private_data, bool success, const char *sidstr)
 {
        struct winbindd_cli_state *state =
                (struct winbindd_cli_state *)private_data;
+       struct dom_sid sid;
 
-       if (success) {
-               DEBUG(10,("uid2sid: uid %lu has sid %s\n",
-                         (unsigned long)(state->request.data.uid), sid));
-               fstrcpy(state->response.data.sid.sid, sid);
-               state->response.data.sid.type = SID_NAME_USER;
-               request_ok(state);
+       if (!success || !string_to_sid(&sid, sidstr)) {
+               ZERO_STRUCT(sid);
+               idmap_cache_set_sid2uid(&sid, state->request.data.uid);
+               request_error(state);
                return;
        }
 
-       request_error(state);
+       DEBUG(10,("uid2sid: uid %lu has sid %s\n",
+                 (unsigned long)(state->request.data.uid), sidstr));
+
+       idmap_cache_set_sid2uid(&sid, state->request.data.uid);
+       fstrcpy(state->response.data.sid.sid, sidstr);
+       state->response.data.sid.type = SID_NAME_USER;
+       request_ok(state);
        return;
 }
 
 void winbindd_uid_to_sid(struct winbindd_cli_state *state)
 {
+       struct dom_sid sid;
+       bool expired;
+
        DEBUG(3, ("[%5lu]: uid to sid %lu\n", (unsigned long)state->pid, 
                  (unsigned long)state->request.data.uid));
 
+       if (idmap_cache_find_uid2sid(state->request.data.uid, &sid,
+                                    &expired)) {
+               DEBUG(10, ("idmap_cache_find_uid2sid found %d%s\n",
+                          (int)state->request.data.uid,
+                          expired ? " (expired)": ""));
+               if (expired && IS_DOMAIN_ONLINE(find_our_domain())) {
+                       DEBUG(10, ("revalidating expired entry\n"));
+                       goto backend;
+               }
+               if (is_null_sid(&sid)) {
+                       DEBUG(10, ("Returning negative cache entry\n"));
+                       request_error(state);
+                       return;
+               }
+               DEBUG(10, ("Returning positive cache entry\n"));
+               sid_to_fstring(state->response.data.sid.sid, &sid);
+               request_ok(state);
+               return;
+       }
+
        /* always go via the async interface (may block) */
+ backend:
        winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, uid2sid_recv, state);
 }