mlsvc_srvsvc.c revision 19d41fcc9d25d65db5db7c75dc9bbb68550868d2
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Server Service RPC (SRVSVC) server-side interface definition.
* The server service provides a remote administration interface.
*
* values.
*/
#include <unistd.h>
#include <netdb.h>
#include <strings.h>
#include <time.h>
#include <tzfile.h>
#include <time.h>
#include <thread.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <smbsrv/libmlsvc.h>
#include <smbsrv/netrauth.h>
#include <smbsrv/mlsvc_util.h>
#include <smbsrv/smb_common_door.h>
/*
* prefmaxlen: Client specified response buffer limit.
* resume_handle: Cookie used to track enumeration across multiple calls.
* n_total: Total number of entries.
* n_enum: Number of entries to enumerate (derived from prefmaxlen).
* n_skip: Number of entries to skip (from incoming resume handle).
* n_read: Number of objects returned for current enumeration request.
*/
typedef struct srvsvc_enum {
struct mlrpc_xaction *);
struct mlrpc_xaction *);
struct mslm_infonres *, srvsvc_enum_t *, int);
struct mslm_infonres *, srvsvc_enum_t *, int);
struct mslm_infonres *, srvsvc_enum_t *, int);
struct mslm_infonres *, srvsvc_enum_t *, int);
struct mslm_infonres *, srvsvc_enum_t *, int);
srvsvc_enum_t *, lmshare_info_t *, void *);
void *);
static char *srvsvc_share_mkpath(struct mlrpc_xaction *, char *);
static char empty_string[1];
static mlrpc_stub_table_t srvsvc_stub_table[];
static mlrpc_service_t srvsvc_service = {
"SRVSVC", /* name */
"Server services", /* desc */
"\\srvsvc", /* endpoint */
PIPE_NTSVCS, /* sec_addr_port */
"4b324fc8-1670-01d3-12785a47bf6ee188", 3, /* abstract */
"8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
0, /* no bind_instance_size */
0, /* no bind_req() */
0, /* no unbind_and_close() */
0, /* use generic_call_stub() */
srvsvc_stub_table /* stub_table */
};
/*
* srvsvc_initialize
*
* This function registers the SRVSVC RPC interface with the RPC runtime
* library. It must be called in order to use either the client side
* or the server side functions.
*/
void
srvsvc_initialize(void)
{
(void) mlrpc_register_service(&srvsvc_service);
}
/*
* srvsvc_s_NetConnectEnum
*
* Under construction. This is just enough to get the interface working.
* Current level 0 and level 1 connection info are supported.
*
* Level 1 request is made by 'srvmgr' (Server Manager)
* utility of NT Server part of NT Domain to MLRPC server
* while double click of share info icon. These values
* are currectly virtual to MLRPC client and does't
* reflect the real state of server.
*/
static int
{
struct mslm_NetConnectInfoBuf0 *ci0;
struct mslm_NetConnectInfoBuf1 *ci1;
case 0:
if (ci0 == 0) {
break;
}
break;
}
param->resume_handle = 0;
break;
case 1:
if (ci1 == 0) {
break;
}
ci1->coni1_netname =
struct mslm_NetConnectInfo1);
break;
}
param->resume_handle = 0;
break;
default:
break;
}
if (status != ERROR_SUCCESS)
return (MLRPC_DRC_OK);
}
/*
* srvsvc_s_NetFileEnum
*
* Under construction. The values used here are fictional values and
* bear no relation to any real values, living or otherwise. I just
* made them up to get the interface working.
*/
static int
{
struct mslm_NetFileInfoBuf3 *fi3;
if (!srvsvc_is_administrator(mxa)) {
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
if (fi3 == 0) {
return (MLRPC_DRC_OK);
}
fi3->fi3_num_locks = 0;
fi3->fi3_pathname =
fi3->fi3_username =
return (MLRPC_DRC_OK);
}
if (param->resume_handle)
return (MLRPC_DRC_OK);
}
/*
* srvsvc_s_NetFileClose
*
* Under construction. This is just enough to get the interface working.
*/
/*ARGSUSED*/
static int
{
return (MLRPC_DRC_OK);
}
/*
* srvsvc_s_NetShareGetInfo
*
* This call is made by Windows2000 to get share information. There are
* probably other information levels but these are the only ones I've
* seen so far.
*
* Returns Win32 error codes.
*/
static int
{
struct mslm_NetShareGetInfo0 *info0;
struct mslm_NetShareGetInfo1 *info1;
struct mslm_NetShareGetInfo2 *info2;
struct mslm_NetShareGetInfo502 *info502;
struct mslm_NetShareGetInfo1005 *info1005;
struct lmshare_info si;
char shr_comment[LMSHR_COMMENT_MAX];
if (status != NERR_Success) {
/*
* Windows clients don't send the \\PIPE path for IPC$.
*/
} else {
return (MLRPC_DRC_OK);
}
}
else
case 0:
if (info0 == 0) {
break;
}
break;
case 1:
if (info1 == 0) {
break;
}
break;
case 2:
if (info2 == 0) {
break;
}
info2->shi2_passwd = 0;
info2->shi2_permissions = 0;
info2->shi2_current_uses = 0;
break;
case 1005:
if (info1005 == 0) {
break;
}
info1005->shi1005_flags = 0;
break;
case 502:
/*
* Level 502 provides level 2 information plus a
* security descriptor. We don't support security
* descriptors on shares yet.
*/
if (info502 == 0) {
break;
}
info502->shi502_passwd = 0;
info502->shi502_permissions = 0;
info502->shi502_current_uses = 0;
info502->shi502_reserved = 0;
break;
default:
break;
}
if (status != ERROR_SUCCESS)
else
return (MLRPC_DRC_OK);
}
/*
* srvsvc_s_NetShareSetInfo
*
* This call is made by SrvMgr to set share information.
* Always returns ERROR_ACCESS_DENIED for now.
*
* Returns Win32 error codes.
*/
static int
{
sizeof (DWORD));
else
return (MLRPC_DRC_OK);
}
/*
* srvsvc_s_NetSessionEnum
*
* Level 1 request is made by the 'srvmgr' (Server Manager) utility on
* NT Server when the user info icon is selected.
*
* Return Values
* If the function succeeds, the return value is NERR_Success.
* If the function fails, the return value can be one of the following
* error codes:
*
* ERROR_ACCESS_DENIED The user does not have access to the requested
* information.
* ERROR_INVALID_LEVEL The value specified for the level parameter is
* invalid.
* ERROR_INVALID_PARAMETER The specified parameter is invalid.
* ERROR_MORE_DATA More entries are available. Specify a large
* enough buffer to receive all entries.
* ERROR_NOT_ENOUGH_MEMORY Insufficient memory is available.
* NERR_ClientNameNotFound A session does not exist with the computer
* name.
* NERR_InvalidComputer The computer name is invalid.
* NERR_UserNotFound The user name could not be found.
*/
static int
{
struct mslm_infonres *infonres;
if (infonres == 0) {
return (MLRPC_DRC_OK);
}
infonres->entriesread = 0;
case 0:
break;
case 1:
break;
default:
break;
}
if (status != 0) {
return (MLRPC_DRC_OK);
}
param->resume_handle = 0;
return (MLRPC_DRC_OK);
}
/*
* mlsvc_NetSessionEnumLevel0
*
* Build the level 0 session information.
*/
/*ARGSUSED*/
static DWORD
struct mlrpc_xaction *mxa)
{
struct mslm_SESSION_INFO_0 *info0;
char *workstation;
char ipaddr_buf[INET_ADDRSTRLEN];
if (info0 == 0)
return (ERROR_NOT_ENOUGH_MEMORY);
if (!ulist)
return (ERROR_NOT_ENOUGH_MEMORY);
/*
* Ignore local tokens (IP address is zero).
*/
total--;
if (!ulist)
return (ERROR_NOT_ENOUGH_MEMORY);
continue;
}
sizeof (ipaddr_buf));
}
return (ERROR_NOT_ENOUGH_MEMORY);
}
}
if (!ulist)
return (ERROR_NOT_ENOUGH_MEMORY);
}
return (ERROR_SUCCESS);
}
/*
* mlsvc_NetSessionEnumLevel1
*
* Build the level 1 session information.
*/
/*ARGSUSED*/
static DWORD
struct mlrpc_xaction *mxa)
{
struct mslm_SESSION_INFO_1 *info1;
char *workstation;
char *account;
char ipaddr_buf[INET_ADDRSTRLEN];
if (info1 == 0)
return (ERROR_NOT_ENOUGH_MEMORY);
if (!ulist)
return (ERROR_NOT_ENOUGH_MEMORY);
/*
* Ignore local user_ctxs (IP address is zero).
*/
total--;
if (!ulist)
return (ERROR_NOT_ENOUGH_MEMORY);
continue;
}
ipaddr_buf, sizeof (ipaddr_buf));
}
account = "Unknown";
account);
return (ERROR_NOT_ENOUGH_MEMORY);
}
}
if (!ulist)
return (ERROR_NOT_ENOUGH_MEMORY);
}
return (ERROR_SUCCESS);
}
/*
* srvsvc_s_NetSessionDel
*
* Ends a network session between a server and a workstation.
* On NT only members of the Administrators or Account Operators
* local groups are permitted to use NetSessionDel.
*
* Return Values
* If the function succeeds, the return value is NERR_Success/
* ERROR_SUCCESS. If the function fails, the return value can be
* one of the following error codes:
*
* ERROR_ACCESS_DENIED The user does not have access to the
* requested information.
* ERROR_INVALID_PARAMETER The specified parameter is invalid.
* ERROR_NOT_ENOUGH_MEMORY Insufficient memory is available.
* NERR_ClientNameNotFound A session does not exist with that
* computer name.
*/
static int
{
if (!srvsvc_is_poweruser(mxa)) {
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
/*
* SRVSVC NetServerGetInfo
*
* IN LPTSTR servername,
* IN DWORD level,
* OUT union switch(level) {
* case 100: mslm_SERVER_INFO_100 *p100;
* case 101: mslm_SERVER_INFO_101 *p101;
* case 102: mslm_SERVER_INFO_102 *p102;
* default: char *nullptr;
* } bufptr,
* OUT DWORD status
*/
static int
{
struct mslm_SERVER_INFO_100 *info100;
struct mslm_SERVER_INFO_101 *info101;
struct mslm_SERVER_INFO_102 *info102;
char sys_comment[SMB_PI_MAX_COMMENT];
char hostname[MAXHOSTNAMELEN];
return (ERROR_NOT_ENOUGH_MEMORY);
}
sizeof (sys_comment));
if (*sys_comment == '\0')
case 100:
if (info100 == 0)
if (info100->sv100_name == 0)
break;
case 101:
if (info101 == 0)
info101->sv101_version_minor = 0;
break;
case 102:
if (info102 == 0)
info102->sv102_version_minor = 0;
/*
* The following level 102 fields are defaulted to zero
* by virtue of the call to bzero above.
*
* sv102_users
* sv102_disc
* sv102_hidden
* sv102_announce
* sv102_anndelta
* sv102_licenses
* sv102_userpath
*/
break;
default:
sizeof (struct mslm_NetServerGetInfo_result));
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
/*
* NetRemoteTOD
*
* Returns information about the time of day on this server.
*
* typedef struct _TIME_OF_DAY_INFO {
* DWORD tod_elapsedt; // seconds since 00:00:00 January 1 1970 GMT
* DWORD tod_msecs; // arbitrary milliseconds (since reset)
* DWORD tod_hours; // current hour [0-23]
* DWORD tod_mins; // current minute [0-59]
* DWORD tod_secs; // current second [0-59]
* DWORD tod_hunds; // current hundredth (0.01) second [0-99]
* LONG tod_timezone; // time zone of the server
* DWORD tod_tinterval; // clock tick time interval
* DWORD tod_day; // day of the month [1-31]
* DWORD tod_month; // month of the year [1-12]
* DWORD tod_year; // current year
* DWORD tod_weekday; // day of the week since sunday [0-6]
* } TIME_OF_DAY_INFO;
*
* The time zone of the server is calculated in minutes from Greenwich
* Mean Time (GMT). For time zones west of Greenwich, the value is
* positive; for time zones east of Greenwich, the value is negative.
* A value of -1 indicates that the time zone is undefined.
*
* The clock tick value represents a resolution of one ten-thousandth
* (0.0001) second.
*/
static int
{
struct mslm_TIME_OF_DAY_INFO *tod;
(void) gettimeofday(&time_val, 0);
return (ERROR_NOT_ENOUGH_MEMORY);
}
return (MLRPC_DRC_OK);
}
/*
* srvsvc_s_NetNameValidate
*
* Perform name validation.
* I've observed that the Computer Management Windows Application
* always send this request with type=0x09 and the flags=0 when
* attempting to validate a share name.
*
* The share name is consider invalid if it contains any of the
* following character (as mentioned in MSDN article #236388).
*
* " / \ [ ] : | < > + ; , ? * =
*
*
* For now, if the type is other than 0x09, return access denied.
*
* Returns Win32 error codes.
*/
/*ARGSUSED*/
static int
{
case 0x09:
break;
default:
break;
}
return (MLRPC_DRC_OK);
}
/*
* srvsvc_s_NetShareAdd
*
* Add a new share. We support info levels 2 and 502 but ignore the
* security descriptor in level 502 requests. Only the administrator,
* or a member of the domain administrators group, is allowed to add
* shares.
*
* This interface is used by the rmtshare command from the NT resource
* kit. Rmtshare allows a client to add or remove shares on a server
* from the client's command line.
*
* Note that we don't support security descriptors on a share. If the
* /grant is used, the share will be created but the subsequent attempt
* to manipulate the security descriptor (NetShareGetInfo) will fail.
* Similarly for the /remove option.
*
* Returns Win32 error codes.
*/
static int
{
struct mslm_SHARE_INFO_2 *info2;
struct lmshare_info si;
char realpath[MAXPATHLEN];
if (!srvsvc_is_poweruser(mxa)) {
return (MLRPC_DRC_OK);
}
case 2:
break;
case 502:
break;
default:
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
if (info2->shi2_remark == 0)
/*
* Derive the real path which will be stored in the
* directory field of the lmshare_info_t structure
* from the path field in this RPC request.
*/
if (parm_stat != NERR_Success) {
0 : &parm_err;
return (MLRPC_DRC_OK);
}
0 : &parm_err;
return (MLRPC_DRC_OK);
}
/*
* srvsvc_estimate_objcnt
*
* Estimate the number of objects that will fit in prefmaxlen.
*/
static uint32_t
{
if (obj_size == 0)
return (0);
return (0);
return (n_obj);
}
/*
* srvsvc_is_administrator
*
* Check whether or not the specified user has administrator privileges,
* i.e. is a member of the Domain Admins or Administrators groups.
*/
static boolean_t
{
}
/*
* srvsvc_is_poweruser
*
* Check whether or not the specified user has power-user privileges,
* i.e. is a member of the Domain Admins, Administrators or Power
* Users groups. This is typically required for operations such as
*
* Returns 1 if the user is a power user, otherwise returns 0.
*/
static boolean_t
{
}
/*
* srvsvc_s_NetShareEnum
*
* Enumerate all shares (see also NetShareEnumSticky).
*
* Request for various levels of information about our shares.
* Level 0: share names.
* Level 1: share name, share type and comment field.
* Level 2: everything that we know about the shares.
* Level 501: level 1 + flags (flags must be zero).
* Level 502: level 2 + security descriptor.
*/
static int
{
struct mslm_infonres *infonres;
return (MLRPC_DRC_OK);
}
infonres->entriesread = 0;
else
if (param->resume_handle) {
}
case 0:
break;
case 1:
break;
case 2:
break;
case 501:
break;
case 502:
break;
default:
break;
}
if (status != 0) {
return (MLRPC_DRC_OK);
}
if (param->resume_handle)
*param->resume_handle = 0;
return (MLRPC_DRC_OK);
}
if (param->resume_handle &&
} else {
*param->resume_handle = 0;
}
}
return (MLRPC_DRC_OK);
}
/*
* srvsvc_s_NetShareEnumSticky
*
* Enumerate sticky shares: all shares except those marked STYPE_SPECIAL.
* Except for excluding STYPE_SPECIAL shares, NetShareEnumSticky is the
* same as NetShareEnum.
*
* Request for various levels of information about our shares.
* Level 0: share names.
* Level 1: share name, share type and comment field.
* Level 2: everything that we know about the shares.
* Level 501: not valid for this request.
* Level 502: level 2 + security descriptor.
*
* We set n_skip to resume_handle, which is used to find the appropriate
* place to resume. The resume_handle is similar to the readdir cookie.
*/
static int
{
struct mslm_infonres *infonres;
return (MLRPC_DRC_OK);
}
infonres->entriesread = 0;
else
if (param->resume_handle) {
}
case 0:
break;
case 1:
break;
case 2:
break;
case 502:
break;
default:
break;
}
if (status != ERROR_SUCCESS) {
return (MLRPC_DRC_OK);
}
if (param->resume_handle)
*param->resume_handle = 0;
return (MLRPC_DRC_OK);
}
if (param->resume_handle &&
} else {
*param->resume_handle = 0;
}
}
return (MLRPC_DRC_OK);
}
/*
* NetShareEnum Level 0
*/
static DWORD
{
struct mslm_SHARE_INFO_0 *info0;
return (ERROR_SUCCESS);
return (ERROR_NOT_ENOUGH_MEMORY);
return (ERROR_NOT_ENOUGH_MEMORY);
continue;
}
++se->se_resume_handle;
continue;
if (smb_is_autohome(si))
continue;
break;
}
if (status != ERROR_SUCCESS)
break;
}
}
return (ERROR_SUCCESS);
}
/*
* NetShareEnum Level 1
*/
static DWORD
{
struct mslm_SHARE_INFO_1 *info1;
return (ERROR_SUCCESS);
return (ERROR_NOT_ENOUGH_MEMORY);
return (ERROR_NOT_ENOUGH_MEMORY);
continue;
}
++se->se_resume_handle;
continue;
if (smb_is_autohome(si))
continue;
break;
}
if (status != ERROR_SUCCESS)
break;
}
}
return (ERROR_SUCCESS);
}
/*
* NetShareEnum Level 2
*/
static DWORD
{
struct mslm_SHARE_INFO_2 *info2;
return (ERROR_SUCCESS);
if (info2 == 0)
return (ERROR_NOT_ENOUGH_MEMORY);
return (ERROR_NOT_ENOUGH_MEMORY);
continue;
}
++se->se_resume_handle;
continue;
if (smb_is_autohome(si))
continue;
break;
}
if (status != ERROR_SUCCESS)
break;
}
}
return (ERROR_SUCCESS);
}
/*
* NetShareEnum Level 501
*/
static DWORD
{
struct mslm_SHARE_INFO_501 *info501;
return (ERROR_SUCCESS);
return (ERROR_NOT_ENOUGH_MEMORY);
return (ERROR_NOT_ENOUGH_MEMORY);
continue;
}
++se->se_resume_handle;
continue;
if (smb_is_autohome(si))
continue;
break;
}
if (status != ERROR_SUCCESS)
break;
}
}
return (ERROR_SUCCESS);
}
/*
* NetShareEnum Level 502
*/
static DWORD
{
struct mslm_SHARE_INFO_502 *info502;
return (ERROR_SUCCESS);
return (ERROR_NOT_ENOUGH_MEMORY);
return (ERROR_NOT_ENOUGH_MEMORY);
continue;
}
++se->se_resume_handle;
continue;
if (smb_is_autohome(si))
continue;
break;
}
if (status != ERROR_SUCCESS)
break;
}
}
return (ERROR_SUCCESS);
}
/*
* mlsvc_NetShareEnumCommon
*
* Build the levels 0, 1, 2, 501 and 502 share information. This function
* is called by the various NetShareEnum levels for each share. If
* we cannot build the share data for some reason, we return an error
* but the actual value of the error is not important to the caller.
* The caller just needs to know not to include this info in the RPC
* response.
*
* Returns:
* ERROR_SUCCESS
* ERROR_NOT_ENOUGH_MEMORY
* ERROR_INVALID_LEVEL
*/
static DWORD
{
struct mslm_SHARE_INFO_0 *info0;
struct mslm_SHARE_INFO_1 *info1;
struct mslm_SHARE_INFO_2 *info2;
struct mslm_SHARE_INFO_501 *info501;
struct mslm_SHARE_INFO_502 *info502;
char shr_comment[LMSHR_COMMENT_MAX];
/*
* Windows clients don't send the \\PIPE path for IPC$.
*/
}
else
case 0:
return (ERROR_NOT_ENOUGH_MEMORY);
break;
case 1:
info1[i].shi1_remark
return (ERROR_NOT_ENOUGH_MEMORY);
break;
case 2:
info2[i].shi2_remark
info2[i].shi2_permissions = 0;
info2[i].shi2_current_uses = 0;
info2[i].shi2_passwd
return (ERROR_NOT_ENOUGH_MEMORY);
break;
case 501:
info501[i].shi501_flags = 0;
return (ERROR_NOT_ENOUGH_MEMORY);
break;
case 502:
info502[i].shi502_permissions = 0;
info502[i].shi502_current_uses = 0;
info502[i].shi502_reserved = 0;
info502[i].shi502_security_descriptor = 0;
return (ERROR_NOT_ENOUGH_MEMORY);
break;
default:
return (ERROR_INVALID_LEVEL);
}
return (ERROR_SUCCESS);
}
/*
* srvsvc_add_autohome
*
* Add the autohome share for the user. The share must not be a permanent
* share to avoid duplicates.
*/
static boolean_t
{
return (B_FALSE);
return (B_FALSE);
return (status == ERROR_SUCCESS);
}
/*
* srvsvc_share_mkpath
*
* Create the share path required by the share enum calls. The path
* is created in a heap buffer ready for use by the caller.
*
* Some Windows over-the-wire backup applications do not work unless a
* drive letter is present in the share path. We don't care about the
* drive letter since the path is fully qualified with the volume name.
*
* Windows clients seem to be mostly okay with forward slashes in
* share paths but they cannot handle one immediately after the drive
* letter, i.e. B:/. For consistency we convert all the slashes in
* the path.
*
* Returns a pointer to a heap buffer containing the share path, which
* could be a null pointer if the heap allocation fails.
*/
static char *
{
char tmpbuf[MAXPATHLEN];
char *p;
/*
*/
p = path;
p += strspn(p, "/");
p += strcspn(p, "/");
p += strspn(p, "/");
}
/*
* srvsvc_s_NetShareDel
*
* Delete a share. Only the administrator, or a member of the domain
* administrators group, is allowed to delete shares.
*
* This interface is used by the rmtshare command from the NT resource
* kit. Rmtshare allows a client to add or remove shares on a server
* from the client's command line.
*
* Returns Win32 error codes.
*/
static int
{
if (!srvsvc_is_poweruser(mxa) ||
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
/*
* srvsvc_s_NetGetFileSecurity
*
*
* Right now, just returns ERROR_ACCESS_DENIED, because we cannot
* get the requested SD here in MLRPC code.
*/
/*ARGSUSED*/
static int
{
return (MLRPC_DRC_OK);
}
/*
* srvsvc_s_NetSetFileSecurity
*
*
* Right now, just returns ERROR_ACCESS_DENIED, because we cannot
* set the requested SD here in MLRPC code.
*/
/*ARGSUSED*/
static int
{
return (MLRPC_DRC_OK);
}
static mlrpc_stub_table_t srvsvc_stub_table[] = {
{0}
};