2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Server Service RPC (SRVSVC) server-side interface definition.
2N/A * The server service provides a remote administration interface.
2N/A *
2N/A * This service uses NERR/Win32 error codes rather than NT status
2N/A * values.
2N/A */
2N/A
2N/A#include <sys/errno.h>
2N/A#include <sys/tzfile.h>
2N/A#include <unistd.h>
2N/A#include <netdb.h>
2N/A#include <strings.h>
2N/A#include <time.h>
2N/A#include <thread.h>
2N/A#include <ctype.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <sys/types.h>
2N/A#include <sys/socket.h>
2N/A#include <netinet/in.h>
2N/A#include <arpa/inet.h>
2N/A#include <libshare.h>
2N/A#include <libnvpair.h>
2N/A#include <sys/idmap.h>
2N/A#include <pwd.h>
2N/A#include <nss_dbdefs.h>
2N/A#include <smbsrv/libsmb.h>
2N/A#include <smbsrv/libntsvcs.h>
2N/A#include <smb/nmpipes.h>
2N/A#include <smb/smb.h>
2N/A#include <smbsrv/netrauth.h>
2N/A#include <smbsrv/ndl/srvsvc.ndl>
2N/A#include "ntsvcs.h"
2N/A
2N/A#define SMB_SRVSVC_MAXBUFLEN (8 * 1024 * 1024)
2N/A#define SMB_SRVSVC_MAXPREFLEN ((uint32_t)(-1))
2N/A
2N/Atypedef struct srvsvc_sd {
2N/A uint8_t *sd_buf;
2N/A uint32_t sd_size;
2N/A} srvsvc_sd_t;
2N/A
2N/Atypedef struct srvsvc_netshare_setinfo {
2N/A char *nss_netname;
2N/A char *nss_comment;
2N/A char *nss_path;
2N/A uint32_t nss_type;
2N/A srvsvc_sd_t nss_sd;
2N/A} srvsvc_netshare_setinfo_t;
2N/A
2N/Atypedef union srvsvc_netshare_getinfo {
2N/A struct mslm_NetShareInfo_0 nsg_info0;
2N/A struct mslm_NetShareInfo_1 nsg_info1;
2N/A struct mslm_NetShareInfo_2 nsg_info2;
2N/A struct mslm_NetShareInfo_501 nsg_info501;
2N/A struct mslm_NetShareInfo_502 nsg_info502;
2N/A struct mslm_NetShareInfo_503 nsg_info503;
2N/A struct mslm_NetShareInfo_1004 nsg_info1004;
2N/A struct mslm_NetShareInfo_1005 nsg_info1005;
2N/A struct mslm_NetShareInfo_1006 nsg_info1006;
2N/A struct mslm_NetShareInfo_1501 nsg_info1501;
2N/A} srvsvc_netshare_getinfo_t;
2N/A
2N/Atypedef struct mslm_infonres srvsvc_infonres_t;
2N/Atypedef struct mslm_NetConnectEnum srvsvc_NetConnectEnum_t;
2N/A
2N/Astatic uint32_t srvsvc_netconnectenum_level0(ndr_xa_t *, smb_svcenum_t *,
2N/A srvsvc_NetConnectEnum_t *);
2N/Astatic uint32_t srvsvc_netconnectenum_level1(ndr_xa_t *, smb_svcenum_t *,
2N/A srvsvc_NetConnectEnum_t *);
2N/Astatic uint32_t srvsvc_netconnectenum_common(ndr_xa_t *,
2N/A srvsvc_NetConnectInfo_t *, smb_netsvc_t *, smb_svcenum_t *);
2N/A
2N/Astatic DWORD srvsvc_NetFileEnum2(ndr_xa_t *, struct mslm_NetFileEnum *,
2N/A smb_svcenum_t *se);
2N/Astatic DWORD srvsvc_NetFileEnum3(ndr_xa_t *, struct mslm_NetFileEnum *,
2N/A smb_svcenum_t *se);
2N/A
2N/Astatic uint32_t srvsvc_NetSessionEnumCommon(ndr_xa_t *, srvsvc_infonres_t *,
2N/A smb_netsvc_t *, smb_svcenum_t *);
2N/A
2N/Astatic DWORD mlsvc_NetShareEnumCommon(ndr_xa_t *, smb_svcenum_t *,
2N/A smb_share_t *, void *);
2N/Astatic uint32_t srvsvc_share_getsd(ndr_xa_t *, smb_share_t *, srvsvc_sd_t *);
2N/Astatic int srvsvc_share_enum(void *, ndr_xa_t *, boolean_t);
2N/Astatic smb_netsvc_t *srvsvc_shareenum_init(smb_svcenum_t *, smb_netuserinfo_t *,
2N/A boolean_t);
2N/A
2N/Astatic int srvsvc_netconnect_qualifier(smb_svcenum_t *, const char *);
2N/Astatic void srvsvc_estimate_limit(smb_svcenum_t *, uint32_t);
2N/Astatic uint32_t srvsvc_get_realpath(const char *, char *, int);
2N/Astatic uint32_t srvsvc_open_sessions(void);
2N/Astatic uint32_t srvsvc_open_connections(void);
2N/Astatic uint32_t srvsvc_open_files(void);
2N/A
2N/Astatic uint32_t srvsvc_modify_share(smb_share_t *,
2N/A srvsvc_netshare_setinfo_t *);
2N/Astatic uint32_t srvsvc_modify_transient_share(smb_share_t *,
2N/A srvsvc_netshare_setinfo_t *);
2N/Astatic uint32_t srvsvc_update_share_flags(smb_share_t *, uint32_t);
2N/Astatic uint32_t srvsvc_get_share_flags(smb_share_t *);
2N/A
2N/Astatic boolean_t srvsvc_share_is_restricted(const char *);
2N/Astatic boolean_t srvsvc_share_is_admin(const char *);
2N/A
2N/Astatic uint32_t srvsvc_sa_add(char *, char *, char *);
2N/Astatic uint32_t srvsvc_sa_delete(const char *, const char *);
2N/Astatic uint32_t srvsvc_sa_modify(smb_share_t *, srvsvc_netshare_setinfo_t *);
2N/Astatic uint32_t srvsvc_sa_setprop(smb_share_t *, nvlist_t *);
2N/A
2N/Astatic ndr_stub_table_t srvsvc_stub_table[];
2N/A
2N/Astatic ndr_service_t srvsvc_service = {
2N/A "SRVSVC", /* name */
2N/A "Server services", /* desc */
2N/A "\\srvsvc", /* endpoint */
2N/A PIPE_NTSVCS, /* sec_addr_port */
2N/A "4b324fc8-1670-01d3-1278-5a47bf6ee188", 3, /* abstract */
2N/A NDR_TRANSFER_SYNTAX_UUID, 2, /* transfer */
2N/A 0, /* no bind_instance_size */
2N/A 0, /* no bind_req() */
2N/A 0, /* no unbind_and_close() */
2N/A 0, /* use generic_call_stub() */
2N/A &TYPEINFO(srvsvc_interface), /* interface ti */
2N/A srvsvc_stub_table /* stub_table */
2N/A};
2N/A
2N/A/*
2N/A * srvsvc_initialize
2N/A *
2N/A * This function registers the SRVSVC RPC interface with the RPC runtime
2N/A * library. It must be called in order to use either the client side
2N/A * or the server side functions.
2N/A */
2N/Avoid
2N/Asrvsvc_initialize(void)
2N/A{
2N/A (void) ndr_svc_register(&srvsvc_service);
2N/A}
2N/A
2N/A/*
2N/A * Turn "dfsroot" property on/off for the specified
2N/A * share and save it.
2N/A *
2N/A * If the requested value is the same as what is already
2N/A * set then no change is required and the function returns.
2N/A */
2N/Auint32_t
2N/Asrvsvc_shr_setdfsroot(smb_share_t *si, boolean_t on)
2N/A{
2N/A char *dfs = NULL;
2N/A nvlist_t *nvl;
2N/A uint32_t nerr;
2N/A
2N/A if (on && ((si->shr_flags & SMB_SHRF_DFSROOT) == 0)) {
2N/A si->shr_flags |= SMB_SHRF_DFSROOT;
2N/A dfs = "true";
2N/A } else if (!on && (si->shr_flags & SMB_SHRF_DFSROOT)) {
2N/A si->shr_flags &= ~SMB_SHRF_DFSROOT;
2N/A dfs = "false";
2N/A }
2N/A
2N/A if (dfs == NULL)
2N/A return (ERROR_SUCCESS);
2N/A
2N/A if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
2N/A return (NERR_InternalError);
2N/A
2N/A if (nvlist_add_string(nvl, SHOPT_DFSROOT, dfs) != 0) {
2N/A nvlist_free(nvl);
2N/A return (NERR_InternalError);
2N/A }
2N/A
2N/A nerr = srvsvc_sa_setprop(si, nvl);
2N/A nvlist_free(nvl);
2N/A
2N/A return (nerr);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_s_NetConnectEnum
2N/A *
2N/A * List tree connections made to a share on this server or all tree
2N/A * connections established from a specific client. Administrator,
2N/A * Server Operator, Print Operator or Power User group membership
2N/A * is required to use this interface.
2N/A *
2N/A * There are three information levels: 0, 1, and 50. We don't support
2N/A * level 50, which is only used by Windows 9x clients.
2N/A *
2N/A * It seems Server Manger (srvmgr) only sends workstation as the qualifier
2N/A * and the Computer Management Interface on Windows 2000 doesn't request
2N/A * a list of connections.
2N/A *
2N/A * Return Values:
2N/A * ERROR_SUCCESS Success
2N/A * ERROR_ACCESS_DENIED Caller does not have access to this call.
2N/A * ERROR_INVALID_PARAMETER One of the parameters is invalid.
2N/A * ERROR_INVALID_LEVEL Unknown information level specified.
2N/A * ERROR_MORE_DATA Partial date returned, more entries available.
2N/A * ERROR_NOT_ENOUGH_MEMORY Insufficient memory is available.
2N/A * NERR_NetNameNotFound The share qualifier cannot be found.
2N/A * NERR_BufTooSmall The supplied buffer is too small.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetConnectEnum(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A srvsvc_NetConnectEnum_t *param = arg;
2N/A smb_netsvc_t *ns;
2N/A smb_svcenum_t se;
2N/A char *qualifier;
2N/A DWORD status = ERROR_SUCCESS;
2N/A
2N/A if (!ndr_is_poweruser(mxa)) {
2N/A status = ERROR_ACCESS_DENIED;
2N/A goto srvsvc_netconnectenum_error;
2N/A }
2N/A
2N/A param->total_entries = srvsvc_open_connections();
2N/A if (param->total_entries == 0) {
2N/A bzero(param, sizeof (srvsvc_NetConnectEnum_t));
2N/A param->status = ERROR_SUCCESS;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A bzero(&se, sizeof (smb_svcenum_t));
2N/A se.se_type = SMB_SVCENUM_TYPE_TREE;
2N/A se.se_level = param->info.level;
2N/A se.se_ntotal = param->total_entries;
2N/A se.se_nlimit = se.se_ntotal;
2N/A
2N/A if (param->pref_max_len == SMB_SRVSVC_MAXPREFLEN ||
2N/A param->pref_max_len > SMB_SRVSVC_MAXBUFLEN)
2N/A se.se_prefmaxlen = SMB_SRVSVC_MAXBUFLEN;
2N/A else
2N/A se.se_prefmaxlen = param->pref_max_len;
2N/A
2N/A if (param->resume_handle) {
2N/A se.se_resume = *param->resume_handle;
2N/A se.se_nskip = se.se_resume;
2N/A *param->resume_handle = 0;
2N/A }
2N/A
2N/A switch (param->info.level) {
2N/A case 0:
2N/A status = srvsvc_netconnectenum_level0(mxa, &se, param);
2N/A break;
2N/A case 1:
2N/A status = srvsvc_netconnectenum_level1(mxa, &se, param);
2N/A break;
2N/A case 50:
2N/A status = ERROR_NOT_SUPPORTED;
2N/A break;
2N/A default:
2N/A status = ERROR_INVALID_LEVEL;
2N/A break;
2N/A }
2N/A
2N/A if (status != ERROR_SUCCESS)
2N/A goto srvsvc_netconnectenum_error;
2N/A
2N/A qualifier = (char *)param->qualifier;
2N/A if (srvsvc_netconnect_qualifier(&se, qualifier) != 0) {
2N/A status = NERR_NetNameNotFound;
2N/A goto srvsvc_netconnectenum_error;
2N/A }
2N/A
2N/A if ((ns = smb_kmod_enum_init(&se)) == NULL) {
2N/A status = ERROR_NOT_ENOUGH_MEMORY;
2N/A goto srvsvc_netconnectenum_error;
2N/A }
2N/A
2N/A status = srvsvc_netconnectenum_common(mxa, &param->info, ns, &se);
2N/A smb_kmod_enum_fini(ns);
2N/A
2N/A if (status != ERROR_SUCCESS)
2N/A goto srvsvc_netconnectenum_error;
2N/A
2N/A if (param->resume_handle &&
2N/A param->pref_max_len != SMB_SRVSVC_MAXPREFLEN) {
2N/A if (se.se_resume < param->total_entries) {
2N/A *param->resume_handle = se.se_resume;
2N/A status = ERROR_MORE_DATA;
2N/A }
2N/A }
2N/A
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A
2N/Asrvsvc_netconnectenum_error:
2N/A bzero(param, sizeof (srvsvc_NetConnectEnum_t));
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * Allocate memory and estimate the number of objects that can
2N/A * be returned for NetConnectEnum level 0.
2N/A */
2N/Astatic uint32_t
2N/Asrvsvc_netconnectenum_level0(ndr_xa_t *mxa, smb_svcenum_t *se,
2N/A srvsvc_NetConnectEnum_t *param)
2N/A{
2N/A srvsvc_NetConnectInfo0_t *info0;
2N/A srvsvc_NetConnectInfoBuf0_t *ci0;
2N/A
2N/A if ((info0 = NDR_NEW(mxa, srvsvc_NetConnectInfo0_t)) == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A bzero(info0, sizeof (srvsvc_NetConnectInfo0_t));
2N/A param->info.ru.info0 = info0;
2N/A
2N/A srvsvc_estimate_limit(se, sizeof (srvsvc_NetConnectInfoBuf0_t));
2N/A if (se->se_nlimit == 0)
2N/A return (NERR_BufTooSmall);
2N/A
2N/A do {
2N/A ci0 = NDR_NEWN(mxa, srvsvc_NetConnectInfoBuf0_t, se->se_nlimit);
2N/A if (ci0 == NULL)
2N/A se->se_nlimit >>= 1;
2N/A } while ((se->se_nlimit > 0) && (ci0 == NULL));
2N/A
2N/A if (ci0 == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A info0->ci0 = ci0;
2N/A info0->entries_read = 0;
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Allocate memory and estimate the number of objects that can
2N/A * be returned for NetConnectEnum level 1.
2N/A */
2N/Astatic uint32_t
2N/Asrvsvc_netconnectenum_level1(ndr_xa_t *mxa, smb_svcenum_t *se,
2N/A srvsvc_NetConnectEnum_t *param)
2N/A{
2N/A srvsvc_NetConnectInfo1_t *info1;
2N/A srvsvc_NetConnectInfoBuf1_t *ci1;
2N/A
2N/A if ((info1 = NDR_NEW(mxa, srvsvc_NetConnectInfo1_t)) == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A bzero(info1, sizeof (srvsvc_NetConnectInfo1_t));
2N/A param->info.ru.info1 = info1;
2N/A
2N/A srvsvc_estimate_limit(se,
2N/A sizeof (srvsvc_NetConnectInfoBuf1_t) + MAXNAMELEN);
2N/A if (se->se_nlimit == 0)
2N/A return (NERR_BufTooSmall);
2N/A
2N/A do {
2N/A ci1 = NDR_NEWN(mxa, srvsvc_NetConnectInfoBuf1_t, se->se_nlimit);
2N/A if (ci1 == NULL)
2N/A se->se_nlimit >>= 1;
2N/A } while ((se->se_nlimit > 0) && (ci1 == NULL));
2N/A
2N/A if (ci1 == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A info1->ci1 = ci1;
2N/A info1->entries_read = 0;
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Request a list of connections from the kernel and set up
2N/A * the connection information to be returned to the client.
2N/A */
2N/Astatic uint32_t
2N/Asrvsvc_netconnectenum_common(ndr_xa_t *mxa, srvsvc_NetConnectInfo_t *info,
2N/A smb_netsvc_t *ns, smb_svcenum_t *se)
2N/A{
2N/A srvsvc_NetConnectInfo0_t *info0;
2N/A srvsvc_NetConnectInfo1_t *info1;
2N/A srvsvc_NetConnectInfoBuf0_t *ci0;
2N/A srvsvc_NetConnectInfoBuf1_t *ci1;
2N/A smb_netsvcitem_t *item;
2N/A smb_netconnectinfo_t *tree;
2N/A
2N/A if (smb_kmod_enum(ns) != 0)
2N/A return (ERROR_INTERNAL_ERROR);
2N/A
2N/A info0 = info->ru.info0;
2N/A ci0 = info0->ci0;
2N/A
2N/A info1 = info->ru.info1;
2N/A ci1 = info1->ci1;
2N/A
2N/A item = list_head(&ns->ns_list);
2N/A while (item != NULL) {
2N/A tree = &item->nsi_un.nsi_tree;
2N/A
2N/A switch (se->se_level) {
2N/A case 0:
2N/A ci0->coni0_id = tree->ci_id;
2N/A ++ci0;
2N/A ++info0->entries_read;
2N/A break;
2N/A case 1:
2N/A ci1->coni1_id = tree->ci_id;
2N/A ci1->coni1_type = tree->ci_type;
2N/A ci1->coni1_num_opens = tree->ci_numopens;
2N/A ci1->coni1_num_users = tree->ci_numusers;
2N/A ci1->coni1_time = tree->ci_time;
2N/A ci1->coni1_username = (uint8_t *)
2N/A NDR_STRDUP(mxa, tree->ci_username);
2N/A ci1->coni1_netname = (uint8_t *)
2N/A NDR_STRDUP(mxa, tree->ci_share);
2N/A ++ci1;
2N/A ++info1->entries_read;
2N/A break;
2N/A default:
2N/A return (ERROR_INVALID_LEVEL);
2N/A }
2N/A
2N/A ++se->se_resume;
2N/A item = list_next(&ns->ns_list, item);
2N/A }
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_netconnect_qualifier
2N/A *
2N/A * The qualifier is a string that specifies a share name or computer name
2N/A * for the connections of interest. If it is a share name then all the
2N/A * connections made to that share name are listed. If it is a computer
2N/A * name (it starts with two backslash characters), then NetConnectEnum
2N/A * lists all connections made from that computer to the specified server.
2N/A */
2N/Astatic int
2N/Asrvsvc_netconnect_qualifier(smb_svcenum_t *se, const char *qualifier)
2N/A{
2N/A if (qualifier == NULL || *qualifier == '\0')
2N/A return (-1);
2N/A
2N/A if (strlen(qualifier) > MAXHOSTNAMELEN)
2N/A return (-1);
2N/A
2N/A (void) strlcpy(se->se_qualifier.seq_qualstr, qualifier,
2N/A sizeof (se->se_qualifier.seq_qualstr));
2N/A if (qualifier[0] == '\\' && qualifier[1] == '\\') {
2N/A se->se_qualifier.seq_mode = SMB_SVCENUM_CONNECT_WKSTN;
2N/A } else {
2N/A if (!smb_share_exists((char *)qualifier))
2N/A return (-1);
2N/A
2N/A se->se_qualifier.seq_mode = SMB_SVCENUM_CONNECT_SHARE;
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic uint32_t
2N/Asrvsvc_open_sessions(void)
2N/A{
2N/A smb_opennum_t opennum;
2N/A
2N/A bzero(&opennum, sizeof (smb_opennum_t));
2N/A if (smb_kmod_get_open_num(&opennum) != 0)
2N/A return (0);
2N/A
2N/A return (opennum.open_users);
2N/A}
2N/A
2N/Astatic uint32_t
2N/Asrvsvc_open_connections(void)
2N/A{
2N/A smb_opennum_t opennum;
2N/A
2N/A bzero(&opennum, sizeof (smb_opennum_t));
2N/A if (smb_kmod_get_open_num(&opennum) != 0)
2N/A return (0);
2N/A
2N/A return (opennum.open_trees);
2N/A}
2N/A
2N/Astatic uint32_t
2N/Asrvsvc_open_files(void)
2N/A{
2N/A smb_opennum_t opennum;
2N/A
2N/A bzero(&opennum, sizeof (smb_opennum_t));
2N/A if (smb_kmod_get_open_num(&opennum) != 0)
2N/A return (0);
2N/A
2N/A return (opennum.open_files);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_s_NetFileEnum
2N/A *
2N/A * Return information on open files or named pipes. Only members of the
2N/A * Administrators or Server Operators local groups are allowed to make
2N/A * this call. Currently, we only support Administrators.
2N/A *
2N/A * If basepath is null, all open resources are enumerated. If basepath
2N/A * is non-null, only resources that have basepath as a prefix should
2N/A * be returned.
2N/A *
2N/A * If username is specified (non-null), only files opened by username
2N/A * should be returned.
2N/A *
2N/A * Notes:
2N/A * 1. We don't validate the servername because we would have to check
2N/A * all primary IPs and the ROI seems unlikely to be worth it.
2N/A * 2. Both basepath and username are currently ignored because both
2N/A * Server Manger (NT 4.0) and CMI (Windows 2000) always set them to null.
2N/A *
2N/A * The level of information requested may be one of:
2N/A *
2N/A * 2 Return the file identification number.
2N/A * This level is not supported on Windows Me/98/95.
2N/A *
2N/A * 3 Return information about the file.
2N/A * This level is not supported on Windows Me/98/95.
2N/A *
2N/A * 50 Windows Me/98/95: Return information about the file.
2N/A *
2N/A * Note:
2N/A * If pref_max_len is unlimited and resume_handle is null, the client
2N/A * expects to receive all data in a single call.
2N/A * If we are unable to do fit all data in a single response, we would
2N/A * normally return ERROR_MORE_DATA with a partial list.
2N/A *
2N/A * Unfortunately, when both of these conditions occur, Server Manager
2N/A * pops up an error box with the message "more data available" and
2N/A * doesn't display any of the returned data. In this case, it is
2N/A * probably better to return ERROR_SUCCESS with the partial list.
2N/A * Windows 2000 doesn't have this problem because it always sends a
2N/A * non-null resume_handle.
2N/A *
2N/A * Return Values:
2N/A * ERROR_SUCCESS Success
2N/A * ERROR_ACCESS_DENIED Caller does not have access to this call.
2N/A * ERROR_INVALID_PARAMETER One of the parameters is invalid.
2N/A * ERROR_INVALID_LEVEL Unknown information level specified.
2N/A * ERROR_MORE_DATA Partial date returned, more entries available.
2N/A * ERROR_NOT_ENOUGH_MEMORY Insufficient memory is available.
2N/A * NERR_BufTooSmall The supplied buffer is too small.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetFileEnum(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct mslm_NetFileEnum *param = arg;
2N/A smb_svcenum_t se;
2N/A DWORD status;
2N/A
2N/A if (!ndr_is_admin(mxa)) {
2N/A bzero(param, sizeof (struct mslm_NetFileEnum));
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A if ((param->total_entries = srvsvc_open_files()) == 0) {
2N/A bzero(param, sizeof (struct mslm_NetFileEnum));
2N/A param->status = ERROR_SUCCESS;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A bzero(&se, sizeof (smb_svcenum_t));
2N/A se.se_type = SMB_SVCENUM_TYPE_FILE;
2N/A se.se_level = param->info.switch_value;
2N/A se.se_ntotal = param->total_entries;
2N/A se.se_nlimit = se.se_ntotal;
2N/A
2N/A if (param->pref_max_len == SMB_SRVSVC_MAXPREFLEN ||
2N/A param->pref_max_len > SMB_SRVSVC_MAXBUFLEN)
2N/A se.se_prefmaxlen = SMB_SRVSVC_MAXBUFLEN;
2N/A else
2N/A se.se_prefmaxlen = param->pref_max_len;
2N/A
2N/A if (param->resume_handle) {
2N/A se.se_resume = *param->resume_handle;
2N/A se.se_nskip = se.se_resume;
2N/A *param->resume_handle = 0;
2N/A }
2N/A
2N/A switch (param->info.switch_value) {
2N/A case 2:
2N/A status = srvsvc_NetFileEnum2(mxa, param, &se);
2N/A break;
2N/A
2N/A case 3:
2N/A status = srvsvc_NetFileEnum3(mxa, param, &se);
2N/A break;
2N/A
2N/A case 50:
2N/A status = ERROR_NOT_SUPPORTED;
2N/A break;
2N/A
2N/A default:
2N/A status = ERROR_INVALID_LEVEL;
2N/A break;
2N/A }
2N/A
2N/A if (status != ERROR_SUCCESS) {
2N/A bzero(param, sizeof (struct mslm_NetFileEnum));
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A if (param->resume_handle &&
2N/A param->pref_max_len != SMB_SRVSVC_MAXPREFLEN) {
2N/A if (se.se_resume < param->total_entries) {
2N/A *param->resume_handle = se.se_resume;
2N/A status = ERROR_MORE_DATA;
2N/A }
2N/A }
2N/A
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * Build level 2 file information.
2N/A *
2N/A * SMB fids are 16-bit values but this interface expects 32-bit file ids.
2N/A * So we use the uniqid here.
2N/A *
2N/A * On success, the caller expects that the info2, fi2 and entries_read
2N/A * fields have been set up.
2N/A */
2N/Astatic DWORD
2N/Asrvsvc_NetFileEnum2(ndr_xa_t *mxa, struct mslm_NetFileEnum *param,
2N/A smb_svcenum_t *se)
2N/A{
2N/A struct mslm_NetFileInfoBuf2 *fi2;
2N/A smb_netsvc_t *ns;
2N/A smb_netsvcitem_t *item;
2N/A smb_netfileinfo_t *ofile;
2N/A uint32_t entries_read = 0;
2N/A
2N/A param->info.ru.info2 = NDR_NEW(mxa, struct mslm_NetFileInfo2);
2N/A if (param->info.ru.info2 == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A srvsvc_estimate_limit(se, sizeof (struct mslm_NetFileInfoBuf2));
2N/A if (se->se_nlimit == 0)
2N/A return (NERR_BufTooSmall);
2N/A
2N/A do {
2N/A fi2 = NDR_NEWN(mxa, struct mslm_NetFileInfoBuf2, se->se_nlimit);
2N/A if (fi2 == NULL)
2N/A se->se_nlimit >>= 1;
2N/A } while ((se->se_nlimit > 0) && (fi2 == NULL));
2N/A
2N/A if (fi2 == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A param->info.ru.info2->fi2 = fi2;
2N/A
2N/A if ((ns = smb_kmod_enum_init(se)) == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A if (smb_kmod_enum(ns) != 0) {
2N/A smb_kmod_enum_fini(ns);
2N/A return (ERROR_INTERNAL_ERROR);
2N/A }
2N/A
2N/A item = list_head(&ns->ns_list);
2N/A while (item != NULL) {
2N/A ofile = &item->nsi_un.nsi_ofile;
2N/A fi2->fi2_id = ofile->fi_uniqid;
2N/A
2N/A ++entries_read;
2N/A ++fi2;
2N/A item = list_next(&ns->ns_list, item);
2N/A }
2N/A
2N/A se->se_resume += entries_read;
2N/A param->info.ru.info2->entries_read = entries_read;
2N/A smb_kmod_enum_fini(ns);
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Build level 3 file information.
2N/A *
2N/A * SMB fids are 16-bit values but this interface expects 32-bit file ids.
2N/A * So we use the uniqid here.
2N/A *
2N/A * On success, the caller expects that the info3, fi3 and entries_read
2N/A * fields have been set up.
2N/A */
2N/Astatic DWORD
2N/Asrvsvc_NetFileEnum3(ndr_xa_t *mxa, struct mslm_NetFileEnum *param,
2N/A smb_svcenum_t *se)
2N/A{
2N/A struct mslm_NetFileInfoBuf3 *fi3;
2N/A smb_netsvc_t *ns;
2N/A smb_netsvcitem_t *item;
2N/A smb_netfileinfo_t *ofile;
2N/A uint32_t entries_read = 0;
2N/A
2N/A param->info.ru.info3 = NDR_NEW(mxa, struct mslm_NetFileInfo3);
2N/A if (param->info.ru.info3 == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A srvsvc_estimate_limit(se,
2N/A sizeof (struct mslm_NetFileInfoBuf3) + MAXNAMELEN);
2N/A if (se->se_nlimit == 0)
2N/A return (NERR_BufTooSmall);
2N/A
2N/A do {
2N/A fi3 = NDR_NEWN(mxa, struct mslm_NetFileInfoBuf3, se->se_nlimit);
2N/A if (fi3 == NULL)
2N/A se->se_nlimit >>= 1;
2N/A } while ((se->se_nlimit > 0) && (fi3 == NULL));
2N/A
2N/A if (fi3 == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A param->info.ru.info3->fi3 = fi3;
2N/A
2N/A if ((ns = smb_kmod_enum_init(se)) == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A if (smb_kmod_enum(ns) != 0) {
2N/A smb_kmod_enum_fini(ns);
2N/A return (ERROR_INTERNAL_ERROR);
2N/A }
2N/A
2N/A item = list_head(&ns->ns_list);
2N/A while (item != NULL) {
2N/A ofile = &item->nsi_un.nsi_ofile;
2N/A fi3->fi3_id = ofile->fi_uniqid;
2N/A fi3->fi3_permissions = ofile->fi_permissions;
2N/A fi3->fi3_num_locks = ofile->fi_numlocks;
2N/A fi3->fi3_pathname = (uint8_t *)
2N/A NDR_STRDUP(mxa, ofile->fi_path);
2N/A fi3->fi3_username = (uint8_t *)
2N/A NDR_STRDUP(mxa, ofile->fi_username);
2N/A
2N/A ++entries_read;
2N/A ++fi3;
2N/A item = list_next(&ns->ns_list, item);
2N/A }
2N/A
2N/A se->se_resume += entries_read;
2N/A param->info.ru.info3->entries_read = entries_read;
2N/A param->total_entries = entries_read;
2N/A smb_kmod_enum_fini(ns);
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_s_NetFileClose
2N/A *
2N/A * NetFileClose forces a file to close. This function can be used when
2N/A * an error prevents closure by other means. Use NetFileClose with
2N/A * caution because it does not flush data, cached on a client, to the
2N/A * file before closing the file.
2N/A *
2N/A * SMB fids are 16-bit values but this interface expects 32-bit file ids.
2N/A * So we use the uniqid here.
2N/A *
2N/A * Return Values
2N/A * ERROR_SUCCESS Operation succeeded.
2N/A * ERROR_ACCESS_DENIED Operation denied.
2N/A * NERR_FileIdNotFound No open file with the specified id.
2N/A *
2N/A * Note: MSDN suggests ERROR_FILE_NOT_FOUND for NetFileClose but network
2N/A * captures using NT show NERR_FileIdNotFound, which is consistent with
2N/A * the NetFileClose2 page on MSDN.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetFileClose(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A static struct {
2N/A int errnum;
2N/A int nerr;
2N/A } errmap[] = {
2N/A 0, ERROR_SUCCESS,
2N/A EACCES, ERROR_ACCESS_DENIED,
2N/A EPERM, ERROR_ACCESS_DENIED,
2N/A EINVAL, ERROR_INVALID_PARAMETER,
2N/A ENOMEM, ERROR_NOT_ENOUGH_MEMORY,
2N/A ENOENT, NERR_FileIdNotFound
2N/A };
2N/A
2N/A struct mslm_NetFileClose *param = arg;
2N/A int i;
2N/A int rc;
2N/A
2N/A if (!ndr_is_admin(mxa)) {
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A rc = smb_kmod_file_close(param->file_id);
2N/A
2N/A for (i = 0; i < (sizeof (errmap) / sizeof (errmap[0])); ++i) {
2N/A if (rc == errmap[i].errnum) {
2N/A param->status = errmap[i].nerr;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A }
2N/A
2N/A param->status = ERROR_INTERNAL_ERROR;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_s_NetShareGetInfo
2N/A *
2N/A * Returns Win32 error codes.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetShareGetInfo(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct mlsm_NetShareGetInfo *param = arg;
2N/A struct mslm_NetShareInfo_0 *info0;
2N/A struct mslm_NetShareInfo_1 *info1;
2N/A struct mslm_NetShareInfo_2 *info2;
2N/A struct mslm_NetShareInfo_501 *info501;
2N/A struct mslm_NetShareInfo_502 *info502;
2N/A struct mslm_NetShareInfo_503 *info503;
2N/A struct mslm_NetShareInfo_1004 *info1004;
2N/A struct mslm_NetShareInfo_1005 *info1005;
2N/A struct mslm_NetShareInfo_1006 *info1006;
2N/A struct mslm_NetShareInfo_1501 *info1501;
2N/A srvsvc_netshare_getinfo_t *info;
2N/A uint8_t *netname;
2N/A uint8_t *comment;
2N/A uint8_t *path;
2N/A smb_share_t si;
2N/A srvsvc_sd_t sd;
2N/A DWORD status;
2N/A
2N/A status = smb_share_lookup((char *)param->netname, &si);
2N/A if (status != NERR_Success) {
2N/A bzero(param, sizeof (struct mlsm_NetShareGetInfo));
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A netname = (uint8_t *)NDR_STRDUP(mxa, si.shr_name);
2N/A comment = (uint8_t *)NDR_STRDUP(mxa, si.shr_cmnt);
2N/A path = (uint8_t *)NDR_STRDUP(mxa, si.shr_winpath);
2N/A info = NDR_NEW(mxa, srvsvc_netshare_getinfo_t);
2N/A bzero(info, sizeof (srvsvc_netshare_getinfo_t));
2N/A
2N/A if (netname == NULL || comment == NULL || info == NULL) {
2N/A bzero(param, sizeof (struct mlsm_NetShareGetInfo));
2N/A param->status = ERROR_NOT_ENOUGH_MEMORY;
2N/A smb_share_free(&si);
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A switch (param->level) {
2N/A case 0:
2N/A info0 = &info->nsg_info0;
2N/A info0->shi0_netname = netname;
2N/A param->result.ru.info0 = info0;
2N/A break;
2N/A
2N/A case 1:
2N/A info1 = &info->nsg_info1;
2N/A info1->shi1_netname = netname;
2N/A info1->shi1_comment = comment;
2N/A info1->shi1_type = si.shr_type;
2N/A param->result.ru.info1 = info1;
2N/A break;
2N/A
2N/A case 2:
2N/A info2 = &info->nsg_info2;
2N/A param->result.ru.info2 = info2;
2N/A
2N/A if (!ndr_is_poweruser(mxa)) {
2N/A status = ERROR_ACCESS_DENIED;
2N/A break;
2N/A }
2N/A
2N/A info2->shi2_netname = netname;
2N/A info2->shi2_comment = comment;
2N/A info2->shi2_path = path;
2N/A info2->shi2_passwd = 0;
2N/A info2->shi2_type = si.shr_type;
2N/A info2->shi2_permissions = 0;
2N/A info2->shi2_max_uses = SHI_USES_UNLIMITED;
2N/A info2->shi2_current_uses = 0;
2N/A break;
2N/A
2N/A case 501:
2N/A info501 = &info->nsg_info501;
2N/A info501->shi501_netname = netname;
2N/A info501->shi501_comment = comment;
2N/A info501->shi501_type = si.shr_type;
2N/A info501->shi501_flags = srvsvc_get_share_flags(&si);
2N/A param->result.ru.info501 = info501;
2N/A break;
2N/A
2N/A case 502:
2N/A info502 = &info->nsg_info502;
2N/A param->result.ru.info502 = info502;
2N/A
2N/A if (!ndr_is_poweruser(mxa)) {
2N/A status = ERROR_ACCESS_DENIED;
2N/A break;
2N/A }
2N/A
2N/A info502->shi502_netname = netname;
2N/A info502->shi502_comment = comment;
2N/A info502->shi502_path = path;
2N/A info502->shi502_passwd = 0;
2N/A info502->shi502_type = si.shr_type;
2N/A info502->shi502_permissions = 0;
2N/A info502->shi502_max_uses = SHI_USES_UNLIMITED;
2N/A info502->shi502_current_uses = 0;
2N/A
2N/A status = srvsvc_share_getsd(mxa, &si, &sd);
2N/A if (status == ERROR_SUCCESS) {
2N/A info502->shi502_reserved = sd.sd_size;
2N/A info502->shi502_security_descriptor = sd.sd_buf;
2N/A } else {
2N/A info502->shi502_reserved = 0;
2N/A info502->shi502_security_descriptor = NULL;
2N/A }
2N/A break;
2N/A
2N/A case 503:
2N/A info503 = &info->nsg_info503;
2N/A param->result.ru.info503 = info503;
2N/A
2N/A if (!ndr_is_poweruser(mxa)) {
2N/A status = ERROR_ACCESS_DENIED;
2N/A break;
2N/A }
2N/A
2N/A info503->shi503_netname = netname;
2N/A info503->shi503_comment = comment;
2N/A info503->shi503_path = path;
2N/A info503->shi503_passwd = NULL;
2N/A info503->shi503_type = si.shr_type;
2N/A info503->shi503_permissions = 0;
2N/A info503->shi503_max_uses = SHI_USES_UNLIMITED;
2N/A info503->shi503_current_uses = 0;
2N/A info503->shi503_servername = NULL;
2N/A
2N/A status = srvsvc_share_getsd(mxa, &si, &sd);
2N/A if (status == ERROR_SUCCESS) {
2N/A info503->shi503_reserved = sd.sd_size;
2N/A info503->shi503_security_descriptor = sd.sd_buf;
2N/A } else {
2N/A info503->shi503_reserved = 0;
2N/A info503->shi503_security_descriptor = NULL;
2N/A }
2N/A break;
2N/A
2N/A case 1004:
2N/A info1004 = &info->nsg_info1004;
2N/A info1004->shi1004_comment = comment;
2N/A param->result.ru.info1004 = info1004;
2N/A break;
2N/A
2N/A case 1005:
2N/A info1005 = &info->nsg_info1005;
2N/A info1005->shi1005_flags = srvsvc_get_share_flags(&si);
2N/A param->result.ru.info1005 = info1005;
2N/A break;
2N/A
2N/A case 1006:
2N/A info1006 = &info->nsg_info1006;
2N/A info1006->shi1006_max_uses = SHI_USES_UNLIMITED;
2N/A param->result.ru.info1006 = info1006;
2N/A break;
2N/A
2N/A case 1501:
2N/A info1501 = &info->nsg_info1501;
2N/A
2N/A status = srvsvc_share_getsd(mxa, &si, &sd);
2N/A if (status == ERROR_SUCCESS) {
2N/A info503->shi503_reserved = sd.sd_size;
2N/A info503->shi503_security_descriptor = sd.sd_buf;
2N/A } else {
2N/A info503->shi503_reserved = 0;
2N/A info503->shi503_security_descriptor = NULL;
2N/A }
2N/A
2N/A param->result.ru.info1501 = info1501;
2N/A break;
2N/A
2N/A default:
2N/A status = ERROR_ACCESS_DENIED;
2N/A break;
2N/A }
2N/A
2N/A smb_share_free(&si);
2N/A
2N/A param->result.switch_value = param->level;
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/Astatic uint32_t
2N/Asrvsvc_share_getsd(ndr_xa_t *mxa, smb_share_t *si, srvsvc_sd_t *sd)
2N/A{
2N/A uint32_t status;
2N/A
2N/A status = srvsvc_sd_get(si, NULL, &sd->sd_size);
2N/A if (status != ERROR_SUCCESS) {
2N/A if (status == ERROR_PATH_NOT_FOUND) {
2N/A bzero(sd, sizeof (srvsvc_sd_t));
2N/A status = ERROR_SUCCESS;
2N/A }
2N/A
2N/A return (status);
2N/A }
2N/A
2N/A if ((sd->sd_buf = NDR_MALLOC(mxa, sd->sd_size)) == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A status = srvsvc_sd_get(si, sd->sd_buf, NULL);
2N/A if (status == ERROR_PATH_NOT_FOUND) {
2N/A bzero(sd, sizeof (srvsvc_sd_t));
2N/A status = ERROR_SUCCESS;
2N/A }
2N/A
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_s_NetShareSetInfo
2N/A *
2N/A * This call is made by SrvMgr to set share information.
2N/A * Only power users groups can manage shares.
2N/A *
2N/A * To avoid misleading errors, we don't report an error
2N/A * when a FS doesn't support ACLs on shares.
2N/A *
2N/A * Returns Win32 error codes.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetShareSetInfo(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct mlsm_NetShareSetInfo *param = arg;
2N/A struct mslm_NetShareInfo_1 *info1;
2N/A struct mslm_NetShareInfo_2 *info2;
2N/A struct mslm_NetShareInfo_501 *info501;
2N/A struct mslm_NetShareInfo_502 *info502;
2N/A struct mslm_NetShareInfo_503 *info503;
2N/A struct mslm_NetShareInfo_1004 *info1004;
2N/A struct mslm_NetShareInfo_1005 *info1005;
2N/A struct mslm_NetShareInfo_1501 *info1501;
2N/A static DWORD parm_err = 0;
2N/A srvsvc_netshare_setinfo_t info;
2N/A smb_share_t si;
2N/A uint8_t *sdbuf;
2N/A int32_t native_os;
2N/A DWORD status;
2N/A
2N/A native_os = ndr_native_os(mxa);
2N/A
2N/A bzero(&si, sizeof (smb_share_t));
2N/A
2N/A if (!ndr_is_poweruser(mxa)) {
2N/A status = ERROR_ACCESS_DENIED;
2N/A goto netsharesetinfo_exit;
2N/A }
2N/A
2N/A if (param->result.ru.nullptr == NULL) {
2N/A status = ERROR_INVALID_PARAMETER;
2N/A goto netsharesetinfo_exit;
2N/A }
2N/A
2N/A if (smb_share_lookup((char *)param->netname, &si) != NERR_Success) {
2N/A status = ERROR_INVALID_NETNAME;
2N/A goto netsharesetinfo_exit;
2N/A }
2N/A
2N/A bzero(&info, sizeof (srvsvc_netshare_setinfo_t));
2N/A
2N/A switch (param->level) {
2N/A case 0:
2N/A status = ERROR_INVALID_LEVEL;
2N/A break;
2N/A
2N/A case 1:
2N/A info1 = (struct mslm_NetShareInfo_1 *)param->result.ru.info1;
2N/A info.nss_netname = (char *)info1->shi1_netname;
2N/A info.nss_comment = (char *)info1->shi1_comment;
2N/A info.nss_type = info1->shi1_type;
2N/A status = srvsvc_modify_share(&si, &info);
2N/A break;
2N/A
2N/A case 2:
2N/A info2 = (struct mslm_NetShareInfo_2 *)param->result.ru.info2;
2N/A info.nss_netname = (char *)info2->shi2_netname;
2N/A info.nss_comment = (char *)info2->shi2_comment;
2N/A info.nss_path = (char *)info2->shi2_path;
2N/A info.nss_type = info2->shi2_type;
2N/A status = srvsvc_modify_share(&si, &info);
2N/A break;
2N/A
2N/A case 501:
2N/A info501 = (struct mslm_NetShareInfo_501 *)
2N/A param->result.ru.info501;
2N/A info.nss_netname = (char *)info501->shi501_netname;
2N/A info.nss_comment = (char *)info501->shi501_comment;
2N/A info.nss_type = info501->shi501_type;
2N/A status = srvsvc_modify_share(&si, &info);
2N/A if (status == ERROR_SUCCESS)
2N/A status = srvsvc_update_share_flags(&si,
2N/A info501->shi501_flags);
2N/A break;
2N/A
2N/A case 502:
2N/A info502 = (struct mslm_NetShareInfo_502 *)
2N/A param->result.ru.info502;
2N/A info.nss_netname = (char *)info502->shi502_netname;
2N/A info.nss_comment = (char *)info502->shi502_comment;
2N/A info.nss_path = (char *)info502->shi502_path;
2N/A info.nss_type = info502->shi502_type;
2N/A info.nss_sd.sd_buf = info502->shi502_security_descriptor;
2N/A status = srvsvc_modify_share(&si, &info);
2N/A break;
2N/A
2N/A case 503:
2N/A info503 = (struct mslm_NetShareInfo_503 *)
2N/A param->result.ru.info503;
2N/A info.nss_netname = (char *)info503->shi503_netname;
2N/A info.nss_comment = (char *)info503->shi503_comment;
2N/A info.nss_path = (char *)info503->shi503_path;
2N/A info.nss_type = info503->shi503_type;
2N/A info.nss_sd.sd_buf = info503->shi503_security_descriptor;
2N/A status = srvsvc_modify_share(&si, &info);
2N/A break;
2N/A
2N/A case 1004:
2N/A info1004 = (struct mslm_NetShareInfo_1004 *)
2N/A param->result.ru.info1004;
2N/A info.nss_comment = (char *)info1004->shi1004_comment;
2N/A status = srvsvc_modify_share(&si, &info);
2N/A break;
2N/A
2N/A case 1005:
2N/A info1005 = (struct mslm_NetShareInfo_1005 *)
2N/A param->result.ru.info1005;
2N/A status = srvsvc_update_share_flags(&si,
2N/A info1005->shi1005_flags);
2N/A break;
2N/A
2N/A case 1006:
2N/A /*
2N/A * We don't limit the maximum number of concurrent
2N/A * connections to a share.
2N/A */
2N/A status = ERROR_SUCCESS;
2N/A break;
2N/A
2N/A case 1501:
2N/A info1501 = (struct mslm_NetShareInfo_1501 *)
2N/A param->result.ru.info1501;
2N/A sdbuf = info1501->shi1501_security_descriptor;
2N/A status = ERROR_SUCCESS;
2N/A
2N/A if (sdbuf != NULL) {
2N/A status = srvsvc_sd_set(&si, sdbuf);
2N/A if (status == ERROR_PATH_NOT_FOUND)
2N/A status = ERROR_SUCCESS;
2N/A }
2N/A break;
2N/A
2N/A default:
2N/A status = ERROR_ACCESS_DENIED;
2N/A break;
2N/A }
2N/A
2N/Anetsharesetinfo_exit:
2N/A smb_share_free(&si);
2N/A if (status != ERROR_SUCCESS)
2N/A bzero(param, sizeof (struct mlsm_NetShareSetInfo));
2N/A
2N/A param->parm_err = (native_os == NATIVE_OS_WIN95) ? 0 : &parm_err;
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/Astatic uint32_t
2N/Asrvsvc_modify_share(smb_share_t *si, srvsvc_netshare_setinfo_t *info)
2N/A{
2N/A uint32_t nerr = NERR_Success;
2N/A
2N/A if (si->shr_flags & SMB_SHRF_TRANS)
2N/A return (srvsvc_modify_transient_share(si, info));
2N/A
2N/A if (info->nss_sd.sd_buf != NULL) {
2N/A nerr = srvsvc_sd_set(si, info->nss_sd.sd_buf);
2N/A if (nerr == ERROR_PATH_NOT_FOUND)
2N/A nerr = NERR_Success;
2N/A }
2N/A
2N/A return (srvsvc_sa_modify(si, info));
2N/A}
2N/A
2N/A/*
2N/A * Update transient shares. This includes autohome shares.
2N/A */
2N/Astatic uint32_t
2N/Asrvsvc_modify_transient_share(smb_share_t *si, srvsvc_netshare_setinfo_t *info)
2N/A{
2N/A uint32_t nerr = NERR_Success;
2N/A boolean_t new_cmnt = B_FALSE;
2N/A char *newname = NULL;
2N/A char *curname;
2N/A
2N/A if (info->nss_netname != NULL && info->nss_netname[0] != '\0' &&
2N/A smb_strcasecmp(info->nss_netname, si->shr_name, 0) != 0) {
2N/A newname = info->nss_netname;
2N/A }
2N/A
2N/A if (info->nss_comment == NULL) {
2N/A if (si->shr_cmnt != NULL) {
2N/A free(si->shr_cmnt);
2N/A si->shr_cmnt = NULL;
2N/A new_cmnt = B_TRUE;
2N/A }
2N/A } else {
2N/A if ((si->shr_cmnt == NULL) ||
2N/A strcmp(info->nss_comment, si->shr_cmnt) != 0) {
2N/A free(si->shr_cmnt);
2N/A if ((si->shr_cmnt = strdup(info->nss_comment)) == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A new_cmnt = B_TRUE;
2N/A }
2N/A }
2N/A
2N/A if (newname != NULL) {
2N/A curname = si->shr_name;
2N/A si->shr_name = newname;
2N/A if ((nerr = smb_share_add(si)) == NERR_Success)
2N/A nerr = smb_share_remove(curname);
2N/A si->shr_name = curname;
2N/A } else if (new_cmnt) {
2N/A nerr = smb_share_add(si);
2N/A }
2N/A
2N/A return (nerr);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_update_share_flags
2N/A *
2N/A * This function updates flags for shares.
2N/A * Flags for Persistent shares are updated in both libshare and the local cache.
2N/A * Flags for Transient shares are updated only in the local cache.
2N/A */
2N/Astatic uint32_t
2N/Asrvsvc_update_share_flags(smb_share_t *si, uint32_t shi_flags)
2N/A{
2N/A uint32_t nerr = NERR_Success;
2N/A uint32_t flag = 0;
2N/A char *csc_value;
2N/A char *abe_value = "false";
2N/A nvlist_t *nvl;
2N/A int err = 0;
2N/A
2N/A if (shi_flags & SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM) {
2N/A flag = SMB_SHRF_ABE;
2N/A abe_value = "true";
2N/A }
2N/A
2N/A si->shr_flags &= ~SMB_SHRF_ABE;
2N/A si->shr_flags |= flag;
2N/A
2N/A switch ((shi_flags & CSC_MASK)) {
2N/A case CSC_CACHE_AUTO_REINT:
2N/A flag = SMB_SHRF_CSC_AUTO;
2N/A break;
2N/A case CSC_CACHE_VDO:
2N/A flag = SMB_SHRF_CSC_VDO;
2N/A break;
2N/A case CSC_CACHE_NONE:
2N/A flag = SMB_SHRF_CSC_DISABLED;
2N/A break;
2N/A case CSC_CACHE_MANUAL_REINT:
2N/A flag = SMB_SHRF_CSC_MANUAL;
2N/A break;
2N/A default:
2N/A return (NERR_InternalError);
2N/A }
2N/A
2N/A si->shr_flags &= ~SMB_SHRF_CSC_MASK;
2N/A si->shr_flags |= flag;
2N/A
2N/A if ((si->shr_flags & SMB_SHRF_TRANS) == 0) {
2N/A csc_value = smb_share_csc_name(si);
2N/A
2N/A if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
2N/A return (NERR_InternalError);
2N/A
2N/A err |= nvlist_add_string(nvl, SHOPT_CSC, csc_value);
2N/A err |= nvlist_add_string(nvl, SHOPT_ABE, abe_value);
2N/A if (err) {
2N/A nvlist_free(nvl);
2N/A return (NERR_InternalError);
2N/A }
2N/A
2N/A nerr = srvsvc_sa_setprop(si, nvl);
2N/A nvlist_free(nvl);
2N/A }
2N/A
2N/A return (nerr);
2N/A}
2N/A
2N/Astatic uint32_t
2N/Asrvsvc_get_share_flags(smb_share_t *si)
2N/A{
2N/A uint32_t flags = 0;
2N/A
2N/A switch (si->shr_flags & SMB_SHRF_CSC_MASK) {
2N/A case SMB_SHRF_CSC_DISABLED:
2N/A flags |= CSC_CACHE_NONE;
2N/A break;
2N/A case SMB_SHRF_CSC_AUTO:
2N/A flags |= CSC_CACHE_AUTO_REINT;
2N/A break;
2N/A case SMB_SHRF_CSC_VDO:
2N/A flags |= CSC_CACHE_VDO;
2N/A break;
2N/A case SMB_SHRF_CSC_MANUAL:
2N/A default:
2N/A /*
2N/A * Default to CSC_CACHE_MANUAL_REINT.
2N/A */
2N/A break;
2N/A }
2N/A
2N/A if (si->shr_flags & SMB_SHRF_DFSROOT)
2N/A flags |= SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT;
2N/A
2N/A if (si->shr_flags & SMB_SHRF_ABE)
2N/A flags |= SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM;
2N/A
2N/A /* if 'smb' zfs property: shortnames=disabled */
2N/A if ((si->shr_flags & SMB_SHRF_SHORTNAME) == 0)
2N/A flags |= SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING;
2N/A
2N/A return (flags);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_s_NetSessionEnum
2N/A *
2N/A * Level 1 request is made by (Server Manager (srvmgr) on NT Server when
2N/A * the user info icon is selected.
2N/A *
2N/A * On success, the return value is NERR_Success.
2N/A * On error, the return value can be one of the following error codes:
2N/A *
2N/A * ERROR_ACCESS_DENIED The user does not have access to the requested
2N/A * information.
2N/A * ERROR_INVALID_LEVEL The value specified for the level is invalid.
2N/A * ERROR_INVALID_PARAMETER The specified parameter is invalid.
2N/A * ERROR_MORE_DATA More entries are available. Specify a large
2N/A * enough buffer to receive all entries.
2N/A * ERROR_NOT_ENOUGH_MEMORY Insufficient memory is available.
2N/A * NERR_ClientNameNotFound A session does not exist with the computer name.
2N/A * NERR_InvalidComputer The computer name is invalid.
2N/A * NERR_UserNotFound The user name could not be found.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetSessionEnum(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct mslm_NetSessionEnum *param = arg;
2N/A srvsvc_infonres_t *info;
2N/A smb_netsvc_t *ns;
2N/A smb_svcenum_t se;
2N/A DWORD status = ERROR_SUCCESS;
2N/A
2N/A if (!ndr_is_admin(mxa)) {
2N/A status = ERROR_ACCESS_DENIED;
2N/A goto srvsvc_netsessionenum_error;
2N/A }
2N/A
2N/A if ((info = NDR_NEW(mxa, srvsvc_infonres_t)) == NULL) {
2N/A status = ERROR_NOT_ENOUGH_MEMORY;
2N/A goto srvsvc_netsessionenum_error;
2N/A }
2N/A
2N/A info->entriesread = 0;
2N/A info->entries = NULL;
2N/A param->result.level = param->level;
2N/A param->result.bufptr.p = info;
2N/A
2N/A if ((param->total_entries = srvsvc_open_sessions()) == 0) {
2N/A param->resume_handle = NULL;
2N/A param->status = ERROR_SUCCESS;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A bzero(&se, sizeof (smb_svcenum_t));
2N/A se.se_type = SMB_SVCENUM_TYPE_USER;
2N/A se.se_level = param->level;
2N/A se.se_ntotal = param->total_entries;
2N/A se.se_nlimit = se.se_ntotal;
2N/A
2N/A if (param->resume_handle) {
2N/A se.se_resume = *param->resume_handle;
2N/A se.se_nskip = se.se_resume;
2N/A *param->resume_handle = 0;
2N/A }
2N/A
2N/A switch (param->level) {
2N/A case 0:
2N/A info->entries = NDR_NEWN(mxa, struct mslm_SESSION_INFO_0,
2N/A se.se_nlimit);
2N/A break;
2N/A case 1:
2N/A info->entries = NDR_NEWN(mxa, struct mslm_SESSION_INFO_1,
2N/A se.se_nlimit);
2N/A break;
2N/A case 2:
2N/A info->entries = NDR_NEWN(mxa, struct mslm_SESSION_INFO_2,
2N/A se.se_nlimit);
2N/A break;
2N/A case 10:
2N/A info->entries = NDR_NEWN(mxa, struct mslm_SESSION_INFO_10,
2N/A se.se_nlimit);
2N/A break;
2N/A case 502:
2N/A info->entries = NDR_NEWN(mxa, struct mslm_SESSION_INFO_502,
2N/A se.se_nlimit);
2N/A break;
2N/A default:
2N/A bzero(param, sizeof (struct mslm_NetSessionEnum));
2N/A param->status = ERROR_INVALID_LEVEL;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A if (info->entries == NULL) {
2N/A status = ERROR_NOT_ENOUGH_MEMORY;
2N/A goto srvsvc_netsessionenum_error;
2N/A }
2N/A
2N/A if ((ns = smb_kmod_enum_init(&se)) == NULL) {
2N/A status = ERROR_NOT_ENOUGH_MEMORY;
2N/A goto srvsvc_netsessionenum_error;
2N/A }
2N/A
2N/A status = srvsvc_NetSessionEnumCommon(mxa, info, ns, &se);
2N/A smb_kmod_enum_fini(ns);
2N/A
2N/A if (status != ERROR_SUCCESS)
2N/A goto srvsvc_netsessionenum_error;
2N/A
2N/A if (param->resume_handle &&
2N/A param->pref_max_len != SMB_SRVSVC_MAXPREFLEN) {
2N/A if (se.se_resume < param->total_entries) {
2N/A *param->resume_handle = se.se_resume;
2N/A status = ERROR_MORE_DATA;
2N/A }
2N/A }
2N/A
2N/A param->total_entries = info->entriesread;
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A
2N/Asrvsvc_netsessionenum_error:
2N/A bzero(param, sizeof (struct mslm_NetSessionEnum));
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/Astatic uint32_t
2N/Asrvsvc_NetSessionEnumCommon(ndr_xa_t *mxa, srvsvc_infonres_t *info,
2N/A smb_netsvc_t *ns, smb_svcenum_t *se)
2N/A{
2N/A struct mslm_SESSION_INFO_0 *info0 = info->entries;
2N/A struct mslm_SESSION_INFO_1 *info1 = info->entries;
2N/A struct mslm_SESSION_INFO_2 *info2 = info->entries;
2N/A struct mslm_SESSION_INFO_10 *info10 = info->entries;
2N/A struct mslm_SESSION_INFO_502 *info502 = info->entries;
2N/A smb_netsvcitem_t *item;
2N/A smb_netuserinfo_t *user;
2N/A char *workstation;
2N/A char account[MAXNAMELEN];
2N/A char ipaddr_buf[INET6_ADDRSTRLEN];
2N/A uint32_t logon_time;
2N/A uint32_t flags;
2N/A uint32_t entries_read = 0;
2N/A
2N/A if (smb_kmod_enum(ns) != 0)
2N/A return (ERROR_INTERNAL_ERROR);
2N/A
2N/A item = list_head(&ns->ns_list);
2N/A while (item != NULL) {
2N/A user = &item->nsi_un.nsi_user;
2N/A
2N/A workstation = user->ui_workstation;
2N/A if (workstation == NULL || *workstation == '\0') {
2N/A (void) smb_inet_ntop(&user->ui_ipaddr, ipaddr_buf,
2N/A SMB_IPSTRLEN(user->ui_ipaddr.a_family));
2N/A workstation = ipaddr_buf;
2N/A }
2N/A
2N/A (void) snprintf(account, MAXNAMELEN, "%s\\%s",
2N/A user->ui_domain, user->ui_account);
2N/A
2N/A logon_time = time(0) - user->ui_logon_time;
2N/A flags = (user->ui_flags & SMB_ATF_GUEST) ? SESS_GUEST : 0;
2N/A
2N/A switch (se->se_level) {
2N/A case 0:
2N/A info0->sesi0_cname = NDR_STRDUP(mxa, workstation);
2N/A if (info0->sesi0_cname == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A ++info0;
2N/A break;
2N/A
2N/A case 1:
2N/A info1->sesi1_cname = NDR_STRDUP(mxa, workstation);
2N/A info1->sesi1_uname = NDR_STRDUP(mxa, account);
2N/A
2N/A if (info1->sesi1_cname == NULL ||
2N/A info1->sesi1_uname == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A info1->sesi1_nopens = user->ui_numopens;
2N/A info1->sesi1_time = logon_time;
2N/A info1->sesi1_itime = 0;
2N/A info1->sesi1_uflags = flags;
2N/A ++info1;
2N/A break;
2N/A
2N/A case 2:
2N/A info2->sesi2_cname = NDR_STRDUP(mxa, workstation);
2N/A info2->sesi2_uname = NDR_STRDUP(mxa, account);
2N/A
2N/A if (info2->sesi2_cname == NULL ||
2N/A info2->sesi2_uname == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A info2->sesi2_nopens = user->ui_numopens;
2N/A info2->sesi2_time = logon_time;
2N/A info2->sesi2_itime = 0;
2N/A info2->sesi2_uflags = flags;
2N/A info2->sesi2_cltype_name = (uint8_t *)"";
2N/A ++info2;
2N/A break;
2N/A
2N/A case 10:
2N/A info10->sesi10_cname = NDR_STRDUP(mxa, workstation);
2N/A info10->sesi10_uname = NDR_STRDUP(mxa, account);
2N/A
2N/A if (info10->sesi10_cname == NULL ||
2N/A info10->sesi10_uname == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A info10->sesi10_time = logon_time;
2N/A info10->sesi10_itime = 0;
2N/A ++info10;
2N/A break;
2N/A
2N/A case 502:
2N/A info502->sesi502_cname = NDR_STRDUP(mxa, workstation);
2N/A info502->sesi502_uname = NDR_STRDUP(mxa, account);
2N/A
2N/A if (info502->sesi502_cname == NULL ||
2N/A info502->sesi502_uname == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A info502->sesi502_nopens = user->ui_numopens;
2N/A info502->sesi502_time = logon_time;
2N/A info502->sesi502_itime = 0;
2N/A info502->sesi502_uflags = flags;
2N/A info502->sesi502_cltype_name = (uint8_t *)"";
2N/A info502->sesi502_transport = (uint8_t *)"";
2N/A ++info502;
2N/A break;
2N/A
2N/A default:
2N/A return (ERROR_INVALID_LEVEL);
2N/A }
2N/A
2N/A ++entries_read;
2N/A item = list_next(&ns->ns_list, item);
2N/A }
2N/A
2N/A info->entriesread = entries_read;
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_s_NetSessionDel
2N/A *
2N/A * Ends a network session between a server and a workstation.
2N/A * On NT only members of the Administrators or Account Operators
2N/A * local groups are permitted to use NetSessionDel.
2N/A *
2N/A * If unc_clientname is NULL, all sessions associated with the
2N/A * specified user will be disconnected.
2N/A *
2N/A * If username is NULL, all sessions from the specified client
2N/A * will be disconnected.
2N/A *
2N/A * Return Values
2N/A * On success, the return value is NERR_Success/ERROR_SUCCESS.
2N/A * On failure, the return value can be one of the following errors:
2N/A *
2N/A * ERROR_ACCESS_DENIED The user does not have access to the
2N/A * requested information.
2N/A * ERROR_INVALID_PARAMETER The specified parameter is invalid.
2N/A * ERROR_NOT_ENOUGH_MEMORY Insufficient memory is available.
2N/A * NERR_ClientNameNotFound A session does not exist with that
2N/A * computer name.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetSessionDel(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A static struct {
2N/A int errnum;
2N/A int nerr;
2N/A } errmap[] = {
2N/A 0, ERROR_SUCCESS,
2N/A EACCES, ERROR_ACCESS_DENIED,
2N/A EPERM, ERROR_ACCESS_DENIED,
2N/A EINVAL, ERROR_INVALID_PARAMETER,
2N/A ENOMEM, ERROR_NOT_ENOUGH_MEMORY,
2N/A ENOENT, NERR_ClientNameNotFound
2N/A };
2N/A
2N/A struct mslm_NetSessionDel *param = arg;
2N/A int i;
2N/A int rc;
2N/A
2N/A if (!ndr_is_admin(mxa)) {
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A rc = smb_kmod_session_close((char *)param->unc_clientname,
2N/A (char *)param->username);
2N/A
2N/A for (i = 0; i < (sizeof (errmap) / sizeof (errmap[0])); ++i) {
2N/A if (rc == errmap[i].errnum) {
2N/A param->status = errmap[i].nerr;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A }
2N/A
2N/A param->status = ERROR_INTERNAL_ERROR;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/Astatic int
2N/Asrvsvc_s_NetServerGetInfo(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct mslm_NetServerGetInfo *param = arg;
2N/A struct mslm_SERVER_INFO_100 *info100;
2N/A struct mslm_SERVER_INFO_101 *info101;
2N/A struct mslm_SERVER_INFO_102 *info102;
2N/A struct mslm_SERVER_INFO_502 *info502;
2N/A struct mslm_SERVER_INFO_503 *info503;
2N/A char sys_comment[SMB_PI_MAX_COMMENT];
2N/A char hostname[NETBIOS_NAME_SZ];
2N/A smb_version_t version;
2N/A
2N/A if (smb_getnetbiosname(hostname, sizeof (hostname)) != 0) {
2N/Anetservergetinfo_no_memory:
2N/A bzero(param, sizeof (struct mslm_NetServerGetInfo));
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A }
2N/A
2N/A (void) smb_config_getstr(SMB_CI_SYS_CMNT, sys_comment,
2N/A sizeof (sys_comment));
2N/A if (*sys_comment == '\0')
2N/A (void) strcpy(sys_comment, " ");
2N/A
2N/A smb_config_get_version(&version);
2N/A
2N/A switch (param->level) {
2N/A case 100:
2N/A info100 = NDR_NEW(mxa, struct mslm_SERVER_INFO_100);
2N/A if (info100 == NULL)
2N/A goto netservergetinfo_no_memory;
2N/A
2N/A bzero(info100, sizeof (struct mslm_SERVER_INFO_100));
2N/A info100->sv100_platform_id = SV_PLATFORM_ID_NT;
2N/A info100->sv100_name = (uint8_t *)NDR_STRDUP(mxa, hostname);
2N/A if (info100->sv100_name == NULL)
2N/A goto netservergetinfo_no_memory;
2N/A
2N/A param->result.bufptr.bufptr100 = info100;
2N/A break;
2N/A
2N/A case 101:
2N/A info101 = NDR_NEW(mxa, struct mslm_SERVER_INFO_101);
2N/A if (info101 == NULL)
2N/A goto netservergetinfo_no_memory;
2N/A
2N/A bzero(info101, sizeof (struct mslm_SERVER_INFO_101));
2N/A info101->sv101_platform_id = SV_PLATFORM_ID_NT;
2N/A info101->sv101_version_major = version.sv_major;
2N/A info101->sv101_version_minor = version.sv_minor;
2N/A info101->sv101_type = SV_TYPE_DEFAULT;
2N/A info101->sv101_name = (uint8_t *)NDR_STRDUP(mxa, hostname);
2N/A info101->sv101_comment
2N/A = (uint8_t *)NDR_STRDUP(mxa, sys_comment);
2N/A
2N/A if (info101->sv101_name == NULL ||
2N/A info101->sv101_comment == NULL)
2N/A goto netservergetinfo_no_memory;
2N/A
2N/A param->result.bufptr.bufptr101 = info101;
2N/A break;
2N/A
2N/A case 102:
2N/A info102 = NDR_NEW(mxa, struct mslm_SERVER_INFO_102);
2N/A if (info102 == NULL)
2N/A goto netservergetinfo_no_memory;
2N/A
2N/A bzero(info102, sizeof (struct mslm_SERVER_INFO_102));
2N/A info102->sv102_platform_id = SV_PLATFORM_ID_NT;
2N/A info102->sv102_version_major = version.sv_major;
2N/A info102->sv102_version_minor = version.sv_minor;
2N/A info102->sv102_type = SV_TYPE_DEFAULT;
2N/A info102->sv102_name = (uint8_t *)NDR_STRDUP(mxa, hostname);
2N/A info102->sv102_comment
2N/A = (uint8_t *)NDR_STRDUP(mxa, sys_comment);
2N/A
2N/A /*
2N/A * The following level 102 fields are defaulted to zero
2N/A * by virtue of the call to bzero above.
2N/A *
2N/A * sv102_users
2N/A * sv102_disc
2N/A * sv102_hidden
2N/A * sv102_announce
2N/A * sv102_anndelta
2N/A * sv102_licenses
2N/A * sv102_userpath
2N/A */
2N/A if (info102->sv102_name == NULL ||
2N/A info102->sv102_comment == NULL)
2N/A goto netservergetinfo_no_memory;
2N/A
2N/A param->result.bufptr.bufptr102 = info102;
2N/A break;
2N/A
2N/A case 502:
2N/A info502 = NDR_NEW(mxa, struct mslm_SERVER_INFO_502);
2N/A if (info502 == NULL)
2N/A goto netservergetinfo_no_memory;
2N/A
2N/A bzero(info502, sizeof (struct mslm_SERVER_INFO_502));
2N/A param->result.bufptr.bufptr502 = info502;
2N/A#ifdef SRVSVC_SATISFY_SMBTORTURE
2N/A break;
2N/A#else
2N/A param->result.level = param->level;
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A#endif /* SRVSVC_SATISFY_SMBTORTURE */
2N/A
2N/A case 503:
2N/A info503 = NDR_NEW(mxa, struct mslm_SERVER_INFO_503);
2N/A if (info503 == NULL)
2N/A goto netservergetinfo_no_memory;
2N/A
2N/A bzero(info503, sizeof (struct mslm_SERVER_INFO_503));
2N/A param->result.bufptr.bufptr503 = info503;
2N/A#ifdef SRVSVC_SATISFY_SMBTORTURE
2N/A break;
2N/A#else
2N/A param->result.level = param->level;
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A#endif /* SRVSVC_SATISFY_SMBTORTURE */
2N/A
2N/A default:
2N/A bzero(&param->result,
2N/A sizeof (struct mslm_NetServerGetInfo_result));
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A param->result.level = param->level;
2N/A param->status = ERROR_SUCCESS;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * NetRemoteTOD
2N/A *
2N/A * Returns information about the time of day on this server.
2N/A *
2N/A * typedef struct _TIME_OF_DAY_INFO {
2N/A * DWORD tod_elapsedt; // seconds since 00:00:00 January 1 1970 GMT
2N/A * DWORD tod_msecs; // arbitrary milliseconds (since reset)
2N/A * DWORD tod_hours; // current hour [0-23]
2N/A * DWORD tod_mins; // current minute [0-59]
2N/A * DWORD tod_secs; // current second [0-59]
2N/A * DWORD tod_hunds; // current hundredth (0.01) second [0-99]
2N/A * LONG tod_timezone; // time zone of the server
2N/A * DWORD tod_tinterval; // clock tick time interval
2N/A * DWORD tod_day; // day of the month [1-31]
2N/A * DWORD tod_month; // month of the year [1-12]
2N/A * DWORD tod_year; // current year
2N/A * DWORD tod_weekday; // day of the week since Sunday [0-6]
2N/A * } TIME_OF_DAY_INFO;
2N/A *
2N/A * The time zone of the server is calculated in minutes from Greenwich
2N/A * Mean Time (GMT). For time zones west of Greenwich, the value is
2N/A * positive; for time zones east of Greenwich, the value is negative.
2N/A * A value of -1 indicates that the time zone is undefined.
2N/A *
2N/A * Determine offset from GMT. If daylight saving time use altzone,
2N/A * otherwise use timezone.
2N/A *
2N/A * The clock tick value represents a resolution of one ten-thousandth
2N/A * (0.0001) second.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetRemoteTOD(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct mslm_NetRemoteTOD *param = arg;
2N/A struct mslm_TIME_OF_DAY_INFO *tod;
2N/A struct timeval time_val;
2N/A struct tm tm;
2N/A time_t gmtoff;
2N/A
2N/A
2N/A (void) gettimeofday(&time_val, 0);
2N/A (void) gmtime_r(&time_val.tv_sec, &tm);
2N/A
2N/A tod = NDR_NEW(mxa, struct mslm_TIME_OF_DAY_INFO);
2N/A if (tod == NULL) {
2N/A bzero(param, sizeof (struct mslm_NetRemoteTOD));
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A }
2N/A
2N/A bzero(tod, sizeof (struct mslm_TIME_OF_DAY_INFO));
2N/A
2N/A tod->tod_elapsedt = time_val.tv_sec;
2N/A tod->tod_msecs = time_val.tv_usec;
2N/A tod->tod_hours = tm.tm_hour;
2N/A tod->tod_mins = tm.tm_min;
2N/A tod->tod_secs = tm.tm_sec;
2N/A tod->tod_hunds = 0;
2N/A tod->tod_tinterval = 1000;
2N/A tod->tod_day = tm.tm_mday;
2N/A tod->tod_month = tm.tm_mon+1;
2N/A tod->tod_year = tm.tm_year+1900;
2N/A tod->tod_weekday = tm.tm_wday;
2N/A
2N/A (void) localtime_r(&time_val.tv_sec, &tm);
2N/A gmtoff = (tm.tm_isdst) ? altzone : timezone;
2N/A tod->tod_timezone = gmtoff / SECSPERMIN;
2N/A
2N/A param->bufptr = tod;
2N/A param->status = ERROR_SUCCESS;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_s_NetNameValidate
2N/A *
2N/A * Perform name validation.
2N/A *
2N/A * Returns Win32 error codes.
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Asrvsvc_s_NetNameValidate(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct mslm_NetNameValidate *param = arg;
2N/A char *name;
2N/A int maxlen;
2N/A int len;
2N/A
2N/A if ((name = (char *)param->pathname) == NULL) {
2N/A param->status = ERROR_INVALID_PARAMETER;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A switch (param->type) {
2N/A case NAMETYPE_SHARE:
2N/A len = strlen(name);
2N/A maxlen = (param->flags & NAMEFLAG_LM2) ?
2N/A SMB_SHARE_OEMNAME_MAX : SMB_SHARE_NTNAME_MAX;
2N/A
2N/A if (len > maxlen) {
2N/A param->status = ERROR_INVALID_NAME;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A param->status = smb_name_validate_share(name);
2N/A break;
2N/A
2N/A case NAMETYPE_USER:
2N/A case NAMETYPE_GROUP:
2N/A param->status = smb_name_validate_account(name);
2N/A break;
2N/A
2N/A case NAMETYPE_DOMAIN: /* NetBIOS domain name */
2N/A param->status = smb_name_validate_nbdomain(name);
2N/A break;
2N/A
2N/A case NAMETYPE_WORKGROUP:
2N/A param->status = smb_name_validate_workgroup(name);
2N/A break;
2N/A
2N/A case NAMETYPE_PASSWORD:
2N/A case NAMETYPE_COMPUTER:
2N/A case NAMETYPE_EVENT:
2N/A case NAMETYPE_SERVICE:
2N/A case NAMETYPE_NET:
2N/A case NAMETYPE_MESSAGE:
2N/A case NAMETYPE_MESSAGEDEST:
2N/A case NAMETYPE_SHAREPASSWORD:
2N/A param->status = ERROR_NOT_SUPPORTED;
2N/A break;
2N/A
2N/A default:
2N/A param->status = ERROR_INVALID_PARAMETER;
2N/A break;
2N/A }
2N/A
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_s_NetShareAdd
2N/A *
2N/A * Add a new share. Only power users groups can manage shares.
2N/A *
2N/A * This interface is used by the rmtshare command from the NT resource
2N/A * kit. Rmtshare allows a client to add or remove shares on a server
2N/A * from the client's command line.
2N/A *
2N/A * Returns Win32 error codes.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetShareAdd(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A static DWORD parm_err = 0;
2N/A DWORD parm_stat;
2N/A struct mslm_NetShareAdd *param = arg;
2N/A struct mslm_NetShareInfo_2 *info2;
2N/A struct mslm_NetShareInfo_502 *info502;
2N/A char realpath[MAXPATHLEN];
2N/A int32_t native_os;
2N/A uint8_t *sdbuf = NULL;
2N/A uint32_t status;
2N/A smb_share_t si;
2N/A
2N/A native_os = ndr_native_os(mxa);
2N/A
2N/A if (!ndr_is_poweruser(mxa)) {
2N/A bzero(param, sizeof (struct mslm_NetShareAdd));
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A switch (param->level) {
2N/A case 2:
2N/A info2 = (struct mslm_NetShareInfo_2 *)param->info.un.info2;
2N/A break;
2N/A
2N/A case 502:
2N/A info502 = (struct mslm_NetShareInfo_502 *)
2N/A param->info.un.info502;
2N/A sdbuf = info502->shi502_security_descriptor;
2N/A info2 = (struct mslm_NetShareInfo_2 *)info502;
2N/A break;
2N/A
2N/A default:
2N/A bzero(param, sizeof (struct mslm_NetShareAdd));
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A if (info2->shi2_netname == NULL || info2->shi2_path == NULL) {
2N/A bzero(param, sizeof (struct mslm_NetShareAdd));
2N/A param->status = NERR_NetNameNotFound;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A if (srvsvc_share_is_restricted((char *)info2->shi2_netname)) {
2N/A bzero(param, sizeof (struct mslm_NetShareAdd));
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A if (info2->shi2_comment == NULL)
2N/A info2->shi2_comment = (uint8_t *)"";
2N/A
2N/A /*
2N/A * Derive the real path which will be stored in the
2N/A * directory field of the smb_share_t structure
2N/A * from the path field in this RPC request.
2N/A */
2N/A parm_stat = srvsvc_get_realpath((const char *)info2->shi2_path,
2N/A realpath, MAXPATHLEN);
2N/A
2N/A if (parm_stat != NERR_Success) {
2N/A bzero(param, sizeof (struct mslm_NetShareAdd));
2N/A param->status = parm_stat;
2N/A param->parm_err
2N/A = (native_os == NATIVE_OS_WIN95) ? 0 : &parm_err;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A param->status = srvsvc_sa_add((char *)info2->shi2_netname, realpath,
2N/A (char *)info2->shi2_comment);
2N/A if (param->status == NERR_Success) {
2N/A status = smb_share_lookup((char *)info2->shi2_netname, &si);
2N/A
2N/A if (status == NERR_Success) {
2N/A if (sdbuf != NULL)
2N/A (void) srvsvc_sd_set(&si, sdbuf);
2N/A smb_share_free(&si);
2N/A }
2N/A }
2N/A param->parm_err = (native_os == NATIVE_OS_WIN95) ? 0 : &parm_err;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_get_realpath
2N/A *
2N/A * Derive the real path for a share from the path provided by a client.
2N/A * For instance, the real path of C:\ may be /cvol or the real path of
2N/A * F:\home may be /vol1/home.
2N/A *
2N/A * clntpath - path provided by the Windows client is in the
2N/A * format of <drive letter>:\<dir>
2N/A * realpath - path that will be stored as the directory field of
2N/A * the smb_share_t structure of the share.
2N/A * maxlen - maximum length of the realpath buffer
2N/A *
2N/A * Return LAN Manager network error code.
2N/A */
2N/Auint32_t
2N/Asrvsvc_get_realpath(const char *clntpath, char *realpath, int maxlen)
2N/A{
2N/A const char *p;
2N/A int len;
2N/A
2N/A if ((p = strchr(clntpath, ':')) != NULL)
2N/A ++p;
2N/A else
2N/A p = clntpath;
2N/A
2N/A (void) strlcpy(realpath, p, maxlen);
2N/A (void) strcanon(realpath, "/\\");
2N/A (void) strsubst(realpath, '\\', '/');
2N/A
2N/A len = strlen(realpath);
2N/A if ((len > 1) && (realpath[len - 1] == '/'))
2N/A realpath[len - 1] = '\0';
2N/A
2N/A return (NERR_Success);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_estimate_limit
2N/A *
2N/A * Estimate the number of objects that will fit in prefmaxlen.
2N/A * nlimit is adjusted here.
2N/A */
2N/Astatic void
2N/Asrvsvc_estimate_limit(smb_svcenum_t *se, uint32_t obj_size)
2N/A{
2N/A DWORD max_cnt;
2N/A
2N/A if (obj_size == 0) {
2N/A se->se_nlimit = 0;
2N/A return;
2N/A }
2N/A
2N/A if ((max_cnt = (se->se_prefmaxlen / obj_size)) == 0) {
2N/A se->se_nlimit = 0;
2N/A return;
2N/A }
2N/A
2N/A if (se->se_ntotal > max_cnt)
2N/A se->se_nlimit = max_cnt;
2N/A else
2N/A se->se_nlimit = se->se_ntotal;
2N/A}
2N/A
2N/A/*
2N/A * Enumerate all shares (see also NetShareEnumSticky).
2N/A *
2N/A * Request for various levels of information about our shares.
2N/A * Level 0: share names.
2N/A * Level 1: share name, share type and comment field.
2N/A * Level 2: everything that we know about the shares.
2N/A * Level 501: level 1 + flags.
2N/A * Level 502: level 2 + security descriptor.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetShareEnum(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A return (srvsvc_share_enum(arg, mxa, B_FALSE));
2N/A}
2N/A
2N/A/*
2N/A * Enumerate sticky shares: all shares except those marked STYPE_SPECIAL.
2N/A * Except for excluding STYPE_SPECIAL shares, NetShareEnumSticky is the
2N/A * same as NetShareEnum.
2N/A *
2N/A * Request for various levels of information about our shares.
2N/A * Level 0: share names.
2N/A * Level 1: share name, share type and comment field.
2N/A * Level 2: everything that we know about the shares.
2N/A * Level 501: not valid for this request.
2N/A * Level 502: level 2 + security descriptor.
2N/A *
2N/A * We set n_skip to resume_handle, which is used to find the appropriate
2N/A * place to resume. The resume_handle is similar to the readdir cookie.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetShareEnumSticky(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A return (srvsvc_share_enum(arg, mxa, B_TRUE));
2N/A}
2N/A
2N/Astatic int
2N/Asrvsvc_share_enum(void *arg, ndr_xa_t *mxa, boolean_t sticky)
2N/A{
2N/A struct mslm_NetShareEnum *param = arg;
2N/A srvsvc_infonres_t *infonres;
2N/A smb_netuserinfo_t *user = &mxa->pipe->np_user;
2N/A smb_svcenum_t se;
2N/A smb_netsvc_t *ns;
2N/A smb_netsvcitem_t *item;
2N/A DWORD status = ERROR_SUCCESS;
2N/A void *info = NULL;
2N/A
2N/A switch (param->level) {
2N/A case 2:
2N/A case 502:
2N/A if (!ndr_is_poweruser(mxa))
2N/A status = ERROR_ACCESS_DENIED;
2N/A break;
2N/A
2N/A case 501:
2N/A if (sticky)
2N/A status = ERROR_INVALID_LEVEL;
2N/A else if (!ndr_is_poweruser(mxa))
2N/A status = ERROR_ACCESS_DENIED;
2N/A else
2N/A status = ERROR_SUCCESS;
2N/A break;
2N/A
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A if (status != ERROR_SUCCESS) {
2N/A bzero(param, sizeof (struct mslm_NetShareEnum));
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A infonres = NDR_NEW(mxa, srvsvc_infonres_t);
2N/A if (infonres == NULL) {
2N/A bzero(param, sizeof (struct mslm_NetShareEnum));
2N/A param->status = ERROR_NOT_ENOUGH_MEMORY;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A param->totalentries = smb_share_count();
2N/A if (param->totalentries == 0) {
2N/A bzero(param, sizeof (struct mslm_NetShareEnum));
2N/A param->status = ERROR_SUCCESS;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A infonres->entriesread = 0;
2N/A infonres->entries = NULL;
2N/A param->result.level = param->level;
2N/A param->result.bufptr.p = infonres;
2N/A
2N/A bzero(&se, sizeof (smb_svcenum_t));
2N/A se.se_type = SMB_SVCENUM_TYPE_SHARE;
2N/A se.se_level = param->level;
2N/A se.se_ntotal = param->totalentries;
2N/A se.se_nlimit = se.se_ntotal;
2N/A
2N/A if (param->prefmaxlen == SMB_SRVSVC_MAXPREFLEN ||
2N/A param->prefmaxlen > SMB_SRVSVC_MAXBUFLEN)
2N/A se.se_prefmaxlen = SMB_SRVSVC_MAXBUFLEN;
2N/A else
2N/A se.se_prefmaxlen = param->prefmaxlen;
2N/A
2N/A if (param->resume_handle) {
2N/A se.se_resume = *param->resume_handle;
2N/A se.se_nskip = se.se_resume;
2N/A *param->resume_handle = 0;
2N/A }
2N/A
2N/A switch (param->level) {
2N/A case 0:
2N/A srvsvc_estimate_limit(&se,
2N/A sizeof (struct mslm_NetShareInfo_0) + MAXNAMELEN);
2N/A info = NDR_NEWN(mxa, struct mslm_NetShareInfo_0, se.se_nlimit);
2N/A break;
2N/A
2N/A case 1:
2N/A srvsvc_estimate_limit(&se,
2N/A sizeof (struct mslm_NetShareInfo_1) + MAXNAMELEN);
2N/A info = NDR_NEWN(mxa, struct mslm_NetShareInfo_1, se.se_nlimit);
2N/A break;
2N/A
2N/A case 2:
2N/A srvsvc_estimate_limit(&se,
2N/A sizeof (struct mslm_NetShareInfo_2) + MAXNAMELEN);
2N/A info = NDR_NEWN(mxa, struct mslm_NetShareInfo_2, se.se_nlimit);
2N/A break;
2N/A
2N/A case 501:
2N/A srvsvc_estimate_limit(&se,
2N/A sizeof (struct mslm_NetShareInfo_501) + MAXNAMELEN);
2N/A info = NDR_NEWN(mxa, struct mslm_NetShareInfo_501,
2N/A se.se_nlimit);
2N/A break;
2N/A
2N/A case 502:
2N/A srvsvc_estimate_limit(&se,
2N/A sizeof (struct mslm_NetShareInfo_502) + MAXNAMELEN);
2N/A info = NDR_NEWN(mxa, struct mslm_NetShareInfo_502,
2N/A se.se_nlimit);
2N/A break;
2N/A
2N/A default:
2N/A status = ERROR_INVALID_LEVEL;
2N/A break;
2N/A }
2N/A
2N/A if (se.se_nlimit == 0) {
2N/A param->status = ERROR_SUCCESS;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A if (info == NULL)
2N/A status = ERROR_NOT_ENOUGH_MEMORY;
2N/A
2N/A if (status != ERROR_SUCCESS) {
2N/A bzero(param, sizeof (struct mslm_NetShareEnum));
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A if ((ns = srvsvc_shareenum_init(&se, user, sticky)) == NULL) {
2N/A bzero(param, sizeof (struct mslm_NetShareEnum));
2N/A param->status = ERROR_NOT_ENOUGH_MEMORY;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A if (smb_kmod_enum(ns) != 0) {
2N/A smb_kmod_enum_fini(ns);
2N/A bzero(param, sizeof (struct mslm_NetShareEnum));
2N/A param->status = ERROR_INTERNAL_ERROR;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A se.se_nitems = 0;
2N/A item = list_head(&ns->ns_list);
2N/A while (item != NULL) {
2N/A status = mlsvc_NetShareEnumCommon(mxa, &se,
2N/A &item->nsi_un.nsi_share, info);
2N/A if (status != ERROR_SUCCESS)
2N/A break;
2N/A
2N/A se.se_nitems++;
2N/A item = list_next(&ns->ns_list, item);
2N/A }
2N/A
2N/A se.se_resume += se.se_nitems;
2N/A infonres->entriesread = se.se_nitems;
2N/A infonres->entries = info;
2N/A smb_kmod_enum_fini(ns);
2N/A
2N/A if (param->resume_handle &&
2N/A param->prefmaxlen != SMB_SRVSVC_MAXPREFLEN) {
2N/A if (se.se_resume < se.se_ntotal) {
2N/A *param->resume_handle = se.se_resume;
2N/A status = ERROR_MORE_DATA;
2N/A }
2N/A }
2N/A
2N/A param->totalentries = se.se_ntotal;
2N/A param->status = status;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * mlsvc_NetShareEnumCommon
2N/A *
2N/A * Build the levels 0, 1, 2, 501 and 502 share information. This function
2N/A * is called by the various NetShareEnum levels for each share. If
2N/A * we cannot build the share data for some reason, we return an error
2N/A * but the actual value of the error is not important to the caller.
2N/A * The caller just needs to know not to include this info in the RPC
2N/A * response.
2N/A *
2N/A * Returns:
2N/A * ERROR_SUCCESS
2N/A * ERROR_NOT_ENOUGH_MEMORY
2N/A * ERROR_INVALID_LEVEL
2N/A */
2N/Astatic DWORD
2N/Amlsvc_NetShareEnumCommon(ndr_xa_t *mxa, smb_svcenum_t *se,
2N/A smb_share_t *si, void *infop)
2N/A{
2N/A static uint8_t empty_string[1];
2N/A struct mslm_NetShareInfo_0 *info0;
2N/A struct mslm_NetShareInfo_1 *info1;
2N/A struct mslm_NetShareInfo_2 *info2;
2N/A struct mslm_NetShareInfo_501 *info501;
2N/A struct mslm_NetShareInfo_502 *info502;
2N/A srvsvc_sd_t sd;
2N/A uint8_t *netname;
2N/A uint8_t *comment = empty_string;
2N/A uint8_t *passwd = empty_string;
2N/A uint8_t *path;
2N/A int i = se->se_nitems;
2N/A
2N/A netname = (uint8_t *)NDR_STRDUP(mxa, si->shr_name);
2N/A path = (uint8_t *)NDR_STRDUP(mxa, si->shr_winpath);
2N/A
2N/A if (si->shr_cmnt != NULL)
2N/A comment = (uint8_t *)NDR_STRDUP(mxa, si->shr_cmnt);
2N/A
2N/A if (!netname || !comment || !path)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A switch (se->se_level) {
2N/A case 0:
2N/A info0 = (struct mslm_NetShareInfo_0 *)infop;
2N/A info0[i].shi0_netname = netname;
2N/A break;
2N/A
2N/A case 1:
2N/A info1 = (struct mslm_NetShareInfo_1 *)infop;
2N/A info1[i].shi1_netname = netname;
2N/A info1[i].shi1_comment = comment;
2N/A info1[i].shi1_type = si->shr_type;
2N/A break;
2N/A
2N/A case 2:
2N/A info2 = (struct mslm_NetShareInfo_2 *)infop;
2N/A info2[i].shi2_netname = netname;
2N/A info2[i].shi2_comment = comment;
2N/A info2[i].shi2_path = path;
2N/A info2[i].shi2_type = si->shr_type;
2N/A info2[i].shi2_permissions = 0;
2N/A info2[i].shi2_max_uses = SHI_USES_UNLIMITED;
2N/A info2[i].shi2_current_uses = 0;
2N/A info2[i].shi2_passwd = passwd;
2N/A break;
2N/A
2N/A case 501:
2N/A info501 = (struct mslm_NetShareInfo_501 *)infop;
2N/A info501[i].shi501_netname = netname;
2N/A info501[i].shi501_comment = comment;
2N/A info501[i].shi501_type = si->shr_type;
2N/A info501[i].shi501_flags = srvsvc_get_share_flags(si);
2N/A break;
2N/A
2N/A case 502:
2N/A info502 = (struct mslm_NetShareInfo_502 *)infop;
2N/A info502[i].shi502_netname = netname;
2N/A info502[i].shi502_comment = comment;
2N/A info502[i].shi502_path = path;
2N/A info502[i].shi502_type = si->shr_type;
2N/A info502[i].shi502_permissions = 0;
2N/A info502[i].shi502_max_uses = SHI_USES_UNLIMITED;
2N/A info502[i].shi502_current_uses = 0;
2N/A info502[i].shi502_passwd = passwd;
2N/A
2N/A if (srvsvc_share_getsd(mxa, si, &sd) == ERROR_SUCCESS) {
2N/A info502[i].shi502_reserved = sd.sd_size;
2N/A info502[i].shi502_security_descriptor = sd.sd_buf;
2N/A } else {
2N/A info502[i].shi502_reserved = 0;
2N/A info502[i].shi502_security_descriptor = NULL;
2N/A }
2N/A
2N/A break;
2N/A
2N/A default:
2N/A return (ERROR_INVALID_LEVEL);
2N/A }
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/Astatic smb_netsvc_t *
2N/Asrvsvc_shareenum_init(smb_svcenum_t *se, smb_netuserinfo_t *user,
2N/A boolean_t sticky)
2N/A{
2N/A smb_netsvc_t *ns;
2N/A char *username;
2N/A
2N/A if ((ns = smb_kmod_enum_init(se)) == NULL)
2N/A return (NULL);
2N/A
2N/A username = (user->ui_posix_name)
2N/A ? user->ui_posix_name : user->ui_account;
2N/A
2N/A (void) strlcpy(se->se_qualifier.seq_qualstr, username, MAXNAMELEN);
2N/A se->se_qualifier.seq_mode = (sticky)
2N/A ? SMB_SVCENUM_SHARE_PERM : SMB_SVCENUM_SHARE_RPC;
2N/A
2N/A return (ns);
2N/A}
2N/A
2N/Astatic int /*ARGSUSED*/
2N/Asrvsvc_s_NetShareCheck(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct mslm_NetShareCheck *param = arg;
2N/A smb_share_t si;
2N/A
2N/A if (param->path == NULL) {
2N/A param->stype = STYPE_DISKTREE;
2N/A param->status = NERR_NetNameNotFound;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A (void) strsubst((char *)param->path, '/', '\\');
2N/A
2N/A if (smb_share_check((const char *)param->path, &si) == 0) {
2N/A param->stype = (si.shr_type & STYPE_MASK);
2N/A param->status = NERR_Success;
2N/A smb_share_free(&si);
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A param->stype = STYPE_DISKTREE;
2N/A param->status = NERR_NetNameNotFound;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * Delete a share. Only members of the Administrators, Server Operators
2N/A * or Power Users local groups are allowed to delete shares.
2N/A *
2N/A * This interface is used by the rmtshare command from the NT resource
2N/A * kit. Rmtshare allows a client to add or remove shares on a server
2N/A * from the client's command line.
2N/A *
2N/A * Returns Win32 error codes.
2N/A */
2N/Astatic int
2N/Asrvsvc_s_NetShareDel(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct mslm_NetShareDel *param = arg;
2N/A smb_share_t si;
2N/A
2N/A if (!ndr_is_poweruser(mxa) ||
2N/A srvsvc_share_is_restricted((char *)param->netname)) {
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A param->status = smb_share_lookup((char *)param->netname, &si);
2N/A if (param->status == NERR_Success) {
2N/A if ((si.shr_flags & SMB_SHRF_DFSROOT) != 0)
2N/A param->status = NERR_IsDfsShare;
2N/A else
2N/A param->status = srvsvc_sa_delete(si.shr_name,
2N/A si.shr_path);
2N/A smb_share_free(&si);
2N/A }
2N/A
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_s_NetGetFileSecurity
2N/A *
2N/A * Get security descriptor of the requested file/folder
2N/A *
2N/A * Right now, just returns ERROR_ACCESS_DENIED, because we cannot
2N/A * get the requested SD here in RPC code.
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Asrvsvc_s_NetGetFileSecurity(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct mslm_NetGetFileSecurity *param = arg;
2N/A
2N/A param->length = 0;
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * srvsvc_s_NetSetFileSecurity
2N/A *
2N/A * Set the given security descriptor for the requested file/folder
2N/A *
2N/A * Right now, just returns ERROR_ACCESS_DENIED, because we cannot
2N/A * set the requested SD here in RPC code.
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Asrvsvc_s_NetSetFileSecurity(void *arg, ndr_xa_t *mxa)
2N/A{
2N/A struct mslm_NetSetFileSecurity *param = arg;
2N/A
2N/A param->status = ERROR_ACCESS_DENIED;
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * Check whether or not there is a restriction on a share. Restricted
2N/A * shares are generally STYPE_SPECIAL, for example, IPC$. All the
2N/A * administration share names are restricted: C$, D$ etc. Returns B_TRUE
2N/A * if the share is restricted. Otherwise B_FALSE is returned to indicate
2N/A * that there are no restrictions.
2N/A */
2N/Astatic boolean_t
2N/Asrvsvc_share_is_restricted(const char *sharename)
2N/A{
2N/A static char *restricted[] = {
2N/A "IPC$"
2N/A };
2N/A
2N/A int i;
2N/A
2N/A if (sharename == NULL)
2N/A return (B_FALSE);
2N/A
2N/A for (i = 0; i < sizeof (restricted)/sizeof (restricted[0]); i++) {
2N/A if (smb_strcasecmp(restricted[i], sharename, 0) == 0)
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A return (srvsvc_share_is_admin(sharename));
2N/A}
2N/A
2N/A/*
2N/A * Check whether or not access to the share should be restricted to
2N/A * administrators. This is a bit of a hack because what we're doing
2N/A * is checking for the default admin shares: C$, D$ etc.. There are
2N/A * other shares that have restrictions: see srvsvc_share_is_restricted().
2N/A *
2N/A * Returns B_TRUE if the shares is an admin share. Otherwise B_FALSE
2N/A * is returned to indicate that there are no restrictions.
2N/A */
2N/Astatic boolean_t
2N/Asrvsvc_share_is_admin(const char *sharename)
2N/A{
2N/A if (sharename == NULL)
2N/A return (B_FALSE);
2N/A
2N/A if (strlen(sharename) == 2 &&
2N/A smb_isalpha(sharename[0]) && sharename[1] == '$') {
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * get mntpnt from path
2N/A * (mntpnt must be freed when done.)
2N/A */
2N/Astatic int
2N/Asrvsvc_get_mntpnt(libshare_handle_t *shdl, const char *path, char **mntpntp)
2N/A{
2N/A char *mntpnt;
2N/A int rc;
2N/A
2N/A if (mntpntp == NULL)
2N/A return (SA_INTERNAL_ERR);
2N/A
2N/A *mntpntp = NULL;
2N/A
2N/A if ((mntpnt = malloc(MAXPATHLEN)) == NULL)
2N/A return (SA_NO_MEMORY);
2N/A
2N/A rc = sa_get_mntpnt_for_path(shdl, path, mntpnt, MAXPATHLEN,
2N/A NULL, NULL, 0, NULL, 0);
2N/A if (rc != SA_OK) {
2N/A free(mntpnt);
2N/A return (rc);
2N/A }
2N/A
2N/A *mntpntp = mntpnt;
2N/A return (SA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Stores the given share in sharemgr
2N/A */
2N/Astatic uint32_t
2N/Asrvsvc_sa_add(char *sharename, char *path, char *cmnt)
2N/A{
2N/A libshare_handle_t *shdl;
2N/A nvlist_t *share;
2N/A char errbuf[512];
2N/A int rc;
2N/A sa_validate_flags_t valflags = SA_VALIDATE_ENABLE;
2N/A char *mntpnt;
2N/A
2N/A if ((shdl = sa_open()) == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A if ((rc = srvsvc_get_mntpnt(shdl, path, &mntpnt)) != SA_OK) {
2N/A sa_close(shdl);
2N/A return (smb_share_lmerr(rc));
2N/A }
2N/A
2N/A if (sa_share_read(shdl, mntpnt, sharename, &share) != SA_OK) {
2N/A valflags |= SA_VALIDATE_NAME;
2N/A if ((share = sa_share_alloc(sharename, path)) == NULL) {
2N/A sa_close(shdl);
2N/A free(mntpnt);
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A }
2N/A }
2N/A
2N/A if (cmnt != NULL && *cmnt != '\0' &&
2N/A ((rc = sa_share_set_desc(share, cmnt)) != SA_OK))
2N/A goto done;
2N/A
2N/A if (sa_share_get_proto(share, SA_PROT_SMB) == NULL) {
2N/A if ((rc = sa_share_set_def_proto(share, SA_PROT_SMB)) != SA_OK)
2N/A goto done;
2N/A }
2N/A
2N/A if ((rc = sa_share_validate(shdl, share, valflags, errbuf,
2N/A sizeof (errbuf))) != SA_OK)
2N/A goto done;
2N/A
2N/A if ((rc = sa_share_write(shdl, share, B_TRUE, B_TRUE)) != SA_OK)
2N/A goto done;
2N/A
2N/A if (!sa_sharing_prop_enabled(shdl, mntpnt, sharename, SA_PROT_SMB))
2N/A rc = sa_sharing_set_prop(shdl, mntpnt, sharename,
2N/A SA_PROT_SMB, "on");
2N/Adone:
2N/A sa_share_free(share);
2N/A free(mntpnt);
2N/A sa_close(shdl);
2N/A return (smb_share_lmerr(rc));
2N/A}
2N/A
2N/A/*
2N/A * Removes the share via libshare
2N/A *
2N/A * Remove the SMB properties from the share.
2N/A * If there are no other protocols enabled for this share,
2N/A * remove it, otherwise save it.
2N/A */
2N/Astatic uint32_t
2N/Asrvsvc_sa_delete(const char *sharename, const char *path)
2N/A{
2N/A libshare_handle_t *shdl;
2N/A nvlist_t *share;
2N/A int rc;
2N/A char *mntpnt;
2N/A
2N/A if ((shdl = sa_open()) == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A if ((rc = srvsvc_get_mntpnt(shdl, path, &mntpnt)) != SA_OK) {
2N/A sa_close(shdl);
2N/A return (smb_share_lmerr(rc));
2N/A }
2N/A
2N/A rc = sa_share_read(shdl, mntpnt, sharename, &share);
2N/A if (rc != SA_OK) {
2N/A free(mntpnt);
2N/A sa_close(shdl);
2N/A return (smb_share_lmerr(rc));
2N/A }
2N/A
2N/A (void) sa_share_rem_proto(share, SA_PROT_SMB);
2N/A if (sa_share_proto_count(share) == 0)
2N/A rc = sa_share_remove(shdl, sharename, mntpnt, B_TRUE);
2N/A else
2N/A rc = sa_share_write(shdl, share, B_TRUE, B_TRUE);
2N/A
2N/A sa_share_free(share);
2N/A free(mntpnt);
2N/A sa_close(shdl);
2N/A
2N/A return (smb_share_lmerr(rc));
2N/A}
2N/A
2N/A/*
2N/A * Update the share information.
2N/A */
2N/Astatic uint32_t
2N/Asrvsvc_sa_modify(smb_share_t *si, srvsvc_netshare_setinfo_t *info)
2N/A{
2N/A libshare_handle_t *shdl;
2N/A nvlist_t *share;
2N/A int rc;
2N/A boolean_t new_cmnt = B_FALSE;
2N/A boolean_t new_name = B_FALSE;
2N/A char *mntpnt;
2N/A
2N/A if ((shdl = sa_open()) == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A if ((rc = srvsvc_get_mntpnt(shdl, si->shr_path, &mntpnt)) != SA_OK) {
2N/A sa_close(shdl);
2N/A return (smb_share_lmerr(rc));
2N/A }
2N/A
2N/A rc = sa_share_read(shdl, mntpnt, si->shr_name, &share);
2N/A if (rc != SA_OK) {
2N/A sa_close(shdl);
2N/A free(mntpnt);
2N/A return (smb_share_lmerr(rc));
2N/A }
2N/A
2N/A if (info->nss_comment == NULL) {
2N/A /*
2N/A * remove share description if it exists
2N/A */
2N/A if (si->shr_cmnt != NULL) {
2N/A (void) sa_share_rem_desc(share);
2N/A new_cmnt = B_TRUE;
2N/A }
2N/A } else {
2N/A if ((si->shr_cmnt == NULL) ||
2N/A strcmp(info->nss_comment, si->shr_cmnt) != 0) {
2N/A /*
2N/A * description has been added or modified
2N/A */
2N/A rc = sa_share_set_desc(share, info->nss_comment);
2N/A if (rc != SA_OK) {
2N/A sa_share_free(share);
2N/A free(mntpnt);
2N/A sa_close(shdl);
2N/A return (smb_share_lmerr(rc));
2N/A }
2N/A new_cmnt = B_TRUE;
2N/A }
2N/A }
2N/A
2N/A if (info->nss_netname != NULL && info->nss_netname[0] != 0 &&
2N/A smb_strcasecmp(info->nss_netname, si->shr_name, 0) != 0) {
2N/A /*
2N/A * info contains new name, update share
2N/A */
2N/A new_name = B_TRUE;
2N/A rc = sa_share_set_name(share, info->nss_netname);
2N/A if (rc != SA_OK) {
2N/A sa_share_free(share);
2N/A free(mntpnt);
2N/A sa_close(shdl);
2N/A return (smb_share_lmerr(rc));
2N/A }
2N/A }
2N/A
2N/A if (new_cmnt || new_name) {
2N/A /*
2N/A * share has been updated, save and publish
2N/A */
2N/A if ((rc = sa_share_write(shdl, share, B_TRUE,
2N/A B_TRUE)) != SA_OK) {
2N/A sa_share_free(share);
2N/A free(mntpnt);
2N/A sa_close(shdl);
2N/A return (smb_share_lmerr(rc));
2N/A }
2N/A
2N/A /*
2N/A * share was renamed, unpublish and remove old share
2N/A */
2N/A if (new_name) {
2N/A rc = sa_share_remove(shdl, si->shr_name,
2N/A mntpnt, B_TRUE);
2N/A }
2N/A }
2N/A
2N/A sa_share_free(share);
2N/A free(mntpnt);
2N/A sa_close(shdl);
2N/A return (smb_share_lmerr(rc));
2N/A}
2N/A
2N/A/*
2N/A * Sets the share properties.
2N/A *
2N/A * Updates the smb protocol properties of the share.
2N/A * The properties are given as a list of name-value pair.
2N/A * The name argument should be the optionset property name and the value
2N/A * should be a valid value for the specified property.
2N/A */
2N/Astatic uint32_t
2N/Asrvsvc_sa_setprop(smb_share_t *si, nvlist_t *nvl)
2N/A{
2N/A nvlist_t *share;
2N/A nvlist_t *proplist;
2N/A nvpair_t *curprop;
2N/A char *propname;
2N/A char *propval;
2N/A boolean_t modified = B_FALSE;
2N/A int rc;
2N/A uint32_t status = NERR_Success;
2N/A char errbuf[512];
2N/A libshare_handle_t *shdl;
2N/A char *mntpnt;
2N/A
2N/A if ((shdl = sa_open()) == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A if ((rc = srvsvc_get_mntpnt(shdl, si->shr_path, &mntpnt)) != SA_OK) {
2N/A sa_close(shdl);
2N/A return (smb_share_lmerr(rc));
2N/A }
2N/A
2N/A rc = sa_share_read(shdl, mntpnt, si->shr_name, &share);
2N/A if (rc != SA_OK) {
2N/A sa_close(shdl);
2N/A free(mntpnt);
2N/A return (smb_share_lmerr(rc));
2N/A }
2N/A
2N/A if ((proplist = sa_share_get_proto(share, SA_PROT_SMB)) == NULL) {
2N/A sa_share_free(share);
2N/A free(mntpnt);
2N/A sa_close(shdl);
2N/A return (NERR_InternalError);
2N/A }
2N/A
2N/A curprop = nvlist_next_nvpair(nvl, NULL);
2N/A while (curprop != NULL) {
2N/A propname = nvpair_name(curprop);
2N/A rc = nvpair_value_string(curprop, &propval);
2N/A if ((rc != 0) || (propname == NULL) || (propval == NULL)) {
2N/A status = NERR_InternalError;
2N/A break;
2N/A }
2N/A
2N/A if (sa_share_set_prop(proplist, propname, propval) != SA_OK) {
2N/A status = NERR_InternalError;
2N/A break;
2N/A }
2N/A modified = B_TRUE;
2N/A
2N/A curprop = nvlist_next_nvpair(nvl, curprop);
2N/A }
2N/A
2N/A if ((status == NERR_Success) && modified) {
2N/A sa_validate_flags_t valflags = SA_VALIDATE_ENABLE;
2N/A /*
2N/A * validate and update share
2N/A */
2N/A rc = sa_share_validate(shdl, share, valflags, errbuf,
2N/A sizeof (errbuf));
2N/A if (rc == SA_OK)
2N/A rc = sa_share_write(shdl, share, B_TRUE, B_TRUE);
2N/A status = smb_share_lmerr(rc);
2N/A }
2N/A
2N/A sa_share_free(share);
2N/A free(mntpnt);
2N/A sa_close(shdl);
2N/A
2N/A return (status);
2N/A}
2N/A
2N/Astatic ndr_stub_table_t srvsvc_stub_table[] = {
2N/A { srvsvc_s_NetConnectEnum, SRVSVC_OPNUM_NetConnectEnum },
2N/A { srvsvc_s_NetFileEnum, SRVSVC_OPNUM_NetFileEnum },
2N/A { srvsvc_s_NetFileClose, SRVSVC_OPNUM_NetFileClose },
2N/A { srvsvc_s_NetShareGetInfo, SRVSVC_OPNUM_NetShareGetInfo },
2N/A { srvsvc_s_NetShareSetInfo, SRVSVC_OPNUM_NetShareSetInfo },
2N/A { srvsvc_s_NetSessionEnum, SRVSVC_OPNUM_NetSessionEnum },
2N/A { srvsvc_s_NetSessionDel, SRVSVC_OPNUM_NetSessionDel },
2N/A { srvsvc_s_NetServerGetInfo, SRVSVC_OPNUM_NetServerGetInfo },
2N/A { srvsvc_s_NetRemoteTOD, SRVSVC_OPNUM_NetRemoteTOD },
2N/A { srvsvc_s_NetNameValidate, SRVSVC_OPNUM_NetNameValidate },
2N/A { srvsvc_s_NetShareAdd, SRVSVC_OPNUM_NetShareAdd },
2N/A { srvsvc_s_NetShareDel, SRVSVC_OPNUM_NetShareDel },
2N/A { srvsvc_s_NetShareEnum, SRVSVC_OPNUM_NetShareEnum },
2N/A { srvsvc_s_NetShareEnumSticky, SRVSVC_OPNUM_NetShareEnumSticky },
2N/A { srvsvc_s_NetShareCheck, SRVSVC_OPNUM_NetShareCheck },
2N/A { srvsvc_s_NetGetFileSecurity, SRVSVC_OPNUM_NetGetFileSecurity },
2N/A { srvsvc_s_NetSetFileSecurity, SRVSVC_OPNUM_NetSetFileSecurity },
2N/A {0}
2N/A};