s3-netapi: Fix Coverity #670: FORWARD_NULL.
[ira/wip.git] / source3 / lib / netapi / user.c
index ae8d2ecd899f471f984132dcf4ea82e68891337a..e23587d6aa4b6f826569dfaed6a3b41e8f0aa175 100644 (file)
@@ -46,7 +46,7 @@ static void convert_USER_INFO_X_to_samr_user_info21(struct USER_INFO_X *infoX,
                fields_present |= SAMR_FIELD_ACCOUNT_NAME;
        }
        if (infoX->usriX_password) {
-               fields_present |= SAMR_FIELD_PASSWORD;
+               fields_present |= SAMR_FIELD_NT_PASSWORD_PRESENT;
        }
        if (infoX->usriX_flags) {
                fields_present |= SAMR_FIELD_ACCT_FLAGS;
@@ -91,35 +91,41 @@ static void convert_USER_INFO_X_to_samr_user_info21(struct USER_INFO_X *infoX,
        unix_to_nt_time_abs(&password_age, infoX->usriX_password_age);
 
        /* TODO: infoX->usriX_priv */
-       init_samr_user_info21(info21,
-                             0,
-                             0,
-                             0,
-                             0,
-                             0,
-                             password_age,
-                             infoX->usriX_name,
-                             infoX->usriX_full_name,
-                             infoX->usriX_home_dir,
-                             infoX->usriX_home_dir_drive,
-                             infoX->usriX_script_path,
-                             infoX->usriX_profile,
-                             infoX->usriX_comment,
-                             infoX->usriX_workstations,
-                             infoX->usriX_usr_comment,
-                             &zero_parameters,
-                             0,
-                             infoX->usriX_primary_group_id,
-                             infoX->usriX_flags,
-                             fields_present,
-                             zero_logon_hours,
-                             0,
-                             0,
-                             infoX->usriX_country_code,
-                             0,
-                             0,
-                             0,
-                             0);
+
+       info21->last_logon              = 0;
+       info21->last_logoff             = 0;
+       info21->last_password_change    = 0;
+       info21->acct_expiry             = 0;
+       info21->allow_password_change   = 0;
+       info21->force_password_change   = 0;
+       info21->account_name.string     = infoX->usriX_name;
+       info21->full_name.string        = infoX->usriX_full_name;
+       info21->home_directory.string   = infoX->usriX_home_dir;
+       info21->home_drive.string       = infoX->usriX_home_dir_drive;
+       info21->logon_script.string     = infoX->usriX_script_path;
+       info21->profile_path.string     = infoX->usriX_profile;
+       info21->description.string      = infoX->usriX_comment;
+       info21->workstations.string     = infoX->usriX_workstations;
+       info21->comment.string          = infoX->usriX_usr_comment;
+       info21->parameters              = zero_parameters;
+       info21->lm_owf_password         = zero_parameters;
+       info21->nt_owf_password         = zero_parameters;
+       info21->unknown3.string         = NULL;
+       info21->buf_count               = 0;
+       info21->buffer                  = NULL;
+       info21->rid                     = infoX->usriX_user_id;
+       info21->primary_gid             = infoX->usriX_primary_group_id;
+       info21->acct_flags              = infoX->usriX_flags;
+       info21->fields_present          = fields_present;
+       info21->logon_hours             = zero_logon_hours;
+       info21->bad_password_count      = infoX->usriX_bad_pw_count;
+       info21->logon_count             = infoX->usriX_num_logons;
+       info21->country_code            = infoX->usriX_country_code;
+       info21->code_page               = infoX->usriX_code_page;
+       info21->lm_password_set         = 0;
+       info21->nt_password_set         = 0;
+       info21->password_expired        = infoX->usriX_password_expired;
+       info21->unknown4                = 0;
 }
 
 /****************************************************************
@@ -132,6 +138,7 @@ static NTSTATUS construct_USER_INFO_X(uint32_t level,
        struct USER_INFO_0 *u0 = NULL;
        struct USER_INFO_1 *u1 = NULL;
        struct USER_INFO_2 *u2 = NULL;
+       struct USER_INFO_3 *u3 = NULL;
        struct USER_INFO_1003 *u1003 = NULL;
        struct USER_INFO_1006 *u1006 = NULL;
        struct USER_INFO_1007 *u1007 = NULL;
@@ -193,6 +200,37 @@ static NTSTATUS construct_USER_INFO_X(uint32_t level,
                        uX->usriX_country_code  = u2->usri2_country_code;
                        uX->usriX_code_page     = u2->usri2_code_page;
                        break;
+               case 3:
+                       u3 = (struct USER_INFO_3 *)buffer;
+                       uX->usriX_name          = u3->usri3_name;
+                       uX->usriX_password_age  = u3->usri3_password_age;
+                       uX->usriX_priv          = u3->usri3_priv;
+                       uX->usriX_home_dir      = u3->usri3_home_dir;
+                       uX->usriX_comment       = u3->usri3_comment;
+                       uX->usriX_flags         = u3->usri3_flags;
+                       uX->usriX_script_path   = u3->usri3_script_path;
+                       uX->usriX_auth_flags    = u3->usri3_auth_flags;
+                       uX->usriX_full_name     = u3->usri3_full_name;
+                       uX->usriX_usr_comment   = u3->usri3_usr_comment;
+                       uX->usriX_parms         = u3->usri3_parms;
+                       uX->usriX_workstations  = u3->usri3_workstations;
+                       uX->usriX_last_logon    = u3->usri3_last_logon;
+                       uX->usriX_last_logoff   = u3->usri3_last_logoff;
+                       uX->usriX_acct_expires  = u3->usri3_acct_expires;
+                       uX->usriX_max_storage   = u3->usri3_max_storage;
+                       uX->usriX_units_per_week= u3->usri3_units_per_week;
+                       uX->usriX_logon_hours   = u3->usri3_logon_hours;
+                       uX->usriX_bad_pw_count  = u3->usri3_bad_pw_count;
+                       uX->usriX_num_logons    = u3->usri3_num_logons;
+                       uX->usriX_logon_server  = u3->usri3_logon_server;
+                       uX->usriX_country_code  = u3->usri3_country_code;
+                       uX->usriX_code_page     = u3->usri3_code_page;
+                       uX->usriX_user_id       = u3->usri3_user_id;
+                       uX->usriX_primary_group_id = u3->usri3_primary_group_id;
+                       uX->usriX_profile       = u3->usri3_profile;
+                       uX->usriX_home_dir_drive = u3->usri3_home_dir_drive;
+                       uX->usriX_password_expired = u3->usri3_password_expired;
+                       break;
                case 1003:
                        u1003 = (struct USER_INFO_1003 *)buffer;
                        uX->usriX_password      = u1003->usri1003_password;
@@ -237,7 +275,6 @@ static NTSTATUS construct_USER_INFO_X(uint32_t level,
                        u1053 = (struct USER_INFO_1053 *)buffer;
                        uX->usriX_home_dir_drive = u1053->usri1053_home_dir_drive;
                        break;
-               case 3:
                case 4:
                default:
                        return NT_STATUS_INVALID_INFO_CLASS;
@@ -312,11 +349,10 @@ static NTSTATUS set_user_info_USER_INFO_X(TALLOC_CTX *ctx,
 WERROR NetUserAdd_r(struct libnetapi_ctx *ctx,
                    struct NetUserAdd *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
-       POLICY_HND connect_handle, domain_handle, user_handle;
+       struct policy_handle connect_handle, domain_handle, user_handle;
        struct lsa_String lsa_account_name;
        struct dom_sid2 *domain_sid = NULL;
        union samr_UserInfo *user_info = NULL;
@@ -346,7 +382,6 @@ WERROR NetUserAdd_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_open_pipe(ctx, r->in.server_name,
                                   &ndr_table_samr.syntax_id,
-                                  &cli,
                                   &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
@@ -360,7 +395,7 @@ WERROR NetUserAdd_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 |
                                          SAMR_DOMAIN_ACCESS_CREATE_USER |
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
@@ -415,7 +450,7 @@ WERROR NetUserAdd_r(struct libnetapi_ctx *ctx,
        uX.usriX_flags |= ACB_NORMAL;
 
        status = set_user_info_USER_INFO_X(ctx, pipe_cli,
-                                          &cli->user_session_key,
+                                          &pipe_cli->auth->user_session_key,
                                           &user_handle,
                                           &uX);
        if (!NT_STATUS_IS_OK(status)) {
@@ -431,10 +466,6 @@ WERROR NetUserAdd_r(struct libnetapi_ctx *ctx,
                               &user_handle);
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
        if (is_valid_policy_hnd(&user_handle)) {
                rpccli_samr_Close(pipe_cli, ctx, &user_handle);
        }
@@ -462,11 +493,10 @@ WERROR NetUserAdd_l(struct libnetapi_ctx *ctx,
 WERROR NetUserDel_r(struct libnetapi_ctx *ctx,
                    struct NetUserDel *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
-       POLICY_HND connect_handle, builtin_handle, domain_handle, user_handle;
+       struct policy_handle connect_handle, builtin_handle, domain_handle, user_handle;
        struct lsa_String lsa_account_name;
        struct samr_Ids user_rids, name_types;
        struct dom_sid2 *domain_sid = NULL;
@@ -479,7 +509,6 @@ WERROR NetUserDel_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_open_pipe(ctx, r->in.server_name,
                                   &ndr_table_samr.syntax_id,
-                                  &cli,
                                   &pipe_cli);
 
        if (!W_ERROR_IS_OK(werr)) {
@@ -488,7 +517,7 @@ WERROR NetUserDel_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
                                          &domain_handle,
@@ -522,7 +551,7 @@ WERROR NetUserDel_r(struct libnetapi_ctx *ctx,
 
        status = rpccli_samr_OpenUser(pipe_cli, ctx,
                                      &domain_handle,
-                                     STD_RIGHT_DELETE_ACCESS,
+                                     SEC_STD_DELETE,
                                      user_rids.ids[0],
                                      &user_handle);
        if (!NT_STATUS_IS_OK(status)) {
@@ -550,10 +579,6 @@ WERROR NetUserDel_r(struct libnetapi_ctx *ctx,
        werr = WERR_OK;
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
        if (is_valid_policy_hnd(&user_handle)) {
                rpccli_samr_Close(pipe_cli, ctx, &user_handle);
        }
@@ -745,7 +770,7 @@ static uint32_t samr_acb_flags_to_netapi_flags(uint32_t acb)
 {
        uint32_t fl = UF_SCRIPT; /* god knows why */
 
-       fl |= ads_acb2uf(acb);
+       fl |= ds_acb2uf(acb);
 
        return fl;
 }
