From 7ba21a339c591f546085fcd8cfbf77c25dcfe11e Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Sun, 11 Apr 2010 13:48:55 +0200 Subject: 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 --- source3/auth/auth_util.c | 315 ---------------------------------------------- source3/auth/token_util.c | 315 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 315 insertions(+), 315 deletions(-) diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c index b7a5aee2c5..647d20cd65 100644 --- a/source3/auth/auth_util.c +++ b/source3/auth/auth_util.c @@ -800,321 +800,6 @@ NTSTATUS create_local_token(struct auth_serversupplied_info *server_info) return status; } -/* - * 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 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= 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); -} - /*************************************************************************** Make (and fill) a server_info struct from a 'struct passwd' by conversion to a struct samu 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 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= 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 */ -- cgit