diff options
author | Simo Sorce <idra@samba.org> | 2008-02-01 14:24:31 -0500 |
---|---|---|
committer | Simo Sorce <idra@samba.org> | 2008-02-01 14:24:31 -0500 |
commit | 2fffc9a1b1fe2a1490e867bb38462e50c282d2b3 (patch) | |
tree | 428e09c9b35138db8b7ca7161c659a71aa129d29 /source3/auth | |
parent | 93a3c5b3f9927973b4ad1496f593ea147052d1e1 (diff) | |
parent | b708005a7106db26d7df689b887b419c9f2ea41c (diff) | |
download | samba-2fffc9a1b1fe2a1490e867bb38462e50c282d2b3.tar.gz samba-2fffc9a1b1fe2a1490e867bb38462e50c282d2b3.tar.bz2 samba-2fffc9a1b1fe2a1490e867bb38462e50c282d2b3.zip |
Merge branch 'v3-2-test' of ssh://git.samba.org/data/git/samba into v3-2-test
(This used to be commit 7dbfc7bdc65314466a83e8121b35c9bcb24b2631)
Diffstat (limited to 'source3/auth')
-rw-r--r-- | source3/auth/auth_domain.c | 6 | ||||
-rw-r--r-- | source3/auth/auth_server.c | 1 | ||||
-rw-r--r-- | source3/auth/auth_unix.c | 3 | ||||
-rw-r--r-- | source3/auth/auth_util.c | 44 | ||||
-rw-r--r-- | source3/auth/auth_winbind.c | 4 | ||||
-rw-r--r-- | source3/auth/pass_check.c | 149 | ||||
-rw-r--r-- | source3/auth/token_util.c | 83 |
7 files changed, 206 insertions, 84 deletions
diff --git a/source3/auth/auth_domain.c b/source3/auth/auth_domain.c index b2c87174fd..40a2985600 100644 --- a/source3/auth/auth_domain.c +++ b/source3/auth/auth_domain.c @@ -124,7 +124,7 @@ machine %s. Error was : %s.\n", dc_name, nt_errstr(result))); if (!lp_client_schannel()) { /* We need to set up a creds chain on an unauthenticated netlogon pipe. */ - uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS; + uint32 neg_flags = NETLOGON_NEG_SELECT_AUTH2_FLAGS; uint32 sec_chan_type = 0; unsigned char machine_pwd[16]; const char *account_name; @@ -270,7 +270,9 @@ static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx, &info3); if (NT_STATUS_IS_OK(nt_status)) { - (*server_info)->was_mapped |= user_info->was_mapped; + if (user_info->was_mapped) { + (*server_info)->was_mapped = user_info->was_mapped; + } if ( ! (*server_info)->guest) { /* if a real user check pam account restrictions */ diff --git a/source3/auth/auth_server.c b/source3/auth/auth_server.c index 9f90ef8ccd..095f0b9fb8 100644 --- a/source3/auth/auth_server.c +++ b/source3/auth/auth_server.c @@ -75,6 +75,7 @@ static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx) connection (tridge) */ if (!grab_server_mutex(desthost)) { + cli_shutdown(cli); return NULL; } diff --git a/source3/auth/auth_unix.c b/source3/auth/auth_unix.c index 4fca5bcbe4..58c765226d 100644 --- a/source3/auth/auth_unix.c +++ b/source3/auth/auth_unix.c @@ -92,7 +92,7 @@ static NTSTATUS check_unix_security(const struct auth_context *auth_context, struct passwd *pass = NULL; become_root(); - pass = Get_Pwnam(user_info->internal_username); + pass = Get_Pwnam_alloc(talloc_tos(), user_info->internal_username); /** @todo This call assumes a ASCII password, no charset transformation is @@ -123,6 +123,7 @@ static NTSTATUS check_unix_security(const struct auth_context *auth_context, } } + TALLOC_FREE(pass); return nt_status; } diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c index 1e33869ea9..ce47e94eb5 100644 --- a/source3/auth/auth_util.c +++ b/source3/auth/auth_util.c @@ -549,11 +549,13 @@ NTSTATUS make_server_info_sam(auth_serversupplied_info **server_info, "for gid %d!\n", gids[i])); continue; } - if (!add_sid_to_array_unique( result, &unix_group_sid, - &result->sids, &result->num_sids )) { + status = add_sid_to_array_unique(result, &unix_group_sid, + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { result->sam_account = NULL; /* Don't free on error exit. */ TALLOC_FREE(result); - return NT_STATUS_NO_MEMORY; + return status; } } @@ -895,9 +897,9 @@ NTSTATUS create_token_from_username(TALLOC_CTX *mem_ctx, const char *username, "for gid %d!\n", gids[i])); continue; } - if (!add_sid_to_array_unique(tmp_ctx, &unix_group_sid, - &group_sids, &num_group_sids )) { - result = NT_STATUS_NO_MEMORY; + result = add_sid_to_array_unique(tmp_ctx, &unix_group_sid, + &group_sids, &num_group_sids); + if (!NT_STATUS_IS_OK(result)) { goto done; } } @@ -1074,11 +1076,12 @@ NTSTATUS make_server_info_pw(auth_serversupplied_info **server_info, return NT_STATUS_NO_SUCH_USER; } - if (!add_sid_to_array_unique(result, &u_sid, - &result->sids, - &result->num_sids)) { + status = add_sid_to_array_unique(result, &u_sid, + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(result); - return NT_STATUS_NO_MEMORY; + return status; } /* For now we throw away the gids and convert via sid_to_gid @@ -1103,7 +1106,7 @@ static NTSTATUS make_new_server_info_guest(auth_serversupplied_info **server_inf struct samu *sampass = NULL; DOM_SID guest_sid; bool ret; - static const char zeros[16] = { 0, }; + char zeros[16]; if ( !(sampass = samu_new( NULL )) ) { return NT_STATUS_NO_MEMORY; @@ -1138,6 +1141,7 @@ static NTSTATUS make_new_server_info_guest(auth_serversupplied_info **server_inf /* annoying, but the Guest really does have a session key, and it is all zeros! */ + ZERO_STRUCT(zeros); (*server_info)->user_session_key = data_blob(zeros, sizeof(zeros)); (*server_info)->lm_session_key = data_blob(zeros, sizeof(zeros)); @@ -1420,10 +1424,10 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, auth_serversupplied_info **server_info, NET_USER_INFO_3 *info3) { - static const char zeros[16] = { 0, }; + char zeros[16]; NTSTATUS nt_status = NT_STATUS_OK; - char *found_username; + char *found_username = NULL; const char *nt_domain; const char *nt_username; struct samu *sam_account = NULL; @@ -1431,8 +1435,8 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, DOM_SID group_sid; bool username_was_mapped; - uid_t uid; - gid_t gid; + uid_t uid = (uid_t)-1; + gid_t gid = (gid_t)-1; auth_serversupplied_info *result; @@ -1624,7 +1628,9 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, &(info3->uni_logon_srv)); /* ensure we are never given NULL session keys */ - + + ZERO_STRUCT(zeros); + if (memcmp(info3->user_sess_key, zeros, sizeof(zeros)) == 0) { result->user_session_key = data_blob_null; } else { @@ -1731,17 +1737,17 @@ bool is_trusted_domain(const char* dom_name) return True; } else { - NSS_STATUS result; + wbcErr result; /* If winbind is around, ask it */ result = wb_is_trusted_domain(dom_name); - if (result == NSS_STATUS_SUCCESS) { + if (result == WBC_ERR_SUCCESS) { return True; } - if (result == NSS_STATUS_NOTFOUND) { + if (result == WBC_ERR_DOMAIN_NOT_FOUND) { /* winbind could not find the domain */ return False; } diff --git a/source3/auth/auth_winbind.c b/source3/auth/auth_winbind.c index 959c550524..b24aa3a75b 100644 --- a/source3/auth/auth_winbind.c +++ b/source3/auth/auth_winbind.c @@ -134,7 +134,9 @@ static NTSTATUS check_winbind_security(const struct auth_context *auth_context, } if (NT_STATUS_IS_OK(nt_status)) { - (*server_info)->was_mapped |= user_info->was_mapped; + if (user_info->was_mapped) { + (*server_info)->was_mapped = user_info->was_mapped; + } } } } else if (NT_STATUS_IS_OK(nt_status)) { diff --git a/source3/auth/pass_check.c b/source3/auth/pass_check.c index 27915bf499..813540d9fa 100644 --- a/source3/auth/pass_check.c +++ b/source3/auth/pass_check.c @@ -26,10 +26,61 @@ #define DBGC_CLASS DBGC_AUTH /* these are kept here to keep the string_combinations function simple */ -static fstring this_user; -#if !defined(WITH_PAM) -static fstring this_salt; -static fstring this_crypted; +static char *ths_user; + +static const char *get_this_user(void) +{ + if (!ths_user) { + return ""; + } + return ths_user; +} + +#if defined(WITH_PAM) || defined(OSF1_ENH_SEC) +static const char *set_this_user(const char *newuser) +{ + char *orig_user = ths_user; + ths_user = SMB_STRDUP(newuser); + SAFE_FREE(orig_user); + return ths_user; +} +#endif + +#if !defined(WITH_PAM) +static char *ths_salt; +/* This must be writable. */ +static char *get_this_salt(void) +{ + return ths_salt; +} + +/* We may be setting a modified version of the same + * string, so don't free before use. */ + +static const char *set_this_salt(const char *newsalt) +{ + char *orig_salt = ths_salt; + ths_salt = SMB_STRDUP(newsalt); + SAFE_FREE(orig_salt); + return ths_salt; +} + +static char *ths_crypted; +static const char *get_this_crypted(void) +{ + if (!ths_crypted) { + return ""; + } + return ths_crypted; +} + +static const char *set_this_crypted(const char *newcrypted) +{ + char *orig_crypted = ths_crypted; + ths_crypted = SMB_STRDUP(newcrypted); + SAFE_FREE(orig_crypted); + return ths_crypted; +} #endif #ifdef WITH_AFS @@ -113,7 +164,7 @@ static bool dfs_auth(char *user, char *password) * Assumes local passwd file is kept in sync w/ DCE RGY! */ - if (strcmp((char *)crypt(password, this_salt), this_crypted)) + if (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted())) { return (False); } @@ -492,29 +543,29 @@ core of password checking routine static NTSTATUS password_check(const char *password) { #ifdef WITH_PAM - return smb_pam_passcheck(this_user, password); + return smb_pam_passcheck(get_this_user(), password); #else bool ret; #ifdef WITH_AFS - if (afs_auth(this_user, password)) + if (afs_auth(get_this_user(), password)) return NT_STATUS_OK; #endif /* WITH_AFS */ #ifdef WITH_DFS - if (dfs_auth(this_user, password)) + if (dfs_auth(get_this_user(), password)) return NT_STATUS_OK; #endif /* WITH_DFS */ #ifdef OSF1_ENH_SEC - ret = (strcmp(osf1_bigcrypt(password, this_salt), - this_crypted) == 0); + ret = (strcmp(osf1_bigcrypt(password, get_this_salt()), + get_this_crypted()) == 0); if (!ret) { DEBUG(2, ("OSF1_ENH_SEC failed. Trying normal crypt.\n")); - ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0); + ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0); } if (ret) { return NT_STATUS_OK; @@ -525,7 +576,7 @@ static NTSTATUS password_check(const char *password) #endif /* OSF1_ENH_SEC */ #ifdef ULTRIX_AUTH - ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0); + ret = (strcmp((char *)crypt16(password, get_this_salt()), get_this_crypted()) == 0); if (ret) { return NT_STATUS_OK; } else { @@ -535,7 +586,7 @@ static NTSTATUS password_check(const char *password) #endif /* ULTRIX_AUTH */ #ifdef LINUX_BIGCRYPT - ret = (linux_bigcrypt(password, this_salt, this_crypted)); + ret = (linux_bigcrypt(password, get_this_salt(), get_this_crypted())); if (ret) { return NT_STATUS_OK; } else { @@ -552,10 +603,10 @@ static NTSTATUS password_check(const char *password) * by crypt. */ - if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0) + if (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0) return NT_STATUS_OK; else - ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0); + ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0); if (ret) { return NT_STATUS_OK; } else { @@ -564,7 +615,7 @@ static NTSTATUS password_check(const char *password) #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */ #ifdef HAVE_BIGCRYPT - ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0); + ret = (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0); if (ret) { return NT_STATUS_OK; } else { @@ -576,7 +627,7 @@ static NTSTATUS password_check(const char *password) DEBUG(1, ("Warning - no crypt available\n")); return NT_STATUS_LOGON_FAILURE; #else /* HAVE_CRYPT */ - ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0); + ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0); if (ret) { return NT_STATUS_OK; } else { @@ -621,7 +672,9 @@ NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *pas * checks below and dive straight into the PAM code. */ - fstrcpy(this_user, user); + if (set_this_user(user) == NULL) { + return NT_STATUS_NO_MEMORY; + } DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen)); @@ -638,8 +691,12 @@ NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *pas /* Copy into global for the convenience of looping code */ /* Also the place to keep the 'password' no matter what crazy struct it started in... */ - fstrcpy(this_crypted, pass->pw_passwd); - fstrcpy(this_salt, pass->pw_passwd); + if (set_this_crypted(pass->pw_passwd) == NULL) { + return NT_STATUS_NO_MEMORY; + } + if (set_this_salt(pass->pw_passwd) == NULL) { + return NT_STATUS_NO_MEMORY; + } #ifdef HAVE_GETSPNAM { @@ -652,8 +709,12 @@ NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *pas spass = getspnam(pass->pw_name); if (spass && spass->sp_pwdp) { - fstrcpy(this_crypted, spass->sp_pwdp); - fstrcpy(this_salt, spass->sp_pwdp); + if (set_this_crypted(spass->sp_pwdp) == NULL) { + return NT_STATUS_NO_MEMORY; + } + if (set_this_salt(spass->sp_pwdp) == NULL) { + return NT_STATUS_NO_MEMORY; + } } } #elif defined(IA_UINFO) @@ -671,8 +732,11 @@ NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *pas #ifdef HAVE_GETPRPWNAM { struct pr_passwd *pr_pw = getprpwnam(pass->pw_name); - if (pr_pw && pr_pw->ufld.fd_encrypt) - fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt); + if (pr_pw && pr_pw->ufld.fd_encrypt) { + if (set_this_crypted(pr_pw->ufld.fd_encrypt) == NULL) { + return NT_STATUS_NO_MEMORY; + } + } } #endif @@ -680,8 +744,11 @@ NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *pas { struct passwd_adjunct *pwret; pwret = getpwanam(s); - if (pwret && pwret->pwa_passwd) - fstrcpy(this_crypted, pwret->pwa_passwd); + if (pwret && pwret->pwa_passwd) { + if (set_this_crypted(pwret->pwa_passwd) == NULL) { + return NT_STATUS_NO_MEMORY; + } + } } #endif @@ -692,8 +759,12 @@ NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *pas user)); mypasswd = getprpwnam(user); if (mypasswd) { - fstrcpy(this_user, mypasswd->ufld.fd_name); - fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt); + if (set_this_user(mypasswd->ufld.fd_name) == NULL) { + return NT_STATUS_NO_MEMORY; + } + if (set_this_crypted(mypasswd->ufld.fd_encrypt) == NULL) { + return NT_STATUS_NO_MEMORY; + } } else { DEBUG(5, ("OSF1_ENH_SEC: No entry for user %s in protected database !\n", @@ -706,7 +777,10 @@ NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *pas { AUTHORIZATION *ap = getauthuid(pass->pw_uid); if (ap) { - fstrcpy(this_crypted, ap->a_password); + if (set_this_crypted(ap->a_password) == NULL) { + endauthent(); + return NT_STATUS_NO_MEMORY; + } endauthent(); } } @@ -715,19 +789,28 @@ NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *pas #if defined(HAVE_TRUNCATED_SALT) /* crypt on some platforms (HPUX in particular) won't work with more than 2 salt characters. */ - this_salt[2] = 0; + { + char *trunc_salt = get_this_salt(); + if (!trunc_salt || strlen(trunc_salt) < 2) { + return NT_STATUS_LOGON_FAILURE; + } + trunc_salt[2] = 0; + if (set_this_salt(trunc_salt) == NULL) { + return NT_STATUS_NO_MEMORY; + } + } #endif - if (!*this_crypted) { + if (!get_this_crypted() || !*get_this_crypted()) { if (!lp_null_passwords()) { DEBUG(2, ("Disallowing %s with null password\n", - this_user)); + get_this_user())); return NT_STATUS_LOGON_FAILURE; } if (!*password) { DEBUG(3, ("Allowing access to %s with null password\n", - this_user)); + get_this_user())); return NT_STATUS_OK; } } diff --git a/source3/auth/token_util.c b/source3/auth/token_util.c index 27c98c9581..fc93060fc6 100644 --- a/source3/auth/token_util.c +++ b/source3/auth/token_util.c @@ -77,12 +77,19 @@ bool nt_token_check_domain_rid( NT_USER_TOKEN *token, uint32 rid ) NT_USER_TOKEN *get_root_nt_token( void ) { - static NT_USER_TOKEN *token = NULL; + struct nt_user_token *token = NULL; DOM_SID u_sid, g_sid; struct passwd *pw; + void *cache_data; - if ( token ) - return token; + cache_data = memcache_lookup_talloc( + NULL, SINGLETON_CACHE_TALLOC, + data_blob_string_const("root_nt_token")); + + if (cache_data != NULL) { + return talloc_get_type_abort( + cache_data, struct nt_user_token); + } if ( !(pw = sys_getpwnam( "root" )) ) { DEBUG(0,("get_root_nt_token: getpwnam(\"root\") failed!\n")); @@ -97,6 +104,11 @@ NT_USER_TOKEN *get_root_nt_token( void ) token = create_local_nt_token(NULL, &u_sid, False, 1, &global_sid_Builtin_Administrators); + + memcache_add_talloc( + NULL, SINGLETON_CACHE_TALLOC, + data_blob_string_const("root_nt_token"), token); + return token; } @@ -128,22 +140,22 @@ NTSTATUS add_aliases(const DOM_SID *domain_sid, if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("pdb_enum_alias_memberships failed: %s\n", nt_errstr(status))); - TALLOC_FREE(tmp_ctx); - return status; + goto done; } for (i=0; i<num_aliases; i++) { DOM_SID alias_sid; sid_compose(&alias_sid, domain_sid, aliases[i]); - if (!add_sid_to_array_unique(token, &alias_sid, - &token->user_sids, - &token->num_sids)) { + status = add_sid_to_array_unique(token, &alias_sid, + &token->user_sids, + &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("add_sid_to_array failed\n")); - TALLOC_FREE(tmp_ctx); - return NT_STATUS_NO_MEMORY; + goto done; } } +done: TALLOC_FREE(tmp_ctx); return NT_STATUS_OK; } @@ -154,6 +166,7 @@ NTSTATUS add_aliases(const DOM_SID *domain_sid, static NTSTATUS add_builtin_administrators( struct nt_user_token *token ) { DOM_SID domadm; + NTSTATUS status; /* nothing to do if we aren't in a domain */ @@ -174,9 +187,11 @@ static NTSTATUS add_builtin_administrators( struct nt_user_token *token ) /* Add Administrators if the user beloongs to Domain Admins */ if ( nt_token_check_sid( &domadm, token ) ) { - if (!add_sid_to_array(token, &global_sid_Builtin_Administrators, - &token->user_sids, &token->num_sids)) { - return NT_STATUS_NO_MEMORY; + status = add_sid_to_array(token, + &global_sid_Builtin_Administrators, + &token->user_sids, &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; } } @@ -284,45 +299,55 @@ struct nt_user_token *create_local_nt_token(TALLOC_CTX *mem_ctx, DEBUG(10, ("Create local NT token for %s\n", sid_string_dbg(user_sid))); - if (!(result = TALLOC_ZERO_P(mem_ctx, NT_USER_TOKEN))) { + if (!(result = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) { DEBUG(0, ("talloc failed\n")); return NULL; } /* Add the user and primary group sid */ - if (!add_sid_to_array(result, user_sid, - &result->user_sids, &result->num_sids)) { + status = add_sid_to_array(result, user_sid, + &result->user_sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { return NULL; } /* For guest, num_groupsids may be zero. */ if (num_groupsids) { - if (!add_sid_to_array(result, &groupsids[0], - &result->user_sids, &result->num_sids)) { + status = add_sid_to_array(result, &groupsids[0], + &result->user_sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { return NULL; } } /* Add in BUILTIN sids */ - if (!add_sid_to_array(result, &global_sid_World, - &result->user_sids, &result->num_sids)) { + status = add_sid_to_array(result, &global_sid_World, + &result->user_sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { return NULL; } - if (!add_sid_to_array(result, &global_sid_Network, - &result->user_sids, &result->num_sids)) { + status = add_sid_to_array(result, &global_sid_Network, + &result->user_sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { return NULL; } if (is_guest) { - if (!add_sid_to_array(result, &global_sid_Builtin_Guests, - &result->user_sids, &result->num_sids)) { + status = add_sid_to_array(result, &global_sid_Builtin_Guests, + &result->user_sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { return NULL; } } else { - if (!add_sid_to_array(result, &global_sid_Authenticated_Users, - &result->user_sids, &result->num_sids)) { + status = add_sid_to_array(result, + &global_sid_Authenticated_Users, + &result->user_sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { return NULL; } } @@ -334,8 +359,10 @@ struct nt_user_token *create_local_nt_token(TALLOC_CTX *mem_ctx, * first group sid as primary above. */ for (i=1; i<num_groupsids; i++) { - if (!add_sid_to_array_unique(result, &groupsids[i], - &result->user_sids, &result->num_sids)) { + status = add_sid_to_array_unique(result, &groupsids[i], + &result->user_sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { return NULL; } } |