nsswitch/idmap_cache.c

説明を見る。
00001 /* 
00002    Unix SMB/CIFS implementation.
00003    ID Mapping Cache
00004 
00005    based on gencache
00006 
00007    Copyright (C) Simo Sorce             2006
00008    Copyright (C) Rafal Szczesniak       2002
00009 
00010    This program is free software; you can redistribute it and/or modify
00011    it under the terms of the GNU General Public License as published by
00012    the Free Software Foundation; either version 2 of the License, or
00013    (at your option) any later version.
00014 
00015    This program is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018    GNU General Public License for more details.
00019 
00020    You should have received a copy of the GNU General Public License
00021    along with this program; if not, write to the Free Software
00022    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/
00023 
00024 #include "includes.h"
00025 #include "winbindd.h"
00026 
00027 #define TIMEOUT_LEN 12
00028 #define IDMAP_CACHE_DATA_FMT    "%12u/%s"
00029 #define IDMAP_READ_CACHE_DATA_FMT_TEMPLATE "%%12u/%%%us"
00030 
00031 struct idmap_cache_ctx {
00032         TDB_CONTEXT *tdb;
00033 };
00034 
00035 static int idmap_cache_destructor(struct idmap_cache_ctx *cache)
00036 {
00037         int ret = 0;
00038 
00039         if (cache && cache->tdb) {
00040                 ret = tdb_close(cache->tdb);
00041                 cache->tdb = NULL;
00042         }
00043 
00044         return ret;
00045 }
00046 
00047 struct idmap_cache_ctx *idmap_cache_init(TALLOC_CTX *memctx)
00048 {
00049         struct idmap_cache_ctx *cache;
00050         char* cache_fname = NULL;
00051 
00052         cache = talloc(memctx, struct idmap_cache_ctx);
00053         if ( ! cache) {
00054                 DEBUG(0, ("Out of memory!\n"));
00055                 return NULL;
00056         }
00057 
00058         cache_fname = lock_path("idmap_cache.tdb");
00059 
00060         DEBUG(10, ("Opening cache file at %s\n", cache_fname));
00061 
00062         cache->tdb = tdb_open_log(cache_fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
00063 
00064         if (!cache->tdb) {
00065                 DEBUG(5, ("Attempt to open %s has failed.\n", cache_fname));
00066                 return NULL;
00067         }
00068 
00069         talloc_set_destructor(cache, idmap_cache_destructor);
00070 
00071         return cache;
00072 }
00073 
00074 void idmap_cache_shutdown(struct idmap_cache_ctx *cache)
00075 {
00076         talloc_free(cache);
00077 }
00078 
00079 NTSTATUS idmap_cache_build_sidkey(TALLOC_CTX *ctx, char **sidkey, const struct id_map *id)
00080 {
00081         *sidkey = talloc_asprintf(ctx, "IDMAP/SID/%s", sid_string_static(id->sid));
00082         if ( ! *sidkey) {
00083                 DEBUG(1, ("failed to build sidkey, OOM?\n"));
00084                 return NT_STATUS_NO_MEMORY;
00085         }
00086 
00087         return NT_STATUS_OK;
00088 }
00089 
00090 NTSTATUS idmap_cache_build_idkey(TALLOC_CTX *ctx, char **idkey, const struct id_map *id)
00091 {
00092         *idkey = talloc_asprintf(ctx, "IDMAP/%s/%lu",
00093                                 (id->xid.type==ID_TYPE_UID)?"UID":"GID",
00094                                 (unsigned long)id->xid.id);
00095         if ( ! *idkey) {
00096                 DEBUG(1, ("failed to build idkey, OOM?\n"));
00097                 return NT_STATUS_NO_MEMORY;
00098         }
00099 
00100         return NT_STATUS_OK;
00101 }
00102 
00103 NTSTATUS idmap_cache_set(struct idmap_cache_ctx *cache, const struct id_map *id)
00104 {
00105         NTSTATUS ret;
00106         time_t timeout = time(NULL) + lp_idmap_cache_time();
00107         TDB_DATA keybuf, databuf;
00108         char *sidkey;
00109         char *idkey;
00110         char *valstr;
00111 
00112         /* Don't cache lookups in the S-1-22-{1,2} domain */
00113         if ( (id->xid.type == ID_TYPE_UID) && 
00114              sid_check_is_in_unix_users(id->sid) )
00115         {
00116                 return NT_STATUS_OK;
00117         }
00118         if ( (id->xid.type == ID_TYPE_GID) && 
00119              sid_check_is_in_unix_groups(id->sid) )
00120         {
00121                 return NT_STATUS_OK;
00122         }
00123         
00124 
00125         ret = idmap_cache_build_sidkey(cache, &sidkey, id);
00126         if (!NT_STATUS_IS_OK(ret)) return ret;
00127 
00128         /* use sidkey as the local memory ctx */
00129         ret = idmap_cache_build_idkey(sidkey, &idkey, id);
00130         if (!NT_STATUS_IS_OK(ret)) {
00131                 goto done;
00132         }
00133 
00134         /* save SID -> ID */
00135 
00136         /* use sidkey as the local memory ctx */
00137         valstr = talloc_asprintf(sidkey, IDMAP_CACHE_DATA_FMT, (int)timeout, idkey);
00138         if (!valstr) {
00139                 DEBUG(0, ("Out of memory!\n"));
00140                 ret = NT_STATUS_NO_MEMORY;
00141                 goto done;
00142         }
00143 
00144         keybuf.dptr = sidkey;
00145         keybuf.dsize = strlen(sidkey)+1;
00146         databuf.dptr = valstr;
00147         databuf.dsize = strlen(valstr)+1;
00148         DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout ="
00149                    " %s (%d seconds %s)\n", keybuf.dptr, valstr , ctime(&timeout),
00150                    (int)(timeout - time(NULL)), 
00151                    timeout > time(NULL) ? "ahead" : "in the past"));
00152 
00153         if (tdb_store(cache->tdb, keybuf, databuf, TDB_REPLACE) != 0) {
00154                 DEBUG(3, ("Failed to store cache entry!\n"));
00155                 ret = NT_STATUS_UNSUCCESSFUL;
00156                 goto done;
00157         }
00158 
00159         /* save ID -> SID */
00160 
00161         /* use sidkey as the local memory ctx */
00162         valstr = talloc_asprintf(sidkey, IDMAP_CACHE_DATA_FMT, (int)timeout, sidkey);
00163         if (!valstr) {
00164                 DEBUG(0, ("Out of memory!\n"));
00165                 ret = NT_STATUS_NO_MEMORY;
00166                 goto done;
00167         }
00168 
00169         keybuf.dptr = idkey;
00170         keybuf.dsize = strlen(idkey)+1;
00171         databuf.dptr = valstr;
00172         databuf.dsize = strlen(valstr)+1;
00173         DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout ="
00174                    " %s (%d seconds %s)\n", keybuf.dptr, valstr, ctime(&timeout),
00175                    (int)(timeout - time(NULL)), 
00176                    timeout > time(NULL) ? "ahead" : "in the past"));
00177 
00178         if (tdb_store(cache->tdb, keybuf, databuf, TDB_REPLACE) != 0) {
00179                 DEBUG(3, ("Failed to store cache entry!\n"));
00180                 ret = NT_STATUS_UNSUCCESSFUL;
00181                 goto done;
00182         }
00183 
00184         ret = NT_STATUS_OK;
00185 
00186 done:
00187         talloc_free(sidkey);
00188         return ret;
00189 }
00190 
00191 NTSTATUS idmap_cache_del(struct idmap_cache_ctx *cache, const struct id_map *id)
00192 {
00193         NTSTATUS ret;
00194         TDB_DATA keybuf;
00195         char *sidkey = NULL;
00196         char *idkey = NULL;
00197 
00198         ret = idmap_cache_build_sidkey(cache, &sidkey, id);
00199         if (!NT_STATUS_IS_OK(ret)) return ret;
00200 
00201         ret = idmap_cache_build_idkey(cache, &idkey, id);
00202         if (!NT_STATUS_IS_OK(ret)) {
00203                 goto done;
00204         }
00205 
00206         /* delete SID */
00207 
00208         keybuf.dptr = sidkey;
00209         keybuf.dsize = strlen(sidkey)+1;
00210         DEBUG(10, ("Deleting cache entry (key = %s)\n", keybuf.dptr));
00211 
00212         if (tdb_delete(cache->tdb, keybuf) != 0) {
00213                 DEBUG(3, ("Failed to delete cache entry!\n"));
00214         }
00215 
00216         /* delete ID */
00217 
00218         keybuf.dptr = idkey;
00219         keybuf.dsize = strlen(idkey)+1;
00220         DEBUG(10, ("Deleting cache entry (key = %s)\n", keybuf.dptr));
00221 
00222         if (tdb_delete(cache->tdb, keybuf) != 0) {
00223                 DEBUG(3, ("Failed to delete cache entry!\n"));
00224         }
00225 
00226 done:
00227         talloc_free(sidkey);
00228         talloc_free(idkey);
00229         return ret;
00230 }
00231 
00232 NTSTATUS idmap_cache_set_negative_sid(struct idmap_cache_ctx *cache, const struct id_map *id)
00233 {
00234         NTSTATUS ret;
00235         time_t timeout = time(NULL) + lp_idmap_negative_cache_time();
00236         TDB_DATA keybuf, databuf;
00237         char *sidkey;
00238         char *valstr;
00239 
00240         ret = idmap_cache_build_sidkey(cache, &sidkey, id);
00241         if (!NT_STATUS_IS_OK(ret)) return ret;
00242 
00243         /* use sidkey as the local memory ctx */
00244         valstr = talloc_asprintf(sidkey, IDMAP_CACHE_DATA_FMT, (int)timeout, "IDMAP/NEGATIVE");
00245         if (!valstr) {
00246                 DEBUG(0, ("Out of memory!\n"));
00247                 ret = NT_STATUS_NO_MEMORY;
00248                 goto done;
00249         }
00250 
00251         keybuf.dptr = sidkey;
00252         keybuf.dsize = strlen(sidkey)+1;
00253         databuf.dptr = valstr;
00254         databuf.dsize = strlen(valstr)+1;
00255         DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout ="
00256                    " %s (%d seconds %s)\n", keybuf.dptr, valstr, ctime(&timeout),
00257                    (int)(timeout - time(NULL)), 
00258                    timeout > time(NULL) ? "ahead" : "in the past"));
00259 
00260         if (tdb_store(cache->tdb, keybuf, databuf, TDB_REPLACE) != 0) {
00261                 DEBUG(3, ("Failed to store cache entry!\n"));
00262                 ret = NT_STATUS_UNSUCCESSFUL;
00263                 goto done;
00264         }
00265 
00266 done:
00267         talloc_free(sidkey);
00268         return ret;
00269 }
00270 
00271 NTSTATUS idmap_cache_set_negative_id(struct idmap_cache_ctx *cache, const struct id_map *id)
00272 {
00273         NTSTATUS ret;
00274         time_t timeout = time(NULL) + lp_idmap_negative_cache_time();
00275         TDB_DATA keybuf, databuf;
00276         char *idkey;
00277         char *valstr;
00278 
00279         ret = idmap_cache_build_idkey(cache, &idkey, id);
00280         if (!NT_STATUS_IS_OK(ret)) return ret;
00281 
00282         /* use idkey as the local memory ctx */
00283         valstr = talloc_asprintf(idkey, IDMAP_CACHE_DATA_FMT, (int)timeout, "IDMAP/NEGATIVE");
00284         if (!valstr) {
00285                 DEBUG(0, ("Out of memory!\n"));
00286                 ret = NT_STATUS_NO_MEMORY;
00287                 goto done;
00288         }
00289 
00290         keybuf.dptr = idkey;
00291         keybuf.dsize = strlen(idkey)+1;
00292         databuf.dptr = valstr;
00293         databuf.dsize = strlen(valstr)+1;
00294         DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout ="
00295                    " %s (%d seconds %s)\n", keybuf.dptr, valstr, ctime(&timeout),
00296                    (int)(timeout - time(NULL)), 
00297                    timeout > time(NULL) ? "ahead" : "in the past"));
00298 
00299         if (tdb_store(cache->tdb, keybuf, databuf, TDB_REPLACE) != 0) {
00300                 DEBUG(3, ("Failed to store cache entry!\n"));
00301                 ret = NT_STATUS_UNSUCCESSFUL;
00302                 goto done;
00303         }
00304 
00305 done:
00306         talloc_free(idkey);
00307         return ret;
00308 }
00309 
00310 NTSTATUS idmap_cache_fill_map(struct id_map *id, const char *value)
00311 {
00312         char *rem;
00313 
00314         /* see if it is a sid */
00315         if ( ! strncmp("IDMAP/SID/", value, 10)) {
00316                 
00317                 if ( ! string_to_sid(id->sid, &value[10])) {
00318                         goto failed;
00319                 }
00320                 
00321                 id->status = ID_MAPPED;
00322 
00323                 return NT_STATUS_OK;
00324         }
00325 
00326         /* not a SID see if it is an UID or a GID */
00327         if ( ! strncmp("IDMAP/UID/", value, 10)) {
00328                 
00329                 /* a uid */
00330                 id->xid.type = ID_TYPE_UID;
00331                 
00332         } else if ( ! strncmp("IDMAP/GID/", value, 10)) {
00333                 
00334                 /* a gid */
00335                 id->xid.type = ID_TYPE_GID;
00336                 
00337         } else {
00338                 
00339                 /* a completely bogus value bail out */
00340                 goto failed;
00341         }
00342         
00343         id->xid.id = strtol(&value[10], &rem, 0);
00344         if (*rem != '\0') {
00345                 goto failed;
00346         }
00347 
00348         id->status = ID_MAPPED;
00349 
00350         return NT_STATUS_OK;
00351 
00352 failed:
00353         DEBUG(1, ("invalid value: %s\n", value));
00354         id->status = ID_UNKNOWN;
00355         return NT_STATUS_INTERNAL_DB_CORRUPTION;
00356 }
00357 
00358 BOOL idmap_cache_is_negative(const char *val)
00359 {
00360         if ( ! strcmp("IDMAP/NEGATIVE", val)) {
00361                 return True;
00362         }
00363         return False;
00364 }
00365 
00366 /* search the cahce for the SID an return a mapping if found *
00367  *
00368  * 4 cases are possible
00369  *
00370  * 1 map found
00371  *      in this case id->status = ID_MAPPED and NT_STATUS_OK is returned
00372  * 2 map not found
00373  *      in this case id->status = ID_UNKNOWN and NT_STATUS_NONE_MAPPED is returned
00374  * 3 negative cache found
00375  *      in this case id->status = ID_UNMAPPED and NT_STATUS_OK is returned
00376  * 4 map found but timer expired
00377  *      in this case id->status = ID_EXPIRED and NT_STATUS_SYNCHRONIZATION_REQUIRED
00378  *      is returned. In this case revalidation of the cache is needed.
00379  */
00380 
00381 NTSTATUS idmap_cache_map_sid(struct idmap_cache_ctx *cache, struct id_map *id)
00382 {
00383         NTSTATUS ret;
00384         TDB_DATA keybuf, databuf;
00385         time_t t, now;
00386         char *sidkey;
00387         char *endptr;
00388 
00389         /* make sure it is marked as unknown by default */
00390         id->status = ID_UNKNOWN;
00391         
00392         ret = idmap_cache_build_sidkey(cache, &sidkey, id);
00393         if (!NT_STATUS_IS_OK(ret)) return ret;
00394 
00395         keybuf.dptr = sidkey;
00396         keybuf.dsize = strlen(sidkey)+1;
00397 
00398         databuf = tdb_fetch(cache->tdb, keybuf);
00399 
00400         if (databuf.dptr == NULL) {
00401                 DEBUG(10, ("Cache entry with key = %s couldn't be found\n", sidkey));
00402                 ret = NT_STATUS_NONE_MAPPED;
00403                 goto done;
00404         }
00405 
00406         t = strtol(databuf.dptr, &endptr, 10);
00407 
00408         if ((endptr == NULL) || (*endptr != '/')) {
00409                 DEBUG(2, ("Invalid gencache data format: %s\n", databuf.dptr));
00410                 /* remove the entry */
00411                 tdb_delete(cache->tdb, keybuf);
00412                 ret = NT_STATUS_NONE_MAPPED;
00413                 goto done;
00414         }
00415 
00416         now = time(NULL);
00417 
00418         /* check it is not negative */
00419         if (strcmp("IDMAP/NEGATIVE", endptr+1) != 0) {
00420 
00421                 DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
00422                            "timeout = %s", t > now ? "valid" :
00423                            "expired", sidkey, endptr+1, ctime(&t)));
00424 
00425                 /* this call if successful will also mark the entry as mapped */
00426                 ret = idmap_cache_fill_map(id, endptr+1);
00427                 if ( ! NT_STATUS_IS_OK(ret)) {
00428                         /* if not valid form delete the entry */
00429                         tdb_delete(cache->tdb, keybuf);
00430                         ret = NT_STATUS_NONE_MAPPED;
00431                         goto done;
00432                 }
00433 
00434                 /* here ret == NT_STATUS_OK and id->status = ID_MAPPED */
00435 
00436                 if (t <= now) {
00437         
00438                         /* we have it, but it is expired */
00439                         id->status = ID_EXPIRED;
00440                                 
00441                         /* We're expired, set an error code
00442                            for upper layer */
00443                         ret = NT_STATUS_SYNCHRONIZATION_REQUIRED;
00444                 }
00445         } else {
00446                 if (t <= now) {
00447                         /* We're expired, delete the NEGATIVE entry and return
00448                            not mapped */
00449                         tdb_delete(cache->tdb, keybuf);
00450                         ret = NT_STATUS_NONE_MAPPED;
00451                 } else {
00452                         /* this is not mapped as it was a negative cache hit */
00453                         id->status = ID_UNMAPPED;
00454                         ret = NT_STATUS_OK;
00455                 }
00456         }
00457         
00458 done:
00459         SAFE_FREE(databuf.dptr);
00460         talloc_free(sidkey);
00461         return ret;
00462 }
00463 
00464 /* search the cahce for the ID an return a mapping if found *
00465  *
00466  * 3 cases are possible
00467  *
00468  * 1 map found
00469  *      in this case id->status = ID_MAPPED and NT_STATUS_OK is returned
00470  * 2 map not found
00471  *      in this case id->status = ID_UNKNOWN and NT_STATUS_NONE_MAPPED is returned
00472  * 3 negative cache found
00473  *      in this case id->status = ID_UNMAPPED and NT_STATUS_OK is returned
00474  * 4 map found but timer expired
00475  *      in this case id->status = ID_EXPIRED and NT_STATUS_SYNCHRONIZATION_REQUIRED
00476  *      is returned. In this case revalidation of the cache is needed.
00477  */
00478 
00479 NTSTATUS idmap_cache_map_id(struct idmap_cache_ctx *cache, struct id_map *id)
00480 {
00481         NTSTATUS ret;
00482         TDB_DATA keybuf, databuf;
00483         time_t t, now;
00484         char *idkey;
00485         char *endptr;
00486 
00487         /* make sure it is marked as unknown by default */
00488         id->status = ID_UNKNOWN;
00489         
00490         ret = idmap_cache_build_idkey(cache, &idkey, id);
00491         if (!NT_STATUS_IS_OK(ret)) return ret;
00492 
00493         keybuf.dptr = idkey;
00494         keybuf.dsize = strlen(idkey)+1;
00495 
00496         databuf = tdb_fetch(cache->tdb, keybuf);
00497 
00498         if (databuf.dptr == NULL) {
00499                 DEBUG(10, ("Cache entry with key = %s couldn't be found\n", idkey));
00500                 ret = NT_STATUS_NONE_MAPPED;
00501                 goto done;
00502         }
00503 
00504         t = strtol(databuf.dptr, &endptr, 10);
00505 
00506         if ((endptr == NULL) || (*endptr != '/')) {
00507                 DEBUG(2, ("Invalid gencache data format: %s\n", databuf.dptr));
00508                 /* remove the entry */
00509                 tdb_delete(cache->tdb, keybuf);
00510                 ret = NT_STATUS_NONE_MAPPED;
00511                 goto done;
00512         }
00513 
00514         now = time(NULL);
00515 
00516         /* check it is not negative */
00517         if (strcmp("IDMAP/NEGATIVE", endptr+1) != 0) {
00518                 
00519                 DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
00520                            "timeout = %s", t > now ? "valid" :
00521                            "expired", idkey, endptr+1, ctime(&t)));
00522 
00523                 /* this call if successful will also mark the entry as mapped */
00524                 ret = idmap_cache_fill_map(id, endptr+1);
00525                 if ( ! NT_STATUS_IS_OK(ret)) {
00526                         /* if not valid form delete the entry */
00527                         tdb_delete(cache->tdb, keybuf);
00528                         ret = NT_STATUS_NONE_MAPPED;
00529                         goto done;
00530                 }
00531 
00532                 /* here ret == NT_STATUS_OK and id->mapped = ID_MAPPED */
00533 
00534                 if (t <= now) {
00535 
00536                         /* we have it, but it is expired */
00537                         id->status = ID_EXPIRED;
00538 
00539                         /* We're expired, set an error code
00540                            for upper layer */
00541                         ret = NT_STATUS_SYNCHRONIZATION_REQUIRED;
00542                 }
00543         } else {
00544                 if (t <= now) {
00545                         /* We're expired, delete the NEGATIVE entry and return
00546                            not mapped */
00547                         tdb_delete(cache->tdb, keybuf);
00548                         ret = NT_STATUS_NONE_MAPPED;
00549                 } else {
00550                         /* this is not mapped as it was a negative cache hit */
00551                         id->status = ID_UNMAPPED;
00552                         ret = NT_STATUS_OK;
00553                 }
00554         }
00555 done:
00556         SAFE_FREE(databuf.dptr);
00557         talloc_free(idkey);
00558         return ret;
00559 }
00560 

Sambaに対してSat Aug 29 21:23:08 2009に生成されました。  doxygen 1.4.7