diff options
-rw-r--r-- | source3/winbindd/idmap_cache.c | 80 | ||||
-rw-r--r-- | source3/winbindd/winbindd.h | 2 | ||||
-rw-r--r-- | source3/winbindd/winbindd_sid.c | 97 |
3 files changed, 160 insertions, 19 deletions
diff --git a/source3/winbindd/idmap_cache.c b/source3/winbindd/idmap_cache.c index f7e1d4e6d1..027ce3b619 100644 --- a/source3/winbindd/idmap_cache.c +++ b/source3/winbindd/idmap_cache.c @@ -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); + } +} diff --git a/source3/winbindd/winbindd.h b/source3/winbindd/winbindd.h index b2fe8b67e7..f6f269ee6d 100644 --- a/source3/winbindd/winbindd.h +++ b/source3/winbindd/winbindd.h @@ -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 */ diff --git a/source3/winbindd/winbindd_sid.c b/source3/winbindd/winbindd_sid.c index 0e8e6ca00b..ce34dadf4c 100644 --- a/source3/winbindd/winbindd_sid.c +++ b/source3/winbindd/winbindd_sid.c @@ -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); } |