@@ -1151,7 +1176,6 @@ static NTSTATUS libnetapi_samr_lookup_user_map_USER_INFO(TALLOC_CTX *mem_ctx,
 WERROR NetUserEnum_r(struct libnetapi_ctx *ctx,
                     struct NetUserEnum *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        struct policy_handle connect_handle;
        struct dom_sid2 *domain_sid = NULL;
@@ -1192,7 +1216,6 @@ WERROR NetUserEnum_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_open_pipe(ctx, r->in.server_name,
                                   &ndr_table_samr.syntax_id,
-                                  &cli,
                                   &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
@@ -1200,7 +1223,7 @@ WERROR NetUserEnum_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli,
                                                  SAMR_ACCESS_ENUM_DOMAINS |
-                                                 SAMR_ACCESS_OPEN_DOMAIN,
+                                                 SAMR_ACCESS_LOOKUP_DOMAIN,
                                                  SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT |
                                                  SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS,
                                                  &connect_handle,
@@ -1211,7 +1234,7 @@ WERROR NetUserEnum_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 |
                                          SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS |
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
@@ -1273,10 +1296,6 @@ WERROR NetUserEnum_r(struct libnetapi_ctx *ctx,
        }
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
        /* if last query */
        if (NT_STATUS_IS_OK(status) ||
            NT_STATUS_IS_ERR(status)) {
@@ -1455,10 +1474,10 @@ static WERROR convert_samr_dispinfo_to_NET_DISPLAY(TALLOC_CTX *mem_ctx,
                                                                          entries_read,
                                                                          buffer);
                default:
-                       return WERR_UNKNOWN_LEVEL;
+                       break;
        }
 
-       return WERR_OK;
+       return WERR_UNKNOWN_LEVEL;
 }
 
 /****************************************************************
@@ -1467,7 +1486,6 @@ static WERROR convert_samr_dispinfo_to_NET_DISPLAY(TALLOC_CTX *mem_ctx,
 WERROR NetQueryDisplayInformation_r(struct libnetapi_ctx *ctx,
                                    struct NetQueryDisplayInformation *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        struct policy_handle connect_handle;
        struct dom_sid2 *domain_sid = NULL;
@@ -1479,6 +1497,9 @@ WERROR NetQueryDisplayInformation_r(struct libnetapi_ctx *ctx,
 
        NTSTATUS status = NT_STATUS_OK;
        WERROR werr;
+       WERROR werr_tmp;
+
+       *r->out.entries_read = 0;
 
        ZERO_STRUCT(connect_handle);
        ZERO_STRUCT(domain_handle);
@@ -1494,7 +1515,6 @@ WERROR NetQueryDisplayInformation_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_open_pipe(ctx, r->in.server_name,
                                   &ndr_table_samr.syntax_id,
-                                  &cli,
                                   &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
@@ -1502,7 +1522,7 @@ WERROR NetQueryDisplayInformation_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 |
                                          SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS |
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
@@ -1523,20 +1543,19 @@ WERROR NetQueryDisplayInformation_r(struct libnetapi_ctx *ctx,
                                               &total_size,
                                               &returned_size,
                                               &info);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
+       werr = ntstatus_to_werror(status);
+       if (NT_STATUS_IS_ERR(status)) {
                goto done;
        }
 
-       werr = convert_samr_dispinfo_to_NET_DISPLAY(ctx, &info,
-                                                   r->in.level,
-                                                   r->out.entries_read,
-                                                   r->out.buffer);
- done:
-       if (!cli) {
-               return werr;
+       werr_tmp = convert_samr_dispinfo_to_NET_DISPLAY(ctx, &info,
+                                                       r->in.level,
+                                                       r->out.entries_read,
+                                                       r->out.buffer);
+       if (!W_ERROR_IS_OK(werr_tmp)) {
+               werr = werr_tmp;
        }
-
+ done:
        /* if last query */
        if (NT_STATUS_IS_OK(status) ||
            NT_STATUS_IS_ERR(status)) {
@@ -1585,7 +1604,6 @@ WERROR NetUserChangePassword_l(struct libnetapi_ctx *ctx,
 WERROR NetUserGetInfo_r(struct libnetapi_ctx *ctx,
                        struct NetUserGetInfo *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
@@ -1623,7 +1641,6 @@ WERROR NetUserGetInfo_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_open_pipe(ctx, r->in.server_name,
                                   &ndr_table_samr.syntax_id,
-                                  &cli,
                                   &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
@@ -1631,7 +1648,7 @@ WERROR NetUserGetInfo_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
                                          &domain_handle,
@@ -1642,7 +1659,7 @@ WERROR NetUserGetInfo_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli,
                                                  SAMR_ACCESS_ENUM_DOMAINS |
-                                                 SAMR_ACCESS_OPEN_DOMAIN,
+                                                 SAMR_ACCESS_LOOKUP_DOMAIN,
                                                  SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT |
                                                  SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS,
                                                  &connect_handle,
@@ -1679,10 +1696,6 @@ WERROR NetUserGetInfo_r(struct libnetapi_ctx *ctx,
        }
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
        if (is_valid_policy_hnd(&user_handle)) {
                rpccli_samr_Close(pipe_cli, ctx, &user_handle);
        }
@@ -1710,7 +1723,6 @@ WERROR NetUserGetInfo_l(struct libnetapi_ctx *ctx,
 WERROR NetUserSetInfo_r(struct libnetapi_ctx *ctx,
                        struct NetUserSetInfo *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
@@ -1734,6 +1746,8 @@ WERROR NetUserSetInfo_r(struct libnetapi_ctx *ctx,
 
        switch (r->in.level) {
                case 0:
+                       user_mask = SAMR_USER_ACCESS_SET_ATTRIBUTES;
+                       break;
                case 1003:
                        user_mask = SAMR_USER_ACCESS_SET_PASSWORD;
                        break;
@@ -1753,9 +1767,17 @@ WERROR NetUserSetInfo_r(struct libnetapi_ctx *ctx,
                        user_mask = SAMR_USER_ACCESS_SET_ATTRIBUTES |
                                    SAMR_USER_ACCESS_GET_GROUPS;
                        break;
+               case 3:
+                       user_mask = STD_RIGHT_READ_CONTROL_ACCESS |
+                                   STD_RIGHT_WRITE_DAC_ACCESS |
+                                   SAMR_USER_ACCESS_GET_GROUPS |
+                                   SAMR_USER_ACCESS_SET_PASSWORD |
+                                   SAMR_USER_ACCESS_SET_ATTRIBUTES |
+                                   SAMR_USER_ACCESS_GET_ATTRIBUTES |
+                                   SAMR_USER_ACCESS_SET_LOC_COM;
+                       break;
                case 1:
                case 2:
-               case 3:
                case 4:
                case 21:
                case 22:
@@ -1773,7 +1795,6 @@ WERROR NetUserSetInfo_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_open_pipe(ctx, r->in.server_name,
                                   &ndr_table_samr.syntax_id,
-                                  &cli,
                                   &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
@@ -1781,7 +1802,7 @@ WERROR NetUserSetInfo_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 |
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
@@ -1793,7 +1814,7 @@ WERROR NetUserSetInfo_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli,
                                                  SAMR_ACCESS_ENUM_DOMAINS |
-                                                 SAMR_ACCESS_OPEN_DOMAIN,
+                                                 SAMR_ACCESS_LOOKUP_DOMAIN,
                                                  SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT |
                                                  SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS,
                                                  &connect_handle,
@@ -1832,7 +1853,7 @@ WERROR NetUserSetInfo_r(struct libnetapi_ctx *ctx,
        }
 
        status = set_user_info_USER_INFO_X(ctx, pipe_cli,
-                                          &cli->user_session_key,
+                                          &pipe_cli->auth->user_session_key,
                                           &user_handle,
                                           &uX);
        if (!NT_STATUS_IS_OK(status)) {
@@ -1843,11 +1864,7 @@ WERROR NetUserSetInfo_r(struct libnetapi_ctx *ctx,
        werr = WERR_OK;
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
-       if (is_valid_policy_hnd(&user_handle)) {
+       if (is_valid_policy_hnd(&user_handle) && pipe_cli) {
                rpccli_samr_Close(pipe_cli, ctx, &user_handle);
        }
 
@@ -2164,7 +2181,6 @@ static NTSTATUS query_USER_MODALS_INFO_to_buffer(TALLOC_CTX *mem_ctx,
 WERROR NetUserModalsGet_r(struct libnetapi_ctx *ctx,
                          struct NetUserModalsGet *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
@@ -2199,7 +2215,6 @@ WERROR NetUserModalsGet_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_open_pipe(ctx, r->in.server_name,
                                   &ndr_table_samr.syntax_id,
-                                  &cli,
                                   &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
@@ -2207,7 +2222,7 @@ WERROR NetUserModalsGet_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          access_mask,
                                          &connect_handle,
                                          &domain_handle,
@@ -2233,10 +2248,6 @@ WERROR NetUserModalsGet_r(struct libnetapi_ctx *ctx,
        }
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
        if (ctx->disable_policy_handle_cache) {
                libnetapi_samr_close_domain_handle(ctx, &domain_handle);
                libnetapi_samr_close_connect_handle(ctx, &connect_handle);
@@ -2635,7 +2646,6 @@ static NTSTATUS set_USER_MODALS_INFO_buffer(TALLOC_CTX *mem_ctx,
 WERROR NetUserModalsSet_r(struct libnetapi_ctx *ctx,
                          struct NetUserModalsSet *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
@@ -2683,7 +2693,6 @@ WERROR NetUserModalsSet_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_open_pipe(ctx, r->in.server_name,
                                   &ndr_table_samr.syntax_id,
-                                  &cli,
                                   &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
@@ -2691,7 +2700,7 @@ WERROR NetUserModalsSet_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          access_mask,
                                          &connect_handle,
                                          &domain_handle,
@@ -2712,10 +2721,6 @@ WERROR NetUserModalsSet_r(struct libnetapi_ctx *ctx,
        }
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
        if (ctx->disable_policy_handle_cache) {
                libnetapi_samr_close_domain_handle(ctx, &domain_handle);
                libnetapi_samr_close_connect_handle(ctx, &connect_handle);
@@ -2736,27 +2741,35 @@ WERROR NetUserModalsSet_l(struct libnetapi_ctx *ctx,
 /****************************************************************
 ****************************************************************/
 
-static NTSTATUS add_GROUP_USERS_INFO_X_buffer(TALLOC_CTX *mem_ctx,
-                                             uint32_t level,
-                                             const char *group_name,
-                                             uint32_t attributes,
-                                             uint8_t **buffer,
-                                             uint32_t *num_entries)
+NTSTATUS add_GROUP_USERS_INFO_X_buffer(TALLOC_CTX *mem_ctx,
+                                      uint32_t level,
+                                      const char *group_name,
+                                      uint32_t attributes,
+                                      uint8_t **buffer,
+                                      uint32_t *num_entries)
 {
        struct GROUP_USERS_INFO_0 u0;
        struct GROUP_USERS_INFO_1 u1;
 
        switch (level) {
                case 0:
-                       u0.grui0_name = talloc_strdup(mem_ctx, group_name);
-                       NT_STATUS_HAVE_NO_MEMORY(u0.grui0_name);
+                       if (group_name) {
+                               u0.grui0_name = talloc_strdup(mem_ctx, group_name);
+                               NT_STATUS_HAVE_NO_MEMORY(u0.grui0_name);
+                       } else {
+                               u0.grui0_name = NULL;
+                       }
 
                        ADD_TO_ARRAY(mem_ctx, struct GROUP_USERS_INFO_0, u0,
                                     (struct GROUP_USERS_INFO_0 **)buffer, num_entries);
                        break;
                case 1:
-                       u1.grui1_name = talloc_strdup(mem_ctx, group_name);
-                       NT_STATUS_HAVE_NO_MEMORY(u1.grui1_name);
+                       if (group_name) {
+                               u1.grui1_name = talloc_strdup(mem_ctx, group_name);
+                               NT_STATUS_HAVE_NO_MEMORY(u1.grui1_name);
+                       } else {
+                               u1.grui1_name = NULL;
+                       }
 
                        u1.grui1_attributes = attributes;
 
@@ -2776,7 +2789,6 @@ static NTSTATUS add_GROUP_USERS_INFO_X_buffer(TALLOC_CTX *mem_ctx,
 WERROR NetUserGetGroups_r(struct libnetapi_ctx *ctx,
                          struct NetUserGetGroups *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        struct policy_handle connect_handle, domain_handle, user_handle;
        struct lsa_String lsa_account_name;
@@ -2802,6 +2814,7 @@ WERROR NetUserGetGroups_r(struct libnetapi_ctx *ctx,
 
        *r->out.buffer = NULL;
        *r->out.entries_read = 0;
+       *r->out.total_entries = 0;
 
        switch (r->in.level) {
                case 0:
@@ -2813,7 +2826,6 @@ WERROR NetUserGetGroups_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_open_pipe(ctx, r->in.server_name,
                                   &ndr_table_samr.syntax_id,
-                                  &cli,
                                   &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
@@ -2821,7 +2833,7 @@ WERROR NetUserGetGroups_r(struct libnetapi_ctx *ctx,
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
                                          &domain_handle,
@@ -2877,12 +2889,13 @@ WERROR NetUserGetGroups_r(struct libnetapi_ctx *ctx,
                                        rids,
                                        &names,
                                        &types);
-       if (!NT_STATUS_IS_OK(status)) {
+       if (!NT_STATUS_IS_OK(status) &&
+           !NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
                werr = ntstatus_to_werror(status);
                goto done;
        }
 
-       for (i=0; i < rid_array->count; i++) {
+       for (i=0; i < names.count; i++) {
                status = add_GROUP_USERS_INFO_X_buffer(ctx,
                                                       r->in.level,
                                                       names.names[i].string,
@@ -2895,16 +2908,263 @@ WERROR NetUserGetGroups_r(struct libnetapi_ctx *ctx,
                }
        }
 
-       if (r->out.entries_read) {
-               *r->out.entries_read = entries_read;
+       *r->out.entries_read = entries_read;
+       *r->out.total_entries = entries_read;
+
+ done:
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
+       }
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetUserGetGroups_l(struct libnetapi_ctx *ctx,
+                         struct NetUserGetGroups *r)
+{
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserGetGroups);
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetUserSetGroups_r(struct libnetapi_ctx *ctx,
+                         struct NetUserSetGroups *r)
+{
+       struct rpc_pipe_client *pipe_cli = NULL;
+       struct policy_handle connect_handle, domain_handle, user_handle, group_handle;
+       struct lsa_String lsa_account_name;
+       struct dom_sid2 *domain_sid = NULL;
+       struct samr_Ids user_rids, name_types;
+       struct samr_Ids group_rids;
+       struct samr_RidWithAttributeArray *rid_array = NULL;
+       struct lsa_String *lsa_names = NULL;
+
+       uint32_t *add_rids = NULL;
+       uint32_t *del_rids = NULL;
+       size_t num_add_rids = 0;
+       size_t num_del_rids = 0;
+
+       uint32_t *member_rids = NULL;
+       size_t num_member_rids = 0;
+
+       struct GROUP_USERS_INFO_0 *i0 = NULL;
+       struct GROUP_USERS_INFO_1 *i1 = NULL;
+
+       int i, k;
+
+       NTSTATUS status = NT_STATUS_OK;
+       WERROR werr;
+
+       ZERO_STRUCT(connect_handle);
+       ZERO_STRUCT(domain_handle);
+
+       if (!r->in.buffer) {
+               return WERR_INVALID_PARAM;
+       }
+
+       switch (r->in.level) {
+               case 0:
+               case 1:
+                       break;
+               default:
+                       return WERR_UNKNOWN_LEVEL;
+       }
+
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &pipe_cli);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
+                                         SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+                                         &connect_handle,
+                                         &domain_handle,
+                                         &domain_sid);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       init_lsa_String(&lsa_account_name, r->in.user_name);
+
+       status = rpccli_samr_LookupNames(pipe_cli, ctx,
+                                        &domain_handle,
+                                        1,
+                                        &lsa_account_name,
+                                        &user_rids,
+                                        &name_types);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       status = rpccli_samr_OpenUser(pipe_cli, ctx,
+                                     &domain_handle,
+                                     SAMR_USER_ACCESS_GET_GROUPS,
+                                     user_rids.ids[0],
+                                     &user_handle);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       switch (r->in.level) {
+               case 0:
+                       i0 = (struct GROUP_USERS_INFO_0 *)r->in.buffer;
+                       break;
+               case 1:
+                       i1 = (struct GROUP_USERS_INFO_1 *)r->in.buffer;
+                       break;
+       }
+
+       lsa_names = talloc_array(ctx, struct lsa_String, r->in.num_entries);
+       if (!lsa_names) {
+               werr = WERR_NOMEM;
+               goto done;
+       }
+
+       for (i=0; i < r->in.num_entries; i++) {
+
+               switch (r->in.level) {
+                       case 0:
+                               init_lsa_String(&lsa_names[i], i0->grui0_name);
+                               i0++;
+                               break;
+                       case 1:
+                               init_lsa_String(&lsa_names[i], i1->grui1_name);
+                               i1++;
+                               break;
+               }
+       }
+
+       status = rpccli_samr_LookupNames(pipe_cli, ctx,
+                                        &domain_handle,
+                                        r->in.num_entries,
+                                        lsa_names,
+                                        &group_rids,
+                                        &name_types);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
        }
-       if (r->out.total_entries) {
-               *r->out.total_entries = entries_read;
+
+       member_rids = group_rids.ids;
+       num_member_rids = group_rids.count;
+
+       status = rpccli_samr_GetGroupsForUser(pipe_cli, ctx,
+                                             &user_handle,
+                                             &rid_array);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       /* add list */
+
+       for (i=0; i < r->in.num_entries; i++) {
+               bool already_member = false;
+               for (k=0; k < rid_array->count; k++) {
+                       if (member_rids[i] == rid_array->rids[k].rid) {
+                               already_member = true;
+                               break;
+                       }
+               }
+               if (!already_member) {
+                       if (!add_rid_to_array_unique(ctx,
+                                                    member_rids[i],
+                                                    &add_rids, &num_add_rids)) {
+                               werr = WERR_GENERAL_FAILURE;
+                               goto done;
+                       }
+               }
+       }
+
+       /* del list */
+
+       for (k=0; k < rid_array->count; k++) {
+               bool keep_member = false;
+               for (i=0; i < r->in.num_entries; i++) {
+                       if (member_rids[i] == rid_array->rids[k].rid) {
+                               keep_member = true;
+                               break;
+                       }
+               }
+               if (!keep_member) {
+                       if (!add_rid_to_array_unique(ctx,
+                                                    rid_array->rids[k].rid,
+                                                    &del_rids, &num_del_rids)) {
+                               werr = WERR_GENERAL_FAILURE;
+                               goto done;
+                       }
+               }
+       }
+
+       /* add list */
+
+       for (i=0; i < num_add_rids; i++) {
+               status = rpccli_samr_OpenGroup(pipe_cli, ctx,
+                                              &domain_handle,
+                                              SAMR_GROUP_ACCESS_ADD_MEMBER,
+                                              add_rids[i],
+                                              &group_handle);
+               if (!NT_STATUS_IS_OK(status)) {
+                       werr = ntstatus_to_werror(status);
+                       goto done;
+               }
+
+               status = rpccli_samr_AddGroupMember(pipe_cli, ctx,
+                                                   &group_handle,
+                                                   user_rids.ids[0],
+                                                   7 /* ? */);
+               if (!NT_STATUS_IS_OK(status)) {
+                       werr = ntstatus_to_werror(status);
+                       goto done;
+               }
+
+               if (is_valid_policy_hnd(&group_handle)) {
+                       rpccli_samr_Close(pipe_cli, ctx, &group_handle);
+               }
+       }
+
+       /* del list */
+
+       for (i=0; i < num_del_rids; i++) {
+               status = rpccli_samr_OpenGroup(pipe_cli, ctx,
+                                              &domain_handle,
+                                              SAMR_GROUP_ACCESS_REMOVE_MEMBER,
+                                              del_rids[i],
+                                              &group_handle);
+               if (!NT_STATUS_IS_OK(status)) {
+                       werr = ntstatus_to_werror(status);
+                       goto done;
+               }
+
+               status = rpccli_samr_DeleteGroupMember(pipe_cli, ctx,
+                                                      &group_handle,
+                                                      user_rids.ids[0]);
+               if (!NT_STATUS_IS_OK(status)) {
+                       werr = ntstatus_to_werror(status);
+                       goto done;
+               }
+
+               if (is_valid_policy_hnd(&group_handle)) {
+                       rpccli_samr_Close(pipe_cli, ctx, &group_handle);
+               }
        }
 
+       werr = WERR_OK;
+
  done:
-       if (!cli) {
-               return werr;
+       if (is_valid_policy_hnd(&group_handle)) {
+               rpccli_samr_Close(pipe_cli, ctx, &group_handle);
        }
 
        if (ctx->disable_policy_handle_cache) {
@@ -2918,8 +3178,253 @@ WERROR NetUserGetGroups_r(struct libnetapi_ctx *ctx,
 /****************************************************************
 ****************************************************************/
 
-WERROR NetUserGetGroups_l(struct libnetapi_ctx *ctx,
-                         struct NetUserGetGroups *r)
+WERROR NetUserSetGroups_l(struct libnetapi_ctx *ctx,
+                         struct NetUserSetGroups *r)
 {
-       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserGetGroups);
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserSetGroups);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS add_LOCALGROUP_USERS_INFO_X_buffer(TALLOC_CTX *mem_ctx,
+                                                  uint32_t level,
+                                                  const char *group_name,
+                                                  uint8_t **buffer,
+                                                  uint32_t *num_entries)
+{
+       struct LOCALGROUP_USERS_INFO_0 u0;
+
+       switch (level) {
+               case 0:
+                       u0.lgrui0_name = talloc_strdup(mem_ctx, group_name);
+                       NT_STATUS_HAVE_NO_MEMORY(u0.lgrui0_name);
+
+                       ADD_TO_ARRAY(mem_ctx, struct LOCALGROUP_USERS_INFO_0, u0,
+                                    (struct LOCALGROUP_USERS_INFO_0 **)buffer, num_entries);
+                       break;
+               default:
+                       return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetUserGetLocalGroups_r(struct libnetapi_ctx *ctx,
+                              struct NetUserGetLocalGroups *r)
+{
+       struct rpc_pipe_client *pipe_cli = NULL;
+       struct policy_handle connect_handle, domain_handle, user_handle,
+       builtin_handle;
+       struct lsa_String lsa_account_name;
+       struct dom_sid2 *domain_sid = NULL;
+       struct samr_Ids user_rids, name_types;
+       struct samr_RidWithAttributeArray *rid_array = NULL;
+       struct lsa_Strings names;
+       struct samr_Ids types;
+       uint32_t *rids = NULL;
+       size_t num_rids = 0;
+       struct dom_sid user_sid;
+       struct lsa_SidArray sid_array;
+       struct samr_Ids domain_rids;
+       struct samr_Ids builtin_rids;
+
+       int i;
+       uint32_t entries_read = 0;
+
+       NTSTATUS status = NT_STATUS_OK;
+       WERROR werr;
+
+       ZERO_STRUCT(connect_handle);
+       ZERO_STRUCT(domain_handle);
+
+       if (!r->out.buffer) {
+               return WERR_INVALID_PARAM;
+       }
+
+       *r->out.buffer = NULL;
+       *r->out.entries_read = 0;
+       *r->out.total_entries = 0;
+
+       switch (r->in.level) {
+               case 0:
+               case 1:
+                       break;
+               default:
+                       return WERR_UNKNOWN_LEVEL;
+       }
+
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &pipe_cli);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
+                                         SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT |
+                                         SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS,
+                                         &connect_handle,
+                                         &domain_handle,
+                                         &domain_sid);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli,
+                                                 SAMR_ACCESS_ENUM_DOMAINS |
+                                                 SAMR_ACCESS_LOOKUP_DOMAIN,
+                                                 SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT |
+                                                 SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS,
+                                                 &connect_handle,
+                                                 &builtin_handle);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       init_lsa_String(&lsa_account_name, r->in.user_name);
+
+       status = rpccli_samr_LookupNames(pipe_cli, ctx,
+                                        &domain_handle,
+                                        1,
+                                        &lsa_account_name,
+                                        &user_rids,
+                                        &name_types);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       status = rpccli_samr_OpenUser(pipe_cli, ctx,
+                                     &domain_handle,
+                                     SAMR_USER_ACCESS_GET_GROUPS,
+                                     user_rids.ids[0],
+                                     &user_handle);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       status = rpccli_samr_GetGroupsForUser(pipe_cli, ctx,
+                                             &user_handle,
+                                             &rid_array);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       if (!sid_compose(&user_sid, domain_sid, user_rids.ids[0])) {
+               werr = WERR_NOMEM;
+               goto done;
+       }
+
+       sid_array.num_sids = rid_array->count + 1;
+       sid_array.sids = TALLOC_ARRAY(ctx, struct lsa_SidPtr, sid_array.num_sids);
+       if (!sid_array.sids) {
+               werr = WERR_NOMEM;
+               goto done;
+       }
+
+       sid_array.sids[0].sid = sid_dup_talloc(ctx, &user_sid);
+       if (!sid_array.sids[0].sid) {
+               werr = WERR_NOMEM;
+               goto done;
+       }
+
+       for (i=0; i < rid_array->count; i++) {
+               struct dom_sid sid;
+
+               if (!sid_compose(&sid, domain_sid, rid_array->rids[i].rid)) {
+                       werr = WERR_NOMEM;
+                       goto done;
+               }
+
+               sid_array.sids[i+1].sid = sid_dup_talloc(ctx, &sid);
+               if (!sid_array.sids[i+1].sid) {
+                       werr = WERR_NOMEM;
+                       goto done;
+               }
+       }
+
+       status = rpccli_samr_GetAliasMembership(pipe_cli, ctx,
+                                               &domain_handle,
+                                               &sid_array,
+                                               &domain_rids);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       for (i=0; i < domain_rids.count; i++) {
+               if (!add_rid_to_array_unique(ctx, domain_rids.ids[i],
+                                            &rids, &num_rids)) {
+                       werr = WERR_NOMEM;
+                       goto done;
+               }
+       }
+
+       status = rpccli_samr_GetAliasMembership(pipe_cli, ctx,
+                                               &builtin_handle,
+                                               &sid_array,
+                                               &builtin_rids);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       for (i=0; i < builtin_rids.count; i++) {
+               if (!add_rid_to_array_unique(ctx, builtin_rids.ids[i],
+                                            &rids, &num_rids)) {
+                       werr = WERR_NOMEM;
+                       goto done;
+               }
+       }
+
+       status = rpccli_samr_LookupRids(pipe_cli, ctx,
+                                       &builtin_handle,
+                                       num_rids,
+                                       rids,
+                                       &names,
+                                       &types);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       for (i=0; i < names.count; i++) {
+               status = add_LOCALGROUP_USERS_INFO_X_buffer(ctx,
+                                                           r->in.level,
+                                                           names.names[i].string,
+                                                           r->out.buffer,
+                                                           &entries_read);
+               if (!NT_STATUS_IS_OK(status)) {
+                       werr = ntstatus_to_werror(status);
+                       goto done;
+               }
+       }
+
+       *r->out.entries_read = entries_read;
+       *r->out.total_entries = entries_read;
+
+ done:
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
+       }
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetUserGetLocalGroups_l(struct libnetapi_ctx *ctx,
+                              struct NetUserGetLocalGroups *r)
+{
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserGetLocalGroups);
 }