netapi: give more correct error code in NetUserGetInfo.
[jra/samba/.git] / source3 / lib / netapi / user.c
index 55d9795f2d1c18f4b978ea7a4f07e31f711d61fa..47053f29af1729217b3ff917da60e0c157105dea 100644 (file)
 /****************************************************************
 ****************************************************************/
 
-WERROR NetUserAdd_l(struct libnetapi_ctx *ctx,
-                   struct NetUserAdd *r)
-{
-       return WERR_NOT_SUPPORTED;
-}
-
-/****************************************************************
-****************************************************************/
-
-static void convert_USER_INFO_1_to_samr_user_info25(struct USER_INFO_1 *info1,
-                                                   DATA_BLOB *user_session_key,
-                                                   struct samr_UserInfo25 *info25)
+static void convert_USER_INFO_X_to_samr_user_info21(struct USER_INFO_X *infoX,
+                                                   struct samr_UserInfo21 *info21)
 {
-       uint32_t fields_present = SAMR_FIELD_ACCT_FLAGS;
+       uint32_t fields_present = 0;
        struct samr_LogonHours zero_logon_hours;
        struct lsa_BinaryString zero_parameters;
-       uint32_t acct_flags = 0;
        NTTIME password_age;
 
-       ZERO_STRUCTP(info25);
+       ZERO_STRUCTP(info21);
        ZERO_STRUCT(zero_logon_hours);
        ZERO_STRUCT(zero_parameters);
 
-       if (info1->usri1_name) {
-               fields_present |= SAMR_FIELD_FULL_NAME;
+       if (infoX->usriX_flags) {
+               fields_present |= SAMR_FIELD_ACCT_FLAGS;
+       }
+       if (infoX->usriX_name) {
+               fields_present |= SAMR_FIELD_ACCOUNT_NAME;
        }
-       if (info1->usri1_password) {
+       if (infoX->usriX_password) {
                fields_present |= SAMR_FIELD_PASSWORD;
        }
-       if (info1->usri1_flags) {
+       if (infoX->usriX_flags) {
                fields_present |= SAMR_FIELD_ACCT_FLAGS;
        }
-       if (info1->usri1_name) {
+       if (infoX->usriX_name) {
                fields_present |= SAMR_FIELD_FULL_NAME;
        }
-       if (info1->usri1_home_dir) {
+       if (infoX->usriX_home_dir) {
                fields_present |= SAMR_FIELD_HOME_DIRECTORY;
        }
-       if (info1->usri1_script_path) {
+       if (infoX->usriX_script_path) {
                fields_present |= SAMR_FIELD_LOGON_SCRIPT;
        }
-       if (info1->usri1_comment) {
+       if (infoX->usriX_comment) {
                fields_present |= SAMR_FIELD_DESCRIPTION;
        }
-       if (info1->usri1_password_age) {
+       if (infoX->usriX_password_age) {
                fields_present |= SAMR_FIELD_FORCE_PWD_CHANGE;
        }
+       if (infoX->usriX_full_name) {
+               fields_present |= SAMR_FIELD_FULL_NAME;
+       }
+       if (infoX->usriX_usr_comment) {
+               fields_present |= SAMR_FIELD_COMMENT;
+       }
+       if (infoX->usriX_profile) {
+               fields_present |= SAMR_FIELD_PROFILE_PATH;
+       }
+       if (infoX->usriX_home_dir_drive) {
+               fields_present |= SAMR_FIELD_HOME_DRIVE;
+       }
+       if (infoX->usriX_primary_group_id) {
+               fields_present |= SAMR_FIELD_PRIMARY_GID;
+       }
+       if (infoX->usriX_country_code) {
+               fields_present |= SAMR_FIELD_COUNTRY_CODE;
+       }
+       if (infoX->usriX_workstations) {
+               fields_present |= SAMR_FIELD_WORKSTATIONS;
+       }
 
-       acct_flags |= info1->usri1_flags | ACB_NORMAL;
-
-       unix_to_nt_time_abs(&password_age, info1->usri1_password_age);
+       unix_to_nt_time_abs(&password_age, infoX->usriX_password_age);
 
-       /* TODO: info1->usri1_priv */
-       init_samr_user_info21(&info25->info,
+       /* TODO: infoX->usriX_priv */
+       init_samr_user_info21(info21,
                              0,
                              0,
                              0,
                              0,
                              0,
                              password_age,
-                             NULL,
-                             info1->usri1_name,
-                             info1->usri1_home_dir,
-                             NULL,
-                             info1->usri1_script_path,
-                             NULL,
-                             info1->usri1_comment,
-                             NULL,
-                             NULL,
+                             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,
-                             0,
-                             acct_flags,
+                             infoX->usriX_primary_group_id,
+                             infoX->usriX_flags,
                              fields_present,
                              zero_logon_hours,
                              0,
                              0,
-                             0,
+                             infoX->usriX_country_code,
                              0,
                              0,
                              0,
                              0);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS construct_USER_INFO_X(uint32_t level,
+                                     uint8_t *buffer,
+                                     struct USER_INFO_X *uX)
+{
+       struct USER_INFO_0 *u0 = NULL;
+       struct USER_INFO_1 *u1 = NULL;
+       struct USER_INFO_2 *u2 = NULL;
+       struct USER_INFO_1003 *u1003 = NULL;
+       struct USER_INFO_1006 *u1006 = NULL;
+       struct USER_INFO_1007 *u1007 = NULL;
+       struct USER_INFO_1009 *u1009 = NULL;
+       struct USER_INFO_1011 *u1011 = NULL;
+       struct USER_INFO_1012 *u1012 = NULL;
+       struct USER_INFO_1014 *u1014 = NULL;
+       struct USER_INFO_1024 *u1024 = NULL;
+       struct USER_INFO_1051 *u1051 = NULL;
+       struct USER_INFO_1052 *u1052 = NULL;
+       struct USER_INFO_1053 *u1053 = NULL;
+
+       if (!buffer || !uX) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       ZERO_STRUCTP(uX);
+
+       switch (level) {
+               case 0:
+                       u0 = (struct USER_INFO_0 *)buffer;
+                       uX->usriX_name          = u0->usri0_name;
+                       break;
+               case 1:
+                       u1 = (struct USER_INFO_1 *)buffer;
+                       uX->usriX_name          = u1->usri1_name;
+                       uX->usriX_password      = u1->usri1_password;
+                       uX->usriX_password_age  = u1->usri1_password_age;
+                       uX->usriX_priv          = u1->usri1_priv;
+                       uX->usriX_home_dir      = u1->usri1_home_dir;
+                       uX->usriX_comment       = u1->usri1_comment;
+                       uX->usriX_flags         = u1->usri1_flags;
+                       uX->usriX_script_path   = u1->usri1_script_path;
+                       break;
+               case 2:
+                       u2 = (struct USER_INFO_2 *)buffer;
+                       uX->usriX_name          = u2->usri2_name;
+                       uX->usriX_password      = u2->usri2_password;
+                       uX->usriX_password_age  = u2->usri2_password_age;
+                       uX->usriX_priv          = u2->usri2_priv;
+                       uX->usriX_home_dir      = u2->usri2_home_dir;
+                       uX->usriX_comment       = u2->usri2_comment;
+                       uX->usriX_flags         = u2->usri2_flags;
+                       uX->usriX_script_path   = u2->usri2_script_path;
+                       uX->usriX_auth_flags    = u2->usri2_auth_flags;
+                       uX->usriX_full_name     = u2->usri2_full_name;
+                       uX->usriX_usr_comment   = u2->usri2_usr_comment;
+                       uX->usriX_parms         = u2->usri2_parms;
+                       uX->usriX_workstations  = u2->usri2_workstations;
+                       uX->usriX_last_logon    = u2->usri2_last_logon;
+                       uX->usriX_last_logoff   = u2->usri2_last_logoff;
+                       uX->usriX_acct_expires  = u2->usri2_acct_expires;
+                       uX->usriX_max_storage   = u2->usri2_max_storage;
+                       uX->usriX_units_per_week= u2->usri2_units_per_week;
+                       uX->usriX_logon_hours   = u2->usri2_logon_hours;
+                       uX->usriX_bad_pw_count  = u2->usri2_bad_pw_count;
+                       uX->usriX_num_logons    = u2->usri2_num_logons;
+                       uX->usriX_logon_server  = u2->usri2_logon_server;
+                       uX->usriX_country_code  = u2->usri2_country_code;
+                       uX->usriX_code_page     = u2->usri2_code_page;
+                       break;
+               case 1003:
+                       u1003 = (struct USER_INFO_1003 *)buffer;
+                       uX->usriX_password      = u1003->usri1003_password;
+                       break;
+               case 1006:
+                       u1006 = (struct USER_INFO_1006 *)buffer;
+                       uX->usriX_home_dir      = u1006->usri1006_home_dir;
+                       break;
+               case 1007:
+                       u1007 = (struct USER_INFO_1007 *)buffer;
+                       uX->usriX_comment       = u1007->usri1007_comment;
+                       break;
+               case 1009:
+                       u1009 = (struct USER_INFO_1009 *)buffer;
+                       uX->usriX_script_path   = u1009->usri1009_script_path;
+                       break;
+               case 1011:
+                       u1011 = (struct USER_INFO_1011 *)buffer;
+                       uX->usriX_full_name     = u1011->usri1011_full_name;
+                       break;
+               case 1012:
+                       u1012 = (struct USER_INFO_1012 *)buffer;
+                       uX->usriX_usr_comment   = u1012->usri1012_usr_comment;
+                       break;
+               case 1014:
+                       u1014 = (struct USER_INFO_1014 *)buffer;
+                       uX->usriX_workstations  = u1014->usri1014_workstations;
+                       break;
+               case 1024:
+                       u1024 = (struct USER_INFO_1024 *)buffer;
+                       uX->usriX_country_code  = u1024->usri1024_country_code;
+                       break;
+               case 1051:
+                       u1051 = (struct USER_INFO_1051 *)buffer;
+                       uX->usriX_primary_group_id = u1051->usri1051_primary_group_id;
+                       break;
+               case 1052:
+                       u1052 = (struct USER_INFO_1052 *)buffer;
+                       uX->usriX_profile       = u1052->usri1052_profile;
+                       break;
+               case 1053:
+                       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;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS set_user_info_USER_INFO_X(TALLOC_CTX *ctx,
+                                         struct rpc_pipe_client *pipe_cli,
+                                         DATA_BLOB *session_key,
+                                         struct policy_handle *user_handle,
+                                         struct USER_INFO_X *uX)
+{
+       union samr_UserInfo user_info;
+       struct samr_UserInfo21 info21;
+       NTSTATUS status;
+
+       if (!uX) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       convert_USER_INFO_X_to_samr_user_info21(uX, &info21);
+
+       ZERO_STRUCT(user_info);
 
-       if (info1->usri1_password) {
-               uchar pwbuf[532];
-               struct MD5Context ctx;
-               uint8_t confounder[16];
-               DATA_BLOB confounded_session_key = data_blob(NULL, 16);
+       if (uX->usriX_password) {
 
-               encode_pw_buffer(pwbuf, info1->usri1_password, STR_UNICODE);
+               user_info.info25.info = info21;
 
-               generate_random_buffer((uint8_t *)confounder, 16);
+               init_samr_CryptPasswordEx(uX->usriX_password,
+                                         session_key,
+                                         &user_info.info25.password);
 
-               MD5Init(&ctx);
-               MD5Update(&ctx, confounder, 16);
-               MD5Update(&ctx, user_session_key->data,
-                               user_session_key->length);
-               MD5Final(confounded_session_key.data, &ctx);
+               status = rpccli_samr_SetUserInfo2(pipe_cli, ctx,
+                                                 user_handle,
+                                                 25,
+                                                 &user_info);
+
+               if (NT_STATUS_EQUAL(status, NT_STATUS(DCERPC_FAULT_INVALID_TAG))) {
+
+                       user_info.info23.info = info21;
+
+                       init_samr_CryptPassword(uX->usriX_password,
+                                               session_key,
+                                               &user_info.info23.password);
+
+                       status = rpccli_samr_SetUserInfo2(pipe_cli, ctx,
+                                                         user_handle,
+                                                         23,
+                                                         &user_info);
+               }
+       } else {
 
-               SamOEMhashBlob(pwbuf, 516, &confounded_session_key);
-               memcpy(&pwbuf[516], confounder, 16);
+               user_info.info21 = info21;
 
-               memcpy(info25->password.data, pwbuf, sizeof(pwbuf));
-               data_blob_free(&confounded_session_key);
+               status = rpccli_samr_SetUserInfo(pipe_cli, ctx,
+                                                user_handle,
+                                                21,
+                                                &user_info);
        }
+
+       return status;
 }
 
 /****************************************************************
@@ -144,21 +316,14 @@ WERROR NetUserAdd_r(struct libnetapi_ctx *ctx,
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
-       uint32_t resume_handle = 0;
-       uint32_t num_entries = 0;
        POLICY_HND connect_handle, domain_handle, user_handle;
-       struct samr_SamArray *sam = NULL;
-       const char *domain_name = NULL;
-       struct lsa_String lsa_domain_name, lsa_account_name;
+       struct lsa_String lsa_account_name;
        struct dom_sid2 *domain_sid = NULL;
-       struct samr_UserInfo25 info25;
        union samr_UserInfo *user_info = NULL;
        struct samr_PwInfo pw_info;
        uint32_t access_granted = 0;
        uint32_t rid = 0;
-       bool domain_found = true;
-       int i;
-       struct USER_INFO_1 *info1;
+       struct USER_INFO_X uX;
 
        ZERO_STRUCT(connect_handle);
        ZERO_STRUCT(domain_handle);
@@ -170,7 +335,6 @@ WERROR NetUserAdd_r(struct libnetapi_ctx *ctx,
 
        switch (r->in.level) {
                case 1:
-                       info1 = (struct USER_INFO_1 *)r->in.buffer;
                        break;
                case 2:
                case 3:
@@ -180,77 +344,34 @@ WERROR NetUserAdd_r(struct libnetapi_ctx *ctx,
                        goto done;
        }
 
-       werr = libnetapi_open_ipc_connection(ctx, r->in.server_name, &cli);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto done;
-       }
-
-       werr = libnetapi_open_pipe(ctx, cli, PI_SAMR, &pipe_cli);
+       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;
        }
 
-       status = rpccli_try_samr_connects(pipe_cli, ctx,
-                                         SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
-                                         &connect_handle);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
-               goto done;
-       }
-
-       status = rpccli_samr_EnumDomains(pipe_cli, ctx,
-                                        &connect_handle,
-                                        &resume_handle,
-                                        &sam,
-                                        0xffffffff,
-                                        &num_entries);
+       status = construct_USER_INFO_X(r->in.level, r->in.buffer, &uX);
        if (!NT_STATUS_IS_OK(status)) {
                werr = ntstatus_to_werror(status);
                goto done;
        }
 
-       for (i=0; i<num_entries; i++) {
-
-               domain_name = sam->entries[i].name.string;
-
-               if (strequal(domain_name, builtin_domain_name())) {
-                       continue;
-               }
-
-               domain_found = true;
-               break;
-       }
-
-       if (!domain_found) {
-               werr = WERR_NO_SUCH_DOMAIN;
-               goto done;
-       }
-
-       init_lsa_String(&lsa_domain_name, domain_name);
-
-       status = rpccli_samr_LookupDomain(pipe_cli, ctx,
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 |
+                                         SAMR_DOMAIN_ACCESS_CREATE_USER |
+                                         SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
-                                         &lsa_domain_name,
+                                         &domain_handle,
                                          &domain_sid);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
-               goto done;
-       }
-
-       status = rpccli_samr_OpenDomain(pipe_cli, ctx,
-                                       &connect_handle,
-                                       SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 |
-                                       SAMR_DOMAIN_ACCESS_CREATE_USER |
-                                       SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
-                                       domain_sid,
-                                       &domain_handle);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
+       if (!W_ERROR_IS_OK(werr)) {
                goto done;
        }
 
-       init_lsa_String(&lsa_account_name, info1->usri1_name);
+       init_lsa_String(&lsa_account_name, uX.usriX_name);
 
        status = rpccli_samr_CreateUser2(pipe_cli, ctx,
                                         &domain_handle,
@@ -291,26 +412,12 @@ WERROR NetUserAdd_r(struct libnetapi_ctx *ctx,
                goto done;
        }
 
-       ZERO_STRUCTP(user_info);
+       uX.usriX_flags |= ACB_NORMAL;
 
-       convert_USER_INFO_1_to_samr_user_info25(info1,
-                                               &cli->user_session_key,
-                                               &info25);
-
-       if (info1->usri1_password) {
-               user_info->info25 = info25;
-               status = rpccli_samr_SetUserInfo2(pipe_cli, ctx,
-                                                 &user_handle,
-                                                 25,
-                                                 user_info);
-       } else {
-               user_info->info21 = info25.info;
-               status = rpccli_samr_SetUserInfo(pipe_cli, ctx,
-                                                &user_handle,
-                                                21,
-                                                user_info);
-
-       }
+       status = set_user_info_USER_INFO_X(ctx, pipe_cli,
+                                          &cli->user_session_key,
+                                          &user_handle,
+                                          &uX);
        if (!NT_STATUS_IS_OK(status)) {
                werr = ntstatus_to_werror(status);
                goto failed;
@@ -331,11 +438,10 @@ WERROR NetUserAdd_r(struct libnetapi_ctx *ctx,
        if (is_valid_policy_hnd(&user_handle)) {
                rpccli_samr_Close(pipe_cli, ctx, &user_handle);
        }
-       if (is_valid_policy_hnd(&domain_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &domain_handle);
-       }
-       if (is_valid_policy_hnd(&connect_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &connect_handle);
+
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
        }
 
        return werr;
@@ -344,6 +450,15 @@ WERROR NetUserAdd_r(struct libnetapi_ctx *ctx,
 /****************************************************************
 ****************************************************************/
 
+WERROR NetUserAdd_l(struct libnetapi_ctx *ctx,
+                   struct NetUserAdd *r)
+{
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserAdd);
+}
+
+/****************************************************************
+****************************************************************/
+
 WERROR NetUserDel_r(struct libnetapi_ctx *ctx,
                    struct NetUserDel *r)
 {
@@ -351,88 +466,34 @@ WERROR NetUserDel_r(struct libnetapi_ctx *ctx,
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
-       uint32_t resume_handle = 0;
-       uint32_t num_entries = 0;
        POLICY_HND connect_handle, builtin_handle, domain_handle, user_handle;
-       struct samr_SamArray *sam = NULL;
-       const char *domain_name = NULL;
-       struct lsa_String lsa_domain_name, lsa_account_name;
+       struct lsa_String lsa_account_name;
        struct samr_Ids user_rids, name_types;
        struct dom_sid2 *domain_sid = NULL;
        struct dom_sid2 user_sid;
-       bool domain_found = true;
-       int i;
 
        ZERO_STRUCT(connect_handle);
        ZERO_STRUCT(builtin_handle);
        ZERO_STRUCT(domain_handle);
        ZERO_STRUCT(user_handle);
 
-       werr = libnetapi_open_ipc_connection(ctx, r->in.server_name, &cli);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto done;
-       }
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &cli,
+                                  &pipe_cli);
 
-       werr = libnetapi_open_pipe(ctx, cli, PI_SAMR, &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
        }
 
-       status = rpccli_try_samr_connects(pipe_cli, ctx,
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
                                          SAMR_ACCESS_OPEN_DOMAIN,
-                                         &connect_handle);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
-               goto done;
-       }
-
-       status = rpccli_samr_EnumDomains(pipe_cli, ctx,
-                                        &connect_handle,
-                                        &resume_handle,
-                                        &sam,
-                                        0xffffffff,
-                                        &num_entries);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
-               goto done;
-       }
-
-       for (i=0; i<num_entries; i++) {
-
-               domain_name = sam->entries[i].name.string;
-
-               if (strequal(domain_name, builtin_domain_name())) {
-                       continue;
-               }
-
-               domain_found = true;
-               break;
-       }
-
-       if (!domain_found) {
-               werr = WERR_NO_SUCH_DOMAIN;
-               goto done;
-       }
-
-       init_lsa_String(&lsa_domain_name, domain_name);
-
-       status = rpccli_samr_LookupDomain(pipe_cli, ctx,
+                                         SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
-                                         &lsa_domain_name,
+                                         &domain_handle,
                                          &domain_sid);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
-               goto done;
-       }
-
-       status = rpccli_samr_OpenDomain(pipe_cli, ctx,
-                                       &connect_handle,
-                                       SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
-                                       domain_sid,
-                                       &domain_handle);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
+       if (!W_ERROR_IS_OK(werr)) {
                goto done;
        }
 
@@ -496,14 +557,11 @@ WERROR NetUserDel_r(struct libnetapi_ctx *ctx,
        if (is_valid_policy_hnd(&user_handle)) {
                rpccli_samr_Close(pipe_cli, ctx, &user_handle);
        }
-       if (is_valid_policy_hnd(&builtin_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &builtin_handle);
-       }
-       if (is_valid_policy_hnd(&domain_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &domain_handle);
-       }
-       if (is_valid_policy_hnd(&connect_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &connect_handle);
+
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
        }
 
        return werr;
@@ -515,71 +573,35 @@ WERROR NetUserDel_r(struct libnetapi_ctx *ctx,
 WERROR NetUserDel_l(struct libnetapi_ctx *ctx,
                    struct NetUserDel *r)
 {
-       return WERR_NOT_SUPPORTED;
-}
-
-/****************************************************************
-****************************************************************/
-
-static WERROR convert_samr_samarray_to_USER_INFO_buffer(TALLOC_CTX *mem_ctx,
-                                                       struct samr_SamArray *sam_array,
-                                                       uint32_t level,
-                                                       uint8_t **buffer)
-{
-       struct USER_INFO_0 *info0 = NULL;
-       int i;
-
-       switch (level) {
-               case 0:
-                       info0 = TALLOC_ZERO_ARRAY(mem_ctx, struct USER_INFO_0,
-                                                 sam_array->count);
-                       W_ERROR_HAVE_NO_MEMORY(info0);
-
-                       for (i=0; i<sam_array->count; i++) {
-                               info0[i].usri0_name = talloc_strdup(mem_ctx,
-                                       sam_array->entries[i].name.string);
-                               W_ERROR_HAVE_NO_MEMORY(info0[i].usri0_name);
-                       }
-
-                       *buffer = (uint8_t *)talloc_memdup(mem_ctx, info0,
-                               sizeof(struct USER_INFO_0) * sam_array->count);
-                       W_ERROR_HAVE_NO_MEMORY(*buffer);
-                       break;
-               default:
-                       return WERR_NOT_SUPPORTED;
-       }
-
-       return WERR_OK;
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserDel);
 }
 
 /****************************************************************
 ****************************************************************/
 
-WERROR NetUserEnum_r(struct libnetapi_ctx *ctx,
-                    struct NetUserEnum *r)
+static NTSTATUS libnetapi_samr_lookup_user(TALLOC_CTX *mem_ctx,
+                                          struct rpc_pipe_client *pipe_cli,
+                                          struct policy_handle *domain_handle,
+                                          struct policy_handle *builtin_handle,
+                                          const char *user_name,
+                                          uint32_t rid,
+                                          uint32_t level,
+                                          struct samr_UserInfo21 **info21,
+                                          struct sec_desc_buf **sec_desc)
 {
-       struct cli_state *cli = NULL;
-       struct rpc_pipe_client *pipe_cli = NULL;
-       struct policy_handle connect_handle;
-       struct dom_sid2 *domain_sid = NULL;
-       struct policy_handle domain_handle;
-       struct samr_SamArray *sam = NULL;
-       uint32_t num_entries = 0;
-       int i;
-       const char *domain_name = NULL;
-       bool domain_found = true;
-       uint32_t dom_resume_handle = 0;
-       struct lsa_String lsa_domain_name;
-
        NTSTATUS status;
-       WERROR werr;
 
-       ZERO_STRUCT(connect_handle);
-       ZERO_STRUCT(domain_handle);
+       struct policy_handle user_handle;
+       union samr_UserInfo *user_info = NULL;
+       struct samr_RidWithAttributeArray *rid_array = NULL;
+       uint32_t access_mask = SEC_STD_READ_CONTROL |
+                              SAMR_USER_ACCESS_GET_ATTRIBUTES |
+                              SAMR_USER_ACCESS_GET_NAME_ETC;
 
-       switch (r->in.level) {
+       ZERO_STRUCT(user_handle);
+
+       switch (level) {
                case 0:
-                       break;
                case 1:
                case 2:
                case 3:
@@ -587,108 +609,339 @@ WERROR NetUserEnum_r(struct libnetapi_ctx *ctx,
                case 11:
                case 20:
                case 23:
+                       break;
                default:
-                       return WERR_NOT_SUPPORTED;
+                       return NT_STATUS_INVALID_LEVEL;
        }
 
-       werr = libnetapi_open_ipc_connection(ctx, r->in.server_name, &cli);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto done;
+       if (level == 0) {
+               return NT_STATUS_OK;
        }
 
-       werr = libnetapi_open_pipe(ctx, cli, PI_SAMR, &pipe_cli);
-       if (!W_ERROR_IS_OK(werr)) {
+       status = rpccli_samr_OpenUser(pipe_cli, mem_ctx,
+                                     domain_handle,
+                                     access_mask,
+                                     rid,
+                                     &user_handle);
+       if (!NT_STATUS_IS_OK(status)) {
                goto done;
        }
 
-       status = rpccli_try_samr_connects(pipe_cli, ctx,
-                                         SAMR_ACCESS_OPEN_DOMAIN |
-                                         SAMR_ACCESS_ENUM_DOMAINS,
-                                         &connect_handle);
+       status = rpccli_samr_QueryUserInfo(pipe_cli, mem_ctx,
+                                          &user_handle,
+                                          21,
+                                          &user_info);
        if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
                goto done;
        }
 
-       status = rpccli_samr_EnumDomains(pipe_cli, ctx,
-                                        &connect_handle,
-                                        &dom_resume_handle,
-                                        &sam,
-                                        0xffffffff,
-                                        &num_entries);
+       status = rpccli_samr_QuerySecurity(pipe_cli, mem_ctx,
+                                          &user_handle,
+                                          SECINFO_DACL,
+                                          sec_desc);
        if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
                goto done;
        }
 
-       for (i=0; i<num_entries; i++) {
-
-               domain_name = sam->entries[i].name.string;
-
-               if (strequal(domain_name, builtin_domain_name())) {
-                       continue;
+       if (level == 1) {
+               status = rpccli_samr_GetGroupsForUser(pipe_cli, mem_ctx,
+                                                     &user_handle,
+                                                     &rid_array);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto done;
                }
 
-               domain_found = true;
-               break;
-       }
-
-       if (!domain_found) {
-               werr = WERR_NO_SUCH_DOMAIN;
-               goto done;
+#if 0
+               status = rpccli_samr_GetAliasMembership(pipe_cli, ctx,
+                                                       &builtin_handle,
+                                                       &sids,
+                                                       &rids);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto done;
+               }
+#endif
        }
 
-       init_lsa_String(&lsa_domain_name, domain_name);
+       *info21 = &user_info->info21;
 
-       status = rpccli_samr_LookupDomain(pipe_cli, ctx,
-                                         &connect_handle,
-                                         &lsa_domain_name,
-                                         &domain_sid);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
-               goto done;
+ done:
+       if (is_valid_policy_hnd(&user_handle)) {
+               rpccli_samr_Close(pipe_cli, mem_ctx, &user_handle);
        }
 
-       status = rpccli_samr_OpenDomain(pipe_cli,
-                                       ctx,
-                                       &connect_handle,
-                                       SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 |
-                                       SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS |
-                                       SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
-                                       domain_sid,
-                                       &domain_handle);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
-               goto done;
-       }
+       return status;
+}
 
-       status = rpccli_samr_EnumDomainUsers(pipe_cli,
-                                            ctx,
-                                            &domain_handle,
-                                            r->in.resume_handle,
-                                            r->in.filter,
-                                            &sam,
-                                            r->in.prefmaxlen,
-                                            r->out.entries_read);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
-               goto done;
-       }
+/****************************************************************
+****************************************************************/
 
-       werr = convert_samr_samarray_to_USER_INFO_buffer(ctx, sam,
-                                                        r->in.level,
-                                                        r->out.buffer);
+static uint32_t samr_acb_flags_to_netapi_flags(uint32_t acb)
+{
+       uint32_t fl = UF_SCRIPT; /* god knows why */
+
+       fl |= ads_acb2uf(acb);
+
+       return fl;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS libnetapi_samr_lookup_user_map_USER_INFO(TALLOC_CTX *mem_ctx,
+                                                        struct rpc_pipe_client *pipe_cli,
+                                                        struct dom_sid *domain_sid,
+                                                        struct policy_handle *domain_handle,
+                                                        struct policy_handle *builtin_handle,
+                                                        const char *user_name,
+                                                        uint32_t rid,
+                                                        uint32_t level,
+                                                        uint8_t **buffer,
+                                                        uint32_t *num_entries)
+{
+       NTSTATUS status;
+
+       struct samr_UserInfo21 *info21 = NULL;
+       struct sec_desc_buf *sec_desc = NULL;
+       struct dom_sid sid;
+
+       struct USER_INFO_0 info0;
+       struct USER_INFO_10 info10;
+       struct USER_INFO_20 info20;
+       struct USER_INFO_23 info23;
+
+       switch (level) {
+               case 0:
+               case 1:
+               case 2:
+               case 3:
+               case 10:
+               case 11:
+               case 20:
+               case 23:
+                       break;
+               default:
+                       return NT_STATUS_INVALID_LEVEL;
+       }
+
+       if (level == 0) {
+               info0.usri0_name = talloc_strdup(mem_ctx, user_name);
+               NT_STATUS_HAVE_NO_MEMORY(info0.usri0_name);
+
+               ADD_TO_ARRAY(mem_ctx, struct USER_INFO_0, info0,
+                            (struct USER_INFO_0 **)buffer, num_entries);
+
+               return NT_STATUS_OK;
+       }
+
+       status = libnetapi_samr_lookup_user(mem_ctx, pipe_cli,
+                                           domain_handle,
+                                           builtin_handle,
+                                           user_name,
+                                           rid,
+                                           level,
+                                           &info21,
+                                           &sec_desc);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       switch (level) {
+               case 10:
+                       info10.usri10_name = talloc_strdup(mem_ctx,
+                               info21->account_name.string);
+                       NT_STATUS_HAVE_NO_MEMORY(info10.usri10_name);
+
+                       info10.usri10_comment = talloc_strdup(mem_ctx,
+                               info21->description.string);
+
+                       info10.usri10_full_name = talloc_strdup(mem_ctx,
+                               info21->full_name.string);
+
+                       info10.usri10_usr_comment = talloc_strdup(mem_ctx,
+                               info21->comment.string);
+
+                       ADD_TO_ARRAY(mem_ctx, struct USER_INFO_10, info10,
+                                    (struct USER_INFO_10 **)buffer, num_entries);
+
+                       break;
+
+               case 20:
+                       info20.usri20_name = talloc_strdup(mem_ctx,
+                               info21->account_name.string);
+                       NT_STATUS_HAVE_NO_MEMORY(info20.usri20_name);
+
+                       info20.usri20_comment = talloc_strdup(mem_ctx,
+                               info21->description.string);
+
+                       info20.usri20_full_name = talloc_strdup(mem_ctx,
+                               info21->full_name.string);
+
+                       info20.usri20_flags =
+                               samr_acb_flags_to_netapi_flags(info21->acct_flags);
+                       info20.usri20_user_id = rid;
+
+                       ADD_TO_ARRAY(mem_ctx, struct USER_INFO_20, info20,
+                                    (struct USER_INFO_20 **)buffer, num_entries);
+
+                       break;
+               case 23:
+                       info23.usri23_name = talloc_strdup(mem_ctx,
+                               info21->account_name.string);
+                       NT_STATUS_HAVE_NO_MEMORY(info23.usri23_name);
+
+                       info23.usri23_comment = talloc_strdup(mem_ctx,
+                               info21->description.string);
+
+                       info23.usri23_full_name = talloc_strdup(mem_ctx,
+                               info21->full_name.string);
+
+                       info23.usri23_flags =
+                               samr_acb_flags_to_netapi_flags(info21->acct_flags);
+
+                       if (!sid_compose(&sid, domain_sid, rid)) {
+                               return NT_STATUS_NO_MEMORY;
+                       }
+
+                       info23.usri23_user_sid =
+                               (struct domsid *)sid_dup_talloc(mem_ctx, &sid);
+
+                       ADD_TO_ARRAY(mem_ctx, struct USER_INFO_23, info23,
+                                    (struct USER_INFO_23 **)buffer, num_entries);
+                       break;
+       }
+
+ done:
+       return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+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;
+       struct policy_handle domain_handle;
+       struct samr_SamArray *sam = NULL;
+       uint32_t filter = ACB_NORMAL;
+       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;
+
+       switch (r->in.level) {
+               case 0:
+               case 10:
+               case 20:
+               case 23:
+                       break;
+               case 1:
+               case 2:
+               case 3:
+               case 11:
+               default:
+                       return WERR_NOT_SUPPORTED;
+       }
+
+       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;
+       }
+
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 |
+                                         SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS |
+                                         SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+                                         &connect_handle,
+                                         &domain_handle,
+                                         &domain_sid);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       switch (r->in.filter) {
+               case FILTER_NORMAL_ACCOUNT:
+                       filter = ACB_NORMAL;
+                       break;
+               case FILTER_TEMP_DUPLICATE_ACCOUNT:
+                       filter = ACB_TEMPDUP;
+                       break;
+               case FILTER_INTERDOMAIN_TRUST_ACCOUNT:
+                       filter = ACB_DOMTRUST;
+                       break;
+               case FILTER_WORKSTATION_TRUST_ACCOUNT:
+                       filter = ACB_WSTRUST;
+                       break;
+               case FILTER_SERVER_TRUST_ACCOUNT:
+                       filter = ACB_SVRTRUST;
+                       break;
+               default:
+                       break;
+       }
+
+       status = rpccli_samr_EnumDomainUsers(pipe_cli,
+                                            ctx,
+                                            &domain_handle,
+                                            r->in.resume_handle,
+                                            filter,
+                                            &sam,
+                                            r->in.prefmaxlen,
+                                            &entries_read);
+       werr = ntstatus_to_werror(status);
+       if (NT_STATUS_IS_ERR(status)) {
+               goto done;
+       }
+
+       for (i=0; i < sam->count; i++) {
+
+               status = libnetapi_samr_lookup_user_map_USER_INFO(ctx, pipe_cli,
+                                                                 domain_sid,
+                                                                 &domain_handle,
+                                                                 NULL, /*&builtin_handle, */
+                                                                 sam->entries[i].name.string,
+                                                                 sam->entries[i].idx,
+                                                                 r->in.level,
+                                                                 r->out.buffer,
+                                                                 r->out.entries_read);
+               if (!NT_STATUS_IS_OK(status)) {
+                       werr = ntstatus_to_werror(status);
+                       goto done;
+               }
+       }
 
  done:
        if (!cli) {
                return werr;
        }
 
-       if (is_valid_policy_hnd(&domain_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &domain_handle);
-       }
-       if (is_valid_policy_hnd(&connect_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &connect_handle);
+       /* if last query */
+       if (NT_STATUS_IS_OK(status) ||
+           NT_STATUS_IS_ERR(status)) {
+
+               if (ctx->disable_policy_handle_cache) {
+                       libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+                       libnetapi_samr_close_connect_handle(ctx, &connect_handle);
+               }
        }
 
        return werr;
@@ -700,7 +953,7 @@ WERROR NetUserEnum_r(struct libnetapi_ctx *ctx,
 WERROR NetUserEnum_l(struct libnetapi_ctx *ctx,
                     struct NetUserEnum *r)
 {
-       return WERR_NOT_SUPPORTED;
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserEnum);
 }
 
 /****************************************************************
@@ -835,11 +1088,11 @@ static WERROR convert_samr_dispinfo_to_NET_DISPLAY_GROUP(TALLOC_CTX *mem_ctx,
 /****************************************************************
 ****************************************************************/
 
-WERROR convert_samr_dispinfo_to_NET_DISPLAY(TALLOC_CTX *mem_ctx,
-                                           union samr_DispInfo *info,
-                                           uint32_t level,
-                                           uint32_t *entries_read,
-                                           void **buffer)
+static WERROR convert_samr_dispinfo_to_NET_DISPLAY(TALLOC_CTX *mem_ctx,
+                                                  union samr_DispInfo *info,
+                                                  uint32_t level,
+                                                  uint32_t *entries_read,
+                                                  void **buffer)
 {
        switch (level) {
                case 1:
@@ -876,18 +1129,11 @@ WERROR NetQueryDisplayInformation_r(struct libnetapi_ctx *ctx,
        struct dom_sid2 *domain_sid = NULL;
        struct policy_handle domain_handle;
        union samr_DispInfo info;
-       struct samr_SamArray *sam = NULL;
-       uint32_t num_entries = 0;
-       int i;
-       const char *domain_name = NULL;
-       bool domain_found = true;
-       uint32_t dom_resume_handle = 0;
-       struct lsa_String lsa_domain_name;
 
        uint32_t total_size = 0;
        uint32_t returned_size = 0;
 
-       NTSTATUS status;
+       NTSTATUS status = NT_STATUS_OK;
        WERROR werr;
 
        ZERO_STRUCT(connect_handle);
@@ -902,73 +1148,24 @@ WERROR NetQueryDisplayInformation_r(struct libnetapi_ctx *ctx,
                        return WERR_UNKNOWN_LEVEL;
        }
 
-       werr = libnetapi_open_ipc_connection(ctx, r->in.server_name, &cli);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto done;
-       }
-
-       werr = libnetapi_open_pipe(ctx, cli, PI_SAMR, &pipe_cli);
+       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;
        }
 
-       status = rpccli_try_samr_connects(pipe_cli, ctx,
-                                         SAMR_ACCESS_OPEN_DOMAIN |
-                                         SAMR_ACCESS_ENUM_DOMAINS,
-                                         &connect_handle);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
-               goto done;
-       }
-
-       status = rpccli_samr_EnumDomains(pipe_cli, ctx,
-                                        &connect_handle,
-                                        &dom_resume_handle,
-                                        &sam,
-                                        0xffffffff,
-                                        &num_entries);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
-               goto done;
-       }
-
-       for (i=0; i<num_entries; i++) {
-
-               domain_name = sam->entries[i].name.string;
-
-               if (strequal(domain_name, builtin_domain_name())) {
-                       continue;
-               }
-
-               domain_found = true;
-               break;
-       }
-
-       if (!domain_found) {
-               werr = WERR_NO_SUCH_DOMAIN;
-               goto done;
-       }
-
-       init_lsa_String(&lsa_domain_name, domain_name);
-
-       status = rpccli_samr_LookupDomain(pipe_cli, ctx,
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 |
+                                         SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS |
+                                         SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
-                                         &lsa_domain_name,
+                                         &domain_handle,
                                          &domain_sid);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
-               goto done;
-       }
-
-       status = rpccli_samr_OpenDomain(pipe_cli,
-                                       ctx,
-                                       &connect_handle,
-                                       SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS |
-                                       SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
-                                       domain_sid,
-                                       &domain_handle);
-       if (!NT_STATUS_IS_OK(status)) {
-               werr = ntstatus_to_werror(status);
+       if (!W_ERROR_IS_OK(werr)) {
                goto done;
        }
 
@@ -996,11 +1193,14 @@ WERROR NetQueryDisplayInformation_r(struct libnetapi_ctx *ctx,
                return werr;
        }
 
-       if (is_valid_policy_hnd(&domain_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &domain_handle);
-       }
-       if (is_valid_policy_hnd(&connect_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &connect_handle);
+       /* if last query */
+       if (NT_STATUS_IS_OK(status) ||
+           NT_STATUS_IS_ERR(status)) {
+
+               if (ctx->disable_policy_handle_cache) {
+                       libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+                       libnetapi_samr_close_connect_handle(ctx, &connect_handle);
+               }
        }
 
        return werr;
@@ -1013,6 +1213,1371 @@ WERROR NetQueryDisplayInformation_r(struct libnetapi_ctx *ctx,
 
 WERROR NetQueryDisplayInformation_l(struct libnetapi_ctx *ctx,
                                    struct NetQueryDisplayInformation *r)
+{
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetQueryDisplayInformation);
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetUserChangePassword_r(struct libnetapi_ctx *ctx,
+                              struct NetUserChangePassword *r)
+{
+       return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetUserChangePassword_l(struct libnetapi_ctx *ctx,
+                              struct NetUserChangePassword *r)
 {
        return WERR_NOT_SUPPORTED;
 }
+
+/****************************************************************
+****************************************************************/
+
+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;
+
+       struct policy_handle connect_handle, domain_handle, builtin_handle, user_handle;
+       struct lsa_String lsa_account_name;
+       struct dom_sid2 *domain_sid = NULL;
+       struct samr_Ids user_rids, name_types;
+       uint32_t num_entries = 0;
+
+       ZERO_STRUCT(connect_handle);
+       ZERO_STRUCT(domain_handle);
+       ZERO_STRUCT(builtin_handle);
+       ZERO_STRUCT(user_handle);
+
+       if (!r->out.buffer) {
+               return WERR_INVALID_PARAM;
+       }
+
+       switch (r->in.level) {
+               case 0:
+               case 10:
+               case 20:
+               case 23:
+                       break;
+               case 1:
+               case 2:
+               case 3:
+               case 4:
+               case 11:
+                       werr = WERR_NOT_SUPPORTED;
+                       goto done;
+               default:
+                       werr = WERR_UNKNOWN_LEVEL;
+                       goto done;
+       }
+
+       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;
+       }
+
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+                                         &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_OPEN_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 = libnetapi_samr_lookup_user_map_USER_INFO(ctx, pipe_cli,
+                                                         domain_sid,
+                                                         &domain_handle,
+                                                         &builtin_handle,
+                                                         r->in.user_name,
+                                                         user_rids.ids[0],
+                                                         r->in.level,
+                                                         r->out.buffer,
+                                                         &num_entries);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+ done:
+       if (!cli) {
+               return werr;
+       }
+
+       if (is_valid_policy_hnd(&user_handle)) {
+               rpccli_samr_Close(pipe_cli, ctx, &user_handle);
+       }
+
+       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 NetUserGetInfo_l(struct libnetapi_ctx *ctx,
+                       struct NetUserGetInfo *r)
+{
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserGetInfo);
+}
+
+/****************************************************************
+****************************************************************/
+
+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;
+
+       struct policy_handle connect_handle, domain_handle, builtin_handle, user_handle;
+       struct lsa_String lsa_account_name;
+       struct dom_sid2 *domain_sid = NULL;
+       struct samr_Ids user_rids, name_types;
+       uint32_t user_mask = 0;
+
+       struct USER_INFO_X uX;
+
+       ZERO_STRUCT(connect_handle);
+       ZERO_STRUCT(domain_handle);
+       ZERO_STRUCT(builtin_handle);
+       ZERO_STRUCT(user_handle);
+
+       if (!r->in.buffer) {
+               return WERR_INVALID_PARAM;
+       }
+
+       switch (r->in.level) {
+               case 0:
+               case 1003:
+                       user_mask = SAMR_USER_ACCESS_SET_PASSWORD;
+                       break;
+               case 1006:
+               case 1007:
+               case 1009:
+               case 1011:
+               case 1014:
+               case 1052:
+               case 1053:
+                       user_mask = SAMR_USER_ACCESS_SET_ATTRIBUTES;
+                       break;
+               case 1012:
+               case 1024:
+                       user_mask = SAMR_USER_ACCESS_SET_LOC_COM;
+               case 1051:
+                       user_mask = SAMR_USER_ACCESS_SET_ATTRIBUTES |
+                                   SAMR_USER_ACCESS_GET_GROUPS;
+                       break;
+               case 1:
+               case 2:
+               case 3:
+               case 4:
+               case 21:
+               case 22:
+               case 1005:
+               case 1008:
+               case 1010:
+               case 1017:
+               case 1020:
+                       werr = WERR_NOT_SUPPORTED;
+                       goto done;
+               default:
+                       werr = WERR_UNKNOWN_LEVEL;
+                       goto done;
+       }
+
+       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;
+       }
+
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 |
+                                         SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+                                         &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_OPEN_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,
+                                     user_mask,
+                                     user_rids.ids[0],
+                                     &user_handle);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       status = construct_USER_INFO_X(r->in.level, r->in.buffer, &uX);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       status = set_user_info_USER_INFO_X(ctx, pipe_cli,
+                                          &cli->user_session_key,
+                                          &user_handle,
+                                          &uX);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       werr = WERR_OK;
+
+ done:
+       if (!cli) {
+               return werr;
+       }
+
+       if (is_valid_policy_hnd(&user_handle)) {
+               rpccli_samr_Close(pipe_cli, ctx, &user_handle);
+       }
+
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
+       }
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetUserSetInfo_l(struct libnetapi_ctx *ctx,
+                       struct NetUserSetInfo *r)
+{
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserSetInfo);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS query_USER_MODALS_INFO_rpc(TALLOC_CTX *mem_ctx,
+                                          struct rpc_pipe_client *pipe_cli,
+                                          struct policy_handle *domain_handle,
+                                          struct samr_DomInfo1 *info1,
+                                          struct samr_DomInfo3 *info3,
+                                          struct samr_DomInfo5 *info5,
+                                          struct samr_DomInfo6 *info6,
+                                          struct samr_DomInfo7 *info7,
+                                          struct samr_DomInfo12 *info12)
+{
+       NTSTATUS status;
+       union samr_DomainInfo *dom_info = NULL;
+
+       if (info1) {
+               status = rpccli_samr_QueryDomainInfo(pipe_cli, mem_ctx,
+                                                    domain_handle,
+                                                    1,
+                                                    &dom_info);
+               NT_STATUS_NOT_OK_RETURN(status);
+
+               *info1 = dom_info->info1;
+       }
+
+       if (info3) {
+               status = rpccli_samr_QueryDomainInfo(pipe_cli, mem_ctx,
+                                                    domain_handle,
+                                                    3,
+                                                    &dom_info);
+               NT_STATUS_NOT_OK_RETURN(status);
+
+               *info3 = dom_info->info3;
+       }
+
+       if (info5) {
+               status = rpccli_samr_QueryDomainInfo(pipe_cli, mem_ctx,
+                                                    domain_handle,
+                                                    5,
+                                                    &dom_info);
+               NT_STATUS_NOT_OK_RETURN(status);
+
+               *info5 = dom_info->info5;
+       }
+
+       if (info6) {
+               status = rpccli_samr_QueryDomainInfo(pipe_cli, mem_ctx,
+                                                    domain_handle,
+                                                    6,
+                                                    &dom_info);
+               NT_STATUS_NOT_OK_RETURN(status);
+
+               *info6 = dom_info->info6;
+       }
+
+       if (info7) {
+               status = rpccli_samr_QueryDomainInfo(pipe_cli, mem_ctx,
+                                                    domain_handle,
+                                                    7,
+                                                    &dom_info);
+               NT_STATUS_NOT_OK_RETURN(status);
+
+               *info7 = dom_info->info7;
+       }
+
+       if (info12) {
+               status = rpccli_samr_QueryDomainInfo2(pipe_cli, mem_ctx,
+                                                     domain_handle,
+                                                     12,
+                                                     &dom_info);
+               NT_STATUS_NOT_OK_RETURN(status);
+
+               *info12 = dom_info->info12;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS query_USER_MODALS_INFO_0(TALLOC_CTX *mem_ctx,
+                                        struct rpc_pipe_client *pipe_cli,
+                                        struct policy_handle *domain_handle,
+                                        struct USER_MODALS_INFO_0 *info0)
+{
+       NTSTATUS status;
+       struct samr_DomInfo1 dom_info1;
+       struct samr_DomInfo3 dom_info3;
+
+       ZERO_STRUCTP(info0);
+
+       status = query_USER_MODALS_INFO_rpc(mem_ctx,
+                                           pipe_cli,
+                                           domain_handle,
+                                           &dom_info1,
+                                           &dom_info3,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       info0->usrmod0_min_passwd_len =
+               dom_info1.min_password_length;
+       info0->usrmod0_max_passwd_age =
+               nt_time_to_unix_abs((NTTIME *)&dom_info1.max_password_age);
+       info0->usrmod0_min_passwd_age =
+               nt_time_to_unix_abs((NTTIME *)&dom_info1.min_password_age);
+       info0->usrmod0_password_hist_len =
+               dom_info1.password_history_length;
+
+       info0->usrmod0_force_logoff =
+               nt_time_to_unix_abs(&dom_info3.force_logoff_time);
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS query_USER_MODALS_INFO_1(TALLOC_CTX *mem_ctx,
+                                        struct rpc_pipe_client *pipe_cli,
+                                        struct policy_handle *domain_handle,
+                                        struct USER_MODALS_INFO_1 *info1)
+{
+       NTSTATUS status;
+       struct samr_DomInfo6 dom_info6;
+       struct samr_DomInfo7 dom_info7;
+
+       status = query_USER_MODALS_INFO_rpc(mem_ctx,
+                                           pipe_cli,
+                                           domain_handle,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           &dom_info6,
+                                           &dom_info7,
+                                           NULL);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       info1->usrmod1_primary =
+               talloc_strdup(mem_ctx, dom_info6.primary.string);
+
+       info1->usrmod1_role = dom_info7.role;
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS query_USER_MODALS_INFO_2(TALLOC_CTX *mem_ctx,
+                                        struct rpc_pipe_client *pipe_cli,
+                                        struct policy_handle *domain_handle,
+                                        struct dom_sid *domain_sid,
+                                        struct USER_MODALS_INFO_2 *info2)
+{
+       NTSTATUS status;
+       struct samr_DomInfo5 dom_info5;
+
+       status = query_USER_MODALS_INFO_rpc(mem_ctx,
+                                           pipe_cli,
+                                           domain_handle,
+                                           NULL,
+                                           NULL,
+                                           &dom_info5,
+                                           NULL,
+                                           NULL,
+                                           NULL);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       info2->usrmod2_domain_name =
+               talloc_strdup(mem_ctx, dom_info5.domain_name.string);
+       info2->usrmod2_domain_id =
+               (struct domsid *)sid_dup_talloc(mem_ctx, domain_sid);
+
+       NT_STATUS_HAVE_NO_MEMORY(info2->usrmod2_domain_name);
+       NT_STATUS_HAVE_NO_MEMORY(info2->usrmod2_domain_id);
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS query_USER_MODALS_INFO_3(TALLOC_CTX *mem_ctx,
+                                        struct rpc_pipe_client *pipe_cli,
+                                        struct policy_handle *domain_handle,
+                                        struct USER_MODALS_INFO_3 *info3)
+{
+       NTSTATUS status;
+       struct samr_DomInfo12 dom_info12;
+
+       status = query_USER_MODALS_INFO_rpc(mem_ctx,
+                                           pipe_cli,
+                                           domain_handle,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           &dom_info12);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       info3->usrmod3_lockout_duration =
+               nt_time_to_unix_abs(&dom_info12.lockout_duration);
+       info3->usrmod3_lockout_observation_window =
+               nt_time_to_unix_abs(&dom_info12.lockout_window);
+       info3->usrmod3_lockout_threshold =
+               dom_info12.lockout_threshold;
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS query_USER_MODALS_INFO_to_buffer(TALLOC_CTX *mem_ctx,
+                                                struct rpc_pipe_client *pipe_cli,
+                                                uint32_t level,
+                                                struct policy_handle *domain_handle,
+                                                struct dom_sid *domain_sid,
+                                                uint8_t **buffer)
+{
+       NTSTATUS status;
+
+       struct USER_MODALS_INFO_0 info0;
+       struct USER_MODALS_INFO_1 info1;
+       struct USER_MODALS_INFO_2 info2;
+       struct USER_MODALS_INFO_3 info3;
+
+       if (!buffer) {
+               return ERROR_INSUFFICIENT_BUFFER;
+       }
+
+       switch (level) {
+               case 0:
+                       status = query_USER_MODALS_INFO_0(mem_ctx,
+                                                         pipe_cli,
+                                                         domain_handle,
+                                                         &info0);
+                       NT_STATUS_NOT_OK_RETURN(status);
+
+                       *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info0,
+                                                          sizeof(info0));
+                       break;
+
+               case 1:
+                       status = query_USER_MODALS_INFO_1(mem_ctx,
+                                                         pipe_cli,
+                                                         domain_handle,
+                                                         &info1);
+                       NT_STATUS_NOT_OK_RETURN(status);
+
+                       *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info1,
+                                                          sizeof(info1));
+                       break;
+               case 2:
+                       status = query_USER_MODALS_INFO_2(mem_ctx,
+                                                         pipe_cli,
+                                                         domain_handle,
+                                                         domain_sid,
+                                                         &info2);
+                       NT_STATUS_NOT_OK_RETURN(status);
+
+                       *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info2,
+                                                          sizeof(info2));
+                       break;
+               case 3:
+                       status = query_USER_MODALS_INFO_3(mem_ctx,
+                                                         pipe_cli,
+                                                         domain_handle,
+                                                         &info3);
+                       NT_STATUS_NOT_OK_RETURN(status);
+
+                       *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info3,
+                                                          sizeof(info3));
+                       break;
+               default:
+                       break;
+       }
+
+       NT_STATUS_HAVE_NO_MEMORY(*buffer);
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+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;
+
+       struct policy_handle connect_handle, domain_handle;
+       struct dom_sid2 *domain_sid = NULL;
+       uint32_t access_mask = SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT;
+
+       ZERO_STRUCT(connect_handle);
+       ZERO_STRUCT(domain_handle);
+
+       if (!r->out.buffer) {
+               return WERR_INVALID_PARAM;
+       }
+
+       switch (r->in.level) {
+               case 0:
+                       access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 |
+                                      SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2;
+                       break;
+               case 1:
+               case 2:
+                       access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2;
+                       break;
+               case 3:
+                       access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1;
+                       break;
+               default:
+                       werr = WERR_UNKNOWN_LEVEL;
+                       goto done;
+       }
+
+       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;
+       }
+
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         access_mask,
+                                         &connect_handle,
+                                         &domain_handle,
+                                         &domain_sid);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       /* 0:  1 + 3 */
+       /* 1:  6 + 7 */
+       /* 2:  5 */
+       /* 3: 12 (DomainInfo2) */
+
+       status = query_USER_MODALS_INFO_to_buffer(ctx,
+                                                 pipe_cli,
+                                                 r->in.level,
+                                                 &domain_handle,
+                                                 domain_sid,
+                                                 r->out.buffer);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+ 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);
+       }
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetUserModalsGet_l(struct libnetapi_ctx *ctx,
+                         struct NetUserModalsGet *r)
+{
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserModalsGet);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS set_USER_MODALS_INFO_rpc(TALLOC_CTX *mem_ctx,
+                                        struct rpc_pipe_client *pipe_cli,
+                                        struct policy_handle *domain_handle,
+                                        struct samr_DomInfo1 *info1,
+                                        struct samr_DomInfo3 *info3,
+                                        struct samr_DomInfo12 *info12)
+{
+       NTSTATUS status;
+       union samr_DomainInfo dom_info;
+
+       if (info1) {
+
+               ZERO_STRUCT(dom_info);
+
+               dom_info.info1 = *info1;
+
+               status = rpccli_samr_SetDomainInfo(pipe_cli, mem_ctx,
+                                                  domain_handle,
+                                                  1,
+                                                  &dom_info);
+               NT_STATUS_NOT_OK_RETURN(status);
+       }
+
+       if (info3) {
+
+               ZERO_STRUCT(dom_info);
+
+               dom_info.info3 = *info3;
+
+               status = rpccli_samr_SetDomainInfo(pipe_cli, mem_ctx,
+                                                  domain_handle,
+                                                  3,
+                                                  &dom_info);
+
+               NT_STATUS_NOT_OK_RETURN(status);
+       }
+
+       if (info12) {
+
+               ZERO_STRUCT(dom_info);
+
+               dom_info.info12 = *info12;
+
+               status = rpccli_samr_SetDomainInfo(pipe_cli, mem_ctx,
+                                                  domain_handle,
+                                                  12,
+                                                  &dom_info);
+
+               NT_STATUS_NOT_OK_RETURN(status);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS set_USER_MODALS_INFO_0_buffer(TALLOC_CTX *mem_ctx,
+                                             struct rpc_pipe_client *pipe_cli,
+                                             struct policy_handle *domain_handle,
+                                             struct USER_MODALS_INFO_0 *info0)
+{
+       NTSTATUS status;
+       struct samr_DomInfo1 dom_info_1;
+       struct samr_DomInfo3 dom_info_3;
+
+       status = query_USER_MODALS_INFO_rpc(mem_ctx,
+                                           pipe_cli,
+                                           domain_handle,
+                                           &dom_info_1,
+                                           &dom_info_3,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       dom_info_1.min_password_length =
+               info0->usrmod0_min_passwd_len;
+       dom_info_1.password_history_length =
+               info0->usrmod0_password_hist_len;
+
+       unix_to_nt_time_abs((NTTIME *)&dom_info_1.max_password_age,
+               info0->usrmod0_max_passwd_age);
+       unix_to_nt_time_abs((NTTIME *)&dom_info_1.min_password_age,
+               info0->usrmod0_min_passwd_age);
+
+       unix_to_nt_time_abs(&dom_info_3.force_logoff_time,
+               info0->usrmod0_force_logoff);
+
+       return set_USER_MODALS_INFO_rpc(mem_ctx,
+                                       pipe_cli,
+                                       domain_handle,
+                                       &dom_info_1,
+                                       &dom_info_3,
+                                       NULL);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS set_USER_MODALS_INFO_3_buffer(TALLOC_CTX *mem_ctx,
+                                             struct rpc_pipe_client *pipe_cli,
+                                             struct policy_handle *domain_handle,
+                                             struct USER_MODALS_INFO_3 *info3)
+{
+       NTSTATUS status;
+       struct samr_DomInfo12 dom_info_12;
+
+       status = query_USER_MODALS_INFO_rpc(mem_ctx,
+                                           pipe_cli,
+                                           domain_handle,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           &dom_info_12);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       unix_to_nt_time_abs((NTTIME *)&dom_info_12.lockout_duration,
+               info3->usrmod3_lockout_duration);
+       unix_to_nt_time_abs((NTTIME *)&dom_info_12.lockout_window,
+               info3->usrmod3_lockout_observation_window);
+       dom_info_12.lockout_threshold = info3->usrmod3_lockout_threshold;
+
+       return set_USER_MODALS_INFO_rpc(mem_ctx,
+                                       pipe_cli,
+                                       domain_handle,
+                                       NULL,
+                                       NULL,
+                                       &dom_info_12);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS set_USER_MODALS_INFO_1001_buffer(TALLOC_CTX *mem_ctx,
+                                                struct rpc_pipe_client *pipe_cli,
+                                                struct policy_handle *domain_handle,
+                                                struct USER_MODALS_INFO_1001 *info1001)
+{
+       NTSTATUS status;
+       struct samr_DomInfo1 dom_info_1;
+
+       status = query_USER_MODALS_INFO_rpc(mem_ctx,
+                                           pipe_cli,
+                                           domain_handle,
+                                           &dom_info_1,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       dom_info_1.min_password_length =
+               info1001->usrmod1001_min_passwd_len;
+
+       return set_USER_MODALS_INFO_rpc(mem_ctx,
+                                       pipe_cli,
+                                       domain_handle,
+                                       &dom_info_1,
+                                       NULL,
+                                       NULL);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS set_USER_MODALS_INFO_1002_buffer(TALLOC_CTX *mem_ctx,
+                                                struct rpc_pipe_client *pipe_cli,
+                                                struct policy_handle *domain_handle,
+                                                struct USER_MODALS_INFO_1002 *info1002)
+{
+       NTSTATUS status;
+       struct samr_DomInfo1 dom_info_1;
+
+       status = query_USER_MODALS_INFO_rpc(mem_ctx,
+                                           pipe_cli,
+                                           domain_handle,
+                                           &dom_info_1,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       unix_to_nt_time_abs((NTTIME *)&dom_info_1.max_password_age,
+               info1002->usrmod1002_max_passwd_age);
+
+       return set_USER_MODALS_INFO_rpc(mem_ctx,
+                                       pipe_cli,
+                                       domain_handle,
+                                       &dom_info_1,
+                                       NULL,
+                                       NULL);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS set_USER_MODALS_INFO_1003_buffer(TALLOC_CTX *mem_ctx,
+                                                struct rpc_pipe_client *pipe_cli,
+                                                struct policy_handle *domain_handle,
+                                                struct USER_MODALS_INFO_1003 *info1003)
+{
+       NTSTATUS status;
+       struct samr_DomInfo1 dom_info_1;
+
+       status = query_USER_MODALS_INFO_rpc(mem_ctx,
+                                           pipe_cli,
+                                           domain_handle,
+                                           &dom_info_1,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       unix_to_nt_time_abs((NTTIME *)&dom_info_1.min_password_age,
+               info1003->usrmod1003_min_passwd_age);
+
+       return set_USER_MODALS_INFO_rpc(mem_ctx,
+                                       pipe_cli,
+                                       domain_handle,
+                                       &dom_info_1,
+                                       NULL,
+                                       NULL);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS set_USER_MODALS_INFO_1004_buffer(TALLOC_CTX *mem_ctx,
+                                                struct rpc_pipe_client *pipe_cli,
+                                                struct policy_handle *domain_handle,
+                                                struct USER_MODALS_INFO_1004 *info1004)
+{
+       NTSTATUS status;
+       struct samr_DomInfo3 dom_info_3;
+
+       status = query_USER_MODALS_INFO_rpc(mem_ctx,
+                                           pipe_cli,
+                                           domain_handle,
+                                           NULL,
+                                           &dom_info_3,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       unix_to_nt_time_abs(&dom_info_3.force_logoff_time,
+               info1004->usrmod1004_force_logoff);
+
+       return set_USER_MODALS_INFO_rpc(mem_ctx,
+                                       pipe_cli,
+                                       domain_handle,
+                                       NULL,
+                                       &dom_info_3,
+                                       NULL);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS set_USER_MODALS_INFO_1005_buffer(TALLOC_CTX *mem_ctx,
+                                                struct rpc_pipe_client *pipe_cli,
+                                                struct policy_handle *domain_handle,
+                                                struct USER_MODALS_INFO_1005 *info1005)
+{
+       NTSTATUS status;
+       struct samr_DomInfo1 dom_info_1;
+
+       status = query_USER_MODALS_INFO_rpc(mem_ctx,
+                                           pipe_cli,
+                                           domain_handle,
+                                           &dom_info_1,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       dom_info_1.password_history_length =
+               info1005->usrmod1005_password_hist_len;
+
+       return set_USER_MODALS_INFO_rpc(mem_ctx,
+                                       pipe_cli,
+                                       domain_handle,
+                                       &dom_info_1,
+                                       NULL,
+                                       NULL);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS set_USER_MODALS_INFO_buffer(TALLOC_CTX *mem_ctx,
+                                           struct rpc_pipe_client *pipe_cli,
+                                           uint32_t level,
+                                           struct policy_handle *domain_handle,
+                                           struct dom_sid *domain_sid,
+                                           uint8_t *buffer)
+{
+       struct USER_MODALS_INFO_0 *info0;
+       struct USER_MODALS_INFO_3 *info3;
+       struct USER_MODALS_INFO_1001 *info1001;
+       struct USER_MODALS_INFO_1002 *info1002;
+       struct USER_MODALS_INFO_1003 *info1003;
+       struct USER_MODALS_INFO_1004 *info1004;
+       struct USER_MODALS_INFO_1005 *info1005;
+
+       if (!buffer) {
+               return ERROR_INSUFFICIENT_BUFFER;
+       }
+
+       switch (level) {
+               case 0:
+                       info0 = (struct USER_MODALS_INFO_0 *)buffer;
+                       return set_USER_MODALS_INFO_0_buffer(mem_ctx,
+                                                            pipe_cli,
+                                                            domain_handle,
+                                                            info0);
+               case 3:
+                       info3 = (struct USER_MODALS_INFO_3 *)buffer;
+                       return set_USER_MODALS_INFO_3_buffer(mem_ctx,
+                                                            pipe_cli,
+                                                            domain_handle,
+                                                            info3);
+               case 1001:
+                       info1001 = (struct USER_MODALS_INFO_1001 *)buffer;
+                       return set_USER_MODALS_INFO_1001_buffer(mem_ctx,
+                                                               pipe_cli,
+                                                               domain_handle,
+                                                               info1001);
+               case 1002:
+                       info1002 = (struct USER_MODALS_INFO_1002 *)buffer;
+                       return set_USER_MODALS_INFO_1002_buffer(mem_ctx,
+                                                               pipe_cli,
+                                                               domain_handle,
+                                                               info1002);
+               case 1003:
+                       info1003 = (struct USER_MODALS_INFO_1003 *)buffer;
+                       return set_USER_MODALS_INFO_1003_buffer(mem_ctx,
+                                                               pipe_cli,
+                                                               domain_handle,
+                                                               info1003);
+               case 1004:
+                       info1004 = (struct USER_MODALS_INFO_1004 *)buffer;
+                       return set_USER_MODALS_INFO_1004_buffer(mem_ctx,
+                                                               pipe_cli,
+                                                               domain_handle,
+                                                               info1004);
+               case 1005:
+                       info1005 = (struct USER_MODALS_INFO_1005 *)buffer;
+                       return set_USER_MODALS_INFO_1005_buffer(mem_ctx,
+                                                               pipe_cli,
+                                                               domain_handle,
+                                                               info1005);
+
+               default:
+                       break;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+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;
+
+       struct policy_handle connect_handle, domain_handle;
+       struct dom_sid2 *domain_sid = NULL;
+       uint32_t access_mask = SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT;
+
+       ZERO_STRUCT(connect_handle);
+       ZERO_STRUCT(domain_handle);
+
+       if (!r->in.buffer) {
+               return WERR_INVALID_PARAM;
+       }
+
+       switch (r->in.level) {
+               case 0:
+                       access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 |
+                                      SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 |
+                                      SAMR_DOMAIN_ACCESS_SET_INFO_1 |
+                                      SAMR_DOMAIN_ACCESS_SET_INFO_2;
+                       break;
+               case 3:
+               case 1001:
+               case 1002:
+               case 1003:
+               case 1005:
+                       access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 |
+                                      SAMR_DOMAIN_ACCESS_SET_INFO_1;
+                       break;
+               case 1004:
+                       access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 |
+                                      SAMR_DOMAIN_ACCESS_SET_INFO_2;
+                       break;
+               case 1:
+               case 2:
+               case 1006:
+               case 1007:
+                       werr = WERR_NOT_SUPPORTED;
+                       break;
+               default:
+                       werr = WERR_UNKNOWN_LEVEL;
+                       goto done;
+       }
+
+       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;
+       }
+
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         access_mask,
+                                         &connect_handle,
+                                         &domain_handle,
+                                         &domain_sid);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       status = set_USER_MODALS_INFO_buffer(ctx,
+                                            pipe_cli,
+                                            r->in.level,
+                                            &domain_handle,
+                                            domain_sid,
+                                            r->in.buffer);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+ 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);
+       }
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetUserModalsSet_l(struct libnetapi_ctx *ctx,
+                         struct NetUserModalsSet *r)
+{
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserModalsSet);
+}
+
+/****************************************************************
+****************************************************************/
+
+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)
+{
+       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);
+
+                       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);
+
+                       u1.grui1_attributes = attributes;
+
+                       ADD_TO_ARRAY(mem_ctx, struct GROUP_USERS_INFO_1, u1,
+                                    (struct GROUP_USERS_INFO_1 **)buffer, num_entries);
+                       break;
+               default:
+                       return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+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;
+       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;
+
+       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;
+
+       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,
+                                  &cli,
+                                  &pipe_cli);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_OPEN_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;
+       }
+
+       status = rpccli_samr_GetGroupsForUser(pipe_cli, ctx,
+                                             &user_handle,
+                                             &rid_array);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       rids = talloc_array(ctx, uint32_t, rid_array->count);
+       if (!rids) {
+               werr = WERR_NOMEM;
+               goto done;
+       }
+
+       for (i=0; i < rid_array->count; i++) {
+               rids[i] = rid_array->rids[i].rid;
+       }
+
+       status = rpccli_samr_LookupRids(pipe_cli, ctx,
+                                       &domain_handle,
+                                       rid_array->count,
+                                       rids,
+                                       &names,
+                                       &types);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       for (i=0; i < rid_array->count; i++) {
+               status = add_GROUP_USERS_INFO_X_buffer(ctx,
+                                                      r->in.level,
+                                                      names.names[i].string,
+                                                      rid_array->rids[i].attributes,
+                                                      r->out.buffer,
+                                                      &entries_read);
+               if (!NT_STATUS_IS_OK(status)) {
+                       werr = ntstatus_to_werror(status);
+                       goto done;
+               }
+       }
+
+       if (r->out.entries_read) {
+               *r->out.entries_read = entries_read;
+       }
+       if (r->out.total_entries) {
+               *r->out.total_entries = entries_read;
+       }
+
+ 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);
+       }
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetUserGetGroups_l(struct libnetapi_ctx *ctx,
+                         struct NetUserGetGroups *r)
+{
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserGetGroups);
+}