diff options
author | Volker Lendecke <vl@samba.org> | 2010-04-11 13:48:55 +0200 |
---|---|---|
committer | Volker Lendecke <vl@samba.org> | 2010-04-11 22:59:45 +0200 |
commit | 7ba21a339c591f546085fcd8cfbf77c25dcfe11e (patch) | |
tree | 820374b6846312c183fe4d27f418bc4a977108d1 /source3/auth/token_util.c | |
parent | 4d84dab21d1b13bbfe3d8f90c88e41ad3c524bb5 (diff) | |
download | samba-7ba21a339c591f546085fcd8cfbf77c25dcfe11e.tar.gz samba-7ba21a339c591f546085fcd8cfbf77c25dcfe11e.tar.bz2 samba-7ba21a339c591f546085fcd8cfbf77c25dcfe11e.zip |
s3: Move user_in_group() and create_token_from_username() to token_utils.c
Goal is to be able to call check_sam_security from winbind
Diffstat (limited to 'source3/auth/token_util.c')
-rw-r--r-- | source3/auth/token_util.c | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/source3/auth/token_util.c b/source3/auth/token_util.c index 86dae3a08c..cf4a54143d 100644 --- a/source3/auth/token_util.c +++ b/source3/auth/token_util.c @@ -558,4 +558,319 @@ void debug_unix_user_token(int dbg_class, int dbg_lev, uid_t uid, gid_t gid, (long int)groups[i])); } +/* + * Create an artificial NT token given just a username. (Initially intended + * for force user) + * + * We go through lookup_name() to avoid problems we had with 'winbind use + * default domain'. + * + * We have 3 cases: + * + * unmapped unix users: Go directly to nss to find the user's group. + * + * A passdb user: The list of groups is provided by pdb_enum_group_memberships. + * + * If the user is provided by winbind, the primary gid is set to "domain + * users" of the user's domain. For an explanation why this is necessary, see + * the thread starting at + * http://lists.samba.org/archive/samba-technical/2006-January/044803.html. + */ + +NTSTATUS create_token_from_username(TALLOC_CTX *mem_ctx, const char *username, + bool is_guest, + uid_t *uid, gid_t *gid, + char **found_username, + struct nt_user_token **token) +{ + NTSTATUS result = NT_STATUS_NO_SUCH_USER; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + DOM_SID user_sid; + enum lsa_SidType type; + gid_t *gids; + DOM_SID *group_sids; + DOM_SID unix_group_sid; + size_t num_group_sids; + size_t num_gids; + size_t i; + + if (!lookup_name_smbconf(tmp_ctx, username, LOOKUP_NAME_ALL, + NULL, NULL, &user_sid, &type)) { + DEBUG(1, ("lookup_name_smbconf for %s failed\n", username)); + goto done; + } + + if (type != SID_NAME_USER) { + DEBUG(1, ("%s is a %s, not a user\n", username, + sid_type_lookup(type))); + goto done; + } + + if (sid_check_is_in_our_domain(&user_sid)) { + bool ret; + + /* This is a passdb user, so ask passdb */ + + struct samu *sam_acct = NULL; + + if ( !(sam_acct = samu_new( tmp_ctx )) ) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + become_root(); + ret = pdb_getsampwsid(sam_acct, &user_sid); + unbecome_root(); + + if (!ret) { + DEBUG(1, ("pdb_getsampwsid(%s) for user %s failed\n", + sid_string_dbg(&user_sid), username)); + DEBUGADD(1, ("Fall back to unix user %s\n", username)); + goto unix_user; + } + + result = pdb_enum_group_memberships(tmp_ctx, sam_acct, + &group_sids, &gids, + &num_group_sids); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(1, ("enum_group_memberships failed for %s (%s): " + "%s\n", username, sid_string_dbg(&user_sid), + nt_errstr(result))); + DEBUGADD(1, ("Fall back to unix user %s\n", username)); + goto unix_user; + } + + /* see the smb_panic() in pdb_default_enum_group_memberships */ + SMB_ASSERT(num_group_sids > 0); + + *gid = gids[0]; + + /* Ensure we're returning the found_username on the right context. */ + *found_username = talloc_strdup(mem_ctx, + pdb_get_username(sam_acct)); + + /* + * If the SID from lookup_name() was the guest sid, passdb knows + * about the mapping of guest sid to lp_guestaccount() + * username and will return the unix_pw info for a guest + * user. Use it if it's there, else lookup the *uid details + * using getpwnam_alloc(). See bug #6291 for details. JRA. + */ + + /* We must always assign the *uid. */ + if (sam_acct->unix_pw == NULL) { + struct passwd *pwd = getpwnam_alloc(sam_acct, *found_username ); + if (!pwd) { + DEBUG(10, ("getpwnam_alloc failed for %s\n", + *found_username)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + result = samu_set_unix(sam_acct, pwd ); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10, ("samu_set_unix failed for %s\n", + *found_username)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + } + *uid = sam_acct->unix_pw->pw_uid; + + } else if (sid_check_is_in_unix_users(&user_sid)) { + + /* This is a unix user not in passdb. We need to ask nss + * directly, without consulting passdb */ + + struct passwd *pass; + + /* + * This goto target is used as a fallback for the passdb + * case. The concrete bug report is when passdb gave us an + * unmapped gid. + */ + + unix_user: + + if (!sid_to_uid(&user_sid, uid)) { + DEBUG(1, ("unix_user case, sid_to_uid for %s (%s) failed\n", + username, sid_string_dbg(&user_sid))); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + uid_to_unix_users_sid(*uid, &user_sid); + + pass = getpwuid_alloc(tmp_ctx, *uid); + if (pass == NULL) { + DEBUG(1, ("getpwuid(%u) for user %s failed\n", + (unsigned int)*uid, username)); + goto done; + } + + if (!getgroups_unix_user(tmp_ctx, username, pass->pw_gid, + &gids, &num_group_sids)) { + DEBUG(1, ("getgroups_unix_user for user %s failed\n", + username)); + goto done; + } + + if (num_group_sids) { + group_sids = TALLOC_ARRAY(tmp_ctx, DOM_SID, num_group_sids); + if (group_sids == NULL) { + DEBUG(1, ("TALLOC_ARRAY failed\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + } else { + group_sids = NULL; + } + + for (i=0; i<num_group_sids; i++) { + gid_to_sid(&group_sids[i], gids[i]); + } + + /* In getgroups_unix_user we always set the primary gid */ + SMB_ASSERT(num_group_sids > 0); + + *gid = gids[0]; + + /* Ensure we're returning the found_username on the right context. */ + *found_username = talloc_strdup(mem_ctx, pass->pw_name); + } else { + + /* This user is from winbind, force the primary gid to the + * user's "domain users" group. Under certain circumstances + * (user comes from NT4), this might be a loss of + * information. But we can not rely on winbind getting the + * correct info. AD might prohibit winbind looking up that + * information. */ + + uint32 dummy; + + /* We must always assign the *uid. */ + if (!sid_to_uid(&user_sid, uid)) { + DEBUG(1, ("winbindd case, sid_to_uid for %s (%s) failed\n", + username, sid_string_dbg(&user_sid))); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + num_group_sids = 1; + group_sids = TALLOC_ARRAY(tmp_ctx, DOM_SID, num_group_sids); + if (group_sids == NULL) { + DEBUG(1, ("TALLOC_ARRAY failed\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + + sid_copy(&group_sids[0], &user_sid); + sid_split_rid(&group_sids[0], &dummy); + sid_append_rid(&group_sids[0], DOMAIN_GROUP_RID_USERS); + + if (!sid_to_gid(&group_sids[0], gid)) { + DEBUG(1, ("sid_to_gid(%s) failed\n", + sid_string_dbg(&group_sids[0]))); + goto done; + } + + gids = gid; + + /* Ensure we're returning the found_username on the right context. */ + *found_username = talloc_strdup(mem_ctx, username); + } + + /* Add the "Unix Group" SID for each gid to catch mapped groups + and their Unix equivalent. This is to solve the backwards + compatibility problem of 'valid users = +ntadmin' where + ntadmin has been paired with "Domain Admins" in the group + mapping table. Otherwise smb.conf would need to be changed + to 'valid user = "Domain Admins"'. --jerry */ + + num_gids = num_group_sids; + for ( i=0; i<num_gids; i++ ) { + gid_t high, low; + + /* don't pickup anything managed by Winbind */ + + if ( lp_idmap_gid(&low, &high) && (gids[i] >= low) && (gids[i] <= high) ) + continue; + + if ( !gid_to_unix_groups_sid( gids[i], &unix_group_sid ) ) { + DEBUG(1,("create_token_from_username: Failed to create SID " + "for gid %u!\n", (unsigned int)gids[i])); + continue; + } + result = add_sid_to_array_unique(tmp_ctx, &unix_group_sid, + &group_sids, &num_group_sids); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + } + + /* Ensure we're creating the nt_token on the right context. */ + *token = create_local_nt_token(mem_ctx, &user_sid, + is_guest, num_group_sids, group_sids); + + if ((*token == NULL) || (*found_username == NULL)) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + result = NT_STATUS_OK; + done: + TALLOC_FREE(tmp_ctx); + return result; +} + +/*************************************************************************** + Build upon create_token_from_username: + + Expensive helper function to figure out whether a user given its name is + member of a particular group. +***************************************************************************/ + +bool user_in_group_sid(const char *username, const DOM_SID *group_sid) +{ + NTSTATUS status; + uid_t uid; + gid_t gid; + char *found_username; + struct nt_user_token *token; + bool result; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + status = create_token_from_username(mem_ctx, username, False, + &uid, &gid, &found_username, + &token); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("could not create token for %s\n", username)); + TALLOC_FREE(mem_ctx); + return False; + } + + result = nt_token_check_sid(group_sid, token); + + TALLOC_FREE(mem_ctx); + return result; +} + +bool user_in_group(const char *username, const char *groupname) +{ + TALLOC_CTX *mem_ctx = talloc_stackframe(); + DOM_SID group_sid; + bool ret; + + ret = lookup_name(mem_ctx, groupname, LOOKUP_NAME_ALL, + NULL, NULL, &group_sid, NULL); + TALLOC_FREE(mem_ctx); + + if (!ret) { + DEBUG(10, ("lookup_name for (%s) failed.\n", groupname)); + return False; + } + + return user_in_group_sid(username, &group_sid); +} + /* END */ |