diff options
Diffstat (limited to 'source3/nsswitch/winbindd_idmap.c')
-rw-r--r-- | source3/nsswitch/winbindd_idmap.c | 588 |
1 files changed, 461 insertions, 127 deletions
diff --git a/source3/nsswitch/winbindd_idmap.c b/source3/nsswitch/winbindd_idmap.c index 23f4b4d081..6d184fec5f 100644 --- a/source3/nsswitch/winbindd_idmap.c +++ b/source3/nsswitch/winbindd_idmap.c @@ -1,19 +1,20 @@ /* Unix SMB/CIFS implementation. - Winbind ID Mapping - Copyright (C) Tim Potter 2000 - Copyright (C) Anthony Liguori <aliguor@us.ibm.com> 2003 + Winbind daemon - user related function + + Copyright (C) Tim Potter 2000 + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -21,175 +22,508 @@ #include "winbindd.h" -static struct { - const char *name; - /* Function to create a member of the idmap_methods list */ - BOOL (*reg_meth)(struct winbindd_idmap_methods **methods); - struct winbindd_idmap_methods *methods; -} builtin_winbindd_idmap_functions[] = { - { "tdb", winbind_idmap_reg_tdb, NULL }, - { "ldap", winbind_idmap_reg_ldap, NULL }, - { NULL, NULL, NULL } -}; - -/* singleton pattern: uberlazy evaluation */ -static struct winbindd_idmap_methods *impl; - -static struct winbindd_idmap_methods *get_impl(const char *name) -{ - int i = 0; - struct winbindd_idmap_methods *ret = NULL; - - while (builtin_winbindd_idmap_functions[i].name && - strcmp(builtin_winbindd_idmap_functions[i].name, name)) { - i++; - } - - if (builtin_winbindd_idmap_functions[i].name) { - if (!builtin_winbindd_idmap_functions[i].methods) { - builtin_winbindd_idmap_functions[i].reg_meth(&builtin_winbindd_idmap_functions[i].methods); - } +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND - ret = builtin_winbindd_idmap_functions[i].methods; - } +/* High water mark keys */ - return ret; -} +#define HWM_GROUP "GROUP HWM" +#define HWM_USER "USER HWM" -/* Initialize backend */ -BOOL winbindd_idmap_init(void) +/* idmap version determines auto-conversion */ +#define IDMAP_VERSION 2 + +/* Globals */ + +static TDB_CONTEXT *idmap_tdb; + +/* Allocate either a user or group id from the pool */ + +static BOOL allocate_id(uid_t *id, BOOL isgroup) { - BOOL ret = False; + int hwm; - DEBUG(3, ("winbindd_idmap_init: using '%s' as backend\n", - lp_winbind_backend())); + /* Get current high water mark */ - if (!impl) { - impl = get_impl(lp_winbind_backend()); - if (!impl) { - DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", - lp_winbind_backend())); + if ((hwm = tdb_fetch_int32(idmap_tdb, + isgroup ? HWM_GROUP : HWM_USER)) == -1) { + return False; } - } - if (impl) { - ret = impl->init(); - } + /* Return next available uid in list */ - DEBUG(3, ("winbind_idmap_init: returning %s\n", ret ? "true" : "false")); + if ((isgroup && (hwm > server_state.gid_high)) || + (!isgroup && (hwm > server_state.uid_high))) { + DEBUG(0, ("winbind %sid range full!\n", isgroup ? "g" : "u")); + return False; + } + + if (id) { + *id = hwm; + } + + hwm++; + + /* Store new high water mark */ + + tdb_store_int32(idmap_tdb, isgroup ? HWM_GROUP : HWM_USER, hwm); - return ret; + return True; } -/* Get UID from SID */ -BOOL winbindd_idmap_get_uid_from_sid(DOM_SID *sid, uid_t *uid) +/* Get an id from a rid */ +static BOOL get_id_from_sid(DOM_SID *sid, uid_t *id, BOOL isgroup) { - BOOL ret = False; + TDB_DATA data, key; + fstring keystr; + BOOL result = False; + + /* Check if sid is present in database */ + sid_to_string(keystr, sid); + + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; + + data = tdb_fetch(idmap_tdb, key); + + if (data.dptr) { + fstring scanstr; + int the_id; + + /* Parse and return existing uid */ + fstrcpy(scanstr, isgroup ? "GID" : "UID"); + fstrcat(scanstr, " %d"); + + if (sscanf(data.dptr, scanstr, &the_id) == 1) { + /* Store uid */ + if (id) { + *id = the_id; + } - if (!impl) { - impl = get_impl(lp_winbind_backend()); - if (!impl) { - DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", - lp_winbind_backend())); + result = True; + } + + SAFE_FREE(data.dptr); + } else { + + /* Allocate a new id for this sid */ + + if (id && allocate_id(id, isgroup)) { + fstring keystr2; + + /* Store new id */ + + slprintf(keystr2, sizeof(keystr2), "%s %d", isgroup ? "GID" : "UID", *id); + + data.dptr = keystr2; + data.dsize = strlen(keystr2) + 1; + + tdb_store(idmap_tdb, key, data, TDB_REPLACE); + tdb_store(idmap_tdb, data, key, TDB_REPLACE); + + result = True; + } } - } - if (impl) { - ret = impl->get_uid_from_sid(sid, uid); - } + return result; +} - return ret; +/* Get a uid from a user sid */ +BOOL winbindd_idmap_get_uid_from_sid(DOM_SID *sid, uid_t *uid) +{ + return get_id_from_sid(sid, uid, False); } -/* Get GID from SID */ +/* Get a gid from a group sid */ BOOL winbindd_idmap_get_gid_from_sid(DOM_SID *sid, gid_t *gid) { - BOOL ret = False; + return get_id_from_sid(sid, gid, True); +} - if (!impl) { - impl = get_impl(lp_winbind_backend()); - if (!impl) { - DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", - lp_winbind_backend())); - } - } +/* Get a uid from a user rid */ +BOOL winbindd_idmap_get_uid_from_rid(const char *dom_name, uint32 rid, uid_t *uid) +{ + struct winbindd_domain *domain; + DOM_SID sid; + + if (!(domain = find_domain_from_name(dom_name))) { + return False; + } - if (impl) { - ret = impl->get_gid_from_sid(sid, gid); - } + sid_copy(&sid, &domain->sid); + sid_append_rid(&sid, rid); - return ret; + return get_id_from_sid(&sid, uid, False); } -/* Get SID from UID */ -BOOL winbindd_idmap_get_sid_from_uid(uid_t uid, DOM_SID *sid) +/* Get a gid from a group rid */ +BOOL winbindd_idmap_get_gid_from_rid(const char *dom_name, uint32 rid, gid_t *gid) +{ + struct winbindd_domain *domain; + DOM_SID sid; + + if (!(domain = find_domain_from_name(dom_name))) { + return False; + } + + sid_copy(&sid, &domain->sid); + sid_append_rid(&sid, rid); + + return get_id_from_sid(&sid, gid, True); +} + + +BOOL get_sid_from_id(int id, DOM_SID *sid, BOOL isgroup) { - BOOL ret = False; + TDB_DATA key, data; + fstring keystr; + BOOL result = False; + + slprintf(keystr, sizeof(keystr), "%s %d", isgroup ? "GID" : "UID", id); + + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; - if (!impl) { - impl = get_impl(lp_winbind_backend()); - if (!impl) { - DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", - lp_winbind_backend())); + data = tdb_fetch(idmap_tdb, key); + + if (data.dptr) { + result = string_to_sid(sid, data.dptr); + SAFE_FREE(data.dptr); } - } - if (impl) { - ret = impl->get_sid_from_uid(uid, sid); - } + return result; +} - return ret; +/* Get a sid from a uid */ +BOOL winbindd_idmap_get_sid_from_uid(uid_t uid, DOM_SID *sid) +{ + return get_sid_from_id((int)uid, sid, False); } -/* Get SID from GID */ +/* Get a sid from a gid */ BOOL winbindd_idmap_get_sid_from_gid(gid_t gid, DOM_SID *sid) { - BOOL ret = False; + return get_sid_from_id((int)gid, sid, True); +} + +/* Get a user rid from a uid */ +BOOL winbindd_idmap_get_rid_from_uid(uid_t uid, uint32 *user_rid, + struct winbindd_domain **domain) +{ + DOM_SID sid; + + if (!get_sid_from_id((int)uid, &sid, False)) { + return False; + } - if (!impl) { - impl = get_impl(lp_winbind_backend()); - } + *domain = find_domain_from_sid(&sid); + if (! *domain) return False; - if (impl) { - ret = impl->get_sid_from_gid(gid, sid); - } else { - DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", - lp_winbind_backend())); - } + sid_split_rid(&sid, user_rid); - return ret; + return True; } -/* Close backend */ -BOOL winbindd_idmap_close(void) +/* Get a group rid from a gid */ + +BOOL winbindd_idmap_get_rid_from_gid(gid_t gid, uint32 *group_rid, + struct winbindd_domain **domain) +{ + DOM_SID sid; + + if (!get_sid_from_id((int)gid, &sid, True)) { + return False; + } + + *domain = find_domain_from_sid(&sid); + if (! *domain) return False; + + sid_split_rid(&sid, group_rid); + + return True; +} + +/* convert one record to the new format */ +static int convert_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *ignored) { - BOOL ret = False; + struct winbindd_domain *domain; + char *p; + DOM_SID sid; + uint32 rid; + fstring keystr; + fstring dom_name; + TDB_DATA key2; + + p = strchr(key.dptr, '/'); + if (!p) + return 0; + + *p = 0; + fstrcpy(dom_name, key.dptr); + *p++ = '/'; + + domain = find_domain_from_name(dom_name); + if (!domain) { + /* We must delete the old record. */ + DEBUG(0,("winbindd: convert_fn : Unable to find domain %s\n", dom_name )); + DEBUG(0,("winbindd: convert_fn : deleting record %s\n", key.dptr )); + tdb_delete(idmap_tdb, key); + return 0; + } + + rid = atoi(p); + + sid_copy(&sid, &domain->sid); + sid_append_rid(&sid, rid); + + sid_to_string(keystr, &sid); + key2.dptr = keystr; + key2.dsize = strlen(keystr) + 1; + + if (tdb_store(idmap_tdb, key2, data, TDB_INSERT) != 0) { + /* not good! */ + DEBUG(0,("winbindd: convert_fn : Unable to update record %s\n", key2.dptr )); + DEBUG(0,("winbindd: convert_fn : conversion failed - idmap corrupt ?\n")); + return -1; + } + + if (tdb_store(idmap_tdb, data, key2, TDB_REPLACE) != 0) { + /* not good! */ + DEBUG(0,("winbindd: convert_fn : Unable to update record %s\n", data.dptr )); + DEBUG(0,("winbindd: convert_fn : conversion failed - idmap corrupt ?\n")); + return -1; + } + + tdb_delete(idmap_tdb, key); + + return 0; +} - if (!impl) { - impl = get_impl(lp_winbind_backend()); - } +#if 0 +/***************************************************************************** + Make a backup copy of the old idmap just to be safe.... JRA. +*****************************************************************************/ - if (impl) { - ret = impl->close(); - } else { - DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", - lp_winbind_backend())); - } +static BOOL backup_old_idmap(const char *idmap_name) +{ + pstring new_name; + int outfd = -1; + SMB_OFF_T size; + struct stat st; + + pstrcpy(new_name, idmap_name); + pstrcat(new_name, ".bak"); + + DEBUG(10,("backup_old_idmap: backing up %s to %s before upgrade.\n", + idmap_name, new_name )); + + if (tdb_lockall(idmap_tdb) == -1) { + DEBUG(10,("backup_old_idmap: failed to lock %s. Error %s\n", + idmap_name, tdb_errorstr(idmap_tdb) )); + return False; + } + if ((outfd = open(new_name, O_CREAT|O_EXCL|O_RDWR, 0600)) == -1) { + DEBUG(10,("backup_old_idmap: failed to open %s. Error %s\n", + new_name, strerror(errno) )); + goto fail; + } + + if (fstat(idmap_tdb->fd, &st) == -1) { + DEBUG(10,("backup_old_idmap: failed to fstat %s. Error %s\n", + idmap_name, strerror(errno) )); + goto fail; + } + + size = (SMB_OFF_T)st.st_size; + + if (transfer_file(idmap_tdb->fd, outfd, size) != size ) { + DEBUG(10,("backup_old_idmap: failed to copy %s. Error %s\n", + idmap_name, strerror(errno) )); + goto fail; + } + + if (close(outfd) == -1) { + DEBUG(10,("backup_old_idmap: failed to close %s. Error %s\n", + idmap_name, strerror(errno) )); + outfd = -1; + goto fail; + } + tdb_unlockall(idmap_tdb); + return True; + +fail: + + if (outfd != -1) + close(outfd); + tdb_unlockall(idmap_tdb); + return False; +} +#endif + +/***************************************************************************** + Convert the idmap database from an older version. +*****************************************************************************/ - return ret; +static BOOL idmap_convert(const char *idmap_name) +{ + int32 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION"); + BOOL bigendianheader = (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False; + + if (vers == IDMAP_VERSION) + return True; + +#if 0 + /* Make a backup copy before doing anything else.... */ + if (!backup_old_idmap(idmap_name)) + return False; +#endif + + if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) { + /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */ + /* + * high and low records were created on a + * big endian machine and will need byte-reversing. + */ + + int32 wm; + + wm = tdb_fetch_int32(idmap_tdb, HWM_USER); + + if (wm != -1) { + wm = IREV(wm); + } else + wm = server_state.uid_low; + + if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) { + DEBUG(0, ("idmap_convert: Unable to byteswap user hwm in idmap database\n")); + return False; + } + + wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP); + if (wm != -1) { + wm = IREV(wm); + } else + wm = server_state.gid_low; + + if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) { + DEBUG(0, ("idmap_convert: Unable to byteswap group hwm in idmap database\n")); + return False; + } + } + + /* the old format stored as DOMAIN/rid - now we store the SID direct */ + tdb_traverse(idmap_tdb, convert_fn, NULL); + + if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == -1) { + DEBUG(0, ("idmap_convert: Unable to byteswap group hwm in idmap database\n")); + return False; + } + + return True; } -/* Dump backend status */ +/***************************************************************************** + Initialise idmap database. +*****************************************************************************/ + +BOOL winbindd_idmap_init(void) +{ + /* Open tdb cache */ + + if (!(idmap_tdb = tdb_open_log(lock_path("winbindd_idmap.tdb"), 0, + TDB_DEFAULT, O_RDWR | O_CREAT, 0600))) { + DEBUG(0, ("winbindd_idmap_init: Unable to open idmap database\n")); + return False; + } + + /* possibly convert from an earlier version */ + if (!idmap_convert(lock_path("winbindd_idmap.tdb"))) { + DEBUG(0, ("winbindd_idmap_init: Unable to open idmap database\n")); + return False; + } + + /* Create high water marks for group and user id */ + + if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) { + if (tdb_store_int32(idmap_tdb, HWM_USER, server_state.uid_low) == -1) { + DEBUG(0, ("winbindd_idmap_init: Unable to initialise user hwm in idmap database\n")); + return False; + } + } + + if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) { + if (tdb_store_int32(idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) { + DEBUG(0, ("winbindd_idmap_init: Unable to initialise group hwm in idmap database\n")); + return False; + } + } + + return True; +} + +BOOL winbindd_idmap_close(void) +{ + if (idmap_tdb) + return (tdb_close(idmap_tdb) == 0); + return True; +} + +/* Dump status information to log file. Display different stuff based on + the debug level: + + Debug Level Information Displayed + ================================================================= + 0 Percentage of [ug]id range allocated + 0 High water marks (next allocated ids) +*/ + +#define DUMP_INFO 0 + void winbindd_idmap_status(void) { - if (!impl) { - impl = get_impl(lp_winbind_backend()); - } + int user_hwm, group_hwm; + + DEBUG(0, ("winbindd idmap status:\n")); + + /* Get current high water marks */ + + if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) { + DEBUG(DUMP_INFO, ("\tCould not get userid high water mark!\n")); + } + + if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) { + DEBUG(DUMP_INFO, ("\tCould not get groupid high water mark!\n")); + } + + /* Display next ids to allocate */ + + if (user_hwm != -1) { + DEBUG(DUMP_INFO, ("\tNext userid to allocate is %d\n", user_hwm)); + } + + if (group_hwm != -1) { + DEBUG(DUMP_INFO, ("\tNext groupid to allocate is %d\n", group_hwm)); + } + + /* Display percentage of id range already allocated. */ + + if (user_hwm != -1) { + int num_users = user_hwm - server_state.uid_low; + int total_users = server_state.uid_high - server_state.uid_low; + + DEBUG(DUMP_INFO, ("\tUser id range is %d%% full (%d of %d)\n", + num_users * 100 / total_users, num_users, + total_users)); + } + + if (group_hwm != -1) { + int num_groups = group_hwm - server_state.gid_low; + int total_groups = server_state.gid_high - server_state.gid_low; + + DEBUG(DUMP_INFO, ("\tGroup id range is %d%% full (%d of %d)\n", + num_groups * 100 / total_groups, num_groups, + total_groups)); + } - if (impl) { - impl->status(); - } else { - DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", - lp_winbind_backend())); - } + /* Display complete mapping of users and groups to rids */ } |